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
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.