diff --git a/intrinsiccv/include/intrinsiccv.h b/intrinsiccv/include/intrinsiccv.h index ee61c43708e63d4ef07308450b4d47faf33943bc..dfb1b2f1517df81a0b6bc31e8367774b25678f3c 100644 --- a/intrinsiccv/include/intrinsiccv.h +++ b/intrinsiccv/include/intrinsiccv.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023-2024 Arm Limited and/or its affiliates +// SPDX-FileCopyrightText: 2023 - 2024 Arm Limited and/or its affiliates // // SPDX-License-Identifier: Apache-2.0 @@ -571,6 +571,28 @@ void INTRINSICCV_C_API(gaussian_blur_5x5_u8)( intrinsiccv_border_type_t border_type, const intrinsiccv_filter_params_t *params); +/// Splits a multi channel source stream into separate 1-channel streams. +/// +/// @param src_data 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) * channels. +/// @param dst_data A C style array of pointers to the destination data. +/// Number of pointers in the array must be the same as the +/// channel number. All pointers must be non-null. +/// @param dst_strides A C style array of stride values for the destination +/// streams. A stride value represents the distance in +/// bytes from the start of one row to the start of the +/// next row in the given destination stream. Number of +/// stride values in the array must be the same as the +/// channel number. All stride values must not be less than +/// width * (element size in bytes). +/// @param width How many pixels are in one row of the source. (One +/// pixel consists of 'channels' number of elements.) +/// @param height How many rows are in the source. +/// @param channels Number of channels in the source. +/// @param element_size Size of one element in bytes. +/// void INTRINSICCV_C_API(split)(const void *src_data, size_t src_stride, void **dst_data, size_t *dst_strides, size_t width, size_t height, size_t channels, @@ -580,6 +602,27 @@ void INTRINSICCV_C_API(transpose)(const void *src, size_t src_stride, void *dst, size_t dst_stride, size_t src_width, size_t src_height, size_t element_size); +/// Merges separate 1-channel source streams to one multi channel stream. +/// +/// @param srcs A C style array of pointers to the source data. +/// Number of pointers in the array must be the same as the +/// channel number. All pointers must be non-null. +/// @param src_strides A C style array of stride values for the source +/// streams. A stride value represents the distance in +/// bytes from the start of one row to the start of the +/// next row in the given source stream. Number of +/// stride values in the array must be the same as the +/// channel number. All stride values must not be less than +/// width * (element size in bytes). +/// @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 in the destination data. Must not +/// be less than width * (element size in bytes) * channels. +/// @param width How many elements are in a row in the source. +/// @param height How many rows are in the source. +/// @param channels Number of channels in the destination. +/// @param element_size Size of one element in bytes. +/// void INTRINSICCV_C_API(merge)(const void **srcs, const size_t *src_strides, void *dst, size_t dst_stride, size_t width, size_t height, size_t channels, diff --git a/test/api/test_merge.cpp b/test/api/test_merge.cpp new file mode 100644 index 0000000000000000000000000000000000000000..987110116efa8890a84638ec65cfff7fdfa5108b --- /dev/null +++ b/test/api/test_merge.cpp @@ -0,0 +1,123 @@ +// SPDX-FileCopyrightText: 2024 Arm Limited and/or its affiliates +// +// SPDX-License-Identifier: Apache-2.0 + +#include +#include + +#include "framework/array.h" +#include "framework/utils.h" + +template +class MergeTest final { + public: + /// Shorthand for internal data layout representation. + using ArrayType = test::Array2D; + + /// Sets the number of padding bytes at the end of rows. + MergeTest& with_padding(size_t padding) { + padding_ = padding; + return *this; + } + + /// Executes the test + void test() { + // Width of input is set to execute 2 vector paths and 1 scalar path + size_t vector_length = test::Options::vector_length(); + size_t input_width = (vector_length * 2) + 1; + size_t output_width = input_width * Channels; + size_t height = 2; + + // Create input and output arrays + std::array inputs; + for (size_t i = 0; i < Channels; ++i) { + inputs[i] = ArrayType{input_width, height, padding_}; + inputs[i].fill(ElementType{0}); + } + + ArrayType expected_output{output_width, height, padding_}; + expected_output.fill(ElementType{0}); + ArrayType actual_output{output_width, height, padding_}; + // Prefill actual_outputs with a different value than expected + actual_output.fill(ElementType{1}); + + // Place 4 set of special values in input and expected output. The size of + // the set is equals to 'Channels'. In the expected output 2 set is placed + // at the beginning of rows, 2 set at the end of the rows. + ElementType running_test_value = test::Options::seed() % 50; + for (size_t i = 0; i < Channels; ++i) { + inputs[i].set(0, 0, {running_test_value}); + expected_output.set(0, i, {running_test_value}); + running_test_value++; + + inputs[i].set(0, vector_length * 2, {running_test_value}); + expected_output.set(0, (vector_length * 2 * Channels) + i, + {running_test_value}); + running_test_value++; + + inputs[i].set(1, 0, {running_test_value}); + expected_output.set(1, i, {running_test_value}); + running_test_value++; + + inputs[i].set(1, vector_length * 2, {running_test_value}); + expected_output.set(1, (vector_length * 2 * Channels) + i, + {running_test_value}); + running_test_value++; + } + + // Call the function to be tested + const void* input_raw_pointers[Channels]; + for (size_t i = 0; i < Channels; ++i) { + input_raw_pointers[i] = inputs[i].data(); + } + size_t strides[Channels]; + for (size_t i = 0; i < Channels; ++i) { + strides[i] = inputs[i].stride(); + } + intrinsiccv_merge(input_raw_pointers, strides, actual_output.data(), + actual_output.stride(), input_width, height, Channels, + sizeof(ElementType)); + + // Compare the results + for (size_t i = 0; i < Channels; ++i) { + EXPECT_EQ_ARRAY2D(expected_output, actual_output); + } + } + + private: + /// Number of padding bytes at the end of rows. + size_t padding_{0}; +}; + +template +class Merge2 : public testing::Test {}; + +using ElementTypes = ::testing::Types; +TYPED_TEST_SUITE(Merge2, ElementTypes); + +TYPED_TEST(Merge2, API) { + MergeTest().test(); + MergeTest().with_padding(test::Options::vector_length()).test(); +} + +template +class Merge3 : public testing::Test {}; + +using ElementTypes = ::testing::Types; +TYPED_TEST_SUITE(Merge3, ElementTypes); + +TYPED_TEST(Merge3, API) { + MergeTest().test(); + MergeTest().with_padding(test::Options::vector_length()).test(); +} + +template +class Merge4 : public testing::Test {}; + +using ElementTypes = ::testing::Types; +TYPED_TEST_SUITE(Merge4, ElementTypes); + +TYPED_TEST(Merge4, API) { + MergeTest().test(); + MergeTest().with_padding(test::Options::vector_length()).test(); +} diff --git a/test/api/test_split.cpp b/test/api/test_split.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bb126bb70dad870e8e6e50b54a738cddc8fc6233 --- /dev/null +++ b/test/api/test_split.cpp @@ -0,0 +1,122 @@ +// SPDX-FileCopyrightText: 2024 Arm Limited and/or its affiliates +// +// SPDX-License-Identifier: Apache-2.0 + +#include +#include + +#include "framework/array.h" +#include "framework/utils.h" + +template +class SplitTest final { + public: + /// Shorthand for internal data layout representation. + using ArrayType = test::Array2D; + + /// Sets the number of padding bytes at the end of rows. + SplitTest& with_padding(size_t padding) { + padding_ = padding; + return *this; + } + + /// Executes the test + void test() { + // Width of input is set to execute 2 vector paths and 1 scalar path + size_t vector_length = test::Options::vector_length(); + size_t output_width = (vector_length * 2) + 1; + size_t input_width = output_width * Channels; + size_t height = 2; + + // Create input and output arrays + ArrayType input{input_width, height, padding_}; + input.fill(ElementType{0}); + + std::array expected_outputs; + std::array actual_outputs; + for (size_t i = 0; i < Channels; ++i) { + expected_outputs[i] = ArrayType{output_width, height, padding_}; + expected_outputs[i].fill(ElementType{0}); + actual_outputs[i] = ArrayType{output_width, height, padding_}; + // Prefill actual_outputs with a different value than expected + actual_outputs[i].fill(ElementType{1}); + } + + // Place 4 set of special values in input and expected output. The size of + // the set is equals to 'Channels'. In the input 2 set is placed at the + // beginning of rows, 2 set at the end of the rows. + ElementType running_test_value = test::Options::seed() % 50; + for (size_t i = 0; i < Channels; ++i) { + input.set(0, i, {running_test_value}); + expected_outputs[i].set(0, 0, {running_test_value}); + running_test_value++; + + input.set(0, (vector_length * 2 * Channels) + i, {running_test_value}); + expected_outputs[i].set(0, vector_length * 2, {running_test_value}); + running_test_value++; + + input.set(1, i, {running_test_value}); + expected_outputs[i].set(1, 0, {running_test_value}); + running_test_value++; + + input.set(1, (vector_length * 2 * Channels) + i, {running_test_value}); + expected_outputs[i].set(1, vector_length * 2, {running_test_value}); + running_test_value++; + } + + // Call the function to be tested + void* actual_raw_pointers[Channels]; + for (size_t i = 0; i < Channels; ++i) { + actual_raw_pointers[i] = actual_outputs[i].data(); + } + size_t strides[Channels]; + for (size_t i = 0; i < Channels; ++i) { + strides[i] = actual_outputs[i].stride(); + } + intrinsiccv_split(input.data(), input.stride(), actual_raw_pointers, + strides, output_width, height, Channels, + sizeof(ElementType)); + + // Compare the results + for (size_t i = 0; i < Channels; ++i) { + EXPECT_EQ_ARRAY2D(expected_outputs[i], actual_outputs[i]); + } + } + + private: + /// Number of padding bytes at the end of rows. + size_t padding_{0}; +}; + +template +class Split2 : public testing::Test {}; + +using ElementTypes = ::testing::Types; +TYPED_TEST_SUITE(Split2, ElementTypes); + +TYPED_TEST(Split2, API) { + SplitTest().test(); + SplitTest().with_padding(test::Options::vector_length()).test(); +} + +template +class Split3 : public testing::Test {}; + +using ElementTypes = ::testing::Types; +TYPED_TEST_SUITE(Split3, ElementTypes); + +TYPED_TEST(Split3, API) { + SplitTest().test(); + SplitTest().with_padding(test::Options::vector_length()).test(); +} + +template +class Split4 : public testing::Test {}; + +using ElementTypes = ::testing::Types; +TYPED_TEST_SUITE(Split4, ElementTypes); + +TYPED_TEST(Split4, API) { + SplitTest().test(); + SplitTest().with_padding(test::Options::vector_length()).test(); +}