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