Orfeo Toolbox  3.16
itkTestMain.h
Go to the documentation of this file.
1 /*=========================================================================
2 
3  Program: Insight Segmentation & Registration Toolkit
4  Module: $RCSfile: itkTestMain.h,v $
5  Language: C++
6  Date: $Date: 2009-03-03 15:09:43 $
7  Version: $Revision: 1.33 $
8 
9  Copyright (c) Insight Software Consortium. All rights reserved.
10  See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details.
11 
12  Portions of this code are covered under the VTK copyright.
13  See VTKCopyright.txt or http://www.kitware.com/VTKCopyright.htm for details.
14 
15  This software is distributed WITHOUT ANY WARRANTY; without even
16  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
17  PURPOSE. See the above copyright notices for more information.
18 
19 =========================================================================*/
20 #ifndef __itkTestMain_h
21 #define __itkTestMain_h
22 
23 // This file is used to create TestDriver executables
24 // These executables are able to register a function pointer to a string name
25 // in a lookup table. By including this file, it creates a main function
26 // that calls RegisterTests() then looks up the function pointer for the test
27 // specified on the command line.
28 #include "itkWin32Header.h"
29 #include <map>
30 #include <string>
31 #include <iostream>
32 #include <fstream>
33 #include "itkNumericTraits.h"
34 #include "itkMultiThreader.h"
35 #include "itkImage.h"
36 #include "itkImageFileReader.h"
37 #include "itkImageFileWriter.h"
39 #include "itkSubtractImageFilter.h"
41 #include "itkExtractImageFilter.h"
43 #include "itkImageRegion.h"
44 #include "itksys/SystemTools.hxx"
45 
46 #define ITK_TEST_DIMENSION_MAX 6
47 
48 typedef int (*MainFuncPointer)(int , char* [] );
49 std::map<std::string, MainFuncPointer> StringToTestFunctionMap;
50 
51 #define REGISTER_TEST(test) \
52 extern int test(int, char* [] ); \
53 StringToTestFunctionMap[#test] = test
54 
55 int RegressionTestImage (const char *testImageFilename,
56  const char *baselineImageFilename,
57  int reportErrors,
58  double intensityTolerance = 2.0,
59  unsigned int numberOfPixelsTolerance = 0,
60  unsigned int radiusTolerance = 0);
61 
62 std::map<std::string,int> RegressionTestBaselines (char *);
63 
64 void RegisterTests();
66 {
67  std::cout << "Available tests:\n";
68  std::map<std::string, MainFuncPointer>::iterator j = StringToTestFunctionMap.begin();
69  int i = 0;
70  while(j != StringToTestFunctionMap.end())
71  {
72  std::cout << i << ". " << j->first << "\n";
73  ++i;
74  ++j;
75  }
76 }
77 
78 int main(int ac, char* av[] )
79 {
80  double intensityTolerance = 2.0;
81  unsigned int numberOfPixelsTolerance = 0;
82  unsigned int radiusTolerance = 0;
83 
84  typedef std::pair< char *, char *> ComparePairType;
85  std::vector< ComparePairType > compareList;
86 
87  RegisterTests();
88  std::string testToRun;
89  if(ac < 2)
90  {
92  std::cout << "To run a test, enter the test number: ";
93  int testNum = 0;
94  std::cin >> testNum;
95  std::map<std::string, MainFuncPointer>::iterator j = StringToTestFunctionMap.begin();
96  int i = 0;
97  while(j != StringToTestFunctionMap.end() && i < testNum)
98  {
99  ++i;
100  ++j;
101  }
102  if(j == StringToTestFunctionMap.end())
103  {
104  std::cerr << testNum << " is an invalid test number\n";
105  return -1;
106  }
107  testToRun = j->first;
108  }
109  else
110  {
111  while( ac > 0 && testToRun.empty() )
112  {
113  if (strcmp(av[1], "--with-threads") == 0)
114  {
115  int numThreads = atoi(av[2]);
117  av += 2;
118  ac -= 2;
119  }
120  else if (strcmp(av[1], "--without-threads") == 0)
121  {
123  av += 1;
124  ac -= 1;
125  }
126  else if (ac > 3 && strcmp(av[1], "--compare") == 0)
127  {
128  compareList.push_back( ComparePairType( av[2], av[3] ) );
129  av += 3;
130  ac -= 3;
131  }
132  else if (ac > 2 && strcmp(av[1], "--compareNumberOfPixelsTolerance") == 0)
133  {
134  numberOfPixelsTolerance = atoi( av[2] );
135  av += 2;
136  ac -= 2;
137  }
138  else if (ac > 2 && strcmp(av[1], "--compareRadiusTolerance") == 0)
139  {
140  radiusTolerance = atoi( av[2] );
141  av += 2;
142  ac -= 2;
143  }
144  else if (ac > 2 && strcmp(av[1], "--compareIntensityTolerance") == 0)
145  {
146  intensityTolerance = atof( av[2] );
147  av += 2;
148  ac -= 2;
149  }
150  else
151  {
152  testToRun = av[1];
153  }
154  }
155  }
156  std::map<std::string, MainFuncPointer>::iterator j = StringToTestFunctionMap.find(testToRun);
157  if(j != StringToTestFunctionMap.end())
158  {
159  MainFuncPointer f = j->second;
160  int result;
161  try
162  {
163  // Invoke the test's "main" function.
164  result = (*f)(ac-1, av+1);
165 
166  // Make a list of possible baselines
167  for( int i=0; i<static_cast<int>(compareList.size()); i++)
168  {
169  char * baselineFilename = compareList[i].first;
170  char * testFilename = compareList[i].second;
171  std::map<std::string,int> baselines = RegressionTestBaselines(baselineFilename);
172  std::map<std::string,int>::iterator baseline = baselines.begin();
173  std::string bestBaseline;
174  int bestBaselineStatus = itk::NumericTraits<int>::max();
175  while (baseline != baselines.end())
176  {
177  baseline->second = RegressionTestImage(testFilename,
178  (baseline->first).c_str(),
179  0,
180  intensityTolerance,
181  numberOfPixelsTolerance,
182  radiusTolerance );
183  if (baseline->second < bestBaselineStatus)
184  {
185  bestBaseline = baseline->first;
186  bestBaselineStatus = baseline->second;
187  }
188  if (baseline->second == 0)
189  {
190  break;
191  }
192  ++baseline;
193  }
194  // if the best we can do still has errors, generate the error images
195  if (bestBaselineStatus)
196  {
197  RegressionTestImage(testFilename,
198  bestBaseline.c_str(),
199  1,
200  intensityTolerance,
201  numberOfPixelsTolerance,
202  radiusTolerance );
203  }
204 
205  // output the matching baseline
206  std::cout << "<DartMeasurement name=\"BaselineImageName\" type=\"text/string\">";
207  std::cout << itksys::SystemTools::GetFilenameName(bestBaseline);
208  std::cout << "</DartMeasurement>" << std::endl;
209 
210  result += bestBaselineStatus;
211  }
212  }
213  catch(const itk::ExceptionObject& e)
214  {
215  std::cerr << "ITK test driver caught an ITK exception:\n";
216  e.Print(std::cerr);
217  result = -1;
218  }
219  catch(const std::exception& e)
220  {
221  std::cerr << "ITK test driver caught an exception:\n";
222  std::cerr << e.what() << "\n";
223  result = -1;
224  }
225  catch(...)
226  {
227  std::cerr << "ITK test driver caught an unknown exception!!!\n";
228  result = -1;
229  }
230  return result;
231  }
233  std::cerr << "Failed: " << testToRun << ": No test registered with name " << testToRun << "\n";
234  return -1;
235 }
236 
237 // Regression Testing Code
238 
239 int RegressionTestImage (const char *testImageFilename,
240  const char *baselineImageFilename,
241  int reportErrors,
242  double intensityTolerance,
243  unsigned int numberOfPixelsTolerance,
244  unsigned int radiusTolerance )
245 {
246  // Use the factory mechanism to read the test and baseline files and convert them to double
249  typedef itk::Image<unsigned char,2> DiffOutputType;
250  typedef itk::ImageFileReader<ImageType> ReaderType;
251 
252  // Read the baseline file
253  ReaderType::Pointer baselineReader = ReaderType::New();
254  baselineReader->SetFileName(baselineImageFilename);
255  try
256  {
257  baselineReader->UpdateLargestPossibleRegion();
258  }
259  catch (itk::ExceptionObject& e)
260  {
261  std::cerr << "Exception detected while reading " << baselineImageFilename << " : " << e.GetDescription();
262  return 1000;
263  }
264 
265  // Read the file generated by the test
266  ReaderType::Pointer testReader = ReaderType::New();
267  testReader->SetFileName(testImageFilename);
268  try
269  {
270  testReader->UpdateLargestPossibleRegion();
271  }
272  catch (itk::ExceptionObject& e)
273  {
274  std::cerr << "Exception detected while reading " << testImageFilename << " : " << e.GetDescription() << std::endl;
275  return 1000;
276  }
277 
278  // The sizes of the baseline and test image must match
279  ImageType::SizeType baselineSize;
280  baselineSize = baselineReader->GetOutput()->GetLargestPossibleRegion().GetSize();
281  ImageType::SizeType testSize;
282  testSize = testReader->GetOutput()->GetLargestPossibleRegion().GetSize();
283 
284  if (baselineSize != testSize)
285  {
286  std::cerr << "The size of the Baseline image and Test image do not match!" << std::endl;
287  std::cerr << "Baseline image: " << baselineImageFilename
288  << " has size " << baselineSize << std::endl;
289  std::cerr << "Test image: " << testImageFilename
290  << " has size " << testSize << std::endl;
291  return 1;
292  }
293 
294  // Now compare the two images
296  DiffType::Pointer diff = DiffType::New();
297  diff->SetValidInput(baselineReader->GetOutput());
298  diff->SetTestInput(testReader->GetOutput());
299  diff->SetDifferenceThreshold( intensityTolerance );
300  diff->SetToleranceRadius( radiusTolerance );
301  diff->UpdateLargestPossibleRegion();
302 
303  unsigned long status = 0;
304  status = diff->GetNumberOfPixelsWithDifferences();
305 
306  // if there are discrepencies, create an diff image
307  if ( (status > numberOfPixelsTolerance) && reportErrors )
308  {
311  typedef itk::ImageFileWriter<DiffOutputType> WriterType;
312  typedef itk::ImageRegion<ITK_TEST_DIMENSION_MAX> RegionType;
313  OutputType::SizeType size; size.Fill(0);
314 
315  RescaleType::Pointer rescale = RescaleType::New();
316  rescale->SetOutputMinimum(itk::NumericTraits<unsigned char>::NonpositiveMin());
317  rescale->SetOutputMaximum(itk::NumericTraits<unsigned char>::max());
318  rescale->SetInput(diff->GetOutput());
319  rescale->UpdateLargestPossibleRegion();
320  size = rescale->GetOutput()->GetLargestPossibleRegion().GetSize();
321 
322  //Get the center slice of the image, In 3D, the first slice
323  //is often a black slice with little debugging information.
324  OutputType::IndexType index; index.Fill(0);
325  for (unsigned int i = 2; i < ITK_TEST_DIMENSION_MAX; i++)
326  {
327  index[i]=size[i]/2;//NOTE: Integer Divide used to get approximately the center slice
328  size[i] = 0;
329  }
330 
331  RegionType region;
332  region.SetIndex(index);
333 
334  region.SetSize(size);
335 
336  ExtractType::Pointer extract = ExtractType::New();
337  extract->SetInput(rescale->GetOutput());
338  extract->SetExtractionRegion(region);
339 
340  WriterType::Pointer writer = WriterType::New();
341  writer->SetInput(extract->GetOutput());
342 
343  std::cout << "<DartMeasurement name=\"ImageError\" type=\"numeric/double\">";
344  std::cout << status;
345  std::cout << "</DartMeasurement>" << std::endl;
346 
347  ::itk::OStringStream diffName;
348  diffName << testImageFilename << ".diff.png";
349  try
350  {
351  rescale->SetInput(diff->GetOutput());
352  rescale->Update();
353  }
354  catch(const std::exception& e)
355  {
356  std::cerr << "Error during rescale of " << diffName.str() << std::endl;
357  std::cerr << e.what() << "\n";
358  }
359  catch (...)
360  {
361  std::cerr << "Error during rescale of " << diffName.str() << std::endl;
362  }
363  writer->SetFileName(diffName.str().c_str());
364  try
365  {
366  writer->Update();
367  }
368  catch(const std::exception& e)
369  {
370  std::cerr << "Error during write of " << diffName.str() << std::endl;
371  std::cerr << e.what() << "\n";
372  }
373  catch (...)
374  {
375  std::cerr << "Error during write of " << diffName.str() << std::endl;
376  }
377 
378  std::cout << "<DartMeasurementFile name=\"DifferenceImage\" type=\"image/png\">";
379  std::cout << diffName.str();
380  std::cout << "</DartMeasurementFile>" << std::endl;
381 
382  ::itk::OStringStream baseName;
383  baseName << testImageFilename << ".base.png";
384  try
385  {
386  rescale->SetInput(baselineReader->GetOutput());
387  rescale->Update();
388  }
389  catch(const std::exception& e)
390  {
391  std::cerr << "Error during rescale of " << baseName.str() << std::endl;
392  std::cerr << e.what() << "\n";
393  }
394  catch (...)
395  {
396  std::cerr << "Error during rescale of " << baseName.str() << std::endl;
397  }
398  try
399  {
400  writer->SetFileName(baseName.str().c_str());
401  writer->Update();
402  }
403  catch(const std::exception& e)
404  {
405  std::cerr << "Error during write of " << baseName.str() << std::endl;
406  std::cerr << e.what() << "\n";
407  }
408  catch (...)
409  {
410  std::cerr << "Error during write of " << baseName.str() << std::endl;
411  }
412 
413  std::cout << "<DartMeasurementFile name=\"BaselineImage\" type=\"image/png\">";
414  std::cout << baseName.str();
415  std::cout << "</DartMeasurementFile>" << std::endl;
416 
417  ::itk::OStringStream testName;
418  testName << testImageFilename << ".test.png";
419  try
420  {
421  rescale->SetInput(testReader->GetOutput());
422  rescale->Update();
423  }
424  catch(const std::exception& e)
425  {
426  std::cerr << "Error during rescale of " << testName.str() << std::endl;
427  std::cerr << e.what() << "\n";
428  }
429  catch (...)
430  {
431  std::cerr << "Error during rescale of " << testName.str() << std::endl;
432  }
433  try
434  {
435  writer->SetFileName(testName.str().c_str());
436  writer->Update();
437  }
438  catch(const std::exception& e)
439  {
440  std::cerr << "Error during write of " << testName.str() << std::endl;
441  std::cerr << e.what() << "\n";
442  }
443  catch (...)
444  {
445  std::cerr << "Error during write of " << testName.str() << std::endl;
446  }
447 
448  std::cout << "<DartMeasurementFile name=\"TestImage\" type=\"image/png\">";
449  std::cout << testName.str();
450  std::cout << "</DartMeasurementFile>" << std::endl;
451 
452 
453  }
454  return (status > numberOfPixelsTolerance) ? 1 : 0;
455 }
456 
457 //
458 // Generate all of the possible baselines
459 // The possible baselines are generated fromn the baselineFilename using the following algorithm:
460 // 1) strip the suffix
461 // 2) append a digit .x
462 // 3) append the original suffix.
463 // It the file exists, increment x and continue
464 //
465 std::map<std::string,int> RegressionTestBaselines (char *baselineFilename)
466 {
467  std::map<std::string,int> baselines;
468  baselines[std::string(baselineFilename)] = 0;
469 
470  std::string originalBaseline(baselineFilename);
471 
472  int x = 0;
473  std::string::size_type suffixPos = originalBaseline.rfind(".");
474  std::string suffix;
475  if (suffixPos != std::string::npos)
476  {
477  suffix = originalBaseline.substr(suffixPos,originalBaseline.length());
478  originalBaseline.erase(suffixPos,originalBaseline.length());
479  }
480  while (++x)
481  {
482  ::itk::OStringStream filename;
483  filename << originalBaseline << "." << x << suffix;
484  std::ifstream filestream(filename.str().c_str());
485  if (!filestream)
486  {
487  break;
488  }
489  baselines[filename.str()] = 0;
490  filestream.close();
491  }
492  return baselines;
493 }
494 
495 // Needed for explicit instantiation
497 
498 #endif

Generated at Sun Feb 3 2013 00:09:40 for Orfeo Toolbox with doxygen 1.8.1.1