ITK  4.4.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 #ifdef LINUX
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 = NULL )
197 {
198 #ifdef LINUX
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 
526  status = diff->GetNumberOfPixelsWithDifferences();
527 
528  if ( ! reportErrors )
529  {
530  //The measurement errors should be reported for both success and errors
531  //to facilitate setting tight tolerances of tests.
532  std::string shortFilename = itksys::SystemTools::GetFilenameName( baselineImageFilename );
533 
534  std::cout << "<DartMeasurement name=\"ImageError " << shortFilename
535  << "\" type=\"numeric/double\">";
536  std::cout << status;
537  std::cout << "</DartMeasurement>" << std::endl;
538  }
539 
540  // if there are discrepencies, create an diff image
541  if ( ( status > numberOfPixelsTolerance ) && reportErrors )
542  {
543 
544  // Report actuall image error to best baseline
545  std::cout << "<DartMeasurement name=\"ImageError\" type=\"numeric/double\">";
546  std::cout << status;
547  std::cout << "</DartMeasurement>" << std::endl;
548 
549 
550  // Report statistics for pixels which exceed tolerances
551  std::cout << "<DartMeasurement name=\"ImageError Minimum\" type=\"numeric/double\">";
552  std::cout << diff->GetMinimumDifference() << "</DartMeasurement>" << std::endl;
553 
554  std::cout << "<DartMeasurement name=\"ImageError Maximum\" type=\"numeric/double\">";
555  std::cout << diff->GetMaximumDifference() << "</DartMeasurement>" << std::endl;
556 
557  std::cout << "<DartMeasurement name=\"ImageError Mean\" type=\"numeric/double\">";
558  std::cout << diff->GetMeanDifference() << "</DartMeasurement>" << std::endl;
559 
560 
563  typedef itk::ImageFileWriter< DiffOutputType > WriterType;
565  OutputType::SizeType size; size.Fill(0);
566 
567  RescaleType::Pointer rescale = RescaleType::New();
568  rescale->SetOutputMinimum( itk::NumericTraits< unsigned char >::NonpositiveMin() );
569  rescale->SetOutputMaximum( itk::NumericTraits< unsigned char >::max() );
570  rescale->SetInput( diff->GetOutput() );
571  rescale->UpdateLargestPossibleRegion();
572  size = rescale->GetOutput()->GetLargestPossibleRegion().GetSize();
573 
574  //Get the center slice of the image, In 3D, the first slice
575  //is often a black slice with little debugging information.
576  OutputType::IndexType index; index.Fill(0);
577  for ( unsigned int i = 2; i < ITK_TEST_DIMENSION_MAX; i++ )
578  {
579  index[i] = size[i] / 2; //NOTE: Integer Divide used to get approximately
580  // the center slice
581  size[i] = 0;
582  }
583 
584  RegionType region;
585  region.SetIndex(index);
586 
587  region.SetSize(size);
588 
589  ExtractType::Pointer extract = ExtractType::New();
590  extract->SetDirectionCollapseToIdentity();
591  extract->SetInput( rescale->GetOutput() );
592  extract->SetExtractionRegion(region);
593 
594  WriterType::Pointer writer = WriterType::New();
595  writer->SetInput( extract->GetOutput() );
596 
597  std::ostringstream diffName;
598  diffName << testImageFilename << ".diff.png";
599  try
600  {
601  rescale->SetInput( diff->GetOutput() );
602  rescale->Update();
603  }
604  catch ( const std::exception & e )
605  {
606  std::cerr << "Error during rescale of " << diffName.str() << std::endl;
607  std::cerr << e.what() << "\n";
608  }
609  catch ( ... )
610  {
611  std::cerr << "Error during rescale of " << diffName.str() << std::endl;
612  }
613  writer->SetFileName( diffName.str().c_str() );
614  try
615  {
616  writer->Update();
617  }
618  catch ( const std::exception & e )
619  {
620  std::cerr << "Error during write of " << diffName.str() << std::endl;
621  std::cerr << e.what() << "\n";
622  }
623  catch ( ... )
624  {
625  std::cerr << "Error during write of " << diffName.str() << std::endl;
626  }
627 
628  std::cout << "<DartMeasurementFile name=\"DifferenceImage\" type=\"image/png\">";
629  std::cout << diffName.str();
630  std::cout << "</DartMeasurementFile>" << std::endl;
631 
632  std::ostringstream baseName;
633  baseName << testImageFilename << ".base.png";
634  try
635  {
636  rescale->SetInput( baselineReader->GetOutput() );
637  rescale->Update();
638  }
639  catch ( const std::exception & e )
640  {
641  std::cerr << "Error during rescale of " << baseName.str() << std::endl;
642  std::cerr << e.what() << "\n";
643  }
644  catch ( ... )
645  {
646  std::cerr << "Error during rescale of " << baseName.str() << std::endl;
647  }
648  try
649  {
650  writer->SetFileName( baseName.str().c_str() );
651  writer->Update();
652  }
653  catch ( const std::exception & e )
654  {
655  std::cerr << "Error during write of " << baseName.str() << std::endl;
656  std::cerr << e.what() << "\n";
657  }
658  catch ( ... )
659  {
660  std::cerr << "Error during write of " << baseName.str() << std::endl;
661  }
662 
663  std::cout << "<DartMeasurementFile name=\"BaselineImage\" type=\"image/png\">";
664  std::cout << baseName.str();
665  std::cout << "</DartMeasurementFile>" << std::endl;
666 
667  std::ostringstream testName;
668  testName << testImageFilename << ".test.png";
669  try
670  {
671  rescale->SetInput( testReader->GetOutput() );
672  rescale->Update();
673  }
674  catch ( const std::exception & e )
675  {
676  std::cerr << "Error during rescale of " << testName.str() << std::endl;
677  std::cerr << e.what() << "\n";
678  }
679  catch ( ... )
680  {
681  std::cerr << "Error during rescale of " << testName.str() << std::endl;
682  }
683  try
684  {
685  writer->SetFileName( testName.str().c_str() );
686  writer->Update();
687  }
688  catch ( const std::exception & e )
689  {
690  std::cerr << "Error during write of " << testName.str() << std::endl;
691  std::cerr << e.what() << "\n";
692  }
693  catch ( ... )
694  {
695  std::cerr << "Error during write of " << testName.str() << std::endl;
696  }
697 
698  std::cout << "<DartMeasurementFile name=\"TestImage\" type=\"image/png\">";
699  std::cout << testName.str();
700  std::cout << "</DartMeasurementFile>" << std::endl;
701  }
702  return ( status > numberOfPixelsTolerance ) ? static_cast<int>(status) : 0;
703 }
704 
705 template< typename TImageType >
706 std::string ComputeHash( const char *testImageFilename )
707 {
708  typedef TImageType ImageType;
709  typedef itk::ImageFileReader< ImageType > ReaderType;
710 
711 
712  // Read the file generated by the test
713  typename ReaderType::Pointer testReader = ReaderType::New();
714  testReader->SetFileName(testImageFilename);
715  try
716  {
717  testReader->UpdateLargestPossibleRegion();
718  }
719  catch ( itk::ExceptionObject & e )
720  {
721  std::cerr << "Exception detected while reading " << testImageFilename << " : " << e.GetDescription() << std::endl;
722  throw; // re-throw
723  }
724 
725  typedef itk::Testing::HashImageFilter<ImageType> HashFilterType;
726 
727  typename HashFilterType::Pointer hasher = HashFilterType::New();
728  hasher->SetInput( testReader->GetOutput() );
729  hasher->Update();
730 
731  return hasher->GetHash();
732 }
733 
734 
735 int HashTestImage( const char *testImageFilename,
736  const std::vector<std::string> &baselineMD5Vector )
737 {
740 
741  if ( iobase.IsNull() )
742  {
743  itkGenericExceptionMacro( "Unable to determine ImageIO reader for \"" << testImageFilename << "\"" );
744  }
745 
746  // Read the image information
747  iobase->SetFileName( testImageFilename );
748  iobase->ReadImageInformation();
749 
750  // get output information about input image
751  itk::ImageIOBase::IOComponentType componentType = iobase->GetComponentType();
752 
753  std::string testMD5 = "";
754  switch(componentType)
755  {
757  testMD5 = ComputeHash< itk::VectorImage<char, ITK_TEST_DIMENSION_MAX> >( testImageFilename );
758  break;
760  testMD5 = ComputeHash< itk::VectorImage<unsigned char, ITK_TEST_DIMENSION_MAX> >( testImageFilename );
761  break;
763  testMD5 = ComputeHash< itk::VectorImage<short, ITK_TEST_DIMENSION_MAX> >( testImageFilename );
764  break;
766  testMD5 = ComputeHash< itk::VectorImage<unsigned short, ITK_TEST_DIMENSION_MAX> >( testImageFilename );
767  break;
769  testMD5 = ComputeHash< itk::VectorImage<int, ITK_TEST_DIMENSION_MAX> >( testImageFilename );
770  break;
772  testMD5 = ComputeHash< itk::VectorImage<unsigned int, ITK_TEST_DIMENSION_MAX> >( testImageFilename );
773  break;
775  testMD5 = ComputeHash< itk::VectorImage<long, ITK_TEST_DIMENSION_MAX> >( testImageFilename );
776  break;
778  testMD5 = ComputeHash< itk::VectorImage<unsigned long, ITK_TEST_DIMENSION_MAX> >( testImageFilename );
779  break;
782  std::cerr << "Hashing is not supporting for float and double images." << std::endl;
783  itkGenericExceptionMacro( "Hashing is not supported for images of float or doubles." );
784  break;
786  default:
787  assert( false ); // should never get here unless we forgot a type
788  itkGenericExceptionMacro( "Logic error!" );
789  }
790 
791  std::vector<std::string>::const_iterator iter = baselineMD5Vector.begin();
792  assert( baselineMD5Vector.size() );
793  do
794  {
795  if ( *iter == testMD5 )
796  {
797  // success, let's get out of here
798  return 0;
799  }
800  }
801  while (++iter != baselineMD5Vector.end() );
802 
803  // failed to match print the different md5s
804  std::cout << "<DartMeasurement name=\"TestMD5\" type=\"text/string\">";
805  std::cout << testMD5;
806  std::cout << "</DartMeasurement>" << std::endl;
807 
808 
809  // print out all md5 baselines
810  for ( iter = baselineMD5Vector.begin(); iter != baselineMD5Vector.end(); ++iter )
811  {
812  std::cout << "<DartMeasurement name=\"BaselineMD5\" type=\"text/string\">";
813  std::cout << *iter;
814  std::cout << "</DartMeasurement>" << std::endl;
815  }
816 
818  typedef itk::Image< double, 2 > SliceImageType;
819  typedef itk::Image< unsigned char, 2 > OutputType;
820  typedef itk::ImageFileReader< ImageType > ReaderType;
823  typedef itk::ImageFileWriter< OutputType > WriterType;
824 
825  // setup reader
826  ReaderType::Pointer reader = ReaderType::New();
827  reader->SetFileName( testImageFilename );
828  reader->UpdateLargestPossibleRegion();
829 
830  ImageType::SizeType size;
831  size = reader->GetOutput()->GetLargestPossibleRegion().GetSize();
832 
833  //Get the center slice of the image, In 3D, the first slice
834  //is often a black slice with little debugging information.
835  ImageType::IndexType index; index.Fill(0);
836  for ( unsigned int i = 2; i < ITK_TEST_DIMENSION_MAX; i++ )
837  {
838  index[i] = size[i] / 2; //NOTE: Integer Divide used to get approximately
839  // the center slice
840  size[i] = 0;
841  }
842 
843 
844  ImageType::RegionType region;
845  region.SetIndex(index);
846 
847  region.SetSize(size);
848 
849  ExtractType::Pointer extract = ExtractType::New();
850  extract->SetDirectionCollapseToIdentity();
851  extract->SetInput( reader->GetOutput() );
852  extract->SetExtractionRegion(region);
853 
854  RescaleType::Pointer rescale = RescaleType::New();
855  rescale->SetOutputMinimum( itk::NumericTraits< unsigned char >::NonpositiveMin() );
856  rescale->SetOutputMaximum( itk::NumericTraits< unsigned char >::max() );
857  rescale->SetInput( extract->GetOutput() );
858 
859  WriterType::Pointer writer = WriterType::New();
860  writer->SetInput( rescale->GetOutput() );
861 
862 
863  std::ostringstream testName;
864  testName << testImageFilename << ".test.png";
865 
866  writer->SetFileName( testName.str().c_str() );
867 
868  try
869  {
870  rescale->UpdateLargestPossibleRegion();
871  writer->Update();
872  }
873  catch ( const std::exception & e )
874  {
875  std::cerr << "Error during rescale and writing of " << testName.str()<< std::endl;
876  std::cerr << e.what() << "\n";
877  }
878  catch ( ... )
879  {
880  std::cerr << "Unknow error during rescale and writing of " << testName.str() << std::endl;
881  }
882 
883  std::cout << "<DartMeasurementFile name=\"TestImage\" type=\"image/png\">";
884  std::cout << testName.str();
885  std::cout << "</DartMeasurementFile>" << std::endl;
886 
887  return 1;
888 }
889 
890 //
891 // Generate all of the possible baselines
892 // The possible baselines are generated fromn the baselineFilename using the
893 // following algorithm:
894 // 1) strip the suffix
895 // 2) append a digit .x
896 // 3) append the original suffix.
897 // It the file exists, increment x and continue
898 //
899 std::map< std::string, int > RegressionTestBaselines(char *baselineFilename)
900 {
901  std::map< std::string, int > baselines;
902  baselines[std::string(baselineFilename)] = 0;
903 
904  std::string originalBaseline(baselineFilename);
905 
906  int x = 0;
907  std::string::size_type suffixPos = originalBaseline.rfind(".");
908  std::string suffix;
909  if ( suffixPos != std::string::npos )
910  {
911  suffix = originalBaseline.substr( suffixPos, originalBaseline.length() );
912  originalBaseline.erase( suffixPos, originalBaseline.length() );
913  }
914  while ( ++x )
915  {
916  std::ostringstream filename;
917  filename << originalBaseline << "." << x << suffix;
918  std::ifstream filestream( filename.str().c_str() );
919  if ( !filestream )
920  {
921  break;
922  }
923  baselines[filename.str()] = 0;
924  filestream.close();
925  }
926  return baselines;
927 }
928 
929 
930 // Needed for explicit instantiation
931 #include "itkTestingComparisonImageFilter.hxx"
932 
933 #endif
934