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