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