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