]> Creatis software - gdcm.git/blob - src/gdcmFileHelper.cxx
Trying to normalize JPEG Writing (not yet finished)
[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/27 16:14:47 $
8   Version:   $Revision: 1.122 $
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 pixelSpacing = FileInternal->GetEntryString(0x0028,0x0030);
1654    if ( pixelSpacing == GDCM_UNFOUND )
1655    {
1656       pixelSpacing = "1.0\\1.0";
1657        // if missing, Pixel Spacing forced to "1.0\1.0"
1658       CopyMandatoryEntry(0x0028,0x0030,pixelSpacing,"DS");
1659    }
1660    
1661    // 'Imager Pixel Spacing' : defaulted to 'Pixel Spacing'
1662    // --> This one is the *legal* one !
1663    if ( ContentType != USER_OWN_IMAGE)
1664    //  we write it only when we are *sure* the image comes from
1665    //         an imager (see also 0008,0x0064)
1666       CheckMandatoryEntry(0x0018,0x1164,pixelSpacing,"DS");
1667
1668 /*
1669 ///Exact meaning of RETired fields
1670
1671 // See page 73 of ACR-NEMA_300-1988.pdf !
1672
1673 // 0020,0020 : Patient Orientation :
1674 Patient direction of the first row and
1675 column of the images. The first entry id the direction of the raws, given by the
1676 direction of the last pixel in the first row from the first pixel in tha row.
1677 the second entry is the direction of the columns, given by the direction of the
1678 last pixel in the first column from the first pixel in that column.
1679 L : Left, F : Feet, A : Anterior, P : Posterior.
1680 Up to 3 letters can be used in combination to indicate oblique planes.
1681
1682 //0020,0030 Image Position (RET)
1683 x,y,z coordinates im mm of the first pixel in the image
1684
1685 // 0020,0035 Image Orientation (RET)
1686 Direction cosines of the R axis of the image system with respect to the
1687 equipment coordinate axes x,y,z, followed by direction cosines of the C axis of
1688 the image system with respect to the same axes
1689
1690 //0020,0050 Location
1691 An image location reference, standard for the modality (such as CT bed position),
1692 used to indicate position. Calculation of position for other purposes
1693 is only from (0020,0030) and (0020,0035)
1694 */
1695
1696 /*
1697 // if imagePositionPatient    not found, default it with imagePositionRet,    if any
1698 // if imageOrientationPatient not found, default it with imageOrientationRet, if any
1699
1700    std::string imagePositionRet        = FileInternal->GetEntryString(0x0020,0x0030);
1701    std::string imageOrientationRet     = FileInternal->GetEntryString(0x0020,0x0035);
1702    std::string imagePositionPatient    = FileInternal->GetEntryString(0x0020,0x0032);
1703    std::string imageOrientationPatient = FileInternal->GetEntryString(0x0020,0x0037);
1704
1705    if(  imagePositionPatient == GDCM_UNFOUND && imageOrientationPatient == GDCM_UNFOUND
1706      && imagePositionRet     != GDCM_UNFOUND && imageOrientationRet     != GDCM_UNFOUND)
1707    {
1708       CopyMandatoryEntry(0x0020, 0x0032,imagePositionRet,"DS");
1709       Archive->Push(0x0020,0x0030); 
1710       CopyMandatoryEntry(0x0020, 0x0037,imageOrientationRet,"DS");
1711       Archive->Push(0x0020,0x0035);
1712    }
1713 */
1714
1715    // Samples Per Pixel (type 1) : default to grayscale
1716    CheckMandatoryEntry(0x0028,0x0002,"1","US");
1717
1718    // --- Check UID-related Entries ---
1719  
1720    // At the end, not to overwrite the original ones,
1721    // needed by 'Referenced SOP Instance UID', 'Referenced SOP Class UID'
1722    // 'SOP Instance UID'  
1723    CopyMandatoryEntry(0x0008,0x0018,sop,"UI");
1724
1725    if ( ContentType == USER_OWN_IMAGE)
1726    {
1727       gdcmDebugMacro( "USER_OWN_IMAGE (2)");
1728        // Conversion Type.
1729        // Other possible values are :
1730        // See PS 3.3, Page 408
1731
1732        // DV = Digitized Video
1733        // DI = Digital Interface 
1734        // DF = Digitized Film
1735        // WSD = Workstation
1736        // SD = Scanned Document
1737        // SI = Scanned Image
1738        // DRW = Drawing
1739        // SYN = Synthetic Image
1740
1741       CheckMandatoryEntry(0x0008,0x0064,"SYN","CS"); // Why not?
1742    } 
1743 /*
1744    if ( ContentType == CREATED_IMAGE)
1745    {
1746    /// \todo : find a trick to pass the Media Storage SOP Instance UID of the images used to create the current image
1747    
1748    }
1749 */
1750
1751    // ---- The user will never have to take any action on the following ----
1752
1753    // new value for 'SOP Instance UID'
1754    //SetMandatoryEntry(0x0008,0x0018,Util::CreateUniqueUID());
1755
1756    // Instance Creation Date
1757    const std::string &date = Util::GetCurrentDate();
1758    CopyMandatoryEntry(0x0008,0x0012,date,"DA");
1759
1760    // Instance Creation Time
1761    const std::string &time = Util::GetCurrentTime();
1762    CopyMandatoryEntry(0x0008,0x0013,time,"TM");
1763
1764    // Study Date
1765    CheckMandatoryEntry(0x0008,0x0020,date,"DA");
1766    // Study Time
1767    CheckMandatoryEntry(0x0008,0x0030,time,"TM");
1768
1769    // Accession Number
1770    //CopyMandatoryEntry(0x0008,0x0050,"");
1771    CheckMandatoryEntry(0x0008,0x0050,"","SH");
1772    
1773
1774    // ----- Add Mandatory Entries if missing ---
1775    // Entries whose type is 1 are mandatory, with a mandatory value
1776    // Entries whose type is 1c are mandatory-inside-a-Sequence,
1777    //                          with a mandatory value
1778    // Entries whose type is 2 are mandatory, with an optional value
1779    // Entries whose type is 2c are mandatory-inside-a-Sequence,
1780    //                          with an optional value
1781    // Entries whose type is 3 are optional
1782
1783    // 'Study Instance UID'
1784    // Keep the value if exists
1785    // The user is allowed to create his own Study, 
1786    //          keeping the same 'Study Instance UID' for various images
1787    // The user may add images to a 'Manufacturer Study',
1788    //          adding new Series to an already existing Study 
1789    CheckMandatoryEntry(0x0020,0x000d,Util::CreateUniqueUID(),"UI");
1790
1791    // 'Serie Instance UID'
1792    // Keep the value if exists
1793    // The user is allowed to create his own Series, 
1794    // keeping the same 'Serie Instance UID' for various images
1795    // The user shouldn't add any image to a 'Manufacturer Serie'
1796    // but there is no way no to prevent him for doing that 
1797    CheckMandatoryEntry(0x0020,0x000e,Util::CreateUniqueUID(),"UI");
1798
1799    // Study ID
1800    CheckMandatoryEntry(0x0020,0x0010,"","SH");
1801
1802    // Series Number
1803    CheckMandatoryEntry(0x0020,0x0011,"","IS");
1804
1805    // Instance Number
1806    CheckMandatoryEntry(0x0020,0x0013,"","IS");
1807
1808    // Patient Orientation
1809    // Can be computed from (0020|0037) :  Image Orientation (Patient)
1810    GDCM_NAME_SPACE::Orientation *o = GDCM_NAME_SPACE::Orientation::New();
1811    std::string ori = o->GetOrientation ( FileInternal );
1812    o->Delete();
1813    if (ori != "\\" && ori != GDCM_UNFOUND)
1814       CheckMandatoryEntry(0x0020,0x0020,ori,"CS");
1815    else
1816       CheckMandatoryEntry(0x0020,0x0020,"","CS");
1817
1818    // Default Patient Position to HFS
1819    CheckMandatoryEntry(0x0018,0x5100,"HFS","CS");
1820
1821    // Modality : if missing we set it to 'OTher'
1822    CheckMandatoryEntry(0x0008,0x0060,"OT","CS");
1823
1824    // Manufacturer : if missing we set it to 'GDCM Factory'
1825    CheckMandatoryEntry(0x0008,0x0070,"GDCM Factory","LO");
1826
1827    // Institution Name : if missing we set it to 'GDCM Hospital'
1828    CheckMandatoryEntry(0x0008,0x0080,"GDCM Hospital","LO");
1829
1830    // Patient's Name : if missing, we set it to 'GDCM^Patient'
1831    CheckMandatoryEntry(0x0010,0x0010,"GDCM^Patient","PN");
1832
1833    // Patient ID : some clinical softwares *demand* it although it's a 'type 2' entry.
1834    CheckMandatoryEntry(0x0010,0x0020,"gdcm ID","LO");
1835
1836    // Patient's Birth Date : 'type 2' entry -> must exist, value not mandatory
1837    CheckMandatoryEntry(0x0010,0x0030,"","DA");
1838
1839    // Patient's Sex :'type 2' entry -> must exist, value not mandatory
1840    CheckMandatoryEntry(0x0010,0x0040,"","CS");
1841
1842    // Referring Physician's Name :'type 2' entry -> must exist, value not mandatory
1843    CheckMandatoryEntry(0x0008,0x0090,"","PN");
1844
1845  /*
1846    // Deal with element 0x0000 (group length) of each group.
1847    // First stage : get all the different Groups
1848
1849   GroupHT grHT;
1850   DocEntry *d = FileInternal->GetFirstEntry();
1851   while(d)
1852   {
1853     grHT[d->GetGroup()] = 0;
1854     d=FileInternal->GetNextEntry();
1855   }
1856   // Second stage : add the missing ones (if any)
1857   for (GroupHT::iterator it = grHT.begin(); it != grHT.end(); ++it)  
1858   {
1859       CheckMandatoryEntry(it->first, 0x0000, "0"); 
1860   }    
1861   // Third stage : update all 'zero level' groups length
1862 */
1863
1864
1865    if (PhotometricInterpretation == 1)
1866    {
1867    }
1868
1869
1870 void FileHelper::CheckMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr )
1871 {
1872    DataEntry *entry = FileInternal->GetDataEntry(group,elem);
1873    if ( !entry )
1874    {
1875       //entry = DataEntry::New(Global::GetDicts()->GetDefaultPubDict()->GetEntry(group,elem));
1876       entry = DataEntry::New(group,elem,vr);
1877       entry->SetString(value);
1878       Archive->Push(entry);
1879       entry->Delete();
1880    }    
1881 }
1882
1883 /// \todo : what is it used for ? (FileHelper::SetMandatoryEntry)
1884 void FileHelper::SetMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr)
1885 {
1886    //DataEntry *entry = DataEntry::New(Global::GetDicts()->GetDefaultPubDict()->GetEntry(group,elem));
1887    DataEntry *entry = DataEntry::New(group,elem,vr);
1888    entry->SetString(value);
1889    Archive->Push(entry);
1890    entry->Delete();
1891 }
1892
1893 void FileHelper::CopyMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr)
1894 {
1895    DataEntry *entry = CopyDataEntry(group,elem,vr);
1896    entry->SetString(value);
1897    Archive->Push(entry);
1898    entry->Delete();
1899 }
1900
1901 /**
1902  * \brief Restore in the File the initial group 0002
1903  */
1904 void FileHelper::RestoreWriteMandatory()
1905 {
1906    // group 0002 may be pushed out for ACR-NEMA writting purposes 
1907    Archive->Restore(0x0002,0x0000);
1908    Archive->Restore(0x0002,0x0001);
1909    Archive->Restore(0x0002,0x0002);
1910    Archive->Restore(0x0002,0x0003);
1911    Archive->Restore(0x0002,0x0010);
1912    Archive->Restore(0x0002,0x0012);
1913    Archive->Restore(0x0002,0x0013);
1914    Archive->Restore(0x0002,0x0016);
1915    Archive->Restore(0x0002,0x0100);
1916    Archive->Restore(0x0002,0x0102);
1917
1918    // FIXME : Check if none is missing !
1919    
1920    Archive->Restore(0x0008,0x0012);
1921    Archive->Restore(0x0008,0x0013);
1922    Archive->Restore(0x0008,0x0016);
1923    Archive->Restore(0x0008,0x0018);
1924    Archive->Restore(0x0008,0x0060);
1925    Archive->Restore(0x0008,0x0070);
1926    Archive->Restore(0x0008,0x0080);
1927    Archive->Restore(0x0008,0x0090);
1928    Archive->Restore(0x0008,0x2112);
1929
1930    Archive->Restore(0x0010,0x0010);
1931    Archive->Restore(0x0010,0x0030);
1932    Archive->Restore(0x0010,0x0040);
1933
1934    Archive->Restore(0x0020,0x000d);
1935    Archive->Restore(0x0020,0x000e);
1936 }
1937
1938 /**
1939  * \brief   CallStartMethod
1940  */
1941 void FileHelper::CallStartMethod()
1942 {
1943    Progress = 0.0f;
1944    Abort    = false;
1945    CommandManager::ExecuteCommand(this,CMD_STARTPROGRESS);
1946 }
1947
1948 /**
1949  * \brief   CallProgressMethod
1950  */
1951 void FileHelper::CallProgressMethod()
1952 {
1953    CommandManager::ExecuteCommand(this,CMD_PROGRESS);
1954 }
1955
1956 /**
1957  * \brief   CallEndMethod
1958  */
1959 void FileHelper::CallEndMethod()
1960 {
1961    Progress = 1.0f;
1962    CommandManager::ExecuteCommand(this,CMD_ENDPROGRESS);
1963 }
1964
1965 //-----------------------------------------------------------------------------
1966 // Private
1967 /**
1968  * \brief Factorization for various forms of constructors.
1969  */
1970 void FileHelper::Initialize()
1971 {
1972    UserFunction = 0;
1973    ContentType = USER_OWN_IMAGE;
1974
1975    WriteMode = WMODE_RAW;
1976    WriteType = ExplicitVR;
1977    
1978    PhotometricInterpretation = 2; // Black = 0
1979
1980    PixelReadConverter  = new PixelReadConvert;
1981    PixelWriteConverter = new PixelWriteConvert;
1982    Archive = new DocEntryArchive( FileInternal );
1983 }
1984
1985 /**
1986  * \brief Reads/[decompresses] the pixels, 
1987  *        *without* making RGB from Palette Colors 
1988  * @return the pixels area, whatever its type 
1989  *         (uint8_t is just for prototyping : feel free to Cast it) 
1990  */ 
1991 uint8_t *FileHelper::GetRaw()
1992 {
1993    PixelReadConverter->SetUserFunction( UserFunction );
1994
1995    uint8_t *raw = PixelReadConverter->GetRaw();
1996    if ( ! raw )
1997    {
1998       // The Raw image migth not be loaded yet:
1999       std::ifstream *fp = FileInternal->OpenFile();
2000       PixelReadConverter->ReadAndDecompressPixelData( fp );
2001       if ( fp ) 
2002          FileInternal->CloseFile();
2003
2004       raw = PixelReadConverter->GetRaw();
2005       if ( ! raw )
2006       {
2007          gdcmWarningMacro( "Read/decompress of pixel data apparently went wrong.");
2008          return 0;
2009       }
2010    }
2011    return raw;
2012 }
2013
2014 /**
2015  * \brief Deal with Grey levels i.e. re-arange them
2016  *        to have low values = dark, high values = bright
2017  */
2018 void FileHelper::ConvertFixGreyLevels(uint8_t *raw, size_t rawSize)
2019 {
2020    uint32_t i; // to please M$VC6
2021    int16_t j;
2022
2023    // Number of Bits Allocated for storing a Pixel is defaulted to 16
2024    // when absent from the file.
2025    int bitsAllocated = FileInternal->GetBitsAllocated();
2026    if ( bitsAllocated == 0 )
2027    {
2028       bitsAllocated = 16;
2029    }
2030
2031    else if (bitsAllocated > 8 && bitsAllocated < 16 && bitsAllocated != 12)
2032    {
2033       bitsAllocated = 16;
2034    }   
2035    // Number of "Bits Stored", defaulted to number of "Bits Allocated"
2036    // when absent from the file.
2037    int bitsStored = FileInternal->GetBitsStored();
2038    if ( bitsStored == 0 )
2039    {
2040       bitsStored = bitsAllocated;
2041    }
2042
2043    if (!FileInternal->IsSignedPixelData())
2044    {
2045       if ( bitsAllocated == 8 )
2046       {
2047          uint8_t *deb = (uint8_t *)raw;
2048          for (i=0; i<rawSize; i++)      
2049          {
2050             *deb = 255 - *deb;
2051             deb++;
2052          }
2053          return;
2054       }
2055
2056       if ( bitsAllocated == 16 )
2057       {
2058          uint16_t mask =1;
2059          for (j=0; j<bitsStored-1; j++)
2060          {
2061             mask = (mask << 1) +1; // will be fff when BitsStored=12
2062          }
2063
2064          uint16_t *deb = (uint16_t *)raw;
2065          for (i=0; i<rawSize/2; i++)      
2066          {
2067             *deb = mask - *deb;
2068             deb++;
2069          }
2070          return;
2071        }
2072    }
2073    else
2074    {
2075       if ( bitsAllocated == 8 )
2076       {
2077          uint8_t smask8 = 255;
2078          uint8_t *deb = (uint8_t *)raw;
2079          for (i=0; i<rawSize; i++)      
2080          {
2081             *deb = smask8 - *deb;
2082             deb++;
2083          }
2084          return;
2085       }
2086       if ( bitsAllocated == 16 )
2087       {
2088          uint16_t smask16 = 65535;
2089          uint16_t *deb = (uint16_t *)raw;
2090          for (i=0; i<rawSize/2; i++)      
2091          {
2092             *deb = smask16 - *deb;
2093             deb++;
2094          }
2095          return;
2096       }
2097    }
2098 }
2099
2100 //-----------------------------------------------------------------------------
2101 /**
2102  * \brief   Prints the FileInternal + info on PixelReadConvertor
2103  * @param   os ostream we want to print in
2104  * @param indent (unused)
2105  */
2106 void FileHelper::Print(std::ostream &os, std::string const &)
2107 {
2108    FileInternal->SetPrintLevel(PrintLevel);
2109    FileInternal->Print(os);
2110
2111    if ( FileInternal->IsReadable() )
2112    {
2113       PixelReadConverter->SetPrintLevel(PrintLevel);
2114       PixelReadConverter->Print(os);
2115    }
2116 }
2117
2118 //-----------------------------------------------------------------------------
2119 } // end namespace gdcm
2120
2121
2122 /* Probabely something to be added to use Rescale Slope/Intercept
2123 Have a look ,at ITK code !
2124
2125 // Internal function to rescale pixel according to Rescale Slope/Intercept
2126 template<class TBuffer, class TSource>
2127 void RescaleFunction(TBuffer* buffer, TSource *source,
2128                      double slope, double intercept, size_t size)
2129 {
2130   size /= sizeof(TSource);
2131
2132   if (slope != 1.0 && intercept != 0.0)
2133     {
2134     // Duff's device.  Instead of this code:
2135     //
2136     //   for(unsigned int i=0; i<size; i++)
2137     //    {
2138     //    buffer[i] = (TBuffer)(source[i]*slope + intercept);
2139     //    }
2140     //
2141     // use Duff's device which exploits "fall through"
2142     register size_t n = (size + 7) / 8;
2143     switch ( size % 8)
2144       {
2145       case 0: do { *buffer++ = (TBuffer)((*source++)*slope + intercept);
2146       case 7:      *buffer++ = (TBuffer)((*source++)*slope + intercept);
2147       case 6:      *buffer++ = (TBuffer)((*source++)*slope + intercept);
2148       case 5:      *buffer++ = (TBuffer)((*source++)*slope + intercept);
2149       case 4:      *buffer++ = (TBuffer)((*source++)*slope + intercept);
2150       case 3:      *buffer++ = (TBuffer)((*source++)*slope + intercept);
2151       case 2:      *buffer++ = (TBuffer)((*source++)*slope + intercept);
2152       case 1:      *buffer++ = (TBuffer)((*source++)*slope + intercept);
2153                  }  while (--n > 0);
2154       }
2155     }
2156   else if (slope == 1.0 && intercept != 0.0)
2157     {
2158     // Duff's device.  Instead of this code:
2159     //
2160     //   for(unsigned int i=0; i<size; i++)
2161     //    {
2162     //    buffer[i] = (TBuffer)(source[i] + intercept);
2163     //    }
2164     //
2165     // use Duff's device which exploits "fall through"
2166     register size_t n = (size + 7) / 8;
2167     switch ( size % 8)
2168       {
2169       case 0: do { *buffer++ = (TBuffer)(*source++ + intercept);
2170       case 7:      *buffer++ = (TBuffer)(*source++ + intercept);
2171       case 6:      *buffer++ = (TBuffer)(*source++ + intercept);
2172       case 5:      *buffer++ = (TBuffer)(*source++ + intercept);
2173       case 4:      *buffer++ = (TBuffer)(*source++ + intercept);
2174       case 3:      *buffer++ = (TBuffer)(*source++ + intercept);
2175       case 2:      *buffer++ = (TBuffer)(*source++ + intercept);
2176       case 1:      *buffer++ = (TBuffer)(*source++ + intercept);
2177                  }  while (--n > 0);
2178       }
2179     }
2180   else if (slope != 1.0 && intercept == 0.0)
2181     {
2182     // Duff's device.  Instead of this code:
2183     //
2184     //   for(unsigned int i=0; i<size; i++)
2185     //    {
2186     //    buffer[i] = (TBuffer)(source[i]*slope);
2187     //    }
2188     //
2189     // use Duff's device which exploits "fall through"
2190     register size_t n = (size + 7) / 8;
2191     switch ( size % 8)
2192       {
2193       case 0: do { *buffer++ = (TBuffer)((*source++)*slope);
2194       case 7:      *buffer++ = (TBuffer)((*source++)*slope);
2195       case 6:      *buffer++ = (TBuffer)((*source++)*slope);
2196       case 5:      *buffer++ = (TBuffer)((*source++)*slope);
2197       case 4:      *buffer++ = (TBuffer)((*source++)*slope);
2198       case 3:      *buffer++ = (TBuffer)((*source++)*slope);
2199       case 2:      *buffer++ = (TBuffer)((*source++)*slope);
2200       case 1:      *buffer++ = (TBuffer)((*source++)*slope);
2201                  }  while (--n > 0);
2202       }
2203     }
2204   else
2205     {
2206     // Duff's device.  Instead of this code:
2207     //
2208     //   for(unsigned int i=0; i<size; i++)
2209     //    {
2210     //    buffer[i] = (TBuffer)(source[i]);
2211     //    }
2212     //
2213     // use Duff's device which exploits "fall through"
2214     register size_t n = (size + 7) / 8;
2215     switch ( size % 8)
2216       {
2217       case 0: do { *buffer++ = (TBuffer)(*source++);
2218       case 7:      *buffer++ = (TBuffer)(*source++);
2219       case 6:      *buffer++ = (TBuffer)(*source++);
2220       case 5:      *buffer++ = (TBuffer)(*source++);
2221       case 4:      *buffer++ = (TBuffer)(*source++);
2222       case 3:      *buffer++ = (TBuffer)(*source++);
2223       case 2:      *buffer++ = (TBuffer)(*source++);
2224       case 1:      *buffer++ = (TBuffer)(*source++);
2225                  }  while (--n > 0);
2226       }
2227    }   
2228 }
2229
2230
2231 template<class TSource>
2232 void RescaleFunction(ImageIOBase::IOComponentType bufferType,
2233                      void* buffer, TSource *source,
2234                      double slope, double intercept, size_t size)
2235 {
2236   switch (bufferType)
2237     {
2238     case ImageIOBase::UCHAR:
2239       RescaleFunction( (unsigned char *)buffer, source, slope, intercept, size);
2240       break;
2241     case ImageIOBase::CHAR:
2242       RescaleFunction( (char *)buffer, source, slope, intercept, size);
2243       break;
2244     case ImageIOBase::USHORT:
2245       RescaleFunction( (unsigned short *)buffer, source, slope, intercept,size);
2246       break;
2247     case ImageIOBase::SHORT:
2248       RescaleFunction( (short *)buffer, source, slope, intercept, size);
2249       break;
2250     case ImageIOBase::UINT:
2251       RescaleFunction( (unsigned int *)buffer, source, slope, intercept, size);
2252       break;
2253     case ImageIOBase::INT:
2254       RescaleFunction( (int *)buffer, source, slope, intercept, size);
2255       break;
2256     case ImageIOBase::FLOAT:
2257       RescaleFunction( (float *)buffer, source, slope, intercept, size);
2258       break;
2259     case ImageIOBase::DOUBLE:
2260       RescaleFunction( (double *)buffer, source, slope, intercept, size);
2261       break;
2262     default:
2263       ::itk::OStringStream message;
2264       message << "itk::ERROR: GDCMImageIO: Unknown component type : " << bufferType;
2265       ::itk::ExceptionObject e(__FILE__, __LINE__, message.str().c_str(),ITK_LOCATION);
2266       throw e;
2267     }
2268 }
2269 */
2270
2271       ::itk::ExceptionObject e(__FILE__, __LINE__, message.str().c_str(),ITK_LOCATION);
2272       throw e;
2273     }
2274 }