ITK  4.1.0
Insight Segmentation and Registration Toolkit
itkTestDriverInclude.h
Go to the documentation of this file.
00001 /*=========================================================================
00002  *
00003  *  Copyright Insight Software Consortium
00004  *
00005  *  Licensed under the Apache License, Version 2.0 (the "License");
00006  *  you may not use this file except in compliance with the License.
00007  *  You may obtain a copy of the License at
00008  *
00009  *         http://www.apache.org/licenses/LICENSE-2.0.txt
00010  *
00011  *  Unless required by applicable law or agreed to in writing, software
00012  *  distributed under the License is distributed on an "AS IS" BASIS,
00013  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00014  *  See the License for the specific language governing permissions and
00015  *  limitations under the License.
00016  *
00017  *=========================================================================*/
00018 /*=========================================================================
00019  *
00020  *  Portions of this file are subject to the VTK Toolkit Version 3 copyright.
00021  *
00022  *  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
00023  *
00024  *  For complete copyright, license and disclaimer of warranty information
00025  *  please refer to the NOTICE file at the top of the ITK source tree.
00026  *
00027  *=========================================================================*/
00028 #ifndef __itkTestDriverInclude_h
00029 #define __itkTestDriverInclude_h
00030 //
00031 // This file is used by the TestDriver executables generated by CMake's
00032 // create_test_sourcelist. It defines a function, ProcessArguments
00033 // that processes the command line for the test driver prior to
00034 // invoking the test. It also defines the RegressiontestImage function
00035 // that is called after a test has been run by the driver.
00036 // command line options prior to invoking the test.
00037 //
00038 
00039 #include "itksys/Process.h"
00040 
00041 #include "itkWin32Header.h"
00042 #include <map>
00043 #include <string>
00044 #include <iostream>
00045 #include <fstream>
00046 #include "itkMultiThreader.h"
00047 #include "itkImage.h"
00048 #include "itkImageFileReader.h"
00049 #include "itkImageFileWriter.h"
00050 #include "itkImageRegionConstIterator.h"
00051 #include "itkTestingStretchIntensityImageFilter.h"
00052 #include "itkTestingExtractSliceImageFilter.h"
00053 #include "itkTestingComparisonImageFilter.h"
00054 #include "itkTestingHashImageFilter.h"
00055 #include "itksys/SystemTools.hxx"
00056 #include "itkIntTypes.h"
00057 #ifdef LINUX
00058 #include "itkFloatingPointExceptions.h"
00059 #endif
00060 #include "vnl/vnl_sample.h"
00061 
00062 #define ITK_TEST_DIMENSION_MAX 6
00063 
00064 int RegressionTestImage(const char *testImageFilename,
00065                         const char *baselineImageFilename,
00066                         int reportErrors,
00067                         double intensityTolerance,
00068                         ::itk::SizeValueType numberOfPixelsTolerance = 0,
00069                         unsigned int radiusTolerance = 0);
00070 
00071 int HashTestImage( const char *testImageFilename,
00072                    const std::string md5hash );
00073 
00074 
00075 std::map< std::string, int > RegressionTestBaselines(char *);
00076 
00077 typedef std::pair< char *, char * > ComparePairType;
00078 
00079 // A structure to hold regression test parameters
00080 typedef struct
00081 {
00082   std::vector< ComparePairType > compareList;
00083   double intensityTolerance;
00084   unsigned int numberOfPixelsTolerance;
00085   unsigned int radiusTolerance;
00086 } RegressionTestParameters;
00087 
00088 RegressionTestParameters regressionTestParameters;
00089 
00090 
00091 typedef std::pair< const char *, std::vector<std::string> > HashPairType;
00092 
00093 std::vector< HashPairType > hashTestList;
00094 
00095 typedef char ** ArgumentStringType;
00096 
00097 
00098 // Types to hold parameters that should be processed later
00099 typedef std::vector< char * > ArgumentsList;
00100 
00101 struct ProcessedOutputType
00102 {
00103   bool externalProcessMustBeCalled;
00104 
00105   ArgumentsList args;
00106   ArgumentsList add_before_libpath;
00107   ArgumentsList add_before_env;
00108   ArgumentsList add_before_env_with_sep;
00109 };
00110 
00111 // A structure to hold redirect output parameters
00112 typedef struct
00113 {
00114   bool redirect;
00115   std::string fileName;
00116 } RedirectOutputParameters;
00117 
00118 RedirectOutputParameters redirectOutputParameters;
00119 
00120 void usage()
00121 {
00122   std::cerr << "usage: itkTestDriver [options] prg [args]" << std::endl;
00123   std::cerr << "       itkTestDriver --no-process [options]" << std::endl;
00124   std::cerr << std::endl;
00125   std::cerr << "itkTestDriver alter the environment, run a test program and compare the images" << std::endl;
00126   std::cerr << "produced." << std::endl;
00127   std::cerr << std::endl;
00128   std::cerr << "Options:" << std::endl;
00129   std::cerr << "  --add-before-libpath PATH" << std::endl;
00130   std::cerr << "      Add a path to the library path environment. This option take care of" << std::endl;
00131   std::cerr << "      choosing the right environment variable for your system." << std::endl;
00132   std::cerr << "      This option can be used several times." << std::endl;
00133   std::cerr << std::endl;
00134   std::cerr << "  --add-before-env NAME VALUE" << std::endl;
00135   std::cerr << "      Add a VALUE to the variable name in the environment." << std::endl;
00136   std::cerr << "      The seperator used is the default one on the system." << std::endl;
00137   std::cerr << "      This option can be used several times." << std::endl;
00138   std::cerr << std::endl;
00139   std::cerr << "  --add-before-env-with-sep NAME VALUE SEP" << std::endl;
00140   std::cerr << "      Add a VALUE to the variable name in the environment using the provided separator." << std::endl;
00141   std::cerr << "      This option can be used several times." << std::endl;
00142   std::cerr << std::endl;
00143   std::cerr << "  --compare TEST BASELINE" << std::endl;
00144   std::cerr << "      Compare the TEST image to the BASELINE one." << std::endl;
00145   std::cerr << "      This option can be used several times." << std::endl;
00146   std::cerr << std::endl;
00147   std::cerr << "  --compare-MD5 TEST md5hash0 [ md5hash1 ... ]" << std::endl;
00148   std::cerr << "      Compare the TEST image file's md5 hash to the provided hash." << std::endl;
00149   std::cerr << "      md5hash0 is required and assumed to be a hash." << std::endl;
00150   std::cerr << "      Additional arguments are considerd hashes when the string is 32 hexi-decimal characters. " << std::endl;
00151   std::cerr << "      This option can be used several times for multiple comparisons." << std::endl;
00152   std::cerr << std::endl;
00153   std::cerr << "  --with-threads THREADS" << std::endl;
00154   std::cerr << "      Use at most THREADS threads." << std::endl;
00155   std::cerr << std::endl;
00156   std::cerr << "  --without-threads" << std::endl;
00157   std::cerr << "      Use at most one thread." << std::endl;
00158   std::cerr << std::endl;
00159   std::cerr << "  --compareNumberOfPixelsTolerance TOLERANCE" << std::endl;
00160   std::cerr << "      When comparing images with --compare, allow TOLERANCE pixels to differ." << std::endl;
00161   std::cerr << "      Default is 0." << std::endl;
00162   std::cerr << std::endl;
00163   std::cerr << "  --compareRadiusTolerance TOLERANCE" << std::endl;
00164   std::cerr << "      Default is 0." << std::endl;
00165   std::cerr << std::endl;
00166   std::cerr << "  --compareIntensityTolerance TOLERANCE" << std::endl;
00167   std::cerr << "      Default is 2.0." << std::endl;
00168   std::cerr << std::endl;
00169   std::cerr << "  --no-process" << std::endl;
00170   std::cerr << "      The test driver will not invoke any process." << std::endl;
00171   std::cerr << std::endl;
00172   std::cerr << "  --full-output" << std::endl;
00173   std::cerr << "      Causes the full output of the test to be passed to cdash." << std::endl;
00174   std::cerr << "  --redirect-output TEST_OUTPUT" << std::endl;
00175   std::cerr << "      Redirects the test output to the file TEST_OUTPUT." << std::endl;
00176   std::cerr << std::endl;
00177   std::cerr << "  --" << std::endl;
00178   std::cerr << "      The options after -- are not interpreted by this program and passed" << std::endl;
00179   std::cerr << "      directly to the test program." << std::endl;
00180   std::cerr << std::endl;
00181   std::cerr << "  --help" << std::endl;
00182   std::cerr << "      Display this message and exit." << std::endl;
00183   std::cerr << std::endl;
00184 }
00185 
00186 
00187 int ProcessArguments(int *ac, ArgumentStringType *av, ProcessedOutputType * processedOutput = NULL )
00188 {
00189 #ifdef LINUX
00190   itk::FloatingPointExceptions::Enable();
00191 #endif
00192   regressionTestParameters.intensityTolerance  = 2.0;
00193   regressionTestParameters.numberOfPixelsTolerance = 0;
00194   regressionTestParameters.radiusTolerance = 0;
00195 
00196   redirectOutputParameters.redirect = false;
00197 
00198   if( processedOutput )
00199     {
00200     processedOutput->externalProcessMustBeCalled = true;
00201     }
00202 
00203   // parse the command line
00204   int  i = 1;
00205   bool skip = false;
00206   while ( i < *ac )
00207   {
00208      if ( !skip && strcmp((*av)[i], "--compare") == 0 )
00209       {
00210       if ( i + 2 >= *ac )
00211         {
00212         usage();
00213         return 1;
00214         }
00215       regressionTestParameters.compareList.push_back( ComparePairType((*av)[i + 1], (*av)[i + 2]) );
00216       (*av) += 3;
00217       *ac -= 3;
00218       }
00219      else if ( !skip && strcmp((*av)[i], "--compare-MD5") == 0 )
00220       {
00221       if ( i + 2 >= *ac )
00222         {
00223         usage();
00224         return 1;
00225         }
00226       const char *filename = (*av)[i + 1];
00227       std::string md5hash0 =  (*av)[i + 2];
00228 
00229      // convert hash to all lowercase letters
00230      for( std::string::iterator iter = md5hash0.begin(); iter != md5hash0.end(); ++iter )
00231        {
00232        *iter = tolower(*iter);
00233        }
00234 
00235      // chech that the hash is of expected format
00236      if ( md5hash0.size() != 32 ||
00237           md5hash0.find_first_not_of( "0123456789abcdef" ) != std::string::npos )
00238        {
00239        std::cerr << "Warning: argument does not appear to be a valid md5 hash \"" << md5hash0 << "\"." << std::endl;
00240        }
00241 
00242      std::vector< std::string > hashVector;
00243      hashVector.push_back( md5hash0 );
00244 
00245      (*av) += 3;
00246      (*ac) -= 3;
00247 
00248      // continue eating hash values
00249      while ( *ac - i > 0 )
00250        {
00251        std::string md5hashN = (*av)[i];
00252 
00253        // convert hash to all lowercase letters
00254        for( std::string::iterator iter = md5hashN.begin(); iter != md5hashN.end(); ++iter )
00255          {
00256          *iter = tolower(*iter);
00257          }
00258 
00259        // check if the next argument is a hash
00260        if ( md5hashN.size() != 32 ||
00261             md5hashN.find_first_not_of( "0123456789abcdef" ) != std::string::npos )
00262          {
00263          break;
00264          }
00265 
00266        // add the hash
00267        hashVector.push_back( md5hashN );
00268 
00269        // successful hash,
00270        // move the arguments along
00271        ++(*av);
00272        --(*ac);
00273        }
00274 
00275 
00276      hashTestList.push_back( HashPairType( filename, hashVector )  );
00277 
00278       }
00279     else if ( !skip && strcmp((*av)[i], "--") == 0 )
00280       {
00281       skip = true;
00282       i += 1;
00283       }
00284     else if ( !skip && strcmp((*av)[i], "--help") == 0 )
00285       {
00286       usage();
00287       return 1;
00288       }
00289     else if ( !skip && strcmp((*av)[i], "--with-threads") == 0 )
00290       {
00291       if ( i + 1 >= *ac )
00292         {
00293         usage();
00294         return 1;
00295         }
00296       // set the environment which will be read by the subprocess
00297       std::string threadEnv = "ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS=";
00298       threadEnv += (*av)[i + 1];
00299       itksys::SystemTools::PutEnv( threadEnv.c_str() );
00300       // and set the number of threads locally for the comparison
00301       itk::MultiThreader::SetGlobalDefaultNumberOfThreads(atoi((*av)[i + 1]));
00302       *av += 2;
00303       *ac -= 2;
00304       }
00305     else if ( !skip && strcmp((*av)[i], "--without-threads") == 0 )
00306       {
00307       itksys::SystemTools::PutEnv( "ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS=1" );
00308       itk::MultiThreader::SetGlobalDefaultNumberOfThreads(1);
00309       *av += 1;
00310       *ac -= 1;
00311       }
00312     else if ( !skip && strcmp((*av)[i], "--compareNumberOfPixelsTolerance") == 0 )
00313       {
00314       if ( i + 1 >= *ac )
00315         {
00316         usage();
00317         return 1;
00318         }
00319       regressionTestParameters.numberOfPixelsTolerance = atoi((*av)[i + 1]);
00320       *av += 2;
00321       *ac -= 2;
00322       }
00323     else if ( !skip && strcmp((*av)[i], "--compareRadiusTolerance") == 0 )
00324       {
00325       if ( i + 1 >= *ac )
00326         {
00327         usage();
00328         return 1;
00329         }
00330      regressionTestParameters.radiusTolerance = atoi((*av)[i + 1]);
00331       (*av) += 2;
00332       *ac -= 2;
00333       }
00334     else if ( !skip && strcmp((*av)[i], "--compareIntensityTolerance") == 0 )
00335       {
00336       if ( i + 1 >= *ac )
00337         {
00338         usage();
00339         return 1;
00340         }
00341       regressionTestParameters.intensityTolerance = atof((*av)[i + 1]);
00342       (*av) += 2;
00343       *ac -= 2;
00344       }
00345     else if ( !skip && strcmp((*av)[i], "--add-before-libpath") == 0 )
00346       {
00347       if ( i + 1 >= *ac )
00348         {
00349         usage();
00350         return 1;
00351         }
00352       if( processedOutput )
00353         {
00354         processedOutput->add_before_libpath.push_back( (*av)[i+1] );
00355         }
00356       (*av) += 2;
00357       *ac -= 2;
00358       }
00359     else if ( !skip && strcmp((*av)[i], "--add-before-env") == 0 )
00360       {
00361       if ( i + 1 >= *ac )
00362         {
00363         usage();
00364         return 1;
00365         }
00366       if( processedOutput )
00367         {
00368         processedOutput->add_before_env.push_back( (*av)[i+1] );
00369         processedOutput->add_before_env.push_back( (*av)[i+2] );
00370         }
00371       (*av) += 3;
00372       *ac -= 3;
00373       }
00374     else if ( !skip && strcmp((*av)[i], "--add-before-env-with-sep") == 0 )
00375       {
00376       if ( i + 1 >= *ac )
00377         {
00378         usage();
00379         return 1;
00380         }
00381       if( processedOutput )
00382         {
00383         processedOutput->add_before_env_with_sep.push_back( (*av)[i+1] );
00384         processedOutput->add_before_env_with_sep.push_back( (*av)[i+2] );
00385         processedOutput->add_before_env_with_sep.push_back( (*av)[i+3] );
00386         }
00387       (*av) += 4;
00388       *ac -= 4;
00389       }
00390     else if ( !skip && strcmp((*av)[i], "--full-output") == 0 )
00391       {
00392       // emit the string to tell ctest that the full output should be
00393       // passed to cdash.
00394       std::cout << "CTEST_FULL_OUTPUT" << std::endl;
00395       (*av) += 1;
00396       *ac -= 1;
00397       }
00398     else if ( !skip && strcmp((*av)[i], "--no-process") == 0 )
00399       {
00400       // The test driver needs to invoke another executable
00401       // For example, the python interpreter to run Wrapping tests.
00402       if( processedOutput )
00403         {
00404         processedOutput->externalProcessMustBeCalled = false;
00405         }
00406       (*av) += 1;
00407       *ac -= 1;
00408       }
00409     else if ( !skip && strcmp((*av)[i], "--redirectOutput") == 0 )
00410       {
00411       if ( i + 1 >= *ac )
00412         {
00413         usage();
00414         return 1;
00415         }
00416       redirectOutputParameters.redirect = true;
00417       redirectOutputParameters.fileName = (*av)[i + 1];
00418       *av += 2;
00419       *ac -= 2;
00420       }
00421     else
00422       {
00423       if( processedOutput )
00424         {
00425         processedOutput->args.push_back((*av)[i]);
00426         }
00427       i += 1;
00428       }
00429   }
00430 
00431   return 0;
00432 }
00433 
00434 
00435 // Regression Testing Code
00436 //
00437 // This method returns:
00438 //  max int, if there is an error reading either file
00439 //  max int - 1, if the sized of the images don't match
00440 //  the number of pixel beyond the tolerance
00441 //  otherwise zero is returned if the difference is with in tolerances
00442 
00443 int RegressionTestImage(const char *testImageFilename,
00444                         const char *baselineImageFilename,
00445                         int reportErrors,
00446                         double intensityTolerance,
00447                         ::itk::SizeValueType numberOfPixelsTolerance,
00448                         unsigned int radiusTolerance)
00449 {
00450   // Use the factory mechanism to read the test and baseline files and convert
00451   // them to double
00452   typedef itk::Image< double, ITK_TEST_DIMENSION_MAX >        ImageType;
00453   typedef itk::Image< unsigned char, ITK_TEST_DIMENSION_MAX > OutputType;
00454   typedef itk::Image< unsigned char, 2 >                      DiffOutputType;
00455   typedef itk::ImageFileReader< ImageType >                   ReaderType;
00456 
00457   // Read the baseline file
00458   ReaderType::Pointer baselineReader = ReaderType::New();
00459   baselineReader->SetFileName(baselineImageFilename);
00460   try
00461     {
00462     baselineReader->UpdateLargestPossibleRegion();
00463     }
00464   catch ( itk::ExceptionObject & e )
00465     {
00466     std::cerr << "Exception detected while reading " << baselineImageFilename << " : "  << e.GetDescription();
00467     return itk::NumericTraits< int >::max();
00468     }
00469 
00470   // Read the file generated by the test
00471   ReaderType::Pointer testReader = ReaderType::New();
00472   testReader->SetFileName(testImageFilename);
00473   try
00474     {
00475     testReader->UpdateLargestPossibleRegion();
00476     }
00477   catch ( itk::ExceptionObject & e )
00478     {
00479     std::cerr << "Exception detected while reading " << testImageFilename << " : "  << e.GetDescription() << std::endl;
00480     return itk::NumericTraits< int >::max();
00481     }
00482 
00483   // The sizes of the baseline and test image must match
00484   ImageType::SizeType baselineSize;
00485   baselineSize = baselineReader->GetOutput()->GetLargestPossibleRegion().GetSize();
00486   ImageType::SizeType testSize;
00487   testSize = testReader->GetOutput()->GetLargestPossibleRegion().GetSize();
00488 
00489   if ( baselineSize != testSize )
00490     {
00491     std::cerr << "The size of the Baseline image and Test image do not match!" << std::endl;
00492     std::cerr << "Baseline image: " << baselineImageFilename
00493               << " has size " << baselineSize << std::endl;
00494     std::cerr << "Test image:     " << testImageFilename
00495               << " has size " << testSize << std::endl;
00496     return itk::NumericTraits< int >::max()-1;
00497     }
00498 
00499   // Now compare the two images
00500   typedef itk::Testing::ComparisonImageFilter< ImageType, ImageType > DiffType;
00501   DiffType::Pointer diff = DiffType::New();
00502   diff->SetValidInput( baselineReader->GetOutput() );
00503   diff->SetTestInput( testReader->GetOutput() );
00504   diff->SetDifferenceThreshold(intensityTolerance);
00505   diff->SetToleranceRadius(radiusTolerance);
00506   diff->UpdateLargestPossibleRegion();
00507 
00508   itk::SizeValueType status = itk::NumericTraits< itk::SizeValueType >::Zero;
00509   status = diff->GetNumberOfPixelsWithDifferences();
00510 
00511   if ( ! reportErrors )
00512     {
00513     //The measurement errors should be reported for both success and errors
00514     //to facilitate setting tight tolerances of tests.
00515     std::string shortFilename = itksys::SystemTools::GetFilenameName( baselineImageFilename );
00516 
00517     std::cout << "<DartMeasurement name=\"ImageError " << shortFilename
00518               << "\" type=\"numeric/double\">";
00519     std::cout << status;
00520     std::cout <<  "</DartMeasurement>" << std::endl;
00521     }
00522 
00523   // if there are discrepencies, create an diff image
00524   if ( ( status > numberOfPixelsTolerance ) && reportErrors )
00525     {
00526 
00527     // Report actuall image error to best baseline
00528     std::cout << "<DartMeasurement name=\"ImageError\" type=\"numeric/double\">";
00529     std::cout << status;
00530     std::cout <<  "</DartMeasurement>" << std::endl;
00531 
00532 
00533     // Report statistics for pixels which exceed tolerances
00534     std::cout << "<DartMeasurement name=\"ImageError Minimum\" type=\"numeric/double\">";
00535     std::cout << diff->GetMinimumDifference() << "</DartMeasurement>" << std::endl;
00536 
00537     std::cout << "<DartMeasurement name=\"ImageError Maximum\" type=\"numeric/double\">";
00538     std::cout << diff->GetMaximumDifference() << "</DartMeasurement>" << std::endl;
00539 
00540     std::cout << "<DartMeasurement name=\"ImageError Mean\" type=\"numeric/double\">";
00541     std::cout << diff->GetMeanDifference() << "</DartMeasurement>" << std::endl;
00542 
00543 
00544     typedef itk::Testing::StretchIntensityImageFilter< ImageType, OutputType >     RescaleType;
00545     typedef itk::Testing::ExtractSliceImageFilter< OutputType, DiffOutputType >    ExtractType;
00546     typedef itk::ImageFileWriter< DiffOutputType >                        WriterType;
00547     typedef itk::ImageRegion< ITK_TEST_DIMENSION_MAX >                    RegionType;
00548     OutputType::SizeType size; size.Fill(0);
00549 
00550     RescaleType::Pointer rescale = RescaleType::New();
00551     rescale->SetOutputMinimum( itk::NumericTraits< unsigned char >::NonpositiveMin() );
00552     rescale->SetOutputMaximum( itk::NumericTraits< unsigned char >::max() );
00553     rescale->SetInput( diff->GetOutput() );
00554     rescale->UpdateLargestPossibleRegion();
00555     size = rescale->GetOutput()->GetLargestPossibleRegion().GetSize();
00556 
00557     //Get the center slice of the image,  In 3D, the first slice
00558     //is often a black slice with little debugging information.
00559     OutputType::IndexType index; index.Fill(0);
00560     for ( unsigned int i = 2; i < ITK_TEST_DIMENSION_MAX; i++ )
00561       {
00562       index[i] = size[i] / 2; //NOTE: Integer Divide used to get approximately
00563                               // the center slice
00564       size[i] = 0;
00565       }
00566 
00567     RegionType region;
00568     region.SetIndex(index);
00569 
00570     region.SetSize(size);
00571 
00572     ExtractType::Pointer extract = ExtractType::New();
00573     extract->SetDirectionCollapseToIdentity();
00574     extract->SetInput( rescale->GetOutput() );
00575     extract->SetExtractionRegion(region);
00576 
00577     WriterType::Pointer writer = WriterType::New();
00578     writer->SetInput( extract->GetOutput() );
00579 
00580     std::ostringstream diffName;
00581     diffName << testImageFilename << ".diff.png";
00582     try
00583       {
00584       rescale->SetInput( diff->GetOutput() );
00585       rescale->Update();
00586       }
00587     catch ( const std::exception & e )
00588       {
00589       std::cerr << "Error during rescale of " << diffName.str() << std::endl;
00590       std::cerr << e.what() << "\n";
00591       }
00592     catch ( ... )
00593       {
00594       std::cerr << "Error during rescale of " << diffName.str() << std::endl;
00595       }
00596     writer->SetFileName( diffName.str().c_str() );
00597     try
00598       {
00599       writer->Update();
00600       }
00601     catch ( const std::exception & e )
00602       {
00603       std::cerr << "Error during write of " << diffName.str() << std::endl;
00604       std::cerr << e.what() << "\n";
00605       }
00606     catch ( ... )
00607       {
00608       std::cerr << "Error during write of " << diffName.str() << std::endl;
00609       }
00610 
00611     std::cout << "<DartMeasurementFile name=\"DifferenceImage\" type=\"image/png\">";
00612     std::cout << diffName.str();
00613     std::cout << "</DartMeasurementFile>" << std::endl;
00614 
00615     std::ostringstream baseName;
00616     baseName << testImageFilename << ".base.png";
00617     try
00618       {
00619       rescale->SetInput( baselineReader->GetOutput() );
00620       rescale->Update();
00621       }
00622     catch ( const std::exception & e )
00623       {
00624       std::cerr << "Error during rescale of " << baseName.str() << std::endl;
00625       std::cerr << e.what() << "\n";
00626       }
00627     catch ( ... )
00628       {
00629       std::cerr << "Error during rescale of " << baseName.str() << std::endl;
00630       }
00631     try
00632       {
00633       writer->SetFileName( baseName.str().c_str() );
00634       writer->Update();
00635       }
00636     catch ( const std::exception & e )
00637       {
00638       std::cerr << "Error during write of " << baseName.str() << std::endl;
00639       std::cerr << e.what() << "\n";
00640       }
00641     catch ( ... )
00642       {
00643       std::cerr << "Error during write of " << baseName.str() << std::endl;
00644       }
00645 
00646     std::cout << "<DartMeasurementFile name=\"BaselineImage\" type=\"image/png\">";
00647     std::cout << baseName.str();
00648     std::cout << "</DartMeasurementFile>" << std::endl;
00649 
00650     std::ostringstream testName;
00651     testName << testImageFilename << ".test.png";
00652     try
00653       {
00654       rescale->SetInput( testReader->GetOutput() );
00655       rescale->Update();
00656       }
00657     catch ( const std::exception & e )
00658       {
00659       std::cerr << "Error during rescale of " << testName.str() << std::endl;
00660       std::cerr << e.what() << "\n";
00661       }
00662     catch ( ... )
00663       {
00664       std::cerr << "Error during rescale of " << testName.str() << std::endl;
00665       }
00666     try
00667       {
00668       writer->SetFileName( testName.str().c_str() );
00669       writer->Update();
00670       }
00671     catch ( const std::exception & e )
00672       {
00673       std::cerr << "Error during write of " << testName.str() << std::endl;
00674       std::cerr << e.what() << "\n";
00675       }
00676     catch ( ... )
00677       {
00678       std::cerr << "Error during write of " << testName.str() << std::endl;
00679       }
00680 
00681     std::cout << "<DartMeasurementFile name=\"TestImage\" type=\"image/png\">";
00682     std::cout << testName.str();
00683     std::cout << "</DartMeasurementFile>" << std::endl;
00684     }
00685   return ( status > numberOfPixelsTolerance ) ? status : 0;
00686 }
00687 
00688 template< typename TImageType >
00689 std::string ComputeHash( const char *testImageFilename )
00690 {
00691   typedef TImageType                        ImageType;
00692   typedef itk::ImageFileReader< ImageType > ReaderType;
00693 
00694 
00695   // Read the file generated by the test
00696   typename ReaderType::Pointer testReader = ReaderType::New();
00697   testReader->SetFileName(testImageFilename);
00698   try
00699     {
00700     testReader->UpdateLargestPossibleRegion();
00701     }
00702   catch ( itk::ExceptionObject & e )
00703     {
00704     std::cerr << "Exception detected while reading " << testImageFilename << " : "  << e.GetDescription() << std::endl;
00705     throw; // re-throw
00706     }
00707 
00708   typedef itk::Testing::HashImageFilter<ImageType> HashFilterType;
00709 
00710   typename HashFilterType::Pointer hasher = HashFilterType::New();
00711   hasher->SetInput( testReader->GetOutput() );
00712   hasher->Update();
00713 
00714   return hasher->GetHash();
00715 }
00716 
00717 
00718 int HashTestImage( const char *testImageFilename,
00719                    const std::vector<std::string> &baselineMD5Vector )
00720 {
00721   itk::ImageIOBase::Pointer iobase =
00722     itk::ImageIOFactory::CreateImageIO( testImageFilename, itk::ImageIOFactory::ReadMode);
00723 
00724   if ( iobase.IsNull() )
00725     {
00726     itkGenericExceptionMacro( "Unable to determine ImageIO reader for \"" << testImageFilename << "\"" );
00727     }
00728 
00729   // Read the image information
00730   iobase->SetFileName( testImageFilename );
00731   iobase->ReadImageInformation();
00732 
00733   // get output information about input image
00734   itk::ImageIOBase::IOComponentType componentType = iobase->GetComponentType();
00735 
00736   std::string testMD5 = "";
00737   switch(componentType)
00738     {
00739     case itk::ImageIOBase::CHAR:
00740       testMD5 = ComputeHash< itk::VectorImage<char, ITK_TEST_DIMENSION_MAX> >( testImageFilename );
00741       break;
00742     case itk::ImageIOBase::UCHAR:
00743       testMD5 = ComputeHash< itk::VectorImage<unsigned char, ITK_TEST_DIMENSION_MAX> >( testImageFilename );
00744       break;
00745     case itk::ImageIOBase::SHORT:
00746       testMD5 = ComputeHash< itk::VectorImage<short, ITK_TEST_DIMENSION_MAX> >( testImageFilename );
00747       break;
00748     case itk::ImageIOBase::USHORT:
00749       testMD5 = ComputeHash< itk::VectorImage<unsigned short, ITK_TEST_DIMENSION_MAX> >( testImageFilename );
00750       break;
00751     case itk::ImageIOBase::INT:
00752       testMD5 = ComputeHash< itk::VectorImage<int, ITK_TEST_DIMENSION_MAX> >( testImageFilename );
00753       break;
00754     case itk::ImageIOBase::UINT:
00755       testMD5 = ComputeHash< itk::VectorImage<unsigned int, ITK_TEST_DIMENSION_MAX> >( testImageFilename );
00756       break;
00757     case itk::ImageIOBase::LONG:
00758       testMD5 = ComputeHash< itk::VectorImage<long, ITK_TEST_DIMENSION_MAX> >( testImageFilename );
00759       break;
00760     case itk::ImageIOBase::ULONG:
00761       testMD5 = ComputeHash< itk::VectorImage<unsigned long, ITK_TEST_DIMENSION_MAX> >( testImageFilename );
00762       break;
00763     case itk::ImageIOBase::FLOAT:
00764     case itk::ImageIOBase::DOUBLE:
00765       std::cerr << "Hashing is not supporting for float and double images." << std::endl;
00766       itkGenericExceptionMacro( "Hashing is not supported for images of float or doubles." );
00767       break;
00768     case itk::ImageIOBase::UNKNOWNCOMPONENTTYPE:
00769     default:
00770       assert( false ); // should never get here unless we forgot a type
00771       itkGenericExceptionMacro( "Logic error!" );
00772     }
00773 
00774   std::vector<std::string>::const_iterator iter = baselineMD5Vector.begin();
00775   assert( baselineMD5Vector.size() );
00776   do
00777     {
00778     if ( *iter == testMD5 )
00779       {
00780       // success, let's get out of here
00781       return 0;
00782       }
00783     }
00784   while (++iter != baselineMD5Vector.end() );
00785 
00786   // failed to match print the different md5s
00787   std::cout << "<DartMeasurement name=\"TestMD5\" type=\"text/string\">";
00788   std::cout << testMD5;
00789   std::cout <<  "</DartMeasurement>" << std::endl;
00790 
00791 
00792   // print out all md5 baselines
00793   for ( iter = baselineMD5Vector.begin(); iter != baselineMD5Vector.end(); ++iter )
00794     {
00795     std::cout << "<DartMeasurement name=\"BaselineMD5\" type=\"text/string\">";
00796     std::cout << *iter;
00797     std::cout <<  "</DartMeasurement>" << std::endl;
00798     }
00799 
00800   typedef itk::Image< double, ITK_TEST_DIMENSION_MAX >                            ImageType;
00801   typedef itk::Image< double, 2 >                                                 SliceImageType;
00802   typedef itk::Image< unsigned char, 2 >                                          OutputType;
00803   typedef itk::ImageFileReader< ImageType >                                       ReaderType;
00804   typedef itk::Testing::StretchIntensityImageFilter< SliceImageType, OutputType > RescaleType;
00805   typedef itk::Testing::ExtractSliceImageFilter< ImageType, SliceImageType >      ExtractType;
00806   typedef itk::ImageFileWriter< OutputType >                                      WriterType;
00807 
00808   // setup reader
00809   ReaderType::Pointer reader = ReaderType::New();
00810   reader->SetFileName( testImageFilename );
00811   reader->UpdateLargestPossibleRegion();
00812 
00813   ImageType::SizeType size;
00814   size = reader->GetOutput()->GetLargestPossibleRegion().GetSize();
00815 
00816   //Get the center slice of the image,  In 3D, the first slice
00817   //is often a black slice with little debugging information.
00818   ImageType::IndexType index; index.Fill(0);
00819   for ( unsigned int i = 2; i < ITK_TEST_DIMENSION_MAX; i++ )
00820     {
00821     index[i] = size[i] / 2; //NOTE: Integer Divide used to get approximately
00822     // the center slice
00823     size[i] = 0;
00824     }
00825 
00826 
00827   ImageType::RegionType region;
00828   region.SetIndex(index);
00829 
00830   region.SetSize(size);
00831 
00832   ExtractType::Pointer extract = ExtractType::New();
00833   extract->SetDirectionCollapseToIdentity();
00834   extract->SetInput( reader->GetOutput() );
00835   extract->SetExtractionRegion(region);
00836 
00837   RescaleType::Pointer rescale = RescaleType::New();
00838   rescale->SetOutputMinimum( itk::NumericTraits< unsigned char >::NonpositiveMin() );
00839   rescale->SetOutputMaximum( itk::NumericTraits< unsigned char >::max() );
00840   rescale->SetInput( extract->GetOutput() );
00841 
00842   WriterType::Pointer writer = WriterType::New();
00843   writer->SetInput( rescale->GetOutput() );
00844 
00845 
00846   std::ostringstream testName;
00847   testName << testImageFilename << ".test.png";
00848 
00849   writer->SetFileName( testName.str().c_str() );
00850 
00851   try
00852     {
00853     rescale->UpdateLargestPossibleRegion();
00854     writer->Update();
00855     }
00856   catch ( const std::exception & e )
00857     {
00858     std::cerr << "Error during rescale and writing of " <<  testName.str()<< std::endl;
00859     std::cerr << e.what() << "\n";
00860     }
00861   catch ( ... )
00862     {
00863     std::cerr << "Unknow error during rescale and writing of " << testName.str() << std::endl;
00864     }
00865 
00866   std::cout << "<DartMeasurementFile name=\"TestImage\" type=\"image/png\">";
00867   std::cout << testName.str();
00868   std::cout << "</DartMeasurementFile>" << std::endl;
00869 
00870   return 1;
00871 }
00872 
00873 //
00874 // Generate all of the possible baselines
00875 // The possible baselines are generated fromn the baselineFilename using the
00876 // following algorithm:
00877 // 1) strip the suffix
00878 // 2) append a digit .x
00879 // 3) append the original suffix.
00880 // It the file exists, increment x and continue
00881 //
00882 std::map< std::string, int > RegressionTestBaselines(char *baselineFilename)
00883 {
00884   std::map< std::string, int > baselines;
00885   baselines[std::string(baselineFilename)] = 0;
00886 
00887   std::string originalBaseline(baselineFilename);
00888 
00889   int                    x = 0;
00890   std::string::size_type suffixPos = originalBaseline.rfind(".");
00891   std::string            suffix;
00892   if ( suffixPos != std::string::npos )
00893     {
00894     suffix = originalBaseline.substr( suffixPos, originalBaseline.length() );
00895     originalBaseline.erase( suffixPos, originalBaseline.length() );
00896     }
00897   while ( ++x )
00898     {
00899     std::ostringstream filename;
00900     filename << originalBaseline << "." << x << suffix;
00901     std::ifstream filestream( filename.str().c_str() );
00902     if ( !filestream )
00903       {
00904       break;
00905       }
00906     baselines[filename.str()] = 0;
00907     filestream.close();
00908     }
00909   return baselines;
00910 }
00911 
00912 
00913 // Needed for explicit instantiation
00914 #include "itkTestingComparisonImageFilter.hxx"
00915 
00916 #endif
00917