diff --git a/intrinsiccv/include/intrinsiccv/intrinsiccv.h b/intrinsiccv/include/intrinsiccv/intrinsiccv.h index e352f29ef35c610612666fe5b40bff394556b614..320d2f7654ea458bbbcc47215cabd6083a622803 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 6622da370bf66283a2ccf44685dcb741146cf758..21e3fac45b32b3f5495aafa0b12b605a2072253b 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 62f6588c6b9f1a9b77b42aa27c4e6f4342d24b4d..4911906cf6462685ebaac4a5b082fb63d6a2e608 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 e8d2798b88410fd9344307944495aa103cde372c..0aa79e30d2b637ec70e2b32b26c0fc43835dbdd4 100644 --- a/test/api/test_gaussian_blur.cpp +++ b/test/api/test_gaussian_blur.cpp @@ -4,6 +4,9 @@ #include +#include "framework/array.h" +#include "framework/generator.h" +#include "framework/kernel.h" #include "framework/utils.h" #include "intrinsiccv/intrinsiccv.h" @@ -15,17 +18,182 @@ 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 kDefaultBorder = { + INTRINSICCV_BORDER_TYPE_REPLICATE}; + +static constexpr std::array kReflectBorder = { + INTRINSICCV_BORDER_TYPE_REFLECT}; + +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> +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, + 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); + } +}; // 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); + +// Tests gaussian_blur_3x3_ API. +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(kAllBorders)) + .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 + 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_array_layouts(make_generator_ptr(medium_array_layouts_3x3)) + .with_border_types(make_generator_ptr(kReflectBorder)) + .test(mask); +} -TYPED_TEST(GaussianBlurTest, UnsupportedBorderType) { +// 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{} + .with_border_types(make_generator_ptr(kAllBorders)) + .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 +213,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 +286,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 +305,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 +322,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 +339,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, @@ -188,6 +356,13 @@ TYPED_TEST(GaussianBlurTest, 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; diff --git a/test/api/test_sobel.cpp b/test/api/test_sobel.cpp index 2a2c7d8a70ebd482111443f3ad0465789078a6dd..e0ac95b1a3db63cc169fe27134add6fe3564a867 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/border.cpp b/test/framework/border.cpp index 6b8263cd65fd5f04d3554e0d3879af6259ae5556..cb88e2ad817303a9e22ca54bd47dce6971975d31 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) { @@ -106,6 +109,176 @@ 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()); + + // 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. + 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()); + + // 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. + 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)); + } +} + +// 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, @@ -123,6 +296,15 @@ 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); + + case INTRINSICCV_BORDER_TYPE_REVERSE: + return reverse(bordered, elements); } } diff --git a/test/framework/kernel.h b/test/framework/kernel.h index cd5bac3570254efcbbecdb50127ce1d7e1ec0ce9..0a2a3b9ed3718d69e1442c7809693cbadeee23d0 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 05f9174d148ae1074ba63a278df216ebebb055ae..ce034e060a57e9a4bd8faea326fbae510b3048e8 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 5cfa50c806bb1686bc81170d17a96e72cbbb9a17..00492c5f986bde85ef40c558005076fc450d46db 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);