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

Generated at Wed Mar 30 00:12:37 2005 for ITK by doxygen 1.3.9.1 written by Dimitri van Heesch, © 1997-2000