28 #ifndef itkTestDriverInclude_h
29 #define itkTestDriverInclude_h
39 #include "itksys/Process.h"
56 #include "itksys/SystemTools.hxx"
58 #if defined(LINUX) && !defined(__MINGW32__)
61 #include "vnl/vnl_sample.h"
63 #define ITK_TEST_DIMENSION_MAX 6
66 const char *baselineImageFilename,
68 double intensityTolerance,
70 unsigned int radiusTolerance = 0);
73 const std::string md5hash );
92 typedef std::pair< const char *, std::vector<std::string> >
HashPairType;
123 std::cerr <<
"usage: itkTestDriver [options] prg [args]" << std::endl;
124 std::cerr <<
" itkTestDriver --no-process [options]" << std::endl;
125 std::cerr << std::endl;
126 std::cerr <<
"itkTestDriver alter the environment, run a test program and compare the images" << std::endl;
127 std::cerr <<
"produced." << std::endl;
128 std::cerr << std::endl;
129 std::cerr <<
"Options:" << std::endl;
130 std::cerr <<
" --add-before-libpath PATH" << std::endl;
131 std::cerr <<
" Add a path to the library path environment. This option take care of" << std::endl;
132 std::cerr <<
" choosing the right environment variable for your system." << std::endl;
133 std::cerr <<
" This option can be used several times." << std::endl;
134 std::cerr << std::endl;
135 std::cerr <<
" --add-before-env NAME VALUE" << std::endl;
136 std::cerr <<
" Add a VALUE to the variable name in the environment." << std::endl;
137 std::cerr <<
" The seperator used is the default one on the system." << std::endl;
138 std::cerr <<
" This option can be used several times." << std::endl;
139 std::cerr << std::endl;
140 std::cerr <<
" --add-before-env-with-sep NAME VALUE SEP" << std::endl;
141 std::cerr <<
" Add a VALUE to the variable name in the environment using the provided separator." << std::endl;
142 std::cerr <<
" This option can be used several times." << std::endl;
143 std::cerr << std::endl;
144 std::cerr <<
" --remove-env NAME" << std::endl;
145 std::cerr <<
" Remove the variable name from the environment." << std::endl;
146 std::cerr <<
" This option can be used several times." << std::endl;
147 std::cerr << std::endl;
148 std::cerr <<
" --compare TEST BASELINE" << std::endl;
149 std::cerr <<
" Compare the TEST image to the BASELINE one." << std::endl;
150 std::cerr <<
" This option can be used several times." << std::endl;
151 std::cerr << std::endl;
152 std::cerr <<
" --compare-MD5 TEST md5hash0 [ md5hash1 ... ]" << std::endl;
153 std::cerr <<
" Compare the TEST image file's md5 hash to the provided hash." << std::endl;
154 std::cerr <<
" md5hash0 is required and assumed to be a hash." << std::endl;
155 std::cerr <<
" Additional arguments are considered hashes when the string is 32 hexi-decimal characters. " << std::endl;
156 std::cerr <<
" This option can be used several times for multiple comparisons." << std::endl;
157 std::cerr << std::endl;
158 std::cerr <<
" --with-threads THREADS" << std::endl;
159 std::cerr <<
" Use at most THREADS threads." << std::endl;
160 std::cerr << std::endl;
161 std::cerr <<
" --without-threads" << std::endl;
162 std::cerr <<
" Use at most one thread." << std::endl;
163 std::cerr << std::endl;
164 std::cerr <<
" --compareNumberOfPixelsTolerance TOLERANCE" << std::endl;
165 std::cerr <<
" When comparing images with --compare, allow TOLERANCE pixels to differ." << std::endl;
166 std::cerr <<
" Default is 0." << std::endl;
167 std::cerr << std::endl;
168 std::cerr <<
" --compareRadiusTolerance TOLERANCE" << std::endl;
169 std::cerr <<
" Default is 0." << std::endl;
170 std::cerr << std::endl;
171 std::cerr <<
" --compareIntensityTolerance TOLERANCE" << std::endl;
172 std::cerr <<
" Default is 2.0." << std::endl;
173 std::cerr << std::endl;
174 std::cerr <<
" --no-process" << std::endl;
175 std::cerr <<
" The test driver will not invoke any process." << std::endl;
176 std::cerr << std::endl;
177 std::cerr <<
" --full-output" << std::endl;
178 std::cerr <<
" Causes the full output of the test to be passed to cdash." << std::endl;
179 std::cerr <<
" --redirect-output TEST_OUTPUT" << std::endl;
180 std::cerr <<
" Redirects the test output to the file TEST_OUTPUT." << std::endl;
181 std::cerr << std::endl;
182 std::cerr <<
" --" << std::endl;
183 std::cerr <<
" The options after -- are not interpreted by this program and passed" << std::endl;
184 std::cerr <<
" directly to the test program." << std::endl;
185 std::cerr << std::endl;
186 std::cerr <<
" --help" << std::endl;
187 std::cerr <<
" Display this message and exit." << std::endl;
188 std::cerr << std::endl;
193 return static_cast<char>( ::tolower(c));
198 #if defined(LINUX) && !defined(__MINGW32__)
205 redirectOutputParameters.
redirect =
false;
207 if( processedOutput )
209 processedOutput->externalProcessMustBeCalled =
true;
217 if ( !skip && strcmp((*av)[i],
"--compare") == 0 )
228 else if ( !skip && strcmp((*av)[i],
"--compare-MD5") == 0 )
235 const char *filename = (*av)[i + 1];
236 std::string md5hash0 = (*av)[i + 2];
239 std::transform(md5hash0.begin(), md5hash0.end(), md5hash0.begin(),
my_to_lower );
242 if ( md5hash0.size() != 32 ||
243 md5hash0.find_first_not_of(
"0123456789abcdef" ) != std::string::npos )
245 std::cerr <<
"Warning: argument does not appear to be a valid md5 hash \"" << md5hash0 <<
"\"." << std::endl;
248 std::vector< std::string > hashVector;
249 hashVector.push_back( md5hash0 );
255 while ( *ac - i > 0 )
257 std::string md5hashN = (*av)[i];
260 std::transform(md5hashN.begin(), md5hashN.end(), md5hashN.begin(),
my_to_lower );
263 if ( md5hashN.size() != 32 ||
264 md5hashN.find_first_not_of(
"0123456789abcdef" ) != std::string::npos )
270 hashVector.push_back( md5hashN );
279 hashTestList.push_back(
HashPairType( filename, hashVector ) );
282 else if ( !skip && strcmp((*av)[i],
"--") == 0 )
287 else if ( !skip && strcmp((*av)[i],
"--help") == 0 )
292 else if ( !skip && strcmp((*av)[i],
"--with-threads") == 0 )
300 std::string threadEnv =
"ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS=";
301 threadEnv += (*av)[i + 1];
302 itksys::SystemTools::PutEnv( threadEnv.c_str() );
304 itk::MultiThreader::SetGlobalDefaultNumberOfThreads(atoi((*av)[i + 1]));
308 else if ( !skip && strcmp((*av)[i],
"--without-threads") == 0 )
310 itksys::SystemTools::PutEnv(
"ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS=1" );
311 itk::MultiThreader::SetGlobalDefaultNumberOfThreads(1);
315 else if ( !skip && strcmp((*av)[i],
"--compareNumberOfPixelsTolerance") == 0 )
326 else if ( !skip && strcmp((*av)[i],
"--compareRadiusTolerance") == 0 )
337 else if ( !skip && strcmp((*av)[i],
"--compareIntensityTolerance") == 0 )
348 else if ( !skip && strcmp((*av)[i],
"--add-before-libpath") == 0 )
355 if( processedOutput )
357 processedOutput->add_before_libpath.push_back( (*av)[i+1] );
362 else if ( !skip && strcmp((*av)[i],
"--add-before-env") == 0 )
369 if( processedOutput )
371 processedOutput->add_before_env.push_back( (*av)[i+1] );
372 processedOutput->add_before_env.push_back( (*av)[i+2] );
377 else if ( !skip && strcmp((*av)[i],
"--add-before-env-with-sep") == 0 )
384 if( processedOutput )
386 processedOutput->add_before_env_with_sep.push_back( (*av)[i+1] );
387 processedOutput->add_before_env_with_sep.push_back( (*av)[i+2] );
388 processedOutput->add_before_env_with_sep.push_back( (*av)[i+3] );
393 else if ( !skip && strcmp((*av)[i],
"--remove-env") == 0 )
401 itksys::SystemTools::UnPutEnv( (*av)[i+1] );
406 else if ( !skip && strcmp((*av)[i],
"--full-output") == 0 )
410 std::cout <<
"CTEST_FULL_OUTPUT" << std::endl;
414 else if ( !skip && strcmp((*av)[i],
"--no-process") == 0 )
418 if( processedOutput )
420 processedOutput->externalProcessMustBeCalled =
false;
425 else if ( !skip && strcmp((*av)[i],
"--redirectOutput") == 0 )
432 redirectOutputParameters.
redirect =
true;
433 redirectOutputParameters.
fileName = (*av)[i + 1];
439 if( processedOutput )
441 processedOutput->args.push_back((*av)[i]);
461 const char *baselineImageFilename,
463 double intensityTolerance,
465 unsigned int radiusTolerance)
475 ReaderType::Pointer baselineReader = ReaderType::New();
476 baselineReader->SetFileName(baselineImageFilename);
479 baselineReader->UpdateLargestPossibleRegion();
483 std::cerr <<
"Exception detected while reading " << baselineImageFilename <<
" : " << e.
GetDescription();
488 ReaderType::Pointer testReader = ReaderType::New();
489 testReader->SetFileName(testImageFilename);
492 testReader->UpdateLargestPossibleRegion();
496 std::cerr <<
"Exception detected while reading " << testImageFilename <<
" : " << e.
GetDescription() << std::endl;
501 ImageType::SizeType baselineSize;
502 baselineSize = baselineReader->GetOutput()->GetLargestPossibleRegion().GetSize();
503 ImageType::SizeType testSize;
504 testSize = testReader->GetOutput()->GetLargestPossibleRegion().GetSize();
506 if ( baselineSize != testSize )
508 std::cerr <<
"The size of the Baseline image and Test image do not match!" << std::endl;
509 std::cerr <<
"Baseline image: " << baselineImageFilename
510 <<
" has size " << baselineSize << std::endl;
511 std::cerr <<
"Test image: " << testImageFilename
512 <<
" has size " << testSize << std::endl;
518 DiffType::Pointer diff = DiffType::New();
519 diff->SetValidInput( baselineReader->GetOutput() );
520 diff->SetTestInput( testReader->GetOutput() );
521 diff->SetDifferenceThreshold(intensityTolerance);
522 diff->SetToleranceRadius(radiusTolerance);
523 diff->UpdateLargestPossibleRegion();
527 if ( ! reportErrors )
531 std::string shortFilename = itksys::SystemTools::GetFilenameName( baselineImageFilename );
533 std::cout <<
"<DartMeasurement name=\"ImageError " << shortFilename
534 <<
"\" type=\"numeric/double\">";
536 std::cout <<
"</DartMeasurement>" << std::endl;
540 if ( ( status > numberOfPixelsTolerance ) && reportErrors )
544 std::cout <<
"<DartMeasurement name=\"ImageError\" type=\"numeric/double\">";
546 std::cout <<
"</DartMeasurement>" << std::endl;
550 std::cout <<
"<DartMeasurement name=\"ImageError Minimum\" type=\"numeric/double\">";
551 std::cout << diff->GetMinimumDifference() <<
"</DartMeasurement>" << std::endl;
553 std::cout <<
"<DartMeasurement name=\"ImageError Maximum\" type=\"numeric/double\">";
554 std::cout << diff->GetMaximumDifference() <<
"</DartMeasurement>" << std::endl;
556 std::cout <<
"<DartMeasurement name=\"ImageError Mean\" type=\"numeric/double\">";
557 std::cout << diff->GetMeanDifference() <<
"</DartMeasurement>" << std::endl;
564 OutputType::SizeType size; size.Fill(0);
566 RescaleType::Pointer rescale = RescaleType::New();
569 rescale->SetInput( diff->GetOutput() );
570 rescale->UpdateLargestPossibleRegion();
571 size = rescale->GetOutput()->GetLargestPossibleRegion().GetSize();
575 OutputType::IndexType index; index.Fill(0);
578 index[i] = size[i] / 2;
584 region.SetIndex(index);
586 region.SetSize(size);
588 ExtractType::Pointer extract = ExtractType::New();
589 extract->SetDirectionCollapseToIdentity();
590 extract->SetInput( rescale->GetOutput() );
591 extract->SetExtractionRegion(region);
593 WriterType::Pointer writer = WriterType::New();
594 writer->SetInput( extract->GetOutput() );
596 std::ostringstream diffName;
597 diffName << testImageFilename <<
".diff.png";
600 rescale->SetInput( diff->GetOutput() );
603 catch (
const std::exception & e )
605 std::cerr <<
"Error during rescale of " << diffName.str() << std::endl;
606 std::cerr << e.what() <<
"\n";
610 std::cerr <<
"Error during rescale of " << diffName.str() << std::endl;
612 writer->SetFileName( diffName.str().c_str() );
617 catch (
const std::exception & e )
619 std::cerr <<
"Error during write of " << diffName.str() << std::endl;
620 std::cerr << e.what() <<
"\n";
624 std::cerr <<
"Error during write of " << diffName.str() << std::endl;
627 std::cout <<
"<DartMeasurementFile name=\"DifferenceImage\" type=\"image/png\">";
628 std::cout << diffName.str();
629 std::cout <<
"</DartMeasurementFile>" << std::endl;
631 std::ostringstream baseName;
632 baseName << testImageFilename <<
".base.png";
635 rescale->SetInput( baselineReader->GetOutput() );
638 catch (
const std::exception & e )
640 std::cerr <<
"Error during rescale of " << baseName.str() << std::endl;
641 std::cerr << e.what() <<
"\n";
645 std::cerr <<
"Error during rescale of " << baseName.str() << std::endl;
649 writer->SetFileName( baseName.str().c_str() );
652 catch (
const std::exception & e )
654 std::cerr <<
"Error during write of " << baseName.str() << std::endl;
655 std::cerr << e.what() <<
"\n";
659 std::cerr <<
"Error during write of " << baseName.str() << std::endl;
662 std::cout <<
"<DartMeasurementFile name=\"BaselineImage\" type=\"image/png\">";
663 std::cout << baseName.str();
664 std::cout <<
"</DartMeasurementFile>" << std::endl;
666 std::ostringstream testName;
667 testName << testImageFilename <<
".test.png";
670 rescale->SetInput( testReader->GetOutput() );
673 catch (
const std::exception & e )
675 std::cerr <<
"Error during rescale of " << testName.str() << std::endl;
676 std::cerr << e.what() <<
"\n";
680 std::cerr <<
"Error during rescale of " << testName.str() << std::endl;
684 writer->SetFileName( testName.str().c_str() );
687 catch (
const std::exception & e )
689 std::cerr <<
"Error during write of " << testName.str() << std::endl;
690 std::cerr << e.what() <<
"\n";
694 std::cerr <<
"Error during write of " << testName.str() << std::endl;
697 std::cout <<
"<DartMeasurementFile name=\"TestImage\" type=\"image/png\">";
698 std::cout << testName.str();
699 std::cout <<
"</DartMeasurementFile>" << std::endl;
701 return ( status > numberOfPixelsTolerance ) ?
static_cast<int>(status) : 0;
704 template<
typename TImageType >
707 typedef TImageType ImageType;
712 typename ReaderType::Pointer testReader = ReaderType::New();
713 testReader->SetFileName(testImageFilename);
716 testReader->UpdateLargestPossibleRegion();
720 std::cerr <<
"Exception detected while reading " << testImageFilename <<
" : " << e.
GetDescription() << std::endl;
726 typename HashFilterType::Pointer hasher = HashFilterType::New();
727 hasher->SetInput( testReader->GetOutput() );
730 return hasher->GetHash();
735 const std::vector<std::string> &baselineMD5Vector )
742 itkGenericExceptionMacro(
"Unable to determine ImageIO reader for \"" << testImageFilename <<
"\"" );
746 iobase->SetFileName( testImageFilename );
747 iobase->ReadImageInformation();
752 std::string testMD5 =
"";
753 switch(componentType)
756 testMD5 = ComputeHash< itk::VectorImage<char, ITK_TEST_DIMENSION_MAX> >( testImageFilename );
759 testMD5 = ComputeHash< itk::VectorImage<unsigned char, ITK_TEST_DIMENSION_MAX> >( testImageFilename );
762 testMD5 = ComputeHash< itk::VectorImage<short, ITK_TEST_DIMENSION_MAX> >( testImageFilename );
765 testMD5 = ComputeHash< itk::VectorImage<unsigned short, ITK_TEST_DIMENSION_MAX> >( testImageFilename );
768 testMD5 = ComputeHash< itk::VectorImage<int, ITK_TEST_DIMENSION_MAX> >( testImageFilename );
771 testMD5 = ComputeHash< itk::VectorImage<unsigned int, ITK_TEST_DIMENSION_MAX> >( testImageFilename );
774 testMD5 = ComputeHash< itk::VectorImage<long, ITK_TEST_DIMENSION_MAX> >( testImageFilename );
777 testMD5 = ComputeHash< itk::VectorImage<unsigned long, ITK_TEST_DIMENSION_MAX> >( testImageFilename );
781 std::cerr <<
"Hashing is not supporting for float and double images." << std::endl;
782 itkGenericExceptionMacro(
"Hashing is not supported for images of float or doubles." );
787 itkGenericExceptionMacro(
"Logic error!" );
790 std::vector<std::string>::const_iterator iter = baselineMD5Vector.begin();
791 assert( baselineMD5Vector.size() );
794 if ( *iter == testMD5 )
800 while (++iter != baselineMD5Vector.end() );
803 std::cout <<
"<DartMeasurement name=\"TestMD5\" type=\"text/string\">";
804 std::cout << testMD5;
805 std::cout <<
"</DartMeasurement>" << std::endl;
809 for ( iter = baselineMD5Vector.begin(); iter != baselineMD5Vector.end(); ++iter )
811 std::cout <<
"<DartMeasurement name=\"BaselineMD5\" type=\"text/string\">";
813 std::cout <<
"</DartMeasurement>" << std::endl;
825 ReaderType::Pointer reader = ReaderType::New();
826 reader->SetFileName( testImageFilename );
827 reader->UpdateLargestPossibleRegion();
829 ImageType::SizeType size;
830 size = reader->GetOutput()->GetLargestPossibleRegion().GetSize();
834 ImageType::IndexType index; index.Fill(0);
837 index[i] = size[i] / 2;
843 ImageType::RegionType region;
844 region.SetIndex(index);
846 region.SetSize(size);
848 ExtractType::Pointer extract = ExtractType::New();
849 extract->SetDirectionCollapseToIdentity();
850 extract->SetInput( reader->GetOutput() );
851 extract->SetExtractionRegion(region);
853 RescaleType::Pointer rescale = RescaleType::New();
856 rescale->SetInput( extract->GetOutput() );
858 WriterType::Pointer writer = WriterType::New();
859 writer->SetInput( rescale->GetOutput() );
862 std::ostringstream testName;
863 testName << testImageFilename <<
".test.png";
865 writer->SetFileName( testName.str().c_str() );
869 rescale->UpdateLargestPossibleRegion();
872 catch (
const std::exception &
e )
874 std::cerr <<
"Error during rescale and writing of " << testName.str()<< std::endl;
875 std::cerr << e.what() <<
"\n";
879 std::cerr <<
"Unknow error during rescale and writing of " << testName.str() << std::endl;
882 std::cout <<
"<DartMeasurementFile name=\"TestImage\" type=\"image/png\">";
883 std::cout << testName.str();
884 std::cout <<
"</DartMeasurementFile>" << std::endl;
900 std::map< std::string, int > baselines;
901 baselines[std::string(baselineFilename)] = 0;
903 std::string originalBaseline(baselineFilename);
906 std::string::size_type suffixPos = originalBaseline.rfind(
".");
908 if ( suffixPos != std::string::npos )
910 suffix = originalBaseline.substr( suffixPos, originalBaseline.length() );
911 originalBaseline.erase( suffixPos, originalBaseline.length() );
915 std::ostringstream filename;
916 filename << originalBaseline <<
"." << x << suffix;
917 std::ifstream filestream( filename.str().c_str() );
922 baselines[filename.str()] = 0;
930 #include "itkTestingComparisonImageFilter.hxx"
std::string ComputeHash(const char *testImageFilename)
unsigned int numberOfPixelsTolerance
ArgumentsList add_before_libpath
static char my_to_lower(const char c)
ArgumentsList add_before_env_with_sep
An image region represents a structured region of data.
int HashTestImage(const char *testImageFilename, const std::string md5hash)
ArgumentsList add_before_env
static const double e
The base of the natural logarithm or Euler's number
std::vector< char * > ArgumentsList
unsigned long SizeValueType
Applies a linear transformation to the intensity levels of the input Image.
int RegressionTestImage(const char *testImageFilename, const char *baselineImageFilename, int reportErrors, double intensityTolerance,::itk::SizeValueType numberOfPixelsTolerance=0, unsigned int radiusTolerance=0)
char ** ArgumentStringType
std::map< std::string, int > RegressionTestBaselines(char *)
RegressionTestParameters regressionTestParameters
std::pair< char *, char * > ComparePairType
bool externalProcessMustBeCalled
Generates a md5 hash string from an image.
Standard exception handling object.
#define ITK_TEST_DIMENSION_MAX
unsigned int radiusTolerance
Writes image data to a single file.
Implements comparison between two images.
Data source that reads image data from a single file.
static ImageIOBasePointer CreateImageIO(const char *path, FileModeType mode)
std::vector< ComparePairType > compareList
int ProcessArguments(int *ac, ArgumentStringType *av, ProcessedOutputType *processedOutput=nullptr)
Define additional traits for native types such as int or float.
RedirectOutputParameters redirectOutputParameters
std::pair< const char *, std::vector< std::string > > HashPairType
double intensityTolerance
std::vector< HashPairType > hashTestList
Templated n-dimensional image class.
virtual const char * GetDescription() const