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