From 14614f9462e753a70088fcac944392a99fbd17fe Mon Sep 17 00:00:00 2001 From: Ioana Ghiban Date: Fri, 10 May 2024 17:57:46 +0200 Subject: [PATCH 1/7] Document Compare API --- kleidicv/include/kleidicv/kleidicv.h | 74 ++++++++++++++++++++++++++-- 1 file changed, 69 insertions(+), 5 deletions(-) diff --git a/kleidicv/include/kleidicv/kleidicv.h b/kleidicv/include/kleidicv/kleidicv.h index c146a5b80..c04dfb170 100644 --- a/kleidicv/include/kleidicv/kleidicv.h +++ b/kleidicv/include/kleidicv/kleidicv.h @@ -76,11 +76,11 @@ extern "C" { /// to @ref KLEIDICV_MAX_IMAGE_PIXELS. /// /// @param src_a Pointer to the first source data. Must be non-null. -/// @param src_b Pointer to the second source data. Must be non-null. /// @param src_a_stride Distance in bytes from the start of one row to the /// start of the next row for the first source data. /// Must be a multiple of sizeof(type). /// Must not be less than width * sizeof(type). +/// @param src_b Pointer to the second source data. Must be non-null. /// @param src_b_stride Distance in bytes from the start of one row to the /// start of the next row for the second source data. /// Must be a multiple of sizeof(type). @@ -119,11 +119,11 @@ KLEIDICV_BINARY_OP(kleidicv_saturating_add_u64, uint64_t); /// Number of elements is limited to @ref KLEIDICV_MAX_IMAGE_PIXELS. /// /// @param src_a Pointer to the first source data. Must be non-null. -/// @param src_b Pointer to the second source data. Must be non-null. /// @param src_a_stride Distance in bytes from the start of one row to the /// start of the next row for the first source data. /// Must be a multiple of sizeof(type). /// Must not be less than width * sizeof(type). +/// @param src_b Pointer to the second source data. Must be non-null. /// @param src_b_stride Distance in bytes from the start of one row to the /// start of the next row for the second source data. /// Must be a multiple of sizeof(type). @@ -162,11 +162,11 @@ KLEIDICV_BINARY_OP(kleidicv_saturating_sub_u64, uint64_t); /// of elements is limited to @ref KLEIDICV_MAX_IMAGE_PIXELS. /// /// @param src_a Pointer to the first source data. Must be non-null. -/// @param src_b Pointer to the second source data. Must be non-null. /// @param src_a_stride Distance in bytes from the start of one row to the /// start of the next row for the first source data. /// Must be a multiple of sizeof(type). /// Must not be less than width * sizeof(type). +/// @param src_b Pointer to the second source data. Must be non-null. /// @param src_b_stride Distance in bytes from the start of one row to the /// start of the next row for the second source data. /// Must be a multiple of sizeof(type). @@ -199,11 +199,11 @@ KLEIDICV_BINARY_OP(kleidicv_saturating_absdiff_s32, int32_t); /// limited to @ref KLEIDICV_MAX_IMAGE_PIXELS. /// /// @param src_a Pointer to the first source data. Must be non-null. -/// @param src_b Pointer to the second source data. Must be non-null. /// @param src_a_stride Distance in bytes from the start of one row to the /// start of the next row for the first source data. /// Must be a multiple of sizeof(type). /// Must not be less than width * sizeof(type). +/// @param src_b Pointer to the second source data. Must be non-null. /// @param src_b_stride Distance in bytes from the start of one row to the /// start of the next row for the second source data. /// Must be a multiple of sizeof(type). @@ -239,11 +239,11 @@ KLEIDICV_BINARY_OP_SCALE(kleidicv_saturating_multiply_s32, int32_t, double); /// to @ref KLEIDICV_MAX_IMAGE_PIXELS. /// /// @param src_a Pointer to the first source data. Must be non-null. -/// @param src_b Pointer to the second source data. Must be non-null. /// @param src_a_stride Distance in bytes from the start of one row to the /// start of the next row for the first source data. /// Must not be less than width * sizeof(type). /// Must be a multiple of sizeof(type). +/// @param src_b Pointer to the second source data. Must be non-null. /// @param src_b_stride Distance in bytes from the start of one row to the /// start of the next row for the second source data. /// Must not be less than width * sizeof(type). @@ -757,6 +757,70 @@ KLEIDICV_API_DECLARATION(kleidicv_threshold_binary_u8, const uint8_t *src, size_t width, size_t height, uint8_t threshold, uint8_t value); +/// Performs an 'equal to' comparison of each element's value in `src_a` with +/// respect to the corresponding element's value in `src_b`. +/// +/// If the result of the comparison is true then the corresponding element in +/// `dst` is set to 255, otherwise to 0. +/// +/// Width and height are the same for the source and for the destination. Number +/// of elements is limited to @ref KLEIDICV_MAX_IMAGE_PIXELS. +/// +/// @param src_a Pointer to the first source data. Must be non-null. +/// @param src_a_stride Distance in bytes from the start of one row to the +/// start of the next row for the first source data. +/// Must be a multiple of sizeof(type). +/// Must not be less than width * sizeof(type). +/// @param src_b Pointer to the second source data. Must be non-null. +/// @param src_b_stride Distance in bytes from the start of one row to the +/// start of the next row for the second source data. +/// Must be a multiple of sizeof(type). +/// Must not be less than width * sizeof(type). +/// @param dst Pointer to the first destination data. Must be non-null. +/// @param dst_stride Distance in bytes from the start of one row to the +/// start of the next row for the destination data. Must +/// not be less than width * sizeof(type). +/// Must be a multiple of sizeof(type). +/// @param width Number of elements in a row. +/// @param height Number of rows in the data. +/// +KLEIDICV_API_DECLARATION(kleidicv_compare_equal_u8, const uint8_t *src_a, + size_t src_a_stride, const uint8_t *src_b, + size_t src_b_stride, uint8_t *dst, size_t dst_stride, + size_t width, size_t height); + +/// Performs a 'strictly greater than' comparison of each element's value in +/// `src_a` with respect to the corresponding element's value in `src_b`. +/// +/// If the result of the comparison is true then the corresponding element in +/// `dst` is set to 255, otherwise to 0. +/// +/// Width and height are the same for the source and for the destination. Number +/// of elements is limited to @ref KLEIDICV_MAX_IMAGE_PIXELS. +/// +/// @param src_a Pointer to the first source data. Must be non-null. +/// @param src_a_stride Distance in bytes from the start of one row to the +/// start of the next row for the first source data. +/// Must be a multiple of sizeof(type). +/// Must not be less than width * sizeof(type). +/// @param src_b Pointer to the second source data. Must be non-null. +/// @param src_b_stride Distance in bytes from the start of one row to the +/// start of the next row for the second source data. +/// Must be a multiple of sizeof(type). +/// Must not be less than width * sizeof(type). +/// @param dst Pointer to the first destination data. Must be non-null. +/// @param dst_stride Distance in bytes from the start of one row to the +/// start of the next row for the destination data. Must +/// not be less than width * sizeof(type). +/// Must be a multiple of sizeof(type). +/// @param width Number of elements in a row. +/// @param height Number of rows in the data. +/// +KLEIDICV_API_DECLARATION(kleidicv_compare_greater_u8, const uint8_t *src_a, + size_t src_a_stride, const uint8_t *src_b, + size_t src_b_stride, uint8_t *dst, size_t dst_stride, + size_t width, size_t height); + /// Creates a morphology context according to the parameters. /// /// Before a @ref kleidicv_dilate_u8 "dilate" or @ref kleidicv_erode_u8 -- GitLab From 841571344794e46d71c95b1b4cbbed681f3220f9 Mon Sep 17 00:00:00 2001 From: Ioana Ghiban Date: Fri, 10 May 2024 17:59:25 +0200 Subject: [PATCH 2/7] Add implementation for NEON Compare EQ and GT --- kleidicv/src/arithmetics/compare_api.cpp | 29 +++++++ kleidicv/src/arithmetics/compare_neon.cpp | 92 +++++++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 kleidicv/src/arithmetics/compare_api.cpp create mode 100644 kleidicv/src/arithmetics/compare_neon.cpp diff --git a/kleidicv/src/arithmetics/compare_api.cpp b/kleidicv/src/arithmetics/compare_api.cpp new file mode 100644 index 000000000..1e416a956 --- /dev/null +++ b/kleidicv/src/arithmetics/compare_api.cpp @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: 2023 - 2024 Arm Limited and/or its affiliates +// +// SPDX-License-Identifier: Apache-2.0 + +#include "kleidicv/dispatch.h" +#include "kleidicv/kleidicv.h" + +namespace kleidicv { + +namespace neon { +template +kleidicv_error_t compare(const T *src_a, size_t src_a_stride, const T *src_b, + size_t src_b_stride, T *dst, size_t dst_stride, + size_t width, size_t height, + kleidicv_cmp_type_t cmp_type); + +} // namespace neon + +namespace sve2 {} // namespace sve2 + +namespace sme2 {} // namespace sme2 + +} // namespace kleidicv + +#define KLEIDICV_DEFINE_C_API(name, type) \ + KLEIDICV_MULTIVERSION_C_API(name, &kleidicv::neon::compare, nullptr, \ + nullptr); + +KLEIDICV_DEFINE_C_API(kleidicv_compare_u8, uint8_t); diff --git a/kleidicv/src/arithmetics/compare_neon.cpp b/kleidicv/src/arithmetics/compare_neon.cpp new file mode 100644 index 000000000..183b0d5c4 --- /dev/null +++ b/kleidicv/src/arithmetics/compare_neon.cpp @@ -0,0 +1,92 @@ +// SPDX-FileCopyrightText: 2023 - 2024 Arm Limited and/or its affiliates +// +// SPDX-License-Identifier: Apache-2.0 + +#include "kleidicv/kleidicv.h" +#include "kleidicv/neon.h" + +namespace kleidicv::neon { + +template +class ComparatorEqual : public UnrollTwice { + public: + using VecTraits = neon::VecTraits; + using VectorType = typename VecTraits::VectorType; + + VectorType vector_path(VectorType src_a, VectorType src_b) { + return vceqq_u8(src_a, src_b); + } + + // NOLINTBEGIN(readability-make-member-function-const) + ScalarType scalar_path(ScalarType src_a, ScalarType src_b) { + return src_a == src_b ? 255 : 0; + } + // NOLINTEND(readability-make-member-function-const) +}; // end of class ComparatorEqual + +template +class ComparatorGreater : public UnrollTwice { + public: + using VecTraits = neon::VecTraits; + using VectorType = typename VecTraits::VectorType; + + VectorType vector_path(VectorType src_a, VectorType src_b) { + return vcgtq_u8(src_a, src_b); + } + + // NOLINTBEGIN(readability-make-member-function-const) + ScalarType scalar_path(ScalarType src_a, ScalarType src_b) { + return src_a > src_b ? 255 : 0; + } + // NOLINTEND(readability-make-member-function-const) +}; // end of class ComparatorGreater + +template +static kleidicv_error_t compare(const ScalarType *src_a, size_t src_a_stride, + const ScalarType *src_b, size_t src_b_stride, + ScalarType *dst, size_t dst_stride, + size_t width, size_t height) { + CHECK_POINTER_AND_STRIDE(src_a, src_a_stride); + CHECK_POINTER_AND_STRIDE(src_b, src_b_stride); + CHECK_POINTER_AND_STRIDE(dst, dst_stride); + CHECK_IMAGE_SIZE(width, height); + + Comparator operation{}; + Rectangle rect{width, height}; + Rows src_a_rows{src_a, src_a_stride}; + Rows src_b_rows{src_b, src_b_stride}; + Rows dst_rows{dst, dst_stride}; + + apply_operation_by_rows(operation, rect, src_a_rows, src_b_rows, dst_rows); + + return KLEIDICV_OK; +} + +template +kleidicv_error_t compare_equal(const ScalarType *src_a, size_t src_a_stride, + const ScalarType *src_b, size_t src_b_stride, + ScalarType *dst, size_t dst_stride, size_t width, + size_t height) { + return compare>( + src_a, src_a_stride, src_b, src_b_stride, dst, dst_stride, width, height); +} + +template +kleidicv_error_t compare_greater(const ScalarType *src_a, size_t src_a_stride, + const ScalarType *src_b, size_t src_b_stride, + ScalarType *dst, size_t dst_stride, + size_t width, size_t height) { + return compare>( + src_a, src_a_stride, src_b, src_b_stride, dst, dst_stride, width, height); +} + +#define KLEIDICV_INSTANTIATE_TEMPLATE(name, stype) \ + template KLEIDICV_TARGET_FN_ATTRS kleidicv_error_t name( \ + const stype *src_a, size_t src_a_stride, const stype *src_b, \ + size_t src_b_stride, stype *dst, size_t dst_stride, size_t width, \ + size_t height) + +KLEIDICV_INSTANTIATE_TEMPLATE(compare_equal, uint8_t); +KLEIDICV_INSTANTIATE_TEMPLATE(compare_greater, uint8_t); + +} // namespace kleidicv::neon -- GitLab From 1949d5ea86adc14063ba940ff1acb82de86ab602 Mon Sep 17 00:00:00 2001 From: Ioana Ghiban Date: Fri, 10 May 2024 18:01:27 +0200 Subject: [PATCH 3/7] Add tests for Compare EQ and GT --- test/api/test_compare.cpp | 431 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 431 insertions(+) create mode 100644 test/api/test_compare.cpp diff --git a/test/api/test_compare.cpp b/test/api/test_compare.cpp new file mode 100644 index 000000000..3db2fddf8 --- /dev/null +++ b/test/api/test_compare.cpp @@ -0,0 +1,431 @@ +// SPDX-FileCopyrightText: 2024 Arm Limited and/or its affiliates +// +// SPDX-License-Identifier: Apache-2.0 + +#include + +#include "framework/operation.h" +#include "kleidicv/ctypes.h" +#include "kleidicv/kleidicv.h" +#include "test_config.h" + +template +bool is_equal(ElementType a, ElementType b) { + return a == b; +} + +template +bool is_greater(ElementType a, ElementType b) { + return a > b; +} + +#define KLEIDICV_PARAMS(name, impl, type, op) \ + template , bool> = true> \ + class name { \ + public: \ + static decltype(auto) api() { return impl; } \ + static decltype(auto) operation() { \ + return [](type a, type b) { return op(a, b); }; \ + } \ + }; + +KLEIDICV_PARAMS(CompareEqualParams, kleidicv_compare_equal_u8, uint8_t, + is_equal); +KLEIDICV_PARAMS(CompareGreaterParams, kleidicv_compare_greater_u8, uint8_t, + is_greater); + +template class OperationParams> +class CompareTestLinear final { + public: + // minimum_size set by caller to trigger the 'big' scale path. + void test_scalar(size_t minimum_size = 1) { + size_t width = test::Options::vector_length() - 1; + test_linear(width, minimum_size); + } + + void test_vector(size_t minimum_size = 1) { + size_t width = test::Options::vector_length() * 2; + test_linear(width, minimum_size); + } + + protected: + static constexpr ElementType min() { + return std::numeric_limits::min(); + } + static constexpr ElementType max() { + return std::numeric_limits::max(); + } + + private: + class GenerateLinearSeries : public test::Generator { + public: + explicit GenerateLinearSeries(ElementType start_from) + : counter_{start_from} {} + + std::optional next() override { return counter_++; } + + private: + ElementType counter_; + }; // end of class GenerateLinearSeries + + // Number of padding bytes at the end of rows. + size_t padding_{0}; + + void test_linear(size_t width, size_t minimum_size) { + size_t image_size = + std::max(minimum_size, static_cast(max() - min())); + size_t height = image_size / width + 1; + test::Array2D source_a(width, height, padding_, 1); + test::Array2D source_b(width, height, padding_, 1); + test::Array2D expected(width, height, padding_, 1); + test::Array2D actual = + test::Array2D(width, height, padding_, 1); + + GenerateLinearSeries generator(min()); + + source_a.fill(generator); + source_b.fill(255); + expected.fill(0); + + calculate_expected(source_a, source_b, expected); + + ASSERT_EQ(KLEIDICV_OK, (OperationParams::api()( + source_a.data(), source_a.stride(), + source_b.data(), source_b.stride(), + actual.data(), actual.stride(), width, height))); + + EXPECT_EQ_ARRAY2D(expected, actual); + } + + protected: + void calculate_expected(const test::Array2D& source_a, + const test::Array2D& source_b, + test::Array2D& expected) { + for (size_t hindex = 0; hindex < source_a.height(); ++hindex) { + for (size_t vindex = 0; vindex < source_a.width(); ++vindex) { + ElementType result = + OperationParams::operation()( + source_a.at(hindex, vindex)[0], source_b.at(hindex, vindex)[0]) + ? 255 + : 0; + // NOLINTBEGIN(clang-analyzer-core.uninitialized.Assign) + *expected.at(hindex, vindex) = result; + // NOLINTEND(clang-analyzer-core.uninitialized.Assign) + } + } + } +}; // end of class CompareTestLinearBase + +template class OperationParams> +class CompareTestBase : public BinaryOperationTest { + protected: + // Needed to initialize value() + using BinaryOperationTest::max; + + // Calls the API-under-test in the appropriate way. + kleidicv_error_t call_api() override { + return OperationParams::api()( + this->inputs_[0].data(), this->inputs_[0].stride(), + this->inputs_[1].data(), this->inputs_[1].stride(), + this->actual_[0].data(), this->actual_[0].stride(), this->width(), + this->height()); + } + + // Filling with max() value only necessary for CompareEqual operation + void setup() override { + if (OperationParams::operation()(0, 0)) { + ElementType expected = max(); + this->expected_[0].fill(expected); + BinaryOperationTest::setup(); + } + } + static constexpr ElementType value() { return 255; } +}; // end of class CompareTestBase + +template +class CompareEqTest final + : public CompareTestBase { + using Elements = typename BinaryOperationTest::Elements; + + const std::vector& test_elements() override { + static const std::vector kTestElements = { + // clang-format off + { 12, 13, 0}, + { 13, 13, this->value()}, + { 14, 13, 0}, + // clang-format on + }; + return kTestElements; + } +}; + +template +class CompareEqTestMin final + : public CompareTestBase { + using Elements = typename BinaryOperationTest::Elements; + using BinaryOperationTest::min; + + const std::vector& test_elements() override { + static const std::vector kTestElements = { + // clang-format off + { min(), min(), this->value()}, + {min() + 1, min(), 0}, + // clang-format on + }; + return kTestElements; + } +}; + +template +class CompareEqTestZero final + : public CompareTestBase { + using Elements = typename BinaryOperationTest::Elements; + + const std::vector& test_elements() override { + static const std::vector kTestElements = { + // clang-format off + { 0, 0, this->value()}, + { 1, 0, 0}, + // clang-format on + }; + return kTestElements; + } +}; + +template +class CompareEqTestMax final + : public CompareTestBase { + using Elements = typename BinaryOperationTest::Elements; + using BinaryOperationTest::max; + + const std::vector& test_elements() override { + static const std::vector kTestElements = { + // clang-format off + { max(), max(), this->value()}, + { max(), max() - 1, 0}, + // clang-format on + }; + return kTestElements; + } +}; + +template +class CompareGtTest final + : public CompareTestBase { + using Elements = typename BinaryOperationTest::Elements; + + const std::vector& test_elements() override { + static const std::vector kTestElements = { + // clang-format off + { 12, 13, 0}, + { 13, 13, 0}, + { 14, 13, this->value()}, + // clang-format on + }; + return kTestElements; + } +}; + +template +class CompareGtTestMin final + : public CompareTestBase { + using Elements = typename BinaryOperationTest::Elements; + using BinaryOperationTest::min; + + const std::vector& test_elements() override { + static const std::vector kTestElements = { + // clang-format off + { min(), min(), 0}, + {min() + 1, min(), this->value()}, + // clang-format on + }; + return kTestElements; + } +}; + +template +class CompareGtTestZero final + : public CompareTestBase { + using Elements = typename BinaryOperationTest::Elements; + + const std::vector& test_elements() override { + static const std::vector kTestElements = { + // clang-format off + { 0, 0, 0}, + { 1, 0, this->value()}, + // clang-format on + }; + return kTestElements; + } +}; + +template +class CompareGtTestMax final + : public CompareTestBase { + using Elements = typename BinaryOperationTest::Elements; + using BinaryOperationTest::max; + + const std::vector& test_elements() override { + static const std::vector kTestElements = { + // clang-format off + { max(), max(), 0}, + { max(), max() - 1, this->value()}, + // clang-format on + }; + return kTestElements; + } +}; + +template +class Compare : public testing::Test {}; + +using ElementTypes = ::testing::Types; + +TYPED_TEST_SUITE(Compare, ElementTypes); + +TYPED_TEST(Compare, TestScalarEq) { + CompareTestLinear{}.test_scalar(); +} + +TYPED_TEST(Compare, TestScalarGt) { + CompareTestLinear{}.test_scalar(); +} + +TYPED_TEST(Compare, TestVectorEq) { + CompareTestLinear{}.test_vector(); +} + +TYPED_TEST(Compare, TestVectorGt) { + CompareTestLinear{}.test_vector(); +} + +TYPED_TEST(Compare, Test) { + CompareEqTest{}.test(); + CompareGtTest{}.test(); +} + +// Tests various padding combinations. +TYPED_TEST(Compare, Padding) { + CompareEqTest{}.with_paddings({0}, {0}).test(); + CompareEqTest{}.with_paddings({0}, {1}).test(); + CompareEqTest{}.with_paddings({1}, {0}).test(); + CompareEqTest{}.with_paddings({1}, {1}).test(); + + CompareGtTest{}.with_paddings({0}, {0}).test(); + CompareGtTest{}.with_paddings({0}, {1}).test(); + CompareGtTest{}.with_paddings({1}, {0}).test(); + CompareGtTest{}.with_paddings({1}, {1}).test(); +} + +TYPED_TEST(Compare, TestMin) { + CompareEqTestMin{}.test(); + CompareGtTestMin{}.test(); +} + +TYPED_TEST(Compare, TestZero) { + CompareEqTestZero{}.test(); + CompareGtTestZero{}.test(); +} + +TYPED_TEST(Compare, TestMax) { + CompareEqTestMax{}.test(); + CompareGtTestMax{}.test(); +} + +TYPED_TEST(Compare, EqualNullPointer) { + const TypeParam src_a[1] = {}; + const TypeParam src_b[1] = {}; + TypeParam dst[1]; + test::test_null_args(CompareEqualParams::api(), src_a, + sizeof(TypeParam), src_b, sizeof(TypeParam), dst, + sizeof(TypeParam), 1, 1); +} + +TYPED_TEST(Compare, GreaterNullPointer) { + const TypeParam src_a[1] = {}; + const TypeParam src_b[1] = {}; + TypeParam dst[1]; + test::test_null_args(CompareGreaterParams::api(), src_a, + sizeof(TypeParam), src_b, sizeof(TypeParam), dst, + sizeof(TypeParam), 1, 1); +} + +TYPED_TEST(Compare, EqualMisalignment) { + if (sizeof(TypeParam) == 1) { + // misalignment impossible + return; + } + TypeParam src_a[1] = {}, src_b[1] = {}, dst[1]; + EXPECT_EQ(KLEIDICV_ERROR_ALIGNMENT, + (CompareEqualParams::api()(src_a, sizeof(TypeParam) + 1, + src_b, sizeof(TypeParam), dst, + sizeof(TypeParam), 1, 1))); + EXPECT_EQ(KLEIDICV_ERROR_ALIGNMENT, + (CompareEqualParams::api()( + src_a, sizeof(TypeParam), src_b, sizeof(TypeParam), dst, + sizeof(TypeParam) + 1, 1, 1))); +} + +TYPED_TEST(Compare, GreaterMisalignment) { + if (sizeof(TypeParam) == 1) { + // misalignment impossible + return; + } + TypeParam src_a[1] = {}, src_b[1] = {}, dst[1]; + EXPECT_EQ(KLEIDICV_ERROR_ALIGNMENT, + (CompareGreaterParams::api()( + src_a, sizeof(TypeParam) + 1, src_b, sizeof(TypeParam), dst, + sizeof(TypeParam), 1, 1))); + EXPECT_EQ(KLEIDICV_ERROR_ALIGNMENT, + (CompareGreaterParams::api()( + src_a, sizeof(TypeParam), src_b, sizeof(TypeParam), dst, + sizeof(TypeParam) + 1, 1, 1))); +} + +TYPED_TEST(Compare, EqualZeroImageSize) { + TypeParam src_a[1] = {}, src_b[1] = {}, dst[1]; + EXPECT_EQ(KLEIDICV_OK, (CompareEqualParams::api()( + src_a, sizeof(TypeParam), src_b, sizeof(TypeParam), + dst, sizeof(TypeParam), 0, 1))); + EXPECT_EQ(KLEIDICV_OK, (CompareEqualParams::api()( + src_a, sizeof(TypeParam), src_b, sizeof(TypeParam), + dst, sizeof(TypeParam), 1, 0))); +} + +TYPED_TEST(Compare, GreaterZeroImageSize) { + TypeParam src_a[1] = {}, src_b[1] = {}, dst[1]; + EXPECT_EQ(KLEIDICV_OK, (CompareGreaterParams::api()( + src_a, sizeof(TypeParam), src_b, sizeof(TypeParam), + dst, sizeof(TypeParam), 0, 1))); + EXPECT_EQ(KLEIDICV_OK, (CompareGreaterParams::api()( + src_a, sizeof(TypeParam), src_b, sizeof(TypeParam), + dst, sizeof(TypeParam), 1, 0))); +} + +TYPED_TEST(Compare, EqualOversizeImage) { + TypeParam src_a[1] = {}, src_b[1] = {}, dst[1]; + EXPECT_EQ(KLEIDICV_ERROR_RANGE, + (CompareEqualParams::api()( + src_a, sizeof(TypeParam), src_b, sizeof(TypeParam), dst, + sizeof(TypeParam), KLEIDICV_MAX_IMAGE_PIXELS + 1, 1))); + EXPECT_EQ(KLEIDICV_ERROR_RANGE, + (CompareEqualParams::api()( + src_a, sizeof(TypeParam), src_b, sizeof(TypeParam), dst, + sizeof(TypeParam), KLEIDICV_MAX_IMAGE_PIXELS, + KLEIDICV_MAX_IMAGE_PIXELS))); +} + +TYPED_TEST(Compare, GreaterOversizeImage) { + TypeParam src_a[1] = {}, src_b[1] = {}, dst[1]; + EXPECT_EQ(KLEIDICV_ERROR_RANGE, + (CompareGreaterParams::api()( + src_a, sizeof(TypeParam), src_b, sizeof(TypeParam), dst, + sizeof(TypeParam), KLEIDICV_MAX_IMAGE_PIXELS + 1, 1))); + EXPECT_EQ(KLEIDICV_ERROR_RANGE, + (CompareGreaterParams::api()( + src_a, sizeof(TypeParam), src_b, sizeof(TypeParam), dst, + sizeof(TypeParam), KLEIDICV_MAX_IMAGE_PIXELS, + KLEIDICV_MAX_IMAGE_PIXELS))); +} -- GitLab From 9b8d8de62e0427697f39afa9c702d9ffb7a1ce0a Mon Sep 17 00:00:00 2001 From: Ioana Ghiban Date: Mon, 13 May 2024 17:46:30 +0200 Subject: [PATCH 4/7] Add implementation for SME2/SVE2 Compare EQ and GT --- kleidicv/src/arithmetics/compare_api.cpp | 57 +++++++++++++++---- kleidicv/src/arithmetics/compare_sc.h | 68 +++++++++++++++++++++++ kleidicv/src/arithmetics/compare_sme2.cpp | 36 ++++++++++++ kleidicv/src/arithmetics/compare_sve2.cpp | 36 ++++++++++++ 4 files changed, 185 insertions(+), 12 deletions(-) create mode 100644 kleidicv/src/arithmetics/compare_sc.h create mode 100644 kleidicv/src/arithmetics/compare_sme2.cpp create mode 100644 kleidicv/src/arithmetics/compare_sve2.cpp diff --git a/kleidicv/src/arithmetics/compare_api.cpp b/kleidicv/src/arithmetics/compare_api.cpp index 1e416a956..7f67fafbc 100644 --- a/kleidicv/src/arithmetics/compare_api.cpp +++ b/kleidicv/src/arithmetics/compare_api.cpp @@ -8,22 +8,55 @@ namespace kleidicv { namespace neon { -template -kleidicv_error_t compare(const T *src_a, size_t src_a_stride, const T *src_b, - size_t src_b_stride, T *dst, size_t dst_stride, - size_t width, size_t height, - kleidicv_cmp_type_t cmp_type); +template +kleidicv_error_t compare_equal(const ScalarType *src_a, size_t src_a_stride, + const ScalarType *src_b, size_t src_b_stride, + ScalarType *dst, size_t dst_stride, size_t width, + size_t height); + +template +kleidicv_error_t compare_greater(const ScalarType *src_a, size_t src_a_stride, + const ScalarType *src_b, size_t src_b_stride, + ScalarType *dst, size_t dst_stride, + size_t width, size_t height); } // namespace neon -namespace sve2 {} // namespace sve2 - -namespace sme2 {} // namespace sme2 +namespace sve2 { +template +kleidicv_error_t compare_equal(const ScalarType *src_a, size_t src_a_stride, + const ScalarType *src_b, size_t src_b_stride, + ScalarType *dst, size_t dst_stride, size_t width, + size_t height); + +template +kleidicv_error_t compare_greater(const ScalarType *src_a, size_t src_a_stride, + const ScalarType *src_b, size_t src_b_stride, + ScalarType *dst, size_t dst_stride, + size_t width, size_t height); +} // namespace sve2 + +namespace sme2 { +template +kleidicv_error_t compare_equal(const ScalarType *src_a, size_t src_a_stride, + const ScalarType *src_b, size_t src_b_stride, + ScalarType *dst, size_t dst_stride, size_t width, + size_t height); + +template +kleidicv_error_t compare_greater(const ScalarType *src_a, size_t src_a_stride, + const ScalarType *src_b, size_t src_b_stride, + ScalarType *dst, size_t dst_stride, + size_t width, size_t height); +} // namespace sme2 } // namespace kleidicv -#define KLEIDICV_DEFINE_C_API(name, type) \ - KLEIDICV_MULTIVERSION_C_API(name, &kleidicv::neon::compare, nullptr, \ - nullptr); +#define KLEIDICV_DEFINE_C_API(api_name, impl_name, type) \ + KLEIDICV_MULTIVERSION_C_API( \ + api_name, &kleidicv::neon::impl_name, \ + KLEIDICV_SVE2_IMPL_IF(&kleidicv::sve2::impl_name), \ + &kleidicv::sme2::impl_name) -KLEIDICV_DEFINE_C_API(kleidicv_compare_u8, uint8_t); +KLEIDICV_DEFINE_C_API(kleidicv_compare_equal_u8, compare_equal, uint8_t); +KLEIDICV_DEFINE_C_API(kleidicv_compare_greater_u8, compare_greater, uint8_t); diff --git a/kleidicv/src/arithmetics/compare_sc.h b/kleidicv/src/arithmetics/compare_sc.h new file mode 100644 index 000000000..493999652 --- /dev/null +++ b/kleidicv/src/arithmetics/compare_sc.h @@ -0,0 +1,68 @@ +// SPDX-FileCopyrightText: 2023 - 2024 Arm Limited and/or its affiliates +// +// SPDX-License-Identifier: Apache-2.0 + +#ifndef KLEIDICV_THRESHOLD_SC_H +#define KLEIDICV_THRESHOLD_SC_H + +#include "kleidicv/kleidicv.h" +#include "kleidicv/sve2.h" + +namespace KLEIDICV_TARGET_NAMESPACE { + +template +class ComparatorEqual : public UnrollTwice { + public: + using ContextType = Context; + using VecTraits = KLEIDICV_TARGET_NAMESPACE::VecTraits; + using VectorType = typename VecTraits::VectorType; + + // NOLINTBEGIN(readability-make-member-function-const) + VectorType vector_path(ContextType ctx, VectorType src_a, + VectorType src_b) KLEIDICV_STREAMING_COMPATIBLE { + svbool_t predicate = svcmpeq(ctx.predicate(), src_a, src_b); + return svsel(predicate, VecTraits::svdup(255), VecTraits::svdup(0)); + } + // NOLINTEND(readability-make-member-function-const) +}; // end of class ComparatorEqual + +template +class ComparatorGreater : public UnrollTwice { + public: + using ContextType = Context; + using VecTraits = KLEIDICV_TARGET_NAMESPACE::VecTraits; + using VectorType = typename VecTraits::VectorType; + + // NOLINTBEGIN(readability-make-member-function-const) + VectorType vector_path(ContextType ctx, VectorType src_a, + VectorType src_b) KLEIDICV_STREAMING_COMPATIBLE { + svbool_t predicate = svcmpgt(ctx.predicate(), src_a, src_b); + return svsel(predicate, VecTraits::svdup(255), VecTraits::svdup(0)); + } + // NOLINTEND(readability-make-member-function-const) +}; // end of class ComparatorGreater + +template +kleidicv_error_t compare_sc(const ScalarType *src_a, size_t src_a_stride, + const ScalarType *src_b, size_t src_b_stride, + ScalarType *dst, size_t dst_stride, size_t width, + size_t height) KLEIDICV_STREAMING_COMPATIBLE { + CHECK_POINTER_AND_STRIDE(src_a, src_a_stride); + CHECK_POINTER_AND_STRIDE(src_b, src_b_stride); + CHECK_POINTER_AND_STRIDE(dst, dst_stride); + CHECK_IMAGE_SIZE(width, height); + + Comparator operation{}; + Rectangle rect{width, height}; + Rows src_a_rows{src_a, src_a_stride}; + Rows src_b_rows{src_b, src_b_stride}; + Rows dst_rows{dst, dst_stride}; + + apply_operation_by_rows(operation, rect, src_a_rows, src_b_rows, dst_rows); + + return KLEIDICV_OK; +} + +} // namespace KLEIDICV_TARGET_NAMESPACE + +#endif // KLEIDICV_COMPARE_SC_H diff --git a/kleidicv/src/arithmetics/compare_sme2.cpp b/kleidicv/src/arithmetics/compare_sme2.cpp new file mode 100644 index 000000000..407e2206b --- /dev/null +++ b/kleidicv/src/arithmetics/compare_sme2.cpp @@ -0,0 +1,36 @@ +// SPDX-FileCopyrightText: 2023 Arm Limited and/or its affiliates +// +// SPDX-License-Identifier: Apache-2.0 + +#include "compare_sc.h" + +namespace kleidicv::sme2 { + +template +KLEIDICV_LOCALLY_STREAMING kleidicv_error_t +compare_equal(const ScalarType *src_a, size_t src_a_stride, + const ScalarType *src_b, size_t src_b_stride, ScalarType *dst, + size_t dst_stride, size_t width, size_t height) { + return compare_sc>( + src_a, src_a_stride, src_b, src_b_stride, dst, dst_stride, width, height); +} + +template +KLEIDICV_LOCALLY_STREAMING kleidicv_error_t +compare_greater(const ScalarType *src_a, size_t src_a_stride, + const ScalarType *src_b, size_t src_b_stride, ScalarType *dst, + size_t dst_stride, size_t width, size_t height) { + return compare_sc>( + src_a, src_a_stride, src_b, src_b_stride, dst, dst_stride, width, height); +} + +#define KLEIDICV_INSTANTIATE_TEMPLATE(name, type) \ + template KLEIDICV_TARGET_FN_ATTRS kleidicv_error_t name( \ + const type *src_a, size_t src_a_stride, const type *src_b, \ + size_t src_b_stride, type *dst, size_t dst_stride, size_t width, \ + size_t height) + +KLEIDICV_INSTANTIATE_TEMPLATE(compare_equal, uint8_t); +KLEIDICV_INSTANTIATE_TEMPLATE(compare_greater, uint8_t); + +} // namespace kleidicv::sme2 diff --git a/kleidicv/src/arithmetics/compare_sve2.cpp b/kleidicv/src/arithmetics/compare_sve2.cpp new file mode 100644 index 000000000..1863845dd --- /dev/null +++ b/kleidicv/src/arithmetics/compare_sve2.cpp @@ -0,0 +1,36 @@ +// SPDX-FileCopyrightText: 2023 Arm Limited and/or its affiliates +// +// SPDX-License-Identifier: Apache-2.0 + +#include "compare_sc.h" + +namespace kleidicv::sve2 { + +template +KLEIDICV_LOCALLY_STREAMING kleidicv_error_t +compare_equal(const ScalarType *src_a, size_t src_a_stride, + const ScalarType *src_b, size_t src_b_stride, ScalarType *dst, + size_t dst_stride, size_t width, size_t height) { + return compare_sc>( + src_a, src_a_stride, src_b, src_b_stride, dst, dst_stride, width, height); +} + +template +KLEIDICV_LOCALLY_STREAMING kleidicv_error_t +compare_greater(const ScalarType *src_a, size_t src_a_stride, + const ScalarType *src_b, size_t src_b_stride, ScalarType *dst, + size_t dst_stride, size_t width, size_t height) { + return compare_sc>( + src_a, src_a_stride, src_b, src_b_stride, dst, dst_stride, width, height); +} + +#define KLEIDICV_INSTANTIATE_TEMPLATE(name, type) \ + template KLEIDICV_TARGET_FN_ATTRS kleidicv_error_t name( \ + const type *src_a, size_t src_a_stride, const type *src_b, \ + size_t src_b_stride, type *dst, size_t dst_stride, size_t width, \ + size_t height) + +KLEIDICV_INSTANTIATE_TEMPLATE(compare_equal, uint8_t); +KLEIDICV_INSTANTIATE_TEMPLATE(compare_greater, uint8_t); + +} // namespace kleidicv::sve2 -- GitLab From 873176037dfcd50a0ed3ef6c2e144091bb13b206 Mon Sep 17 00:00:00 2001 From: Ioana Ghiban Date: Thu, 16 May 2024 14:49:56 +0200 Subject: [PATCH 5/7] Expose compare_u8 in OpenCV HAL --- adapters/opencv/CMakeLists.txt | 8 +++++++- adapters/opencv/kleidicv_hal.cpp | 19 +++++++++++++++++++ adapters/opencv/kleidicv_hal.h | 16 ++++++++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/adapters/opencv/CMakeLists.txt b/adapters/opencv/CMakeLists.txt index 3be5fa7b6..1317b966d 100644 --- a/adapters/opencv/CMakeLists.txt +++ b/adapters/opencv/CMakeLists.txt @@ -19,7 +19,13 @@ target_include_directories(kleidicv_hal PRIVATE ${OpenCV_SOURCE_DIR}/modules/core/include ${OpenCV_SOURCE_DIR}/modules/imgproc/include ) -target_compile_options(kleidicv_hal PRIVATE $) +target_compile_options( + kleidicv_hal + PRIVATE + $ + "-Wno-old-style-cast" +) + set_target_properties(kleidicv_hal PROPERTIES CXX_STANDARD 17) if(NOT BUILD_SHARED_LIBS) diff --git a/adapters/opencv/kleidicv_hal.cpp b/adapters/opencv/kleidicv_hal.cpp index 67b769b19..04b672a08 100644 --- a/adapters/opencv/kleidicv_hal.cpp +++ b/adapters/opencv/kleidicv_hal.cpp @@ -11,6 +11,8 @@ #include #include "kleidicv/kleidicv.h" +#include "opencv2/core/base.hpp" +#include "opencv2/core/hal/interface.h" #include "opencv2/imgproc/hal/interface.h" namespace kleidicv::hal { @@ -713,4 +715,21 @@ int exp32f(const float *src, float *dst, int len) { len * sizeof(float), len, 1)); } +int compare_u8(const uchar *src1_data, size_t src1_step, const uchar *src2_data, + size_t src2_step, uchar *dst_data, size_t dst_step, int width, + int height, int operation) { + switch (operation) { + case cv::CMP_EQ: + return convert_error( + kleidicv_compare_equal_u8(src1_data, src1_step, src2_data, src2_step, + dst_data, dst_step, width, height)); + case cv::CMP_GT: + return convert_error(kleidicv_compare_greater_u8( + src1_data, src1_step, src2_data, src2_step, dst_data, dst_step, width, + height)); + default: + return CV_HAL_ERROR_NOT_IMPLEMENTED; + } +} + } // namespace kleidicv::hal diff --git a/adapters/opencv/kleidicv_hal.h b/adapters/opencv/kleidicv_hal.h index 25f1b9587..5a662e213 100644 --- a/adapters/opencv/kleidicv_hal.h +++ b/adapters/opencv/kleidicv_hal.h @@ -80,6 +80,10 @@ int sobel(const uchar *src_data, size_t src_step, uchar *dst_data, int margin_bottom, int dx, int dy, int ksize, double scale, double delta, int border_type); +int compare_u8(const uchar *src1_data, size_t src1_step, const uchar *src2_data, + size_t src2_step, uchar *dst_data, size_t dst_step, int width, + int height, int operation); + #if KLEIDICV_EXPERIMENTAL_FEATURE_CANNY int canny(const uchar *src_data, size_t src_step, uchar *dst_data, size_t dst_step, int width, int height, int cn, double lowThreshold, @@ -323,6 +327,18 @@ static inline int kleidicv_exp32f_with_fallback(const float *src, float *dst, #undef cv_hal_exp32f #define cv_hal_exp32f kleidicv_exp32f_with_fallback +// compare +static inline int kleidicv_compare_u8_with_fallback( + const uchar *src1_data, size_t src1_step, const uchar *src2_data, + size_t src2_step, uchar *dst_data, size_t dst_step, int width, int height, + int operation) { + return KLEIDICV_HAL_FALLBACK_FORWARD( + compare_u8, cv_hal_cmp8u, src1_data, src1_step, src2_data, src2_step, + dst_data, dst_step, width, height, operation); +} +#undef cv_hal_cmp8u +#define cv_hal_cmp8u kleidicv_compare_u8_with_fallback + #endif // OPENCV_CORE_HAL_REPLACEMENT_HPP // Remove no longer needed macro definitions. -- GitLab From 165d7b21bfb51480bcb4824aa06503951f2d5b1b Mon Sep 17 00:00:00 2001 From: Ioana Ghiban Date: Thu, 16 May 2024 14:52:52 +0200 Subject: [PATCH 6/7] Add compare_u8 to functionality.md --- doc/functionality.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/functionality.md b/doc/functionality.md index 79368aeae..d09a7a721 100644 --- a/doc/functionality.md +++ b/doc/functionality.md @@ -19,6 +19,8 @@ See `doc/opencv.md` for details of the functionality available in OpenCV. | Threshold binary | | x | | | | | | | | | | SaturatingAddAbsWithThreshold| | | x | | | | | | | | | Scale | | x | | | | | | | | | +| CompareEqual | | x | | | | | | | | | +| CompareGreater | | x | | | | | | | | | # Logical operations | | u8 | -- GitLab From f74dac6f562c5bb2f7f27473dc95705c67504d91 Mon Sep 17 00:00:00 2001 From: Ioana Ghiban Date: Thu, 16 May 2024 15:09:45 +0200 Subject: [PATCH 7/7] Add compare_u8 to opencv.md --- doc/opencv.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/doc/opencv.md b/doc/opencv.md index aa687ec16..0aa0bb525 100644 --- a/doc/opencv.md +++ b/doc/opencv.md @@ -171,3 +171,14 @@ Currently converting to different data types is not supported. This function sca ### `exp` Exponential function. Currently only `CV_32F` type is supported. + +### `compare` +Performs element-wise comparison of two arrays. +Currently comparing an array and scalar value is not supported (the HAL only allows for matching sizes of the sources). + +Notes on parameters: +* `src1_data`, `src2_data`, `dst_data` - only support `CV_8U` depth. +* `operation` - flag specifying correspondence between the arrays. +Supported [OpenCV cmp types](https://docs.opencv.org/5.x/d2/de8/group__core__array.html#ga0cc47ff833d40b58ecbe1d609a53d784) are: + + `cv::CMP_EQ ` + + `cv::CMP_GT` -- GitLab