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