From 0d558b15b2493915973946c6ec216b6e56ae7899 Mon Sep 17 00:00:00 2001 From: Ioana Ghiban Date: Tue, 20 Feb 2024 16:31:58 +0200 Subject: [PATCH 1/6] [test] Add tests for gaussian_blur Small fix applied when checking the buffer type size of the context given to the gaussian_blur operation. --- intrinsiccv/include/intrinsiccv/intrinsiccv.h | 4 +- .../src/filters/gaussian_blur_neon.cpp | 2 +- intrinsiccv/src/filters/gaussian_blur_sc.h | 2 +- test/api/test_gaussian_blur.cpp | 160 +++++++++++++++--- test/api/test_sobel.cpp | 8 +- test/framework/kernel.h | 8 +- test/framework/utils.cpp | 6 + test/framework/utils.h | 3 + 8 files changed, 157 insertions(+), 36 deletions(-) diff --git a/intrinsiccv/include/intrinsiccv/intrinsiccv.h b/intrinsiccv/include/intrinsiccv/intrinsiccv.h index e352f29ef..320d2f765 100644 --- a/intrinsiccv/include/intrinsiccv/intrinsiccv.h +++ b/intrinsiccv/include/intrinsiccv/intrinsiccv.h @@ -959,8 +959,8 @@ intrinsiccv_error_t intrinsiccv_canny_u8(const uint8_t *src, size_t src_stride, /// @param context Pointer where to return the created context's address. /// @param channels Number of channels in the data. Must be not more than /// @ref INTRINSICCV_MAXIMUM_CHANNEL_COUNT. -/// @param type_size Element size in bytes. Must not be more than -/// @ref INTRINSICCV_MAXIMUM_TYPE_SIZE. +/// @param type_size Size of buffer element in bytes. It must be double the +/// size of the type the filter operation is executed on. /// @param image Image dimensions. Its size must not be more than /// @ref INTRINSICCV_MAX_IMAGE_PIXELS. /// diff --git a/intrinsiccv/src/filters/gaussian_blur_neon.cpp b/intrinsiccv/src/filters/gaussian_blur_neon.cpp index 6622da370..21e3fac45 100644 --- a/intrinsiccv/src/filters/gaussian_blur_neon.cpp +++ b/intrinsiccv/src/filters/gaussian_blur_neon.cpp @@ -165,7 +165,7 @@ intrinsiccv_error_t discrete_gaussian_blur( auto *workspace = reinterpret_cast(context); - if (workspace->buffer_type_size() != sizeof(ScalarType)) { + if (workspace->buffer_type_size() != 2 * sizeof(ScalarType)) { return INTRINSICCV_ERROR_CONTEXT_MISMATCH; } diff --git a/intrinsiccv/src/filters/gaussian_blur_sc.h b/intrinsiccv/src/filters/gaussian_blur_sc.h index 62f6588c6..4911906cf 100644 --- a/intrinsiccv/src/filters/gaussian_blur_sc.h +++ b/intrinsiccv/src/filters/gaussian_blur_sc.h @@ -105,7 +105,7 @@ intrinsiccv_error_t discrete_gaussian_blur( auto *workspace = reinterpret_cast(context); - if (workspace->buffer_type_size() != sizeof(ScalarType)) { + if (workspace->buffer_type_size() != 2 * sizeof(ScalarType)) { return INTRINSICCV_ERROR_CONTEXT_MISMATCH; } diff --git a/test/api/test_gaussian_blur.cpp b/test/api/test_gaussian_blur.cpp index e8d2798b8..7959b8e57 100644 --- a/test/api/test_gaussian_blur.cpp +++ b/test/api/test_gaussian_blur.cpp @@ -4,7 +4,9 @@ #include -#include "framework/utils.h" +#include "framework/array.h" +#include "framework/generator.h" +#include "framework/kernel.h" #include "intrinsiccv/intrinsiccv.h" #define INTRINSICCV_GAUSSIAN_BLUR(type, kernel_suffix, type_suffix) \ @@ -15,17 +17,125 @@ INTRINSICCV_GAUSSIAN_BLUR(uint8_t, 3x3, u8); INTRINSICCV_GAUSSIAN_BLUR(uint8_t, 5x5, u8); +// Implements KernelTestParams for Gaussian Blur operators. +template +struct GaussianBlurKernelTestParams; + +template +struct GaussianBlurKernelTestParams { + using InputType = uint8_t; + using IntermediateType = uint16_t; + using OutputType = uint8_t; + + static constexpr size_t kKernelSize = KernelSize; +}; // end of struct GaussianBlurKernelTestParams + +static constexpr std::array kSupportedBorders = { + INTRINSICCV_BORDER_TYPE_REPLICATE, + // INTRINSICCV_BORDER_TYPE_REFLECT, + // INTRINSICCV_BORDER_TYPE_WRAP, + // INTRINSICCV_BORDER_TYPE_REVERSE, +}; + +// Test for GaussianBlur operator. +template +class GaussianBlurTest : public test::KernelTest { + using typename test::KernelTest::InputType; + using typename test::KernelTest::IntermediateType; + using typename test::KernelTest::OutputType; + + intrinsiccv_error_t call_api(const test::Array2D *input, + test::Array2D *output, + intrinsiccv_border_type_t border_type, + intrinsiccv_border_values_t) override { + auto api = KernelTestParams::kKernelSize == 3 + ? gaussian_blur_3x3() + : gaussian_blur_5x5(); + + intrinsiccv_filter_context_t *context = nullptr; + auto ret = intrinsiccv_filter_create( + &context, input->channels(), sizeof(IntermediateType), + intrinsiccv_rectangle_t{input->width() / input->channels(), + input->height()}); + if (ret != INTRINSICCV_OK) { + return ret; + } + + ret = api(input->data(), input->stride(), output->data(), output->stride(), + input->width() / input->channels(), input->height(), + input->channels(), border_type, context); + auto releaseRet = intrinsiccv_filter_release(context); + if (releaseRet != INTRINSICCV_OK) { + return releaseRet; + } + + return ret; + } + + // Apply rounding to nearest integer division. + IntermediateType scale_result(const test::Kernel &kernel, + IntermediateType result) override { + return kernel.width() == 3 ? ((result + 8) / 16) : ((result + 128) / 256); + } + + public: + void test(test::Array2D mask) { + test::Kernel kernel{mask}; + // Use the default array layouts for testing. + auto array_layouts = + test::default_array_layouts(mask.width(), mask.height()); + // Use the default border values for testing. + auto kSupportedBorderValues = test::default_border_values(); + // Create generators and execute test. + test::SequenceGenerator tested_array_layouts{array_layouts}; + test::SequenceGenerator tested_borders{kSupportedBorders}; + test::SequenceGenerator tested_border_values{kSupportedBorderValues}; + test::PseudoRandomNumberGenerator element_generator; + this->test::KernelTest::test( + kernel, tested_array_layouts, tested_borders, tested_border_values, + element_generator); + } +}; // end of class class GaussianBlur3x3Test + using ElementTypes = ::testing::Types; template -class GaussianBlurTest : public testing::Test {}; +class GaussianBlur : public testing::Test {}; -TYPED_TEST_SUITE(GaussianBlurTest, ElementTypes); +TYPED_TEST_SUITE(GaussianBlur, ElementTypes); -TYPED_TEST(GaussianBlurTest, UnsupportedBorderType) { +// Tests gaussian_blur_3x3_ API. +TYPED_TEST(GaussianBlur, 3x3) { + using KernelTestParams = GaussianBlurKernelTestParams; + // 3x3 GaussianBlur operator. + test::Array2D mask{3, 3}; + // clang-format off + mask.set(0, 0, { 1, 2, 1}); + mask.set(1, 0, { 2, 4, 2}); + mask.set(2, 0, { 1, 2, 1}); + // clang-format on + GaussianBlurTest{}.test(mask); +} + +// Tests gaussian_blur_5x5_ API. +TYPED_TEST(GaussianBlur, 5x5) { + using KernelTestParams = GaussianBlurKernelTestParams; + // 5x5 GaussianBlur operator. + test::Array2D mask{5, 5}; + // clang-format off + mask.set(0, 0, { 1, 4, 6, 4, 1}); + mask.set(1, 0, { 4, 16, 24, 16, 4}); + mask.set(2, 0, { 6, 24, 36, 24, 6}); + mask.set(3, 0, { 4, 16, 24, 16, 4}); + mask.set(4, 0, { 1, 4, 6, 4, 1}); + // clang-format on + GaussianBlurTest{}.test(mask); +} + +TYPED_TEST(GaussianBlur, UnsupportedBorderType) { intrinsiccv_filter_context_t *context = nullptr; ASSERT_EQ(INTRINSICCV_OK, - intrinsiccv_filter_create(&context, 1, sizeof(TypeParam), + intrinsiccv_filter_create(&context, 1, 2 * sizeof(TypeParam), intrinsiccv_rectangle_t{1, 1})); TypeParam src[1] = {}, dst[1]; for (intrinsiccv_border_type_t border : { @@ -45,54 +155,54 @@ TYPED_TEST(GaussianBlurTest, UnsupportedBorderType) { EXPECT_EQ(INTRINSICCV_OK, intrinsiccv_filter_release(context)); } -TYPED_TEST(GaussianBlurTest, NullPointer) { +TYPED_TEST(GaussianBlur, NullPointer) { intrinsiccv_filter_context_t *context = nullptr; ASSERT_EQ(INTRINSICCV_OK, - intrinsiccv_filter_create(&context, 1, sizeof(TypeParam), + intrinsiccv_filter_create(&context, 1, 2 * sizeof(TypeParam), intrinsiccv_rectangle_t{1, 1})); TypeParam src[1] = {}, dst[1]; test::test_null_args(gaussian_blur_3x3(), src, sizeof(TypeParam), dst, sizeof(TypeParam), 1, 1, 1, - INTRINSICCV_BORDER_TYPE_REFLECT, context); + INTRINSICCV_BORDER_TYPE_REPLICATE, context); test::test_null_args(gaussian_blur_5x5(), src, sizeof(TypeParam), dst, sizeof(TypeParam), 1, 1, 1, - INTRINSICCV_BORDER_TYPE_REFLECT, context); + INTRINSICCV_BORDER_TYPE_REPLICATE, context); EXPECT_EQ(INTRINSICCV_OK, intrinsiccv_filter_release(context)); } -TYPED_TEST(GaussianBlurTest, Misalignment) { +TYPED_TEST(GaussianBlur, Misalignment) { if (sizeof(TypeParam) == 1) { // misalignment impossible return; } intrinsiccv_filter_context_t *context = nullptr; ASSERT_EQ(INTRINSICCV_OK, - intrinsiccv_filter_create(&context, 1, sizeof(TypeParam), + intrinsiccv_filter_create(&context, 1, 2 * sizeof(TypeParam), intrinsiccv_rectangle_t{1, 1})); TypeParam src[1] = {}, dst[1]; EXPECT_EQ(INTRINSICCV_ERROR_ALIGNMENT, gaussian_blur_3x3()( src, sizeof(TypeParam) + 1, dst, sizeof(TypeParam), 1, 1, 1, - INTRINSICCV_BORDER_TYPE_REFLECT, context)); + INTRINSICCV_BORDER_TYPE_REPLICATE, context)); EXPECT_EQ(INTRINSICCV_ERROR_ALIGNMENT, gaussian_blur_3x3()( src, sizeof(TypeParam), dst, sizeof(TypeParam) + 1, 1, 1, 1, - INTRINSICCV_BORDER_TYPE_REFLECT, context)); + INTRINSICCV_BORDER_TYPE_REPLICATE, context)); EXPECT_EQ(INTRINSICCV_ERROR_ALIGNMENT, gaussian_blur_5x5()( src, sizeof(TypeParam) + 1, dst, sizeof(TypeParam), 1, 1, 1, - INTRINSICCV_BORDER_TYPE_REFLECT, context)); + INTRINSICCV_BORDER_TYPE_REPLICATE, context)); EXPECT_EQ(INTRINSICCV_ERROR_ALIGNMENT, gaussian_blur_5x5()( src, sizeof(TypeParam), dst, sizeof(TypeParam) + 1, 1, 1, 1, - INTRINSICCV_BORDER_TYPE_REFLECT, context)); + INTRINSICCV_BORDER_TYPE_REPLICATE, context)); EXPECT_EQ(INTRINSICCV_OK, intrinsiccv_filter_release(context)); } -TYPED_TEST(GaussianBlurTest, ImageSize) { +TYPED_TEST(GaussianBlur, ImageSize) { intrinsiccv_filter_context_t *context = nullptr; ASSERT_EQ(INTRINSICCV_OK, - intrinsiccv_filter_create(&context, 1, sizeof(TypeParam), + intrinsiccv_filter_create(&context, 1, 2 * sizeof(TypeParam), intrinsiccv_rectangle_t{1, 1})); TypeParam src[1], dst[1]; EXPECT_EQ(INTRINSICCV_ERROR_RANGE, @@ -118,10 +228,10 @@ TYPED_TEST(GaussianBlurTest, ImageSize) { EXPECT_EQ(INTRINSICCV_OK, intrinsiccv_filter_release(context)); } -TYPED_TEST(GaussianBlurTest, ChannelNumber) { +TYPED_TEST(GaussianBlur, ChannelNumber) { intrinsiccv_filter_context_t *context = nullptr; ASSERT_EQ(INTRINSICCV_OK, - intrinsiccv_filter_create(&context, 1, sizeof(TypeParam), + intrinsiccv_filter_create(&context, 1, 2 * sizeof(TypeParam), intrinsiccv_rectangle_t{1, 1})); TypeParam src[1], dst[1]; EXPECT_EQ(INTRINSICCV_ERROR_RANGE, @@ -137,10 +247,10 @@ TYPED_TEST(GaussianBlurTest, ChannelNumber) { EXPECT_EQ(INTRINSICCV_OK, intrinsiccv_filter_release(context)); } -TYPED_TEST(GaussianBlurTest, InvalidContextSizeType) { +TYPED_TEST(GaussianBlur, InvalidContextSizeType) { intrinsiccv_filter_context_t *context = nullptr; ASSERT_EQ(INTRINSICCV_OK, - intrinsiccv_filter_create(&context, 1, sizeof(TypeParam) + 1, + intrinsiccv_filter_create(&context, 1, 2 * sizeof(TypeParam) + 1, intrinsiccv_rectangle_t{1, 1})); TypeParam src[1], dst[1]; EXPECT_EQ(INTRINSICCV_ERROR_CONTEXT_MISMATCH, @@ -154,10 +264,10 @@ TYPED_TEST(GaussianBlurTest, InvalidContextSizeType) { EXPECT_EQ(INTRINSICCV_OK, intrinsiccv_filter_release(context)); } -TYPED_TEST(GaussianBlurTest, InvalidContextChannelNumber) { +TYPED_TEST(GaussianBlur, InvalidContextChannelNumber) { intrinsiccv_filter_context_t *context = nullptr; ASSERT_EQ(INTRINSICCV_OK, - intrinsiccv_filter_create(&context, 2, sizeof(TypeParam), + intrinsiccv_filter_create(&context, 2, 2 * sizeof(TypeParam), intrinsiccv_rectangle_t{1, 1})); TypeParam src[1], dst[1]; EXPECT_EQ(INTRINSICCV_ERROR_CONTEXT_MISMATCH, @@ -171,10 +281,10 @@ TYPED_TEST(GaussianBlurTest, InvalidContextChannelNumber) { EXPECT_EQ(INTRINSICCV_OK, intrinsiccv_filter_release(context)); } -TYPED_TEST(GaussianBlurTest, InvalidContextImageSize) { +TYPED_TEST(GaussianBlur, InvalidContextImageSize) { intrinsiccv_filter_context_t *context = nullptr; ASSERT_EQ(INTRINSICCV_OK, - intrinsiccv_filter_create(&context, 1, sizeof(TypeParam), + intrinsiccv_filter_create(&context, 1, 2 * sizeof(TypeParam), intrinsiccv_rectangle_t{1, 1})); TypeParam src[1], dst[1]; EXPECT_EQ(INTRINSICCV_ERROR_CONTEXT_MISMATCH, diff --git a/test/api/test_sobel.cpp b/test/api/test_sobel.cpp index 2a2c7d8a7..e0ac95b1a 100644 --- a/test/api/test_sobel.cpp +++ b/test/api/test_sobel.cpp @@ -40,12 +40,6 @@ static constexpr std::array kSupportedBorders = { INTRINSICCV_BORDER_TYPE_REPLICATE, }; -// Dummy border values because BORDER_TYPE_CONSTANT is not used -static constexpr std::array - kSupportedBorderValues = {{ - {0, 0, 0, 0}, // default - }}; - // Test for Sobel 3x3 operator. template class Sobel3x3Test : public test::KernelTest { @@ -71,6 +65,8 @@ class Sobel3x3Test : public test::KernelTest { // Use the default array layouts for testing. auto array_layouts = test::default_array_layouts(mask.width(), mask.height()); + // Use the default border values for testing. + auto kSupportedBorderValues = test::default_border_values(); // Create generators and execute test. test::SequenceGenerator tested_array_layouts{array_layouts}; test::SequenceGenerator tested_borders{kSupportedBorders}; diff --git a/test/framework/kernel.h b/test/framework/kernel.h index cd5bac357..0a2a3b9ed 100644 --- a/test/framework/kernel.h +++ b/test/framework/kernel.h @@ -170,7 +170,8 @@ class KernelTest { for (size_t column = 0; column < expected_.width(); ++column) { IntermediateType result; result = calculate_expected_at(kernel, source, row, column); - expected_.at(row, column)[0] = static_cast(result); + expected_.at(row, column)[0] = + static_cast(scale_result(kernel, result)); } } } @@ -192,6 +193,11 @@ class KernelTest { return result; } + virtual IntermediateType scale_result(const Kernel&, + IntermediateType result) { + return result; + } + // Creates arrays for a given layout. void create_arrays(const Kernel& kernel, const ArrayLayout& array_layout) { diff --git a/test/framework/utils.cpp b/test/framework/utils.cpp index 05f9174d1..ce034e060 100644 --- a/test/framework/utils.cpp +++ b/test/framework/utils.cpp @@ -48,6 +48,12 @@ template void dump(const TwoDimensional *); template void dump(const TwoDimensional *); template void dump(const TwoDimensional *); +std::array default_border_values() { + return {{ + {0, 0, 0, 0}, // default + }}; +} + std::array small_array_layouts(size_t min_width, size_t min_height) { size_t vl = test::Options::vector_length(); diff --git a/test/framework/utils.h b/test/framework/utils.h index 5cfa50c80..00492c5f9 100644 --- a/test/framework/utils.h +++ b/test/framework/utils.h @@ -82,6 +82,9 @@ class Options { template void dump(const TwoDimensional *elements); +// Returns default border values. +std::array default_border_values(); + // Returns an array of just a few small layouts. std::array small_array_layouts(size_t min_width, size_t min_height); -- GitLab From 231ed1a5c5ae77e319480b8921b64c65712135fb Mon Sep 17 00:00:00 2001 From: Ioana Ghiban Date: Fri, 23 Feb 2024 12:45:07 +0200 Subject: [PATCH 2/6] Implemented BORDER_TYPE_REFLECT and WRAP --- test/api/test_gaussian_blur.cpp | 7 +- test/framework/border.cpp | 116 ++++++++++++++++++++++++++++++++ 2 files changed, 119 insertions(+), 4 deletions(-) diff --git a/test/api/test_gaussian_blur.cpp b/test/api/test_gaussian_blur.cpp index 7959b8e57..65ecd639f 100644 --- a/test/api/test_gaussian_blur.cpp +++ b/test/api/test_gaussian_blur.cpp @@ -30,10 +30,9 @@ struct GaussianBlurKernelTestParams { static constexpr size_t kKernelSize = KernelSize; }; // end of struct GaussianBlurKernelTestParams -static constexpr std::array kSupportedBorders = { - INTRINSICCV_BORDER_TYPE_REPLICATE, - // INTRINSICCV_BORDER_TYPE_REFLECT, - // INTRINSICCV_BORDER_TYPE_WRAP, +static constexpr std::array kSupportedBorders = { + INTRINSICCV_BORDER_TYPE_REPLICATE, INTRINSICCV_BORDER_TYPE_REFLECT, + INTRINSICCV_BORDER_TYPE_WRAP, // INTRINSICCV_BORDER_TYPE_REVERSE, }; diff --git a/test/framework/border.cpp b/test/framework/border.cpp index 6b8263cd6..9a830d33b 100644 --- a/test/framework/border.cpp +++ b/test/framework/border.cpp @@ -106,6 +106,116 @@ static void constant(const Bordered *bordered, } } +// Creates reflected border elements. +// +// Reflecting means that the border mirrors the elements of the array. +// For example: +// | left border | elements | right border | +// | C B A | A B C D E | E D C | +template +static void reflect(const Bordered *bordered, + TwoDimensional *elements) { + ASSERT_LE((bordered->left() + bordered->right()) * elements->channels(), + elements->width()); + ASSERT_LE(bordered->top() + bordered->bottom(), elements->height()); + + // Left and right border columns. + for (size_t row = 0; row < elements->height(); ++row) { + for (size_t channel = 0; channel < elements->channels(); ++channel) { + // Prepare left border columns. + for (size_t column = 0; column < bordered->left(); ++column) { + size_t src_column = + (bordered->left() + column) * elements->channels() + channel; + size_t dst_column = + (bordered->left() - 1 - column) * elements->channels() + channel; + elements->at(row, dst_column)[0] = elements->at(row, src_column)[0]; + } + + // Prepare right border columns. + for (size_t column = 0; column < bordered->right(); ++column) { + size_t src_column = + elements->width() - + (bordered->right() + 1 + column) * elements->channels() + channel; + size_t dst_column = + elements->width() - + (bordered->right() - column) * elements->channels() + channel; + elements->at(row, dst_column)[0] = elements->at(row, src_column)[0]; + } + } + } + + // Reflect top border rows. + size_t reflected_top_row = bordered->top(); + for (size_t row = 0; row < bordered->top(); ++row) { + auto row_ptr = elements->at(reflected_top_row + row, 0); + std::copy(row_ptr, row_ptr + elements->width(), + elements->at(bordered->top() - 1 - row, 0)); + } + + // Reflect bottom border rows. + size_t reflected_bottom_row = elements->height() - bordered->bottom() - 1; + for (size_t row = 0; row < bordered->bottom(); ++row) { + auto row_ptr = elements->at(reflected_bottom_row - row, 0); + std::copy(row_ptr, row_ptr + elements->width(), + elements->at(reflected_bottom_row + row + 1, 0)); + } +} + +// Creates wrapping border elements. +// +// Wrapping means that the border 'continues' the elements of the array. +// For example: +// | left border | elements | right border | +// | C D E | A B C D E | A B C | +template +static void wrap(const Bordered *bordered, + TwoDimensional *elements) { + ASSERT_LE((bordered->left() + bordered->right()) * elements->channels(), + elements->width()); + ASSERT_LE(bordered->top() + bordered->bottom(), elements->height()); + + // Left and right border columns. + for (size_t row = 0; row < elements->height(); ++row) { + for (size_t channel = 0; channel < elements->channels(); ++channel) { + // Prepare left border columns. + for (size_t column = 0; column < bordered->left(); ++column) { + size_t src_column = + elements->width() - + (bordered->right() + 1 + column) * elements->channels() + channel; + size_t dst_column = + (bordered->left() - 1 - column) * elements->channels() + channel; + elements->at(row, dst_column)[0] = elements->at(row, src_column)[0]; + } + + // Prepare right border columns. + for (size_t column = 0; column < bordered->right(); ++column) { + size_t src_column = + (bordered->left() + column) * elements->channels() + channel; + size_t dst_column = + elements->width() - + (bordered->right() - column) * elements->channels() + channel; + elements->at(row, dst_column)[0] = elements->at(row, src_column)[0]; + } + } + } + + // Wrap top border rows. + size_t wrapped_top_row = elements->height() - bordered->bottom() - 1; + for (size_t row = 0; row < bordered->top(); ++row) { + auto row_ptr = elements->at(wrapped_top_row - row, 0); + std::copy(row_ptr, row_ptr + elements->width(), + elements->at(bordered->top() - 1 - row, 0)); + } + + // Wrap bottom border rows. + size_t wrapped_bottom_row = bordered->top(); + for (size_t row = 0; row < bordered->bottom(); ++row) { + auto row_ptr = elements->at(wrapped_bottom_row + row, 0); + std::copy(row_ptr, row_ptr + elements->width(), + elements->at(wrapped_top_row + row + 1, 0)); + } +} + template void prepare_borders(intrinsiccv_border_type_t border_type, intrinsiccv_border_values_t border_values, @@ -123,6 +233,12 @@ void prepare_borders(intrinsiccv_border_type_t border_type, case INTRINSICCV_BORDER_TYPE_CONSTANT: return constant(bordered, border_values, elements); + + case INTRINSICCV_BORDER_TYPE_REFLECT: + return reflect(bordered, elements); + + case INTRINSICCV_BORDER_TYPE_WRAP: + return wrap(bordered, elements); } } -- GitLab From 3d07d051b5b912276f0666ab703ccfafe50786e0 Mon Sep 17 00:00:00 2001 From: Ioana Ghiban Date: Fri, 23 Feb 2024 16:55:49 +0200 Subject: [PATCH 3/6] Implemented BORDER_TYPE_REVERSE --- test/api/test_gaussian_blur.cpp | 9 ++-- test/framework/border.cpp | 74 +++++++++++++++++++++++++++++++-- 2 files changed, 76 insertions(+), 7 deletions(-) diff --git a/test/api/test_gaussian_blur.cpp b/test/api/test_gaussian_blur.cpp index 65ecd639f..1ef6c163b 100644 --- a/test/api/test_gaussian_blur.cpp +++ b/test/api/test_gaussian_blur.cpp @@ -7,6 +7,7 @@ #include "framework/array.h" #include "framework/generator.h" #include "framework/kernel.h" +#include "framework/utils.h" #include "intrinsiccv/intrinsiccv.h" #define INTRINSICCV_GAUSSIAN_BLUR(type, kernel_suffix, type_suffix) \ @@ -30,10 +31,11 @@ struct GaussianBlurKernelTestParams { static constexpr size_t kKernelSize = KernelSize; }; // end of struct GaussianBlurKernelTestParams -static constexpr std::array kSupportedBorders = { - INTRINSICCV_BORDER_TYPE_REPLICATE, INTRINSICCV_BORDER_TYPE_REFLECT, +static constexpr std::array kSupportedBorders = { + INTRINSICCV_BORDER_TYPE_REPLICATE, + INTRINSICCV_BORDER_TYPE_REFLECT, INTRINSICCV_BORDER_TYPE_WRAP, - // INTRINSICCV_BORDER_TYPE_REVERSE, + INTRINSICCV_BORDER_TYPE_REVERSE, }; // Test for GaussianBlur operator. @@ -83,6 +85,7 @@ class GaussianBlurTest : public test::KernelTest { // Use the default array layouts for testing. auto array_layouts = test::default_array_layouts(mask.width(), mask.height()); + test::KernelTest::with_debug(); // Use the default border values for testing. auto kSupportedBorderValues = test::default_border_values(); // Create generators and execute test. diff --git a/test/framework/border.cpp b/test/framework/border.cpp index 9a830d33b..cb88e2ad8 100644 --- a/test/framework/border.cpp +++ b/test/framework/border.cpp @@ -74,14 +74,16 @@ static void constant(const Bordered *bordered, elements->width()); ASSERT_LE(bordered->top() + bordered->bottom(), elements->height()); - // Left and right border columns. + // Constant left and right border columns. for (size_t row = 0; row < elements->height(); ++row) { for (size_t channel = 0; channel < elements->channels(); ++channel) { + // Prepare left border columns. for (size_t column = 0; column < bordered->left(); ++column) { size_t dst_column = column * elements->channels() + channel; elements->at(row, dst_column)[0] = border_values.left; } + // Prepare right border columns. for (size_t column = 0; column < bordered->right(); ++column) { size_t dst_column = elements->width() + @@ -91,13 +93,14 @@ static void constant(const Bordered *bordered, } } - // Top and bottom border rows. + // Constant top border rows. for (size_t row = 0; row < bordered->top(); ++row) { for (size_t column = 0; column < elements->width(); ++column) { elements->at(row, column)[0] = border_values.top; } } + // Constant bottom border rows. for (size_t row = elements->height() - bordered->bottom(); row < elements->height(); ++row) { for (size_t column = 0; column < elements->width(); ++column) { @@ -119,7 +122,7 @@ static void reflect(const Bordered *bordered, elements->width()); ASSERT_LE(bordered->top() + bordered->bottom(), elements->height()); - // Left and right border columns. + // Reflect left and right border columns. for (size_t row = 0; row < elements->height(); ++row) { for (size_t channel = 0; channel < elements->channels(); ++channel) { // Prepare left border columns. @@ -174,7 +177,7 @@ static void wrap(const Bordered *bordered, elements->width()); ASSERT_LE(bordered->top() + bordered->bottom(), elements->height()); - // Left and right border columns. + // Wrap left and right border columns. for (size_t row = 0; row < elements->height(); ++row) { for (size_t channel = 0; channel < elements->channels(); ++channel) { // Prepare left border columns. @@ -216,6 +219,66 @@ static void wrap(const Bordered *bordered, } } +// Creates reversed border elements. + +// Reversing means that the border mirrors the elements of the array, +// skipping the elements on the edge. +// For example: +// | left border | elements | right border | +// | D C B | A B C D E | D C B | +template +static void reverse(const Bordered *bordered, + TwoDimensional *elements) { + ASSERT_LE((bordered->left() + bordered->right()) * elements->channels(), + elements->width()); + ASSERT_LE((bordered->left() + 1) * elements->channels(), elements->width()); + ASSERT_LE((bordered->right() + 1) * elements->channels(), elements->width()); + ASSERT_LE(bordered->top() + bordered->bottom(), elements->height()); + ASSERT_LE(bordered->top() + 1, elements->height()); + ASSERT_LE(bordered->bottom() + 1, elements->height()); + + // Reverse left and right border columns. + for (size_t row = 0; row < elements->height(); ++row) { + for (size_t channel = 0; channel < elements->channels(); ++channel) { + // Prepare left border columns. + for (size_t column = 0; column < bordered->left(); ++column) { + size_t src_column = + (bordered->left() + column + 1) * elements->channels() + channel; + size_t dst_column = + (bordered->left() - 1 - column) * elements->channels() + channel; + elements->at(row, dst_column)[0] = elements->at(row, src_column)[0]; + } + + // Prepare right border columns. + for (size_t column = 0; column < bordered->right(); ++column) { + size_t src_column = + elements->width() - + (bordered->right() + 2 + column) * elements->channels() + channel; + size_t dst_column = + elements->width() - + (bordered->right() - column) * elements->channels() + channel; + elements->at(row, dst_column)[0] = elements->at(row, src_column)[0]; + } + } + } + + // Reverse top border rows. + size_t reversed_top_row = bordered->top() + 1; + for (size_t row = 0; row < bordered->top(); ++row) { + auto row_ptr = elements->at(reversed_top_row + row, 0); + std::copy(row_ptr, row_ptr + elements->width(), + elements->at(bordered->top() - 1 - row, 0)); + } + + // Reverse bottom border rows. + size_t reversed_bottom_row = elements->height() - bordered->bottom() - 2; + for (size_t row = 0; row < bordered->bottom(); ++row) { + auto row_ptr = elements->at(reversed_bottom_row - row, 0); + std::copy(row_ptr, row_ptr + elements->width(), + elements->at(reversed_bottom_row + row + 2, 0)); + } +} + template void prepare_borders(intrinsiccv_border_type_t border_type, intrinsiccv_border_values_t border_values, @@ -239,6 +302,9 @@ void prepare_borders(intrinsiccv_border_type_t border_type, case INTRINSICCV_BORDER_TYPE_WRAP: return wrap(bordered, elements); + + case INTRINSICCV_BORDER_TYPE_REVERSE: + return reverse(bordered, elements); } } -- GitLab From 8eeada8ec8d2ba29bbf65b1ebf16d41025d7469f Mon Sep 17 00:00:00 2001 From: Ioana Ghiban Date: Mon, 26 Feb 2024 18:48:34 +0200 Subject: [PATCH 4/6] Improve Line Coverage. --- test/api/test_gaussian_blur.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/api/test_gaussian_blur.cpp b/test/api/test_gaussian_blur.cpp index 1ef6c163b..80be3780a 100644 --- a/test/api/test_gaussian_blur.cpp +++ b/test/api/test_gaussian_blur.cpp @@ -300,6 +300,13 @@ TYPED_TEST(GaussianBlur, InvalidContextImageSize) { EXPECT_EQ(INTRINSICCV_OK, intrinsiccv_filter_release(context)); } +TEST(FilterCreate, TooBigImage) { + intrinsiccv_filter_context_t *context = nullptr; + intrinsiccv_rectangle_t rect{INTRINSICCV_MAX_IMAGE_PIXELS, 1}; + EXPECT_EQ(INTRINSICCV_ERROR_ALLOCATION, + intrinsiccv_filter_create(&context, 1, 1, rect)); +} + TEST(FilterCreate, ImageSize) { intrinsiccv_filter_context_t *context = nullptr; -- GitLab From c2bdd4b8c340a11178651fb948589522c6e475d4 Mon Sep 17 00:00:00 2001 From: Ioana Ghiban Date: Tue, 27 Feb 2024 17:30:39 +0200 Subject: [PATCH 5/6] Improve tests' performance --- test/api/test_gaussian_blur.cpp | 110 ++++++++++++++++++++++++-------- 1 file changed, 82 insertions(+), 28 deletions(-) diff --git a/test/api/test_gaussian_blur.cpp b/test/api/test_gaussian_blur.cpp index 80be3780a..90f2807e7 100644 --- a/test/api/test_gaussian_blur.cpp +++ b/test/api/test_gaussian_blur.cpp @@ -31,20 +31,72 @@ struct GaussianBlurKernelTestParams { static constexpr size_t kKernelSize = KernelSize; }; // end of struct GaussianBlurKernelTestParams -static constexpr std::array kSupportedBorders = { - INTRINSICCV_BORDER_TYPE_REPLICATE, - INTRINSICCV_BORDER_TYPE_REFLECT, - INTRINSICCV_BORDER_TYPE_WRAP, - INTRINSICCV_BORDER_TYPE_REVERSE, -}; +static constexpr std::array kDefaultBorder = { + INTRINSICCV_BORDER_TYPE_REPLICATE}; + +static constexpr std::array kReflectBorder = { + INTRINSICCV_BORDER_TYPE_REFLECT}; + +// static constexpr std::array kWrapBorder = { +// INTRINSICCV_BORDER_TYPE_WRAP}; + +static constexpr std::array kReverseBorder = { + INTRINSICCV_BORDER_TYPE_REVERSE}; + +template +std::unique_ptr> +make_generator_ptr(IterableType &elements) { + test::Generator *pg = + new test::SequenceGenerator(elements); + return std::unique_ptr>( + pg); +} // Test for GaussianBlur operator. template class GaussianBlurTest : public test::KernelTest { + using Base = test::KernelTest; using typename test::KernelTest::InputType; using typename test::KernelTest::IntermediateType; using typename test::KernelTest::OutputType; + public: + GaussianBlurTest() + : small_array_layouts_{test::small_array_layouts( + KernelTestParams::kKernelSize, KernelTestParams::kKernelSize)} { + array_layout_generator_ = make_generator_ptr(small_array_layouts_); + border_type_generator_ = make_generator_ptr(kDefaultBorder); + } + + GaussianBlurTest &with_array_layouts( + std::unique_ptr> g) { + array_layout_generator_ = std::move(g); + return *this; + } + + GaussianBlurTest &with_border_types( + std::unique_ptr> g) { + border_type_generator_ = std::move(g); + return *this; + } + + void test(test::Array2D mask) { + test::Kernel kernel{mask}; + // Use the default border values for testing. + auto kSupportedBorderValues = test::default_border_values(); + // Create generators and execute test. + test::SequenceGenerator tested_border_values{kSupportedBorderValues}; + test::PseudoRandomNumberGenerator element_generator; + Base::test(kernel, *array_layout_generator_, *border_type_generator_, + tested_border_values, element_generator); + } + + protected: + std::array small_array_layouts_; + std::unique_ptr> array_layout_generator_; + std::unique_ptr> + border_type_generator_; + intrinsiccv_error_t call_api(const test::Array2D *input, test::Array2D *output, intrinsiccv_border_type_t border_type, @@ -78,25 +130,6 @@ class GaussianBlurTest : public test::KernelTest { IntermediateType result) override { return kernel.width() == 3 ? ((result + 8) / 16) : ((result + 128) / 256); } - - public: - void test(test::Array2D mask) { - test::Kernel kernel{mask}; - // Use the default array layouts for testing. - auto array_layouts = - test::default_array_layouts(mask.width(), mask.height()); - test::KernelTest::with_debug(); - // Use the default border values for testing. - auto kSupportedBorderValues = test::default_border_values(); - // Create generators and execute test. - test::SequenceGenerator tested_array_layouts{array_layouts}; - test::SequenceGenerator tested_borders{kSupportedBorders}; - test::SequenceGenerator tested_border_values{kSupportedBorderValues}; - test::PseudoRandomNumberGenerator element_generator; - this->test::KernelTest::test( - kernel, tested_array_layouts, tested_borders, tested_border_values, - element_generator); - } }; // end of class class GaussianBlur3x3Test using ElementTypes = ::testing::Types; @@ -107,8 +140,24 @@ class GaussianBlur : public testing::Test {}; TYPED_TEST_SUITE(GaussianBlur, ElementTypes); // Tests gaussian_blur_3x3_ API. -TYPED_TEST(GaussianBlur, 3x3) { +TYPED_TEST(GaussianBlur, 3x3Small) { + using KernelTestParams = GaussianBlurKernelTestParams; + // 3x3 GaussianBlur operator. + test::Array2D mask{3, 3}; + // clang-format off + mask.set(0, 0, { 1, 2, 1}); + mask.set(1, 0, { 2, 4, 2}); + mask.set(2, 0, { 1, 2, 1}); + // clang-format on + GaussianBlurTest{} + .with_border_types(make_generator_ptr(kReflectBorder)) + .test(mask); +} + +TYPED_TEST(GaussianBlur, 3x3Default) { using KernelTestParams = GaussianBlurKernelTestParams; + std::array medium_array_layouts_3x3 = + test::default_array_layouts(3, 3); // 3x3 GaussianBlur operator. test::Array2D mask{3, 3}; // clang-format off @@ -116,7 +165,10 @@ TYPED_TEST(GaussianBlur, 3x3) { mask.set(1, 0, { 2, 4, 2}); mask.set(2, 0, { 1, 2, 1}); // clang-format on - GaussianBlurTest{}.test(mask); + GaussianBlurTest{} + .with_array_layouts(make_generator_ptr(medium_array_layouts_3x3)) + .with_border_types(make_generator_ptr(kReflectBorder)) + .test(mask); } // Tests gaussian_blur_5x5_ API. @@ -131,7 +183,9 @@ TYPED_TEST(GaussianBlur, 5x5) { mask.set(3, 0, { 4, 16, 24, 16, 4}); mask.set(4, 0, { 1, 4, 6, 4, 1}); // clang-format on - GaussianBlurTest{}.test(mask); + GaussianBlurTest{} + .with_border_types(make_generator_ptr(kReverseBorder)) + .test(mask); } TYPED_TEST(GaussianBlur, UnsupportedBorderType) { -- GitLab From 876fc1704b0217bdc6cfe15dd6194f7f63675abb Mon Sep 17 00:00:00 2001 From: Ioana Ghiban Date: Wed, 28 Feb 2024 10:02:40 +0200 Subject: [PATCH 6/6] Cover all border types. --- test/api/test_gaussian_blur.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/test/api/test_gaussian_blur.cpp b/test/api/test_gaussian_blur.cpp index 90f2807e7..0aa79e30d 100644 --- a/test/api/test_gaussian_blur.cpp +++ b/test/api/test_gaussian_blur.cpp @@ -37,11 +37,12 @@ static constexpr std::array kDefaultBorder = { static constexpr std::array kReflectBorder = { INTRINSICCV_BORDER_TYPE_REFLECT}; -// static constexpr std::array kWrapBorder = { -// INTRINSICCV_BORDER_TYPE_WRAP}; - -static constexpr std::array kReverseBorder = { - INTRINSICCV_BORDER_TYPE_REVERSE}; +static constexpr std::array kAllBorders = { + INTRINSICCV_BORDER_TYPE_REPLICATE, + INTRINSICCV_BORDER_TYPE_REFLECT, + INTRINSICCV_BORDER_TYPE_WRAP, + INTRINSICCV_BORDER_TYPE_REVERSE, +}; template std::unique_ptr> @@ -142,6 +143,7 @@ TYPED_TEST_SUITE(GaussianBlur, ElementTypes); // Tests gaussian_blur_3x3_ API. TYPED_TEST(GaussianBlur, 3x3Small) { using KernelTestParams = GaussianBlurKernelTestParams; + // 3x3 GaussianBlur operator. test::Array2D mask{3, 3}; // clang-format off @@ -150,7 +152,7 @@ TYPED_TEST(GaussianBlur, 3x3Small) { mask.set(2, 0, { 1, 2, 1}); // clang-format on GaussianBlurTest{} - .with_border_types(make_generator_ptr(kReflectBorder)) + .with_border_types(make_generator_ptr(kAllBorders)) .test(mask); } @@ -184,7 +186,7 @@ TYPED_TEST(GaussianBlur, 5x5) { mask.set(4, 0, { 1, 4, 6, 4, 1}); // clang-format on GaussianBlurTest{} - .with_border_types(make_generator_ptr(kReverseBorder)) + .with_border_types(make_generator_ptr(kAllBorders)) .test(mask); } -- GitLab