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