From 176f29bf275faa63fd6ed755af5db03d5b74697f Mon Sep 17 00:00:00 2001 From: Maksims Svecovs Date: Thu, 18 Jan 2024 21:37:42 +0000 Subject: [PATCH] [test] RGB and Gray conversion testing Signed-off-by: Maksims Svecovs --- intrinsiccv/include/intrinsiccv.h | 176 ++++++++++++++++++++++++++++++ test/api/test_rgb_and_gray.cpp | 175 +++++++++++++++++++++++++++++ 2 files changed, 351 insertions(+) create mode 100644 test/api/test_rgb_and_gray.cpp diff --git a/intrinsiccv/include/intrinsiccv.h b/intrinsiccv/include/intrinsiccv.h index 70ed2cf3c..10b29c61b 100644 --- a/intrinsiccv/include/intrinsiccv.h +++ b/intrinsiccv/include/intrinsiccv.h @@ -121,42 +121,218 @@ INTRINSICCV_BINARY_OP(saturating_multiply_s32, int32_t, double); INTRINSICCV_BINARY_OP(add_abs_with_threshold, int16_t, int16_t); +/// Converts a grayscale image to RGB. All channels are 8-bit wide. +/// +/// Destination data is filled as follows: R = G = B = Gray +/// resulting in | R,G,B | R,G,B | R,G,B | ... image +/// where each letter represents one byte of data, and one pixel is represented +/// by 3 bytes. There is no padding between the pixels. +/// +/// @param src Pointer to the first source data. Must be non-null. +/// @param src_stride Distance in bytes between the row first elements for +/// the first source data. Must not be less than +/// width * sizeof(uint8). +/// @param dst Pointer to the destination data. Must be non-null. +/// @param dst_stride Distance in bytes between the row first elements for +/// the destination data. Must not be less than +/// width * sizeof(uint8). +/// @param width How many elements are in a row. +/// @param height How many rows are in the data. +/// void INTRINSICCV_C_API(gray_to_rgb_u8)(const uint8_t *src, size_t src_stride, uint8_t *dst, size_t dst_stride, size_t width, size_t height); +/// Converts a grayscale image to RGBA. All channels are 8-bit wide. +/// +/// Destination data is filled as follows: R = G = B = Gray, A = 0xFF +/// resulting in | R,G,B,A | R,G,B,A | R,G,B,A | ... image +/// where each letter represents one byte of data, and one pixel is represented +/// by 4 bytes. There is no padding between the pixels. +/// +/// @param src Pointer to the first source data. Must be non-null. +/// @param src_stride Distance in bytes between the row first elements for +/// the first source data. Must not be less than +/// width * sizeof(uint8). +/// @param dst Pointer to the destination data. Must be non-null. +/// @param dst_stride Distance in bytes between the row first elements for +/// the destination data. Must not be less than +/// width * sizeof(uint8). +/// @param width How many elements are in a row. +/// @param height How many rows are in the data. +/// void INTRINSICCV_C_API(gray_to_rgba_u8)(const uint8_t *src, size_t src_stride, uint8_t *dst, size_t dst_stride, size_t width, size_t height); +/// Converts an RGB image to BGR. All channels are 8-bit wide. +/// +/// Destination data is filled as follows: +/// | B,G,R | B,G,R | B,G,R | ... +/// Each letter represents one byte of data, and one pixel is represented +/// by 3 bytes. There is no padding between the pixels. +/// +/// @param src Pointer to the first source data. Must be non-null. +/// @param src_stride Distance in bytes between the row first elements for +/// the first source data. Must not be less than +/// width * sizeof(uint8). +/// @param dst Pointer to the destination data. Must be non-null. +/// @param dst_stride Distance in bytes between the row first elements for +/// the destination data. Must not be less than +/// width * sizeof(uint8). +/// @param width How many elements are in a row. +/// @param height How many rows are in the data. +/// void INTRINSICCV_C_API(rgb_to_bgr_u8)(const uint8_t *src, size_t src_stride, uint8_t *dst, size_t dst_stride, size_t width, size_t height); +/// Copies a source RBG image to destination buffer. +/// All channels are 8-bit wide. +/// +/// @param src Pointer to the first source data. Must be non-null. +/// @param src_stride Distance in bytes between the row first elements for +/// the first source data. Must not be less than +/// width * sizeof(uint8). +/// @param dst Pointer to the destination data. Must be non-null. +/// @param dst_stride Distance in bytes between the row first elements for +/// the destination data. Must not be less than +/// width * sizeof(uint8). +/// @param width How many elements are in a row. +/// @param height How many rows are in the data. +/// void INTRINSICCV_C_API(rgb_to_rgb_u8)(const uint8_t *src, size_t src_stride, uint8_t *dst, size_t dst_stride, size_t width, size_t height); +/// Converts an RGBA image to BGRA. All channels are 8-bit wide. +/// +/// Destination data is filled as follows: +/// | B,G,R,A | B,G,R,A | B,G,R,A | ... +/// Each letter represents one byte of data, and one pixel is represented +/// by 4 bytes. There is no padding between the pixels. +/// +/// @param src Pointer to the first source data. Must be non-null. +/// @param src_stride Distance in bytes between the row first elements for +/// the first source data. Must not be less than +/// width * sizeof(uint8). +/// @param dst Pointer to the destination data. Must be non-null. +/// @param dst_stride Distance in bytes between the row first elements for +/// the destination data. Must not be less than +/// width * sizeof(uint8). +/// @param width How many elements are in a row. +/// @param height How many rows are in the data. +/// void INTRINSICCV_C_API(rgba_to_bgra_u8)(const uint8_t *src, size_t src_stride, uint8_t *dst, size_t dst_stride, size_t width, size_t height); +/// Copies a source RBGA image to destination buffer. +/// All channels are 8-bit wide. +/// +/// @param src Pointer to the first source data. Must be non-null. +/// @param src_stride Distance in bytes between the row first elements for +/// the first source data. Must not be less than +/// width * sizeof(uint8). +/// @param dst Pointer to the destination data. Must be non-null. +/// @param dst_stride Distance in bytes between the row first elements for +/// the destination data. Must not be less than +/// width * sizeof(uint8). +/// @param width How many elements are in a row. +/// @param height How many rows are in the data. +/// void INTRINSICCV_C_API(rgba_to_rgba_u8)(const uint8_t *src, size_t src_stride, uint8_t *dst, size_t dst_stride, size_t width, size_t height); +/// Converts an RGB image to BGRA. All channels are 8-bit wide. +/// +/// Corresponding colours are set while Alpha channel is set to 0xFF. +/// Destination data is filled as follows: +/// | B,G,R,A | B,G,R,A | B,G,R,A | ... +/// Each letter represents one byte of data, and one pixel is represented +/// by 4 bytes. There is no padding between the pixels. +/// +/// @param src Pointer to the first source data. Must be non-null. +/// @param src_stride Distance in bytes between the row first elements for +/// the first source data. Must not be less than +/// width * sizeof(uint8). +/// @param dst Pointer to the destination data. Must be non-null. +/// @param dst_stride Distance in bytes between the row first elements for +/// the destination data. Must not be less than +/// width * sizeof(uint8). +/// @param width How many elements are in a row. +/// @param height How many rows are in the data. +/// void INTRINSICCV_C_API(rgb_to_bgra_u8)(const uint8_t *src, size_t src_stride, uint8_t *dst, size_t dst_stride, size_t width, size_t height); +/// Converts an RGB image to RGBA. All channels are 8-bit wide. +/// +/// Corresponding colours are set while Alpha channel is set to 0xFF. +/// Destination data is filled as follows: +/// | R,G,B,A | R,G,B,A | R,G,B,A | ... +/// Each letter represents one byte of data, and one pixel is represented +/// by 4 bytes. There is no padding between the pixels. +/// +/// @param src Pointer to the first source data. Must be non-null. +/// @param src_stride Distance in bytes between the row first elements for +/// the first source data. Must not be less than +/// width * sizeof(uint8). +/// @param dst Pointer to the destination data. Must be non-null. +/// @param dst_stride Distance in bytes between the row first elements for +/// the destination data. Must not be less than +/// width * sizeof(uint8). +/// @param width How many elements are in a row. +/// @param height How many rows are in the data. +/// void INTRINSICCV_C_API(rgb_to_rgba_u8)(const uint8_t *src, size_t src_stride, uint8_t *dst, size_t dst_stride, size_t width, size_t height); +/// Converts an RGBA image to BGR. All channels are 8-bit wide. +/// +/// Corresponding colours are set while Alpha channel is discarded. +/// Destination data is filled as follows: +/// | B,G,R | B,G,R | B,G,R | ... +/// Each letter represents one byte of data, and one pixel is represented +/// by 3 bytes. There is no padding between the pixels. +/// +/// @param src Pointer to the first source data. Must be non-null. +/// @param src_stride Distance in bytes between the row first elements for +/// the first source data. Must not be less than +/// width * sizeof(uint8). +/// @param dst Pointer to the destination data. Must be non-null. +/// @param dst_stride Distance in bytes between the row first elements for +/// the destination data. Must not be less than +/// width * sizeof(uint8). +/// @param width How many elements are in a row. +/// @param height How many rows are in the data. +/// void INTRINSICCV_C_API(rgba_to_bgr_u8)(const uint8_t *src, size_t src_stride, uint8_t *dst, size_t dst_stride, size_t width, size_t height); +/// Converts an RGBA image to RGB. All channels are 8-bit wide. +/// +/// Corresponding colours are set while Alpha channel is discarded. +/// Destination data is filled as follows: +/// | R,G,B | R,G,B | R,G,B | ... +/// Each letter represents one byte of data, and one pixel is represented +/// by 3 bytes. There is no padding between the pixels. +/// +/// @param src Pointer to the first source data. Must be non-null. +/// @param src_stride Distance in bytes between the row first elements for +/// the first source data. Must not be less than +/// width * sizeof(uint8). +/// @param dst Pointer to the destination data. Must be non-null. +/// @param dst_stride Distance in bytes between the row first elements for +/// the destination data. Must not be less than +/// width * sizeof(uint8). +/// @param width How many elements are in a row. +/// @param height How many rows are in the data. +/// void INTRINSICCV_C_API(rgba_to_rgb_u8)(const uint8_t *src, size_t src_stride, uint8_t *dst, size_t dst_stride, size_t width, size_t height); diff --git a/test/api/test_rgb_and_gray.cpp b/test/api/test_rgb_and_gray.cpp new file mode 100644 index 000000000..6395cf3d0 --- /dev/null +++ b/test/api/test_rgb_and_gray.cpp @@ -0,0 +1,175 @@ +// SPDX-FileCopyrightText: 2024 Arm Limited and/or its affiliates +// +// SPDX-License-Identifier: Apache-2.0 + +#include +#include + +#include "framework/array.h" +#include "framework/utils.h" + +class GrayTest final { + public: + GrayTest(bool hasAlpha) : hasAlpha_(hasAlpha) { + outChannels_ = hasAlpha_ ? 4 : 3; + } + + template + void execute_test(F impl) { + // Set width to be more than vector length, but not quite a multiple of + // vector length to force both vector and scalar paths + size_t logical_width = 3 * test::Options::vector_lanes() - 1; + test::Array2D source{logical_width, 3}; + test::Array2D actual{logical_width * outChannels_, 3}; + test::Array2D expected{logical_width * outChannels_, 3}; + + source.set(0, 0, {1}); + source.set(1, 0, {0xFF}); + source.set(2, 0, {10, 15, 1}); + + calculate_expected(source, expected); + + impl(source.data(), source.stride(), actual.data(), actual.stride(), + logical_width, actual.height()); + + EXPECT_EQ_ARRAY2D(actual, expected); + } + + private: + void calculate_expected(test::Array2D &src, + test::Array2D &expected) { + for (size_t vindex = 0; vindex < expected.height(); vindex++) { + for (size_t hindex = 0; hindex < expected.width() / outChannels_; + hindex++) { + // NOLINTBEGIN(clang-analyzer-core.uninitialized.Assign) + uint8_t y = *src.at(vindex, hindex); + // NOLINTEND(clang-analyzer-core.uninitialized.Assign) + + if (hasAlpha_) { + expected.set(vindex, hindex * outChannels_, {y, y, y, 0xff}); + } else { + expected.set(vindex, hindex * outChannels_, {y, y, y}); + } + } + } + } + + bool hasAlpha_; + size_t outChannels_; +}; + +class ColourTest final { + public: + ColourTest(size_t src_channels, size_t dst_channels, bool swapBlue) + : inChannels_(src_channels), + outChannels_(dst_channels), + swapBlue_(swapBlue) {} + + template + void execute_test(F impl) { + // Set width to be more than vector length, but not quite a multiple of + // vector length to force both vector and scalar paths + size_t logical_width = 3 * test::Options::vector_lanes() - 1; + test::Array2D source{logical_width * inChannels_, 3}; + test::Array2D actual{logical_width * outChannels_, 3}; + test::Array2D expected{logical_width * outChannels_, 3}; + + source.set(0, 0, {123, 230, 11, 203}); + source.set(1, 0, {0xFF, 0xFF, 0xFF, 0}); + source.set(2, 0, {0, 0, 0, 0xFF}); + + calculate_expected(source, expected); + + impl(source.data(), source.stride(), actual.data(), actual.stride(), + logical_width, actual.height()); + + EXPECT_EQ_ARRAY2D(actual, expected); + } + + private: + void calculate_expected(test::Array2D &src, + test::Array2D &expected) { + for (size_t vindex = 0; vindex < expected.height(); vindex++) { + for (size_t hindex = 0; hindex < expected.width() / outChannels_; + hindex++) { + // NOLINTBEGIN(clang-analyzer-core.uninitialized.Assign) + uint8_t r = *src.at(vindex, hindex * inChannels_); + uint8_t g = *src.at(vindex, hindex * inChannels_ + 1); + uint8_t b = *src.at(vindex, hindex * inChannels_ + 2); + // NOLINTEND(clang-analyzer-core.uninitialized.Assign) + uint8_t a = 0xff; + if (inChannels_ == 4) { + a = *src.at(vindex, hindex * inChannels_ + 3); + } + + if (outChannels_ == 4) { + if (swapBlue_) { + expected.set(vindex, hindex * outChannels_, {b, g, r, a}); + } else { + expected.set(vindex, hindex * outChannels_, {r, g, b, a}); + } + } else { + if (swapBlue_) { + expected.set(vindex, hindex * outChannels_, {b, g, r}); + } else { + expected.set(vindex, hindex * outChannels_, {r, g, b}); + } + } + } + } + } + + size_t inChannels_; + size_t outChannels_; + bool swapBlue_; +}; + +TEST(GRAY2, RGB) { + GrayTest gray_test(false); + gray_test.execute_test(intrinsiccv_gray_to_rgb_u8); +} + +TEST(GRAY2, RGBA) { + GrayTest gray_test(true); + gray_test.execute_test(intrinsiccv_gray_to_rgba_u8); +} + +TEST(RGB2, RGB) { + ColourTest colour_test(3, 3, false); + colour_test.execute_test(intrinsiccv_rgb_to_rgb_u8); +} + +TEST(RGBA2, RGBA) { + ColourTest colour_test(4, 4, false); + colour_test.execute_test(intrinsiccv_rgba_to_rgba_u8); +} + +TEST(RGB2, BGR) { + ColourTest colour_test(3, 3, true); + colour_test.execute_test(intrinsiccv_rgb_to_bgr_u8); +} + +TEST(RGBA2, BGRA) { + ColourTest colour_test(4, 4, true); + colour_test.execute_test(intrinsiccv_rgba_to_bgra_u8); +} + +TEST(RGB2, BGRA) { + ColourTest colour_test(3, 4, true); + colour_test.execute_test(intrinsiccv_rgb_to_bgra_u8); +} + +TEST(RGB2, RGBA) { + ColourTest colour_test(3, 4, false); + colour_test.execute_test(intrinsiccv_rgb_to_rgba_u8); +} + +TEST(RGBA2, BGR) { + ColourTest colour_test(4, 3, true); + colour_test.execute_test(intrinsiccv_rgba_to_bgr_u8); +} + +TEST(RGBA2, RGB) { + ColourTest colour_test(4, 3, false); + colour_test.execute_test(intrinsiccv_rgba_to_rgb_u8); +} -- GitLab