diff --git a/intrinsiccv/include/intrinsiccv.h b/intrinsiccv/include/intrinsiccv.h index 0159e254571999282d0c39e4c956a55eac89b2e3..aa02dfb5ffc8593a23656eccafedabf94fc46068 100644 --- a/intrinsiccv/include/intrinsiccv.h +++ b/intrinsiccv/include/intrinsiccv.h @@ -329,7 +329,7 @@ void INTRINSICCV_C_API(erode_u8)(const uint8_t *src, size_t src_stride, /// @param src Pointer to the source data. Must be non-null. /// @param src_stride Distance in bytes between the row first elements for /// the source data. Must not be less than -/// width * sizeof(type). +/// width * (element size in bytes). /// @param width How many elements are in a row /// @param height How many rows are in the data /// @@ -385,6 +385,19 @@ void INTRINSICCV_C_API(merge)(const void **srcs, const size_t *src_strides, size_t height, size_t channels, size_t element_size); +/// Calculates minimum and maximum element value across the source data. +/// +/// @param src Pointer to the source data. Must be non-null. +/// @param src_stride Distance in bytes from the start of one row to the +/// start of the next row in the source data. Must not be +/// less than width * (element size in bytes). +/// @param width How many elements are in a row +/// @param height How many rows are in the data +/// @param min_value Pointer to save result minimum value to, or nullptr if +/// minimum is not to be calculated. +/// @param max_value Pointer to save result maximum value to, or nullptr if +/// maximum is not to be calculated. +/// void INTRINSICCV_C_API(min_max_u8)(const uint8_t *src, size_t src_stride, size_t width, size_t height, uint8_t *min_value, uint8_t *max_value); diff --git a/test/api/test_min_max.cpp b/test/api/test_min_max.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9e8238cc282dd75e2883563e132885d237f8a5e1 --- /dev/null +++ b/test/api/test_min_max.cpp @@ -0,0 +1,218 @@ +// SPDX-FileCopyrightText: 2023 Arm Limited and/or its affiliates +// +// SPDX-License-Identifier: Apache-2.0 + +#include +#include + +#include "framework/array.h" +#include "framework/utils.h" + +#define INTRINSICCV_MIN_MAX(type, suffix) \ + INTRINSICCV_API(min_max, intrinsiccv_min_max_##suffix, type) + +INTRINSICCV_MIN_MAX(int8_t, s8); +INTRINSICCV_MIN_MAX(uint8_t, u8); +INTRINSICCV_MIN_MAX(int16_t, s16); +INTRINSICCV_MIN_MAX(uint16_t, u16); +INTRINSICCV_MIN_MAX(int32_t, s32); + +template +class MinMaxTest { + using ArrayType = test::Array2D; + + /// Returns the number of padding bytes at the end of rows. + size_t padding() const { return padding_; } + + /// Returns the minimum value for ElementType. + static constexpr ElementType min() { + return std::numeric_limits::min(); + } + + /// Returns the maximum value for ElementType. + static constexpr ElementType max() { + return std::numeric_limits::max(); + } + + /// Returns the number of vector lanes. + size_t lanes() const { return test::Options::vector_lanes(); } + + /// Number of padding bytes at the end of rows. + size_t padding_{0}; + + struct Elements { + std::initializer_list source_row0_vector; + std::initializer_list source_row0_scalar; + std::initializer_list source_row1_vector; + std::initializer_list source_row1_scalar; + ElementType filler_value; + ElementType expected_min; + ElementType expected_max; + }; + + // clang-format off + static constexpr Elements test_elements[] = { + { + {}, {}, + {}, {}, + 10, + 10, 10 + }, + { + {}, {}, + {}, {}, + min(), + min(), min() + }, + { + {}, {}, + {}, {}, + 0, + 0, 0 + }, + { + {}, {}, + {}, {}, + max(), + max(), max() + }, + { + {min()+10}, {}, + {max()}, {}, + min()+20, + min()+10, max() + }, + { + {}, {min()}, + {}, {max() - 10}, + min()+20, + min(), max()-10 + }, + { + {9}, {}, + {}, {11}, + 10, + 9, 11 + }, + { + {}, {9}, + {11}, {}, + 10, + 9, 11 + }, + { + {11}, {}, + {}, {9}, + 10, + 9, 11 + }, + { + {10, 9, 11}, {}, + {}, {}, + 10, + 9, 11 + }, + { + {9, 10, 11}, {}, + {}, {}, + 10, + 9, 11 + }, + { + { 3, 4, 5, 6}, {15, 16, 17}, + {20, 21, 22, 23}, {35, 2, 33}, + 10, + 2, 35 + }, + { + { 3, 4, 5, 6}, {15, 16, 17}, + {20, 2, 22, 23}, {35, 36, 33}, + 10, + 2, 36 + }, + { + { 1, 2, 3, 4}, {15, 16, 42}, + { 1, 2, 3, 4}, {15, 16, 42}, + 10, + 1, 42 + }, + + }; + // clang-format on + + // We have 2 rows, source_row0 and source_row1 + size_t height() const { return 2; } + + /// Tested number of elements in a row. + size_t width() const { + // Sufficient number of elements to exercise both vector and scalar paths. + return 3 * lanes() - 1; + } + + public: + MinMaxTest& with_padding(size_t padding) { + padding_ = padding; + return *this; + } + + void test() { + for (auto testData : test_elements) { + ArrayType source{width(), height(), padding()}; + ASSERT_TRUE(source.valid()); + source.fill(testData.filler_value); + + // Fill elements one vector length apart. + for (size_t column_index = 0; column_index + lanes() < width(); + column_index += lanes()) { + source.set(0, column_index, testData.source_row0_vector); + source.set(1, column_index, testData.source_row1_vector); + } + source.set(0, (width() / lanes()) * lanes(), testData.source_row0_scalar); + source.set(1, (width() / lanes()) * lanes(), testData.source_row1_scalar); + + ElementType expected_min = testData.expected_min; + ElementType expected_max = testData.expected_max; + + ElementType actual_min = max(); + ElementType actual_max = min(); + + min_max()(source.data(), source.stride(), width(), height(), + nullptr, nullptr); + EXPECT_EQ(actual_min, max()); + EXPECT_EQ(actual_max, min()); + + actual_min = max(); + actual_max = min(); + min_max()(source.data(), source.stride(), width(), height(), + &actual_min, nullptr); + EXPECT_EQ(actual_min, expected_min); + EXPECT_EQ(actual_max, min()); + + actual_min = max(); + actual_max = min(); + min_max()(source.data(), source.stride(), width(), height(), + nullptr, &actual_max); + EXPECT_EQ(actual_min, max()); + EXPECT_EQ(actual_max, expected_max); + + actual_min = max(); + actual_max = min(); + min_max()(source.data(), source.stride(), width(), height(), + &actual_min, &actual_max); + EXPECT_EQ(actual_min, expected_min); + EXPECT_EQ(actual_max, expected_max); + } + } +}; // end of class MinMaxTest + +template +class MinMax : public testing::Test {}; + +using ElementTypes = + ::testing::Types; +TYPED_TEST_SUITE(MinMax, ElementTypes); + +TYPED_TEST(MinMax, API) { + MinMaxTest{}.test(); + MinMaxTest{}.with_padding(test::Options::vector_length()).test(); +}