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