]> Creatis software - gdcm.git/blob - src/gdcmFileHelper.cxx
f27d8d99a9ec899a3ddfa0a0aeb93a770a37f1a6
[gdcm.git] / src / gdcmFileHelper.cxx
1 /*=========================================================================
2                                                                                 
3   Program:   gdcm
4   Module:    $RCSfile: gdcmFileHelper.cxx,v $
5   Language:  C++
6
7   Date:      $Date: 2008/09/15 15:49:22 $
8   Version:   $Revision: 1.138 $
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_NAME_SPACE::File
55 GDCM_NAME_SPACE::File *f = new GDCM_NAME_SPACE::File(fileName);
56 // or (advanced) :
57 // user may also decide he doesn't want to load some parts of the header
58 GDCM_NAME_SPACE::File *f = new GDCM_NAME_SPACE::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_NAME_SPACE::FileHelper
71 GDCM_NAME_SPACE::FileHelper *fh = new GDCM_NAME_SPACE::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_NAME_SPACE::FileHelper
81 GDCM_NAME_SPACE::File *fh = new GDCM_NAME_SPACE::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_NAME_SPACE::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_NAME_SPACE::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_NAME_SPACE::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_NAME_SPACE::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_NAME_SPACE::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 << "(" 
879                            << FileInternal->GetXSize() << " * "
880                            << FileInternal->GetYSize() << " * "
881                            << FileInternal->GetZSize() << " * "
882                            << FileInternal->GetTSize() << " * "   
883                            << FileInternal->GetSamplesPerPixel() << " * "
884                            << numberBitsAllocated / 8   
885                            << ") / Found :" 
886                            << userDataSize );
887                return false;
888             }
889             break;
890          case WMODE_RGB :
891             if ( abs((long)(rgbSize-userDataSize))>1) // ignore padding zero
892             {
893                gdcmWarningMacro( "Data size (RGB) is incorrect. Should be " 
894                           << rgbSize << " / Found " 
895                           << userDataSize );
896                return false;
897             }
898             break;
899       }
900    }
901    return true;
902 }
903
904 /**
905  * \brief Updates the File to write RAW data (as opposed to RGB data)
906  *       (modifies, when necessary, photochromatic interpretation, 
907  *       bits allocated, Pixels element VR)
908  *       WARNING : if SetPhotometricInterpretationToMonochrome1() was called
909  *                 before Pixel Elements is modified :-( 
910  */ 
911 void FileHelper::SetWriteToRaw()
912 {
913    if ( FileInternal->GetNumberOfScalarComponents() == 3 
914     && !FileInternal->HasLUT() )
915    {
916       SetWriteToRGB();
917    } 
918    else
919    {
920       // 0x0028,0x0004 : Photometric Interpretation
921       DataEntry *photInt = CopyDataEntry(0x0028,0x0004,"CS");
922       if (FileInternal->HasLUT() )
923       {
924          photInt->SetString("PALETTE COLOR ");
925       }
926       else
927       {
928          if (GetPhotometricInterpretation() == 2)
929             photInt->SetString("MONOCHROME2 ");  // 0 = Black
930          else
931             photInt->SetString("MONOCHROME1 ");  // 0 = White !
932       }
933
934       PixelWriteConverter->SetReadData(PixelReadConverter->GetRaw(),
935                                        PixelReadConverter->GetRawSize());
936
937       std::string vr = "OB";
938       if ( FileInternal->GetBitsAllocated()>8 )
939          vr = "OW";
940       if ( FileInternal->GetBitsAllocated()==24 ) // For RGB ACR files 
941          vr = "OB";
942        // For non RAW data. Mainly JPEG/JPEG2000
943       if( WriteType == JPEG || WriteType == JPEG2000)
944       {
945          vr = "OW";
946       }
947
948       DataEntry *pixel = 
949          CopyDataEntry(GetFile()->GetGrPixel(),GetFile()->GetNumPixel(),vr);
950       pixel->SetFlag(DataEntry::FLAG_PIXELDATA);
951       pixel->SetBinArea(PixelWriteConverter->GetData(),false);
952       pixel->SetLength(
953          static_cast< uint32_t >(PixelWriteConverter->GetDataSize()) );
954
955       if (!FileInternal->HasLUT() && GetPhotometricInterpretation() == 1)
956       {
957           ConvertFixGreyLevels( pixel->GetBinArea(), pixel->GetLength() );
958       }
959
960       Archive->Push(photInt);
961       Archive->Push(pixel);
962
963       photInt->Delete();
964       pixel->Delete();
965    }
966 }
967
968 /**
969  * \brief Updates the File to write RGB data (as opposed to RAW data)
970  *       (modifies, when necessary, photochromatic interpretation, 
971  *       samples per pixel, Planar configuration, 
972  *       bits allocated, bits stored, high bit -ACR 24 bits-
973  *       Pixels element VR, pushes out the LUT, )
974  */ 
975 void FileHelper::SetWriteToRGB()
976 {
977    if ( FileInternal->GetNumberOfScalarComponents()==3 )
978    {
979       PixelReadConverter->BuildRGBImage();
980       
981       DataEntry *spp = CopyDataEntry(0x0028,0x0002,"US");
982       spp->SetString("3 ");  // Don't drop trailing space
983
984       DataEntry *planConfig = CopyDataEntry(0x0028,0x0006,"US");
985       planConfig->SetString("0 "); // Don't drop trailing space
986
987       DataEntry *photInt = CopyDataEntry(0x0028,0x0004,"CS");
988       photInt->SetString("RGB "); // Don't drop trailing space
989
990       if ( PixelReadConverter->GetRGB() )
991       {
992          PixelWriteConverter->SetReadData(PixelReadConverter->GetRGB(),
993                                           PixelReadConverter->GetRGBSize());
994       }
995       else // Raw data
996       {
997          PixelWriteConverter->SetReadData(PixelReadConverter->GetRaw(),
998                                           PixelReadConverter->GetRawSize());
999       }
1000
1001       std::string vr = "OB";
1002       if ( FileInternal->GetBitsAllocated()>8 )
1003          vr = "OW";
1004       if ( FileInternal->GetBitsAllocated()==24 ) // For RGB ACR files 
1005          vr = "OB";
1006       DataEntry *pixel = 
1007          CopyDataEntry(GetFile()->GetGrPixel(),GetFile()->GetNumPixel(),vr);
1008       pixel->SetFlag(DataEntry::FLAG_PIXELDATA);
1009       pixel->SetBinArea(PixelWriteConverter->GetData(),false);
1010       pixel->SetLength(PixelWriteConverter->GetDataSize());
1011
1012       Archive->Push(spp);
1013       Archive->Push(planConfig);
1014       Archive->Push(photInt);
1015       Archive->Push(pixel);
1016
1017       spp->Delete();
1018       planConfig->Delete();
1019       photInt->Delete();
1020       pixel->Delete();
1021
1022       // Remove any LUT
1023       Archive->Push(0x0028,0x1101);
1024       Archive->Push(0x0028,0x1102);
1025       Archive->Push(0x0028,0x1103);
1026       Archive->Push(0x0028,0x1201);
1027       Archive->Push(0x0028,0x1202);
1028       Archive->Push(0x0028,0x1203);
1029
1030       // push out Palette Color Lookup Table UID, if any
1031       Archive->Push(0x0028,0x1199);
1032
1033       // For old '24 Bits' ACR-NEMA
1034       // Thus, we have a RGB image and the bits allocated = 24 and 
1035       // samples per pixels = 1 (in the read file)
1036       if ( FileInternal->GetBitsAllocated()==24 ) 
1037       {
1038          DataEntry *bitsAlloc = CopyDataEntry(0x0028,0x0100,"US");
1039          bitsAlloc->SetString("8 ");
1040
1041          DataEntry *bitsStored = CopyDataEntry(0x0028,0x0101,"US");
1042          bitsStored->SetString("8 ");
1043
1044          DataEntry *highBit = CopyDataEntry(0x0028,0x0102,"US");
1045          highBit->SetString("7 ");
1046
1047          Archive->Push(bitsAlloc);
1048          Archive->Push(bitsStored);
1049          Archive->Push(highBit);
1050
1051          bitsAlloc->Delete();
1052          bitsStored->Delete();
1053          highBit->Delete();
1054       }
1055    }
1056    else
1057    {
1058       SetWriteToRaw();
1059    }
1060 }
1061
1062 /**
1063  * \brief Restore the File write mode  
1064  */ 
1065 void FileHelper::RestoreWrite()
1066 {
1067    Archive->Restore(0x0028,0x0002);
1068    Archive->Restore(0x0028,0x0004);
1069    
1070    Archive->Restore(0x0028,0x0006);
1071    Archive->Restore(GetFile()->GetGrPixel(),GetFile()->GetNumPixel());
1072
1073    // For old ACR-NEMA (24 bits problem)
1074    Archive->Restore(0x0028,0x0100);
1075    Archive->Restore(0x0028,0x0101);
1076    Archive->Restore(0x0028,0x0102);
1077
1078    // For the LUT
1079    Archive->Restore(0x0028,0x1101);
1080    Archive->Restore(0x0028,0x1102);
1081    Archive->Restore(0x0028,0x1103);
1082    Archive->Restore(0x0028,0x1201);
1083    Archive->Restore(0x0028,0x1202);
1084    Archive->Restore(0x0028,0x1203);
1085
1086    // For the Palette Color Lookup Table UID
1087    Archive->Restore(0x0028,0x1203); 
1088
1089    // group 0002 may be pushed out for ACR-NEMA writting purposes 
1090    Archive->Restore(0x0002,0x0000);
1091    Archive->Restore(0x0002,0x0001);
1092    Archive->Restore(0x0002,0x0002);
1093    Archive->Restore(0x0002,0x0003);
1094    Archive->Restore(0x0002,0x0010);
1095    Archive->Restore(0x0002,0x0012);
1096    Archive->Restore(0x0002,0x0013);
1097    Archive->Restore(0x0002,0x0016);
1098    Archive->Restore(0x0002,0x0100);
1099    Archive->Restore(0x0002,0x0102);
1100
1101 }
1102
1103 /**
1104  * \brief Pushes out the whole group 0002
1105  *        FIXME : better, set a flag to tell the writer not to write it ...
1106  *        FIXME : method should probably have an other name !
1107  *                SetWriteFileTypeToACR is NOT opposed to 
1108  *                SetWriteFileTypeToExplicitVR and SetWriteFileTypeToImplicitVR
1109  */ 
1110 void FileHelper::SetWriteFileTypeToACR()
1111 {
1112    Archive->Push(0x0002,0x0000);
1113    Archive->Push(0x0002,0x0001);
1114    Archive->Push(0x0002,0x0002);
1115    Archive->Push(0x0002,0x0003);
1116    Archive->Push(0x0002,0x0010);
1117    Archive->Push(0x0002,0x0012);
1118    Archive->Push(0x0002,0x0013);
1119    Archive->Push(0x0002,0x0016);
1120    Archive->Push(0x0002,0x0100);
1121    Archive->Push(0x0002,0x0102);
1122 }
1123
1124 /**
1125  * \brief Sets in the File the TransferSyntax to 'JPEG2000'
1126  */
1127 void FileHelper::SetWriteFileTypeToJPEG2000()
1128 {
1129    std::string ts = Util::DicomString(
1130    Global::GetTS()->GetSpecialTransferSyntax(TS::JPEG2000Lossless) );
1131
1132    DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1133    tss->SetString(ts);
1134
1135    Archive->Push(tss);
1136    tss->Delete();   
1137 }
1138
1139 /**
1140  * \brief Sets in the File the TransferSyntax to 'JPEG'
1141  */
1142 void FileHelper::SetWriteFileTypeToJPEG()
1143 {
1144    std::string ts = Util::DicomString(
1145       Global::GetTS()->GetSpecialTransferSyntax(TS::JPEGLosslessProcess14_1) );
1146
1147    DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1148    tss->SetString(ts);
1149
1150    Archive->Push(tss);
1151    tss->Delete();
1152 }
1153
1154 /**
1155  * \brief Sets in the File the TransferSyntax to 'Explicit VR Little Endian"   
1156  */ 
1157 void FileHelper::SetWriteFileTypeToExplicitVR()
1158 {
1159    std::string ts = Util::DicomString( 
1160       Global::GetTS()->GetSpecialTransferSyntax(TS::ExplicitVRLittleEndian) );
1161
1162    DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1163    tss->SetString(ts);
1164    Archive->Push(tss);
1165    tss->Delete();
1166 }
1167
1168 /**
1169  * \brief Sets in the File the TransferSyntax to 'Implicit VR Little Endian"   
1170  */ 
1171 void FileHelper::SetWriteFileTypeToImplicitVR()
1172 {
1173    std::string ts = Util::DicomString(
1174       Global::GetTS()->GetSpecialTransferSyntax(TS::ImplicitVRLittleEndian) );
1175
1176    DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1177    tss->SetString(ts);
1178    Archive->Push(tss);
1179    tss->Delete();
1180 }
1181
1182 /**
1183  * \brief Set the Write not to Libido format
1184  */ 
1185 void FileHelper::SetWriteToLibido()
1186 {
1187    DataEntry *oldRow = FileInternal->GetDataEntry(0x0028, 0x0010);
1188    DataEntry *oldCol = FileInternal->GetDataEntry(0x0028, 0x0011);
1189    
1190    if ( oldRow && oldCol )
1191    {
1192       std::string rows, columns; 
1193
1194       DataEntry *newRow=DataEntry::New(0x0028, 0x0010, "US");
1195       DataEntry *newCol=DataEntry::New(0x0028, 0x0011, "US");
1196
1197       newRow->Copy(oldCol);
1198       newCol->Copy(oldRow);
1199
1200       newRow->SetString(oldCol->GetString());
1201       newCol->SetString(oldRow->GetString());
1202
1203       Archive->Push(newRow);
1204       Archive->Push(newCol);
1205
1206       newRow->Delete();
1207       newCol->Delete();
1208    }
1209
1210    DataEntry *libidoCode = CopyDataEntry(0x0008,0x0010,"LO");
1211    libidoCode->SetString("ACRNEMA_LIBIDO_1.1");
1212    Archive->Push(libidoCode);
1213    libidoCode->Delete();
1214 }
1215
1216 /**
1217  * \brief Set the Write not to No Libido format
1218  */ 
1219 void FileHelper::SetWriteToNoLibido()
1220 {
1221    DataEntry *recCode = FileInternal->GetDataEntry(0x0008,0x0010);
1222    if ( recCode )
1223    {
1224       if ( recCode->GetString() == "ACRNEMA_LIBIDO_1.1" )
1225       {
1226          DataEntry *libidoCode = CopyDataEntry(0x0008,0x0010,"LO");
1227          libidoCode->SetString("");
1228          Archive->Push(libidoCode);
1229          libidoCode->Delete();
1230       }
1231    }
1232 }
1233
1234 /**
1235  * \brief Restore the Write format
1236  */ 
1237 void FileHelper::RestoreWriteOfLibido()
1238 {
1239    Archive->Restore(0x0028,0x0010);
1240    Archive->Restore(0x0028,0x0011);
1241    Archive->Restore(0x0008,0x0010);
1242
1243    // Restore 'LibIDO-special' entries, if any
1244    Archive->Restore(0x0028,0x0015);
1245    Archive->Restore(0x0028,0x0016);
1246    Archive->Restore(0x0028,0x0017);
1247    Archive->Restore(0x0028,0x00199);
1248 }
1249
1250 /**
1251  * \brief   Duplicates a DataEntry or creates it.
1252  * @param   group   Group number of the Entry 
1253  * @param   elem  Element number of the Entry
1254  * @param   vr  Value Representation of the Entry
1255  * \return  pointer to the new Bin Entry (NULL when creation failed).
1256  */ 
1257 DataEntry *FileHelper::CopyDataEntry(uint16_t group, uint16_t elem,
1258                                    const VRKey &vr)
1259 {
1260    DocEntry *oldE = FileInternal->GetDocEntry(group, elem);
1261    DataEntry *newE;
1262
1263    if ( oldE && vr != GDCM_VRUNKNOWN ) 
1264       if ( oldE->GetVR() != vr )
1265          oldE = NULL;
1266
1267    if ( oldE )
1268    {
1269       newE = DataEntry::New(group, elem, vr);
1270       newE->Copy(oldE);
1271    }
1272    else
1273    {
1274       newE = GetFile()->NewDataEntry(group, elem, vr);
1275    }
1276
1277    return newE;
1278 }
1279
1280 /**
1281  * \brief   This method is called automatically, just before writting
1282  *         in order to produce a 'True Dicom V3' image.
1283  *
1284  *         We cannot know *how* the user made the File :
1285  *         (reading an old ACR-NEMA file or a not very clean DICOM file ...) 
1286  *          Just before writting :
1287  *             - we check the Entries
1288  *             - we create the mandatory entries if they are missing
1289  *             - we modify the values if necessary
1290  *             - we push the sensitive entries to the Archive
1291  *          The writing process will restore the entries as they where before 
1292  *          entering FileHelper::CheckMandatoryElements, so the user will always
1293  *          see the entries just as they were before he decided to write.
1294  *
1295  * \note
1296  *       -  Entries whose type is 1 are mandatory, with a mandatory value
1297  *       -  Entries whose type is 1c are mandatory-inside-a-Sequence,
1298  *                             with a mandatory value
1299  *       -  Entries whose type is 2 are mandatory, with an optional value
1300  *       -  Entries whose type is 2c are mandatory-inside-a-Sequence,
1301  *                             with an optional value
1302  *       -  Entries whose type is 3 are optional
1303  * 
1304  * \todo 
1305  *         - warn the user if we had to add some entries :
1306  *         even if a mandatory entry is missing, we add it, with a default value
1307  *         (we don't want to give up the writting process if user forgot to
1308  *         specify Lena's Patient ID, for instance ...)
1309  *         - read the whole PS 3.3 Part of DICOM  (890 pages)
1310  *         and write a *full* checker (probably one method per Modality ...)
1311  *         Any contribution is welcome. 
1312  *         - write a user callable full checker, to allow post reading
1313  *         and/or pre writting image consistency check.           
1314  */ 
1315
1316 /* -------------------------------------------------------------------------------------
1317 To be moved to User's guide / WIKI  ?
1318
1319 We have to deal with 4 *very* different cases :
1320 -1) user created ex nihilo his own image and wants to write it as a Dicom image.
1321     USER_OWN_IMAGE
1322 -2) user modified the pixels of an existing image.
1323    FILTERED_IMAGE
1324 -3) user created a new image, using a set of existing images (eg MIP, MPR, cartography image)
1325    CREATED_IMAGE
1326 -4) user modified/added some tags *without processing* the pixels (anonymization...)
1327    UNMODIFIED_PIXELS_IMAGE
1328 -Probabely some more to be added.
1329  --> Set it with FileHelper::SetContentType(int);
1330  
1331 GDCM_NAME_SPACE::FileHelper::CheckMandatoryElements() deals automatically with these cases.
1332
1333 1)2)3)4)
1334 0008 0012 Instance Creation Date
1335 0008 0013 Instance Creation Time
1336 0008 0018 SOP Instance UID
1337 are *always* created with the current values; user has *no* possible intervention on
1338 them.
1339
1340 'Serie Instance UID'(0x0020,0x000e)
1341 'Study Instance UID'(0x0020,0x000d) are kept as is if already exist,
1342                                     created  if it doesn't.
1343  The user is allowed to create his own Series/Studies, 
1344      keeping the same 'Serie Instance UID' / 'Study Instance UID' for various images
1345  Warning :     
1346  The user shouldn't add any image to a 'Manufacturer Serie'
1347      but there is no way no to allow him to do that
1348      
1349  None of the 'shadow elements' are droped out.
1350      
1351
1352 1)
1353 'Conversion Type (0x0008,0x0064) is forced to 'SYN' (Synthetic Image).
1354  
1355 1)3)
1356 'Media Storage SOP Class UID' (0x0002,0x0002)
1357 'SOP Class UID'               (0x0008,0x0016) are set to 
1358                                                [Secondary Capture Image Storage]
1359 'Image Type'                  (0x0008,0x0008) is forced to  "DERIVED\PRIMARY"
1360 Conversion Type               (0x0008,0x0064) is forced to 'SYN' (Synthetic Image)
1361
1362 2)4)
1363 If 'SOP Class UID' exists in the native image  ('true DICOM' image)
1364     we create the 'Source Image Sequence' SeqEntry (0x0008, 0x2112)    
1365     --> 'Referenced SOP Class UID' (0x0008, 0x1150)
1366          whose value is the original 'SOP Class UID'
1367     --> 'Referenced SOP Instance UID' (0x0008, 0x1155)
1368          whose value is the original 'SOP Class UID'
1369
1370 3) TO DO : find a trick to allow user to pass to the writter the list of the Dicom images 
1371           or the Series, (or the Study ?) he used to created his image 
1372           (MIP, MPR, cartography image, ...)
1373            These info should be stored (?)
1374           0008 1110 SQ 1 Referenced Study Sequence
1375           0008 1115 SQ 1 Referenced Series Sequence
1376           0008 1140 SQ 1 Referenced Image Sequence
1377        
1378 4) When user *knows* he didn't modified the pixels, we keep some informations unchanged :
1379 'Media Storage SOP Class UID' (0x0002,0x0002)
1380 'SOP Class UID'               (0x0008,0x0016)
1381 'Image Type'                  (0x0008,0x0008)
1382 'Conversion Type'             (0x0008,0x0064)
1383
1384
1385 Bellow follows the full description (hope so !) of the consistency checks performed 
1386 by GDCM_NAME_SPACE::FileHelper::CheckMandatoryElements()
1387
1388
1389 -->'Media Storage SOP Class UID' (0x0002,0x0002)
1390 -->'SOP Class UID'               (0x0008,0x0016) are defaulted to 
1391                                                [Secondary Capture Image Storage]
1392 --> 'Image Type'  (0x0008,0x0008)
1393      is forced to  "DERIVED\PRIMARY"
1394      (The written image is no longer an 'ORIGINAL' one)
1395   Except if user knows he didn't modify the image (e.g. : he just anonymized the file)
1396    
1397  -->  Conversion Type (0x0008,0x0064)
1398      is defaulted to 'SYN' (Synthetic Image)
1399   when *he* knows he created his own image ex nihilo
1400             
1401 --> 'Modality' (0x0008,0x0060)   
1402     is defaulted to "OT" (other) if missing.   
1403     (a fully user created image belongs to *no* modality)
1404       
1405 --> 'Media Storage SOP Instance UID' (0x0002,0x0003)
1406 --> 'Implementation Class UID'       (0x0002,0x0012)
1407     are automatically generated; no user intervention possible
1408
1409 --> 'Serie Instance UID'(0x0020,0x000e)
1410 --> 'Study Instance UID'(0x0020,0x000d) are kept as is if already exist
1411                                              created  if it doesn't.
1412      The user is allowed to create his own Series/Studies, 
1413      keeping the same 'Serie Instance UID' / 'Study Instance UID' 
1414      for various images
1415      Warning :     
1416      The user shouldn't add any image to a 'Manufacturer Serie'
1417      but there is no way no to allowed him to do that 
1418              
1419 --> If 'SOP Class UID' exists in the native image  ('true DICOM' image)
1420     we create the 'Source Image Sequence' SeqEntry (0x0008, 0x2112)
1421     
1422     --> 'Referenced SOP Class UID' (0x0008, 0x1150)
1423          whose value is the original 'SOP Class UID'
1424     --> 'Referenced SOP Instance UID' (0x0008, 0x1155)
1425          whose value is the original 'SOP Class UID'
1426     
1427 --> Bits Stored, Bits Allocated, Hight Bit Position are checked for consistency
1428 --> Pixel Spacing     (0x0028,0x0030) is defaulted to "1.0\1.0"
1429 --> Samples Per Pixel (0x0028,0x0002) is defaulted to 1 (grayscale)
1430
1431 --> Imager Pixel Spacing (0x0018,0x1164) : defaulted to Pixel Spacing value
1432
1433 --> Instance Creation Date, Instance Creation Time are forced to current Date and Time
1434
1435 --> Study Date, Study Time are defaulted to current Date and Time
1436    (they remain unchanged if they exist)
1437
1438 --> Patient Orientation : (0x0020,0x0020), if not present, is deduced from 
1439     Image Orientation (Patient) : (0020|0037) or from
1440     Image Orientation (RET)     : (0020 0035)
1441    
1442 --> Study ID, Series Number, Instance Number, Patient Orientation (Type 2)
1443     are created, with empty value if there are missing.
1444
1445 --> Manufacturer, Institution Name, Patient's Name, (Type 2)
1446     are defaulted with a 'gdcm' value.
1447     
1448 --> Patient ID, Patient's Birth Date, Patient's Sex, (Type 2)
1449 --> Referring Physician's Name  (Type 2)
1450     are created, with empty value if there are missing.
1451
1452  -------------------------------------------------------------------------------------*/
1453
1454 void FileHelper::CheckMandatoryElements()
1455 {
1456    std::string sop =  Util::CreateUniqueUID();
1457
1458    // --------------------- For Meta Elements ---------------------
1459    // just to remember : 'official' 0002 group
1460    if ( WriteType != ACR && WriteType != ACR_LIBIDO )
1461    {
1462      // Group 000002 (Meta Elements) already pushed out
1463   
1464    //0002 0000 UL 1 Meta Group Length
1465    //0002 0001 OB 1 File Meta Information Version
1466    //0002 0002 UI 1 Media Storage SOP Class UID
1467    //0002 0003 UI 1 Media Storage SOP Instance UID
1468    //0002 0010 UI 1 Transfer Syntax UID
1469    //0002 0012 UI 1 Implementation Class UID
1470    //0002 0013 SH 1 Implementation Version Name
1471    //0002 0016 AE 1 Source Application Entity Title
1472    //0002 0100 UI 1 Private Information Creator
1473    //0002 0102 OB 1 Private Information
1474
1475    // Push out 'ACR-NEMA-special' entries, if any
1476       Archive->Push(0x0008,0x0001); // Length to End
1477       Archive->Push(0x0008,0x0010); // Recognition Code
1478       Archive->Push(0x0028,0x0005); // Image Dimension
1479
1480    // Create them if not found
1481    // Always modify the value
1482    // Push the entries to the archive.
1483       CopyMandatoryEntry(0x0002,0x0000,"0","UL");
1484
1485       DataEntry *e_0002_0001 = CopyDataEntry(0x0002,0x0001, "OB");
1486       e_0002_0001->SetBinArea((uint8_t*)Util::GetFileMetaInformationVersion(),
1487                                false);
1488       e_0002_0001->SetLength(2);
1489       Archive->Push(e_0002_0001);
1490       e_0002_0001->Delete(); 
1491
1492       if ( ContentType == FILTERED_IMAGE || ContentType == UNMODIFIED_PIXELS_IMAGE)
1493       {      
1494    // we keep the original 'Media Storage SOP Class UID', we default it if missing
1495          CheckMandatoryEntry(0x0002,0x0002,"1.2.840.10008.5.1.4.1.1.7","UI"); 
1496       }
1497       else
1498       {
1499    // It's *not* an image comming straight from a source. We force
1500    // 'Media Storage SOP Class UID'  --> [Secondary Capture Image Storage]
1501          CopyMandatoryEntry(0x0002,0x0002,"1.2.840.10008.5.1.4.1.1.7","UI");
1502       }
1503
1504    // 'Media Storage SOP Instance UID'
1505       CopyMandatoryEntry(0x0002,0x0003,sop,"UI");
1506
1507    // 'Implementation Class UID'
1508    // FIXME : in all examples we have, 0x0002,0x0012 is not so long :
1509    //         seems to be Root UID + 4 digits (?)
1510       CopyMandatoryEntry(0x0002,0x0012,Util::CreateUniqueUID(),"UI");
1511
1512    // 'Implementation Version Name'
1513       std::string version = "GDCM ";
1514       version += Util::GetVersion();
1515       CopyMandatoryEntry(0x0002,0x0013,version,"SH");
1516    }
1517
1518    // --------------------- For DataSet ---------------------
1519
1520    // check whether 0018|0015 [CS] [Body Part Examined] value is UPPER CASE
1521    //      (avoid dciodvfy to complain!)
1522    DataEntry *e_0018_0015 = FileInternal->GetDataEntry(0x0018, 0x0015);  
1523    if ( e_0018_0015)
1524    {
1525       std::string bodyPartExamined = e_0018_0015->GetString();
1526       std::transform(bodyPartExamined.begin(), bodyPartExamined.end(), bodyPartExamined.begin(), 
1527                     (int(*)(int)) toupper);
1528       CopyMandatoryEntry(0x0018,0x0015,bodyPartExamined,"CS");       
1529    }
1530
1531    if ( ContentType != USER_OWN_IMAGE) // when it's not a user made image
1532    { 
1533    // If 'SOP Class UID' and 'SOP Instance UID' exist ('true DICOM' image)
1534    // we create the 'Source Image Sequence' SeqEntry
1535    // to hold informations about the Source Image
1536  
1537       // 'SOP Instance UID' 
1538       DataEntry *e_0008_0016 = FileInternal->GetDataEntry(0x0008, 0x0016);
1539       //
1540       DataEntry *e_0008_0018 = FileInternal->GetDataEntry(0x0008, 0x0018);
1541       if ( e_0008_0016 && e_0008_0018)
1542       {
1543          // Create 'Source Image Sequence' SeqEntry
1544          SeqEntry *sis = SeqEntry::New (0x0008, 0x2112);
1545          SQItem *sqi = SQItem::New(1);
1546       
1547          // create 'Referenced SOP Class UID' from 'SOP Class UID'
1548
1549          DataEntry *e_0008_1150 = DataEntry::New(0x0008, 0x1150, "UI");
1550          e_0008_1150->SetString( e_0008_0016->GetString());
1551          sqi->AddEntry(e_0008_1150);
1552          e_0008_1150->Delete();
1553       
1554          // create 'Referenced SOP Instance UID' from 'SOP Instance UID'
1555         // DataEntry *e_0008_0018 = FileInternal->GetDataEntry(0x0008, 0x0018);
1556          
1557          DataEntry *e_0008_1155 = DataEntry::New(0x0008, 0x1155, "UI"); 
1558          e_0008_1155->SetString( e_0008_0018->GetString());
1559          sqi->AddEntry(e_0008_1155);
1560          e_0008_1155->Delete();
1561       
1562          sis->AddSQItem(sqi,1);
1563          sqi->Delete();
1564
1565          // temporarily replaces any previous 'Source Image Sequence' 
1566          Archive->Push(sis);
1567          sis->Delete();
1568          // FIXME : is 'Image Type' *really* depending on the presence of 'SOP Class UID'?
1569          
1570          if ( ContentType == FILTERED_IMAGE) // the user *knows* he just modified the pixels
1571          { 
1572             DataEntry *e_0008_0008 = FileInternal->GetDataEntry(0x0008, 0x0008);  
1573             if ( e_0008_0008)
1574             {
1575                std::string imageType = e_0008_0008->GetString();
1576                std::string::size_type p = imageType.find("ORIGINAL");
1577                if (p == 0) // image is ORIGINAL one
1578                {            
1579                  // the image is no longer an 'Original' one
1580                  CopyMandatoryEntry(0x0008,0x0008,"DERIVED\\PRIMARY","CS");
1581                }
1582                // if Image Type was not ORIGINAL\..., we keep it.
1583              }
1584              else // 0008_0008 was missing, wee add it.
1585              {
1586                  CopyMandatoryEntry(0x0008,0x0008,"DERIVED\\PRIMARY","CS");             
1587              }  
1588          }    
1589       }
1590    }
1591       
1592    if ( ContentType == FILTERED_IMAGE || ContentType == UNMODIFIED_PIXELS_IMAGE)
1593    {      
1594    // we keep the original 'Media Storage SOP Class UID', we default it if missing (it should be present !)
1595          CheckMandatoryEntry(0x0008,0x0016,"1.2.840.10008.5.1.4.1.1.7","UI");      
1596    }
1597    else
1598    {
1599    // It's *not* an image comming straight from a source. We force
1600    // 'Media Storage SOP Class UID'  --> [Secondary Capture Image Storage]
1601          CopyMandatoryEntry(0x0008,0x0016,"1.2.840.10008.5.1.4.1.1.7", "UI");      
1602    }
1603      
1604    Archive->Push(0x0028,0x005); // [Image Dimensions (RET)
1605    // Push out 'LibIDO-special' entries, if any
1606    Archive->Push(0x0028,0x0015);
1607    Archive->Push(0x0028,0x0016);
1608    Archive->Push(0x0028,0x0017);
1609    Archive->Push(0x0028,0x0198);  // very old versions
1610    Archive->Push(0x0028,0x0199);
1611
1612    // Replace deprecated 0028 0012 US Planes   
1613    // by new             0028 0008 IS Number of Frames
1614
1615   ///\todo : find if there is a rule!
1616    DataEntry *e_0028_0012 = FileInternal->GetDataEntry(0x0028, 0x0012);
1617    if ( e_0028_0012 )
1618    {
1619       CopyMandatoryEntry(0x0028, 0x0008,e_0028_0012->GetString(),"IS");
1620       Archive->Push(0x0028,0x0012);      
1621    }
1622
1623    // Deal with the pb of (Bits Stored = 12)
1624    // - we're gonna write the image as Bits Stored = 16
1625    if ( FileInternal->GetEntryString(0x0028,0x0100) ==  "12")
1626    {
1627       CopyMandatoryEntry(0x0028,0x0100,"16","US");
1628    }
1629
1630    // Check if user wasn't drunk ;-)
1631
1632    std::ostringstream s;
1633    // check 'Bits Allocated' vs decent values
1634    int nbBitsAllocated = FileInternal->GetBitsAllocated();
1635    if ( (nbBitsAllocated == 0 || nbBitsAllocated > 32)
1636      || ( nbBitsAllocated > 8 && nbBitsAllocated <16) )
1637    {
1638       CopyMandatoryEntry(0x0028,0x0100,"16","US");
1639       gdcmWarningMacro("(0028,0100) changed from "
1640          << nbBitsAllocated << " to 16 for consistency purpose");
1641       nbBitsAllocated = 16; 
1642    }
1643    // check 'Bits Stored' vs 'Bits Allocated'   
1644    int nbBitsStored = FileInternal->GetBitsStored();
1645    if ( nbBitsStored == 0 || nbBitsStored > nbBitsAllocated )
1646    {
1647       s.str("");
1648       s << nbBitsAllocated;
1649       CopyMandatoryEntry(0x0028,0x0101,s.str(),"US");
1650       gdcmWarningMacro("(0028,0101) changed from "
1651                        << nbBitsStored << " to " << nbBitsAllocated
1652                        << " for consistency purpose" );
1653       nbBitsStored = nbBitsAllocated; 
1654     }
1655    // check 'Hight Bit Position' vs 'Bits Allocated' and 'Bits Stored'
1656    int highBitPosition = FileInternal->GetHighBitPosition();
1657    if ( highBitPosition == 0 || 
1658         highBitPosition > nbBitsAllocated-1 ||
1659         highBitPosition < nbBitsStored-1  )
1660    {
1661       s.str("");
1662       s << nbBitsStored - 1; 
1663       CopyMandatoryEntry(0x0028,0x0102,s.str(),"US");
1664       gdcmWarningMacro("(0028,0102) changed from "
1665                        << highBitPosition << " to " << nbBitsAllocated-1
1666                        << " for consistency purpose");
1667    }
1668
1669    // check Pixel Representation (default it as 0 -unsigned-)
1670
1671    DataEntry *e_0028_0103 = FileInternal->GetDataEntry(0x0028, 0x0103);
1672    if ( !e_0028_0103 )
1673    {
1674       gdcmWarningMacro("PixelRepresentation (0028,0103) is supposed to be mandatory");
1675       CopyMandatoryEntry(0x0028, 0x0103,"0","US"); 
1676    }
1677    else
1678    {
1679       int sign = (int)e_0028_0103->GetValue(0);
1680       if (sign !=1 && sign !=0)
1681       {
1682          gdcmWarningMacro("PixelRepresentation (0028,0103) is supposed to be =1 or =0");
1683          CopyMandatoryEntry(0x0028, 0x0103,"0","US");
1684       }
1685    }
1686
1687    std::string pixelAspectRatio = FileInternal->GetEntryString(0x0028,0x0034);
1688    if ( pixelAspectRatio == GDCM_UNFOUND ) // avoid conflict with pixelSpacing !
1689    {
1690       std::string pixelSpacing = FileInternal->GetEntryString(0x0028,0x0030);
1691       if ( pixelSpacing == GDCM_UNFOUND )
1692       {
1693          pixelSpacing = "1.0\\1.0";
1694           // if missing, Pixel Spacing forced to "1.0\1.0"
1695          CopyMandatoryEntry(0x0028,0x0030,pixelSpacing,"DS");
1696       }
1697   
1698       // 'Imager Pixel Spacing' : defaulted to 'Pixel Spacing'
1699       // --> This one is the *legal* one !
1700       if ( ContentType != USER_OWN_IMAGE)
1701       //  we write it only when we are *sure* the image comes from
1702       //         an imager (see also 0008,0x0064)
1703          CheckMandatoryEntry(0x0018,0x1164,pixelSpacing,"DS");
1704    } 
1705 /*
1706 ///Exact meaning of RETired fields
1707
1708 // See page 73 of ACR-NEMA_300-1988.pdf !
1709
1710 // 0020,0020 : Patient Orientation :
1711 Patient direction of the first row and
1712 column of the images. The first entry id the direction of the raws, given by the
1713 direction of the last pixel in the first row from the first pixel in tha row.
1714 the second entry is the direction of the columns, given by the direction of the
1715 last pixel in the first column from the first pixel in that column.
1716 L : Left, F : Feet, A : Anterior, P : Posterior.
1717 Up to 3 letters can be used in combination to indicate oblique planes.
1718
1719 //0020,0030 Image Position (RET)
1720 x,y,z coordinates im mm of the first pixel in the image
1721
1722 // 0020,0035 Image Orientation (RET)
1723 Direction cosines of the R axis of the image system with respect to the
1724 equipment coordinate axes x,y,z, followed by direction cosines of the C axis of
1725 the image system with respect to the same axes
1726
1727 //0020,0050 Location
1728 An image location reference, standard for the modality (such as CT bed position),
1729 used to indicate position. Calculation of position for other purposes
1730 is only from (0020,0030) and (0020,0035)
1731 */
1732
1733 /*
1734 // if imagePositionPatient    not found, default it with imagePositionRet,    if any
1735 // if imageOrientationPatient not found, default it with imageOrientationRet, if any
1736
1737    std::string imagePositionRet        = FileInternal->GetEntryString(0x0020,0x0030);
1738    std::string imageOrientationRet     = FileInternal->GetEntryString(0x0020,0x0035);
1739    std::string imagePositionPatient    = FileInternal->GetEntryString(0x0020,0x0032);
1740    std::string imageOrientationPatient = FileInternal->GetEntryString(0x0020,0x0037);
1741
1742    if(  imagePositionPatient == GDCM_UNFOUND && imageOrientationPatient == GDCM_UNFOUND
1743      && imagePositionRet     != GDCM_UNFOUND && imageOrientationRet     != GDCM_UNFOUND)
1744    {
1745       CopyMandatoryEntry(0x0020, 0x0032,imagePositionRet,"DS");
1746       Archive->Push(0x0020,0x0030); 
1747       CopyMandatoryEntry(0x0020, 0x0037,imageOrientationRet,"DS");
1748       Archive->Push(0x0020,0x0035);
1749    }
1750 */
1751
1752    // Samples Per Pixel (type 1) : default to grayscale
1753    CheckMandatoryEntry(0x0028,0x0002,"1","US");
1754
1755    // --- Check UID-related Entries ---
1756  
1757    // At the end, not to overwrite the original ones,
1758    // needed by 'Referenced SOP Instance UID', 'Referenced SOP Class UID'
1759    // 'SOP Instance UID'  
1760    CopyMandatoryEntry(0x0008,0x0018,sop,"UI");
1761
1762    if ( ContentType == USER_OWN_IMAGE)
1763    {
1764       gdcmDebugMacro( "USER_OWN_IMAGE (2)");
1765        // Conversion Type.
1766        // Other possible values are :
1767        // See PS 3.3, Page 408
1768
1769        // DV = Digitized Video
1770        // DI = Digital Interface 
1771        // DF = Digitized Film
1772        // WSD = Workstation
1773        // SD = Scanned Document
1774        // SI = Scanned Image
1775        // DRW = Drawing
1776        // SYN = Synthetic Image
1777
1778       CheckMandatoryEntry(0x0008,0x0064,"SYN","CS"); // Why not?
1779    } 
1780 /*
1781    if ( ContentType == CREATED_IMAGE)
1782    {
1783    /// \todo : find a trick to pass the Media Storage SOP Instance UID of the images used to create the current image
1784    
1785    }
1786 */
1787
1788    // ---- The user will never have to take any action on the following ----
1789
1790    // new value for 'SOP Instance UID'
1791    //SetMandatoryEntry(0x0008,0x0018,Util::CreateUniqueUID());
1792
1793    // Instance Creation Date
1794    const std::string &date = Util::GetCurrentDate();
1795    CopyMandatoryEntry(0x0008,0x0012,date,"DA");
1796
1797    // Instance Creation Time
1798    const std::string &time = Util::GetCurrentTime();
1799    CopyMandatoryEntry(0x0008,0x0013,time,"TM");
1800
1801    // Study Date
1802    CheckMandatoryEntry(0x0008,0x0020,date,"DA");
1803    // Study Time
1804    CheckMandatoryEntry(0x0008,0x0030,time,"TM");
1805
1806    // Accession Number
1807    //CopyMandatoryEntry(0x0008,0x0050,"");
1808    CheckMandatoryEntry(0x0008,0x0050,"","SH");
1809    
1810
1811    // ----- Add Mandatory Entries if missing ---
1812    // Entries whose type is 1 are mandatory, with a mandatory value
1813    // Entries whose type is 1c are mandatory-inside-a-Sequence,
1814    //                          with a mandatory value
1815    // Entries whose type is 2 are mandatory, with an optional value
1816    // Entries whose type is 2c are mandatory-inside-a-Sequence,
1817    //                          with an optional value
1818    // Entries whose type is 3 are optional
1819
1820    // 'Study Instance UID'
1821    // Keep the value if exists
1822    // The user is allowed to create his own Study, 
1823    //          keeping the same 'Study Instance UID' for various images
1824    // The user may add images to a 'Manufacturer Study',
1825    //          adding new Series to an already existing Study 
1826    CheckMandatoryEntry(0x0020,0x000d,Util::CreateUniqueUID(),"UI");
1827
1828    // 'Serie Instance UID'
1829    // Keep the value if exists
1830    // The user is allowed to create his own Series, 
1831    // keeping the same 'Serie Instance UID' for various images
1832    // The user shouldn't add any image to a 'Manufacturer Serie'
1833    // but there is no way no to prevent him for doing that 
1834    CheckMandatoryEntry(0x0020,0x000e,Util::CreateUniqueUID(),"UI");
1835
1836    // Study ID
1837    CheckMandatoryEntry(0x0020,0x0010,"","SH");
1838
1839    // Series Number
1840    CheckMandatoryEntry(0x0020,0x0011,"","IS");
1841
1842    // Instance Number
1843    CheckMandatoryEntry(0x0020,0x0013,"","IS");
1844
1845    // Patient Orientation
1846    // Can be computed from (0020|0037) :  Image Orientation (Patient)
1847    GDCM_NAME_SPACE::Orientation *o = GDCM_NAME_SPACE::Orientation::New();
1848    std::string ori = o->GetOrientation ( FileInternal );
1849    o->Delete();
1850    if (ori != "\\" && ori != GDCM_UNFOUND)
1851       CheckMandatoryEntry(0x0020,0x0020,ori,"CS");
1852    else
1853       CheckMandatoryEntry(0x0020,0x0020,"","CS");
1854
1855    // Default Patient Position to HFS
1856    CheckMandatoryEntry(0x0018,0x5100,"HFS","CS");
1857
1858    // Modality : if missing we set it to 'OTher'
1859    CheckMandatoryEntry(0x0008,0x0060,"OT","CS");
1860
1861    // Manufacturer : if missing we set it to 'GDCM Factory'
1862    CheckMandatoryEntry(0x0008,0x0070,"GDCM Factory","LO");
1863
1864    // Institution Name : if missing we set it to 'GDCM Hospital'
1865    CheckMandatoryEntry(0x0008,0x0080,"GDCM Hospital","LO");
1866
1867    // Patient's Name : if missing, we set it to 'GDCM^Patient'
1868    CheckMandatoryEntry(0x0010,0x0010,"GDCM^Patient","PN");
1869
1870    // Patient ID : some clinical softwares *demand* it although it's a 'type 2' entry.
1871    CheckMandatoryEntry(0x0010,0x0020,"gdcm ID","LO");
1872
1873    // Patient's Birth Date : 'type 2' entry -> must exist, value not mandatory
1874    CheckMandatoryEntry(0x0010,0x0030,"","DA");
1875
1876    // Patient's Sex :'type 2' entry -> must exist, value not mandatory
1877    CheckMandatoryEntry(0x0010,0x0040,"","CS");
1878
1879    // Referring Physician's Name :'type 2' entry -> must exist, value not mandatory
1880    CheckMandatoryEntry(0x0008,0x0090,"","PN");
1881
1882  /*
1883    // Deal with element 0x0000 (group length) of each group.
1884    // First stage : get all the different Groups
1885
1886   GroupHT grHT;
1887   DocEntry *d = FileInternal->GetFirstEntry();
1888   while(d)
1889   {
1890     grHT[d->GetGroup()] = 0;
1891     d=FileInternal->GetNextEntry();
1892   }
1893   // Second stage : add the missing ones (if any)
1894   for (GroupHT::iterator it = grHT.begin(); it != grHT.end(); ++it)  
1895   {
1896       CheckMandatoryEntry(it->first, 0x0000, "0"); 
1897   }    
1898   // Third stage : update all 'zero level' groups length
1899 */
1900
1901
1902    if (PhotometricInterpretation == 1)
1903    {
1904    }
1905
1906
1907 void FileHelper::CheckMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr )
1908 {
1909    DataEntry *entry = FileInternal->GetDataEntry(group,elem);
1910    if ( !entry )
1911    {
1912       //entry = DataEntry::New(Global::GetDicts()->GetDefaultPubDict()->GetEntry(group,elem));
1913       entry = DataEntry::New(group,elem,vr);
1914       entry->SetString(value);
1915       Archive->Push(entry);
1916       entry->Delete();
1917    }    
1918 }
1919
1920 /// \todo : what is it used for ? (FileHelper::SetMandatoryEntry)
1921 void FileHelper::SetMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr)
1922 {
1923    //DataEntry *entry = DataEntry::New(Global::GetDicts()->GetDefaultPubDict()->GetEntry(group,elem));
1924    DataEntry *entry = DataEntry::New(group,elem,vr);
1925    entry->SetString(value);
1926    Archive->Push(entry);
1927    entry->Delete();
1928 }
1929
1930 void FileHelper::CopyMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr)
1931 {
1932    DataEntry *entry = CopyDataEntry(group,elem,vr);
1933    entry->SetString(value);
1934    Archive->Push(entry);
1935    entry->Delete();
1936 }
1937
1938 /**
1939  * \brief Restore in the File the initial group 0002
1940  */
1941 void FileHelper::RestoreWriteMandatory()
1942 {
1943    // group 0002 may be pushed out for ACR-NEMA writting purposes 
1944    Archive->Restore(0x0002,0x0000);
1945    Archive->Restore(0x0002,0x0001);
1946    Archive->Restore(0x0002,0x0002);
1947    Archive->Restore(0x0002,0x0003);
1948    Archive->Restore(0x0002,0x0010);
1949    Archive->Restore(0x0002,0x0012);
1950    Archive->Restore(0x0002,0x0013);
1951    Archive->Restore(0x0002,0x0016);
1952    Archive->Restore(0x0002,0x0100);
1953    Archive->Restore(0x0002,0x0102);
1954
1955    // FIXME : Check if none is missing !
1956    
1957    Archive->Restore(0x0008,0x0012);
1958    Archive->Restore(0x0008,0x0013);
1959    Archive->Restore(0x0008,0x0016);
1960    Archive->Restore(0x0008,0x0018);
1961    Archive->Restore(0x0008,0x0060);
1962    Archive->Restore(0x0008,0x0070);
1963    Archive->Restore(0x0008,0x0080);
1964    Archive->Restore(0x0008,0x0090);
1965    Archive->Restore(0x0008,0x2112);
1966
1967    Archive->Restore(0x0010,0x0010);
1968    Archive->Restore(0x0010,0x0030);
1969    Archive->Restore(0x0010,0x0040);
1970
1971    Archive->Restore(0x0020,0x000d);
1972    Archive->Restore(0x0020,0x000e);
1973 }
1974
1975 /**
1976  * \brief   CallStartMethod
1977  */
1978 void FileHelper::CallStartMethod()
1979 {
1980    Progress = 0.0f;
1981    Abort    = false;
1982    CommandManager::ExecuteCommand(this,CMD_STARTPROGRESS);
1983 }
1984
1985 /**
1986  * \brief   CallProgressMethod
1987  */
1988 void FileHelper::CallProgressMethod()
1989 {
1990    CommandManager::ExecuteCommand(this,CMD_PROGRESS);
1991 }
1992
1993 /**
1994  * \brief   CallEndMethod
1995  */
1996 void FileHelper::CallEndMethod()
1997 {
1998    Progress = 1.0f;
1999    CommandManager::ExecuteCommand(this,CMD_ENDPROGRESS);
2000 }
2001
2002 //-----------------------------------------------------------------------------
2003 // Private
2004 /**
2005  * \brief Factorization for various forms of constructors.
2006  */
2007 void FileHelper::Initialize()
2008 {
2009    UserFunction = 0;
2010    ContentType = USER_OWN_IMAGE;
2011
2012    WriteMode = WMODE_RAW;
2013    WriteType = ExplicitVR;
2014    
2015    PhotometricInterpretation = 2; // Black = 0
2016
2017    PixelReadConverter  = new PixelReadConvert;
2018    PixelWriteConverter = new PixelWriteConvert;
2019    Archive = new DocEntryArchive( FileInternal );
2020    
2021    KeepOverlays = false;
2022 }
2023
2024 /**
2025  * \brief Reads/[decompresses] the pixels, 
2026  *        *without* making RGB from Palette Colors 
2027  * @return the pixels area, whatever its type 
2028  *         (uint8_t is just for prototyping : feel free to Cast it) 
2029  */ 
2030 uint8_t *FileHelper::GetRaw()
2031 {
2032    PixelReadConverter->SetUserFunction( UserFunction );
2033
2034    uint8_t *raw = PixelReadConverter->GetRaw();
2035    if ( ! raw )
2036    {
2037       // The Raw image migth not be loaded yet:
2038       std::ifstream *fp = FileInternal->OpenFile();
2039       PixelReadConverter->ReadAndDecompressPixelData( fp );
2040       if ( fp ) 
2041          FileInternal->CloseFile();
2042
2043       raw = PixelReadConverter->GetRaw();
2044       if ( ! raw )
2045       {
2046          gdcmWarningMacro( "Read/decompress of pixel data apparently went wrong.");
2047          return 0;
2048       }
2049    }
2050    return raw;
2051 }
2052
2053 /**
2054  * \brief Deal with Grey levels i.e. re-arange them
2055  *        to have low values = dark, high values = bright
2056  */
2057 void FileHelper::ConvertFixGreyLevels(uint8_t *raw, size_t rawSize)
2058 {
2059    uint32_t i; // to please M$VC6
2060    int16_t j;
2061
2062    // Number of Bits Allocated for storing a Pixel is defaulted to 16
2063    // when absent from the file.
2064    int bitsAllocated = FileInternal->GetBitsAllocated();
2065    if ( bitsAllocated == 0 )
2066    {
2067       bitsAllocated = 16;
2068    }
2069
2070    else if (bitsAllocated > 8 && bitsAllocated < 16 && bitsAllocated != 12)
2071    {
2072       bitsAllocated = 16;
2073    }   
2074    // Number of "Bits Stored", defaulted to number of "Bits Allocated"
2075    // when absent from the file.
2076    int bitsStored = FileInternal->GetBitsStored();
2077    if ( bitsStored == 0 )
2078    {
2079       bitsStored = bitsAllocated;
2080    }
2081
2082    if (!FileInternal->IsSignedPixelData())
2083    {
2084       if ( bitsAllocated == 8 )
2085       {
2086          uint8_t *deb = (uint8_t *)raw;
2087          for (i=0; i<rawSize; i++)      
2088          {
2089             *deb = 255 - *deb;
2090             deb++;
2091          }
2092          return;
2093       }
2094
2095       if ( bitsAllocated == 16 )
2096       {
2097          uint16_t mask =1;
2098          for (j=0; j<bitsStored-1; j++)
2099          {
2100             mask = (mask << 1) +1; // will be fff when BitsStored=12
2101          }
2102
2103          uint16_t *deb = (uint16_t *)raw;
2104          for (i=0; i<rawSize/2; i++)      
2105          {
2106             *deb = mask - *deb;
2107             deb++;
2108          }
2109          return;
2110        }
2111    }
2112    else
2113    {
2114       if ( bitsAllocated == 8 )
2115       {
2116          uint8_t smask8 = 255;
2117          uint8_t *deb = (uint8_t *)raw;
2118          for (i=0; i<rawSize; i++)      
2119          {
2120             *deb = smask8 - *deb;
2121             deb++;
2122          }
2123          return;
2124       }
2125       if ( bitsAllocated == 16 )
2126       {
2127          uint16_t smask16 = 65535;
2128          uint16_t *deb = (uint16_t *)raw;
2129          for (i=0; i<rawSize/2; i++)      
2130          {
2131             *deb = smask16 - *deb;
2132             deb++;
2133          }
2134          return;
2135       }
2136    }
2137 }
2138
2139 //-----------------------------------------------------------------------------
2140 /**
2141  * \brief   Prints the FileInternal + info on PixelReadConvertor
2142  * @param   os ostream we want to print in
2143  * @param indent (unused)
2144  */
2145 void FileHelper::Print(std::ostream &os, std::string const &)
2146 {
2147    FileInternal->SetPrintLevel(PrintLevel);
2148    FileInternal->Print(os);
2149
2150    if ( FileInternal->IsReadable() )
2151    {
2152       PixelReadConverter->SetPrintLevel(PrintLevel);
2153       PixelReadConverter->Print(os);
2154    }
2155 }
2156
2157 //-----------------------------------------------------------------------------
2158 } // end namespace gdcm
2159
2160
2161 /* Probabely something to be added to use Rescale Slope/Intercept
2162 Have a look at ITK code !
2163
2164 // Internal function to rescale pixel according to Rescale Slope/Intercept
2165 template<class TBuffer, class TSource>
2166 void RescaleFunction(TBuffer* buffer, TSource *source,
2167                      double slope, double intercept, size_t size)
2168 {
2169   size /= sizeof(TSource);
2170
2171   if (slope != 1.0 && intercept != 0.0)
2172     {
2173     // Duff's device.  Instead of this code:
2174     //
2175     //   for(unsigned int i=0; i<size; i++)
2176     //    {
2177     //    buffer[i] = (TBuffer)(source[i]*slope + intercept);
2178     //    }
2179     //
2180     // use Duff's device which exploits "fall through"
2181     register size_t n = (size + 7) / 8;
2182     switch ( size % 8)
2183       {
2184       case 0: do { *buffer++ = (TBuffer)((*source++)*slope + intercept);
2185       case 7:      *buffer++ = (TBuffer)((*source++)*slope + intercept);
2186       case 6:      *buffer++ = (TBuffer)((*source++)*slope + intercept);
2187       case 5:      *buffer++ = (TBuffer)((*source++)*slope + intercept);
2188       case 4:      *buffer++ = (TBuffer)((*source++)*slope + intercept);
2189       case 3:      *buffer++ = (TBuffer)((*source++)*slope + intercept);
2190       case 2:      *buffer++ = (TBuffer)((*source++)*slope + intercept);
2191       case 1:      *buffer++ = (TBuffer)((*source++)*slope + intercept);
2192                  }  while (--n > 0);
2193       }
2194     }
2195   else if (slope == 1.0 && intercept != 0.0)
2196     {
2197     // Duff's device.  Instead of this code:
2198     //
2199     //   for(unsigned int i=0; i<size; i++)
2200     //    {
2201     //    buffer[i] = (TBuffer)(source[i] + intercept);
2202     //    }
2203     //
2204     // use Duff's device which exploits "fall through"
2205     register size_t n = (size + 7) / 8;
2206     switch ( size % 8)
2207       {
2208       case 0: do { *buffer++ = (TBuffer)(*source++ + intercept);
2209       case 7:      *buffer++ = (TBuffer)(*source++ + intercept);
2210       case 6:      *buffer++ = (TBuffer)(*source++ + intercept);
2211       case 5:      *buffer++ = (TBuffer)(*source++ + intercept);
2212       case 4:      *buffer++ = (TBuffer)(*source++ + intercept);
2213       case 3:      *buffer++ = (TBuffer)(*source++ + intercept);
2214       case 2:      *buffer++ = (TBuffer)(*source++ + intercept);
2215       case 1:      *buffer++ = (TBuffer)(*source++ + intercept);
2216                  }  while (--n > 0);
2217       }
2218     }
2219   else if (slope != 1.0 && intercept == 0.0)
2220     {
2221     // Duff's device.  Instead of this code:
2222     //
2223     //   for(unsigned int i=0; i<size; i++)
2224     //    {
2225     //    buffer[i] = (TBuffer)(source[i]*slope);
2226     //    }
2227     //
2228     // use Duff's device which exploits "fall through"
2229     register size_t n = (size + 7) / 8;
2230     switch ( size % 8)
2231       {
2232       case 0: do { *buffer++ = (TBuffer)((*source++)*slope);
2233       case 7:      *buffer++ = (TBuffer)((*source++)*slope);
2234       case 6:      *buffer++ = (TBuffer)((*source++)*slope);
2235       case 5:      *buffer++ = (TBuffer)((*source++)*slope);
2236       case 4:      *buffer++ = (TBuffer)((*source++)*slope);
2237       case 3:      *buffer++ = (TBuffer)((*source++)*slope);
2238       case 2:      *buffer++ = (TBuffer)((*source++)*slope);
2239       case 1:      *buffer++ = (TBuffer)((*source++)*slope);
2240                  }  while (--n > 0);
2241       }
2242     }
2243   else
2244     {
2245     // Duff's device.  Instead of this code:
2246     //
2247     //   for(unsigned int i=0; i<size; i++)
2248     //    {
2249     //    buffer[i] = (TBuffer)(source[i]);
2250     //    }
2251     //
2252     // use Duff's device which exploits "fall through"
2253     register size_t n = (size + 7) / 8;
2254     switch ( size % 8)
2255       {
2256       case 0: do { *buffer++ = (TBuffer)(*source++);
2257       case 7:      *buffer++ = (TBuffer)(*source++);
2258       case 6:      *buffer++ = (TBuffer)(*source++);
2259       case 5:      *buffer++ = (TBuffer)(*source++);
2260       case 4:      *buffer++ = (TBuffer)(*source++);
2261       case 3:      *buffer++ = (TBuffer)(*source++);
2262       case 2:      *buffer++ = (TBuffer)(*source++);
2263       case 1:      *buffer++ = (TBuffer)(*source++);
2264                  }  while (--n > 0);
2265       }
2266    }   
2267 }
2268
2269
2270 template<class TSource>
2271 void RescaleFunction(ImageIOBase::IOComponentType bufferType,
2272                      void* buffer, TSource *source,
2273                      double slope, double intercept, size_t size)
2274 {
2275   switch (bufferType)
2276     {
2277     case ImageIOBase::UCHAR:
2278       RescaleFunction( (unsigned char *)buffer, source, slope, intercept, size);
2279       break;
2280     case ImageIOBase::CHAR:
2281       RescaleFunction( (char *)buffer, source, slope, intercept, size);
2282       break;
2283     case ImageIOBase::USHORT:
2284       RescaleFunction( (unsigned short *)buffer, source, slope, intercept,size);
2285       break;
2286     case ImageIOBase::SHORT:
2287       RescaleFunction( (short *)buffer, source, slope, intercept, size);
2288       break;
2289     case ImageIOBase::UINT:
2290       RescaleFunction( (unsigned int *)buffer, source, slope, intercept, size);
2291       break;
2292     case ImageIOBase::INT:
2293       RescaleFunction( (int *)buffer, source, slope, intercept, size);
2294       break;
2295     case ImageIOBase::FLOAT:
2296       RescaleFunction( (float *)buffer, source, slope, intercept, size);
2297       break;
2298     case ImageIOBase::DOUBLE:
2299       RescaleFunction( (double *)buffer, source, slope, intercept, size);
2300       break;
2301     default:
2302       ::itk::OStringStream message;
2303       message << "itk::ERROR: GDCMImageIO: Unknown component type : " << bufferType;
2304       ::itk::ExceptionObject e(__FILE__, __LINE__, message.str().c_str(),ITK_LOCATION);
2305       throw e;
2306     }
2307 }
2308 */