Expand test_equal_images

The `test_equal_images` function has been expanded to supply the
generated image in a `Canvas` or a `DataSet` in addition to a `View`.
Much of the templating code has been removed from `test_equal_images`
and most of the code has moved into the `vtkm_rendering_testing`
library.
This commit is contained in:
Kenneth Moreland 2021-12-30 09:28:09 -07:00
parent 12e1164179
commit 0fe9300eed
4 changed files with 254 additions and 124 deletions

@ -56,3 +56,10 @@ unexpected behavior when multiple versions of a supposed singleton static
object exist. In particular, this was causing a failure when the static
objects holding testing directories was created by the test translation
unit but was then unavailable to `vtkm_rendering_testing`.
## Expand test_equal_images
The `test_equal_images` function has been expanded to supply the generated
image in a `Canvas` or a `DataSet` in addition to a `View`. Much of the
templating code has been removed from `test_equal_images` and most of the
code has moved into the `vtkm_rendering_testing` library.

@ -30,6 +30,7 @@ set(unit_tests
set(library_sources
RenderTest.cxx
Testing.cxx
)
vtkm_library(

@ -0,0 +1,166 @@
//============================================================================
// Copyright (c) Kitware, Inc.
// All rights reserved.
// See LICENSE.txt for details.
//
// This software is distributed WITHOUT ANY WARRANTY; without even
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the above copyright notice for more information.
//============================================================================
#include <vtkm/rendering/testing/Testing.h>
TestEqualResult test_equal_images(vtkm::rendering::View& view,
const std::vector<std::string>& fileNames,
const vtkm::IdComponent& averageRadius,
const vtkm::IdComponent& pixelShiftRadius,
const vtkm::FloatDefault& allowedPixelErrorRatio,
const vtkm::FloatDefault& threshold,
const bool& writeDiff,
const bool& returnOnPass)
{
view.Paint();
return test_equal_images(view.GetCanvas(),
fileNames,
averageRadius,
pixelShiftRadius,
allowedPixelErrorRatio,
threshold,
writeDiff,
returnOnPass);
}
TestEqualResult test_equal_images(const vtkm::rendering::Canvas& canvas,
const std::vector<std::string>& fileNames,
const vtkm::IdComponent& averageRadius,
const vtkm::IdComponent& pixelShiftRadius,
const vtkm::FloatDefault& allowedPixelErrorRatio,
const vtkm::FloatDefault& threshold,
const bool& writeDiff,
const bool& returnOnPass)
{
canvas.RefreshColorBuffer();
return test_equal_images(canvas.GetDataSet(),
fileNames,
averageRadius,
pixelShiftRadius,
allowedPixelErrorRatio,
threshold,
writeDiff,
returnOnPass);
}
TestEqualResult test_equal_images(const vtkm::cont::DataSet& dataset,
const std::vector<std::string>& fileNames,
const vtkm::IdComponent& averageRadius,
const vtkm::IdComponent& pixelShiftRadius,
const vtkm::FloatDefault& allowedPixelErrorRatio,
const vtkm::FloatDefault& threshold,
const bool& writeDiff,
const bool& returnOnPass)
{
vtkm::cont::ScopedRuntimeDeviceTracker runtime(vtkm::cont::DeviceAdapterTagAny{});
TestEqualResult testResults;
if (fileNames.empty())
{
testResults.PushMessage("No valid image file names were provided");
return testResults;
}
const std::string testImageName = vtkm::cont::testing::Testing::WriteDirPath(
vtkm::io::PrefixStringToFilename(fileNames[0], "test-"));
vtkm::io::WriteImageFile(dataset, testImageName, dataset.GetField(0).GetName());
std::stringstream dartXML;
dartXML << "<DartMeasurementFile name=\"TestImage\" type=\"image/png\">";
dartXML << testImageName;
dartXML << "</DartMeasurementFile>\n";
for (const auto& fileName : fileNames)
{
VTKM_LOG_S(vtkm::cont::LogLevel::Info, "testing image file: " << fileName);
TestEqualResult imageResult;
vtkm::cont::DataSet imageDataSet;
const std::string testImagePath = vtkm::cont::testing::Testing::RegressionImagePath(fileName);
try
{
imageDataSet = vtkm::io::ReadImageFile(testImagePath, "baseline-image");
}
catch (const vtkm::cont::ErrorExecution& error)
{
VTKM_LOG_S(vtkm::cont::LogLevel::Error, error.GetMessage());
imageResult.PushMessage(error.GetMessage());
const std::string outputImagePath = vtkm::cont::testing::Testing::WriteDirPath(fileName);
vtkm::io::WriteImageFile(dataset, outputImagePath, dataset.GetField(0).GetName());
imageResult.PushMessage("File '" + fileName +
"' did not exist but has been generated here: " + outputImagePath);
testResults.PushMessage(imageResult.GetMergedMessage());
continue;
}
catch (const vtkm::cont::ErrorBadValue& error)
{
VTKM_LOG_S(vtkm::cont::LogLevel::Error, error.GetMessage());
imageResult.PushMessage(error.GetMessage());
imageResult.PushMessage("Unsupported file type for image: " + fileName);
testResults.PushMessage(imageResult.GetMergedMessage());
continue;
}
dartXML << "<DartMeasurementFile name=\"BaselineImage\" type=\"image/png\">";
dartXML << testImagePath;
dartXML << "</DartMeasurementFile>\n";
imageDataSet.AddPointField("generated-image", dataset.GetField(0).GetData());
vtkm::filter::ImageDifference filter;
filter.SetPrimaryField("baseline-image");
filter.SetSecondaryField("generated-image");
filter.SetAverageRadius(averageRadius);
filter.SetPixelShiftRadius(pixelShiftRadius);
filter.SetAllowedPixelErrorRatio(allowedPixelErrorRatio);
filter.SetPixelDiffThreshold(threshold);
auto resultDataSet = filter.Execute(imageDataSet);
if (!filter.GetImageDiffWithinThreshold())
{
imageResult.PushMessage("Image Difference was not within the expected threshold for: " +
fileName);
}
if (writeDiff && resultDataSet.HasPointField("image-diff"))
{
const std::string diffName = vtkm::cont::testing::Testing::WriteDirPath(
vtkm::io::PrefixStringToFilename(fileName, "diff-"));
vtkm::io::WriteImageFile(resultDataSet, diffName, "image-diff");
dartXML << "<DartMeasurementFile name=\"DifferenceImage\" type=\"image/png\">";
dartXML << diffName;
dartXML << "</DartMeasurementFile>\n";
}
if (imageResult && returnOnPass)
{
VTKM_LOG_S(vtkm::cont::LogLevel::Info, "Test passed for image " << fileName);
if (!testResults)
{
VTKM_LOG_S(vtkm::cont::LogLevel::Info,
"Other image errors: " << testResults.GetMergedMessage());
}
return imageResult;
}
testResults.PushMessage(imageResult.GetMergedMessage());
}
VTKM_LOG_S(vtkm::cont::LogLevel::Info, "Test Results: " << testResults.GetMergedMessage());
if (!testResults)
{
std::cout << dartXML.str();
}
return testResults;
}

@ -22,11 +22,15 @@
#include <vtkm/io/FileUtils.h>
#include <vtkm/io/ImageUtils.h>
#include <vtkm/rendering/View.h>
#include <vtkm/rendering/testing/vtkm_rendering_testing_export.h>
#include <fstream>
#include <sstream>
#include <vector>
/// \brief Tests multiple image files against a provided view pointer for differences
/// \brief Tests multiple image files against a provided view for differences.
///
/// This function tests multiple files provided via fileNames against the rendered
/// canvas generated by the provided view using the ImageDifference filter. If one
@ -37,128 +41,75 @@
/// This function will generate an image if the provided file is missing. If a file is
/// missing the image will be generated for that file and the test will continue.
///
template <typename ViewType>
VTKM_RENDERING_TESTING_EXPORT TestEqualResult
test_equal_images(vtkm::rendering::View& view,
const std::vector<std::string>& fileNames,
const vtkm::IdComponent& averageRadius = 0,
const vtkm::IdComponent& pixelShiftRadius = 0,
const vtkm::FloatDefault& allowedPixelErrorRatio = 0.00025f,
const vtkm::FloatDefault& threshold = 0.05f,
const bool& writeDiff = true,
const bool& returnOnPass = true);
/// \brief Tests multiple image files against a provided canvas for differences.
///
/// This function tests multiple files provided via fileNames against the rendered
/// canvas using the ImageDifference filter. If one of the provided images is within
/// the error threshold for the image difference this function will return true.
/// Otherwise the view is too different from the images and this will return false
/// with corresponding error messages.
///
/// The canvas must already be rendered when this is called.
///
/// This function will generate an image if the provided file is missing. If a file is
/// missing the image will be generated for that file and the test will continue.
///
VTKM_RENDERING_TESTING_EXPORT TestEqualResult
test_equal_images(const vtkm::rendering::Canvas& canvas,
const std::vector<std::string>& fileNames,
const vtkm::IdComponent& averageRadius = 0,
const vtkm::IdComponent& pixelShiftRadius = 0,
const vtkm::FloatDefault& allowedPixelErrorRatio = 0.00025f,
const vtkm::FloatDefault& threshold = 0.05f,
const bool& writeDiff = true,
const bool& returnOnPass = true);
/// \brief Tests multiple image files against a provided image for differences.
///
/// This function tests multiple files provided via fileNames against the provided
/// rendered image using the ImageDifference filter. If one of the provided images
/// is within the error threshold for the image difference this function will return
/// true. Otherwise the view is too different from the images and this will return
/// false with corresponding error messages.
///
/// The provided `DataSet` must contain a `CellSetStructured<2>` and its first field
/// must be the colors to compare.
///
/// This function will generate an image if the provided file is missing. If a file is
/// missing the image will be generated for that file and the test will continue.
///
VTKM_RENDERING_TESTING_EXPORT TestEqualResult
test_equal_images(const vtkm::cont::DataSet& generatedImage,
const std::vector<std::string>& fileNames,
const vtkm::IdComponent& averageRadius = 0,
const vtkm::IdComponent& pixelShiftRadius = 0,
const vtkm::FloatDefault& allowedPixelErrorRatio = 0.00025f,
const vtkm::FloatDefault& threshold = 0.05f,
const bool& writeDiff = true,
const bool& returnOnPass = true);
/// \brief Tests multiple image files against a provided image for differences.
///
/// This form of `test_equal_images` takes a single filename and searches for a list
/// of files that match that name or that name with numbers appended to it. It then
/// calls the equivalent `test_equal_images` with the list of files found.
///
/// The `ImageType` can be any type of object that other forms of `test_equal_images`
/// accept such as a `View`, a `Canvas`, or a `DataSet` containing an image.
///
template <typename ImageType>
inline TestEqualResult test_equal_images(
ViewType& view,
const std::vector<std::string>& fileNames,
const vtkm::IdComponent& averageRadius = 0,
const vtkm::IdComponent& pixelShiftRadius = 0,
const vtkm::FloatDefault& allowedPixelErrorRatio = 0.00025f,
const vtkm::FloatDefault& threshold = 0.05f,
const bool& writeDiff = true,
const bool& returnOnPass = true)
{
vtkm::cont::ScopedRuntimeDeviceTracker runtime(vtkm::cont::DeviceAdapterTagAny{});
TestEqualResult testResults;
if (fileNames.empty())
{
testResults.PushMessage("No valid image file names were provided");
return testResults;
}
view.Paint();
view.GetCanvas().RefreshColorBuffer();
const std::string testImageName = vtkm::cont::testing::Testing::WriteDirPath(
vtkm::io::PrefixStringToFilename(fileNames[0], "test-"));
vtkm::io::WriteImageFile(view.GetCanvas().GetDataSet(), testImageName, "color");
std::stringstream dartXML;
dartXML << "<DartMeasurementFile name=\"TestImage\" type=\"image/png\">";
dartXML << testImageName;
dartXML << "</DartMeasurementFile>\n";
for (const auto& fileName : fileNames)
{
VTKM_LOG_S(vtkm::cont::LogLevel::Info, "testing image file: " << fileName);
TestEqualResult imageResult;
vtkm::cont::DataSet imageDataSet;
const std::string testImagePath = vtkm::cont::testing::Testing::RegressionImagePath(fileName);
try
{
imageDataSet = vtkm::io::ReadImageFile(testImagePath, "baseline-image");
}
catch (const vtkm::cont::ErrorExecution& error)
{
VTKM_LOG_S(vtkm::cont::LogLevel::Error, error.GetMessage());
imageResult.PushMessage(error.GetMessage());
const std::string outputImagePath = vtkm::cont::testing::Testing::WriteDirPath(fileName);
vtkm::io::WriteImageFile(view.GetCanvas().GetDataSet(), outputImagePath, "color");
imageResult.PushMessage("File '" + fileName +
"' did not exist but has been generated here: " + outputImagePath);
testResults.PushMessage(imageResult.GetMergedMessage());
continue;
}
catch (const vtkm::cont::ErrorBadValue& error)
{
VTKM_LOG_S(vtkm::cont::LogLevel::Error, error.GetMessage());
imageResult.PushMessage(error.GetMessage());
imageResult.PushMessage("Unsupported file type for image: " + fileName);
testResults.PushMessage(imageResult.GetMergedMessage());
continue;
}
dartXML << "<DartMeasurementFile name=\"BaselineImage\" type=\"image/png\">";
dartXML << testImagePath;
dartXML << "</DartMeasurementFile>\n";
imageDataSet.AddPointField("generated-image", view.GetCanvas().GetColorBuffer());
vtkm::filter::ImageDifference filter;
filter.SetPrimaryField("baseline-image");
filter.SetSecondaryField("generated-image");
filter.SetAverageRadius(averageRadius);
filter.SetPixelShiftRadius(pixelShiftRadius);
filter.SetAllowedPixelErrorRatio(allowedPixelErrorRatio);
filter.SetPixelDiffThreshold(threshold);
auto resultDataSet = filter.Execute(imageDataSet);
if (!filter.GetImageDiffWithinThreshold())
{
imageResult.PushMessage("Image Difference was not within the expected threshold for: " +
fileName);
}
if (writeDiff && resultDataSet.HasPointField("image-diff"))
{
const std::string diffName = vtkm::cont::testing::Testing::WriteDirPath(
vtkm::io::PrefixStringToFilename(fileName, "diff-"));
vtkm::io::WriteImageFile(resultDataSet, diffName, "image-diff");
dartXML << "<DartMeasurementFile name=\"DifferenceImage\" type=\"image/png\">";
dartXML << diffName;
dartXML << "</DartMeasurementFile>\n";
}
if (imageResult && returnOnPass)
{
VTKM_LOG_S(vtkm::cont::LogLevel::Info, "Test passed for image " << fileName);
if (!testResults)
{
VTKM_LOG_S(vtkm::cont::LogLevel::Info,
"Other image errors: " << testResults.GetMergedMessage());
}
return imageResult;
}
testResults.PushMessage(imageResult.GetMergedMessage());
}
VTKM_LOG_S(vtkm::cont::LogLevel::Info, "Test Results: " << testResults.GetMergedMessage());
if (!testResults)
{
std::cout << dartXML.str();
}
return testResults;
}
template <typename ViewType>
inline TestEqualResult test_equal_images(
ViewType& view,
ImageType&& image,
const std::string& fileName,
const vtkm::IdComponent& averageRadius = 0,
const vtkm::IdComponent& pixelShiftRadius = 0,
@ -202,8 +153,13 @@ inline TestEqualResult test_equal_images(
fileNames.push_back(fileName);
}
return test_equal_images(
view, fileNames, averageRadius, pixelShiftRadius, allowedPixelErrorRatio, threshold, writeDiff);
return test_equal_images(std::forward<ImageType>(image),
fileNames,
averageRadius,
pixelShiftRadius,
allowedPixelErrorRatio,
threshold,
writeDiff);
}
#endif // vtk_m_rendering_testing_Testing_h