Medical Imaging Interaction Toolkit  2018.4.99-389bf124
Medical Imaging Interaction Toolkit
mitkLabelSetImageIO.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 #ifndef __mitkLabelSetImageWriter__cpp
14 #define __mitkLabelSetImageWriter__cpp
15 
16 #include "mitkLabelSetImageIO.h"
18 #include "mitkIOMimeTypes.h"
19 #include "mitkImageAccessByItk.h"
20 #include "mitkLabelSetIOHelper.h"
22 #include <mitkLocaleSwitch.h>
23 
24 // itk
25 #include "itkImageFileReader.h"
26 #include "itkImageFileWriter.h"
27 #include "itkMetaDataDictionary.h"
28 #include "itkMetaDataObject.h"
29 #include "itkNrrdImageIO.h"
30 
31 namespace mitk
32 {
34  : AbstractFileIO(LabelSetImage::GetStaticNameOfClass(), IOMimeTypes::NRRD_MIMETYPE(), "MITK Multilabel Image")
35  {
38  this->RegisterService();
39  }
40 
42  {
44  return Unsupported;
45  const auto *input = static_cast<const LabelSetImage *>(this->GetInput());
46  if (input)
47  return Supported;
48  else
49  return Unsupported;
50  }
51 
53  {
55 
56  auto input = dynamic_cast<const LabelSetImage *>(this->GetInput());
57 
58  mitk::LocaleSwitch localeSwitch("C");
59 
61 
62  // image write
63  if (inputVector.IsNull())
64  {
65  mitkThrow() << "Cannot write non-image data";
66  }
67 
68  itk::NrrdImageIO::Pointer nrrdImageIo = itk::NrrdImageIO::New();
69 
70  // Clone the image geometry, because we might have to change it
71  // for writing purposes
72  BaseGeometry::Pointer geometry = inputVector->GetGeometry()->Clone();
73 
74  // Check if geometry information will be lost
75  if (inputVector->GetDimension() == 2 && !geometry->Is2DConvertable())
76  {
77  MITK_WARN << "Saving a 2D image with 3D geometry information. Geometry information will be lost! You might "
78  "consider using Convert2Dto3DImageFilter before saving.";
79 
80  // set matrix to identity
81  mitk::AffineTransform3D::Pointer affTrans = mitk::AffineTransform3D::New();
82  affTrans->SetIdentity();
83  mitk::Vector3D spacing = geometry->GetSpacing();
84  mitk::Point3D origin = geometry->GetOrigin();
85  geometry->SetIndexToWorldTransform(affTrans);
86  geometry->SetSpacing(spacing);
87  geometry->SetOrigin(origin);
88  }
89 
90  LocalFile localFile(this);
91  const std::string path = localFile.GetFileName();
92 
93  MITK_INFO << "Writing image: " << path << std::endl;
94 
95  try
96  {
97  // Implementation of writer using itkImageIO directly. This skips the use
98  // of templated itkImageFileWriter, which saves the multiplexing on MITK side.
99 
100  const unsigned int dimension = inputVector->GetDimension();
101  const unsigned int *const dimensions = inputVector->GetDimensions();
102  const mitk::PixelType pixelType = inputVector->GetPixelType();
103  const mitk::Vector3D mitkSpacing = geometry->GetSpacing();
104  const mitk::Point3D mitkOrigin = geometry->GetOrigin();
105 
106  // Due to templating in itk, we are forced to save a 4D spacing and 4D Origin,
107  // though they are not supported in MITK
108  itk::Vector<double, 4u> spacing4D;
109  spacing4D[0] = mitkSpacing[0];
110  spacing4D[1] = mitkSpacing[1];
111  spacing4D[2] = mitkSpacing[2];
112  spacing4D[3] = 1; // There is no support for a 4D spacing. However, we should have a valid value here
113 
114  itk::Vector<double, 4u> origin4D;
115  origin4D[0] = mitkOrigin[0];
116  origin4D[1] = mitkOrigin[1];
117  origin4D[2] = mitkOrigin[2];
118  origin4D[3] = 0; // There is no support for a 4D origin. However, we should have a valid value here
119 
120  // Set the necessary information for imageIO
121  nrrdImageIo->SetNumberOfDimensions(dimension);
122  nrrdImageIo->SetPixelType(pixelType.GetPixelType());
123  nrrdImageIo->SetComponentType(pixelType.GetComponentType() < PixelComponentUserType ?
124  static_cast<itk::ImageIOBase::IOComponentType>(pixelType.GetComponentType()) :
125  itk::ImageIOBase::UNKNOWNCOMPONENTTYPE);
126  nrrdImageIo->SetNumberOfComponents(pixelType.GetNumberOfComponents());
127 
128  itk::ImageIORegion ioRegion(dimension);
129 
130  for (unsigned int i = 0; i < dimension; i++)
131  {
132  nrrdImageIo->SetDimensions(i, dimensions[i]);
133  nrrdImageIo->SetSpacing(i, spacing4D[i]);
134  nrrdImageIo->SetOrigin(i, origin4D[i]);
135 
136  mitk::Vector3D mitkDirection;
137  mitkDirection.SetVnlVector(geometry->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(i));
138  itk::Vector<double, 4u> direction4D;
139  direction4D[0] = mitkDirection[0];
140  direction4D[1] = mitkDirection[1];
141  direction4D[2] = mitkDirection[2];
142 
143  // MITK only supports a 3x3 direction matrix. Due to templating in itk, however, we must
144  // save a 4x4 matrix for 4D images. in this case, add an homogneous component to the matrix.
145  if (i == 3)
146  {
147  direction4D[3] = 1; // homogenous component
148  }
149  else
150  {
151  direction4D[3] = 0;
152  }
153  vnl_vector<double> axisDirection(dimension);
154  for (unsigned int j = 0; j < dimension; j++)
155  {
156  axisDirection[j] = direction4D[j] / spacing4D[i];
157  }
158  nrrdImageIo->SetDirection(i, axisDirection);
159 
160  ioRegion.SetSize(i, inputVector->GetLargestPossibleRegion().GetSize(i));
161  ioRegion.SetIndex(i, inputVector->GetLargestPossibleRegion().GetIndex(i));
162  }
163 
164  // use compression if available
165  nrrdImageIo->UseCompressionOn();
166 
167  nrrdImageIo->SetIORegion(ioRegion);
168  nrrdImageIo->SetFileName(path);
169 
170  // label set specific meta data
171  char keybuffer[512];
172  char valbuffer[512];
173 
174  sprintf(keybuffer, "modality");
175  sprintf(valbuffer, "org.mitk.image.multilabel");
176  itk::EncapsulateMetaData<std::string>(
177  nrrdImageIo->GetMetaDataDictionary(), std::string(keybuffer), std::string(valbuffer));
178 
179  sprintf(keybuffer, "layers");
180  sprintf(valbuffer, "%1d", input->GetNumberOfLayers());
181  itk::EncapsulateMetaData<std::string>(
182  nrrdImageIo->GetMetaDataDictionary(), std::string(keybuffer), std::string(valbuffer));
183 
184  for (unsigned int layerIdx = 0; layerIdx < input->GetNumberOfLayers(); layerIdx++)
185  {
186  sprintf(keybuffer, "layer_%03u", layerIdx); // layer idx
187  sprintf(valbuffer, "%1u", input->GetNumberOfLabels(layerIdx)); // number of labels for the layer
188  itk::EncapsulateMetaData<std::string>(
189  nrrdImageIo->GetMetaDataDictionary(), std::string(keybuffer), std::string(valbuffer));
190 
191  auto iter = input->GetLabelSet(layerIdx)->IteratorConstBegin();
192  unsigned int count(0);
193  while (iter != input->GetLabelSet(layerIdx)->IteratorConstEnd())
194  {
195  std::unique_ptr<TiXmlDocument> document;
196  document.reset(new TiXmlDocument());
197 
198  auto *decl = new TiXmlDeclaration("1.0", "", ""); // TODO what to write here? encoding? etc....
199  document->LinkEndChild(decl);
200  TiXmlElement *labelElem = mitk::LabelSetIOHelper::GetLabelAsTiXmlElement(iter->second);
201  document->LinkEndChild(labelElem);
202  TiXmlPrinter printer;
203  printer.SetIndent("");
204  printer.SetLineBreak("");
205 
206  document->Accept(&printer);
207 
208  sprintf(keybuffer, "org.mitk.label_%03u_%05u", layerIdx, count);
209  itk::EncapsulateMetaData<std::string>(
210  nrrdImageIo->GetMetaDataDictionary(), std::string(keybuffer), printer.Str());
211  ++iter;
212  ++count;
213  }
214  }
215  // end label set specific meta data
216 
217  ImageReadAccessor imageAccess(inputVector);
218  nrrdImageIo->Write(imageAccess.GetData());
219  }
220  catch (const std::exception &e)
221  {
222  mitkThrow() << e.what();
223  }
224  // end image write
225  }
226 
228  {
230  return Unsupported;
231  const std::string fileName = this->GetLocalFileName();
232  itk::NrrdImageIO::Pointer io = itk::NrrdImageIO::New();
233  io->SetFileName(fileName);
234  io->ReadImageInformation();
235 
236  itk::MetaDataDictionary imgMetaDataDictionary = io->GetMetaDataDictionary();
237  std::string value("");
238  itk::ExposeMetaData<std::string>(imgMetaDataDictionary, "modality", value);
239  if (value.compare("org.mitk.image.multilabel") == 0)
240  {
241  return Supported;
242  }
243  else
244  return Unsupported;
245  }
246 
247  std::vector<BaseData::Pointer> LabelSetImageIO::Read()
248  {
249  mitk::LocaleSwitch localeSwitch("C");
250 
251  // begin regular image loading, adapted from mitkItkImageIO
252  itk::NrrdImageIO::Pointer nrrdImageIO = itk::NrrdImageIO::New();
254 
255  const unsigned int MINDIM = 2;
256  const unsigned int MAXDIM = 4;
257 
258  const std::string path = this->GetLocalFileName();
259 
260  MITK_INFO << "loading " << path << " via itk::ImageIOFactory... " << std::endl;
261 
262  // Check to see if we can read the file given the name or prefix
263  if (path.empty())
264  {
265  mitkThrow() << "Empty filename in mitk::ItkImageIO ";
266  }
267 
268  // Got to allocate space for the image. Determine the characteristics of
269  // the image.
270  nrrdImageIO->SetFileName(path);
271  nrrdImageIO->ReadImageInformation();
272 
273  unsigned int ndim = nrrdImageIO->GetNumberOfDimensions();
274  if (ndim < MINDIM || ndim > MAXDIM)
275  {
276  MITK_WARN << "Sorry, only dimensions 2, 3 and 4 are supported. The given file has " << ndim
277  << " dimensions! Reading as 4D.";
278  ndim = MAXDIM;
279  }
280 
281  itk::ImageIORegion ioRegion(ndim);
282  itk::ImageIORegion::SizeType ioSize = ioRegion.GetSize();
283  itk::ImageIORegion::IndexType ioStart = ioRegion.GetIndex();
284 
285  unsigned int dimensions[MAXDIM];
286  dimensions[0] = 0;
287  dimensions[1] = 0;
288  dimensions[2] = 0;
289  dimensions[3] = 0;
290 
291  ScalarType spacing[MAXDIM];
292  spacing[0] = 1.0f;
293  spacing[1] = 1.0f;
294  spacing[2] = 1.0f;
295  spacing[3] = 1.0f;
296 
297  Point3D origin;
298  origin.Fill(0);
299 
300  unsigned int i;
301  for (i = 0; i < ndim; ++i)
302  {
303  ioStart[i] = 0;
304  ioSize[i] = nrrdImageIO->GetDimensions(i);
305  if (i < MAXDIM)
306  {
307  dimensions[i] = nrrdImageIO->GetDimensions(i);
308  spacing[i] = nrrdImageIO->GetSpacing(i);
309  if (spacing[i] <= 0)
310  spacing[i] = 1.0f;
311  }
312  if (i < 3)
313  {
314  origin[i] = nrrdImageIO->GetOrigin(i);
315  }
316  }
317 
318  ioRegion.SetSize(ioSize);
319  ioRegion.SetIndex(ioStart);
320 
321  MITK_INFO << "ioRegion: " << ioRegion << std::endl;
322  nrrdImageIO->SetIORegion(ioRegion);
323  void *buffer = new unsigned char[nrrdImageIO->GetImageSizeInBytes()];
324  nrrdImageIO->Read(buffer);
325 
326  image->Initialize(MakePixelType(nrrdImageIO), ndim, dimensions);
327  image->SetImportChannel(buffer, 0, Image::ManageMemory);
328 
329  // access direction of itk::Image and include spacing
330  mitk::Matrix3D matrix;
331  matrix.SetIdentity();
332  unsigned int j, itkDimMax3 = (ndim >= 3 ? 3 : ndim);
333  for (i = 0; i < itkDimMax3; ++i)
334  for (j = 0; j < itkDimMax3; ++j)
335  matrix[i][j] = nrrdImageIO->GetDirection(j)[i];
336 
337  // re-initialize PlaneGeometry with origin and direction
338  PlaneGeometry *planeGeometry = image->GetSlicedGeometry(0)->GetPlaneGeometry(0);
339  planeGeometry->SetOrigin(origin);
340  planeGeometry->GetIndexToWorldTransform()->SetMatrix(matrix);
341 
342  // re-initialize SlicedGeometry3D
343  SlicedGeometry3D *slicedGeometry = image->GetSlicedGeometry(0);
344  slicedGeometry->InitializeEvenlySpaced(planeGeometry, image->GetDimension(2));
345  slicedGeometry->SetSpacing(spacing);
346 
347  MITK_INFO << slicedGeometry->GetCornerPoint(false, false, false);
348  MITK_INFO << slicedGeometry->GetCornerPoint(true, true, true);
349 
350  // re-initialize TimeGeometry
352  timeGeometry->Initialize(slicedGeometry, image->GetDimension(3));
353  image->SetTimeGeometry(timeGeometry);
354 
355  buffer = nullptr;
356  MITK_INFO << "number of image components: " << image->GetPixelType().GetNumberOfComponents() << std::endl;
357 
358  const itk::MetaDataDictionary &dictionary = nrrdImageIO->GetMetaDataDictionary();
359  for (auto iter = dictionary.Begin(), iterEnd = dictionary.End(); iter != iterEnd;
360  ++iter)
361  {
362  std::string key = std::string("meta.") + iter->first;
363  if (iter->second->GetMetaDataObjectTypeInfo() == typeid(std::string))
364  {
365  std::string value =
366  dynamic_cast<itk::MetaDataObject<std::string> *>(iter->second.GetPointer())->GetMetaDataObjectValue();
367  image->SetProperty(key.c_str(), mitk::StringProperty::New(value));
368  }
369  }
370 
371  // end regular image loading
372 
374 
375  // get labels and add them as properties to the image
376  char keybuffer[256];
377 
378  unsigned int numberOfLayers = GetIntByKey(dictionary, "layers");
379  std::string _xmlStr;
380  mitk::Label::Pointer label;
381 
382  for (unsigned int layerIdx = 0; layerIdx < numberOfLayers; layerIdx++)
383  {
384  sprintf(keybuffer, "layer_%03u", layerIdx);
385  int numberOfLabels = GetIntByKey(dictionary, keybuffer);
386 
387  mitk::LabelSet::Pointer labelSet = mitk::LabelSet::New();
388 
389  for (int labelIdx = 0; labelIdx < numberOfLabels; labelIdx++)
390  {
391  TiXmlDocument doc;
392  sprintf(keybuffer, "label_%03u_%05d", layerIdx, labelIdx);
393  _xmlStr = GetStringByKey(dictionary, keybuffer);
394  doc.Parse(_xmlStr.c_str());
395 
396  TiXmlElement *labelElem = doc.FirstChildElement("Label");
397  if (labelElem == nullptr)
398  mitkThrow() << "Error parsing NRRD header for mitk::LabelSetImage IO";
399 
401 
402  if (label->GetValue() == 0) // set exterior label is needed to hold exterior information
403  output->SetExteriorLabel(label);
404  labelSet->AddLabel(label);
405  labelSet->SetLayer(layerIdx);
406  }
407  output->AddLabelSetToLayer(layerIdx, labelSet);
408  }
409 
410  MITK_INFO << "...finished!" << std::endl;
411 
412  std::vector<BaseData::Pointer> result;
413  result.push_back(output.GetPointer());
414  return result;
415  }
416 
417  int LabelSetImageIO::GetIntByKey(const itk::MetaDataDictionary &dic, const std::string &str)
418  {
419  std::vector<std::string> imgMetaKeys = dic.GetKeys();
420  std::vector<std::string>::const_iterator itKey = imgMetaKeys.begin();
421  std::string metaString("");
422  for (; itKey != imgMetaKeys.end(); itKey++)
423  {
424  itk::ExposeMetaData<std::string>(dic, *itKey, metaString);
425  if (itKey->find(str.c_str()) != std::string::npos)
426  {
427  return atoi(metaString.c_str());
428  }
429  }
430  return 0;
431  }
432 
433  std::string LabelSetImageIO::GetStringByKey(const itk::MetaDataDictionary &dic, const std::string &str)
434  {
435  std::vector<std::string> imgMetaKeys = dic.GetKeys();
436  std::vector<std::string>::const_iterator itKey = imgMetaKeys.begin();
437  std::string metaString("");
438  for (; itKey != imgMetaKeys.end(); itKey++)
439  {
440  itk::ExposeMetaData<std::string>(dic, *itKey, metaString);
441  if (itKey->find(str.c_str()) != std::string::npos)
442  {
443  return metaString;
444  }
445  }
446  return metaString;
447  }
448 
449  LabelSetImageIO *LabelSetImageIO::IOClone() const { return new LabelSetImageIO(*this); }
450 } // namespace
451 
452 #endif //__mitkLabelSetImageWriter__cpp
MITKMULTILABEL_EXPORT Image::Pointer ConvertLabelSetImageToImage(LabelSetImage::ConstPointer labelSetImage)
Convert mitk::LabelSetImage to mitk::Image (itk::VectorImage)
The IOMimeTypes class.
void SetSpacing(const mitk::Vector3D &aSpacing, bool enforceSetSpacing=false)
Set the spacing (m_Spacing).
#define MITK_INFO
Definition: mitkLogMacros.h:18
static itk::SmartPointer< mitk::Label > LoadLabelFromTiXmlDocument(TiXmlElement *labelElem)
Creates a mitk::Label from a TiXmlElement.
ConfidenceLevel GetReaderConfidenceLevel() const override
double ScalarType
vcl_size_t GetNumberOfComponents() const
Get the number of components of which each element consists.
MITKMULTILABEL_EXPORT LabelSetImage::Pointer ConvertImageToLabelSetImage(Image::Pointer image)
Convert mitk::Image to mitk::LabelSetImage, templating and differentation between itk::Image and itk:...
int GetIntByKey(const itk::MetaDataDictionary &dic, const std::string &str)
std::string GetStringByKey(const itk::MetaDataDictionary &dic, const std::string &str)
DataCollection - Class to facilitate loading/accessing structured data.
Point3D GetCornerPoint(int id) const
Get the position of the corner number id (in world coordinates)
void Write() override
Write the base data to the specified location or output stream.
static Pointer New()
virtual void InitializeEvenlySpaced(mitk::PlaneGeometry *geometry2D, unsigned int slices)
Completely initialize this instance as evenly-spaced with slices parallel to the provided PlaneGeomet...
static const int PixelComponentUserType
int GetComponentType() const
Get the component type (the scalar (!) type). Each element may contain m_NumberOfComponents (more tha...
void SetRanking(int ranking)
Set the service ranking for this file writer.
std::string GetLocalFileName() const
Get a local file name for reading.
ConfidenceLevel GetReaderConfidenceLevel() const override
MITKCORE_EXPORT mitk::PixelType MakePixelType(vtkImageData *vtkimagedata)
deduct the PixelType for a given vtk image
#define MITK_WARN
Definition: mitkLogMacros.h:19
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.
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.
const BaseData * GetInput() const override
Get the input data set via SetInput().
ConfidenceLevel GetWriterConfidenceLevel() const override
A local file representation for streams.
itk::ImageIOBase::IOPixelType GetPixelType() const
mitk::Image::Pointer image
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.
static TiXmlElement * GetLabelAsTiXmlElement(Label *label)
Creates a TiXmlElement from a mitk::Label.
LabelSetImage class for handling labels and layers in a segmentation session.
ConfidenceLevel GetWriterConfidenceLevel() const override
Describes a two-dimensional, rectangular plane.
std::vector< BaseData::Pointer > Read() override
Reads a number of mitk::LabelSetImages from the file system.
ImageReadAccessor class to get locked read access for a particular image part.
Abstract class for implementing a reader and writer.
static Pointer New()
const void * GetData() const
Gives const access to the data.
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.