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 double intensityTolerance = 2.0;
00079 unsigned int numberOfPixelsTolerance = 0;
00080 unsigned int radiusTolerance = 0;
00081
00082 typedef std::pair< char *, char *> ComparePairType;
00083 std::vector< ComparePairType > compareList;
00084
00085 RegisterTests();
00086 std::string testToRun;
00087 if(ac < 2)
00088 {
00089 PrintAvailableTests();
00090 std::cout << "To run a test, enter the test number: ";
00091 int testNum = 0;
00092 std::cin >> testNum;
00093 std::map<std::string, MainFuncPointer>::iterator j = StringToTestFunctionMap.begin();
00094 int i = 0;
00095 while(j != StringToTestFunctionMap.end() && i < testNum)
00096 {
00097 ++i;
00098 ++j;
00099 }
00100 if(j == StringToTestFunctionMap.end())
00101 {
00102 std::cerr << testNum << " is an invalid test number\n";
00103 return -1;
00104 }
00105 testToRun = j->first;
00106 }
00107 else
00108 {
00109 while( ac > 0 && testToRun.empty() )
00110 {
00111 if (strcmp(av[1], "--with-threads") == 0)
00112 {
00113 int numThreads = atoi(av[2]);
00114 itk::MultiThreader::SetGlobalDefaultNumberOfThreads(numThreads);
00115 av += 2;
00116 ac -= 2;
00117 }
00118 else if (strcmp(av[1], "--without-threads") == 0)
00119 {
00120 itk::MultiThreader::SetGlobalDefaultNumberOfThreads(1);
00121 av += 1;
00122 ac -= 1;
00123 }
00124 else if (ac > 3 && strcmp(av[1], "--compare") == 0)
00125 {
00126 compareList.push_back( ComparePairType( av[2], av[3] ) );
00127 av += 3;
00128 ac -= 3;
00129 }
00130 else if (ac > 2 && strcmp(av[1], "--compareNumberOfPixelsTolerance") == 0)
00131 {
00132 numberOfPixelsTolerance = atoi( av[2] );
00133 av += 2;
00134 ac -= 2;
00135 }
00136 else if (ac > 2 && strcmp(av[1], "--compareRadiusTolerance") == 0)
00137 {
00138 radiusTolerance = atoi( av[2] );
00139 av += 2;
00140 ac -= 2;
00141 }
00142 else if (ac > 2 && strcmp(av[1], "--compareIntensityTolerance") == 0)
00143 {
00144 intensityTolerance = atof( av[2] );
00145 av += 2;
00146 ac -= 2;
00147 }
00148 else
00149 {
00150 testToRun = av[1];
00151 }
00152 }
00153 }
00154 std::map<std::string, MainFuncPointer>::iterator j = StringToTestFunctionMap.find(testToRun);
00155 if(j != StringToTestFunctionMap.end())
00156 {
00157 MainFuncPointer f = j->second;
00158 int result;
00159 try
00160 {
00161
00162 result = (*f)(ac-1, av+1);
00163
00164
00165 for( int i=0; i<static_cast<int>(compareList.size()); i++)
00166 {
00167 char * baselineFilename = compareList[i].first;
00168 char * testFilename = compareList[i].second;
00169 std::map<std::string,int> baselines = RegressionTestBaselines(baselineFilename);
00170 std::map<std::string,int>::iterator baseline = baselines.begin();
00171 std::string bestBaseline;
00172 int bestBaselineStatus = itk::NumericTraits<int>::max();
00173 while (baseline != baselines.end())
00174 {
00175 baseline->second = RegressionTestImage(testFilename,
00176 (baseline->first).c_str(),
00177 0,
00178 intensityTolerance,
00179 numberOfPixelsTolerance,
00180 radiusTolerance );
00181 if (baseline->second < bestBaselineStatus)
00182 {
00183 bestBaseline = baseline->first;
00184 bestBaselineStatus = baseline->second;
00185 }
00186 if (baseline->second == 0)
00187 {
00188 break;
00189 }
00190 ++baseline;
00191 }
00192
00193 if (bestBaselineStatus)
00194 {
00195 RegressionTestImage(testFilename,
00196 bestBaseline.c_str(),
00197 1,
00198 intensityTolerance,
00199 numberOfPixelsTolerance,
00200 radiusTolerance );
00201 }
00202
00203
00204 std::cout << "<DartMeasurement name=\"BaselineImageName\" type=\"text/string\">";
00205 std::cout << itksys::SystemTools::GetFilenameName(bestBaseline);
00206 std::cout << "</DartMeasurement>" << std::endl;
00207
00208 result += bestBaselineStatus;
00209 }
00210 }
00211 catch(const itk::ExceptionObject& e)
00212 {
00213 std::cerr << "ITK test driver caught an ITK exception:\n";
00214 e.Print(std::cerr);
00215 result = -1;
00216 }
00217 catch(const std::exception& e)
00218 {
00219 std::cerr << "ITK test driver caught an exception:\n";
00220 std::cerr << e.what() << "\n";
00221 result = -1;
00222 }
00223 catch(...)
00224 {
00225 std::cerr << "ITK test driver caught an unknown exception!!!\n";
00226 result = -1;
00227 }
00228 return result;
00229 }
00230 PrintAvailableTests();
00231 std::cerr << "Failed: " << testToRun << ": No test registered with name " << testToRun << "\n";
00232 return -1;
00233 }
00234
00235
00236
00237 int RegressionTestImage (const char *testImageFilename,
00238 const char *baselineImageFilename,
00239 int reportErrors,
00240 double intensityTolerance,
00241 unsigned int numberOfPixelsTolerance,
00242 unsigned int radiusTolerance )
00243 {
00244
00245 typedef itk::Image<double,ITK_TEST_DIMENSION_MAX> ImageType;
00246 typedef itk::Image<unsigned char,ITK_TEST_DIMENSION_MAX> OutputType;
00247 typedef itk::Image<unsigned char,2> DiffOutputType;
00248 typedef itk::ImageFileReader<ImageType> ReaderType;
00249
00250
00251 ReaderType::Pointer baselineReader = ReaderType::New();
00252 baselineReader->SetFileName(baselineImageFilename);
00253 try
00254 {
00255 baselineReader->UpdateLargestPossibleRegion();
00256 }
00257 catch (itk::ExceptionObject& e)
00258 {
00259 std::cerr << "Exception detected while reading " << baselineImageFilename << " : " << e.GetDescription();
00260 return 1000;
00261 }
00262
00263
00264 ReaderType::Pointer testReader = ReaderType::New();
00265 testReader->SetFileName(testImageFilename);
00266 try
00267 {
00268 testReader->UpdateLargestPossibleRegion();
00269 }
00270 catch (itk::ExceptionObject& e)
00271 {
00272 std::cerr << "Exception detected while reading " << testImageFilename << " : " << e.GetDescription() << std::endl;
00273 return 1000;
00274 }
00275
00276
00277 ImageType::SizeType baselineSize;
00278 baselineSize = baselineReader->GetOutput()->GetLargestPossibleRegion().GetSize();
00279 ImageType::SizeType testSize;
00280 testSize = testReader->GetOutput()->GetLargestPossibleRegion().GetSize();
00281
00282 if (baselineSize != testSize)
00283 {
00284 std::cerr << "The size of the Baseline image and Test image do not match!" << std::endl;
00285 std::cerr << "Baseline image: " << baselineImageFilename
00286 << " has size " << baselineSize << std::endl;
00287 std::cerr << "Test image: " << testImageFilename
00288 << " has size " << testSize << std::endl;
00289 return 1;
00290 }
00291
00292
00293 typedef itk::DifferenceImageFilter<ImageType,ImageType> DiffType;
00294 DiffType::Pointer diff = DiffType::New();
00295 diff->SetValidInput(baselineReader->GetOutput());
00296 diff->SetTestInput(testReader->GetOutput());
00297 diff->SetDifferenceThreshold( intensityTolerance );
00298 diff->SetToleranceRadius( radiusTolerance );
00299 diff->UpdateLargestPossibleRegion();
00300
00301 unsigned long status = 0;
00302 status = diff->GetNumberOfPixelsWithDifferences();
00303
00304
00305 if ( (status > numberOfPixelsTolerance) && reportErrors )
00306 {
00307 typedef itk::RescaleIntensityImageFilter<ImageType,OutputType> RescaleType;
00308 typedef itk::ExtractImageFilter<OutputType,DiffOutputType> ExtractType;
00309 typedef itk::ImageFileWriter<DiffOutputType> WriterType;
00310 typedef itk::ImageRegion<ITK_TEST_DIMENSION_MAX> RegionType;
00311 OutputType::SizeType size; size.Fill(0);
00312
00313 RescaleType::Pointer rescale = RescaleType::New();
00314 rescale->SetOutputMinimum(itk::NumericTraits<unsigned char>::NonpositiveMin());
00315 rescale->SetOutputMaximum(itk::NumericTraits<unsigned char>::max());
00316 rescale->SetInput(diff->GetOutput());
00317 rescale->UpdateLargestPossibleRegion();
00318 size = rescale->GetOutput()->GetLargestPossibleRegion().GetSize();
00319
00320
00321
00322 OutputType::IndexType index; index.Fill(0);
00323 for (unsigned int i = 2; i < ITK_TEST_DIMENSION_MAX; i++)
00324 {
00325 index[i]=size[i]/2;
00326 size[i] = 0;
00327 }
00328
00329 RegionType region;
00330 region.SetIndex(index);
00331
00332 region.SetSize(size);
00333
00334 ExtractType::Pointer extract = ExtractType::New();
00335 extract->SetInput(rescale->GetOutput());
00336 extract->SetExtractionRegion(region);
00337
00338 WriterType::Pointer writer = WriterType::New();
00339 writer->SetInput(extract->GetOutput());
00340
00341 std::cout << "<DartMeasurement name=\"ImageError\" type=\"numeric/double\">";
00342 std::cout << status;
00343 std::cout << "</DartMeasurement>" << std::endl;
00344
00345 ::itk::OStringStream diffName;
00346 diffName << testImageFilename << ".diff.png";
00347 try
00348 {
00349 rescale->SetInput(diff->GetOutput());
00350 rescale->Update();
00351 }
00352 catch(const std::exception& e)
00353 {
00354 std::cerr << "Error during rescale of " << diffName.str() << std::endl;
00355 std::cerr << e.what() << "\n";
00356 }
00357 catch (...)
00358 {
00359 std::cerr << "Error during rescale of " << diffName.str() << std::endl;
00360 }
00361 writer->SetFileName(diffName.str().c_str());
00362 try
00363 {
00364 writer->Update();
00365 }
00366 catch(const std::exception& e)
00367 {
00368 std::cerr << "Error during write of " << diffName.str() << std::endl;
00369 std::cerr << e.what() << "\n";
00370 }
00371 catch (...)
00372 {
00373 std::cerr << "Error during write of " << diffName.str() << std::endl;
00374 }
00375
00376 std::cout << "<DartMeasurementFile name=\"DifferenceImage\" type=\"image/png\">";
00377 std::cout << diffName.str();
00378 std::cout << "</DartMeasurementFile>" << std::endl;
00379
00380 ::itk::OStringStream baseName;
00381 baseName << testImageFilename << ".base.png";
00382 try
00383 {
00384 rescale->SetInput(baselineReader->GetOutput());
00385 rescale->Update();
00386 }
00387 catch(const std::exception& e)
00388 {
00389 std::cerr << "Error during rescale of " << baseName.str() << std::endl;
00390 std::cerr << e.what() << "\n";
00391 }
00392 catch (...)
00393 {
00394 std::cerr << "Error during rescale of " << baseName.str() << std::endl;
00395 }
00396 try
00397 {
00398 writer->SetFileName(baseName.str().c_str());
00399 writer->Update();
00400 }
00401 catch(const std::exception& e)
00402 {
00403 std::cerr << "Error during write of " << baseName.str() << std::endl;
00404 std::cerr << e.what() << "\n";
00405 }
00406 catch (...)
00407 {
00408 std::cerr << "Error during write of " << baseName.str() << std::endl;
00409 }
00410
00411 std::cout << "<DartMeasurementFile name=\"BaselineImage\" type=\"image/png\">";
00412 std::cout << baseName.str();
00413 std::cout << "</DartMeasurementFile>" << std::endl;
00414
00415 ::itk::OStringStream testName;
00416 testName << testImageFilename << ".test.png";
00417 try
00418 {
00419 rescale->SetInput(testReader->GetOutput());
00420 rescale->Update();
00421 }
00422 catch(const std::exception& e)
00423 {
00424 std::cerr << "Error during rescale of " << testName.str() << std::endl;
00425 std::cerr << e.what() << "\n";
00426 }
00427 catch (...)
00428 {
00429 std::cerr << "Error during rescale of " << testName.str() << std::endl;
00430 }
00431 try
00432 {
00433 writer->SetFileName(testName.str().c_str());
00434 writer->Update();
00435 }
00436 catch(const std::exception& e)
00437 {
00438 std::cerr << "Error during write of " << testName.str() << std::endl;
00439 std::cerr << e.what() << "\n";
00440 }
00441 catch (...)
00442 {
00443 std::cerr << "Error during write of " << testName.str() << std::endl;
00444 }
00445
00446 std::cout << "<DartMeasurementFile name=\"TestImage\" type=\"image/png\">";
00447 std::cout << testName.str();
00448 std::cout << "</DartMeasurementFile>" << std::endl;
00449
00450
00451 }
00452 return (status > numberOfPixelsTolerance) ? 1 : 0;
00453 }
00454
00455
00456
00457
00458
00459
00460
00461
00462
00463 std::map<std::string,int> RegressionTestBaselines (char *baselineFilename)
00464 {
00465 std::map<std::string,int> baselines;
00466 baselines[std::string(baselineFilename)] = 0;
00467
00468 std::string originalBaseline(baselineFilename);
00469
00470 int x = 0;
00471 std::string::size_type suffixPos = originalBaseline.rfind(".");
00472 std::string suffix;
00473 if (suffixPos != std::string::npos)
00474 {
00475 suffix = originalBaseline.substr(suffixPos,originalBaseline.length());
00476 originalBaseline.erase(suffixPos,originalBaseline.length());
00477 }
00478 while (++x)
00479 {
00480 ::itk::OStringStream filename;
00481 filename << originalBaseline << "." << x << suffix;
00482 std::ifstream filestream(filename.str().c_str());
00483 if (!filestream)
00484 {
00485 break;
00486 }
00487 baselines[filename.str()] = 0;
00488 filestream.close();
00489 }
00490 return baselines;
00491 }
00492
00493
00494 #include "itkDifferenceImageFilter.txx"
00495