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