testdriver-itk.hxx
Go to the documentation of this file.
1 // ===========================================================================
2 // Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
3 // Copyright (c) Insight Software Consortium
4 // Copyright (c) 2011 University of Pennsylvania
5 //
6 // Portions of this file are subject to the VTK Toolkit Version 3 copyright.
7 //
8 // For complete copyright, license and disclaimer of warranty information
9 // please refer to the COPYRIGHT file.
10 // ===========================================================================
11 
12  /*=========================================================================
13  *
14  * Copyright Insight Software Consortium
15  *
16  * Licensed under the Apache License, Version 2.0 (the "License");
17  * you may not use this file except in compliance with the License.
18  * You may obtain a copy of the License at
19  *
20  * http://www.apache.org/licenses/LICENSE-2.0.txt
21  *
22  * Unless required by applicable law or agreed to in writing, software
23  * distributed under the License is distributed on an "AS IS" BASIS,
24  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
25  * See the License for the specific language governing permissions and
26  * limitations under the License.
27  *
28  *=========================================================================*/
29 /*=========================================================================
30  *
31  * Portions of this file are subject to the VTK Toolkit Version 3 copyright.
32  *
33  * Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
34  *
35  * For complete copyright, license and disclaimer of warranty information
36  * please refer to the NOTICE file at the top of the ITK source tree.
37  *
38  *=========================================================================*/
39 
40 /**
41  * @file testdriver-itk.hxx
42  * @brief ITK-based implementation of test driver.
43  *
44  * This implementation of the test driver utilizes the ITK.
45  *
46  * This file is in parts a modified version of the itkTestDriverInclude.h
47  * file which is part of the TestKernel module of the ITK 4 project and
48  * in other parts contains code from the ImageCompareCommand.cxx file
49  * which is part of the ITK 3.20 release.
50  */
51 
52 
53 
54 #pragma once
55 #ifndef _BASIS_TESTDRIVER_ITK_HXX
56 #define _BASIS_TESTDRIVER_ITK_HXX
57 
58 
59 // avoid dependency on all IO libraries which are commonly used
60 // has to be undefined before including the reader or writer
61 #ifdef ITK_IO_FACTORY_REGISTER_MANAGER
62 # undef ITK_IO_FACTORY_REGISTER_MANAGER
63 #endif
64 
65 #include <iostream>
66 
67 #include <itkImage.h>
68 #include <itkImageFileReader.h>
69 #include <itkImageFileWriter.h>
70 #include <itkRescaleIntensityImageFilter.h>
71 #include <itkExtractImageFilter.h>
72 #if defined(ITK_VERSION_MAJOR) && ITK_VERSION_MAJOR < 4
73 # include <itkDifferenceImageFilter.h>
74 #else
75 # include <itkTestingComparisonImageFilter.h>
76 #endif
77 #include <itkOrientImageFilter.h>
78 
79 #include <itkGDCMImageIOFactory.h>
80 #include <itkMetaImageIOFactory.h>
81 #include <itkJPEGImageIOFactory.h>
82 #include <itkPNGImageIOFactory.h>
83 #include <itkTIFFImageIOFactory.h>
84 #include <itkBMPImageIOFactory.h>
85 #include <itkVTKImageIOFactory.h>
86 #include <itkNrrdImageIOFactory.h>
87 #include <itkGiplImageIOFactory.h>
88 #include <itkNiftiImageIOFactory.h>
89 #include <itkObjectFactoryBase.h>
90 
91 
92 // maximum number of dimensions of test and baseline images
93 #define ITK_TEST_DIMENSION_MAX BASIS_MAX_TEST_IMAGE_DIMENSION
94 
95 
96 // ---------------------------------------------------------------------------
98 {
99  itk::ObjectFactoryBase::RegisterFactory(itk::MetaImageIOFactory::New());
100  itk::ObjectFactoryBase::RegisterFactory(itk::GDCMImageIOFactory::New());
101  itk::ObjectFactoryBase::RegisterFactory(itk::JPEGImageIOFactory::New());
102  itk::ObjectFactoryBase::RegisterFactory(itk::VTKImageIOFactory::New());
103  itk::ObjectFactoryBase::RegisterFactory(itk::PNGImageIOFactory::New());
104  itk::ObjectFactoryBase::RegisterFactory(itk::TIFFImageIOFactory::New());
105  itk::ObjectFactoryBase::RegisterFactory(itk::BMPImageIOFactory::New());
106  itk::ObjectFactoryBase::RegisterFactory(itk::NrrdImageIOFactory::New());
107  itk::ObjectFactoryBase::RegisterFactory(itk::GiplImageIOFactory::New());
108  itk::ObjectFactoryBase::RegisterFactory(itk::NiftiImageIOFactory::New());
109 }
110 
111 // ---------------------------------------------------------------------------
112 // This implementation of the image regression test was copied from the
113 // Testing/Code/IO/ImageCompareCommand.cxx file of the ITK 3.20 release.
114 // Then the function has been modified such that first of all the function
115 // prototype matches the one of the corresponding function of the ITK 4
116 // TestKernel module and furthermore the generation of the reports is
117 // identical as well. This includes the extraction of the center slice instead
118 // of the first slice to generate PNG images for inclusion in the report.
119 //
120 // The orientationInsensitive flag has been added to allow for differences
121 // in the image orientations on disk.
122 int RegressionTestImage (const char* testImageFilename,
123  const char* baselineImageFilename,
124  int reportErrors,
125  double intensityTolerance,
126  unsigned int numberOfPixelsTolerance,
127  unsigned int radiusTolerance,
128  bool orientationInsensitive)
129 {
130  // Use the factory mechanism to read the test and baseline files and convert them to double
131  typedef itk::Image<double, ITK_TEST_DIMENSION_MAX> ImageType;
132  typedef itk::Image<unsigned char, ITK_TEST_DIMENSION_MAX> OutputType;
133  typedef itk::Image<unsigned char, 2> DiffOutputType;
134  typedef itk::ImageFileReader<ImageType> ReaderType;
135 
136  // Read the baseline file
137  ReaderType::Pointer baselineReader = ReaderType::New();
138  baselineReader->SetFileName(baselineImageFilename);
139  try
140  {
141  baselineReader->UpdateLargestPossibleRegion();
142  }
143  catch (itk::ExceptionObject& e)
144  {
145  std::cerr << "Exception detected while reading " << baselineImageFilename << " : " << e;
146  return 1000;
147  }
148 
149  // Read the file generated by the test
150  ReaderType::Pointer testReader = ReaderType::New();
151  testReader->SetFileName(testImageFilename);
152  try
153  {
154  testReader->UpdateLargestPossibleRegion();
155  }
156  catch (itk::ExceptionObject& e)
157  {
158  std::cerr << "Exception detected while reading " << testImageFilename << " : " << e << std::endl;
159  return 1000;
160  }
161 
162  ImageType::Pointer baselineImage = baselineReader->GetOutput();
163  ImageType::Pointer testImage = testReader ->GetOutput();
164 
165  testImage ->DisconnectPipeline();
166  baselineImage->DisconnectPipeline();
167 
168  ImageType::SizeType baselineSize = baselineImage->GetLargestPossibleRegion().GetSize();
169  ImageType::SizeType testSize = testImage ->GetLargestPossibleRegion().GetSize();
170 
171  if (orientationInsensitive) {
172  const unsigned int OrientImageDimension = 3;
173  typedef itk::Image<double, OrientImageDimension> OrienterImageType;
174  typedef itk::ExtractImageFilter<ImageType, OrienterImageType> ExtractorType;
175  typedef itk::CastImageFilter<OrienterImageType, ImageType> UpCasterType;
176  typedef itk::OrientImageFilter<OrienterImageType, OrienterImageType> OrienterType;
177 
178  ExtractorType::Pointer extractor = ExtractorType::New();
179  OrienterType ::Pointer orienter = OrienterType ::New();
180  UpCasterType ::Pointer caster = UpCasterType ::New();
181 
182  ImageType::SizeType extract_size;
183  ImageType::IndexType extract_index;
184  ImageType::RegionType extract_region;
185  extract_index.Fill(0);
186  extract_size .Fill(0);
187 
188  for (unsigned int i = 0; i < ITK_TEST_DIMENSION_MAX; i++) {
189  if (baselineSize[i] > 1) extract_size[i] = baselineSize[i];
190  }
191 
192  extract_region.SetIndex(extract_index);
193  extract_region.SetSize (extract_size);
194  extractor->SetExtractionRegion(extract_region);
195 #if !defined(ITK_VERSION_MAJOR) || ITK_VERSION_MAJOR > 3
196  extractor->SetDirectionCollapseToSubmatrix();
197 #endif
198 
199  orienter->UseImageDirectionOn();
200  orienter->SetDesiredCoordinateOrientation(itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_RPI);
201 
202 
203  extractor->SetInput(baselineImage);
204  orienter ->SetInput(extractor->GetOutput());
205  caster ->SetInput(orienter ->GetOutput());
206 
207  try
208  {
209  caster->Update();
210  }
211  catch (itk::ExceptionObject& e)
212  {
213  std::cerr << "Failed to change orientation of baseline image to RPI : " << e << std::endl;
214  return 1000;
215  }
216 
217  baselineImage = caster->GetOutput();
218  baselineSize = baselineImage->GetLargestPossibleRegion().GetSize();
219  baselineImage->DisconnectPipeline();
220 
221 
222  extractor->SetInput(testImage);
223  orienter ->SetInput(extractor->GetOutput());
224  caster ->SetInput(orienter ->GetOutput());
225 
226  try
227  {
228  caster->Update();
229  }
230  catch (itk::ExceptionObject& e)
231  {
232  std::cerr << "Failed to change orientation of test image to RPI : " << e << std::endl;
233  return 1000;
234  }
235 
236  testImage = caster->GetOutput();
237  testSize = testImage->GetLargestPossibleRegion().GetSize();
238  testImage->DisconnectPipeline();
239  }
240 
241  // The sizes of the baseline and test image must match
242  if (baselineSize != testSize)
243  {
244  std::cerr << "The size of the Baseline image and Test image do not match!" << std::endl;
245  std::cerr << "Baseline image: " << baselineImageFilename
246  << " has size " << baselineSize << std::endl;
247  std::cerr << "Test image: " << testImageFilename
248  << " has size " << testSize << std::endl;
249  return 1;
250  }
251 
252  // Now compare the two images
253 #if defined(ITK_VERSION_MAJOR) && ITK_VERSION_MAJOR < 4
254  typedef itk::DifferenceImageFilter<ImageType,ImageType> DiffType;
255 #else
256  typedef itk::Testing::ComparisonImageFilter<ImageType,ImageType> DiffType;
257 #endif
258  DiffType::Pointer diff = DiffType::New();
259  diff->SetValidInput(baselineImage);
260  diff->SetTestInput(testImage);
261 
262  diff->SetDifferenceThreshold( intensityTolerance );
263  diff->SetToleranceRadius( radiusTolerance );
264 
265  diff->UpdateLargestPossibleRegion();
266 
267  bool differenceFailed = false;
268 
269  double averageIntensityDifference = diff->GetTotalDifference();
270 
271  unsigned long numberOfPixelsWithDifferences = diff->GetNumberOfPixelsWithDifferences();
272 
273  //The measurement errors should be reported for both success and errors
274  //to facilitate setting tight tolerances of tests.
275  if (reportErrors) {
276  std::cout << "<DartMeasurement name=\"ImageError\" type=\"numeric/double\">";
277  std::cout << numberOfPixelsWithDifferences;
278  std::cout << "</DartMeasurement>" << std::endl;
279  }
280 
281  if( averageIntensityDifference > 0.0 ) {
282  if( numberOfPixelsWithDifferences > numberOfPixelsTolerance ) {
283  differenceFailed = true;
284  } else {
285  differenceFailed = false;
286  }
287  } else {
288  differenceFailed = false;
289  }
290 
291  if (differenceFailed && reportErrors) {
292  typedef itk::RescaleIntensityImageFilter<ImageType,OutputType> RescaleType;
293  typedef itk::ExtractImageFilter<OutputType,DiffOutputType> ExtractType;
294  typedef itk::ImageFileWriter<DiffOutputType> WriterType;
295  typedef itk::ImageRegion<ITK_TEST_DIMENSION_MAX> RegionType;
296 
297  OutputType::IndexType index; index.Fill(0);
298  OutputType::SizeType size; size.Fill(0);
299 
300  RescaleType::Pointer rescale = RescaleType::New();
301 
302  rescale->SetOutputMinimum(itk::NumericTraits<unsigned char>::NonpositiveMin());
303  rescale->SetOutputMaximum(itk::NumericTraits<unsigned char>::max());
304  rescale->SetInput(diff->GetOutput());
305  rescale->UpdateLargestPossibleRegion();
306 
307  //Note: This modification has been applied to the ImageCompareCommand
308  // implementation of the ITK 3.18 vs. ITK 4.0
309  //
310  //Get the center slice of the image, In 3D, the first slice
311  //is often a black slice with little debugging information.
312  size = rescale->GetOutput()->GetLargestPossibleRegion().GetSize();
313  for (unsigned int i = 2; i < ITK_TEST_DIMENSION_MAX; i++) {
314  index[i] = size[i] / 2; //NOTE: Integer Divide used to get approximately
315  // the center slice
316  size[i] = 0;
317  }
318 
319  RegionType region;
320  region.SetIndex(index);
321  region.SetSize(size);
322 
323  ExtractType::Pointer extract = ExtractType::New();
324 
325  extract->SetInput(rescale->GetOutput());
326  extract->SetExtractionRegion(region);
327 #if !defined(ITK_VERSION_MAJOR) || ITK_VERSION_MAJOR > 3
328  extract->SetDirectionCollapseToIdentity();
329 #endif
330 
331  WriterType::Pointer writer = WriterType::New();
332  writer->SetInput(extract->GetOutput());
333 
334  std::ostringstream diffName;
335  diffName << testImageFilename << ".diff.png";
336  try {
337  rescale->SetInput(diff->GetOutput());
338  rescale->Update();
339  } catch(const std::exception& e) {
340  std::cerr << "Error during rescale of " << diffName.str() << std::endl;
341  std::cerr << e.what() << "\n";
342  }
343  catch (...)
344  {
345  std::cerr << "Error during rescale of " << diffName.str() << std::endl;
346  }
347  writer->SetFileName(diffName.str().c_str());
348  try
349  {
350  writer->Update();
351  }
352  catch(const std::exception& e)
353  {
354  std::cerr << "Error during write of " << diffName.str() << std::endl;
355  std::cerr << e.what() << "\n";
356  }
357  catch (...)
358  {
359  std::cerr << "Error during write of " << diffName.str() << std::endl;
360  }
361 
362  std::cout << "<DartMeasurementFile name=\"DifferenceImage\" type=\"image/png\">";
363  std::cout << diffName.str();
364  std::cout << "</DartMeasurementFile>" << std::endl;
365 
366  std::ostringstream baseName;
367  baseName << testImageFilename << ".base.png";
368  try
369  {
370  rescale->SetInput(baselineImage);
371  rescale->Update();
372  }
373  catch(const std::exception& e)
374  {
375  std::cerr << "Error during rescale of " << baseName.str() << std::endl;
376  std::cerr << e.what() << "\n";
377  }
378  catch (...)
379  {
380  std::cerr << "Error during rescale of " << baseName.str() << std::endl;
381  }
382  try
383  {
384  writer->SetFileName(baseName.str().c_str());
385  writer->Update();
386  }
387  catch(const std::exception& e)
388  {
389  std::cerr << "Error during write of " << baseName.str() << std::endl;
390  std::cerr << e.what() << "\n";
391  }
392  catch (...)
393  {
394  std::cerr << "Error during write of " << baseName.str() << std::endl;
395  }
396 
397  std::cout << "<DartMeasurementFile name=\"BaselineImage\" type=\"image/png\">";
398  std::cout << baseName.str();
399  std::cout << "</DartMeasurementFile>" << std::endl;
400 
401  std::ostringstream testName;
402  testName << testImageFilename << ".test.png";
403  try
404  {
405  rescale->SetInput(testImage);
406  rescale->Update();
407  }
408  catch(const std::exception& e)
409  {
410  std::cerr << "Error during rescale of " << testName.str() << std::endl;
411  std::cerr << e.what() << "\n";
412  }
413  catch (...)
414  {
415  std::cerr << "Error during rescale of " << testName.str() << std::endl;
416  }
417  try
418  {
419  writer->SetFileName(testName.str().c_str());
420  writer->Update();
421  }
422  catch(const std::exception& e)
423  {
424  std::cerr << "Error during write of " << testName.str() << std::endl;
425  std::cerr << e.what() << "\n";
426  }
427  catch (...)
428  {
429  std::cerr << "Error during write of " << testName.str() << std::endl;
430  }
431 
432  std::cout << "<DartMeasurementFile name=\"TestImage\" type=\"image/png\">";
433  std::cout << testName.str();
434  std::cout << "</DartMeasurementFile>" << std::endl;
435 
436  }
437  return differenceFailed;
438 }
439 
440 
441 #endif // _BASIS_TESTDRIVER_ITK_HXX
#define ITK_TEST_DIMENSION_MAX
MultiStringArg diff("", "diff", "Compare the <test> file to the <baseline> file byte by byte." " Can by used to compare any files including text files." " For images, the --compare option should be used instead.", false, "<test> <baseline>", 2, false, &diff_visitor)
int RegressionTestImage(const char *testImageFilename, const char *baselineImageFilename, int reportErrors, double intensityTolerance, unsigned int numberOfPixelsTolerance, unsigned int radiusTolerance, bool orientationInsensitive)
void RegisterRequiredFactories()