ImageComparisonTests.cpp 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <AzCore/UnitTest/TestTypes.h>
  9. #include <Atom/Utils/ImageComparison.h>
  10. namespace UnitTest
  11. {
  12. using namespace AZ::Utils;
  13. class ImageComparisonTests
  14. : public LeakDetectionFixture
  15. {
  16. protected:
  17. static const AZ::RHI::Format DefaultFormat = AZ::RHI::Format::R8G8B8A8_UNORM;
  18. static constexpr size_t BytesPerPixel = 4;
  19. AZStd::vector<uint8_t> CreateTestRGBAImageData(AZ::RHI::Size size)
  20. {
  21. AZStd::vector<uint8_t> buffer;
  22. const size_t bufferSize = BytesPerPixel * size.m_width * size.m_height;
  23. buffer.reserve(bufferSize);
  24. for (uint32_t i = 0; i < bufferSize; ++i)
  25. {
  26. buffer.push_back(aznumeric_cast<uint8_t>(i % 255));
  27. }
  28. return buffer;
  29. }
  30. void SetPixel(AZStd::vector<uint8_t>& image, size_t pixelIndex, uint8_t r, uint8_t g, uint8_t b, uint8_t a = 255)
  31. {
  32. image[pixelIndex * BytesPerPixel + 0] = r;
  33. image[pixelIndex * BytesPerPixel + 1] = g;
  34. image[pixelIndex * BytesPerPixel + 2] = b;
  35. image[pixelIndex * BytesPerPixel + 3] = a;
  36. };
  37. };
  38. TEST_F(ImageComparisonTests, Error_ImageSizesDontMatch)
  39. {
  40. AZ::RHI::Size sizeA{1, 1, 1};
  41. AZ::RHI::Size sizeB{1, 2, 1};
  42. auto outcome = CalcImageDiffRms(
  43. CreateTestRGBAImageData(sizeA), sizeA, DefaultFormat,
  44. CreateTestRGBAImageData(sizeB), sizeB, DefaultFormat);
  45. EXPECT_FALSE(outcome.IsSuccess());
  46. }
  47. TEST_F(ImageComparisonTests, Error_BufferSizeDoesntMatchImageSize)
  48. {
  49. AZ::RHI::Size size{1, 1, 1};
  50. AZ::RHI::Size wrongSize{1, 2, 1};
  51. auto outcome = CalcImageDiffRms(
  52. CreateTestRGBAImageData(size), wrongSize, DefaultFormat,
  53. CreateTestRGBAImageData(size), wrongSize, DefaultFormat);
  54. EXPECT_FALSE(outcome.IsSuccess());
  55. }
  56. TEST_F(ImageComparisonTests, Error_UnsupportedFormat)
  57. {
  58. AZ::RHI::Format format = AZ::RHI::Format::G8R8_G8B8_UNORM;
  59. AZ::RHI::Size size{1, 1, 1};
  60. auto outcome = CalcImageDiffRms(
  61. CreateTestRGBAImageData(size), size, format,
  62. CreateTestRGBAImageData(size), size, format);
  63. EXPECT_FALSE(outcome.IsSuccess());
  64. }
  65. TEST_F(ImageComparisonTests, Error_FormatsDontMatch)
  66. {
  67. AZ::RHI::Size size{1, 1, 1};
  68. auto outcome = CalcImageDiffRms(
  69. CreateTestRGBAImageData(size), size, AZ::RHI::Format::R8G8B8A8_SNORM,
  70. CreateTestRGBAImageData(size), size, AZ::RHI::Format::R8G8B8A8_UNORM);
  71. EXPECT_FALSE(outcome.IsSuccess());
  72. }
  73. TEST_F(ImageComparisonTests, CheckThreshold_SmallIdenticalImages)
  74. {
  75. AZ::RHI::Size size{16, 9, 1};
  76. auto outcome = CalcImageDiffRms(
  77. CreateTestRGBAImageData(size), size, DefaultFormat,
  78. CreateTestRGBAImageData(size), size, DefaultFormat);
  79. EXPECT_TRUE(outcome.IsSuccess());
  80. EXPECT_EQ(0.0f, outcome.GetValue().m_diffScore);
  81. }
  82. TEST_F(ImageComparisonTests, CheckThreshold_LargeIdenticalImages)
  83. {
  84. AZ::RHI::Size size{1620, 1080, 1};
  85. AZStd::vector<uint8_t> imageA = CreateTestRGBAImageData(size);
  86. AZStd::vector<uint8_t> imageB = imageA;
  87. auto outcome = CalcImageDiffRms(
  88. imageA, size, DefaultFormat,
  89. imageB, size, DefaultFormat);
  90. EXPECT_TRUE(outcome.IsSuccess());
  91. EXPECT_EQ(0.0f, outcome.GetValue().m_diffScore);
  92. }
  93. TEST_F(ImageComparisonTests, CheckMaxChannelDifference_R)
  94. {
  95. const AZStd::vector<uint8_t> imageA = { 255, 255, 255, 255 };
  96. const AZStd::vector<uint8_t> imageB = { 0, 125, 255, 255 };
  97. const int16_t maxChannelDiff = 255;
  98. const int16_t res = CalcMaxChannelDifference(imageA, imageB, 0);
  99. EXPECT_EQ(res, maxChannelDiff);
  100. }
  101. TEST_F(ImageComparisonTests, CheckMaxChannelDifference_G)
  102. {
  103. const AZStd::vector<uint8_t> imageA = { 255, 255, 255, 255 };
  104. const AZStd::vector<uint8_t> imageB = { 250, 125, 255, 255 };
  105. const int16_t maxChannelDiff = 130;
  106. const int16_t res = CalcMaxChannelDifference(imageA, imageB, 0);
  107. EXPECT_EQ(res, maxChannelDiff);
  108. }
  109. TEST_F(ImageComparisonTests, CheckMaxChannelDifference_B)
  110. {
  111. const AZStd::vector<uint8_t> imageA = { 255, 255, 255, 255 };
  112. const AZStd::vector<uint8_t> imageB = { 250, 125, 100, 255 };
  113. const int16_t maxChannelDiff = 155;
  114. const int16_t res = CalcMaxChannelDifference(imageA, imageB, 0);
  115. EXPECT_EQ(res, maxChannelDiff);
  116. }
  117. TEST_F(ImageComparisonTests, CheckMaxChannelDifference_A)
  118. {
  119. const AZStd::vector<uint8_t> imageA = { 0, 0, 0, 255 };
  120. const AZStd::vector<uint8_t> imageB = { 0, 1, 2, 0 };
  121. const int16_t maxChannelDiff = 255;
  122. const int16_t res = CalcMaxChannelDifference(imageA, imageB, 0);
  123. EXPECT_EQ(res, maxChannelDiff);
  124. }
  125. TEST_F(ImageComparisonTests, CheckThreshold_SmallImagesWithDifferences)
  126. {
  127. AZ::RHI::Size size{2, 2, 1};
  128. AZStd::vector<uint8_t> imageA = CreateTestRGBAImageData(size);
  129. AZStd::vector<uint8_t> imageB = CreateTestRGBAImageData(size);
  130. // Difference of 1 (R)
  131. SetPixel(imageA, 0, 100, 200, 5);
  132. SetPixel(imageB, 0, 101, 200, 5);
  133. // Difference of 2 (G)
  134. SetPixel(imageA, 1, 255, 255, 255);
  135. SetPixel(imageB, 1, 255, 253, 255);
  136. // Difference of 5 (B)
  137. SetPixel(imageA, 2, 0, 0, 0);
  138. SetPixel(imageB, 2, 0, 0, 5);
  139. // Difference of 100 (RGB all different)
  140. SetPixel(imageA, 3, 100, 100, 100);
  141. SetPixel(imageB, 3, 101, 102, 0);
  142. auto outcome = CalcImageDiffRms(
  143. imageA, size, DefaultFormat,
  144. imageB, size, DefaultFormat);
  145. EXPECT_TRUE(outcome.IsSuccess());
  146. // Result should be:
  147. // sqrt( (1^2 + 2^2 + 5^2 + 100^2) / (255.0^2) / 4 )
  148. EXPECT_FLOAT_EQ(0.19637232876f, outcome.GetValue().m_diffScore);
  149. }
  150. TEST_F(ImageComparisonTests, CheckThreshold_SmallImagesWithAlphaDifference)
  151. {
  152. AZ::RHI::Size size{2, 2, 1};
  153. AZStd::vector<uint8_t> imageA = CreateTestRGBAImageData(size);
  154. AZStd::vector<uint8_t> imageB = CreateTestRGBAImageData(size);
  155. // Difference of 1 (R)
  156. SetPixel(imageA, 0, 100, 200, 5);
  157. SetPixel(imageB, 0, 101, 200, 5);
  158. // Difference of 2 (G)
  159. SetPixel(imageA, 1, 255, 255, 255);
  160. SetPixel(imageB, 1, 255, 253, 255);
  161. // Difference of 5 (B)
  162. SetPixel(imageA, 2, 0, 0, 0);
  163. SetPixel(imageB, 2, 0, 0, 5);
  164. // Difference of 100 in the alpha channel
  165. SetPixel(imageA, 3, 0, 0, 0, 100);
  166. SetPixel(imageB, 3, 0, 0, 0, 0);
  167. auto outcome = CalcImageDiffRms(
  168. imageA, size, DefaultFormat,
  169. imageB, size, DefaultFormat);
  170. EXPECT_TRUE(outcome.IsSuccess());
  171. // Result should be:
  172. // sqrt( (1^2 + 2^2 + 5^2 + 100^2) / (255.0^2) / 4 )
  173. EXPECT_FLOAT_EQ(0.19637232876f, outcome.GetValue().m_diffScore);
  174. }
  175. TEST_F(ImageComparisonTests, CheckThreshold_IgnoreImperceptibleDifferences)
  176. {
  177. AZ::RHI::Size size{2, 2, 1};
  178. AZStd::vector<uint8_t> imageA = CreateTestRGBAImageData(size);
  179. AZStd::vector<uint8_t> imageB = CreateTestRGBAImageData(size);
  180. // Difference of 1 (R)
  181. SetPixel(imageA, 0, 100, 200, 5);
  182. SetPixel(imageB, 0, 101, 200, 5);
  183. // Difference of 2 (G)
  184. SetPixel(imageA, 1, 255, 255, 255);
  185. SetPixel(imageB, 1, 255, 253, 255);
  186. // Difference of 5 (B)
  187. SetPixel(imageA, 2, 0, 0, 0);
  188. SetPixel(imageB, 2, 0, 0, 5);
  189. // Difference of 4 (RGB all different)
  190. SetPixel(imageA, 3, 100, 100, 100);
  191. SetPixel(imageB, 3, 101, 102, 96);
  192. const float minDiffFilter = 3.9f / 255.0f;
  193. auto outcome = CalcImageDiffRms(
  194. imageA, size, DefaultFormat,
  195. imageB, size, DefaultFormat,
  196. minDiffFilter);
  197. EXPECT_TRUE(outcome.IsSuccess());
  198. // Result should be:
  199. // sqrt( (1^2 + 2^2 + 5^2 + 4^2) / (255.0^2) / 4 )
  200. EXPECT_FLOAT_EQ(0.01329868624, outcome.GetValue().m_diffScore);
  201. // Result should be:
  202. // sqrt( (5^2 + 4^2) / (255.0^2) / 4 )
  203. EXPECT_FLOAT_EQ(0.01255514556f, outcome.GetValue().m_filteredDiffScore);
  204. }
  205. }