]> Creatis software - gdcm.git/blob - src/gdcmHeader.cxx
bc79a67529d62409f16936ade3ec814f1ecbcae5
[gdcm.git] / src / gdcmHeader.cxx
1 /*=========================================================================
2                                                                                 
3   Program:   gdcm
4   Module:    $RCSfile: gdcmHeader.cxx,v $
5   Language:  C++
6   Date:      $Date: 2005/01/11 20:49:44 $
7   Version:   $Revision: 1.230 $
8                                                                                 
9   Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de
10   l'Image). All rights reserved. See Doc/License.txt or
11   http://www.creatis.insa-lyon.fr/Public/Gdcm/License.html for details.
12                                                                                 
13      This software is distributed WITHOUT ANY WARRANTY; without even
14      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15      PURPOSE.  See the above copyright notices for more information.
16                                                                                 
17 =========================================================================*/
18
19 #include "gdcmHeader.h"
20 #include "gdcmGlobal.h"
21 #include "gdcmUtil.h"
22 #include "gdcmDebug.h"
23 #include "gdcmTS.h"
24 #include "gdcmValEntry.h"
25 #include "gdcmBinEntry.h"
26 #include <stdio.h> //sscanf
27
28 #include <vector>
29
30 namespace gdcm 
31 {
32 //-----------------------------------------------------------------------------
33 // Constructor / Destructor
34 /**
35  * \brief  Constructor 
36  * @param  filename name of the file whose header we want to analyze
37  */
38 Header::Header( std::string const &filename ):
39             Document( filename )
40 {    
41    // for some ACR-NEMA images GrPixel, NumPixel is *not* 7fe0,0010
42    // We may encounter the 'RETired' (0x0028, 0x0200) tag
43    // (Image Location") . This Element contains the number of
44    // the group that contains the pixel data (hence the "Pixel Data"
45    // is found by indirection through the "Image Location").
46    // Inside the group pointed by "Image Location" the searched element
47    // is conventionally the element 0x0010 (when the norm is respected).
48    // When the "Image Location" is Missing we default to group 0x7fe0.
49    // Note: this IS the right place for the code
50  
51    // Image Location
52    const std::string &imgLocation = GetEntry(0x0028, 0x0200);
53    if ( imgLocation == GDCM_UNFOUND )
54    {
55       // default value
56       GrPixel = 0x7fe0;
57    }
58    else
59    {
60       GrPixel = (uint16_t) atoi( imgLocation.c_str() );
61    }   
62
63    // sometimes Image Location value doesn't follow 
64    // the supposed processor endianity. 
65    // see gdcmData/cr172241.dcm      
66    if ( GrPixel == 0xe07f )
67    {
68       GrPixel = 0x7fe0;
69    }
70
71    if ( GrPixel != 0x7fe0 )
72    {
73       // This is a kludge for old dirty Philips imager.
74       NumPixel = 0x1010;
75    }
76    else
77    {
78       NumPixel = 0x0010;
79    }
80
81    // Now, we know GrPixel and NumPixel.
82    // Let's create a VirtualDictEntry to allow a further VR modification
83    // and force VR to match with BitsAllocated.
84
85    DocEntry *entry = GetDocEntry(GrPixel, NumPixel); 
86    if ( entry != 0 )
87    {
88
89       std::string PixelVR;
90       // 8 bits allocated is a 'O Bytes' , as well as 24 (old ACR-NEMA RGB)
91       // more than 8 (i.e 12, 16) is a 'O Words'
92       if ( GetBitsAllocated() == 8 || GetBitsAllocated() == 24 ) 
93          PixelVR = "OB";
94       else
95          PixelVR = "OW";
96
97       DictEntry* newEntry = NewVirtualDictEntry(
98                              GrPixel, NumPixel,
99                              PixelVR, "PXL", "Pixel Data");
100  
101       entry->SetDictEntry( newEntry );
102    } 
103 }
104
105 /**
106  * \brief Constructor used when we want to generate dicom files from scratch
107  */
108 Header::Header()
109            :Document()
110 {
111    InitializeDefaultHeader();
112 }
113
114 /**
115  * \brief   Canonical destructor.
116  */
117 Header::~Header ()
118 {
119 }
120
121 /**
122  * \brief Performs some consistency checking on various 'File related' 
123  *       (as opposed to 'DicomDir related') entries 
124  *       then writes in a file all the (Dicom Elements) included the Pixels 
125  * @param fileName file name to write to
126  * @param filetype Type of the File to be written 
127  *          (ACR-NEMA, ExplicitVR, ImplicitVR)
128  */
129 bool Header::Write(std::string fileName, FileType filetype)
130 {
131    std::ofstream *fp = new std::ofstream(fileName.c_str(), 
132                                          std::ios::out | std::ios::binary);
133    if (*fp == NULL)
134    {
135       gdcmVerboseMacro("Failed to open (write) File: " << fileName.c_str());
136       return false;
137    }
138
139    // Bits Allocated
140    if ( GetEntry(0x0028,0x0100) ==  "12")
141    {
142       SetEntry("16", 0x0028,0x0100);
143    }
144
145   /// \todo correct 'Pixel group' Length if necessary
146
147    int i_lgPix = GetEntryLength(GrPixel, NumPixel);
148    if (i_lgPix != -2)
149    {
150       // no (GrPixel, NumPixel) element
151       std::string s_lgPix = Util::Format("%d", i_lgPix+12);
152       s_lgPix = Util::DicomString( s_lgPix.c_str() );
153       ReplaceOrCreate(s_lgPix,GrPixel, 0x0000);
154    }
155
156    // FIXME : should be nice if we could move it to File
157    //         (or in future gdcmPixelData class)
158
159    // Drop Palette Color, if necessary
160    
161    if ( GetEntry(0x0028,0x0002).c_str()[0] == '3' )
162    {
163       // if SamplesPerPixel = 3, sure we don't need any LUT !   
164       // Drop 0028|1101, 0028|1102, 0028|1103
165       // Drop 0028|1201, 0028|1202, 0028|1203
166
167       DocEntry *e = GetDocEntry(0x0028,0x01101);
168       if (e)
169       {
170          RemoveEntryNoDestroy(e);
171       }
172       e = GetDocEntry(0x0028,0x1102);
173       if (e)
174       {
175          RemoveEntryNoDestroy(e);
176       }
177       e = GetDocEntry(0x0028,0x1103);
178       if (e)
179       {
180          RemoveEntryNoDestroy(e);
181       }
182       e = GetDocEntry(0x0028,0x01201);
183       if (e)
184       {
185          RemoveEntryNoDestroy(e);
186       }
187       e = GetDocEntry(0x0028,0x1202);
188       if (e)
189       {
190          RemoveEntryNoDestroy(e);
191       }
192       e = GetDocEntry(0x0028,0x1203);
193       if (e)
194       {
195           RemoveEntryNoDestroy(e);
196       }
197    }
198
199 #ifdef GDCM_WORDS_BIGENDIAN
200    // Super Duper hack that will make gdcm a BOMB ! but should
201    // Fix temporarily the dashboard
202    BinEntry *b = GetBinEntry(0x7fe0,0x0010);
203    if ( GetEntry(0x0028,0x0100) ==  "16")
204    {
205       uint16_t *im16 = (uint16_t*)b->GetBinArea();
206       int lgr = b->GetLength();
207       for( int i = 0; i < lgr / 2; i++ )
208       {
209          im16[i]= (im16[i] >> 8) | (im16[i] << 8 );
210       }
211    }
212 #endif //GDCM_WORDS_BIGENDIAN
213
214    Document::WriteContent(fp,filetype);
215
216 #ifdef GDCM_WORDS_BIGENDIAN
217    // Flip back the pixel ... I told you this is a hack
218    if ( GetEntry(0x0028,0x0100) ==  "16")
219    {
220       uint16_t *im16 = (uint16_t*)b->GetBinArea();
221       int lgr = b->GetLength();
222       for( int i = 0; i < lgr / 2; i++ )
223       {
224          im16[i]= (im16[i] >> 8) | (im16[i] << 8 );
225       }
226    }
227 #endif //GDCM_WORDS_BIGENDIAN
228
229    fp->close();
230    delete fp;
231
232    return true;
233 }
234
235 //-----------------------------------------------------------------------------
236 // Print
237
238
239 //-----------------------------------------------------------------------------
240 // Public
241
242 /**
243  * \brief  This predicate, based on hopefully reasonable heuristics,
244  *         decides whether or not the current Header was properly parsed
245  *         and contains the mandatory information for being considered as
246  *         a well formed and usable Dicom/Acr File.
247  * @return true when Header is the one of a reasonable Dicom/Acr file,
248  *         false otherwise. 
249  */
250 bool Header::IsReadable()
251 {
252    if( !Document::IsReadable() )
253    {
254       return false;
255    }
256
257    const std::string &res = GetEntry(0x0028, 0x0005);
258    if ( res != GDCM_UNFOUND && atoi(res.c_str()) > 4 )
259    {
260       return false; // Image Dimensions
261    }
262    if ( !GetDocEntry(0x0028, 0x0100) )
263    {
264       return false; // "Bits Allocated"
265    }
266    if ( !GetDocEntry(0x0028, 0x0101) )
267    {
268       return false; // "Bits Stored"
269    }
270    if ( !GetDocEntry(0x0028, 0x0102) )
271    {
272       return false; // "High Bit"
273    }
274    if ( !GetDocEntry(0x0028, 0x0103) )
275    {
276       return false; // "Pixel Representation" i.e. 'Sign'
277    }
278
279    return true;
280 }
281
282 /**
283  * \brief   Retrieve the number of columns of image.
284  * @return  The encountered size when found, 0 by default.
285  *          0 means the file is NOT USABLE. The caller will have to check
286  */
287 int Header::GetXSize()
288 {
289    const std::string &strSize = GetEntry(0x0028,0x0011);
290    if ( strSize == GDCM_UNFOUND )
291    {
292       return 0;
293    }
294
295    return atoi( strSize.c_str() );
296 }
297
298 /**
299  * \brief   Retrieve the number of lines of image.
300  * \warning The defaulted value is 1 as opposed to Header::GetXSize()
301  * @return  The encountered size when found, 1 by default 
302  *          (The ACR-NEMA file contains a Signal, not an Image).
303  */
304 int Header::GetYSize()
305 {
306    const std::string &strSize = GetEntry(0x0028,0x0010);
307    if ( strSize != GDCM_UNFOUND )
308    {
309       return atoi( strSize.c_str() );
310    }
311    if ( IsDicomV3() )
312    {
313       return 0;
314    }
315
316    // The Rows (0028,0010) entry was optional for ACR/NEMA. It might
317    // hence be a signal (1D image). So we default to 1:
318    return 1;
319 }
320
321 /**
322  * \brief   Retrieve the number of planes of volume or the number
323  *          of frames of a multiframe.
324  * \warning When present we consider the "Number of Frames" as the third
325  *          dimension. When Missing we consider the third dimension as
326  *          being the ACR-NEMA "Planes" tag content.
327  * @return  The encountered size when found, 1 by default (single image).
328  */
329 int Header::GetZSize()
330 {
331    // Both  DicomV3 and ACR/Nema consider the "Number of Frames"
332    // as the third dimension.
333    const std::string &strSize = GetEntry(0x0028,0x0008);
334    if ( strSize != GDCM_UNFOUND )
335    {
336       return atoi( strSize.c_str() );
337    }
338
339    // We then consider the "Planes" entry as the third dimension 
340    const std::string &strSize2 = GetEntry(0x0028,0x0012);
341    if ( strSize2 != GDCM_UNFOUND )
342    {
343       return atoi( strSize2.c_str() );
344    }
345
346    return 1;
347 }
348
349 /**
350   * \brief gets the info from 0028,0030 : Pixel Spacing
351   *             else 1.0
352   * @return X dimension of a pixel
353   */
354 float Header::GetXSpacing()
355 {
356    float xspacing, yspacing;
357    const std::string &strSpacing = GetEntry(0x0028,0x0030);
358
359    if ( strSpacing == GDCM_UNFOUND )
360    {
361       gdcmVerboseMacro( "Unfound Pixel Spacing (0028,0030)" );
362       return 1.;
363    }
364
365    int nbValues;
366    if( ( nbValues = sscanf( strSpacing.c_str(), 
367          "%f\\%f", &yspacing, &xspacing)) != 2 )
368    {
369       // if single value is found, xspacing is defaulted to yspacing
370       if ( nbValues == 1 )
371       {
372          xspacing = yspacing;
373       }
374
375       if ( xspacing == 0.0 )
376          xspacing = 1.0;
377
378       return xspacing;
379
380    }
381
382    // to avoid troubles with David Clunie's-like images
383    if ( xspacing == 0. && yspacing == 0.)
384       return 1.;
385
386    if ( xspacing == 0.)
387    {
388       gdcmVerboseMacro("gdcmData/CT-MONO2-8-abdo.dcm problem");
389       // seems to be a bug in the header ...
390       nbValues = sscanf( strSpacing.c_str(), "%f\\0\\%f", &yspacing, &xspacing);
391       gdcmAssertMacro( nbValues == 2 );
392    }
393
394    return xspacing;
395 }
396
397 /**
398   * \brief gets the info from 0028,0030 : Pixel Spacing
399   *             else 1.0
400   * @return Y dimension of a pixel
401   */
402 float Header::GetYSpacing()
403 {
404    float yspacing = 1.;
405    std::string strSpacing = GetEntry(0x0028,0x0030);
406   
407    if ( strSpacing == GDCM_UNFOUND )
408    {
409       gdcmVerboseMacro("Unfound Pixel Spacing (0028,0030)");
410       return 1.;
411     }
412
413    // if sscanf cannot read any float value, it won't affect yspacing
414    sscanf( strSpacing.c_str(), "%f", &yspacing);
415
416    if ( yspacing == 0.0 )
417       yspacing = 1.0;
418
419    return yspacing;
420
421
422 /**
423  * \brief gets the info from 0018,0088 : Space Between Slices
424  *                 else from 0018,0050 : Slice Thickness
425  *                 else 1.0
426  * @return Z dimension of a voxel-to be
427  */
428 float Header::GetZSpacing()
429 {
430    // Spacing Between Slices : distance entre le milieu de chaque coupe
431    // Les coupes peuvent etre :
432    //   jointives     (Spacing between Slices = Slice Thickness)
433    //   chevauchantes (Spacing between Slices < Slice Thickness)
434    //   disjointes    (Spacing between Slices > Slice Thickness)
435    // Slice Thickness : epaisseur de tissus sur laquelle est acquis le signal
436    //   ca interesse le physicien de l'IRM, pas le visualisateur de volumes ...
437    //   Si le Spacing Between Slices est Missing, 
438    //   on suppose que les coupes sont jointives
439    
440    const std::string &strSpacingBSlices = GetEntry(0x0018,0x0088);
441
442    if ( strSpacingBSlices == GDCM_UNFOUND )
443    {
444       gdcmVerboseMacro("Unfound Spacing Between Slices (0018,0088)");
445       const std::string &strSliceThickness = GetEntry(0x0018,0x0050);       
446       if ( strSliceThickness == GDCM_UNFOUND )
447       {
448          gdcmVerboseMacro("Unfound Slice Thickness (0018,0050)");
449          return 1.;
450       }
451       else
452       {
453          // if no 'Spacing Between Slices' is found, 
454          // we assume slices join together
455          // (no overlapping, no interslice gap)
456          // if they don't, we're fucked up
457          return (float)atof( strSliceThickness.c_str() );
458       }
459    }
460    //else
461    return (float)atof( strSpacingBSlices.c_str() );
462 }
463
464 /**
465  *\brief gets the info from 0028,1052 : Rescale Intercept
466  * @return Rescale Intercept
467  */
468 float Header::GetRescaleIntercept()
469 {
470    float resInter = 0.;
471    /// 0028 1052 DS IMG Rescale Intercept
472    const std::string &strRescInter = GetEntry(0x0028,0x1052);
473    if ( strRescInter != GDCM_UNFOUND )
474    {
475       if( sscanf( strRescInter.c_str(), "%f", &resInter) != 1 )
476       {
477          // bug in the element 0x0028,0x1052
478          gdcmVerboseMacro( "Rescale Intercept (0028,1052) is empty." );
479       }
480    }
481
482    return resInter;
483 }
484
485 /**
486  *\brief   gets the info from 0028,1053 : Rescale Slope
487  * @return Rescale Slope
488  */
489 float Header::GetRescaleSlope()
490 {
491    float resSlope = 1.;
492    //0028 1053 DS IMG Rescale Slope
493    std::string strRescSlope = GetEntry(0x0028,0x1053);
494    if ( strRescSlope != GDCM_UNFOUND )
495    {
496       if( sscanf( strRescSlope.c_str(), "%f", &resSlope) != 1)
497       {
498          // bug in the element 0x0028,0x1053
499          gdcmVerboseMacro( "Rescale Slope (0028,1053) is empty.");
500       }
501    }
502
503    return resSlope;
504 }
505
506 /**
507  * \brief This function is intended to user who doesn't want 
508  *   to have to manage a LUT and expects to get an RBG Pixel image
509  *   (or a monochrome one ...) 
510  * \warning to be used with GetImagePixels()
511  * @return 1 if Gray level, 3 if Color (RGB, YBR or PALETTE COLOR)
512  */
513 int Header::GetNumberOfScalarComponents()
514 {
515    if ( GetSamplesPerPixel() == 3 )
516    {
517       return 3;
518    }
519       
520    // 0028 0100 US IMG Bits Allocated
521    // (in order no to be messed up by old RGB images)
522    if ( GetEntry(0x0028,0x0100) == "24" )
523    {
524       return 3;
525    }
526        
527    std::string strPhotometricInterpretation = GetEntry(0x0028,0x0004);
528
529    if ( ( strPhotometricInterpretation == "PALETTE COLOR ") )
530    {
531       if ( HasLUT() )// PALETTE COLOR is NOT enough
532       {
533          return 3;
534       }
535       else
536       {
537          return 1;
538       }
539    }
540
541    // beware of trailing space at end of string      
542    // DICOM tags are never of odd length
543    if ( strPhotometricInterpretation == GDCM_UNFOUND   || 
544         Util::DicomStringEqual(strPhotometricInterpretation, "MONOCHROME1") ||
545         Util::DicomStringEqual(strPhotometricInterpretation, "MONOCHROME2") )
546    {
547       return 1;
548    }
549    else
550    {
551       // we assume that *all* kinds of YBR are dealt with
552       return 3;
553    }
554 }
555
556 /**
557  * \brief This function is intended to user that DOESN'T want 
558  *  to get RGB pixels image when it's stored as a PALETTE COLOR image
559  *   - the (vtk) user is supposed to know how deal with LUTs - 
560  * \warning to be used with GetImagePixelsRaw()
561  * @return 1 if Gray level, 3 if Color (RGB or YBR - NOT 'PALETTE COLOR' -)
562  */
563 int Header::GetNumberOfScalarComponentsRaw()
564 {
565    // 0028 0100 US IMG Bits Allocated
566    // (in order no to be messed up by old RGB images)
567    if ( Header::GetEntry(0x0028,0x0100) == "24" )
568    {
569       return 3;
570    }
571
572    // we assume that *all* kinds of YBR are dealt with
573    return GetSamplesPerPixel();
574 }
575
576 //
577 // --------------  Remember ! ----------------------------------
578 //
579 // Image Position Patient                              (0020,0032):
580 // If not found (ACR_NEMA) we try Image Position       (0020,0030)
581 // If not found (ACR-NEMA), we consider Slice Location (0020,1041)
582 //                                   or Location       (0020,0050) 
583 // as the Z coordinate, 
584 // 0. for all the coordinates if nothing is found
585
586 // \todo find a way to inform the caller nothing was found
587 // \todo How to tell the caller a wrong number of values was found?
588 //
589 // ---------------------------------------------------------------
590 //
591
592 /**
593  * \brief gets the info from 0020,0032 : Image Position Patient
594  *                 else from 0020,0030 : Image Position (RET)
595  *                 else 0.
596  * @return up-left image corner X position
597  */
598 float Header::GetXOrigin()
599 {
600    float xImPos, yImPos, zImPos;  
601    std::string strImPos = GetEntry(0x0020,0x0032);
602
603    if ( strImPos == GDCM_UNFOUND )
604    {
605       gdcmVerboseMacro( "Unfound Image Position Patient (0020,0032)");
606       strImPos = GetEntry(0x0020,0x0030); // For ACR-NEMA images
607       if ( strImPos == GDCM_UNFOUND )
608       {
609          gdcmVerboseMacro( "Unfound Image Position (RET) (0020,0030)");
610          /// \todo How to tell the caller nothing was found ?
611          return 0.;
612       }
613    }
614
615    if( sscanf( strImPos.c_str(), "%f\\%f\\%f", &xImPos, &yImPos, &zImPos) != 3 )
616    {
617       return 0.;
618    }
619
620    return xImPos;
621 }
622
623 /**
624  * \brief gets the info from 0020,0032 : Image Position Patient
625  *                 else from 0020,0030 : Image Position (RET)
626  *                 else 0.
627  * @return up-left image corner Y position
628  */
629 float Header::GetYOrigin()
630 {
631    float xImPos, yImPos, zImPos;
632    std::string strImPos = GetEntry(0x0020,0x0032);
633
634    if ( strImPos == GDCM_UNFOUND)
635    {
636       gdcmVerboseMacro( "Unfound Image Position Patient (0020,0032)");
637       strImPos = GetEntry(0x0020,0x0030); // For ACR-NEMA images
638       if ( strImPos == GDCM_UNFOUND )
639       {
640          gdcmVerboseMacro( "Unfound Image Position (RET) (0020,0030)");
641          /// \todo How to tell the caller nothing was found ?
642          return 0.;
643       }  
644    }
645
646    if( sscanf( strImPos.c_str(), "%f\\%f\\%f", &xImPos, &yImPos, &zImPos) != 3 )
647    {
648       return 0.;
649    }
650
651    return yImPos;
652 }
653
654 /**
655  * \brief gets the info from 0020,0032 : Image Position Patient
656  *                 else from 0020,0030 : Image Position (RET)
657  *                 else from 0020,1041 : Slice Location
658  *                 else from 0020,0050 : Location
659  *                 else 0.
660  * @return up-left image corner Z position
661  */
662 float Header::GetZOrigin()
663 {
664    float xImPos, yImPos, zImPos; 
665    std::string strImPos = GetEntry(0x0020,0x0032);
666
667    if ( strImPos != GDCM_UNFOUND )
668    {
669       if( sscanf( strImPos.c_str(), "%f\\%f\\%f", &xImPos, &yImPos, &zImPos) != 3)
670       {
671          gdcmVerboseMacro( "Wrong Image Position Patient (0020,0032)");
672          return 0.;  // bug in the element 0x0020,0x0032
673       }
674       else
675       {
676          return zImPos;
677       }
678    }
679
680    strImPos = GetEntry(0x0020,0x0030); // For ACR-NEMA images
681    if ( strImPos != GDCM_UNFOUND )
682    {
683       if( sscanf( strImPos.c_str(), 
684           "%f\\%f\\%f", &xImPos, &yImPos, &zImPos ) != 3 )
685       {
686          gdcmVerboseMacro( "Wrong Image Position (RET) (0020,0030)");
687          return 0.;  // bug in the element 0x0020,0x0032
688       }
689       else
690       {
691          return zImPos;
692       }
693    }
694
695    std::string strSliceLocation = GetEntry(0x0020,0x1041); // for *very* old ACR-NEMA images
696    if ( strSliceLocation != GDCM_UNFOUND )
697    {
698       if( sscanf( strSliceLocation.c_str(), "%f", &zImPos) != 1)
699       {
700          gdcmVerboseMacro( "Wrong Slice Location (0020,1041)");
701          return 0.;  // bug in the element 0x0020,0x1041
702       }
703       else
704       {
705          return zImPos;
706       }
707    }
708    gdcmVerboseMacro( "Unfound Slice Location (0020,1041)");
709
710    std::string strLocation = GetEntry(0x0020,0x0050);
711    if ( strLocation != GDCM_UNFOUND )
712    {
713       if( sscanf( strLocation.c_str(), "%f", &zImPos) != 1)
714       {
715          gdcmVerboseMacro( "Wrong Location (0020,0050)");
716          return 0.;  // bug in the element 0x0020,0x0050
717       }
718       else
719       {
720          return zImPos;
721       }
722    }
723    gdcmVerboseMacro( "Unfound Location (0020,0050)");  
724
725    return 0.; // Hopeless
726 }
727
728 /**
729  * \brief gets the info from 0020,0013 : Image Number else 0.
730  * @return image number
731  */
732 int Header::GetImageNumber()
733 {
734    // The function i atoi() takes the address of an area of memory as
735    // parameter and converts the string stored at that location to an integer
736    // using the external decimal to internal binary conversion rules. This may
737    // be preferable to sscanf() since atoi() is a much smaller, simpler and
738    // faster function. sscanf() can do all possible conversions whereas
739    // atoi() can only do single decimal integer conversions.
740    //0020 0013 IS REL Image Number
741    std::string strImNumber = GetEntry(0x0020,0x0013);
742    if ( strImNumber != GDCM_UNFOUND )
743    {
744       return atoi( strImNumber.c_str() );
745    }
746    return 0;   //Hopeless
747 }
748
749 /**
750  * \brief gets the info from 0008,0060 : Modality
751  * @return Modality Type
752  */
753 ModalityType Header::GetModality()
754 {
755    // 0008 0060 CS ID Modality
756    std::string strModality = GetEntry(0x0008,0x0060);
757    if ( strModality != GDCM_UNFOUND )
758    {
759            if ( strModality.find("AU") < strModality.length()) return AU;
760       else if ( strModality.find("AS") < strModality.length()) return AS;
761       else if ( strModality.find("BI") < strModality.length()) return BI;
762       else if ( strModality.find("CF") < strModality.length()) return CF;
763       else if ( strModality.find("CP") < strModality.length()) return CP;
764       else if ( strModality.find("CR") < strModality.length()) return CR;
765       else if ( strModality.find("CT") < strModality.length()) return CT;
766       else if ( strModality.find("CS") < strModality.length()) return CS;
767       else if ( strModality.find("DD") < strModality.length()) return DD;
768       else if ( strModality.find("DF") < strModality.length()) return DF;
769       else if ( strModality.find("DG") < strModality.length()) return DG;
770       else if ( strModality.find("DM") < strModality.length()) return DM;
771       else if ( strModality.find("DS") < strModality.length()) return DS;
772       else if ( strModality.find("DX") < strModality.length()) return DX;
773       else if ( strModality.find("ECG") < strModality.length()) return ECG;
774       else if ( strModality.find("EPS") < strModality.length()) return EPS;
775       else if ( strModality.find("FA") < strModality.length()) return FA;
776       else if ( strModality.find("FS") < strModality.length()) return FS;
777       else if ( strModality.find("HC") < strModality.length()) return HC;
778       else if ( strModality.find("HD") < strModality.length()) return HD;
779       else if ( strModality.find("LP") < strModality.length()) return LP;
780       else if ( strModality.find("LS") < strModality.length()) return LS;
781       else if ( strModality.find("MA") < strModality.length()) return MA;
782       else if ( strModality.find("MR") < strModality.length()) return MR;
783       else if ( strModality.find("NM") < strModality.length()) return NM;
784       else if ( strModality.find("OT") < strModality.length()) return OT;
785       else if ( strModality.find("PT") < strModality.length()) return PT;
786       else if ( strModality.find("RF") < strModality.length()) return RF;
787       else if ( strModality.find("RG") < strModality.length()) return RG;
788       else if ( strModality.find("RTDOSE")   < strModality.length()) return RTDOSE;
789       else if ( strModality.find("RTIMAGE")  < strModality.length()) return RTIMAGE;
790       else if ( strModality.find("RTPLAN")   < strModality.length()) return RTPLAN;
791       else if ( strModality.find("RTSTRUCT") < strModality.length()) return RTSTRUCT;
792       else if ( strModality.find("SM") < strModality.length()) return SM;
793       else if ( strModality.find("ST") < strModality.length()) return ST;
794       else if ( strModality.find("TG") < strModality.length()) return TG;
795       else if ( strModality.find("US") < strModality.length()) return US;
796       else if ( strModality.find("VF") < strModality.length()) return VF;
797       else if ( strModality.find("XA") < strModality.length()) return XA;
798       else if ( strModality.find("XC") < strModality.length()) return XC;
799
800       else
801       {
802          /// \todo throw error return value ???
803          /// specified <> unknow in our database
804          return Unknow;
805       }
806    }
807
808    return Unknow;
809 }
810
811 /**
812  * \brief   Retrieve the number of Bits Stored (actually used)
813  *          (as opposite to number of Bits Allocated)
814  * @return  The encountered number of Bits Stored, 0 by default.
815  *          0 means the file is NOT USABLE. The caller has to check it !
816  */
817 int Header::GetBitsStored()
818 {
819    std::string strSize = GetEntry( 0x0028, 0x0101 );
820    if ( strSize == GDCM_UNFOUND )
821    {
822       gdcmVerboseMacro("(0028,0101) is supposed to be mandatory");
823       return 0;  // It's supposed to be mandatory
824                  // the caller will have to check
825    }
826    return atoi( strSize.c_str() );
827 }
828
829 /**
830  * \brief   Retrieve the high bit position.
831  * \warning The method defaults to 0 when information is Missing.
832  *          The responsability of checking this value is left to the caller.
833  * @return  The high bit positin when present. 0 when Missing.
834  */
835 int Header::GetHighBitPosition()
836 {
837    std::string strSize = GetEntry( 0x0028, 0x0102 );
838    if ( strSize == GDCM_UNFOUND )
839    {
840       gdcmVerboseMacro( "(0028,0102) is supposed to be mandatory");
841       return 0;
842    }
843    return atoi( strSize.c_str() );
844 }
845
846 /**
847  * \brief   Check wether the pixels are signed or UNsigned data.
848  * \warning The method defaults to false (UNsigned) when information is Missing.
849  *          The responsability of checking this value is left to the caller.
850  * @return  True when signed, false when UNsigned
851  */
852 bool Header::IsSignedPixelData()
853 {
854    std::string strSize = GetEntry( 0x0028, 0x0103 );
855    if ( strSize == GDCM_UNFOUND )
856    {
857       gdcmVerboseMacro( "(0028,0103) is supposed to be mandatory");
858       return false;
859    }
860    int sign = atoi( strSize.c_str() );
861    if ( sign == 0 ) 
862    {
863       return false;
864    }
865    return true;
866 }
867
868 /**
869  * \brief   Retrieve the number of Bits Allocated
870  *          (8, 12 -compacted ACR-NEMA files, 16, ...)
871  * @return  The encountered number of Bits Allocated, 0 by default.
872  *          0 means the file is NOT USABLE. The caller has to check it !
873  */
874 int Header::GetBitsAllocated()
875 {
876    std::string strSize = GetEntry(0x0028,0x0100);
877    if ( strSize == GDCM_UNFOUND )
878    {
879       gdcmVerboseMacro( "(0028,0100) is supposed to be mandatory");
880       return 0; // It's supposed to be mandatory
881                 // the caller will have to check
882    }
883    return atoi( strSize.c_str() );
884 }
885
886 /**
887  * \brief   Retrieve the number of Samples Per Pixel
888  *          (1 : gray level, 3 : RGB -1 or 3 Planes-)
889  * @return  The encountered number of Samples Per Pixel, 1 by default.
890  *          (Gray level Pixels)
891  */
892 int Header::GetSamplesPerPixel()
893 {
894    const std::string& strSize = GetEntry(0x0028,0x0002);
895    if ( strSize == GDCM_UNFOUND )
896    {
897       gdcmVerboseMacro( "(0028,0002) is supposed to be mandatory");
898       return 1; // Well, it's supposed to be mandatory ...
899                 // but sometimes it's missing : *we* assume Gray pixels
900    }
901    return atoi( strSize.c_str() );
902 }
903
904 /**
905  * \brief   Check wether this a monochrome picture or not by accessing
906  *          the "Photometric Interpretation" tag ( 0x0028, 0x0004 ).
907  * @return  true when "MONOCHROME1" or "MONOCHROME2". False otherwise.
908  */
909 bool Header::IsMonochrome()
910 {
911    const std::string& PhotometricInterp = GetEntry( 0x0028, 0x0004 );
912    if (  Util::DicomStringEqual(PhotometricInterp, "MONOCHROME1")
913       || Util::DicomStringEqual(PhotometricInterp, "MONOCHROME2") )
914    {
915       return true;
916    }
917    if ( PhotometricInterp == GDCM_UNFOUND )
918    {
919       gdcmVerboseMacro( "Not found : Photometric Interpretation (0028,0004)");
920    }
921    return false;
922 }
923
924 /**
925  * \brief   Check wether this a "PALETTE COLOR" picture or not by accessing
926  *          the "Photometric Interpretation" tag ( 0x0028, 0x0004 ).
927  * @return  true when "PALETTE COLOR". False otherwise.
928  */
929 bool Header::IsPaletteColor()
930 {
931    std::string PhotometricInterp = GetEntry( 0x0028, 0x0004 );
932    if (   PhotometricInterp == "PALETTE COLOR " )
933    {
934       return true;
935    }
936    if ( PhotometricInterp == GDCM_UNFOUND )
937    {
938       gdcmVerboseMacro( "Not found : Palette color (0028,0004)");
939    }
940    return false;
941 }
942
943 /**
944  * \brief   Check wether this a "YBR_FULL" color picture or not by accessing
945  *          the "Photometric Interpretation" tag ( 0x0028, 0x0004 ).
946  * @return  true when "YBR_FULL". False otherwise.
947  */
948 bool Header::IsYBRFull()
949 {
950    std::string PhotometricInterp = GetEntry( 0x0028, 0x0004 );
951    if (   PhotometricInterp == "YBR_FULL" )
952    {
953       return true;
954    }
955    if ( PhotometricInterp == GDCM_UNFOUND )
956    {
957       gdcmVerboseMacro( "Not found : YBR Full (0028,0004)");
958    }
959    return false;
960 }
961
962 /**
963  * \brief   Retrieve the Planar Configuration for RGB images
964  *          (0 : RGB Pixels , 1 : R Plane + G Plane + B Plane)
965  * @return  The encountered Planar Configuration, 0 by default.
966  */
967 int Header::GetPlanarConfiguration()
968 {
969    std::string strSize = GetEntry(0x0028,0x0006);
970    if ( strSize == GDCM_UNFOUND )
971    {
972       gdcmVerboseMacro( "Not found : Planar Configuration (0028,0006)");
973       return 0;
974    }
975    return atoi( strSize.c_str() );
976 }
977
978 /**
979  * \brief   Return the size (in bytes) of a single pixel of data.
980  * @return  The size in bytes of a single pixel of data; 0 by default
981  *          0 means the file is NOT USABLE; the caller will have to check
982  */
983 int Header::GetPixelSize()
984 {
985    // 0028 0100 US IMG Bits Allocated
986    // (in order no to be messed up by old RGB images)
987    //   if (Header::GetEntry(0x0028,0x0100) == "24")
988    //      return 3;
989
990    std::string pixelType = GetPixelType();
991    if ( pixelType ==  "8U" || pixelType == "8S" )
992    {
993       return 1;
994    }
995    if ( pixelType == "16U" || pixelType == "16S")
996    {
997       return 2;
998    }
999    if ( pixelType == "32U" || pixelType == "32S")
1000    {
1001       return 4;
1002    }
1003    if ( pixelType == "FD" )
1004    {
1005       return 8;
1006    }
1007    gdcmVerboseMacro( "Unknown pixel type");
1008    return 0;
1009 }
1010
1011 /**
1012  * \brief   Build the Pixel Type of the image.
1013  *          Possible values are:
1014  *          - 8U  unsigned  8 bit,
1015  *          - 8S    signed  8 bit,
1016  *          - 16U unsigned 16 bit,
1017  *          - 16S   signed 16 bit,
1018  *          - 32U unsigned 32 bit,
1019  *          - 32S   signed 32 bit,
1020  *          - FD floating double 64 bits (Not kosher DICOM, but so usefull!)
1021  * \warning 12 bit images appear as 16 bit.
1022  *          24 bit images appear as 8 bit
1023  * @return  0S if nothing found. NOT USABLE file. The caller has to check
1024  */
1025 std::string Header::GetPixelType()
1026 {
1027    std::string bitsAlloc = GetEntry(0x0028, 0x0100); // Bits Allocated
1028    if ( bitsAlloc == GDCM_UNFOUND )
1029    {
1030       gdcmVerboseMacro( "Missing  Bits Allocated (0028,0100)");
1031       bitsAlloc = "16";
1032    }
1033
1034    if ( bitsAlloc == "64" )
1035    {
1036       return "FD";
1037    }
1038    else if ( bitsAlloc == "12" )
1039    {
1040       // It will be unpacked
1041       bitsAlloc = "16";
1042    }
1043    else if ( bitsAlloc == "24" )
1044    {
1045       // (in order no to be messed up
1046       bitsAlloc = "8";  // by old RGB images)
1047    }
1048
1049    std::string sign = GetEntry(0x0028, 0x0103);//"Pixel Representation"
1050
1051    if (sign == GDCM_UNFOUND )
1052    {
1053       gdcmVerboseMacro( "Missing Pixel Representation (0028,0103)");
1054       bitsAlloc = "0";
1055    }
1056    else if ( sign == "0" )
1057    {
1058       sign = "U";
1059    }
1060    else
1061    {
1062       sign = "S";
1063    }
1064    return bitsAlloc + sign;
1065 }
1066
1067
1068 /**
1069  * \brief   Recover the offset (from the beginning of the file) 
1070  *          of *image* pixels (not *icone image* pixels, if any !)
1071  * @return Pixel Offset
1072  */
1073 size_t Header::GetPixelOffset()
1074 {
1075    DocEntry* pxlElement = GetDocEntry(GrPixel,NumPixel);
1076    if ( pxlElement )
1077    {
1078       return pxlElement->GetOffset();
1079    }
1080    else
1081    {
1082 #ifdef GDCM_DEBUG
1083       std::cout << "Big trouble : Pixel Element ("
1084                 << std::hex << GrPixel<<","<< NumPixel<< ") NOT found"
1085                 << std::endl;  
1086 #endif //GDCM_DEBUG
1087       return 0;
1088    }
1089 }
1090
1091 /// \todo TODO : unify those two (previous one and next one)
1092 /**
1093  * \brief   Recover the pixel area length (in Bytes)
1094  * @return Pixel Element Length, as stored in the header
1095  *         (NOT the memory space necessary to hold the Pixels 
1096  *          -in case of embeded compressed image-)
1097  *         0 : NOT USABLE file. The caller has to check.
1098  */
1099 size_t Header::GetPixelAreaLength()
1100 {
1101    DocEntry* pxlElement = GetDocEntry(GrPixel,NumPixel);
1102    if ( pxlElement )
1103    {
1104       return pxlElement->GetLength();
1105    }
1106    else
1107    {
1108 #ifdef GDCM_DEBUG
1109       std::cout << "Big trouble : Pixel Element ("
1110                 << std::hex << GrPixel<<","<< NumPixel<< ") NOT found"
1111                 << std::endl;
1112 #endif //GDCM_DEBUG
1113       return 0;
1114    }
1115 }
1116
1117 /**
1118   * \brief tells us if LUT are used
1119   * \warning Right now, 'Segmented xxx Palette Color Lookup Table Data'
1120   *          are NOT considered as LUT, since nobody knows
1121   *          how to deal with them
1122   *          Please warn me if you know sbdy that *does* know ... jprx
1123   * @return true if LUT Descriptors and LUT Tables were found 
1124   */
1125 bool Header::HasLUT()
1126 {
1127    // Check the presence of the LUT Descriptors, and LUT Tables    
1128    // LutDescriptorRed    
1129    if ( !GetDocEntry(0x0028,0x1101) )
1130    {
1131       return false;
1132    }
1133    // LutDescriptorGreen 
1134    if ( !GetDocEntry(0x0028,0x1102) )
1135    {
1136       return false;
1137    }
1138    // LutDescriptorBlue 
1139    if ( !GetDocEntry(0x0028,0x1103) )
1140    {
1141       return false;
1142    }
1143    // Red Palette Color Lookup Table Data
1144    if ( !GetDocEntry(0x0028,0x1201) )
1145    {
1146       return false;
1147    }
1148    // Green Palette Color Lookup Table Data       
1149    if ( !GetDocEntry(0x0028,0x1202) )
1150    {
1151       return false;
1152    }
1153    // Blue Palette Color Lookup Table Data      
1154    if ( !GetDocEntry(0x0028,0x1203) )
1155    {
1156       return false;
1157    }
1158
1159    // FIXME : (0x0028,0x3006) : LUT Data (CTX dependent)
1160    //         NOT taken into account, but we don't know how to use it ...   
1161    return true;
1162 }
1163
1164 /**
1165   * \brief gets the info from 0028,1101 : Lookup Table Desc-Red
1166   *             else 0
1167   * @return Lookup Table number of Bits , 0 by default
1168   *          when (0028,0004),Photometric Interpretation = [PALETTE COLOR ]
1169   * @ return bit number of each LUT item 
1170   */
1171 int Header::GetLUTNbits()
1172 {
1173    std::vector<std::string> tokens;
1174    int lutNbits;
1175
1176    //Just hope Lookup Table Desc-Red = Lookup Table Desc-Red
1177    //                                = Lookup Table Desc-Blue
1178    // Consistency already checked in GetLUTLength
1179    std::string lutDescription = GetEntry(0x0028,0x1101);
1180    if ( lutDescription == GDCM_UNFOUND )
1181    {
1182       return 0;
1183    }
1184
1185    tokens.clear(); // clean any previous value
1186    Util::Tokenize ( lutDescription, tokens, "\\" );
1187    //LutLength=atoi(tokens[0].c_str());
1188    //LutDepth=atoi(tokens[1].c_str());
1189
1190    lutNbits = atoi( tokens[2].c_str() );
1191    tokens.clear();
1192
1193    return lutNbits;
1194 }
1195
1196 /**
1197  * \brief Accesses the info from 0002,0010 : Transfer Syntax and TS
1198  *        else 1.
1199  * @return The full Transfer Syntax Name (as opposed to Transfer Syntax UID)
1200  */
1201 std::string Header::GetTransferSyntaxName()
1202 {
1203    // use the TS (TS : Transfer Syntax)
1204    std::string transferSyntax = GetEntry(0x0002,0x0010);
1205
1206    if ( transferSyntax == GDCM_NOTLOADED )
1207    {
1208       gdcmVerboseMacro( "Transfer Syntax not loaded. " << std::endl
1209                << "Better you increase MAX_SIZE_LOAD_ELEMENT_VALUE" );
1210       return "Uncompressed ACR-NEMA";
1211    }
1212    if ( transferSyntax == GDCM_UNFOUND )
1213    {
1214       gdcmVerboseMacro( "Unfound Transfer Syntax (0002,0010)");
1215       return "Uncompressed ACR-NEMA";
1216    }
1217
1218    while ( ! isdigit((unsigned char)transferSyntax[transferSyntax.length()-1]) )
1219    {
1220       transferSyntax.erase(transferSyntax.length()-1, 1);
1221    }
1222    // we do it only when we need it
1223    TS* ts         = Global::GetTS();
1224    std::string tsName = ts->GetValue( transferSyntax );
1225
1226    //delete ts; /// \todo Seg Fault when deleted ?!
1227    return tsName;
1228 }
1229
1230 //-----------------------------------------------------------------------------
1231 // Protected
1232
1233 /**
1234  * \brief anonymize a Header (removes Patient's personal info)
1235  *        (read the code to see which ones ...)
1236  */
1237 bool Header::AnonymizeHeader()
1238 {
1239    // If exist, replace by spaces
1240    SetEntry ("  ",0x0010, 0x2154); // Telephone   
1241    SetEntry ("  ",0x0010, 0x1040); // Adress
1242    SetEntry ("  ",0x0010, 0x0020); // Patient ID
1243
1244    DocEntry* patientNameHE = GetDocEntry (0x0010, 0x0010);
1245   
1246    if ( patientNameHE ) // we replace it by Study Instance UID (why not)
1247    {
1248       std::string studyInstanceUID =  GetEntry (0x0020, 0x000d);
1249       if ( studyInstanceUID != GDCM_UNFOUND )
1250       {
1251          ReplaceOrCreate(studyInstanceUID, 0x0010, 0x0010);
1252       }
1253       else
1254       {
1255          ReplaceOrCreate("anonymised", 0x0010, 0x0010);
1256       }
1257    }
1258
1259   // Just for fun :-(
1260   // (if any) remove or replace all the stuff that contains a Date
1261
1262 //0008 0012 DA ID Instance Creation Date
1263 //0008 0020 DA ID Study Date
1264 //0008 0021 DA ID Series Date
1265 //0008 0022 DA ID Acquisition Date
1266 //0008 0023 DA ID Content Date
1267 //0008 0024 DA ID Overlay Date
1268 //0008 0025 DA ID Curve Date
1269 //0008 002a DT ID Acquisition Datetime
1270 //0018 9074 DT ACQ Frame Acquisition Datetime
1271 //0018 9151 DT ACQ Frame Reference Datetime
1272 //0018 a002 DT ACQ Contribution Date Time
1273 //0020 3403 SH REL Modified Image Date (RET)
1274 //0032 0032 DA SDY Study Verified Date
1275 //0032 0034 DA SDY Study Read Date
1276 //0032 1000 DA SDY Scheduled Study Start Date
1277 //0032 1010 DA SDY Scheduled Study Stop Date
1278 //0032 1040 DA SDY Study Arrival Date
1279 //0032 1050 DA SDY Study Completion Date
1280 //0038 001a DA VIS Scheduled Admission Date
1281 //0038 001c DA VIS Scheduled Discharge Date
1282 //0038 0020 DA VIS Admitting Date
1283 //0038 0030 DA VIS Discharge Date
1284 //0040 0002 DA PRC Scheduled Procedure Step Start Date
1285 //0040 0004 DA PRC Scheduled Procedure Step End Date
1286 //0040 0244 DA PRC Performed Procedure Step Start Date
1287 //0040 0250 DA PRC Performed Procedure Step End Date
1288 //0040 2004 DA PRC Issue Date of Imaging Service Request
1289 //0040 4005 DT PRC Scheduled Procedure Step Start Date and Time
1290 //0040 4011 DT PRC Expected Completion Date and Time
1291 //0040 a030 DT PRC Verification Date Time
1292 //0040 a032 DT PRC Observation Date Time
1293 //0040 a120 DT PRC DateTime
1294 //0040 a121 DA PRC Date
1295 //0040 a13a DT PRC Referenced Datetime
1296 //0070 0082 DA ??? Presentation Creation Date
1297 //0100 0420 DT ??? SOP Autorization Date and Time
1298 //0400 0105 DT ??? Digital Signature DateTime
1299 //2100 0040 DA PJ Creation Date
1300 //3006 0008 DA SSET Structure Set Date
1301 //3008 0024 DA ??? Treatment Control Point Date
1302 //3008 0054 DA ??? First Treatment Date
1303 //3008 0056 DA ??? Most Recent Treatment Date
1304 //3008 0162 DA ??? Safe Position Exit Date
1305 //3008 0166 DA ??? Safe Position Return Date
1306 //3008 0250 DA ??? Treatment Date
1307 //300a 0006 DA RT RT Plan Date
1308 //300a 022c DA RT Air Kerma Rate Reference Date
1309 //300e 0004 DA RT Review Date
1310
1311    return true;
1312 }
1313
1314 /**
1315   * \brief gets the info from 0020,0037 : Image Orientation Patient
1316   * @param iop adress of the (6)float aray to receive values
1317   * @return cosines of image orientation patient
1318   */
1319 void Header::GetImageOrientationPatient( float iop[6] )
1320 {
1321    std::string strImOriPat;
1322    //iop is supposed to be float[6]
1323    iop[0] = iop[1] = iop[2] = iop[3] = iop[4] = iop[5] = 0.;
1324
1325    // 0020 0037 DS REL Image Orientation (Patient)
1326    if ( (strImOriPat = GetEntry(0x0020,0x0037)) != GDCM_UNFOUND )
1327    {
1328       if( sscanf( strImOriPat.c_str(), "%f\\%f\\%f\\%f\\%f\\%f", 
1329           &iop[0], &iop[1], &iop[2], &iop[3], &iop[4], &iop[5]) != 6 )
1330       {
1331          gdcmVerboseMacro( "Wrong Image Orientation Patient (0020,0037). Less than 6 values were found." );
1332       }
1333    }
1334    //For ACR-NEMA
1335    // 0020 0035 DS REL Image Orientation (RET)
1336    else if ( (strImOriPat = GetEntry(0x0020,0x0035)) != GDCM_UNFOUND )
1337    {
1338       if( sscanf( strImOriPat.c_str(), "%f\\%f\\%f\\%f\\%f\\%f", 
1339           &iop[0], &iop[1], &iop[2], &iop[3], &iop[4], &iop[5]) != 6 )
1340       {
1341          gdcmVerboseMacro( "wrong Image Orientation Patient (0020,0035). Less than 6 values were found." );
1342       }
1343    }
1344 }
1345
1346 /**
1347  * \brief Initialize a default DICOM header that should contain all the
1348  *        field require by other reader. DICOM standard does not 
1349  *        explicitely defines those fields, heuristic has been choosen.
1350  *        This is not perfect as we are writting a CT image...
1351  */
1352 void Header::InitializeDefaultHeader()
1353 {
1354    typedef struct
1355    {
1356       const char *value;
1357       uint16_t group;
1358       uint16_t elem;
1359    } DICOM_DEFAULT_VALUE;
1360
1361    std::string date = Util::GetCurrentDate();
1362    std::string time = Util::GetCurrentTime();
1363    std::string uid = Util::CreateUniqueUID();
1364    std::string uidMedia = uid;
1365    std::string uidClass = uid + ".1";
1366    std::string uidInst  = uid + ".10";
1367    std::string uidStudy = uid + ".100";
1368    std::string uidSerie = uid + ".1000";
1369
1370    static DICOM_DEFAULT_VALUE defaultvalue[] = {
1371     { "146 ",                      0x0002, 0x0000}, // Meta Element Group Length // FIXME: how to recompute ?
1372     { "1.2.840.10008.5.1.4.1.1.2", 0x0002, 0x0002}, // Media Storage SOP Class UID (CT Image Storage)
1373     { uidClass.c_str(),            0x0002, 0x0003}, // Media Storage SOP Instance UID
1374     { uidClass.c_str(),            0x0002, 0x0012}, // META Implementation Class UID
1375     { "GDCM",                      0x0002, 0x0016}, // Source Application Entity Title
1376
1377     { date.c_str(),                0x0008, 0x0012}, // Instance Creation Date
1378     { time.c_str(),                0x0008, 0x0013}, // Instance Creation Time
1379     { "1.2.840.10008.5.1.4.1.1.2", 0x0008, 0x0016}, // SOP Class UID
1380     { uidInst.c_str(),             0x0008, 0x0018}, // SOP Instance UID
1381     { "CT",                        0x0008, 0x0060}, // Modality    
1382     { "GDCM",                      0x0008, 0x0070}, // Manufacturer
1383     { "GDCM",                      0x0008, 0x0080}, // Institution Name
1384     { "http://www-creatis.insa-lyon.fr/Public/Gdcm", 0x0008, 0x0081},  // Institution Address
1385
1386     { "GDCM",                      0x0010, 0x0010}, // Patient's Name
1387     { "GDCMID",                    0x0010, 0x0020}, // Patient ID
1388
1389     { uidStudy.c_str(),            0x0020, 0x000d}, // Study Instance UID
1390     { uidSerie.c_str(),            0x0020, 0x000e}, // Series Instance UID
1391     { "1",                         0x0020, 0x0010}, // StudyID
1392     { "1",                         0x0020, 0x0011}, // SeriesNumber
1393
1394     { "1",                         0x0028, 0x0002}, // Samples per pixel 1 or 3
1395     { "MONOCHROME1",               0x0028, 0x0004}, // photochromatic interpretation
1396     { "0",                         0x0028, 0x0010}, // nbRows
1397     { "0",                         0x0028, 0x0011}, // nbCols
1398     { "8",                         0x0028, 0x0100}, // BitsAllocated 8 or 12 or 16
1399     { "8",                         0x0028, 0x0101}, // BitsStored    <= BitsAllocated
1400     { "7",                         0x0028, 0x0102}, // HighBit       <= BitsAllocated - 1
1401     { "0",                         0x0028, 0x0103}, // Pixel Representation 0(unsigned) or 1(signed)
1402     { 0, 0, 0 }
1403    };
1404
1405    // default value
1406    // Special case this is the image (not a string)
1407    GrPixel = 0x7fe0;
1408    NumPixel = 0x0010;
1409    ReplaceOrCreate(0, 0, GrPixel, NumPixel);
1410
1411    // All remaining strings:
1412    unsigned int i = 0;
1413    DICOM_DEFAULT_VALUE current = defaultvalue[i];
1414    while( current.value )
1415    {
1416       ReplaceOrCreate(current.value, current.group, current.elem);
1417       current = defaultvalue[++i];
1418    }
1419 }
1420
1421
1422 //-----------------------------------------------------------------------------
1423 // Private
1424
1425 //-----------------------------------------------------------------------------
1426
1427 } // end namespace gdcm