00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #include "itkWin32Header.h"
00027 #include <map>
00028 #include <string>
00029 #include <iostream>
00030 #include <fstream>
00031 #include "itkNumericTraits.h"
00032 #include "itkMultiThreader.h"
00033 #include "itkImage.h"
00034 #include "itkImageFileReader.h"
00035 #include "itkImageFileWriter.h"
00036 #include "itkImageRegionConstIterator.h"
00037 #include "itkSubtractImageFilter.h"
00038 #include "itkRescaleIntensityImageFilter.h"
00039 #include "itkExtractImageFilter.h"
00040 #include "itkDifferenceImageFilter.h"
00041 #include "itkImageRegion.h"
00042 #include "itksys/SystemTools.hxx"
00043
00044 #define ITK_TEST_DIMENSION_MAX 6
00045
00046 typedef int (*MainFuncPointer)(int , char* [] );
00047 std::map<std::string, MainFuncPointer> StringToTestFunctionMap;
00048
00049 #define REGISTER_TEST(test) \
00050 extern int test(int, char* [] ); \
00051 StringToTestFunctionMap[#test] = test
00052
00053 int RegressionTestImage (const char *testImageFilename,
00054 const char *baselineImageFilename,
00055 int reportErrors,
00056 double intensityTolerance = 2.0,
00057 unsigned int numberOfPixelsTolerance = 0,
00058 unsigned int radiusTolerance = 0);
00059
00060 std::map<std::string,int> RegressionTestBaselines (char *);
00061
00062 void RegisterTests();
00063 void PrintAvailableTests()
00064 {
00065 std::cout << "Available tests:\n";
00066 std::map<std::string, MainFuncPointer>::iterator j = StringToTestFunctionMap.begin();
00067 int i = 0;
00068 while(j != StringToTestFunctionMap.end())
00069 {
00070 std::cout << i << ". " << j->first << "\n";
00071 ++i;
00072 ++j;
00073 }
00074 }
00075
00076 int main(int ac, char* av[] )
00077 {
00078 char *baselineFilename = NULL;
00079 char *testFilename = NULL;
00080 double intensityTolerance = 2.0;
00081 unsigned int numberOfPixelsTolerance = 0;
00082 unsigned int radiusTolerance = 0;
00083
00084 RegisterTests();
00085 std::string testToRun;
00086 if(ac < 2)
00087 {
00088 PrintAvailableTests();
00089 std::cout << "To run a test, enter the test number: ";
00090 int testNum = 0;
00091 std::cin >> testNum;
00092 std::map<std::string, MainFuncPointer>::iterator j = StringToTestFunctionMap.begin();
00093 int i = 0;
00094 while(j != StringToTestFunctionMap.end() && i < testNum)
00095 {
00096 ++i;
00097 ++j;
00098 }
00099 if(j == StringToTestFunctionMap.end())
00100 {
00101 std::cerr << testNum << " is an invalid test number\n";
00102 return -1;
00103 }
00104 testToRun = j->first;
00105 }
00106 else
00107 {
00108 if (strcmp(av[1], "--with-threads") == 0)
00109 {
00110 int numThreads = atoi(av[2]);
00111 itk::MultiThreader::SetGlobalDefaultNumberOfThreads(numThreads);
00112 av += 2;
00113 ac -= 2;
00114 }
00115 else if (strcmp(av[1], "--without-threads") == 0)
00116 {
00117 itk::MultiThreader::SetGlobalDefaultNumberOfThreads(1);
00118 av += 1;
00119 ac -= 1;
00120 }
00121 if (ac > 3 && strcmp(av[1], "--compare") == 0)
00122 {
00123 baselineFilename = av[2];
00124 testFilename = av[3];
00125 av += 3;
00126 ac -= 3;
00127
00128
00129 if (ac > 2 && strcmp(av[1], "--compareNumberOfPixelsTolerance") == 0)
00130 {
00131 numberOfPixelsTolerance = atoi( av[2] );
00132 av += 2;
00133 ac -= 2;
00134 }
00135 if (ac > 2 && strcmp(av[1], "--compareRadiusTolerance") == 0)
00136 {
00137 radiusTolerance = atoi( av[2] );
00138 av += 2;
00139 ac -= 2;
00140 }
00141 if (ac > 2 && strcmp(av[1], "--compareIntensityTolerance") == 0)
00142 {
00143 intensityTolerance = atof( av[2] );
00144 av += 2;
00145 ac -= 2;
00146 }
00147 }
00148 testToRun = av[1];
00149 }
00150 std::map<std::string, MainFuncPointer>::iterator j = StringToTestFunctionMap.find(testToRun);
00151 if(j != StringToTestFunctionMap.end())
00152 {
00153 MainFuncPointer f = j->second;
00154 int result;
00155 try
00156 {
00157
00158 result = (*f)(ac-1, av+1);
00159
00160
00161 if (baselineFilename && testFilename)
00162 {
00163 std::map<std::string,int> baselines = RegressionTestBaselines(baselineFilename);
00164 std::map<std::string,int>::iterator baseline = baselines.begin();
00165 std::string bestBaseline;
00166 int bestBaselineStatus = itk::NumericTraits<int>::max();
00167 while (baseline != baselines.end())
00168 {
00169 baseline->second = RegressionTestImage(testFilename,
00170 (baseline->first).c_str(),
00171 0,
00172 intensityTolerance,
00173 numberOfPixelsTolerance,
00174 radiusTolerance );
00175 if (baseline->second < bestBaselineStatus)
00176 {
00177 bestBaseline = baseline->first;
00178 bestBaselineStatus = baseline->second;
00179 }
00180 if (baseline->second == 0)
00181 {
00182 break;
00183 }
00184 ++baseline;
00185 }
00186
00187 if (bestBaselineStatus)
00188 {
00189 RegressionTestImage(testFilename,
00190 bestBaseline.c_str(),
00191 1,
00192 intensityTolerance,
00193 numberOfPixelsTolerance,
00194 radiusTolerance );
00195 }
00196
00197
00198 std::cout << "<DartMeasurement name=\"BaselineImageName\" type=\"text/string\">";
00199 std::cout << itksys::SystemTools::GetFilenameName(bestBaseline);
00200 std::cout << "</DartMeasurement>" << std::endl;
00201
00202 result += bestBaselineStatus;
00203 }
00204 }
00205 catch(const itk::ExceptionObject& e)
00206 {
00207 std::cerr << "ITK test driver caught an ITK exception:\n";
00208 std::cerr << e.GetFile() << ":" << e.GetLine() << ":\n"
00209 << e.GetDescription() << "\n";
00210 result = -1;
00211 }
00212 catch(const std::exception& e)
00213 {
00214 std::cerr << "ITK test driver caught an exception:\n";
00215 std::cerr << e.what() << "\n";
00216 result = -1;
00217 }
00218 catch(...)
00219 {
00220 std::cerr << "ITK test driver caught an unknown exception!!!\n";
00221 result = -1;
00222 }
00223 return result;
00224 }
00225 PrintAvailableTests();
00226 std::cerr << "Failed: " << testToRun << ": No test registered with name " << testToRun << "\n";
00227 return -1;
00228 }
00229
00230
00231
00232 int RegressionTestImage (const char *testImageFilename,
00233 const char *baselineImageFilename,
00234 int reportErrors,
00235 double intensityTolerance,
00236 unsigned int numberOfPixelsTolerance,
00237 unsigned int radiusTolerance )
00238 {
00239
00240 typedef itk::Image<double,ITK_TEST_DIMENSION_MAX> ImageType;
00241 typedef itk::Image<unsigned char,ITK_TEST_DIMENSION_MAX> OutputType;
00242 typedef itk::Image<unsigned char,2> DiffOutputType;
00243 typedef itk::ImageFileReader<ImageType> ReaderType;
00244
00245
00246 ReaderType::Pointer baselineReader = ReaderType::New();
00247 baselineReader->SetFileName(baselineImageFilename);
00248 try
00249 {
00250 baselineReader->UpdateLargestPossibleRegion();
00251 }
00252 catch (itk::ExceptionObject& e)
00253 {
00254 std::cerr << "Exception detected while reading " << baselineImageFilename << " : " << e.GetDescription();
00255 return 1000;
00256 }
00257
00258
00259 ReaderType::Pointer testReader = ReaderType::New();
00260 testReader->SetFileName(testImageFilename);
00261 try
00262 {
00263 testReader->UpdateLargestPossibleRegion();
00264 }
00265 catch (itk::ExceptionObject& e)
00266 {
00267 std::cerr << "Exception detected while reading " << testImageFilename << " : " << e.GetDescription() << std::endl;
00268 return 1000;
00269 }
00270
00271
00272 ImageType::SizeType baselineSize;
00273 baselineSize = baselineReader->GetOutput()->GetLargestPossibleRegion().GetSize();
00274 ImageType::SizeType testSize;
00275 testSize = testReader->GetOutput()->GetLargestPossibleRegion().GetSize();
00276
00277 if (baselineSize != testSize)
00278 {
00279 std::cerr << "The size of the Baseline image and Test image do not match!" << std::endl;
00280 std::cerr << "Baseline image: " << baselineImageFilename
00281 << " has size " << baselineSize << std::endl;
00282 std::cerr << "Test image: " << testImageFilename
00283 << " has size " << testSize << std::endl;
00284 return 1;
00285 }
00286
00287
00288 typedef itk::DifferenceImageFilter<ImageType,ImageType> DiffType;
00289 DiffType::Pointer diff = DiffType::New();
00290 diff->SetValidInput(baselineReader->GetOutput());
00291 diff->SetTestInput(testReader->GetOutput());
00292 diff->SetDifferenceThreshold( intensityTolerance );
00293 diff->SetToleranceRadius( radiusTolerance );
00294 diff->UpdateLargestPossibleRegion();
00295
00296 unsigned long status = 0;
00297 status = diff->GetNumberOfPixelsWithDifferences();
00298
00299
00300 if ( (status > numberOfPixelsTolerance) && reportErrors )
00301 {
00302 typedef itk::RescaleIntensityImageFilter<ImageType,OutputType> RescaleType;
00303 typedef itk::ExtractImageFilter<OutputType,DiffOutputType> ExtractType;
00304 typedef itk::ImageFileWriter<DiffOutputType> WriterType;
00305 typedef itk::ImageRegion<ITK_TEST_DIMENSION_MAX> RegionType;
00306 OutputType::IndexType index; index.Fill(0);
00307 OutputType::SizeType size; size.Fill(0);
00308
00309 RescaleType::Pointer rescale = RescaleType::New();
00310 rescale->SetOutputMinimum(itk::NumericTraits<unsigned char>::NonpositiveMin());
00311 rescale->SetOutputMaximum(itk::NumericTraits<unsigned char>::max());
00312 rescale->SetInput(diff->GetOutput());
00313 rescale->UpdateLargestPossibleRegion();
00314
00315 RegionType region;
00316 region.SetIndex(index);
00317
00318 size = rescale->GetOutput()->GetLargestPossibleRegion().GetSize();
00319 for (unsigned int i = 2; i < ITK_TEST_DIMENSION_MAX; i++)
00320 {
00321 size[i] = 0;
00322 }
00323 region.SetSize(size);
00324
00325 ExtractType::Pointer extract = ExtractType::New();
00326 extract->SetInput(rescale->GetOutput());
00327 extract->SetExtractionRegion(region);
00328
00329 WriterType::Pointer writer = WriterType::New();
00330 writer->SetInput(extract->GetOutput());
00331
00332 std::cout << "<DartMeasurement name=\"ImageError\" type=\"numeric/double\">";
00333 std::cout << status;
00334 std::cout << "</DartMeasurement>" << std::endl;
00335
00336 ::itk::OStringStream diffName;
00337 diffName << testImageFilename << ".diff.png";
00338 try
00339 {
00340 rescale->SetInput(diff->GetOutput());
00341 rescale->Update();
00342 }
00343 catch (...)
00344 {
00345 std::cerr << "Error during rescale of " << diffName.str() << std::endl;
00346 }
00347 writer->SetFileName(diffName.str().c_str());
00348 try
00349 {
00350 writer->Update();
00351 }
00352 catch (...)
00353 {
00354 std::cerr << "Error during write of " << diffName.str() << std::endl;
00355 }
00356
00357 std::cout << "<DartMeasurementFile name=\"DifferenceImage\" type=\"image/png\">";
00358 std::cout << diffName.str();
00359 std::cout << "</DartMeasurementFile>" << std::endl;
00360
00361 ::itk::OStringStream baseName;
00362 baseName << testImageFilename << ".base.png";
00363 try
00364 {
00365 rescale->SetInput(baselineReader->GetOutput());
00366 rescale->Update();
00367 }
00368 catch (...)
00369 {
00370 std::cerr << "Error during rescale of " << baseName.str() << std::endl;
00371 }
00372 try
00373 {
00374 writer->SetFileName(baseName.str().c_str());
00375 writer->Update();
00376 }
00377 catch (...)
00378 {
00379 std::cerr << "Error during write of " << baseName.str() << std::endl;
00380 }
00381
00382 std::cout << "<DartMeasurementFile name=\"BaselineImage\" type=\"image/png\">";
00383 std::cout << baseName.str();
00384 std::cout << "</DartMeasurementFile>" << std::endl;
00385
00386 ::itk::OStringStream testName;
00387 testName << testImageFilename << ".test.png";
00388 try
00389 {
00390 rescale->SetInput(testReader->GetOutput());
00391 rescale->Update();
00392 }
00393 catch (...)
00394 {
00395 std::cerr << "Error during rescale of " << testName.str()
00396 << std::endl;
00397 }
00398 try
00399 {
00400 writer->SetFileName(testName.str().c_str());
00401 writer->Update();
00402 }
00403 catch (...)
00404 {
00405 std::cerr << "Error during write of " << testName.str() << std::endl;
00406 }
00407
00408 std::cout << "<DartMeasurementFile name=\"TestImage\" type=\"image/png\">";
00409 std::cout << testName.str();
00410 std::cout << "</DartMeasurementFile>" << std::endl;
00411
00412
00413 }
00414 return (status > numberOfPixelsTolerance) ? 1 : 0;
00415 }
00416
00417
00418
00419
00420
00421
00422
00423
00424
00425 std::map<std::string,int> RegressionTestBaselines (char *baselineFilename)
00426 {
00427 std::map<std::string,int> baselines;
00428 baselines[std::string(baselineFilename)] = 0;
00429
00430 std::string originalBaseline(baselineFilename);
00431
00432 int x = 0;
00433 std::string::size_type suffixPos = originalBaseline.rfind(".");
00434 std::string suffix;
00435 if (suffixPos != std::string::npos)
00436 {
00437 suffix = originalBaseline.substr(suffixPos,originalBaseline.length());
00438 originalBaseline.erase(suffixPos,originalBaseline.length());
00439 }
00440 while (++x)
00441 {
00442 ::itk::OStringStream filename;
00443 filename << originalBaseline << "." << x << suffix;
00444 std::ifstream filestream(filename.str().c_str());
00445 if (!filestream)
00446 {
00447 break;
00448 }
00449 baselines[filename.str()] = 0;
00450 filestream.close();
00451 }
00452 return baselines;
00453 }
00454
00455
00456 #include "itkDifferenceImageFilter.txx"
00457