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