change arg ordering, support setting number of error pixels allowed

This commit is contained in:
nadavi 2021-01-05 20:09:01 +00:00
parent 142a538d32
commit da751cf061
4 changed files with 129 additions and 52 deletions

@ -35,16 +35,31 @@ public:
VTKM_CONT ImageDifference(); VTKM_CONT ImageDifference();
VTKM_CONT vtkm::IdComponent GetRadius() const { return this->Radius; } VTKM_CONT vtkm::IdComponent GetAverageRadius() const { return this->AverageRadius; }
VTKM_CONT void SetRadius(const vtkm::IdComponent& radius) { this->Radius = radius; } VTKM_CONT void SetAverageRadius(const vtkm::IdComponent& averageRadius)
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)
{ {
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; } VTKM_CONT bool GetImageDiffWithinThreshold() const { return this->ImageDiffWithinThreshold; }
@ -91,9 +106,10 @@ public:
vtkm::filter::PolicyBase<DerivedPolicy> policy); vtkm::filter::PolicyBase<DerivedPolicy> policy);
private: private:
vtkm::IdComponent Radius; vtkm::IdComponent AverageRadius;
vtkm::FloatDefault Threshold; vtkm::IdComponent PixelShiftRadius;
bool AveragePixels; vtkm::FloatDefault AllowedPixelErrorRatio;
vtkm::FloatDefault PixelDiffThreshold;
bool ImageDiffWithinThreshold; bool ImageDiffWithinThreshold;
std::string SecondaryFieldName; std::string SecondaryFieldName;
vtkm::cont::Field::Association SecondaryFieldAssociation; vtkm::cont::Field::Association SecondaryFieldAssociation;

@ -12,7 +12,6 @@
#include <vtkm/filter/ImageDifference.h> #include <vtkm/filter/ImageDifference.h>
#include <vtkm/BinaryOperators.h>
#include <vtkm/cont/Algorithm.h> #include <vtkm/cont/Algorithm.h>
#include <vtkm/cont/ArrayPortalToIterators.h> #include <vtkm/cont/ArrayPortalToIterators.h>
#include <vtkm/cont/Logging.h> #include <vtkm/cont/Logging.h>
@ -25,11 +24,25 @@ namespace vtkm
namespace filter 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() inline VTKM_CONT ImageDifference::ImageDifference()
: vtkm::filter::FilterField<ImageDifference>() : vtkm::filter::FilterField<ImageDifference>()
, Radius(0) , AverageRadius(0)
, Threshold(0.05f) , PixelShiftRadius(0)
, AveragePixels(false) , AllowedPixelErrorRatio(0.0001f)
, PixelDiffThreshold(0.05f)
, ImageDiffWithinThreshold(true) , ImageDiffWithinThreshold(true)
, SecondaryFieldName("image-2") , SecondaryFieldName("image-2")
, SecondaryFieldAssociation(vtkm::cont::Field::Association::ANY) , 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<T, StorageType> secondaryOutput;
vtkm::cont::ArrayHandle<vtkm::FloatDefault, StorageType> thresholdOutput; 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); VTKM_LOG_S(vtkm::cont::LogLevel::Info,
auto averageWorklet = vtkm::worklet::AveragePointNeighborhood(this->Radius); "Performing Average with radius: " << this->AverageRadius);
auto averageWorklet = vtkm::worklet::AveragePointNeighborhood(this->AverageRadius);
this->Invoke(averageWorklet, cellSet, primary, primaryOutput); this->Invoke(averageWorklet, cellSet, primary, primaryOutput);
this->Invoke(averageWorklet, cellSet, secondary, secondaryOutput); 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>>(); 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"); 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); this->Invoke(diffWorklet, cellSet, primaryOutput, secondaryOutput, diffOutput, thresholdOutput);
} }
else else
@ -92,17 +107,26 @@ inline VTKM_CONT vtkm::cont::DataSet ImageDifference::DoExecute(
this->Invoke(diffWorklet, primaryOutput, secondaryOutput, diffOutput, thresholdOutput); 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::cont::ArrayHandle<vtkm::FloatDefault, StorageType> errorPixels;
vtkm::FloatDefault maxThreshold = vtkm::cont::Algorithm::CopyIf(thresholdOutput,
vtkm::cont::Algorithm::Reduce(thresholdOutput, vtkm::FloatDefault(0), vtkm::Maximum()); thresholdOutput,
if (maxThreshold > this->Threshold) errorPixels,
detail::GreaterThanThreshold(this->PixelDiffThreshold));
if (errorPixels.GetNumberOfValues() >
thresholdOutput.GetNumberOfValues() * this->AllowedPixelErrorRatio)
{ {
this->ImageDiffWithinThreshold = false; this->ImageDiffWithinThreshold = false;
} }
VTKM_LOG_S(vtkm::cont::LogLevel::Info, 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; vtkm::cont::DataSet clone;
clone.CopyStructure(input); clone.CopyStructure(input);

@ -102,8 +102,8 @@ void TestImageDifference()
vtkm::filter::ImageDifference filter; vtkm::filter::ImageDifference filter;
filter.SetPrimaryField("primary"); filter.SetPrimaryField("primary");
filter.SetSecondaryField("secondary"); filter.SetSecondaryField("secondary");
filter.SetThreshold(0.05f); filter.SetPixelDiffThreshold(0.05f);
filter.SetRadius(0); filter.SetPixelShiftRadius(0);
vtkm::cont::DataSet result = filter.Execute(dataSet); vtkm::cont::DataSet result = filter.Execute(dataSet);
std::vector<vtkm::Vec4f> expectedDiff = { std::vector<vtkm::Vec4f> expectedDiff = {
@ -125,9 +125,9 @@ void TestImageDifference()
vtkm::filter::ImageDifference filter; vtkm::filter::ImageDifference filter;
filter.SetPrimaryField("primary"); filter.SetPrimaryField("primary");
filter.SetSecondaryField("secondary"); filter.SetSecondaryField("secondary");
filter.SetThreshold(0.05f); filter.SetPixelDiffThreshold(0.05f);
filter.SetRadius(1); filter.SetPixelShiftRadius(1);
filter.SetAveragePixels(true); filter.SetAverageRadius(1);
vtkm::cont::DataSet result = filter.Execute(dataSet); vtkm::cont::DataSet result = filter.Execute(dataSet);
std::vector<vtkm::Vec4f> expectedDiff = { std::vector<vtkm::Vec4f> expectedDiff = {
@ -149,8 +149,8 @@ void TestImageDifference()
vtkm::filter::ImageDifference filter; vtkm::filter::ImageDifference filter;
filter.SetPrimaryField("primary"); filter.SetPrimaryField("primary");
filter.SetSecondaryField("secondary"); filter.SetSecondaryField("secondary");
filter.SetThreshold(0.05f); filter.SetPixelDiffThreshold(0.05f);
filter.SetRadius(0); filter.SetPixelShiftRadius(0);
vtkm::cont::DataSet result = filter.Execute(dataSet); vtkm::cont::DataSet result = filter.Execute(dataSet);
std::vector<vtkm::Vec4f> expectedDiff = { std::vector<vtkm::Vec4f> expectedDiff = {
@ -166,6 +166,30 @@ void TestImageDifference()
expectedDiff, expectedThreshold, result, filter.GetImageDiffWithinThreshold(), false); 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, VTKM_LOG_S(vtkm::cont::LogLevel::Info,
"Non Matching Images (Different R pixel), Large Threshold"); "Non Matching Images (Different R pixel), Large Threshold");
@ -173,8 +197,8 @@ void TestImageDifference()
vtkm::filter::ImageDifference filter; vtkm::filter::ImageDifference filter;
filter.SetPrimaryField("primary"); filter.SetPrimaryField("primary");
filter.SetSecondaryField("secondary"); filter.SetSecondaryField("secondary");
filter.SetThreshold(3.0f); filter.SetPixelDiffThreshold(3.0f);
filter.SetRadius(0); filter.SetPixelShiftRadius(0);
vtkm::cont::DataSet result = filter.Execute(dataSet); vtkm::cont::DataSet result = filter.Execute(dataSet);
std::vector<vtkm::Vec4f> expectedDiff = { std::vector<vtkm::Vec4f> expectedDiff = {

@ -38,9 +38,10 @@
template <typename ViewType> template <typename ViewType>
inline TestEqualResult test_equal_images(const std::shared_ptr<ViewType> view, inline TestEqualResult test_equal_images(const std::shared_ptr<ViewType> view,
const std::vector<std::string>& fileNames, 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::FloatDefault& threshold = 0.05f,
const vtkm::IdComponent& radius = 0,
const bool& average = false,
const bool& writeDiff = true, const bool& writeDiff = true,
const bool& returnOnPass = true) const bool& returnOnPass = true)
{ {
@ -95,9 +96,10 @@ inline TestEqualResult test_equal_images(const std::shared_ptr<ViewType> view,
vtkm::filter::ImageDifference filter; vtkm::filter::ImageDifference filter;
filter.SetPrimaryField("baseline-image"); filter.SetPrimaryField("baseline-image");
filter.SetSecondaryField("generated-image"); filter.SetSecondaryField("generated-image");
filter.SetThreshold(threshold); filter.SetAverageRadius(averageRadius);
filter.SetRadius(radius); filter.SetPixelShiftRadius(pixelShiftRadius);
filter.SetAveragePixels(average); filter.SetAllowedPixelErrorRatio(allowedPixelErrorRatio);
filter.SetPixelDiffThreshold(threshold);
auto resultDataSet = filter.Execute(imageDataSet); auto resultDataSet = filter.Execute(imageDataSet);
if (!filter.GetImageDiffWithinThreshold()) if (!filter.GetImageDiffWithinThreshold())
@ -133,13 +135,15 @@ inline TestEqualResult test_equal_images(const std::shared_ptr<ViewType> view,
template <typename ViewType> template <typename ViewType>
inline TestEqualResult test_equal_images(const std::shared_ptr<ViewType> view, inline TestEqualResult test_equal_images(const std::shared_ptr<ViewType> view,
const std::string& fileName, 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::FloatDefault& threshold = 0.05f,
const vtkm::IdComponent& radius = 0,
const bool& average = false,
const bool& writeDiff = true) const bool& writeDiff = true)
{ {
std::vector<std::string> fileNames{ fileName }; 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` /// \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 /// test_equal_images will then be called on the vector of valid fileNames
/// ///
template <typename ViewType> template <typename ViewType>
inline TestEqualResult test_equal_images_matching_name(const std::shared_ptr<ViewType> view, inline TestEqualResult test_equal_images_matching_name(
const std::string& fileName, const std::shared_ptr<ViewType> view,
const vtkm::FloatDefault& threshold = 0.05f, const std::string& fileName,
const vtkm::IdComponent& radius = 0, const vtkm::IdComponent& averageRadius = 0,
const bool& average = false, const vtkm::IdComponent& pixelShiftRadius = 0,
const bool& writeDiff = true, const vtkm::FloatDefault& allowedPixelErrorRatio = 0.0001f,
const bool& returnOnPass = true) const vtkm::FloatDefault& threshold = 0.05f,
const bool& writeDiff = true,
const bool& returnOnPass = true)
{ {
std::vector<std::string> fileNames; std::vector<std::string> fileNames;
auto found = fileName.rfind("."); 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()); 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);
} }