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