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