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 
00004   Program:   Insight Segmentation & Registration Toolkit
00005   Module:    $RCSfile: itkTestMain.h,v $
00006   Language:  C++
00007   Date:      $Date: 2007/02/05 13:35:53 $
00008   Version:   $Revision: 1.25 $
00009 
00010   Copyright (c) Insight Software Consortium. All rights reserved.
00011   See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details.
00012 
00013   Portions of this code are covered under the VTK copyright.
00014   See VTKCopyright.txt or http://www.kitware.com/VTKCopyright.htm for details.
00015 
00016      This software is distributed WITHOUT ANY WARRANTY; without even 
00017      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
00018      PURPOSE.  See the above copyright notices for more information.
00019 
00020 =========================================================================*/
00021 
00022 // This file is used to create TestDriver executables
00023 // These executables are able to register a function pointer to a string name
00024 // in a lookup table.   By including this file, it creates a main function
00025 // that calls RegisterTests() then looks up the function pointer for the test
00026 // specified on the command line.
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       // Advanced options for image comparison (The options must appear in 
00129       // this order in the command line because this parser code is not that smart)
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       // Invoke the test's "main" function.
00159       result = (*f)(ac-1, av+1);
00160 
00161       // Make a list of possible baselines
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         // if the best we can do still has errors, generate the error images
00188         if (bestBaselineStatus)
00189           {
00190           baseline->second = RegressionTestImage(testFilename,
00191                                                  bestBaseline.c_str(),
00192                                                  1, 
00193                                                  intensityTolerance,
00194                                                  numberOfPixelsTolerance, 
00195                                                  radiusTolerance );
00196           }
00197 
00198         // output the matching baseline
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 // Regression Testing Code
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   // Use the factory mechanism to read the test and baseline files and convert them to double
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   // Read the baseline file
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   // Read the file generated by the test
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   // The sizes of the baseline and test image must match
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   // Now compare the two images
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   // if there are discrepencies, create an diff image
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 // Generate all of the possible baselines
00423 // The possible baselines are generated fromn the baselineFilename using the following algorithm:
00424 // 1) strip the suffix
00425 // 2) append a digit .x
00426 // 3) append the original suffix.
00427 // It the file exists, increment x and continue
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 // Needed for explicit instantiation
00460 #include "itkDifferenceImageFilter.txx"
00461 

Generated at Mon Mar 12 03:11:07 2007 for ITK by doxygen 1.5.1 written by Dimitri van Heesch, © 1997-2000