Main Page   Groups   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Namespace Members   Compound Members   File Members   Concepts

itkTestMain.h

Go to the documentation of this file.
00001 /*=========================================================================
00002 
00003   Program:   Insight Segmentation & Registration Toolkit
00004   Module:    $RCSfile: itkTestMain.h,v $
00005   Language:  C++
00006   Date:      $Date: 2007/07/09 01:19:33 $
00007   Version:   $Revision: 1.26 $
00008 
00009   Copyright (c) Insight Software Consortium. All rights reserved.
00010   See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details.
00011 
00012   Portions of this code are covered under the VTK copyright.
00013   See VTKCopyright.txt or http://www.kitware.com/VTKCopyright.htm for details.
00014 
00015      This software is distributed WITHOUT ANY WARRANTY; without even 
00016      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
00017      PURPOSE.  See the above copyright notices for more information.
00018 
00019 =========================================================================*/
00020 
00021 // This file is used to create TestDriver executables
00022 // These executables are able to register a function pointer to a string name
00023 // in a lookup table.   By including this file, it creates a main function
00024 // that calls RegisterTests() then looks up the function pointer for the test
00025 // specified on the command line.
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       // Advanced options for image comparison (The options must appear in 
00128       // this order in the command line because this parser code is not that smart)
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       // Invoke the test's "main" function.
00158       result = (*f)(ac-1, av+1);
00159 
00160       // Make a list of possible baselines
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         // if the best we can do still has errors, generate the error images
00187          if (bestBaselineStatus)
00188           {
00189           RegressionTestImage(testFilename,
00190                               bestBaseline.c_str(),
00191                               1, 
00192                               intensityTolerance,
00193                               numberOfPixelsTolerance, 
00194                               radiusTolerance );
00195           }
00196 
00197         // output the matching baseline
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 // Regression Testing Code
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   // Use the factory mechanism to read the test and baseline files and convert them to double
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   // Read the baseline file
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   // Read the file generated by the test
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   // The sizes of the baseline and test image must match
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   // Now compare the two images
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   double status = diff->GetTotalDifference();
00297 
00298   const unsigned int numberOfFailedPixels = 
00299     diff->GetNumberOfPixelsWithDifferences();
00300 
00301   // if there are discrepencies, create an diff image
00302   if ( status && reportErrors &&  
00303       ( numberOfFailedPixels > numberOfPixelsTolerance ) )
00304     {
00305     typedef itk::RescaleIntensityImageFilter<ImageType,OutputType> RescaleType;
00306     typedef itk::ExtractImageFilter<OutputType,DiffOutputType> ExtractType;
00307     typedef itk::ImageFileWriter<DiffOutputType> WriterType;
00308     typedef itk::ImageRegion<ITK_TEST_DIMENSION_MAX> RegionType;
00309     OutputType::IndexType index; index.Fill(0);
00310     OutputType::SizeType size; size.Fill(0);
00311 
00312     RescaleType::Pointer rescale = RescaleType::New();
00313       rescale->SetOutputMinimum(itk::NumericTraits<unsigned char>::NonpositiveMin());
00314       rescale->SetOutputMaximum(itk::NumericTraits<unsigned char>::max());
00315       rescale->SetInput(diff->GetOutput());
00316       rescale->UpdateLargestPossibleRegion();
00317 
00318     RegionType region;
00319     region.SetIndex(index);
00320     
00321     size = rescale->GetOutput()->GetLargestPossibleRegion().GetSize();
00322     for (unsigned int i = 2; i < ITK_TEST_DIMENSION_MAX; i++)
00323       {
00324       size[i] = 0;
00325       }
00326     region.SetSize(size);
00327 
00328     ExtractType::Pointer extract = ExtractType::New();
00329       extract->SetInput(rescale->GetOutput());
00330       extract->SetExtractionRegion(region);
00331 
00332     WriterType::Pointer writer = WriterType::New();
00333       writer->SetInput(extract->GetOutput());
00334 
00335     std::cout << "<DartMeasurement name=\"ImageError\" type=\"numeric/double\">";
00336     std::cout << status;
00337     std::cout <<  "</DartMeasurement>" << std::endl;
00338 
00339     ::itk::OStringStream diffName;
00340       diffName << testImageFilename << ".diff.png";
00341     try
00342       {
00343       rescale->SetInput(diff->GetOutput());
00344       rescale->Update();
00345       }
00346     catch (...)
00347       {
00348       std::cerr << "Error during rescale of " << diffName.str() << std::endl;
00349       }
00350     writer->SetFileName(diffName.str().c_str());
00351     try
00352       {
00353       writer->Update();
00354       }
00355     catch (...)
00356       {
00357       std::cerr << "Error during write of " << diffName.str() << std::endl;
00358       }
00359 
00360     std::cout << "<DartMeasurementFile name=\"DifferenceImage\" type=\"image/png\">";
00361     std::cout << diffName.str();
00362     std::cout << "</DartMeasurementFile>" << std::endl;
00363 
00364     ::itk::OStringStream baseName;
00365     baseName << testImageFilename << ".base.png";
00366     try
00367       {
00368       rescale->SetInput(baselineReader->GetOutput());
00369       rescale->Update();
00370       }
00371     catch (...)
00372       {
00373       std::cerr << "Error during rescale of " << baseName.str() << std::endl;
00374       }
00375     try
00376       {
00377       writer->SetFileName(baseName.str().c_str());
00378       writer->Update();
00379       }
00380     catch (...)
00381       {
00382       std::cerr << "Error during write of " << baseName.str() << std::endl;
00383       }
00384 
00385     std::cout << "<DartMeasurementFile name=\"BaselineImage\" type=\"image/png\">";
00386     std::cout << baseName.str();
00387     std::cout << "</DartMeasurementFile>" << std::endl;
00388 
00389     ::itk::OStringStream testName;
00390     testName << testImageFilename << ".test.png";
00391     try
00392       {
00393       rescale->SetInput(testReader->GetOutput());
00394       rescale->Update();
00395       }
00396     catch (...)
00397       {
00398       std::cerr << "Error during rescale of " << testName.str()
00399                 << std::endl;
00400       }
00401     try
00402       {
00403       writer->SetFileName(testName.str().c_str());
00404       writer->Update();
00405       }
00406     catch (...)
00407       {
00408       std::cerr << "Error during write of " << testName.str() << std::endl;
00409       }
00410 
00411     std::cout << "<DartMeasurementFile name=\"TestImage\" type=\"image/png\">";
00412     std::cout << testName.str();
00413     std::cout << "</DartMeasurementFile>" << std::endl;
00414 
00415 
00416     }
00417   return (status != 0) ? 1 : 0;
00418 }
00419 
00420 //
00421 // Generate all of the possible baselines
00422 // The possible baselines are generated fromn the baselineFilename using the following algorithm:
00423 // 1) strip the suffix
00424 // 2) append a digit .x
00425 // 3) append the original suffix.
00426 // It the file exists, increment x and continue
00427 //
00428 std::map<std::string,int> RegressionTestBaselines (char *baselineFilename)
00429 {
00430   std::map<std::string,int> baselines;
00431   baselines[std::string(baselineFilename)] = 0;
00432 
00433   std::string originalBaseline(baselineFilename);
00434 
00435   int x = 0;
00436   std::string::size_type suffixPos = originalBaseline.rfind(".");
00437   std::string suffix;
00438   if (suffixPos != std::string::npos)
00439     {
00440     suffix = originalBaseline.substr(suffixPos,originalBaseline.length());
00441     originalBaseline.erase(suffixPos,originalBaseline.length());
00442     }
00443   while (++x)
00444     {
00445     ::itk::OStringStream filename;
00446     filename << originalBaseline << "." << x << suffix;
00447     std::ifstream filestream(filename.str().c_str());
00448     if (!filestream)
00449       {
00450         break;
00451       }
00452     baselines[filename.str()] = 0;
00453     filestream.close();
00454     }
00455   return baselines;
00456 }
00457 
00458 // Needed for explicit instantiation
00459 #include "itkDifferenceImageFilter.txx"
00460 

Generated at Sun Sep 23 14:24:16 2007 for ITK by doxygen 1.5.1 written by Dimitri van Heesch, © 1997-2000