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: 2008-10-24 16:21:55 $
00007   Version:   $Revision: 1.31 $
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   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       // Invoke the test's "main" function.
00162       result = (*f)(ac-1, av+1);
00163 
00164       // Make a list of possible baselines
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         // if the best we can do still has errors, generate the error images
00193          if (bestBaselineStatus)
00194           {
00195           RegressionTestImage(testFilename,
00196                               bestBaseline.c_str(),
00197                               1, 
00198                               intensityTolerance,
00199                               numberOfPixelsTolerance, 
00200                               radiusTolerance );
00201           }
00202 
00203         // output the matching baseline
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 // Regression Testing Code
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   // Use the factory mechanism to read the test and baseline files and convert them to double
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   // Read the baseline file
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   // Read the file generated by the test
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   // The sizes of the baseline and test image must match
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   // Now compare the two images
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   // if there are discrepencies, create an diff image
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::IndexType index; index.Fill(0);
00312     OutputType::SizeType size; size.Fill(0);
00313 
00314     RescaleType::Pointer rescale = RescaleType::New();
00315       rescale->SetOutputMinimum(itk::NumericTraits<unsigned char>::NonpositiveMin());
00316       rescale->SetOutputMaximum(itk::NumericTraits<unsigned char>::max());
00317       rescale->SetInput(diff->GetOutput());
00318       rescale->UpdateLargestPossibleRegion();
00319 
00320     RegionType region;
00321     region.SetIndex(index);
00322     
00323     size = rescale->GetOutput()->GetLargestPossibleRegion().GetSize();
00324     for (unsigned int i = 2; i < ITK_TEST_DIMENSION_MAX; i++)
00325       {
00326       size[i] = 0;
00327       }
00328     region.SetSize(size);
00329 
00330     ExtractType::Pointer extract = ExtractType::New();
00331       extract->SetInput(rescale->GetOutput());
00332       extract->SetExtractionRegion(region);
00333 
00334     WriterType::Pointer writer = WriterType::New();
00335       writer->SetInput(extract->GetOutput());
00336 
00337     std::cout << "<DartMeasurement name=\"ImageError\" type=\"numeric/double\">";
00338     std::cout << status;
00339     std::cout <<  "</DartMeasurement>" << std::endl;
00340 
00341     ::itk::OStringStream diffName;
00342       diffName << testImageFilename << ".diff.png";
00343     try
00344       {
00345       rescale->SetInput(diff->GetOutput());
00346       rescale->Update();
00347       }
00348     catch (...)
00349       {
00350       std::cerr << "Error during rescale of " << diffName.str() << std::endl;
00351       }
00352     writer->SetFileName(diffName.str().c_str());
00353     try
00354       {
00355       writer->Update();
00356       }
00357     catch (...)
00358       {
00359       std::cerr << "Error during write of " << diffName.str() << std::endl;
00360       }
00361 
00362     std::cout << "<DartMeasurementFile name=\"DifferenceImage\" type=\"image/png\">";
00363     std::cout << diffName.str();
00364     std::cout << "</DartMeasurementFile>" << std::endl;
00365 
00366     ::itk::OStringStream baseName;
00367     baseName << testImageFilename << ".base.png";
00368     try
00369       {
00370       rescale->SetInput(baselineReader->GetOutput());
00371       rescale->Update();
00372       }
00373     catch (...)
00374       {
00375       std::cerr << "Error during rescale of " << baseName.str() << std::endl;
00376       }
00377     try
00378       {
00379       writer->SetFileName(baseName.str().c_str());
00380       writer->Update();
00381       }
00382     catch (...)
00383       {
00384       std::cerr << "Error during write of " << baseName.str() << std::endl;
00385       }
00386 
00387     std::cout << "<DartMeasurementFile name=\"BaselineImage\" type=\"image/png\">";
00388     std::cout << baseName.str();
00389     std::cout << "</DartMeasurementFile>" << std::endl;
00390 
00391     ::itk::OStringStream testName;
00392     testName << testImageFilename << ".test.png";
00393     try
00394       {
00395       rescale->SetInput(testReader->GetOutput());
00396       rescale->Update();
00397       }
00398     catch (...)
00399       {
00400       std::cerr << "Error during rescale of " << testName.str()
00401                 << std::endl;
00402       }
00403     try
00404       {
00405       writer->SetFileName(testName.str().c_str());
00406       writer->Update();
00407       }
00408     catch (...)
00409       {
00410       std::cerr << "Error during write of " << testName.str() << std::endl;
00411       }
00412 
00413     std::cout << "<DartMeasurementFile name=\"TestImage\" type=\"image/png\">";
00414     std::cout << testName.str();
00415     std::cout << "</DartMeasurementFile>" << std::endl;
00416 
00417 
00418     }
00419   return (status > numberOfPixelsTolerance) ? 1 : 0;
00420 }
00421 
00422 //
00423 // Generate all of the possible baselines
00424 // The possible baselines are generated fromn the baselineFilename using the following algorithm:
00425 // 1) strip the suffix
00426 // 2) append a digit .x
00427 // 3) append the original suffix.
00428 // It the file exists, increment x and continue
00429 //
00430 std::map<std::string,int> RegressionTestBaselines (char *baselineFilename)
00431 {
00432   std::map<std::string,int> baselines;
00433   baselines[std::string(baselineFilename)] = 0;
00434 
00435   std::string originalBaseline(baselineFilename);
00436 
00437   int x = 0;
00438   std::string::size_type suffixPos = originalBaseline.rfind(".");
00439   std::string suffix;
00440   if (suffixPos != std::string::npos)
00441     {
00442     suffix = originalBaseline.substr(suffixPos,originalBaseline.length());
00443     originalBaseline.erase(suffixPos,originalBaseline.length());
00444     }
00445   while (++x)
00446     {
00447     ::itk::OStringStream filename;
00448     filename << originalBaseline << "." << x << suffix;
00449     std::ifstream filestream(filename.str().c_str());
00450     if (!filestream)
00451       {
00452         break;
00453       }
00454     baselines[filename.str()] = 0;
00455     filestream.close();
00456     }
00457   return baselines;
00458 }
00459 
00460 // Needed for explicit instantiation
00461 #include "itkDifferenceImageFilter.txx"
00462 

Generated at Thu Nov 6 00:25:18 2008 for ITK by doxygen 1.5.1 written by Dimitri van Heesch, © 1997-2000