ITK
4.0.0
Insight Segmentation and Registration Toolkit
|
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