Medical Imaging Interaction Toolkit  2016.11.0
Medical Imaging Interaction Toolkit
mitkItkImageIOTest.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 "mitkException.h"
19 #include <mitkTestFixture.h>
20 #include <mitkTestingMacros.h>
21 
22 #include "mitkIOUtil.h"
23 #include "mitkITKImageImport.h"
24 #include <mitkExtractSliceFilter.h>
25 
26 #include "itksys/SystemTools.hxx"
27 #include <itkImageRegionIterator.h>
28 
29 #include <fstream>
30 #include <iostream>
31 
32 #ifdef WIN32
33 #include "process.h"
34 #else
35 #include <unistd.h>
36 #endif
37 
38 class mitkItkImageIOTestSuite : public mitk::TestFixture
39 {
40  CPPUNIT_TEST_SUITE(mitkItkImageIOTestSuite);
41  MITK_TEST(TestImageWriterJpg);
42  MITK_TEST(TestImageWriterPng1);
43  MITK_TEST(TestImageWriterPng2);
44  MITK_TEST(TestImageWriterPng3);
45  MITK_TEST(TestImageWriterSimple);
46  MITK_TEST(TestWrite3DImageWithOnePlane);
47  MITK_TEST(TestWrite3DImageWithTwoPlanes);
48  MITK_TEST(TestWrite3DplusT_ArbitraryTG);
49  MITK_TEST(TestWrite3DplusT_ProportionalTG);
50  CPPUNIT_TEST_SUITE_END();
51 
52 public:
53  void setUp() override {}
54  void tearDown() override {}
55  void TestImageWriterJpg() { TestImageWriter("NrrdWritingTestImage.jpg"); }
56  void TestImageWriterPng1() { TestImageWriter("Png2D-bw.png"); }
57  void TestImageWriterPng2() { TestImageWriter("RenderingTestData/rgbImage.png"); }
58  void TestImageWriterPng3() { TestImageWriter("RenderingTestData/rgbaImage.png"); }
59  void TestWrite3DplusT_ArbitraryTG()
60  {
61  TestImageWriter("3D+t-ITKIO-TestData/LinearModel_4D_arbitrary_time_geometry.nrrd");
62  }
63 
64  void TestWrite3DplusT_ProportionalTG()
65  {
66  TestImageWriter("3D+t-ITKIO-TestData/LinearModel_4D_prop_time_geometry.nrrd");
67  }
68 
69  void TestImageWriterSimple()
70  {
71  // TODO
72  }
73 
74  std::string AppendExtension(const std::string &filename, const char *extension)
75  {
76  std::string new_filename = filename;
77 
78  new_filename += extension;
79  return new_filename;
80  }
81 
82  bool CompareImageMetaData(mitk::Image::Pointer image, mitk::Image::Pointer reference, bool checkPixelType = true)
83  {
84  // switch to AreIdentical() methods as soon as Bug 11925 (Basic comparison operators) is fixed
85 
86  if (image->GetDimension() != reference->GetDimension())
87  {
88  MITK_ERROR << "The image dimension differs: IN (" << image->GetDimension() << ") REF("
89  << reference->GetDimension() << ")";
90  return false;
91  }
92 
93  // pixel type
94  if (checkPixelType &&
95  (image->GetPixelType() != reference->GetPixelType() &&
96  image->GetPixelType().GetBitsPerComponent() != reference->GetPixelType().GetBitsPerComponent()))
97  {
98  MITK_ERROR << "Pixeltype differs ( image=" << image->GetPixelType().GetPixelTypeAsString() << "["
99  << image->GetPixelType().GetBitsPerComponent() << "]"
100  << " reference=" << reference->GetPixelType().GetPixelTypeAsString() << "["
101  << reference->GetPixelType().GetBitsPerComponent() << "]"
102  << " )";
103  return false;
104  }
105 
106  return true;
107  }
108 
109  /*
110  Test writing picture formats like *.bmp, *.png, *.tiff or *.jpg
111  NOTE: Saving as picture format must ignore PixelType comparison - not all bits per components are supported (see
112  specification of the format)
113  */
114  void TestPictureWriting(mitk::Image *image, const std::string &filename, const std::string &extension)
115  {
116  const std::string fullFileName = AppendExtension(filename, extension.c_str());
117 
118  mitk::Image::Pointer singleSliceImage = NULL;
119  if (image->GetDimension() == 3)
120  {
122  extractFilter->SetInput(image);
123  extractFilter->SetWorldGeometry(image->GetSlicedGeometry()->GetPlaneGeometry(0));
124 
125  extractFilter->Update();
126  singleSliceImage = extractFilter->GetOutput();
127 
128  // test 3D writing in format supporting only 2D
129  mitk::IOUtil::Save(image, fullFileName);
130 
131  // test images
132  unsigned int foundImagesCount = 0;
133 
134  // if the image only contains one sinlge slice the itkImageSeriesWriter won't add a number like
135  // filename.XX.extension
136  if (image->GetDimension(2) == 1)
137  {
138  std::stringstream series_filenames;
139  series_filenames << filename << extension;
140  mitk::Image::Pointer compareImage = mitk::IOUtil::LoadImage(series_filenames.str());
141  if (compareImage.IsNotNull())
142  {
143  foundImagesCount++;
145  CompareImageMetaData(singleSliceImage, compareImage, false),
146  "Image meta data unchanged after writing and loading again. "); // ignore bits per component
147  }
148  remove(series_filenames.str().c_str());
149  }
150  else // test the whole slice stack
151  {
152  for (unsigned int i = 0; i < image->GetDimension(2); i++)
153  {
154  std::stringstream series_filenames;
155  series_filenames << filename << "." << i + 1 << extension;
156  mitk::Image::Pointer compareImage = mitk::IOUtil::LoadImage(series_filenames.str());
157  if (compareImage.IsNotNull())
158  {
159  foundImagesCount++;
161  CompareImageMetaData(singleSliceImage, compareImage, false),
162  "Image meta data unchanged after writing and loading again. "); // ignore bits per component
163  }
164  remove(series_filenames.str().c_str());
165  }
166  }
167  MITK_TEST_CONDITION(foundImagesCount == image->GetDimension(2),
168  "All 2D-Slices of a 3D image were stored correctly.");
169  }
170  else if (image->GetDimension() == 2)
171  {
172  singleSliceImage = image;
173  }
174 
175  // test 2D writing
176  if (singleSliceImage.IsNotNull())
177  {
178  try
179  {
180  mitk::IOUtil::Save(singleSliceImage, fullFileName);
181 
182  mitk::Image::Pointer compareImage = mitk::IOUtil::LoadImage(fullFileName.c_str());
183  MITK_TEST_CONDITION_REQUIRED(compareImage.IsNotNull(), "Image stored was succesfully loaded again");
184 
186  CompareImageMetaData(singleSliceImage, compareImage, false),
187  "Image meta data unchanged after writing and loading again. "); // ignore bits per component
188  remove(fullFileName.c_str());
189  }
190  catch (itk::ExceptionObject &e)
191  {
192  MITK_TEST_FAILED_MSG(<< "Exception during file writing for ." << extension << ": " << e.what());
193  }
194  }
195  }
196 
200  void TestNRRDWriting(const mitk::Image *image)
201  {
202  CPPUNIT_ASSERT_MESSAGE("Internal error. Passed reference image is null.", image);
203 
204  std::ofstream tmpStream;
205  std::string tmpFilePath = mitk::IOUtil::CreateTemporaryFile(tmpStream, "XXXXXX.nrrd");
206  tmpStream.close();
207 
208  try
209  {
210  mitk::IOUtil::Save(image, tmpFilePath);
211 
212  mitk::Image::Pointer compareImage = mitk::IOUtil::LoadImage(tmpFilePath);
213  CPPUNIT_ASSERT_MESSAGE("Image stored in NRRD format was succesfully loaded again", compareImage.IsNotNull());
214 
215  /*It would make sence to check the images as well (see commented cppunit assert),
216  but currently there seems to be a problem (exception) with most of the test images
217  (partly it seems to be a problem when try to access the pixel content by AccessByItk_1
218  in mitk::CompareImageDataFilter.
219  This problem should be dealt with in Bug 19533 - mitkITKImageIOTest needs improvement */
220  // CPPUNIT_ASSERT_MESSAGE("Images are equal.", mitk::Equal(*image, *compareImage, mitk::eps, true));
221  CPPUNIT_ASSERT_MESSAGE(
222  "TimeGeometries are equal.",
223  mitk::Equal(*(image->GetTimeGeometry()), *(compareImage->GetTimeGeometry()), mitk::eps, true));
224 
225  remove(tmpFilePath.c_str());
226  }
227  catch (...)
228  {
229  std::remove(tmpFilePath.c_str());
230  CPPUNIT_FAIL("Exception during NRRD file writing");
231  }
232  }
233 
237  void TestMHDWriting(const mitk::Image *image)
238  {
239  CPPUNIT_ASSERT_MESSAGE("Internal error. Passed reference image is null.", image);
240 
241  std::ofstream tmpStream;
242  std::string tmpFilePath = mitk::IOUtil::CreateTemporaryFile(tmpStream, "XXXXXX.mhd");
243  tmpStream.close();
244 
245  std::string tmpFilePathWithoutExt = tmpFilePath.substr(0, tmpFilePath.size() - 4);
246 
247  try
248  {
249  mitk::IOUtil::Save(image, tmpFilePath);
250 
251  mitk::Image::Pointer compareImage = mitk::IOUtil::LoadImage(tmpFilePath);
252  CPPUNIT_ASSERT_MESSAGE("Image stored in MHD format was succesfully loaded again! ", compareImage.IsNotNull());
253 
254  CPPUNIT_ASSERT_MESSAGE(".mhd file exists",
255  itksys::SystemTools::FileExists((tmpFilePathWithoutExt + ".mhd").c_str()));
256  CPPUNIT_ASSERT_MESSAGE(".raw or .zraw exists",
257  itksys::SystemTools::FileExists((tmpFilePathWithoutExt + ".raw").c_str()) ||
258  itksys::SystemTools::FileExists((tmpFilePathWithoutExt + ".zraw").c_str()));
259 
260  /*It would make sence to check the images as well (see commented cppunit assert),
261  but currently there seems to be a problem (exception) with most of the test images
262  (partly it seems to be a problem when try to access the pixel content by AccessByItk_1
263  in mitk::CompareImageDataFilter.
264  This problem should be dealt with in Bug 19533 - mitkITKImageIOTest needs improvement */
265  // CPPUNIT_ASSERT_MESSAGE("Images are equal.", mitk::Equal(*image, *compareImage, mitk::eps, true));
266  CPPUNIT_ASSERT_MESSAGE("TimeGeometries are equal.",
267  mitk::Equal(*(image->GetTimeGeometry()), *(compareImage->GetTimeGeometry()), 5e-4, true));
268 
269  // delete
270  remove(tmpFilePath.c_str());
271  remove((tmpFilePathWithoutExt + ".raw").c_str());
272  remove((tmpFilePathWithoutExt + ".zraw").c_str());
273  }
274  catch (...)
275  {
276  CPPUNIT_FAIL("Exception during.mhd file writing");
277  }
278  }
279 
288  void TestImageWriter(std::string sourcefile)
289  {
290  sourcefile = GetTestDataFilePath(sourcefile);
291 
292  // load image
293  CPPUNIT_ASSERT_MESSAGE("Checking whether source image exists", itksys::SystemTools::FileExists(sourcefile.c_str()));
294 
295  mitk::Image::Pointer image = NULL;
296 
297  try
298  {
299  image = mitk::IOUtil::LoadImage(sourcefile);
300  }
301  catch (...)
302  {
303  CPPUNIT_FAIL("Exception during file loading:");
304  }
305 
306  CPPUNIT_ASSERT_MESSAGE("loaded image not NULL", image.IsNotNull());
307 
308  // write ITK .mhd image (2D and 3D only)
309  if (image->GetDimension() <= 3)
310  {
311  TestMHDWriting(image);
312  }
313 
314  // testing more component image writing as nrrd files
315  TestNRRDWriting(image);
316 
317  std::ofstream tmpStream;
318  std::string tmpFilePath = mitk::IOUtil::CreateTemporaryFile(tmpStream, "XXXXXX");
319  tmpStream.close();
320 
321  TestPictureWriting(image, tmpFilePath, ".png");
322  TestPictureWriting(image, tmpFilePath, ".jpg");
323  TestPictureWriting(image, tmpFilePath, ".tiff");
324  TestPictureWriting(image, tmpFilePath, ".bmp");
325  // always end with this!
326  }
327 
331  void TestWrite3DImageWithOnePlane()
332  {
333  typedef itk::Image<unsigned char, 3> ImageType;
334 
335  ImageType::Pointer itkImage = ImageType::New();
336 
337  ImageType::IndexType start;
338  start.Fill(0);
339 
340  ImageType::SizeType size;
341  size[0] = 100;
342  size[1] = 100;
343  size[2] = 1;
344 
345  ImageType::RegionType region;
346  region.SetSize(size);
347  region.SetIndex(start);
348  itkImage->SetRegions(region);
349  itkImage->Allocate();
350  itkImage->FillBuffer(0);
351 
352  itk::ImageRegionIterator<ImageType> imageIterator(itkImage, itkImage->GetLargestPossibleRegion());
353 
354  // Make two squares
355  while (!imageIterator.IsAtEnd())
356  {
357  if ((imageIterator.GetIndex()[0] > 5 && imageIterator.GetIndex()[0] < 20) &&
358  (imageIterator.GetIndex()[1] > 5 && imageIterator.GetIndex()[1] < 20))
359  {
360  imageIterator.Set(255);
361  }
362 
363  if ((imageIterator.GetIndex()[0] > 50 && imageIterator.GetIndex()[0] < 70) &&
364  (imageIterator.GetIndex()[1] > 50 && imageIterator.GetIndex()[1] < 70))
365  {
366  imageIterator.Set(60);
367  }
368  ++imageIterator;
369  }
370 
371  mitk::Image::Pointer image = mitk::ImportItkImage(itkImage);
372 
373  mitk::IOUtil::SaveImage(image, mitk::IOUtil::CreateTemporaryFile("3Dto2DTestImageXXXXXX.nrrd"));
374  mitk::IOUtil::SaveImage(image, mitk::IOUtil::CreateTemporaryFile("3Dto2DTestImageXXXXXX.png"));
375  }
376 
380  void TestWrite3DImageWithTwoPlanes()
381  {
382  typedef itk::Image<unsigned char, 3> ImageType;
383 
384  ImageType::Pointer itkImage = ImageType::New();
385 
386  ImageType::IndexType start;
387  start.Fill(0);
388 
389  ImageType::SizeType size;
390  size[0] = 100;
391  size[1] = 100;
392  size[2] = 2;
393 
394  ImageType::RegionType region;
395  region.SetSize(size);
396  region.SetIndex(start);
397  itkImage->SetRegions(region);
398  itkImage->Allocate();
399  itkImage->FillBuffer(0);
400 
401  itk::ImageRegionIterator<ImageType> imageIterator(itkImage, itkImage->GetLargestPossibleRegion());
402 
403  // Make two squares
404  while (!imageIterator.IsAtEnd())
405  {
406  if ((imageIterator.GetIndex()[0] > 5 && imageIterator.GetIndex()[0] < 20) &&
407  (imageIterator.GetIndex()[1] > 5 && imageIterator.GetIndex()[1] < 20))
408  {
409  imageIterator.Set(255);
410  }
411  if ((imageIterator.GetIndex()[0] > 50 && imageIterator.GetIndex()[0] < 70) &&
412  (imageIterator.GetIndex()[1] > 50 && imageIterator.GetIndex()[1] < 70))
413  {
414  imageIterator.Set(60);
415  }
416  ++imageIterator;
417  }
418  mitk::Image::Pointer image = mitk::ImportItkImage(itkImage);
419 
420  mitk::IOUtil::SaveImage(image, mitk::IOUtil::CreateTemporaryFile("3Dto2DTestImageXXXXXX.nrrd"));
421 
422  CPPUNIT_ASSERT_THROW(mitk::IOUtil::SaveImage(image, mitk::IOUtil::CreateTemporaryFile("3Dto2DTestImageXXXXXX.png")),
424  }
425 };
426 
427 MITK_TEST_SUITE_REGISTRATION(mitkItkImageIO)
static void Save(const mitk::BaseData *data, const std::string &path)
Save a mitk::BaseData instance.
Definition: mitkIOUtil.cpp:824
itk::SmartPointer< Self > Pointer
MITK_TEST_SUITE_REGISTRATION(mitkImageToItk)
#define MITK_ERROR
Definition: mitkLogMacros.h:24
#define MITK_TEST_CONDITION_REQUIRED(COND, MSG)
#define MITK_TEST(TESTMETHOD)
Adds a test to the current test suite.
static Pointer New()
const mitk::TimeGeometry * GetTimeGeometry() const
Return the TimeGeometry of the data as const pointer.
Definition: mitkBaseData.h:52
static std::string GetTestDataFilePath(const std::string &testData)
Get the absolute path for test data.
Image::Pointer ImportItkImage(const itk::SmartPointer< ItkOutputImageType > &itkimage, const BaseGeometry *geometry=nullptr, bool update=true)
Imports an itk::Image (with a specific type) as an mitk::Image.Instantiates instance of ITKImageImpor...
static bool SaveImage(mitk::Image::Pointer image, const std::string &path)
SaveImage Convenience method to save an arbitrary mitkImage.
Definition: mitkIOUtil.cpp:870
map::core::discrete::Elements< 3 >::InternalImageType ImageType
An object of this class represents an exception of MITK. Please don't instantiate exceptions manually...
Definition: mitkException.h:49
#define MITK_TEST_CONDITION(COND, MSG)
static const std::string filename
#define MITK_TEST_FAILED_MSG(MSG)
Fail and finish test with message MSG.
Image class for storing images.
Definition: mitkImage.h:76
virtual mitk::PlaneGeometry * GetPlaneGeometry(int s) const
Returns the PlaneGeometry of the slice (s).
Test fixture for parameterized tests.
MITKNEWMODULE_EXPORT bool Equal(mitk::ExampleDataStructure *leftHandSide, mitk::ExampleDataStructure *rightHandSide, mitk::ScalarType eps, bool verbose)
Returns true if the example data structures are considered equal.
static std::string CreateTemporaryFile(std::ofstream &tmpStream, const std::string &templateName="XXXXXX", std::string path=std::string())
Definition: mitkIOUtil.cpp:407
MITKCORE_EXPORT const ScalarType eps
SlicedGeometry3D * GetSlicedGeometry(unsigned int t=0) const
Convenience access method for the geometry, which is of type SlicedGeometry3D (or a sub-class of it)...
unsigned int GetDimension() const
Get dimension of the image.
Definition: mitkImage.cpp:110
static mitk::Image::Pointer LoadImage(const std::string &path)
LoadImage Convenience method to load an arbitrary mitkImage.
Definition: mitkIOUtil.cpp:597
static itkEventMacro(BoundingShapeInteractionEvent, itk::AnyEvent) class MITKBOUNDINGSHAPE_EXPORT BoundingShapeInteractor Pointer New()
Basic interaction methods for mitk::GeometryData.