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