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

Generated at Tue Sep 16 11:32:10 2003 for ITK by doxygen 1.2.15 written by Dimitri van Heesch, © 1997-2000