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