1 /*=========================================================================
2  *
3  *  Copyright Insight Software Consortium
4  *
5  *  Licensed under the Apache License, Version 2.0 (the "License");
6  *  you may not use this file except in compliance with the License.
7  *  You may obtain a copy of the License at
8  *
9  *         http://www.apache.org/licenses/LICENSE-2.0.txt
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  *
17  *=========================================================================*/
18 /*=========================================================================
19  *
20  *  Portions of this file are subject to the VTK Toolkit Version 3 copyright.
21  *
22  *  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
23  *
24  *  For complete copyright, license and disclaimer of warranty information
25  *  please refer to the NOTICE file at the top of the ITK source tree.
26  *
27  *=========================================================================*/
28 
29 #include "itkVersion.h"
30 #include "itkGDCMImageIO.h"
31 #include "itkIOCommon.h"
32 #include "itkArray.h"
33 #include "itkByteSwapper.h"
34 #include "vnl/vnl_cross.h"
35 
36 #include "itkMetaDataObject.h"
37 
38 #include "itksys/SystemTools.hxx"
39 #include "itksys/Base64.h"
40 
41 #include "gdcmImageHelper.h"
42 #include "gdcmFileExplicitFilter.h"
43 #include "gdcmImageChangeTransferSyntax.h"
44 #include "gdcmDataSetHelper.h"
45 #include "gdcmStringFilter.h"
46 #include "gdcmImageApplyLookupTable.h"
47 #include "gdcmImageChangePlanarConfiguration.h"
48 #include "gdcmRescaler.h"
49 #include "gdcmImageReader.h"
50 #include "gdcmImageWriter.h"
51 #include "gdcmUIDGenerator.h"
52 #include "gdcmAttribute.h"
53 #include "gdcmGlobal.h"
54 #include "gdcmMediaStorage.h"
55 
56 #include <fstream>
57 #include <sstream>
58 
59 namespace itk {
60 
61 
62 class InternalHeader
63 {
64 public:
InternalHeader()65   InternalHeader() {}
~InternalHeader()66   ~InternalHeader()
67   {
68     delete m_Header;
69   }
70   gdcm::File *m_Header{nullptr};
71 };
72 
GDCMImageIO()73 GDCMImageIO::GDCMImageIO()
74 {
75   this->m_DICOMHeader = new InternalHeader;
76   this->SetNumberOfDimensions(3); //needed for getting the 3 coordinates of
77                                   // the origin, even if it is a 2D slice.
78   m_ByteOrder = LittleEndian;     //default
79   m_FileType = Binary;            //default...always true
80   m_RescaleSlope = 1.0;
81   m_RescaleIntercept = 0.0;
82   // UIDPrefix is the ITK root id tacked with a ".1"
83   // allowing to designate a subspace of the id space for ITK generated DICOM
84   m_UIDPrefix = "1.2.826.0.1.3680043.2.1125." "1";
85 
86   // Purely internal use, no user access:
87   m_StudyInstanceUID = "";
88   m_SeriesInstanceUID = "";
89   m_FrameOfReferenceInstanceUID = "";
90 
91   m_KeepOriginalUID = false;
92 
93   m_LoadPrivateTags = false;
94 
95   m_InternalComponentType = UNKNOWNCOMPONENTTYPE;
96 
97   // by default assume that images will be 2D.
98   // This number is updated according the information
99   // received through the MetaDataDictionary
100   m_GlobalNumberOfDimensions = 2;
101   // By default use JPEG2000. For legacy system, one should prefer JPEG since
102   // JPEG2000 was only recently added to the DICOM standard
103   this->Self::SetCompressor("");
104 
105   const char *extensions[] =
106     {
107       ".dcm",".DCM", ".dicom", ".DICOM"
108     };
109 
110   for(auto ext : extensions)
111     {
112     this->AddSupportedWriteExtension(ext);
113     this->AddSupportedReadExtension(ext);
114     }
115 
116 }
117 
~GDCMImageIO()118 GDCMImageIO::~GDCMImageIO()
119 {
120   delete this->m_DICOMHeader;
121 }
122 
123 /**
124  * Helper function to test for some dicom like formatting.
125  * @param file A stream to test if the file is dicom like
126  * @return true if the structure of the file is dicom like
127  */
128 static bool
readNoPreambleDicom(std::ifstream & file)129 readNoPreambleDicom( std::ifstream & file )  // NOTE: This file is duplicated in itkDCMTKImageIO.cxx
130 {
131   // Adapted from https://stackoverflow.com/questions/2381983/c-how-to-read-parts-of-a-file-dicom
132   /* This heuristic tries to determine if the file follows the basic structure of a dicom file organization.
133    * Any file that begins with the a byte sequence
134    * where groupNo matches below will be then read several SOP Instance sections.
135    */
136 
137   unsigned short groupNo = 0xFFFF;
138   unsigned short tagElementNo = 0xFFFF;
139   do {
140     file.read(reinterpret_cast<char *>(&groupNo),sizeof(unsigned short));
141     ByteSwapper<unsigned short>::SwapFromSystemToLittleEndian(&groupNo);
142     file.read(reinterpret_cast<char *>(&tagElementNo),sizeof(unsigned short));
143     ByteSwapper<unsigned short>::SwapFromSystemToLittleEndian(&tagElementNo);
144 
145     if(groupNo != 0x0002 && groupNo != 0x0008) // Only groupNo 2 & 8 are supported without preambles
146     {
147       return false;
148     }
149 
150     char vrcode[3] = { '\0', '\0', '\0' };
151     file.read(vrcode, 2);
152 
153     long length=std::numeric_limits<long>::max();
154     const std::string vr{vrcode};
155     if ( vr == "AE" || vr == "AS" || vr == "AT"
156          || vr == "CS" || vr == "DA" || vr == "DS"
157          || vr == "DT" || vr == "FL" || vr == "FD"
158          || vr == "IS" || vr == "LO" || vr == "PN"
159          || vr == "SH" || vr == "SL" || vr == "SS"
160          || vr == "ST" || vr == "TM" || vr == "UI"
161          || vr == "UL" || vr == "US"
162         )
163     {
164       unsigned short uslength=0;
165       file.read(reinterpret_cast<char *>(&uslength),sizeof(unsigned short));
166       ByteSwapper<unsigned short>::SwapFromSystemToLittleEndian(&uslength);
167       length = uslength;
168     }
169     else {
170       //Read the reserved byte
171       unsigned short uslength=0;
172       file.read(reinterpret_cast<char *>(&uslength),sizeof(unsigned short));
173       ByteSwapper<unsigned short>::SwapFromSystemToLittleEndian(&uslength);
174 
175       unsigned int uilength=0;
176       file.read(reinterpret_cast<char *>(&uilength),sizeof(unsigned int));
177       ByteSwapper<unsigned int>::SwapFromSystemToLittleEndian(&uilength);
178 
179       length = uilength;
180     }
181     if(length <= 0 )
182     {
183       return false;
184     }
185     file.ignore(length);
186     if(file.eof())
187     {
188       return false;
189     }
190   } while (groupNo == 2);
191 
192 #if defined( NDEBUG )
193    std::ostringstream itkmsg;
194    itkmsg << "No DICOM magic number found, but the file appears to be DICOM without a preamble.\n"
195           << "Proceeding without caution.";
196       ::itk::OutputWindowDisplayDebugText( itkmsg.str().c_str() );
197 #endif
198   return true;
199 }
200 
201   // This method will only test if the header looks like a
202 // GDCM image file.
CanReadFile(const char * filename)203 bool GDCMImageIO::CanReadFile(const char *filename)
204 {
205   std::ifstream file;
206   try
207     {
208     this->OpenFileForReading( file, filename );
209     }
210   catch( ExceptionObject & )
211     {
212     return false;
213     }
214 
215   //
216   // sniff for the DICM signature first at 128
217   // then at zero, and if either place has it then
218   // ask GDCM to try reading it.
219   //
220   // There isn't a definitive way to check for DICOM files;
221   // This was actually cribbed from DICOMParser in VTK
222   bool dicomsig(false);
223   for(long int off = 128; off >= 0; off -= 128)
224     {
225     file.seekg(off,std::ios_base::beg);
226     if(file.fail() || file.eof())
227       {
228       return false;
229       }
230     char buf[5];
231     file.read(buf,4);
232     if(file.fail())
233       {
234       return false;
235       }
236     buf[4] = '\0';
237     const std::string sig{buf};
238     if(sig == "DICM")
239       {
240       dicomsig = true;
241       }
242     }
243 //Do not allow CanRead to return true for non-compliant DICOM files
244 #define GDCMPARSER_IGNORE_MAGIC_NUMBER
245 #if defined(GDCMPARSER_IGNORE_MAGIC_NUMBER) && !defined(__EMSCRIPTEN__)
246   //
247   // Try it anyways...
248   //
249   if(!dicomsig)
250     {
251     file.seekg(0,std::ios_base::beg);
252     dicomsig = readNoPreambleDicom( file );
253     }
254 #endif // GDCMPARSER_IGNORE_MAGIC_NUMBER
255   if(dicomsig)
256     {
257     // Check to see if its a valid dicom file gdcm is able to parse:
258     // We are parsing the header one time here:
259     gdcm::ImageReader reader;
260     reader.SetFileName(filename);
261     if ( reader.Read() )
262       {
263       return true;
264       }
265     }
266   return false;
267 }
268 
Read(void * pointer)269 void GDCMImageIO::Read(void *pointer)
270 {
271   // ensure file can be opened for reading, before doing any more work
272   std::ifstream inputFileStream;
273   // let any exceptions propagate
274   this->OpenFileForReading( inputFileStream, m_FileName );
275   inputFileStream.close();
276 
277   itkAssertInDebugAndIgnoreInReleaseMacro( gdcm::ImageHelper::GetForceRescaleInterceptSlope() );
278   gdcm::ImageReader reader;
279   reader.SetFileName( m_FileName.c_str() );
280   if ( !reader.Read() )
281     {
282     itkExceptionMacro(<< "Cannot read requested file");
283     }
284 
285   gdcm::Image & image = reader.GetImage();
286 #ifndef NDEBUG
287   gdcm::PixelFormat pixeltype_debug = image.GetPixelFormat();
288   itkAssertInDebugAndIgnoreInReleaseMacro(image.GetNumberOfDimensions() == 2 || image.GetNumberOfDimensions() == 3);
289 #endif
290   SizeValueType len = image.GetBufferLength();
291 
292   // I think ITK only allow RGB image by pixel (and not by plane)
293   if ( image.GetPlanarConfiguration() == 1 )
294     {
295     gdcm::ImageChangePlanarConfiguration icpc;
296     icpc.SetInput(image);
297     icpc.SetPlanarConfiguration(0);
298     icpc.Change();
299     image = icpc.GetOutput();
300     }
301 
302   gdcm::PhotometricInterpretation pi = image.GetPhotometricInterpretation();
303   if ( pi == gdcm::PhotometricInterpretation::PALETTE_COLOR )
304     {
305     gdcm::ImageApplyLookupTable ialut;
306     ialut.SetInput(image);
307     ialut.Apply();
308     image = ialut.GetOutput();
309     len *= 3;
310     }
311 
312   if ( !image.GetBuffer( (char*)pointer ) )
313     {
314     itkExceptionMacro(<< "Failed to get the buffer!");
315     }
316 
317   const gdcm::PixelFormat & pixeltype = image.GetPixelFormat();
318 #ifndef NDEBUG
319   // ImageApplyLookupTable is meant to change the pixel type for PALETTE_COLOR images
320   // (from single values to triple values per pixel)
321   if ( pi != gdcm::PhotometricInterpretation::PALETTE_COLOR )
322     {
323     itkAssertInDebugAndIgnoreInReleaseMacro( pixeltype_debug == pixeltype );
324     }
325 #endif
326 
327   if ( m_RescaleSlope != 1.0 || m_RescaleIntercept != 0.0 )
328     {
329     gdcm::Rescaler r;
330     r.SetIntercept(m_RescaleIntercept);
331     r.SetSlope(m_RescaleSlope);
332     r.SetPixelFormat(pixeltype);
333     gdcm::PixelFormat outputpt = r.ComputeInterceptSlopePixelType();
334     auto * copy = new char[len];
335     memcpy(copy, (char *)pointer, len);
336     r.Rescale( (char *)pointer, copy, len );
337     delete[] copy;
338     // WARNING: sizeof(Real World Value) != sizeof(Stored Pixel)
339     len = len * outputpt.GetPixelSize() / pixeltype.GetPixelSize();
340     }
341 
342 #ifndef NDEBUG
343   // \postcondition
344   // Now that len was updated (after unpacker 12bits -> 16bits, rescale...) ,
345   // can now check compat:
346   const SizeValueType numberOfBytesToBeRead =
347     static_cast< SizeValueType >( this->GetImageSizeInBytes() );
348   itkAssertInDebugAndIgnoreInReleaseMacro(numberOfBytesToBeRead == len);   // programmer error
349 #endif
350 }
351 
352 
InternalReadImageInformation()353 void GDCMImageIO::InternalReadImageInformation()
354 {
355   // ensure file can be opened for reading, before doing any more work
356   std::ifstream inputFileStream;
357   // let any exceptions propagate
358   this->OpenFileForReading( inputFileStream, m_FileName );
359   inputFileStream.close();
360 
361   // In general this should be relatively safe to assume
362   gdcm::ImageHelper::SetForceRescaleInterceptSlope(true);
363 
364   gdcm::ImageReader reader;
365   reader.SetFileName( m_FileName.c_str() );
366   if ( !reader.Read() )
367     {
368     itkExceptionMacro(<< "Cannot read requested file");
369     }
370   const gdcm::Image &   image = reader.GetImage();
371   const gdcm::File &    f = reader.GetFile();
372   const gdcm::DataSet & ds = f.GetDataSet();
373   const unsigned int *  dims = image.GetDimensions();
374 
375   const gdcm::PixelFormat & pixeltype = image.GetPixelFormat();
376   switch ( pixeltype )
377     {
378     case gdcm::PixelFormat::INT8:
379       m_InternalComponentType = ImageIOBase::CHAR; // Is it signed char ?
380       break;
381     case gdcm::PixelFormat::UINT8:
382       m_InternalComponentType = ImageIOBase::UCHAR;
383       break;
384     /* INT12 / UINT12 should not happen anymore in any modern DICOM */
385     case gdcm::PixelFormat::INT12:
386       m_InternalComponentType = ImageIOBase::SHORT;
387       break;
388     case gdcm::PixelFormat::UINT12:
389       m_InternalComponentType = ImageIOBase::USHORT;
390       break;
391     case gdcm::PixelFormat::INT16:
392       m_InternalComponentType = ImageIOBase::SHORT;
393       break;
394     case gdcm::PixelFormat::UINT16:
395       m_InternalComponentType = ImageIOBase::USHORT;
396       break;
397     // RT / SC have 32bits
398     case gdcm::PixelFormat::INT32:
399       m_InternalComponentType = ImageIOBase::INT;
400       break;
401     case gdcm::PixelFormat::UINT32:
402       m_InternalComponentType = ImageIOBase::UINT;
403       break;
404     //case gdcm::PixelFormat::FLOAT16: // TODO
405     case gdcm::PixelFormat::FLOAT32:
406       m_InternalComponentType = ImageIOBase::FLOAT;
407       break;
408     case gdcm::PixelFormat::FLOAT64:
409       m_InternalComponentType = ImageIOBase::DOUBLE;
410       break;
411     default:
412       itkExceptionMacro("Unhandled PixelFormat: " << pixeltype);
413     }
414 
415   m_RescaleIntercept = image.GetIntercept();
416   m_RescaleSlope = image.GetSlope();
417   gdcm::Rescaler r;
418   r.SetIntercept(m_RescaleIntercept);
419   r.SetSlope(m_RescaleSlope);
420   r.SetPixelFormat(pixeltype);
421   gdcm::PixelFormat::ScalarType outputpt = r.ComputeInterceptSlopePixelType();
422 
423   itkAssertInDebugAndIgnoreInReleaseMacro(pixeltype <= outputpt);
424 
425   m_ComponentType = UNKNOWNCOMPONENTTYPE;
426   switch ( outputpt )
427     {
428     case gdcm::PixelFormat::INT8:
429       m_ComponentType = ImageIOBase::CHAR; // Is it signed char ?
430       break;
431     case gdcm::PixelFormat::UINT8:
432       m_ComponentType = ImageIOBase::UCHAR;
433       break;
434     /* INT12 / UINT12 should not happen anymore in any modern DICOM */
435     case gdcm::PixelFormat::INT12:
436       m_ComponentType = ImageIOBase::SHORT;
437       break;
438     case gdcm::PixelFormat::UINT12:
439       m_ComponentType = ImageIOBase::USHORT;
440       break;
441     case gdcm::PixelFormat::INT16:
442       m_ComponentType = ImageIOBase::SHORT;
443       break;
444     case gdcm::PixelFormat::UINT16:
445       m_ComponentType = ImageIOBase::USHORT;
446       break;
447     // RT / SC have 32bits
448     case gdcm::PixelFormat::INT32:
449       m_ComponentType = ImageIOBase::INT;
450       break;
451     case gdcm::PixelFormat::UINT32:
452       m_ComponentType = ImageIOBase::UINT;
453       break;
454     //case gdcm::PixelFormat::FLOAT16: // TODO
455     case gdcm::PixelFormat::FLOAT32:
456       m_ComponentType = ImageIOBase::FLOAT;
457       break;
458     case gdcm::PixelFormat::FLOAT64:
459       m_ComponentType = ImageIOBase::DOUBLE;
460       break;
461     default:
462       itkExceptionMacro("Unhandled PixelFormat: " << outputpt);
463     }
464 
465   m_NumberOfComponents = pixeltype.GetSamplesPerPixel();
466   if ( image.GetPhotometricInterpretation() ==
467        gdcm::PhotometricInterpretation::PALETTE_COLOR )
468     {
469     itkAssertInDebugAndIgnoreInReleaseMacro(m_NumberOfComponents == 1);
470     // TODO: need to do the LUT ourself...
471     //itkExceptionMacro(<< "PALETTE_COLOR is not implemented yet");
472     // AFAIK ITK user don't care about the palette so always apply it and fake a
473     // RGB image for them
474     m_NumberOfComponents = 3;
475     }
476   if ( m_NumberOfComponents == 1 )
477     {
478     this->SetPixelType(SCALAR);
479     }
480   else
481     {
482     this->SetPixelType(RGB); // What if image is YBR ? This is a problem since
483                              // integer conversion is lossy
484     }
485 
486   // set values in case we don't find them
487   //this->SetNumberOfDimensions(  image.GetNumberOfDimensions() );
488   m_Dimensions[0] = dims[0];
489   m_Dimensions[1] = dims[1];
490 
491   double spacing[3];
492 
493   //
494   //
495   // This is a WORKAROUND for a bug in GDCM -- in
496   // ImageHeplper::GetSpacingTagFromMediaStorage it was not
497   // handling some MediaStorage types
498   // so we have to punt here.
499   gdcm::MediaStorage ms;
500 
501   ms.SetFromFile(f);
502   switch(ms)
503     {
504     case gdcm::MediaStorage::HardcopyGrayscaleImageStorage:
505     case gdcm::MediaStorage::GEPrivate3DModelStorage:
506     case gdcm::MediaStorage::Philips3D:
507     case gdcm::MediaStorage::VideoEndoscopicImageStorage:
508     case gdcm::MediaStorage::UltrasoundMultiFrameImageStorage:
509     case gdcm::MediaStorage::UltrasoundImageStorage: // ??
510     case gdcm::MediaStorage::UltrasoundImageStorageRetired:
511     case gdcm::MediaStorage::UltrasoundMultiFrameImageStorageRetired:
512       {
513       std::vector<double> sp;
514       gdcm::Tag spacingTag(0x0028,0x0030);
515       if(ds.FindDataElement(spacingTag) &&
516          !ds.GetDataElement(spacingTag).IsEmpty())
517         {
518         gdcm::DataElement de = ds.GetDataElement(spacingTag);
519         const gdcm::Global &g = gdcm::GlobalInstance;
520         const gdcm::Dicts &dicts = g.GetDicts();
521         const gdcm::DictEntry &entry = dicts.GetDictEntry(de.GetTag());
522         const gdcm::VR & vr = entry.GetVR();
523         switch(vr)
524           {
525           case gdcm::VR::DS:
526             {
527             std::stringstream m_Ss;
528 
529             gdcm::Element<gdcm::VR::DS,gdcm::VM::VM1_n> m_El;
530 
531             const gdcm::ByteValue *                     bv = de.GetByteValue();
532             assert( bv );
533 
534             std::string s = std::string( bv->GetPointer(), bv->GetLength() );
535 
536             m_Ss.str( s );
537             // Stupid file: CT-MONO2-8-abdo.dcm
538             // The spacing is something like that: [0.2\0\0.200000]
539             // I would need to throw an expection that VM is not compatible
540             m_El.SetLength( entry.GetVM().GetLength() * entry.GetVR().GetSizeof() );
541             m_El.Read( m_Ss );
542 
543             assert( m_El.GetLength() == 2 );
544             for(unsigned long i = 0; i < m_El.GetLength(); ++i)
545               sp.push_back( m_El.GetValue(i) );
546             std::swap( sp[0], sp[1]);
547             assert( sp.size() == (unsigned int)entry.GetVM() );
548             }
549             break;
550           case gdcm::VR::IS:
551             {
552             std::stringstream m_Ss;
553 
554             gdcm::Element<gdcm::VR::IS,gdcm::VM::VM1_n> m_El;
555 
556             const gdcm::ByteValue *bv = de.GetByteValue();
557             assert( bv );
558 
559             std::string s = std::string( bv->GetPointer(), bv->GetLength() );
560             m_Ss.str( s );
561             m_El.SetLength( entry.GetVM().GetLength() * entry.GetVR().GetSizeof() );
562             m_El.Read( m_Ss );
563             for(unsigned long i = 0; i < m_El.GetLength(); ++i)
564               sp.push_back( m_El.GetValue(i) );
565             assert( sp.size() == (unsigned int)entry.GetVM() );
566             }
567             break;
568           default:
569             assert(0);
570             break;
571           }
572         spacing[0] = sp[0];
573         spacing[1] = sp[1];
574         }
575       else
576         {
577         spacing[0] = 1.0;
578         spacing[1] = 1.0;
579         }
580       spacing[2] = 1.0; // punt?
581       }
582       break;
583     default:
584       {
585       const double *sp;
586       sp = image.GetSpacing();
587       spacing[0] = sp[0];
588       spacing[1] = sp[1];
589       spacing[2] = sp[2];
590       }
591     break;
592     }
593 
594   const double *origin = image.GetOrigin();
595   for(unsigned i = 0; i < 3; ++i)
596     {
597     m_Spacing[i] = spacing[i];
598     m_Origin[i] = origin[i];
599     }
600   if ( image.GetNumberOfDimensions() == 3 )
601     {
602     m_Dimensions[2] = dims[2];
603     }
604   else
605     {
606     m_Dimensions[2] = 1;
607     }
608 
609   const double *       dircos = image.GetDirectionCosines();
610   vnl_vector< double > rowDirection(3), columnDirection(3);
611   rowDirection[0] = dircos[0];
612   rowDirection[1] = dircos[1];
613   rowDirection[2] = dircos[2];
614 
615   columnDirection[0] = dircos[3];
616   columnDirection[1] = dircos[4];
617   columnDirection[2] = dircos[5];
618 
619   vnl_vector< double > sliceDirection = vnl_cross_3d(rowDirection, columnDirection);
620 
621   // orthogonalize
622   sliceDirection.normalize();
623   rowDirection = vnl_cross_3d(columnDirection,sliceDirection).normalize();
624   columnDirection = vnl_cross_3d(sliceDirection,rowDirection);
625 
626   this->SetDirection(0, rowDirection);
627   this->SetDirection(1, columnDirection);
628   this->SetDirection(2, sliceDirection);
629 
630   //Now copying the gdcm dictionary to the itk dictionary:
631   MetaDataDictionary & dico = this->GetMetaDataDictionary();
632 
633   // in the case of re-use by ImageSeriesReader, clear the dictionary
634   // before populating it.
635   dico.Clear();
636 
637   gdcm::StringFilter sf;
638   sf.SetFile(f);
639   auto it = ds.Begin();
640 
641   // Copy of the header->content
642   for (; it != ds.End(); ++it )
643     {
644     const gdcm::DataElement & ref = *it;
645     const gdcm::Tag &         tag = ref.GetTag();
646     // Compute VR from the toplevel file, and the currently processed dataset:
647     gdcm::VR vr = gdcm::DataSetHelper::ComputeVR(f, ds, tag);
648 
649     // Process binary field and encode them as mime64: only when we do not know
650     // of any better
651     // representation. VR::US is binary, but user want ASCII representation.
652     if ( vr & ( gdcm::VR::OB | gdcm::VR::OF | gdcm::VR::OW | gdcm::VR::SQ | gdcm::VR::UN ) )
653       {
654       // itkAssertInDebugAndIgnoreInReleaseMacro( vr & gdcm::VR::VRBINARY );
655       /*
656        * Old behavior was to skip SQ, Pixel Data element. I decided that it is not safe to mime64
657        * VR::UN element. There used to be a bug in gdcm 1.2.0 and VR:UN element.
658        */
659       if ( (m_LoadPrivateTags || tag.IsPublic()) && vr != gdcm::VR::SQ
660            && tag != gdcm::Tag(0x7fe0, 0x0010) /* && vr != gdcm::VR::UN*/ )
661         {
662         const gdcm::ByteValue *bv = ref.GetByteValue();
663         if ( bv )
664           {
665           // base64 streams have to be a multiple of 4 bytes in length
666           int encodedLengthEstimate = 2 * bv->GetLength();
667           encodedLengthEstimate = ( ( encodedLengthEstimate / 4 ) + 1 ) * 4;
668 
669           auto * bin = new char[encodedLengthEstimate];
670           auto encodedLengthActual = static_cast< unsigned int >(
671             itksysBase64_Encode(
672               (const unsigned char *)bv->GetPointer(),
673               static_cast< SizeValueType >( bv->GetLength() ),
674               (unsigned char *)bin,
675               static_cast< int >( 0 ) ) );
676           std::string encodedValue(bin, encodedLengthActual);
677           EncapsulateMetaData< std::string >(dico, tag.PrintAsPipeSeparatedString(), encodedValue);
678           delete[] bin;
679           }
680         }
681       }
682     else /* if ( vr & gdcm::VR::VRASCII ) */
683       {
684       // Only copying field from the public DICOM dictionary
685       if ( m_LoadPrivateTags || tag.IsPublic() )
686         {
687         EncapsulateMetaData< std::string >( dico, tag.PrintAsPipeSeparatedString(), sf.ToString(tag) );
688         }
689       }
690     }
691 
692 #if defined( ITKIO_DEPRECATED_GDCM1_API )
693   // Now is a good time to fill in the class member:
694   char name[512];
695   this->GetPatientName(name);
696   this->GetPatientID(name);
697   this->GetPatientSex(name);
698   this->GetPatientAge(name);
699   this->GetStudyID(name);
700   this->GetPatientDOB(name);
701   this->GetStudyDescription(name);
702   this->GetBodyPart(name);
703   this->GetNumberOfSeriesInStudy(name);
704   this->GetNumberOfStudyRelatedSeries(name);
705   this->GetStudyDate(name);
706   this->GetModality(name);
707   this->GetManufacturer(name);
708   this->GetInstitution(name);
709   this->GetModel(name);
710   this->GetScanOptions(name);
711 #endif
712 }
713 
ReadImageInformation()714 void GDCMImageIO::ReadImageInformation()
715 {
716   this->InternalReadImageInformation();
717 }
718 
CanWriteFile(const char * name)719 bool GDCMImageIO::CanWriteFile(const char *name)
720 {
721   std::string filename = name;
722 
723   if (  filename.empty() )
724     {
725     itkDebugMacro(<< "No filename specified.");
726     return false;
727     }
728 
729   return this->HasSupportedWriteExtension(name, false);
730 }
731 
WriteImageInformation()732 void GDCMImageIO::WriteImageInformation()
733 {}
734 
Write(const void * buffer)735 void GDCMImageIO::Write(const void *buffer)
736 {
737   // ensure file can be opened for writing, before doing any more work
738   std::ofstream outputFileStream;
739   // let any exceptions propagate
740   this->OpenFileForWriting( outputFileStream, m_FileName );
741   outputFileStream.close();
742 
743   // global static:
744   gdcm::UIDGenerator::SetRoot( m_UIDPrefix.c_str() );
745 
746   // echo "ITK" | od -b
747   gdcm::FileMetaInformation::AppendImplementationClassUID("111.124.113");
748   const std::string project_name = std::string("GDCM/ITK ") + itk::Version::GetITKVersion();
749   gdcm::FileMetaInformation::SetSourceApplicationEntityTitle( project_name.c_str() );
750 
751   gdcm::ImageWriter   writer;
752   gdcm::FileMetaInformation &     fmi = writer.GetFile().GetHeader();
753   gdcm::DataSet &     header = writer.GetFile().GetDataSet();
754   gdcm::Global &      g = gdcm::Global::GetInstance();
755   const gdcm::Dicts & dicts = g.GetDicts();
756   const gdcm::Dict &  pubdict = dicts.GetPublicDict();
757 
758   MetaDataDictionary & dict = this->GetMetaDataDictionary();
759 
760   gdcm::Tag            tag;
761 
762   itk::MetaDataDictionary::ConstIterator itr = dict.Begin();
763   const itk::MetaDataDictionary::ConstIterator end = dict.End();
764 
765   gdcm::StringFilter                     sf;
766   sf.SetFile( writer.GetFile() );
767 
768   while ( itr != end )
769     {
770     std::string value;
771     const std::string & key = itr->first; //Needed for bcc32
772     ExposeMetaData< std::string >( dict, key, value );
773 
774     // Convert DICOM name to DICOM (group,element)
775     bool b = tag.ReadFromPipeSeparatedString( key.c_str() );
776 
777     // Anything that has been changed in the MetaData Dict will be pushed
778     // into the DICOM header:
779     if ( b /*tag != gdcm::Tag(0xffff,0xffff)*/ /*dictEntry*/ )
780       {
781       const gdcm::DictEntry & dictEntry = pubdict.GetDictEntry(tag);
782       gdcm::VR::VRType        vrtype = dictEntry.GetVR();
783       if ( dictEntry.GetVR() == gdcm::VR::SQ )
784         {
785         // How did we reach here ?
786         }
787       else if ( vrtype & ( gdcm::VR::OB | gdcm::VR::OF | gdcm::VR::OW /*|
788                                                                         gdcm::VR::SQ*/   | gdcm::VR::UN ) )
789         {
790         // Custom VR::VRBINARY
791         // convert value from Base64
792         auto * bin = new uint8_t[value.size()];
793         auto decodedLengthActual = static_cast< unsigned int >(
794           itksysBase64_Decode(
795             (const unsigned char *)value.c_str(),
796             static_cast< SizeValueType >( 0 ),
797             (unsigned char *)bin,
798             static_cast< SizeValueType >( value.size() ) ) );
799         if ( /*tag.GetGroup() != 0 ||*/ tag.GetElement() != 0 ) // ?
800           {
801           gdcm::DataElement de(tag);
802           de.SetByteValue( (char *)bin, decodedLengthActual );
803           de.SetVR( dictEntry.GetVR() );
804           if ( tag.GetGroup() == 0x2 )
805             {
806             fmi.Insert(de);
807             }
808           else
809             {
810             header.Insert(de);
811             }
812           }
813         delete[] bin;
814         }
815       else // VRASCII
816         {
817         // TODO, should we keep:
818         // (0028,0106) US/SS 0                                        #2, 1
819         // SmallestImagePixelValue
820         // (0028,0107) US/SS 4095                                     #2, 1
821         // LargestImagePixelValue
822         if ( !tag.IsGroupLength() ) // Get rid of group length, they are not
823                                     // useful
824           {
825           gdcm::DataElement de(tag);
826           if ( dictEntry.GetVR().IsVRFile() )
827             {
828             de.SetVR( dictEntry.GetVR() );
829             }
830 #if GDCM_MAJOR_VERSION == 2 && GDCM_MINOR_VERSION == 0 && GDCM_BUILD_VERSION <= 12
831           // This will not work in the vast majority of cases but to get at
832           // least something working in GDCM 2.0.12
833           de.SetByteValue( value.c_str(), static_cast<uint32_t>(value.size()) );
834 #else
835           // Even padding string with space. If string is not even, SetByteValue() will
836           // pad it with '\0' which is not what is expected except for VR::UI
837           // (see standard section 6.2).
838           if ( vrtype & ( gdcm::VR::UI ) )
839             {
840             const std::string si = sf.FromString( tag, value.c_str(), value.size() );
841             de.SetByteValue( si.c_str(), static_cast<uint32_t>(si.size()) );
842             }
843           else
844             {
845             const gdcm::String<> si = sf.FromString( tag, value.c_str(), value.size() );
846             de.SetByteValue( si.c_str(), static_cast<uint32_t>(si.size()) );
847             }
848 #endif
849           if ( tag.GetGroup() == 0x2 )
850             {
851             fmi.Insert(de);
852             }
853           else
854             {
855             header.Insert(de);   //value, tag.GetGroup(), tag.GetElement());
856             }
857           }
858         }
859       }
860     else
861       {
862       // This is not a DICOM entry, then check if it is one of the
863       // ITK standard ones
864       if ( key == ITK_NumberOfDimensions )
865         {
866         unsigned int numberOfDimensions = 0;
867         ExposeMetaData< unsigned int >(dict, key, numberOfDimensions);
868         m_GlobalNumberOfDimensions = numberOfDimensions;
869         m_Origin.resize(m_GlobalNumberOfDimensions);
870         m_Spacing.resize(m_GlobalNumberOfDimensions);
871         m_Direction.resize(m_GlobalNumberOfDimensions);
872         for (unsigned int i = 0; i < m_GlobalNumberOfDimensions; i++)
873           {
874           m_Direction[i].resize(m_GlobalNumberOfDimensions);
875           }
876         }
877       else if ( key == ITK_Origin )
878         {
879         using DoubleArrayType = Array< double >;
880         DoubleArrayType originArray( 3 );
881         ExposeMetaData< DoubleArrayType >(dict, key, originArray);
882         m_Origin.resize( 3 );
883         m_Origin[0] = originArray[0];
884         m_Origin[1] = originArray[1];
885         m_Origin[2] = originArray[2];
886         }
887       else if ( key == ITK_Spacing )
888         {
889         using DoubleArrayType = Array< double >;
890         DoubleArrayType spacingArray( 3 );
891         ExposeMetaData< DoubleArrayType >(dict, key, spacingArray);
892         m_Spacing.resize( 3 );
893         m_Spacing[0] = spacingArray[0];
894         m_Spacing[1] = spacingArray[1];
895         m_Spacing[2] = spacingArray[2];
896         }
897       else if( key == ITK_ZDirection )
898         {
899         using DoubleMatrixType = Matrix< double >;
900         DoubleMatrixType directionMatrix;
901         ExposeMetaData< DoubleMatrixType >( dict, key, directionMatrix );
902         for(int i = 0; i<3; i++)
903           {
904           for(int j = 0; j<3; j++)
905             {
906             m_Direction[i][j]=directionMatrix[i][j];
907             }
908           }
909         }
910       else
911         {
912         itkDebugMacro(
913           << "GDCMImageIO: non-DICOM and non-ITK standard key = " << key);
914         }
915       }
916 
917     ++itr;
918     }
919 
920   gdcm::SmartPointer< gdcm::Image > simage = new gdcm::Image;
921   gdcm::Image &                     image = *simage;
922   image.SetNumberOfDimensions(2);   // good default
923   image.SetDimension(0, static_cast<unsigned int>(m_Dimensions[0]));
924   image.SetDimension(1, static_cast<unsigned int>(m_Dimensions[1]));
925   //image.SetDimension(2, m_Dimensions[2] );
926   image.SetSpacing(0, m_Spacing[0]);
927   image.SetSpacing(1, m_Spacing[1]);
928   if ( m_Spacing.size() > 2 )
929     {
930     image.SetSpacing(2, m_Spacing[2]);
931     }
932   // Set the origin (image position patient)
933   // If the meta dictionary contains the tag "0020 0032", use it
934   std::string tempString;
935   const bool hasIPP = ExposeMetaData<std::string>(dict,"0020|0032",tempString);
936   if( hasIPP)
937     {
938     double origin3D[3];
939     sscanf(tempString.c_str(), "%lf\\%lf\\%lf", &(origin3D[0]), &(origin3D[1]), &(origin3D[2]) );
940     image.SetOrigin(0, origin3D[0]);
941     image.SetOrigin(1, origin3D[1]);
942     image.SetOrigin(2, origin3D[2]);
943     }
944   else
945     {
946     image.SetOrigin(0, m_Origin[0]);
947     image.SetOrigin(1, m_Origin[1]);
948     if ( m_Origin.size() == 3 )
949       {
950       image.SetOrigin(2, m_Origin[2]);
951       }
952     else
953       {
954       image.SetOrigin(2, 0);
955       }
956     }
957   if ( m_NumberOfDimensions > 2 && m_Dimensions[2] != 1 )
958     {
959     // resize num of dim to 3:
960     image.SetNumberOfDimensions(3);
961     image.SetDimension(2, static_cast<unsigned int>(m_Dimensions[2]));
962     }
963 
964   // Do the direction now:
965   // if the meta dictionary contains the tag "0020 0037", use it
966   const bool hasIOP = ExposeMetaData<std::string>(dict, "0020|0037",tempString);
967   if (hasIOP)
968   {
969     double directions[6];
970     sscanf(tempString.c_str(), "%lf\\%lf\\%lf\\%lf\\%lf\\%lf", &(directions[0]), &(directions[1]), &(directions[2]),&(directions[3]),&(directions[4]),&(directions[5]));
971     image.SetDirectionCosines(0, directions[0]);
972     image.SetDirectionCosines(1, directions[1]);
973     image.SetDirectionCosines(2, directions[2]);
974     image.SetDirectionCosines(3, directions[3]);
975     image.SetDirectionCosines(4, directions[4]);
976     image.SetDirectionCosines(5, directions[5]);
977   }
978   else
979   {
980     image.SetDirectionCosines(0, m_Direction[0][0]);
981     image.SetDirectionCosines(1, m_Direction[0][1]);
982     if ( m_Direction.size() == 3 )
983       {
984       image.SetDirectionCosines(2, m_Direction[0][2]);
985       }
986     else
987       {
988       image.SetDirectionCosines(2, 0);
989       }
990     image.SetDirectionCosines(3, m_Direction[1][0]);
991     image.SetDirectionCosines(4, m_Direction[1][1]);
992     if ( m_Direction.size() == 3 )
993       {
994       image.SetDirectionCosines(5, m_Direction[1][2]);
995       }
996     else
997       {
998       image.SetDirectionCosines(5, 0);
999       }
1000   }
1001 
1002   // reset any previous value:
1003   m_RescaleSlope = 1.0;
1004   m_RescaleIntercept = 0.0;
1005 
1006   // Get user defined rescale slope/intercept
1007   std::string rescaleintercept;
1008   ExposeMetaData< std::string >(dict, "0028|1052", rescaleintercept);
1009   std::string rescaleslope;
1010   ExposeMetaData< std::string >(dict, "0028|1053", rescaleslope);
1011   if ( !rescaleintercept.empty() && !rescaleslope.empty() )
1012     {
1013     std::stringstream sstr1;
1014     sstr1 << rescaleintercept;
1015     if ( !( sstr1 >> m_RescaleIntercept ) )
1016       {
1017       itkExceptionMacro("Problem reading RescaleIntercept: " << rescaleintercept);
1018       }
1019     std::stringstream sstr2;
1020     sstr2 << rescaleslope;
1021     if ( !( sstr2 >> m_RescaleSlope ) )
1022       {
1023       itkExceptionMacro("Problem reading RescaleSlope: " << rescaleslope);
1024       }
1025     // header->InsertValEntry( "US", 0x0028, 0x1054 ); // Rescale Type
1026     }
1027   else if ( !rescaleintercept.empty() || !rescaleslope.empty() ) // xor
1028     {
1029     itkExceptionMacro("Both RescaleSlope & RescaleIntercept need to be present");
1030     }
1031 
1032   // Handle the bitDepth:
1033   std::string bitsAllocated;
1034   std::string bitsStored;
1035   std::string highBit;
1036   std::string pixelRep;
1037   // Get user defined bit representation:
1038   ExposeMetaData< std::string >(dict, "0028|0100", bitsAllocated);
1039   ExposeMetaData< std::string >(dict, "0028|0101", bitsStored);
1040   ExposeMetaData< std::string >(dict, "0028|0102", highBit);
1041   ExposeMetaData< std::string >(dict, "0028|0103", pixelRep);
1042 
1043   gdcm::PixelFormat pixeltype = gdcm::PixelFormat::UNKNOWN;
1044   switch ( this->GetComponentType() )
1045     {
1046     case ImageIOBase::CHAR:
1047       pixeltype = gdcm::PixelFormat::INT8;
1048       break;
1049     case ImageIOBase::UCHAR:
1050       pixeltype = gdcm::PixelFormat::UINT8;
1051       break;
1052     case ImageIOBase::SHORT:
1053       pixeltype = gdcm::PixelFormat::INT16;
1054       break;
1055     case ImageIOBase::USHORT:
1056       pixeltype = gdcm::PixelFormat::UINT16;
1057       break;
1058     case ImageIOBase::INT:
1059       pixeltype = gdcm::PixelFormat::INT32;
1060       break;
1061     case ImageIOBase::UINT:
1062       pixeltype = gdcm::PixelFormat::UINT32;
1063       break;
1064     //Disabling FLOAT and DOUBLE for now...
1065     case ImageIOBase::FLOAT:
1066       pixeltype = gdcm::PixelFormat::FLOAT32;
1067       break;
1068     case ImageIOBase::DOUBLE:
1069       pixeltype = gdcm::PixelFormat::FLOAT64;
1070       break;
1071     default:
1072       itkExceptionMacro(<< "DICOM does not support this component type");
1073     }
1074   itkAssertInDebugAndIgnoreInReleaseMacro(pixeltype != gdcm::PixelFormat::UNKNOWN);
1075   gdcm::PhotometricInterpretation pi;
1076   if ( this->GetNumberOfComponents() == 1 )
1077     {
1078     pi = gdcm::PhotometricInterpretation::MONOCHROME2;
1079     }
1080   else if ( this->GetNumberOfComponents() == 3 )
1081     {
1082     pi = gdcm::PhotometricInterpretation::RGB;
1083     // (0028,0006) US 0                                        #2, 1
1084     // PlanarConfiguration
1085     }
1086   else
1087     {
1088     itkExceptionMacro(<< "DICOM does not support this component type");
1089     }
1090   pixeltype.SetSamplesPerPixel( static_cast<short unsigned int>( this->GetNumberOfComponents() ) );
1091 
1092   // Compute the outpixeltype
1093   gdcm::PixelFormat outpixeltype = gdcm::PixelFormat::UNKNOWN;
1094   if ( pixeltype == gdcm::PixelFormat::FLOAT32 || pixeltype == gdcm::PixelFormat::FLOAT64 )
1095     {
1096     if ( !bitsAllocated.empty() && !bitsStored.empty() && !highBit.empty() && !pixelRep.empty() )
1097       {
1098       outpixeltype.SetBitsAllocated( static_cast<unsigned short int>(std::stoi( bitsAllocated.c_str() ) ));
1099       outpixeltype.SetBitsStored( static_cast<unsigned short int>(std::stoi( bitsStored.c_str() )) );
1100       outpixeltype.SetHighBit( static_cast<unsigned short int>(std::stoi( highBit.c_str()) ) );
1101       outpixeltype.SetPixelRepresentation( static_cast<unsigned short int>(std::stoi( pixelRep.c_str() )) );
1102       if ( this->GetNumberOfComponents() != 1 )
1103         {
1104         itkExceptionMacro(<< "Sorry Dave I can't do that");
1105         }
1106       itkAssertInDebugAndIgnoreInReleaseMacro(outpixeltype != gdcm::PixelFormat::UNKNOWN);
1107       }
1108     else
1109       {
1110       itkExceptionMacro(<< "A Floating point buffer was passed but the stored pixel type was not specified."
1111                            "This is currently not supported");
1112       }
1113     }
1114   else if( this->GetInternalComponentType() != UNKNOWNCOMPONENTTYPE )
1115     {
1116     const IOComponentType internalComponentType = this->GetInternalComponentType();
1117     switch( internalComponentType )
1118       {
1119       //
1120       //  This set of conversions deal with less cases than the conversion from
1121       //  gdcm::PixelFormat to itk::ImageIOBase, because the INT12 and INT16
1122       //  types map both to SHORT, and because the FLOAT32 and FLOAT64 have
1123       //  already been taken care of. The float case use an Integer internal
1124       //  storage, and specifies the precision desired for it.
1125       //
1126       case ImageIOBase::CHAR:
1127         outpixeltype = gdcm::PixelFormat::INT8;
1128         break;
1129       case ImageIOBase::UCHAR:
1130         outpixeltype = gdcm::PixelFormat::UINT8;
1131         break;
1132       case ImageIOBase::SHORT:
1133         outpixeltype = gdcm::PixelFormat::INT16;
1134         break;
1135       case ImageIOBase::USHORT:
1136         outpixeltype = gdcm::PixelFormat::UINT16;
1137         break;
1138       case ImageIOBase::INT:
1139         outpixeltype = gdcm::PixelFormat::INT32;
1140         break;
1141       case ImageIOBase::UINT:
1142         outpixeltype = gdcm::PixelFormat::UINT32;
1143         break;
1144       default:
1145         itkExceptionMacro(<< "DICOM does not support this component type");
1146       }
1147     }
1148 
1149   image.SetPhotometricInterpretation(pi);
1150   if( outpixeltype != gdcm::PixelFormat::UNKNOWN )
1151     {
1152     image.SetPixelFormat(outpixeltype);
1153     }
1154   else
1155     {
1156     outpixeltype = pixeltype;
1157     image.SetPixelFormat(pixeltype);
1158     }
1159   const SizeValueType len = image.GetBufferLength();
1160 
1161   const size_t numberOfBytes = this->GetImageSizeInBytes();
1162 
1163   gdcm::DataElement pixeldata( gdcm::Tag(0x7fe0, 0x0010) );
1164   // Handle rescaler here:
1165   // Whenever shift / scale is needed... do it !
1166   if( m_RescaleIntercept != 0.0 || m_RescaleSlope != 1.0 || outpixeltype != pixeltype )
1167     {
1168     gdcm::Rescaler ir;
1169     double rescaleIntercept = m_RescaleIntercept;
1170     if( m_RescaleIntercept == 0.0 && outpixeltype != pixeltype )
1171       {
1172       // force type conversion when outputpixeltype != pixeltype
1173       rescaleIntercept = -0.0;
1174       }
1175     ir.SetIntercept( rescaleIntercept );
1176     ir.SetSlope( m_RescaleSlope );
1177     ir.SetPixelFormat( pixeltype );
1178 
1179     // Workaround because SetUseTargetPixelType does not apply to
1180     // InverseRescale
1181     const auto minValue = static_cast<double>( outpixeltype.GetMin() );
1182     const auto maxValue = static_cast<double>( outpixeltype.GetMax() );
1183     const double minValueMapped = minValue * m_RescaleSlope + m_RescaleIntercept;
1184     const double maxValueMapped = maxValue * m_RescaleSlope + m_RescaleIntercept;
1185     ir.SetMinMaxForPixelType( minValueMapped, maxValueMapped );
1186 
1187     image.SetIntercept(m_RescaleIntercept);
1188     image.SetSlope(m_RescaleSlope);
1189     auto * copyBuffer = new char[len];
1190     const auto * inputBuffer = static_cast< const char *>( buffer );
1191     ir.InverseRescale(copyBuffer, inputBuffer, numberOfBytes);
1192     pixeldata.SetByteValue(copyBuffer, static_cast<uint32_t>(len));
1193     delete[] copyBuffer;
1194     }
1195   else
1196     {
1197     itkAssertInDebugAndIgnoreInReleaseMacro(len == numberOfBytes);
1198     // only do a straight copy:
1199     const auto * inputBuffer = static_cast< const char *>( buffer );
1200     pixeldata.SetByteValue( inputBuffer, static_cast<unsigned int>(numberOfBytes) );
1201     }
1202   image.SetDataElement(pixeldata);
1203 
1204   // Handle compression here:
1205   // If user ask to use compression:
1206   if ( m_UseCompression )
1207     {
1208     gdcm::ImageChangeTransferSyntax change;
1209     if ( m_CompressionType == JPEG )
1210       {
1211       change.SetTransferSyntax(gdcm::TransferSyntax::JPEGLosslessProcess14_1);
1212       }
1213     else if ( m_CompressionType == JPEG2000 )
1214       {
1215       change.SetTransferSyntax(gdcm::TransferSyntax::JPEG2000Lossless);
1216       }
1217     else
1218       {
1219       itkExceptionMacro(<< "Unknown compression type");
1220       }
1221     change.SetInput(image);
1222     bool b = change.Change();
1223     if ( !b )
1224       {
1225       itkExceptionMacro(<< "Could not change the Transfer Syntax for Compression");
1226       }
1227     writer.SetImage( change.GetOutput() );
1228     }
1229   else
1230     {
1231     writer.SetImage(image);
1232     }
1233 
1234   if ( !m_KeepOriginalUID )
1235     {
1236     // UID generation part:
1237     // We only create *ONE* Study/Series.Frame of Reference Instance UID
1238     if ( m_StudyInstanceUID.empty() )
1239       {
1240       // As long as user maintain there gdcmIO they will keep the same
1241       // Study/Series instance UID.
1242       gdcm::UIDGenerator uid;
1243       m_StudyInstanceUID = uid.Generate();
1244       m_SeriesInstanceUID = uid.Generate();
1245       m_FrameOfReferenceInstanceUID = uid.Generate();
1246       }
1247     //std::string uid = uid.Generate();
1248     const char *studyuid = m_StudyInstanceUID.c_str();
1249       {
1250       gdcm::DataElement de( gdcm::Tag(0x0020, 0x000d) ); // Study
1251       de.SetByteValue( studyuid, static_cast<unsigned int>(strlen(studyuid)) );
1252       de.SetVR( gdcm::Attribute< 0x0020, 0x000d >::GetVR() );
1253       header.Replace(de);
1254       }
1255     const char *seriesuid = m_SeriesInstanceUID.c_str();
1256       {
1257       gdcm::DataElement de( gdcm::Tag(0x0020, 0x000e) ); // Series
1258       de.SetByteValue( seriesuid, static_cast<unsigned int>(strlen(seriesuid)) );
1259       de.SetVR( gdcm::Attribute< 0x0020, 0x000e >::GetVR() );
1260       header.Replace(de);
1261       }
1262     const char *frameofreferenceuid = m_FrameOfReferenceInstanceUID.c_str();
1263       {
1264       gdcm::DataElement de( gdcm::Tag(0x0020, 0x0052) ); // Frame of Reference
1265       de.SetByteValue( frameofreferenceuid, static_cast<unsigned int>(strlen( frameofreferenceuid)) );
1266       de.SetVR( gdcm::Attribute< 0x0020, 0x0052 >::GetVR() );
1267       header.Replace(de);
1268       }
1269     }
1270 
1271   if ( image.GetTransferSyntax() != gdcm::TransferSyntax::ImplicitVRLittleEndian )
1272     {
1273     gdcm::FileExplicitFilter fef;
1274     //fef.SetChangePrivateTags( true );
1275     fef.SetFile( writer.GetFile() );
1276     if ( !fef.Change() )
1277       {
1278       itkExceptionMacro(<< "Failed to change to Explicit Transfer Syntax");
1279       }
1280     }
1281 
1282   writer.SetFileName( m_FileName.c_str() );
1283   if ( !writer.Write() )
1284     {
1285     itkExceptionMacro(<< "DICOM does not support this component type");
1286     }
1287 }
1288 
1289 #if defined( ITKIO_DEPRECATED_GDCM1_API )
1290 // Convenience methods to query patient and scanner information. These
1291 // methods are here for compatibility with the DICOMImageIO2 class.
GetPatientName(char * name)1292 void GDCMImageIO::GetPatientName(char *name)
1293 {
1294   MetaDataDictionary & dict = this->GetMetaDataDictionary();
1295 
1296   ExposeMetaData< std::string >(dict, "0010|0010", m_PatientName);
1297   strcpy ( name, m_PatientName.c_str() );
1298 }
1299 
GetPatientID(char * name)1300 void GDCMImageIO::GetPatientID(char *name)
1301 {
1302   MetaDataDictionary & dict = this->GetMetaDataDictionary();
1303 
1304   ExposeMetaData< std::string >(dict, "0010|0020", m_PatientID);
1305   strcpy ( name, m_PatientID.c_str() );
1306 }
1307 
GetPatientSex(char * name)1308 void GDCMImageIO::GetPatientSex(char *name)
1309 {
1310   MetaDataDictionary & dict = this->GetMetaDataDictionary();
1311 
1312   ExposeMetaData< std::string >(dict, "0010|0040", m_PatientSex);
1313   strcpy ( name, m_PatientSex.c_str() );
1314 }
1315 
GetPatientAge(char * name)1316 void GDCMImageIO::GetPatientAge(char *name)
1317 {
1318   MetaDataDictionary & dict = this->GetMetaDataDictionary();
1319 
1320   ExposeMetaData< std::string >(dict, "0010|1010", m_PatientAge);
1321   strcpy ( name, m_PatientAge.c_str() );
1322 }
1323 
GetStudyID(char * name)1324 void GDCMImageIO::GetStudyID(char *name)
1325 {
1326   MetaDataDictionary & dict = this->GetMetaDataDictionary();
1327 
1328   ExposeMetaData< std::string >(dict, "0020|0010", m_StudyID);
1329   strcpy ( name, m_StudyID.c_str() );
1330 }
1331 
GetPatientDOB(char * name)1332 void GDCMImageIO::GetPatientDOB(char *name)
1333 {
1334   MetaDataDictionary & dict = this->GetMetaDataDictionary();
1335 
1336   ExposeMetaData< std::string >(dict, "0010|0030", m_PatientDOB);
1337   strcpy ( name, m_PatientDOB.c_str() );
1338 }
1339 
GetStudyDescription(char * name)1340 void GDCMImageIO::GetStudyDescription(char *name)
1341 {
1342   MetaDataDictionary & dict = this->GetMetaDataDictionary();
1343 
1344   ExposeMetaData< std::string >(dict, "0008|1030", m_StudyDescription);
1345   strcpy ( name, m_StudyDescription.c_str() );
1346 }
1347 
GetBodyPart(char * name)1348 void GDCMImageIO::GetBodyPart(char *name)
1349 {
1350   MetaDataDictionary & dict = this->GetMetaDataDictionary();
1351 
1352   ExposeMetaData< std::string >(dict, "0018|0015", m_BodyPart);
1353   strcpy ( name, m_BodyPart.c_str() );
1354 }
1355 
GetNumberOfSeriesInStudy(char * name)1356 void GDCMImageIO::GetNumberOfSeriesInStudy(char *name)
1357 {
1358   MetaDataDictionary & dict = this->GetMetaDataDictionary();
1359 
1360   ExposeMetaData< std::string >(dict, "0020|1000", m_NumberOfSeriesInStudy);
1361   strcpy ( name, m_NumberOfSeriesInStudy.c_str() );
1362 }
1363 
GetNumberOfStudyRelatedSeries(char * name)1364 void GDCMImageIO::GetNumberOfStudyRelatedSeries(char *name)
1365 {
1366   MetaDataDictionary & dict = this->GetMetaDataDictionary();
1367 
1368   ExposeMetaData< std::string >(dict, "0020|1206", m_NumberOfStudyRelatedSeries);
1369   strcpy ( name, m_NumberOfStudyRelatedSeries.c_str() );
1370 }
1371 
GetStudyDate(char * name)1372 void GDCMImageIO::GetStudyDate(char *name)
1373 {
1374   MetaDataDictionary & dict = this->GetMetaDataDictionary();
1375 
1376   ExposeMetaData< std::string >(dict, "0008|0020", m_StudyDate);
1377   strcpy ( name, m_StudyDate.c_str() );
1378 }
1379 
GetModality(char * name)1380 void GDCMImageIO::GetModality(char *name)
1381 {
1382   MetaDataDictionary & dict = this->GetMetaDataDictionary();
1383 
1384   ExposeMetaData< std::string >(dict, "0008|0060", m_Modality);
1385   strcpy ( name, m_Modality.c_str() );
1386 }
1387 
GetManufacturer(char * name)1388 void GDCMImageIO::GetManufacturer(char *name)
1389 {
1390   MetaDataDictionary & dict = this->GetMetaDataDictionary();
1391 
1392   ExposeMetaData< std::string >(dict, "0008|0070", m_Manufacturer);
1393   strcpy ( name, m_Manufacturer.c_str() );
1394 }
1395 
GetInstitution(char * name)1396 void GDCMImageIO::GetInstitution(char *name)
1397 {
1398   MetaDataDictionary & dict = this->GetMetaDataDictionary();
1399 
1400   ExposeMetaData< std::string >(dict, "0008|0080", m_Institution);
1401   strcpy ( name, m_Institution.c_str() );
1402 }
1403 
GetModel(char * name)1404 void GDCMImageIO::GetModel(char *name)
1405 {
1406   MetaDataDictionary & dict = this->GetMetaDataDictionary();
1407 
1408   ExposeMetaData< std::string >(dict, "0008|1090", m_Model);
1409   strcpy ( name, m_Model.c_str() );
1410 }
1411 
GetScanOptions(char * name)1412 void GDCMImageIO::GetScanOptions(char *name)
1413 {
1414   MetaDataDictionary & dict = this->GetMetaDataDictionary();
1415 
1416   ExposeMetaData< std::string >(dict, "0018|0022", m_ScanOptions);
1417   strcpy ( name, m_ScanOptions.c_str() );
1418 }
1419 #endif
1420 
GetValueFromTag(const std::string & tag,std::string & value)1421 bool GDCMImageIO::GetValueFromTag(const std::string & tag, std::string & value)
1422 {
1423   MetaDataDictionary & dict = this->GetMetaDataDictionary();
1424 
1425   std::string tag_lower = tag;
1426   std::transform( tag_lower.begin(), tag_lower.end(), tag_lower.begin(),
1427                   static_cast<int(*)(int)>( ::tolower ) );
1428 
1429   return ExposeMetaData< std::string >(dict, tag_lower, value);
1430 }
1431 
GetLabelFromTag(const std::string & tag,std::string & labelId)1432 bool GDCMImageIO::GetLabelFromTag(const std::string & tag,
1433                                   std::string & labelId)
1434 {
1435   gdcm::Tag t;
1436   if ( t.ReadFromPipeSeparatedString( tag.c_str() ) && t.IsPublic() )
1437     {
1438     const gdcm::Global &    g = gdcm::Global::GetInstance();
1439     const gdcm::Dicts &     dicts = g.GetDicts();
1440     const gdcm::DictEntry & entry = dicts.GetDictEntry(t);
1441     labelId = entry.GetName();
1442     return true;
1443     }
1444   return false;
1445 }
1446 
InternalSetCompressor(const std::string & _compressor)1447 void GDCMImageIO::InternalSetCompressor(const std::string &_compressor )
1448 {
1449 
1450   if (_compressor == "" ||
1451       _compressor == "JPEG2000")
1452     {
1453     m_CompressionType = JPEG2000;
1454     }
1455   else if (_compressor == "JPEG" )
1456     {
1457     m_CompressionType = JPEG;
1458     }
1459   else
1460     {
1461     this->Superclass::InternalSetCompressor(_compressor);
1462     }
1463 }
1464 
PrintSelf(std::ostream & os,Indent indent) const1465 void GDCMImageIO::PrintSelf(std::ostream & os, Indent indent) const
1466 {
1467   Superclass::PrintSelf(os, indent);
1468   os << indent << "Internal Component Type: " << this->GetComponentTypeAsString(m_InternalComponentType)
1469      << std::endl;
1470   os << indent << "RescaleSlope: " << m_RescaleSlope << std::endl;
1471   os << indent << "RescaleIntercept: " << m_RescaleIntercept << std::endl;
1472   os << indent << "KeepOriginalUID:" << ( m_KeepOriginalUID ? "On" : "Off" ) << std::endl;
1473   os << indent << "LoadPrivateTags:" << ( m_LoadPrivateTags ? "On" : "Off" ) << std::endl;
1474   os << indent << "UIDPrefix: " << m_UIDPrefix << std::endl;
1475   os << indent << "StudyInstanceUID: " << m_StudyInstanceUID << std::endl;
1476   os << indent << "SeriesInstanceUID: " << m_SeriesInstanceUID << std::endl;
1477   os << indent << "FrameOfReferenceInstanceUID: " << m_FrameOfReferenceInstanceUID << std::endl;
1478   os << indent << "CompressionType:" << m_CompressionType << std::endl;
1479 
1480 #if defined( ITKIO_DEPRECATED_GDCM1_API )
1481   os << indent << "Patient Name:" << m_PatientName << std::endl;
1482   os << indent << "Patient ID:" << m_PatientID << std::endl;
1483   os << indent << "Patient Sex:" << m_PatientSex << std::endl;
1484   os << indent << "Patient Age:" << m_PatientAge << std::endl;
1485   os << indent << "Study ID:" << m_StudyID << std::endl;
1486   os << indent << "Patient DOB:" << m_PatientDOB << std::endl;
1487   os << indent << "Study Description:" << m_StudyDescription << std::endl;
1488   os << indent << "Body Part:" << m_BodyPart << std::endl;
1489   os << indent << "Number Of Series In Study:" << m_NumberOfSeriesInStudy << std::endl;
1490   os << indent << "Number Of Study Related Series:" << m_NumberOfStudyRelatedSeries << std::endl;
1491   os << indent << "Study Date:" << m_StudyDate << std::endl;
1492   os << indent << "Modality:" << m_Modality << std::endl;
1493   os << indent << "Manufacturer:" << m_Manufacturer << std::endl;
1494   os << indent << "Institution Name:" << m_Institution << std::endl;
1495   os << indent << "Model:" << m_Model << std::endl;
1496   os << indent << "Scan Options:" << m_ScanOptions << std::endl;
1497 #endif
1498 }
1499 } // end namespace itk
1500