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