BASIS  r3148
testdriver-itk.hxx
Go to the documentation of this file.
00001 /**
00002  * @file  testdriver-itk.hxx
00003  * @brief ITK-based implementation of test driver.
00004  *
00005  * This implementation of the test driver utilizes the ITK.
00006  *
00007  * This file is in parts a modified version of the itkTestDriverInclude.h
00008  * file which is part of the TestKernel module of the ITK 4 project and
00009  * in other parts contains code from the ImageCompareCommand.cxx file
00010  * which is part of the ITK 3.20 release.
00011  *
00012  * Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen<br />
00013  * Copyright (c) Insight Software Consortium.<br />
00014  * Copyright (c) 2011 University of Pennsylvania.
00015  *
00016  * Portions of this file are subject to the VTK Toolkit Version 3 copyright.
00017  *
00018  * For complete copyright, license and disclaimer of warranty information
00019  * please refer to the COPYRIGHT file.
00020  *
00021  * Contact: SBIA Group <sbia-software at uphs.upenn.edu>
00022  */
00023 
00024  /*=========================================================================
00025  *
00026  *  Copyright Insight Software Consortium
00027  *
00028  *  Licensed under the Apache License, Version 2.0 (the "License");
00029  *  you may not use this file except in compliance with the License.
00030  *  You may obtain a copy of the License at
00031  *
00032  *         http://www.apache.org/licenses/LICENSE-2.0.txt
00033  *
00034  *  Unless required by applicable law or agreed to in writing, software
00035  *  distributed under the License is distributed on an "AS IS" BASIS,
00036  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00037  *  See the License for the specific language governing permissions and
00038  *  limitations under the License.
00039  *
00040  *=========================================================================*/
00041 /*=========================================================================
00042  *
00043  *  Portions of this file are subject to the VTK Toolkit Version 3 copyright.
00044  *
00045  *  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
00046  *
00047  *  For complete copyright, license and disclaimer of warranty information
00048  *  please refer to the NOTICE file at the top of the ITK source tree.
00049  *
00050  *=========================================================================*/
00051 
00052 #pragma once
00053 #ifndef _BASIS_TESTDRIVER_ITK_HXX
00054 #define _BASIS_TESTDRIVER_ITK_HXX
00055 
00056 
00057 // avoid dependency on all IO libraries which are commonly used
00058 // has to be undefined before including the reader or writer
00059 #ifdef ITK_IO_FACTORY_REGISTER_MANAGER
00060 #  undef ITK_IO_FACTORY_REGISTER_MANAGER
00061 #endif
00062 
00063 
00064 #include <itkImage.h>
00065 #include <itkImageFileReader.h>
00066 #include <itkImageFileWriter.h>
00067 #include <itkRescaleIntensityImageFilter.h>
00068 #include <itkExtractImageFilter.h>
00069 #if defined(ITK_VERSION_MAJOR) && ITK_VERSION_MAJOR < 4
00070 #  include <itkDifferenceImageFilter.h>
00071 #else
00072 #  include <itkTestingComparisonImageFilter.h>
00073 #endif
00074 #include <itkOrientImageFilter.h>
00075 
00076 #include <itkGDCMImageIOFactory.h>
00077 #include <itkMetaImageIOFactory.h>
00078 #include <itkJPEGImageIOFactory.h>
00079 #include <itkPNGImageIOFactory.h>
00080 #include <itkTIFFImageIOFactory.h>
00081 #include <itkBMPImageIOFactory.h>
00082 #include <itkVTKImageIOFactory.h>
00083 #include <itkNrrdImageIOFactory.h>
00084 #include <itkGiplImageIOFactory.h>
00085 #include <itkNiftiImageIOFactory.h>
00086 #include <itkObjectFactoryBase.h>
00087 
00088 
00089 // maximum number of dimensions of test and baseline images
00090 #define ITK_TEST_DIMENSION_MAX BASIS_MAX_TEST_IMAGE_DIMENSION
00091 
00092 
00093 // ---------------------------------------------------------------------------
00094 void RegisterRequiredFactories()
00095 {
00096     itk::ObjectFactoryBase::RegisterFactory(itk::MetaImageIOFactory::New());
00097     itk::ObjectFactoryBase::RegisterFactory(itk::GDCMImageIOFactory::New());
00098     itk::ObjectFactoryBase::RegisterFactory(itk::JPEGImageIOFactory::New());
00099     itk::ObjectFactoryBase::RegisterFactory(itk::VTKImageIOFactory::New());
00100     itk::ObjectFactoryBase::RegisterFactory(itk::PNGImageIOFactory::New());
00101     itk::ObjectFactoryBase::RegisterFactory(itk::TIFFImageIOFactory::New());
00102     itk::ObjectFactoryBase::RegisterFactory(itk::BMPImageIOFactory::New());
00103     itk::ObjectFactoryBase::RegisterFactory(itk::NrrdImageIOFactory::New());
00104     itk::ObjectFactoryBase::RegisterFactory(itk::GiplImageIOFactory::New());
00105     itk::ObjectFactoryBase::RegisterFactory(itk::NiftiImageIOFactory::New());
00106 }
00107 
00108 // ---------------------------------------------------------------------------
00109 // This implementation of the image regression test was copied from the
00110 // Testing/Code/IO/ImageCompareCommand.cxx file of the ITK 3.20 release.
00111 // Then the function has been modified such that first of all the function
00112 // prototype matches the one of the corresponding function of the ITK 4
00113 // TestKernel module and furthermore the generation of the reports is
00114 // identical as well. This includes the extraction of the center slice instead
00115 // of the first slice to generate PNG images for inclusion in the report.
00116 //
00117 // The orientationInsensitive flag has been added to allow for differences
00118 // in the image orientations on disk.
00119 int RegressionTestImage (const char*  testImageFilename,
00120                          const char*  baselineImageFilename,
00121                          int          reportErrors,
00122                          double       intensityTolerance,
00123                          unsigned int numberOfPixelsTolerance,
00124                          unsigned int radiusTolerance,
00125                          bool         orientationInsensitive)
00126 {
00127   // Use the factory mechanism to read the test and baseline files and convert them to double
00128   typedef itk::Image<double,        ITK_TEST_DIMENSION_MAX> ImageType;
00129   typedef itk::Image<unsigned char, ITK_TEST_DIMENSION_MAX> OutputType;
00130   typedef itk::Image<unsigned char, 2>                      DiffOutputType;
00131   typedef itk::ImageFileReader<ImageType>                   ReaderType;
00132 
00133   // Read the baseline file
00134   ReaderType::Pointer baselineReader = ReaderType::New();
00135     baselineReader->SetFileName(baselineImageFilename);
00136   try
00137     {
00138     baselineReader->UpdateLargestPossibleRegion();
00139     }
00140   catch (itk::ExceptionObject& e)
00141     {
00142     std::cerr << "Exception detected while reading " << baselineImageFilename << " : "  << e;
00143     return 1000;
00144     }
00145 
00146   // Read the file generated by the test
00147   ReaderType::Pointer testReader = ReaderType::New();
00148     testReader->SetFileName(testImageFilename);
00149   try
00150     {
00151     testReader->UpdateLargestPossibleRegion();
00152     }
00153   catch (itk::ExceptionObject& e)
00154     {
00155     std::cerr << "Exception detected while reading " << testImageFilename << " : "  << e << std::endl;
00156     return 1000;
00157     }
00158 
00159   ImageType::Pointer baselineImage = baselineReader->GetOutput();
00160   ImageType::Pointer testImage     = testReader    ->GetOutput();
00161 
00162   testImage    ->DisconnectPipeline();
00163   baselineImage->DisconnectPipeline();
00164 
00165   ImageType::SizeType baselineSize = baselineImage->GetLargestPossibleRegion().GetSize();
00166   ImageType::SizeType testSize     = testImage    ->GetLargestPossibleRegion().GetSize();
00167 
00168   if (orientationInsensitive) {
00169     const unsigned int OrientImageDimension = 3;
00170     typedef itk::Image<double, OrientImageDimension>                     OrienterImageType;
00171     typedef itk::ExtractImageFilter<ImageType, OrienterImageType>        ExtractorType;
00172     typedef itk::CastImageFilter<OrienterImageType, ImageType>           UpCasterType;
00173     typedef itk::OrientImageFilter<OrienterImageType, OrienterImageType> OrienterType;
00174 
00175     ExtractorType::Pointer extractor = ExtractorType::New();
00176     OrienterType ::Pointer orienter  = OrienterType ::New();
00177     UpCasterType ::Pointer caster    = UpCasterType ::New();
00178 
00179     ImageType::SizeType   extract_size;
00180     ImageType::IndexType  extract_index;
00181     ImageType::RegionType extract_region;
00182     extract_index.Fill(0);
00183     extract_size .Fill(0);
00184 
00185     for (unsigned int i = 0; i < ITK_TEST_DIMENSION_MAX; i++) {
00186         if (baselineSize[i] > 1) extract_size[i] = baselineSize[i];
00187     }
00188 
00189     extract_region.SetIndex(extract_index);
00190     extract_region.SetSize (extract_size);
00191     extractor->SetExtractionRegion(extract_region);
00192 
00193     orienter->UseImageDirectionOn();
00194     orienter->SetDesiredCoordinateOrientation(itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_RPI);
00195 
00196 
00197     extractor->SetInput(baselineImage);
00198     orienter ->SetInput(extractor->GetOutput());
00199     caster   ->SetInput(orienter ->GetOutput());
00200 
00201     try
00202       {
00203       caster->Update();
00204       }
00205     catch (itk::ExceptionObject& e)
00206       {
00207       std::cerr << "Failed to change orientation of baseline image to RPI : " << e << std::endl;
00208       return 1000;
00209       }
00210 
00211     baselineImage = caster->GetOutput();
00212     baselineSize = baselineImage->GetLargestPossibleRegion().GetSize();
00213     baselineImage->DisconnectPipeline();
00214 
00215 
00216     extractor->SetInput(testImage);
00217     orienter ->SetInput(extractor->GetOutput());
00218     caster   ->SetInput(orienter ->GetOutput());
00219 
00220     try
00221       {
00222       caster->Update();
00223       }
00224     catch (itk::ExceptionObject& e)
00225       {
00226       std::cerr << "Failed to change orientation of test image to RPI : " << e << std::endl;
00227       return 1000;
00228       }
00229 
00230     testImage = caster->GetOutput();
00231     testSize  = testImage->GetLargestPossibleRegion().GetSize();
00232     testImage->DisconnectPipeline();
00233   }
00234 
00235   // The sizes of the baseline and test image must match
00236   if (baselineSize != testSize)
00237     {
00238     std::cerr << "The size of the Baseline image and Test image do not match!" << std::endl;
00239     std::cerr << "Baseline image: " << baselineImageFilename
00240               << " has size " << baselineSize << std::endl;
00241     std::cerr << "Test image:     " << testImageFilename
00242               << " has size " << testSize << std::endl;
00243     return 1;
00244     }
00245 
00246   // Now compare the two images
00247 #if defined(ITK_VERSION_MAJOR) && ITK_VERSION_MAJOR < 4
00248     typedef itk::DifferenceImageFilter<ImageType,ImageType> DiffType;
00249 #else
00250     typedef itk::Testing::ComparisonImageFilter<ImageType,ImageType> DiffType;
00251 #endif
00252     DiffType::Pointer diff = DiffType::New();
00253     diff->SetValidInput(baselineImage);
00254     diff->SetTestInput(testImage);
00255     
00256     diff->SetDifferenceThreshold( intensityTolerance );
00257     diff->SetToleranceRadius( radiusTolerance );
00258 
00259     diff->UpdateLargestPossibleRegion();
00260 
00261     bool differenceFailed = false;
00262   
00263     double averageIntensityDifference = diff->GetTotalDifference();
00264 
00265     unsigned long numberOfPixelsWithDifferences = diff->GetNumberOfPixelsWithDifferences();
00266 
00267     //The measurement errors should be reported for both success and errors
00268     //to facilitate setting tight tolerances of tests.
00269     if (reportErrors) {
00270         std::cout << "<DartMeasurement name=\"ImageError\" type=\"numeric/double\">";
00271         std::cout << numberOfPixelsWithDifferences;
00272         std::cout <<  "</DartMeasurement>" << std::endl;
00273     }
00274 
00275     if( averageIntensityDifference > 0.0 ) {
00276         if( numberOfPixelsWithDifferences > numberOfPixelsTolerance ) {
00277             differenceFailed = true;
00278         } else {
00279             differenceFailed = false;
00280         }
00281     } else {
00282         differenceFailed = false; 
00283     }
00284 
00285     if (differenceFailed && reportErrors) {
00286         typedef itk::RescaleIntensityImageFilter<ImageType,OutputType>    RescaleType;
00287         typedef itk::ExtractImageFilter<OutputType,DiffOutputType>        ExtractType;
00288         typedef itk::ImageFileWriter<DiffOutputType>                      WriterType;
00289         typedef itk::ImageRegion<ITK_TEST_DIMENSION_MAX>                  RegionType;
00290 
00291         OutputType::IndexType index; index.Fill(0);
00292         OutputType::SizeType size; size.Fill(0);
00293 
00294         RescaleType::Pointer rescale = RescaleType::New();
00295 
00296         rescale->SetOutputMinimum(itk::NumericTraits<unsigned char>::NonpositiveMin());
00297         rescale->SetOutputMaximum(itk::NumericTraits<unsigned char>::max());
00298         rescale->SetInput(diff->GetOutput());
00299         rescale->UpdateLargestPossibleRegion();
00300 
00301         //Note: This modification has been applied to the ImageCompareCommand
00302         //      implementation of the ITK 3.18 vs. ITK 4.0
00303         //
00304         //Get the center slice of the image,  In 3D, the first slice
00305         //is often a black slice with little debugging information.
00306         size = rescale->GetOutput()->GetLargestPossibleRegion().GetSize();
00307         for (unsigned int i = 2; i < ITK_TEST_DIMENSION_MAX; i++) {
00308             index[i] = size[i] / 2; //NOTE: Integer Divide used to get approximately
00309                                     // the center slice
00310             size[i] = 0;
00311         }
00312 
00313         RegionType region;
00314         region.SetIndex(index);
00315         region.SetSize(size);
00316 
00317         ExtractType::Pointer extract = ExtractType::New();
00318 
00319         extract->SetInput(rescale->GetOutput());
00320         extract->SetExtractionRegion(region);
00321 
00322         WriterType::Pointer writer = WriterType::New();
00323         writer->SetInput(extract->GetOutput());
00324 
00325         itksys_ios::ostringstream diffName;
00326         diffName << testImageFilename << ".diff.png";
00327         try {
00328             rescale->SetInput(diff->GetOutput());
00329             rescale->Update();
00330         } catch(const std::exception& e) {
00331           std::cerr << "Error during rescale of " << diffName.str() << std::endl;
00332           std::cerr << e.what() << "\n";
00333           }
00334         catch (...)
00335           {
00336           std::cerr << "Error during rescale of " << diffName.str() << std::endl;
00337           }
00338         writer->SetFileName(diffName.str().c_str());
00339         try
00340           {
00341           writer->Update();
00342           }
00343         catch(const std::exception& e)
00344           {
00345           std::cerr << "Error during write of " << diffName.str() << std::endl;
00346           std::cerr << e.what() << "\n";
00347           }
00348         catch (...)
00349       {
00350       std::cerr << "Error during write of " << diffName.str() << std::endl;
00351       }
00352 
00353     std::cout << "<DartMeasurementFile name=\"DifferenceImage\" type=\"image/png\">";
00354     std::cout << diffName.str();
00355     std::cout << "</DartMeasurementFile>" << std::endl;
00356 
00357     itksys_ios::ostringstream baseName;
00358     baseName << testImageFilename << ".base.png";
00359     try
00360       {
00361       rescale->SetInput(baselineImage);
00362       rescale->Update();
00363       }
00364     catch(const std::exception& e)
00365       {
00366       std::cerr << "Error during rescale of " << baseName.str() << std::endl;
00367       std::cerr << e.what() << "\n";
00368       }
00369     catch (...)
00370       {
00371       std::cerr << "Error during rescale of " << baseName.str() << std::endl;
00372       }
00373     try
00374       {
00375       writer->SetFileName(baseName.str().c_str());
00376       writer->Update();
00377       }
00378     catch(const std::exception& e)
00379       {
00380       std::cerr << "Error during write of " << baseName.str() << std::endl;
00381       std::cerr << e.what() << "\n";
00382       }
00383     catch (...)
00384       {
00385       std::cerr << "Error during write of " << baseName.str() << std::endl;
00386       }
00387 
00388     std::cout << "<DartMeasurementFile name=\"BaselineImage\" type=\"image/png\">";
00389     std::cout << baseName.str();
00390     std::cout << "</DartMeasurementFile>" << std::endl;
00391 
00392     itksys_ios::ostringstream testName;
00393     testName << testImageFilename << ".test.png";
00394     try
00395       {
00396       rescale->SetInput(testImage);
00397       rescale->Update();
00398       }
00399     catch(const std::exception& e)
00400       {
00401       std::cerr << "Error during rescale of " << testName.str() << std::endl;
00402       std::cerr << e.what() << "\n";
00403       }
00404     catch (...)
00405       {
00406       std::cerr << "Error during rescale of " << testName.str() << std::endl;
00407       }
00408     try
00409       {
00410       writer->SetFileName(testName.str().c_str());
00411       writer->Update();
00412       }
00413     catch(const std::exception& e)
00414       {
00415       std::cerr << "Error during write of " << testName.str() << std::endl;
00416       std::cerr << e.what() << "\n";
00417       }
00418     catch (...)
00419       {
00420       std::cerr << "Error during write of " << testName.str() << std::endl;
00421       }
00422 
00423     std::cout << "<DartMeasurementFile name=\"TestImage\" type=\"image/png\">";
00424     std::cout << testName.str();
00425     std::cout << "</DartMeasurementFile>" << std::endl;
00426 
00427     }
00428   return differenceFailed;
00429 }
00430 
00431 
00432 #endif // _BASIS_TESTDRIVER_ITK_HXX