From dcbb92c56126bbf85629b513e7593506ed5a7392 Mon Sep 17 00:00:00 2001 From: Michael Platings Date: Thu, 30 May 2024 10:59:57 +0000 Subject: [PATCH] Add bitwise_and --- CHANGELOG.md | 2 +- benchmark/benchmark.cpp | 1 + doc/functionality.md | 5 + kleidicv/include/kleidicv/kleidicv.h | 27 +++++ kleidicv/include/kleidicv/neon_intrinsics.h | 8 ++ kleidicv/src/logical/bitwise_and_api.cpp | 45 ++++++++ kleidicv/src/logical/bitwise_and_neon.cpp | 54 ++++++++++ kleidicv/src/logical/bitwise_and_sc.h | 47 ++++++++ kleidicv/src/logical/bitwise_and_sme2.cpp | 25 +++++ kleidicv/src/logical/bitwise_and_sve2.cpp | 25 +++++ test/api/test_bitwise_and.cpp | 113 ++++++++++++++++++++ 11 files changed, 351 insertions(+), 1 deletion(-) create mode 100644 kleidicv/src/logical/bitwise_and_api.cpp create mode 100644 kleidicv/src/logical/bitwise_and_neon.cpp create mode 100644 kleidicv/src/logical/bitwise_and_sc.h create mode 100644 kleidicv/src/logical/bitwise_and_sme2.cpp create mode 100644 kleidicv/src/logical/bitwise_and_sve2.cpp create mode 100644 test/api/test_bitwise_and.cpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 240a62e8c..096a534e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,7 @@ This changelog aims to follow the guiding principles of ### Added - Exponential function for float. - +- Bitwise and. - Gaussian Blur for 7x7 kernels. ### Fixed diff --git a/benchmark/benchmark.cpp b/benchmark/benchmark.cpp index 8da446649..73dc7547b 100644 --- a/benchmark/benchmark.cpp +++ b/benchmark/benchmark.cpp @@ -40,6 +40,7 @@ static void bench_binary_op(Function f, benchmark::State& state) { BENCH_BINARY_OP(saturating_add_s8, int8_t); BENCH_BINARY_OP(saturating_sub_u16, uint16_t); BENCH_BINARY_OP(saturating_absdiff_s32, int32_t); +BENCH_BINARY_OP(bitwise_and, uint8_t); template static void bench_unary_op(Function f, size_t channels, diff --git a/doc/functionality.md b/doc/functionality.md index 874f5442d..79368aeae 100644 --- a/doc/functionality.md +++ b/doc/functionality.md @@ -20,6 +20,11 @@ See `doc/opencv.md` for details of the functionality available in OpenCV. | SaturatingAddAbsWithThreshold| | | x | | | | | | | | | Scale | | x | | | | | | | | | +# Logical operations +| | u8 | +|------------------------------|-----| +| Bitwise And | x | + ## Color conversions | | u8 | |-----------|-----| diff --git a/kleidicv/include/kleidicv/kleidicv.h b/kleidicv/include/kleidicv/kleidicv.h index 17f3298e6..c146a5b80 100644 --- a/kleidicv/include/kleidicv/kleidicv.h +++ b/kleidicv/include/kleidicv/kleidicv.h @@ -264,6 +264,33 @@ KLEIDICV_API_DECLARATION(kleidicv_saturating_add_abs_with_threshold_s16, int16_t *dst, size_t dst_stride, size_t width, size_t height, int16_t threshold); +/// Bitwise-ands the values of the corresponding elements in `src_a` and +/// `src_b`, and puts the result into `dst`. +/// +/// Source data length (in bytes) is `stride` * `height`. Width and height are +/// the same for the two sources 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_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_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 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 be a multiple of sizeof(type). +/// Must not be less than width * sizeof(type). +/// @param width Number of elements in a row. +/// @param height Number of rows in the data. +/// +KLEIDICV_BINARY_OP(kleidicv_bitwise_and, uint8_t); + /// Converts a grayscale image to RGB. All channels are 8-bit wide. /// /// Destination data is filled as follows: R = G = B = Gray diff --git a/kleidicv/include/kleidicv/neon_intrinsics.h b/kleidicv/include/kleidicv/neon_intrinsics.h index ed11762fb..62053bc8e 100644 --- a/kleidicv/include/kleidicv/neon_intrinsics.h +++ b/kleidicv/include/kleidicv/neon_intrinsics.h @@ -80,6 +80,14 @@ static inline uint16x8_t vabdq(uint16x8_t lhs, uint16x8_t rhs) { return vabdq_u1 static inline int32x4_t vabdq(int32x4_t lhs, int32x4_t rhs) { return vabdq_s32(lhs, rhs); } static inline uint32x4_t vabdq(uint32x4_t lhs, uint32x4_t rhs) { return vabdq_u32(lhs, rhs); } +// ----------------------------------------------------------------------------- +// vand* +// ----------------------------------------------------------------------------- + +static inline uint8x16_t vandq(uint8x16_t lhs, uint8x16_t rhs) { return vandq_u8(lhs, rhs); } +static inline uint16x8_t vandq(uint16x8_t lhs, uint16x8_t rhs) { return vandq_u16(lhs, rhs); } +static inline uint32x4_t vandq(uint32x4_t lhs, uint32x4_t rhs) { return vandq_u32(lhs, rhs); } + // ----------------------------------------------------------------------------- // vqabs* // ----------------------------------------------------------------------------- diff --git a/kleidicv/src/logical/bitwise_and_api.cpp b/kleidicv/src/logical/bitwise_and_api.cpp new file mode 100644 index 000000000..6bf0cd89f --- /dev/null +++ b/kleidicv/src/logical/bitwise_and_api.cpp @@ -0,0 +1,45 @@ +// SPDX-FileCopyrightText: 2024 Arm Limited and/or its affiliates +// +// SPDX-License-Identifier: Apache-2.0 + +#include "kleidicv/dispatch.h" +#include "kleidicv/kleidicv.h" +#include "kleidicv/types.h" + +namespace kleidicv { + +namespace neon { + +template +kleidicv_error_t bitwise_and(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); + +} // namespace neon + +namespace sve2 { + +template +kleidicv_error_t bitwise_and(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); + +} // namespace sve2 + +namespace sme2 { +template +kleidicv_error_t bitwise_and(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); + +} // namespace sme2 + +} // namespace kleidicv + +#define KLEIDICV_DEFINE_C_API(name, type) \ + KLEIDICV_MULTIVERSION_C_API( \ + name, &kleidicv::neon::bitwise_and, \ + KLEIDICV_SVE2_IMPL_IF(&kleidicv::sve2::bitwise_and), \ + &kleidicv::sme2::bitwise_and) + +KLEIDICV_DEFINE_C_API(kleidicv_bitwise_and, uint8_t); diff --git a/kleidicv/src/logical/bitwise_and_neon.cpp b/kleidicv/src/logical/bitwise_and_neon.cpp new file mode 100644 index 000000000..39393cbc1 --- /dev/null +++ b/kleidicv/src/logical/bitwise_and_neon.cpp @@ -0,0 +1,54 @@ +// SPDX-FileCopyrightText: 2024 Arm Limited and/or its affiliates +// +// SPDX-License-Identifier: Apache-2.0 + +#include + +#include "kleidicv/kleidicv.h" +#include "kleidicv/neon.h" + +namespace kleidicv::neon { + +template +class BitwiseAnd final : public UnrollTwice { + public: + using VecTraits = neon::VecTraits; + using VectorType = typename VecTraits::VectorType; + + VectorType vector_path(VectorType src_a, VectorType src_b) { + return vandq(src_a, src_b); + } + + ScalarType scalar_path(ScalarType src_a, ScalarType src_b) { + return src_a & src_b; + } +}; // end of class BitwiseAnd + +template +kleidicv_error_t bitwise_and(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) { + 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); + + BitwiseAnd 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}; + neon::apply_operation_by_rows(operation, rect, src_a_rows, src_b_rows, + dst_rows); + return KLEIDICV_OK; +} + +#define KLEIDICV_INSTANTIATE_TEMPLATE(type) \ + template KLEIDICV_TARGET_FN_ATTRS kleidicv_error_t bitwise_and( \ + 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(uint8_t); + +} // namespace kleidicv::neon diff --git a/kleidicv/src/logical/bitwise_and_sc.h b/kleidicv/src/logical/bitwise_and_sc.h new file mode 100644 index 000000000..85f9ed63d --- /dev/null +++ b/kleidicv/src/logical/bitwise_and_sc.h @@ -0,0 +1,47 @@ +// SPDX-FileCopyrightText: 2024 Arm Limited and/or its affiliates +// +// SPDX-License-Identifier: Apache-2.0 + +#ifndef KLEIDICV_BITWISE_AND_SC_H +#define KLEIDICV_BITWISE_AND_SC_H + +#include "kleidicv/kleidicv.h" +#include "kleidicv/sve2.h" + +namespace KLEIDICV_TARGET_NAMESPACE { + +template +class BitwiseAnd final : public UnrollTwice { + public: + using ContextType = Context; + using VecTraits = KLEIDICV_TARGET_NAMESPACE::VecTraits; + using VectorType = typename VecTraits::VectorType; + + VectorType vector_path(ContextType ctx, VectorType src_a, + VectorType src_b) KLEIDICV_STREAMING_COMPATIBLE { + return svand_x(ctx.predicate(), src_a, src_b); + } +}; // end of class BitwiseAnd + +template +kleidicv_error_t bitwise_and_sc(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_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); + + BitwiseAnd 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_BITWISE_AND_SC_H diff --git a/kleidicv/src/logical/bitwise_and_sme2.cpp b/kleidicv/src/logical/bitwise_and_sme2.cpp new file mode 100644 index 000000000..b380babe9 --- /dev/null +++ b/kleidicv/src/logical/bitwise_and_sme2.cpp @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText: 2024 Arm Limited and/or its affiliates +// +// SPDX-License-Identifier: Apache-2.0 + +#include "bitwise_and_sc.h" + +namespace kleidicv::sme2 { + +template +KLEIDICV_LOCALLY_STREAMING kleidicv_error_t bitwise_and( + 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) { + return bitwise_and_sc(src_a, src_a_stride, src_b, src_b_stride, dst, + dst_stride, width, height); +} + +#define KLEIDICV_INSTANTIATE_TEMPLATE(type) \ + template KLEIDICV_TARGET_FN_ATTRS kleidicv_error_t bitwise_and( \ + 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(uint8_t); + +} // namespace kleidicv::sme2 diff --git a/kleidicv/src/logical/bitwise_and_sve2.cpp b/kleidicv/src/logical/bitwise_and_sve2.cpp new file mode 100644 index 000000000..37aa81beb --- /dev/null +++ b/kleidicv/src/logical/bitwise_and_sve2.cpp @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText: 2024 Arm Limited and/or its affiliates +// +// SPDX-License-Identifier: Apache-2.0 + +#include "bitwise_and_sc.h" + +namespace kleidicv::sve2 { + +template +kleidicv_error_t bitwise_and(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) { + return bitwise_and_sc(src_a, src_a_stride, src_b, src_b_stride, dst, + dst_stride, width, height); +} + +#define KLEIDICV_INSTANTIATE_TEMPLATE(type) \ + template KLEIDICV_TARGET_FN_ATTRS kleidicv_error_t bitwise_and( \ + 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(uint8_t); + +} // namespace kleidicv::sve2 diff --git a/test/api/test_bitwise_and.cpp b/test/api/test_bitwise_and.cpp new file mode 100644 index 000000000..2cd70685b --- /dev/null +++ b/test/api/test_bitwise_and.cpp @@ -0,0 +1,113 @@ +// SPDX-FileCopyrightText: 2024 Arm Limited and/or its affiliates +// +// SPDX-License-Identifier: Apache-2.0 + +#include + +#include + +#include "framework/operation.h" +#include "kleidicv/kleidicv.h" +#include "test_config.h" + +KLEIDICV_API(bitwise_and, kleidicv_bitwise_and, uint8_t) + +template +class BitwiseAndTest final : public BinaryOperationTest { + // Expose constructor of base class. + using BinaryOperationTest::BinaryOperationTest; + + protected: + using Elements = typename BinaryOperationTest::Elements; + using BinaryOperationTest::max; + + // Calls the API-under-test in the appropriate way. + kleidicv_error_t call_api() override { + return bitwise_and()( + 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()); + } + + const std::vector& test_elements() override { + static const std::vector kTestElements = { + // clang-format off + { 0, 0, 0}, + { 1, 1, 1}, + { 2, 1, 0}, + { 6, 12, 4}, + { 0, max(), 0}, + {max(), 0, 0}, + { 2, max(), 2}, + {max(), max(), max()}, + {max(), max() - 1, max() - 1}, + // clang-format on + }; + + return kTestElements; + } +}; // end of class BitwiseAndTest + +template +class BitwiseAnd : public testing::Test {}; + +using ElementTypes = ::testing::Types; +TYPED_TEST_SUITE(BitwiseAnd, ElementTypes); + +// Tests kleidicv_bitwise_and API. +TYPED_TEST(BitwiseAnd, API) { + // Test without padding. + BitwiseAndTest{}.test(); + // Test with padding. + BitwiseAndTest{} + .with_padding(test::Options::vector_length()) + .test(); + + TypeParam src[1], dst[1]; + test::test_null_args(bitwise_and(), src, sizeof(TypeParam), src, + sizeof(TypeParam), dst, sizeof(TypeParam), 1, 1); +} + +TYPED_TEST(BitwiseAnd, Misalignment) { + if (sizeof(TypeParam) == 1) { + // misalignment impossible + return; + } + TypeParam src[1] = {}, dst[1]; + EXPECT_EQ(KLEIDICV_ERROR_ALIGNMENT, + bitwise_and()(src, sizeof(TypeParam) + 1, src, + sizeof(TypeParam), dst, sizeof(TypeParam), + 1, 1)); + EXPECT_EQ(KLEIDICV_ERROR_ALIGNMENT, + bitwise_and()(src, sizeof(TypeParam), src, + sizeof(TypeParam) + 1, dst, + sizeof(TypeParam), 1, 1)); + EXPECT_EQ( + KLEIDICV_ERROR_ALIGNMENT, + bitwise_and()(src, sizeof(TypeParam), src, sizeof(TypeParam), + dst, sizeof(TypeParam) + 1, 1, 1)); +} + +TYPED_TEST(BitwiseAnd, ZeroImageSize) { + TypeParam src[1] = {}, dst[1]; + EXPECT_EQ(KLEIDICV_OK, bitwise_and()(src, sizeof(TypeParam), src, + sizeof(TypeParam), dst, + sizeof(TypeParam), 0, 1)); + EXPECT_EQ(KLEIDICV_OK, bitwise_and()(src, sizeof(TypeParam), src, + sizeof(TypeParam), dst, + sizeof(TypeParam), 1, 0)); +} + +TYPED_TEST(BitwiseAnd, OversizeImage) { + TypeParam src[1] = {}, dst[1]; + EXPECT_EQ(KLEIDICV_ERROR_RANGE, + bitwise_and()(src, sizeof(TypeParam), src, + sizeof(TypeParam), dst, sizeof(TypeParam), + KLEIDICV_MAX_IMAGE_PIXELS + 1, 1)); + EXPECT_EQ(KLEIDICV_ERROR_RANGE, + bitwise_and()(src, sizeof(TypeParam), src, + sizeof(TypeParam), dst, sizeof(TypeParam), + KLEIDICV_MAX_IMAGE_PIXELS, + KLEIDICV_MAX_IMAGE_PIXELS)); +} -- GitLab