Medical Imaging Interaction Toolkit  2016.11.0
Medical Imaging Interaction Toolkit
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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.