Skip to content

Commit

Permalink
Image tools: reworked GetImageDifference function
Browse files Browse the repository at this point in the history
  • Loading branch information
TheMostDiligent committed Jan 24, 2025
1 parent 7039a2a commit c17267f
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 51 deletions.
54 changes: 36 additions & 18 deletions Common/interface/ImageTools.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,16 +56,43 @@ struct ImageDiffInfo
};
typedef struct ImageDiffInfo ImageDiffInfo;


/// Attributes for GetImageDifference function
struct GetImageDifferenceAttribs
{
/// Image width
Uint32 Width DEFAULT_INITIALIZER(0);

/// Image height
Uint32 Height DEFAULT_INITIALIZER(0);

/// A pointer to the first image data
const void* pImage1 DEFAULT_INITIALIZER(nullptr);

/// Number of channels in the first image
Uint32 NumChannels1 DEFAULT_INITIALIZER(0);

/// Row stride of the first image data, in bytes
Uint32 Stride1 DEFAULT_INITIALIZER(0);

/// A pointer to the second image data
const void* pImage2 DEFAULT_INITIALIZER(nullptr);

/// Number of channels in the second image
Uint32 NumChannels2 DEFAULT_INITIALIZER(0);

/// Row stride of the second image data, in bytes
Uint32 Stride2 DEFAULT_INITIALIZER(0);

/// Difference threshold
Uint32 Threshold DEFAULT_INITIALIZER(0);
};
typedef struct GetImageDifferenceAttribs GetImageDifferenceAttribs;

/// Compute the difference between two images
///
/// \param [in] Width Image width
/// \param [in] Height Image height
/// \param [in] NumChannels Number of channels in the image
/// \param [in] pImage1 Pointer to the first image data
/// \param [in] Stride1 Row stride of the first image data, in bytes
/// \param [in] pImage2 Pointer to the second image data
/// \param [in] Stride2 Row stride of the second image data, in bytes
/// \param [in] Threshold Difference threshold
/// \param [in] Attribs Image difference attributes, see Diligent::GetImageDifferenceAttribs.
///
/// \return The image difference information, see Diligent::ImageDiffInfo.
///
/// \remarks The difference between two pixels is calculated as the maximum of the
Expand All @@ -74,16 +101,7 @@ typedef struct ImageDiffInfo ImageDiffInfo;
/// The root mean square difference is calculated as the square root of
/// the average of the squares of all differences, not counting pixels that
/// are equal.
void DILIGENT_GLOBAL_FUNCTION(GetImageDifference)(
Uint32 Width,
Uint32 Height,
Uint32 NumChannels,
const void* pImage1,
Uint32 Stride1,
const void* pImage2,
Uint32 Stride2,
Uint32 Threshold,
ImageDiffInfo REF ImageDiff);
void DILIGENT_GLOBAL_FUNCTION(GetImageDifference)(const GetImageDifferenceAttribs REF Attribs, ImageDiffInfo REF ImageDiff);


// clang-format off
Expand Down
51 changes: 20 additions & 31 deletions Common/src/ImageTools.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,49 +34,45 @@
namespace Diligent
{

void GetImageDifference(Uint32 Width,
Uint32 Height,
Uint32 NumChannels,
const void* pImage1,
Uint32 Stride1,
const void* pImage2,
Uint32 Stride2,
Uint32 Threshold,
ImageDiffInfo& Diff)
void GetImageDifference(const GetImageDifferenceAttribs& Attribs,
ImageDiffInfo& Diff)
{
Diff = {};

if (pImage1 == nullptr || pImage2 == nullptr)
if (Attribs.pImage1 == nullptr || Attribs.pImage2 == nullptr)
{
UNEXPECTED("Image pointers cannot be null");
return;
}

if (Stride1 < Width * NumChannels)
VERIFY(Attribs.NumChannels1 != 0, "NumChannels1 cannot be zero");
if (Attribs.Stride1 < Attribs.Width * Attribs.NumChannels1)
{
UNEXPECTED("Stride1 is too small. It must be at least ", Width * NumChannels, " bytes long.");
UNEXPECTED("Stride1 is too small. It must be at least ", Attribs.Width * Attribs.NumChannels1, " bytes long.");
return;
}

if (Stride2 < Width * NumChannels)
VERIFY(Attribs.NumChannels2 != 0, "NumChannels2 cannot be zero");
if (Attribs.Stride2 < Attribs.Width * Attribs.NumChannels2)
{
UNEXPECTED("Stride2 is too small. It must be at least ", Width * NumChannels, " bytes long.");
UNEXPECTED("Stride2 is too small. It must be at least ", Attribs.Width * Attribs.NumChannels2, " bytes long.");
return;
}

for (Uint32 row = 0; row < Height; ++row)
const Uint32 NumChannels = std::min(Attribs.NumChannels1, Attribs.NumChannels2);
for (Uint32 row = 0; row < Attribs.Height; ++row)
{
const Uint8* pRow1 = reinterpret_cast<const Uint8*>(pImage1) + row * Stride1;
const Uint8* pRow2 = reinterpret_cast<const Uint8*>(pImage2) + row * Stride2;
const Uint8* pRow1 = reinterpret_cast<const Uint8*>(Attribs.pImage1) + row * Attribs.Stride1;
const Uint8* pRow2 = reinterpret_cast<const Uint8*>(Attribs.pImage2) + row * Attribs.Stride2;

for (Uint32 col = 0; col < Width; ++col)
for (Uint32 col = 0; col < Attribs.Width; ++col)
{
Uint32 PixelDiff = 0;
for (Uint32 ch = 0; ch < NumChannels; ++ch)
{
const Uint32 ChannelDiff = static_cast<Uint32>(
std::abs(static_cast<int>(pRow1[col * NumChannels + ch]) -
static_cast<int>(pRow2[col * NumChannels + ch])));
std::abs(static_cast<int>(pRow1[col * Attribs.NumChannels1 + ch]) -
static_cast<int>(pRow2[col * Attribs.NumChannels2 + ch])));
PixelDiff = std::max(PixelDiff, ChannelDiff);
}

Expand All @@ -87,7 +83,7 @@ void GetImageDifference(Uint32 Width,
Diff.RmsDiff += static_cast<float>(PixelDiff * PixelDiff);
Diff.MaxDiff = std::max(Diff.MaxDiff, PixelDiff);

if (PixelDiff > Threshold)
if (PixelDiff > Attribs.Threshold)
{
++Diff.NumDiffPixelsAboveThreshold;
}
Expand Down Expand Up @@ -170,17 +166,10 @@ void ComputeDifferenceImage(

extern "C"
{
void Diligent_GetImageDifference(Diligent::Uint32 Width,
Diligent::Uint32 Height,
Diligent::Uint32 NumChannels,
const void* pImage1,
Diligent::Uint32 Stride1,
const void* pImage2,
Diligent::Uint32 Stride2,
Diligent::Uint32 Threshold,
Diligent::ImageDiffInfo& ImageDiff)
void Diligent_GetImageDifference(const Diligent::GetImageDifferenceAttribs& Attribs,
Diligent::ImageDiffInfo& ImageDiff)
{
Diligent::GetImageDifference(Width, Height, NumChannels, pImage1, Stride1, pImage2, Stride2, Threshold, ImageDiff);
Diligent::GetImageDifference(Attribs, ImageDiff);
}

void Diligent_ComputeDifferenceImage(Diligent::Uint32 Width,
Expand Down
56 changes: 54 additions & 2 deletions Tests/DiligentCoreTest/src/Common/ImageToolsTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ TEST(Common_ImageTools, GetImageDifference)
constexpr Uint32 Height = 2;
constexpr Uint32 Stride1 = 11;
constexpr Uint32 Stride2 = 12;
constexpr Uint32 Stride3 = 9;

// clang-format off
constexpr char Image1[Stride1 * Height] = {
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20,
Expand All @@ -55,11 +57,29 @@ TEST(Common_ImageTools, GetImageDifference)
// ^ ^ ^ ^ ^
// 3 4 5 4 4
};
constexpr char Image3[Stride3 * Height] = {
1, 2, 5, 8, 7, 8, 10, 20, 30,
// ^ ^
// -1 -3
6, 4, 5, 6, 8, 6, 40, 50, 60,
// ^ ^ ^
// 3 4 5
};
// clang-format on

{
GetImageDifferenceAttribs Attribs;
Attribs.Width = Width;
Attribs.Height = Height;
Attribs.pImage1 = Image1;
Attribs.NumChannels1 = 3;
Attribs.Stride1 = Stride1;
Attribs.pImage2 = Image1;
Attribs.NumChannels2 = 3;
Attribs.Stride2 = Stride1;

ImageDiffInfo Diff;
GetImageDifference(Width, Height, 3, Image1, Stride1, Image1, Stride1, 3, Diff);
GetImageDifference(Attribs, Diff);
EXPECT_EQ(Diff.NumDiffPixels, 0);
EXPECT_EQ(Diff.NumDiffPixelsAboveThreshold, 0);
EXPECT_EQ(Diff.MaxDiff, 0);
Expand All @@ -68,8 +88,40 @@ TEST(Common_ImageTools, GetImageDifference)
}

{
GetImageDifferenceAttribs Attribs;
Attribs.Width = Width;
Attribs.Height = Height;
Attribs.pImage1 = Image1;
Attribs.NumChannels1 = 3;
Attribs.Stride1 = Stride1;
Attribs.pImage2 = Image2;
Attribs.NumChannels2 = 3;
Attribs.Stride2 = Stride2;
Attribs.Threshold = 3;

ImageDiffInfo Diff;
GetImageDifference(Attribs, Diff);
EXPECT_EQ(Diff.NumDiffPixels, 3);
EXPECT_EQ(Diff.NumDiffPixelsAboveThreshold, 2);
EXPECT_EQ(Diff.MaxDiff, 5);
EXPECT_FLOAT_EQ(Diff.AvgDiff, 4.f);
EXPECT_FLOAT_EQ(Diff.RmsDiff, std::sqrt((9.f + 16.f + 25.f) / 3.f));
}

{
GetImageDifferenceAttribs Attribs;
Attribs.Width = Width;
Attribs.Height = Height;
Attribs.pImage1 = Image1;
Attribs.NumChannels1 = 3;
Attribs.Stride1 = Stride1;
Attribs.pImage2 = Image3;
Attribs.NumChannels2 = 2;
Attribs.Stride2 = Stride3;
Attribs.Threshold = 3;

ImageDiffInfo Diff;
GetImageDifference(Width, Height, 3, Image1, Stride1, Image2, Stride2, 3, Diff);
GetImageDifference(Attribs, Diff);
EXPECT_EQ(Diff.NumDiffPixels, 3);
EXPECT_EQ(Diff.NumDiffPixelsAboveThreshold, 2);
EXPECT_EQ(Diff.MaxDiff, 5);
Expand Down

0 comments on commit c17267f

Please sign in to comment.