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