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