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