Medical Imaging Interaction Toolkit  2018.4.99-3e3f1a6e
Medical Imaging Interaction Toolkit
mitkItkImageIO.cpp
Go to the documentation of this file.
1 /*============================================================================
2 
3 The Medical Imaging Interaction Toolkit (MITK)
4 
5 Copyright (c) German Cancer Research Center (DKFZ)
6 All rights reserved.
7 
8 Use of this source code is governed by a 3-clause BSD license that can be
9 found in the LICENSE file.
10 
11 ============================================================================*/
12 
13 #include "mitkItkImageIO.h"
14 
16 #include <mitkCoreServices.h>
17 #include <mitkCustomMimeType.h>
18 #include <mitkIOMimeTypes.h>
20 #include <mitkImage.h>
21 #include <mitkImageReadAccessor.h>
22 #include <mitkLocaleSwitch.h>
23 
24 #include <itkImage.h>
25 #include <itkImageFileReader.h>
26 #include <itkImageIOFactory.h>
27 #include <itkImageIORegion.h>
28 #include <itkMetaDataObject.h>
29 
30 #include <algorithm>
31 
32 namespace mitk
33 {
34  const char *const PROPERTY_NAME_TIMEGEOMETRY_TYPE = "org.mitk.timegeometry.type";
35  const char *const PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS = "org.mitk.timegeometry.timepoints";
36  const char *const PROPERTY_KEY_TIMEGEOMETRY_TYPE = "org_mitk_timegeometry_type";
37  const char *const PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS = "org_mitk_timegeometry_timepoints";
38 
40  : AbstractFileIO(other), m_ImageIO(dynamic_cast<itk::ImageIOBase *>(other.m_ImageIO->Clone().GetPointer()))
41  {
42  this->InitializeDefaultMetaDataKeys();
43  }
44 
45  std::vector<std::string> ItkImageIO::FixUpImageIOExtensions(const std::string &imageIOName)
46  {
47  std::vector<std::string> extensions;
48  // Try to fix-up some known ITK image IO classes
49  if (imageIOName == "GiplImageIO")
50  {
51  extensions.push_back("gipl");
52  extensions.push_back("gipl.gz");
53  }
54  else if (imageIOName == "GDCMImageIO")
55  {
56  extensions.push_back("gdcm");
57  extensions.push_back("dcm");
58  extensions.push_back("DCM");
59  extensions.push_back("dc3");
60  extensions.push_back("DC3");
61  extensions.push_back("ima");
62  extensions.push_back("img");
63  }
64  else if (imageIOName == "PNGImageIO")
65  {
66  extensions.push_back("png");
67  extensions.push_back("PNG");
68  }
69  else if (imageIOName == "StimulateImageIO")
70  {
71  extensions.push_back("spr");
72  }
73  else if (imageIOName == "HDF5ImageIO")
74  {
75  extensions.push_back("hdf");
76  extensions.push_back("h4");
77  extensions.push_back("hdf4");
78  extensions.push_back("h5");
79  extensions.push_back("hdf5");
80  extensions.push_back("he4");
81  extensions.push_back("he5");
82  extensions.push_back("hd5");
83  }
84  else if ("GE4ImageIO" == imageIOName || "GE5ImageIO" == imageIOName || "Bruker2dseqImageIO" == imageIOName)
85  {
86  extensions.push_back("");
87  }
88 
89  if (!extensions.empty())
90  {
91  MITK_DEBUG << "Fixing up known extensions for " << imageIOName;
92  }
93 
94  return extensions;
95  }
96 
97  void ItkImageIO::FixUpCustomMimeTypeName(const std::string &imageIOName, CustomMimeType &customMimeType)
98  {
99  if ("GE4ImageIO" == imageIOName)
100  {
101  customMimeType.SetName(this->AbstractFileReader::GetMimeTypePrefix() + "ge4");
102  }
103  else if ("GE5ImageIO" == imageIOName)
104  {
105  customMimeType.SetName(this->AbstractFileReader::GetMimeTypePrefix() + "ge5");
106  }
107  else if ("Bruker2dseqImageIO" == imageIOName)
108  {
109  customMimeType.SetName(this->AbstractFileReader::GetMimeTypePrefix() + "bruker2dseq");
110  }
111  }
112 
113  ItkImageIO::ItkImageIO(itk::ImageIOBase::Pointer imageIO)
114  : AbstractFileIO(Image::GetStaticNameOfClass()), m_ImageIO(imageIO)
115  {
116  if (m_ImageIO.IsNull())
117  {
118  mitkThrow() << "ITK ImageIOBase argument must not be nullptr";
119  }
120 
123 
124  std::vector<std::string> readExtensions = m_ImageIO->GetSupportedReadExtensions();
125 
126  if (readExtensions.empty())
127  {
128  std::string imageIOName = m_ImageIO->GetNameOfClass();
129  MITK_DEBUG << "ITK ImageIOBase " << imageIOName << " does not provide read extensions";
130  readExtensions = FixUpImageIOExtensions(imageIOName);
131  }
132 
133  CustomMimeType customReaderMimeType;
134  customReaderMimeType.SetCategory("Images");
135  for (std::vector<std::string>::const_iterator iter = readExtensions.begin(), endIter = readExtensions.end();
136  iter != endIter;
137  ++iter)
138  {
139  std::string extension = *iter;
140  if (!extension.empty() && extension[0] == '.')
141  {
142  extension.assign(iter->begin() + 1, iter->end());
143  }
144  customReaderMimeType.AddExtension(extension);
145  }
146 
147  auto extensions = customReaderMimeType.GetExtensions();
148  if (extensions.empty() || (extensions.size() == 1 && extensions[0].empty()))
149  {
150  std::string imageIOName = m_ImageIO->GetNameOfClass();
151  FixUpCustomMimeTypeName(imageIOName, customReaderMimeType);
152  }
153 
154  this->AbstractFileReader::SetMimeType(customReaderMimeType);
155 
156  std::vector<std::string> writeExtensions = imageIO->GetSupportedWriteExtensions();
157  if (writeExtensions.empty())
158  {
159  std::string imageIOName = imageIO->GetNameOfClass();
160  MITK_DEBUG << "ITK ImageIOBase " << imageIOName << " does not provide write extensions";
161  writeExtensions = FixUpImageIOExtensions(imageIOName);
162  }
163 
164  if (writeExtensions != readExtensions)
165  {
166  CustomMimeType customWriterMimeType;
167  customWriterMimeType.SetCategory("Images");
168  for (std::vector<std::string>::const_iterator iter = writeExtensions.begin(), endIter = writeExtensions.end();
169  iter != endIter;
170  ++iter)
171  {
172  std::string extension = *iter;
173  if (!extension.empty() && extension[0] == '.')
174  {
175  extension.assign(iter->begin() + 1, iter->end());
176  }
177  customWriterMimeType.AddExtension(extension);
178  }
179 
180  auto extensions = customWriterMimeType.GetExtensions();
181  if (extensions.empty() || (extensions.size() == 1 && extensions[0].empty()))
182  {
183  std::string imageIOName = m_ImageIO->GetNameOfClass();
184  FixUpCustomMimeTypeName(imageIOName, customWriterMimeType);
185  }
186 
187  this->AbstractFileWriter::SetMimeType(customWriterMimeType);
188  }
189 
190  std::string description = std::string("ITK ") + imageIO->GetNameOfClass();
191  this->SetReaderDescription(description);
192  this->SetWriterDescription(description);
193 
194  this->RegisterService();
195  }
196 
197  ItkImageIO::ItkImageIO(const CustomMimeType &mimeType, itk::ImageIOBase::Pointer imageIO, int rank)
198  : AbstractFileIO(Image::GetStaticNameOfClass(), mimeType, std::string("ITK ") + imageIO->GetNameOfClass()),
199  m_ImageIO(imageIO)
200  {
201  if (m_ImageIO.IsNull())
202  {
203  mitkThrow() << "ITK ImageIOBase argument must not be nullptr";
204  }
205 
208 
209  if (rank)
210  {
211  this->AbstractFileReader::SetRanking(rank);
212  this->AbstractFileWriter::SetRanking(rank);
213  }
214 
215  this->RegisterService();
216  }
217 
218  std::vector<TimePointType> ConvertMetaDataObjectToTimePointList(const itk::MetaDataObjectBase* data)
219  {
220  const auto* timeGeometryTimeData =
221  dynamic_cast<const itk::MetaDataObject<std::string>*>(data);
222  std::vector<TimePointType> result;
223 
224  if (timeGeometryTimeData)
225  {
226  std::string dataStr = timeGeometryTimeData->GetMetaDataObjectValue();
227  std::stringstream stream(dataStr);
228  TimePointType tp;
229  while (stream >> tp)
230  {
231  result.push_back(tp);
232  }
233  }
234 
235  return result;
236  };
237 
238  itk::MetaDataObjectBase::Pointer ConvertTimePointListToMetaDataObject(const mitk::TimeGeometry* timeGeometry)
239  {
240  std::stringstream stream;
241  stream << timeGeometry->GetTimeBounds(0)[0];
242  const auto maxTimePoints = timeGeometry->CountTimeSteps();
243  for (TimeStepType pos = 0; pos < maxTimePoints; ++pos)
244  {
245  stream << " " << timeGeometry->GetTimeBounds(pos)[1];
246  }
247  auto result = itk::MetaDataObject<std::string>::New();
248  result->SetMetaDataObjectValue(stream.str());
249  return result.GetPointer();
250  };
251 
252  std::vector<BaseData::Pointer> ItkImageIO::Read()
253  {
254  std::vector<BaseData::Pointer> result;
255  mitk::LocaleSwitch localeSwitch("C");
256 
258 
259  const unsigned int MINDIM = 2;
260  const unsigned int MAXDIM = 4;
261 
262  const std::string path = this->GetLocalFileName();
263 
264  MITK_INFO << "loading " << path << " via itk::ImageIOFactory... " << std::endl;
265 
266  // Check to see if we can read the file given the name or prefix
267  if (path.empty())
268  {
269  mitkThrow() << "Empty filename in mitk::ItkImageIO ";
270  }
271 
272  // Got to allocate space for the image. Determine the characteristics of
273  // the image.
274  m_ImageIO->SetFileName(path);
275  m_ImageIO->ReadImageInformation();
276 
277  unsigned int ndim = m_ImageIO->GetNumberOfDimensions();
278  if (ndim < MINDIM || ndim > MAXDIM)
279  {
280  MITK_WARN << "Sorry, only dimensions 2, 3 and 4 are supported. The given file has " << ndim
281  << " dimensions! Reading as 4D.";
282  ndim = MAXDIM;
283  }
284 
285  itk::ImageIORegion ioRegion(ndim);
286  itk::ImageIORegion::SizeType ioSize = ioRegion.GetSize();
287  itk::ImageIORegion::IndexType ioStart = ioRegion.GetIndex();
288 
289  unsigned int dimensions[MAXDIM];
290  dimensions[0] = 0;
291  dimensions[1] = 0;
292  dimensions[2] = 0;
293  dimensions[3] = 0;
294 
295  ScalarType spacing[MAXDIM];
296  spacing[0] = 1.0f;
297  spacing[1] = 1.0f;
298  spacing[2] = 1.0f;
299  spacing[3] = 1.0f;
300 
301  Point3D origin;
302  origin.Fill(0);
303 
304  unsigned int i;
305  for (i = 0; i < ndim; ++i)
306  {
307  ioStart[i] = 0;
308  ioSize[i] = m_ImageIO->GetDimensions(i);
309  if (i < MAXDIM)
310  {
311  dimensions[i] = m_ImageIO->GetDimensions(i);
312  spacing[i] = m_ImageIO->GetSpacing(i);
313  if (spacing[i] <= 0)
314  spacing[i] = 1.0f;
315  }
316  if (i < 3)
317  {
318  origin[i] = m_ImageIO->GetOrigin(i);
319  }
320  }
321 
322  ioRegion.SetSize(ioSize);
323  ioRegion.SetIndex(ioStart);
324 
325  MITK_INFO << "ioRegion: " << ioRegion << std::endl;
326  m_ImageIO->SetIORegion(ioRegion);
327  void *buffer = new unsigned char[m_ImageIO->GetImageSizeInBytes()];
328  m_ImageIO->Read(buffer);
329 
330  image->Initialize(MakePixelType(m_ImageIO), ndim, dimensions);
331  image->SetImportChannel(buffer, 0, Image::ManageMemory);
332 
333  const itk::MetaDataDictionary &dictionary = m_ImageIO->GetMetaDataDictionary();
334 
335  // access direction of itk::Image and include spacing
336  mitk::Matrix3D matrix;
337  matrix.SetIdentity();
338  unsigned int j, itkDimMax3 = (ndim >= 3 ? 3 : ndim);
339  for (i = 0; i < itkDimMax3; ++i)
340  for (j = 0; j < itkDimMax3; ++j)
341  matrix[i][j] = m_ImageIO->GetDirection(j)[i];
342 
343  // re-initialize PlaneGeometry with origin and direction
344  PlaneGeometry *planeGeometry = image->GetSlicedGeometry(0)->GetPlaneGeometry(0);
345  planeGeometry->SetOrigin(origin);
346  planeGeometry->GetIndexToWorldTransform()->SetMatrix(matrix);
347 
348  // re-initialize SlicedGeometry3D
349  SlicedGeometry3D *slicedGeometry = image->GetSlicedGeometry(0);
350  slicedGeometry->InitializeEvenlySpaced(planeGeometry, image->GetDimension(2));
351  slicedGeometry->SetSpacing(spacing);
352 
353  MITK_INFO << slicedGeometry->GetCornerPoint(false, false, false);
354  MITK_INFO << slicedGeometry->GetCornerPoint(true, true, true);
355 
356  // re-initialize TimeGeometry
357  TimeGeometry::Pointer timeGeometry;
358 
359  if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TYPE) || dictionary.HasKey(PROPERTY_KEY_TIMEGEOMETRY_TYPE))
360  { // also check for the name because of backwards compatibility. Past code version stored with the name and not with
361  // the key
362  itk::MetaDataObject<std::string>::ConstPointer timeGeometryTypeData = nullptr;
363  if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TYPE))
364  {
365  timeGeometryTypeData =
366  dynamic_cast<const itk::MetaDataObject<std::string> *>(dictionary.Get(PROPERTY_NAME_TIMEGEOMETRY_TYPE));
367  }
368  else
369  {
370  timeGeometryTypeData =
371  dynamic_cast<const itk::MetaDataObject<std::string> *>(dictionary.Get(PROPERTY_KEY_TIMEGEOMETRY_TYPE));
372  }
373 
374  if (timeGeometryTypeData->GetMetaDataObjectValue() == ArbitraryTimeGeometry::GetStaticNameOfClass())
375  {
376  MITK_INFO << "used time geometry: " << ArbitraryTimeGeometry::GetStaticNameOfClass();
377  typedef std::vector<TimePointType> TimePointVector;
378  TimePointVector timePoints;
379 
380  if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS))
381  {
382  timePoints = ConvertMetaDataObjectToTimePointList(dictionary.Get(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS));
383  }
384  else if (dictionary.HasKey(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS))
385  {
386  timePoints = ConvertMetaDataObjectToTimePointList(dictionary.Get(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS));
387  }
388 
389  if (timePoints.empty())
390  {
391  MITK_ERROR << "Stored timepoints are empty. Meta information seems to bee invalid. Switch to ProportionalTimeGeometry fallback";
392  }
393  else if (timePoints.size() - 1 != image->GetDimension(3))
394  {
395  MITK_ERROR << "Stored timepoints (" << timePoints.size() - 1 << ") and size of image time dimension ("
396  << image->GetDimension(3) << ") do not match. Switch to ProportionalTimeGeometry fallback";
397  }
398  else
399  {
401  TimePointVector::const_iterator pos = timePoints.begin();
402  auto prePos = pos++;
403 
404  for (; pos != timePoints.end(); ++prePos, ++pos)
405  {
406  arbitraryTimeGeometry->AppendNewTimeStepClone(slicedGeometry, *prePos, *pos);
407  }
408 
409  timeGeometry = arbitraryTimeGeometry;
410  }
411  }
412  }
413 
414  if (timeGeometry.IsNull())
415  { // Fallback. If no other valid time geometry has been created, create a ProportionalTimeGeometry
416  MITK_INFO << "used time geometry: " << ProportionalTimeGeometry::GetStaticNameOfClass();
418  propTimeGeometry->Initialize(slicedGeometry, image->GetDimension(3));
419  timeGeometry = propTimeGeometry;
420  }
421 
422  image->SetTimeGeometry(timeGeometry);
423 
424  buffer = nullptr;
425  MITK_INFO << "number of image components: " << image->GetPixelType().GetNumberOfComponents();
426 
427  for (auto iter = dictionary.Begin(), iterEnd = dictionary.End(); iter != iterEnd;
428  ++iter)
429  {
430  if (iter->second->GetMetaDataObjectTypeInfo() == typeid(std::string))
431  {
432  const std::string &key = iter->first;
433  std::string assumedPropertyName = key;
434  std::replace(assumedPropertyName.begin(), assumedPropertyName.end(), '_', '.');
435 
436  std::string mimeTypeName = GetMimeType()->GetName();
437 
438  // Check if there is already a info for the key and our mime type.
440  IPropertyPersistence::InfoResultType infoList = propPersistenceService->GetInfoByKey(key);
441 
442  auto predicate = [&mimeTypeName](const PropertyPersistenceInfo::ConstPointer &x) {
443  return x.IsNotNull() && x->GetMimeTypeName() == mimeTypeName;
444  };
445  auto finding = std::find_if(infoList.begin(), infoList.end(), predicate);
446 
447  if (finding == infoList.end())
448  {
449  auto predicateWild = [](const PropertyPersistenceInfo::ConstPointer &x) {
450  return x.IsNotNull() && x->GetMimeTypeName() == PropertyPersistenceInfo::ANY_MIMETYPE_NAME();
451  };
452  finding = std::find_if(infoList.begin(), infoList.end(), predicateWild);
453  }
454 
456 
457  if (finding != infoList.end())
458  {
459  assumedPropertyName = (*finding)->GetName();
460  info = *finding;
461  }
462  else
463  { // we have not found anything suitable so we generate our own info
464  auto newInfo = PropertyPersistenceInfo::New();
465  newInfo->SetNameAndKey(assumedPropertyName, key);
466  newInfo->SetMimeTypeName(PropertyPersistenceInfo::ANY_MIMETYPE_NAME());
467  info = newInfo;
468  }
469 
470  std::string value =
471  dynamic_cast<itk::MetaDataObject<std::string> *>(iter->second.GetPointer())->GetMetaDataObjectValue();
472 
473  mitk::BaseProperty::Pointer loadedProp = info->GetDeserializationFunction()(value);
474 
475  image->SetProperty(assumedPropertyName.c_str(), loadedProp);
476 
477  // Read properties should be persisted unless they are default properties
478  // which are written anyway
479  bool isDefaultKey(false);
480 
481  for (const auto &defaultKey : m_DefaultMetaDataKeys)
482  {
483  if (defaultKey.length() <= assumedPropertyName.length())
484  {
485  // does the start match the default key
486  if (assumedPropertyName.substr(0, defaultKey.length()).find(defaultKey) != std::string::npos)
487  {
488  isDefaultKey = true;
489  break;
490  }
491  }
492  }
493 
494  if (!isDefaultKey)
495  {
496  propPersistenceService->AddInfo(info);
497  }
498  }
499  }
500 
501  MITK_INFO << "...finished!";
502 
503  result.push_back(image.GetPointer());
504  return result;
505  }
506 
508  {
509  return m_ImageIO->CanReadFile(GetLocalFileName().c_str()) ? IFileReader::Supported : IFileReader::Unsupported;
510  }
511 
513  {
514  const auto *image = dynamic_cast<const mitk::Image *>(this->GetInput());
515 
516  if (image == nullptr)
517  {
518  mitkThrow() << "Cannot write non-image data";
519  }
520 
521  // Switch the current locale to "C"
522  LocaleSwitch localeSwitch("C");
523 
524  // Clone the image geometry, because we might have to change it
525  // for writing purposes
526  BaseGeometry::Pointer geometry = image->GetGeometry()->Clone();
527 
528  // Check if geometry information will be lost
529  if (image->GetDimension() == 2 && !geometry->Is2DConvertable())
530  {
531  MITK_WARN << "Saving a 2D image with 3D geometry information. Geometry information will be lost! You might "
532  "consider using Convert2Dto3DImageFilter before saving.";
533 
534  // set matrix to identity
535  mitk::AffineTransform3D::Pointer affTrans = mitk::AffineTransform3D::New();
536  affTrans->SetIdentity();
537  mitk::Vector3D spacing = geometry->GetSpacing();
538  mitk::Point3D origin = geometry->GetOrigin();
539  geometry->SetIndexToWorldTransform(affTrans);
540  geometry->SetSpacing(spacing);
541  geometry->SetOrigin(origin);
542  }
543 
544  LocalFile localFile(this);
545  const std::string path = localFile.GetFileName();
546 
547  MITK_INFO << "Writing image: " << path << std::endl;
548 
549  try
550  {
551  // Implementation of writer using itkImageIO directly. This skips the use
552  // of templated itkImageFileWriter, which saves the multiplexing on MITK side.
553 
554  const unsigned int dimension = image->GetDimension();
555  const unsigned int *const dimensions = image->GetDimensions();
556  const mitk::PixelType pixelType = image->GetPixelType();
557  const mitk::Vector3D mitkSpacing = geometry->GetSpacing();
558  const mitk::Point3D mitkOrigin = geometry->GetOrigin();
559 
560  // Due to templating in itk, we are forced to save a 4D spacing and 4D Origin,
561  // though they are not supported in MITK
562  itk::Vector<double, 4u> spacing4D;
563  spacing4D[0] = mitkSpacing[0];
564  spacing4D[1] = mitkSpacing[1];
565  spacing4D[2] = mitkSpacing[2];
566  spacing4D[3] = 1; // There is no support for a 4D spacing. However, we should have a valid value here
567 
568  itk::Vector<double, 4u> origin4D;
569  origin4D[0] = mitkOrigin[0];
570  origin4D[1] = mitkOrigin[1];
571  origin4D[2] = mitkOrigin[2];
572  origin4D[3] = 0; // There is no support for a 4D origin. However, we should have a valid value here
573 
574  // Set the necessary information for imageIO
575  m_ImageIO->SetNumberOfDimensions(dimension);
576  m_ImageIO->SetPixelType(pixelType.GetPixelType());
577  m_ImageIO->SetComponentType(pixelType.GetComponentType() < PixelComponentUserType ?
578  static_cast<itk::ImageIOBase::IOComponentType>(pixelType.GetComponentType()) :
579  itk::ImageIOBase::UNKNOWNCOMPONENTTYPE);
580  m_ImageIO->SetNumberOfComponents(pixelType.GetNumberOfComponents());
581 
582  itk::ImageIORegion ioRegion(dimension);
583 
584  for (unsigned int i = 0; i < dimension; i++)
585  {
586  m_ImageIO->SetDimensions(i, dimensions[i]);
587  m_ImageIO->SetSpacing(i, spacing4D[i]);
588  m_ImageIO->SetOrigin(i, origin4D[i]);
589 
590  mitk::Vector3D mitkDirection;
591  mitkDirection.SetVnlVector(geometry->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(i));
592  itk::Vector<double, 4u> direction4D;
593  direction4D[0] = mitkDirection[0];
594  direction4D[1] = mitkDirection[1];
595  direction4D[2] = mitkDirection[2];
596 
597  // MITK only supports a 3x3 direction matrix. Due to templating in itk, however, we must
598  // save a 4x4 matrix for 4D images. in this case, add an homogneous component to the matrix.
599  if (i == 3)
600  {
601  direction4D[3] = 1; // homogenous component
602  }
603  else
604  {
605  direction4D[3] = 0;
606  }
607  vnl_vector<double> axisDirection(dimension);
608  for (unsigned int j = 0; j < dimension; j++)
609  {
610  axisDirection[j] = direction4D[j] / spacing4D[i];
611  }
612  m_ImageIO->SetDirection(i, axisDirection);
613 
614  ioRegion.SetSize(i, image->GetLargestPossibleRegion().GetSize(i));
615  ioRegion.SetIndex(i, image->GetLargestPossibleRegion().GetIndex(i));
616  }
617 
618  // use compression if available
619  m_ImageIO->UseCompressionOn();
620 
621  m_ImageIO->SetIORegion(ioRegion);
622  m_ImageIO->SetFileName(path);
623 
624  // Handle time geometry
625  const auto *arbitraryTG = dynamic_cast<const ArbitraryTimeGeometry *>(image->GetTimeGeometry());
626  if (arbitraryTG)
627  {
628  itk::EncapsulateMetaData<std::string>(m_ImageIO->GetMetaDataDictionary(),
631 
632  auto metaTimePoints = ConvertTimePointListToMetaDataObject(arbitraryTG);
633  m_ImageIO->GetMetaDataDictionary().Set(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS, metaTimePoints);
634  }
635 
636  // Handle properties
637  mitk::PropertyList::Pointer imagePropertyList = image->GetPropertyList();
638 
639  for (const auto &property : *imagePropertyList->GetMap())
640  {
642  IPropertyPersistence::InfoResultType infoList = propPersistenceService->GetInfo(property.first, GetMimeType()->GetName(), true);
643 
644  if (infoList.empty())
645  {
646  continue;
647  }
648 
649  std::string value = infoList.front()->GetSerializationFunction()(property.second);
650 
652  {
653  continue;
654  }
655 
656  std::string key = infoList.front()->GetKey();
657 
658  itk::EncapsulateMetaData<std::string>(m_ImageIO->GetMetaDataDictionary(), key, value);
659  }
660 
661  ImageReadAccessor imageAccess(image);
662  LocaleSwitch localeSwitch2("C");
663  m_ImageIO->Write(imageAccess.GetData());
664  }
665  catch (const std::exception &e)
666  {
667  mitkThrow() << e.what();
668  }
669  }
670 
672  {
673  // Check if the image dimension is supported
674  const auto *image = dynamic_cast<const Image *>(this->GetInput());
675  if (image == nullptr)
676  {
677  // We cannot write a null object, DUH!
679  }
680 
681  if (!m_ImageIO->SupportsDimension(image->GetDimension()))
682  {
683  // okay, dimension is not supported. We have to look at a special case:
684  // 3D-Image with one slice. We can treat that as a 2D image.
685  if ((image->GetDimension() == 3) && (image->GetSlicedGeometry()->GetSlices() == 1))
686  return IFileWriter::Supported;
687  else
689  }
690 
691  // Check if geometry information will be lost
692  if (image->GetDimension() == 2 && !image->GetGeometry()->Is2DConvertable())
693  {
695  }
696  return IFileWriter::Supported;
697  }
698 
699  ItkImageIO *ItkImageIO::IOClone() const { return new ItkImageIO(*this); }
701  {
702  this->m_DefaultMetaDataKeys.push_back("NRRD.space");
703  this->m_DefaultMetaDataKeys.push_back("NRRD.kinds");
704  this->m_DefaultMetaDataKeys.push_back(PROPERTY_NAME_TIMEGEOMETRY_TYPE);
705  this->m_DefaultMetaDataKeys.push_back(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS);
706  this->m_DefaultMetaDataKeys.push_back("ITK.InputFilterName");
707  }
708 }
static const char * GetStaticNameOfClass()
virtual TimeStepType CountTimeSteps() const =0
Returns the number of time steps.
void SetSpacing(const mitk::Vector3D &aSpacing, bool enforceSetSpacing=false)
Set the spacing (m_Spacing).
#define MITK_INFO
Definition: mitkLogMacros.h:18
#define MITK_ERROR
Definition: mitkLogMacros.h:20
const char *const PROPERTY_KEY_TIMEGEOMETRY_TYPE
double ScalarType
vcl_size_t GetNumberOfComponents() const
Get the number of components of which each element consists.
std::string GetName() const
Returns the unique name for the MimeType.
const char *const PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS
ItkImageIO(itk::ImageIOBase::Pointer imageIO)
#define MITK_DEBUG
Definition: mitkLogMacros.h:22
STL namespace.
DataCollection - Class to facilitate loading/accessing structured data.
Point3D GetCornerPoint(int id) const
Get the position of the corner number id (in world coordinates)
MITKCORE_EXPORT itk::MetaDataObjectBase::Pointer ConvertTimePointListToMetaDataObject(const mitk::TimeGeometry *timeGeometry)
virtual void InitializeEvenlySpaced(mitk::PlaneGeometry *geometry2D, unsigned int slices)
Completely initialize this instance as evenly-spaced with slices parallel to the provided PlaneGeomet...
static void info(const char *fmt,...)
Definition: svm.cpp:86
static const int PixelComponentUserType
void SetMimeType(const CustomMimeType &mimeType)
The CustomMimeType class represents a custom mime-type which may be registered as a service object...
int GetComponentType() const
Get the component type (the scalar (!) type). Each element may contain m_NumberOfComponents (more tha...
std::vector< std::string > GetExtensions() const
Returns all extensions that this MimeType can handle.
void SetRanking(int ranking)
Set the service ranking for this file writer.
std::string GetLocalFileName() const
Get a local file name for reading.
void SetMimeTypePrefix(const std::string &prefix)
ConfidenceLevel GetWriterConfidenceLevel() const override
void Write() override
Write the base data to the specified location or output stream.
MITKCORE_EXPORT mitk::PixelType MakePixelType(vtkImageData *vtkimagedata)
deduct the PixelType for a given vtk image
virtual void FixUpCustomMimeTypeName(const std::string &imageIOName, CustomMimeType &customMimeType)
static std::string DEFAULT_BASE_NAME()
#define MITK_WARN
Definition: mitkLogMacros.h:19
virtual TimeBounds GetTimeBounds() const =0
Get the time bounds (in ms)
ConfidenceLevel
A confidence level describing the confidence of the reader or writer in handling the given data...
Definition: mitkIFileIO.h:45
Convenience class to temporarily change the current locale.
static IPropertyPersistence * GetPropertyPersistence(us::ModuleContext *context=us::GetModuleContext())
Get an IPropertyPersistence instance.
ConfidenceLevel GetReaderConfidenceLevel() const override
std::pair< us::ServiceRegistration< IFileReader >, us::ServiceRegistration< IFileWriter > > RegisterService(us::ModuleContext *context=us::GetModuleContext())
#define mitkThrow()
void SetOrigin(const Point3D &origin)
Set the origin, i.e. the upper-left corner of the plane.
virtual std::vector< std::string > FixUpImageIOExtensions(const std::string &imageIOName)
const BaseData * GetInput() const override
Get the input data set via SetInput().
Image class for storing images.
Definition: mitkImage.h:72
void SetReaderDescription(const std::string &description)
mitk::ScalarType TimePointType
A local file representation for streams.
std::vcl_size_t TimeStepType
static const std::string VALUE_CANNOT_BE_CONVERTED_TO_STRING
Default return value if a property which can not be returned as string.
itk::ImageIOBase::IOPixelType GetPixelType() const
mitk::Image::Pointer image
virtual void InitializeDefaultMetaDataKeys()
void AddExtension(const std::string &extension)
static Pointer New()
Describes the geometry of a data object consisting of slices.
void SetRanking(int ranking)
Set the service ranking for this file reader.
void SetWriterDescription(const std::string &description)
void SetCategory(const std::string &category)
std::vector< itk::SmartPointer< BaseData > > Read() override
Reads a path or stream and creates a list of BaseData objects.
const char *const PROPERTY_NAME_TIMEGEOMETRY_TYPE
static const char * replace[]
This is a dictionary to replace long names of classes, modules, etc. to shorter versions in the conso...
void SetMimeType(const CustomMimeType &mimeType)
static std::string GetName(std::string fileName, std::string suffix)
MITKCORE_EXPORT std::vector< TimePointType > ConvertMetaDataObjectToTimePointList(const itk::MetaDataObjectBase *data)
A RAII helper class for core service objects.
Describes a two-dimensional, rectangular plane.
std::list< PropertyPersistenceInfo::ConstPointer > InfoResultType
ImageReadAccessor class to get locked read access for a particular image part.
static mitk::PlanarFigure::Pointer Clone(mitk::PlanarFigure::Pointer original)
Abstract class for implementing a reader and writer.
std::string GetMimeTypePrefix() const
const CustomMimeType * GetMimeType() const
const char *const PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS
Class for defining the data type of pixels.
Definition: mitkPixelType.h:51
mitk::AffineTransform3D * GetIndexToWorldTransform()
Get the transformation used to convert from index to world coordinates.
void SetName(const std::string &name)