Medical Imaging Interaction Toolkit  2018.4.99-a3d2e8fb
Medical Imaging Interaction Toolkit
mitkImageWriter.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 "mitkImageWriter.h"
14 
15 #include "mitkImage.h"
16 #include "mitkImageAccessByItk.h"
17 #include "mitkImageReadAccessor.h"
18 #include "mitkImageTimeSelector.h"
19 #include "mitkItkPictureWrite.h"
20 #include <mitkLocaleSwitch.h>
21 
22 #include <itkImageIOBase.h>
23 #include <itkImageIOFactory.h>
24 
25 mitk::ImageWriter::ImageWriter() : m_UseCompression(true)
26 {
27  this->SetNumberOfRequiredInputs(1);
28  m_MimeType = "";
30 }
31 
33 {
34 }
35 
36 void mitk::ImageWriter::SetFileName(const char *fileName)
37 {
38  if (fileName && (fileName == this->m_FileName))
39  {
40  return;
41  }
42  if (fileName)
43  {
44  this->m_FileName = fileName;
46  this->m_Extension.clear();
47  std::size_t pos = this->m_FileName.find_last_of("/\\");
48  if (pos != std::string::npos)
49  {
50  std::size_t ppos = this->m_FileName.find_first_of('.', pos);
51  if (ppos != std::string::npos)
52  {
53  this->m_FileNameWithoutExtension = this->m_FileName.substr(0, ppos);
54  this->m_Extension = this->m_FileName.substr(ppos);
55  }
56  }
57  }
58  else
59  {
60  this->m_FileName.clear();
61  this->m_FileNameWithoutExtension.clear();
62  this->m_Extension.clear();
63  }
64  this->Modified();
65 }
66 
67 void mitk::ImageWriter::SetFileName(const std::string &fileName)
68 {
69  this->SetFileName(fileName.c_str());
70 }
71 
72 void mitk::ImageWriter::SetExtension(const char *extension)
73 {
74  if (extension && (extension == this->m_Extension))
75  {
76  return;
77  }
78  if (extension)
79  {
80  this->m_Extension = extension;
82  }
83  else
84  {
85  this->m_Extension.clear();
87  }
88  this->Modified();
89 }
90 
91 void mitk::ImageWriter::SetExtension(const std::string &extension)
92 {
93  this->SetFileName(extension.c_str());
94 }
95 
97 {
98  this->m_Extension = ".mhd";
100  this->Modified();
101 }
102 
103 #include <vtkConfigure.h>
104 #include <vtkImageData.h>
105 #include <vtkXMLImageDataWriter.h>
106 static void writeVti(const char *filename, mitk::Image *image, int t = 0)
107 {
108  vtkXMLImageDataWriter *vtkwriter = vtkXMLImageDataWriter::New();
109  vtkwriter->SetFileName(filename);
110  vtkwriter->SetInputData(image->GetVtkImageData(t));
111  vtkwriter->Write();
112  vtkwriter->Delete();
113 }
114 
115 #include <itkRGBAPixel.h>
116 
117 void mitk::ImageWriter::WriteByITK(mitk::Image *image, const std::string &fileName)
118 {
119  MITK_INFO << "Writing image: " << fileName << std::endl;
120  // Pictures and picture series like .png are written via a different mechanism then volume images.
121  // So, they are still multiplexed and thus not support vector images.
122  if (fileName.find(".png") != std::string::npos || fileName.find(".tif") != std::string::npos ||
123  fileName.find(".jpg") != std::string::npos || fileName.find(".bmp") != std::string::npos)
124  {
125  try
126  {
127  // switch processing of single/multi-component images
128  if (image->GetPixelType(0).GetNumberOfComponents() == 1)
129  {
130  AccessByItk_1(image, _mitkItkPictureWrite, fileName);
131  }
132  else
133  {
137  fileName);
138  }
139  }
140  catch (itk::ExceptionObject &e)
141  {
142  std::cerr << "Caught " << e.what() << std::endl;
143  }
144  catch (std::exception &e)
145  {
146  std::cerr << "Caught std::exception " << e.what() << std::endl;
147  }
148 
149  return;
150  }
151 
152  // Implementation of writer using itkImageIO directly. This skips the use
153  // of templated itkImageFileWriter, which saves the multiplexing on MITK side.
154 
155  unsigned int dimension = image->GetDimension();
156  unsigned int *dimensions = image->GetDimensions();
157  mitk::PixelType pixelType = image->GetPixelType();
158  mitk::Vector3D mitkSpacing = image->GetGeometry()->GetSpacing();
159  mitk::Point3D mitkOrigin = image->GetGeometry()->GetOrigin();
160 
161  // Due to templating in itk, we are forced to save a 4D spacing and 4D Origin, though they are not supported in MITK
162  itk::Vector<double, 4u> spacing4D;
163  spacing4D[0] = mitkSpacing[0];
164  spacing4D[1] = mitkSpacing[1];
165  spacing4D[2] = mitkSpacing[2];
166  spacing4D[3] = 1; // There is no support for a 4D spacing. However, we should have an valid value here
167 
168  itk::Vector<double, 4u> origin4D;
169  origin4D[0] = mitkOrigin[0];
170  origin4D[1] = mitkOrigin[1];
171  origin4D[2] = mitkOrigin[2];
172  origin4D[3] = 0; // There is no support for a 4D origin. However, we should have an valid value here
173 
174  itk::ImageIOBase::Pointer imageIO =
175  itk::ImageIOFactory::CreateImageIO(fileName.c_str(), itk::ImageIOFactory::WriteMode);
176 
177  if (imageIO.IsNull())
178  {
179  itkExceptionMacro(<< "Error: Could not create itkImageIO via factory for file " << fileName);
180  }
181 
182  // Set the necessary information for imageIO
183  imageIO->SetNumberOfDimensions(dimension);
184  imageIO->SetPixelType(pixelType.GetPixelType());
185  imageIO->SetComponentType(pixelType.GetComponentType() < PixelComponentUserType ?
186  static_cast<itk::ImageIOBase::IOComponentType>(pixelType.GetComponentType()) :
187  itk::ImageIOBase::UNKNOWNCOMPONENTTYPE);
188  imageIO->SetNumberOfComponents(pixelType.GetNumberOfComponents());
189 
190  itk::ImageIORegion ioRegion(dimension);
191 
192  for (unsigned int i = 0; i < dimension; i++)
193  {
194  imageIO->SetDimensions(i, dimensions[i]);
195  imageIO->SetSpacing(i, spacing4D[i]);
196  imageIO->SetOrigin(i, origin4D[i]);
197 
198  mitk::Vector3D mitkDirection;
199  mitkDirection.SetVnlVector(
200  image->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(i));
201  itk::Vector<double, 4u> direction4D;
202  direction4D[0] = mitkDirection[0];
203  direction4D[1] = mitkDirection[1];
204  direction4D[2] = mitkDirection[2];
205 
206  // MITK only supports a 3x3 direction matrix. Due to templating in itk, however, we must
207  // save a 4x4 matrix for 4D images. in this case, add an homogneous component to the matrix.
208  if (i == 3)
209  direction4D[3] = 1; // homogenous component
210  else
211  direction4D[3] = 0;
212 
213  vnl_vector<double> axisDirection(dimension);
214  for (unsigned int j = 0; j < dimension; j++)
215  {
216  axisDirection[j] = direction4D[j] / spacing4D[i];
217  }
218  imageIO->SetDirection(i, axisDirection);
219 
220  ioRegion.SetSize(i, image->GetLargestPossibleRegion().GetSize(i));
221  ioRegion.SetIndex(i, image->GetLargestPossibleRegion().GetIndex(i));
222  }
223 
224  // use compression if available
225  imageIO->SetUseCompression(m_UseCompression);
226 
227  imageIO->SetIORegion(ioRegion);
228  imageIO->SetFileName(fileName);
229 
230  ImageReadAccessor imageAccess(image);
231  imageIO->Write(imageAccess.GetData());
232 }
233 
235 {
236  mitk::LocaleSwitch localeSwitch("C");
237 
238  if (m_FileName == "")
239  {
240  itkWarningMacro(<< "Sorry, filename has not been set!");
241  return;
242  }
243 
244  FILE *tempFile = fopen(m_FileName.c_str(), "w");
245  if (tempFile == nullptr)
246  {
247  itkExceptionMacro(<< "File location not writeable");
248  return;
249  }
250  fclose(tempFile);
251  remove(m_FileName.c_str());
252 
253  // Creating clone of input image, since i might change the geometry
254  mitk::Image::Pointer input = this->GetInput()->Clone();
255 
256  // Check if geometry information will be lost
257  if (input->GetDimension() == 2)
258  {
259  if (!input->GetGeometry()->Is2DConvertable())
260  {
261  MITK_WARN << "Saving a 2D image with 3D geometry information. Geometry information will be lost! You might "
262  "consider using Convert2Dto3DImageFilter before saving.";
263 
264  // set matrix to identity
265  mitk::AffineTransform3D::Pointer affTrans = mitk::AffineTransform3D::New();
266  affTrans->SetIdentity();
267  mitk::Vector3D spacing = input->GetGeometry()->GetSpacing();
268  mitk::Point3D origin = input->GetGeometry()->GetOrigin();
269  input->GetGeometry()->SetIndexToWorldTransform(affTrans);
270  input->GetGeometry()->SetSpacing(spacing);
271  input->GetGeometry()->SetOrigin(origin);
272  }
273  }
274 
275  bool vti = (m_Extension.find(".vti") != std::string::npos);
276 
277  // If the extension is NOT .pic and NOT .nrrd and NOT .nii and NOT .nii.gz the following block is entered
278  if (m_Extension.find(".pic") == std::string::npos && m_Extension.find(".nrrd") == std::string::npos &&
279  m_Extension.find(".nii") == std::string::npos && m_Extension.find(".nii.gz") == std::string::npos)
280  {
281  if (input->GetDimension() > 3)
282  {
283  int t, timesteps;
284 
285  timesteps = input->GetDimension(3);
287  timeSelector->SetInput(input);
288  mitk::Image::Pointer image = timeSelector->GetOutput();
289  for (t = 0; t < timesteps; ++t)
290  {
291  std::ostringstream filename;
292  timeSelector->SetTimeNr(t);
293  timeSelector->Update();
294  if (input->GetTimeGeometry()->IsValidTimeStep(t))
295  {
296  const mitk::TimeBounds &timebounds = input->GetTimeGeometry()->GetTimeBounds(t);
297  filename << m_FileNameWithoutExtension << "_S" << std::setprecision(0) << timebounds[0] << "_E"
298  << std::setprecision(0) << timebounds[1] << "_T" << t << m_Extension;
299  }
300  else
301  {
302  itkWarningMacro(<< "Error on write: TimeGeometry invalid of image " << filename.str() << ".");
303  filename << m_FileNameWithoutExtension << "_T" << t << m_Extension;
304  }
305  if (vti)
306  {
307  writeVti(filename.str().c_str(), input, t);
308  }
309  else
310  {
311  WriteByITK(image, filename.str());
312  }
313  }
314  }
315  else if (vti)
316  {
317  writeVti(m_FileName.c_str(), input);
318  }
319  else
320  {
321  WriteByITK(input, m_FileName);
322  }
323  }
324  else
325  {
326  // use the PicFileWriter for the .pic data type
327  if (m_Extension.find(".pic") != std::string::npos)
328  {
329  /* PicFileWriter::Pointer picWriter = PicFileWriter::New();
330  size_t found;
331  found = m_FileName.find( m_Extension ); // !!! HAS to be at the very end of the filename (not somewhere in the middle)
332  if( m_FileName.length() > 3 && found != m_FileName.length() - 4 )
333  {
334  //if Extension not in Filename
335  std::ostringstream filename;
336  filename << m_FileName.c_str() << m_Extension;
337  picWriter->SetFileName( filename.str().c_str() );
338  }
339  else
340  {
341  picWriter->SetFileName( m_FileName.c_str() );
342  }
343  picWriter->SetInputImage( input );
344  picWriter->Write();
345  */ }
346 
347  // use the ITK .nrrd Image writer
348  if (m_Extension.find(".nrrd") != std::string::npos || m_Extension.find(".nii") != std::string::npos ||
349  m_Extension.find(".nii.gz") != std::string::npos)
350  {
351  WriteByITK(input, this->m_FileName);
352  }
353  }
354  m_MimeType = "application/MITK.Pic";
355 }
356 
358 {
359  if (input)
360  {
361  return this->CanWriteBaseDataType(input->GetData());
362  }
363  return false;
364 }
365 
367 {
368  if (input && CanWriteDataType(input))
369  this->ProcessObject::SetNthInput(0, dynamic_cast<mitk::Image *>(input->GetData()));
370 }
371 
373 {
374  return m_MimeType;
375 }
376 
378 {
379  std::vector<std::string> possibleFileExtensions;
380  possibleFileExtensions.push_back(".pic");
381  possibleFileExtensions.push_back(".pic.gz");
382  possibleFileExtensions.push_back(".bmp");
383  possibleFileExtensions.push_back(".dcm");
384  possibleFileExtensions.push_back(".DCM");
385  possibleFileExtensions.push_back(".dicom");
386  possibleFileExtensions.push_back(".DICOM");
387  possibleFileExtensions.push_back(".gipl");
388  possibleFileExtensions.push_back(".gipl.gz");
389  possibleFileExtensions.push_back(".mha");
390  possibleFileExtensions.push_back(".nii");
391  possibleFileExtensions.push_back(".nii.gz");
392  possibleFileExtensions.push_back(".nrrd");
393  possibleFileExtensions.push_back(".nhdr");
394  possibleFileExtensions.push_back(".png");
395  possibleFileExtensions.push_back(".PNG");
396  possibleFileExtensions.push_back(".spr");
397  possibleFileExtensions.push_back(".mhd");
398  possibleFileExtensions.push_back(".vtk");
399  possibleFileExtensions.push_back(".vti");
400  possibleFileExtensions.push_back(".hdr");
401  possibleFileExtensions.push_back(".img");
402  possibleFileExtensions.push_back(".img.gz");
403  possibleFileExtensions.push_back(".png");
404  possibleFileExtensions.push_back(".tif");
405  possibleFileExtensions.push_back(".jpg");
406  return possibleFileExtensions;
407 }
408 
410 {
412 }
413 
415 {
416  return m_Extension;
417 }
418 
420 {
421  this->ProcessObject::SetNthInput(0, image);
422 }
423 
425 {
426  if (this->GetNumberOfInputs() < 1)
427  {
428  return nullptr;
429  }
430  else
431  {
432  return static_cast<const mitk::Image *>(this->ProcessObject::GetInput(0));
433  }
434 }
435 
437 {
438  return "Image.nrrd";
439 }
440 
442 {
443  return "Nearly Raw Raster Data (*.nrrd);;"
444  "NIfTI format (*.nii *.nii.gz);;"
445  "VTK Image Data Files (*.vti);;"
446  "NRRD with detached header (*.nhdr);;"
447  "Analyze Format (*.hdr);;"
448  "MetaImage (*.mhd);;"
449  "Sets of 2D slices (*.png *.tiff *.jpg *.jpeg *.bmp);;"
450  "DICOM (*.dcm *.DCM *.dicom *.DICOM);;"
451  "UMDS GIPL Format Files (*.gipl *.gipl.gz)";
452 }
453 
455 {
456  return ".nrrd";
457 }
458 
460 {
461  return dynamic_cast<mitk::Image *>(data.GetPointer());
462 }
463 
465 {
466  if (this->CanWriteBaseDataType(data))
467  {
468  this->SetInput(dynamic_cast<mitk::Image *>(data.GetPointer()));
469  this->Update();
470  }
471 }
472 
473 void mitk::ImageWriter::SetUseCompression(bool useCompression)
474 {
475  m_UseCompression = useCompression;
476 }
Pointer Clone() const
void SetDefaultExtension()
Set the extension to be added to the filename to the default.
itk::FixedArray< ScalarType, 2 > TimeBounds
Standard typedef for time-bounds.
#define MITK_INFO
Definition: mitkLogMacros.h:18
const mitk::PixelType GetPixelType(int n=0) const
Returns the PixelType of channel n.
Definition: mitkImage.cpp:101
vcl_size_t GetNumberOfComponents() const
Get the number of components of which each element consists.
unsigned int * GetDimensions() const
Get the sizes of all dimensions as an integer-array.
Definition: mitkImage.cpp:1309
#define MITK_ACCESSBYITK_PIXEL_TYPES_SEQ
Definition: mitkConfig.h:26
const mitk::Image * GetInput()
static void Update(vtkPolyData *)
Definition: mitkSurface.cpp:31
std::string GetSupportedBaseData() const override
virtual void WriteByITK(mitk::Image *image, const std::string &fileName)
virtual vtkImageData * GetVtkImageData(int t=0, int n=0)
Get a volume at a specific time t of channel n as a vtkImageData.
Definition: mitkImage.cpp:217
static void writeVti(const char *filename, mitk::Image *image, int t=0)
std::vector< std::string > GetPossibleFileExtensions() override
Return the possible file extensions for the data type associated with the writer. ...
std::string m_MimeType
std::string GetWritenMIMEType() override
Return the MimeType of the saved File.
#define AccessByItk_1(mitkImage, itkImageTypeFunction, arg1)
static const int PixelComponentUserType
void SetFileName(const char *fileName) override
#define AccessFixedPixelTypeByItk_1(mitkImage, itkImageTypeFunction, pixelTypeSeq, arg1)
void DoWrite(BaseData::Pointer data) override
std::vector< mitk::TimePointType > timebounds
int GetComponentType() const
Get the component type (the scalar (!) type). Each element may contain m_NumberOfComponents (more tha...
static const char * GetStaticNameOfClass()
Definition: mitkImage.h:84
BaseData * GetData() const
Get the data object (instance of BaseData, e.g., an Image) managed by this DataNode.
#define MITK_WARN
Definition: mitkLogMacros.h:19
unsigned int GetDimension() const
Get dimension of the image.
Definition: mitkImage.cpp:106
Convenience class to temporarily change the current locale.
bool CanWriteBaseDataType(BaseData::Pointer data) override
bool CanWriteDataType(DataNode *) override
Check if the Writer can write the Content of the.
const char * GetFileDialogPattern() override
void GenerateData() override
Image class for storing images.
Definition: mitkImage.h:72
const char * GetDefaultExtension() override
itk::ImageIOBase::IOPixelType GetPixelType() const
mitk::Image::Pointer image
const Point3D GetOrigin() const
Get the origin, e.g. the upper-left corner of the plane.
const mitk::Vector3D GetSpacing() const
Get the spacing (size of a pixel).
void SetInput(mitk::Image *input)
const RegionType & GetLargestPossibleRegion() const
std::string m_FileName
void _mitkItkPictureWriteComposite(itk::Image< TPixel, VImageDimension > *itkImage, const std::string &fileName)
ITK-Like method to be called for writing an image.
void SetUseCompression(bool useCompression)
void _mitkItkPictureWrite(itk::Image< TPixel, VImageDimension > *itkImage, const std::string &fileName)
ITK-Like method to be called for writing an single-component image using the AccessByItk Macros...
virtual void SetExtension(const char *extension)
Explicitly set the extension to be added to the filename.
std::string m_FileNameWithoutExtension
ImageReadAccessor class to get locked read access for a particular image part.
mitk::BaseGeometry * GetGeometry(int t=0) const
Return the geometry, which is a TimeGeometry, of the data as non-const pointer.
Definition: mitkBaseData.h:138
std::string m_Extension
const char * GetDefaultFilename() override
const void * GetData() const
Gives const access to the data.
Class for nodes of the DataTree.
Definition: mitkDataNode.h:57
static Pointer New()
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.
#define MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES_SEQ
Definition: mitkConfig.h:24
std::string GetFileExtension() override
Return the extension to be added to the filename.