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