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 Sat Mar 31 02:29:56 2007 for ITK by doxygen 1.3.8 written by Dimitri van Heesch, © 1997-2000