]> Creatis software - gdcm.git/blob - src/gdcmHeader.cxx
* src/gdcmDocEntrySet.[h|cxx], gdcmDocument.[h|cxx] : amelioration of
[gdcm.git] / src / gdcmHeader.cxx
1 /*=========================================================================
2                                                                                 
3   Program:   gdcm
4   Module:    $RCSfile: gdcmHeader.cxx,v $
5   Language:  C++
6   Date:      $Date: 2005/01/06 15:36:48 $
7   Version:   $Revision: 1.220 $
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 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    // 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 = GetDocEntryByNumber(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 fp file pointer on an already open file
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       dbg.Verbose(2, "Failed to open (write) File: " , fileName.c_str());
135       return false;
136    }
137
138    // Bits Allocated
139    if ( GetEntryByNumber(0x0028,0x0100) ==  "12")
140    {
141       SetEntryByNumber("16", 0x0028,0x0100);
142    }
143
144   /// \todo correct 'Pixel group' Length if necessary
145
146    int i_lgPix = GetEntryLengthByNumber(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       ReplaceOrCreateByNumber(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 ( GetEntryByNumber(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 = GetDocEntryByNumber(0x0028,0x01101);
167       if (e)
168       {
169          RemoveEntryNoDestroy(e);
170       }
171       e = GetDocEntryByNumber(0x0028,0x1102);
172       if (e)
173       {
174          RemoveEntryNoDestroy(e);
175       }
176       e = GetDocEntryByNumber(0x0028,0x1103);
177       if (e)
178       {
179          RemoveEntryNoDestroy(e);
180       }
181       e = GetDocEntryByNumber(0x0028,0x01201);
182       if (e)
183       {
184          RemoveEntryNoDestroy(e);
185       }
186       e = GetDocEntryByNumber(0x0028,0x1202);
187       if (e)
188       {
189          RemoveEntryNoDestroy(e);
190       }
191       e = GetDocEntryByNumber(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 = GetEntryByNumber(0x0028, 0x0005);
228    if ( res != GDCM_UNFOUND && atoi(res.c_str()) > 4 )
229    {
230       return false; // Image Dimensions
231    }
232    if ( !GetDocEntryByNumber(0x0028, 0x0100) )
233    {
234       return false; // "Bits Allocated"
235    }
236    if ( !GetDocEntryByNumber(0x0028, 0x0101) )
237    {
238       return false; // "Bits Stored"
239    }
240    if ( !GetDocEntryByNumber(0x0028, 0x0102) )
241    {
242       return false; // "High Bit"
243    }
244    if ( !GetDocEntryByNumber(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 = GetEntryByNumber(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 = GetEntryByNumber(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 absent 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 = GetEntryByNumber(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 = GetEntryByNumber(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 = GetEntryByNumber(0x0028,0x0030);
328
329    if ( strSpacing == GDCM_UNFOUND )
330    {
331       dbg.Verbose(0, "Header::GetXSpacing: 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       dbg.Verbose(0, "Header::GetXSpacing: 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       assert( 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 = GetEntryByNumber(0x0028,0x0030);
376   
377    if ( strSpacing == GDCM_UNFOUND )
378    {
379       dbg.Verbose(0, "Header::GetYSpacing: 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 absent, 
408    //   on suppose que les coupes sont jointives
409    
410    const std::string &strSpacingBSlices = GetEntryByNumber(0x0018,0x0088);
411
412    if ( strSpacingBSlices == GDCM_UNFOUND )
413    {
414       dbg.Verbose(0, "Header::GetZSpacing: unfound StrSpacingBSlices");
415       const std::string &strSliceThickness = GetEntryByNumber(0x0018,0x0050);       
416       if ( strSliceThickness == GDCM_UNFOUND )
417       {
418          return 1.;
419       }
420       else
421       {
422          // if no 'Spacing Between Slices' is found, 
423          // we assume slices join together
424          // (no overlapping, no interslice gap)
425          // if they don't, we're fucked up
426          return (float)atof( strSliceThickness.c_str() );
427       }
428    }
429    //else
430    return (float)atof( strSpacingBSlices.c_str() );
431 }
432
433 /**
434  *\brief gets the info from 0028,1052 : Rescale Intercept
435  * @return Rescale Intercept
436  */
437 float Header::GetRescaleIntercept()
438 {
439    float resInter = 0.;
440    /// 0028 1052 DS IMG Rescale Intercept
441    const std::string &strRescInter = GetEntryByNumber(0x0028,0x1052);
442    if ( strRescInter != GDCM_UNFOUND )
443    {
444       if( sscanf( strRescInter.c_str(), "%f", &resInter) != 1 )
445       {
446          // bug in the element 0x0028,0x1052
447          dbg.Verbose(0, "Header::GetRescaleIntercept: Rescale Slope "
448                         "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 = GetEntryByNumber(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          dbg.Verbose(0, "Header::GetRescaleSlope: Rescale Slope 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 ( GetEntryByNumber(0x0028,0x0100) == "24" )
493    {
494       return 3;
495    }
496        
497    std::string strPhotometricInterpretation = GetEntryByNumber(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::GetEntryByNumber(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 = GetEntryByNumber(0x0020,0x0032);
572
573    if ( strImPos == GDCM_UNFOUND )
574    {
575       dbg.Verbose(0, "Header::GetXImagePosition: unfound Image "
576                      "Position Patient (0020,0032)");
577       strImPos = GetEntryByNumber(0x0020,0x0030); // For ACR-NEMA images
578       if ( strImPos == GDCM_UNFOUND )
579       {
580          dbg.Verbose(0, "Header::GetXImagePosition: unfound Image "
581                         "Position (RET) (0020,0030)");
582          /// \todo How to tell the caller nothing was found ?
583          return 0.;
584       }
585    }
586
587    if( sscanf( strImPos.c_str(), "%f\\%f\\%f", &xImPos, &yImPos, &zImPos) != 3 )
588    {
589       return 0.;
590    }
591
592    return xImPos;
593 }
594
595 /**
596  * \brief gets the info from 0020,0032 : Image Position Patient
597  *                 else from 0020,0030 : Image Position (RET)
598  *                 else 0.
599  * @return up-left image corner Y position
600  */
601 float Header::GetYOrigin()
602 {
603    float xImPos, yImPos, zImPos;
604    std::string strImPos = GetEntryByNumber(0x0020,0x0032);
605
606    if ( strImPos == GDCM_UNFOUND)
607    {
608       dbg.Verbose(0, "Header::GetYImagePosition: unfound Image "
609                      "Position Patient (0020,0032)");
610       strImPos = GetEntryByNumber(0x0020,0x0030); // For ACR-NEMA images
611       if ( strImPos == GDCM_UNFOUND )
612       {
613          dbg.Verbose(0, "Header::GetYImagePosition: unfound Image "
614                         "Position (RET) (0020,0030)");
615          /// \todo How to tell the caller nothing was found ?
616          return 0.;
617       }  
618    }
619
620    if( sscanf( strImPos.c_str(), "%f\\%f\\%f", &xImPos, &yImPos, &zImPos) != 3 )
621    {
622       return 0.;
623    }
624
625    return yImPos;
626 }
627
628 /**
629  * \brief gets the info from 0020,0032 : Image Position Patient
630  *                 else from 0020,0030 : Image Position (RET)
631  *                 else from 0020,1041 : Slice Location
632  *                 else from 0020,0050 : Location
633  *                 else 0.
634  * @return up-left image corner Z position
635  */
636 float Header::GetZOrigin()
637 {
638    float xImPos, yImPos, zImPos; 
639    std::string strImPos = GetEntryByNumber(0x0020,0x0032);
640
641    if ( strImPos != GDCM_UNFOUND )
642    {
643       if( sscanf( strImPos.c_str(), "%f\\%f\\%f", &xImPos, &yImPos, &zImPos) != 3)
644       {
645          dbg.Verbose(0, "Header::GetZImagePosition: wrong Image "
646                         "Position Patient (0020,0032)");
647          return 0.;  // bug in the element 0x0020,0x0032
648       }
649       else
650       {
651          return zImPos;
652       }
653    }
654
655    strImPos = GetEntryByNumber(0x0020,0x0030); // For ACR-NEMA images
656    if ( strImPos != GDCM_UNFOUND )
657    {
658       if( sscanf( strImPos.c_str(), 
659           "%f\\%f\\%f", &xImPos, &yImPos, &zImPos ) != 3 )
660       {
661          dbg.Verbose(0, "Header::GetZImagePosition: wrong Image Position (RET) (0020,0030)");
662          return 0.;  // bug in the element 0x0020,0x0032
663       }
664       else
665       {
666          return zImPos;
667       }
668    }
669
670    std::string strSliceLocation = GetEntryByNumber(0x0020,0x1041); // for *very* old ACR-NEMA images
671    if ( strSliceLocation != GDCM_UNFOUND )
672    {
673       if( sscanf( strSliceLocation.c_str(), "%f", &zImPos) != 1)
674       {
675          dbg.Verbose(0, "Header::GetZImagePosition: wrong Slice Location (0020,1041)");
676          return 0.;  // bug in the element 0x0020,0x1041
677       }
678       else
679       {
680          return zImPos;
681       }
682    }
683    dbg.Verbose(0, "Header::GetZImagePosition: unfound Slice Location (0020,1041)");
684
685    std::string strLocation = GetEntryByNumber(0x0020,0x0050);
686    if ( strLocation != GDCM_UNFOUND )
687    {
688       if( sscanf( strLocation.c_str(), "%f", &zImPos) != 1)
689       {
690          dbg.Verbose(0, "Header::GetZImagePosition: wrong Location (0020,0050)");
691          return 0.;  // bug in the element 0x0020,0x0050
692       }
693       else
694       {
695          return zImPos;
696       }
697    }
698    dbg.Verbose(0, "Header::GetYImagePosition: unfound Location (0020,0050)");  
699
700    return 0.; // Hopeless
701 }
702
703 /**
704  * \brief gets the info from 0020,0013 : Image Number else 0.
705  * @return image number
706  */
707 int Header::GetImageNumber()
708 {
709    // The function i atoi() takes the address of an area of memory as
710    // parameter and converts the string stored at that location to an integer
711    // using the external decimal to internal binary conversion rules. This may
712    // be preferable to sscanf() since atoi() is a much smaller, simpler and
713    // faster function. sscanf() can do all possible conversions whereas
714    // atoi() can only do single decimal integer conversions.
715    //0020 0013 IS REL Image Number
716    std::string strImNumber = GetEntryByNumber(0x0020,0x0013);
717    if ( strImNumber != GDCM_UNFOUND )
718    {
719       return atoi( strImNumber.c_str() );
720    }
721    return 0;   //Hopeless
722 }
723
724 /**
725  * \brief gets the info from 0008,0060 : Modality
726  * @return Modality Type
727  */
728 ModalityType Header::GetModality()
729 {
730    // 0008 0060 CS ID Modality
731    std::string strModality = GetEntryByNumber(0x0008,0x0060);
732    if ( strModality != GDCM_UNFOUND )
733    {
734            if ( strModality.find("AU") < strModality.length()) return AU;
735       else if ( strModality.find("AS") < strModality.length()) return AS;
736       else if ( strModality.find("BI") < strModality.length()) return BI;
737       else if ( strModality.find("CF") < strModality.length()) return CF;
738       else if ( strModality.find("CP") < strModality.length()) return CP;
739       else if ( strModality.find("CR") < strModality.length()) return CR;
740       else if ( strModality.find("CT") < strModality.length()) return CT;
741       else if ( strModality.find("CS") < strModality.length()) return CS;
742       else if ( strModality.find("DD") < strModality.length()) return DD;
743       else if ( strModality.find("DF") < strModality.length()) return DF;
744       else if ( strModality.find("DG") < strModality.length()) return DG;
745       else if ( strModality.find("DM") < strModality.length()) return DM;
746       else if ( strModality.find("DS") < strModality.length()) return DS;
747       else if ( strModality.find("DX") < strModality.length()) return DX;
748       else if ( strModality.find("ECG") < strModality.length()) return ECG;
749       else if ( strModality.find("EPS") < strModality.length()) return EPS;
750       else if ( strModality.find("FA") < strModality.length()) return FA;
751       else if ( strModality.find("FS") < strModality.length()) return FS;
752       else if ( strModality.find("HC") < strModality.length()) return HC;
753       else if ( strModality.find("HD") < strModality.length()) return HD;
754       else if ( strModality.find("LP") < strModality.length()) return LP;
755       else if ( strModality.find("LS") < strModality.length()) return LS;
756       else if ( strModality.find("MA") < strModality.length()) return MA;
757       else if ( strModality.find("MR") < strModality.length()) return MR;
758       else if ( strModality.find("NM") < strModality.length()) return NM;
759       else if ( strModality.find("OT") < strModality.length()) return OT;
760       else if ( strModality.find("PT") < strModality.length()) return PT;
761       else if ( strModality.find("RF") < strModality.length()) return RF;
762       else if ( strModality.find("RG") < strModality.length()) return RG;
763       else if ( strModality.find("RTDOSE")   < strModality.length()) return RTDOSE;
764       else if ( strModality.find("RTIMAGE")  < strModality.length()) return RTIMAGE;
765       else if ( strModality.find("RTPLAN")   < strModality.length()) return RTPLAN;
766       else if ( strModality.find("RTSTRUCT") < strModality.length()) return RTSTRUCT;
767       else if ( strModality.find("SM") < strModality.length()) return SM;
768       else if ( strModality.find("ST") < strModality.length()) return ST;
769       else if ( strModality.find("TG") < strModality.length()) return TG;
770       else if ( strModality.find("US") < strModality.length()) return US;
771       else if ( strModality.find("VF") < strModality.length()) return VF;
772       else if ( strModality.find("XA") < strModality.length()) return XA;
773       else if ( strModality.find("XC") < strModality.length()) return XC;
774
775       else
776       {
777          /// \todo throw error return value ???
778          /// specified <> unknow in our database
779          return Unknow;
780       }
781    }
782
783    return Unknow;
784 }
785
786 /**
787  * \brief   Retrieve the number of Bits Stored (actually used)
788  *          (as opposite to number of Bits Allocated)
789  * @return  The encountered number of Bits Stored, 0 by default.
790  *          0 means the file is NOT USABLE. The caller has to check it !
791  */
792 int Header::GetBitsStored()
793 {
794    std::string strSize = GetEntryByNumber( 0x0028, 0x0101 );
795    if ( strSize == GDCM_UNFOUND )
796    {
797       dbg.Verbose(0, "Header::GetBitsStored: this is supposed to "
798                      "be mandatory");
799       return 0;  // It's supposed to be mandatory
800                  // the caller will have to check
801    }
802    return atoi( strSize.c_str() );
803 }
804
805 /**
806  * \brief   Retrieve the high bit position.
807  * \warning The method defaults to 0 when information is absent.
808  *          The responsability of checking this value is left to the caller.
809  * @return  The high bit positin when present. 0 when absent.
810  */
811 int Header::GetHighBitPosition()
812 {
813    std::string strSize = GetEntryByNumber( 0x0028, 0x0102 );
814    if ( strSize == GDCM_UNFOUND )
815    {
816       dbg.Verbose(0, "Header::GetHighBitPosition: this is supposed "
817                      "to be mandatory");
818       return 0;
819    }
820    return atoi( strSize.c_str() );
821 }
822
823 /**
824  * \brief   Check wether the pixels are signed or UNsigned data.
825  * \warning The method defaults to false (UNsigned) when information is absent.
826  *          The responsability of checking this value is left to the caller.
827  * @return  True when signed, false when UNsigned
828  */
829 bool Header::IsSignedPixelData()
830 {
831    std::string strSize = GetEntryByNumber( 0x0028, 0x0103 );
832    if ( strSize == GDCM_UNFOUND )
833    {
834       dbg.Verbose(0, "Header::IsSignedPixelData: this is supposed "
835                      "to be mandatory");
836       return false;
837    }
838    int sign = atoi( strSize.c_str() );
839    if ( sign == 0 ) 
840    {
841       return false;
842    }
843    return true;
844 }
845
846 /**
847  * \brief   Retrieve the number of Bits Allocated
848  *          (8, 12 -compacted ACR-NEMA files, 16, ...)
849  * @return  The encountered number of Bits Allocated, 0 by default.
850  *          0 means the file is NOT USABLE. The caller has to check it !
851  */
852 int Header::GetBitsAllocated()
853 {
854    std::string strSize = GetEntryByNumber(0x0028,0x0100);
855    if ( strSize == GDCM_UNFOUND )
856    {
857       dbg.Verbose(0, "Header::GetBitsStored: this is supposed to "
858                      "be mandatory");
859       return 0; // It's supposed to be mandatory
860                 // the caller will have to check
861    }
862    return atoi( strSize.c_str() );
863 }
864
865 /**
866  * \brief   Retrieve the number of Samples Per Pixel
867  *          (1 : gray level, 3 : RGB -1 or 3 Planes-)
868  * @return  The encountered number of Samples Per Pixel, 1 by default.
869  *          (Gray level Pixels)
870  */
871 int Header::GetSamplesPerPixel()
872 {
873    const std::string& strSize = GetEntryByNumber(0x0028,0x0002);
874    if ( strSize == GDCM_UNFOUND )
875    {
876       dbg.Verbose(0, "Header::GetBitsStored: this is supposed to "
877                      "be mandatory");
878       return 1; // Well, it's supposed to be mandatory ...
879                 // but sometimes it's missing : *we* assume Gray pixels
880    }
881    return atoi( strSize.c_str() );
882 }
883
884 /**
885  * \brief   Check wether this a monochrome picture or not by accessing
886  *          the "Photometric Interpretation" tag ( 0x0028, 0x0004 ).
887  * @return  true when "MONOCHROME1" or "MONOCHROME2". False otherwise.
888  */
889 bool Header::IsMonochrome()
890 {
891    const std::string& PhotometricInterp = GetEntryByNumber( 0x0028, 0x0004 );
892    if (  Util::DicomStringEqual(PhotometricInterp, "MONOCHROME1")
893       || Util::DicomStringEqual(PhotometricInterp, "MONOCHROME2") )
894    {
895       return true;
896    }
897    if ( PhotometricInterp == GDCM_UNFOUND )
898    {
899       dbg.Verbose(0, "Header::IsMonochrome: absent Photometric "
900                      "Interpretation");
901    }
902    return false;
903 }
904
905 /**
906  * \brief   Check wether this a "PALETTE COLOR" picture or not by accessing
907  *          the "Photometric Interpretation" tag ( 0x0028, 0x0004 ).
908  * @return  true when "PALETTE COLOR". False otherwise.
909  */
910 bool Header::IsPaletteColor()
911 {
912    std::string PhotometricInterp = GetEntryByNumber( 0x0028, 0x0004 );
913    if (   PhotometricInterp == "PALETTE COLOR " )
914    {
915       return true;
916    }
917    if ( PhotometricInterp == GDCM_UNFOUND )
918    {
919       dbg.Verbose(0, "Header::IsPaletteColor: absent Photometric "
920                      "Interpretation");
921    }
922    return false;
923 }
924
925 /**
926  * \brief   Check wether this a "YBR_FULL" color picture or not by accessing
927  *          the "Photometric Interpretation" tag ( 0x0028, 0x0004 ).
928  * @return  true when "YBR_FULL". False otherwise.
929  */
930 bool Header::IsYBRFull()
931 {
932    std::string PhotometricInterp = GetEntryByNumber( 0x0028, 0x0004 );
933    if (   PhotometricInterp == "YBR_FULL" )
934    {
935       return true;
936    }
937    if ( PhotometricInterp == GDCM_UNFOUND )
938    {
939       dbg.Verbose(0, "Header::IsYBRFull: absent Photometric "
940                      "Interpretation");
941    }
942    return false;
943 }
944
945 /**
946  * \brief   Retrieve the Planar Configuration for RGB images
947  *          (0 : RGB Pixels , 1 : R Plane + G Plane + B Plane)
948  * @return  The encountered Planar Configuration, 0 by default.
949  */
950 int Header::GetPlanarConfiguration()
951 {
952    std::string strSize = GetEntryByNumber(0x0028,0x0006);
953    if ( strSize == GDCM_UNFOUND )
954    {
955       return 0;
956    }
957    return atoi( strSize.c_str() );
958 }
959
960 /**
961  * \brief   Return the size (in bytes) of a single pixel of data.
962  * @return  The size in bytes of a single pixel of data; 0 by default
963  *          0 means the file is NOT USABLE; the caller will have to check
964  */
965 int Header::GetPixelSize()
966 {
967    // 0028 0100 US IMG Bits Allocated
968    // (in order no to be messed up by old RGB images)
969    //   if (Header::GetEntryByNumber(0x0028,0x0100) == "24")
970    //      return 3;
971
972    std::string pixelType = GetPixelType();
973    if ( pixelType ==  "8U" || pixelType == "8S" )
974    {
975       return 1;
976    }
977    if ( pixelType == "16U" || pixelType == "16S")
978    {
979       return 2;
980    }
981    if ( pixelType == "32U" || pixelType == "32S")
982    {
983       return 4;
984    }
985    if ( pixelType == "FD" )
986    {
987       return 8;
988    }
989    dbg.Verbose(0, "Header::GetPixelSize: Unknown pixel type");
990    return 0;
991 }
992
993 /**
994  * \brief   Build the Pixel Type of the image.
995  *          Possible values are:
996  *          - 8U  unsigned  8 bit,
997  *          - 8S    signed  8 bit,
998  *          - 16U unsigned 16 bit,
999  *          - 16S   signed 16 bit,
1000  *          - 32U unsigned 32 bit,
1001  *          - 32S   signed 32 bit,
1002  *          - FD floating double 64 bits (Not kosher DICOM, but so usefull!)
1003  * \warning 12 bit images appear as 16 bit.
1004  *          24 bit images appear as 8 bit
1005  * @return  0S if nothing found. NOT USABLE file. The caller has to check
1006  */
1007 std::string Header::GetPixelType()
1008 {
1009    std::string bitsAlloc = GetEntryByNumber(0x0028, 0x0100); // Bits Allocated
1010    if ( bitsAlloc == GDCM_UNFOUND )
1011    {
1012       dbg.Verbose(0, "Header::GetPixelType: unfound Bits Allocated");
1013       bitsAlloc = "16";
1014    }
1015
1016    if ( bitsAlloc == "64" )
1017    {
1018       return "FD";
1019    }
1020    else if ( bitsAlloc == "12" )
1021    {
1022       // It will be unpacked
1023       bitsAlloc = "16";
1024    }
1025    else if ( bitsAlloc == "24" )
1026    {
1027       // (in order no to be messed up
1028       bitsAlloc = "8";  // by old RGB images)
1029    }
1030
1031    std::string sign = GetEntryByNumber(0x0028, 0x0103);//"Pixel Representation"
1032
1033    if (sign == GDCM_UNFOUND )
1034    {
1035       dbg.Verbose(0, "Header::GetPixelType: unfound Pixel Representation");
1036       bitsAlloc = "0";
1037    }
1038    else if ( sign == "0" )
1039    {
1040       sign = "U";
1041    }
1042    else
1043    {
1044       sign = "S";
1045    }
1046    return bitsAlloc + sign;
1047 }
1048
1049
1050 /**
1051  * \brief   Recover the offset (from the beginning of the file) 
1052  *          of *image* pixels (not *icone image* pixels, if any !)
1053  * @return Pixel Offset
1054  */
1055 size_t Header::GetPixelOffset()
1056 {
1057    DocEntry* pxlElement = GetDocEntryByNumber(GrPixel,NumPixel);
1058    if ( pxlElement )
1059    {
1060       return pxlElement->GetOffset();
1061    }
1062    else
1063    {
1064 #ifdef GDCM_DEBUG
1065       std::cout << "Big trouble : Pixel Element ("
1066                 << std::hex << GrPixel<<","<< NumPixel<< ") NOT found"
1067                 << std::endl;  
1068 #endif //GDCM_DEBUG
1069       return 0;
1070    }
1071 }
1072
1073 /// \todo TODO : unify those two (previous one and next one)
1074 /**
1075  * \brief   Recover the pixel area length (in Bytes)
1076  * @return Pixel Element Length, as stored in the header
1077  *         (NOT the memory space necessary to hold the Pixels 
1078  *          -in case of embeded compressed image-)
1079  *         0 : NOT USABLE file. The caller has to check.
1080  */
1081 size_t Header::GetPixelAreaLength()
1082 {
1083    DocEntry* pxlElement = GetDocEntryByNumber(GrPixel,NumPixel);
1084    if ( pxlElement )
1085    {
1086       return pxlElement->GetLength();
1087    }
1088    else
1089    {
1090 #ifdef GDCM_DEBUG
1091       std::cout << "Big trouble : Pixel Element ("
1092                 << std::hex << GrPixel<<","<< NumPixel<< ") NOT found"
1093                 << std::endl;
1094 #endif //GDCM_DEBUG
1095       return 0;
1096    }
1097 }
1098
1099 /**
1100   * \brief tells us if LUT are used
1101   * \warning Right now, 'Segmented xxx Palette Color Lookup Table Data'
1102   *          are NOT considered as LUT, since nobody knows
1103   *          how to deal with them
1104   *          Please warn me if you know sbdy that *does* know ... jprx
1105   * @return true if LUT Descriptors and LUT Tables were found 
1106   */
1107 bool Header::HasLUT()
1108 {
1109    // Check the presence of the LUT Descriptors, and LUT Tables    
1110    // LutDescriptorRed    
1111    if ( !GetDocEntryByNumber(0x0028,0x1101) )
1112    {
1113       return false;
1114    }
1115    // LutDescriptorGreen 
1116    if ( !GetDocEntryByNumber(0x0028,0x1102) )
1117    {
1118       return false;
1119    }
1120    // LutDescriptorBlue 
1121    if ( !GetDocEntryByNumber(0x0028,0x1103) )
1122    {
1123       return false;
1124    }
1125    // Red Palette Color Lookup Table Data
1126    if ( !GetDocEntryByNumber(0x0028,0x1201) )
1127    {
1128       return false;
1129    }
1130    // Green Palette Color Lookup Table Data       
1131    if ( !GetDocEntryByNumber(0x0028,0x1202) )
1132    {
1133       return false;
1134    }
1135    // Blue Palette Color Lookup Table Data      
1136    if ( !GetDocEntryByNumber(0x0028,0x1203) )
1137    {
1138       return false;
1139    }
1140
1141    // FIXME : (0x0028,0x3006) : LUT Data (CTX dependent)
1142    //         NOT taken into account, but we don't know how to use it ...   
1143    return true;
1144 }
1145
1146 /**
1147   * \brief gets the info from 0028,1101 : Lookup Table Desc-Red
1148   *             else 0
1149   * @return Lookup Table number of Bits , 0 by default
1150   *          when (0028,0004),Photometric Interpretation = [PALETTE COLOR ]
1151   * @ return bit number of each LUT item 
1152   */
1153 int Header::GetLUTNbits()
1154 {
1155    std::vector<std::string> tokens;
1156    int lutNbits;
1157
1158    //Just hope Lookup Table Desc-Red = Lookup Table Desc-Red
1159    //                                = Lookup Table Desc-Blue
1160    // Consistency already checked in GetLUTLength
1161    std::string lutDescription = GetEntryByNumber(0x0028,0x1101);
1162    if ( lutDescription == GDCM_UNFOUND )
1163    {
1164       return 0;
1165    }
1166
1167    tokens.clear(); // clean any previous value
1168    Util::Tokenize ( lutDescription, tokens, "\\" );
1169    //LutLength=atoi(tokens[0].c_str());
1170    //LutDepth=atoi(tokens[1].c_str());
1171
1172    lutNbits = atoi( tokens[2].c_str() );
1173    tokens.clear();
1174
1175    return lutNbits;
1176 }
1177
1178 /**
1179  * \brief Accesses the info from 0002,0010 : Transfert Syntax and TS
1180  *        else 1.
1181  * @return The full Transfert Syntax Name (as opposed to Transfert Syntax UID)
1182  */
1183 std::string Header::GetTransfertSyntaxName()
1184 {
1185    // use the TS (TS : Transfert Syntax)
1186    std::string transfertSyntax = GetEntryByNumber(0x0002,0x0010);
1187
1188    if ( transfertSyntax == GDCM_NOTLOADED )
1189    {
1190       std::cout << "Transfert Syntax not loaded. " << std::endl
1191                << "Better you increase MAX_SIZE_LOAD_ELEMENT_VALUE"
1192                << std::endl;
1193       return "Uncompressed ACR-NEMA";
1194    }
1195    if ( transfertSyntax == GDCM_UNFOUND )
1196    {
1197       dbg.Verbose(0, "Header::GetTransfertSyntaxName:"
1198                      " unfound Transfert Syntax (0002,0010)");
1199       return "Uncompressed ACR-NEMA";
1200    }
1201
1202    while ( ! isdigit((unsigned char)transfertSyntax[transfertSyntax.length()-1]) )
1203    {
1204       transfertSyntax.erase(transfertSyntax.length()-1, 1);
1205    }
1206    // we do it only when we need it
1207    TS* ts         = Global::GetTS();
1208    std::string tsName = ts->GetValue( transfertSyntax );
1209
1210    //delete ts; /// \todo Seg Fault when deleted ?!
1211    return tsName;
1212 }
1213
1214 //-----------------------------------------------------------------------------
1215 // Protected
1216
1217 /**
1218  * \brief anonymize a Header (removes Patient's personal info)
1219  *        (read the code to see which ones ...)
1220  */
1221 bool Header::AnonymizeHeader()
1222 {
1223    // If exist, replace by spaces
1224    SetEntryByNumber ("  ",0x0010, 0x2154); // Telephone   
1225    SetEntryByNumber ("  ",0x0010, 0x1040); // Adress
1226    SetEntryByNumber ("  ",0x0010, 0x0020); // Patient ID
1227
1228    DocEntry* patientNameHE = GetDocEntryByNumber (0x0010, 0x0010);
1229   
1230    if ( patientNameHE ) // we replace it by Study Instance UID (why not)
1231    {
1232       std::string studyInstanceUID =  GetEntryByNumber (0x0020, 0x000d);
1233       if ( studyInstanceUID != GDCM_UNFOUND )
1234       {
1235          ReplaceOrCreateByNumber(studyInstanceUID, 0x0010, 0x0010);
1236       }
1237       else
1238       {
1239          ReplaceOrCreateByNumber("anonymised", 0x0010, 0x0010);
1240       }
1241    }
1242
1243   // Just for fun :-(
1244   // (if any) remove or replace all the stuff that contains a Date
1245
1246 //0008 0012 DA ID Instance Creation Date
1247 //0008 0020 DA ID Study Date
1248 //0008 0021 DA ID Series Date
1249 //0008 0022 DA ID Acquisition Date
1250 //0008 0023 DA ID Content Date
1251 //0008 0024 DA ID Overlay Date
1252 //0008 0025 DA ID Curve Date
1253 //0008 002a DT ID Acquisition Datetime
1254 //0018 9074 DT ACQ Frame Acquisition Datetime
1255 //0018 9151 DT ACQ Frame Reference Datetime
1256 //0018 a002 DT ACQ Contribution Date Time
1257 //0020 3403 SH REL Modified Image Date (RET)
1258 //0032 0032 DA SDY Study Verified Date
1259 //0032 0034 DA SDY Study Read Date
1260 //0032 1000 DA SDY Scheduled Study Start Date
1261 //0032 1010 DA SDY Scheduled Study Stop Date
1262 //0032 1040 DA SDY Study Arrival Date
1263 //0032 1050 DA SDY Study Completion Date
1264 //0038 001a DA VIS Scheduled Admission Date
1265 //0038 001c DA VIS Scheduled Discharge Date
1266 //0038 0020 DA VIS Admitting Date
1267 //0038 0030 DA VIS Discharge Date
1268 //0040 0002 DA PRC Scheduled Procedure Step Start Date
1269 //0040 0004 DA PRC Scheduled Procedure Step End Date
1270 //0040 0244 DA PRC Performed Procedure Step Start Date
1271 //0040 0250 DA PRC Performed Procedure Step End Date
1272 //0040 2004 DA PRC Issue Date of Imaging Service Request
1273 //0040 4005 DT PRC Scheduled Procedure Step Start Date and Time
1274 //0040 4011 DT PRC Expected Completion Date and Time
1275 //0040 a030 DT PRC Verification Date Time
1276 //0040 a032 DT PRC Observation Date Time
1277 //0040 a120 DT PRC DateTime
1278 //0040 a121 DA PRC Date
1279 //0040 a13a DT PRC Referenced Datetime
1280 //0070 0082 DA ??? Presentation Creation Date
1281 //0100 0420 DT ??? SOP Autorization Date and Time
1282 //0400 0105 DT ??? Digital Signature DateTime
1283 //2100 0040 DA PJ Creation Date
1284 //3006 0008 DA SSET Structure Set Date
1285 //3008 0024 DA ??? Treatment Control Point Date
1286 //3008 0054 DA ??? First Treatment Date
1287 //3008 0056 DA ??? Most Recent Treatment Date
1288 //3008 0162 DA ??? Safe Position Exit Date
1289 //3008 0166 DA ??? Safe Position Return Date
1290 //3008 0250 DA ??? Treatment Date
1291 //300a 0006 DA RT RT Plan Date
1292 //300a 022c DA RT Air Kerma Rate Reference Date
1293 //300e 0004 DA RT Review Date
1294
1295    return true;
1296 }
1297
1298 /**
1299   * \brief gets the info from 0020,0037 : Image Orientation Patient
1300   * @param iop adress of the (6)float aray to receive values
1301   * @return cosines of image orientation patient
1302   */
1303 void Header::GetImageOrientationPatient( float iop[6] )
1304 {
1305    std::string strImOriPat;
1306    //iop is supposed to be float[6]
1307    iop[0] = iop[1] = iop[2] = iop[3] = iop[4] = iop[5] = 0.;
1308
1309    // 0020 0037 DS REL Image Orientation (Patient)
1310    if ( (strImOriPat = GetEntryByNumber(0x0020,0x0037)) != GDCM_UNFOUND )
1311    {
1312       if( sscanf( strImOriPat.c_str(), "%f\\%f\\%f\\%f\\%f\\%f", 
1313           &iop[0], &iop[1], &iop[2], &iop[3], &iop[4], &iop[5]) != 6 )
1314       {
1315          dbg.Verbose(0, "Header::GetImageOrientationPatient: "
1316                         "wrong Image Orientation Patient (0020,0037)");
1317       }
1318    }
1319    //For ACR-NEMA
1320    // 0020 0035 DS REL Image Orientation (RET)
1321    else if ( (strImOriPat = GetEntryByNumber(0x0020,0x0035)) != GDCM_UNFOUND )
1322    {
1323       if( sscanf( strImOriPat.c_str(), "%f\\%f\\%f\\%f\\%f\\%f", 
1324           &iop[0], &iop[1], &iop[2], &iop[3], &iop[4], &iop[5]) != 6 )
1325       {
1326          dbg.Verbose(0, "Header::GetImageOrientationPatient: "
1327                         "wrong Image Orientation Patient (0020,0035)");
1328       }
1329    }
1330 }
1331
1332 /**
1333  * \brief Initialize a default DICOM header that should contain all the
1334  *        field require by other reader. DICOM standard does not 
1335  *        explicitely defines those fields, heuristic has been choosen.
1336  *        This is not perfect as we are writting a CT image...
1337  */
1338 void Header::InitializeDefaultHeader()
1339 {
1340    typedef struct
1341    {
1342       const char *value;
1343       uint16_t group;
1344       uint16_t elem;
1345    } DICOM_DEFAULT_VALUE;
1346
1347    std::string date = Util::GetCurrentDate();
1348    std::string time = Util::GetCurrentTime();
1349    std::string uid = Util::CreateUniqueUID();
1350    std::string uidMedia = uid;
1351    std::string uidClass = uid + ".1";
1352    std::string uidInst  = uid + ".10";
1353    std::string uidStudy = uid + ".100";
1354    std::string uidSerie = uid + ".1000";
1355
1356    static DICOM_DEFAULT_VALUE defaultvalue[] = {
1357     { "146 ",                      0x0002, 0x0000}, // Meta Element Group Length // FIXME: how to recompute ?
1358     { "1.2.840.10008.5.1.4.1.1.2", 0x0002, 0x0002}, // Media Storage SOP Class UID (CT Image Storage)
1359     { uidClass.c_str(),            0x0002, 0x0003}, // Media Storage SOP Instance UID
1360     { uidClass.c_str(),            0x0002, 0x0012}, // META Implementation Class UID
1361     { "GDCM",                      0x0002, 0x0016}, // Source Application Entity Title
1362
1363     { date.c_str(),                0x0008, 0x0012}, // Instance Creation Date
1364     { time.c_str(),                0x0008, 0x0013}, // Instance Creation Time
1365     { "1.2.840.10008.5.1.4.1.1.2", 0x0008, 0x0016}, // SOP Class UID
1366     { uidInst.c_str(),             0x0008, 0x0018}, // SOP Instance UID
1367     { "CT",                        0x0008, 0x0060}, // Modality    
1368     { "GDCM",                      0x0008, 0x0070}, // Manufacturer
1369     { "GDCM",                      0x0008, 0x0080}, // Institution Name
1370     { "http://www-creatis.insa-lyon.fr/Public/Gdcm", 0x0008, 0x0081},  // Institution Address
1371
1372     { "GDCM",                      0x0010, 0x0010}, // Patient's Name
1373     { "GDCMID",                    0x0010, 0x0020}, // Patient ID
1374
1375     { uidStudy.c_str(),            0x0020, 0x000d}, // Study Instance UID
1376     { uidSerie.c_str(),            0x0020, 0x000e}, // Series Instance UID
1377     { "1",                         0x0020, 0x0010}, // StudyID
1378     { "1",                         0x0020, 0x0011}, // SeriesNumber
1379
1380     { "1",                         0x0028, 0x0002}, // Samples per pixel 1 or 3
1381     { "MONOCHROME1",               0x0028, 0x0004}, // photochromatic interpretation
1382     { "0",                         0x0028, 0x0010}, // nbRows
1383     { "0",                         0x0028, 0x0011}, // nbCols
1384     { "8",                         0x0028, 0x0100}, // BitsAllocated 8 or 12 or 16
1385     { "8",                         0x0028, 0x0101}, // BitsStored    <= BitsAllocated
1386     { "7",                         0x0028, 0x0102}, // HighBit       <= BitsAllocated - 1
1387     { "0",                         0x0028, 0x0103}, // Pixel Representation 0(unsigned) or 1(signed)
1388     { 0, 0, 0 }
1389    };
1390
1391    // default value
1392    // Special case this is the image (not a string)
1393    GrPixel = 0x7fe0;
1394    NumPixel = 0x0010;
1395    ReplaceOrCreateByNumber(0, 0, GrPixel, NumPixel);
1396
1397    // All remaining strings:
1398    unsigned int i = 0;
1399    DICOM_DEFAULT_VALUE current = defaultvalue[i];
1400    while( current.value )
1401    {
1402       ReplaceOrCreateByNumber(current.value, current.group, current.elem);
1403       current = defaultvalue[++i];
1404    }
1405 }
1406
1407
1408 //-----------------------------------------------------------------------------
1409 // Private
1410
1411 //-----------------------------------------------------------------------------
1412
1413 } // end namespace gdcm