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

Generated at Thu May 25 00:08:12 2006 for ITK by doxygen 1.3.5 written by Dimitri van Heesch, © 1997-2000