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