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