]> Creatis software - gdcm.git/blob - src/gdcmFileHelper.cxx
c12ae7b9e7a1a76fb17a230277430f44f00330f6
[gdcm.git] / src / gdcmFileHelper.cxx
1 /*=========================================================================
2                                                                                 
3   Program:   gdcm
4   Module:    $RCSfile: gdcmFileHelper.cxx,v $
5   Language:  C++
6
7   Date:      $Date: 2007/10/25 07:52:59 $
8   Version:   $Revision: 1.136 $
9                                                                                 
10   Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de
11   l'Image). All rights reserved. See Doc/License.txt or
12   http://www.creatis.insa-lyon.fr/Public/Gdcm/License.html for details.
13                                                                                 
14      This software is distributed WITHOUT ANY WARRANTY; without even
15      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
16      PURPOSE.  See the above copyright notices for more information.
17                                                                                 
18 =========================================================================*/
19
20 #include "gdcmFileHelper.h"
21 #include "gdcmGlobal.h"
22 #include "gdcmTS.h"
23 #include "gdcmDocument.h"
24 #include "gdcmDebug.h"
25 #include "gdcmUtil.h"
26 #include "gdcmSeqEntry.h"
27 #include "gdcmSQItem.h"
28 #include "gdcmDataEntry.h"
29 #include "gdcmDocEntry.h"
30 #include "gdcmFile.h"
31 #include "gdcmPixelReadConvert.h"
32 #include "gdcmPixelWriteConvert.h"
33 #include "gdcmDocEntryArchive.h"
34 #include "gdcmDictSet.h"
35 #include "gdcmOrientation.h"
36
37
38
39 #include <algorithm>  // for transform?
40
41 #if defined(__BORLANDC__)
42    #include <mem.h>   // for memset
43    #include <ctype.h> //for toupper
44    #include <math.h>
45 #endif 
46
47 #include <fstream>
48
49 /*
50 // ----------------------------- WARNING -------------------------
51
52 These lines will be moved to the document-to-be 'User's Guide'
53
54 // To read an image, user needs a gdcm::File
55 gdcm::File *f = new gdcm::File(fileName);
56 // or (advanced) :
57 // user may also decide he doesn't want to load some parts of the header
58 gdcm::File *f = new gdcm::File();
59 f->SetFileName(fileName);
60    f->SetLoadMode(LD_NOSEQ);               // or      
61    f->SetLoadMode(LD_NOSHADOW);            // or
62    f->SetLoadMode(LD_NOSEQ | LD_NOSHADOW); // or
63    f->SetLoadMode(LD_NOSHADOWSEQ);
64 f->Load();
65
66 // To decide whether it's an 'image of interest for him, or not,
67 // user can now check some values
68 std::string v = f->GetEntryValue(groupNb,ElementNb);
69
70 // to get the pixels, user needs a gdcm::FileHelper
71 gdcm::FileHelper *fh = new gdcm::FileHelper(f);
72
73 // user may ask not to convert Palette (if any) to RGB
74 uint8_t *pixels = fh->GetImageDataRaw();
75 int imageLength = fh->GetImageDataRawSize();
76
77 // He can now use the pixels, create a new image, ...
78 uint8_t *userPixels = ...
79
80 //To re-write the image, user re-uses the gdcm::FileHelper
81 gdcm::File *fh = new gdcm::FileHelper();
82
83 fh->SetTypeToRaw(); // Even if it was possible to convert Palette to RGB
84                     // (WriteMode is set)
85
86 // If user wants to write the file as MONOCHROME1 (0=white)
87 fh->SetPhotometricInterpretationToMonochrome1();
88
89 fh->SetWriteTypeToDcmExpl();  // he wants Explicit Value Representation
90                               // Little Endian is the default,
91                               // bigendian not supported for writting
92                                 (-->SetWriteType(ExplicitVR);)
93                                    -->WriteType = ExplicitVR;
94 fh->SetWriteTypeToJPEG();     // lossless compression   
95 fh->SetWriteTypeToJPEG2000(); // lossless compression   
96
97 fh->SetImageData( userPixels, userPixelsLength);
98 or
99 fh->SetUserData( userPixels, userPixelsLength); // this one performs compression, when required
100    
101 fh->Write(newFileName);      // overwrites the file, if any
102
103
104
105
106 These lines will be moved to the document-to-be 'Developer's Guide'
107
108 WriteMode : WMODE_RAW / WMODE_RGB
109 WriteType : ImplicitVR, ExplicitVR, ACR, ACR_LIBIDO
110 PhotometricInterpretation : MONOCHROME2 (0=black), MONOCHROME2 (0=white)
111
112 fh->SetImageData( userPixels, userPixelsLength);
113 or
114 fh->SetUserData( userPixels, userPixelsLength);
115    PixelWriteConverter->SetUserData(inData, expectedSize);
116    
117    
118 fh->SetWriteMode(WMODE_RAW / WMODE_RGB)
119
120 fh->SetWriteType( ImplicitVR/ExplicitVR/ACR/ACR_LIBIDO/JPEG/JPEG2000)
121       
122 fh->Write(newFileName);
123    CheckMandatoryElements(); // Checks existing ones / Add missing ones
124    Fix VR if unknown elements
125    SetWriteFileTypeToImplicitVR() / SetWriteFileTypeToExplicitVR(); /
126    SetWriteFileTypeToACR() / SetWriteFileTypeToJPEG() / SetWriteFileTypeToJ2K()
127       (Modifies TransferSyntax if any; Pushes to the Archives old one)
128    SetWriteToRaw(); / SetWriteToRGB();
129       (Modifies and pushes to the Archive, when necessary : photochr. interp., 
130        samples per pixel, Planar configuration, 
131        bits allocated, bits stored, high bit -ACR 24 bits-
132        Pixels element VR, pushes out the LUT )
133           SetWriteToRaw()
134              Sets Photometric Interpretation
135              DataEntry *pixel =CopyDataEntry(7fe0,0010,VR)
136              Sets VR, BinArea, Length for PixelData
137              if MONOCHROME1
138                 ConvertFixGreyLevels
139              Archive->Push(photInt);
140              Archive->Push(pixel);
141              photInt->Delete();
142              pixel->Delete();
143         SetWriteToRGB()
144            if NumberOfScalarComponents==1
145               SetWriteToRaw(); return;
146            PixelReadConverter->BuildRGBImage()
147            DataEntry *pixel =CopyDataEntry(7fe0,0010,VR)
148            Archives spp, planConfig,photInt, pixel
149            Pushes out any LUT               
150    CheckWriteIntegrity();
151       (checks user given pixels length)
152    FileInternal->Write(fileName,WriteType)
153       fp = opens file(fileName); // out|binary
154       ComputeGroup0002Length( );
155       Document::WriteContent(fp, writetype);
156          writes Dicom File Preamble not ACR-NEMA
157          ElementSet::WriteContent(fp, writetype);
158             writes recursively all DataElements    
159    RestoreWrite();
160          (moves back to the gdcm::File all the archived elements)
161 */
162
163
164
165
166 namespace GDCM_NAME_SPACE 
167 {
168 typedef std::map<uint16_t, int> GroupHT;    //  Hash Table
169 //-------------------------------------------------------------------------
170 // Constructor / Destructor
171 /**
172  * \brief Constructor dedicated to deal with the *pixels* area of a ACR/DICOMV3
173  *        file (gdcm::File only deals with the ... header)
174  *        Opens (in read only and when possible) an existing file and checks
175  *        for DICOM compliance. Returns NULL on failure.
176  *        It will be up to the user to load the pixels into memory
177  *        ( GetImageDataSize() + GetImageData() methods)
178  * \note  the in-memory representation of all available tags found in
179  *        the DICOM header is post-poned to first header information access.
180  *        This avoid a double parsing of public part of the header when
181  *        one sets an a posteriori shadow dictionary (efficiency can be
182  *        seen as a side effect).   
183  */
184 FileHelper::FileHelper( )
185
186    FileInternal = File::New( );
187    Initialize();
188 }
189
190 /**
191  * \brief Constructor dedicated to deal with the *pixels* area of a ACR/DICOMV3
192  *        file (File only deals with the ... header)
193  *        Opens (in read only and when possible) an existing file and checks
194  *        for DICOM compliance. Returns NULL on failure.
195  *        It will be up to the user to load the pixels into memory
196  *        ( GetImageDataSize() + GetImageData() methods)
197  * \note  the in-memory representation of all available tags found in
198  *        the DICOM header is post-poned to first header information access.
199  *        This avoid a double parsing of public part of the header when
200  *        user sets an a posteriori shadow dictionary (efficiency can be
201  *        seen as a side effect).   
202  * @param header already built Header
203  */
204 FileHelper::FileHelper(File *header)
205 {
206    gdcmAssertMacro(header);
207
208    FileInternal = header;
209    FileInternal->Register();
210    Initialize();
211    if ( FileInternal->IsReadable() )
212    {
213       PixelReadConverter->GrabInformationsFromFile( FileInternal, this );
214    }
215 }
216
217 /**
218  * \brief canonical destructor
219  * \note  If the header (gdcm::File) was created by the FileHelper constructor,
220  *        it is destroyed by the FileHelper
221  */
222 FileHelper::~FileHelper()
223
224    if ( PixelReadConverter )
225    {
226       delete PixelReadConverter;
227    }
228    if ( PixelWriteConverter )
229    {
230       delete PixelWriteConverter;
231    }
232    if ( Archive )
233    {
234       delete Archive;
235    }
236
237    FileInternal->Unregister();
238 }
239
240 //-----------------------------------------------------------------------------
241 // Public
242
243 /**
244  * \brief Sets the LoadMode of the internal gdcm::File as a boolean string. 
245  *        NO_SEQ, NO_SHADOW, NO_SHADOWSEQ ... (nothing more, right now)
246  *        WARNING : before using NO_SHADOW, be sure *all* your files
247  *        contain accurate values in the 0x0000 element (if any) 
248  *        of *each* Shadow Group. The parser will fail if the size is wrong !
249  * @param   loadMode Load mode to be used    
250  */
251 void FileHelper::SetLoadMode(int loadMode) 
252
253    GetFile()->SetLoadMode( loadMode ); 
254 }
255 /**
256  * \brief Sets the LoadMode of the internal gdcm::File
257  * @param  fileName name of the file to be open  
258  */
259 void FileHelper::SetFileName(std::string const &fileName)
260 {
261    FileInternal->SetFileName( fileName );
262 }
263
264 /**
265  * \brief   Loader  
266  * @return false if file cannot be open or no swap info was found,
267  *         or no tag was found.
268  */
269 bool FileHelper::Load()
270
271    if ( !FileInternal->Load() )
272       return false;
273
274    PixelReadConverter->GrabInformationsFromFile( FileInternal, this );
275    return true;
276 }
277
278 /**
279  * \brief   Accesses an existing DataEntry through its (group, element) 
280  *          and modifies its content with the given value.
281  * @param   content new value (string) to substitute with
282  * @param   group  group number of the Dicom Element to modify
283  * @param   elem element number of the Dicom Element to modify
284  * \return  false if DataEntry not found
285  */
286 bool FileHelper::SetEntryString(std::string const &content,
287                                 uint16_t group, uint16_t elem)
288
289    return FileInternal->SetEntryString(content, group, elem);
290 }
291
292
293 /**
294  * \brief   Accesses an existing DataEntry through its (group, element) 
295  *          and modifies its content with the given value.
296  * @param   content new value (void*  -> uint8_t*) to substitute with
297  * @param   lgth new value length
298  * @param   group  group number of the Dicom Element to modify
299  * @param   elem element number of the Dicom Element to modify
300  * \return  false if DataEntry not found
301  */
302 bool FileHelper::SetEntryBinArea(uint8_t *content, int lgth,
303                                  uint16_t group, uint16_t elem)
304 {
305    return FileInternal->SetEntryBinArea(content, lgth, group, elem);
306 }
307
308 /**
309  * \brief   Modifies the value of a given DataEntry when it exists.
310  *          Creates it with the given value when unexistant.
311  * @param   content (string) value to be set
312  * @param   group   Group number of the Entry 
313  * @param   elem  Element number of the Entry
314  * @param   vr  Value Representation of the DataElement to be inserted
315  * \return  pointer to the modified/created DataEntry (NULL when creation
316  *          failed).
317  */ 
318 DataEntry *FileHelper::InsertEntryString(std::string const &content,
319                                          uint16_t group, uint16_t elem,
320                                          VRKey const &vr )
321 {
322    return FileInternal->InsertEntryString(content, group, elem, vr);
323 }
324
325 /**
326  * \brief   Modifies the value of a given DataEntry when it exists.
327  *          Creates it with the given value when unexistant.
328  *          A copy of the binArea is made to be kept in the Document.
329  * @param   binArea (binary) value to be set
330  * @param   lgth new value length
331  * @param   group   Group number of the Entry 
332  * @param   elem  Element number of the Entry
333  * @param   vr  Value Representation of the DataElement to be inserted 
334  * \return  pointer to the modified/created DataEntry (NULL when creation
335  *          failed).
336  */
337 DataEntry *FileHelper::InsertEntryBinArea(uint8_t *binArea, int lgth,
338                                           uint16_t group, uint16_t elem,
339                                           VRKey const &vr )
340 {
341    return FileInternal->InsertEntryBinArea(binArea, lgth, group, elem, vr);
342 }
343
344 /**
345  * \brief   Adds an empty SeqEntry 
346  *          (remove any existing entry with same group,elem)
347  * @param   group   Group number of the Entry 
348  * @param   elem  Element number of the Entry
349  * \return  pointer to the created SeqEntry (NULL when creation
350  *          failed).
351  */
352 SeqEntry *FileHelper::InsertSeqEntry(uint16_t group, uint16_t elem)
353 {
354    return FileInternal->InsertSeqEntry(group, elem);
355 }
356
357 /**
358  * \brief   Get the size of the image data
359  *          If the image can be RGB (with a lut or by default), the size 
360  *          corresponds to the RGB image
361  *         (use GetImageDataRawSize if you want to be sure to get *only*
362  *          the size of the pixels)
363  * @return  The image size
364  */
365 size_t FileHelper::GetImageDataSize()
366 {
367    if ( PixelWriteConverter->GetUserData() )
368    {
369       return PixelWriteConverter->GetUserDataSize();
370    }
371    return PixelReadConverter->GetRGBSize();
372 }
373
374 /**
375  * \brief   Get the size of the image data.
376  *          If the image could be converted to RGB using a LUT, 
377  *          this transformation is not taken into account by GetImageDataRawSize
378  *          (use GetImageDataSize if you wish)
379  * @return  The raw image size
380  */
381 size_t FileHelper::GetImageDataRawSize()
382 {
383    if ( PixelWriteConverter->GetUserData() )
384    {
385       return PixelWriteConverter->GetUserDataSize();
386    }
387    return PixelReadConverter->GetRawSize();
388 }
389
390 /**
391  * \brief brings pixels into memory :  
392  *          - Allocates necessary memory,
393  *          - Reads the pixels from disk (uncompress if necessary),
394  *          - Transforms YBR pixels, if any, into RGB pixels,
395  *          - Transforms 3 planes R, G, B, if any, into a single RGB Plane
396  *          - Transforms single Grey plane + 3 Palettes into a RGB Plane
397  *          - Copies the pixel data (image[s]/volume[s]) to newly allocated zone.
398  * @return  Pointer to newly allocated pixel data.
399  *          (uint8_t is just for prototyping. feel free to cast)
400  *          NULL if alloc fails 
401  */
402 uint8_t *FileHelper::GetImageData()
403 {
404    if ( PixelWriteConverter->GetUserData() )
405    {
406       return PixelWriteConverter->GetUserData();
407    }
408
409    if ( ! GetRaw() )
410    {
411       // If the decompression failed nothing can be done.
412       return 0;
413    }
414
415    if ( FileInternal->HasLUT() && PixelReadConverter->BuildRGBImage() )
416    {
417       return PixelReadConverter->GetRGB();
418    }
419    else
420    {
421       // When no LUT or LUT conversion fails, return the Raw
422       return PixelReadConverter->GetRaw();
423    }
424 }
425
426 /**
427  * \brief brings pixels into memory :  
428  *          - Allocates necessary memory, 
429  *          - Transforms YBR pixels (if any) into RGB pixels
430  *          - Transforms 3 planes R, G, B  (if any) into a single RGB Plane
431  *          - Copies the pixel data (image[s]/volume[s]) to newly allocated zone. 
432  *          - DOES NOT transform Grey plane + 3 Palettes into a RGB Plane
433  * @return  Pointer to newly allocated pixel data.
434  *          (uint8_t is just for prototyping. feel free to cast)
435  *          NULL if alloc fails
436  */
437 uint8_t *FileHelper::GetImageDataRaw ()
438 {
439    return GetRaw();
440 }
441
442 //#ifndef GDCM_LEGACY_REMOVE
443 /*
444  * \brief   Useless function, since PixelReadConverter forces us 
445  *          copy the Pixels anyway.  
446  *          Reads the pixels from disk (uncompress if necessary),
447  *          Transforms YBR pixels, if any, into RGB pixels
448  *          Transforms 3 planes R, G, B, if any, into a single RGB Plane
449  *          Transforms single Grey plane + 3 Palettes into a RGB Plane   
450  *          Copies at most MaxSize bytes of pixel data to caller allocated
451  *          memory space.
452  * \warning This function allows people that want to build a volume
453  *          from an image stack *not to* have, first to get the image pixels, 
454  *          and then move them to the volume area.
455  *          It's absolutely useless for any VTK user since vtk chooses 
456  *          to invert the lines of an image, that is the last line comes first
457  *          (for some axis related reasons?). Hence he will have 
458  *          to load the image line by line, starting from the end.
459  *          VTK users have to call GetImageData
460  *     
461  * @param   destination Address (in caller's memory space) at which the
462  *          pixel data should be copied
463  * @param   maxSize Maximum number of bytes to be copied. When MaxSize
464  *          is not sufficient to hold the pixel data the copy is not
465  *          executed (i.e. no partial copy).
466  * @return  On success, the number of bytes actually copied. Zero on
467  *          failure e.g. MaxSize is lower than necessary.
468  */
469  /*
470 size_t FileHelper::GetImageDataIntoVector (void *destination, size_t maxSize)
471 {
472    if ( ! GetRaw() )
473    {
474       // If the decompression failed nothing can be done.
475       return 0;
476    }
477
478    if ( FileInternal->HasLUT() && PixelReadConverter->BuildRGBImage() )
479    {
480       if ( PixelReadConverter->GetRGBSize() > maxSize )
481       {
482          gdcmWarningMacro( "Pixel data bigger than caller's expected MaxSize");
483          return 0;
484       }
485       memcpy( destination,
486               (void*)PixelReadConverter->GetRGB(),
487               PixelReadConverter->GetRGBSize() );
488       return PixelReadConverter->GetRGBSize();
489    }
490
491    // Either no LUT conversion necessary or LUT conversion failed
492    if ( PixelReadConverter->GetRawSize() > maxSize )
493    {
494       gdcmWarningMacro( "Pixel data bigger than caller's expected MaxSize");
495       return 0;
496    }
497    memcpy( destination,
498            (void *)PixelReadConverter->GetRaw(),
499            PixelReadConverter->GetRawSize() );
500    return PixelReadConverter->GetRawSize();
501 }
502 */
503 //#endif
504
505 /**
506  * \brief   Points the internal pointer to the callers inData
507  *          image representation, BUT WITHOUT COPYING THE DATA.
508  *          'image' Pixels are presented as C-like 2D arrays : line per line.
509  *          'volume'Pixels are presented as C-like 3D arrays : plane per plane 
510  * \warning Since the pixels are not copied, it is the caller's responsability
511  *          not to deallocate its data before gdcm uses them (e.g. with
512  *          the Write() method )
513  * @param inData user supplied pixel area (uint8_t* is just for the compiler.
514  *               user is allowed to pass any kind of pixels since the size is
515  *               given in bytes) 
516  * @param expectedSize total image size, *in Bytes*
517  */
518 void FileHelper::SetImageData(uint8_t *inData, size_t expectedSize)
519 {
520    PixelWriteConverter->SetUserData(inData, expectedSize);
521    /// \todo : shouldn't we call SetCompressJPEGUserData/SetCompressJPEG2000UserData
522    ///         here, too?
523 }
524
525 /**
526  * \brief   Set the image data defined by the user
527  * \warning When writting the file, this data are get as default data to write
528  * @param inData user supplied pixel area (uint8_t* is just for the compiler.
529  *               user is allowed to pass any kind of pixels since the size is
530  *               given in bytes) 
531  * @param expectedSize total image size, *in Bytes* 
532  */
533 void FileHelper::SetUserData(uint8_t *inData, size_t expectedSize)
534 {
535   // Shouldn't we move theese lines to FileHelper::Write()?
536 /*  
537    if( WriteType == JPEG2000 )
538    {
539       PixelWriteConverter->SetCompressJPEG2000UserData(inData, expectedSize, FileInternal);
540    }
541    else if( WriteType == JPEG )
542    {
543       PixelWriteConverter->SetCompressJPEGUserData(inData, expectedSize, FileInternal);
544    }
545    else
546    {
547       PixelWriteConverter->SetUserData(inData, expectedSize);
548    }
549    */
550    // Just try!
551    PixelWriteConverter->SetUserData(inData, expectedSize);
552 }
553
554 /**
555  * \brief   Get the image data defined by the user
556  * \warning When writting the file, this data are get as default data to write
557  */
558 uint8_t *FileHelper::GetUserData()
559 {
560    return PixelWriteConverter->GetUserData();
561 }
562
563 /**
564  * \brief   Get the image data size defined by the user
565  * \warning When writting the file, this data are get as default data to write
566  */
567 size_t FileHelper::GetUserDataSize()
568 {
569    return PixelWriteConverter->GetUserDataSize();
570 }
571
572 /**
573  * \brief   Get the image data from the file.
574  *          If a LUT is found, the data are expanded to be RGB
575  */
576 uint8_t *FileHelper::GetRGBData()
577 {
578    return PixelReadConverter->GetRGB();
579 }
580
581 /**
582  * \brief   Get the image data size from the file.
583  *          If a LUT is found, the data are expanded to be RGB
584  */
585 size_t FileHelper::GetRGBDataSize()
586 {
587    return PixelReadConverter->GetRGBSize();
588 }
589
590 /**
591  * \brief   Get the image data from the file.
592  *          Even when a LUT is found, the data are not expanded to RGB!
593  */
594 uint8_t *FileHelper::GetRawData()
595 {
596    return PixelReadConverter->GetRaw();
597 }
598
599 /**
600  * \brief   Get the image data size from the file.
601  *          Even when a LUT is found, the data are not expanded to RGB!
602  */
603 size_t FileHelper::GetRawDataSize()
604 {
605    return PixelReadConverter->GetRawSize();
606 }
607
608 /**
609  * \brief Access to the underlying PixelReadConverter RGBA LUT
610  */
611 uint8_t* FileHelper::GetLutRGBA()
612 {
613    if ( PixelReadConverter->GetLutRGBA() ==0 )
614       PixelReadConverter->BuildLUTRGBA();
615    return PixelReadConverter->GetLutRGBA();
616 }
617
618 /**
619  * \brief Access to the underlying PixelReadConverter RGBA LUT Item Number
620  */
621 int FileHelper::GetLutItemNumber()
622 {
623    return PixelReadConverter->GetLutItemNumber();
624 }
625
626 /**
627  * \brief Access to the underlying PixelReadConverter RGBA LUT Item Size
628  */
629 int FileHelper::GetLutItemSize()
630 {
631    return PixelReadConverter->GetLutItemSize();
632 }
633
634 /**
635  * \brief Writes on disk A SINGLE Dicom file
636  *        NO test is performed on  processor "Endiannity".
637  *        It's up to the user to call his Reader properly
638  * @param fileName name of the file to be created
639  *                 (any already existing file is over written)
640  * @return false if write fails
641  */
642 bool FileHelper::WriteRawData(std::string const &fileName)
643 {
644    std::ofstream fp1(fileName.c_str(), std::ios::out | std::ios::binary );
645    if (!fp1)
646    {
647       gdcmWarningMacro( "Fail to open (write) file:" << fileName.c_str());
648       return false;
649    }
650
651    if ( PixelWriteConverter->GetUserData() )
652    {
653       fp1.write( (char *)PixelWriteConverter->GetUserData(), 
654                  PixelWriteConverter->GetUserDataSize() );
655    }
656    else if ( PixelReadConverter->GetRGB() )
657    {
658       fp1.write( (char *)PixelReadConverter->GetRGB(), 
659                  PixelReadConverter->GetRGBSize());
660    }
661    else if ( PixelReadConverter->GetRaw() )
662    {
663       fp1.write( (char *)PixelReadConverter->GetRaw(), 
664                  PixelReadConverter->GetRawSize());
665    }
666    else
667    {
668       gdcmErrorMacro( "Nothing written." );
669    }
670
671    fp1.close();
672
673    return true;
674 }
675
676 /**
677  * \brief Writes on disk A SINGLE Dicom file, 
678  *        using the Implicit Value Representation convention
679  *        NO test is performed on  processor "Endianity".
680  * @param fileName name of the file to be created
681  *                 (any already existing file is overwritten)
682  * @return false if write fails
683  */
684
685 bool FileHelper::WriteDcmImplVR (std::string const &fileName)
686 {
687    SetWriteTypeToDcmImplVR();
688    return Write(fileName);
689 }
690
691 /**
692 * \brief Writes on disk A SINGLE Dicom file, 
693  *        using the Explicit Value Representation convention
694  *        NO test is performed on  processor "Endiannity". 
695  * @param fileName name of the file to be created
696  *                 (any already existing file is overwritten)
697  * @return false if write fails
698  */
699
700 bool FileHelper::WriteDcmExplVR (std::string const &fileName)
701 {
702    SetWriteTypeToDcmExplVR();
703    return Write(fileName);
704 }
705
706 /**
707  * \brief Writes on disk A SINGLE Dicom file, 
708  *        using the ACR-NEMA convention
709  *        NO test is performed on  processor "Endiannity".
710  *        (a l'attention des logiciels cliniques 
711  *        qui ne prennent en entrée QUE des images ACR ...
712  * \warning if a DICOM_V3 header is supplied,
713  *         groups < 0x0008 and shadow groups are ignored
714  * \warning NO TEST is performed on processor "Endiannity".
715  * @param fileName name of the file to be created
716  *                 (any already existing file is overwritten)
717  * @return false if write fails
718  */
719
720 bool FileHelper::WriteAcr (std::string const &fileName)
721 {
722    SetWriteTypeToAcr();
723    return Write(fileName);
724 }
725
726 /**
727  * \brief Writes on disk A SINGLE Dicom file, 
728  * @param fileName name of the file to be created
729  *                 (any already existing file is overwritten)
730  * @return false if write fails
731  */
732 bool FileHelper::Write(std::string const &fileName)
733
734    CheckMandatoryElements(); //called once, here !
735    
736    switch(WriteType)
737    {
738       case ImplicitVR:
739          SetWriteFileTypeToImplicitVR();
740          break;
741  
742       case Unknown:  // should never happen; ExplicitVR is the default value
743       case ExplicitVR:
744    
745    // We let DocEntry::WriteContent to put vr=UN for undocumented Shadow Groups !
746          SetWriteFileTypeToExplicitVR();
747
748   break;
749       case ACR:
750       case ACR_LIBIDO:
751       // NOTHING is done here just for LibIDO.
752       // Just to avoid further trouble if user creates a file ex-nihilo,
753       // wants to write it as an ACR-NEMA file,
754       // and forgets to create any Entry belonging to group 0008
755       // (shame on him !)
756       // We add Recognition Code (RET)
757         if ( ! FileInternal->GetDataEntry(0x0008, 0x0010) )
758             FileInternal->InsertEntryString("ACR-NEMA V1.0 ", 
759                                              0x0008, 0x0010, "LO");
760          SetWriteFileTypeToACR();
761         // SetWriteFileTypeToImplicitVR(); // ACR IS implicit VR !
762          break;
763  
764       /// \todo FIXME : JPEG/JPEG2000 may be either ExplicitVR or ImplicitVR      
765       case JPEG:
766          SetWriteFileTypeToJPEG();
767          // was :
768          //PixelWriteConverter->SetCompressJPEGUserData(
769          //   inData, expectedSize, FileInternal);
770          PixelWriteConverter->SetCompressJPEGUserData(
771                  PixelWriteConverter->GetUserData(),
772                  PixelWriteConverter->GetUserDataSize(),FileInternal);
773          break;
774
775       case JPEG2000:
776          /// \todo Maybe we should consider doing the compression here !
777          // PixelWriteConverter->SetCompressJPEG2000UserData(inData, expectedSize, FileInternal);
778
779          SetWriteFileTypeToJPEG2000();
780          PixelWriteConverter->SetCompressJPEG2000UserData(
781             PixelWriteConverter->GetUserData(),
782             PixelWriteConverter->GetUserDataSize(),
783             FileInternal);
784          break;
785    }
786
787    // --------------------------------------------------------------
788    // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
789    //
790    // if recognition code tells us we dealt with a LibIDO image
791    // we reproduce on disk the switch between lineNumber and columnNumber
792    // just before writting ...
793    /// \todo the best trick would be *change* the recognition code
794    ///       but pb expected if user deals with, e.g. COMPLEX images
795    
796    if ( WriteType == ACR_LIBIDO )
797    {
798       SetWriteToLibido();
799    }
800    else
801    {
802       SetWriteToNoLibido();
803    }
804    // ----------------- End of Special Patch ----------------
805   
806    switch(WriteMode)
807    {
808       case WMODE_RAW :
809          SetWriteToRaw(); // modifies and pushes to the archive, when necessary
810          break;
811       case WMODE_RGB :
812          SetWriteToRGB(); // modifies and pushes to the archive, when necessary
813          break;
814    }
815
816    bool check;
817    if (WriteType == JPEG || WriteType == JPEG2000)
818       check = true;
819    else
820       check = CheckWriteIntegrity(); // verifies length
821
822    if (check)
823    {
824       check = FileInternal->Write(fileName,WriteType);
825    }
826
827    RestoreWrite();
828   // RestoreWriteFileType();
829   // RestoreWriteMandatory();
830
831    // --------------------------------------------------------------
832    // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
833    // 
834    // ...and we restore the header to be Dicom Compliant again 
835    // just after writting
836    RestoreWriteOfLibido();
837    // ----------------- End of Special Patch ----------------
838
839    return check;
840 }
841
842 //-----------------------------------------------------------------------------
843 // Protected
844 /**
845  * \brief Verifies the size of the user given PixelData
846  * @return true if check is successfull
847  */
848 bool FileHelper::CheckWriteIntegrity()
849 {
850    if ( PixelWriteConverter->GetUserData() )
851    {
852       int numberBitsAllocated = FileInternal->GetBitsAllocated();
853       if ( numberBitsAllocated == 0 || numberBitsAllocated == 12 )
854       {
855          gdcmWarningMacro( "numberBitsAllocated changed from "
856                           << numberBitsAllocated << " to 16 "
857                           << " for consistency purpose" );
858          numberBitsAllocated = 16;
859       }
860
861       size_t decSize = FileInternal->GetXSize()
862                      * FileInternal->GetYSize()
863                      * FileInternal->GetZSize()
864                      * FileInternal->GetTSize()     
865                      * FileInternal->GetSamplesPerPixel()
866                      * ( numberBitsAllocated / 8 );
867       size_t rgbSize = decSize;
868       if ( FileInternal->HasLUT() )
869          rgbSize = decSize * 3;
870
871       size_t userDataSize = PixelWriteConverter->GetUserDataSize();
872       switch(WriteMode)
873       {
874          case WMODE_RAW :
875             if ( abs((long)(decSize-userDataSize))>1) // ignore padding zero
876             {
877                gdcmWarningMacro( "Data size (Raw) is incorrect. Should be " 
878                            << decSize << " / Found :" 
879                            << userDataSize );
880                return false;
881             }
882             break;
883          case WMODE_RGB :
884             if ( abs((long)(rgbSize-userDataSize))>1) // ignore padding zero
885             {
886                gdcmWarningMacro( "Data size (RGB) is incorrect. Should be " 
887                           << rgbSize << " / Found " 
888                           << userDataSize );
889                return false;
890             }
891             break;
892       }
893    }
894    return true;
895 }
896
897 /**
898  * \brief Updates the File to write RAW data (as opposed to RGB data)
899  *       (modifies, when necessary, photochromatic interpretation, 
900  *       bits allocated, Pixels element VR)
901  *       WARNING : if SetPhotometricInterpretationToMonochrome1() was called
902  *                 before Pixel Elements is modified :-( 
903  */ 
904 void FileHelper::SetWriteToRaw()
905 {
906    if ( FileInternal->GetNumberOfScalarComponents() == 3 
907     && !FileInternal->HasLUT() )
908    {
909       SetWriteToRGB();
910    } 
911    else
912    {
913       // 0x0028,0x0004 : Photometric Interpretation
914       DataEntry *photInt = CopyDataEntry(0x0028,0x0004,"CS");
915       if (FileInternal->HasLUT() )
916       {
917          photInt->SetString("PALETTE COLOR ");
918       }
919       else
920       {
921          if (GetPhotometricInterpretation() == 2)
922             photInt->SetString("MONOCHROME2 ");  // 0 = Black
923          else
924             photInt->SetString("MONOCHROME1 ");  // 0 = White !
925       }
926
927       PixelWriteConverter->SetReadData(PixelReadConverter->GetRaw(),
928                                        PixelReadConverter->GetRawSize());
929
930       std::string vr = "OB";
931       if ( FileInternal->GetBitsAllocated()>8 )
932          vr = "OW";
933       if ( FileInternal->GetBitsAllocated()==24 ) // For RGB ACR files 
934          vr = "OB";
935        // For non RAW data. Mainly JPEG/JPEG2000
936       if( WriteType == JPEG || WriteType == JPEG2000)
937       {
938          vr = "OW";
939       }
940
941       DataEntry *pixel = 
942          CopyDataEntry(GetFile()->GetGrPixel(),GetFile()->GetNumPixel(),vr);
943       pixel->SetFlag(DataEntry::FLAG_PIXELDATA);
944       pixel->SetBinArea(PixelWriteConverter->GetData(),false);
945       pixel->SetLength(
946          static_cast< uint32_t >(PixelWriteConverter->GetDataSize()) );
947
948       if (!FileInternal->HasLUT() && GetPhotometricInterpretation() == 1)
949       {
950           ConvertFixGreyLevels( pixel->GetBinArea(), pixel->GetLength() );
951       }
952
953       Archive->Push(photInt);
954       Archive->Push(pixel);
955
956       photInt->Delete();
957       pixel->Delete();
958    }
959 }
960
961 /**
962  * \brief Updates the File to write RGB data (as opposed to RAW data)
963  *       (modifies, when necessary, photochromatic interpretation, 
964  *       samples per pixel, Planar configuration, 
965  *       bits allocated, bits stored, high bit -ACR 24 bits-
966  *       Pixels element VR, pushes out the LUT, )
967  */ 
968 void FileHelper::SetWriteToRGB()
969 {
970    if ( FileInternal->GetNumberOfScalarComponents()==3 )
971    {
972       PixelReadConverter->BuildRGBImage();
973       
974       DataEntry *spp = CopyDataEntry(0x0028,0x0002,"US");
975       spp->SetString("3 ");  // Don't drop trailing space
976
977       DataEntry *planConfig = CopyDataEntry(0x0028,0x0006,"US");
978       planConfig->SetString("0 "); // Don't drop trailing space
979
980       DataEntry *photInt = CopyDataEntry(0x0028,0x0004,"CS");
981       photInt->SetString("RGB "); // Don't drop trailing space
982
983       if ( PixelReadConverter->GetRGB() )
984       {
985          PixelWriteConverter->SetReadData(PixelReadConverter->GetRGB(),
986                                           PixelReadConverter->GetRGBSize());
987       }
988       else // Raw data
989       {
990          PixelWriteConverter->SetReadData(PixelReadConverter->GetRaw(),
991                                           PixelReadConverter->GetRawSize());
992       }
993
994       std::string vr = "OB";
995       if ( FileInternal->GetBitsAllocated()>8 )
996          vr = "OW";
997       if ( FileInternal->GetBitsAllocated()==24 ) // For RGB ACR files 
998          vr = "OB";
999       DataEntry *pixel = 
1000          CopyDataEntry(GetFile()->GetGrPixel(),GetFile()->GetNumPixel(),vr);
1001       pixel->SetFlag(DataEntry::FLAG_PIXELDATA);
1002       pixel->SetBinArea(PixelWriteConverter->GetData(),false);
1003       pixel->SetLength(PixelWriteConverter->GetDataSize());
1004
1005       Archive->Push(spp);
1006       Archive->Push(planConfig);
1007       Archive->Push(photInt);
1008       Archive->Push(pixel);
1009
1010       spp->Delete();
1011       planConfig->Delete();
1012       photInt->Delete();
1013       pixel->Delete();
1014
1015       // Remove any LUT
1016       Archive->Push(0x0028,0x1101);
1017       Archive->Push(0x0028,0x1102);
1018       Archive->Push(0x0028,0x1103);
1019       Archive->Push(0x0028,0x1201);
1020       Archive->Push(0x0028,0x1202);
1021       Archive->Push(0x0028,0x1203);
1022
1023       // push out Palette Color Lookup Table UID, if any
1024       Archive->Push(0x0028,0x1199);
1025
1026       // For old '24 Bits' ACR-NEMA
1027       // Thus, we have a RGB image and the bits allocated = 24 and 
1028       // samples per pixels = 1 (in the read file)
1029       if ( FileInternal->GetBitsAllocated()==24 ) 
1030       {
1031          DataEntry *bitsAlloc = CopyDataEntry(0x0028,0x0100,"US");
1032          bitsAlloc->SetString("8 ");
1033
1034          DataEntry *bitsStored = CopyDataEntry(0x0028,0x0101,"US");
1035          bitsStored->SetString("8 ");
1036
1037          DataEntry *highBit = CopyDataEntry(0x0028,0x0102,"US");
1038          highBit->SetString("7 ");
1039
1040          Archive->Push(bitsAlloc);
1041          Archive->Push(bitsStored);
1042          Archive->Push(highBit);
1043
1044          bitsAlloc->Delete();
1045          bitsStored->Delete();
1046          highBit->Delete();
1047       }
1048    }
1049    else
1050    {
1051       SetWriteToRaw();
1052    }
1053 }
1054
1055 /**
1056  * \brief Restore the File write mode  
1057  */ 
1058 void FileHelper::RestoreWrite()
1059 {
1060    Archive->Restore(0x0028,0x0002);
1061    Archive->Restore(0x0028,0x0004);
1062    
1063    Archive->Restore(0x0028,0x0006);
1064    Archive->Restore(GetFile()->GetGrPixel(),GetFile()->GetNumPixel());
1065
1066    // For old ACR-NEMA (24 bits problem)
1067    Archive->Restore(0x0028,0x0100);
1068    Archive->Restore(0x0028,0x0101);
1069    Archive->Restore(0x0028,0x0102);
1070
1071    // For the LUT
1072    Archive->Restore(0x0028,0x1101);
1073    Archive->Restore(0x0028,0x1102);
1074    Archive->Restore(0x0028,0x1103);
1075    Archive->Restore(0x0028,0x1201);
1076    Archive->Restore(0x0028,0x1202);
1077    Archive->Restore(0x0028,0x1203);
1078
1079    // For the Palette Color Lookup Table UID
1080    Archive->Restore(0x0028,0x1203); 
1081
1082    // group 0002 may be pushed out for ACR-NEMA writting purposes 
1083    Archive->Restore(0x0002,0x0000);
1084    Archive->Restore(0x0002,0x0001);
1085    Archive->Restore(0x0002,0x0002);
1086    Archive->Restore(0x0002,0x0003);
1087    Archive->Restore(0x0002,0x0010);
1088    Archive->Restore(0x0002,0x0012);
1089    Archive->Restore(0x0002,0x0013);
1090    Archive->Restore(0x0002,0x0016);
1091    Archive->Restore(0x0002,0x0100);
1092    Archive->Restore(0x0002,0x0102);
1093
1094 }
1095
1096 /**
1097  * \brief Pushes out the whole group 0002
1098  *        FIXME : better, set a flag to tell the writer not to write it ...
1099  *        FIXME : method should probably have an other name !
1100  *                SetWriteFileTypeToACR is NOT opposed to 
1101  *                SetWriteFileTypeToExplicitVR and SetWriteFileTypeToImplicitVR
1102  */ 
1103 void FileHelper::SetWriteFileTypeToACR()
1104 {
1105    Archive->Push(0x0002,0x0000);
1106    Archive->Push(0x0002,0x0001);
1107    Archive->Push(0x0002,0x0002);
1108    Archive->Push(0x0002,0x0003);
1109    Archive->Push(0x0002,0x0010);
1110    Archive->Push(0x0002,0x0012);
1111    Archive->Push(0x0002,0x0013);
1112    Archive->Push(0x0002,0x0016);
1113    Archive->Push(0x0002,0x0100);
1114    Archive->Push(0x0002,0x0102);
1115 }
1116
1117 /**
1118  * \brief Sets in the File the TransferSyntax to 'JPEG2000'
1119  */
1120 void FileHelper::SetWriteFileTypeToJPEG2000()
1121 {
1122    std::string ts = Util::DicomString(
1123    Global::GetTS()->GetSpecialTransferSyntax(TS::JPEG2000Lossless) );
1124
1125    DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1126    tss->SetString(ts);
1127
1128    Archive->Push(tss);
1129    tss->Delete();   
1130 }
1131
1132 /**
1133  * \brief Sets in the File the TransferSyntax to 'JPEG'
1134  */
1135 void FileHelper::SetWriteFileTypeToJPEG()
1136 {
1137    std::string ts = Util::DicomString(
1138       Global::GetTS()->GetSpecialTransferSyntax(TS::JPEGLosslessProcess14_1) );
1139
1140    DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1141    tss->SetString(ts);
1142
1143    Archive->Push(tss);
1144    tss->Delete();
1145 }
1146
1147 /**
1148  * \brief Sets in the File the TransferSyntax to 'Explicit VR Little Endian"   
1149  */ 
1150 void FileHelper::SetWriteFileTypeToExplicitVR()
1151 {
1152    std::string ts = Util::DicomString( 
1153       Global::GetTS()->GetSpecialTransferSyntax(TS::ExplicitVRLittleEndian) );
1154
1155    DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1156    tss->SetString(ts);
1157    Archive->Push(tss);
1158    tss->Delete();
1159 }
1160
1161 /**
1162  * \brief Sets in the File the TransferSyntax to 'Implicit VR Little Endian"   
1163  */ 
1164 void FileHelper::SetWriteFileTypeToImplicitVR()
1165 {
1166    std::string ts = Util::DicomString(
1167       Global::GetTS()->GetSpecialTransferSyntax(TS::ImplicitVRLittleEndian) );
1168
1169    DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1170    tss->SetString(ts);
1171    Archive->Push(tss);
1172    tss->Delete();
1173 }
1174
1175 /**
1176  * \brief Set the Write not to Libido format
1177  */ 
1178 void FileHelper::SetWriteToLibido()
1179 {
1180    DataEntry *oldRow = FileInternal->GetDataEntry(0x0028, 0x0010);
1181    DataEntry *oldCol = FileInternal->GetDataEntry(0x0028, 0x0011);
1182    
1183    if ( oldRow && oldCol )
1184    {
1185       std::string rows, columns; 
1186
1187       DataEntry *newRow=DataEntry::New(0x0028, 0x0010, "US");
1188       DataEntry *newCol=DataEntry::New(0x0028, 0x0011, "US");
1189
1190       newRow->Copy(oldCol);
1191       newCol->Copy(oldRow);
1192
1193       newRow->SetString(oldCol->GetString());
1194       newCol->SetString(oldRow->GetString());
1195
1196       Archive->Push(newRow);
1197       Archive->Push(newCol);
1198
1199       newRow->Delete();
1200       newCol->Delete();
1201    }
1202
1203    DataEntry *libidoCode = CopyDataEntry(0x0008,0x0010,"LO");
1204    libidoCode->SetString("ACRNEMA_LIBIDO_1.1");
1205    Archive->Push(libidoCode);
1206    libidoCode->Delete();
1207 }
1208
1209 /**
1210  * \brief Set the Write not to No Libido format
1211  */ 
1212 void FileHelper::SetWriteToNoLibido()
1213 {
1214    DataEntry *recCode = FileInternal->GetDataEntry(0x0008,0x0010);
1215    if ( recCode )
1216    {
1217       if ( recCode->GetString() == "ACRNEMA_LIBIDO_1.1" )
1218       {
1219          DataEntry *libidoCode = CopyDataEntry(0x0008,0x0010,"LO");
1220          libidoCode->SetString("");
1221          Archive->Push(libidoCode);
1222          libidoCode->Delete();
1223       }
1224    }
1225 }
1226
1227 /**
1228  * \brief Restore the Write format
1229  */ 
1230 void FileHelper::RestoreWriteOfLibido()
1231 {
1232    Archive->Restore(0x0028,0x0010);
1233    Archive->Restore(0x0028,0x0011);
1234    Archive->Restore(0x0008,0x0010);
1235
1236    // Restore 'LibIDO-special' entries, if any
1237    Archive->Restore(0x0028,0x0015);
1238    Archive->Restore(0x0028,0x0016);
1239    Archive->Restore(0x0028,0x0017);
1240    Archive->Restore(0x0028,0x00199);
1241 }
1242
1243 /**
1244  * \brief   Duplicates a DataEntry or creates it.
1245  * @param   group   Group number of the Entry 
1246  * @param   elem  Element number of the Entry
1247  * @param   vr  Value Representation of the Entry
1248  * \return  pointer to the new Bin Entry (NULL when creation failed).
1249  */ 
1250 DataEntry *FileHelper::CopyDataEntry(uint16_t group, uint16_t elem,
1251                                    const VRKey &vr)
1252 {
1253    DocEntry *oldE = FileInternal->GetDocEntry(group, elem);
1254    DataEntry *newE;
1255
1256    if ( oldE && vr != GDCM_VRUNKNOWN ) 
1257       if ( oldE->GetVR() != vr )
1258          oldE = NULL;
1259
1260    if ( oldE )
1261    {
1262       newE = DataEntry::New(group, elem, vr);
1263       newE->Copy(oldE);
1264    }
1265    else
1266    {
1267       newE = GetFile()->NewDataEntry(group, elem, vr);
1268    }
1269
1270    return newE;
1271 }
1272
1273 /**
1274  * \brief   This method is called automatically, just before writting
1275  *         in order to produce a 'True Dicom V3' image.
1276  *
1277  *         We cannot know *how* the user made the File :
1278  *         (reading an old ACR-NEMA file or a not very clean DICOM file ...) 
1279  *          Just before writting :
1280  *             - we check the Entries
1281  *             - we create the mandatory entries if they are missing
1282  *             - we modify the values if necessary
1283  *             - we push the sensitive entries to the Archive
1284  *          The writing process will restore the entries as they where before 
1285  *          entering FileHelper::CheckMandatoryElements, so the user will always
1286  *          see the entries just as they were before he decided to write.
1287  *
1288  * \note
1289  *       -  Entries whose type is 1 are mandatory, with a mandatory value
1290  *       -  Entries whose type is 1c are mandatory-inside-a-Sequence,
1291  *                             with a mandatory value
1292  *       -  Entries whose type is 2 are mandatory, with an optional value
1293  *       -  Entries whose type is 2c are mandatory-inside-a-Sequence,
1294  *                             with an optional value
1295  *       -  Entries whose type is 3 are optional
1296  * 
1297  * \todo 
1298  *         - warn the user if we had to add some entries :
1299  *         even if a mandatory entry is missing, we add it, with a default value
1300  *         (we don't want to give up the writting process if user forgot to
1301  *         specify Lena's Patient ID, for instance ...)
1302  *         - read the whole PS 3.3 Part of DICOM  (890 pages)
1303  *         and write a *full* checker (probably one method per Modality ...)
1304  *         Any contribution is welcome. 
1305  *         - write a user callable full checker, to allow post reading
1306  *         and/or pre writting image consistency check.           
1307  */ 
1308
1309 /* -------------------------------------------------------------------------------------
1310 To be moved to User's guide / WIKI  ?
1311
1312 We have to deal with 4 *very* different cases :
1313 -1) user created ex nihilo his own image and wants to write it as a Dicom image.
1314     USER_OWN_IMAGE
1315 -2) user modified the pixels of an existing image.
1316    FILTERED_IMAGE
1317 -3) user created a new image, using a set of existing images (eg MIP, MPR, cartography image)
1318    CREATED_IMAGE
1319 -4) user modified/added some tags *without processing* the pixels (anonymization...)
1320    UNMODIFIED_PIXELS_IMAGE
1321 -Probabely some more to be added.
1322  --> Set it with FileHelper::SetContentType(int);
1323  
1324 gdcm::FileHelper::CheckMandatoryElements() deals automatically with these cases.
1325
1326 1)2)3)4)
1327 0008 0012 Instance Creation Date
1328 0008 0013 Instance Creation Time
1329 0008 0018 SOP Instance UID
1330 are *always* created with the current values; user has *no* possible intervention on
1331 them.
1332
1333 'Serie Instance UID'(0x0020,0x000e)
1334 'Study Instance UID'(0x0020,0x000d) are kept as is if already exist,
1335                                     created  if it doesn't.
1336  The user is allowed to create his own Series/Studies, 
1337      keeping the same 'Serie Instance UID' / 'Study Instance UID' for various images
1338  Warning :     
1339  The user shouldn't add any image to a 'Manufacturer Serie'
1340      but there is no way no to allow him to do that
1341      
1342  None of the 'shadow elements' are droped out.
1343      
1344
1345 1)
1346 'Conversion Type (0x0008,0x0064) is forced to 'SYN' (Synthetic Image).
1347  
1348 1)3)
1349 'Media Storage SOP Class UID' (0x0002,0x0002)
1350 'SOP Class UID'               (0x0008,0x0016) are set to 
1351                                                [Secondary Capture Image Storage]
1352 'Image Type'                  (0x0008,0x0008) is forced to  "DERIVED\PRIMARY"
1353 Conversion Type               (0x0008,0x0064) is forced to 'SYN' (Synthetic Image)
1354
1355 2)4)
1356 If 'SOP Class UID' exists in the native image  ('true DICOM' image)
1357     we create the 'Source Image Sequence' SeqEntry (0x0008, 0x2112)    
1358     --> 'Referenced SOP Class UID' (0x0008, 0x1150)
1359          whose value is the original 'SOP Class UID'
1360     --> 'Referenced SOP Instance UID' (0x0008, 0x1155)
1361          whose value is the original 'SOP Class UID'
1362
1363 3) TO DO : find a trick to allow user to pass to the writter the list of the Dicom images 
1364           or the Series, (or the Study ?) he used to created his image 
1365           (MIP, MPR, cartography image, ...)
1366            These info should be stored (?)
1367           0008 1110 SQ 1 Referenced Study Sequence
1368           0008 1115 SQ 1 Referenced Series Sequence
1369           0008 1140 SQ 1 Referenced Image Sequence
1370        
1371 4) When user *knows* he didn't modified the pixels, we keep some informations unchanged :
1372 'Media Storage SOP Class UID' (0x0002,0x0002)
1373 'SOP Class UID'               (0x0008,0x0016)
1374 'Image Type'                  (0x0008,0x0008)
1375 'Conversion Type'             (0x0008,0x0064)
1376
1377
1378 Bellow follows the full description (hope so !) of the consistency checks performed 
1379 by gdcm::FileHelper::CheckMandatoryElements()
1380
1381
1382 -->'Media Storage SOP Class UID' (0x0002,0x0002)
1383 -->'SOP Class UID'               (0x0008,0x0016) are defaulted to 
1384                                                [Secondary Capture Image Storage]
1385 --> 'Image Type'  (0x0008,0x0008)
1386      is forced to  "DERIVED\PRIMARY"
1387      (The written image is no longer an 'ORIGINAL' one)
1388   Except if user knows he didn't modify the image (e.g. : he just anonymized the file)
1389    
1390  -->  Conversion Type (0x0008,0x0064)
1391      is defaulted to 'SYN' (Synthetic Image)
1392   when *he* knows he created his own image ex nihilo
1393             
1394 --> 'Modality' (0x0008,0x0060)   
1395     is defaulted to "OT" (other) if missing.   
1396     (a fully user created image belongs to *no* modality)
1397       
1398 --> 'Media Storage SOP Instance UID' (0x0002,0x0003)
1399 --> 'Implementation Class UID'       (0x0002,0x0012)
1400     are automatically generated; no user intervention possible
1401
1402 --> 'Serie Instance UID'(0x0020,0x000e)
1403 --> 'Study Instance UID'(0x0020,0x000d) are kept as is if already exist
1404                                              created  if it doesn't.
1405      The user is allowed to create his own Series/Studies, 
1406      keeping the same 'Serie Instance UID' / 'Study Instance UID' 
1407      for various images
1408      Warning :     
1409      The user shouldn't add any image to a 'Manufacturer Serie'
1410      but there is no way no to allowed him to do that 
1411              
1412 --> If 'SOP Class UID' exists in the native image  ('true DICOM' image)
1413     we create the 'Source Image Sequence' SeqEntry (0x0008, 0x2112)
1414     
1415     --> 'Referenced SOP Class UID' (0x0008, 0x1150)
1416          whose value is the original 'SOP Class UID'
1417     --> 'Referenced SOP Instance UID' (0x0008, 0x1155)
1418          whose value is the original 'SOP Class UID'
1419     
1420 --> Bits Stored, Bits Allocated, Hight Bit Position are checked for consistency
1421 --> Pixel Spacing     (0x0028,0x0030) is defaulted to "1.0\1.0"
1422 --> Samples Per Pixel (0x0028,0x0002) is defaulted to 1 (grayscale)
1423
1424 --> Imager Pixel Spacing (0x0018,0x1164) : defaulted to Pixel Spacing value
1425
1426 --> Instance Creation Date, Instance Creation Time are forced to current Date and Time
1427
1428 --> Study Date, Study Time are defaulted to current Date and Time
1429    (they remain unchanged if they exist)
1430
1431 --> Patient Orientation : (0x0020,0x0020), if not present, is deduced from 
1432     Image Orientation (Patient) : (0020|0037) or from
1433     Image Orientation (RET)     : (0020 0035)
1434    
1435 --> Study ID, Series Number, Instance Number, Patient Orientation (Type 2)
1436     are created, with empty value if there are missing.
1437
1438 --> Manufacturer, Institution Name, Patient's Name, (Type 2)
1439     are defaulted with a 'gdcm' value.
1440     
1441 --> Patient ID, Patient's Birth Date, Patient's Sex, (Type 2)
1442 --> Referring Physician's Name  (Type 2)
1443     are created, with empty value if there are missing.
1444
1445  -------------------------------------------------------------------------------------*/
1446
1447 void FileHelper::CheckMandatoryElements()
1448 {
1449    std::string sop =  Util::CreateUniqueUID();
1450
1451    // --------------------- For Meta Elements ---------------------
1452    // just to remember : 'official' 0002 group
1453    if ( WriteType != ACR && WriteType != ACR_LIBIDO )
1454    {
1455      // Group 000002 (Meta Elements) already pushed out
1456   
1457    //0002 0000 UL 1 Meta Group Length
1458    //0002 0001 OB 1 File Meta Information Version
1459    //0002 0002 UI 1 Media Storage SOP Class UID
1460    //0002 0003 UI 1 Media Storage SOP Instance UID
1461    //0002 0010 UI 1 Transfer Syntax UID
1462    //0002 0012 UI 1 Implementation Class UID
1463    //0002 0013 SH 1 Implementation Version Name
1464    //0002 0016 AE 1 Source Application Entity Title
1465    //0002 0100 UI 1 Private Information Creator
1466    //0002 0102 OB 1 Private Information
1467
1468    // Push out 'ACR-NEMA-special' entries, if any
1469       Archive->Push(0x0008,0x0001); // Length to End
1470       Archive->Push(0x0008,0x0010); // Recognition Code
1471       Archive->Push(0x0028,0x0005); // Image Dimension
1472
1473    // Create them if not found
1474    // Always modify the value
1475    // Push the entries to the archive.
1476       CopyMandatoryEntry(0x0002,0x0000,"0","UL");
1477
1478       DataEntry *e_0002_0001 = CopyDataEntry(0x0002,0x0001, "OB");
1479       e_0002_0001->SetBinArea((uint8_t*)Util::GetFileMetaInformationVersion(),
1480                                false);
1481       e_0002_0001->SetLength(2);
1482       Archive->Push(e_0002_0001);
1483       e_0002_0001->Delete(); 
1484
1485       if ( ContentType == FILTERED_IMAGE || ContentType == UNMODIFIED_PIXELS_IMAGE)
1486       {      
1487    // we keep the original 'Media Storage SOP Class UID', we default it if missing
1488          CheckMandatoryEntry(0x0002,0x0002,"1.2.840.10008.5.1.4.1.1.7","UI"); 
1489       }
1490       else
1491       {
1492    // It's *not* an image comming straight from a source. We force
1493    // 'Media Storage SOP Class UID'  --> [Secondary Capture Image Storage]
1494          CopyMandatoryEntry(0x0002,0x0002,"1.2.840.10008.5.1.4.1.1.7","UI");
1495       }
1496
1497    // 'Media Storage SOP Instance UID'
1498       CopyMandatoryEntry(0x0002,0x0003,sop,"UI");
1499
1500    // 'Implementation Class UID'
1501    // FIXME : in all examples we have, 0x0002,0x0012 is not so long :
1502    //         seems to be Root UID + 4 digits (?)
1503       CopyMandatoryEntry(0x0002,0x0012,Util::CreateUniqueUID(),"UI");
1504
1505    // 'Implementation Version Name'
1506       std::string version = "GDCM ";
1507       version += Util::GetVersion();
1508       CopyMandatoryEntry(0x0002,0x0013,version,"SH");
1509    }
1510
1511    // --------------------- For DataSet ---------------------
1512
1513    // check whether 0018|0015 [CS] [Body Part Examined] value is UPPER CASE
1514    //      (avoid dciodvfy to complain!)
1515    DataEntry *e_0018_0015 = FileInternal->GetDataEntry(0x0018, 0x0015);  
1516    if ( e_0018_0015)
1517    {
1518       std::string bodyPartExamined = e_0018_0015->GetString();
1519       std::transform(bodyPartExamined.begin(), bodyPartExamined.end(), bodyPartExamined.begin(), 
1520                     (int(*)(int)) toupper);
1521       CopyMandatoryEntry(0x0018,0x0015,bodyPartExamined,"CS");       
1522    }
1523
1524    if ( ContentType != USER_OWN_IMAGE) // when it's not a user made image
1525    { 
1526    // If 'SOP Class UID' and 'SOP Instance UID' exist ('true DICOM' image)
1527    // we create the 'Source Image Sequence' SeqEntry
1528    // to hold informations about the Source Image
1529  
1530       // 'SOP Instance UID' 
1531       DataEntry *e_0008_0016 = FileInternal->GetDataEntry(0x0008, 0x0016);
1532       //
1533       DataEntry *e_0008_0018 = FileInternal->GetDataEntry(0x0008, 0x0018);
1534       if ( e_0008_0016 && e_0008_0018)
1535       {
1536          // Create 'Source Image Sequence' SeqEntry
1537          SeqEntry *sis = SeqEntry::New (0x0008, 0x2112);
1538          SQItem *sqi = SQItem::New(1);
1539       
1540          // create 'Referenced SOP Class UID' from 'SOP Class UID'
1541
1542          DataEntry *e_0008_1150 = DataEntry::New(0x0008, 0x1150, "UI");
1543          e_0008_1150->SetString( e_0008_0016->GetString());
1544          sqi->AddEntry(e_0008_1150);
1545          e_0008_1150->Delete();
1546       
1547          // create 'Referenced SOP Instance UID' from 'SOP Instance UID'
1548         // DataEntry *e_0008_0018 = FileInternal->GetDataEntry(0x0008, 0x0018);
1549          
1550          DataEntry *e_0008_1155 = DataEntry::New(0x0008, 0x1155, "UI"); 
1551          e_0008_1155->SetString( e_0008_0018->GetString());
1552          sqi->AddEntry(e_0008_1155);
1553          e_0008_1155->Delete();
1554       
1555          sis->AddSQItem(sqi,1);
1556          sqi->Delete();
1557
1558          // temporarily replaces any previous 'Source Image Sequence' 
1559          Archive->Push(sis);
1560          sis->Delete();
1561          // FIXME : is 'Image Type' *really* depending on the presence of 'SOP Class UID'?
1562          
1563          if ( ContentType == FILTERED_IMAGE) // the user *knows* he just modified the pixels
1564          { 
1565             DataEntry *e_0008_0008 = FileInternal->GetDataEntry(0x0008, 0x0008);  
1566             if ( e_0008_0008)
1567             {
1568                std::string imageType = e_0008_0008->GetString();
1569                std::string::size_type p = imageType.find("ORIGINAL");
1570                if (p == 0) // image is ORIGINAL one
1571                {            
1572                  // the image is no longer an 'Original' one
1573                  CopyMandatoryEntry(0x0008,0x0008,"DERIVED\\PRIMARY","CS");
1574                }
1575                // if Image Type was not ORIGINAL\..., we keep it.
1576              }
1577              else // 0008_0008 was missing, wee add it.
1578              {
1579                  CopyMandatoryEntry(0x0008,0x0008,"DERIVED\\PRIMARY","CS");             
1580              }  
1581          }    
1582       }
1583    }
1584       
1585    if ( ContentType == FILTERED_IMAGE || ContentType == UNMODIFIED_PIXELS_IMAGE)
1586    {      
1587    // we keep the original 'Media Storage SOP Class UID', we default it if missing (it should be present !)
1588          CheckMandatoryEntry(0x0008,0x0016,"1.2.840.10008.5.1.4.1.1.7","UI");      
1589    }
1590    else
1591    {
1592    // It's *not* an image comming straight from a source. We force
1593    // 'Media Storage SOP Class UID'  --> [Secondary Capture Image Storage]
1594          CopyMandatoryEntry(0x0008,0x0016,"1.2.840.10008.5.1.4.1.1.7", "UI");      
1595    }
1596      
1597    Archive->Push(0x0028,0x005); // [Image Dimensions (RET)
1598    // Push out 'LibIDO-special' entries, if any
1599    Archive->Push(0x0028,0x0015);
1600    Archive->Push(0x0028,0x0016);
1601    Archive->Push(0x0028,0x0017);
1602    Archive->Push(0x0028,0x0198);  // very old versions
1603    Archive->Push(0x0028,0x0199);
1604
1605    // Replace deprecated 0028 0012 US Planes   
1606    // by new             0028 0008 IS Number of Frames
1607
1608   ///\todo : find if there is a rule!
1609    DataEntry *e_0028_0012 = FileInternal->GetDataEntry(0x0028, 0x0012);
1610    if ( e_0028_0012 )
1611    {
1612       CopyMandatoryEntry(0x0028, 0x0008,e_0028_0012->GetString(),"IS");
1613       Archive->Push(0x0028,0x0012);      
1614    }
1615
1616    // Deal with the pb of (Bits Stored = 12)
1617    // - we're gonna write the image as Bits Stored = 16
1618    if ( FileInternal->GetEntryString(0x0028,0x0100) ==  "12")
1619    {
1620       CopyMandatoryEntry(0x0028,0x0100,"16","US");
1621    }
1622
1623    // Check if user wasn't drunk ;-)
1624
1625    std::ostringstream s;
1626    // check 'Bits Allocated' vs decent values
1627    int nbBitsAllocated = FileInternal->GetBitsAllocated();
1628    if ( (nbBitsAllocated == 0 || nbBitsAllocated > 32)
1629      || ( nbBitsAllocated > 8 && nbBitsAllocated <16) )
1630    {
1631       CopyMandatoryEntry(0x0028,0x0100,"16","US");
1632       gdcmWarningMacro("(0028,0100) changed from "
1633          << nbBitsAllocated << " to 16 for consistency purpose");
1634       nbBitsAllocated = 16; 
1635    }
1636    // check 'Bits Stored' vs 'Bits Allocated'   
1637    int nbBitsStored = FileInternal->GetBitsStored();
1638    if ( nbBitsStored == 0 || nbBitsStored > nbBitsAllocated )
1639    {
1640       s.str("");
1641       s << nbBitsAllocated;
1642       CopyMandatoryEntry(0x0028,0x0101,s.str(),"US");
1643       gdcmWarningMacro("(0028,0101) changed from "
1644                        << nbBitsStored << " to " << nbBitsAllocated
1645                        << " for consistency purpose" );
1646       nbBitsStored = nbBitsAllocated; 
1647     }
1648    // check 'Hight Bit Position' vs 'Bits Allocated' and 'Bits Stored'
1649    int highBitPosition = FileInternal->GetHighBitPosition();
1650    if ( highBitPosition == 0 || 
1651         highBitPosition > nbBitsAllocated-1 ||
1652         highBitPosition < nbBitsStored-1  )
1653    {
1654       s.str("");
1655       s << nbBitsStored - 1; 
1656       CopyMandatoryEntry(0x0028,0x0102,s.str(),"US");
1657       gdcmWarningMacro("(0028,0102) changed from "
1658                        << highBitPosition << " to " << nbBitsAllocated-1
1659                        << " for consistency purpose");
1660    }
1661
1662    // check Pixel Representation (default it as 0 -unsigned-)
1663
1664    DataEntry *e_0028_0103 = FileInternal->GetDataEntry(0x0028, 0x0103);
1665    if ( !e_0028_0103 )
1666    {
1667       gdcmWarningMacro("PixelRepresentation (0028,0103) is supposed to be mandatory");
1668       CopyMandatoryEntry(0x0028, 0x0103,"0","US"); 
1669    }
1670    else
1671    {
1672       int sign = (int)e_0028_0103->GetValue(0);
1673       if (sign !=1 && sign !=0)
1674       {
1675          gdcmWarningMacro("PixelRepresentation (0028,0103) is supposed to be =1 or =0");
1676          CopyMandatoryEntry(0x0028, 0x0103,"0","US");
1677       }
1678    }
1679
1680    std::string pixelAspectRatio = FileInternal->GetEntryString(0x0028,0x0034);
1681    if ( pixelAspectRatio == GDCM_UNFOUND ) // avoid conflict with pixelSpacing !
1682    {
1683       std::string pixelSpacing = FileInternal->GetEntryString(0x0028,0x0030);
1684       if ( pixelSpacing == GDCM_UNFOUND )
1685       {
1686          pixelSpacing = "1.0\\1.0";
1687           // if missing, Pixel Spacing forced to "1.0\1.0"
1688          CopyMandatoryEntry(0x0028,0x0030,pixelSpacing,"DS");
1689       }
1690   
1691       // 'Imager Pixel Spacing' : defaulted to 'Pixel Spacing'
1692       // --> This one is the *legal* one !
1693       if ( ContentType != USER_OWN_IMAGE)
1694       //  we write it only when we are *sure* the image comes from
1695       //         an imager (see also 0008,0x0064)
1696          CheckMandatoryEntry(0x0018,0x1164,pixelSpacing,"DS");
1697    } 
1698 /*
1699 ///Exact meaning of RETired fields
1700
1701 // See page 73 of ACR-NEMA_300-1988.pdf !
1702
1703 // 0020,0020 : Patient Orientation :
1704 Patient direction of the first row and
1705 column of the images. The first entry id the direction of the raws, given by the
1706 direction of the last pixel in the first row from the first pixel in tha row.
1707 the second entry is the direction of the columns, given by the direction of the
1708 last pixel in the first column from the first pixel in that column.
1709 L : Left, F : Feet, A : Anterior, P : Posterior.
1710 Up to 3 letters can be used in combination to indicate oblique planes.
1711
1712 //0020,0030 Image Position (RET)
1713 x,y,z coordinates im mm of the first pixel in the image
1714
1715 // 0020,0035 Image Orientation (RET)
1716 Direction cosines of the R axis of the image system with respect to the
1717 equipment coordinate axes x,y,z, followed by direction cosines of the C axis of
1718 the image system with respect to the same axes
1719
1720 //0020,0050 Location
1721 An image location reference, standard for the modality (such as CT bed position),
1722 used to indicate position. Calculation of position for other purposes
1723 is only from (0020,0030) and (0020,0035)
1724 */
1725
1726 /*
1727 // if imagePositionPatient    not found, default it with imagePositionRet,    if any
1728 // if imageOrientationPatient not found, default it with imageOrientationRet, if any
1729
1730    std::string imagePositionRet        = FileInternal->GetEntryString(0x0020,0x0030);
1731    std::string imageOrientationRet     = FileInternal->GetEntryString(0x0020,0x0035);
1732    std::string imagePositionPatient    = FileInternal->GetEntryString(0x0020,0x0032);
1733    std::string imageOrientationPatient = FileInternal->GetEntryString(0x0020,0x0037);
1734
1735    if(  imagePositionPatient == GDCM_UNFOUND && imageOrientationPatient == GDCM_UNFOUND
1736      && imagePositionRet     != GDCM_UNFOUND && imageOrientationRet     != GDCM_UNFOUND)
1737    {
1738       CopyMandatoryEntry(0x0020, 0x0032,imagePositionRet,"DS");
1739       Archive->Push(0x0020,0x0030); 
1740       CopyMandatoryEntry(0x0020, 0x0037,imageOrientationRet,"DS");
1741       Archive->Push(0x0020,0x0035);
1742    }
1743 */
1744
1745    // Samples Per Pixel (type 1) : default to grayscale
1746    CheckMandatoryEntry(0x0028,0x0002,"1","US");
1747
1748    // --- Check UID-related Entries ---
1749  
1750    // At the end, not to overwrite the original ones,
1751    // needed by 'Referenced SOP Instance UID', 'Referenced SOP Class UID'
1752    // 'SOP Instance UID'  
1753    CopyMandatoryEntry(0x0008,0x0018,sop,"UI");
1754
1755    if ( ContentType == USER_OWN_IMAGE)
1756    {
1757       gdcmDebugMacro( "USER_OWN_IMAGE (2)");
1758        // Conversion Type.
1759        // Other possible values are :
1760        // See PS 3.3, Page 408
1761
1762        // DV = Digitized Video
1763        // DI = Digital Interface 
1764        // DF = Digitized Film
1765        // WSD = Workstation
1766        // SD = Scanned Document
1767        // SI = Scanned Image
1768        // DRW = Drawing
1769        // SYN = Synthetic Image
1770
1771       CheckMandatoryEntry(0x0008,0x0064,"SYN","CS"); // Why not?
1772    } 
1773 /*
1774    if ( ContentType == CREATED_IMAGE)
1775    {
1776    /// \todo : find a trick to pass the Media Storage SOP Instance UID of the images used to create the current image
1777    
1778    }
1779 */
1780
1781    // ---- The user will never have to take any action on the following ----
1782
1783    // new value for 'SOP Instance UID'
1784    //SetMandatoryEntry(0x0008,0x0018,Util::CreateUniqueUID());
1785
1786    // Instance Creation Date
1787    const std::string &date = Util::GetCurrentDate();
1788    CopyMandatoryEntry(0x0008,0x0012,date,"DA");
1789
1790    // Instance Creation Time
1791    const std::string &time = Util::GetCurrentTime();
1792    CopyMandatoryEntry(0x0008,0x0013,time,"TM");
1793
1794    // Study Date
1795    CheckMandatoryEntry(0x0008,0x0020,date,"DA");
1796    // Study Time
1797    CheckMandatoryEntry(0x0008,0x0030,time,"TM");
1798
1799    // Accession Number
1800    //CopyMandatoryEntry(0x0008,0x0050,"");
1801    CheckMandatoryEntry(0x0008,0x0050,"","SH");
1802    
1803
1804    // ----- Add Mandatory Entries if missing ---
1805    // Entries whose type is 1 are mandatory, with a mandatory value
1806    // Entries whose type is 1c are mandatory-inside-a-Sequence,
1807    //                          with a mandatory value
1808    // Entries whose type is 2 are mandatory, with an optional value
1809    // Entries whose type is 2c are mandatory-inside-a-Sequence,
1810    //                          with an optional value
1811    // Entries whose type is 3 are optional
1812
1813    // 'Study Instance UID'
1814    // Keep the value if exists
1815    // The user is allowed to create his own Study, 
1816    //          keeping the same 'Study Instance UID' for various images
1817    // The user may add images to a 'Manufacturer Study',
1818    //          adding new Series to an already existing Study 
1819    CheckMandatoryEntry(0x0020,0x000d,Util::CreateUniqueUID(),"UI");
1820
1821    // 'Serie Instance UID'
1822    // Keep the value if exists
1823    // The user is allowed to create his own Series, 
1824    // keeping the same 'Serie Instance UID' for various images
1825    // The user shouldn't add any image to a 'Manufacturer Serie'
1826    // but there is no way no to prevent him for doing that 
1827    CheckMandatoryEntry(0x0020,0x000e,Util::CreateUniqueUID(),"UI");
1828
1829    // Study ID
1830    CheckMandatoryEntry(0x0020,0x0010,"","SH");
1831
1832    // Series Number
1833    CheckMandatoryEntry(0x0020,0x0011,"","IS");
1834
1835    // Instance Number
1836    CheckMandatoryEntry(0x0020,0x0013,"","IS");
1837
1838    // Patient Orientation
1839    // Can be computed from (0020|0037) :  Image Orientation (Patient)
1840    GDCM_NAME_SPACE::Orientation *o = GDCM_NAME_SPACE::Orientation::New();
1841    std::string ori = o->GetOrientation ( FileInternal );
1842    o->Delete();
1843    if (ori != "\\" && ori != GDCM_UNFOUND)
1844       CheckMandatoryEntry(0x0020,0x0020,ori,"CS");
1845    else
1846       CheckMandatoryEntry(0x0020,0x0020,"","CS");
1847
1848    // Default Patient Position to HFS
1849    CheckMandatoryEntry(0x0018,0x5100,"HFS","CS");
1850
1851    // Modality : if missing we set it to 'OTher'
1852    CheckMandatoryEntry(0x0008,0x0060,"OT","CS");
1853
1854    // Manufacturer : if missing we set it to 'GDCM Factory'
1855    CheckMandatoryEntry(0x0008,0x0070,"GDCM Factory","LO");
1856
1857    // Institution Name : if missing we set it to 'GDCM Hospital'
1858    CheckMandatoryEntry(0x0008,0x0080,"GDCM Hospital","LO");
1859
1860    // Patient's Name : if missing, we set it to 'GDCM^Patient'
1861    CheckMandatoryEntry(0x0010,0x0010,"GDCM^Patient","PN");
1862
1863    // Patient ID : some clinical softwares *demand* it although it's a 'type 2' entry.
1864    CheckMandatoryEntry(0x0010,0x0020,"gdcm ID","LO");
1865
1866    // Patient's Birth Date : 'type 2' entry -> must exist, value not mandatory
1867    CheckMandatoryEntry(0x0010,0x0030,"","DA");
1868
1869    // Patient's Sex :'type 2' entry -> must exist, value not mandatory
1870    CheckMandatoryEntry(0x0010,0x0040,"","CS");
1871
1872    // Referring Physician's Name :'type 2' entry -> must exist, value not mandatory
1873    CheckMandatoryEntry(0x0008,0x0090,"","PN");
1874
1875  /*
1876    // Deal with element 0x0000 (group length) of each group.
1877    // First stage : get all the different Groups
1878
1879   GroupHT grHT;
1880   DocEntry *d = FileInternal->GetFirstEntry();
1881   while(d)
1882   {
1883     grHT[d->GetGroup()] = 0;
1884     d=FileInternal->GetNextEntry();
1885   }
1886   // Second stage : add the missing ones (if any)
1887   for (GroupHT::iterator it = grHT.begin(); it != grHT.end(); ++it)  
1888   {
1889       CheckMandatoryEntry(it->first, 0x0000, "0"); 
1890   }    
1891   // Third stage : update all 'zero level' groups length
1892 */
1893
1894
1895    if (PhotometricInterpretation == 1)
1896    {
1897    }
1898
1899
1900 void FileHelper::CheckMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr )
1901 {
1902    DataEntry *entry = FileInternal->GetDataEntry(group,elem);
1903    if ( !entry )
1904    {
1905       //entry = DataEntry::New(Global::GetDicts()->GetDefaultPubDict()->GetEntry(group,elem));
1906       entry = DataEntry::New(group,elem,vr);
1907       entry->SetString(value);
1908       Archive->Push(entry);
1909       entry->Delete();
1910    }    
1911 }
1912
1913 /// \todo : what is it used for ? (FileHelper::SetMandatoryEntry)
1914 void FileHelper::SetMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr)
1915 {
1916    //DataEntry *entry = DataEntry::New(Global::GetDicts()->GetDefaultPubDict()->GetEntry(group,elem));
1917    DataEntry *entry = DataEntry::New(group,elem,vr);
1918    entry->SetString(value);
1919    Archive->Push(entry);
1920    entry->Delete();
1921 }
1922
1923 void FileHelper::CopyMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr)
1924 {
1925    DataEntry *entry = CopyDataEntry(group,elem,vr);
1926    entry->SetString(value);
1927    Archive->Push(entry);
1928    entry->Delete();
1929 }
1930
1931 /**
1932  * \brief Restore in the File the initial group 0002
1933  */
1934 void FileHelper::RestoreWriteMandatory()
1935 {
1936    // group 0002 may be pushed out for ACR-NEMA writting purposes 
1937    Archive->Restore(0x0002,0x0000);
1938    Archive->Restore(0x0002,0x0001);
1939    Archive->Restore(0x0002,0x0002);
1940    Archive->Restore(0x0002,0x0003);
1941    Archive->Restore(0x0002,0x0010);
1942    Archive->Restore(0x0002,0x0012);
1943    Archive->Restore(0x0002,0x0013);
1944    Archive->Restore(0x0002,0x0016);
1945    Archive->Restore(0x0002,0x0100);
1946    Archive->Restore(0x0002,0x0102);
1947
1948    // FIXME : Check if none is missing !
1949    
1950    Archive->Restore(0x0008,0x0012);
1951    Archive->Restore(0x0008,0x0013);
1952    Archive->Restore(0x0008,0x0016);
1953    Archive->Restore(0x0008,0x0018);
1954    Archive->Restore(0x0008,0x0060);
1955    Archive->Restore(0x0008,0x0070);
1956    Archive->Restore(0x0008,0x0080);
1957    Archive->Restore(0x0008,0x0090);
1958    Archive->Restore(0x0008,0x2112);
1959
1960    Archive->Restore(0x0010,0x0010);
1961    Archive->Restore(0x0010,0x0030);
1962    Archive->Restore(0x0010,0x0040);
1963
1964    Archive->Restore(0x0020,0x000d);
1965    Archive->Restore(0x0020,0x000e);
1966 }
1967
1968 /**
1969  * \brief   CallStartMethod
1970  */
1971 void FileHelper::CallStartMethod()
1972 {
1973    Progress = 0.0f;
1974    Abort    = false;
1975    CommandManager::ExecuteCommand(this,CMD_STARTPROGRESS);
1976 }
1977
1978 /**
1979  * \brief   CallProgressMethod
1980  */
1981 void FileHelper::CallProgressMethod()
1982 {
1983    CommandManager::ExecuteCommand(this,CMD_PROGRESS);
1984 }
1985
1986 /**
1987  * \brief   CallEndMethod
1988  */
1989 void FileHelper::CallEndMethod()
1990 {
1991    Progress = 1.0f;
1992    CommandManager::ExecuteCommand(this,CMD_ENDPROGRESS);
1993 }
1994
1995 //-----------------------------------------------------------------------------
1996 // Private
1997 /**
1998  * \brief Factorization for various forms of constructors.
1999  */
2000 void FileHelper::Initialize()
2001 {
2002    UserFunction = 0;
2003    ContentType = USER_OWN_IMAGE;
2004
2005    WriteMode = WMODE_RAW;
2006    WriteType = ExplicitVR;
2007    
2008    PhotometricInterpretation = 2; // Black = 0
2009
2010    PixelReadConverter  = new PixelReadConvert;
2011    PixelWriteConverter = new PixelWriteConvert;
2012    Archive = new DocEntryArchive( FileInternal );
2013    
2014    KeepOverlays = false;
2015 }
2016
2017 /**
2018  * \brief Reads/[decompresses] the pixels, 
2019  *        *without* making RGB from Palette Colors 
2020  * @return the pixels area, whatever its type 
2021  *         (uint8_t is just for prototyping : feel free to Cast it) 
2022  */ 
2023 uint8_t *FileHelper::GetRaw()
2024 {
2025    PixelReadConverter->SetUserFunction( UserFunction );
2026
2027    uint8_t *raw = PixelReadConverter->GetRaw();
2028    if ( ! raw )
2029    {
2030       // The Raw image migth not be loaded yet:
2031       std::ifstream *fp = FileInternal->OpenFile();
2032       PixelReadConverter->ReadAndDecompressPixelData( fp );
2033       if ( fp ) 
2034          FileInternal->CloseFile();
2035
2036       raw = PixelReadConverter->GetRaw();
2037       if ( ! raw )
2038       {
2039          gdcmWarningMacro( "Read/decompress of pixel data apparently went wrong.");
2040          return 0;
2041       }
2042    }
2043    return raw;
2044 }
2045
2046 /**
2047  * \brief Deal with Grey levels i.e. re-arange them
2048  *        to have low values = dark, high values = bright
2049  */
2050 void FileHelper::ConvertFixGreyLevels(uint8_t *raw, size_t rawSize)
2051 {
2052    uint32_t i; // to please M$VC6
2053    int16_t j;
2054
2055    // Number of Bits Allocated for storing a Pixel is defaulted to 16
2056    // when absent from the file.
2057    int bitsAllocated = FileInternal->GetBitsAllocated();
2058    if ( bitsAllocated == 0 )
2059    {
2060       bitsAllocated = 16;
2061    }
2062
2063    else if (bitsAllocated > 8 && bitsAllocated < 16 && bitsAllocated != 12)
2064    {
2065       bitsAllocated = 16;
2066    }   
2067    // Number of "Bits Stored", defaulted to number of "Bits Allocated"
2068    // when absent from the file.
2069    int bitsStored = FileInternal->GetBitsStored();
2070    if ( bitsStored == 0 )
2071    {
2072       bitsStored = bitsAllocated;
2073    }
2074
2075    if (!FileInternal->IsSignedPixelData())
2076    {
2077       if ( bitsAllocated == 8 )
2078       {
2079          uint8_t *deb = (uint8_t *)raw;
2080          for (i=0; i<rawSize; i++)      
2081          {
2082             *deb = 255 - *deb;
2083             deb++;
2084          }
2085          return;
2086       }
2087
2088       if ( bitsAllocated == 16 )
2089       {
2090          uint16_t mask =1;
2091          for (j=0; j<bitsStored-1; j++)
2092          {
2093             mask = (mask << 1) +1; // will be fff when BitsStored=12
2094          }
2095
2096          uint16_t *deb = (uint16_t *)raw;
2097          for (i=0; i<rawSize/2; i++)      
2098          {
2099             *deb = mask - *deb;
2100             deb++;
2101          }
2102          return;
2103        }
2104    }
2105    else
2106    {
2107       if ( bitsAllocated == 8 )
2108       {
2109          uint8_t smask8 = 255;
2110          uint8_t *deb = (uint8_t *)raw;
2111          for (i=0; i<rawSize; i++)      
2112          {
2113             *deb = smask8 - *deb;
2114             deb++;
2115          }
2116          return;
2117       }
2118       if ( bitsAllocated == 16 )
2119       {
2120          uint16_t smask16 = 65535;
2121          uint16_t *deb = (uint16_t *)raw;
2122          for (i=0; i<rawSize/2; i++)      
2123          {
2124             *deb = smask16 - *deb;
2125             deb++;
2126          }
2127          return;
2128       }
2129    }
2130 }
2131
2132 //-----------------------------------------------------------------------------
2133 /**
2134  * \brief   Prints the FileInternal + info on PixelReadConvertor
2135  * @param   os ostream we want to print in
2136  * @param indent (unused)
2137  */
2138 void FileHelper::Print(std::ostream &os, std::string const &)
2139 {
2140    FileInternal->SetPrintLevel(PrintLevel);
2141    FileInternal->Print(os);
2142
2143    if ( FileInternal->IsReadable() )
2144    {
2145       PixelReadConverter->SetPrintLevel(PrintLevel);
2146       PixelReadConverter->Print(os);
2147    }
2148 }
2149
2150 //-----------------------------------------------------------------------------
2151 } // end namespace gdcm
2152
2153
2154 /* Probabely something to be added to use Rescale Slope/Intercept
2155 Have a look at ITK code !
2156
2157 // Internal function to rescale pixel according to Rescale Slope/Intercept
2158 template<class TBuffer, class TSource>
2159 void RescaleFunction(TBuffer* buffer, TSource *source,
2160                      double slope, double intercept, size_t size)
2161 {
2162   size /= sizeof(TSource);
2163
2164   if (slope != 1.0 && intercept != 0.0)
2165     {
2166     // Duff's device.  Instead of this code:
2167     //
2168     //   for(unsigned int i=0; i<size; i++)
2169     //    {
2170     //    buffer[i] = (TBuffer)(source[i]*slope + intercept);
2171     //    }
2172     //
2173     // use Duff's device which exploits "fall through"
2174     register size_t n = (size + 7) / 8;
2175     switch ( size % 8)
2176       {
2177       case 0: do { *buffer++ = (TBuffer)((*source++)*slope + intercept);
2178       case 7:      *buffer++ = (TBuffer)((*source++)*slope + intercept);
2179       case 6:      *buffer++ = (TBuffer)((*source++)*slope + intercept);
2180       case 5:      *buffer++ = (TBuffer)((*source++)*slope + intercept);
2181       case 4:      *buffer++ = (TBuffer)((*source++)*slope + intercept);
2182       case 3:      *buffer++ = (TBuffer)((*source++)*slope + intercept);
2183       case 2:      *buffer++ = (TBuffer)((*source++)*slope + intercept);
2184       case 1:      *buffer++ = (TBuffer)((*source++)*slope + intercept);
2185                  }  while (--n > 0);
2186       }
2187     }
2188   else if (slope == 1.0 && intercept != 0.0)
2189     {
2190     // Duff's device.  Instead of this code:
2191     //
2192     //   for(unsigned int i=0; i<size; i++)
2193     //    {
2194     //    buffer[i] = (TBuffer)(source[i] + intercept);
2195     //    }
2196     //
2197     // use Duff's device which exploits "fall through"
2198     register size_t n = (size + 7) / 8;
2199     switch ( size % 8)
2200       {
2201       case 0: do { *buffer++ = (TBuffer)(*source++ + intercept);
2202       case 7:      *buffer++ = (TBuffer)(*source++ + intercept);
2203       case 6:      *buffer++ = (TBuffer)(*source++ + intercept);
2204       case 5:      *buffer++ = (TBuffer)(*source++ + intercept);
2205       case 4:      *buffer++ = (TBuffer)(*source++ + intercept);
2206       case 3:      *buffer++ = (TBuffer)(*source++ + intercept);
2207       case 2:      *buffer++ = (TBuffer)(*source++ + intercept);
2208       case 1:      *buffer++ = (TBuffer)(*source++ + intercept);
2209                  }  while (--n > 0);
2210       }
2211     }
2212   else if (slope != 1.0 && intercept == 0.0)
2213     {
2214     // Duff's device.  Instead of this code:
2215     //
2216     //   for(unsigned int i=0; i<size; i++)
2217     //    {
2218     //    buffer[i] = (TBuffer)(source[i]*slope);
2219     //    }
2220     //
2221     // use Duff's device which exploits "fall through"
2222     register size_t n = (size + 7) / 8;
2223     switch ( size % 8)
2224       {
2225       case 0: do { *buffer++ = (TBuffer)((*source++)*slope);
2226       case 7:      *buffer++ = (TBuffer)((*source++)*slope);
2227       case 6:      *buffer++ = (TBuffer)((*source++)*slope);
2228       case 5:      *buffer++ = (TBuffer)((*source++)*slope);
2229       case 4:      *buffer++ = (TBuffer)((*source++)*slope);
2230       case 3:      *buffer++ = (TBuffer)((*source++)*slope);
2231       case 2:      *buffer++ = (TBuffer)((*source++)*slope);
2232       case 1:      *buffer++ = (TBuffer)((*source++)*slope);
2233                  }  while (--n > 0);
2234       }
2235     }
2236   else
2237     {
2238     // Duff's device.  Instead of this code:
2239     //
2240     //   for(unsigned int i=0; i<size; i++)
2241     //    {
2242     //    buffer[i] = (TBuffer)(source[i]);
2243     //    }
2244     //
2245     // use Duff's device which exploits "fall through"
2246     register size_t n = (size + 7) / 8;
2247     switch ( size % 8)
2248       {
2249       case 0: do { *buffer++ = (TBuffer)(*source++);
2250       case 7:      *buffer++ = (TBuffer)(*source++);
2251       case 6:      *buffer++ = (TBuffer)(*source++);
2252       case 5:      *buffer++ = (TBuffer)(*source++);
2253       case 4:      *buffer++ = (TBuffer)(*source++);
2254       case 3:      *buffer++ = (TBuffer)(*source++);
2255       case 2:      *buffer++ = (TBuffer)(*source++);
2256       case 1:      *buffer++ = (TBuffer)(*source++);
2257                  }  while (--n > 0);
2258       }
2259    }   
2260 }
2261
2262
2263 template<class TSource>
2264 void RescaleFunction(ImageIOBase::IOComponentType bufferType,
2265                      void* buffer, TSource *source,
2266                      double slope, double intercept, size_t size)
2267 {
2268   switch (bufferType)
2269     {
2270     case ImageIOBase::UCHAR:
2271       RescaleFunction( (unsigned char *)buffer, source, slope, intercept, size);
2272       break;
2273     case ImageIOBase::CHAR:
2274       RescaleFunction( (char *)buffer, source, slope, intercept, size);
2275       break;
2276     case ImageIOBase::USHORT:
2277       RescaleFunction( (unsigned short *)buffer, source, slope, intercept,size);
2278       break;
2279     case ImageIOBase::SHORT:
2280       RescaleFunction( (short *)buffer, source, slope, intercept, size);
2281       break;
2282     case ImageIOBase::UINT:
2283       RescaleFunction( (unsigned int *)buffer, source, slope, intercept, size);
2284       break;
2285     case ImageIOBase::INT:
2286       RescaleFunction( (int *)buffer, source, slope, intercept, size);
2287       break;
2288     case ImageIOBase::FLOAT:
2289       RescaleFunction( (float *)buffer, source, slope, intercept, size);
2290       break;
2291     case ImageIOBase::DOUBLE:
2292       RescaleFunction( (double *)buffer, source, slope, intercept, size);
2293       break;
2294     default:
2295       ::itk::OStringStream message;
2296       message << "itk::ERROR: GDCMImageIO: Unknown component type : " << bufferType;
2297       ::itk::ExceptionObject e(__FILE__, __LINE__, message.str().c_str(),ITK_LOCATION);
2298       throw e;
2299     }
2300 }
2301 */