]> Creatis software - gdcm.git/blob - src/gdcmFile.cxx
ffa7ae08bb998bb79502119382fcd58471e55adf
[gdcm.git] / src / gdcmFile.cxx
1 /*=========================================================================
2                                                                                 
3   Program:   gdcm
4   Module:    $RCSfile: gdcmFile.cxx,v $
5   Language:  C++
6   Date:      $Date: 2005/01/28 15:10:56 $
7   Version:   $Revision: 1.205 $
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 #include "gdcmFile.h"
20 #include "gdcmGlobal.h"
21 #include "gdcmUtil.h"
22 #include "gdcmDebug.h"
23 #include "gdcmTS.h"
24 #include "gdcmValEntry.h"
25 #include "gdcmBinEntry.h"
26 #include "gdcmRLEFramesInfo.h"
27 #include "gdcmJPEGFragmentsInfo.h"
28
29 #include <stdio.h> //sscanf
30 #include <vector>
31
32 namespace gdcm 
33 {
34 //-----------------------------------------------------------------------------
35 // Constructor / Destructor
36 /**
37  * \brief  Constructor 
38  * @param  filename name of the file whose header we want to analyze
39  */
40 File::File( std::string const &filename ):
41    Document( filename )
42 {    
43    RLEInfo  = new RLEFramesInfo;
44    JPEGInfo = new JPEGFragmentsInfo;
45
46    // for some ACR-NEMA images GrPixel, NumPixel is *not* 7fe0,0010
47    // We may encounter the 'RETired' (0x0028, 0x0200) tag
48    // (Image Location") . This entry contains the number of
49    // the group that contains the pixel data (hence the "Pixel Data"
50    // is found by indirection through the "Image Location").
51    // Inside the group pointed by "Image Location" the searched element
52    // is conventionally the element 0x0010 (when the norm is respected).
53    // When the "Image Location" is missing we default to group 0x7fe0.
54    // Note: this IS the right place for the code
55  
56    // Image Location
57    const std::string &imgLocation = GetEntryValue(0x0028, 0x0200);
58    if ( imgLocation == GDCM_UNFOUND )
59    {
60       // default value
61       GrPixel = 0x7fe0;
62    }
63    else
64    {
65       GrPixel = (uint16_t) atoi( imgLocation.c_str() );
66    }   
67
68    // sometimes Image Location value doesn't follow
69    // the supposed processor endianness.
70    // see gdcmData/cr172241.dcm
71    if ( GrPixel == 0xe07f )
72    {
73       GrPixel = 0x7fe0;
74    }
75
76    if ( GrPixel != 0x7fe0 )
77    {
78       // This is a kludge for old dirty Philips imager.
79       NumPixel = 0x1010;
80    }
81    else
82    {
83       NumPixel = 0x0010;
84    }
85
86    // Now, we know GrPixel and NumPixel.
87    // Let's create a VirtualDictEntry to allow a further VR modification
88    // and force VR to match with BitsAllocated.
89    DocEntry *entry = GetDocEntry(GrPixel, NumPixel); 
90    if ( entry != 0 )
91    {
92       // Compute the RLE or JPEG info
93       OpenFile();
94       std::string ts = GetTransferSyntax();
95       Fp->seekg( entry->GetOffset(), std::ios::beg );
96       if ( Global::GetTS()->IsRLELossless(ts) ) 
97          ComputeRLEInfo();
98       else if ( Global::GetTS()->IsJPEG(ts) )
99          ComputeJPEGFragmentInfo();
100       CloseFile();
101
102       // Create a new BinEntry to change the the DictEntry
103       // The changed DictEntry will have 
104       // - a correct PixelVR OB or OW)
105       // - a VM to "PXL"
106       // - the name to "Pixel Data"
107       BinEntry *oldEntry = dynamic_cast<BinEntry *>(entry);
108       if(oldEntry)
109       {
110          std::string PixelVR;
111          // 8 bits allocated is a 'O Bytes' , as well as 24 (old ACR-NEMA RGB)
112          // more than 8 (i.e 12, 16) is a 'O Words'
113          if ( GetBitsAllocated() == 8 || GetBitsAllocated() == 24 ) 
114             PixelVR = "OB";
115          else
116             PixelVR = "OW";
117
118          // Change only made if usefull
119          if( PixelVR != oldEntry->GetVR() )
120          {
121             DictEntry* newDict = NewVirtualDictEntry(GrPixel,NumPixel,
122                                                      PixelVR,"1","Pixel Data");
123
124             BinEntry *newEntry = new BinEntry(newDict);
125             newEntry->Copy(entry);
126             newEntry->SetBinArea(oldEntry->GetBinArea(),oldEntry->IsSelfArea());
127             oldEntry->SetSelfArea(false);
128
129             RemoveEntry(oldEntry);
130             AddEntry(newEntry);
131          }
132       }
133    }
134 }
135
136 /**
137  * \brief Constructor used when we want to generate dicom files from scratch
138  */
139 File::File():
140    Document()
141 {
142    RLEInfo  = new RLEFramesInfo;
143    JPEGInfo = new JPEGFragmentsInfo;
144    InitializeDefaultFile();
145 }
146
147 /**
148  * \brief   Canonical destructor.
149  */
150 File::~File ()
151 {
152    if( RLEInfo )
153       delete RLEInfo;
154    if( JPEGInfo )
155       delete JPEGInfo;
156 }
157
158 /**
159  * \brief Performs some consistency checking on various 'File related' 
160  *       (as opposed to 'DicomDir related') entries 
161  *       then writes in a file all the (Dicom Elements) included the Pixels 
162  * @param fileName file name to write to
163  * @param filetype Type of the File to be written 
164  *          (ACR, ExplicitVR, ImplicitVR)
165  */
166 bool File::Write(std::string fileName, FileType filetype)
167 {
168    std::ofstream *fp = new std::ofstream(fileName.c_str(), 
169                                          std::ios::out | std::ios::binary);
170    if (*fp == NULL)
171    {
172       gdcmVerboseMacro("Failed to open (write) File: " << fileName.c_str());
173       return false;
174    }
175
176    // Entry : 0002|0000 = group length -> recalculated
177    ValEntry *e0002 = GetValEntry(0x0002,0x0000);
178    if( e0002 )
179    {
180       std::ostringstream sLen;
181       sLen << ComputeGroup0002Length(filetype);
182       e0002->SetValue(sLen.str());
183    }
184
185    // Bits Allocated
186    if ( GetEntryValue(0x0028,0x0100) ==  "12")
187    {
188       SetValEntry("16", 0x0028,0x0100);
189    }
190
191    int i_lgPix = GetEntryLength(GrPixel, NumPixel);
192    if (i_lgPix != -2)
193    {
194       // no (GrPixel, NumPixel) element
195       std::string s_lgPix = Util::Format("%d", i_lgPix+12);
196       s_lgPix = Util::DicomString( s_lgPix.c_str() );
197       InsertValEntry(s_lgPix,GrPixel, 0x0000);
198    }
199
200    // FIXME : should be nice if we could move it to File
201    //         (or in future gdcmPixelData class)
202
203    // Drop Palette Color, if necessary
204    if ( GetEntryValue(0x0028,0x0002).c_str()[0] == '3' )
205    {
206       // if SamplesPerPixel = 3, sure we don't need any LUT !   
207       // Drop 0028|1101, 0028|1102, 0028|1103
208       // Drop 0028|1201, 0028|1202, 0028|1203
209
210       DocEntry *e = GetDocEntry(0x0028,0x01101);
211       if (e)
212       {
213          RemoveEntryNoDestroy(e);
214       }
215       e = GetDocEntry(0x0028,0x1102);
216       if (e)
217       {
218          RemoveEntryNoDestroy(e);
219       }
220       e = GetDocEntry(0x0028,0x1103);
221       if (e)
222       {
223          RemoveEntryNoDestroy(e);
224       }
225       e = GetDocEntry(0x0028,0x01201);
226       if (e)
227       {
228          RemoveEntryNoDestroy(e);
229       }
230       e = GetDocEntry(0x0028,0x1202);
231       if (e)
232       {
233          RemoveEntryNoDestroy(e);
234       }
235       e = GetDocEntry(0x0028,0x1203);
236       if (e)
237       {
238           RemoveEntryNoDestroy(e);
239       }
240    }
241
242 /*
243 #ifdef GDCM_WORDS_BIGENDIAN
244    // Super Super hack that will make gdcm a BOMB ! but should
245    // Fix temporarily the dashboard
246    BinEntry *b = GetBinEntry(GrPixel,NumPixel);
247    if ( GetPixelSize() ==  16 )
248    {
249       uint16_t *im16 = (uint16_t*)b->GetBinArea();
250       int lgr = b->GetLength();
251       for( int i = 0; i < lgr / 2; i++ )
252       {
253          im16[i]= (im16[i] >> 8) | (im16[i] << 8 );
254       }
255    }
256 #endif //GDCM_WORDS_BIGENDIAN
257 */
258
259    Document::WriteContent(fp,filetype);
260
261 /*
262 #ifdef GDCM_WORDS_BIGENDIAN
263    // Flip back the pixel ... I told you this is a hack
264    if ( GetPixelSize() ==  16 )
265    {
266       uint16_t *im16 = (uint16_t*)b->GetBinArea();
267       int lgr = b->GetLength();
268       for( int i = 0; i < lgr / 2; i++ )
269       {
270          im16[i]= (im16[i] >> 8) | (im16[i] << 8 );
271       }
272    }
273 #endif //GDCM_WORDS_BIGENDIAN
274 */
275
276    fp->close();
277    delete fp;
278
279    return true;
280 }
281
282 //-----------------------------------------------------------------------------
283 // Print
284
285
286 //-----------------------------------------------------------------------------
287 // Public
288
289 /**
290  * \brief  This predicate, based on hopefully reasonable heuristics,
291  *         decides whether or not the current File was properly parsed
292  *         and contains the mandatory information for being considered as
293  *         a well formed and usable Dicom/Acr File.
294  * @return true when File is the one of a reasonable Dicom/Acr file,
295  *         false otherwise. 
296  */
297 bool File::IsReadable()
298 {
299    if( !Document::IsReadable() )
300    {
301       return false;
302    }
303
304    const std::string &res = GetEntryValue(0x0028, 0x0005);
305    if ( res != GDCM_UNFOUND && atoi(res.c_str()) > 4 )
306    {
307       return false; // Image Dimensions
308    }
309    if ( !GetDocEntry(0x0028, 0x0100) )
310    {
311       return false; // "Bits Allocated"
312    }
313    if ( !GetDocEntry(0x0028, 0x0101) )
314    {
315       return false; // "Bits Stored"
316    }
317    if ( !GetDocEntry(0x0028, 0x0102) )
318    {
319       return false; // "High Bit"
320    }
321    if ( !GetDocEntry(0x0028, 0x0103) )
322    {
323       return false; // "Pixel Representation" i.e. 'Sign'
324    }
325
326    return true;
327 }
328
329 /**
330  * \brief   Retrieve the number of columns of image.
331  * @return  The encountered size when found, 0 by default.
332  *          0 means the file is NOT USABLE. The caller will have to check
333  */
334 int File::GetXSize()
335 {
336    const std::string &strSize = GetEntryValue(0x0028,0x0011);
337    if ( strSize == GDCM_UNFOUND )
338    {
339       return 0;
340    }
341
342    return atoi( strSize.c_str() );
343 }
344
345 /**
346  * \brief   Retrieve the number of lines of image.
347  * \warning The defaulted value is 1 as opposed to File::GetXSize()
348  * @return  The encountered size when found, 1 by default 
349  *          (The ACR-NEMA file contains a Signal, not an Image).
350  */
351 int File::GetYSize()
352 {
353    const std::string &strSize = GetEntryValue(0x0028,0x0010);
354    if ( strSize != GDCM_UNFOUND )
355    {
356       return atoi( strSize.c_str() );
357    }
358    if ( IsDicomV3() )
359    {
360       return 0;
361    }
362
363    // The Rows (0028,0010) entry was optional for ACR/NEMA. It might
364    // hence be a signal (1D image). So we default to 1:
365    return 1;
366 }
367
368 /**
369  * \brief   Retrieve the number of planes of volume or the number
370  *          of frames of a multiframe.
371  * \warning When present we consider the "Number of Frames" as the third
372  *          dimension. When Missing we consider the third dimension as
373  *          being the ACR-NEMA "Planes" tag content.
374  * @return  The encountered size when found, 1 by default (single image).
375  */
376 int File::GetZSize()
377 {
378    // Both  DicomV3 and ACR/Nema consider the "Number of Frames"
379    // as the third dimension.
380    const std::string &strSize = GetEntryValue(0x0028,0x0008);
381    if ( strSize != GDCM_UNFOUND )
382    {
383       return atoi( strSize.c_str() );
384    }
385
386    // We then consider the "Planes" entry as the third dimension 
387    const std::string &strSize2 = GetEntryValue(0x0028,0x0012);
388    if ( strSize2 != GDCM_UNFOUND )
389    {
390       return atoi( strSize2.c_str() );
391    }
392
393    return 1;
394 }
395
396 /**
397   * \brief gets the info from 0028,0030 : Pixel Spacing
398   *             else 1.0
399   * @return X dimension of a pixel
400   */
401 float File::GetXSpacing()
402 {
403    float xspacing, yspacing;
404    const std::string &strSpacing = GetEntryValue(0x0028,0x0030);
405
406    if ( strSpacing == GDCM_UNFOUND )
407    {
408       gdcmVerboseMacro( "Unfound Pixel Spacing (0028,0030)" );
409       return 1.;
410    }
411
412    int nbValues;
413    if( ( nbValues = sscanf( strSpacing.c_str(), 
414          "%f\\%f", &yspacing, &xspacing)) != 2 )
415    {
416       // if single value is found, xspacing is defaulted to yspacing
417       if ( nbValues == 1 )
418       {
419          xspacing = yspacing;
420       }
421
422       if ( xspacing == 0.0 )
423          xspacing = 1.0;
424
425       return xspacing;
426
427    }
428
429    // to avoid troubles with David Clunie's-like images
430    if ( xspacing == 0. && yspacing == 0.)
431       return 1.;
432
433    if ( xspacing == 0.)
434    {
435       gdcmVerboseMacro("gdcmData/CT-MONO2-8-abdo.dcm problem");
436       // seems to be a bug in the header ...
437       nbValues = sscanf( strSpacing.c_str(), "%f\\0\\%f", &yspacing, &xspacing);
438       gdcmAssertMacro( nbValues == 2 );
439    }
440
441    return xspacing;
442 }
443
444 /**
445   * \brief gets the info from 0028,0030 : Pixel Spacing
446   *             else 1.0
447   * @return Y dimension of a pixel
448   */
449 float File::GetYSpacing()
450 {
451    float yspacing = 1.;
452    std::string strSpacing = GetEntryValue(0x0028,0x0030);
453   
454    if ( strSpacing == GDCM_UNFOUND )
455    {
456       gdcmVerboseMacro("Unfound Pixel Spacing (0028,0030)");
457       return 1.;
458     }
459
460    // if sscanf cannot read any float value, it won't affect yspacing
461    sscanf( strSpacing.c_str(), "%f", &yspacing);
462
463    if ( yspacing == 0.0 )
464       yspacing = 1.0;
465
466    return yspacing;
467
468
469 /**
470  * \brief gets the info from 0018,0088 : Space Between Slices
471  *                 else from 0018,0050 : Slice Thickness
472  *                 else 1.0
473  * @return Z dimension of a voxel-to be
474  */
475 float File::GetZSpacing()
476 {
477    // Spacing Between Slices : distance entre le milieu de chaque coupe
478    // Les coupes peuvent etre :
479    //   jointives     (Spacing between Slices = Slice Thickness)
480    //   chevauchantes (Spacing between Slices < Slice Thickness)
481    //   disjointes    (Spacing between Slices > Slice Thickness)
482    // Slice Thickness : epaisseur de tissus sur laquelle est acquis le signal
483    //   ca interesse le physicien de l'IRM, pas le visualisateur de volumes ...
484    //   Si le Spacing Between Slices est Missing, 
485    //   on suppose que les coupes sont jointives
486    
487    const std::string &strSpacingBSlices = GetEntryValue(0x0018,0x0088);
488
489    if ( strSpacingBSlices == GDCM_UNFOUND )
490    {
491       gdcmVerboseMacro("Unfound Spacing Between Slices (0018,0088)");
492       const std::string &strSliceThickness = GetEntryValue(0x0018,0x0050);       
493       if ( strSliceThickness == GDCM_UNFOUND )
494       {
495          gdcmVerboseMacro("Unfound Slice Thickness (0018,0050)");
496          return 1.;
497       }
498       else
499       {
500          // if no 'Spacing Between Slices' is found, 
501          // we assume slices join together
502          // (no overlapping, no interslice gap)
503          // if they don't, we're fucked up
504          return (float)atof( strSliceThickness.c_str() );
505       }
506    }
507    //else
508    return (float)atof( strSpacingBSlices.c_str() );
509 }
510
511 /**
512  *\brief gets the info from 0028,1052 : Rescale Intercept
513  * @return Rescale Intercept
514  */
515 float File::GetRescaleIntercept()
516 {
517    float resInter = 0.;
518    /// 0028 1052 DS IMG Rescale Intercept
519    const std::string &strRescInter = GetEntryValue(0x0028,0x1052);
520    if ( strRescInter != GDCM_UNFOUND )
521    {
522       if( sscanf( strRescInter.c_str(), "%f", &resInter) != 1 )
523       {
524          // bug in the element 0x0028,0x1052
525          gdcmVerboseMacro( "Rescale Intercept (0028,1052) is empty." );
526       }
527    }
528
529    return resInter;
530 }
531
532 /**
533  *\brief   gets the info from 0028,1053 : Rescale Slope
534  * @return Rescale Slope
535  */
536 float File::GetRescaleSlope()
537 {
538    float resSlope = 1.;
539    //0028 1053 DS IMG Rescale Slope
540    std::string strRescSlope = GetEntryValue(0x0028,0x1053);
541    if ( strRescSlope != GDCM_UNFOUND )
542    {
543       if( sscanf( strRescSlope.c_str(), "%f", &resSlope) != 1)
544       {
545          // bug in the element 0x0028,0x1053
546          gdcmVerboseMacro( "Rescale Slope (0028,1053) is empty.");
547       }
548    }
549
550    return resSlope;
551 }
552
553 /**
554  * \brief This function is intended to user who doesn't want 
555  *   to have to manage a LUT and expects to get an RBG Pixel image
556  *   (or a monochrome one ...) 
557  * \warning to be used with GetImagePixels()
558  * @return 1 if Gray level, 3 if Color (RGB, YBR or PALETTE COLOR)
559  */
560 int File::GetNumberOfScalarComponents()
561 {
562    if ( GetSamplesPerPixel() == 3 )
563    {
564       return 3;
565    }
566       
567    // 0028 0100 US IMG Bits Allocated
568    // (in order no to be messed up by old RGB images)
569    if ( GetEntryValue(0x0028,0x0100) == "24" )
570    {
571       return 3;
572    }
573        
574    std::string strPhotometricInterpretation = GetEntryValue(0x0028,0x0004);
575
576    if ( ( strPhotometricInterpretation == "PALETTE COLOR ") )
577    {
578       if ( HasLUT() )// PALETTE COLOR is NOT enough
579       {
580          return 3;
581       }
582       else
583       {
584          return 1;
585       }
586    }
587
588    // beware of trailing space at end of string      
589    // DICOM tags are never of odd length
590    if ( strPhotometricInterpretation == GDCM_UNFOUND   || 
591         Util::DicomStringEqual(strPhotometricInterpretation, "MONOCHROME1") ||
592         Util::DicomStringEqual(strPhotometricInterpretation, "MONOCHROME2") )
593    {
594       return 1;
595    }
596    else
597    {
598       // we assume that *all* kinds of YBR are dealt with
599       return 3;
600    }
601 }
602
603 /**
604  * \brief This function is intended to user that DOESN'T want 
605  *  to get RGB pixels image when it's stored as a PALETTE COLOR image
606  *   - the (vtk) user is supposed to know how deal with LUTs - 
607  * \warning to be used with GetImagePixelsRaw()
608  * @return 1 if Gray level, 3 if Color (RGB or YBR - NOT 'PALETTE COLOR' -)
609  */
610 int File::GetNumberOfScalarComponentsRaw()
611 {
612    // 0028 0100 US IMG Bits Allocated
613    // (in order no to be messed up by old RGB images)
614    if ( File::GetEntryValue(0x0028,0x0100) == "24" )
615    {
616       return 3;
617    }
618
619    // we assume that *all* kinds of YBR are dealt with
620    return GetSamplesPerPixel();
621 }
622
623 //
624 // --------------  Remember ! ----------------------------------
625 //
626 // Image Position Patient                              (0020,0032):
627 // If not found (ACR_NEMA) we try Image Position       (0020,0030)
628 // If not found (ACR-NEMA), we consider Slice Location (0020,1041)
629 //                                   or Location       (0020,0050) 
630 // as the Z coordinate, 
631 // 0. for all the coordinates if nothing is found
632 //
633 // ---------------------------------------------------------------
634 //
635
636 /**
637  * \brief gets the info from 0020,0032 : Image Position Patient
638  *                 else from 0020,0030 : Image Position (RET)
639  *                 else 0.
640  * @return up-left image corner X position
641  */
642 float File::GetXOrigin()
643 {
644    float xImPos, yImPos, zImPos;  
645    std::string strImPos = GetEntryValue(0x0020,0x0032);
646
647    if ( strImPos == GDCM_UNFOUND )
648    {
649       gdcmVerboseMacro( "Unfound Image Position Patient (0020,0032)");
650       strImPos = GetEntryValue(0x0020,0x0030); // For ACR-NEMA images
651       if ( strImPos == GDCM_UNFOUND )
652       {
653          gdcmVerboseMacro( "Unfound Image Position (RET) (0020,0030)");
654          return 0.;
655       }
656    }
657
658    if( sscanf( strImPos.c_str(), "%f\\%f\\%f", &xImPos, &yImPos, &zImPos) != 3 )
659    {
660       return 0.;
661    }
662
663    return xImPos;
664 }
665
666 /**
667  * \brief gets the info from 0020,0032 : Image Position Patient
668  *                 else from 0020,0030 : Image Position (RET)
669  *                 else 0.
670  * @return up-left image corner Y position
671  */
672 float File::GetYOrigin()
673 {
674    float xImPos, yImPos, zImPos;
675    std::string strImPos = GetEntryValue(0x0020,0x0032);
676
677    if ( strImPos == GDCM_UNFOUND)
678    {
679       gdcmVerboseMacro( "Unfound Image Position Patient (0020,0032)");
680       strImPos = GetEntryValue(0x0020,0x0030); // For ACR-NEMA images
681       if ( strImPos == GDCM_UNFOUND )
682       {
683          gdcmVerboseMacro( "Unfound Image Position (RET) (0020,0030)");
684          return 0.;
685       }  
686    }
687
688    if( sscanf( strImPos.c_str(), "%f\\%f\\%f", &xImPos, &yImPos, &zImPos) != 3 )
689    {
690       return 0.;
691    }
692
693    return yImPos;
694 }
695
696 /**
697  * \brief gets the info from 0020,0032 : Image Position Patient
698  *                 else from 0020,0030 : Image Position (RET)
699  *                 else from 0020,1041 : Slice Location
700  *                 else from 0020,0050 : Location
701  *                 else 0.
702  * @return up-left image corner Z position
703  */
704 float File::GetZOrigin()
705 {
706    float xImPos, yImPos, zImPos; 
707    std::string strImPos = GetEntryValue(0x0020,0x0032);
708
709    if ( strImPos != GDCM_UNFOUND )
710    {
711       if( sscanf( strImPos.c_str(), "%f\\%f\\%f", &xImPos, &yImPos, &zImPos) != 3)
712       {
713          gdcmVerboseMacro( "Wrong Image Position Patient (0020,0032)");
714          return 0.;  // bug in the element 0x0020,0x0032
715       }
716       else
717       {
718          return zImPos;
719       }
720    }
721
722    strImPos = GetEntryValue(0x0020,0x0030); // For ACR-NEMA images
723    if ( strImPos != GDCM_UNFOUND )
724    {
725       if( sscanf( strImPos.c_str(), 
726           "%f\\%f\\%f", &xImPos, &yImPos, &zImPos ) != 3 )
727       {
728          gdcmVerboseMacro( "Wrong Image Position (RET) (0020,0030)");
729          return 0.;  // bug in the element 0x0020,0x0032
730       }
731       else
732       {
733          return zImPos;
734       }
735    }
736
737    std::string strSliceLocation = GetEntryValue(0x0020,0x1041); // for *very* old ACR-NEMA images
738    if ( strSliceLocation != GDCM_UNFOUND )
739    {
740       if( sscanf( strSliceLocation.c_str(), "%f", &zImPos) != 1)
741       {
742          gdcmVerboseMacro( "Wrong Slice Location (0020,1041)");
743          return 0.;  // bug in the element 0x0020,0x1041
744       }
745       else
746       {
747          return zImPos;
748       }
749    }
750    gdcmVerboseMacro( "Unfound Slice Location (0020,1041)");
751
752    std::string strLocation = GetEntryValue(0x0020,0x0050);
753    if ( strLocation != GDCM_UNFOUND )
754    {
755       if( sscanf( strLocation.c_str(), "%f", &zImPos) != 1)
756       {
757          gdcmVerboseMacro( "Wrong Location (0020,0050)");
758          return 0.;  // bug in the element 0x0020,0x0050
759       }
760       else
761       {
762          return zImPos;
763       }
764    }
765    gdcmVerboseMacro( "Unfound Location (0020,0050)");  
766
767    return 0.; // Hopeless
768 }
769
770 /**
771  * \brief gets the info from 0020,0013 : Image Number else 0.
772  * @return image number
773  */
774 int File::GetImageNumber()
775 {
776    // The function i atoi() takes the address of an area of memory as
777    // parameter and converts the string stored at that location to an integer
778    // using the external decimal to internal binary conversion rules. This may
779    // be preferable to sscanf() since atoi() is a much smaller, simpler and
780    // faster function. sscanf() can do all possible conversions whereas
781    // atoi() can only do single decimal integer conversions.
782    //0020 0013 IS REL Image Number
783    std::string strImNumber = GetEntryValue(0x0020,0x0013);
784    if ( strImNumber != GDCM_UNFOUND )
785    {
786       return atoi( strImNumber.c_str() );
787    }
788    return 0;   //Hopeless
789 }
790
791 /**
792  * \brief gets the info from 0008,0060 : Modality
793  * @return Modality Type
794  */
795 ModalityType File::GetModality()
796 {
797    // 0008 0060 CS ID Modality
798    std::string strModality = GetEntryValue(0x0008,0x0060);
799    if ( strModality != GDCM_UNFOUND )
800    {
801            if ( strModality.find("AU") < strModality.length()) return AU;
802       else if ( strModality.find("AS") < strModality.length()) return AS;
803       else if ( strModality.find("BI") < strModality.length()) return BI;
804       else if ( strModality.find("CF") < strModality.length()) return CF;
805       else if ( strModality.find("CP") < strModality.length()) return CP;
806       else if ( strModality.find("CR") < strModality.length()) return CR;
807       else if ( strModality.find("CT") < strModality.length()) return CT;
808       else if ( strModality.find("CS") < strModality.length()) return CS;
809       else if ( strModality.find("DD") < strModality.length()) return DD;
810       else if ( strModality.find("DF") < strModality.length()) return DF;
811       else if ( strModality.find("DG") < strModality.length()) return DG;
812       else if ( strModality.find("DM") < strModality.length()) return DM;
813       else if ( strModality.find("DS") < strModality.length()) return DS;
814       else if ( strModality.find("DX") < strModality.length()) return DX;
815       else if ( strModality.find("ECG") < strModality.length()) return ECG;
816       else if ( strModality.find("EPS") < strModality.length()) return EPS;
817       else if ( strModality.find("FA") < strModality.length()) return FA;
818       else if ( strModality.find("FS") < strModality.length()) return FS;
819       else if ( strModality.find("HC") < strModality.length()) return HC;
820       else if ( strModality.find("HD") < strModality.length()) return HD;
821       else if ( strModality.find("LP") < strModality.length()) return LP;
822       else if ( strModality.find("LS") < strModality.length()) return LS;
823       else if ( strModality.find("MA") < strModality.length()) return MA;
824       else if ( strModality.find("MR") < strModality.length()) return MR;
825       else if ( strModality.find("NM") < strModality.length()) return NM;
826       else if ( strModality.find("OT") < strModality.length()) return OT;
827       else if ( strModality.find("PT") < strModality.length()) return PT;
828       else if ( strModality.find("RF") < strModality.length()) return RF;
829       else if ( strModality.find("RG") < strModality.length()) return RG;
830       else if ( strModality.find("RTDOSE")   < strModality.length()) return RTDOSE;
831       else if ( strModality.find("RTIMAGE")  < strModality.length()) return RTIMAGE;
832       else if ( strModality.find("RTPLAN")   < strModality.length()) return RTPLAN;
833       else if ( strModality.find("RTSTRUCT") < strModality.length()) return RTSTRUCT;
834       else if ( strModality.find("SM") < strModality.length()) return SM;
835       else if ( strModality.find("ST") < strModality.length()) return ST;
836       else if ( strModality.find("TG") < strModality.length()) return TG;
837       else if ( strModality.find("US") < strModality.length()) return US;
838       else if ( strModality.find("VF") < strModality.length()) return VF;
839       else if ( strModality.find("XA") < strModality.length()) return XA;
840       else if ( strModality.find("XC") < strModality.length()) return XC;
841
842       else
843       {
844          /// \todo throw error return value ???
845          /// specified <> unknown in our database
846          return Unknow;
847       }
848    }
849
850    return Unknow;
851 }
852
853 /**
854  * \brief   Retrieve the number of Bits Stored (actually used)
855  *          (as opposite to number of Bits Allocated)
856  * @return  The encountered number of Bits Stored, 0 by default.
857  *          0 means the file is NOT USABLE. The caller has to check it !
858  */
859 int File::GetBitsStored()
860 {
861    std::string strSize = GetEntryValue( 0x0028, 0x0101 );
862    if ( strSize == GDCM_UNFOUND )
863    {
864       gdcmVerboseMacro("(0028,0101) is supposed to be mandatory");
865       return 0;  // It's supposed to be mandatory
866                  // the caller will have to check
867    }
868    return atoi( strSize.c_str() );
869 }
870
871 /**
872  * \brief   Retrieve the high bit position.
873  * \warning The method defaults to 0 when information is Missing.
874  *          The responsability of checking this value is left to the caller.
875  * @return  The high bit positin when present. 0 when Missing.
876  */
877 int File::GetHighBitPosition()
878 {
879    std::string strSize = GetEntryValue( 0x0028, 0x0102 );
880    if ( strSize == GDCM_UNFOUND )
881    {
882       gdcmVerboseMacro( "(0028,0102) is supposed to be mandatory");
883       return 0;
884    }
885    return atoi( strSize.c_str() );
886 }
887
888 /**
889  * \brief   Check whether the pixels are signed or UNsigned data.
890  * \warning The method defaults to false (UNsigned) when information is Missing.
891  *          The responsability of checking this value is left to the caller.
892  * @return  True when signed, false when UNsigned
893  */
894 bool File::IsSignedPixelData()
895 {
896    std::string strSize = GetEntryValue( 0x0028, 0x0103 );
897    if ( strSize == GDCM_UNFOUND )
898    {
899       gdcmVerboseMacro( "(0028,0103) is supposed to be mandatory");
900       return false;
901    }
902    int sign = atoi( strSize.c_str() );
903    if ( sign == 0 ) 
904    {
905       return false;
906    }
907    return true;
908 }
909
910 /**
911  * \brief   Retrieve the number of Bits Allocated
912  *          (8, 12 -compacted ACR-NEMA files, 16, ...)
913  * @return  The encountered number of Bits Allocated, 0 by default.
914  *          0 means the file is NOT USABLE. The caller has to check it !
915  */
916 int File::GetBitsAllocated()
917 {
918    std::string strSize = GetEntryValue(0x0028,0x0100);
919    if ( strSize == GDCM_UNFOUND )
920    {
921       gdcmVerboseMacro( "(0028,0100) is supposed to be mandatory");
922       return 0; // It's supposed to be mandatory
923                 // the caller will have to check
924    }
925    return atoi( strSize.c_str() );
926 }
927
928 /**
929  * \brief   Retrieve the number of Samples Per Pixel
930  *          (1 : gray level, 3 : RGB -1 or 3 Planes-)
931  * @return  The encountered number of Samples Per Pixel, 1 by default.
932  *          (Gray level Pixels)
933  */
934 int File::GetSamplesPerPixel()
935 {
936    const std::string& strSize = GetEntryValue(0x0028,0x0002);
937    if ( strSize == GDCM_UNFOUND )
938    {
939       gdcmVerboseMacro( "(0028,0002) is supposed to be mandatory");
940       return 1; // Well, it's supposed to be mandatory ...
941                 // but sometimes it's missing : *we* assume Gray pixels
942    }
943    return atoi( strSize.c_str() );
944 }
945
946 /**
947  * \brief   Check whether this a monochrome picture or not by accessing
948  *          the "Photometric Interpretation" tag ( 0x0028, 0x0004 ).
949  * @return  true when "MONOCHROME1" or "MONOCHROME2". False otherwise.
950  */
951 bool File::IsMonochrome()
952 {
953    const std::string& PhotometricInterp = GetEntryValue( 0x0028, 0x0004 );
954    if (  Util::DicomStringEqual(PhotometricInterp, "MONOCHROME1")
955       || Util::DicomStringEqual(PhotometricInterp, "MONOCHROME2") )
956    {
957       return true;
958    }
959    if ( PhotometricInterp == GDCM_UNFOUND )
960    {
961       gdcmVerboseMacro( "Not found : Photometric Interpretation (0028,0004)");
962    }
963    return false;
964 }
965
966 /**
967  * \brief   Check whether this a "PALETTE COLOR" picture or not by accessing
968  *          the "Photometric Interpretation" tag ( 0x0028, 0x0004 ).
969  * @return  true when "PALETTE COLOR". False otherwise.
970  */
971 bool File::IsPaletteColor()
972 {
973    std::string PhotometricInterp = GetEntryValue( 0x0028, 0x0004 );
974    if (   PhotometricInterp == "PALETTE COLOR " )
975    {
976       return true;
977    }
978    if ( PhotometricInterp == GDCM_UNFOUND )
979    {
980       gdcmVerboseMacro( "Not found : Palette color (0028,0004)");
981    }
982    return false;
983 }
984
985 /**
986  * \brief   Check whether this a "YBR_FULL" color picture or not by accessing
987  *          the "Photometric Interpretation" tag ( 0x0028, 0x0004 ).
988  * @return  true when "YBR_FULL". False otherwise.
989  */
990 bool File::IsYBRFull()
991 {
992    std::string PhotometricInterp = GetEntryValue( 0x0028, 0x0004 );
993    if (   PhotometricInterp == "YBR_FULL" )
994    {
995       return true;
996    }
997    if ( PhotometricInterp == GDCM_UNFOUND )
998    {
999       gdcmVerboseMacro( "Not found : YBR Full (0028,0004)");
1000    }
1001    return false;
1002 }
1003
1004 /**
1005  * \brief   Retrieve the Planar Configuration for RGB images
1006  *          (0 : RGB Pixels , 1 : R Plane + G Plane + B Plane)
1007  * @return  The encountered Planar Configuration, 0 by default.
1008  */
1009 int File::GetPlanarConfiguration()
1010 {
1011    std::string strSize = GetEntryValue(0x0028,0x0006);
1012    if ( strSize == GDCM_UNFOUND )
1013    {
1014       gdcmVerboseMacro( "Not found : Planar Configuration (0028,0006)");
1015       return 0;
1016    }
1017    return atoi( strSize.c_str() );
1018 }
1019
1020 /**
1021  * \brief   Return the size (in bytes) of a single pixel of data.
1022  * @return  The size in bytes of a single pixel of data; 0 by default
1023  *          0 means the file is NOT USABLE; the caller will have to check
1024  */
1025 int File::GetPixelSize()
1026 {
1027    // 0028 0100 US IMG Bits Allocated
1028    // (in order no to be messed up by old RGB images)
1029    //   if (File::GetEntryValue(0x0028,0x0100) == "24")
1030    //      return 3;
1031
1032    std::string pixelType = GetPixelType();
1033    if ( pixelType ==  "8U" || pixelType == "8S" )
1034    {
1035       return 1;
1036    }
1037    if ( pixelType == "16U" || pixelType == "16S")
1038    {
1039       return 2;
1040    }
1041    if ( pixelType == "32U" || pixelType == "32S")
1042    {
1043       return 4;
1044    }
1045    if ( pixelType == "FD" )
1046    {
1047       return 8;
1048    }
1049    gdcmVerboseMacro( "Unknown pixel type");
1050    return 0;
1051 }
1052
1053 /**
1054  * \brief   Build the Pixel Type of the image.
1055  *          Possible values are:
1056  *          - 8U  unsigned  8 bit,
1057  *          - 8S    signed  8 bit,
1058  *          - 16U unsigned 16 bit,
1059  *          - 16S   signed 16 bit,
1060  *          - 32U unsigned 32 bit,
1061  *          - 32S   signed 32 bit,
1062  *          - FD floating double 64 bits (Not kosher DICOM, but so usefull!)
1063  * \warning 12 bit images appear as 16 bit.
1064  *          24 bit images appear as 8 bit
1065  * @return  0S if nothing found. NOT USABLE file. The caller has to check
1066  */
1067 std::string File::GetPixelType()
1068 {
1069    std::string bitsAlloc = GetEntryValue(0x0028, 0x0100); // Bits Allocated
1070    if ( bitsAlloc == GDCM_UNFOUND )
1071    {
1072       gdcmVerboseMacro( "Missing  Bits Allocated (0028,0100)");
1073       bitsAlloc = "16"; // default and arbitrary value, not to polute the output
1074    }
1075
1076    if ( bitsAlloc == "64" )
1077    {
1078       return "FD";
1079    }
1080    else if ( bitsAlloc == "12" )
1081    {
1082       // It will be unpacked
1083       bitsAlloc = "16";
1084    }
1085    else if ( bitsAlloc == "24" )
1086    {
1087       // (in order no to be messed up
1088       bitsAlloc = "8";  // by old RGB images)
1089    }
1090
1091    std::string sign = GetEntryValue(0x0028, 0x0103);//"Pixel Representation"
1092
1093    if (sign == GDCM_UNFOUND )
1094    {
1095       gdcmVerboseMacro( "Missing Pixel Representation (0028,0103)");
1096       sign = "U"; // default and arbitrary value, not to polute the output
1097    }
1098    else if ( sign == "0" )
1099    {
1100       sign = "U";
1101    }
1102    else
1103    {
1104       sign = "S";
1105    }
1106    return bitsAlloc + sign;
1107 }
1108
1109
1110 /**
1111  * \brief   Recover the offset (from the beginning of the file) 
1112  *          of *image* pixels (not *icone image* pixels, if any !)
1113  * @return Pixel Offset
1114  */
1115 size_t File::GetPixelOffset()
1116 {
1117    DocEntry* pxlElement = GetDocEntry(GrPixel,NumPixel);
1118    if ( pxlElement )
1119    {
1120       return pxlElement->GetOffset();
1121    }
1122    else
1123    {
1124 #ifdef GDCM_DEBUG
1125       std::cout << "Big trouble : Pixel Element ("
1126                 << std::hex << GrPixel<<","<< NumPixel<< ") NOT found"
1127                 << std::endl;  
1128 #endif //GDCM_DEBUG
1129       return 0;
1130    }
1131 }
1132
1133 /**
1134  * \brief   Recover the pixel area length (in Bytes)
1135  * @return Pixel Element Length, as stored in the header
1136  *         (NOT the memory space necessary to hold the Pixels 
1137  *          -in case of embeded compressed image-)
1138  *         0 : NOT USABLE file. The caller has to check.
1139  */
1140 size_t File::GetPixelAreaLength()
1141 {
1142    DocEntry* pxlElement = GetDocEntry(GrPixel,NumPixel);
1143    if ( pxlElement )
1144    {
1145       return pxlElement->GetLength();
1146    }
1147    else
1148    {
1149 #ifdef GDCM_DEBUG
1150       std::cout << "Big trouble : Pixel Element ("
1151                 << std::hex << GrPixel<<","<< NumPixel<< ") NOT found"
1152                 << std::endl;
1153 #endif //GDCM_DEBUG
1154       return 0;
1155    }
1156 }
1157
1158 /**
1159   * \brief tells us if LUT are used
1160   * \warning Right now, 'Segmented xxx Palette Color Lookup Table Data'
1161   *          are NOT considered as LUT, since nobody knows
1162   *          how to deal with them
1163   *          Please warn me if you know sbdy that *does* know ... jprx
1164   * @return true if LUT Descriptors and LUT Tables were found 
1165   */
1166 bool File::HasLUT()
1167 {
1168    // Check the presence of the LUT Descriptors, and LUT Tables    
1169    // LutDescriptorRed    
1170    if ( !GetDocEntry(0x0028,0x1101) )
1171    {
1172       return false;
1173    }
1174    // LutDescriptorGreen 
1175    if ( !GetDocEntry(0x0028,0x1102) )
1176    {
1177       return false;
1178    }
1179    // LutDescriptorBlue 
1180    if ( !GetDocEntry(0x0028,0x1103) )
1181    {
1182       return false;
1183    }
1184    // Red Palette Color Lookup Table Data
1185    if ( !GetDocEntry(0x0028,0x1201) )
1186    {
1187       return false;
1188    }
1189    // Green Palette Color Lookup Table Data       
1190    if ( !GetDocEntry(0x0028,0x1202) )
1191    {
1192       return false;
1193    }
1194    // Blue Palette Color Lookup Table Data      
1195    if ( !GetDocEntry(0x0028,0x1203) )
1196    {
1197       return false;
1198    }
1199
1200    // FIXME : (0x0028,0x3006) : LUT Data (CTX dependent)
1201    //         NOT taken into account, but we don't know how to use it ...   
1202    return true;
1203 }
1204
1205 /**
1206   * \brief gets the info from 0028,1101 : Lookup Table Desc-Red
1207   *             else 0
1208   * @return Lookup Table number of Bits , 0 by default
1209   *          when (0028,0004),Photometric Interpretation = [PALETTE COLOR ]
1210   * @ return bit number of each LUT item 
1211   */
1212 int File::GetLUTNbits()
1213 {
1214    std::vector<std::string> tokens;
1215    int lutNbits;
1216
1217    //Just hope Lookup Table Desc-Red = Lookup Table Desc-Red
1218    //                                = Lookup Table Desc-Blue
1219    // Consistency already checked in GetLUTLength
1220    std::string lutDescription = GetEntryValue(0x0028,0x1101);
1221    if ( lutDescription == GDCM_UNFOUND )
1222    {
1223       return 0;
1224    }
1225
1226    tokens.clear(); // clean any previous value
1227    Util::Tokenize ( lutDescription, tokens, "\\" );
1228    //LutLength=atoi(tokens[0].c_str());
1229    //LutDepth=atoi(tokens[1].c_str());
1230
1231    lutNbits = atoi( tokens[2].c_str() );
1232    tokens.clear();
1233
1234    return lutNbits;
1235 }
1236
1237 /**
1238   * \brief gets the info from 0020,0037 : Image Orientation Patient
1239   * (needed to organize DICOM files based on their x,y,z position)
1240   * @param iop adress of the (6)float aray to receive values
1241   * @return cosines of image orientation patient
1242   */
1243 void File::GetImageOrientationPatient( float iop[6] )
1244 {
1245    std::string strImOriPat;
1246    //iop is supposed to be float[6]
1247    iop[0] = iop[1] = iop[2] = iop[3] = iop[4] = iop[5] = 0.;
1248
1249    // 0020 0037 DS REL Image Orientation (Patient)
1250    if ( (strImOriPat = GetEntryValue(0x0020,0x0037)) != GDCM_UNFOUND )
1251    {
1252       if( sscanf( strImOriPat.c_str(), "%f\\%f\\%f\\%f\\%f\\%f", 
1253           &iop[0], &iop[1], &iop[2], &iop[3], &iop[4], &iop[5]) != 6 )
1254       {
1255          gdcmVerboseMacro( "Wrong Image Orientation Patient (0020,0037). Less than 6 values were found." );
1256       }
1257    }
1258    //For ACR-NEMA
1259    // 0020 0035 DS REL Image Orientation (RET)
1260    else if ( (strImOriPat = GetEntryValue(0x0020,0x0035)) != GDCM_UNFOUND )
1261    {
1262       if( sscanf( strImOriPat.c_str(), "%f\\%f\\%f\\%f\\%f\\%f", 
1263           &iop[0], &iop[1], &iop[2], &iop[3], &iop[4], &iop[5]) != 6 )
1264       {
1265          gdcmVerboseMacro( "wrong Image Orientation Patient (0020,0035). Less than 6 values were found." );
1266       }
1267    }
1268 }
1269
1270 /**
1271  * \brief anonymize a File (removes Patient's personal info)
1272  *        (read the code to see which ones ...)
1273  */
1274 bool File::AnonymizeFile()
1275 {
1276    // If exist, replace by spaces
1277    SetValEntry ("  ",0x0010, 0x2154); // Telephone   
1278    SetValEntry ("  ",0x0010, 0x1040); // Adress
1279    SetValEntry ("  ",0x0010, 0x0020); // Patient ID
1280
1281    DocEntry* patientNameHE = GetDocEntry (0x0010, 0x0010);
1282   
1283    if ( patientNameHE ) // we replace it by Study Instance UID (why not)
1284    {
1285       std::string studyInstanceUID =  GetEntryValue (0x0020, 0x000d);
1286       if ( studyInstanceUID != GDCM_UNFOUND )
1287       {
1288          InsertValEntry(studyInstanceUID, 0x0010, 0x0010);
1289       }
1290       else
1291       {
1292          InsertValEntry("anonymised", 0x0010, 0x0010);
1293       }
1294    }
1295
1296   // Just for fun :-(
1297   // (if any) remove or replace all the stuff that contains a Date
1298
1299 //0008 0012 DA ID Instance Creation Date
1300 //0008 0020 DA ID Study Date
1301 //0008 0021 DA ID Series Date
1302 //0008 0022 DA ID Acquisition Date
1303 //0008 0023 DA ID Content Date
1304 //0008 0024 DA ID Overlay Date
1305 //0008 0025 DA ID Curve Date
1306 //0008 002a DT ID Acquisition Datetime
1307 //0018 9074 DT ACQ Frame Acquisition Datetime
1308 //0018 9151 DT ACQ Frame Reference Datetime
1309 //0018 a002 DT ACQ Contribution Date Time
1310 //0020 3403 SH REL Modified Image Date (RET)
1311 //0032 0032 DA SDY Study Verified Date
1312 //0032 0034 DA SDY Study Read Date
1313 //0032 1000 DA SDY Scheduled Study Start Date
1314 //0032 1010 DA SDY Scheduled Study Stop Date
1315 //0032 1040 DA SDY Study Arrival Date
1316 //0032 1050 DA SDY Study Completion Date
1317 //0038 001a DA VIS Scheduled Admission Date
1318 //0038 001c DA VIS Scheduled Discharge Date
1319 //0038 0020 DA VIS Admitting Date
1320 //0038 0030 DA VIS Discharge Date
1321 //0040 0002 DA PRC Scheduled Procedure Step Start Date
1322 //0040 0004 DA PRC Scheduled Procedure Step End Date
1323 //0040 0244 DA PRC Performed Procedure Step Start Date
1324 //0040 0250 DA PRC Performed Procedure Step End Date
1325 //0040 2004 DA PRC Issue Date of Imaging Service Request
1326 //0040 4005 DT PRC Scheduled Procedure Step Start Date and Time
1327 //0040 4011 DT PRC Expected Completion Date and Time
1328 //0040 a030 DT PRC Verification Date Time
1329 //0040 a032 DT PRC Observation Date Time
1330 //0040 a120 DT PRC DateTime
1331 //0040 a121 DA PRC Date
1332 //0040 a13a DT PRC Referenced Datetime
1333 //0070 0082 DA ??? Presentation Creation Date
1334 //0100 0420 DT ??? SOP Autorization Date and Time
1335 //0400 0105 DT ??? Digital Signature DateTime
1336 //2100 0040 DA PJ Creation Date
1337 //3006 0008 DA SSET Structure Set Date
1338 //3008 0024 DA ??? Treatment Control Point Date
1339 //3008 0054 DA ??? First Treatment Date
1340 //3008 0056 DA ??? Most Recent Treatment Date
1341 //3008 0162 DA ??? Safe Position Exit Date
1342 //3008 0166 DA ??? Safe Position Return Date
1343 //3008 0250 DA ??? Treatment Date
1344 //300a 0006 DA RT RT Plan Date
1345 //300a 022c DA RT Air Kerma Rate Reference Date
1346 //300e 0004 DA RT Review Date
1347
1348    return true;
1349 }
1350
1351 //-----------------------------------------------------------------------------
1352 // Protected
1353 /**
1354  * \brief Initialize a default DICOM File that should contain all the
1355  *        field require by other reader. DICOM standard does not 
1356  *        explicitely defines those fields, heuristic has been choosen.
1357  *        This is not perfect as we are writting a CT image...
1358  */
1359 void File::InitializeDefaultFile()
1360 {
1361    typedef struct
1362    {
1363       const char *value;
1364       uint16_t group;
1365       uint16_t elem;
1366    } DICOM_DEFAULT_VALUE;
1367
1368    std::string date = Util::GetCurrentDate();
1369    std::string time = Util::GetCurrentTime();
1370    std::string uid  = Util::CreateUniqueUID();
1371    std::string uidMedia = uid;
1372    std::string uidInst  = uid;
1373    std::string uidClass = Util::CreateUniqueUID();
1374    std::string uidStudy = Util::CreateUniqueUID();
1375    std::string uidSerie = Util::CreateUniqueUID();
1376
1377    static DICOM_DEFAULT_VALUE defaultvalue[] = {
1378     { "146 ",                      0x0002, 0x0000}, // Meta Element Group Length // FIXME: how to recompute ?
1379     { "1.2.840.10008.5.1.4.1.1.2", 0x0002, 0x0002}, // Media Storage SOP Class UID (CT Image Storage)
1380     { uidClass.c_str(),            0x0002, 0x0003}, // Media Storage SOP Instance UID
1381     { "1.2.840.10008.1.2.1 ",      0x0002, 0x0010}, // Transfer Syntax UID (Explicit VR Little Endian)
1382     { uidClass.c_str(),            0x0002, 0x0012}, // META Implementation Class UID
1383     { "GDCM",                      0x0002, 0x0016}, // Source Application Entity Title
1384
1385     { date.c_str(),                0x0008, 0x0012}, // Instance Creation Date
1386     { time.c_str(),                0x0008, 0x0013}, // Instance Creation Time
1387     { "1.2.840.10008.5.1.4.1.1.2", 0x0008, 0x0016}, // SOP Class UID
1388     { uidInst.c_str(),             0x0008, 0x0018}, // SOP Instance UID
1389     { "CT",                        0x0008, 0x0060}, // Modality    
1390     { "GDCM",                      0x0008, 0x0070}, // Manufacturer
1391     { "GDCM",                      0x0008, 0x0080}, // Institution Name
1392     { "http://www-creatis.insa-lyon.fr/Public/Gdcm", 0x0008, 0x0081},  // Institution Address
1393
1394     { "GDCM",                      0x0010, 0x0010}, // Patient's Name
1395     { "GDCMID",                    0x0010, 0x0020}, // Patient ID
1396
1397     { uidStudy.c_str(),            0x0020, 0x000d}, // Study Instance UID
1398     { uidSerie.c_str(),            0x0020, 0x000e}, // Series Instance UID
1399     { "1",                         0x0020, 0x0010}, // StudyID
1400     { "1",                         0x0020, 0x0011}, // SeriesNumber
1401
1402     { "1",                         0x0028, 0x0002}, // Samples per pixel 1 or 3
1403     { "MONOCHROME1",               0x0028, 0x0004}, // photochromatic interpretation
1404     { "0",                         0x0028, 0x0010}, // nbRows
1405     { "0",                         0x0028, 0x0011}, // nbCols
1406     { "8",                         0x0028, 0x0100}, // BitsAllocated 8 or 12 or 16
1407     { "8",                         0x0028, 0x0101}, // BitsStored    <= BitsAllocated
1408     { "7",                         0x0028, 0x0102}, // HighBit       <= BitsAllocated - 1
1409     { "0",                         0x0028, 0x0103}, // Pixel Representation 0(unsigned) or 1(signed)
1410     { 0, 0, 0 }
1411    };
1412
1413    // default value
1414    // Special case this is the image (not a string)
1415    GrPixel = 0x7fe0;
1416    NumPixel = 0x0010;
1417    InsertBinEntry(0, 0, GrPixel, NumPixel);
1418
1419    // All remaining strings:
1420    unsigned int i = 0;
1421    DICOM_DEFAULT_VALUE current = defaultvalue[i];
1422    while( current.value )
1423    {
1424       InsertValEntry(current.value, current.group, current.elem);
1425       current = defaultvalue[++i];
1426    }
1427 }
1428
1429 //-----------------------------------------------------------------------------
1430 // Private
1431 /**
1432  * \brief Parse pixel data from disk of [multi-]fragment RLE encoding.
1433  *        Compute the RLE extra information and store it in \ref RLEInfo
1434  *        for later pixel retrieval usage.
1435  */
1436 void File::ComputeRLEInfo()
1437 {
1438    std::string ts = GetTransferSyntax();
1439    if ( !Global::GetTS()->IsRLELossless(ts) ) 
1440    {
1441       return;
1442    }
1443
1444    // Encoded pixel data: for the time being we are only concerned with
1445    // Jpeg or RLE Pixel data encodings.
1446    // As stated in PS 3.5-2003, section 8.2 p44:
1447    // "If sent in Encapsulated Format (i.e. other than the Native Format) the
1448    //  value representation OB is used".
1449    // Hence we expect an OB value representation. Concerning OB VR,
1450    // the section PS 3.5-2003, section A.4.c p 58-59, states:
1451    // "For the Value Representations OB and OW, the encoding shall meet the
1452    //   following specifications depending on the Data element tag:"
1453    //   [...snip...]
1454    //    - the first item in the sequence of items before the encoded pixel
1455    //      data stream shall be basic offset table item. The basic offset table
1456    //      item value, however, is not required to be present"
1457    ReadAndSkipEncapsulatedBasicOffsetTable();
1458
1459    // Encapsulated RLE Compressed Images (see PS 3.5-2003, Annex G)
1460    // Loop on the individual frame[s] and store the information
1461    // on the RLE fragments in a RLEFramesInfo.
1462    // Note: - when only a single frame is present, this is a
1463    //         classical image.
1464    //       - when more than one frame are present, then we are in 
1465    //         the case of a multi-frame image.
1466    long frameLength;
1467    while ( (frameLength = ReadTagLength(0xfffe, 0xe000)) )
1468    { 
1469       // Parse the RLE Header and store the corresponding RLE Segment
1470       // Offset Table information on fragments of this current Frame.
1471       // Note that the fragment pixels themselves are not loaded
1472       // (but just skipped).
1473       long frameOffset = Fp->tellg();
1474
1475       uint32_t nbRleSegments = ReadInt32();
1476       if ( nbRleSegments > 16 )
1477       {
1478          // There should be at most 15 segments (refer to RLEFrame class)
1479          gdcmVerboseMacro( "Too many segments.");
1480       }
1481  
1482       uint32_t rleSegmentOffsetTable[16];
1483       for( int k = 1; k <= 15; k++ )
1484       {
1485          rleSegmentOffsetTable[k] = ReadInt32();
1486       }
1487
1488       // Deduce from both the RLE Header and the frameLength the
1489       // fragment length, and again store this info in a
1490       // RLEFramesInfo.
1491       long rleSegmentLength[15];
1492       // skipping (not reading) RLE Segments
1493       if ( nbRleSegments > 1)
1494       {
1495          for(unsigned int k = 1; k <= nbRleSegments-1; k++)
1496          {
1497              rleSegmentLength[k] =  rleSegmentOffsetTable[k+1]
1498                                   - rleSegmentOffsetTable[k];
1499              SkipBytes(rleSegmentLength[k]);
1500           }
1501        }
1502
1503        rleSegmentLength[nbRleSegments] = frameLength 
1504                                       - rleSegmentOffsetTable[nbRleSegments];
1505        SkipBytes(rleSegmentLength[nbRleSegments]);
1506
1507        // Store the collected info
1508        RLEFrame *newFrame = new RLEFrame;
1509        newFrame->SetNumberOfFragments(nbRleSegments);
1510        for( unsigned int uk = 1; uk <= nbRleSegments; uk++ )
1511        {
1512           newFrame->SetOffset(uk,frameOffset + rleSegmentOffsetTable[uk]);
1513           newFrame->SetLength(uk,rleSegmentLength[uk]);
1514        }
1515        RLEInfo->AddFrame(newFrame);
1516    }
1517
1518    // Make sure that at the end of the item we encounter a 'Sequence
1519    // Delimiter Item':
1520    if ( !ReadTag(0xfffe, 0xe0dd) )
1521    {
1522       gdcmVerboseMacro( "No sequence delimiter item at end of RLE item sequence");
1523    }
1524 }
1525
1526 /**
1527  * \brief Parse pixel data from disk of [multi-]fragment Jpeg encoding.
1528  *        Compute the jpeg extra information (fragment[s] offset[s] and
1529  *        length) and store it[them] in \ref JPEGInfo for later pixel
1530  *        retrieval usage.
1531  */
1532 void File::ComputeJPEGFragmentInfo()
1533 {
1534    // If you need to, look for comments of ComputeRLEInfo().
1535    std::string ts = GetTransferSyntax();
1536    if ( ! Global::GetTS()->IsJPEG(ts) )
1537    {
1538       return;
1539    }
1540
1541    ReadAndSkipEncapsulatedBasicOffsetTable();
1542
1543    // Loop on the fragments[s] and store the parsed information in a
1544    // JPEGInfo.
1545    long fragmentLength;
1546    while ( (fragmentLength = ReadTagLength(0xfffe, 0xe000)) )
1547    { 
1548       long fragmentOffset = Fp->tellg();
1549
1550        // Store the collected info
1551        JPEGFragment *newFragment = new JPEGFragment;
1552        newFragment->SetOffset(fragmentOffset);
1553        newFragment->SetLength(fragmentLength);
1554        JPEGInfo->AddFragment(newFragment);
1555
1556        SkipBytes(fragmentLength);
1557    }
1558
1559    // Make sure that at the end of the item we encounter a 'Sequence
1560    // Delimiter Item':
1561    if ( !ReadTag(0xfffe, 0xe0dd) )
1562    {
1563       gdcmVerboseMacro( "No sequence delimiter item at end of JPEG item sequence");
1564    }
1565 }
1566
1567 /**
1568  * \brief   Assuming the internal file pointer \ref Document::Fp 
1569  *          is placed at the beginning of a tag check whether this
1570  *          tag is (TestGroup, TestElement).
1571  * \warning On success the internal file pointer \ref Document::Fp
1572  *          is modified to point after the tag.
1573  *          On failure (i.e. when the tag wasn't the expected tag
1574  *          (TestGroup, TestElement) the internal file pointer
1575  *          \ref Document::Fp is restored to it's original position.
1576  * @param   testGroup   The expected group of the tag.
1577  * @param   testElement The expected Element of the tag.
1578  * @return  True on success, false otherwise.
1579  */
1580 bool File::ReadTag(uint16_t testGroup, uint16_t testElement)
1581 {
1582    long positionOnEntry = Fp->tellg();
1583    long currentPosition = Fp->tellg();          // On debugging purposes
1584
1585    //// Read the Item Tag group and element, and make
1586    // sure they are what we expected:
1587    uint16_t itemTagGroup;
1588    uint16_t itemTagElement;
1589    try
1590    {
1591       itemTagGroup   = ReadInt16();
1592       itemTagElement = ReadInt16();
1593    }
1594    catch ( FormatError e )
1595    {
1596       //std::cerr << e << std::endl;
1597       return false;
1598    }
1599    if ( itemTagGroup != testGroup || itemTagElement != testElement )
1600    {
1601       gdcmVerboseMacro( "Wrong Item Tag found:"
1602        << "   We should have found tag ("
1603        << std::hex << testGroup << "," << testElement << ")" << std::endl
1604        << "   but instead we encountered tag ("
1605        << std::hex << itemTagGroup << "," << itemTagElement << ")"
1606        << "  at address: " << "  0x(" << (unsigned int)currentPosition  << ")" 
1607        ) ;
1608       Fp->seekg(positionOnEntry, std::ios::beg);
1609
1610       return false;
1611    }
1612    return true;
1613 }
1614
1615 /**
1616  * \brief   Assuming the internal file pointer \ref Document::Fp 
1617  *          is placed at the beginning of a tag (TestGroup, TestElement),
1618  *          read the length associated to the Tag.
1619  * \warning On success the internal file pointer \ref Document::Fp
1620  *          is modified to point after the tag and it's length.
1621  *          On failure (i.e. when the tag wasn't the expected tag
1622  *          (TestGroup, TestElement) the internal file pointer
1623  *          \ref Document::Fp is restored to it's original position.
1624  * @param   testGroup   The expected group of the tag.
1625  * @param   testElement The expected Element of the tag.
1626  * @return  On success returns the length associated to the tag. On failure
1627  *          returns 0.
1628  */
1629 uint32_t File::ReadTagLength(uint16_t testGroup, uint16_t testElement)
1630 {
1631
1632    if ( !ReadTag(testGroup, testElement) )
1633    {
1634       return 0;
1635    }
1636                                                                                 
1637    //// Then read the associated Item Length
1638    long currentPosition = Fp->tellg();
1639    uint32_t itemLength  = ReadInt32();
1640    {
1641       gdcmVerboseMacro( "Basic Item Length is: "
1642         << itemLength << std::endl
1643         << "  at address: " << std::hex << (unsigned int)currentPosition);
1644    }
1645    return itemLength;
1646 }
1647
1648 /**
1649  * \brief When parsing the Pixel Data of an encapsulated file, read
1650  *        the basic offset table (when present, and BTW dump it).
1651  */
1652 void File::ReadAndSkipEncapsulatedBasicOffsetTable()
1653 {
1654    //// Read the Basic Offset Table Item Tag length...
1655    uint32_t itemLength = ReadTagLength(0xfffe, 0xe000);
1656
1657    // When present, read the basic offset table itself.
1658    // Notes: - since the presence of this basic offset table is optional
1659    //          we can't rely on it for the implementation, and we will simply
1660    //          trash it's content (when present).
1661    //        - still, when present, we could add some further checks on the
1662    //          lengths, but we won't bother with such fuses for the time being.
1663    if ( itemLength != 0 )
1664    {
1665       char *basicOffsetTableItemValue = new char[itemLength + 1];
1666       Fp->read(basicOffsetTableItemValue, itemLength);
1667
1668 #ifdef GDCM_DEBUG
1669       for (unsigned int i=0; i < itemLength; i += 4 )
1670       {
1671          uint32_t individualLength = str2num( &basicOffsetTableItemValue[i],
1672                                               uint32_t);
1673          gdcmVerboseMacro( "Read one length: " << 
1674                           std::hex << individualLength );
1675       }
1676 #endif //GDCM_DEBUG
1677
1678       delete[] basicOffsetTableItemValue;
1679    }
1680 }
1681
1682 //-----------------------------------------------------------------------------
1683
1684 } // end namespace gdcm