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