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 Apr 1 02:45:27 2007 for ITK by doxygen 1.3.8 written by Dimitri van Heesch, © 1997-2000