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