Orfeo Toolbox  3.16
itkGDCMImageIO.cxx
Go to the documentation of this file.
1 /*=========================================================================
2 
3  Program: Insight Segmentation & Registration Toolkit
4  Module: $RCSfile: itkGDCMImageIO.cxx,v $
5  Language: C++
6  Date: $Date: 2010-07-09 16:09:39 $
7  Version: $Revision: 1.171 $
8 
9  Copyright (c) Insight Software Consortium. All rights reserved.
10  See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details.
11 
12  Portions of this code are covered under the VTK copyright.
13  See VTKCopyright.txt or http://www.kitware.com/VTKCopyright.htm for details.
14 
15  This software is distributed WITHOUT ANY WARRANTY; without even
16  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
17  PURPOSE. See the above copyright notices for more information.
18 
19 =========================================================================*/
20 #include "gdcmFile.h"
21 
22 #include "itkVersion.h"
23 #include "itkGDCMImageIO.h"
24 #include "itkIOCommon.h"
25 #include "itkPoint.h"
26 #include "itkArray.h"
27 #include "itkMatrix.h"
28 #include <vnl/vnl_vector.h>
29 #include <vnl/vnl_matrix.h>
30 #include <vnl/vnl_cross.h>
31 
32 #include "itkMetaDataObject.h"
33 
34 #include <itksys/SystemTools.hxx>
35 #include <itksys/Base64.h>
36 
37 #if GDCM_MAJOR_VERSION < 2
38 #include "gdcmValEntry.h" //internal of gdcm
39 #include "gdcmBinEntry.h" //internal of gdcm
40 #include "gdcmFileHelper.h"
41 #include "gdcmUtil.h"
42 #include "gdcmGlobal.h" // access to dictionary
43 #include "gdcmDictSet.h" // access to dictionary
44 #else
45 #include "gdcmImageHelper.h"
46 #include "gdcmFileExplicitFilter.h"
47 #include "gdcmImageChangeTransferSyntax.h"
48 #include "gdcmDataSetHelper.h"
49 #include "gdcmStringFilter.h"
50 #include "gdcmImageApplyLookupTable.h"
51 #include "gdcmImageChangePlanarConfiguration.h"
52 #include "gdcmUnpacker12Bits.h"
53 #include "gdcmRescaler.h"
54 #include "gdcmFileMetaInformation.h"
55 #include "gdcmImageReader.h"
56 #include "gdcmImageWriter.h"
57 #include "gdcmUIDGenerator.h"
58 #include "gdcmAttribute.h"
59 #include "gdcmGlobal.h"
60 #include "gdcmDicts.h"
61 #include "gdcmDictEntry.h"
62 #endif
63 
64 #include <fstream>
65 #include <math.h> //for fabs on SGI
66 #include <itksys/ios/sstream>
67 
68 namespace itk
69 {
71 {
72 public:
74  gdcm::File *m_Header;
75 };
76 
77 // Initialize static members
78 
79 /* WARNING GDCM 1.x only WARNING Those options have no effect on GDCM 2.x as parsing is fast enough
80  *
81  * m_LoadPrivateTagsDefault:
82  * When this flag is set to false, GDCM 1.x will try to use the value stored in each private Group Length attribute value.
83  * This is a modest optimization feature that can be found in some ACR-NEMA file and/or DICOM pre-2008 file.
84  * Because it is required by the standard that DICOM file reader can read file where Group Length attribute value
85  * would be invalid, turning this flag to off, on the one hand might lead to some speed improvement, but on the
86  * other hand will make your DICOM implementation non-standard.
87  * Technically Group Length value could be incorrect and GDCM might even seek over some public element and not
88  * just the desired current group of attributes. Do not turn this option to false unless you understand the
89  * consequences.
90  *
91  * m_LoadSequencesDefault:
92  * Following the same idea (modest speed improvement), one can use a feature from DICOM and use the Value Length
93  * of a Sequence attribute to 'seek' over a large number of nested attributes.
94  * Again this feature can lead to some modest speed improvement, but seek'ing over public sequence is not
95  * a good idea. For instance you could be reading some new Enhanced MR Image Storage, where the Pixel Spacing
96  * is stored within a Sequence attribute, therefore Pixel Spacing would not be correct after a call to
97  * ExecuteInformation.
98  */
101 
102 
103 #if GDCM_MAJOR_VERSION < 2
104 // Minimal functionality to handle 12bits pixel for the Interval Calculator:
105 // Technically BitsAllocated should be considered but since GDCM
106 // always presents
107 // 12 Bits allocated stored pixel as if it was 16 bits allocated data
108 // we do not need to take it into account here.
109 template <
110  unsigned char VBitsAllocated,
111  unsigned char VBitsStored,
112  unsigned char VHighBit,
113  unsigned char VPixelRepresentation
114  >
115 struct m_Pixel; // no implementation for now
116 typedef m_Pixel<16,12,11,0> Pixel16_12_11_0;
117 typedef m_Pixel<16,12,11,1> Pixel16_12_11_1;
118 
119 template <>
120 class NumericTraits< Pixel16_12_11_0 > {
121 public:
122  typedef unsigned short ValueType;
123  static unsigned short min(void) { return 0; }
124  static unsigned short max(void) { return 4096; } // 2^12
125 };
126 template <>
127 class NumericTraits< Pixel16_12_11_1 > {
128 public:
129  typedef signed short ValueType;
130  static signed short min(void) { return -2048; } // -2^12
131  static signed short max(void) { return 2047; } // 2^12 - 1
132 };
133 
134 
135 class ICDirect // IntervalCalculatorDirect
136 {
137 public:
138  double operator() (double value, double slope, double intercept)
139  {
140  return value * slope + intercept;
141  }
142 };
143 class ICInverse // IntervalCalculatorInverse
144 {
145 public:
146  double operator() (double value, double slope, double intercept)
147  {
148  return ( value - intercept ) / slope;
149  }
150 };
151 
152 template <typename PixelType, typename TOperation>
154 {
155 public:
157  ComputeWithMinMax(double slope, double intercept, double minimum, double maximum)
158  {
160  double dmax, dmin; // do computation in double
161  TOperation op;
162  dmax = op(maximum, slope , intercept);
163  dmin = op(minimum, slope , intercept);
164 
165  // do the case in order:
166  if( dmin >= NumericTraits<unsigned char>::min() && dmax <= NumericTraits<unsigned char>::max() )
167  {
168  comptype = ImageIOBase::UCHAR;
169  }
170  else if( dmin >= NumericTraits<char>::min() && dmax <= NumericTraits<char>::max() )
171  {
172  comptype = ImageIOBase::CHAR;
173  }
174  else if( dmin >= NumericTraits<unsigned short>::min() && dmax <= NumericTraits<unsigned short>::max() )
175  {
176  comptype = ImageIOBase::USHORT;
177  }
178  else if( dmin >= NumericTraits<short>::min() && dmax <= NumericTraits<short>::max() )
179  {
180  comptype = ImageIOBase::SHORT;
181  }
182  else if( dmin >= NumericTraits<unsigned int>::min() && dmax <= NumericTraits<unsigned int>::max() )
183  {
184  comptype = ImageIOBase::UINT;
185  }
186  else if( dmin >= NumericTraits<int>::min() && dmax <= NumericTraits<int>::max() )
187  {
188  comptype = ImageIOBase::INT;
189  }
190  else
191  {
193  }
194  return comptype;
195  }
196 
198  Compute(double slope, double intercept)
199  {
200  typename NumericTraits<PixelType>::ValueType maximum = NumericTraits<PixelType>::max();
201  typename NumericTraits<PixelType>::ValueType minimum = NumericTraits<PixelType>::min();
202  return ComputeWithMinMax(slope, intercept, minimum, maximum);
203  }
204 
205 };
206 #endif
207 
209  : m_LoadSequences( m_LoadSequencesDefault ),
210  m_LoadPrivateTags( m_LoadPrivateTagsDefault )
211 {
212  this->m_DICOMHeader = new InternalHeader;
213  this->SetNumberOfDimensions(3); //needed for getting the 3 coordinates of
214  // the origin, even if it is a 2D slice.
215  m_ByteOrder = LittleEndian; //default
216  m_FileType = Binary; //default...always true
217  m_RescaleSlope = 1.0;
218  m_RescaleIntercept = 0.0;
219  // UIDPrefix is the ITK root id tacked with a ".1"
220  // allowing to designate a subspace of the id space for ITK generated DICOM
221  m_UIDPrefix = "1.2.826.0.1.3680043.2.1125." "1";
222 
223  // Purely internal use, no user access:
224  m_StudyInstanceUID = "";
225  m_SeriesInstanceUID = "";
227 
228  m_KeepOriginalUID = false;
229  m_MaxSizeLoadEntry = 0xfff;
230 
232 
233  // by default assume that images will be 2D.
234  // This number is updated according the information
235  // received through the MetaDataDictionary
237  // By default use JPEG2000. For legacy system, one should prefer JPEG since
238  // JPEG2000 was only recently added to the DICOM standard
240 }
241 
243 {
244  if (this->m_DICOMHeader->m_Header)
245  {
246  delete this->m_DICOMHeader->m_Header;
247  }
248  delete this->m_DICOMHeader;
249 }
250 
251 bool GDCMImageIO::OpenGDCMFileForReading(std::ifstream& os,
252  const char* filename)
253 {
254  // Make sure that we have a file to
255  if ( *filename == 0 )
256  {
257  itkExceptionMacro(<<"A FileName must be specified.");
258  }
259 
260  // Close file from any previous image
261  if ( os.is_open() )
262  {
263  os.close();
264  }
265 
266  // Open the new file for reading
267  itkDebugMacro(<< "Initialize: opening file " << filename);
268 
269  // Actually open the file
270  os.open( filename, std::ios::in | std::ios::binary );
271 
272  if ( os.fail() )
273  {
274  return false;
275  }
276 
277  return true;
278 }
279 
280 
281 bool GDCMImageIO::OpenGDCMFileForWriting(std::ofstream& os,
282  const char* filename)
283 {
284  // Make sure that we have a file to
285  if ( *filename == 0 )
286  {
287  itkExceptionMacro(<<"A FileName must be specified.");
288  }
289 
290  // Close file from any previous image
291  if ( os.is_open() )
292  {
293  os.close();
294  }
295 
296  // Open the new file for writing
297  itkDebugMacro(<< "Initialize: opening file " << filename);
298 
299 #ifdef __sgi
300  // Create the file. This is required on some older sgi's
301  std::ofstream tFile(filename,std::ios::out);
302  tFile.close();
303 #endif
304 
305  // Actually open the file
306  os.open( filename, std::ios::out | std::ios::binary );
307 
308  if( os.fail() )
309  {
310  itkExceptionMacro(<< "Could not open file: "
311  << filename << " for writing."
312  << std::endl
313  << "Reason: "
314  << itksys::SystemTools::GetLastSystemError());
315  }
316 
317 
318  return true;
319 }
320 
321 // This method will only test if the header looks like a
322 // GDCM image file.
323 bool GDCMImageIO::CanReadFile(const char* filename)
324 {
325  std::ifstream file;
326  std::string fname(filename);
327 
328  if( fname == "" )
329  {
330  itkDebugMacro(<<"No filename specified.");
331  return false;
332  }
333 
334  //Check for file existence:
335  if ( ! this->OpenGDCMFileForReading(file, filename))
336  {
337  return false;
338  }
339 
340  // Check to see if its a valid dicom file gdcm is able to parse:
341  // We are parsing the header one time here:
342 #if GDCM_MAJOR_VERSION < 2
343  bool preamble;
344  if( gdcm::Document::CanReadFile(file, preamble) )
345  {
346  itkWarningMacro(<< "The DICOM file: "
347  << filename
348  << " does not have a preamble.");
349  return true;
350  }
351 #else
352  gdcm::ImageReader reader;
353  reader.SetFileName( filename );
354  if( reader.Read() )
355  {
356  return true;
357  }
358 #endif
359  return false;
360 }
361 
362 #if GDCM_MAJOR_VERSION < 2
363 // Use a MACRO to exploit the Duff's device trick so that we can use for the direct
364 // rescale function AND the inverse rescale function
365 #define DUFF_DEVICE_8(aCount, aAction) \
366 { \
367  const size_t count_ = (aCount); \
368  register size_t times_ = (count_ + 7) >> 3; \
369  switch (count_ & 7){ \
370  case 0: do { aAction; \
371  case 7: aAction; \
372  case 6: aAction; \
373  case 5: aAction; \
374  case 4: aAction; \
375  case 3: aAction; \
376  case 2: aAction; \
377  case 1: aAction; \
378  } while (--times_ > 0); \
379  } \
380 }
381 
382 // Internal function to rescale pixel according to Rescale Slope/Intercept
383 template<class TBuffer, class TSource>
384 void RescaleFunction(TBuffer* buffer, TSource *source,
385  double slope, double intercept, size_t size)
386 {
387  size /= sizeof(TSource);
388 
389  if (slope != 1.0 && intercept != 0.0)
390  {
391  // Duff's device. Instead of this code:
392  //
393  // for(unsigned int i=0; i<size; i++)
394  // {
395  // buffer[i] = (TBuffer)(source[i]*slope + intercept);
396  // }
397  //
398  // use Duff's device which exploits "fall through"
399  DUFF_DEVICE_8(size, *buffer++ = (TBuffer)((*source++)*slope + intercept) );
400  }
401  else if (slope == 1.0 && intercept != 0.0)
402  {
403  // Duff's device. Instead of this code:
404  //
405  // for(unsigned int i=0; i<size; i++)
406  // {
407  // buffer[i] = (TBuffer)(source[i] + intercept);
408  // }
409  //
410  // use Duff's device which exploits "fall through"
411  TSource sintercept = (TSource)intercept;
412  if (sintercept == intercept)
413  {
414  // intercept is "really" the same type as source, e.g. a whole
415  // number intercept when the source is of type short
416  DUFF_DEVICE_8(size, *buffer++ = (TBuffer)(*source++ + sintercept) );
417  }
418  else
419  {
420  DUFF_DEVICE_8(size, *buffer++ = (TBuffer)(*source++ + intercept) );
421  }
422  }
423  else if (slope != 1.0 && intercept == 0.0)
424  {
425  // Duff's device. Instead of this code:
426  //
427  // for(unsigned int i=0; i<size; i++)
428  // {
429  // buffer[i] = (TBuffer)(source[i]*slope);
430  // }
431  //
432  // use Duff's device which exploits "fall through"
433  DUFF_DEVICE_8(size, *buffer++ = (TBuffer)((*source++)*slope) );
434  }
435  else
436  {
437  // Duff's device. Instead of this code:
438  //
439  // for(unsigned int i=0; i<size; i++)
440  // {
441  // buffer[i] = (TBuffer)(source[i]);
442  // }
443  //
444  // use Duff's device which exploits "fall through"
445  DUFF_DEVICE_8(size, *buffer++ = (TBuffer)(*source++) );
446  }
447 }
448 
449 // FIXME: Sorry for the duplicated code, but I cannot think of any other solution other
450 // than a template member function of class where arg would be deduce, unfortunately
451 // this does not work AFAIK on VS6.
452 // Internal function to implement the inverse operation of
453 // rescale pixel according to Rescale Slope/Intercept
454 template<class TBuffer, class TSource>
455 void RescaleFunctionInverse(TBuffer* buffer, TSource *source,
456  double slope, double intercept, size_t size)
457 {
458  size /= sizeof(TSource);
459 
460  if (slope != 1.0 && intercept != 0.0)
461  {
462  // Duff's device. Instead of this code:
463  //
464  // for(unsigned int i=0; i<size; i++)
465  // {
466  // buffer[i] = (TBuffer)(source[i]*slope + intercept);
467  // }
468  //
469  // use Duff's device which exploits "fall through"
470  DUFF_DEVICE_8(size, *buffer++ = (TBuffer)((*source++ - intercept) / slope) );
471  }
472  else if (slope == 1.0 && intercept != 0.0)
473  {
474  // Duff's device. Instead of this code:
475  //
476  // for(unsigned int i=0; i<size; i++)
477  // {
478  // buffer[i] = (TBuffer)(source[i] + intercept);
479  // }
480  //
481  // use Duff's device which exploits "fall through"
482  TSource sintercept = (TSource)intercept;
483  if (sintercept == intercept)
484  {
485  // intercept is "really" the same type as source, e.g. a whole
486  // number intercept when the source is of type short
487  DUFF_DEVICE_8(size, *buffer++ = (TBuffer)(*source++ - sintercept) );
488  }
489  else
490  {
491  DUFF_DEVICE_8(size, *buffer++ = (TBuffer)(*source++ - intercept) );
492  }
493  }
494  else if (slope != 1.0 && intercept == 0.0)
495  {
496  // Duff's device. Instead of this code:
497  //
498  // for(unsigned int i=0; i<size; i++)
499  // {
500  // buffer[i] = (TBuffer)(source[i]*slope);
501  // }
502  //
503  // use Duff's device which exploits "fall through"
504  DUFF_DEVICE_8(size, *buffer++ = (TBuffer)((*source++) / slope) );
505  }
506  else
507  {
508  // Duff's device. Instead of this code:
509  //
510  // for(unsigned int i=0; i<size; i++)
511  // {
512  // buffer[i] = (TBuffer)(source[i]);
513  // }
514  //
515  // use Duff's device which exploits "fall through"
516  DUFF_DEVICE_8(size, *buffer++ = (TBuffer)(*source++) );
517  }
518 }
519 
520 
521 template<class TSource>
523  void* buffer, TSource *source,
524  double slope, double intercept, size_t size)
525 {
526  switch (bufferType)
527  {
528  case ImageIOBase::UCHAR:
529  RescaleFunction( (unsigned char *)buffer, source, slope, intercept, size);
530  break;
531  case ImageIOBase::CHAR:
532  RescaleFunction( (char *)buffer, source, slope, intercept, size);
533  break;
534  case ImageIOBase::USHORT:
535  RescaleFunction( (unsigned short *)buffer, source, slope, intercept,size);
536  break;
537  case ImageIOBase::SHORT:
538  RescaleFunction( (short *)buffer, source, slope, intercept, size);
539  break;
540  case ImageIOBase::UINT:
541  RescaleFunction( (unsigned int *)buffer, source, slope, intercept, size);
542  break;
543  case ImageIOBase::INT:
544  RescaleFunction( (int *)buffer, source, slope, intercept, size);
545  break;
546  case ImageIOBase::FLOAT:
547  RescaleFunction( (float *)buffer, source, slope, intercept, size);
548  break;
549  case ImageIOBase::DOUBLE:
550  RescaleFunction( (double *)buffer, source, slope, intercept, size);
551  break;
552  default:
553  ::itk::OStringStream message;
554  message << "itk::ERROR: GDCMImageIO: Unknown component type : " << bufferType;
555  ::itk::ExceptionObject e(__FILE__, __LINE__, message.str().c_str(),ITK_LOCATION);
556  throw e;
557  }
558 }
559 
560 template<class TSource>
562  void* buffer, TSource *source,
563  double slope, double intercept, size_t size)
564 {
565  switch (bufferType)
566  {
567  case ImageIOBase::UCHAR:
568  RescaleFunctionInverse( (unsigned char *)buffer, source, slope, intercept, size);
569  break;
570  case ImageIOBase::CHAR:
571  RescaleFunctionInverse( (char *)buffer, source, slope, intercept, size);
572  break;
573  case ImageIOBase::USHORT:
574  RescaleFunctionInverse( (unsigned short *)buffer, source, slope, intercept,size);
575  break;
576  case ImageIOBase::SHORT:
577  RescaleFunctionInverse( (short *)buffer, source, slope, intercept, size);
578  break;
579  case ImageIOBase::UINT:
580  RescaleFunctionInverse( (unsigned int *)buffer, source, slope, intercept, size);
581  break;
582  case ImageIOBase::INT:
583  RescaleFunctionInverse( (int *)buffer, source, slope, intercept, size);
584  break;
585  // DICOM does not allow floating point type as stored value:
586  //case ImageIOBase::FLOAT:
587  // RescaleFunctionInverse( (float *)buffer, source, slope, intercept, size);
588  // break;
589  //case ImageIOBase::DOUBLE:
590  // RescaleFunctionInverse( (double *)buffer, source, slope, intercept, size);
591  // break;
592  default:
593  ::itk::OStringStream message;
594  message << "itk::ERROR: GDCMImageIO: Unknown component type : " << bufferType;
595  ::itk::ExceptionObject e(__FILE__, __LINE__, message.str().c_str(),ITK_LOCATION);
596  throw e;
597  }
598 }
599 #endif
600 
601 
602 #if GDCM_MAJOR_VERSION < 2
603 void GDCMImageIO::Read(void* buffer)
604 {
605  //Should I handle differently dicom lut ?
606  //GdcmHeader.HasLUT()
607 
608  gdcm::File *header = this->m_DICOMHeader->m_Header;
609  if( !header->IsReadable() )
610  {
611  itkExceptionMacro(<< "Could not read file: "
612  << m_FileName << std::endl
613  << "Reason: "
614  << itksys::SystemTools::GetLastSystemError());
615  }
616  gdcm::FileHelper gfile(header);
617 
618  size_t size = gfile.GetImageDataSize();
619  // Handle nasty case, where header says: single scalar but provides a LUT
620  if( header->HasLUT() && m_NumberOfComponents == 1 )
621  {
622  size = gfile.GetImageDataRawSize();
623  }
624  // source pointer is only a placeholder and can be not null when all info
625  // but the Pixel Data element 7fe0,0010 are present.
626  unsigned char *source = (unsigned char*)gfile.GetImageData();
627 
628  // We can rescale pixel only in grayscale image
629  if( m_NumberOfComponents == 1 )
630  {
632  {
633  case ImageIOBase::UCHAR:
634  {
635  RescaleFunction(m_ComponentType, buffer, (unsigned char*)source,
637  }
638  break;
639  case ImageIOBase::CHAR:
640  {
641  RescaleFunction(m_ComponentType, buffer, (char*)source,
643  }
644  break;
645  case ImageIOBase::USHORT:
646  {
647  RescaleFunction(m_ComponentType, buffer, (unsigned short*)source,
649  }
650  break;
651  case ImageIOBase::SHORT:
652  {
653  RescaleFunction(m_ComponentType, buffer, (short*)source,
655  }
656  break;
657  case ImageIOBase::UINT:
658  {
659  RescaleFunction(m_ComponentType, buffer, (unsigned int*)source,
661  }
662  break;
663  case ImageIOBase::INT:
664  {
665  RescaleFunction(m_ComponentType, buffer, (int*)source,
667  }
668  break;
669  case ImageIOBase::FLOAT:
670  {
671  RescaleFunction(m_ComponentType, buffer, (float*)source,
673  }
674  break;
675  case ImageIOBase::DOUBLE:
676  {
677  RescaleFunction(m_ComponentType, buffer, (double *)source,
679  }
680  break;
681  default:
682  itkExceptionMacro(<< "Unknown component type :" << m_InternalComponentType);
683  }
684  }
685  else
686  {
687  // This is a RGB buffer, only do a straight copy:
688  memcpy(buffer, source, size);
689  }
690 
691 // NOTE: source should not be deleted. gdcm controls the pointer.
692 
693 }
694 #else
695 // GDCM 2.x version
696 void GDCMImageIO::Read(void* pointer)
697 {
698  const char *filename = m_FileName.c_str();
699  assert( gdcm::ImageHelper::GetForceRescaleInterceptSlope() );
700  gdcm::ImageReader reader;
701  reader.SetFileName( filename );
702  if( !reader.Read() )
703  {
704  itkExceptionMacro(<< "Cannot read requested file");
705  return;
706  }
707 
708  gdcm::Image &image = reader.GetImage();
709  assert( image.GetNumberOfDimensions() == 2 || image.GetNumberOfDimensions() == 3 );
710  unsigned long len = image.GetBufferLength();
711 
712  const unsigned long numberOfBytesToBeRead =
713  static_cast< unsigned long>( this->GetImageSizeInBytes() );
714 
715  // I think ITK only allow RGB image by pixel (and not by plane)
716  if( image.GetPlanarConfiguration() == 1 )
717  {
718  gdcm::ImageChangePlanarConfiguration icpc;
719  icpc.SetInput( image );
720  icpc.SetPlanarConfiguration( 0 );
721  icpc.Change();
722  image = icpc.GetOutput();
723  }
724 
725  if( image.GetPhotometricInterpretation() == gdcm::PhotometricInterpretation::PALETTE_COLOR )
726  {
727  gdcm::ImageApplyLookupTable ialut;
728  ialut.SetInput( image );
729  ialut.Apply();
730  image = ialut.GetOutput();
731  len *= 3;
732  }
733 
734  image.GetBuffer((char*)pointer);
735 
736  const gdcm::PixelFormat &pixeltype = image.GetPixelFormat();
737  if( pixeltype == gdcm::PixelFormat::UINT12 || pixeltype == gdcm::PixelFormat::INT12 )
738  {
739  assert( m_RescaleSlope == 1.0 && m_RescaleIntercept == 0.0 );
740  assert( pixeltype.GetSamplesPerPixel() == 1 );
741  // FIXME: I could avoid this extra copy:
742  char * copy = new char[len];
743  memcpy(copy, pointer, len);
744  gdcm::Unpacker12Bits u12;
745  u12.Unpack((char*)pointer, copy, len);
746  // update len just in case:
747  len = 16 * len / 12;
748  delete[] copy;
749  }
750  if( m_RescaleSlope != 1.0 || m_RescaleIntercept != 0.0 )
751  {
752  gdcm::Rescaler r;
753  r.SetIntercept( m_RescaleIntercept );
754  r.SetSlope( m_RescaleSlope );
755  r.SetPixelFormat( pixeltype );
756  gdcm::PixelFormat outputpt = r.ComputeInterceptSlopePixelType();
757  char * copy = new char[len];
758  memcpy(copy, (char*)pointer, len);
759  r.Rescale((char*)pointer,copy,len);
760  delete[] copy;
761  // WARNING: sizeof(Real World Value) != sizeof(Stored Pixel)
762  len = len * outputpt.GetPixelSize() / pixeltype.GetPixelSize();
763  }
764 
765  // \postcondition
766  // Now that len was updated (after unpacker 12bits -> 16bits, rescale...) , can now check compat:
767  assert( numberOfBytesToBeRead == len ); // programmer error
768 
769 }
770 #endif
771 
772 
773 #if GDCM_MAJOR_VERSION < 2
775 {
776  //read header
777  if ( ! this->OpenGDCMFileForReading(file, m_FileName.c_str()) )
778  {
779  itkExceptionMacro(<< "Could not read file: "
780  << m_FileName << std::endl
781  << "Reason: "
782  << itksys::SystemTools::GetLastSystemError());
783  }
784 
785  gdcm::File *header = new gdcm::File;
786  delete this->m_DICOMHeader->m_Header;
787  this->m_DICOMHeader->m_Header = header;
788  header->SetMaxSizeLoadEntry(m_MaxSizeLoadEntry);
789  header->SetFileName( m_FileName );
790  header->SetLoadMode( (m_LoadSequences ? 0 : gdcm::LD_NOSEQ)
791  | (m_LoadPrivateTags ? 0 : gdcm::LD_NOSHADOW));
792  bool headerLoaded = header->Load();
793  if ( !headerLoaded )
794  {
795  itkExceptionMacro(<< "Could not load header from file: "
796  << m_FileName << std::endl
797  << "Reason: "
798  << itksys::SystemTools::GetLastSystemError());
799  }
800  if( !header->IsReadable() )
801  {
802  itkExceptionMacro(<< "Could not read header from file: "
803  << m_FileName << std::endl
804  << "Reason: "
805  << itksys::SystemTools::GetLastSystemError());
806  }
807 
808  // We don't need to positionate the Endian related stuff (by using
809  // this->SetDataByteOrderToBigEndian() or SetDataByteOrderToLittleEndian()
810  // since the reading of the file is done by gdcm.
811  // But we do need to set up the data type for downstream filters:
812 
813  int numComp = header->GetNumberOfScalarComponents();
814  this->SetNumberOfComponents(numComp);
815  if (numComp == 1)
816  {
817  this->SetPixelType(SCALAR);
818  }
819  else
820  {
821  this->SetPixelType(RGB);
822  }
823  std::string type = header->GetPixelType();
824  if( type == "8U")
825  {
827  }
828  else if( type == "8S")
829  {
831  }
832  else if( type == "16U")
833  {
835  }
836  else if( type == "16S")
837  {
839  }
840  else if( type == "32U")
841  {
843  }
844  else if( type == "32S")
845  {
847  }
848  else if ( type == "FD" )
849  {
850  //64 bits Double image
852  }
853  else
854  {
855  itkExceptionMacro(<<"Unrecognized type:" << type << " in file " << m_FileName);
856  }
857  // The internal component type (as on disk) will by default match
858  // the external component type. The external component type may
859  // change (below) as a function of the rescale slope and intersept.
861 
862  // set values in case we don't find them
863  m_Dimensions[0] = header->GetXSize();
864  m_Dimensions[1] = header->GetYSize();
865  m_Dimensions[2] = header->GetZSize();
866 
867  m_Spacing[0] = header->GetXSpacing();
868  m_Spacing[1] = header->GetYSpacing();
869  m_Spacing[2] = header->GetZSpacing();
870 
871 
872  float imageOrientation[6];
873  header->GetImageOrientationPatient(imageOrientation);
874  vnl_vector<double> rowDirection(3), columnDirection(3);
875 
876  rowDirection[0] = imageOrientation[0];
877  rowDirection[1] = imageOrientation[1];
878  rowDirection[2] = imageOrientation[2];
879 
880  columnDirection[0] = imageOrientation[3];
881  columnDirection[1] = imageOrientation[4];
882  columnDirection[2] = imageOrientation[5];
883 
884  vnl_vector<double> sliceDirection = vnl_cross_3d(rowDirection, columnDirection);
885  m_Direction.resize(3);
886  this->SetDirection(0, rowDirection);
887  this->SetDirection(1, columnDirection);
888  this->SetDirection(2, sliceDirection);
889 
890  // Dicom's origin is always in LPS
891  m_Origin[0] = header->GetXOrigin();
892  m_Origin[1] = header->GetYOrigin();
893  m_Origin[2] = header->GetZOrigin();
894 
895  //For grayscale image :
896  m_RescaleSlope = header->GetRescaleSlope();
897  m_RescaleIntercept = header->GetRescaleIntercept();
898 
899  // Before copying the image we need to check the slope/offset
900  // If they are not integer the scalar become FLOAT:
901  // Copy paste from DICOMAppHelper.cxx:
902  // 0028 1052 DS IMG Rescale Intercept
903  // 0028 1053 DS IMG Rescale Slope
904 
905  int s = int(m_RescaleSlope);
906  int i = int(m_RescaleIntercept);
907  float fs = float(s);
908  float fi = float(i);
909 
910  double slope_dif = vcl_fabs(fs - m_RescaleSlope);
911  double inter_dif = vcl_fabs(fi - m_RescaleIntercept);
912  if (slope_dif > 0.0 || inter_dif > 0.0)
913  {
915  {
917  }
918  }
919  else
920  {
921  // Let's make sure that the Stored pixel component type will be large enough to cover
922  // the actual pixel values:
923  switch (m_ComponentType)
924  {
925  case ImageIOBase::UCHAR:
926  m_ComponentType =
928  break;
929  case ImageIOBase::CHAR:
930  m_ComponentType =
932  break;
933  case ImageIOBase::USHORT:
934  m_ComponentType =
936  break;
937  case ImageIOBase::SHORT:
938  m_ComponentType =
940  break;
941  // RT Dose and Secondary Capture might have 32bits integer...
942  case ImageIOBase::UINT:
943  m_ComponentType =
945  break;
946  case ImageIOBase::INT:
947  m_ComponentType =
949  break;
950  default:
952  break;
953  }
954  // Handle here the special case where we are dealing with 12bits data :
955  if( header->GetEntryValue(0x0028, 0x0101) == "12" ) // Bits Stored
956  {
957  std::string sign = header->GetEntryValue(0x0028, 0x0103); // Pixel Representation
958  if ( sign == "0" )
959  {
960  m_ComponentType =
962  }
963  else if ( sign == "1" )
964  {
965  m_ComponentType =
967  }
968  else
969  {
970  itkExceptionMacro(<< "Pixel Representation cannot be handled: " << sign );
971  }
972  }
973  }
974 
975  //Now copying the gdcm dictionary to the itk dictionary:
976  MetaDataDictionary & dico = this->GetMetaDataDictionary();
977 
978  gdcm::DocEntry* d = header->GetFirstEntry();
979 
980  // Copy of the header->content
981  while(d)
982  {
983  // Because BinEntry is a ValEntry...
984  if ( gdcm::BinEntry* b = dynamic_cast<gdcm::BinEntry*>(d) )
985  {
986  if (b->GetName() != "Pixel Data" && b->GetName() != gdcm::GDCM_UNKNOWN
987  && b->GetVR() != "UN" )
988  {
989  if (b->GetValue() == gdcm::GDCM_BINLOADED )
990  {
991  // base64 streams have to be a multiple of 4 bytes long
992  int encodedLengthEstimate = 2 * b->GetLength();
993  encodedLengthEstimate = ((encodedLengthEstimate / 4) + 1) * 4;
994 
995  char *bin = new char[encodedLengthEstimate];
996  unsigned int encodedLengthActual = static_cast<unsigned int>(
997  itksysBase64_Encode(
998  (const unsigned char *) b->GetBinArea(),
999  static_cast< unsigned long>( b->GetLength() ),
1000  (unsigned char *) bin,
1001  static_cast< int >( 0 ) ));
1002  std::string encodedValue(bin, encodedLengthActual);
1003  EncapsulateMetaData<std::string>(dico, b->GetKey(), encodedValue);
1004  delete []bin;
1005  }
1006  }
1007  }
1008  else if ( gdcm::ValEntry* v = dynamic_cast<gdcm::ValEntry*>(d) )
1009  {
1010  // Only copying field from the public DICOM dictionary
1011  if( v->GetName() != gdcm::GDCM_UNKNOWN )
1012  {
1013  EncapsulateMetaData<std::string>(dico, v->GetKey(), v->GetValue() );
1014  }
1015  }
1016  //else
1017  // We skip pb of SQ recursive exploration, and we do not copy binary entries
1018 
1019  d = header->GetNextEntry();
1020  }
1021 
1022  // Now is a good time to fill in the class member:
1023  char name[512];
1024  this->GetPatientName(name);
1025  this->GetPatientID(name);
1026  this->GetPatientSex(name);
1027  this->GetPatientAge(name);
1028  this->GetStudyID(name);
1029  this->GetPatientDOB(name);
1030  this->GetStudyDescription(name);
1031  this->GetBodyPart(name);
1032  this->GetNumberOfSeriesInStudy(name);
1033  this->GetNumberOfStudyRelatedSeries(name);
1034  this->GetStudyDate(name);
1035  this->GetModality(name);
1036  this->GetManufacturer(name);
1037  this->GetInstitution(name);
1038  this->GetModel(name);
1039  this->GetScanOptions(name);
1040 
1041 }
1042 #else
1043 
1044 // TODO: this function was not part of gdcm::Tag API as of gdcm 2.0.10:
1045 std::string PrintAsPipeSeparatedString(const gdcm::Tag& tag)
1046 {
1047  itksys_ios::ostringstream os;
1048  os << std::hex << std::setw( 4 ) << std::setfill( '0' )
1049  << tag[0] << '|' << std::setw( 4 ) << std::setfill( '0' )
1050  << tag[1];
1051  std::string ret = os.str();
1052  return ret;
1053 }
1054 
1055 void GDCMImageIO::InternalReadImageInformation(std::ifstream& file)
1056 {
1057  //read header
1058  if ( ! this->OpenGDCMFileForReading(file, m_FileName.c_str()) )
1059  {
1060  itkExceptionMacro(<< "Cannot read requested file");
1061  }
1062 
1063  // In general this should be relatively safe to assume
1064  gdcm::ImageHelper::SetForceRescaleInterceptSlope(true);
1065 
1066  const char *filename = m_FileName.c_str();
1067  gdcm::ImageReader reader;
1068  reader.SetFileName( filename );
1069  if( !reader.Read() )
1070  {
1071  itkExceptionMacro(<< "Cannot read requested file");
1072  }
1073  const gdcm::Image &image = reader.GetImage();
1074  const gdcm::File &f = reader.GetFile();
1075  const gdcm::DataSet &ds = f.GetDataSet();
1076  const unsigned int *dims = image.GetDimensions();
1077 
1078  const gdcm::PixelFormat &pixeltype = image.GetPixelFormat();
1079 
1080  m_RescaleIntercept = image.GetIntercept();
1081  m_RescaleSlope = image.GetSlope();
1082  gdcm::Rescaler r;
1083  r.SetIntercept( m_RescaleIntercept );
1084  r.SetSlope( m_RescaleSlope );
1085  r.SetPixelFormat( pixeltype );
1086  gdcm::PixelFormat::ScalarType outputpt = r.ComputeInterceptSlopePixelType();
1087 
1088  assert( pixeltype <= outputpt );
1089 
1091  switch( outputpt )
1092  {
1093  case gdcm::PixelFormat::INT8:
1094  m_ComponentType = ImageIOBase::CHAR; // Is it signed char ?
1095  break;
1096  case gdcm::PixelFormat::UINT8:
1098  break;
1099  /* INT12 / UINT12 should not happen anymore in any modern DICOM */
1100  case gdcm::PixelFormat::INT12:
1102  break;
1103  case gdcm::PixelFormat::UINT12:
1105  break;
1106  case gdcm::PixelFormat::INT16:
1108  break;
1109  case gdcm::PixelFormat::UINT16:
1111  break;
1112  // RT / SC have 32bits
1113  case gdcm::PixelFormat::INT32:
1115  break;
1116  case gdcm::PixelFormat::UINT32:
1118  break;
1119  //case gdcm::PixelFormat::FLOAT16: // TODO
1120  case gdcm::PixelFormat::FLOAT32:
1122  break;
1123  case gdcm::PixelFormat::FLOAT64:
1125  break;
1126  default:
1127  itkExceptionMacro( "Unhandled PixelFormat: " << outputpt );
1128  }
1129 
1130  m_NumberOfComponents = pixeltype.GetSamplesPerPixel();
1131  if( image.GetPhotometricInterpretation() ==
1132  gdcm::PhotometricInterpretation::PALETTE_COLOR )
1133  {
1134  assert( m_NumberOfComponents == 1 );
1135  // TODO: need to do the LUT ourself...
1136  //itkExceptionMacro(<< "PALETTE_COLOR is not implemented yet");
1137  // AFAIK ITK user don't care about the palette so always apply it and fake a RGB image for them
1139  }
1140  if (m_NumberOfComponents == 1)
1141  {
1142  this->SetPixelType(SCALAR);
1143  }
1144  else
1145  {
1146  this->SetPixelType(RGB); // What if image is YBR ? This is a problem since integer conversion is lossy
1147  }
1148 
1149  // set values in case we don't find them
1150  //this->SetNumberOfDimensions( image.GetNumberOfDimensions() );
1151  m_Dimensions[0] = dims[0];
1152  m_Dimensions[1] = dims[1];
1153 
1154  const double *spacing = image.GetSpacing();
1155  m_Spacing[0] = spacing[0];
1156  m_Spacing[1] = spacing[1];
1157  m_Spacing[2] = spacing[2];
1158 
1159  const double *origin = image.GetOrigin();
1160  m_Origin[0] = origin[0];
1161  m_Origin[1] = origin[1];
1162  m_Origin[2] = origin[2];
1163 
1164  if( image.GetNumberOfDimensions() == 3 )
1165  {
1166  m_Dimensions[2] = dims[2];
1167  }
1168  else
1169  {
1170  m_Dimensions[2] = 1;
1171  }
1172 
1173  const double *dircos = image.GetDirectionCosines();
1174  vnl_vector<double> rowDirection(3), columnDirection(3);
1175  rowDirection[0] = dircos[0];
1176  rowDirection[1] = dircos[1];
1177  rowDirection[2] = dircos[2];
1178  columnDirection[0] = dircos[3];
1179  columnDirection[1] = dircos[4];
1180  columnDirection[2] = dircos[5];
1181 
1182  vnl_vector<double> sliceDirection = vnl_cross_3d(rowDirection, columnDirection);
1183  this->SetDirection(0, rowDirection);
1184  this->SetDirection(1, columnDirection);
1185  this->SetDirection(2, sliceDirection);
1186 
1187  //Now copying the gdcm dictionary to the itk dictionary:
1188  MetaDataDictionary & dico = this->GetMetaDataDictionary();
1189 
1190  gdcm::StringFilter sf;
1191  sf.SetFile( f );
1192  gdcm::DataSet::ConstIterator it = ds.Begin();
1193 
1194  // Copy of the header->content
1195  for(; it != ds.End(); ++it)
1196  {
1197  const gdcm::DataElement &ref = *it;
1198  const gdcm::Tag &tag = ref.GetTag();
1199  // Compute VR from the toplevel file, and the currently processed dataset:
1200  gdcm::VR vr = gdcm::DataSetHelper::ComputeVR(f, ds, tag);
1201 
1202  // Process binary field and encode them as mime64: only when we do not know of any better
1203  // representation. VR::US is binary, but user want ASCII representation.
1204  if ( vr & (gdcm::VR::OB | gdcm::VR::OF | gdcm::VR::OW | gdcm::VR::SQ | gdcm::VR::UN) )
1205  {
1206  // assert( vr & gdcm::VR::VRBINARY );
1207  /*
1208  * Old behavior was to skip SQ, Pixel Data element. I decided that it is not safe to mime64
1209  * VR::UN element. There used to be a bug in gdcm 1.2.0 and VR:UN element.
1210  */
1211  if ( (m_LoadPrivateTags || tag.IsPublic()) && vr != gdcm::VR::SQ
1212  && tag != gdcm::Tag(0x7fe0,0x0010) /* && vr != gdcm::VR::UN*/ )
1213  {
1214  const gdcm::ByteValue *bv = ref.GetByteValue();
1215  if( bv )
1216  {
1217  // base64 streams have to be a multiple of 4 bytes long
1218  int encodedLengthEstimate = 2 * bv->GetLength();
1219  encodedLengthEstimate = ((encodedLengthEstimate / 4) + 1) * 4;
1220 
1221  char *bin = new char[encodedLengthEstimate];
1222  unsigned int encodedLengthActual = static_cast<unsigned int>(
1223  itksysBase64_Encode(
1224  (const unsigned char *) bv->GetPointer(),
1225  static_cast< unsigned long>( bv->GetLength() ),
1226  (unsigned char *) bin,
1227  static_cast< int >( 0 ) ));
1228  std::string encodedValue(bin, encodedLengthActual);
1229  EncapsulateMetaData<std::string>(dico, PrintAsPipeSeparatedString(tag), encodedValue);
1230  delete []bin;
1231  }
1232  }
1233  }
1234  else /* if ( vr & gdcm::VR::VRASCII ) */
1235  {
1236  // Only copying field from the public DICOM dictionary
1237  if( m_LoadPrivateTags || tag.IsPublic() )
1238  {
1239  EncapsulateMetaData<std::string>(dico, PrintAsPipeSeparatedString(tag), sf.ToString( tag ) );
1240  }
1241  }
1242 
1243  }
1244 
1245 
1246  // Now is a good time to fill in the class member:
1247  char name[512];
1248  this->GetPatientName(name);
1249  this->GetPatientID(name);
1250  this->GetPatientSex(name);
1251  this->GetPatientAge(name);
1252  this->GetStudyID(name);
1253  this->GetPatientDOB(name);
1254  this->GetStudyDescription(name);
1255  this->GetBodyPart(name);
1256  this->GetNumberOfSeriesInStudy(name);
1257  this->GetNumberOfStudyRelatedSeries(name);
1258  this->GetStudyDate(name);
1259  this->GetModality(name);
1260  this->GetManufacturer(name);
1261  this->GetInstitution(name);
1262  this->GetModel(name);
1263  this->GetScanOptions(name);
1264 
1265 }
1266 #endif
1267 
1269 {
1270  std::ifstream file;
1271  this->InternalReadImageInformation(file);
1272 }
1273 
1274 
1275 bool GDCMImageIO::CanWriteFile(const char* name)
1276 {
1277  std::string filename = name;
1278 
1279  if( filename == "" )
1280  {
1281  itkDebugMacro(<<"No filename specified.");
1282  return false;
1283  }
1284 
1285  std::string::size_type dcmPos = filename.rfind(".dcm");
1286  if ( (dcmPos != std::string::npos)
1287  && (dcmPos == filename.length() - 4) )
1288  {
1289  return true;
1290  }
1291 
1292  dcmPos = filename.rfind(".DCM");
1293  if ( (dcmPos != std::string::npos)
1294  && (dcmPos == filename.length() - 4) )
1295  {
1296  return true;
1297  }
1298 
1299  std::string::size_type dicomPos = filename.rfind(".dicom");
1300  if ( (dicomPos != std::string::npos)
1301  && (dicomPos == filename.length() - 6) )
1302  {
1303  return true;
1304  }
1305 
1306  dicomPos = filename.rfind(".DICOM");
1307  if ( (dicomPos != std::string::npos)
1308  && (dicomPos == filename.length() - 6) )
1309  {
1310  return true;
1311  }
1312 
1313  return false;
1314 }
1315 
1317 {
1318 }
1319 
1320 #if GDCM_MAJOR_VERSION < 2
1321 void GDCMImageIO::Write(const void* buffer)
1322 {
1323  std::ofstream file;
1324  if ( !this->OpenGDCMFileForWriting(file, m_FileName.c_str()) )
1325  {
1326  return;
1327  }
1328  file.close();
1329 
1330  gdcm::File *header = new gdcm::File();
1331  gdcm::FileHelper *gfile = new gdcm::FileHelper( header );
1332 
1333  std::string value;
1334  MetaDataDictionary & dict = this->GetMetaDataDictionary();
1335 #if defined(_MSC_VER) && _MSC_VER < 1300
1336  // Not using real iterators, but instead the GetKeys() method
1337  // since VS6 is broken and does not export properly iterators
1338  // GetKeys will duplicate the entire DICOM header
1339  std::vector<std::string> keys = dict.GetKeys();
1340  for( std::vector<std::string>::const_iterator it = keys.begin();
1341  it != keys.end(); ++it )
1342  {
1343  const std::string &key = *it; //Needed for bcc32
1344 #else
1345  //Smarter approach using real iterators
1348  while(itr != end)
1349  {
1350  const std::string &key = itr->first; //Needed for bcc32
1351 #endif
1352  ExposeMetaData<std::string>(dict, key, value);
1353 
1354  // Convert DICOM name to DICOM (group,element)
1355  gdcm::DictEntry *dictEntry =
1356  header->GetPubDict()->GetEntry(key);
1357  // Anything that has been changed in the MetaData Dict will be pushed
1358  // into the DICOM header:
1359  if (dictEntry)
1360  {
1361  if (dictEntry->GetVR() != "OB" && dictEntry->GetVR() != "OW")
1362  {
1363  // TODO, should we keep:
1364  // (0028,0106) US/SS 0 # 2, 1 SmallestImagePixelValue
1365  // (0028,0107) US/SS 4095 # 2, 1 LargestImagePixelValue
1366  if(dictEntry->GetElement() != 0) // Get rid of group length, they are not useful
1367  {
1368  header->InsertValEntry( value,
1369  dictEntry->GetGroup(),
1370  dictEntry->GetElement());
1371  }
1372  }
1373  else
1374  {
1375  // convert value from Base64
1376  uint8_t *bin = new uint8_t[value.size()];
1377  unsigned int decodedLengthActual = static_cast<unsigned int>(
1378  itksysBase64_Decode(
1379  (const unsigned char *) value.c_str(),
1380  static_cast<unsigned long>( 0 ),
1381  (unsigned char *) bin,
1382  static_cast<unsigned long>( value.size())));
1383  if(dictEntry->GetGroup() != 0 || dictEntry->GetElement() != 0)
1384  {
1385  header->InsertBinEntry( bin,
1386  decodedLengthActual,
1387  dictEntry->GetGroup(),
1388  dictEntry->GetElement());
1389  }
1390  delete []bin;
1391  }
1392  }
1393  else
1394  {
1395  // This is not a DICOM entry, then check if it is one of the
1396  // ITK standard ones
1397  if( key == ITK_NumberOfDimensions )
1398  {
1399  unsigned int numberOfDimensions = 0;
1400  ExposeMetaData<unsigned int>(dict, key, numberOfDimensions);
1401  m_GlobalNumberOfDimensions = numberOfDimensions;
1402  m_Origin.resize( m_GlobalNumberOfDimensions );
1403  m_Spacing.resize( m_GlobalNumberOfDimensions );
1404  }
1405  else if( key == ITK_Origin )
1406  {
1407  typedef Array< double > DoubleArrayType;
1408  DoubleArrayType originArray;
1409  ExposeMetaData< DoubleArrayType >( dict, key, originArray );
1410  m_Origin[0] = originArray[0];
1411  m_Origin[1] = originArray[1];
1412  m_Origin[2] = originArray[2];
1413  }
1414  else if( key == ITK_Spacing )
1415  {
1416  typedef Array< double > DoubleArrayType;
1417  DoubleArrayType spacingArray;
1418  ExposeMetaData< DoubleArrayType >( dict, key, spacingArray );
1419  m_Spacing[0] = spacingArray[0];
1420  m_Spacing[1] = spacingArray[1];
1421  m_Spacing[2] = spacingArray[2];
1422  }
1423  else
1424  {
1425  itkDebugMacro(
1426  << "GDCMImageIO: non-DICOM and non-ITK standard key = " << key );
1427  }
1428  }
1429 
1430 #if !(defined(_MSC_VER) && _MSC_VER < 1300)
1431  ++itr;
1432 #endif
1433  }
1434 
1435  // Handle the dimension of image:
1436  itksys_ios::ostringstream str;
1437  str << m_Dimensions[0];
1438  header->InsertValEntry( str.str(), 0x0028,0x0011); // Columns
1439 
1440  str.str("");
1441  str << m_Dimensions[1];
1442  header->InsertValEntry( str.str(), 0x0028,0x0010); // Rows
1443 
1444  if( m_Dimensions.size() > 2 && m_Dimensions[2] > 1 )
1445  {
1446  str.str("");
1447  str << m_Dimensions[2];
1448  //header->Insert(str.str(),0x0028,0x0012); // Planes
1449  header->InsertValEntry(str.str(),0x0028,0x0008); // Number of Frames
1450  }
1451 
1452  // Handle pixel spacing:
1453  str.str("");
1454  str.setf( itksys_ios::ios::fixed ); //forcing precision to 6 digits
1455  str << m_Spacing[1] << "\\" << m_Spacing[0];
1456  header->InsertValEntry(str.str(),0x0028,0x0030); // Pixel Spacing
1457 
1458  // Anyway we will still allow writing of the 3d component of the spacing
1459  // when we are writing 3d images:
1460  if(m_Dimensions.size() > 2 && m_Dimensions[2]>1)
1461  {
1462  str.str("");
1463  str << m_Spacing[2];
1464  header->InsertValEntry(str.str(),0x0018,0x0088); // Spacing Between Slices
1465  }
1466 
1467  // This code still needs work. Spacing, origin and direction are all 3D, yet
1468  // the image is 2D. If the user set these, all is well, because the user will
1469  // pass in the proper number (3) of elements. However, ImageSeriesWriter will
1470  // call its ImageIO with 2D images and only pass in spacing, origin and
1471  // direction with 2 elements. For now, we expect that the MetaDataDictionary
1472  // will have the proper settings for pixel spacing, spacing between slices,
1473  // image position patient and the row/column direction cosines.
1474 
1475  // In the case where user specifically set the Image Position (Patient) either
1476  // directly or indirectly using SetMetaDataDictionaryArray, we should not try
1477  // to override it's input
1478  if( !header->GetValEntry(0x0020,0x0032 ) )
1479  {
1480  str.str("");
1481  str << m_Origin[0] << "\\" << m_Origin[1] << "\\";
1482 
1483  if( m_Origin.size() == 3 )
1484  {
1485  str << m_Origin[2];
1486  }
1487  else // We are coming from the default SeriesWriter which is passing us a 2D image
1488  // therefore default to a Z position = 0, this will make the image at least valid
1489  // if not correct
1490  {
1491  str << 0.;
1492  }
1493  header->InsertValEntry(str.str(),0x0020,0x0032); // Image Position (Patient)
1494  }
1495 
1496  // Handle Direction = Image Orientation Patient
1497  // Same comment as above, if user tell us what the Orientation is, we should not try
1498  // to set if from the Image as we might have lost some information
1499  if( !header->GetValEntry(0x0020,0x0037 ) )
1500  {
1501  str.str("");
1502  str << m_Direction[0][0] << "\\"
1503  << m_Direction[0][1] << "\\";
1504  /*
1505  * This is where the 3rd component of the direction is being lost
1506  * ITK mechanism does not support 2D image, placed in 3D world...
1507  */
1508  if( m_Direction.size() == 3 )
1509  {
1510  str << m_Direction[0][2] << "\\";
1511  }
1512  else
1513  {
1514  str << 0. << "\\";
1515  }
1516  str << m_Direction[1][0] << "\\"
1517  << m_Direction[1][1] << "\\";
1518  if( m_Direction.size() == 3 )
1519  {
1520  str << m_Direction[1][2];
1521  }
1522  else
1523  {
1524  str << 0.;
1525  }
1526  header->InsertValEntry(str.str(),0x0020,0x0037); // Image Orientation (Patient)
1527  }
1528 
1529  str.unsetf( itksys_ios::ios::fixed ); // back to normal
1530 
1531  // reset any previous value:
1532  m_RescaleSlope = 1.0;
1533  m_RescaleIntercept = 0.0;
1534 
1535  // Get user defined rescale slope/intercept
1536  std::string rescaleintercept;
1537  ExposeMetaData<std::string>(dict, "0028|1052" , rescaleintercept);
1538  std::string rescaleslope;
1539  ExposeMetaData<std::string>(dict, "0028|1053" , rescaleslope);
1540  if( rescaleintercept != "" && rescaleslope != "" )
1541  {
1542  itksys_ios::stringstream sstr1;
1543  sstr1 << rescaleintercept;
1544  if( ! (sstr1 >> m_RescaleIntercept) )
1545  {
1546  itkExceptionMacro( "Problem reading RescaleIntercept: " << rescaleintercept );
1547  }
1548  itksys_ios::stringstream sstr2;
1549  sstr2 << rescaleslope;
1550  if( !(sstr2 >> m_RescaleSlope) )
1551  {
1552  itkExceptionMacro( "Problem reading RescaleSlope: " << rescaleslope );
1553  }
1554  header->InsertValEntry( "US", 0x0028, 0x1054 ); // Rescale Type
1555  }
1556  else if( rescaleintercept != "" || rescaleslope != "" ) // xor
1557  {
1558  itkExceptionMacro( "Both RescaleSlope & RescaleIntercept need to be present" );
1559  }
1560 
1561  // Write Explicit for both 1 and 3 components images:
1562  gfile->SetWriteTypeToDcmExplVR();
1563 
1564  // Handle the bitDepth:
1565  std::string bitsAllocated;
1566  std::string bitsStored;
1567  std::string highBit;
1568  std::string pixelRep;
1569  // Get user defined bit representation:
1570  ExposeMetaData<std::string>(dict, "0028|0100", bitsAllocated);
1571  ExposeMetaData<std::string>(dict, "0028|0101", bitsStored);
1572  ExposeMetaData<std::string>(dict, "0028|0102", highBit);
1573  ExposeMetaData<std::string>(dict, "0028|0103", pixelRep);
1574  // If one is missing then recompute them from the image itself:
1575  if( bitsAllocated == "" || bitsStored == "" || highBit == "" || pixelRep == "" )
1576  {
1577  if( m_NumberOfComponents == 1 )
1578  {
1579  switch (this->GetComponentType())
1580  {
1581  case ImageIOBase::CHAR:
1582  bitsAllocated = "8"; // Bits Allocated
1583  bitsStored = "8"; // Bits Stored
1584  highBit = "7"; // High Bit
1585  pixelRep = "1"; // Pixel Representation
1586  break;
1587 
1588  case ImageIOBase::UCHAR:
1589  bitsAllocated = "8"; // Bits Allocated
1590  bitsStored = "8"; // Bits Stored
1591  highBit = "7"; // High Bit
1592  pixelRep = "0"; // Pixel Representation
1593  break;
1594 
1595  case ImageIOBase::SHORT:
1596  bitsAllocated = "16"; // Bits Allocated
1597  bitsStored = "16"; // Bits Stored
1598  highBit = "15"; // High Bit
1599  pixelRep = "1"; // Pixel Representation
1600  break;
1601 
1602  case ImageIOBase::USHORT:
1603  bitsAllocated = "16"; // Bits Allocated
1604  bitsStored = "16"; // Bits Stored
1605  highBit = "15"; // High Bit
1606  pixelRep = "0"; // Pixel Representation
1607  break;
1608 
1609  //Disabling INT and UINT for now...
1610  case ImageIOBase::INT:
1611  bitsAllocated = "32"; // Bits Allocated
1612  bitsStored = "32"; // Bits Stored
1613  highBit = "31"; // High Bit
1614  pixelRep = "1"; // Pixel Representation
1615  break;
1616 
1617  case ImageIOBase::UINT:
1618  bitsAllocated = "32"; // Bits Allocated
1619  bitsStored = "32"; // Bits Stored
1620  highBit = "31"; // High Bit
1621  pixelRep = "0"; // Pixel Representation
1622  break;
1623 
1624  case ImageIOBase::FLOAT:
1625  case ImageIOBase::DOUBLE:
1626  // Disable that mode for now as we would need to compute on the fly the min/max of the image to
1627  // compute a somewhat correct shift/scale transform:
1628  itkExceptionMacro(<<"A Floating point buffer was passed but the stored pixel type was not specified."
1629  "This is currently not supported" );
1630  break;
1631  default:
1632  itkExceptionMacro(<<"DICOM does not support this component type");
1633  }
1634  }
1635  else if( m_NumberOfComponents == 3 )
1636  {
1637  // Write the image as RGB DICOM
1638  gfile->SetWriteModeToRGB();
1639  switch (this->GetComponentType())
1640  {
1641  case ImageIOBase::CHAR:
1642  bitsAllocated = "8"; // Bits Allocated
1643  bitsStored = "8"; // Bits Stored
1644  highBit = "7"; // High Bit
1645  pixelRep = "1"; // Pixel Representation
1646  break;
1647 
1648  case ImageIOBase::UCHAR:
1649  bitsAllocated = "8"; // Bits Allocated
1650  bitsStored = "8"; // Bits Stored
1651  highBit = "7"; // High Bit
1652  pixelRep = "0"; // Pixel Representation
1653  break;
1654  default:
1655  itkExceptionMacro(<<"DICOM does not support this component type");
1656  }
1657  }
1658  else
1659  {
1660  itkExceptionMacro(
1661  <<"DICOM does not support RGBPixels with components != 3");
1662  }
1663  }
1664  // Write component specific information in the header:
1665  header->InsertValEntry( bitsAllocated, 0x0028, 0x0100 ); //Bits Allocated
1666  header->InsertValEntry( bitsStored, 0x0028, 0x0101 ); //Bits Stored
1667  header->InsertValEntry( highBit, 0x0028, 0x0102 ); //High Bit
1668  header->InsertValEntry( pixelRep, 0x0028, 0x0103 ); //Pixel Representation
1669 
1670  str.str("");
1671  str << m_NumberOfComponents;
1672  header->InsertValEntry(str.str(),0x0028,0x0002); // Samples per Pixel
1673 
1674  // Now is a good time to compute the internal type that will be used to store the image on disk:
1675  std::string type = header->GetPixelType();
1676  if( type == "8U")
1677  {
1679  }
1680  else if( type == "8S")
1681  {
1683  }
1684  else if( type == "16U")
1685  {
1687  }
1688  else if( type == "16S")
1689  {
1691  }
1692  else if( type == "32U")
1693  {
1695  }
1696  else if( type == "32S")
1697  {
1699  }
1700  else
1701  {
1702  itkExceptionMacro(<<"Unrecognized type:" << type << " in file " << m_FileName);
1703  }
1704 
1705  if( !m_KeepOriginalUID )
1706  {
1707  // UID generation part:
1708  // We only create *ONE* Study/Series.Frame of Reference Instance UID
1709  if( m_StudyInstanceUID.empty() )
1710  {
1711  // As long as user maintain there gdcmIO they will keep the same
1712  // Study/Series instance UID.
1713  m_StudyInstanceUID = gdcm::Util::CreateUniqueUID( m_UIDPrefix );
1714  m_SeriesInstanceUID = gdcm::Util::CreateUniqueUID( m_UIDPrefix );
1715  m_FrameOfReferenceInstanceUID = gdcm::Util::CreateUniqueUID( m_UIDPrefix );
1716  }
1717  std::string uid = gdcm::Util::CreateUniqueUID( m_UIDPrefix );
1718 
1719  header->InsertValEntry( uid, 0x0008, 0x0018); //[SOP Instance UID]
1720  header->InsertValEntry( uid, 0x0002, 0x0003); //[Media Stored SOP Instance UID]
1721  header->InsertValEntry( m_StudyInstanceUID, 0x0020, 0x000d); //[Study Instance UID]
1722  header->InsertValEntry( m_SeriesInstanceUID, 0x0020, 0x000e); //[Series Instance UID]
1723  //header->InsertValEntry( m_FrameOfReferenceInstanceUID, 0x0020, 0x0052); //[Frame of Reference UID]
1724  // Secondary Capture Image Storage SOP Class
1725  //header->InsertValEntry( "1.2.840.10008.5.1.4.1.1.7", 0x0002, 0x0012); //[Implementation Class UID]
1726  }
1727 
1728  // size is the size of the actual image in memory
1729  size_t size = static_cast< size_t >( this->GetImageSizeInBytes() );
1730 
1731  // numberOfBytes is the number of bytes the image will hold on disk, most of the time
1732  // those two are equal
1733  size_t numberOfBytes;
1734 #if GDCM_MAJOR_VERSION <= 1 && GDCM_MINOR_VERSION <= 2 && GDCM_BUILD_VERSION <= 3
1735  numberOfBytes = size;
1736 #else
1737  numberOfBytes = gfile->ComputeExpectedImageDataSize();
1738 #endif
1739 
1740  //copy data from buffer to DICOM buffer
1741  uint8_t* imageData = new uint8_t[numberOfBytes];
1742 
1743  // Technically when user is passing dictionary back m_InternalComponentType should still be set
1744  // We only need to recompute it when the user passes in a non-DICOM input file
1745  // FIXME: is this robust in all cases ?
1747 
1748  // Do the inverse rescale !
1749  if( m_NumberOfComponents == 1 )
1750  {
1751  switch(m_ComponentType)
1752  {
1753  case ImageIOBase::UCHAR:
1754  {
1755  RescaleFunctionInverse(m_InternalComponentType, imageData, (unsigned char*)buffer,
1757  }
1758  break;
1759  case ImageIOBase::CHAR:
1760  {
1761  RescaleFunctionInverse(m_InternalComponentType, imageData, (char*)buffer,
1763  }
1764  break;
1765  case ImageIOBase::USHORT:
1766  {
1767  RescaleFunctionInverse(m_InternalComponentType, imageData, (unsigned short*)buffer,
1769  }
1770  break;
1771  case ImageIOBase::SHORT:
1772  {
1773  RescaleFunctionInverse(m_InternalComponentType, imageData, (short*)buffer,
1775  }
1776  break;
1777  case ImageIOBase::UINT:
1778  {
1779  RescaleFunctionInverse(m_InternalComponentType, imageData, (unsigned int*)buffer,
1781  }
1782  break;
1783  case ImageIOBase::INT:
1784  {
1785  RescaleFunctionInverse(m_InternalComponentType, imageData, (int*)buffer,
1787  }
1788  break;
1789  case ImageIOBase::FLOAT:
1790  {
1791  RescaleFunctionInverse(m_InternalComponentType, imageData, (float*)buffer,
1793  }
1794  break;
1795  case ImageIOBase::DOUBLE:
1796  {
1797  RescaleFunctionInverse(m_InternalComponentType, imageData, (double *)buffer,
1799  }
1800  break;
1801  default:
1802  itkExceptionMacro(<< "Unknown component type :" << m_ComponentType);
1803  }
1804  }
1805  else
1806  {
1807  // This is a RGB buffer, only do a straight copy:
1808  memcpy(imageData, buffer, numberOfBytes);
1809  }
1810 
1811  // If user ask to use compression:
1812  if( m_UseCompression )
1813  {
1814  if( m_CompressionType == JPEG )
1815  {
1816  gfile->SetWriteTypeToJPEG();
1817  }
1818  else if ( m_CompressionType == JPEG2000 )
1819  {
1820  gfile->SetWriteTypeToJPEG2000();
1821  }
1822  else
1823  {
1824  itkExceptionMacro(<< "Unknown compression type" );
1825  }
1826  }
1827 
1828  gfile->SetUserData( imageData, numberOfBytes);
1829  if( ! gfile->Write( m_FileName ) )
1830  {
1831  itkExceptionMacro(<< "Cannot write the requested file:"
1832  << m_FileName
1833  << std::endl
1834  << "Reason: "
1835  << itksys::SystemTools::GetLastSystemError());
1836  }
1837 
1838  // Clean up
1839  delete [] imageData;
1840  delete gfile;
1841  delete header;
1842 }
1843 #else
1844 void GDCMImageIO::Write(const void* buffer)
1845 {
1846  std::ofstream file;
1847  if ( !this->OpenGDCMFileForWriting(file, m_FileName.c_str()) )
1848  {
1849  return;
1850  }
1851  file.close();
1852  // global static:
1853  gdcm::UIDGenerator::SetRoot( m_UIDPrefix.c_str() );
1854 
1855  // echo "ITK" | od -b
1856  gdcm::FileMetaInformation::AppendImplementationClassUID( "111.124.113" );
1857  const std::string project_name = std::string("GDCM/ITK ") + itk::Version::GetITKVersion();
1858  gdcm::FileMetaInformation::SetSourceApplicationEntityTitle( project_name.c_str() );
1859 
1860  gdcm::ImageWriter writer;
1861  gdcm::DataSet &header = writer.GetFile().GetDataSet();
1862  gdcm::Global &g = gdcm::Global::GetInstance();
1863  const gdcm::Dicts &dicts = g.GetDicts();
1864  const gdcm::Dict &pubdict = dicts.GetPublicDict();
1865 
1866  std::string value;
1867  MetaDataDictionary & dict = this->GetMetaDataDictionary();
1868  gdcm::Tag tag;
1869  //Smarter approach using real iterators
1872  gdcm::StringFilter sf;
1873  sf.SetFile( writer.GetFile() );
1874 
1875  while(itr != end)
1876  {
1877  const std::string &key = itr->first; //Needed for bcc32
1878  ExposeMetaData<std::string>(dict, key, value);
1879 
1880  // Convert DICOM name to DICOM (group,element)
1881  bool b = tag.ReadFromPipeSeparatedString( key.c_str() );
1882 
1883  // Anything that has been changed in the MetaData Dict will be pushed
1884  // into the DICOM header:
1885  if ( b /*tag != gdcm::Tag(0xffff,0xffff)*/ /*dictEntry*/)
1886  {
1887  const gdcm::DictEntry &dictEntry = pubdict.GetDictEntry(tag);
1888  gdcm::VR::VRType vrtype = dictEntry.GetVR();
1889  if ( dictEntry.GetVR() == gdcm::VR::SQ )
1890  {
1891  // How did we reach here ?
1892  }
1893  else if ( vrtype & (gdcm::VR::OB | gdcm::VR::OF | gdcm::VR::OW /*| gdcm::VR::SQ*/ | gdcm::VR::UN) )
1894  {
1895  // Custom VR::VRBINARY
1896  // convert value from Base64
1897  uint8_t *bin = new uint8_t[value.size()];
1898  unsigned int decodedLengthActual = static_cast<unsigned int>(
1899  itksysBase64_Decode(
1900  (const unsigned char *) value.c_str(),
1901  static_cast<unsigned long>( 0 ),
1902  (unsigned char *) bin,
1903  static_cast<unsigned long>( value.size())));
1904  if( /*tag.GetGroup() != 0 ||*/ tag.GetElement() != 0) // ?
1905  {
1906  gdcm::DataElement de( tag );
1907  de.SetByteValue( (char*)bin, decodedLengthActual );
1908  de.SetVR( dictEntry.GetVR() );
1909  header.Insert( de );
1910  }
1911  delete []bin;
1912  }
1913  else // VRASCII
1914  {
1915  // TODO, should we keep:
1916  // (0028,0106) US/SS 0 # 2, 1 SmallestImagePixelValue
1917  // (0028,0107) US/SS 4095 # 2, 1 LargestImagePixelValue
1918  if(!tag.IsGroupLength()) // Get rid of group length, they are not useful
1919  {
1920  gdcm::DataElement de( tag );
1921  if( dictEntry.GetVR().IsVRFile() )
1922  de.SetVR( dictEntry.GetVR() );
1923 #if GDCM_MAJOR_VERSION == 2 && GDCM_MINOR_VERSION <= 12
1924  // This will not work in the vast majority of cases but to get at least something working in GDCM 2.0.12
1925  de.SetByteValue( value.c_str(), value.size() );
1926 #else
1927  std::string si = sf.FromString( tag, value.c_str(), value.size() );
1928  de.SetByteValue( si.c_str(), si.size() );
1929 #endif
1930  header.Insert( de ); //value, tag.GetGroup(), tag.GetElement());
1931  }
1932  }
1933  }
1934  else
1935  {
1936  // This is not a DICOM entry, then check if it is one of the
1937  // ITK standard ones
1938  if( key == ITK_NumberOfDimensions )
1939  {
1940  unsigned int numberOfDimensions = 0;
1941  ExposeMetaData<unsigned int>(dict, key, numberOfDimensions);
1942  m_GlobalNumberOfDimensions = numberOfDimensions;
1943  m_Origin.resize( m_GlobalNumberOfDimensions );
1944  m_Spacing.resize( m_GlobalNumberOfDimensions );
1945  }
1946  else if( key == ITK_Origin )
1947  {
1948  typedef Array< double > DoubleArrayType;
1949  DoubleArrayType originArray;
1950  ExposeMetaData< DoubleArrayType >( dict, key, originArray );
1951  m_Origin[0] = originArray[0];
1952  m_Origin[1] = originArray[1];
1953  m_Origin[2] = originArray[2];
1954  }
1955  else if( key == ITK_Spacing )
1956  {
1957  typedef Array< double > DoubleArrayType;
1958  DoubleArrayType spacingArray;
1959  ExposeMetaData< DoubleArrayType >( dict, key, spacingArray );
1960  m_Spacing[0] = spacingArray[0];
1961  m_Spacing[1] = spacingArray[1];
1962  m_Spacing[2] = spacingArray[2];
1963  }
1964  else
1965  {
1966  itkDebugMacro(
1967  << "GDCMImageIO: non-DICOM and non-ITK standard key = " << key );
1968  }
1969  }
1970 
1971  ++itr;
1972  }
1973  //std::cout << header << std::endl;
1974 
1975  //this->SetNumberOfDimensions(3);
1976  //gdcm::Image &image = writer.GetImage();
1977  gdcm::SmartPointer<gdcm::Image> simage = new gdcm::Image;
1978  gdcm::Image &image = *simage;
1979  image.SetNumberOfDimensions( 2 ); // good default
1980  image.SetDimension(0, m_Dimensions[0] );
1981  image.SetDimension(1, m_Dimensions[1] );
1982  //image.SetDimension(2, m_Dimensions[2] );
1983  image.SetSpacing(0, m_Spacing[0] );
1984  image.SetSpacing(1, m_Spacing[1] );
1985  image.SetSpacing(2, m_Spacing[2] );
1986  image.SetOrigin(0, m_Origin[0] );
1987  image.SetOrigin(1, m_Origin[1] );
1988  image.SetOrigin(2, m_Origin[2] );
1989  if( m_NumberOfDimensions > 2 && m_Dimensions[2] != 1 )
1990  {
1991  // resize num of dim to 3:
1992  image.SetNumberOfDimensions( 3 );
1993  image.SetDimension(2, m_Dimensions[2] );
1994  }
1995 
1996  // Do the direction now:
1997  image.SetDirectionCosines(0,m_Direction[0][0]);
1998  image.SetDirectionCosines(1,m_Direction[0][1]);
1999  image.SetDirectionCosines(2,m_Direction[0][2]);
2000  image.SetDirectionCosines(3,m_Direction[1][0]);
2001  image.SetDirectionCosines(4,m_Direction[1][1]);
2002  image.SetDirectionCosines(5,m_Direction[1][2]);
2003 
2004  // reset any previous value:
2005  m_RescaleSlope = 1.0;
2006  m_RescaleIntercept = 0.0;
2007 
2008  // Get user defined rescale slope/intercept
2009  std::string rescaleintercept;
2010  ExposeMetaData<std::string>(dict, "0028|1052" , rescaleintercept);
2011  std::string rescaleslope;
2012  ExposeMetaData<std::string>(dict, "0028|1053" , rescaleslope);
2013  if( rescaleintercept != "" && rescaleslope != "" )
2014  {
2015  itksys_ios::stringstream sstr1;
2016  sstr1 << rescaleintercept;
2017  if( ! (sstr1 >> m_RescaleIntercept) )
2018  {
2019  itkExceptionMacro( "Problem reading RescaleIntercept: " << rescaleintercept );
2020  }
2021  itksys_ios::stringstream sstr2;
2022  sstr2 << rescaleslope;
2023  if( !(sstr2 >> m_RescaleSlope) )
2024  {
2025  itkExceptionMacro( "Problem reading RescaleSlope: " << rescaleslope );
2026  }
2027  // header->InsertValEntry( "US", 0x0028, 0x1054 ); // Rescale Type
2028  }
2029  else if( rescaleintercept != "" || rescaleslope != "" ) // xor
2030  {
2031  itkExceptionMacro( "Both RescaleSlope & RescaleIntercept need to be present" );
2032  }
2033 
2034  // Handle the bitDepth:
2035  std::string bitsAllocated;
2036  std::string bitsStored;
2037  std::string highBit;
2038  std::string pixelRep;
2039  // Get user defined bit representation:
2040  ExposeMetaData<std::string>(dict, "0028|0100", bitsAllocated);
2041  ExposeMetaData<std::string>(dict, "0028|0101", bitsStored);
2042  ExposeMetaData<std::string>(dict, "0028|0102", highBit);
2043  ExposeMetaData<std::string>(dict, "0028|0103", pixelRep);
2044 
2045  gdcm::PixelFormat pixeltype = gdcm::PixelFormat::UNKNOWN;
2046  switch (this->GetComponentType())
2047  {
2048  case ImageIOBase::CHAR:
2049  pixeltype = gdcm::PixelFormat::INT8;
2050  break;
2051  case ImageIOBase::UCHAR:
2052  pixeltype = gdcm::PixelFormat::UINT8;
2053  break;
2054  case ImageIOBase::SHORT:
2055  pixeltype = gdcm::PixelFormat::INT16;
2056  break;
2057  case ImageIOBase::USHORT:
2058  pixeltype = gdcm::PixelFormat::UINT16;
2059  break;
2060  case ImageIOBase::INT:
2061  pixeltype = gdcm::PixelFormat::INT32;
2062  break;
2063  case ImageIOBase::UINT:
2064  pixeltype = gdcm::PixelFormat::UINT32;
2065  break;
2066  //Disabling FLOAT and DOUBLE for now...
2067  case ImageIOBase::FLOAT:
2068  pixeltype = gdcm::PixelFormat::FLOAT32;
2069  break;
2070  case ImageIOBase::DOUBLE:
2071  pixeltype = gdcm::PixelFormat::FLOAT64;
2072  break;
2073  default:
2074  itkExceptionMacro(<<"DICOM does not support this component type");
2075  }
2076  assert( pixeltype != gdcm::PixelFormat::UNKNOWN );
2077  gdcm::PhotometricInterpretation pi;
2078  if( this->GetNumberOfComponents() == 1 )
2079  {
2080  pi = gdcm::PhotometricInterpretation::MONOCHROME2;
2081  }
2082  else if( this->GetNumberOfComponents() == 3 )
2083  {
2085  // (0028,0006) US 0 # 2, 1 PlanarConfiguration
2086  }
2087  else
2088  {
2089  itkExceptionMacro(<<"DICOM does not support this component type");
2090  }
2091  pixeltype.SetSamplesPerPixel( this->GetNumberOfComponents() );
2092 
2093  // Compute the outpixeltype
2094  gdcm::PixelFormat outpixeltype = gdcm::PixelFormat::UNKNOWN;
2095  if( pixeltype == gdcm::PixelFormat::FLOAT32 || pixeltype == gdcm::PixelFormat::FLOAT64 )
2096  {
2097  if( bitsAllocated != "" && bitsStored != "" && highBit != "" && pixelRep != "" )
2098  {
2099  outpixeltype.SetBitsAllocated( atoi(bitsAllocated.c_str()) );
2100  outpixeltype.SetBitsStored( atoi(bitsStored.c_str()) );
2101  outpixeltype.SetHighBit( atoi(highBit.c_str()) );
2102  outpixeltype.SetPixelRepresentation( atoi(pixelRep.c_str()) );
2103  if( this->GetNumberOfComponents() != 1 )
2104  {
2105  itkExceptionMacro(<<"Sorry Dave I can't do that" );
2106  }
2107  assert( outpixeltype != gdcm::PixelFormat::UNKNOWN );
2108  }
2109  else
2110  {
2111  itkExceptionMacro(<<"A Floating point buffer was passed but the stored pixel type was not specified."
2112  "This is currently not supported" );
2113  }
2114  }
2115 
2116  image.SetPhotometricInterpretation( pi );
2117  if( outpixeltype != gdcm::PixelFormat::UNKNOWN )
2118  image.SetPixelFormat( outpixeltype );
2119  else
2120  image.SetPixelFormat( pixeltype );
2121  unsigned long len = image.GetBufferLength();
2122 
2123  size_t numberOfBytes = this->GetImageSizeInBytes();
2124 
2125  gdcm::DataElement pixeldata( gdcm::Tag(0x7fe0,0x0010) );
2126  // Handle rescaler here:
2127  // Whenever shift / scale is needed... do it !
2128  if( m_RescaleIntercept != 0 || m_RescaleSlope != 1 )
2129  {
2130  // rescale from float to unsigned short
2131  gdcm::Rescaler ir;
2132  ir.SetIntercept( m_RescaleIntercept );
2133  ir.SetSlope( m_RescaleSlope );
2134  ir.SetPixelFormat( pixeltype );
2135  ir.SetMinMaxForPixelType( outpixeltype.GetMin(), outpixeltype.GetMax() );
2136  image.SetIntercept( m_RescaleIntercept );
2137  image.SetSlope( m_RescaleSlope );
2138  char* copy = new char[len];
2139  ir.InverseRescale(copy,(char*)buffer,numberOfBytes );
2140  pixeldata.SetByteValue( copy, len);
2141  delete[] copy;
2142  }
2143  else
2144  {
2145  assert( len == numberOfBytes );
2146  // only do a straight copy:
2147  pixeldata.SetByteValue( (char*)buffer, numberOfBytes );
2148  }
2149  image.SetDataElement( pixeldata );
2150 
2151 
2152  // Handle compression here:
2153  // If user ask to use compression:
2154  if( m_UseCompression )
2155  {
2156  gdcm::ImageChangeTransferSyntax change;
2157  if( m_CompressionType == JPEG )
2158  {
2159  change.SetTransferSyntax( gdcm::TransferSyntax::JPEGLosslessProcess14_1 );
2160  }
2161  else if ( m_CompressionType == JPEG2000 )
2162  {
2163  change.SetTransferSyntax( gdcm::TransferSyntax::JPEG2000Lossless );
2164  }
2165  else
2166  {
2167  itkExceptionMacro(<< "Unknown compression type" );
2168  }
2169  change.SetInput( image );
2170  bool b = change.Change();
2171  if( !b )
2172  {
2173  itkExceptionMacro(<< "Could not change the Transfer Syntax for Compression" );
2174  }
2175  writer.SetImage( change.GetOutput() );
2176  }
2177  else
2178  {
2179  writer.SetImage( image );
2180  }
2181 
2182  if( !m_KeepOriginalUID )
2183  {
2184  // UID generation part:
2185  // We only create *ONE* Study/Series.Frame of Reference Instance UID
2186  if( m_StudyInstanceUID.empty() )
2187  {
2188  // As long as user maintain there gdcmIO they will keep the same
2189  // Study/Series instance UID.
2190  gdcm::UIDGenerator uid;
2191  m_StudyInstanceUID = uid.Generate();
2192  m_SeriesInstanceUID = uid.Generate();
2193  m_FrameOfReferenceInstanceUID = uid.Generate();
2194  }
2195  //std::string uid = uid.Generate();
2196  const char *studyuid = m_StudyInstanceUID.c_str();
2197  {
2198  gdcm::DataElement de( gdcm::Tag(0x0020,0x000d) ); // Study
2199  de.SetByteValue( studyuid, strlen(studyuid) );
2200  de.SetVR( gdcm::Attribute<0x0020, 0x000d>::GetVR() );
2201  header.Insert( de );
2202  }
2203  const char *seriesuid = m_SeriesInstanceUID.c_str();
2204  {
2205  gdcm::DataElement de( gdcm::Tag(0x0020,0x000e) ); // Series
2206  de.SetByteValue( seriesuid, strlen(seriesuid) );
2207  de.SetVR( gdcm::Attribute<0x0020, 0x000e>::GetVR() );
2208  header.Insert( de );
2209  }
2210  }
2211 
2212  if( image.GetTransferSyntax() != gdcm::TransferSyntax::ImplicitVRLittleEndian )
2213  {
2214  gdcm::FileExplicitFilter fef;
2215  //fef.SetChangePrivateTags( true );
2216  fef.SetFile( writer.GetFile() );
2217  if(!fef.Change())
2218  {
2219  itkExceptionMacro(<<"Failed to change to Explicit Transfer Syntax");
2220  }
2221  }
2222 
2223  const char *filename = m_FileName.c_str();
2224  writer.SetFileName( filename );
2225  if( !writer.Write() )
2226  {
2227  itkExceptionMacro(<<"DICOM does not support this component type");
2228  }
2229 }
2230 
2231 #endif
2232 
2233 // Convenience methods to query patient and scanner information. These
2234 // methods are here for compatibility with the DICOMImageIO2 class.
2236 {
2237  MetaDataDictionary & dict = this->GetMetaDataDictionary();
2238  ExposeMetaData<std::string>(dict, "0010|0010", m_PatientName);
2239  strcpy (name, m_PatientName.c_str());
2240 }
2241 
2242 void GDCMImageIO::GetPatientID( char *name)
2243 {
2244  MetaDataDictionary & dict = this->GetMetaDataDictionary();
2245  ExposeMetaData<std::string>(dict, "0010|0020", m_PatientID);
2246  strcpy (name, m_PatientID.c_str());
2247 }
2248 
2250 {
2251  MetaDataDictionary & dict = this->GetMetaDataDictionary();
2252  ExposeMetaData<std::string>(dict, "0010|0040", m_PatientSex);
2253  strcpy (name, m_PatientSex.c_str());
2254 }
2255 
2257 {
2258  MetaDataDictionary & dict = this->GetMetaDataDictionary();
2259  ExposeMetaData<std::string>(dict, "0010|1010", m_PatientAge);
2260  strcpy (name, m_PatientAge.c_str());
2261 }
2262 
2263 void GDCMImageIO::GetStudyID( char *name)
2264 {
2265  MetaDataDictionary & dict = this->GetMetaDataDictionary();
2266  ExposeMetaData<std::string>(dict, "0020|0010", m_StudyID);
2267  strcpy (name, m_StudyID.c_str());
2268 }
2269 
2271 {
2272  MetaDataDictionary & dict = this->GetMetaDataDictionary();
2273  ExposeMetaData<std::string>(dict, "0010|0030", m_PatientDOB);
2274  strcpy (name, m_PatientDOB.c_str());
2275 }
2276 
2278 {
2279  MetaDataDictionary & dict = this->GetMetaDataDictionary();
2280  ExposeMetaData<std::string>(dict, "0008|1030", m_StudyDescription);
2281  strcpy (name, m_StudyDescription.c_str());
2282 }
2283 
2284 void GDCMImageIO::GetBodyPart( char *name)
2285 {
2286  MetaDataDictionary & dict = this->GetMetaDataDictionary();
2287  ExposeMetaData<std::string>(dict, "0018|0015", m_BodyPart);
2288  strcpy (name, m_BodyPart.c_str());
2289 }
2290 
2292 {
2293  MetaDataDictionary & dict = this->GetMetaDataDictionary();
2294  ExposeMetaData<std::string>(dict, "0020|1000", m_NumberOfSeriesInStudy);
2295  strcpy (name, m_NumberOfSeriesInStudy.c_str());
2296 }
2297 
2299 {
2300  MetaDataDictionary & dict = this->GetMetaDataDictionary();
2301  ExposeMetaData<std::string>(dict, "0020|1206", m_NumberOfStudyRelatedSeries);
2302  strcpy (name, m_NumberOfStudyRelatedSeries.c_str());
2303 }
2304 
2305 void GDCMImageIO::GetStudyDate( char *name)
2306 {
2307  MetaDataDictionary & dict = this->GetMetaDataDictionary();
2308  ExposeMetaData<std::string>(dict, "0008|0020", m_StudyDate);
2309  strcpy (name, m_StudyDate.c_str());
2310 }
2311 
2312 void GDCMImageIO::GetModality( char *name)
2313 {
2314  MetaDataDictionary & dict = this->GetMetaDataDictionary();
2315  ExposeMetaData<std::string>(dict, "0008|0060", m_Modality);
2316  strcpy (name, m_Modality.c_str());
2317 }
2318 
2320 {
2321  MetaDataDictionary & dict = this->GetMetaDataDictionary();
2322  ExposeMetaData<std::string>(dict, "0008|0070", m_Manufacturer);
2323  strcpy (name, m_Manufacturer.c_str());
2324 }
2325 
2327 {
2328  MetaDataDictionary & dict = this->GetMetaDataDictionary();
2329  ExposeMetaData<std::string>(dict, "0008|0080", m_Institution);
2330  strcpy (name, m_Institution.c_str());
2331 }
2332 
2333 void GDCMImageIO::GetModel( char *name)
2334 {
2335  MetaDataDictionary & dict = this->GetMetaDataDictionary();
2336  ExposeMetaData<std::string>(dict, "0008|1090", m_Model);
2337  strcpy (name, m_Model.c_str());
2338 }
2339 
2341 {
2342  MetaDataDictionary & dict = this->GetMetaDataDictionary();
2343  ExposeMetaData<std::string>(dict, "0018|0022", m_ScanOptions);
2344  strcpy (name, m_ScanOptions.c_str());
2345 }
2346 
2347 bool GDCMImageIO::GetValueFromTag(const std::string & tag, std::string & value)
2348 {
2349  MetaDataDictionary & dict = this->GetMetaDataDictionary();
2350  return ExposeMetaData<std::string>(dict, tag, value);
2351 }
2352 
2353 #if GDCM_MAJOR_VERSION < 2
2354 bool GDCMImageIO::GetLabelFromTag( const std::string & tagkey,
2355  std::string & labelId )
2356 {
2357  gdcm::Dict *pubDict = gdcm::Global::GetDicts()->GetDefaultPubDict();
2358 
2359  gdcm::DictEntry *dictentry = pubDict->GetEntry( tagkey );
2360 
2361  bool found;
2362 
2363  // If tagkey was found (ie DICOM tag from public dictionary),
2364  // then return the name:
2365  if( dictentry )
2366  {
2367  labelId = dictentry->GetName();
2368  found = true;
2369  }
2370  else
2371  {
2372  labelId = "Unknown";
2373  found = false;
2374  }
2375  return found;
2376 }
2377 #else
2378 bool GDCMImageIO::GetLabelFromTag( const std::string & tag,
2379  std::string & labelId )
2380 {
2381  gdcm::Tag t;
2382  if( t.ReadFromPipeSeparatedString( tag.c_str() ) && t.IsPublic() )
2383  {
2384  const gdcm::Global &g = gdcm::Global::GetInstance();
2385  const gdcm::Dicts &dicts = g.GetDicts();
2386  const gdcm::DictEntry &entry = dicts.GetDictEntry(t);
2387  labelId = entry.GetName();
2388  return true;
2389  }
2390  return false;
2391 }
2392 #endif
2393 
2394 
2395 void GDCMImageIO::PrintSelf(std::ostream& os, Indent indent) const
2396 {
2397  Superclass::PrintSelf(os, indent);
2398  os << indent << "Internal Component Type: " << this->GetComponentTypeAsString(m_InternalComponentType)
2399  << std::endl;
2400  os << indent << "RescaleSlope: " << m_RescaleSlope << std::endl;
2401  os << indent << "RescaleIntercept: " << m_RescaleIntercept << std::endl;
2402  os << indent << "MaxSizeLoadEntry: " << m_MaxSizeLoadEntry << std::endl;
2403  os << indent << "KeepOriginalUID:" << (m_KeepOriginalUID ? "On" : "Off") << std::endl;
2404  os << indent << "UIDPrefix: " << m_UIDPrefix << std::endl;
2405  os << indent << "StudyInstanceUID: " << m_StudyInstanceUID << std::endl;
2406  os << indent << "SeriesInstanceUID: " << m_SeriesInstanceUID << std::endl;
2407  os << indent << "FrameOfReferenceInstanceUID: " << m_FrameOfReferenceInstanceUID << std::endl;
2408  os << indent << "LoadSequences:" << m_LoadSequences << std::endl;
2409  os << indent << "LoadPrivateTags:" << m_LoadPrivateTags << std::endl;
2410  os << indent << "CompressionType:" << m_CompressionType << std::endl;
2411 
2412  os << indent << "Patient Name:" << m_PatientName << std::endl;
2413  os << indent << "Patient ID:" << m_PatientID << std::endl;
2414  os << indent << "Patient Sex:" << m_PatientSex << std::endl;
2415  os << indent << "Patient Age:" << m_PatientAge << std::endl;
2416  os << indent << "Study ID:" << m_StudyID << std::endl;
2417  os << indent << "Patient DOB:" << m_PatientDOB << std::endl;
2418  os << indent << "Study Description:" << m_StudyDescription << std::endl;
2419  os << indent << "Body Part:" << m_BodyPart << std::endl;
2420  os << indent << "Number Of Series In Study:" << m_NumberOfSeriesInStudy << std::endl;
2421  os << indent << "Number Of Study Related Series:" << m_NumberOfStudyRelatedSeries << std::endl;
2422  os << indent << "Study Date:" << m_StudyDate << std::endl;
2423  os << indent << "Modality:" << m_Modality << std::endl;
2424  os << indent << "Manufacturer:" << m_Manufacturer << std::endl;
2425  os << indent << "Institution Name:" << m_Institution << std::endl;
2426  os << indent << "Model:" << m_Model << std::endl;
2427  os << indent << "Scan Options:" << m_ScanOptions << std::endl;
2428 }
2429 
2430 
2431 } // end namespace itk

Generated at Sat Feb 2 2013 23:38:57 for Orfeo Toolbox with doxygen 1.8.1.1