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