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