Skip to content

Commit

Permalink
Common: added image tools with GetImageDifference function
Browse files Browse the repository at this point in the history
  • Loading branch information
TheMostDiligent committed Jan 24, 2025
1 parent 4350541 commit 379a8b0
Show file tree
Hide file tree
Showing 7 changed files with 299 additions and 6 deletions.
2 changes: 2 additions & 0 deletions Common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ set(INTERFACE
interface/FixedBlockMemoryAllocator.hpp
interface/GeometryPrimitives.h
interface/HashUtils.hpp
interface/ImageTools.h
interface/LRUCache.hpp
interface/FixedLinearAllocator.hpp
interface/DynamicLinearAllocator.hpp
Expand Down Expand Up @@ -58,6 +59,7 @@ set(SOURCE
src/FileWrapper.cpp
src/FixedBlockMemoryAllocator.cpp
src/GeometryPrimitives.cpp
src/ImageTools.cpp
src/MemoryFileStream.cpp
src/Serializer.cpp
src/SpinLock.cpp
Expand Down
90 changes: 90 additions & 0 deletions Common/interface/ImageTools.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* Copyright 2025 Diligent Graphics LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* In no event and under no legal theory, whether in tort (including negligence),
* contract, or otherwise, unless required by applicable law (such as deliberate
* and grossly negligent acts) or agreed to in writing, shall any Contributor be
* liable for any damages, including any direct, indirect, special, incidental,
* or consequential damages of any character arising as a result of this License or
* out of the use or inability to use the software (including but not limited to damages
* for loss of goodwill, work stoppage, computer failure or malfunction, or any and
* all other commercial damages or losses), even if such Contributor has been advised
* of the possibility of such damages.
*/

#pragma once

/// \file
/// Image processing tools

#include "../../Primitives/interface/BasicTypes.h"


DILIGENT_BEGIN_NAMESPACE(Diligent)

#include "../../Primitives/interface/DefineRefMacro.h"

/// Image difference information
struct ImageDiffInfo
{
/// The number of pixels that differ
Uint32 NumDiffPixels DEFAULT_INITIALIZER(0);

/// The number of pixels that differ above the threshold
Uint32 NumDiffPixelsAboveThreshold DEFAULT_INITIALIZER(0);

/// The maximum difference between any two pixels
Uint32 MaxDiff DEFAULT_INITIALIZER(0);

/// The average difference between all pixels, not counting pixels that are equal
float AvgDiff DEFAULT_INITIALIZER(0);

/// The root mean square difference between all pixels, not counting pixels that are equal
float RmsDiff DEFAULT_INITIALIZER(0);
};
typedef struct ImageDiffInfo ImageDiffInfo;

/// 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
/// \return The image difference information, see Diligent::ImageDiffInfo.
///
/// \remarks The difference between two pixels is calculated as the maximum of the
/// absolute differences of all channels. The average difference is the
/// average of all differences, not counting pixels that are equal.
/// 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);

#include "../../Primitives/interface/UndefRefMacro.h"

DILIGENT_END_NAMESPACE // namespace Diligent
121 changes: 121 additions & 0 deletions Common/src/ImageTools.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/*
* Copyright 2025 Diligent Graphics LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* In no event and under no legal theory, whether in tort (including negligence),
* contract, or otherwise, unless required by applicable law (such as deliberate
* and grossly negligent acts) or agreed to in writing, shall any Contributor be
* liable for any damages, including any direct, indirect, special, incidental,
* or consequential damages of any character arising as a result of this License or
* out of the use or inability to use the software (including but not limited to damages
* for loss of goodwill, work stoppage, computer failure or malfunction, or any and
* all other commercial damages or losses), even if such Contributor has been advised
* of the possibility of such damages.
*/

#include "ImageTools.h"

#include <algorithm>
#include <cmath>

#include "DebugUtilities.hpp"

namespace Diligent
{

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

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

if (Stride1 < Width * NumChannels)
{
UNEXPECTED("Stride1 is too small. It must be at least ", Width * NumChannels, " bytes long.");
return;
}

if (Stride2 < Width * NumChannels)
{
UNEXPECTED("Stride2 is too small. It must be at least ", Width * NumChannels, " bytes long.");
return;
}

for (Uint32 row = 0; row < Height; ++row)
{
const Uint8* pRow1 = reinterpret_cast<const Uint8*>(pImage1) + row * Stride1;
const Uint8* pRow2 = reinterpret_cast<const Uint8*>(pImage2) + row * Stride2;

for (Uint32 col = 0; col < 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])));
PixelDiff = std::max(PixelDiff, ChannelDiff);
}

if (PixelDiff != 0)
{
++Diff.NumDiffPixels;
Diff.AvgDiff += static_cast<float>(PixelDiff);
Diff.RmsDiff += static_cast<float>(PixelDiff * PixelDiff);
Diff.MaxDiff = std::max(Diff.MaxDiff, PixelDiff);

if (PixelDiff > Threshold)
{
++Diff.NumDiffPixelsAboveThreshold;
}
}
}
}

if (Diff.NumDiffPixels > 0)
{
Diff.AvgDiff /= static_cast<float>(Diff.NumDiffPixels);
Diff.RmsDiff = std::sqrt(Diff.RmsDiff / static_cast<float>(Diff.NumDiffPixels));
}
}

} // namespace Diligent

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)
{
Diligent::GetImageDifference(Width, Height, NumChannels, pImage1, Stride1, pImage2, Stride2, Threshold, ImageDiff);
}
}
4 changes: 2 additions & 2 deletions Graphics/GraphicsAccessories/src/GraphicsAccessories.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2019-2024 Diligent Graphics LLC
* Copyright 2019-2025 Diligent Graphics LLC
* Copyright 2015-2019 Egor Yusov
*
* Licensed under the Apache License, Version 2.0 (the "License");
Expand Down Expand Up @@ -1884,7 +1884,7 @@ BIND_FLAGS SwapChainUsageFlagsToBindFlags(SWAP_CHAIN_USAGE_FLAGS SwapChainUsage)
break;

default:
UNEXPECTED("Unexpeced swap chain usage flag");
UNEXPECTED("Unexpected swap chain usage flag");
}
}
return BindFlags;
Expand Down
80 changes: 80 additions & 0 deletions Tests/DiligentCoreTest/src/Common/ImageToolsTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Copyright 2025 Diligent Graphics LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* In no event and under no legal theory, whether in tort (including negligence),
* contract, or otherwise, unless required by applicable law (such as deliberate
* and grossly negligent acts) or agreed to in writing, shall any Contributor be
* liable for any damages, including any direct, indirect, special, incidental,
* or consequential damages of any character arising as a result of this License or
* out of the use or inability to use the software (including but not limited to damages
* for loss of goodwill, work stoppage, computer failure or malfunction, or any and
* all other commercial damages or losses), even if such Contributor has been advised
* of the possibility of such damages.
*/

#include "ImageTools.h"

#include <cmath>

#include "gtest/gtest.h"

using namespace Diligent;

namespace
{

TEST(Common_ImageTools, GetImageDifference)
{
constexpr Uint32 Width = 3;
constexpr Uint32 Height = 2;
constexpr Uint32 Stride1 = 11;
constexpr Uint32 Stride2 = 12;
// clang-format off
constexpr char Image1[Stride1 * Height] = {
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20,
9, 8, 7, 5, 6, 4, 3, 2, 1, 30, 40,
};
constexpr char Image2[Stride2 * Height] = {
1, 2, 3, 5, 8, 8, 7, 8, 9, 10, 20, 30,
// ^ ^ ^
// -1 -3 -2
6, 4, 2, 5, 6, 4, 7, 6, 1, 40, 50, 60,
// ^ ^ ^ ^ ^
// 3 4 5 4 4
};
// clang-format on

{
ImageDiffInfo Diff;
GetImageDifference(Width, Height, 3, Image1, Stride1, Image1, Stride1, 3, Diff);
EXPECT_EQ(Diff.NumDiffPixels, 0);
EXPECT_EQ(Diff.NumDiffPixelsAboveThreshold, 0);
EXPECT_EQ(Diff.MaxDiff, 0);
EXPECT_EQ(Diff.AvgDiff, 0.f);
EXPECT_EQ(Diff.RmsDiff, 0.f);
}

{
ImageDiffInfo Diff;
GetImageDifference(Width, Height, 3, Image1, Stride1, Image2, Stride2, 3, 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));
}
}

} // namespace
4 changes: 2 additions & 2 deletions Tests/DiligentCoreTest/src/Common/ParsingToolsTest.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2019-2024 Diligent Graphics LLC
* Copyright 2019-2025 Diligent Graphics LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -345,7 +345,7 @@ TEST(Common_ParsingTools, SkipString)
Test("abc", "ab", true);
Test("abc", "ad", false);
Test("abc", "abc", true);
Test("abc", "abd", false);
Test("abc", "abx", false);
Test("abc", "abcd", false);
}

Expand Down
4 changes: 2 additions & 2 deletions Tests/DiligentCoreTest/src/Common/StringToolsTest.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2019-2022 Diligent Graphics LLC
* Copyright 2019-2025 Diligent Graphics LLC
* Copyright 2015-2019 Egor Yusov
*
* Licensed under the Apache License, Version 2.0 (the "License");
Expand Down Expand Up @@ -41,7 +41,7 @@ TEST(Common_StringTools, StreqSuff)
EXPECT_TRUE(!StreqSuff("ab", "abc", "_def"));
EXPECT_TRUE(!StreqSuff("abc_de", "abc", "_def"));
EXPECT_TRUE(!StreqSuff("abc_def", "ab", "_def"));
EXPECT_TRUE(!StreqSuff("abc_def", "abd", "_def"));
EXPECT_TRUE(!StreqSuff("abc_def", "abx", "_def"));
EXPECT_TRUE(!StreqSuff("abc_def", "abc", "_de"));
EXPECT_TRUE(!StreqSuff("abc", "abc", "_def"));
EXPECT_TRUE(!StreqSuff("abc_def", "", "_def"));
Expand Down

0 comments on commit 379a8b0

Please sign in to comment.