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