Merge topic 'image-diff-improvements'
dd3193d69 consolidate contour-wedge baseline images to favor new pixel diff calculation da751cf06 change arg ordering, support setting number of error pixels allowed Acked-by: Kitware Robot <kwrobot@kitware.com> Merge-request: !2374
This commit is contained in:
commit
3f020101c4
@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c1a54c159483076a2eed1f7cf8f8d947e6ec59dcfbd8ed41355e92cf052d344e
|
||||
size 19884
|
@ -35,16 +35,31 @@ public:
|
||||
|
||||
VTKM_CONT ImageDifference();
|
||||
|
||||
VTKM_CONT vtkm::IdComponent GetRadius() const { return this->Radius; }
|
||||
VTKM_CONT void SetRadius(const vtkm::IdComponent& radius) { this->Radius = radius; }
|
||||
|
||||
VTKM_CONT vtkm::FloatDefault GetThreshold() const { return this->Threshold; }
|
||||
VTKM_CONT void SetThreshold(const vtkm::FloatDefault& threshold) { this->Threshold = threshold; }
|
||||
|
||||
VTKM_CONT bool GetAveragePixels() const { return this->AveragePixels; }
|
||||
VTKM_CONT void SetAveragePixels(const bool& averagePixels)
|
||||
VTKM_CONT vtkm::IdComponent GetAverageRadius() const { return this->AverageRadius; }
|
||||
VTKM_CONT void SetAverageRadius(const vtkm::IdComponent& averageRadius)
|
||||
{
|
||||
this->AveragePixels = averagePixels;
|
||||
this->AverageRadius = averageRadius;
|
||||
}
|
||||
|
||||
VTKM_CONT vtkm::IdComponent GetPixelShiftRadius() const { return this->PixelShiftRadius; }
|
||||
VTKM_CONT void SetPixelShiftRadius(const vtkm::IdComponent& pixelShiftRadius)
|
||||
{
|
||||
this->PixelShiftRadius = pixelShiftRadius;
|
||||
}
|
||||
|
||||
VTKM_CONT vtkm::FloatDefault GetAllowedPixelErrorRatio() const
|
||||
{
|
||||
return this->AllowedPixelErrorRatio;
|
||||
}
|
||||
VTKM_CONT void SetAllowedPixelErrorRatio(const vtkm::FloatDefault& pixelErrorRatio)
|
||||
{
|
||||
this->AllowedPixelErrorRatio = pixelErrorRatio;
|
||||
}
|
||||
|
||||
VTKM_CONT vtkm::FloatDefault GetPixelDiffThreshold() const { return this->PixelDiffThreshold; }
|
||||
VTKM_CONT void SetPixelDiffThreshold(const vtkm::FloatDefault& threshold)
|
||||
{
|
||||
this->PixelDiffThreshold = threshold;
|
||||
}
|
||||
|
||||
VTKM_CONT bool GetImageDiffWithinThreshold() const { return this->ImageDiffWithinThreshold; }
|
||||
@ -91,9 +106,10 @@ public:
|
||||
vtkm::filter::PolicyBase<DerivedPolicy> policy);
|
||||
|
||||
private:
|
||||
vtkm::IdComponent Radius;
|
||||
vtkm::FloatDefault Threshold;
|
||||
bool AveragePixels;
|
||||
vtkm::IdComponent AverageRadius;
|
||||
vtkm::IdComponent PixelShiftRadius;
|
||||
vtkm::FloatDefault AllowedPixelErrorRatio;
|
||||
vtkm::FloatDefault PixelDiffThreshold;
|
||||
bool ImageDiffWithinThreshold;
|
||||
std::string SecondaryFieldName;
|
||||
vtkm::cont::Field::Association SecondaryFieldAssociation;
|
||||
|
@ -12,7 +12,6 @@
|
||||
|
||||
#include <vtkm/filter/ImageDifference.h>
|
||||
|
||||
#include <vtkm/BinaryOperators.h>
|
||||
#include <vtkm/cont/Algorithm.h>
|
||||
#include <vtkm/cont/ArrayPortalToIterators.h>
|
||||
#include <vtkm/cont/Logging.h>
|
||||
@ -25,11 +24,25 @@ namespace vtkm
|
||||
namespace filter
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
struct GreaterThanThreshold
|
||||
{
|
||||
GreaterThanThreshold(const vtkm::FloatDefault& thresholdError)
|
||||
: ThresholdError(thresholdError)
|
||||
{
|
||||
}
|
||||
VTKM_EXEC_CONT bool operator()(const vtkm::FloatDefault& x) const { return x > ThresholdError; }
|
||||
vtkm::FloatDefault ThresholdError;
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
inline VTKM_CONT ImageDifference::ImageDifference()
|
||||
: vtkm::filter::FilterField<ImageDifference>()
|
||||
, Radius(0)
|
||||
, Threshold(0.05f)
|
||||
, AveragePixels(false)
|
||||
, AverageRadius(0)
|
||||
, PixelShiftRadius(0)
|
||||
, AllowedPixelErrorRatio(0.0001f)
|
||||
, PixelDiffThreshold(0.05f)
|
||||
, ImageDiffWithinThreshold(true)
|
||||
, SecondaryFieldName("image-2")
|
||||
, SecondaryFieldAssociation(vtkm::cont::Field::Association::ANY)
|
||||
@ -64,10 +77,11 @@ inline VTKM_CONT vtkm::cont::DataSet ImageDifference::DoExecute(
|
||||
vtkm::cont::ArrayHandle<T, StorageType> secondaryOutput;
|
||||
vtkm::cont::ArrayHandle<vtkm::FloatDefault, StorageType> thresholdOutput;
|
||||
|
||||
if (this->AveragePixels && this->Radius > 0)
|
||||
if (this->AverageRadius > 0)
|
||||
{
|
||||
VTKM_LOG_S(vtkm::cont::LogLevel::Info, "Performing Average with radius: " << this->Radius);
|
||||
auto averageWorklet = vtkm::worklet::AveragePointNeighborhood(this->Radius);
|
||||
VTKM_LOG_S(vtkm::cont::LogLevel::Info,
|
||||
"Performing Average with radius: " << this->AverageRadius);
|
||||
auto averageWorklet = vtkm::worklet::AveragePointNeighborhood(this->AverageRadius);
|
||||
this->Invoke(averageWorklet, cellSet, primary, primaryOutput);
|
||||
this->Invoke(averageWorklet, cellSet, secondary, secondaryOutput);
|
||||
}
|
||||
@ -79,10 +93,11 @@ inline VTKM_CONT vtkm::cont::DataSet ImageDifference::DoExecute(
|
||||
secondaryField.GetData().template Cast<vtkm::cont::ArrayHandle<T, StorageType>>();
|
||||
}
|
||||
|
||||
if (this->Radius > 0)
|
||||
if (this->PixelShiftRadius > 0)
|
||||
{
|
||||
VTKM_LOG_S(vtkm::cont::LogLevel::Info, "Diffing image in Neighborhood");
|
||||
auto diffWorklet = vtkm::worklet::ImageDifferenceNeighborhood(this->Radius, this->Threshold);
|
||||
auto diffWorklet =
|
||||
vtkm::worklet::ImageDifferenceNeighborhood(this->PixelShiftRadius, this->PixelDiffThreshold);
|
||||
this->Invoke(diffWorklet, cellSet, primaryOutput, secondaryOutput, diffOutput, thresholdOutput);
|
||||
}
|
||||
else
|
||||
@ -92,17 +107,26 @@ inline VTKM_CONT vtkm::cont::DataSet ImageDifference::DoExecute(
|
||||
this->Invoke(diffWorklet, primaryOutput, secondaryOutput, diffOutput, thresholdOutput);
|
||||
}
|
||||
|
||||
// Dummy calculate the threshold. If any value is greater than the min our images
|
||||
// are not similar enough.
|
||||
vtkm::FloatDefault maxThreshold =
|
||||
vtkm::cont::Algorithm::Reduce(thresholdOutput, vtkm::FloatDefault(0), vtkm::Maximum());
|
||||
if (maxThreshold > this->Threshold)
|
||||
|
||||
vtkm::cont::ArrayHandle<vtkm::FloatDefault, StorageType> errorPixels;
|
||||
vtkm::cont::Algorithm::CopyIf(thresholdOutput,
|
||||
thresholdOutput,
|
||||
errorPixels,
|
||||
detail::GreaterThanThreshold(this->PixelDiffThreshold));
|
||||
if (errorPixels.GetNumberOfValues() >
|
||||
thresholdOutput.GetNumberOfValues() * this->AllowedPixelErrorRatio)
|
||||
{
|
||||
this->ImageDiffWithinThreshold = false;
|
||||
}
|
||||
|
||||
VTKM_LOG_S(vtkm::cont::LogLevel::Info,
|
||||
"Difference within threshold: " << this->ImageDiffWithinThreshold);
|
||||
"Difference within threshold: "
|
||||
<< this->ImageDiffWithinThreshold
|
||||
<< ", for pixels outside threshold: " << errorPixels.GetNumberOfValues()
|
||||
<< ", with total number of pixels: " << thresholdOutput.GetNumberOfValues()
|
||||
<< ", and an allowable percentage of errored pixels: "
|
||||
<< this->AllowedPixelErrorRatio << ", with a total summed threshold error: "
|
||||
<< vtkm::cont::Algorithm::Reduce(errorPixels, static_cast<FloatDefault>(0)));
|
||||
|
||||
vtkm::cont::DataSet clone;
|
||||
clone.CopyStructure(input);
|
||||
|
@ -53,7 +53,7 @@ void TestContourFilterWedge()
|
||||
auto view = vtkm::rendering::testing::GetViewPtr<M, C, V3>(
|
||||
result, "gyroid", canvas, mapper, scene, colorTable, static_cast<vtkm::FloatDefault>(0.08));
|
||||
|
||||
VTKM_TEST_ASSERT(test_equal_images_matching_name(view, "contour-wedge.png"));
|
||||
VTKM_TEST_ASSERT(test_equal_images(view, "contour-wedge.png"));
|
||||
}
|
||||
|
||||
void TestContourFilter()
|
||||
|
@ -102,8 +102,8 @@ void TestImageDifference()
|
||||
vtkm::filter::ImageDifference filter;
|
||||
filter.SetPrimaryField("primary");
|
||||
filter.SetSecondaryField("secondary");
|
||||
filter.SetThreshold(0.05f);
|
||||
filter.SetRadius(0);
|
||||
filter.SetPixelDiffThreshold(0.05f);
|
||||
filter.SetPixelShiftRadius(0);
|
||||
vtkm::cont::DataSet result = filter.Execute(dataSet);
|
||||
|
||||
std::vector<vtkm::Vec4f> expectedDiff = {
|
||||
@ -125,9 +125,9 @@ void TestImageDifference()
|
||||
vtkm::filter::ImageDifference filter;
|
||||
filter.SetPrimaryField("primary");
|
||||
filter.SetSecondaryField("secondary");
|
||||
filter.SetThreshold(0.05f);
|
||||
filter.SetRadius(1);
|
||||
filter.SetAveragePixels(true);
|
||||
filter.SetPixelDiffThreshold(0.05f);
|
||||
filter.SetPixelShiftRadius(1);
|
||||
filter.SetAverageRadius(1);
|
||||
vtkm::cont::DataSet result = filter.Execute(dataSet);
|
||||
|
||||
std::vector<vtkm::Vec4f> expectedDiff = {
|
||||
@ -149,8 +149,8 @@ void TestImageDifference()
|
||||
vtkm::filter::ImageDifference filter;
|
||||
filter.SetPrimaryField("primary");
|
||||
filter.SetSecondaryField("secondary");
|
||||
filter.SetThreshold(0.05f);
|
||||
filter.SetRadius(0);
|
||||
filter.SetPixelDiffThreshold(0.05f);
|
||||
filter.SetPixelShiftRadius(0);
|
||||
vtkm::cont::DataSet result = filter.Execute(dataSet);
|
||||
|
||||
std::vector<vtkm::Vec4f> expectedDiff = {
|
||||
@ -166,6 +166,30 @@ void TestImageDifference()
|
||||
expectedDiff, expectedThreshold, result, filter.GetImageDiffWithinThreshold(), false);
|
||||
}
|
||||
|
||||
{
|
||||
VTKM_LOG_S(vtkm::cont::LogLevel::Info, "Non Matching Images (Different R pixel)");
|
||||
auto dataSet = FillDataSet(static_cast<vtkm::FloatDefault>(3));
|
||||
vtkm::filter::ImageDifference filter;
|
||||
filter.SetPrimaryField("primary");
|
||||
filter.SetSecondaryField("secondary");
|
||||
filter.SetPixelDiffThreshold(0.05f);
|
||||
filter.SetPixelShiftRadius(0);
|
||||
filter.SetAllowedPixelErrorRatio(1.00f);
|
||||
vtkm::cont::DataSet result = filter.Execute(dataSet);
|
||||
|
||||
std::vector<vtkm::Vec4f> expectedDiff = {
|
||||
{ 2, 0, 0, 0 }, { 2, 0, 0, 0 }, { 2, 0, 0, 0 }, { 2, 0, 0, 0 }, { 2, 0, 0, 0 },
|
||||
{ 2, 0, 0, 0 }, { 2, 0, 0, 0 }, { 2, 0, 0, 0 }, { 2, 0, 0, 0 }, { 2, 0, 0, 0 },
|
||||
{ 2, 0, 0, 0 }, { 2, 0, 0, 0 }, { 2, 0, 0, 0 }, { 2, 0, 0, 0 }, { 2, 0, 0, 0 },
|
||||
{ 2, 0, 0, 0 }, { 2, 0, 0, 0 }, { 2, 0, 0, 0 }, { 2, 0, 0, 0 }, { 2, 0, 0, 0 },
|
||||
{ 2, 0, 0, 0 }, { 2, 0, 0, 0 }, { 2, 0, 0, 0 }, { 2, 0, 0, 0 }, { 2, 0, 0, 0 }
|
||||
};
|
||||
std::vector<vtkm::FloatDefault> expectedThreshold = { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 };
|
||||
CheckResult(
|
||||
expectedDiff, expectedThreshold, result, filter.GetImageDiffWithinThreshold(), true);
|
||||
}
|
||||
|
||||
{
|
||||
VTKM_LOG_S(vtkm::cont::LogLevel::Info,
|
||||
"Non Matching Images (Different R pixel), Large Threshold");
|
||||
@ -173,8 +197,8 @@ void TestImageDifference()
|
||||
vtkm::filter::ImageDifference filter;
|
||||
filter.SetPrimaryField("primary");
|
||||
filter.SetSecondaryField("secondary");
|
||||
filter.SetThreshold(3.0f);
|
||||
filter.SetRadius(0);
|
||||
filter.SetPixelDiffThreshold(3.0f);
|
||||
filter.SetPixelShiftRadius(0);
|
||||
vtkm::cont::DataSet result = filter.Execute(dataSet);
|
||||
|
||||
std::vector<vtkm::Vec4f> expectedDiff = {
|
||||
|
@ -38,9 +38,10 @@
|
||||
template <typename ViewType>
|
||||
inline TestEqualResult test_equal_images(const std::shared_ptr<ViewType> view,
|
||||
const std::vector<std::string>& fileNames,
|
||||
const vtkm::IdComponent& averageRadius = 0,
|
||||
const vtkm::IdComponent& pixelShiftRadius = 0,
|
||||
const vtkm::FloatDefault& allowedPixelErrorRatio = 0.0001f,
|
||||
const vtkm::FloatDefault& threshold = 0.05f,
|
||||
const vtkm::IdComponent& radius = 0,
|
||||
const bool& average = false,
|
||||
const bool& writeDiff = true,
|
||||
const bool& returnOnPass = true)
|
||||
{
|
||||
@ -95,9 +96,10 @@ inline TestEqualResult test_equal_images(const std::shared_ptr<ViewType> view,
|
||||
vtkm::filter::ImageDifference filter;
|
||||
filter.SetPrimaryField("baseline-image");
|
||||
filter.SetSecondaryField("generated-image");
|
||||
filter.SetThreshold(threshold);
|
||||
filter.SetRadius(radius);
|
||||
filter.SetAveragePixels(average);
|
||||
filter.SetAverageRadius(averageRadius);
|
||||
filter.SetPixelShiftRadius(pixelShiftRadius);
|
||||
filter.SetAllowedPixelErrorRatio(allowedPixelErrorRatio);
|
||||
filter.SetPixelDiffThreshold(threshold);
|
||||
auto resultDataSet = filter.Execute(imageDataSet);
|
||||
|
||||
if (!filter.GetImageDiffWithinThreshold())
|
||||
@ -133,13 +135,15 @@ inline TestEqualResult test_equal_images(const std::shared_ptr<ViewType> view,
|
||||
template <typename ViewType>
|
||||
inline TestEqualResult test_equal_images(const std::shared_ptr<ViewType> view,
|
||||
const std::string& fileName,
|
||||
const vtkm::IdComponent& averageRadius = 0,
|
||||
const vtkm::IdComponent& pixelShiftRadius = 0,
|
||||
const vtkm::FloatDefault& allowedPixelErrorRatio = 0.0001f,
|
||||
const vtkm::FloatDefault& threshold = 0.05f,
|
||||
const vtkm::IdComponent& radius = 0,
|
||||
const bool& average = false,
|
||||
const bool& writeDiff = true)
|
||||
{
|
||||
std::vector<std::string> fileNames{ fileName };
|
||||
return test_equal_images(view, fileNames, threshold, radius, average, writeDiff);
|
||||
return test_equal_images(
|
||||
view, fileNames, averageRadius, pixelShiftRadius, allowedPixelErrorRatio, threshold, writeDiff);
|
||||
}
|
||||
|
||||
/// \brief Tests multiple images in the format `fileName#.png`
|
||||
@ -156,13 +160,15 @@ inline TestEqualResult test_equal_images(const std::shared_ptr<ViewType> view,
|
||||
/// test_equal_images will then be called on the vector of valid fileNames
|
||||
///
|
||||
template <typename ViewType>
|
||||
inline TestEqualResult test_equal_images_matching_name(const std::shared_ptr<ViewType> view,
|
||||
const std::string& fileName,
|
||||
const vtkm::FloatDefault& threshold = 0.05f,
|
||||
const vtkm::IdComponent& radius = 0,
|
||||
const bool& average = false,
|
||||
const bool& writeDiff = true,
|
||||
const bool& returnOnPass = true)
|
||||
inline TestEqualResult test_equal_images_matching_name(
|
||||
const std::shared_ptr<ViewType> view,
|
||||
const std::string& fileName,
|
||||
const vtkm::IdComponent& averageRadius = 0,
|
||||
const vtkm::IdComponent& pixelShiftRadius = 0,
|
||||
const vtkm::FloatDefault& allowedPixelErrorRatio = 0.0001f,
|
||||
const vtkm::FloatDefault& threshold = 0.05f,
|
||||
const bool& writeDiff = true,
|
||||
const bool& returnOnPass = true)
|
||||
{
|
||||
std::vector<std::string> fileNames;
|
||||
auto found = fileName.rfind(".");
|
||||
@ -183,7 +189,14 @@ inline TestEqualResult test_equal_images_matching_name(const std::shared_ptr<Vie
|
||||
}
|
||||
fileNames.emplace_back(fileNameStream.str());
|
||||
}
|
||||
return test_equal_images(view, fileNames, threshold, radius, average, writeDiff, returnOnPass);
|
||||
return test_equal_images(view,
|
||||
fileNames,
|
||||
averageRadius,
|
||||
pixelShiftRadius,
|
||||
allowedPixelErrorRatio,
|
||||
threshold,
|
||||
writeDiff,
|
||||
returnOnPass);
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user