ITK  4.0.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 int RegressionTestImage(const char *testImageFilename,
00438                         const char *baselineImageFilename,
00439                         int reportErrors,
00440                         double intensityTolerance,
00441                         ::itk::SizeValueType numberOfPixelsTolerance,
00442                         unsigned int radiusTolerance)
00443 {
00444   // Use the factory mechanism to read the test and baseline files and convert
00445   // them to double
00446   typedef itk::Image< double, ITK_TEST_DIMENSION_MAX >        ImageType;
00447   typedef itk::Image< unsigned char, ITK_TEST_DIMENSION_MAX > OutputType;
00448   typedef itk::Image< unsigned char, 2 >                      DiffOutputType;
00449   typedef itk::ImageFileReader< ImageType >                   ReaderType;
00450 
00451   // Read the baseline file
00452   ReaderType::Pointer baselineReader = ReaderType::New();
00453   baselineReader->SetFileName(baselineImageFilename);
00454   try
00455     {
00456     baselineReader->UpdateLargestPossibleRegion();
00457     }
00458   catch ( itk::ExceptionObject & e )
00459     {
00460     std::cerr << "Exception detected while reading " << baselineImageFilename << " : "  << e.GetDescription();
00461     return 1000;
00462     }
00463 
00464   // Read the file generated by the test
00465   ReaderType::Pointer testReader = ReaderType::New();
00466   testReader->SetFileName(testImageFilename);
00467   try
00468     {
00469     testReader->UpdateLargestPossibleRegion();
00470     }
00471   catch ( itk::ExceptionObject & e )
00472     {
00473     std::cerr << "Exception detected while reading " << testImageFilename << " : "  << e.GetDescription() << std::endl;
00474     return 1000;
00475     }
00476 
00477   // The sizes of the baseline and test image must match
00478   ImageType::SizeType baselineSize;
00479   baselineSize = baselineReader->GetOutput()->GetLargestPossibleRegion().GetSize();
00480   ImageType::SizeType testSize;
00481   testSize = testReader->GetOutput()->GetLargestPossibleRegion().GetSize();
00482 
00483   if ( baselineSize != testSize )
00484     {
00485     std::cerr << "The size of the Baseline image and Test image do not match!" << std::endl;
00486     std::cerr << "Baseline image: " << baselineImageFilename
00487               << " has size " << baselineSize << std::endl;
00488     std::cerr << "Test image:     " << testImageFilename
00489               << " has size " << testSize << std::endl;
00490     return 1;
00491     }
00492 
00493   // Now compare the two images
00494   typedef itk::Testing::ComparisonImageFilter< ImageType, ImageType > DiffType;
00495   DiffType::Pointer diff = DiffType::New();
00496   diff->SetValidInput( baselineReader->GetOutput() );
00497   diff->SetTestInput( testReader->GetOutput() );
00498   diff->SetDifferenceThreshold(intensityTolerance);
00499   diff->SetToleranceRadius(radiusTolerance);
00500   diff->UpdateLargestPossibleRegion();
00501 
00502   itk::SizeValueType status = itk::NumericTraits< itk::SizeValueType >::Zero;
00503   status = diff->GetNumberOfPixelsWithDifferences();
00504 
00505   //The measurement errors should be reported for both success and errors
00506   //to facilitate setting tight tolerances of tests.
00507   std::cout << "<DartMeasurement name=\"ImageError\" type=\"numeric/double\">";
00508   std::cout << status;
00509   std::cout <<  "</DartMeasurement>" << std::endl;
00510 
00511   // if there are discrepencies, create an diff image
00512   if ( ( status > numberOfPixelsTolerance ) && reportErrors )
00513     {
00514     typedef itk::Testing::StretchIntensityImageFilter< ImageType, OutputType >     RescaleType;
00515     typedef itk::Testing::ExtractSliceImageFilter< OutputType, DiffOutputType >    ExtractType;
00516     typedef itk::ImageFileWriter< DiffOutputType >                        WriterType;
00517     typedef itk::ImageRegion< ITK_TEST_DIMENSION_MAX >                    RegionType;
00518     OutputType::SizeType size; size.Fill(0);
00519 
00520     RescaleType::Pointer rescale = RescaleType::New();
00521     rescale->SetOutputMinimum( itk::NumericTraits< unsigned char >::NonpositiveMin() );
00522     rescale->SetOutputMaximum( itk::NumericTraits< unsigned char >::max() );
00523     rescale->SetInput( diff->GetOutput() );
00524     rescale->UpdateLargestPossibleRegion();
00525     size = rescale->GetOutput()->GetLargestPossibleRegion().GetSize();
00526 
00527     //Get the center slice of the image,  In 3D, the first slice
00528     //is often a black slice with little debugging information.
00529     OutputType::IndexType index; index.Fill(0);
00530     for ( unsigned int i = 2; i < ITK_TEST_DIMENSION_MAX; i++ )
00531       {
00532       index[i] = size[i] / 2; //NOTE: Integer Divide used to get approximately
00533                               // the center slice
00534       size[i] = 0;
00535       }
00536 
00537     RegionType region;
00538     region.SetIndex(index);
00539 
00540     region.SetSize(size);
00541 
00542     ExtractType::Pointer extract = ExtractType::New();
00543     extract->SetDirectionCollapseToIdentity();
00544     extract->SetInput( rescale->GetOutput() );
00545     extract->SetExtractionRegion(region);
00546 
00547     WriterType::Pointer writer = WriterType::New();
00548     writer->SetInput( extract->GetOutput() );
00549 
00550     std::ostringstream diffName;
00551     diffName << testImageFilename << ".diff.png";
00552     try
00553       {
00554       rescale->SetInput( diff->GetOutput() );
00555       rescale->Update();
00556       }
00557     catch ( const std::exception & e )
00558       {
00559       std::cerr << "Error during rescale of " << diffName.str() << std::endl;
00560       std::cerr << e.what() << "\n";
00561       }
00562     catch ( ... )
00563       {
00564       std::cerr << "Error during rescale of " << diffName.str() << std::endl;
00565       }
00566     writer->SetFileName( diffName.str().c_str() );
00567     try
00568       {
00569       writer->Update();
00570       }
00571     catch ( const std::exception & e )
00572       {
00573       std::cerr << "Error during write of " << diffName.str() << std::endl;
00574       std::cerr << e.what() << "\n";
00575       }
00576     catch ( ... )
00577       {
00578       std::cerr << "Error during write of " << diffName.str() << std::endl;
00579       }
00580 
00581     std::cout << "<DartMeasurementFile name=\"DifferenceImage\" type=\"image/png\">";
00582     std::cout << diffName.str();
00583     std::cout << "</DartMeasurementFile>" << std::endl;
00584 
00585     std::ostringstream baseName;
00586     baseName << testImageFilename << ".base.png";
00587     try
00588       {
00589       rescale->SetInput( baselineReader->GetOutput() );
00590       rescale->Update();
00591       }
00592     catch ( const std::exception & e )
00593       {
00594       std::cerr << "Error during rescale of " << baseName.str() << std::endl;
00595       std::cerr << e.what() << "\n";
00596       }
00597     catch ( ... )
00598       {
00599       std::cerr << "Error during rescale of " << baseName.str() << std::endl;
00600       }
00601     try
00602       {
00603       writer->SetFileName( baseName.str().c_str() );
00604       writer->Update();
00605       }
00606     catch ( const std::exception & e )
00607       {
00608       std::cerr << "Error during write of " << baseName.str() << std::endl;
00609       std::cerr << e.what() << "\n";
00610       }
00611     catch ( ... )
00612       {
00613       std::cerr << "Error during write of " << baseName.str() << std::endl;
00614       }
00615 
00616     std::cout << "<DartMeasurementFile name=\"BaselineImage\" type=\"image/png\">";
00617     std::cout << baseName.str();
00618     std::cout << "</DartMeasurementFile>" << std::endl;
00619 
00620     std::ostringstream testName;
00621     testName << testImageFilename << ".test.png";
00622     try
00623       {
00624       rescale->SetInput( testReader->GetOutput() );
00625       rescale->Update();
00626       }
00627     catch ( const std::exception & e )
00628       {
00629       std::cerr << "Error during rescale of " << testName.str() << std::endl;
00630       std::cerr << e.what() << "\n";
00631       }
00632     catch ( ... )
00633       {
00634       std::cerr << "Error during rescale of " << testName.str() << std::endl;
00635       }
00636     try
00637       {
00638       writer->SetFileName( testName.str().c_str() );
00639       writer->Update();
00640       }
00641     catch ( const std::exception & e )
00642       {
00643       std::cerr << "Error during write of " << testName.str() << std::endl;
00644       std::cerr << e.what() << "\n";
00645       }
00646     catch ( ... )
00647       {
00648       std::cerr << "Error during write of " << testName.str() << std::endl;
00649       }
00650 
00651     std::cout << "<DartMeasurementFile name=\"TestImage\" type=\"image/png\">";
00652     std::cout << testName.str();
00653     std::cout << "</DartMeasurementFile>" << std::endl;
00654     }
00655   return ( status > numberOfPixelsTolerance ) ? 1 : 0;
00656 }
00657 
00658 template< typename TImageType >
00659 std::string ComputeHash( const char *testImageFilename )
00660 {
00661   typedef TImageType                        ImageType;
00662   typedef itk::ImageFileReader< ImageType > ReaderType;
00663 
00664 
00665   // Read the file generated by the test
00666   typename ReaderType::Pointer testReader = ReaderType::New();
00667   testReader->SetFileName(testImageFilename);
00668   try
00669     {
00670     testReader->UpdateLargestPossibleRegion();
00671     }
00672   catch ( itk::ExceptionObject & e )
00673     {
00674     std::cerr << "Exception detected while reading " << testImageFilename << " : "  << e.GetDescription() << std::endl;
00675     throw; // re-throw
00676     }
00677 
00678   typedef itk::Testing::HashImageFilter<ImageType> HashFilterType;
00679 
00680   typename HashFilterType::Pointer hasher = HashFilterType::New();
00681   hasher->SetInput( testReader->GetOutput() );
00682   hasher->Update();
00683 
00684   return hasher->GetHash();
00685 }
00686 
00687 
00688 int HashTestImage( const char *testImageFilename,
00689                    const std::vector<std::string> &baselineMD5Vector )
00690 {
00691   itk::ImageIOBase::Pointer iobase =
00692     itk::ImageIOFactory::CreateImageIO( testImageFilename, itk::ImageIOFactory::ReadMode);
00693 
00694   if ( iobase.IsNull() )
00695     {
00696     itkGenericExceptionMacro( "Unable to determine ImageIO reader for \"" << testImageFilename << "\"" );
00697     }
00698 
00699   // Read the image information
00700   iobase->SetFileName( testImageFilename );
00701   iobase->ReadImageInformation();
00702 
00703   // get output information about input image
00704   itk::ImageIOBase::IOComponentType componentType = iobase->GetComponentType();
00705 
00706   std::string testMD5 = "";
00707   switch(componentType)
00708     {
00709     case itk::ImageIOBase::CHAR:
00710       testMD5 = ComputeHash< itk::VectorImage<char, ITK_TEST_DIMENSION_MAX> >( testImageFilename );
00711       break;
00712     case itk::ImageIOBase::UCHAR:
00713       testMD5 = ComputeHash< itk::VectorImage<unsigned char, ITK_TEST_DIMENSION_MAX> >( testImageFilename );
00714       break;
00715     case itk::ImageIOBase::SHORT:
00716       testMD5 = ComputeHash< itk::VectorImage<short, ITK_TEST_DIMENSION_MAX> >( testImageFilename );
00717       break;
00718     case itk::ImageIOBase::USHORT:
00719       testMD5 = ComputeHash< itk::VectorImage<unsigned short, ITK_TEST_DIMENSION_MAX> >( testImageFilename );
00720       break;
00721     case itk::ImageIOBase::INT:
00722       testMD5 = ComputeHash< itk::VectorImage<int, ITK_TEST_DIMENSION_MAX> >( testImageFilename );
00723       break;
00724     case itk::ImageIOBase::UINT:
00725       testMD5 = ComputeHash< itk::VectorImage<unsigned int, ITK_TEST_DIMENSION_MAX> >( testImageFilename );
00726       break;
00727     case itk::ImageIOBase::LONG:
00728       testMD5 = ComputeHash< itk::VectorImage<long, ITK_TEST_DIMENSION_MAX> >( testImageFilename );
00729       break;
00730     case itk::ImageIOBase::ULONG:
00731       testMD5 = ComputeHash< itk::VectorImage<unsigned long, ITK_TEST_DIMENSION_MAX> >( testImageFilename );
00732       break;
00733     case itk::ImageIOBase::FLOAT:
00734     case itk::ImageIOBase::DOUBLE:
00735       std::cerr << "Hashing is not supporting for float and double images." << std::endl;
00736       itkGenericExceptionMacro( "Hashing is not supported for images of float or doubles." );
00737       break;
00738     case itk::ImageIOBase::UNKNOWNCOMPONENTTYPE:
00739     default:
00740       assert( false ); // should never get here unless we forgot a type
00741       itkGenericExceptionMacro( "Logic error!" );
00742     }
00743 
00744   std::vector<std::string>::const_iterator iter = baselineMD5Vector.begin();
00745   assert( baselineMD5Vector.size() );
00746   do
00747     {
00748     if ( *iter == testMD5 )
00749       {
00750       // success, let's get out of here
00751       return 0;
00752       }
00753     }
00754   while (++iter != baselineMD5Vector.end() );
00755 
00756   // failed to match print the different md5s
00757   std::cout << "<DartMeasurement name=\"TestMD5\" type=\"text/string\">";
00758   std::cout << testMD5;
00759   std::cout <<  "</DartMeasurement>" << std::endl;
00760 
00761 
00762   // print out all md5 baselines
00763   for ( iter = baselineMD5Vector.begin(); iter != baselineMD5Vector.end(); ++iter )
00764     {
00765     std::cout << "<DartMeasurement name=\"BaselineMD5\" type=\"text/string\">";
00766     std::cout << *iter;
00767     std::cout <<  "</DartMeasurement>" << std::endl;
00768     }
00769 
00770   typedef itk::Image< double, ITK_TEST_DIMENSION_MAX >                            ImageType;
00771   typedef itk::Image< double, 2 >                                                 SliceImageType;
00772   typedef itk::Image< unsigned char, 2 >                                          OutputType;
00773   typedef itk::ImageFileReader< ImageType >                                       ReaderType;
00774   typedef itk::Testing::StretchIntensityImageFilter< SliceImageType, OutputType > RescaleType;
00775   typedef itk::Testing::ExtractSliceImageFilter< ImageType, SliceImageType >      ExtractType;
00776   typedef itk::ImageFileWriter< OutputType >                                      WriterType;
00777 
00778   // setup reader
00779   ReaderType::Pointer reader = ReaderType::New();
00780   reader->SetFileName( testImageFilename );
00781   reader->UpdateLargestPossibleRegion();
00782 
00783   ImageType::SizeType size;
00784   size = reader->GetOutput()->GetLargestPossibleRegion().GetSize();
00785 
00786   //Get the center slice of the image,  In 3D, the first slice
00787   //is often a black slice with little debugging information.
00788   ImageType::IndexType index; index.Fill(0);
00789   for ( unsigned int i = 2; i < ITK_TEST_DIMENSION_MAX; i++ )
00790     {
00791     index[i] = size[i] / 2; //NOTE: Integer Divide used to get approximately
00792     // the center slice
00793     size[i] = 0;
00794     }
00795 
00796 
00797   ImageType::RegionType region;
00798   region.SetIndex(index);
00799 
00800   region.SetSize(size);
00801 
00802   ExtractType::Pointer extract = ExtractType::New();
00803   extract->SetDirectionCollapseToIdentity();
00804   extract->SetInput( reader->GetOutput() );
00805   extract->SetExtractionRegion(region);
00806 
00807   RescaleType::Pointer rescale = RescaleType::New();
00808   rescale->SetOutputMinimum( itk::NumericTraits< unsigned char >::NonpositiveMin() );
00809   rescale->SetOutputMaximum( itk::NumericTraits< unsigned char >::max() );
00810   rescale->SetInput( extract->GetOutput() );
00811 
00812   WriterType::Pointer writer = WriterType::New();
00813   writer->SetInput( rescale->GetOutput() );
00814 
00815 
00816   std::ostringstream testName;
00817   testName << testImageFilename << ".test.png";
00818 
00819   writer->SetFileName( testName.str().c_str() );
00820 
00821   try
00822     {
00823     rescale->UpdateLargestPossibleRegion();
00824     writer->Update();
00825     }
00826   catch ( const std::exception & e )
00827     {
00828     std::cerr << "Error during rescale and writing of " <<  testName.str()<< std::endl;
00829     std::cerr << e.what() << "\n";
00830     }
00831   catch ( ... )
00832     {
00833     std::cerr << "Unknow error during rescale and writing of " << testName.str() << std::endl;
00834     }
00835 
00836   std::cout << "<DartMeasurementFile name=\"TestImage\" type=\"image/png\">";
00837   std::cout << testName.str();
00838   std::cout << "</DartMeasurementFile>" << std::endl;
00839 
00840   return 1;
00841 }
00842 
00843 //
00844 // Generate all of the possible baselines
00845 // The possible baselines are generated fromn the baselineFilename using the
00846 // following algorithm:
00847 // 1) strip the suffix
00848 // 2) append a digit .x
00849 // 3) append the original suffix.
00850 // It the file exists, increment x and continue
00851 //
00852 std::map< std::string, int > RegressionTestBaselines(char *baselineFilename)
00853 {
00854   std::map< std::string, int > baselines;
00855   baselines[std::string(baselineFilename)] = 0;
00856 
00857   std::string originalBaseline(baselineFilename);
00858 
00859   int                    x = 0;
00860   std::string::size_type suffixPos = originalBaseline.rfind(".");
00861   std::string            suffix;
00862   if ( suffixPos != std::string::npos )
00863     {
00864     suffix = originalBaseline.substr( suffixPos, originalBaseline.length() );
00865     originalBaseline.erase( suffixPos, originalBaseline.length() );
00866     }
00867   while ( ++x )
00868     {
00869     std::ostringstream filename;
00870     filename << originalBaseline << "." << x << suffix;
00871     std::ifstream filestream( filename.str().c_str() );
00872     if ( !filestream )
00873       {
00874       break;
00875       }
00876     baselines[filename.str()] = 0;
00877     filestream.close();
00878     }
00879   return baselines;
00880 }
00881 
00882 
00883 // Needed for explicit instantiation
00884 #include "itkTestingComparisonImageFilter.hxx"
00885 
00886 #endif
00887