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: 2009-03-03 15:09:43 $
00007   Version:   $Revision: 1.33 $
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 #ifndef __itkTestMain_h
00021 #define __itkTestMain_h
00022 
00023 // This file is used to create TestDriver executables
00024 // These executables are able to register a function pointer to a string name
00025 // in a lookup table.   By including this file, it creates a main function
00026 // that calls RegisterTests() then looks up the function pointer for the test
00027 // specified on the command line.
00028 #include "itkWin32Header.h"
00029 #include <map>
00030 #include <string>
00031 #include <iostream>
00032 #include <fstream>
00033 #include "itkNumericTraits.h"
00034 #include "itkMultiThreader.h"
00035 #include "itkImage.h"
00036 #include "itkImageFileReader.h"
00037 #include "itkImageFileWriter.h"
00038 #include "itkImageRegionConstIterator.h"
00039 #include "itkSubtractImageFilter.h"
00040 #include "itkRescaleIntensityImageFilter.h"
00041 #include "itkExtractImageFilter.h"
00042 #include "itkDifferenceImageFilter.h"
00043 #include "itkImageRegion.h"
00044 #include "itksys/SystemTools.hxx"
00045 
00046 #define ITK_TEST_DIMENSION_MAX 6
00047 
00048 typedef int (*MainFuncPointer)(int , char* [] );
00049 std::map<std::string, MainFuncPointer> StringToTestFunctionMap;
00050 
00051 #define REGISTER_TEST(test) \
00052 extern int test(int, char* [] ); \
00053 StringToTestFunctionMap[#test] = test
00054 
00055 int RegressionTestImage (const char *testImageFilename, 
00056                          const char *baselineImageFilename, 
00057                          int reportErrors,
00058                          double intensityTolerance = 2.0,
00059                          unsigned int numberOfPixelsTolerance = 0,
00060                          unsigned int radiusTolerance = 0);
00061 
00062 std::map<std::string,int> RegressionTestBaselines (char *);
00063 
00064 void RegisterTests();
00065 void PrintAvailableTests()
00066 {
00067   std::cout << "Available tests:\n";
00068   std::map<std::string, MainFuncPointer>::iterator j = StringToTestFunctionMap.begin();
00069   int i = 0;
00070   while(j != StringToTestFunctionMap.end())
00071     {
00072     std::cout << i << ". " << j->first << "\n";
00073     ++i;
00074     ++j;
00075     }
00076 }
00077 
00078 int main(int ac, char* av[] )
00079 {
00080   double intensityTolerance  = 2.0;
00081   unsigned int numberOfPixelsTolerance = 0;
00082   unsigned int radiusTolerance = 0;
00083 
00084   typedef std::pair< char *, char *> ComparePairType;
00085   std::vector< ComparePairType > compareList;
00086 
00087   RegisterTests();
00088   std::string testToRun;
00089   if(ac < 2)
00090     {
00091     PrintAvailableTests();
00092     std::cout << "To run a test, enter the test number: ";
00093     int testNum = 0;
00094     std::cin >> testNum;
00095     std::map<std::string, MainFuncPointer>::iterator j = StringToTestFunctionMap.begin();
00096     int i = 0;
00097     while(j != StringToTestFunctionMap.end() && i < testNum)
00098       {
00099       ++i;
00100       ++j;
00101       }
00102     if(j == StringToTestFunctionMap.end())
00103       {
00104       std::cerr << testNum << " is an invalid test number\n";
00105       return -1;
00106       }
00107     testToRun = j->first;
00108     }
00109   else
00110     {
00111     while( ac > 0 && testToRun.empty() )
00112       {
00113       if (strcmp(av[1], "--with-threads") == 0)
00114         {
00115         int numThreads = atoi(av[2]);
00116         itk::MultiThreader::SetGlobalDefaultNumberOfThreads(numThreads);
00117         av += 2;
00118         ac -= 2;
00119         }
00120       else if (strcmp(av[1], "--without-threads") == 0)
00121         {
00122         itk::MultiThreader::SetGlobalDefaultNumberOfThreads(1);
00123         av += 1;
00124         ac -= 1;
00125         }
00126       else if (ac > 3 && strcmp(av[1], "--compare") == 0)
00127         {
00128         compareList.push_back( ComparePairType( av[2], av[3] ) );
00129         av += 3;
00130         ac -= 3;
00131         }
00132       else if (ac > 2 && strcmp(av[1], "--compareNumberOfPixelsTolerance") == 0)
00133         {
00134         numberOfPixelsTolerance = atoi( av[2] );
00135         av += 2;
00136         ac -= 2;
00137         }
00138       else if (ac > 2 && strcmp(av[1], "--compareRadiusTolerance") == 0)
00139         {
00140         radiusTolerance = atoi( av[2] );
00141         av += 2;
00142         ac -= 2;
00143         }
00144       else if (ac > 2 && strcmp(av[1], "--compareIntensityTolerance") == 0)
00145         {
00146         intensityTolerance = atof( av[2] );
00147         av += 2;
00148         ac -= 2;
00149         }
00150       else
00151         {
00152         testToRun = av[1];
00153         }
00154       }
00155     }
00156   std::map<std::string, MainFuncPointer>::iterator j = StringToTestFunctionMap.find(testToRun);
00157   if(j != StringToTestFunctionMap.end())
00158     {
00159     MainFuncPointer f = j->second;
00160     int result;
00161     try
00162       {
00163       // Invoke the test's "main" function.
00164       result = (*f)(ac-1, av+1);
00165 
00166       // Make a list of possible baselines
00167       for( int i=0; i<static_cast<int>(compareList.size()); i++)
00168         {
00169         char * baselineFilename = compareList[i].first;
00170         char * testFilename = compareList[i].second;
00171         std::map<std::string,int> baselines = RegressionTestBaselines(baselineFilename);
00172         std::map<std::string,int>::iterator baseline = baselines.begin();
00173         std::string bestBaseline;
00174         int bestBaselineStatus = itk::NumericTraits<int>::max();
00175         while (baseline != baselines.end())
00176           {
00177           baseline->second = RegressionTestImage(testFilename,
00178                                                  (baseline->first).c_str(),
00179                                                  0, 
00180                                                  intensityTolerance, 
00181                                                  numberOfPixelsTolerance, 
00182                                                  radiusTolerance );
00183           if (baseline->second < bestBaselineStatus)
00184             {
00185             bestBaseline = baseline->first;
00186             bestBaselineStatus = baseline->second;
00187             }
00188           if (baseline->second == 0)
00189             {
00190             break;
00191             }
00192           ++baseline;
00193           }
00194         // if the best we can do still has errors, generate the error images
00195          if (bestBaselineStatus)
00196           {
00197           RegressionTestImage(testFilename,
00198                               bestBaseline.c_str(),
00199                               1, 
00200                               intensityTolerance,
00201                               numberOfPixelsTolerance, 
00202                               radiusTolerance );
00203           }
00204 
00205         // output the matching baseline
00206         std::cout << "<DartMeasurement name=\"BaselineImageName\" type=\"text/string\">";
00207         std::cout << itksys::SystemTools::GetFilenameName(bestBaseline);
00208         std::cout << "</DartMeasurement>" << std::endl;
00209         
00210         result += bestBaselineStatus;
00211         }
00212       }
00213     catch(const itk::ExceptionObject& e)
00214       {
00215       std::cerr << "ITK test driver caught an ITK exception:\n";
00216       e.Print(std::cerr);
00217       result = -1;
00218       }
00219     catch(const std::exception& e)
00220       {
00221       std::cerr << "ITK test driver caught an exception:\n";
00222       std::cerr << e.what() << "\n";
00223       result = -1;
00224       }
00225     catch(...)
00226       {
00227       std::cerr << "ITK test driver caught an unknown exception!!!\n";
00228       result = -1;
00229       }
00230     return result;
00231     }
00232   PrintAvailableTests();
00233   std::cerr << "Failed: " << testToRun << ": No test registered with name " << testToRun << "\n";
00234   return -1;
00235 }
00236 
00237 // Regression Testing Code
00238 
00239 int RegressionTestImage (const char *testImageFilename, 
00240                          const char *baselineImageFilename, 
00241                          int reportErrors,
00242                          double intensityTolerance,
00243                          unsigned int numberOfPixelsTolerance, 
00244                          unsigned int radiusTolerance )
00245 {
00246   // Use the factory mechanism to read the test and baseline files and convert them to double
00247   typedef itk::Image<double,ITK_TEST_DIMENSION_MAX>        ImageType;
00248   typedef itk::Image<unsigned char,ITK_TEST_DIMENSION_MAX> OutputType;
00249   typedef itk::Image<unsigned char,2>                      DiffOutputType;
00250   typedef itk::ImageFileReader<ImageType>                  ReaderType;
00251 
00252   // Read the baseline file
00253   ReaderType::Pointer baselineReader = ReaderType::New();
00254     baselineReader->SetFileName(baselineImageFilename);
00255   try
00256     {
00257     baselineReader->UpdateLargestPossibleRegion();
00258     }
00259   catch (itk::ExceptionObject& e)
00260     {
00261     std::cerr << "Exception detected while reading " << baselineImageFilename << " : "  << e.GetDescription();
00262     return 1000;
00263     }
00264 
00265   // Read the file generated by the test
00266   ReaderType::Pointer testReader = ReaderType::New();
00267     testReader->SetFileName(testImageFilename);
00268   try
00269     {
00270     testReader->UpdateLargestPossibleRegion();
00271     }
00272   catch (itk::ExceptionObject& e)
00273     {
00274     std::cerr << "Exception detected while reading " << testImageFilename << " : "  << e.GetDescription() << std::endl;
00275     return 1000;
00276     }
00277 
00278   // The sizes of the baseline and test image must match
00279   ImageType::SizeType baselineSize;
00280     baselineSize = baselineReader->GetOutput()->GetLargestPossibleRegion().GetSize();
00281   ImageType::SizeType testSize;
00282     testSize = testReader->GetOutput()->GetLargestPossibleRegion().GetSize();
00283   
00284   if (baselineSize != testSize)
00285     {
00286     std::cerr << "The size of the Baseline image and Test image do not match!" << std::endl;
00287     std::cerr << "Baseline image: " << baselineImageFilename
00288               << " has size " << baselineSize << std::endl;
00289     std::cerr << "Test image:     " << testImageFilename
00290               << " has size " << testSize << std::endl;
00291     return 1;
00292     }
00293 
00294   // Now compare the two images
00295   typedef itk::DifferenceImageFilter<ImageType,ImageType> DiffType;
00296   DiffType::Pointer diff = DiffType::New();
00297     diff->SetValidInput(baselineReader->GetOutput());
00298     diff->SetTestInput(testReader->GetOutput());
00299     diff->SetDifferenceThreshold( intensityTolerance );
00300     diff->SetToleranceRadius( radiusTolerance );
00301     diff->UpdateLargestPossibleRegion();
00302 
00303     unsigned long status = 0;
00304     status = diff->GetNumberOfPixelsWithDifferences();
00305 
00306   // if there are discrepencies, create an diff image
00307   if ( (status > numberOfPixelsTolerance) && reportErrors )
00308     {
00309     typedef itk::RescaleIntensityImageFilter<ImageType,OutputType> RescaleType;
00310     typedef itk::ExtractImageFilter<OutputType,DiffOutputType>     ExtractType;
00311     typedef itk::ImageFileWriter<DiffOutputType>                   WriterType;
00312     typedef itk::ImageRegion<ITK_TEST_DIMENSION_MAX>               RegionType;
00313     OutputType::SizeType size; size.Fill(0);
00314 
00315     RescaleType::Pointer rescale = RescaleType::New();
00316       rescale->SetOutputMinimum(itk::NumericTraits<unsigned char>::NonpositiveMin());
00317       rescale->SetOutputMaximum(itk::NumericTraits<unsigned char>::max());
00318       rescale->SetInput(diff->GetOutput());
00319       rescale->UpdateLargestPossibleRegion();
00320     size = rescale->GetOutput()->GetLargestPossibleRegion().GetSize();
00321 
00322     //Get the center slice of the image,  In 3D, the first slice
00323     //is often a black slice with little debugging information.
00324     OutputType::IndexType index; index.Fill(0);
00325     for (unsigned int i = 2; i < ITK_TEST_DIMENSION_MAX; i++)
00326       {
00327       index[i]=size[i]/2;//NOTE: Integer Divide used to get approximately the center slice
00328       size[i] = 0;
00329       }
00330 
00331     RegionType region;
00332     region.SetIndex(index);
00333 
00334     region.SetSize(size);
00335 
00336     ExtractType::Pointer extract = ExtractType::New();
00337       extract->SetInput(rescale->GetOutput());
00338       extract->SetExtractionRegion(region);
00339 
00340     WriterType::Pointer writer = WriterType::New();
00341       writer->SetInput(extract->GetOutput());
00342 
00343     std::cout << "<DartMeasurement name=\"ImageError\" type=\"numeric/double\">";
00344     std::cout << status;
00345     std::cout <<  "</DartMeasurement>" << std::endl;
00346 
00347     ::itk::OStringStream diffName;
00348       diffName << testImageFilename << ".diff.png";
00349     try
00350       {
00351       rescale->SetInput(diff->GetOutput());
00352       rescale->Update();
00353       }
00354     catch(const std::exception& e)
00355       {
00356       std::cerr << "Error during rescale of " << diffName.str() << std::endl;
00357       std::cerr << e.what() << "\n";
00358       }
00359     catch (...)
00360       {
00361       std::cerr << "Error during rescale of " << diffName.str() << std::endl;
00362       }
00363     writer->SetFileName(diffName.str().c_str());
00364     try
00365       {
00366       writer->Update();
00367       }
00368     catch(const std::exception& e)
00369       {
00370       std::cerr << "Error during write of " << diffName.str() << std::endl;
00371       std::cerr << e.what() << "\n";
00372       }
00373     catch (...)
00374       {
00375       std::cerr << "Error during write of " << diffName.str() << std::endl;
00376       }
00377 
00378     std::cout << "<DartMeasurementFile name=\"DifferenceImage\" type=\"image/png\">";
00379     std::cout << diffName.str();
00380     std::cout << "</DartMeasurementFile>" << std::endl;
00381 
00382     ::itk::OStringStream baseName;
00383     baseName << testImageFilename << ".base.png";
00384     try
00385       {
00386       rescale->SetInput(baselineReader->GetOutput());
00387       rescale->Update();
00388       }
00389     catch(const std::exception& e)
00390       {
00391       std::cerr << "Error during rescale of " << baseName.str() << std::endl;
00392       std::cerr << e.what() << "\n";
00393       }
00394     catch (...)
00395       {
00396       std::cerr << "Error during rescale of " << baseName.str() << std::endl;
00397       }
00398     try
00399       {
00400       writer->SetFileName(baseName.str().c_str());
00401       writer->Update();
00402       }
00403     catch(const std::exception& e)
00404       {
00405       std::cerr << "Error during write of " << baseName.str() << std::endl;
00406       std::cerr << e.what() << "\n";
00407       }
00408     catch (...)
00409       {
00410       std::cerr << "Error during write of " << baseName.str() << std::endl;
00411       }
00412 
00413     std::cout << "<DartMeasurementFile name=\"BaselineImage\" type=\"image/png\">";
00414     std::cout << baseName.str();
00415     std::cout << "</DartMeasurementFile>" << std::endl;
00416 
00417     ::itk::OStringStream testName;
00418     testName << testImageFilename << ".test.png";
00419     try
00420       {
00421       rescale->SetInput(testReader->GetOutput());
00422       rescale->Update();
00423       }
00424     catch(const std::exception& e)
00425       {
00426       std::cerr << "Error during rescale of " << testName.str() << std::endl;
00427       std::cerr << e.what() << "\n";
00428       }
00429     catch (...)
00430       {
00431       std::cerr << "Error during rescale of " << testName.str() << std::endl;
00432       }
00433     try
00434       {
00435       writer->SetFileName(testName.str().c_str());
00436       writer->Update();
00437       }
00438     catch(const std::exception& e)
00439       {
00440       std::cerr << "Error during write of " << testName.str() << std::endl;
00441       std::cerr << e.what() << "\n";
00442       }
00443     catch (...)
00444       {
00445       std::cerr << "Error during write of " << testName.str() << std::endl;
00446       }
00447 
00448     std::cout << "<DartMeasurementFile name=\"TestImage\" type=\"image/png\">";
00449     std::cout << testName.str();
00450     std::cout << "</DartMeasurementFile>" << std::endl;
00451 
00452 
00453     }
00454   return (status > numberOfPixelsTolerance) ? 1 : 0;
00455 }
00456 
00457 //
00458 // Generate all of the possible baselines
00459 // The possible baselines are generated fromn the baselineFilename using the following algorithm:
00460 // 1) strip the suffix
00461 // 2) append a digit .x
00462 // 3) append the original suffix.
00463 // It the file exists, increment x and continue
00464 //
00465 std::map<std::string,int> RegressionTestBaselines (char *baselineFilename)
00466 {
00467   std::map<std::string,int> baselines;
00468   baselines[std::string(baselineFilename)] = 0;
00469 
00470   std::string originalBaseline(baselineFilename);
00471 
00472   int x = 0;
00473   std::string::size_type suffixPos = originalBaseline.rfind(".");
00474   std::string suffix;
00475   if (suffixPos != std::string::npos)
00476     {
00477     suffix = originalBaseline.substr(suffixPos,originalBaseline.length());
00478     originalBaseline.erase(suffixPos,originalBaseline.length());
00479     }
00480   while (++x)
00481     {
00482     ::itk::OStringStream filename;
00483     filename << originalBaseline << "." << x << suffix;
00484     std::ifstream filestream(filename.str().c_str());
00485     if (!filestream)
00486       {
00487       break;
00488       }
00489     baselines[filename.str()] = 0;
00490     filestream.close();
00491     }
00492   return baselines;
00493 }
00494 
00495 // Needed for explicit instantiation
00496 #include "itkDifferenceImageFilter.txx"
00497 
00498 #endif
00499 

Generated at Tue Sep 15 05:07:41 2009 for ITK by doxygen 1.5.8 written by Dimitri van Heesch, © 1997-2000