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: 2003/12/15 14:13:19 $
00008   Version:   $Revision: 1.18 $
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 
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 *, const char *, int);
00054 std::map<std::string,int> RegressionTestBaselines (char *);
00055 
00056 void RegisterTests();
00057 void PrintAvailableTests()
00058 {
00059   std::cout << "Available tests:\n";
00060   std::map<std::string, MainFuncPointer>::iterator j = StringToTestFunctionMap.begin();
00061   int i = 0;
00062   while(j != StringToTestFunctionMap.end())
00063     {
00064     std::cout << i << ". " << j->first << "\n";
00065     ++i;
00066     ++j;
00067     }
00068 }
00069 
00070 int main(int ac, char* av[] )
00071 {
00072   char *baselineFilename = NULL;
00073   char *testFilename = NULL;
00074 
00075   RegisterTests();
00076   std::string testToRun;
00077   if(ac < 2)
00078     {
00079     PrintAvailableTests();
00080     std::cout << "To run a test, enter the test number: ";
00081     int testNum = 0;
00082     std::cin >> testNum;
00083     std::map<std::string, MainFuncPointer>::iterator j = StringToTestFunctionMap.begin();
00084     int i = 0;
00085     while(j != StringToTestFunctionMap.end() && i < testNum)
00086       {
00087       ++i;
00088       ++j;
00089       }
00090     if(j == StringToTestFunctionMap.end())
00091       {
00092       std::cerr << testNum << " is an invalid test number\n";
00093       return -1;
00094       }
00095     testToRun = j->first;
00096     }
00097   else
00098     {
00099     if (strcmp(av[1], "--with-threads") == 0)
00100       {
00101       int numThreads = atoi(av[2]);
00102       itk::MultiThreader::SetGlobalDefaultNumberOfThreads(numThreads);
00103       av += 2;
00104       ac -= 2;
00105       }
00106     else if (strcmp(av[1], "--without-threads") == 0)
00107       {
00108       itk::MultiThreader::SetGlobalDefaultNumberOfThreads(1);
00109       av += 1;
00110       ac -= 1;
00111       }
00112     else if (strcmp(av[1], "--compare") == 0)
00113       {
00114       baselineFilename = av[2];
00115       testFilename = av[3];
00116       av += 3;
00117       ac -= 3;
00118       }
00119     testToRun = av[1];
00120     }
00121   std::map<std::string, MainFuncPointer>::iterator j = StringToTestFunctionMap.find(testToRun);
00122   if(j != StringToTestFunctionMap.end())
00123     {
00124     MainFuncPointer f = j->second;
00125     int result;
00126     try
00127       {
00128       // Invoke the test's "main" function.
00129       result = (*f)(ac-1, av+1);
00130 
00131       // Make a list of possible baselines
00132       if (baselineFilename && testFilename)
00133         {
00134         std::map<std::string,int> baselines = RegressionTestBaselines(baselineFilename);
00135         std::map<std::string,int>::iterator baseline = baselines.begin();
00136         std::string bestBaseline;
00137         int bestBaselineStatus = itk::NumericTraits<int>::max();
00138         while (baseline != baselines.end())
00139           {
00140           baseline->second = RegressionTestImage(testFilename,
00141                                                  (baseline->first).c_str(),
00142                                                  0);
00143           if (baseline->second < bestBaselineStatus)
00144             {
00145             bestBaseline = baseline->first;
00146             bestBaselineStatus = baseline->second;
00147             }
00148           if (baseline->second == 0)
00149             {
00150             break;
00151             }
00152           ++baseline;
00153           }
00154         // if the best we can do still has errors, generate the error images
00155         if (bestBaselineStatus)
00156           {
00157           baseline->second = RegressionTestImage(testFilename,
00158                                                  bestBaseline.c_str(),
00159                                                  1);
00160           }
00161         result += bestBaselineStatus;
00162         }
00163       }
00164     catch(const itk::ExceptionObject& e)
00165       {
00166       std::cerr << "ITK test driver caught an ITK exception:\n";
00167       std::cerr << e.GetFile() << ":" << e.GetLine() << ":\n"
00168                 << e.GetDescription() << "\n";
00169       result = -1;
00170       }
00171     catch(const std::exception& e)
00172       {
00173       std::cerr << "ITK test driver caught an exception:\n";
00174       std::cerr << e.what() << "\n";
00175       result = -1;
00176       }
00177     catch(...)
00178       {
00179       std::cerr << "ITK test driver caught an unknown exception!!!\n";
00180       result = -1;
00181       }
00182     return result;
00183     }
00184   PrintAvailableTests();
00185   std::cerr << "Failed: " << testToRun << ": No test registered with name " << testToRun << "\n";
00186   return -1;
00187 }
00188 
00189 // Regression Testing Code
00190 
00191 int RegressionTestImage (const char *testImageFilename, const char *baselineImageFilename, int reportErrors)
00192 {
00193   // Use the factory mechanism to read the test and baseline files and convert them to double
00194   typedef itk::Image<double,ITK_TEST_DIMENSION_MAX> ImageType;
00195   typedef itk::Image<unsigned char,ITK_TEST_DIMENSION_MAX> OutputType;
00196   typedef itk::Image<unsigned char,2> DiffOutputType;
00197   typedef itk::ImageFileReader<ImageType> ReaderType;
00198 
00199   // Read the baseline file
00200   ReaderType::Pointer baselineReader = ReaderType::New();
00201     baselineReader->SetFileName(baselineImageFilename);
00202   try
00203     {
00204     baselineReader->UpdateLargestPossibleRegion();
00205     }
00206   catch (itk::ExceptionObject& e)
00207     {
00208     std::cerr << "Exception detected while reading " << baselineImageFilename << " : "  << e.GetDescription();
00209     return 1000;
00210     }
00211 
00212   // Read the file generated by the test
00213   ReaderType::Pointer testReader = ReaderType::New();
00214     testReader->SetFileName(testImageFilename);
00215   try
00216     {
00217     testReader->UpdateLargestPossibleRegion();
00218     }
00219   catch (itk::ExceptionObject& e)
00220     {
00221     std::cerr << "Exception detected while reading " << testImageFilename << " : "  << e.GetDescription() << std::endl;
00222     return 1000;
00223     }
00224 
00225   // The sizes of the baseline and test image must match
00226   ImageType::SizeType baselineSize;
00227     baselineSize = baselineReader->GetOutput()->GetLargestPossibleRegion().GetSize();
00228   ImageType::SizeType testSize;
00229     testSize = testReader->GetOutput()->GetLargestPossibleRegion().GetSize();
00230   
00231   if (baselineSize != testSize)
00232     {
00233     std::cerr << "The size of the Baseline image and Test image do not match!" << std::endl;
00234     std::cerr << "Baseline image: " << baselineImageFilename
00235               << " has size " << baselineSize << std::endl;
00236     std::cerr << "Test image:     " << testImageFilename
00237               << " has size " << testSize << std::endl;
00238     return 1;
00239     }
00240 
00241   // Now compare the two images
00242   typedef itk::DifferenceImageFilter<ImageType,ImageType> DiffType;
00243   DiffType::Pointer diff = DiffType::New();
00244     diff->SetValidInput(baselineReader->GetOutput());
00245     diff->SetTestInput(testReader->GetOutput());
00246     diff->SetDifferenceThreshold(2.0);
00247     diff->UpdateLargestPossibleRegion();
00248 
00249   double status = diff->GetTotalDifference();
00250 
00251   // if there are discrepencies, create an diff image
00252   if (status && reportErrors)
00253     {
00254     typedef itk::RescaleIntensityImageFilter<ImageType,OutputType> RescaleType;
00255     typedef itk::ExtractImageFilter<OutputType,DiffOutputType> ExtractType;
00256     typedef itk::ImageFileWriter<DiffOutputType> WriterType;
00257     typedef itk::ImageRegion<ITK_TEST_DIMENSION_MAX> RegionType;
00258     OutputType::IndexType index; index.Fill(0);
00259     OutputType::SizeType size; size.Fill(0);
00260 
00261     RescaleType::Pointer rescale = RescaleType::New();
00262       rescale->SetOutputMinimum(itk::NumericTraits<unsigned char>::NonpositiveMin());
00263       rescale->SetOutputMaximum(itk::NumericTraits<unsigned char>::max());
00264       rescale->SetInput(diff->GetOutput());
00265       rescale->UpdateLargestPossibleRegion();
00266 
00267     RegionType region;
00268     region.SetIndex(index);
00269     
00270     size = rescale->GetOutput()->GetLargestPossibleRegion().GetSize();
00271     for (unsigned int i = 2; i < ITK_TEST_DIMENSION_MAX; i++)
00272       {
00273       size[i] = 0;
00274       }
00275     region.SetSize(size);
00276 
00277     ExtractType::Pointer extract = ExtractType::New();
00278       extract->SetInput(rescale->GetOutput());
00279       extract->SetExtractionRegion(region);
00280 
00281     WriterType::Pointer writer = WriterType::New();
00282       writer->SetInput(extract->GetOutput());
00283 
00284     std::cout << "<DartMeasurement name=\"ImageError\" type=\"numeric/double\">";
00285     std::cout << status;
00286     std::cout <<  "</DartMeasurement>" << std::endl;
00287 
00288     ::itk::OStringStream diffName;
00289       diffName << testImageFilename << ".diff.png";
00290     try
00291       {
00292       rescale->SetInput(diff->GetOutput());
00293       rescale->Update();
00294       }
00295     catch (...)
00296       {
00297       std::cerr << "Error during rescale of " << diffName.str() << std::endl;
00298       }
00299     writer->SetFileName(diffName.str().c_str());
00300     try
00301       {
00302       writer->Update();
00303       }
00304     catch (...)
00305       {
00306       std::cerr << "Error during write of " << diffName.str() << std::endl;
00307       }
00308 
00309     std::cout << "<DartMeasurementFile name=\"DifferenceImage\" type=\"image/png\">";
00310     std::cout << diffName.str();
00311     std::cout << "</DartMeasurementFile>" << std::endl;
00312 
00313     ::itk::OStringStream baseName;
00314     baseName << testImageFilename << ".base.png";
00315     try
00316       {
00317       rescale->SetInput(baselineReader->GetOutput());
00318       rescale->Update();
00319       }
00320     catch (...)
00321       {
00322       std::cerr << "Error during rescale of " << baseName.str() << std::endl;
00323       }
00324     try
00325       {
00326       writer->SetFileName(baseName.str().c_str());
00327       writer->Update();
00328       }
00329     catch (...)
00330       {
00331       std::cerr << "Error during write of " << baseName.str() << std::endl;
00332       }
00333 
00334     std::cout << "<DartMeasurementFile name=\"BaselineImage\" type=\"image/png\">";
00335     std::cout << baseName.str();
00336     std::cout << "</DartMeasurementFile>" << std::endl;
00337 
00338     ::itk::OStringStream testName;
00339     testName << testImageFilename << ".test.png";
00340     try
00341       {
00342       rescale->SetInput(testReader->GetOutput());
00343       rescale->Update();
00344       }
00345     catch (...)
00346       {
00347       std::cerr << "Error during rescale of " << testName.str()
00348                 << std::endl;
00349       }
00350     try
00351       {
00352       writer->SetFileName(testName.str().c_str());
00353       writer->Update();
00354       }
00355     catch (...)
00356       {
00357       std::cerr << "Error during write of " << testName.str() << std::endl;
00358       }
00359 
00360     std::cout << "<DartMeasurementFile name=\"TestImage\" type=\"image/png\">";
00361     std::cout << testName.str();
00362     std::cout << "</DartMeasurementFile>" << std::endl;
00363 
00364 
00365     }
00366   return (status != 0) ? 1 : 0;
00367 }
00368 
00369 //
00370 // Generate all of the possible baselines
00371 // The possible baselines are generated fromn the baselineFilename using the following algorithm:
00372 // 1) strip the suffix
00373 // 2) append a digit _x
00374 // 3) append the original suffix.
00375 // It the file exists, increment x and continue
00376 //
00377 std::map<std::string,int> RegressionTestBaselines (char *baselineFilename)
00378 {
00379   std::map<std::string,int> baselines;
00380   baselines[std::string(baselineFilename)] = 0;
00381 
00382   std::string originalBaseline(baselineFilename);
00383 
00384   int x = 0;
00385   std::string::size_type suffixPos = originalBaseline.rfind(".");
00386   std::string suffix;
00387   if (suffixPos != std::string::npos)
00388     {
00389     suffix = originalBaseline.substr(suffixPos,originalBaseline.length());
00390     originalBaseline.erase(suffixPos,originalBaseline.length());
00391     }
00392   while (++x)
00393     {
00394     ::itk::OStringStream filename;
00395     filename << originalBaseline << "." << x << suffix;
00396     std::ifstream filestream(filename.str().c_str());
00397     if (!filestream)
00398       {
00399         break;
00400       }
00401     baselines[filename.str()] = 0;
00402     filestream.close();
00403     }
00404   return baselines;
00405 }

Generated at Sun Jan 25 13:19:46 2004 for ITK by doxygen 1.3.3 written by Dimitri van Heesch, © 1997-2000