]> Creatis software - gdcm.git/blob - src/gdcmFile.cxx
Hope, on a day, GetYSpacing will return accurate info, for any image
[gdcm.git] / src / gdcmFile.cxx
1 /*=========================================================================
2                                                                                 
3   Program:   gdcm
4   Module:    $RCSfile: gdcmFile.cxx,v $
5   Language:  C++
6   Date:      $Date: 2006/06/21 14:05:00 $
7   Version:   $Revision: 1.324 $
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.html 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 //
20 // --------------  Remember ! ----------------------------------
21 //
22 // Image Position (Patient)                            (0020,0032):
23 // If not found (ACR_NEMA) we try Image Position       (0020,0030)
24 // If not found (ACR-NEMA), we consider Slice Location (0020,1041)
25 //                                   or Location       (0020,0050) 
26 //                                   as the Z coordinate, 
27 // 0. for all the coordinates if nothing is found
28 //
29 // Image Position (Patient) (0020,0032) VM=3
30 // -->
31 //  The attribute Patient Orientation (0020,0020) from the General Image Module 
32 // is of type 2C and has the condition Required if image does not require 
33 // Image Orientation (0020,0037) and Image Position (0020,0032). 
34 // However, if the image does require the attributes 
35 // - Image Orientation (Patient) (0020,0037), VM=6
36 // - Image Position (Patient)    (0020,0032), VM=3
37 // then attribute Patient Orientation (0020,0020) should not be present
38 //  in the images.
39 //
40 // Remember also :
41 // Patient Position (0018,5100) values :
42
43 //  HFS   = Head First-Supine, where increasing (positive axis direction) :
44 //     X -> to the direction pointed to by the patient's oustretched left arm
45 //     Y -> to the anterior-to-posterior direction in the patient's body
46 //     Z -> to the feet-to-head direction in the patient's body
47
48 //  HFP   = Head First-Prone, where increasing (positive axis direction) :
49 //     X -> to the direction pointed to by the patient's oustretched left arm
50 //     Y -> to the anterior-to-posterior direction in the patient's body
51 //     Z -> to the feet-to-head direction in the patient's body
52
53 //  FFS  = Feet First-Supine, where increasing (positive axis direction) :
54 //     X -> to the direction pointed to by the patient's oustretched left arm
55 //     Y -> to the anterior-to-posterion direction in the patient's body
56 //     Z -> to the feet-to-head direction in the patient's body
57
58 //  FFP  = Feet First-Prone, where increasing (positive axis direction) :
59 //     X -> to the direction pointed to by the patient's oustretched left arm
60 //     Y -> to the posterior-to-anterior direction in the patient's body
61 //     Z -> to the feet-to-head direction in the patient's body
62
63 // HFDR = Head First-Decubitus Right
64 // HFDL = Head First-Decubitus Left
65 // FFDR = Feet First-Decubitus Right
66 // FFDL = Feet First-Decubitus Left
67
68 //  we can also find (non standard!)     
69
70 // SEMIERECT
71 // SUPINE
72
73 // CS 2 Patient Orientation (0020 0020)
74 //    When the coordinates of the image 
75 //    are always present, this field is almost never used.
76 //    Better we don't trust it too much ...
77 //    Found Values are :
78 //     L\P
79 //     L\FP
80 //     P\F
81 //     L\F
82 //     P\FR
83 //     R\F
84 //
85 // (0020|0037) [Image Orientation (Patient)] [1\0\0\0\1\0 ]
86
87                
88 // ---------------------------------------------------------------
89 //
90 #include "gdcmFile.h"
91 #include "gdcmGlobal.h"
92 #include "gdcmUtil.h"
93 #include "gdcmDebug.h"
94 #include "gdcmTS.h"
95 #include "gdcmSeqEntry.h"
96 #include "gdcmRLEFramesInfo.h"
97 #include "gdcmJPEGFragmentsInfo.h"
98 #include "gdcmDataEntry.h"
99
100 #include <vector>
101 #include <stdio.h>  //sscanf
102 #include <stdlib.h> // for atoi
103
104 namespace gdcm 
105 {
106
107 //-----------------------------------------------------------------------------
108 // Constructor / Destructor
109
110 /**
111  * \brief Constructor used when we want to generate dicom files from scratch
112  */
113 File::File():
114    Document()
115 {
116    RLEInfo  = new RLEFramesInfo;
117    JPEGInfo = new JPEGFragmentsInfo;
118    GrPixel  = 0x7fe0;  // to avoid further troubles
119    NumPixel = 0x0010;
120    BasicOffsetTableItemValue = 0;
121    FourthDimensionLocation = TagKey(0,0);
122 }
123
124
125 /**
126  * \brief   Canonical destructor.
127  */
128 File::~File()
129 {
130    if ( RLEInfo )
131       delete RLEInfo;
132    if ( JPEGInfo )
133       delete JPEGInfo;
134    delete[] BasicOffsetTableItemValue;
135 }
136
137 //-----------------------------------------------------------------------------
138 // Public
139 /**
140  * \brief   Loader  
141  * @return false if file cannot be open or no swap info was found,
142  *         or no tag was found.
143  */
144 bool File::Load( ) 
145 {
146    if ( ! this->Document::Load( ) )
147       return false;
148
149     return DoTheLoadingJob( );   
150 }
151
152 /**
153  * \brief   Does the Loading Job (internal use only)
154  * @return false if file cannot be open or no swap info was found,
155  *         or no tag was found.
156  */
157 bool File::DoTheLoadingJob( ) 
158 {
159    // for some ACR-NEMA images GrPixel, NumPixel is *not* 7fe0,0010
160    // We may encounter the 'RETired' (0x0028, 0x0200) tag
161    // (Image Location") . This entry contains the number of
162    // the group that contains the pixel data (hence the "Pixel Data"
163    // is found by indirection through the "Image Location").
164    // Inside the group pointed by "Image Location" the searched element
165    // is conventionally the element 0x0010 (when the norm is respected).
166    // When the "Image Location" is missing we default to group 0x7fe0.
167    // Note: this IS the right place for the code
168  
169    // Image Location
170    const std::string &imgLocation = GetEntryString(0x0028, 0x0200);
171    if ( imgLocation == GDCM_UNFOUND )
172    {
173       // default value
174       GrPixel = 0x7fe0;
175    }
176    else
177    {
178       GrPixel = (uint16_t) atoi( imgLocation.c_str() );
179    }   
180
181    // sometimes Image Location value doesn't follow
182    // the supposed processor endianness.
183    // see gdcmData/cr172241.dcm
184    if ( GrPixel == 0xe07f )
185    {
186       GrPixel = 0x7fe0;
187    }
188
189    if ( GrPixel != 0x7fe0 )
190    {
191       // This is a kludge for old dirty Philips imager.
192       NumPixel = 0x1010;
193    }
194    else
195    {
196       NumPixel = 0x0010;
197    }
198
199    // Now, we know GrPixel and NumPixel.
200    // Let's create a VirtualDictEntry to allow a further VR modification
201    // and force VR to match with BitsAllocated.
202    DocEntry *entry = GetDocEntry(GrPixel, NumPixel); 
203    if ( entry != 0 )
204    {
205       // Compute the RLE or JPEG info
206       OpenFile();
207       const std::string &ts = GetTransferSyntax();
208       Fp->seekg( entry->GetOffset(), std::ios::beg );
209       if ( Global::GetTS()->IsRLELossless(ts) ) 
210          ComputeRLEInfo();
211       else if ( Global::GetTS()->IsJPEG(ts) )
212          ComputeJPEGFragmentInfo();
213       CloseFile();
214
215       // Create a new DataEntry to change the DictEntry
216       // The changed DictEntry will have 
217       // - a correct PixelVR OB or OW)
218       // - the name to "Pixel Data"
219       
220        //==>Take it easy!
221        //==> Just change the VR !
222
223 /* 
224       DataEntry *oldEntry = dynamic_cast<DataEntry *>(entry);
225       if (oldEntry)
226       {
227          VRKey PixelVR;
228          // 8 bits allocated is a 'O Bytes' , as well as 24 (old ACR-NEMA RGB)
229          // more than 8 (i.e 12, 16) is a 'O Words'
230          if ( GetBitsAllocated() == 8 || GetBitsAllocated() == 24 ) 
231             PixelVR = "OB";
232          else
233             PixelVR = "OW";
234
235          // Change only made if usefull
236          if ( PixelVR != oldEntry->GetVR() )
237          {       
238             //DictEntry* newDict = DictEntry::New(GrPixel,NumPixel,
239             //                                    PixelVR,"1","Pixel Data");
240             //DataEntry *newEntry = DataEntry::New(newDict);
241             //newDict->Delete();
242             //newEntry->Copy(entry);
243             //newEntry->SetBinArea(oldEntry->GetBinArea(),oldEntry->IsSelfArea());
244             //oldEntry->SetSelfArea(false);
245
246             //RemoveEntry(oldEntry);
247             //AddEntry(newEntry);
248             //newEntry->Delete();
249   
250          }
251       }
252 */
253          VRKey PixelVR;
254          // 8 bits allocated is a 'OB(ytes)' , as well as 24 (old ACR-NEMA RGB)
255          // more than 8 (i.e 12, 16) is a 'OW(ords)'
256          if ( GetBitsAllocated() == 8 || GetBitsAllocated() == 24 ) 
257             PixelVR = "OB";
258          else
259             PixelVR = "OW";
260          // Change only made if usefull
261          if ( PixelVR != entry->GetVR() )
262          { 
263             entry->SetVR(PixelVR);  
264          }         
265    }
266    return true;
267 }
268
269 /**
270  * \brief  This predicate, based on hopefully reasonable heuristics,
271  *         decides whether or not the current File was properly parsed
272  *         and contains the mandatory information for being considered as
273  *         a well formed and usable Dicom/Acr File.
274  * @return true when File is the one of a reasonable Dicom/Acr file,
275  *         false otherwise. 
276  */
277 bool File::IsReadable()
278 {
279    if ( !Document::IsReadable() )
280    {
281       return false;
282    }
283
284    const std::string &res = GetEntryString(0x0028, 0x0005);
285    if ( res != GDCM_UNFOUND && atoi(res.c_str()) > 4 )
286    {
287       gdcmWarningMacro("Wrong Image Dimensions" << res);
288       return false; // Image Dimensions
289    }
290    bool b0028_0100 = true;
291    if ( !GetDocEntry(0x0028, 0x0100) )
292    {
293       gdcmWarningMacro("Bits Allocated (0028|0100) not found"); 
294       //return false; // "Bits Allocated"
295       b0028_0100 = false;
296    }
297    bool b0028_0101 = true;
298    if ( !GetDocEntry(0x0028, 0x0101) )
299    {
300       gdcmWarningMacro("Bits Stored (0028|0101) not found");
301       //return false; // "Bits Stored"
302       b0028_0101 = false;
303    }
304    bool b0028_0102 = true;
305    if ( !GetDocEntry(0x0028, 0x0102) )
306    {
307       gdcmWarningMacro("Hight Bit (0028|0102) not found"); 
308       //return false; // "High Bit"
309       b0028_0102 = false;
310    }
311    bool b0028_0103 = true;
312    if ( !GetDocEntry(0x0028, 0x0103) )
313    {
314       gdcmWarningMacro("Pixel Representation (0028|0103) not found");
315       //return false; // "Pixel Representation" i.e. 'Sign' ( 0 : unsigned, 1 : signed)
316       b0028_0103 = false;
317    }
318
319    if ( !b0028_0100 && !b0028_0101 && !b0028_0102 && !b0028_0103)
320    {
321       gdcmWarningMacro("Too much mandatory Tags missing !");
322       return false;
323    }
324
325    if ( !GetDocEntry(GrPixel, NumPixel) )
326    {
327       gdcmWarningMacro("Pixel Dicom Element " << std::hex <<
328                         GrPixel << "|" << NumPixel << "not found");
329       return false; // Pixel Dicom Element not found :-(
330    }
331    return true;
332 }
333
334 /**
335  * \brief gets the info from 0020,0013 : Image Number else 0.
336  * @return image number
337  */
338 int File::GetImageNumber()
339 {
340    //0020 0013 : Image Number
341    std::string strImNumber = GetEntryString(0x0020,0x0013);
342    if ( strImNumber != GDCM_UNFOUND )
343    {
344       return atoi( strImNumber.c_str() );
345    }
346    return 0;   //Hopeless
347 }
348
349 /**
350  * \brief gets the info from 0008,0060 : Modality
351  * @return Modality Type
352  */
353 ModalityType File::GetModality()
354 {
355    // 0008 0060 : Modality
356    std::string strModality = GetEntryString(0x0008,0x0060);
357    if ( strModality != GDCM_UNFOUND )
358    {
359            if ( strModality.find("AU")  < strModality.length()) return AU;
360       else if ( strModality.find("AS")  < strModality.length()) return AS;
361       else if ( strModality.find("BI")  < strModality.length()) return BI;
362       else if ( strModality.find("CF")  < strModality.length()) return CF;
363       else if ( strModality.find("CP")  < strModality.length()) return CP;
364       else if ( strModality.find("CR")  < strModality.length()) return CR;
365       else if ( strModality.find("CT")  < strModality.length()) return CT;
366       else if ( strModality.find("CS")  < strModality.length()) return CS;
367       else if ( strModality.find("DD")  < strModality.length()) return DD;
368       else if ( strModality.find("DF")  < strModality.length()) return DF;
369       else if ( strModality.find("DG")  < strModality.length()) return DG;
370       else if ( strModality.find("DM")  < strModality.length()) return DM;
371       else if ( strModality.find("DS")  < strModality.length()) return DS;
372       else if ( strModality.find("DX")  < strModality.length()) return DX;
373       else if ( strModality.find("ECG") < strModality.length()) return ECG;
374       else if ( strModality.find("EPS") < strModality.length()) return EPS;
375       else if ( strModality.find("FA")  < strModality.length()) return FA;
376       else if ( strModality.find("FS")  < strModality.length()) return FS;
377       else if ( strModality.find("HC")  < strModality.length()) return HC;
378       else if ( strModality.find("HD")  < strModality.length()) return HD;
379       else if ( strModality.find("LP")  < strModality.length()) return LP;
380       else if ( strModality.find("LS")  < strModality.length()) return LS;
381       else if ( strModality.find("MA")  < strModality.length()) return MA;
382       else if ( strModality.find("MR")  < strModality.length()) return MR;
383       else if ( strModality.find("NM")  < strModality.length()) return NM;
384       else if ( strModality.find("OT")  < strModality.length()) return OT;
385       else if ( strModality.find("PT")  < strModality.length()) return PT;
386       else if ( strModality.find("RF")  < strModality.length()) return RF;
387       else if ( strModality.find("RG")  < strModality.length()) return RG;
388       else if ( strModality.find("RTDOSE")   
389                                         < strModality.length()) return RTDOSE;
390       else if ( strModality.find("RTIMAGE")  
391                                         < strModality.length()) return RTIMAGE;
392       else if ( strModality.find("RTPLAN")
393                                         < strModality.length()) return RTPLAN;
394       else if ( strModality.find("RTSTRUCT") 
395                                         < strModality.length()) return RTSTRUCT;
396       else if ( strModality.find("SM")  < strModality.length()) return SM;
397       else if ( strModality.find("ST")  < strModality.length()) return ST;
398       else if ( strModality.find("TG")  < strModality.length()) return TG;
399       else if ( strModality.find("US")  < strModality.length()) return US;
400       else if ( strModality.find("VF")  < strModality.length()) return VF;
401       else if ( strModality.find("XA")  < strModality.length()) return XA;
402       else if ( strModality.find("XC")  < strModality.length()) return XC;
403
404       else
405       {
406          /// \todo throw error return value ???
407          /// specified <> unknown in our database
408          return Unknow;
409       }
410    }
411    return Unknow;
412 }
413
414 /**
415  * \brief   Retrieve the number of columns of image.
416  * @return  The encountered size when found, 0 by default.
417  *          0 means the file is NOT USABLE. The caller will have to check
418  */
419 int File::GetXSize()
420 {
421    DataEntry *entry = GetDataEntry(0x0028,0x0011);
422    if( entry )
423       return (int)entry->GetValue(0);
424    return 0;
425 }
426
427 /**
428  * \brief   Retrieve the number of lines of image.
429  * \warning The defaulted value is 1 as opposed to File::GetXSize()
430  * @return  The encountered size when found, 1 by default 
431  *          (The ACR-NEMA file contains a Signal, not an Image).
432  */
433 int File::GetYSize()
434 {
435    DataEntry *entry = GetDataEntry(0x0028,0x0010);
436    if( entry )
437       return (int)entry->GetValue(0);
438
439    if ( IsDicomV3() )
440    {
441       return 0;
442    }
443
444    // The Rows (0028,0010) entry was optional for ACR/NEMA.
445    // (at least some images didn't have it.)
446    // It might hence be a signal (1D image). So we default to 1:
447    return 1;
448 }
449
450 /**
451  * \brief   Retrieve the number of planes of volume or the number
452  *          of frames of a multiframe.
453  * \warning When present we consider the "Number of Frames" as the third
454  *          dimension. When missing we consider the third dimension as
455  *          being the ACR-NEMA "Planes" tag content.
456  * @return  The encountered size when found, 1 by default (single image).
457  */
458 int File::GetZSize()
459 {
460    // Both  DicomV3 and ACR/Nema consider the "Number of Frames"
461    // as the third dimension.
462    DataEntry *entry = GetDataEntry(0x0028,0x0008);
463    if( entry )
464       return (int)entry->GetValue(0);
465
466    // We then consider the "Planes" entry as the third dimension 
467    entry = GetDataEntry(0x0028,0x0012);
468    if( entry )
469       return (int)entry->GetValue(0);
470    return 1;
471 }
472
473 /**
474  * \brief   Retrieve the -unnormalized- number of 'times' of '4D image'.
475  *          User has to tell gdcm the location of this '4th Dimension component'
476  *          using SetFourthDimensionLocation() method before.
477  * \warning The defaulted value is 1.
478  * @return  The encountered size when found, 1 by default 
479  *          (The file doesn't contain a '4D image'.).
480  */
481 int File::GetTSize()
482 {
483    if (FourthDimensionLocation == TagKey(0,0) )// 4D location is not set : not a 4D object
484       return 1;
485       
486    DataEntry *entry = GetDataEntry(FourthDimensionLocation.GetGroup(),
487                                    FourthDimensionLocation.GetElement() );
488    if( !entry )   
489    {
490       gdcmWarningMacro( " FourthDimensionLocation not found at : " <<
491                     std::hex << FourthDimensionLocation.GetGroup()
492                   << "|" << FourthDimensionLocation.GetElement());
493       return 1;
494    }
495    else
496    {
497       return (int)entry->GetValue(0);
498    }      
499 }  
500
501 /**
502   * \brief gets the info from 0018,1164 : ImagerPixelSpacing
503   *                      then 0028,0030 : Pixel Spacing
504   *             else 1.0
505   * @return X dimension of a pixel
506   */
507 float File::GetXSpacing()
508 {
509    float xspacing = 1.0;
510    uint32_t nbValue;
511    DataEntry *entry;
512    bool ok = false;   
513 /*
514 From:David Clunie - view profile
515 Date:Wed, May 24 2006 1:12 pm
516 Email:David Clunie <dclu...@dclunie.com>
517 Groups:comp.protocols.dicom
518
519 The short answer is that:
520
521 - (0018,1164) describes a spacing equivalent to that which
522   would be measured off a film in projection radiography
523
524 - (0018,7022) does not describe the image pixels themselves,
525   since detector elements may have been binned to produce
526   pixels
527
528 - (0018,7020) may be different from (0018,7022) since there
529   may be non-sensitive material separating individual
530   detectors (i.e. the size is smaller than the spacing
531   between centers)
532
533 Only (0018,1164) is relevant when measuring things; the
534 detector-specific attributes are there to describe the
535 acquisition.
536
537 David
538
539 PS. For ultrasound you need to use Region Calibration. 
540 */
541  
542 /*   
543 It *SHOULD* first find the IOD and then deduce which tags to read
544 Eg: Cross section this is in Pixel Spacing (0028,0030)
545 CR is in Imager Pixel Spacing (0018,1164)
546 US is in Pixel Aspect Ratio (0028,0034)
547 RT is in :
548 (3002,0011) Image Plane Pixel Spacing
549 (3002,0012) RT Image Position
550 and
551 (3004,000c) for deducing Z spacing 
552 */
553
554    std::string SOPClassUID = GetEntryString(0x0008,0x0016);
555
556    /// \todo check the various SOP Class
557    ///       to get the Pixel Spacing at the proper location
558    
559    ///\todo find images to check if it *actually* works    
560            
561    if (Util::DicomStringEqual( SOPClassUID,"1.2.840.10008.5.1.4.1.1.6")
562    // Ultrasound Image Storage (Retired)
563     || Util::DicomStringEqual( SOPClassUID,"1.2.840.10008.5.1.4.1.1.6.1")
564    // Ultrasound Image Storage
565     || Util::DicomStringEqual( SOPClassUID,"1.2.840.10008.5.1.4.1.1.3")
566    // Ultrasound Multi-Frame Storage (Retired)
567     || Util::DicomStringEqual( SOPClassUID,"1.2.840.10008.5.1.4.1.1.3.1") )
568    // Ultrasound Multi-FrameImage Storage
569    {
570       // - check if  SOPClassUID contains 2 parts (e.g. "4\3")
571       // - guess how to deduce the spacing (FOV ?, ??)
572       
573       entry = GetDataEntry(0x0028,0x0034);
574       if ( entry )
575       {
576          nbValue = entry->GetValueCount();
577          if( nbValue !=2 ) {
578             gdcmWarningMacro("PixelAspectRatio (0x0028,0x0034) "
579             << "has a wrong number of values :" << nbValue);
580          }
581          xspacing = 1.0; // We get Pixel Aspect Ratio, not Spacing ...
582          ok = true;
583       }
584   
585       if (ok)
586          return xspacing;
587    }
588 /*      
589    if (Util::DicomStringEqual( SOPClassUID,"1.2.840.10008.5.1.4.1.1.1") ) 
590    // Computed Radiography Image Storage   
591
592    // CR is in Imager Pixel Spacing (0018,1164)//    
593
594 */
595    // go on with old method ...
596    // ---------------------
597    // To follow David Clunie's advice, we first check ImagerPixelSpacing
598
599    entry = GetDataEntry(0x0018,0x1164);
600    if( entry )
601    {
602       nbValue = entry->GetValueCount();
603       // Can't use IsValueCountValid because of the complex heuristic.
604       if( nbValue !=2 )
605          gdcmWarningMacro("ImagerPixelSpacing (0x0018,0x1164) "
606          << "has a wrong number of values :" << nbValue);
607      
608       if( nbValue >= 3 )
609          xspacing = (float)entry->GetValue(2);
610       else if( nbValue >= 2 )
611          xspacing = (float)entry->GetValue(1);
612       else
613          xspacing = (float)entry->GetValue(0);
614
615       if ( xspacing == 0.0 )
616          xspacing = 1.0;
617       return xspacing;
618    }
619    else
620    {
621       gdcmWarningMacro( "Unfound Imager Pixel Spacing (0018,1164)" );
622    }
623
624    entry = GetDataEntry(0x0028,0x0030);
625    if( entry )
626    {
627       nbValue = entry->GetValueCount();
628       if( nbValue !=2 )
629          gdcmWarningMacro("PixelSpacing (0x0018,0x0030) "
630           << "has a wrong number of values :" << nbValue);      
631       
632       if( nbValue >= 3 )
633          xspacing = (float)entry->GetValue(2);
634       else if( nbValue >= 2 )
635          xspacing = (float)entry->GetValue(1);
636       else
637          xspacing = (float)entry->GetValue(0);
638
639       if ( xspacing == 0.0 )
640          xspacing = 1.0;
641       return xspacing;
642    }
643    else
644    {
645       gdcmWarningMacro( "Unfound Pixel Spacing (0028,0030)" );
646    }
647    return xspacing;
648 }
649
650 /**
651   * \brief gets the info from 0018,1164 : ImagerPixelSpacing
652   *               then from   0028,0030 : Pixel Spacing                         
653   *             else 1.0
654   * @return Y dimension of a pixel
655   */
656 float File::GetYSpacing()
657 {
658    float yspacing;
659    uint32_t nbValue;
660    DataEntry *entry;
661    bool ok = false;
662      
663    std::string SOPClassUID = GetEntryString(0x0008,0x0016);
664
665    /// \todo check the various SOP Class
666    ///       to get the Pixel Spacing at the proper location
667    
668    ///\todo find images to check if it *actually* works       
669
670    if (Util::DicomStringEqual( SOPClassUID,"1.2.840.10008.5.1.4.1.1.6")
671    // Ultrasound Image Storage (Retired)
672     || Util::DicomStringEqual( SOPClassUID,"1.2.840.10008.5.1.4.1.1.6.1")
673    // Ultrasound Image Storage
674     || Util::DicomStringEqual( SOPClassUID,"1.2.840.10008.5.1.4.1.1.3")
675    // Ultrasound Multi-Frame Storage (Retired)
676     || Util::DicomStringEqual( SOPClassUID,"1.2.840.10008.5.1.4.1.1.3.1") )
677    // Ultrasound Multi-FrameImage Storage      
678    {
679       // - check if  SOPClassUID contains 2 parts (e.g. "4\3")
680       // - no way to deduce the spacing/
681       
682       entry = GetDataEntry(0x0028,0x0034);
683       if ( entry )
684       {       
685          nbValue = entry->GetValueCount();
686          if( nbValue ==2 ) {
687             yspacing = (float)entry->GetValue(0)/(float)entry->GetValue(1);
688             //std::cout << "ys " << yspacing << std::endl;
689             ok = true;
690       }
691       else
692       {
693          gdcmWarningMacro("PixelAspectRatio (0x0028,0x0034) "
694                << "has a wrong number of values :" << nbValue);
695          if (nbValue == 0 ) {
696             ok = false;
697          }
698          else if (nbValue == 1 ) {
699             yspacing = 1.0; // We get Pixel Aspect Ratio, not Spacing ...
700             ok = true;
701          } 
702       }                  
703    }
704   
705       if (ok)
706          return yspacing;      
707    }   
708
709    // go on with old method ...
710    // ---------------------
711    // To follow David Clunie's advice, we first check ImagerPixelSpacing
712    yspacing = 1.0;
713    // To follow David Clunie's advice, we first check ImagerPixelSpacing
714
715    entry = GetDataEntry(0x0018,0x1164);
716    if( entry )
717    {
718       yspacing = (float)entry->GetValue(0);
719
720       if ( yspacing == 0.0 )
721          yspacing = 1.0;
722       return yspacing;
723    }
724    else
725    {
726       gdcmWarningMacro( "Unfound Imager Pixel Spacing (0018,1164)" );
727    }
728
729    entry = GetDataEntry(0x0028,0x0030);
730    if( entry )
731    {
732       yspacing = (float)entry->GetValue(0);
733
734       if ( yspacing == 0.0 )
735          yspacing = 1.0;
736       return yspacing;
737    }
738    else
739    {
740       gdcmWarningMacro( "Unfound Pixel Spacing (0028,0030)" );
741    }
742
743    return yspacing;
744
745
746 /**
747  * \brief gets the info from 0018,0088 : Space Between Slices
748  *                 else from 0018,0050 : Slice Thickness
749  *                 else 1.0
750  *
751  * When an element is missing, we suppose slices join together
752  * (no overlapping, no interslice gap) but we have no way to check it !
753  * For *Dicom* images, ZSpacing *should be* calculated using 
754  * XOrigin, YOrigin, ZOrigin (of the top left image corner)
755  * of 2 consecutive images, and the Orientation
756  * Computing ZSpacing on a single image is not really meaningfull ! 
757  * @return Z dimension of a voxel-to be
758  */
759 float File::GetZSpacing()
760 {
761
762    float zspacing = 1.0f;
763
764    // Spacing Between Slices : distance between the middle of 2 slices
765    // Slices may be :
766    //   jointives     (Spacing between Slices = Slice Thickness)
767    //   overlapping   (Spacing between Slices < Slice Thickness)
768    //   disjointes    (Spacing between Slices > Slice Thickness)
769    // Slice Thickness : epaisseur de tissus sur laquelle est acquis le signal
770    //   It only concerns the MRI guys, not people wanting to visualize volumes
771    //   If Spacing Between Slices is missing, 
772    //   we suppose slices joint together
773    DataEntry *entry = GetDataEntry(0x0018,0x0088);
774    if( entry )
775    {      zspacing = (float)entry->GetValue(0);
776
777       if ( zspacing == 0.0 )
778          zspacing = 1.0;
779       return zspacing;
780    }
781    else
782       gdcmWarningMacro("Unfound Spacing Between Slices (0018,0088)");
783
784    // if no 'Spacing Between Slices' is found, 
785    // we assume slices join together
786    // (no overlapping, no interslice gap)
787    entry = GetDataEntry(0x0018,0x0050);
788    if( entry )
789    {
790       zspacing = (float)entry->GetValue(0);
791
792       if ( zspacing == 0.0 )
793          zspacing = 1.0;
794       return zspacing;
795    }
796    else
797       gdcmWarningMacro("Unfound Slice Thickness (0018,0050)");
798
799    // if no 'Spacing Between Slices' is found, 
800    // we assume slices join together
801    // (no overlapping, no interslice gap)
802    entry = GetDataEntry(0x3004,0x000c);
803    if( entry )
804    {
805       float z1 = (float)entry->GetValue(0);
806       float z2 = (float)entry->GetValue(1);
807       zspacing = z2 - z1; // can be negative...
808
809       if ( zspacing == 0.0 )
810          zspacing = 1.0;
811       return zspacing;
812    }
813
814    return zspacing;
815 }
816
817 /**
818  * \brief gets the info from 0020,0032 : Image Position Patient
819  *                 else from 0020,0030 : Image Position (RET)
820  *                 else 0.
821  * @return up-left image corner X position
822  */
823 float File::GetXOrigin()
824 {
825    DataEntry *entry = GetDataEntry(0x0020,0x0032);
826    if( !entry )
827    {
828       gdcmWarningMacro( "Unfound Image Position Patient (0020,0032)");
829       entry = GetDataEntry(0x0020,0x0030);
830       if( !entry )
831       {
832          gdcmWarningMacro( "Unfound Image Position (RET) (0020,0030)");
833          return 0.0f;
834       }
835    }
836
837    if( entry->GetValueCount() == 3 )
838    {
839       if (!entry->IsValueCountValid() )
840       {
841          gdcmErrorMacro( "Invalid Value Count" );
842       }
843       return (float)entry->GetValue(0);
844    }
845    return 0.0f;
846 }
847
848 /**
849  * \brief gets the info from 0020,0032 : Image Position Patient
850  *                 else from 0020,0030 : Image Position (RET)
851  *                 else 0.
852  * @return up-left image corner Y position
853  */
854 float File::GetYOrigin()
855 {
856    DataEntry *entry = GetDataEntry(0x0020,0x0032);
857    if( !entry )
858    {
859       gdcmWarningMacro( "Unfound Image Position Patient (0020,0032)");
860       entry = GetDataEntry(0x0020,0x0030);
861       if( !entry )
862       {
863          gdcmWarningMacro( "Unfound Image Position (RET) (0020,0030)");
864          return 0.0f;
865       }
866    }
867
868    if( entry->GetValueCount() == 3 )
869    {
870       if (!entry->IsValueCountValid() )
871       {
872          gdcmErrorMacro( "Invalid Value Count" );
873       }
874       return (float)entry->GetValue(1);
875    }
876    return 0.0f;
877 }
878
879 /**
880  * \brief gets the info from 0020,0032 : Image Position Patient
881  *                 else from 0020,0030 : Image Position (RET)
882  *                 else from 0020,1041 : Slice Location
883  *                 else from 0020,0050 : Location
884  *                 else 0.
885  * @return up-left image corner Z position
886  */
887 float File::GetZOrigin()
888 {
889    DataEntry *entry = GetDataEntry(0x0020,0x0032);
890    if( entry )
891    {
892       if( entry->GetValueCount() == 3 )
893       {
894          if (!entry->IsValueCountValid() )
895          {
896             gdcmErrorMacro( "Invalid Value Count" );
897          }
898          return (float)entry->GetValue(2);
899       }
900       gdcmWarningMacro( "Wrong Image Position Patient (0020,0032)");
901       return 0.0f;
902    }
903
904    entry = GetDataEntry(0x0020,0x0030);
905    if( entry )
906    {
907       if( entry->GetValueCount() == 3 )
908       {
909          if (!entry->IsValueCountValid() )
910          {
911             gdcmErrorMacro( "Invalid Value Count" );
912          }
913          return (float)entry->GetValue(2);
914       }
915       gdcmWarningMacro( "Wrong Image Position (RET) (0020,0030)");
916       return 0.0f;
917    }
918
919    // for *very* old ACR-NEMA images
920    entry = GetDataEntry(0x0020,0x1041);
921    if( entry )
922    {
923       if( entry->GetValueCount() == 1 )
924       {
925          if (!entry->IsValueCountValid() )
926          {
927             gdcmErrorMacro( "Invalid Value Count" );
928          }
929          return (float)entry->GetValue(0); // VM=1 !
930       }
931       gdcmWarningMacro( "Wrong Slice Location (0020,1041)");
932       return 0.0f;
933    }
934
935    entry = GetDataEntry(0x0020,0x0050);
936    if( entry )
937    {
938       if( entry->GetValueCount() == 1 )
939       {
940          if (!entry->IsValueCountValid() )
941          {
942             gdcmErrorMacro( "Invalid Value Count" );
943          }
944          return (float)entry->GetValue(0);
945       }
946       gdcmWarningMacro( "Wrong Location (0020,0050)");
947       return 0.0f;
948    }
949    return 0.; // Hopeless
950 }
951
952 /**
953   * \brief gets the info from 0020,0037 : Image Orientation Patient
954   *                   or from 0020 0035 : Image Orientation (RET)
955   *
956   * (needed to organize DICOM files based on their x,y,z position)
957   *
958   * @param iop adress of the (6)float array to receive values.
959   *        (defaulted as 1.,0.,0.,0.,1.,0. if nothing -or inconsistent stuff-
960   *        is found.
961   * @return true when one of the tag -with consistent values- is found
962   *         false when nothing or inconsistent stuff - is found
963   */
964 bool File::GetImageOrientationPatient( float iop[6] )
965 {
966    std::string strImOriPat;
967    //iop is supposed to be float[6]
968    iop[0] = iop[4] = 1.;
969    iop[1] = iop[2] = iop[3] = iop[5] = 0.;
970
971    // 0020 0037 DS REL Image Orientation (Patient)
972    if ( (strImOriPat = GetEntryString(0x0020,0x0037)) != GDCM_UNFOUND )
973    {
974       if ( sscanf( strImOriPat.c_str(), "%f \\ %f \\%f \\%f \\%f \\%f ", 
975           &iop[0], &iop[1], &iop[2], &iop[3], &iop[4], &iop[5]) != 6 )
976       {
977          gdcmWarningMacro( "Wrong Image Orientation Patient (0020,0037)."
978                         << " Less than 6 values were found." );
979          return false;
980       }
981       return true;
982    }
983    //For ACR-NEMA
984    // 0020 0035 DS REL Image Orientation (RET)
985    else if ( (strImOriPat = GetEntryString(0x0020,0x0035)) != GDCM_UNFOUND )
986    {
987       if ( sscanf( strImOriPat.c_str(), "%f \\ %f \\%f \\%f \\%f \\%f ", 
988           &iop[0], &iop[1], &iop[2], &iop[3], &iop[4], &iop[5]) != 6 )
989       {
990          gdcmWarningMacro( "wrong Image Orientation Patient (0020,0035). "
991                         << "Less than 6 values were found." );
992          return false;
993       }
994       return true;
995    }
996    return false;
997 }
998
999 /**
1000  * \brief   Retrieve the number of Bits Stored (actually used)
1001  *          (as opposed to number of Bits Allocated)
1002  * @return  The encountered number of Bits Stored, 0 by default.
1003  *          0 means the file is NOT USABLE. The caller has to check it !
1004  */
1005 int File::GetBitsStored()
1006 {
1007    DataEntry *entry = GetDataEntry(0x0028,0x0101);
1008    if( !entry )
1009    {
1010       gdcmWarningMacro("BitsStored (0028,0101) is supposed to be mandatory");
1011       return 0;
1012    }
1013    return (int)entry->GetValue(0);
1014 }
1015
1016 /**
1017  * \brief   Retrieve the number of Bits Allocated
1018  *          (8, 12 -compacted ACR-NEMA files-, 16, 24 -old RGB ACR-NEMA files-,)
1019  * @return  The encountered Number of Bits Allocated, 0 by default.
1020  *          0 means the file is NOT USABLE. The caller has to check it !
1021  */
1022 int File::GetBitsAllocated()
1023 {
1024    DataEntry *entry = GetDataEntry(0x0028,0x0100);
1025    if( !entry )
1026    {
1027       gdcmWarningMacro("BitsAllocated (0028,0100) is supposed to be mandatory");
1028       return 0;
1029    }
1030    return (int)entry->GetValue(0);
1031 }
1032
1033 /**
1034  * \brief   Retrieve the high bit position.
1035  * \warning The method defaults to 0 when information is missing.
1036  *          The responsability of checking this value is left to the caller.
1037  * @return  The high bit position when present. 0 when missing.
1038  */
1039 int File::GetHighBitPosition()
1040 {
1041    DataEntry *entry = GetDataEntry(0x0028,0x0102);
1042    if( !entry )
1043    {
1044       gdcmWarningMacro("HighBitPosition (0028,0102) is supposed to be mandatory");
1045       return 0;
1046    }
1047    return (int)entry->GetValue(0);
1048 }
1049
1050 /**
1051  * \brief   Retrieve the number of Samples Per Pixel
1052  *          (1 : gray level, 3 : RGB/YBR -1 or 3 Planes-)
1053  * @return  The encountered number of Samples Per Pixel, 1 by default.
1054  *          (we assume Gray level Pixels)
1055  */
1056 int File::GetSamplesPerPixel()
1057 {
1058    DataEntry *entry = GetDataEntry(0x0028,0x0002);
1059    if( !entry )
1060    {
1061       gdcmWarningMacro("SamplesPerPixel (0028,0002) is supposed to be mandatory");
1062       return 1; // Well, it's supposed to be mandatory ...
1063                 // but sometimes it's missing : *we* assume Gray pixels
1064    }
1065    return (int)entry->GetValue(0);
1066 }
1067
1068 /**
1069  * \brief   Retrieve the Planar Configuration for RGB images
1070  *          (0 : RGB Pixels , 1 : R Plane + G Plane + B Plane)
1071  * @return  The encountered Planar Configuration, 0 by default.
1072  */
1073 int File::GetPlanarConfiguration()
1074 {
1075    DataEntry *entry = GetDataEntry(0x0028,0x0006);
1076    if( !entry )
1077    {
1078       return 0;
1079    }
1080    return (int)entry->GetValue(0);
1081 }
1082
1083 /**
1084  * \brief   Return the size (in bytes) of a single pixel of data.
1085  * @return  The size in bytes of a single pixel of data; 0 by default
1086  *          0 means the file is NOT USABLE; the caller will have to check
1087  */
1088 int File::GetPixelSize()
1089 {
1090    // 0028 0100 US IMG Bits Allocated
1091    // (in order no to be messed up by old ACR-NEMA RGB images)
1092    assert( !(GetEntryString(0x0028,0x0100) == "24") );
1093
1094    std::string pixelType = GetPixelType();
1095    if ( pixelType ==  "8U" || pixelType == "8S" )
1096    {
1097       return 1;
1098    }
1099    if ( pixelType == "16U" || pixelType == "16S")
1100    {
1101       return 2;
1102    }
1103    if ( pixelType == "32U" || pixelType == "32S")
1104    {
1105       return 4;
1106    }
1107    if ( pixelType == "FD" )
1108    {
1109       return 8;
1110    }
1111    gdcmWarningMacro( "Unknown pixel type: " << pixelType);
1112    return 0;
1113 }
1114
1115 /**
1116  * \brief   Build the Pixel Type of the image.
1117  *          Possible values are:
1118  *          - 8U  unsigned  8 bit,
1119  *          - 8S    signed  8 bit,
1120  *          - 16U unsigned 16 bit,
1121  *          - 16S   signed 16 bit,
1122  *          - 32U unsigned 32 bit,
1123  *          - 32S   signed 32 bit,
1124  *          - FD floating double 64 bits (Not kosher DICOM, but so usefull!)
1125  * \warning 12 bit images appear as 16 bit.
1126  *          24 bit images appear as 8 bit + photochromatic interp ="RGB "
1127  *                                        + Planar Configuration = 0
1128  * @return  0S if nothing found. NOT USABLE file. The caller has to check
1129  */
1130 std::string File::GetPixelType()
1131 {
1132    std::string bitsAlloc = GetEntryString(0x0028, 0x0100); // Bits Allocated
1133    if ( bitsAlloc == GDCM_UNFOUND )
1134    {
1135       gdcmWarningMacro( "Bits Allocated (0028,0100) supposed to be mandatory");
1136       bitsAlloc = "16"; // default and arbitrary value, not to polute the output
1137    }
1138
1139    if ( bitsAlloc == "64" )
1140    {
1141       return "FD";
1142    }
1143    else if ( bitsAlloc == "12" )
1144    {
1145       // It will be unpacked
1146       bitsAlloc = "16";
1147    }
1148    else if ( bitsAlloc == "24" )
1149    {
1150       // (in order no to be messed up by old RGB images)
1151       bitsAlloc = "8";
1152    }
1153
1154    std::string sign;
1155    if( IsSignedPixelData() )
1156    {
1157       sign = "S";
1158    }
1159    else
1160    {
1161       sign = "U";
1162    }
1163    return bitsAlloc + sign;
1164 }
1165
1166 /**
1167  * \brief   Check whether the pixels are signed (1) or UNsigned (0) data.
1168  * \warning The method defaults to false (UNsigned) when tag 0028|0103
1169  *          is missing.
1170  *          The responsability of checking this value is left to the caller
1171  *          (NO transformation is performed on the pixels to make then >0)
1172  * @return  True when signed, false when UNsigned
1173  */
1174 bool File::IsSignedPixelData()
1175 {
1176    DataEntry *entry = GetDataEntry(0x0028, 0x0103);//"Pixel Representation"
1177    if( !entry )
1178    {
1179       gdcmWarningMacro( "Pixel Representation (0028,0103) supposed to be "
1180                       << "mandatory");
1181       return false;
1182    }
1183    return entry->GetValue(0) != 0;
1184 }
1185
1186 /**
1187  * \brief   Check whether this a monochrome picture (gray levels) or not,
1188  *          using "Photometric Interpretation" tag (0x0028,0x0004).
1189  * @return  true when "MONOCHROME1" or "MONOCHROME2". False otherwise.
1190  */
1191 bool File::IsMonochrome()
1192 {
1193    const std::string &PhotometricInterp = GetEntryString( 0x0028, 0x0004 );
1194    if (  Util::DicomStringEqual(PhotometricInterp, "MONOCHROME1")
1195       || Util::DicomStringEqual(PhotometricInterp, "MONOCHROME2") )
1196    {
1197       return true;
1198    }
1199    if ( PhotometricInterp == GDCM_UNFOUND )
1200    {
1201       gdcmWarningMacro( "Photometric Interpretation (0028,0004) supposed to be "
1202                          << "mandatory");
1203    }
1204    return false;
1205 }
1206
1207 /**
1208  * \brief   Check whether this a MONOCHROME1 picture (high values = dark)
1209  *            or not using "Photometric Interpretation" tag (0x0028,0x0004).
1210  * @return  true when "MONOCHROME1" . False otherwise.
1211  */
1212 bool File::IsMonochrome1()
1213 {
1214    const std::string &PhotometricInterp = GetEntryString( 0x0028, 0x0004 );
1215    if (  Util::DicomStringEqual(PhotometricInterp, "MONOCHROME1") )
1216    {
1217       return true;
1218    }
1219    if ( PhotometricInterp == GDCM_UNFOUND )
1220    {
1221       gdcmWarningMacro( "Photometric Interpretation (0028,0004) : supposed to"
1222       << " be mandatory! ");
1223    }
1224    return false;
1225 }
1226
1227 /**
1228  * \brief   Check whether this a "PALETTE COLOR" picture or not by accessing
1229  *          the "Photometric Interpretation" tag ( 0x0028, 0x0004 ).
1230  * @return  true when "PALETTE COLOR". False otherwise.
1231  */
1232 bool File::IsPaletteColor()
1233 {
1234    std::string PhotometricInterp = GetEntryString( 0x0028, 0x0004 );
1235    if (   PhotometricInterp == "PALETTE COLOR " )
1236    {
1237       return true;
1238    }
1239    if ( PhotometricInterp == GDCM_UNFOUND )
1240    {
1241       gdcmDebugMacro( "Not found : Palette color (0028,0004)");
1242    }
1243    return false;
1244 }
1245
1246 /**
1247  * \brief   Check whether this a "YBR_FULL" color picture or not by accessing
1248  *          the "Photometric Interpretation" tag ( 0x0028, 0x0004 ).
1249  * @return  true when "YBR_FULL". False otherwise.
1250  */
1251 bool File::IsYBRFull()
1252 {
1253    std::string PhotometricInterp = GetEntryString( 0x0028, 0x0004 );
1254    if (   PhotometricInterp == "YBR_FULL" )
1255    {
1256       return true;
1257    }
1258    if ( PhotometricInterp == GDCM_UNFOUND )
1259    {
1260       gdcmDebugMacro( "Not found : YBR Full (0028,0004)");
1261    }
1262    return false;
1263 }
1264
1265 /**
1266   * \brief tells us if LUT are used
1267   * \warning Right now, 'Segmented xxx Palette Color Lookup Table Data'
1268   *          are NOT considered as LUT, since nobody knows
1269   *          how to deal with them
1270   *          Please warn me if you know sbdy that *does* know ... jprx
1271   * @return true if LUT Descriptors and LUT Tables were found 
1272   */
1273 bool File::HasLUT()
1274 {
1275    // Check the presence of the LUT Descriptors, and LUT Tables    
1276    // LutDescriptorRed    
1277    if ( !GetDocEntry(0x0028,0x1101) )
1278    {
1279       return false;
1280    }
1281    // LutDescriptorGreen 
1282    if ( !GetDocEntry(0x0028,0x1102) )
1283    {
1284       return false;
1285    }
1286    // LutDescriptorBlue 
1287    if ( !GetDocEntry(0x0028,0x1103) )
1288    {
1289       return false;
1290    }
1291    // Red Palette Color Lookup Table Data
1292    if ( !GetDocEntry(0x0028,0x1201) )
1293    {
1294       return false;
1295    }
1296    // Green Palette Color Lookup Table Data       
1297    if ( !GetDocEntry(0x0028,0x1202) )
1298    {
1299       return false;
1300    }
1301    // Blue Palette Color Lookup Table Data      
1302    if ( !GetDocEntry(0x0028,0x1203) )
1303    {
1304       return false;
1305    }
1306
1307    // FIXME : (0x0028,0x3006) : LUT Data (CTX dependent)
1308    //         NOT taken into account, but we don't know how to use it ...   
1309    return true;
1310 }
1311
1312 /**
1313   * \brief gets the info from 0028,1101 : Lookup Table Desc-Red
1314   *             else 0
1315   * @return Lookup Table number of Bits , 0 by default
1316   *          when (0028,0004),Photometric Interpretation = [PALETTE COLOR ]
1317   * @ return bit number of each LUT item 
1318   */
1319 int File::GetLUTNbits()
1320 {
1321    std::vector<std::string> tokens;
1322    int lutNbits;
1323
1324    //Just hope Lookup Table Desc-Red = Lookup Table Desc-Red
1325    //                                = Lookup Table Desc-Blue
1326    // Consistency already checked in GetLUTLength
1327    std::string lutDescription = GetEntryString(0x0028,0x1101);
1328    if ( lutDescription == GDCM_UNFOUND )
1329    {
1330       return 0;
1331    }
1332
1333    tokens.clear(); // clean any previous value
1334    Util::Tokenize ( lutDescription, tokens, "\\" );
1335    //LutLength=atoi(tokens[0].c_str());
1336    //LutDepth=atoi(tokens[1].c_str());
1337
1338    lutNbits = atoi( tokens[2].c_str() );
1339    tokens.clear();
1340
1341    return lutNbits;
1342 }
1343
1344 /**
1345  *\brief gets the info from 0028,1052 : Rescale Intercept
1346  * @return Rescale Intercept. defaulted to 0.0 is not found or empty
1347  */
1348 float File::GetRescaleIntercept()
1349 {
1350    // 0028 1052 DS IMG Rescale Intercept
1351    DataEntry *entry = GetDataEntry(0x0028, 0x1052);
1352    if( !entry )
1353    {
1354       gdcmWarningMacro( "Missing Rescale Intercept (0028,1052)");
1355       return 0.0f;
1356    }
1357    return (float)entry->GetValue(0);
1358
1359 }
1360
1361 /**
1362  *\brief   gets the info from 0028,1053 : Rescale Slope
1363  * @return Rescale Slope. defaulted to 1.0 is not found or empty
1364  */
1365 float File::GetRescaleSlope()
1366 {
1367    // 0028 1053 DS IMG Rescale Slope
1368    DataEntry *entry = GetDataEntry(0x0028, 0x1053);
1369    if( !entry )
1370    {
1371       gdcmDebugMacro( "Missing Rescale Slope (0028,1053)");
1372       return 1.0f;
1373    }
1374    return (float)entry->GetValue(0);
1375 }
1376
1377 /**
1378  * \brief This function is intended to user who doesn't want 
1379  *   to have to manage a LUT and expects to get an RBG Pixel image
1380  *   (or a monochrome one, if no LUT found ...) 
1381  * \warning to be used with GetImagePixels()
1382  * @return 1 if Gray level, 3 if Color (RGB, YBR, *or PALETTE COLOR*)
1383  */
1384 int File::GetNumberOfScalarComponents()
1385 {
1386    if ( GetSamplesPerPixel() == 3 )
1387    {
1388       return 3;
1389    }
1390
1391    // 0028 0100 US IMG Bits Allocated
1392    // (in order no to be messed up by old RGB images)
1393    if ( GetEntryString(0x0028,0x0100) == "24" )
1394    {
1395       return 3;
1396    }
1397
1398    std::string strPhotometricInterpretation = GetEntryString(0x0028,0x0004);
1399
1400    if ( ( strPhotometricInterpretation == "PALETTE COLOR ") )
1401    {
1402       if ( HasLUT() )// PALETTE COLOR is NOT enough
1403       {
1404          return 3;
1405       }
1406       else
1407       {
1408          return 1;
1409       }
1410    }
1411
1412    // beware of trailing space at end of string      
1413    // DICOM tags are never of odd length
1414    if ( strPhotometricInterpretation == GDCM_UNFOUND   || 
1415         Util::DicomStringEqual(strPhotometricInterpretation, "MONOCHROME1") ||
1416         Util::DicomStringEqual(strPhotometricInterpretation, "MONOCHROME2") )
1417    {
1418       return 1;
1419    }
1420    else
1421    {
1422       // we assume that *all* kinds of YBR are dealt with
1423       return 3;
1424    }
1425 }
1426
1427 /**
1428  * \brief This function is intended to user that DOESN'T want 
1429  *  to get RGB pixels image when it's stored as a PALETTE COLOR image
1430  *   - the (vtk) user is supposed to know how deal with LUTs - 
1431  * \warning to be used with GetImagePixelsRaw()
1432  * @return 1 if Gray level, 3 if Color (RGB or YBR - NOT 'PALETTE COLOR' -)
1433  */
1434 int File::GetNumberOfScalarComponentsRaw()
1435 {
1436    // 0028 0100 US IMG Bits Allocated
1437    // (in order no to be messed up by old RGB images)
1438    if ( File::GetEntryString(0x0028,0x0100) == "24" )
1439    {
1440       return 3;
1441    }
1442
1443    // we assume that *all* kinds of YBR are dealt with
1444    return GetSamplesPerPixel();
1445 }
1446
1447 /**
1448  * \brief   Recover the offset (from the beginning of the file) 
1449  *          of *image* pixels (not *icone image* pixels, if any !)
1450  * @return Pixel Offset
1451  */
1452 size_t File::GetPixelOffset()
1453 {
1454    DocEntry *pxlElement = GetDocEntry(GrPixel, NumPixel);
1455    if ( pxlElement )
1456    {
1457       return pxlElement->GetOffset();
1458    }
1459    else
1460    {
1461       gdcmWarningMacro( "Big trouble : Pixel Element ("
1462                       << std::hex << GrPixel<<","<< NumPixel<< ") NOT found" );
1463       return 0;
1464    }
1465 }
1466
1467 /**
1468  * \brief   Recover the pixel area length (in Bytes)
1469  * @return Pixel Element Length, as stored in the header
1470  *         (NOT the memory space necessary to hold the Pixels 
1471  *          -in case of embeded compressed image-)
1472  *         0 : NOT USABLE file. The caller has to check.
1473  */
1474 size_t File::GetPixelAreaLength()
1475 {
1476    DocEntry *pxlElement = GetDocEntry(GrPixel, NumPixel);
1477    if ( pxlElement )
1478    {
1479       return pxlElement->GetLength();
1480    }
1481    else
1482    {
1483       gdcmWarningMacro( "Big trouble : Pixel Element ("
1484                       << std::hex << GrPixel<<","<< NumPixel<< ") NOT found" );
1485       return 0;
1486    }
1487 }
1488
1489 /**
1490  * \brief Adds the characteristics of a new element we want to anonymize
1491  * @param   group  Group number of the target tag.
1492  * @param   elem Element number of the target tag.
1493  * @param   value new value (string) to substitute with 
1494  */
1495 void File::AddAnonymizeElement (uint16_t group, uint16_t elem, 
1496                                 std::string const &value) 
1497
1498    DicomElement el;
1499    el.Group = group;
1500    el.Elem  = elem;
1501    el.Value = value;
1502    UserAnonymizeList.push_back(el); 
1503 }
1504
1505 /**
1506  * \brief Overwrites in the file the values of the DicomElements
1507  *       held in the list 
1508  */
1509 void File::AnonymizeNoLoad()
1510 {
1511    std::fstream *fp = new std::fstream(Filename.c_str(), 
1512                               std::ios::in | std::ios::out | std::ios::binary); 
1513    gdcm::DocEntry *d;
1514    uint32_t offset;
1515    uint32_t lgth;
1516    uint32_t valLgth = 0;
1517    std::string *spaces;
1518    for (ListElements::iterator it = UserAnonymizeList.begin();  
1519                                it != UserAnonymizeList.end();
1520                              ++it)
1521    { 
1522    
1523       //std::cout << "File::AnonymizeNoLoad -------" << std::hex <<(*it).Group <<"|"<< 
1524       //         (*it).Elem 
1525       //         << "[" << (*it).Value << "] "<< std::dec << std::endl; 
1526       d = GetDocEntry( (*it).Group, (*it).Elem);
1527
1528       if ( d == NULL)
1529          continue;
1530
1531       if ( dynamic_cast<SeqEntry *>(d) )
1532       {
1533          gdcmWarningMacro( "You cannot 'Anonymize' a SeqEntry ");
1534          continue;
1535       }
1536       
1537       valLgth = (*it).Value.size();
1538       if (valLgth == 0)
1539          continue;
1540
1541       offset = d->GetOffset();
1542       lgth =   d->GetLength();
1543       
1544       //std::cout << "lgth " << lgth << " valLgth " << valLgth << std::endl;
1545       if (valLgth < lgth)
1546       {
1547          spaces = new std::string( lgth-valLgth, ' ');
1548          (*it).Value = (*it).Value + *spaces;
1549          //std::cout << "[" << (*it).Value << "] " << lgth << std::endl;
1550          delete spaces;
1551       }
1552       fp->seekp( offset, std::ios::beg );
1553       fp->write( (*it).Value.c_str(), lgth );
1554      
1555    }
1556    fp->close();
1557    delete fp;
1558 }
1559
1560 /**
1561  * \brief anonymize a File (remove Patient's personal info passed with
1562  *        AddAnonymizeElement()
1563  * \note You cannot Anonymize a DataEntry (to be fixed)
1564  */
1565 bool File::AnonymizeFile()
1566 {
1567    // If Anonymisation list is empty, let's perform some basic anonymization
1568    if ( UserAnonymizeList.begin() == UserAnonymizeList.end() )
1569    {
1570       // If exist, replace by spaces
1571       SetEntryString("  ",0x0010, 0x2154); // Telephone   
1572       SetEntryString("  ",0x0010, 0x1040); // Adress
1573       SetEntryString("  ",0x0010, 0x0020); // Patient ID
1574
1575       DocEntry *patientNameHE = GetDocEntry (0x0010, 0x0010);
1576   
1577       if ( patientNameHE ) // we replace it by Study Instance UID (why not ?)
1578       {
1579          std::string studyInstanceUID =  GetEntryString (0x0020, 0x000d);
1580          if ( studyInstanceUID != GDCM_UNFOUND )
1581          {
1582             SetEntryString(studyInstanceUID, 0x0010, 0x0010);
1583          }
1584          else
1585          {
1586             SetEntryString("anonymized", 0x0010, 0x0010);
1587          }
1588       }
1589    }
1590    else
1591    {
1592       gdcm::DocEntry *d;
1593       for (ListElements::iterator it = UserAnonymizeList.begin();  
1594                                   it != UserAnonymizeList.end();
1595                                 ++it)
1596       {  
1597          d = GetDocEntry( (*it).Group, (*it).Elem);
1598
1599          if ( d == NULL)
1600             continue;
1601
1602          if ( dynamic_cast<SeqEntry *>(d) )
1603          {
1604             gdcmWarningMacro( "You cannot 'Anonymize' a SeqEntry ");
1605             continue;
1606          }
1607
1608          if ( dynamic_cast<DataEntry *>(d) )
1609          {
1610             gdcmWarningMacro( "To 'Anonymize' a DataEntry, better use AnonymizeNoLoad (FIXME) ");
1611             continue;
1612          }
1613          else
1614             SetEntryString ((*it).Value, (*it).Group, (*it).Elem);
1615       }
1616 }
1617
1618   // In order to make definitively impossible any further identification
1619   // remove or replace all the stuff that contains a Date
1620
1621 //0008 0012 DA ID Instance Creation Date
1622 //0008 0020 DA ID Study Date
1623 //0008 0021 DA ID Series Date
1624 //0008 0022 DA ID Acquisition Date
1625 //0008 0023 DA ID Content Date
1626 //0008 0024 DA ID Overlay Date
1627 //0008 0025 DA ID Curve Date
1628 //0008 002a DT ID Acquisition Datetime
1629 //0018 9074 DT ACQ Frame Acquisition Datetime
1630 //0018 9151 DT ACQ Frame Reference Datetime
1631 //0018 a002 DT ACQ Contribution Date Time
1632 //0020 3403 SH REL Modified Image Date (RET)
1633 //0032 0032 DA SDY Study Verified Date
1634 //0032 0034 DA SDY Study Read Date
1635 //0032 1000 DA SDY Scheduled Study Start Date
1636 //0032 1010 DA SDY Scheduled Study Stop Date
1637 //0032 1040 DA SDY Study Arrival Date
1638 //0032 1050 DA SDY Study Completion Date
1639 //0038 001a DA VIS Scheduled Admission Date
1640 //0038 001c DA VIS Scheduled Discharge Date
1641 //0038 0020 DA VIS Admitting Date
1642 //0038 0030 DA VIS Discharge Date
1643 //0040 0002 DA PRC Scheduled Procedure Step Start Date
1644 //0040 0004 DA PRC Scheduled Procedure Step End Date
1645 //0040 0244 DA PRC Performed Procedure Step Start Date
1646 //0040 0250 DA PRC Performed Procedure Step End Date
1647 //0040 2004 DA PRC Issue Date of Imaging Service Request
1648 //0040 4005 DT PRC Scheduled Procedure Step Start Date and Time
1649 //0040 4011 DT PRC Expected Completion Date and Time
1650 //0040 a030 DT PRC Verification Date Time
1651 //0040 a032 DT PRC Observation Date Time
1652 //0040 a120 DT PRC DateTime
1653 //0040 a121 DA PRC Date
1654 //0040 a13a DT PRC Referenced Datetime
1655 //0070 0082 DA ??? Presentation Creation Date
1656 //0100 0420 DT ??? SOP Autorization Date and Time
1657 //0400 0105 DT ??? Digital Signature DateTime
1658 //2100 0040 DA PJ Creation Date
1659 //3006 0008 DA SSET Structure Set Date
1660 //3008 0024 DA ??? Treatment Control Point Date
1661 //3008 0054 DA ??? First Treatment Date
1662 //3008 0056 DA ??? Most Recent Treatment Date
1663 //3008 0162 DA ??? Safe Position Exit Date
1664 //3008 0166 DA ??? Safe Position Return Date
1665 //3008 0250 DA ??? Treatment Date
1666 //300a 0006 DA RT RT Plan Date
1667 //300a 022c DA RT Air Kerma Rate Reference Date
1668 //300e 0004 DA RT Review Date
1669
1670    return true;
1671 }
1672
1673 /**
1674  * \brief Performs some consistency checking on various 'File related' 
1675  *       (as opposed to 'DicomDir related') entries 
1676  *       then writes in a file all the (Dicom Elements) included the Pixels 
1677  * @param fileName file name to write to
1678  * @param writetype type of the file to be written 
1679  *          (ACR, ExplicitVR, ImplicitVR)
1680  */
1681 bool File::Write(std::string fileName, FileType writetype)
1682 {
1683    std::ofstream *fp = new std::ofstream(fileName.c_str(), 
1684                                          std::ios::out | std::ios::binary);
1685    if (*fp == NULL)
1686    {
1687       gdcmWarningMacro("Failed to open (write) File: " << fileName.c_str());
1688       return false;
1689    }
1690
1691    // Entry : 0002|0000 = group length -> recalculated
1692    DataEntry *e0000 = GetDataEntry(0x0002,0x0000);
1693    if ( e0000 )
1694    {
1695       std::ostringstream sLen;
1696       sLen << ComputeGroup0002Length( );
1697       e0000->SetString(sLen.str());
1698    }
1699
1700    /// \todo FIXME : Derma?.dcm does not have it...let's remove it ?!? JPRx
1701    if( writetype != JPEG )
1702    {
1703       int i_lgPix = GetEntryLength(GrPixel, NumPixel);
1704       if (i_lgPix != -2)
1705       {
1706          // no (GrPixel, NumPixel) element
1707          std::string s_lgPix = Util::Format("%d", i_lgPix+12);
1708          s_lgPix = Util::DicomString( s_lgPix.c_str() );
1709          InsertEntryString(s_lgPix,GrPixel, 0x0000, "UL");   
1710       }
1711    }
1712    Document::WriteContent(fp, writetype);
1713
1714    fp->close();
1715    delete fp;
1716
1717    return true;
1718 }
1719
1720 //-----------------------------------------------------------------------------
1721 // Protected
1722
1723
1724 //-----------------------------------------------------------------------------
1725 // Private
1726 /**
1727  * \brief Parse pixel data from disk of [multi-]fragment RLE encoding.
1728  *        Compute the RLE extra information and store it in \ref RLEInfo
1729  *        for later pixel retrieval usage.
1730  */
1731 void File::ComputeRLEInfo()
1732 {
1733    std::string ts = GetTransferSyntax();
1734    if ( !Global::GetTS()->IsRLELossless(ts) ) 
1735    {
1736       return;
1737    }
1738
1739    // Encoded pixel data: for the time being we are only concerned with
1740    // Jpeg or RLE Pixel data encodings.
1741    // As stated in PS 3.5-2003, section 8.2 p44:
1742    // "If sent in Encapsulated Format (i.e. other than the Native Format) the
1743    //  value representation OB is used".
1744    // Hence we expect an OB value representation. Concerning OB VR,
1745    // the section PS 3.5-2003, section A.4.c p 58-59, states:
1746    // "For the Value Representations OB and OW, the encoding shall meet the
1747    //   following specifications depending on the Data element tag:"
1748    //   [...snip...]
1749    //    - the first item in the sequence of items before the encoded pixel
1750    //      data stream shall be basic offset table item. The basic offset table
1751    //      item value, however, is not required to be present"
1752    ReadEncapsulatedBasicOffsetTable();
1753
1754    // Encapsulated RLE Compressed Images (see PS 3.5-2003, Annex G)
1755    // Loop on the individual frame[s] and store the information
1756    // on the RLE fragments in a RLEFramesInfo.
1757    // Note: - when only a single frame is present, this is a
1758    //         classical image.
1759    //       - when more than one frame are present, then we are in 
1760    //         the case of a multi-frame image.
1761    long frameLength;
1762    int i=0;
1763    uint32_t sum = 0;
1764    while ( (frameLength = ReadTagLength(0xfffe, 0xe000)) != 0 )
1765    { 
1766       // Since we have read the basic offset table, let's check the value were correct
1767       // or else produce a warning:
1768       if ( BasicOffsetTableItemValue )
1769         {
1770         // If a BasicOffsetTableItemValue was read
1771         uint32_t individualLength = BasicOffsetTableItemValue[i];
1772         assert( individualLength == sum ); // REMOVE that if this is a problem
1773         if( individualLength != sum )
1774           {
1775           gdcmWarningMacro( "BasicOffsetTableItemValue differs from the fragment lenght" );
1776           }
1777         sum += frameLength + 8;
1778         i++;
1779         }
1780       // Parse the RLE Header and store the corresponding RLE Segment
1781       // Offset Table information on fragments of this current Frame.
1782       // Note that the fragment pixels themselves are not loaded
1783       // (but just skipped).
1784       long frameOffset = Fp->tellg(); // once per fragment
1785
1786       uint32_t nbRleSegments = ReadInt32();
1787       if ( nbRleSegments > 16 )
1788       {
1789          // There should be at most 15 segments (refer to RLEFrame class)
1790          gdcmWarningMacro( "Too many segments.");
1791       }
1792  
1793       uint32_t rleSegmentOffsetTable[16];
1794       for( int k = 1; k <= 15; k++ )
1795       {
1796          rleSegmentOffsetTable[k] = ReadInt32();
1797       }
1798
1799       // Deduce from both RLE Header and frameLength 
1800       // the fragment length, and again store this info
1801       // in a RLEFramesInfo.
1802       long rleSegmentLength[15];
1803       // skipping (not reading) RLE Segments
1804       if ( nbRleSegments > 1)
1805       {
1806          for(unsigned int k = 1; k <= nbRleSegments-1; k++)
1807          {
1808              rleSegmentLength[k] =  rleSegmentOffsetTable[k+1]
1809                                   - rleSegmentOffsetTable[k];
1810              SkipBytes(rleSegmentLength[k]);
1811           }
1812        }
1813
1814        rleSegmentLength[nbRleSegments] = frameLength 
1815                                       - rleSegmentOffsetTable[nbRleSegments];
1816        SkipBytes(rleSegmentLength[nbRleSegments]);
1817
1818        // Store the collected info
1819        RLEFrame *newFrame = new RLEFrame;
1820        newFrame->SetNumberOfFragments(nbRleSegments);
1821        for( unsigned int uk = 1; uk <= nbRleSegments; uk++ )
1822        {
1823           newFrame->SetOffset(uk,frameOffset + rleSegmentOffsetTable[uk]);
1824           newFrame->SetLength(uk,rleSegmentLength[uk]);
1825        }
1826        RLEInfo->AddFrame(newFrame);
1827    }
1828
1829    // Make sure that  we encounter a 'Sequence Delimiter Item'
1830    // at the end of the item :
1831    if ( !ReadTag(0xfffe, 0xe0dd) ) // once per RLE File
1832    {
1833       gdcmWarningMacro( "No sequence delimiter item at end of RLE item sequence");
1834    }
1835 }
1836
1837 /**
1838  * \brief Parse pixel data from disk of [multi-]fragment Jpeg encoding.
1839  *        Compute the jpeg extra information (fragment[s] offset[s] and
1840  *        length) and store it[them] in \ref JPEGInfo for later pixel
1841  *        retrieval usage.
1842  */
1843 void File::ComputeJPEGFragmentInfo()
1844 {
1845    // If you need to, look for comments of ComputeRLEInfo().
1846    std::string ts = GetTransferSyntax();
1847    if ( ! Global::GetTS()->IsJPEG(ts) )
1848    {
1849       return;
1850    }
1851
1852    ReadEncapsulatedBasicOffsetTable();
1853
1854    // Loop on the fragments[s] and store the parsed information in a
1855    // JPEGInfo.
1856    long fragmentLength;
1857    int i=0;
1858    uint32_t sum = 0;
1859    while ( (fragmentLength = ReadTagLength(0xfffe, 0xe000)) != 0 )
1860    { 
1861       // Since we have read the basic offset table, let's check the value were correct
1862       // or else produce a warning:
1863       // A.4 Transfer syntaxes for encapsulation of encoded pixel data:
1864       // When the Item Value is present, the Basic Offset Table Item Value shall contain
1865       // concatenated 32-bit unsigned integer values that are byte offsets to the first
1866       // byte of the Item Tag of the first fragment for each frame in the Sequence of
1867       // Items. These offsets are measured from the first byte of the first Item Tag
1868       // following the Basic Offset Table item (See Table A.4-2).
1869
1870       if ( BasicOffsetTableItemValue )
1871         {
1872         // If a BasicOffsetTableItemValue was read
1873         uint32_t individualLength = BasicOffsetTableItemValue[i];
1874         //assert( individualLength == sum ); // Seems like 00191113.dcm is off by one ??
1875         if( individualLength != sum )
1876           {
1877           gdcmWarningMacro( "BasicOffsetTableItemValue differs from the fragment lenght:" <<
1878               individualLength << " != " << sum );
1879           }
1880         sum += fragmentLength + 8;
1881         i++;
1882         }
1883
1884       long fragmentOffset = Fp->tellg(); // Once per fragment
1885       // Store the collected info
1886       JPEGFragment *newFragment = new JPEGFragment;
1887       newFragment->SetOffset(fragmentOffset);
1888       newFragment->SetLength(fragmentLength);
1889       JPEGInfo->AddFragment(newFragment);
1890
1891       SkipBytes(fragmentLength);
1892    }
1893
1894    // Make sure that  we encounter a 'Sequence Delimiter Item'
1895    // at the end of the item :
1896    if ( !ReadTag(0xfffe, 0xe0dd) )
1897    {
1898       gdcmWarningMacro( "No sequence delimiter item at end of JPEG item sequence");
1899    }
1900 }
1901
1902 /**
1903  * \brief   Assuming the internal file pointer \ref Document::Fp 
1904  *          is placed at the beginning of a tag, check whether this
1905  *          tag is (TestGroup, TestElem).
1906  * \warning On success the internal file pointer \ref Document::Fp
1907  *          is modified to point after the tag.
1908  *          On failure (i.e. when the tag wasn't the expected tag
1909  *          (TestGroup, TestElem) the internal file pointer
1910  *          \ref Document::Fp is restored to it's original position.
1911  * @param   testGroup The expected group   of the tag.
1912  * @param   testElem  The expected Element of the tag.
1913  * @return  True on success, false otherwise.
1914  */
1915 bool File::ReadTag(uint16_t testGroup, uint16_t testElem)
1916 {
1917    long positionOnEntry = Fp->tellg(); // Only when reading fragments
1918    //long currentPosition = positionOnEntry;      // On debugging purposes
1919
1920    // Read the Item Tag group and element, and make
1921    // sure they are what we expected:
1922    uint16_t itemTagGroup;
1923    uint16_t itemTagElem;
1924    try
1925    {
1926       itemTagGroup = ReadInt16();
1927       itemTagElem  = ReadInt16();
1928    }
1929    catch ( FormatError )
1930    {
1931       gdcmErrorMacro( "Can not read tag for "
1932        << "   We should have found tag ("
1933        << DictEntry::TranslateToKey(testGroup,testElem) << ")"
1934        ) ;
1935
1936       return false;
1937    }
1938    if ( itemTagGroup != testGroup || itemTagElem != testElem )
1939    {
1940        // in order not to pollute output we don't warn on 'delimitors'
1941       if (itemTagGroup != 0xfffe ||  testGroup != 0xfffe )
1942          gdcmWarningMacro( "Wrong Item Tag found:"
1943           << "   We should have found tag ("
1944           << DictEntry::TranslateToKey(testGroup,testElem) << ")" << std::endl
1945           << "   but instead we encountered tag ("
1946           << DictEntry::TranslateToKey(itemTagGroup,itemTagElem) << ")"
1947           << "  at address: " << "  0x(" << std::hex 
1948           << (unsigned int)positionOnEntry  << std::dec << ")" 
1949           ) ;
1950       Fp->seekg(positionOnEntry, std::ios::beg);
1951
1952       return false;
1953    }
1954    return true;
1955 }
1956
1957 /**
1958  * \brief   Assuming the internal file pointer \ref Document::Fp 
1959  *          is placed at the beginning of a tag (TestGroup, TestElement),
1960  *          read the length associated to the Tag.
1961  * \warning On success the internal file pointer \ref Document::Fp
1962  *          is modified to point after the tag and it's length.
1963  *          On failure (i.e. when the tag wasn't the expected tag
1964  *          (TestGroup, TestElement) the internal file pointer
1965  *          \ref Document::Fp is restored to it's original position.
1966  * @param   testGroup The expected Group   of the tag.
1967  * @param   testElem  The expected Element of the tag.
1968  * @return  On success returns the length associated to the tag. On failure
1969  *          returns 0.
1970  */
1971 uint32_t File::ReadTagLength(uint16_t testGroup, uint16_t testElem)
1972 {
1973
1974    if ( !ReadTag(testGroup, testElem) )
1975    {
1976       // Avoid polutting output
1977       if ( testGroup != 0xfffe ) 
1978          gdcmErrorMacro( "ReadTag did not succeed for ("
1979                     << DictEntry::TranslateToKey(testGroup,testElem) 
1980                     << ")..." );
1981       return 0;
1982    }
1983                                                                                 
1984    //// Then read the associated Item Length
1985    
1986    // long currentPosition = Fp->tellg(); // save time // JPRx
1987    uint32_t itemLength  = ReadInt32();
1988    gdcmDebugMacro( "Basic Item Length is: " << itemLength 
1989 //        << "  at address: " << std::hex << (unsigned int)currentPosition
1990    );
1991    return itemLength;
1992 }
1993
1994 /**
1995  * \brief When parsing the Pixel Data of an encapsulated file, read
1996  *        the basic offset table (when present, and BTW dump it).
1997  */
1998 void File::ReadEncapsulatedBasicOffsetTable()
1999 {
2000    //// Read the Basic Offset Table Item Tag length...
2001    uint32_t itemLength = ReadTagLength(0xfffe, 0xe000);
2002
2003    // When present, read the basic offset table itself.
2004    // Notes: - since the presence of this basic offset table is optional
2005    //          we can't rely on it for the implementation, and we will simply
2006    //          trash it's content (when present).
2007    //        - still, when present, we could add some further checks on the
2008    //          lengths, but we won't bother with such fuses for the time being.
2009    if ( itemLength != 0 )
2010    {
2011       char *charBasicOffsetTableItemValue = new char[itemLength];
2012       Fp->read(charBasicOffsetTableItemValue, itemLength);
2013       unsigned int nbEntries = itemLength/4;
2014       assert( nbEntries*4 == itemLength); // Make sure this is a multiple
2015       BasicOffsetTableItemValue = new uint32_t[nbEntries];
2016
2017       for (unsigned int i=0; i < nbEntries; i++ )
2018       {
2019          BasicOffsetTableItemValue[i] = *((uint32_t*)(&charBasicOffsetTableItemValue[4*i]));
2020 #if defined(GDCM_WORDS_BIGENDIAN) || defined(GDCM_FORCE_BIGENDIAN_EMULATION)
2021          uint32_t val = BasicOffsetTableItemValue[i];
2022          BasicOffsetTableItemValue[i] 
2023            = (  (val<<24)               | ((val<<8)  & 0x00ff0000) | 
2024               ( (val>>8)  & 0x0000ff00) |  (val>>24)               );
2025 #endif
2026          gdcmDebugMacro( "Read one length for: " << 
2027                           std::hex << BasicOffsetTableItemValue[i] );
2028       }
2029
2030       delete[] charBasicOffsetTableItemValue;
2031    }
2032 }
2033
2034 // These are the deprecated method that one day should be removed (after the next release)
2035
2036 //#ifndef GDCM_LEGACY_REMOVE
2037 /*
2038  * \ brief   Loader. (DEPRECATED :  temporaryly kept not to break the API)
2039  * @ param   fileName file to be open for parsing
2040  * @ return false if file cannot be open or no swap info was found,
2041  *         or no tag was found.
2042  * @deprecated Use the Load() [ + SetLoadMode() ] + SetFileName() functions instead
2043  */
2044  /*
2045 bool File::Load( std::string const &fileName ) 
2046 {
2047    GDCM_LEGACY_REPLACED_BODY(File::Load(std::string), "1.2",
2048                              File::Load());
2049    SetFileName( fileName );
2050    if ( ! this->Document::Load( ) )
2051       return false;
2052
2053    return DoTheLoadingJob( );
2054 }
2055 #endif
2056 */
2057 //-----------------------------------------------------------------------------
2058 // Print
2059
2060 //-----------------------------------------------------------------------------
2061 } // end namespace gdcm