ITK  4.9.0
Insight Segmentation and Registration Toolkit
itkTestDriverInclude.h
Go to the documentation of this file.
1 /*=========================================================================
2  *
3  * Copyright Insight Software Consortium
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0.txt
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  *=========================================================================*/
18 /*=========================================================================
19  *
20  * Portions of this file are subject to the VTK Toolkit Version 3 copyright.
21  *
22  * Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
23  *
24  * For complete copyright, license and disclaimer of warranty information
25  * please refer to the NOTICE file at the top of the ITK source tree.
26  *
27  *=========================================================================*/
28 #ifndef itkTestDriverInclude_h
29 #define itkTestDriverInclude_h
30 //
31 // This file is used by the TestDriver executables generated by CMake's
32 // create_test_sourcelist. It defines a function, ProcessArguments
33 // that processes the command line for the test driver prior to
34 // invoking the test. It also defines the RegressionTestImage function
35 // that is called after a test has been run by the driver.
36 // command line options prior to invoking the test.
37 //
38 
39 #include "itksys/Process.h"
40 
41 #include "itkWin32Header.h"
42 #include <map>
43 #include <algorithm>
44 #include <string>
45 #include <iostream>
46 #include <fstream>
47 #include "itkMultiThreader.h"
48 #include "itkImage.h"
49 #include "itkImageFileReader.h"
50 #include "itkImageFileWriter.h"
56 #include "itksys/SystemTools.hxx"
57 #include "itkIntTypes.h"
58 #if defined(LINUX) && !defined(__MINGW32__)
60 #endif
61 #include "vnl/vnl_sample.h"
62 
63 #define ITK_TEST_DIMENSION_MAX 6
64 
65 int RegressionTestImage(const char *testImageFilename,
66  const char *baselineImageFilename,
67  int reportErrors,
68  double intensityTolerance,
69  ::itk::SizeValueType numberOfPixelsTolerance = 0,
70  unsigned int radiusTolerance = 0);
71 
72 int HashTestImage( const char *testImageFilename,
73  const std::string md5hash );
74 
75 
76 std::map< std::string, int > RegressionTestBaselines(char *);
77 
78 typedef std::pair< char *, char * > ComparePairType;
79 
80 // A structure to hold regression test parameters
81 typedef struct
82 {
83  std::vector< ComparePairType > compareList;
86  unsigned int radiusTolerance;
88 
90 
91 
92 typedef std::pair< const char *, std::vector<std::string> > HashPairType;
93 
94 std::vector< HashPairType > hashTestList;
95 
96 typedef char ** ArgumentStringType;
97 
98 
99 // Types to hold parameters that should be processed later
100 typedef std::vector< char * > ArgumentsList;
101 
103 {
105 
110 };
111 
112 // A structure to hold redirect output parameters
113 typedef struct
114 {
115  bool redirect;
116  std::string fileName;
118 
120 
121 void usage()
122 {
123  std::cerr << "usage: itkTestDriver [options] prg [args]" << std::endl;
124  std::cerr << " itkTestDriver --no-process [options]" << std::endl;
125  std::cerr << std::endl;
126  std::cerr << "itkTestDriver alter the environment, run a test program and compare the images" << std::endl;
127  std::cerr << "produced." << std::endl;
128  std::cerr << std::endl;
129  std::cerr << "Options:" << std::endl;
130  std::cerr << " --add-before-libpath PATH" << std::endl;
131  std::cerr << " Add a path to the library path environment. This option take care of" << std::endl;
132  std::cerr << " choosing the right environment variable for your system." << std::endl;
133  std::cerr << " This option can be used several times." << std::endl;
134  std::cerr << std::endl;
135  std::cerr << " --add-before-env NAME VALUE" << std::endl;
136  std::cerr << " Add a VALUE to the variable name in the environment." << std::endl;
137  std::cerr << " The seperator used is the default one on the system." << std::endl;
138  std::cerr << " This option can be used several times." << std::endl;
139  std::cerr << std::endl;
140  std::cerr << " --add-before-env-with-sep NAME VALUE SEP" << std::endl;
141  std::cerr << " Add a VALUE to the variable name in the environment using the provided separator." << std::endl;
142  std::cerr << " This option can be used several times." << std::endl;
143  std::cerr << std::endl;
144  std::cerr << " --remove-env NAME" << std::endl;
145  std::cerr << " Remove the variable name from the environment." << std::endl;
146  std::cerr << " This option can be used several times." << std::endl;
147  std::cerr << std::endl;
148  std::cerr << " --compare TEST BASELINE" << std::endl;
149  std::cerr << " Compare the TEST image to the BASELINE one." << std::endl;
150  std::cerr << " This option can be used several times." << std::endl;
151  std::cerr << std::endl;
152  std::cerr << " --compare-MD5 TEST md5hash0 [ md5hash1 ... ]" << std::endl;
153  std::cerr << " Compare the TEST image file's md5 hash to the provided hash." << std::endl;
154  std::cerr << " md5hash0 is required and assumed to be a hash." << std::endl;
155  std::cerr << " Additional arguments are considered hashes when the string is 32 hexi-decimal characters. " << std::endl;
156  std::cerr << " This option can be used several times for multiple comparisons." << std::endl;
157  std::cerr << std::endl;
158  std::cerr << " --with-threads THREADS" << std::endl;
159  std::cerr << " Use at most THREADS threads." << std::endl;
160  std::cerr << std::endl;
161  std::cerr << " --without-threads" << std::endl;
162  std::cerr << " Use at most one thread." << std::endl;
163  std::cerr << std::endl;
164  std::cerr << " --compareNumberOfPixelsTolerance TOLERANCE" << std::endl;
165  std::cerr << " When comparing images with --compare, allow TOLERANCE pixels to differ." << std::endl;
166  std::cerr << " Default is 0." << std::endl;
167  std::cerr << std::endl;
168  std::cerr << " --compareRadiusTolerance TOLERANCE" << std::endl;
169  std::cerr << " Default is 0." << std::endl;
170  std::cerr << std::endl;
171  std::cerr << " --compareIntensityTolerance TOLERANCE" << std::endl;
172  std::cerr << " Default is 2.0." << std::endl;
173  std::cerr << std::endl;
174  std::cerr << " --no-process" << std::endl;
175  std::cerr << " The test driver will not invoke any process." << std::endl;
176  std::cerr << std::endl;
177  std::cerr << " --full-output" << std::endl;
178  std::cerr << " Causes the full output of the test to be passed to cdash." << std::endl;
179  std::cerr << " --redirect-output TEST_OUTPUT" << std::endl;
180  std::cerr << " Redirects the test output to the file TEST_OUTPUT." << std::endl;
181  std::cerr << std::endl;
182  std::cerr << " --" << std::endl;
183  std::cerr << " The options after -- are not interpreted by this program and passed" << std::endl;
184  std::cerr << " directly to the test program." << std::endl;
185  std::cerr << std::endl;
186  std::cerr << " --help" << std::endl;
187  std::cerr << " Display this message and exit." << std::endl;
188  std::cerr << std::endl;
189 }
190 
191 static char my_to_lower(const char c)
192 {
193  return static_cast<char>( ::tolower(c));
194 }
195 
196 int ProcessArguments(int *ac, ArgumentStringType *av, ProcessedOutputType * processedOutput = ITK_NULLPTR )
197 {
198 #if defined(LINUX) && !defined(__MINGW32__)
200 #endif
201  regressionTestParameters.intensityTolerance = 2.0;
202  regressionTestParameters.numberOfPixelsTolerance = 0;
203  regressionTestParameters.radiusTolerance = 0;
204 
205  redirectOutputParameters.redirect = false;
206 
207  if( processedOutput )
208  {
209  processedOutput->externalProcessMustBeCalled = true;
210  }
211 
212  // parse the command line
213  int i = 1;
214  bool skip = false;
215  while ( i < *ac )
216  {
217  if ( !skip && strcmp((*av)[i], "--compare") == 0 )
218  {
219  if ( i + 2 >= *ac )
220  {
221  usage();
222  return 1;
223  }
224  regressionTestParameters.compareList.push_back( ComparePairType((*av)[i + 1], (*av)[i + 2]) );
225  (*av) += 3;
226  *ac -= 3;
227  }
228  else if ( !skip && strcmp((*av)[i], "--compare-MD5") == 0 )
229  {
230  if ( i + 2 >= *ac )
231  {
232  usage();
233  return 1;
234  }
235  const char *filename = (*av)[i + 1];
236  std::string md5hash0 = (*av)[i + 2];
237 
238  // convert hash to all lowercase letters
239  std::transform(md5hash0.begin(), md5hash0.end(), md5hash0.begin(), my_to_lower );
240 
241  // chech that the hash is of expected format
242  if ( md5hash0.size() != 32 ||
243  md5hash0.find_first_not_of( "0123456789abcdef" ) != std::string::npos )
244  {
245  std::cerr << "Warning: argument does not appear to be a valid md5 hash \"" << md5hash0 << "\"." << std::endl;
246  }
247 
248  std::vector< std::string > hashVector;
249  hashVector.push_back( md5hash0 );
250 
251  (*av) += 3;
252  (*ac) -= 3;
253 
254  // continue eating hash values
255  while ( *ac - i > 0 )
256  {
257  std::string md5hashN = (*av)[i];
258 
259  // convert hash to all lowercase letters
260  std::transform(md5hashN.begin(), md5hashN.end(), md5hashN.begin(), my_to_lower );
261 
262  // check if the next argument is a hash
263  if ( md5hashN.size() != 32 ||
264  md5hashN.find_first_not_of( "0123456789abcdef" ) != std::string::npos )
265  {
266  break;
267  }
268 
269  // add the hash
270  hashVector.push_back( md5hashN );
271 
272  // successful hash,
273  // move the arguments along
274  ++(*av);
275  --(*ac);
276  }
277 
278 
279  hashTestList.push_back( HashPairType( filename, hashVector ) );
280 
281  }
282  else if ( !skip && strcmp((*av)[i], "--") == 0 )
283  {
284  skip = true;
285  i += 1;
286  }
287  else if ( !skip && strcmp((*av)[i], "--help") == 0 )
288  {
289  usage();
290  return 1;
291  }
292  else if ( !skip && strcmp((*av)[i], "--with-threads") == 0 )
293  {
294  if ( i + 2 >= *ac )
295  {
296  usage();
297  return 1;
298  }
299  // set the environment which will be read by the subprocess
300  std::string threadEnv = "ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS=";
301  threadEnv += (*av)[i + 1];
302  itksys::SystemTools::PutEnv( threadEnv.c_str() );
303  // and set the number of threads locally for the comparison
304  itk::MultiThreader::SetGlobalDefaultNumberOfThreads(atoi((*av)[i + 1]));
305  *av += 2;
306  *ac -= 2;
307  }
308  else if ( !skip && strcmp((*av)[i], "--without-threads") == 0 )
309  {
310  itksys::SystemTools::PutEnv( "ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS=1" );
311  itk::MultiThreader::SetGlobalDefaultNumberOfThreads(1);
312  *av += 1;
313  *ac -= 1;
314  }
315  else if ( !skip && strcmp((*av)[i], "--compareNumberOfPixelsTolerance") == 0 )
316  {
317  if ( i + 2 >= *ac )
318  {
319  usage();
320  return 1;
321  }
322  regressionTestParameters.numberOfPixelsTolerance = atoi((*av)[i + 1]);
323  *av += 2;
324  *ac -= 2;
325  }
326  else if ( !skip && strcmp((*av)[i], "--compareRadiusTolerance") == 0 )
327  {
328  if ( i + 1 >= *ac )
329  {
330  usage();
331  return 1;
332  }
333  regressionTestParameters.radiusTolerance = atoi((*av)[i + 1]);
334  (*av) += 2;
335  *ac -= 2;
336  }
337  else if ( !skip && strcmp((*av)[i], "--compareIntensityTolerance") == 0 )
338  {
339  if ( i + 2 >= *ac )
340  {
341  usage();
342  return 1;
343  }
344  regressionTestParameters.intensityTolerance = atof((*av)[i + 1]);
345  (*av) += 2;
346  *ac -= 2;
347  }
348  else if ( !skip && strcmp((*av)[i], "--add-before-libpath") == 0 )
349  {
350  if ( i + 2 >= *ac )
351  {
352  usage();
353  return 1;
354  }
355  if( processedOutput )
356  {
357  processedOutput->add_before_libpath.push_back( (*av)[i+1] );
358  }
359  (*av) += 2;
360  *ac -= 2;
361  }
362  else if ( !skip && strcmp((*av)[i], "--add-before-env") == 0 )
363  {
364  if ( i + 3 >= *ac )
365  {
366  usage();
367  return 1;
368  }
369  if( processedOutput )
370  {
371  processedOutput->add_before_env.push_back( (*av)[i+1] );
372  processedOutput->add_before_env.push_back( (*av)[i+2] );
373  }
374  (*av) += 3;
375  *ac -= 3;
376  }
377  else if ( !skip && strcmp((*av)[i], "--add-before-env-with-sep") == 0 )
378  {
379  if ( i + 4 >= *ac )
380  {
381  usage();
382  return 1;
383  }
384  if( processedOutput )
385  {
386  processedOutput->add_before_env_with_sep.push_back( (*av)[i+1] );
387  processedOutput->add_before_env_with_sep.push_back( (*av)[i+2] );
388  processedOutput->add_before_env_with_sep.push_back( (*av)[i+3] );
389  }
390  (*av) += 4;
391  *ac -= 4;
392  }
393  else if ( !skip && strcmp((*av)[i], "--remove-env") == 0 )
394  {
395  if ( i + 2 >= *ac )
396  {
397  usage();
398  return 1;
399  }
400 
401  itksys::SystemTools::UnPutEnv( (*av)[i+1] );
402 
403  (*av) += 2;
404  *ac -= 2;
405  }
406  else if ( !skip && strcmp((*av)[i], "--full-output") == 0 )
407  {
408  // emit the string to tell ctest that the full output should be
409  // passed to cdash.
410  std::cout << "CTEST_FULL_OUTPUT" << std::endl;
411  (*av) += 1;
412  *ac -= 1;
413  }
414  else if ( !skip && strcmp((*av)[i], "--no-process") == 0 )
415  {
416  // The test driver needs to invoke another executable
417  // For example, the python interpreter to run Wrapping tests.
418  if( processedOutput )
419  {
420  processedOutput->externalProcessMustBeCalled = false;
421  }
422  (*av) += 1;
423  *ac -= 1;
424  }
425  else if ( !skip && strcmp((*av)[i], "--redirectOutput") == 0 )
426  {
427  if ( i + 1 >= *ac )
428  {
429  usage();
430  return 1;
431  }
432  redirectOutputParameters.redirect = true;
433  redirectOutputParameters.fileName = (*av)[i + 1];
434  *av += 2;
435  *ac -= 2;
436  }
437  else
438  {
439  if( processedOutput )
440  {
441  processedOutput->args.push_back((*av)[i]);
442  }
443  i += 1;
444  }
445  }
446 
447  return 0;
448 }
449 
450 
451 // Regression Testing Code
452 //
453 // This method returns:
454 // max int , if there is an error reading baselineImageFilename
455 // max int - 2, if there is an error reading testImageFilename
456 // max int - 1, if the size of the images don't match
457 // the number of pixel beyond the tolerance
458 // otherwise zero is returned if the difference is with in tolerances
459 
460 int RegressionTestImage(const char *testImageFilename,
461  const char *baselineImageFilename,
462  int reportErrors,
463  double intensityTolerance,
464  ::itk::SizeValueType numberOfPixelsTolerance,
465  unsigned int radiusTolerance)
466 {
467  // Use the factory mechanism to read the test and baseline files and convert
468  // them to double
471  typedef itk::Image< unsigned char, 2 > DiffOutputType;
472  typedef itk::ImageFileReader< ImageType > ReaderType;
473 
474  // Read the baseline file
475  ReaderType::Pointer baselineReader = ReaderType::New();
476  baselineReader->SetFileName(baselineImageFilename);
477  try
478  {
479  baselineReader->UpdateLargestPossibleRegion();
480  }
481  catch ( itk::ExceptionObject & e )
482  {
483  std::cerr << "Exception detected while reading " << baselineImageFilename << " : " << e.GetDescription();
485  }
486 
487  // Read the file generated by the test
488  ReaderType::Pointer testReader = ReaderType::New();
489  testReader->SetFileName(testImageFilename);
490  try
491  {
492  testReader->UpdateLargestPossibleRegion();
493  }
494  catch ( itk::ExceptionObject & e )
495  {
496  std::cerr << "Exception detected while reading " << testImageFilename << " : " << e.GetDescription() << std::endl;
498  }
499 
500  // The sizes of the baseline and test image must match
501  ImageType::SizeType baselineSize;
502  baselineSize = baselineReader->GetOutput()->GetLargestPossibleRegion().GetSize();
503  ImageType::SizeType testSize;
504  testSize = testReader->GetOutput()->GetLargestPossibleRegion().GetSize();
505 
506  if ( baselineSize != testSize )
507  {
508  std::cerr << "The size of the Baseline image and Test image do not match!" << std::endl;
509  std::cerr << "Baseline image: " << baselineImageFilename
510  << " has size " << baselineSize << std::endl;
511  std::cerr << "Test image: " << testImageFilename
512  << " has size " << testSize << std::endl;
514  }
515 
516  // Now compare the two images
518  DiffType::Pointer diff = DiffType::New();
519  diff->SetValidInput( baselineReader->GetOutput() );
520  diff->SetTestInput( testReader->GetOutput() );
521  diff->SetDifferenceThreshold(intensityTolerance);
522  diff->SetToleranceRadius(radiusTolerance);
523  diff->UpdateLargestPossibleRegion();
524 
525  itk::SizeValueType status = diff->GetNumberOfPixelsWithDifferences();
526 
527  if ( ! reportErrors )
528  {
529  //The measurement errors should be reported for both success and errors
530  //to facilitate setting tight tolerances of tests.
531  std::string shortFilename = itksys::SystemTools::GetFilenameName( baselineImageFilename );
532 
533  std::cout << "<DartMeasurement name=\"ImageError " << shortFilename
534  << "\" type=\"numeric/double\">";
535  std::cout << status;
536  std::cout << "</DartMeasurement>" << std::endl;
537  }
538 
539  // if there are discrepencies, create an diff image
540  if ( ( status > numberOfPixelsTolerance ) && reportErrors )
541  {
542 
543  // Report actuall image error to best baseline
544  std::cout << "<DartMeasurement name=\"ImageError\" type=\"numeric/double\">";
545  std::cout << status;
546  std::cout << "</DartMeasurement>" << std::endl;
547 
548 
549  // Report statistics for pixels which exceed tolerances
550  std::cout << "<DartMeasurement name=\"ImageError Minimum\" type=\"numeric/double\">";
551  std::cout << diff->GetMinimumDifference() << "</DartMeasurement>" << std::endl;
552 
553  std::cout << "<DartMeasurement name=\"ImageError Maximum\" type=\"numeric/double\">";
554  std::cout << diff->GetMaximumDifference() << "</DartMeasurement>" << std::endl;
555 
556  std::cout << "<DartMeasurement name=\"ImageError Mean\" type=\"numeric/double\">";
557  std::cout << diff->GetMeanDifference() << "</DartMeasurement>" << std::endl;
558 
559 
562  typedef itk::ImageFileWriter< DiffOutputType > WriterType;
564  OutputType::SizeType size; size.Fill(0);
565 
566  RescaleType::Pointer rescale = RescaleType::New();
567  rescale->SetOutputMinimum( itk::NumericTraits< unsigned char >::NonpositiveMin() );
568  rescale->SetOutputMaximum( itk::NumericTraits< unsigned char >::max() );
569  rescale->SetInput( diff->GetOutput() );
570  rescale->UpdateLargestPossibleRegion();
571  size = rescale->GetOutput()->GetLargestPossibleRegion().GetSize();
572 
573  //Get the center slice of the image, In 3D, the first slice
574  //is often a black slice with little debugging information.
575  OutputType::IndexType index; index.Fill(0);
576  for ( unsigned int i = 2; i < ITK_TEST_DIMENSION_MAX; i++ )
577  {
578  index[i] = size[i] / 2; //NOTE: Integer Divide used to get approximately
579  // the center slice
580  size[i] = 0;
581  }
582 
583  RegionType region;
584  region.SetIndex(index);
585 
586  region.SetSize(size);
587 
588  ExtractType::Pointer extract = ExtractType::New();
589  extract->SetDirectionCollapseToIdentity();
590  extract->SetInput( rescale->GetOutput() );
591  extract->SetExtractionRegion(region);
592 
593  WriterType::Pointer writer = WriterType::New();
594  writer->SetInput( extract->GetOutput() );
595 
596  std::ostringstream diffName;
597  diffName << testImageFilename << ".diff.png";
598  try
599  {
600  rescale->SetInput( diff->GetOutput() );
601  rescale->Update();
602  }
603  catch ( const std::exception & e )
604  {
605  std::cerr << "Error during rescale of " << diffName.str() << std::endl;
606  std::cerr << e.what() << "\n";
607  }
608  catch ( ... )
609  {
610  std::cerr << "Error during rescale of " << diffName.str() << std::endl;
611  }
612  writer->SetFileName( diffName.str().c_str() );
613  try
614  {
615  writer->Update();
616  }
617  catch ( const std::exception & e )
618  {
619  std::cerr << "Error during write of " << diffName.str() << std::endl;
620  std::cerr << e.what() << "\n";
621  }
622  catch ( ... )
623  {
624  std::cerr << "Error during write of " << diffName.str() << std::endl;
625  }
626 
627  std::cout << "<DartMeasurementFile name=\"DifferenceImage\" type=\"image/png\">";
628  std::cout << diffName.str();
629  std::cout << "</DartMeasurementFile>" << std::endl;
630 
631  std::ostringstream baseName;
632  baseName << testImageFilename << ".base.png";
633  try
634  {
635  rescale->SetInput( baselineReader->GetOutput() );
636  rescale->Update();
637  }
638  catch ( const std::exception & e )
639  {
640  std::cerr << "Error during rescale of " << baseName.str() << std::endl;
641  std::cerr << e.what() << "\n";
642  }
643  catch ( ... )
644  {
645  std::cerr << "Error during rescale of " << baseName.str() << std::endl;
646  }
647  try
648  {
649  writer->SetFileName( baseName.str().c_str() );
650  writer->Update();
651  }
652  catch ( const std::exception & e )
653  {
654  std::cerr << "Error during write of " << baseName.str() << std::endl;
655  std::cerr << e.what() << "\n";
656  }
657  catch ( ... )
658  {
659  std::cerr << "Error during write of " << baseName.str() << std::endl;
660  }
661 
662  std::cout << "<DartMeasurementFile name=\"BaselineImage\" type=\"image/png\">";
663  std::cout << baseName.str();
664  std::cout << "</DartMeasurementFile>" << std::endl;
665 
666  std::ostringstream testName;
667  testName << testImageFilename << ".test.png";
668  try
669  {
670  rescale->SetInput( testReader->GetOutput() );
671  rescale->Update();
672  }
673  catch ( const std::exception & e )
674  {
675  std::cerr << "Error during rescale of " << testName.str() << std::endl;
676  std::cerr << e.what() << "\n";
677  }
678  catch ( ... )
679  {
680  std::cerr << "Error during rescale of " << testName.str() << std::endl;
681  }
682  try
683  {
684  writer->SetFileName( testName.str().c_str() );
685  writer->Update();
686  }
687  catch ( const std::exception & e )
688  {
689  std::cerr << "Error during write of " << testName.str() << std::endl;
690  std::cerr << e.what() << "\n";
691  }
692  catch ( ... )
693  {
694  std::cerr << "Error during write of " << testName.str() << std::endl;
695  }
696 
697  std::cout << "<DartMeasurementFile name=\"TestImage\" type=\"image/png\">";
698  std::cout << testName.str();
699  std::cout << "</DartMeasurementFile>" << std::endl;
700  }
701  return ( status > numberOfPixelsTolerance ) ? static_cast<int>(status) : 0;
702 }
703 
704 template< typename TImageType >
705 std::string ComputeHash( const char *testImageFilename )
706 {
707  typedef TImageType ImageType;
708  typedef itk::ImageFileReader< ImageType > ReaderType;
709 
710 
711  // Read the file generated by the test
712  typename ReaderType::Pointer testReader = ReaderType::New();
713  testReader->SetFileName(testImageFilename);
714  try
715  {
716  testReader->UpdateLargestPossibleRegion();
717  }
718  catch ( itk::ExceptionObject & e )
719  {
720  std::cerr << "Exception detected while reading " << testImageFilename << " : " << e.GetDescription() << std::endl;
721  throw; // re-throw
722  }
723 
724  typedef itk::Testing::HashImageFilter<ImageType> HashFilterType;
725 
726  typename HashFilterType::Pointer hasher = HashFilterType::New();
727  hasher->SetInput( testReader->GetOutput() );
728  hasher->Update();
729 
730  return hasher->GetHash();
731 }
732 
733 
734 int HashTestImage( const char *testImageFilename,
735  const std::vector<std::string> &baselineMD5Vector )
736 {
739 
740  if ( iobase.IsNull() )
741  {
742  itkGenericExceptionMacro( "Unable to determine ImageIO reader for \"" << testImageFilename << "\"" );
743  }
744 
745  // Read the image information
746  iobase->SetFileName( testImageFilename );
747  iobase->ReadImageInformation();
748 
749  // get output information about input image
750  itk::ImageIOBase::IOComponentType componentType = iobase->GetComponentType();
751 
752  std::string testMD5 = "";
753  switch(componentType)
754  {
756  testMD5 = ComputeHash< itk::VectorImage<char, ITK_TEST_DIMENSION_MAX> >( testImageFilename );
757  break;
759  testMD5 = ComputeHash< itk::VectorImage<unsigned char, ITK_TEST_DIMENSION_MAX> >( testImageFilename );
760  break;
762  testMD5 = ComputeHash< itk::VectorImage<short, ITK_TEST_DIMENSION_MAX> >( testImageFilename );
763  break;
765  testMD5 = ComputeHash< itk::VectorImage<unsigned short, ITK_TEST_DIMENSION_MAX> >( testImageFilename );
766  break;
768  testMD5 = ComputeHash< itk::VectorImage<int, ITK_TEST_DIMENSION_MAX> >( testImageFilename );
769  break;
771  testMD5 = ComputeHash< itk::VectorImage<unsigned int, ITK_TEST_DIMENSION_MAX> >( testImageFilename );
772  break;
774  testMD5 = ComputeHash< itk::VectorImage<long, ITK_TEST_DIMENSION_MAX> >( testImageFilename );
775  break;
777  testMD5 = ComputeHash< itk::VectorImage<unsigned long, ITK_TEST_DIMENSION_MAX> >( testImageFilename );
778  break;
781  std::cerr << "Hashing is not supporting for float and double images." << std::endl;
782  itkGenericExceptionMacro( "Hashing is not supported for images of float or doubles." );
783  break;
785  default:
786  assert( false ); // should never get here unless we forgot a type
787  itkGenericExceptionMacro( "Logic error!" );
788  }
789 
790  std::vector<std::string>::const_iterator iter = baselineMD5Vector.begin();
791  assert( baselineMD5Vector.size() );
792  do
793  {
794  if ( *iter == testMD5 )
795  {
796  // success, let's get out of here
797  return 0;
798  }
799  }
800  while (++iter != baselineMD5Vector.end() );
801 
802  // failed to match print the different md5s
803  std::cout << "<DartMeasurement name=\"TestMD5\" type=\"text/string\">";
804  std::cout << testMD5;
805  std::cout << "</DartMeasurement>" << std::endl;
806 
807 
808  // print out all md5 baselines
809  for ( iter = baselineMD5Vector.begin(); iter != baselineMD5Vector.end(); ++iter )
810  {
811  std::cout << "<DartMeasurement name=\"BaselineMD5\" type=\"text/string\">";
812  std::cout << *iter;
813  std::cout << "</DartMeasurement>" << std::endl;
814  }
815 
817  typedef itk::Image< double, 2 > SliceImageType;
818  typedef itk::Image< unsigned char, 2 > OutputType;
819  typedef itk::ImageFileReader< ImageType > ReaderType;
822  typedef itk::ImageFileWriter< OutputType > WriterType;
823 
824  // setup reader
825  ReaderType::Pointer reader = ReaderType::New();
826  reader->SetFileName( testImageFilename );
827  reader->UpdateLargestPossibleRegion();
828 
829  ImageType::SizeType size;
830  size = reader->GetOutput()->GetLargestPossibleRegion().GetSize();
831 
832  //Get the center slice of the image, In 3D, the first slice
833  //is often a black slice with little debugging information.
834  ImageType::IndexType index; index.Fill(0);
835  for ( unsigned int i = 2; i < ITK_TEST_DIMENSION_MAX; i++ )
836  {
837  index[i] = size[i] / 2; //NOTE: Integer Divide used to get approximately
838  // the center slice
839  size[i] = 0;
840  }
841 
842 
843  ImageType::RegionType region;
844  region.SetIndex(index);
845 
846  region.SetSize(size);
847 
848  ExtractType::Pointer extract = ExtractType::New();
849  extract->SetDirectionCollapseToIdentity();
850  extract->SetInput( reader->GetOutput() );
851  extract->SetExtractionRegion(region);
852 
853  RescaleType::Pointer rescale = RescaleType::New();
854  rescale->SetOutputMinimum( itk::NumericTraits< unsigned char >::NonpositiveMin() );
855  rescale->SetOutputMaximum( itk::NumericTraits< unsigned char >::max() );
856  rescale->SetInput( extract->GetOutput() );
857 
858  WriterType::Pointer writer = WriterType::New();
859  writer->SetInput( rescale->GetOutput() );
860 
861 
862  std::ostringstream testName;
863  testName << testImageFilename << ".test.png";
864 
865  writer->SetFileName( testName.str().c_str() );
866 
867  try
868  {
869  rescale->UpdateLargestPossibleRegion();
870  writer->Update();
871  }
872  catch ( const std::exception & e )
873  {
874  std::cerr << "Error during rescale and writing of " << testName.str()<< std::endl;
875  std::cerr << e.what() << "\n";
876  }
877  catch ( ... )
878  {
879  std::cerr << "Unknow error during rescale and writing of " << testName.str() << std::endl;
880  }
881 
882  std::cout << "<DartMeasurementFile name=\"TestImage\" type=\"image/png\">";
883  std::cout << testName.str();
884  std::cout << "</DartMeasurementFile>" << std::endl;
885 
886  return 1;
887 }
888 
889 //
890 // Generate all of the possible baselines
891 // The possible baselines are generated fromn the baselineFilename using the
892 // following algorithm:
893 // 1) strip the suffix
894 // 2) append a digit .x
895 // 3) append the original suffix.
896 // It the file exists, increment x and continue
897 //
898 std::map< std::string, int > RegressionTestBaselines(char *baselineFilename)
899 {
900  std::map< std::string, int > baselines;
901  baselines[std::string(baselineFilename)] = 0;
902 
903  std::string originalBaseline(baselineFilename);
904 
905  int x = 0;
906  std::string::size_type suffixPos = originalBaseline.rfind(".");
907  std::string suffix;
908  if ( suffixPos != std::string::npos )
909  {
910  suffix = originalBaseline.substr( suffixPos, originalBaseline.length() );
911  originalBaseline.erase( suffixPos, originalBaseline.length() );
912  }
913  while ( ++x )
914  {
915  std::ostringstream filename;
916  filename << originalBaseline << "." << x << suffix;
917  std::ifstream filestream( filename.str().c_str() );
918  if ( !filestream )
919  {
920  break;
921  }
922  baselines[filename.str()] = 0;
923  filestream.close();
924  }
925  return baselines;
926 }
927 
928 
929 // Needed for explicit instantiation
930 #include "itkTestingComparisonImageFilter.hxx"
931 
932 #endif
std::string ComputeHash(const char *testImageFilename)
ArgumentsList add_before_libpath
static char my_to_lower(const char c)
ArgumentsList add_before_env_with_sep
An image region represents a structured region of data.
int HashTestImage(const char *testImageFilename, const std::string md5hash)
static const double e
The base of the natural logarithm or Euler&#39;s number
Definition: itkMath.h:47
std::vector< char * > ArgumentsList
unsigned long SizeValueType
Definition: itkIntTypes.h:143
Applies a linear transformation to the intensity levels of the input Image.
int RegressionTestImage(const char *testImageFilename, const char *baselineImageFilename, int reportErrors, double intensityTolerance,::itk::SizeValueType numberOfPixelsTolerance=0, unsigned int radiusTolerance=0)
char ** ArgumentStringType
std::map< std::string, int > RegressionTestBaselines(char *)
RegressionTestParameters regressionTestParameters
std::pair< char *, char * > ComparePairType
static T max(const T &)
Generates a md5 hash string from an image.
Standard exception handling object.
#define ITK_TEST_DIMENSION_MAX
bool IsNull() const
Writes image data to a single file.
Implements comparison between two images.
Data source that reads image data from a single file.
static ImageIOBasePointer CreateImageIO(const char *path, FileModeType mode)
std::vector< ComparePairType > compareList
int ProcessArguments(int *ac, ArgumentStringType *av, ProcessedOutputType *processedOutput=nullptr)
void usage()
Define additional traits for native types such as int or float.
RedirectOutputParameters redirectOutputParameters
std::pair< const char *, std::vector< std::string > > HashPairType
std::vector< HashPairType > hashTestList
Templated n-dimensional image class.
Definition: itkImage.h:75
Decrease the image size by cropping the image to the selected region bounds.
virtual const char * GetDescription() const