]> Creatis software - gdcm.git/blob - src/gdcmFileHelper.cxx
Fix comments
[gdcm.git] / src / gdcmFileHelper.cxx
1 /*=========================================================================
2                                                                                 
3   Program:   gdcm
4   Module:    $RCSfile: gdcmFileHelper.cxx,v $
5   Language:  C++
6
7   Date:      $Date: 2007/07/05 10:53:48 $
8   Version:   $Revision: 1.116 $
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  * \brief Verifies the size of the user given PixelData
822  * @return true if check is successfull
823  */
824 bool FileHelper::CheckWriteIntegrity()
825 {
826    if ( PixelWriteConverter->GetUserData() )
827    {
828       int numberBitsAllocated = FileInternal->GetBitsAllocated();
829       if ( numberBitsAllocated == 0 || numberBitsAllocated == 12 )
830       {
831          gdcmWarningMacro( "numberBitsAllocated changed from " 
832                           << numberBitsAllocated << " to 16 " 
833                           << " for consistency purpose" );
834          numberBitsAllocated = 16;
835       }
836
837       size_t decSize = FileInternal->GetXSize()
838                      * FileInternal->GetYSize() 
839                      * FileInternal->GetZSize()
840                      * FileInternal->GetTSize()     
841                      * FileInternal->GetSamplesPerPixel()
842                      * ( numberBitsAllocated / 8 );
843       size_t rgbSize = decSize;
844       if ( FileInternal->HasLUT() )
845          rgbSize = decSize * 3;
846
847       switch(WriteMode)
848       {
849          case WMODE_RAW :
850             if ( decSize!=PixelWriteConverter->GetUserDataSize() )
851             {
852                gdcmWarningMacro( "Data size (Raw) is incorrect. Should be " 
853                            << decSize << " / Found :" 
854                            << PixelWriteConverter->GetUserDataSize() );
855                return false;
856             }
857             break;
858          case WMODE_RGB :
859             if ( rgbSize!=PixelWriteConverter->GetUserDataSize() )
860             {
861                gdcmWarningMacro( "Data size (RGB) is incorrect. Should be " 
862                           << decSize << " / Found " 
863                           << PixelWriteConverter->GetUserDataSize() );
864                return false;
865             }
866             break;
867       }
868    }
869
870    return true;
871 }
872
873 /**
874  * \brief Updates the File to write RAW data (as opposed to RGB data)
875  *       (modifies, when necessary, photochromatic interpretation, 
876  *       bits allocated, Pixels element VR)
877  *       WARNING : if SetPhotometricInterpretationToMonochrome1() was called
878  *                 before Pixel Elements if modified :-( 
879  */ 
880 void FileHelper::SetWriteToRaw()
881 {
882    if ( FileInternal->GetNumberOfScalarComponents() == 3 
883     && !FileInternal->HasLUT() )
884    {
885       SetWriteToRGB();
886    } 
887    else
888    {
889       // 0x0028,0x0004 : Photometric Interpretation
890       DataEntry *photInt = CopyDataEntry(0x0028,0x0004,"CS");
891       if (FileInternal->HasLUT() )
892       {
893          photInt->SetString("PALETTE COLOR ");
894       }
895       else
896       {     
897          if (GetPhotometricInterpretation() == 2)
898             photInt->SetString("MONOCHROME2 ");  // 0 = Black
899          else
900             photInt->SetString("MONOCHROME1 ");  // 0 = White !
901       }
902     
903       PixelWriteConverter->SetReadData(PixelReadConverter->GetRaw(),
904                                        PixelReadConverter->GetRawSize());
905
906       std::string vr = "OB";
907       if ( FileInternal->GetBitsAllocated()>8 )
908          vr = "OW";
909       if ( FileInternal->GetBitsAllocated()==24 ) // For RGB ACR files 
910          vr = "OB";
911        // For non RAW data. Mainly JPEG
912       if( WriteType == JPEG || WriteType == JPEG2000)
913       {
914          vr = "OW";
915       }
916       
917       DataEntry *pixel = 
918          CopyDataEntry(GetFile()->GetGrPixel(),GetFile()->GetNumPixel(),vr);
919       pixel->SetFlag(DataEntry::FLAG_PIXELDATA);
920       pixel->SetBinArea(PixelWriteConverter->GetData(),false);
921       pixel->SetLength(PixelWriteConverter->GetDataSize());
922      
923       if (!FileInternal->HasLUT() && GetPhotometricInterpretation() == 1)
924       {
925          ConvertFixGreyLevels( pixel->GetBinArea(), pixel->GetLength() );  
926       }      
927
928       Archive->Push(photInt);
929       Archive->Push(pixel);
930
931       photInt->Delete();
932       pixel->Delete();
933    }
934 }
935
936 /**
937  * \brief Updates the File to write RGB data (as opposed to RAW data)
938  *       (modifies, when necessary, photochromatic interpretation, 
939  *       samples per pixel, Planar configuration, 
940  *       bits allocated, bits stored, high bit -ACR 24 bits-
941  *       Pixels element VR, pushes out the LUT, )
942  */ 
943 void FileHelper::SetWriteToRGB()
944 {
945    if ( FileInternal->GetNumberOfScalarComponents()==3 )
946    {
947       PixelReadConverter->BuildRGBImage();
948       
949       DataEntry *spp = CopyDataEntry(0x0028,0x0002,"US");
950       spp->SetString("3 ");
951
952       DataEntry *planConfig = CopyDataEntry(0x0028,0x0006,"US");
953       planConfig->SetString("0 ");
954
955       DataEntry *photInt = CopyDataEntry(0x0028,0x0004,"CS");
956       photInt->SetString("RGB ");
957
958       if ( PixelReadConverter->GetRGB() )
959       {
960          PixelWriteConverter->SetReadData(PixelReadConverter->GetRGB(),
961                                           PixelReadConverter->GetRGBSize());
962       }
963       else // Raw data
964       {
965          PixelWriteConverter->SetReadData(PixelReadConverter->GetRaw(),
966                                           PixelReadConverter->GetRawSize());
967       }
968
969       std::string vr = "OB";
970       if ( FileInternal->GetBitsAllocated()>8 )
971          vr = "OW";
972       if ( FileInternal->GetBitsAllocated()==24 ) // For RGB ACR files 
973          vr = "OB";
974       DataEntry *pixel = 
975          CopyDataEntry(GetFile()->GetGrPixel(),GetFile()->GetNumPixel(),vr);
976       pixel->SetFlag(DataEntry::FLAG_PIXELDATA);
977       pixel->SetBinArea(PixelWriteConverter->GetData(),false);
978       pixel->SetLength(PixelWriteConverter->GetDataSize());
979
980       Archive->Push(spp);
981       Archive->Push(planConfig);
982       Archive->Push(photInt);
983       Archive->Push(pixel);
984
985       spp->Delete();
986       planConfig->Delete();
987       photInt->Delete();
988       pixel->Delete();
989
990       // Remove any LUT
991       Archive->Push(0x0028,0x1101);
992       Archive->Push(0x0028,0x1102);
993       Archive->Push(0x0028,0x1103);
994       Archive->Push(0x0028,0x1201);
995       Archive->Push(0x0028,0x1202);
996       Archive->Push(0x0028,0x1203);
997
998       // push out Palette Color Lookup Table UID, if any
999       Archive->Push(0x0028,0x1199);
1000
1001       // For old '24 Bits' ACR-NEMA
1002       // Thus, we have a RGB image and the bits allocated = 24 and 
1003       // samples per pixels = 1 (in the read file)
1004       if ( FileInternal->GetBitsAllocated()==24 ) 
1005       {
1006          DataEntry *bitsAlloc = CopyDataEntry(0x0028,0x0100,"US");
1007          bitsAlloc->SetString("8 ");
1008
1009          DataEntry *bitsStored = CopyDataEntry(0x0028,0x0101,"US");
1010          bitsStored->SetString("8 ");
1011
1012          DataEntry *highBit = CopyDataEntry(0x0028,0x0102,"US");
1013          highBit->SetString("7 ");
1014
1015          Archive->Push(bitsAlloc);
1016          Archive->Push(bitsStored);
1017          Archive->Push(highBit);
1018
1019          bitsAlloc->Delete();
1020          bitsStored->Delete();
1021          highBit->Delete();
1022       }
1023    }
1024    else
1025    {
1026       SetWriteToRaw();
1027    }
1028 }
1029
1030 /**
1031  * \brief Restore the File write mode  
1032  */ 
1033 void FileHelper::RestoreWrite()
1034 {
1035
1036    Archive->Restore(0x0028,0x0002);
1037    Archive->Restore(0x0028,0x0004);
1038    
1039    Archive->Restore(0x0028,0x0006);
1040    Archive->Restore(GetFile()->GetGrPixel(),GetFile()->GetNumPixel());
1041
1042    // For old ACR-NEMA (24 bits problem)
1043    Archive->Restore(0x0028,0x0100);
1044    Archive->Restore(0x0028,0x0101);
1045    Archive->Restore(0x0028,0x0102);
1046
1047    // For the LUT
1048    Archive->Restore(0x0028,0x1101);
1049    Archive->Restore(0x0028,0x1102);
1050    Archive->Restore(0x0028,0x1103);
1051    Archive->Restore(0x0028,0x1201);
1052    Archive->Restore(0x0028,0x1202);
1053    Archive->Restore(0x0028,0x1203);
1054
1055    // For the Palette Color Lookup Table UID
1056    Archive->Restore(0x0028,0x1203); 
1057
1058    // group 0002 may be pushed out for ACR-NEMA writting purposes 
1059    Archive->Restore(0x0002,0x0000);
1060    Archive->Restore(0x0002,0x0001);
1061    Archive->Restore(0x0002,0x0002);
1062    Archive->Restore(0x0002,0x0003);
1063    Archive->Restore(0x0002,0x0010);
1064    Archive->Restore(0x0002,0x0012);
1065    Archive->Restore(0x0002,0x0013);
1066    Archive->Restore(0x0002,0x0016);
1067    Archive->Restore(0x0002,0x0100);
1068    Archive->Restore(0x0002,0x0102);
1069
1070 }
1071
1072 /**
1073  * \brief Pushes out the whole group 0002
1074  *        FIXME : better, set a flag to tell the writer not to write it ...
1075  *        FIXME : method should probably have an other name !
1076  *                SetWriteFileTypeToACR is NOT opposed to 
1077  *                SetWriteFileTypeToExplicitVR and SetWriteFileTypeToImplicitVR
1078  */ 
1079 void FileHelper::SetWriteFileTypeToACR()
1080 {
1081    Archive->Push(0x0002,0x0000);
1082    Archive->Push(0x0002,0x0001);
1083    Archive->Push(0x0002,0x0002);
1084    Archive->Push(0x0002,0x0003);
1085    Archive->Push(0x0002,0x0010);
1086    Archive->Push(0x0002,0x0012);
1087    Archive->Push(0x0002,0x0013);
1088    Archive->Push(0x0002,0x0016);
1089    Archive->Push(0x0002,0x0100);
1090    Archive->Push(0x0002,0x0102);
1091 }
1092
1093  /**
1094   * \brief Sets in the File the TransferSyntax to 'JPEG2000'
1095   */
1096 void FileHelper::SetWriteFileTypeToJPEG2000()
1097 {
1098    std::string ts = Util::DicomString(
1099    Global::GetTS()->GetSpecialTransferSyntax(TS::JPEG2000Lossless) );
1100    
1101    DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1102    tss->SetString(ts);
1103
1104    Archive->Push(tss);
1105    tss->Delete();   
1106 }
1107
1108 /**
1109  * \brief Sets in the File the TransferSyntax to 'JPEG'
1110  */ 
1111 void FileHelper::SetWriteFileTypeToJPEG()
1112 {
1113    std::string ts = Util::DicomString( 
1114       Global::GetTS()->GetSpecialTransferSyntax(TS::JPEGBaselineProcess1) );
1115
1116    DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1117    tss->SetString(ts);
1118
1119    Archive->Push(tss);
1120    tss->Delete();
1121 }
1122
1123 /**
1124  * \brief Sets in the File the TransferSyntax to 'Explicit VR Little Endian"   
1125  */ 
1126 void FileHelper::SetWriteFileTypeToExplicitVR()
1127 {
1128    std::string ts = Util::DicomString( 
1129       Global::GetTS()->GetSpecialTransferSyntax(TS::ExplicitVRLittleEndian) );
1130
1131    DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1132    tss->SetString(ts);
1133    Archive->Push(tss);
1134    tss->Delete();
1135 }
1136
1137 /**
1138  * \brief Sets in the File the TransferSyntax to 'Implicit VR Little Endian"   
1139  */ 
1140 void FileHelper::SetWriteFileTypeToImplicitVR()
1141 {
1142    std::string ts = Util::DicomString(
1143       Global::GetTS()->GetSpecialTransferSyntax(TS::ImplicitVRLittleEndian) );
1144
1145    DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1146    tss->SetString(ts);
1147    Archive->Push(tss);
1148    tss->Delete();
1149 }
1150
1151 /**
1152  * \brief Set the Write not to Libido format
1153  */ 
1154 void FileHelper::SetWriteToLibido()
1155 {
1156    DataEntry *oldRow = FileInternal->GetDataEntry(0x0028, 0x0010);
1157    DataEntry *oldCol = FileInternal->GetDataEntry(0x0028, 0x0011);
1158    
1159    if ( oldRow && oldCol )
1160    {
1161       std::string rows, columns; 
1162
1163       //DataEntry *newRow=DataEntry::New(oldRow->GetDictEntry());
1164       //DataEntry *newCol=DataEntry::New(oldCol->GetDictEntry());
1165       
1166       DataEntry *newRow=DataEntry::New(0x0028, 0x0010, "US");
1167       DataEntry *newCol=DataEntry::New(0x0028, 0x0011, "US");
1168       
1169       newRow->Copy(oldCol);
1170       newCol->Copy(oldRow);
1171
1172       newRow->SetString(oldCol->GetString());
1173       newCol->SetString(oldRow->GetString());
1174
1175       Archive->Push(newRow);
1176       Archive->Push(newCol);
1177
1178       newRow->Delete();
1179       newCol->Delete();
1180    }
1181
1182    DataEntry *libidoCode = CopyDataEntry(0x0008,0x0010,"LO");
1183    libidoCode->SetString("ACRNEMA_LIBIDO_1.1");
1184    Archive->Push(libidoCode);
1185    libidoCode->Delete();
1186 }
1187
1188 /**
1189  * \brief Set the Write not to No Libido format
1190  */ 
1191 void FileHelper::SetWriteToNoLibido()
1192 {
1193    DataEntry *recCode = FileInternal->GetDataEntry(0x0008,0x0010);
1194    if ( recCode )
1195    {
1196       if ( recCode->GetString() == "ACRNEMA_LIBIDO_1.1" )
1197       {
1198          DataEntry *libidoCode = CopyDataEntry(0x0008,0x0010,"LO");
1199          libidoCode->SetString("");
1200          Archive->Push(libidoCode);
1201          libidoCode->Delete();
1202       }
1203    }
1204 }
1205
1206 /**
1207  * \brief Restore the Write format
1208  */ 
1209 void FileHelper::RestoreWriteOfLibido()
1210 {
1211    Archive->Restore(0x0028,0x0010);
1212    Archive->Restore(0x0028,0x0011);
1213    Archive->Restore(0x0008,0x0010);
1214
1215    // Restore 'LibIDO-special' entries, if any
1216    Archive->Restore(0x0028,0x0015);
1217    Archive->Restore(0x0028,0x0016);
1218    Archive->Restore(0x0028,0x0017);
1219    Archive->Restore(0x0028,0x00199);
1220 }
1221
1222 /**
1223  * \brief   Duplicates a DataEntry or creates it.
1224  * @param   group   Group number of the Entry 
1225  * @param   elem  Element number of the Entry
1226  * @param   vr  Value Representation of the Entry
1227  * \return  pointer to the new Bin Entry (NULL when creation failed).
1228  */ 
1229 DataEntry *FileHelper::CopyDataEntry(uint16_t group, uint16_t elem,
1230                                    const VRKey &vr)
1231 {
1232    DocEntry *oldE = FileInternal->GetDocEntry(group, elem);
1233    DataEntry *newE;
1234
1235    if ( oldE && vr != GDCM_VRUNKNOWN ) 
1236       if ( oldE->GetVR() != vr )
1237          oldE = NULL;
1238
1239    if ( oldE )
1240    {
1241       //newE = DataEntry::New(oldE->GetDictEntry());
1242       newE = DataEntry::New(group, elem, vr);
1243       newE->Copy(oldE);
1244    }
1245    else
1246    {
1247       newE = GetFile()->NewDataEntry(group, elem, vr);
1248    }
1249
1250    return newE;
1251 }
1252
1253 /**
1254  * \brief   This method is called automatically, just before writting
1255  *         in order to produce a 'True Dicom V3' image.
1256  *
1257  *         We cannot know *how* the user made the File :
1258  *         (reading an old ACR-NEMA file or a not very clean DICOM file ...) 
1259  *          Just before writting :
1260  *             - we check the Entries
1261  *             - we create the mandatory entries if they are missing
1262  *             - we modify the values if necessary
1263  *             - we push the sensitive entries to the Archive
1264  *          The writing process will restore the entries as they where before 
1265  *          entering FileHelper::CheckMandatoryElements, so the user will always
1266  *          see the entries just as they were before he decided to write.
1267  *
1268  * \note
1269  *       -  Entries whose type is 1 are mandatory, with a mandatory value
1270  *       -  Entries whose type is 1c are mandatory-inside-a-Sequence,
1271  *                             with a mandatory value
1272  *       -  Entries whose type is 2 are mandatory, with an optional value
1273  *       -  Entries whose type is 2c are mandatory-inside-a-Sequence,
1274  *                             with an optional value
1275  *       -  Entries whose type is 3 are optional
1276  * 
1277  * \todo 
1278  *         - warn the user if we had to add some entries :
1279  *         even if a mandatory entry is missing, we add it, with a default value
1280  *         (we don't want to give up the writting process if user forgot to
1281  *         specify Lena's Patient ID, for instance ...)
1282  *         - read the whole PS 3.3 Part of DICOM  (890 pages)
1283  *         and write a *full* checker (probably one method per Modality ...)
1284  *         Any contribution is welcome. 
1285  *         - write a user callable full checker, to allow post reading
1286  *         and/or pre writting image consistency check.           
1287  */ 
1288
1289 /* -------------------------------------------------------------------------------------
1290 To be moved to User's guide / WIKI  ?
1291
1292 We have to deal with 4 *very* different cases :
1293 -1) user created ex nihilo his own image and wants to write it as a Dicom image.
1294     USER_OWN_IMAGE
1295 -2) user modified the pixels of an existing image.
1296    FILTERED_IMAGE
1297 -3) user created a new image, using a set of existing images (eg MIP, MPR, cartography image)
1298    CREATED_IMAGE
1299 -4) user modified/added some tags *without processing* the pixels (anonymization..
1300    UNMODIFIED_PIXELS_IMAGE
1301 -Probabely some more to be added  
1302  
1303 gdcm::FileHelper::CheckMandatoryElements() deals automatically with these cases.
1304
1305 1)2)3)4)
1306 0008 0012 Instance Creation Date
1307 0008 0013 Instance Creation Time
1308 0008 0018 SOP Instance UID
1309 are *always* created with the current values; user has *no* possible intervention on
1310 them.
1311
1312 'Serie Instance UID'(0x0020,0x000e)
1313 'Study Instance UID'(0x0020,0x000d) are kept as is if already exist,
1314                                     created  if it doesn't.
1315  The user is allowed to create his own Series/Studies, 
1316      keeping the same 'Serie Instance UID' / 'Study Instance UID' for various images
1317  Warning :     
1318  The user shouldn't add any image to a 'Manufacturer Serie'
1319      but there is no way no to allow him to do that
1320      
1321  None of the 'shadow elements' are droped out.
1322      
1323
1324 1)
1325 'Conversion Type (0x0008,0x0064) is forced to 'SYN' (Synthetic Image).
1326  
1327 1)3)
1328 'Media Storage SOP Class UID' (0x0002,0x0002)
1329 'SOP Class UID'               (0x0008,0x0016) are set to 
1330                                                [Secondary Capture Image Storage]
1331 'Image Type'                  (0x0008,0x0008) is forced to  "DERIVED\PRIMARY"
1332 Conversion Type               (0x0008,0x0064) is forced to 'SYN' (Synthetic Image)
1333
1334 2)4)
1335 If 'SOP Class UID' exists in the native image  ('true DICOM' image)
1336     we create the 'Source Image Sequence' SeqEntry (0x0008, 0x2112)    
1337     --> 'Referenced SOP Class UID' (0x0008, 0x1150)
1338          whose value is the original 'SOP Class UID'
1339     --> 'Referenced SOP Instance UID' (0x0008, 0x1155)
1340          whose value is the original 'SOP Class UID'
1341
1342 3) TODO : find a trick to allow user to pass to the writter the list of the Dicom images 
1343           or the Series, (or the Study ?) he used to created his image 
1344           (MIP, MPR, cartography image, ...)
1345            These info should be stored (?)
1346           0008 1110 SQ 1 Referenced Study Sequence
1347           0008 1115 SQ 1 Referenced Series Sequence
1348           0008 1140 SQ 1 Referenced Image Sequence
1349        
1350 4) When user *knows* he didn't modified the pixels, we keep some informations unchanged :
1351 'Media Storage SOP Class UID' (0x0002,0x0002)
1352 'SOP Class UID'               (0x0008,0x0016)
1353 'Image Type'                  (0x0008,0x0008)
1354 'Conversion Type'             (0x0008,0x0064)
1355
1356
1357 Bellow follows the full description (hope so !) of the consistency checks performed 
1358 by gdcm::FileHelper::CheckMandatoryElements()
1359
1360
1361 -->'Media Storage SOP Class UID' (0x0002,0x0002)
1362 -->'SOP Class UID'               (0x0008,0x0016) are defaulted to 
1363                                                [Secondary Capture Image Storage]
1364 --> 'Image Type'  (0x0008,0x0008)
1365      is forced to  "DERIVED\PRIMARY"
1366      (The written image is no longer an 'ORIGINAL' one)
1367   Except if user knows he didn't modify the image (e.g. : he just anonymized the file)
1368    
1369  -->  Conversion Type (0x0008,0x0064)
1370      is defaulted to 'SYN' (Synthetic Image)
1371   when *he* knows he created his own image ex nihilo
1372             
1373 --> 'Modality' (0x0008,0x0060)   
1374     is defaulted to "OT" (other) if missing.   
1375     (a fully user created image belongs to *no* modality)
1376       
1377 --> 'Media Storage SOP Instance UID' (0x0002,0x0003)
1378 --> 'Implementation Class UID'       (0x0002,0x0012)
1379     are automatically generated; no user intervention possible
1380
1381 --> 'Serie Instance UID'(0x0020,0x000e)
1382 --> 'Study Instance UID'(0x0020,0x000d) are kept as is if already exist
1383                                              created  if it doesn't.
1384      The user is allowed to create his own Series/Studies, 
1385      keeping the same 'Serie Instance UID' / 'Study Instance UID' 
1386      for various images
1387      Warning :     
1388      The user shouldn't add any image to a 'Manufacturer Serie'
1389      but there is no way no to allowed him to do that 
1390              
1391 --> If 'SOP Class UID' exists in the native image  ('true DICOM' image)
1392     we create the 'Source Image Sequence' SeqEntry (0x0008, 0x2112)
1393     
1394     --> 'Referenced SOP Class UID' (0x0008, 0x1150)
1395          whose value is the original 'SOP Class UID'
1396     --> 'Referenced SOP Instance UID' (0x0008, 0x1155)
1397          whose value is the original 'SOP Class UID'
1398     
1399 --> Bits Stored, Bits Allocated, Hight Bit Position are checked for consistency
1400 --> Pixel Spacing     (0x0028,0x0030) is defaulted to "1.0\1.0"
1401 --> Samples Per Pixel (0x0028,0x0002) is defaulted to 1 (grayscale)
1402
1403 --> Imager Pixel Spacing (0x0018,0x1164) : defaulted to Pixel Spacing value
1404
1405 --> Instance Creation Date, Instance Creation Time are forced to current Date and Time
1406
1407 --> Study Date, Study Time are defaulted to current Date and Time
1408    (they remain unchanged if they exist)
1409
1410 --> Patient Orientation : (0x0020,0x0020), if not present, is deduced from 
1411     Image Orientation (Patient) : (0020|0037) or from
1412     Image Orientation (RET)     : (0020 0035)
1413    
1414 --> Study ID, Series Number, Instance Number, Patient Orientation (Type 2)
1415     are created, with empty value if there are missing.
1416
1417 --> Manufacturer, Institution Name, Patient's Name, (Type 2)
1418     are defaulted with a 'gdcm' value.
1419     
1420 --> Patient ID, Patient's Birth Date, Patient's Sex, (Type 2)
1421 --> Referring Physician's Name  (Type 2)
1422     are created, with empty value if there are missing.
1423
1424  -------------------------------------------------------------------------------------*/
1425
1426 void FileHelper::CheckMandatoryElements()
1427 {
1428    std::string sop =  Util::CreateUniqueUID();
1429
1430    // --------------------- For Meta Elements ---------------------
1431    // just to remember : 'official' 0002 group
1432    if ( WriteType != ACR && WriteType != ACR_LIBIDO )
1433    {
1434      // Group 000002 (Meta Elements) already pushed out
1435   
1436    //0002 0000 UL 1 Meta Group Length
1437    //0002 0001 OB 1 File Meta Information Version
1438    //0002 0002 UI 1 Media Stored SOP Class UID
1439    //0002 0003 UI 1 Media Stored SOP Instance UID
1440    //0002 0010 UI 1 Transfer Syntax UID
1441    //0002 0012 UI 1 Implementation Class UID
1442    //0002 0013 SH 1 Implementation Version Name
1443    //0002 0016 AE 1 Source Application Entity Title
1444    //0002 0100 UI 1 Private Information Creator
1445    //0002 0102 OB 1 Private Information
1446  
1447    // Push out 'ACR-NEMA-special' entries, if any
1448       Archive->Push(0x0008,0x0001); // Length to End
1449       Archive->Push(0x0008,0x0010); // Recognition Code
1450       Archive->Push(0x0028,0x0005); // Image Dimension  
1451   
1452    // Create them if not found
1453    // Always modify the value
1454    // Push the entries to the archive.
1455       CopyMandatoryEntry(0x0002,0x0000,"0","UL");
1456
1457       DataEntry *e_0002_0001 = CopyDataEntry(0x0002,0x0001, "OB");
1458       e_0002_0001->SetBinArea((uint8_t*)Util::GetFileMetaInformationVersion(),
1459                                false);
1460       e_0002_0001->SetLength(2);
1461       Archive->Push(e_0002_0001);
1462       e_0002_0001->Delete(); 
1463
1464       if ( ContentType == FILTERED_IMAGE || ContentType == UNMODIFIED_PIXELS_IMAGE)
1465       {      
1466    // we keep the original 'Media Storage SOP Class UID', we default it if missing
1467          CheckMandatoryEntry(0x0002,0x0002,"1.2.840.10008.5.1.4.1.1.7","UI"); 
1468       }
1469       else
1470       {
1471    // It's *not* an image comming straight from a source. We force
1472    // 'Media Storage SOP Class UID'  --> [Secondary Capture Image Storage]
1473          CopyMandatoryEntry(0x0002,0x0002,"1.2.840.10008.5.1.4.1.1.7","UI");
1474       }
1475       
1476    // 'Media Storage SOP Instance UID'   
1477       CopyMandatoryEntry(0x0002,0x0003,sop,"UI");
1478       
1479    // 'Implementation Class UID'
1480    // FIXME : in all examples we have, 0x0002,0x0012 is not so long :
1481    //         seems to be Root UID + 4 digits (?)
1482       CopyMandatoryEntry(0x0002,0x0012,Util::CreateUniqueUID(),"UI");
1483
1484    // 'Implementation Version Name'
1485       std::string version = "GDCM ";
1486       version += Util::GetVersion();
1487       CopyMandatoryEntry(0x0002,0x0013,version,"SH");
1488    }
1489
1490    // --------------------- For DataSet ---------------------
1491
1492    if ( ContentType != USER_OWN_IMAGE) // when it's not a user made image
1493    { 
1494    
1495       gdcmDebugMacro( "USER_OWN_IMAGE (1)");
1496     // If 'SOP Class UID' exists ('true DICOM' image)
1497    // we create the 'Source Image Sequence' SeqEntry
1498    // to hold informations about the Source Image
1499   
1500       DataEntry *e_0008_0016 = FileInternal->GetDataEntry(0x0008, 0x0016);
1501       if ( e_0008_0016 )
1502       {
1503       // Create 'Source Image Sequence' SeqEntry
1504 //     SeqEntry *sis = SeqEntry::New (
1505 //            Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0008, 0x2112) );
1506       SeqEntry *sis = SeqEntry::New (0x0008, 0x2112);
1507       SQItem *sqi = SQItem::New(1);
1508       // (we assume 'SOP Instance UID' exists too) 
1509       // create 'Referenced SOP Class UID'
1510 //     DataEntry *e_0008_1150 = DataEntry::New(
1511 //            Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0008, 0x1150) );
1512       DataEntry *e_0008_1150 = DataEntry::New(0x0008, 0x1150, "UI");
1513       e_0008_1150->SetString( e_0008_0016->GetString());
1514       sqi->AddEntry(e_0008_1150);
1515       e_0008_1150->Delete();
1516       
1517       // create 'Referenced SOP Instance UID'
1518       DataEntry *e_0008_0018 = FileInternal->GetDataEntry(0x0008, 0x0018);
1519 //      DataEntry *e_0008_1155 = DataEntry::New(
1520 //            Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0008, 0x1155) );
1521       DataEntry *e_0008_1155 = DataEntry::New(0x0008, 0x1155, "UI");
1522       e_0008_1155->SetString( e_0008_0018->GetString());
1523       sqi->AddEntry(e_0008_1155);
1524       e_0008_1155->Delete();
1525
1526       sis->AddSQItem(sqi,1);
1527       sqi->Delete();
1528
1529       // temporarily replaces any previous 'Source Image Sequence' 
1530       Archive->Push(sis);
1531       sis->Delete();
1532  
1533       // FIXME : is 'Image Type' *really* depending on the presence of 'SOP Class UID'?
1534        if ( ContentType == FILTERED_IMAGE)      
1535       // the user *knows* he just modified the pixels
1536       // the image is no longer an 'Original' one
1537          CopyMandatoryEntry(0x0008,0x0008,"DERIVED\\PRIMARY","CS");    
1538       }
1539    }
1540       
1541    if ( ContentType == FILTERED_IMAGE || ContentType == UNMODIFIED_PIXELS_IMAGE)
1542    {      
1543    // we keep the original 'Media Storage SOP Class UID', we default it if missing (it should be present !)
1544          CheckMandatoryEntry(0x0008,0x0016,"1.2.840.10008.5.1.4.1.1.7","UI");      
1545    }
1546    else
1547    {
1548    // It's *not* an image comming straight from a source. We force
1549    // 'Media Storage SOP Class UID'  --> [Secondary Capture Image Storage]
1550          CopyMandatoryEntry(0x0008,0x0016,"1.2.840.10008.5.1.4.1.1.7", "UI");      
1551    }
1552      
1553    Archive->Push(0x0028,0x005); // [Image Dimensions (RET)
1554    // Push out 'LibIDO-special' entries, if any
1555    Archive->Push(0x0028,0x0015);
1556    Archive->Push(0x0028,0x0016);
1557    Archive->Push(0x0028,0x0017);
1558    Archive->Push(0x0028,0x0198);  // very old versions
1559    Archive->Push(0x0028,0x0199);
1560  
1561    // Replace deprecated 0028 0012 US Planes   
1562    // by new             0028 0008 IS Number of Frames
1563    
1564   ///\todo : find if there is a rule!
1565    DataEntry *e_0028_0012 = FileInternal->GetDataEntry(0x0028, 0x0012);
1566    if ( e_0028_0012 )
1567    {
1568       CopyMandatoryEntry(0x0028, 0x0008,e_0028_0012->GetString(),"IS");
1569       Archive->Push(0x0028,0x0012);      
1570    }
1571
1572    // Deal with the pb of (Bits Stored = 12)
1573    // - we're gonna write the image as Bits Stored = 16
1574    if ( FileInternal->GetEntryString(0x0028,0x0100) ==  "12")
1575    {
1576       CopyMandatoryEntry(0x0028,0x0100,"16","US");
1577    }
1578
1579    // Check if user wasn't drunk ;-)
1580
1581    std::ostringstream s;
1582    // check 'Bits Allocated' vs decent values
1583    int nbBitsAllocated = FileInternal->GetBitsAllocated();
1584    if ( nbBitsAllocated == 0 || nbBitsAllocated > 32 
1585       || ( nbBitsAllocated > 8 && nbBitsAllocated <16) )
1586    {
1587       CopyMandatoryEntry(0x0028,0x0100,"16","US");
1588       gdcmWarningMacro("(0028,0100) changed from "
1589          << nbBitsAllocated << " to 16 for consistency purpose");
1590       nbBitsAllocated = 16; 
1591    }
1592    // check 'Bits Stored' vs 'Bits Allocated'   
1593    int nbBitsStored = FileInternal->GetBitsStored();
1594    if ( nbBitsStored == 0 || nbBitsStored > nbBitsAllocated )
1595    {
1596       s.str("");
1597       s << nbBitsAllocated;
1598       CopyMandatoryEntry(0x0028,0x0101,s.str(),"US");
1599       gdcmWarningMacro("(0028,0101) changed from "
1600                        << nbBitsStored << " to " << nbBitsAllocated
1601                        << " for consistency purpose" );
1602       nbBitsStored = nbBitsAllocated; 
1603     }
1604    // check 'Hight Bit Position' vs 'Bits Allocated' and 'Bits Stored'
1605    int highBitPosition = FileInternal->GetHighBitPosition();
1606    if ( highBitPosition == 0 || 
1607         highBitPosition > nbBitsAllocated-1 ||
1608         highBitPosition < nbBitsStored-1  )
1609    {
1610       s.str("");
1611       s << nbBitsStored - 1; 
1612       CopyMandatoryEntry(0x0028,0x0102,s.str(),"US");
1613       gdcmWarningMacro("(0028,0102) changed from "
1614                        << highBitPosition << " to " << nbBitsAllocated-1
1615                        << " for consistency purpose");
1616    }
1617
1618    // check Pixel Representation (default it as 0 -unsigned-)
1619    
1620    DataEntry *e_0028_0103 = FileInternal->GetDataEntry(0x0028, 0x0103);
1621    if ( !e_0028_0103 )
1622    {
1623       gdcmWarningMacro("PixelRepresentation (0028,0103) is supposed to be mandatory");
1624       CopyMandatoryEntry(0x0028, 0x0103,"0","US"); 
1625    }
1626    else
1627    {
1628       int sign = (int)e_0028_0103->GetValue(0);
1629       if (sign !=1 && sign !=0)
1630       {
1631          gdcmWarningMacro("PixelRepresentation (0028,0103) is supposed to be =1 or =0");
1632          CopyMandatoryEntry(0x0028, 0x0103,"0","US");
1633       }
1634    }
1635
1636    std::string pixelSpacing = FileInternal->GetEntryString(0x0028,0x0030);
1637    if ( pixelSpacing == GDCM_UNFOUND )
1638    {
1639       pixelSpacing = "1.0\\1.0";
1640        // if missing, Pixel Spacing forced to "1.0\1.0"
1641       CopyMandatoryEntry(0x0028,0x0030,pixelSpacing,"DS");
1642    }
1643    
1644    // 'Imager Pixel Spacing' : defaulted to 'Pixel Spacing'
1645    // --> This one is the *legal* one !
1646    if ( ContentType != USER_OWN_IMAGE)
1647    //  we write it only when we are *sure* the image comes from
1648    //         an imager (see also 0008,0x0064)          
1649       CheckMandatoryEntry(0x0018,0x1164,pixelSpacing,"DS");
1650
1651 /*
1652 ///Exact meaning of RETired fields
1653
1654 // See page 73 of ACR-NEMA_300-1988.pdf !
1655
1656 // 0020,0020 : Patient Orientation : 
1657 Patient direction of the first row and
1658 column of the images. The first entry id the direction of the raws, given by the
1659 direction of the last pixel in the first row from the first pixel in tha row.
1660 the second entry is the direction of the columns, given by the direction of the
1661 last pixel in the first column from the first pixel in that column.
1662 L : Left, F : Feet, A : Anterior, P : Posterior.
1663 Up to 3 letters can be used in combination to indicate oblique planes.
1664
1665 //0020,0030 Image Position (RET)
1666 x,y,z coordinates im mm of the first pixel in the image
1667
1668 // 0020,0035 Image Orientation (RET)
1669 Direction cosines of the R axis of the image system with respect to the
1670 equipment coordinate axes x,y,z, followed by direction cosines of the C axis of
1671 the image system with respect to the same axes
1672
1673 //0020,0050 Location
1674 An image location reference, standard for the modality (such as CT bed
1675 position), used to indicate position. Calculation of position for other purposes
1676 is only from (0020,0030) and (0020,0035)
1677 */
1678  
1679 /*
1680 // if imagePositionPatient    not found, default it with imagePositionRet,    if any
1681 // if imageOrientationPatient not found, default it with imageOrientationRet, if any
1682
1683    std::string imagePositionRet        = FileInternal->GetEntryString(0x0020,0x0030);
1684    std::string imageOrientationRet     = FileInternal->GetEntryString(0x0020,0x0035);
1685    std::string imagePositionPatient    = FileInternal->GetEntryString(0x0020,0x0032);
1686    std::string imageOrientationPatient = FileInternal->GetEntryString(0x0020,0x0037);
1687    
1688    if(  imagePositionPatient == GDCM_UNFOUND && imageOrientationPatient == GDCM_UNFOUND
1689      && imagePositionRet     != GDCM_UNFOUND && imageOrientationRet     != GDCM_UNFOUND)
1690    {
1691       CopyMandatoryEntry(0x0020, 0x0032,imagePositionRet,"DS");
1692       Archive->Push(0x0020,0x0030); 
1693       CopyMandatoryEntry(0x0020, 0x0037,imageOrientationRet,"DS");
1694       Archive->Push(0x0020,0x0035);        
1695    }        
1696 */
1697     
1698    // Samples Per Pixel (type 1) : default to grayscale 
1699    CheckMandatoryEntry(0x0028,0x0002,"1","US");
1700
1701    // --- Check UID-related Entries ---
1702  
1703    // At the end, not to overwrite the original ones,
1704    // needed by 'Referenced SOP Instance UID', 'Referenced SOP Class UID'   
1705    // 'SOP Instance UID'  
1706    CopyMandatoryEntry(0x0008,0x0018,sop,"UI");
1707
1708    if ( ContentType == USER_OWN_IMAGE)
1709    {
1710       gdcmDebugMacro( "USER_OWN_IMAGE (2)");   
1711        // Conversion Type.
1712        // Other possible values are :
1713        // See PS 3.3, Page 408
1714    
1715        // DV = Digitized Video
1716        // DI = Digital Interface   
1717        // DF = Digitized Film
1718        // WSD = Workstation
1719        // SD = Scanned Document
1720        // SI = Scanned Image
1721        // DRW = Drawing
1722        // SYN = Synthetic Image
1723            
1724       CheckMandatoryEntry(0x0008,0x0064,"SYN","CS"); // Why not?
1725    } 
1726 /*
1727    if ( ContentType == CREATED_IMAGE)
1728    {
1729    /// \todo : find a trick to pass the Media Storage SOP Instance UID of the images used to create the current image
1730    
1731    }
1732 */
1733            
1734    // ---- The user will never have to take any action on the following ----
1735
1736    // new value for 'SOP Instance UID'
1737    //SetMandatoryEntry(0x0008,0x0018,Util::CreateUniqueUID());
1738
1739    // Instance Creation Date
1740    const std::string &date = Util::GetCurrentDate();
1741    CopyMandatoryEntry(0x0008,0x0012,date,"DA");
1742  
1743    // Instance Creation Time
1744    const std::string &time = Util::GetCurrentTime();
1745    CopyMandatoryEntry(0x0008,0x0013,time,"TM");
1746
1747    // Study Date
1748    CheckMandatoryEntry(0x0008,0x0020,date,"DA");
1749    // Study Time
1750    CheckMandatoryEntry(0x0008,0x0030,time,"TM");
1751
1752    // Accession Number
1753    //CopyMandatoryEntry(0x0008,0x0050,"");
1754    CheckMandatoryEntry(0x0008,0x0050,"","SH");
1755    
1756
1757    // ----- Add Mandatory Entries if missing ---
1758    // Entries whose type is 1 are mandatory, with a mandatory value
1759    // Entries whose type is 1c are mandatory-inside-a-Sequence,
1760    //                          with a mandatory value
1761    // Entries whose type is 2 are mandatory, with an optional value
1762    // Entries whose type is 2c are mandatory-inside-a-Sequence,
1763    //                          with an optional value
1764    // Entries whose type is 3 are optional
1765
1766    // 'Study Instance UID'
1767    // Keep the value if exists
1768    // The user is allowed to create his own Study, 
1769    //          keeping the same 'Study Instance UID' for various images
1770    // The user may add images to a 'Manufacturer Study',
1771    //          adding new Series to an already existing Study 
1772    CheckMandatoryEntry(0x0020,0x000d,Util::CreateUniqueUID(),"UI");
1773
1774    // 'Serie Instance UID'
1775    // Keep the value if exists
1776    // The user is allowed to create his own Series, 
1777    // keeping the same 'Serie Instance UID' for various images
1778    // The user shouldn't add any image to a 'Manufacturer Serie'
1779    // but there is no way no to prevent him for doing that 
1780    CheckMandatoryEntry(0x0020,0x000e,Util::CreateUniqueUID(),"UI");
1781
1782    // Study ID
1783    CheckMandatoryEntry(0x0020,0x0010,"","SH");
1784
1785    // Series Number
1786    CheckMandatoryEntry(0x0020,0x0011,"","IS");
1787
1788    // Instance Number
1789    CheckMandatoryEntry(0x0020,0x0013,"","IS");
1790    
1791    // Patient Orientation
1792    // Can be computed from (0020|0037) :  Image Orientation (Patient)
1793    GDCM_NAME_SPACE::Orientation *o = GDCM_NAME_SPACE::Orientation::New();
1794    std::string ori = o->GetOrientation ( FileInternal );
1795    o->Delete();
1796    if (ori != "\\" && ori != GDCM_UNFOUND)
1797       CheckMandatoryEntry(0x0020,0x0020,ori,"CS");
1798    else   
1799       CheckMandatoryEntry(0x0020,0x0020,"","CS");
1800
1801    // Default Patient Position to HFS
1802    CheckMandatoryEntry(0x0018,0x5100,"HFS","CS");
1803
1804    // Modality : if missing we set it to 'OTher'
1805    CheckMandatoryEntry(0x0008,0x0060,"OT","CS");
1806
1807    // Manufacturer : if missing we set it to 'GDCM Factory'
1808    CheckMandatoryEntry(0x0008,0x0070,"GDCM Factory","LO");
1809
1810    // Institution Name : if missing we set it to 'GDCM Hospital'
1811    CheckMandatoryEntry(0x0008,0x0080,"GDCM Hospital","LO");
1812
1813    // Patient's Name : if missing, we set it to 'GDCM^Patient'
1814    CheckMandatoryEntry(0x0010,0x0010,"GDCM^Patient","PN");
1815
1816    // Patient ID : some clinical softwares *demand* it although it's a 'type 2' entry.
1817    CheckMandatoryEntry(0x0010,0x0020,"gdcm ID","LO");
1818
1819    // Patient's Birth Date : 'type 2' entry -> must exist, value not mandatory
1820    CheckMandatoryEntry(0x0010,0x0030,"","DA");
1821
1822    // Patient's Sex :'type 2' entry -> must exist, value not mandatory
1823    CheckMandatoryEntry(0x0010,0x0040,"","CS");
1824
1825    // Referring Physician's Name :'type 2' entry -> must exist, value not mandatory
1826    CheckMandatoryEntry(0x0008,0x0090,"","PN");
1827
1828  /*
1829    // Deal with element 0x0000 (group length) of each group.
1830    // First stage : get all the different Groups
1831    
1832   GroupHT grHT;
1833   DocEntry *d = FileInternal->GetFirstEntry();
1834   while(d)
1835   {
1836     grHT[d->GetGroup()] = 0;
1837     d=FileInternal->GetNextEntry();
1838   }
1839   // Second stage : add the missing ones (if any)
1840   for (GroupHT::iterator it = grHT.begin(); it != grHT.end(); ++it)  
1841   {
1842       CheckMandatoryEntry(it->first, 0x0000, "0"); 
1843   }    
1844   // Third stage : update all 'zero level' groups length
1845 */ 
1846
1847
1848    if (PhotometricInterpretation == 1)
1849    {
1850    }
1851
1852
1853 void FileHelper::CheckMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr )
1854 {
1855    DataEntry *entry = FileInternal->GetDataEntry(group,elem);
1856    if ( !entry )
1857    {
1858       //entry = DataEntry::New(Global::GetDicts()->GetDefaultPubDict()->GetEntry(group,elem));
1859       entry = DataEntry::New(group,elem,vr);
1860       entry->SetString(value);
1861       Archive->Push(entry);
1862       entry->Delete();
1863    }    
1864 }
1865
1866 /// \todo : what is it used for ? (FileHelper::SetMandatoryEntry) 
1867 void FileHelper::SetMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr)
1868 {
1869    //DataEntry *entry = DataEntry::New(Global::GetDicts()->GetDefaultPubDict()->GetEntry(group,elem));
1870    DataEntry *entry = DataEntry::New(group,elem,vr);
1871    entry->SetString(value);
1872    Archive->Push(entry);
1873    entry->Delete();
1874 }
1875
1876 void FileHelper::CopyMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr)
1877 {
1878    DataEntry *entry = CopyDataEntry(group,elem,vr);
1879    entry->SetString(value);
1880    Archive->Push(entry);
1881    entry->Delete();
1882 }
1883
1884 /**
1885  * \brief Restore in the File the initial group 0002
1886  */
1887 void FileHelper::RestoreWriteMandatory()
1888 {
1889    // group 0002 may be pushed out for ACR-NEMA writting purposes 
1890    Archive->Restore(0x0002,0x0000);
1891    Archive->Restore(0x0002,0x0001);
1892    Archive->Restore(0x0002,0x0002);
1893    Archive->Restore(0x0002,0x0003);
1894    Archive->Restore(0x0002,0x0010);
1895    Archive->Restore(0x0002,0x0012);
1896    Archive->Restore(0x0002,0x0013);
1897    Archive->Restore(0x0002,0x0016);
1898    Archive->Restore(0x0002,0x0100);
1899    Archive->Restore(0x0002,0x0102);
1900
1901    // FIXME : Check if none is missing !
1902    
1903    Archive->Restore(0x0008,0x0012);
1904    Archive->Restore(0x0008,0x0013);
1905    Archive->Restore(0x0008,0x0016);
1906    Archive->Restore(0x0008,0x0018);
1907    Archive->Restore(0x0008,0x0060);
1908    Archive->Restore(0x0008,0x0070);
1909    Archive->Restore(0x0008,0x0080);
1910    Archive->Restore(0x0008,0x0090);
1911    Archive->Restore(0x0008,0x2112);
1912
1913    Archive->Restore(0x0010,0x0010);
1914    Archive->Restore(0x0010,0x0030);
1915    Archive->Restore(0x0010,0x0040);
1916
1917    Archive->Restore(0x0020,0x000d);
1918    Archive->Restore(0x0020,0x000e);
1919 }
1920
1921
1922 /**
1923  * \brief   CallStartMethod
1924  */
1925 void FileHelper::CallStartMethod()
1926 {
1927    Progress = 0.0f;
1928    Abort    = false;
1929    CommandManager::ExecuteCommand(this,CMD_STARTPROGRESS);
1930 }
1931
1932 /**
1933  * \brief   CallProgressMethod
1934  */
1935 void FileHelper::CallProgressMethod()
1936 {
1937    CommandManager::ExecuteCommand(this,CMD_PROGRESS);
1938 }
1939
1940 /**
1941  * \brief   CallEndMethod
1942  */
1943 void FileHelper::CallEndMethod()
1944 {
1945    Progress = 1.0f;
1946    CommandManager::ExecuteCommand(this,CMD_ENDPROGRESS);
1947 }
1948
1949 //-----------------------------------------------------------------------------
1950 // Private
1951 /**
1952  * \brief Factorization for various forms of constructors.
1953  */
1954 void FileHelper::Initialize()
1955 {
1956    UserFunction = 0;
1957    ContentType = USER_OWN_IMAGE;
1958    
1959    WriteMode = WMODE_RAW;
1960    WriteType = ExplicitVR;
1961    
1962    PhotometricInterpretation = 2; // Black = 0
1963    
1964    PixelReadConverter  = new PixelReadConvert;
1965    PixelWriteConverter = new PixelWriteConvert;
1966    Archive = new DocEntryArchive( FileInternal );
1967 }
1968
1969 /**
1970  * \brief Reads/[decompresses] the pixels, 
1971  *        *without* making RGB from Palette Colors 
1972  * @return the pixels area, whatever its type 
1973  *         (uint8_t is just for prototyping : feel free to Cast it) 
1974  */ 
1975 uint8_t *FileHelper::GetRaw()
1976 {
1977    PixelReadConverter->SetUserFunction( UserFunction );
1978
1979    uint8_t *raw = PixelReadConverter->GetRaw();
1980    if ( ! raw )
1981    {
1982       // The Raw image migth not be loaded yet:
1983       std::ifstream *fp = FileInternal->OpenFile();
1984       PixelReadConverter->ReadAndDecompressPixelData( fp );
1985       if ( fp ) 
1986          FileInternal->CloseFile();
1987
1988       raw = PixelReadConverter->GetRaw();
1989       if ( ! raw )
1990       {
1991          gdcmWarningMacro( "Read/decompress of pixel data apparently went wrong.");
1992          return 0;
1993       }
1994    }
1995    return raw;
1996 }
1997
1998
1999 /**
2000  * \brief Deal with Grey levels i.e. re-arange them
2001  *        to have low values = dark, high values = bright
2002  */
2003 void FileHelper::ConvertFixGreyLevels(uint8_t *raw, size_t rawSize)
2004 {
2005    uint32_t i; // to please M$VC6
2006    int16_t j;
2007
2008    // Number of Bits Allocated for storing a Pixel is defaulted to 16
2009    // when absent from the file.
2010    int bitsAllocated = FileInternal->GetBitsAllocated();
2011    if ( bitsAllocated == 0 )
2012    {
2013       bitsAllocated = 16;
2014    }
2015
2016    else if (bitsAllocated > 8 && bitsAllocated < 16 && bitsAllocated != 12)
2017    {
2018       bitsAllocated = 16;
2019    }   
2020    // Number of "Bits Stored", defaulted to number of "Bits Allocated"
2021    // when absent from the file.
2022    int bitsStored = FileInternal->GetBitsStored();
2023    if ( bitsStored == 0 )
2024    {
2025       bitsStored = bitsAllocated;
2026    }
2027
2028    if (!FileInternal->IsSignedPixelData())
2029    {
2030       if ( bitsAllocated == 8 )
2031       {
2032          uint8_t *deb = (uint8_t *)raw;
2033          for (i=0; i<rawSize; i++)      
2034          {
2035             *deb = 255 - *deb;
2036             deb++;
2037          }
2038          return;
2039       }
2040
2041       if ( bitsAllocated == 16 )
2042       {
2043          uint16_t mask =1;
2044          for (j=0; j<bitsStored-1; j++)
2045          {
2046             mask = (mask << 1) +1; // will be fff when BitsStored=12
2047          }
2048
2049          uint16_t *deb = (uint16_t *)raw;
2050          for (i=0; i<rawSize/2; i++)      
2051          {
2052             *deb = mask - *deb;
2053             deb++;
2054          }
2055          return;
2056        }
2057    }
2058    else
2059    {
2060       if ( bitsAllocated == 8 )
2061       {
2062          uint8_t smask8 = 255;
2063          uint8_t *deb = (uint8_t *)raw;
2064          for (i=0; i<rawSize; i++)      
2065          {
2066             *deb = smask8 - *deb;
2067             deb++;
2068          }
2069          return;
2070       }
2071       if ( bitsAllocated == 16 )
2072       {
2073          uint16_t smask16 = 65535;
2074          uint16_t *deb = (uint16_t *)raw;
2075          for (i=0; i<rawSize/2; i++)      
2076          {
2077             *deb = smask16 - *deb;
2078             deb++;
2079          }
2080          return;
2081       }
2082    }
2083 }
2084
2085 //-----------------------------------------------------------------------------
2086 /**
2087  * \brief   Prints the common part of DataEntry, SeqEntry
2088  * @param   os ostream we want to print in
2089  * @param indent (unused)
2090  */
2091 void FileHelper::Print(std::ostream &os, std::string const &)
2092 {
2093    FileInternal->SetPrintLevel(PrintLevel);
2094    FileInternal->Print(os);
2095
2096    if ( FileInternal->IsReadable() )
2097    {
2098       PixelReadConverter->SetPrintLevel(PrintLevel);
2099       PixelReadConverter->Print(os);
2100    }
2101 }
2102
2103 //-----------------------------------------------------------------------------
2104 } // end namespace gdcm
2105
2106
2107 /* Probabely something to be added to use Rescale Slope/Intercept
2108 Have a look ,at ITK code !
2109
2110 // Internal function to rescale pixel according to Rescale Slope/Intercept
2111 template<class TBuffer, class TSource>
2112 void RescaleFunction(TBuffer* buffer, TSource *source,
2113                      double slope, double intercept, size_t size)
2114 {
2115   size /= sizeof(TSource);
2116
2117   if (slope != 1.0 && intercept != 0.0)
2118     {
2119     // Duff's device.  Instead of this code:
2120     //
2121     //   for(unsigned int i=0; i<size; i++)
2122     //    {
2123     //    buffer[i] = (TBuffer)(source[i]*slope + intercept);
2124     //    }
2125     //
2126     // use Duff's device which exploits "fall through"
2127     register size_t n = (size + 7) / 8;
2128     switch ( size % 8)
2129       {
2130       case 0: do { *buffer++ = (TBuffer)((*source++)*slope + intercept);
2131       case 7:      *buffer++ = (TBuffer)((*source++)*slope + intercept);
2132       case 6:      *buffer++ = (TBuffer)((*source++)*slope + intercept);
2133       case 5:      *buffer++ = (TBuffer)((*source++)*slope + intercept);
2134       case 4:      *buffer++ = (TBuffer)((*source++)*slope + intercept);
2135       case 3:      *buffer++ = (TBuffer)((*source++)*slope + intercept);
2136       case 2:      *buffer++ = (TBuffer)((*source++)*slope + intercept);
2137       case 1:      *buffer++ = (TBuffer)((*source++)*slope + intercept);
2138                  }  while (--n > 0);
2139       }
2140     }
2141   else if (slope == 1.0 && intercept != 0.0)
2142     {
2143     // Duff's device.  Instead of this code:
2144     //
2145     //   for(unsigned int i=0; i<size; i++)
2146     //    {
2147     //    buffer[i] = (TBuffer)(source[i] + intercept);
2148     //    }
2149     //
2150     // use Duff's device which exploits "fall through"
2151     register size_t n = (size + 7) / 8;
2152     switch ( size % 8)
2153       {
2154       case 0: do { *buffer++ = (TBuffer)(*source++ + intercept);
2155       case 7:      *buffer++ = (TBuffer)(*source++ + intercept);
2156       case 6:      *buffer++ = (TBuffer)(*source++ + intercept);
2157       case 5:      *buffer++ = (TBuffer)(*source++ + intercept);
2158       case 4:      *buffer++ = (TBuffer)(*source++ + intercept);
2159       case 3:      *buffer++ = (TBuffer)(*source++ + intercept);
2160       case 2:      *buffer++ = (TBuffer)(*source++ + intercept);
2161       case 1:      *buffer++ = (TBuffer)(*source++ + intercept);
2162                  }  while (--n > 0);
2163       }
2164     }
2165   else if (slope != 1.0 && intercept == 0.0)
2166     {
2167     // Duff's device.  Instead of this code:
2168     //
2169     //   for(unsigned int i=0; i<size; i++)
2170     //    {
2171     //    buffer[i] = (TBuffer)(source[i]*slope);
2172     //    }
2173     //
2174     // use Duff's device which exploits "fall through"
2175     register size_t n = (size + 7) / 8;
2176     switch ( size % 8)
2177       {
2178       case 0: do { *buffer++ = (TBuffer)((*source++)*slope);
2179       case 7:      *buffer++ = (TBuffer)((*source++)*slope);
2180       case 6:      *buffer++ = (TBuffer)((*source++)*slope);
2181       case 5:      *buffer++ = (TBuffer)((*source++)*slope);
2182       case 4:      *buffer++ = (TBuffer)((*source++)*slope);
2183       case 3:      *buffer++ = (TBuffer)((*source++)*slope);
2184       case 2:      *buffer++ = (TBuffer)((*source++)*slope);
2185       case 1:      *buffer++ = (TBuffer)((*source++)*slope);
2186                  }  while (--n > 0);
2187       }
2188     }
2189   else
2190     {
2191     // Duff's device.  Instead of this code:
2192     //
2193     //   for(unsigned int i=0; i<size; i++)
2194     //    {
2195     //    buffer[i] = (TBuffer)(source[i]);
2196     //    }
2197     //
2198     // use Duff's device which exploits "fall through"
2199     register size_t n = (size + 7) / 8;
2200     switch ( size % 8)
2201       {
2202       case 0: do { *buffer++ = (TBuffer)(*source++);
2203       case 7:      *buffer++ = (TBuffer)(*source++);
2204       case 6:      *buffer++ = (TBuffer)(*source++);
2205       case 5:      *buffer++ = (TBuffer)(*source++);
2206       case 4:      *buffer++ = (TBuffer)(*source++);
2207       case 3:      *buffer++ = (TBuffer)(*source++);
2208       case 2:      *buffer++ = (TBuffer)(*source++);
2209       case 1:      *buffer++ = (TBuffer)(*source++);
2210                  }  while (--n > 0);
2211       }
2212     }
2213     
2214     
2215 }
2216
2217
2218 template<class TSource>
2219 void RescaleFunction(ImageIOBase::IOComponentType bufferType,
2220                      void* buffer, TSource *source,
2221                      double slope, double intercept, size_t size)
2222 {
2223   switch (bufferType)
2224     {
2225     case ImageIOBase::UCHAR:
2226       RescaleFunction( (unsigned char *)buffer, source, slope, intercept, size);
2227       break;
2228     case ImageIOBase::CHAR:
2229       RescaleFunction( (char *)buffer, source, slope, intercept, size);
2230       break;
2231     case ImageIOBase::USHORT:
2232       RescaleFunction( (unsigned short *)buffer, source, slope, intercept,size);
2233       break;
2234     case ImageIOBase::SHORT:
2235       RescaleFunction( (short *)buffer, source, slope, intercept, size);
2236       break;
2237     case ImageIOBase::UINT:
2238       RescaleFunction( (unsigned int *)buffer, source, slope, intercept, size);
2239       break;
2240     case ImageIOBase::INT:
2241       RescaleFunction( (int *)buffer, source, slope, intercept, size);
2242       break;
2243     case ImageIOBase::FLOAT:
2244       RescaleFunction( (float *)buffer, source, slope, intercept, size);
2245       break;
2246     case ImageIOBase::DOUBLE:
2247       RescaleFunction( (double *)buffer, source, slope, intercept, size);
2248       break;
2249     default:
2250       ::itk::OStringStream message;
2251       message << "itk::ERROR: GDCMImageIO: Unknown component type : " << bufferType;
2252       ::itk::ExceptionObject e(__FILE__, __LINE__, message.str().c_str(),ITK_LOCATION);
2253       throw e;
2254     }
2255 }
2256 */