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