]> Creatis software - gdcm.git/blob - src/gdcmHeaderHelper.cxx
1b1af705be2c14e6c164129a9f881278fac274c4
[gdcm.git] / src / gdcmHeaderHelper.cxx
1 /*=========================================================================
2                                                                                 
3   Program:   gdcm
4   Module:    $RCSfile: gdcmHeaderHelper.cxx,v $
5   Language:  C++
6   Date:      $Date: 2004/06/20 18:08:48 $
7   Version:   $Revision: 1.37 $
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 "gdcmHeaderHelper.h"
20 #include "gdcmDirList.h"
21
22 #include "gdcmDebug.h"
23 #include <math.h>
24 #include <algorithm>
25 #include <vector>
26
27 //-----------------------------------------------------------------------------
28 /**
29  * \brief   constructor
30  */
31 gdcmHeaderHelper::gdcmHeaderHelper() : gdcmHeader( ) {
32
33 }
34
35 /**
36  * \brief   constructor
37  * @param   InFilename Name of the file to deal with
38  * @param   exception_on_error
39  * @param   enable_sequences = true to allow the header 
40  *          to be parsed *inside* the SeQuences, 
41  *          when they have an actual length 
42  * @param   ignore_shadow = true if user wants to skip shadow groups 
43  *           during parsing, to save memory space         
44  */
45 gdcmHeaderHelper::gdcmHeaderHelper(const char *InFilename, 
46                                    bool exception_on_error,
47                                    bool enable_sequences,
48                                    bool ignore_shadow)                             
49                  : gdcmHeader( InFilename, 
50                                exception_on_error,
51                                enable_sequences,
52                                ignore_shadow)
53 {
54 }
55
56 //-----------------------------------------------------------------------------
57 // Print
58
59 //-----------------------------------------------------------------------------
60 // Public
61 /**
62  * \brief   Returns the size (in bytes) of a single pixel of data.
63  * @return  The size in bytes of a single pixel of data.
64  *
65  */
66 int gdcmHeaderHelper::GetPixelSize() {
67
68      // 0028 0100 US IMG Bits Allocated
69      // (in order no to be messed up by old RGB images)
70    if (gdcmHeader::GetEntryByNumber(0x0028,0x0100) == "24")
71       return 3;
72          
73    std::string PixelType = GetPixelType();
74    if (PixelType == "8U"  || PixelType == "8S")
75       return 1;
76    if (PixelType == "16U" || PixelType == "16S")
77       return 2;
78    if (PixelType == "32U" || PixelType == "32S")
79       return 4;
80    if (PixelType == "FD") // to help unfortunate users to manage DOUBLE
81       return 8;
82    dbg.Verbose(0, "gdcmHeader::GetPixelSize: Unknown pixel type");
83    return 0;
84 }
85
86 /**
87  * \ingroup gdcmHeaderHelper
88  * \brief   Build the Pixel Type of the image.
89  *          Possible values are:
90  *          - 8U  unsigned  8 bit,
91  *          - 8S    signed  8 bit,
92  *          - 16U unsigned 16 bit,
93  *          - 16S   signed 16 bit,
94  *          - 32U unsigned 32 bit,
95  *          - 32S   signed 32 bit,
96  *          - FD    Double,
97  * \warning 12 bit images appear as 16 bit.
98  *          24 bit images appear as 8 bit
99  *          64 bit means 'DOUBLE' images 
100  *                 (no DOUBLE images in kosher DICOM,
101  *                  but so usefull for people that miss them ;-)
102  * @return  
103  */
104 std::string gdcmHeaderHelper::GetPixelType() {
105    std::string BitsAlloc;
106    BitsAlloc = GetEntryByNumber(0x0028, 0x0100);
107    if (BitsAlloc == GDCM_UNFOUND) { // Bits Allocated
108       dbg.Verbose(0, "gdcmHeader::GetPixelType: unfound Bits Allocated");
109       BitsAlloc = std::string("16");
110    }
111    if (BitsAlloc == "12")           // It will be unpacked
112       BitsAlloc = std::string("16");
113    else if (BitsAlloc == "24")      // (in order no to be messed up
114       BitsAlloc = std::string("8"); // by old RGB images)
115     
116    std::string Signed;
117    Signed = GetEntryByNumber(0x0028, 0x0103);
118    if (Signed == GDCM_UNFOUND) { // "Pixel Representation"
119       dbg.Verbose(0, "gdcmHeader::GetPixelType: unfound Pixel Representation");
120       BitsAlloc = std::string("0");
121    }
122    if (BitsAlloc == "64") // to help users that want to deal with DOUBLE
123       return("FD");
124       
125    if (Signed == "0")
126       Signed = std::string("U");
127    else
128       Signed = std::string("S");
129
130    return( BitsAlloc + Signed);
131 }
132
133 /**
134   * \ingroup gdcmHeaderHelper
135   * \brief gets the info from 0028,0030 : Pixel Spacing
136   *             else 1.0
137   * @return X dimension of a pixel
138   */
139 float gdcmHeaderHelper::GetXSpacing() {
140     float xspacing, yspacing;
141     std::string StrSpacing = GetEntryByNumber(0x0028,0x0030);
142     
143    if (StrSpacing == GDCM_UNFOUND) {
144       dbg.Verbose(0, "gdcmHeader::GetXSpacing: unfound Pixel Spacing (0028,0030)");
145       return 1.;
146     }
147   int nbValues;
148   if( (nbValues = sscanf( StrSpacing.c_str(), "%f\\%f", &yspacing, &xspacing)) != 2) {
149     if (nbValues==1)  // if single value is found, xspacing is defaulted to yspacing
150        return yspacing;
151   }  
152   if (xspacing == 0.) {
153     dbg.Verbose(0, "gdcmHeader::GetYSpacing: gdcmData/CT-MONO2-8-abdo.dcm problem");
154     // seems to be a bug in the header ...
155     sscanf( StrSpacing.c_str(), "%f\\0\\%f", &yspacing, &xspacing);
156   }
157   return xspacing;
158 }
159
160 /**
161   * \ingroup gdcmHeaderHelper
162   * \brief gets the info from 0028,0030 : Pixel Spacing
163   *             else 1.0
164   * @return Y dimension of a pixel
165   */
166 float gdcmHeaderHelper::GetYSpacing() {
167    float yspacing;
168    std::string StrSpacing = GetEntryByNumber(0x0028,0x0030);
169   
170    if (StrSpacing == GDCM_UNFOUND) {
171       dbg.Verbose(0, "gdcmHeader::GetYSpacing: unfound Pixel Spacing (0028,0030)");
172       return 1.;
173     }
174   sscanf( StrSpacing.c_str(), "%f", &yspacing);
175   return yspacing;
176
177
178 /**
179   *\ingroup gdcmHeaderHelper
180   *\brief gets the info from 0018,0088 : Space Between Slices
181   *                else from 0018,0050 : Slice Thickness
182    *                else 1.0
183   * @return Z dimension of a voxel-to be
184   */
185 float gdcmHeaderHelper::GetZSpacing() {
186    // Spacing Between Slices : distance entre le milieu de chaque coupe
187    // Les coupes peuvent etre :
188    //   jointives     (Spacing between Slices = Slice Thickness)
189    //   chevauchantes (Spacing between Slices < Slice Thickness)
190    //   disjointes    (Spacing between Slices > Slice Thickness)
191    // Slice Thickness : epaisseur de tissus sur laquelle est acquis le signal
192    //   ca interesse le physicien de l'IRM, pas le visualisateur de volumes ...
193    //   Si le Spacing Between Slices est absent, 
194    //   on suppose que les coupes sont jointives
195    
196    std::string StrSpacingBSlices = GetEntryByNumber(0x0018,0x0088);
197
198    if (StrSpacingBSlices == GDCM_UNFOUND) {
199       dbg.Verbose(0, "gdcmHeader::GetZSpacing: unfound StrSpacingBSlices");
200       std::string StrSliceThickness = GetEntryByNumber(0x0018,0x0050);       
201       if (StrSliceThickness == GDCM_UNFOUND)
202          return 1.;
203       else
204          // if no 'Spacing Between Slices' is found, 
205          // we assume slices join together
206          // (no overlapping, no interslice gap)
207          // if they don't, we're fucked up
208          return atof(StrSliceThickness.c_str());  
209    } else {
210       return atof(StrSpacingBSlices.c_str());
211    }
212 }
213
214 /**
215   *\ingroup gdcmHeaderHelper
216   *\brief gets the info from 0028,1052 : Rescale Intercept
217   * @return Rescale Intercept
218  */
219 float gdcmHeaderHelper::GetRescaleIntercept() {
220   float resInter = 0.;
221   std::string StrRescInter = GetEntryByNumber(0x0028,0x1052); //0028 1052 DS IMG Rescale Intercept
222   if (StrRescInter != GDCM_UNFOUND) {
223       if( sscanf( StrRescInter.c_str(), "%f", &resInter) != 1) {
224          dbg.Verbose(0, "gdcmHeader::GetRescaleIntercept: Rescale Slope is empty");
225            // bug in the element 0x0028,0x1052
226       }    
227    }
228   return resInter;
229 }
230
231 /**
232   *\ingroup gdcmHeaderHelper
233   *\brief gets the info from 0028,1053 : Rescale Slope
234   * @return Rescale Slope
235  */
236  float gdcmHeaderHelper::GetRescaleSlope() {
237   float resSlope = 1.;
238   std::string StrRescSlope = GetEntryByNumber(0x0028,0x1053); //0028 1053 DS IMG Rescale Slope
239   if (StrRescSlope != GDCM_UNFOUND) {
240       if( sscanf( StrRescSlope.c_str(), "%f", &resSlope) != 1) {
241          dbg.Verbose(0, "gdcmHeader::GetRescaleSlope: Rescale Slope is empty");
242            // bug in the element 0x0028,0x1053
243       }    
244    }  
245    return resSlope;
246 }
247
248 /**
249   * \ingroup gdcmHeaderHelper
250   * \brief This function is intended to user who doesn't want 
251   *   to have to manage a LUT and expects to get an RBG Pixel image
252   *   (or a monochrome one ...) 
253   * \warning to be used with GetImagePixels()
254   * @return 1 if Gray level, 3 if Color (RGB, YBR or PALETTE COLOR)
255   */
256 int gdcmHeaderHelper::GetNumberOfScalarComponents() {
257    if (GetSamplesPerPixel() ==3)
258       return 3;
259       
260      // 0028 0100 US IMG Bits Allocated
261      // (in order no to be messed up by old RGB images)
262    if (gdcmHeader::GetEntryByNumber(0x0028,0x0100) == "24")
263       return 3;
264        
265    std::string PhotometricInterpretation = 
266                   gdcmHeader::GetEntryByNumber(0x0028,0x0004);
267
268    if ( ( PhotometricInterpretation == "PALETTE COLOR ") ) {
269       if (HasLUT())   // PALETTE COLOR is NOT enough
270          return 3;
271       else
272          return 1;       
273    }   
274                   
275       //beware of trailing space at end of string                                               
276    if (PhotometricInterpretation.find(GDCM_UNFOUND) < 
277                            PhotometricInterpretation.length() || 
278        PhotometricInterpretation.find("MONOCHROME1") < 
279                            PhotometricInterpretation.length() || 
280        PhotometricInterpretation.find("MONOCHROME2") < 
281                            PhotometricInterpretation.length() ) 
282        return 1;
283     else
284     // we assume that *all* kinds of YBR are dealt with
285       return 3;
286 }
287
288 /**
289   * \ingroup gdcmHeaderHelper
290   * \brief This function is intended to user that DOESN'T want 
291   *  to get RGB pixels image when it's stored as a PALETTE COLOR image
292   *   - the (vtk) user is supposed to know how deal with LUTs - 
293   * \warning to be used with GetImagePixelsRaw()
294   * @return 1 if Gray level, 3 if Color (RGB or YBR - NOT 'PALETTE COLOR' -)
295   */
296 int gdcmHeaderHelper::GetNumberOfScalarComponentsRaw() {
297       
298      // 0028 0100 US IMG Bits Allocated
299      // (in order no to be messed up by old RGB images)
300    if (gdcmHeader::GetEntryByNumber(0x0028,0x0100) == "24")
301       return 3;
302
303     // we assume that *all* kinds of YBR are dealt with
304       return GetSamplesPerPixel();
305 }
306
307 /**
308   *\ingroup gdcmHeaderHelper
309   *\brief gets the info from 0020,000d : Study Instance UID
310   *\todo ? : return the ACR-NEMA element value if DICOM one is not found 
311   * @return Study Instance UID
312  */
313  std::string gdcmHeaderHelper::GetStudyUID(){
314   return GetEntryByNumber(0x0020,0x000d); //0020 000d UI REL Study Instance UID
315 }
316
317 /**
318   *\ingroup gdcmHeaderHelper
319   *\brief gets the info from 0020,000e : Series Instance UID
320   *\todo ? : return the ACR-NEMA element value if DICOM one is not found 
321   * @return Series Instance UID
322  */
323  std::string gdcmHeaderHelper::GetSeriesUID(){
324   return GetEntryByNumber(0x0020,0x000e); //0020 000e UI REL Series Instance UID
325 }
326
327 /**
328   *\ingroup gdcmHeaderHelper
329   *\brief gets the info from 0008,0016 : SOP Class UID
330   *\todo ? : return the ACR-NEMA element value if DICOM one is not found 
331   * @return SOP Class UID
332  */
333  std::string gdcmHeaderHelper::GetClassUID(){
334   return GetEntryByNumber(0x0008,0x0016); //0008 0016 UI ID SOP Class UID
335 }
336
337 /**
338   *\brief gets the info from 0008,0018 : SOP Instance UID
339   *\todo ? : return the ACR-NEMA element value if DICOM one is not found 
340   * @return SOP Instance UID
341  */
342  std::string gdcmHeaderHelper::GetInstanceUID(){
343   return GetEntryByNumber(0x0008,0x0018); //0008 0018 UI ID SOP Instance UID
344 }
345 //
346 // --------------  Remember ! ----------------------------------
347 //
348 // Image Position Patient                              (0020,0032):
349 // If not found (ACR_NEMA) we try Image Position       (0020,0030)
350 // If not found (ACR-NEMA), we consider Slice Location (0020,1041)
351 //                                   or Location       (0020,0050) 
352 // as the Z coordinate, 
353 // 0. for all the coordinates if nothing is found
354
355 // \todo find a way to inform the caller nothing was found
356 // \todo How to tell the caller a wrong number of values was found?
357 //
358 // ---------------------------------------------------------------
359 //
360
361 /**
362   * \brief gets the info from 0020,0032 : Image Position Patient
363   *                 else from 0020,0030 : Image Position (RET)
364   *                 else 0.
365   * @return up-left image corner X position
366   */
367     
368 float gdcmHeaderHelper::GetXOrigin() {
369     float xImPos, yImPos, zImPos;  
370     std::string StrImPos = GetEntryByNumber(0x0020,0x0032);
371
372     if (StrImPos == GDCM_UNFOUND) {
373        dbg.Verbose(0, "gdcmHeader::GetXImagePosition: unfound Image Position Patient (0020,0032)");
374        StrImPos = GetEntryByNumber(0x0020,0x0030); // For ACR-NEMA images
375        if (StrImPos == GDCM_UNFOUND) {
376           dbg.Verbose(0, "gdcmHeader::GetXImagePosition: unfound Image Position (RET) (0020,0030)");
377           /// \todo How to tell the caller nothing was found ?
378          return 0.;
379        }  
380      }
381    if( sscanf( StrImPos.c_str(), "%f\\%f\\%f", &xImPos, &yImPos, &zImPos) != 3)
382      return 0.;
383    return xImPos;
384 }
385
386 /**
387   * \brief gets the info from 0020,0032 : Image Position Patient
388   *                 else from 0020,0030 : Image Position (RET)
389   *                 else 0.
390   * @return up-left image corner Y position
391   */
392 float gdcmHeaderHelper::GetYOrigin() {
393     float xImPos, yImPos, zImPos;
394     std::string StrImPos = GetEntryByNumber(0x0020,0x0032);
395
396     if (StrImPos == GDCM_UNFOUND) {
397        dbg.Verbose(0, "gdcmHeader::GetYImagePosition: unfound Image Position Patient (0020,0032)");
398        StrImPos = GetEntryByNumber(0x0020,0x0030); // For ACR-NEMA images
399        if (StrImPos == GDCM_UNFOUND) {
400           dbg.Verbose(0, "gdcmHeader::GetYImagePosition: unfound Image Position (RET) (0020,0030)");
401           /// \todo How to tell the caller nothing was found ?
402            return 0.;
403        }  
404      }
405    if( sscanf( StrImPos.c_str(), "%f\\%f\\%f", &xImPos, &yImPos, &zImPos) != 3)
406      return 0.;
407    return yImPos;
408 }
409
410 /**
411   * \brief gets the info from 0020,0032 : Image Position Patient
412   * \               else from 0020,0030 : Image Position (RET)
413   * \               else from 0020,1041 : Slice Location
414   * \               else from 0020,0050 : Location
415   * \               else 0.
416   * @return up-left image corner Z position
417   */
418 float gdcmHeaderHelper::GetZOrigin() {
419    float xImPos, yImPos, zImPos; 
420    std::string StrImPos = GetEntryByNumber(0x0020,0x0032);
421    if (StrImPos != GDCM_UNFOUND) {
422       if( sscanf( StrImPos.c_str(), "%f\\%f\\%f", &xImPos, &yImPos, &zImPos) != 3) {
423          dbg.Verbose(0, "gdcmHeader::GetZImagePosition: wrong Image Position Patient (0020,0032)");
424          return 0.;  // bug in the element 0x0020,0x0032
425       } else {
426          return zImPos;
427       }    
428    }  
429    StrImPos = GetEntryByNumber(0x0020,0x0030); // For ACR-NEMA images
430    if (StrImPos != GDCM_UNFOUND) {
431       if( sscanf( StrImPos.c_str(), "%f\\%f\\%f", &xImPos, &yImPos, &zImPos) != 3) {
432          dbg.Verbose(0, "gdcmHeader::GetZImagePosition: wrong Image Position (RET) (0020,0030)");
433          return 0.;  // bug in the element 0x0020,0x0032
434       } else {
435          return zImPos;
436       }    
437    }                
438    std::string StrSliceLocation = GetEntryByNumber(0x0020,0x1041);// for *very* old ACR-NEMA images
439    if (StrSliceLocation != GDCM_UNFOUND) {
440       if( sscanf( StrSliceLocation.c_str(), "%f", &zImPos) !=1) {
441          dbg.Verbose(0, "gdcmHeader::GetZImagePosition: wrong Slice Location (0020,1041)");
442          return 0.;  // bug in the element 0x0020,0x1041
443       } else {
444          return zImPos;
445       }
446    }   
447    dbg.Verbose(0, "gdcmHeader::GetZImagePosition: unfound Slice Location (0020,1041)");
448    std::string StrLocation = GetEntryByNumber(0x0020,0x0050);
449    if (StrLocation != GDCM_UNFOUND) {
450       if( sscanf( StrLocation.c_str(), "%f", &zImPos) !=1) {
451          dbg.Verbose(0, "gdcmHeader::GetZImagePosition: wrong Location (0020,0050)");
452          return 0.;  // bug in the element 0x0020,0x0050
453       } else {
454          return zImPos;
455       }
456    }
457    dbg.Verbose(0, "gdcmHeader::GetYImagePosition: unfound Location (0020,0050)");  
458    return 0.; // Hopeless
459 }
460
461 /**
462   * \brief gets the info from 0020,0013 : Image Number
463   * \               else 0.
464   * @return image number
465   */
466 int gdcmHeaderHelper::GetImageNumber() {
467   // The function i atoi() takes the address of an area of memory as
468   // parameter and converts the string stored at that location to an integer
469   // using the external decimal to internal binary conversion rules. This may
470   // be preferable to sscanf() since atoi() is a much smaller, simpler and
471   // faster function. sscanf() can do all possible conversions whereas
472   // atoi() can only do single decimal integer conversions.
473   std::string StrImNumber = GetEntryByNumber(0x0020,0x0013); //0020 0013 IS REL Image Number
474   if (StrImNumber != GDCM_UNFOUND) {
475     return atoi( StrImNumber.c_str() );
476   }
477   return 0;   //Hopeless
478 }
479
480 /**
481   * \brief gets the info from 0008,0060 : Modality
482   * @return Modality Type
483   */
484 ModalityType gdcmHeaderHelper::GetModality(void) {
485   // 0008 0060 CS ID Modality
486   std::string StrModality = GetEntryByNumber(0x0008,0x0060);
487   if (StrModality != GDCM_UNFOUND) {
488          if ( StrModality.find("AU") < StrModality.length()) return AU;
489     else if ( StrModality.find("AS") < StrModality.length()) return AS;
490     else if ( StrModality.find("BI") < StrModality.length()) return BI;
491     else if ( StrModality.find("CF") < StrModality.length()) return CF;
492     else if ( StrModality.find("CP") < StrModality.length()) return CP;
493     else if ( StrModality.find("CR") < StrModality.length()) return CR;
494     else if ( StrModality.find("CT") < StrModality.length()) return CT;
495     else if ( StrModality.find("CS") < StrModality.length()) return CS;
496     else if ( StrModality.find("DD") < StrModality.length()) return DD;
497     else if ( StrModality.find("DF") < StrModality.length()) return DF;
498     else if ( StrModality.find("DG") < StrModality.length()) return DG;
499     else if ( StrModality.find("DM") < StrModality.length()) return DM;
500     else if ( StrModality.find("DS") < StrModality.length()) return DS;
501     else if ( StrModality.find("DX") < StrModality.length()) return DX;
502     else if ( StrModality.find("ECG") < StrModality.length()) return ECG;
503     else if ( StrModality.find("EPS") < StrModality.length()) return EPS;
504     else if ( StrModality.find("FA") < StrModality.length()) return FA;
505     else if ( StrModality.find("FS") < StrModality.length()) return FS;
506     else if ( StrModality.find("HC") < StrModality.length()) return HC;
507     else if ( StrModality.find("HD") < StrModality.length()) return HD;
508     else if ( StrModality.find("LP") < StrModality.length()) return LP;
509     else if ( StrModality.find("LS") < StrModality.length()) return LS;
510     else if ( StrModality.find("MA") < StrModality.length()) return MA;
511     else if ( StrModality.find("MR") < StrModality.length()) return MR;
512     else if ( StrModality.find("NM") < StrModality.length()) return NM;
513     else if ( StrModality.find("OT") < StrModality.length()) return OT;
514     else if ( StrModality.find("PT") < StrModality.length()) return PT;
515     else if ( StrModality.find("RF") < StrModality.length()) return RF;
516     else if ( StrModality.find("RG") < StrModality.length()) return RG;
517     else if ( StrModality.find("RTDOSE")  < StrModality.length()) return RTDOSE;
518     else if ( StrModality.find("RTIMAGE") < StrModality.length()) return RTIMAGE;
519     else if ( StrModality.find("RTPLAN")  < StrModality.length()) return RTPLAN;
520     else if ( StrModality.find("RTSTRUCT")< StrModality.length()) return RTSTRUCT;
521     else if ( StrModality.find("SM") < StrModality.length()) return SM;
522     else if ( StrModality.find("ST") < StrModality.length()) return ST;
523     else if ( StrModality.find("TG") < StrModality.length()) return TG;
524     else if ( StrModality.find("US") < StrModality.length()) return US;
525     else if ( StrModality.find("VF") < StrModality.length()) return VF;
526     else if ( StrModality.find("XA") < StrModality.length()) return XA;
527     else if ( StrModality.find("XC") < StrModality.length()) return XC;
528
529     else
530     {
531       /// \todo throw error return value ???
532       /// specified <> unknow in our database
533       return Unknow;
534     }
535   }
536   return Unknow;
537 }
538
539 /**
540   * \brief gets the info from 0020,0037 : Image Orientation Patient
541   * @param iop adress of the (6)float aray to receive values
542   * @return cosines of image orientation patient
543   */
544 void gdcmHeaderHelper::GetImageOrientationPatient( float* iop ) {
545
546   //iop is supposed to be float[6]
547   iop[0] = iop[1] = iop[2] = iop[3] = iop[4] = iop[5] = 0;
548   
549   // 0020 0037 DS REL Image Orientation (Patient)
550   std::string StrImOriPat = GetEntryByNumber(0x0020,0x0037);
551   if (StrImOriPat != GDCM_UNFOUND) {
552     if( sscanf( StrImOriPat.c_str(), "%f\\%f\\%f\\%f\\%f\\%f", 
553             &iop[0], &iop[1], &iop[2], &iop[3], &iop[4], &iop[5]) != 6) {
554          dbg.Verbose(0, "gdcmHeader::GetImageOrientationPatient: wrong Image Orientation Patient (0020,0037)");
555          return ;  // bug in the element 0x0020,0x0037
556     } 
557     else
558       return ;
559   }
560   
561   //For ACR-NEMA
562   // 0020 0035 DS REL Image Orientation (RET)
563   StrImOriPat = GetEntryByNumber(0x0020,0x0035);
564   if (StrImOriPat != GDCM_UNFOUND) {
565     if( sscanf( StrImOriPat.c_str(), "%f\\%f\\%f\\%f\\%f\\%f", 
566             &iop[0], &iop[1], &iop[2], &iop[3], &iop[4], &iop[5]) != 6) {
567          dbg.Verbose(0, "gdcmHeader::GetImageOrientationPatient: wrong Image Orientation Patient (0020,0035)");
568          return ;  // bug in the element 0x0020,0x0035
569     } 
570     else
571       return ;
572   }
573 }
574
575 //-----------------------------------------------------------------------------
576 // Protected
577
578 //-----------------------------------------------------------------------------
579 // Private
580
581 //-----------------------------------------------------------------------------
582
583
584
585 //-----------------------------------------------------------------------------
586 // Constructor / Destructor
587 gdcmSerieHeaderHelper::~gdcmSerieHeaderHelper(){
588   /// \todo
589   for (std::list<gdcmHeaderHelper*>::iterator it  = CoherentGdcmFileList.begin();
590         it != CoherentGdcmFileList.end(); it++)
591   {
592     delete *it;
593   }
594   CoherentGdcmFileList.clear();
595 }
596
597 //-----------------------------------------------------------------------------
598 // Print
599
600 //-----------------------------------------------------------------------------
601 // Public
602 /**
603  * \brief add a gdcmFile to the list based on file name
604  * @param   filename Name of the file to deal with
605  */
606 void gdcmSerieHeaderHelper::AddFileName(std::string filename) {
607   gdcmHeaderHelper *GdcmFile = new gdcmHeaderHelper( filename.c_str() );
608   this->CoherentGdcmFileList.push_back( GdcmFile );
609 }
610
611 /**
612  * \brief add a gdcmFile to the list
613  * @param   file gdcmHeaderHelper to add
614  */
615 void gdcmSerieHeaderHelper::AddGdcmFile(gdcmHeaderHelper *file){
616   this->CoherentGdcmFileList.push_back( file );
617 }
618
619 /**
620  * \brief Sets the Directory
621  * @param   dir Name of the directory to deal with
622  */
623 void gdcmSerieHeaderHelper::SetDirectory(std::string dir){
624   gdcmDirList filenames_list(dir);  //OS specific
625   
626   for(gdcmDirList::iterator it = filenames_list.begin(); 
627       it !=filenames_list.end(); it++)
628   {
629     gdcmHeaderHelper *file = new gdcmHeaderHelper( it->c_str() );
630     this->CoherentGdcmFileList.push_back( file );
631   }
632 }
633
634 /**
635  * \brief Sorts the File List
636  * \warning This could be implemented in a 'Strategy Pattern' approach
637  *          But as I don't know how to do it, I leave it this way
638  *          BTW, this is also a Strategy, I don't know this is the best approach :)
639 */
640 void gdcmSerieHeaderHelper::OrderGdcmFileList(){
641   if( ImagePositionPatientOrdering() ) {
642     return ;
643   }
644   else if( ImageNumberOrdering() ) {
645     return ;
646   } else  {
647     FileNameOrdering();
648   }
649 }
650
651 /**
652  * \brief Gets the *coherent* File List
653  * @return the *coherent* File List
654 */
655 std::list<gdcmHeaderHelper*> &gdcmSerieHeaderHelper::GetGdcmFileList() {
656   return CoherentGdcmFileList;
657 }
658
659 //-----------------------------------------------------------------------------
660 // Protected
661
662 //-----------------------------------------------------------------------------
663 // Private
664 /**
665  * \ingroup gdcmHeaderHelper
666  * \brief sorts the images, according to their Patient Position
667  *  We may order, considering :
668  *   -# Image Number
669  *   -# Image Position Patient
670  *   -# More to come :)
671  * @return false only if the header is bugged !
672  */
673 bool gdcmSerieHeaderHelper::ImagePositionPatientOrdering()
674 //based on Jolinda's algorithm
675 {
676   //iop is calculated based on the file file
677   float *cosines = new float[6];
678   float normal[3];
679   float ipp[3];
680   float dist;
681   float min, max;
682   bool first = true;
683   int n=0;
684   std::vector<float> distlist;
685
686   //!\todo rewrite this for loop.
687   for (std::list<gdcmHeaderHelper*>::iterator it  = CoherentGdcmFileList.begin();
688         it != CoherentGdcmFileList.end(); it++)
689   {
690     if(first) {
691       (*it)->GetImageOrientationPatient(cosines);
692       
693       //You only have to do this once for all slices in the volume. Next, for
694       //each slice, calculate the distance along the slice normal using the IPP
695       //tag ("dist" is initialized to zero before reading the first slice) :
696       normal[0] = cosines[1]*cosines[5] - cosines[2]*cosines[4];
697       normal[1] = cosines[2]*cosines[3] - cosines[0]*cosines[5];
698       normal[2] = cosines[0]*cosines[4] - cosines[1]*cosines[3];
699   
700       ipp[0] = (*it)->GetXOrigin();
701       ipp[1] = (*it)->GetYOrigin();
702       ipp[2] = (*it)->GetZOrigin();
703
704       dist = 0;
705       for (int i = 0; i < 3; ++i)
706           dist += normal[i]*ipp[i];
707     
708       if( dist == 0 )
709       {
710         delete[] cosines;
711         return false;
712       }
713
714       distlist.push_back( dist );
715
716       max = min = dist;
717       first = false;
718     }
719     else {
720       ipp[0] = (*it)->GetXOrigin();
721       ipp[1] = (*it)->GetYOrigin();
722       ipp[2] = (*it)->GetZOrigin();
723   
724       dist = 0;
725       for (int i = 0; i < 3; ++i)
726           dist += normal[i]*ipp[i];
727
728       if( dist == 0 )
729       {
730         delete[] cosines;
731         return false;
732       }
733       
734       distlist.push_back( dist );
735
736       min = (min < dist) ? min : dist;
737       max = (max > dist) ? max : dist;
738     }
739     n++;
740   }
741
742     //Then I order the slices according to the value "dist". Finally, once
743     //I've read in all the slices, I calculate the z-spacing as the difference
744     //between the "dist" values for the first two slices.
745     std::vector<gdcmHeaderHelper*> CoherentGdcmFileVector(n);
746     //CoherentGdcmFileVector.reserve( n );
747     CoherentGdcmFileVector.resize( n );
748     //assert( CoherentGdcmFileVector.capacity() >= n );
749
750     float step = (max - min)/(n - 1);
751     int pos;
752     n = 0;
753     
754     //VC++ don't understand what scope is !! it -> it2
755     for (std::list<gdcmHeaderHelper*>::iterator it2  = CoherentGdcmFileList.begin();
756         it2 != CoherentGdcmFileList.end(); it2++, n++)
757     {
758       //2*n sort algo !!
759       //Assumption: all files are present (no one missing)
760       pos = (int)( fabs( (distlist[n]-min)/step) + .5 );
761             
762       CoherentGdcmFileVector[pos] = *it2;
763     }
764
765   CoherentGdcmFileList.clear();  //this doesn't delete list's element, node only
766   
767   //VC++ don't understand what scope is !! it -> it3
768   for (std::vector<gdcmHeaderHelper*>::iterator it3  = CoherentGdcmFileVector.begin();
769         it3 != CoherentGdcmFileVector.end(); it3++)
770   {
771     CoherentGdcmFileList.push_back( *it3 );
772   }
773
774   distlist.clear();
775   CoherentGdcmFileVector.clear();
776   delete[] cosines;
777   
778   return true;
779 }
780
781 /**
782  * \ingroup gdcmHeaderHelper
783  * \brief sorts the images, according to their Image Number
784  * @return false only if the header is bugged !
785  */
786
787 bool gdcmSerieHeaderHelper::ImageNumberOrdering() {
788   int min, max, pos;
789   int n = 0;//CoherentGdcmFileList.size() is a O(N) operation !!
790   unsigned char *partition;
791   
792   std::list<gdcmHeaderHelper*>::iterator it  = CoherentGdcmFileList.begin();
793   min = max = (*it)->GetImageNumber();
794
795   for (; it != CoherentGdcmFileList.end(); it++, n++)
796   {
797     pos = (*it)->GetImageNumber();
798
799     //else
800     min = (min < pos) ? min : pos;
801   }
802
803   //bzeros(partition, n); //Cette fonction est déconseillée, utilisez plutôt memset.
804   partition = new unsigned char[n];
805   memset(partition, 0, n);
806
807   std::vector<gdcmHeaderHelper*> CoherentGdcmFileVector(n);
808
809   //VC++ don't understand what scope is !! it -> it2
810   for (std::list<gdcmHeaderHelper*>::iterator it2  = CoherentGdcmFileList.begin();
811         it2 != CoherentGdcmFileList.end(); it2++)
812   {
813     pos = (*it2)->GetImageNumber();
814     CoherentGdcmFileVector[pos - min] = *it2;
815     partition[pos - min]++;
816   }
817   
818   unsigned char mult = 1;
819   for(int i=0; i<n ; i++)
820   {
821     mult *= partition[i];
822   }
823
824   //VC++ don't understand what scope is !! it -> it3
825   CoherentGdcmFileList.clear();  //this doesn't delete list's element, node only
826   for (std::vector<gdcmHeaderHelper*>::iterator it3  = CoherentGdcmFileVector.begin();
827         it3 != CoherentGdcmFileVector.end(); it3++)
828   {
829     CoherentGdcmFileList.push_back( *it3 );
830   }
831   CoherentGdcmFileVector.clear();
832   
833   delete[] partition;
834   return (mult!=0);
835 }
836
837
838 /**
839  * \ingroup gdcmHeaderHelper
840  * \brief sorts the images, according to their File Name
841  * @return false only if the header is bugged !
842  */
843  bool gdcmSerieHeaderHelper::FileNameOrdering() {
844   //using the sort
845   //sort(CoherentGdcmFileList.begin(), CoherentGdcmFileList.end());
846   return true;
847 }
848
849 //-----------------------------------------------------------------------------