11 #include "PlusConfigure.h" 12 #include "vtksys/CommandLineArguments.hxx" 18 #include "vtkDataSetReader.h" 19 #include "vtkDataSetWriter.h" 20 #include "vtkImageClip.h" 21 #include "vtkImageData.h" 22 #include "vtkImageMathematics.h" 23 #include "vtkImageHistogramStatistics.h" 24 #include "vtkMetaImageReader.h" 31 if ( testVol == NULL )
33 LOG_ERROR(
"Test volume is invalid" );
38 LOG_ERROR(
"Reference volume is invalid" );
42 int* testExt = testVol->GetExtent();
43 int* refExt = refVol->GetExtent();
44 if ( testExt[0] != refExt[0] || testExt[1] != refExt[1]
45 || testExt[2] != refExt[2] || testExt[3] != refExt[3]
46 || testExt[4] != refExt[4] || testExt[5] != refExt[5] )
48 LOG_ERROR(
"Test volume extents (" << testExt[0] <<
", " << testExt[1] <<
", " << testExt[2] <<
", " << testExt[3] <<
", " << testExt[4] <<
", " << testExt[5] <<
")" \
49 <<
" do not match reference volume extents (" << refExt[0] <<
", " << refExt[1] <<
", " << refExt[2] <<
", " << refExt[3] <<
", " << refExt[4] <<
", " << refExt[5] <<
")" )
53 double* testSpacing = testVol->GetSpacing();
54 double* refSpacing = refVol->GetSpacing();
55 if ( testSpacing[0] != refSpacing[0] || testSpacing[1] != refSpacing[1] || testSpacing[2] != refSpacing[2] )
57 LOG_ERROR(
"Test volume spacing (" << testSpacing[0] <<
", " << testSpacing[1] <<
", " << testSpacing[2] <<
")" \
58 <<
" does not match reference volume spacing (" << refSpacing[0] <<
", " << refSpacing[1] <<
", " << refSpacing[2] <<
")" )
62 double* testOrigin = testVol->GetOrigin();
63 double* refOrigin = refVol->GetOrigin();
64 if ( testOrigin[0] != refOrigin[0] || testOrigin[1] != refOrigin[1] || testOrigin[2] != refOrigin[2] )
66 LOG_ERROR(
"Test volume origin (" << testOrigin[0] <<
", " << testOrigin[1] <<
", " << testOrigin[2] <<
")" \
67 <<
" does not match reference volume origin (" << refOrigin[0] <<
", " << refOrigin[1] <<
", " << refOrigin[2] <<
")" )
71 vtkSmartPointer<vtkImageMathematics> imageDiff = vtkSmartPointer<vtkImageMathematics>::New();
72 imageDiff->SetOperationToSubtract();
73 imageDiff->SetInputData( 0, testVol );
74 imageDiff->SetInputData( 1, refVol );
76 vtkSmartPointer<vtkImageMathematics> imageAbsDiff = vtkSmartPointer<vtkImageMathematics>::New();
77 imageAbsDiff->SetOperationToAbsoluteValue();
78 imageAbsDiff->SetInputConnection( imageDiff->GetOutputPort() );
80 vtkSmartPointer<vtkImageHistogramStatistics> statistics = vtkSmartPointer<vtkImageHistogramStatistics>::New();
81 statistics->SetInputConnection( imageAbsDiff->GetOutputPort() );
82 statistics->GenerateHistogramImageOff();
85 double maxVal = statistics->GetMaximum();
86 double meanVal = statistics->GetMean();
87 double stdev = statistics->GetStandardDeviation();
89 LOG_INFO(
"Absolute difference between pixels: max=" << maxVal <<
", mean=" << meanVal <<
", stdev=" << stdev );
90 if ( stdev > simpleCompareMaxError )
92 LOG_ERROR(
"Standard deviation of the absolute difference between the images (" << stdev <<
") is larger than the maximum allowed image difference (" << simpleCompareMaxError <<
")" );
96 LOG_INFO(
"Standard deviation of the absolute difference between the images (" << stdev <<
") is smaller than the maximum allowed image difference (" << simpleCompareMaxError <<
")" );
103 int main(
int argc,
char** argv )
105 bool printHelp(
false );
106 std::string inputGTFileName;
107 std::string inputGTAlphaFileName;
108 std::string inputTestingFileName;
109 std::string inputTestingAlphaFileName;
110 std::string inputSliceAlphaFileName;
111 std::string outputStatsFileName;
112 std::string outputTrueDiffFileName;
113 std::string outputAbsoluteDiffFileName;
114 std::vector<int> roiOriginV;
115 std::vector<int> roiSizeV;
116 double simpleCompareMaxError = -1;
118 int verboseLevel = vtkPlusLogger::LOG_LEVEL_UNDEFINED;
120 vtksys::CommandLineArguments args;
121 args.Initialize( argc, argv );
123 args.AddArgument(
"--ground-truth-image", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &inputGTFileName,
"The ground truth volume being compared against" );
124 args.AddArgument(
"--ground-truth-alpha", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &inputGTAlphaFileName,
"The ground truth volume's alpha component" );
125 args.AddArgument(
"--testing-image", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &inputTestingFileName,
"The testing image to compare to the ground truth" );
126 args.AddArgument(
"--testing-alpha", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &inputTestingAlphaFileName,
"The testing volume's alpha component" );
127 args.AddArgument(
"--slices-alpha", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &inputSliceAlphaFileName,
"The alpha component for when the slices are pasted into the volume, without hole filling" );
128 args.AddArgument(
"--roi-origin", vtksys::CommandLineArguments::MULTI_ARGUMENT, &roiOriginV,
"The three x y z values describing the origin for the region of interest's extent, in volume IJK coordinates" );
129 args.AddArgument(
"--roi-size", vtksys::CommandLineArguments::MULTI_ARGUMENT, &roiSizeV,
"The three x y z values describing the size for the region of interest's extent, in volume IJK coordinates" );
130 args.AddArgument(
"--output-stats-file", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &outputStatsFileName,
"The file to dump the statistics for the comparison" );
131 args.AddArgument(
"--output-diff-volume-true", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &outputTrueDiffFileName,
"Save the true difference volume to this file" );
132 args.AddArgument(
"--output-diff-volume-absolute", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &outputAbsoluteDiffFileName,
"Save the absolute difference volume to this file" );
133 args.AddArgument(
"--simple-compare-max-error", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &simpleCompareMaxError,
"If specified, a simple comparison of the volumes is performed (no detailed statistics are computed, only the ground truth and test volumes are used) and if the stdev of pixel values of the absolute difference image is larger than the specified value then the test returns with failure" );
134 args.AddArgument(
"--verbose", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &verboseLevel,
"Verbose level (1=error only, 2=warning, 3=info, 4=debug, 5=trace)" );
135 args.AddArgument(
"--help", vtksys::CommandLineArguments::NO_ARGUMENT, &printHelp,
"Print this help." );
139 std::cerr <<
"Problem parsing arguments" << std::endl;
140 std::cout <<
"Help: " << args.GetHelp() << std::endl;
141 exit( EXIT_FAILURE );
146 std::cout << args.GetHelp() << std::endl;
147 exit( EXIT_SUCCESS );
155 if ( inputGTFileName.empty() || inputTestingFileName.empty() )
157 LOG_ERROR(
"ground-truth-image and testing-image are required arguments" );
158 LOG_ERROR(
"Help: " << args.GetHelp() );
159 exit( EXIT_FAILURE );
164 LOG_INFO(
"Reading input ground truth image: " << inputGTFileName );
165 vtkSmartPointer<vtkMetaImageReader> reader = vtkSmartPointer<vtkMetaImageReader>::New();
166 reader->SetFileName( inputGTFileName.c_str() );
168 vtkImageData* groundTruth = vtkImageData::SafeDownCast( reader->GetOutput() );
170 LOG_INFO(
"Reading input testing image: " << inputTestingFileName );
171 vtkSmartPointer<vtkMetaImageReader> reader3 = vtkSmartPointer<vtkMetaImageReader>::New();
172 reader3->SetFileName( inputTestingFileName.c_str() );
174 vtkImageData* testingImage = vtkImageData::SafeDownCast( reader3->GetOutput() );
179 if ( simpleCompareMaxError >= 0 )
181 LOG_INFO(
"Perform simple volume comparison" );
193 timeInfo = localtime( &rawtime );
194 char timeAndDate[60];
195 strftime( timeAndDate, 60,
"%Y %m %d %H:%M", timeInfo );
198 if ( inputGTAlphaFileName.empty() || inputTestingAlphaFileName.empty() || inputSliceAlphaFileName.empty() || outputStatsFileName.empty() )
200 LOG_ERROR(
"ground-truth-image, ground-truth-alpha, testing-image, testing-alpha, slices-alpha, and output-stats-file are required arguments!" );
201 LOG_ERROR(
"Help: " << args.GetHelp() );
202 exit( EXIT_FAILURE );
207 LOG_INFO(
"Reading input ground truth alpha: " << inputGTAlphaFileName );
208 vtkSmartPointer<vtkMetaImageReader> reader2 = vtkSmartPointer<vtkMetaImageReader>::New();
209 reader2->SetFileName( inputGTAlphaFileName.c_str() );
211 vtkImageData* groundTruthAlpha = vtkImageData::SafeDownCast( reader2->GetOutput() );
213 LOG_INFO(
"Reading input testing alpha: " << inputTestingAlphaFileName );
214 vtkSmartPointer<vtkMetaImageReader> reader4 = vtkSmartPointer<vtkMetaImageReader>::New();
215 reader4->SetFileName( inputTestingAlphaFileName.c_str() );
217 vtkImageData* testingAlpha = vtkImageData::SafeDownCast( reader4->GetOutput() );
219 LOG_INFO(
"Reading input slices alpha: " << inputSliceAlphaFileName );
220 vtkSmartPointer<vtkMetaImageReader> reader5 = vtkSmartPointer<vtkMetaImageReader>::New();
221 reader5->SetFileName( inputSliceAlphaFileName.c_str() );
223 vtkImageData* slicesAlpha = vtkImageData::SafeDownCast( reader5->GetOutput() );
226 int* extentGT = groundTruth->GetExtent();
227 int* extentGTAlpha = groundTruthAlpha->GetExtent();
228 int* extentTestAlpha = testingAlpha->GetExtent();
229 int* extentSlicesAlpha = slicesAlpha->GetExtent();
231 for (
int i = 0;
i < 6;
i++ )
232 if ( extentGT[
i] != extentGTAlpha[
i] || extentGT[
i] != extentSlicesAlpha[
i] || extentGT[
i] != extentTestAlpha[
i] || extentGT[
i] != extentSlicesAlpha[
i] )
236 LOG_ERROR(
"Image sizes do not match!" );
237 exit( EXIT_FAILURE );
242 vtkImageData* groundTruthRoi = groundTruth;
243 vtkImageData* groundTruthAlphaRoi = groundTruthAlpha;
244 vtkImageData* testingImageRoi = testingImage;
245 vtkImageData* testingAlphaRoi = testingAlpha;
246 vtkImageData* slicesAlphaRoi = slicesAlpha;
247 int roiExtent[6] = {0};
248 for (
int extentIndex = 0; extentIndex < 6; extentIndex++ )
250 roiExtent[extentIndex] = extentGT[extentIndex];
254 vtkSmartPointer<vtkImageClip> clipGT = vtkSmartPointer<vtkImageClip>::New();
255 vtkSmartPointer<vtkImageClip> clipGTAlpha = vtkSmartPointer<vtkImageClip>::New();
256 vtkSmartPointer<vtkImageClip> clipTest = vtkSmartPointer<vtkImageClip>::New();
257 vtkSmartPointer<vtkImageClip> clipTestAlpha = vtkSmartPointer<vtkImageClip>::New();
258 vtkSmartPointer<vtkImageClip> clipSlicesAlpha = vtkSmartPointer<vtkImageClip>::New();
260 if ( roiOriginV.size() == 3 && roiSizeV.size() == 3 )
264 roiExtent[0] = roiOriginV[0];
265 roiExtent[1] = roiOriginV[0] + roiSizeV[0] - 1;
266 roiExtent[2] = roiOriginV[1];
267 roiExtent[3] = roiOriginV[1] + roiSizeV[1] - 1;
268 roiExtent[4] = roiOriginV[2];
269 roiExtent[5] = roiOriginV[2] + roiSizeV[2] - 1;
272 if ( roiExtent[0] < extentGT[0] || roiExtent[1] >= extentGT[1] ||
273 roiExtent[2] < extentGT[2] || roiExtent[3] >= extentGT[3] ||
274 roiExtent[4] < extentGT[4] || roiExtent[5] >= extentGT[5] )
276 LOG_ERROR(
"Region of interest contains data outside the original volume's IJK coordinates! Extents are: " << roiExtent[0] <<
" " << roiExtent[1] <<
" " << roiExtent[2] <<
" " << roiExtent[3] <<
" " << roiExtent[4] <<
" " << roiExtent[5] <<
"\n" <<
"Original extent is: " << extentGT[0] <<
" " << extentGT[1] <<
" " << extentGT[2] <<
" " << extentGT[3] <<
" " << extentGT[4] <<
" " << extentGT[5] );
277 exit( EXIT_FAILURE );
280 clipGT->SetInputData( groundTruth );
281 clipGT->SetClipData( 1 );
282 clipGT->SetOutputWholeExtent( roiExtent );
284 groundTruthRoi = clipGT->GetOutput();
286 clipGTAlpha->SetInputData( groundTruthAlpha );
287 clipGTAlpha->SetClipData( 1 );
288 clipGTAlpha->SetOutputWholeExtent( roiExtent );
289 clipGTAlpha->Update();
290 groundTruthAlphaRoi = clipGTAlpha->GetOutput();
292 clipTest->SetInputData( testingImage );
293 clipTest->SetClipData( 1 );
294 clipTest->SetOutputWholeExtent( roiExtent );
296 testingImageRoi = clipTest->GetOutput();
298 clipTestAlpha->SetInputData( testingAlpha );
299 clipTestAlpha->SetClipData( 1 );
300 clipTestAlpha->SetOutputWholeExtent( roiExtent );
301 clipTestAlpha->Update();
302 testingAlphaRoi = clipTestAlpha->GetOutput();
304 clipSlicesAlpha->SetInputData( slicesAlpha );
305 clipSlicesAlpha->SetClipData( 1 );
306 clipSlicesAlpha->SetOutputWholeExtent( roiExtent );
307 clipSlicesAlpha->Update();
308 slicesAlphaRoi = clipSlicesAlpha->GetOutput();
310 else if ( roiOriginV.size() != 0 || roiSizeV.size() != 0 )
312 LOG_ERROR(
"ROI size and origin each need to be 3 values in volume IJK coordinates: X Y Z" );
313 exit( EXIT_FAILURE );
317 LOG_INFO(
"Calculating difference images and statistics..." );
318 vtkSmartPointer<vtkPlusCompareVolumes> histogramGenerator = vtkSmartPointer<vtkPlusCompareVolumes>::New();
319 histogramGenerator->SetInputGT( groundTruthRoi );
320 histogramGenerator->SetInputGTAlpha( groundTruthAlphaRoi );
321 histogramGenerator->SetInputTest( testingImageRoi );
322 histogramGenerator->SetInputTestAlpha( testingAlphaRoi );
323 histogramGenerator->SetInputSliceAlpha( slicesAlphaRoi );
324 histogramGenerator->Update();
327 if ( !outputStatsFileName.empty() )
329 LOG_INFO(
"Writing output statistics: " << outputStatsFileName );
330 int* TruHistogram = histogramGenerator->GetTrueHistogramPtr();
331 int* AbsHistogram = histogramGenerator->GetAbsoluteHistogramPtr();
332 int* AbsHistogramWithHoles = histogramGenerator->GetAbsoluteHistogramWithHolesPtr();
333 std::ifstream testingFile( outputStatsFileName.c_str() );
334 std::ofstream outputStatsFile;
335 if ( testingFile.is_open() )
339 outputStatsFile.open( outputStatsFileName.c_str(), std::ios::out | std::ios::app );
340 outputStatsFile << timeAndDate <<
"," 341 << inputGTFileName <<
"," 342 << inputGTAlphaFileName <<
"," 343 << inputSliceAlphaFileName <<
"," 344 << inputTestingFileName <<
"," 345 << inputTestingAlphaFileName <<
"," 346 << roiExtent[0] <<
"_" << roiExtent[1] <<
"_" << roiExtent[2] <<
"_" << roiExtent[3] <<
"_" << roiExtent[4] <<
"_" << roiExtent[5] <<
"," 347 << histogramGenerator->GetNumberOfHoles() <<
"," 348 << histogramGenerator->GetNumberOfFilledHoles() <<
"," 349 << histogramGenerator->GetNumberVoxelsVisible() <<
"," 350 << histogramGenerator->GetTrueMaximum() <<
"," 351 << histogramGenerator->GetTrueMinimum() <<
"," 352 << histogramGenerator->GetTrueMedian() <<
"," 353 << histogramGenerator->GetTrueMean() <<
"," 354 << histogramGenerator->GetTrueStdev() <<
"," 355 << histogramGenerator->GetTrue5thPercentile() <<
"," 356 << histogramGenerator->GetTrue95thPercentile() <<
"," 357 << histogramGenerator->GetAbsoluteMaximum() <<
"," 358 << histogramGenerator->GetAbsoluteMinimum() <<
"," 359 << histogramGenerator->GetAbsoluteMedian() <<
"," 360 << histogramGenerator->GetAbsoluteMean() <<
"," 361 << histogramGenerator->GetAbsoluteStdev() <<
"," 362 << histogramGenerator->GetAbsolute5thPercentile() <<
"," 363 << histogramGenerator->GetAbsolute95thPercentile() <<
"," 364 << histogramGenerator->GetRMS() <<
"," 365 << histogramGenerator->GetAbsoluteMeanWithHoles();
366 for (
int i = 0;
i < 511;
i++ )
367 { outputStatsFile <<
"," << TruHistogram[
i]; }
368 for (
int i = 0;
i < 256;
i++ )
369 { outputStatsFile <<
"," << AbsHistogram[
i]; }
370 for (
int i = 0;
i < 256;
i++ )
371 { outputStatsFile <<
"," << AbsHistogramWithHoles[
i]; }
372 outputStatsFile << std::endl;
378 outputStatsFile.open( outputStatsFileName.c_str() );
379 outputStatsFile <<
"Time,Dataset - Ground Truth,Dataset - Ground Truth Alpha,Dataset - Slices Alpha,Dataset - Testing Image,Dataset - Testing Alpha,Region of Interest Extent,Number of Holes,Number of Filled Holes,Number of Visible Voxels,True Maximum Error,True Minimum Error,True Median Error,True Mean Error,True Standard Deviation,True 5th Percentile,True 95th Percentile,Absolute Maximum Error,Absolute Minimum Error,Absolute Median Error,Absolute Mean Error,Absolute Standard Deviation,Absolute 5th Percentile,Absolute 95th Percentile,RMS,Absolute Mean Error Including Holes";
380 for (
int i = 0;
i < 511;
i++ )
382 outputStatsFile <<
"," << (
i - 255 );
384 for (
int i = 0;
i < 256;
i++ )
386 outputStatsFile <<
"," <<
i;
388 for (
int i = 0;
i < 256;
i++ )
390 outputStatsFile <<
"," <<
i;
392 outputStatsFile << std::endl;
393 outputStatsFile << timeAndDate <<
"," 394 << inputGTFileName <<
"," 395 << inputGTAlphaFileName <<
"," 396 << inputSliceAlphaFileName <<
"," 397 << inputTestingFileName <<
"," 398 << inputTestingAlphaFileName <<
"," 399 << roiExtent[0] <<
"_" << roiExtent[1] <<
"_" << roiExtent[2] <<
"_" << roiExtent[3] <<
"_" << roiExtent[4] <<
"_" << roiExtent[5] <<
"," 400 << histogramGenerator->GetNumberOfHoles() <<
"," 401 << histogramGenerator->GetNumberOfFilledHoles() <<
"," 402 << histogramGenerator->GetNumberVoxelsVisible() <<
"," 403 << histogramGenerator->GetTrueMaximum() <<
"," 404 << histogramGenerator->GetTrueMinimum() <<
"," 405 << histogramGenerator->GetTrueMedian() <<
"," 406 << histogramGenerator->GetTrueMean() <<
"," 407 << histogramGenerator->GetTrueStdev() <<
"," 408 << histogramGenerator->GetTrue5thPercentile() <<
"," 409 << histogramGenerator->GetTrue95thPercentile() <<
"," 410 << histogramGenerator->GetAbsoluteMaximum() <<
"," 411 << histogramGenerator->GetAbsoluteMinimum() <<
"," 412 << histogramGenerator->GetAbsoluteMedian() <<
"," 413 << histogramGenerator->GetAbsoluteMean() <<
"," 414 << histogramGenerator->GetAbsoluteStdev() <<
"," 415 << histogramGenerator->GetAbsolute5thPercentile() <<
"," 416 << histogramGenerator->GetAbsolute95thPercentile() <<
"," 417 << histogramGenerator->GetRMS() <<
"," 418 << histogramGenerator->GetAbsoluteMeanWithHoles();
419 for (
int i = 0;
i < 511;
i++ )
420 { outputStatsFile <<
"," << TruHistogram[
i]; }
421 for (
int i = 0;
i < 256;
i++ )
422 { outputStatsFile <<
"," << AbsHistogram[
i]; }
423 for (
int i = 0;
i < 256;
i++ )
424 { outputStatsFile <<
"," << AbsHistogramWithHoles[
i]; }
425 outputStatsFile << std::endl;
429 if ( !outputAbsoluteDiffFileName.empty() )
431 LOG_INFO(
"Writing absolute difference image: " << outputAbsoluteDiffFileName );
432 vtkSmartPointer<vtkDataSetWriter> writerAbs = vtkSmartPointer<vtkDataSetWriter>::New();
433 writerAbs->SetFileTypeToBinary();
434 writerAbs->SetInputData( histogramGenerator->GetOutputAbsoluteDifferenceImage() );
435 writerAbs->SetFileName( outputAbsoluteDiffFileName.c_str() );
439 if ( !outputTrueDiffFileName.empty() )
441 LOG_INFO(
"Writing true difference image: " << outputTrueDiffFileName );
442 vtkSmartPointer<vtkDataSetWriter> writerTru = vtkSmartPointer<vtkDataSetWriter>::New();
443 writerTru->SetFileTypeToBinary();
444 writerTru->SetInputData( histogramGenerator->GetOutputTrueDifferenceImage() );
445 writerTru->SetFileName( outputTrueDiffFileName.c_str() );
int main(int argc, char **argv)
static vtkIGSIOLogger * Instance()
int SimpleCompareVolumes(vtkImageData *testVol, vtkImageData *refVol, double simpleCompareMaxError)