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