From 8a40eca25200de1007dda6f513431fd84cad9b46 Mon Sep 17 00:00:00 2001 From: Michael Platings Date: Thu, 12 Dec 2024 12:13:33 +0000 Subject: [PATCH] Fix constant border handling in erode & dilate Also update signature of remap and warp_perspective functions to avoid the same mistake there. --- CHANGELOG.md | 3 + adapters/opencv/kleidicv_hal.cpp | 85 +++----- adapters/opencv/kleidicv_hal.h | 5 +- benchmark/benchmark.cpp | 24 +- conformity/opencv/test_morphology.cpp | 73 +++++++ conformity/opencv/tests.cpp | 3 +- conformity/opencv/tests.h | 1 + kleidicv/include/kleidicv/ctypes.h | 12 - kleidicv/include/kleidicv/kleidicv.h | 16 +- .../include/kleidicv/morphology/workspace.h | 55 ++--- kleidicv/include/kleidicv/transform/remap.h | 10 +- .../kleidicv/transform/warp_perspective.h | 6 +- kleidicv/include/kleidicv/types.h | 30 --- kleidicv/src/morphology/morphology_api.cpp | 4 +- kleidicv/src/morphology/morphology_neon.cpp | 6 +- kleidicv/src/morphology/morphology_sc.h | 6 +- kleidicv/src/transform/remap_neon.cpp | 20 +- kleidicv/src/transform/remap_sc.h | 5 +- kleidicv/src/transform/remap_sme2.cpp | 13 +- kleidicv/src/transform/remap_sve2.cpp | 20 +- .../src/transform/warp_perspective_api.cpp | 5 +- .../src/transform/warp_perspective_neon.cpp | 5 +- kleidicv/src/transform/warp_perspective_sc.h | 5 +- .../src/transform/warp_perspective_sve2.cpp | 8 +- .../include/kleidicv_thread/kleidicv_thread.h | 6 +- kleidicv_thread/src/kleidicv_thread.cpp | 12 +- test/api/test_blur_and_downsample.cpp | 5 +- test/api/test_gaussian_blur.cpp | 5 +- test/api/test_morphology.cpp | 206 ++++++++++-------- test/api/test_remap.cpp | 78 +++---- test/api/test_separable_filter_2d.cpp | 5 +- test/api/test_sobel.cpp | 5 +- test/api/test_thread.cpp | 32 +-- test/api/test_warp_perspective.cpp | 35 ++- test/framework/border.cpp | 35 +-- test/framework/border.h | 3 +- test/framework/kernel.h | 26 +-- test/framework/test_border.cpp | 12 +- test/framework/test_kernel.cpp | 15 +- test/framework/utils.cpp | 6 - test/framework/utils.h | 15 +- 41 files changed, 476 insertions(+), 445 deletions(-) create mode 100644 conformity/opencv/test_morphology.cpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 94c128c23..6db386e0a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,9 @@ This changelog aims to follow the guiding principles of ### Added - Implementation of Rotate 90 degrees clockwise. +### Fixed +- Handling of cv::erode and cv::dilate non-default constant borders. + ## 0.3.0 - 2024-12-12 ### Added diff --git a/adapters/opencv/kleidicv_hal.cpp b/adapters/opencv/kleidicv_hal.cpp index 72c9f7405..eb7d7a9f8 100644 --- a/adapters/opencv/kleidicv_hal.cpp +++ b/adapters/opencv/kleidicv_hal.cpp @@ -21,6 +21,7 @@ #include "opencv2/core/hal/interface.h" #include "opencv2/core/types.hpp" #include "opencv2/core/utility.hpp" +#include "opencv2/imgproc.hpp" #include "opencv2/imgproc/hal/interface.h" namespace kleidicv::hal { @@ -76,6 +77,16 @@ static size_t get_type_size(int depth) { } } +template +static std::array get_border_value( + const double border_f64[4]) { + std::array result = {}; + for (int i = 0; i < 4; ++i) { + result[i] = cv::saturate_cast(border_f64[i]); + } + return result; +} + static kleidicv_error_t parallel(kleidicv_thread_callback callback, void *callback_data, void * /*parallel_data*/, unsigned task_count) { @@ -678,7 +689,7 @@ int morphology_init(cvhalFilter2D **cvcontext, int operation, int src_type, int kernel_type, uchar *kernel_data, size_t kernel_step, int kernel_width, int kernel_height, int anchor_x, int anchor_y, int cvborder_type, - const double cvborder_values[4], int iterations, + const double border_value_f64[4], int iterations, bool allow_submatrix, bool allow_in_place) { // Some parameters are unused. (void)allow_in_place; @@ -745,25 +756,17 @@ int morphology_init(cvhalFilter2D **cvcontext, int operation, int src_type, return CV_HAL_ERROR_NOT_IMPLEMENTED; } - kleidicv_border_values_t border_values = {}; - if (border_type == kleidicv_border_type_t::KLEIDICV_BORDER_TYPE_CONSTANT) { - border_values.top = cvborder_values[0]; - border_values.left = cvborder_values[1]; - border_values.bottom = cvborder_values[2]; - border_values.right = cvborder_values[3]; - - // In case of default border values for dilate() '0' should be used. - auto border_max_value = - std::numeric_limits::max(); - if ((operation == CV_HAL_MORPH_DILATE) && - (border_values.top == border_max_value) && - (border_values.left == border_max_value) && - (border_values.bottom == border_max_value) && - (border_values.right == border_max_value)) { - border_values.top = 0; - border_values.left = 0; - border_values.bottom = 0; - border_values.right = 0; + std::array border_value; + + if (border_type == KLEIDICV_BORDER_TYPE_CONSTANT) { + cv::Scalar default_border_value = cv::morphologyDefaultBorderValue(); + if (border_value_f64[0] == default_border_value[0] && + border_value_f64[1] == default_border_value[1] && + border_value_f64[2] == default_border_value[2] && + border_value_f64[3] == default_border_value[3]) { + border_value.fill(operation == CV_HAL_MORPH_DILATE ? 0 : 255); + } else { + border_value = get_border_value(border_value_f64); } } @@ -784,7 +787,7 @@ int morphology_init(cvhalFilter2D **cvcontext, int operation, int src_type, static_cast(kernel_height)}, kleidicv_point_t{static_cast(anchor_x), static_cast(anchor_y)}, - border_type, border_values, channels, iterations, type_size, + border_type, border_value.data(), channels, iterations, type_size, kleidicv_rectangle_t{static_cast(max_width), static_cast(max_height)})) { return convert_error(err); @@ -1285,19 +1288,14 @@ int remap_s16(int src_type, const uchar *src_data, size_t src_step, int src_width, int src_height, uchar *dst_data, size_t dst_step, int dst_width, int dst_height, const int16_t *mapxy, size_t mapxy_step, int border_type, - const double border_value[4]) { + const double border_value_f64[4]) { kleidicv_border_type_t kleidicv_border_type; if (from_opencv(border_type, kleidicv_border_type)) { return CV_HAL_ERROR_NOT_IMPLEMENTED; } - kleidicv_border_values_t border_values = {}; - if (border_type == kleidicv_border_type_t::KLEIDICV_BORDER_TYPE_CONSTANT) { - border_values.top = border_value[0]; - border_values.left = border_value[1]; - border_values.bottom = border_value[2]; - border_values.right = border_value[3]; - } + auto border_value = get_border_value(border_value_f64); + auto mt = get_multithreading(); if (src_type == CV_8UC1) { @@ -1306,7 +1304,7 @@ int remap_s16(int src_type, const uchar *src_data, size_t src_step, static_cast(src_height), dst_data, dst_step, static_cast(dst_width), static_cast(dst_height), CV_MAT_CN(src_type), mapxy, mapxy_step, kleidicv_border_type, - border_values, mt)); + border_value.data(), mt)); } return CV_HAL_ERROR_NOT_IMPLEMENTED; @@ -1317,19 +1315,14 @@ int remap_s16point5(int src_type, const uchar *src_data, size_t src_step, size_t dst_step, int dst_width, int dst_height, const int16_t *mapxy, size_t mapxy_step, const uint16_t *mapfrac, size_t mapfrac_step, - int border_type, const double border_value[4]) { + int border_type, const double border_value_f64[4]) { kleidicv_border_type_t kleidicv_border_type; if (from_opencv(border_type, kleidicv_border_type)) { return CV_HAL_ERROR_NOT_IMPLEMENTED; } - kleidicv_border_values_t border_values = {}; - if (border_type == kleidicv_border_type_t::KLEIDICV_BORDER_TYPE_CONSTANT) { - border_values.top = border_value[0]; - border_values.left = border_value[1]; - border_values.bottom = border_value[2]; - border_values.right = border_value[3]; - } + auto border_value = get_border_value(border_value_f64); + auto mt = get_multithreading(); if (src_type == CV_8UC1) { @@ -1338,7 +1331,7 @@ int remap_s16point5(int src_type, const uchar *src_data, size_t src_step, static_cast(src_height), dst_data, dst_step, static_cast(dst_width), static_cast(dst_height), 1, mapxy, mapxy_step, mapfrac, mapfrac_step, kleidicv_border_type, - border_values, mt)); + border_value.data(), mt)); } return CV_HAL_ERROR_NOT_IMPLEMENTED; @@ -1414,7 +1407,7 @@ int warp_perspective(int src_type, const uchar *src_data, size_t src_step, int src_width, int src_height, uchar *dst_data, size_t dst_step, int dst_width, int dst_height, const double transformation[9], int interpolation, - int border_type, const double border_value[4]) { + int border_type, const double border_value_f64[4]) { kleidicv_border_type_t kleidicv_border_type; if (from_opencv(border_type, kleidicv_border_type)) { return CV_HAL_ERROR_NOT_IMPLEMENTED; @@ -1430,14 +1423,8 @@ int warp_perspective(int src_type, const uchar *src_data, size_t src_step, return CV_HAL_ERROR_NOT_IMPLEMENTED; } - kleidicv_border_values_t border_values = {}; - if (kleidicv_border_type == - kleidicv_border_type_t::KLEIDICV_BORDER_TYPE_CONSTANT) { - border_values.top = border_value[0]; - border_values.left = border_value[1]; - border_values.bottom = border_value[2]; - border_values.right = border_value[3]; - } + auto border_value = get_border_value(border_value_f64); + auto mt = get_multithreading(); if (src_type == CV_8UC1) { @@ -1446,7 +1433,7 @@ int warp_perspective(int src_type, const uchar *src_data, size_t src_step, static_cast(src_height), dst_data, dst_step, static_cast(dst_width), static_cast(dst_height), float_transformation, 1, kleidicv_interpolation_type, - kleidicv_border_type, border_values, mt)); + kleidicv_border_type, border_value.data(), mt)); } return CV_HAL_ERROR_NOT_IMPLEMENTED; diff --git a/adapters/opencv/kleidicv_hal.h b/adapters/opencv/kleidicv_hal.h index 4e9b9c283..e184cecdc 100644 --- a/adapters/opencv/kleidicv_hal.h +++ b/adapters/opencv/kleidicv_hal.h @@ -82,9 +82,8 @@ int morphology_init(cvhalFilter2D **context, int operation, int src_type, int dst_type, int max_width, int max_height, int kernel_type, uchar *kernel_data, size_t kernel_step, int kernel_width, int kernel_height, int anchor_x, - int anchor_y, int border_type, - const double border_values[4], int iterations, - bool allow_submatrix, bool allow_in_place); + int anchor_y, int border_type, const double border_value[4], + int iterations, bool allow_submatrix, bool allow_in_place); int morphology_operation(cvhalFilter2D *context, uchar *src_data, size_t src_step, uchar *dst_data, size_t dst_step, diff --git a/benchmark/benchmark.cpp b/benchmark/benchmark.cpp index ba7287b65..b62eb9685 100644 --- a/benchmark/benchmark.cpp +++ b/benchmark/benchmark.cpp @@ -412,11 +412,11 @@ BENCHMARK(yuv_sp_to_bgra); template static void morphology(Function f, benchmark::State& state) { kleidicv_morphology_context_t* context = nullptr; + const T border_value[4] = {}; kleidicv_error_t err = kleidicv_morphology_create( &context, kleidicv_rectangle_t{KernelSize, KernelSize}, - kleidicv_point_t{0, 0}, KLEIDICV_BORDER_TYPE_REPLICATE, - kleidicv_border_values_t{0, 0, 0, 0}, 1, 1, sizeof(T), - kleidicv_rectangle_t{image_width, image_height}); + kleidicv_point_t{0, 0}, KLEIDICV_BORDER_TYPE_REPLICATE, border_value, 1, + 1, sizeof(T), kleidicv_rectangle_t{image_width, image_height}); if (err != KLEIDICV_OK) { state.SkipWithError("Could not initialize morphology context."); return; @@ -612,12 +612,12 @@ template static void remap_s16(Function f, MapFunc mf, size_t channels, kleidicv_border_type_t border_type, benchmark::State& state) { - bench_functor(state, [f, mf, channels, border_type]() { + const T border_value[4] = {}; + bench_functor(state, [f, mf, channels, border_type, border_value]() { (void)f(get_source_buffer_a(), image_width * sizeof(T), image_width, image_height, get_destination_buffer(), image_width * sizeof(T), image_width, image_height, channels, mf(), - image_width * 2 * sizeof(int16_t), border_type, - kleidicv_border_values_t{}); + image_width * 2 * sizeof(int16_t), border_type, border_value); }); } @@ -644,13 +644,13 @@ template static void remap_s16point5(Function f, MapFunc mf, size_t channels, kleidicv_border_type_t border_type, benchmark::State& state) { - bench_functor(state, [f, mf, channels, border_type]() { + const T border_value[4] = {}; + bench_functor(state, [f, mf, channels, border_type, border_value]() { (void)f(get_source_buffer_a(), image_width * sizeof(T), image_width, image_height, get_destination_buffer(), image_width * sizeof(T), image_width, image_height, channels, mf(), image_width * 2 * sizeof(int16_t), get_random_mapfrac(), - image_width * sizeof(uint16_t), border_type, - kleidicv_border_values_t{}); + image_width * sizeof(uint16_t), border_type, border_value); }); } @@ -719,11 +719,13 @@ static void warp_perspective(Function f, const float transform[9], kleidicv_interpolation_type_t interpolation, kleidicv_border_type_t border_type, benchmark::State& state) { - bench_functor(state, [f, transform, channels, interpolation, border_type]() { + const T border_value[4] = {}; + bench_functor(state, [f, transform, channels, interpolation, border_type, + border_value]() { (void)f(get_source_buffer_a(), image_width * sizeof(T), image_width, image_height, get_destination_buffer(), image_width * sizeof(T), image_width, image_height, transform, channels, interpolation, - border_type, kleidicv_border_values_t{}); + border_type, border_value); }); } diff --git a/conformity/opencv/test_morphology.cpp b/conformity/opencv/test_morphology.cpp new file mode 100644 index 000000000..05259170a --- /dev/null +++ b/conformity/opencv/test_morphology.cpp @@ -0,0 +1,73 @@ +// SPDX-FileCopyrightText: 2024 Arm Limited and/or its affiliates +// +// SPDX-License-Identifier: Apache-2.0 + +#include +#include +#include + +#include "opencv2/core/hal/interface.h" +#include "tests.h" + +template +static cv::Mat exec_morphology(cv::Mat& input_mat) { + cv::Mat result; + cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::Scalar border_value = DefaultBorder + ? cv::morphologyDefaultBorderValue() + : cv::Scalar(123, -DBL_MAX, FLT_MAX, 0); + auto func = Erode ? &cv::erode : &cv::dilate; + func(input_mat, result, kernel, cv::Point(-1, -1), 1, cv::BORDER_CONSTANT, + border_value); + return result; +} + +#if MANAGER +template +bool test_morphology(int index, RecreatedMessageQueue& request_queue, + RecreatedMessageQueue& reply_queue) { + cv::RNG rng(0); + + for (size_t x = 5; x <= 16; ++x) { + for (size_t y = 5; y <= 16; ++y) { + cv::Mat input(x, y, CV_8UC(Channels)); + rng.fill(input, cv::RNG::UNIFORM, 0, 255); + + cv::Mat actual = exec_morphology(input); + cv::Mat expected = get_expected_from_subordinate(index, request_queue, + reply_queue, input); + + if (are_matrices_different(0, actual, expected)) { + fail_print_matrices(x, y, input, actual, expected); + return true; + } + } + } + + return false; +} +#endif + +std::vector& morphology_tests_get() { + // clang-format off + static std::vector tests = { + TEST("Dilate, default border, 1 channel", (test_morphology), (exec_morphology)), + TEST("Dilate, default border, 2 channel", (test_morphology), (exec_morphology)), + TEST("Dilate, default border, 3 channel", (test_morphology), (exec_morphology)), + TEST("Dilate, default border, 4 channel", (test_morphology), (exec_morphology)), + TEST("Erode, default border, 1 channel", (test_morphology), (exec_morphology)), + TEST("Erode, default border, 2 channel", (test_morphology), (exec_morphology)), + TEST("Erode, default border, 3 channel", (test_morphology), (exec_morphology)), + TEST("Erode, default border, 4 channel", (test_morphology), (exec_morphology)), + TEST("Dilate, custom border, 1 channel", (test_morphology), (exec_morphology)), + TEST("Dilate, custom border, 2 channel", (test_morphology), (exec_morphology)), + TEST("Dilate, custom border, 3 channel", (test_morphology), (exec_morphology)), + TEST("Dilate, custom border, 4 channel", (test_morphology), (exec_morphology)), + TEST("Erode, custom border, 1 channel", (test_morphology), (exec_morphology)), + TEST("Erode, custom border, 2 channel", (test_morphology), (exec_morphology)), + TEST("Erode, custom border, 3 channel", (test_morphology), (exec_morphology)), + TEST("Erode, custom border, 4 channel", (test_morphology), (exec_morphology)), + }; + // clang-format on + return tests; +} diff --git a/conformity/opencv/tests.cpp b/conformity/opencv/tests.cpp index 7bc7a48e6..afb0cbeb2 100644 --- a/conformity/opencv/tests.cpp +++ b/conformity/opencv/tests.cpp @@ -23,9 +23,9 @@ static std::vector merge_tests( } std::vector all_tests = merge_tests({ - // clang-format off binary_op_tests_get, cvtcolor_tests_get, + morphology_tests_get, #if KLEIDICV_ENABLE_ALL_OPENCV_HAL separable_filter_2d_tests_get, #endif @@ -43,7 +43,6 @@ std::vector all_tests = merge_tests({ warp_perspective_tests_get, blur_and_downsample_tests_get, scharr_interleaved_tests_get, - // clang-format on }); #if MANAGER diff --git a/conformity/opencv/tests.h b/conformity/opencv/tests.h index fc1d85602..c5b8c4f75 100644 --- a/conformity/opencv/tests.h +++ b/conformity/opencv/tests.h @@ -11,6 +11,7 @@ std::vector& binary_op_tests_get(); std::vector& cvtcolor_tests_get(); +std::vector& morphology_tests_get(); std::vector& separable_filter_2d_tests_get(); std::vector& gaussian_blur_tests_get(); std::vector& rgb2yuv_tests_get(); diff --git a/kleidicv/include/kleidicv/ctypes.h b/kleidicv/include/kleidicv/ctypes.h index 59a177d17..7905dc742 100644 --- a/kleidicv/include/kleidicv/ctypes.h +++ b/kleidicv/include/kleidicv/ctypes.h @@ -53,18 +53,6 @@ typedef struct { size_t height; } kleidicv_rectangle_t; -/// Struct to store border values -typedef struct { - /// Top border value - double top; - /// Left border value - double left; - /// Bottom border value - double bottom; - /// Right border value - double right; -} kleidicv_border_values_t; - /// KleidiCV border types typedef enum { /// The border is a constant value. diff --git a/kleidicv/include/kleidicv/kleidicv.h b/kleidicv/include/kleidicv/kleidicv.h index 7cb4e08b7..f41523ada 100644 --- a/kleidicv/include/kleidicv/kleidicv.h +++ b/kleidicv/include/kleidicv/kleidicv.h @@ -42,7 +42,7 @@ #define KLEIDICV_MAXIMUM_TYPE_SIZE (8) /// Maximum number of channels -#define KLEIDICV_MAXIMUM_CHANNEL_COUNT (8) +#define KLEIDICV_MAXIMUM_CHANNEL_COUNT (4) #ifdef __cplusplus extern "C" { @@ -905,7 +905,7 @@ KLEIDICV_API_DECLARATION(kleidicv_compare_greater_u8, const uint8_t *src_a, /// are: \n /// - @ref KLEIDICV_BORDER_TYPE_CONSTANT \n /// - @ref KLEIDICV_BORDER_TYPE_REPLICATE -/// @param border_values Border values if the border_type is +/// @param border_value Border value if the border_type is /// @ref KLEIDICV_BORDER_TYPE_CONSTANT. /// @param channels Number of channels in the data. Must be not more than /// @ref KLEIDICV_MAXIMUM_CHANNEL_COUNT. @@ -918,7 +918,7 @@ KLEIDICV_API_DECLARATION(kleidicv_compare_greater_u8, const uint8_t *src_a, kleidicv_error_t kleidicv_morphology_create( kleidicv_morphology_context_t **context, kleidicv_rectangle_t kernel, kleidicv_point_t anchor, kleidicv_border_type_t border_type, - kleidicv_border_values_t border_values, size_t channels, size_t iterations, + const uint8_t *border_value, size_t channels, size_t iterations, size_t type_size, kleidicv_rectangle_t image); /// Releases a morphology context that was previously created using @ref @@ -1790,7 +1790,7 @@ KLEIDICV_API_DECLARATION(kleidicv_in_range_f32, const float *src, /// @param border_type Way of handling the border. The supported border types /// are: \n /// - @ref KLEIDICV_BORDER_TYPE_REPLICATE -/// @param border_values Border values if the border_type is +/// @param border_value Border value if the border_type is /// @ref KLEIDICV_BORDER_TYPE_CONSTANT. KLEIDICV_API_DECLARATION(kleidicv_remap_s16_u8, const uint8_t *src, size_t src_stride, size_t src_width, size_t src_height, @@ -1798,7 +1798,7 @@ KLEIDICV_API_DECLARATION(kleidicv_remap_s16_u8, const uint8_t *src, size_t dst_height, size_t channels, const int16_t *mapxy, size_t mapxy_stride, kleidicv_border_type_t border_type, - kleidicv_border_values_t border_values); + const uint8_t *border_value); #ifndef DOXYGEN /// Internal - not part of the public API and its direct use is not supported. @@ -1811,7 +1811,7 @@ KLEIDICV_API_DECLARATION(kleidicv_remap_s16point5_u8, const uint8_t *src, const int16_t *mapxy, size_t mapxy_stride, const uint16_t *mapfrac, size_t mapfrac_stride, kleidicv_border_type_t border_type, - kleidicv_border_values_t border_values); + const uint8_t *border_value); #endif // DOXYGEN #endif // KLEIDICV_EXPERIMENTAL_FEATURE_REMAP @@ -1903,14 +1903,14 @@ kleidicv_error_t kleidicv_scharr_interleaved_s16_u8( /// @param border_type Way of handling the border. The supported border types /// are: \n /// - @ref KLEIDICV_BORDER_TYPE_REPLICATE -/// @param border_values Border values if the border_type is +/// @param border_value Border value if the border_type is /// @ref KLEIDICV_BORDER_TYPE_CONSTANT. kleidicv_error_t kleidicv_warp_perspective_u8( const uint8_t *src, size_t src_stride, size_t src_width, size_t src_height, uint8_t *dst, size_t dst_stride, size_t dst_width, size_t dst_height, const float transformation[9], size_t channels, kleidicv_interpolation_type_t interpolation, - kleidicv_border_type_t border_type, kleidicv_border_values_t border_values); + kleidicv_border_type_t border_type, const uint8_t *border_value); #endif // KLEIDICV_EXPERIMENTAL_FEATURE_WARP_PERSPECTIVE #ifdef __cplusplus diff --git a/kleidicv/include/kleidicv/morphology/workspace.h b/kleidicv/include/kleidicv/morphology/workspace.h index fb7c9e486..58695b34d 100644 --- a/kleidicv/include/kleidicv/morphology/workspace.h +++ b/kleidicv/include/kleidicv/morphology/workspace.h @@ -6,6 +6,7 @@ #define KLEIDICV_MORPHOLOGY_WORKSPACE_H #include +#include #include #include #include @@ -69,8 +70,8 @@ class MorphologyWorkspace final { // Creates a workspace on the heap. static kleidicv_error_t create( Pointer &workspace, kleidicv_rectangle_t kernel, kleidicv_point_t anchor, - BorderType border_type, kleidicv_border_values_t border_values, - size_t channels, size_t iterations, size_t type_size, + BorderType border_type, const uint8_t *border_value, size_t channels, + size_t iterations, size_t type_size, kleidicv_rectangle_t image) KLEIDICV_STREAMING_COMPATIBLE { // These values are arbitrarily choosen. const size_t rows_per_iteration = std::max(2 * kernel.height, 32UL); @@ -140,7 +141,14 @@ class MorphologyWorkspace final { workspace->kernel_ = kernel; workspace->anchor_ = anchor; workspace->border_type_ = border_type; - workspace->border_values_ = border_values; + if (border_type == BorderType::CONSTANT) { + if (border_value == nullptr) { + return KLEIDICV_ERROR_NULL_POINTER; + } + for (size_t i = 0; i < channels; ++i) { + workspace->border_value_[i] = border_value[i]; + } + } workspace->channels_ = channels; workspace->iterations_ = iterations; workspace->type_size_ = type_size; @@ -152,7 +160,6 @@ class MorphologyWorkspace final { kleidicv_rectangle_t kernel() const { return kernel_; } kleidicv_point_t anchor() const { return anchor_; } BorderType border_type() const { return border_type_; } - kleidicv_border_values_t border_values() const { return border_values_; } size_t channels() const { return channels_; } size_t iterations() const { return iterations_; } size_t type_size() const { return type_size_; } @@ -163,7 +170,7 @@ class MorphologyWorkspace final { template void process(Rectangle rect, Rows src_rows, Rows dst_rows, Margin margin, - Border border, BorderType border_type, + BorderType border_type, O operation) KLEIDICV_STREAMING_COMPATIBLE { using S = typename O::SourceType; using B = typename O::BufferType; @@ -188,12 +195,6 @@ class MorphologyWorkspace final { vertical_height_ = rect.height(); row_index_ = 0; - // Constant border values. - auto left_border_value = saturating_cast(border.left()); - auto top_border_value = saturating_cast(border.top()); - auto right_border_value = saturating_cast(border.right()); - auto bottom_border_value = saturating_cast(border.bottom()); - // Used by replicate border type. auto first_src_rows = src_rows; auto last_src_rows = src_rows.at(rect.height() - 1); @@ -202,23 +203,21 @@ class MorphologyWorkspace final { for (size_t index = 0; index < horizontal_height; ++index) { switch (border_type) { case BorderType::CONSTANT: { - make_constant_border(wide_rows, 0, margin.left(), left_border_value); + make_constant_border(wide_rows, 0, margin.left()); - if (row_index_ < margin.top()) { - make_constant_border(wide_rows, margin.left(), wide_rows_src_width_, - top_border_value); - } else if (row_index_ < (margin.top() + rect.height())) { + if (row_index_ < margin.top() || + row_index_ >= margin.top() + rect.height()) { + make_constant_border(wide_rows, margin.left(), + wide_rows_src_width_); + } else { copy_data(src_rows, wide_rows.at(0, margin.left()), wide_rows_src_width_); // Advance source rows. ++src_rows; - } else { - make_constant_border(wide_rows, margin.left(), wide_rows_src_width_, - bottom_border_value); } make_constant_border(wide_rows, margin.left() + wide_rows_src_width_, - margin.right(), right_border_value); + margin.right()); // Advance counters. ++row_index_; @@ -270,7 +269,7 @@ class MorphologyWorkspace final { ++src_rows; } else { make_constant_border(wide_rows, margin.left(), - wide_rows_src_width_, bottom_border_value); + wide_rows_src_width_); } // Advance row counter. @@ -331,12 +330,14 @@ class MorphologyWorkspace final { return height; } - template - void make_constant_border(Rows dst_rows, size_t dst_index, size_t count, - BorderType value) KLEIDICV_STREAMING_COMPATIBLE { + template + void make_constant_border(Rows dst_rows, size_t dst_index, + size_t count) KLEIDICV_STREAMING_COMPATIBLE { auto dst = &dst_rows.at(0, dst_index)[0]; - for (size_t index = 0; index < count * dst_rows.channels(); ++index) { - dst[index] = value; + for (size_t index = 0; index < count; ++index) { + for (size_t channel = 0; channel < dst_rows.channels(); ++channel) { + dst[index * dst_rows.channels() + channel] = border_value_[channel]; + } } } @@ -360,7 +361,7 @@ class MorphologyWorkspace final { kleidicv_rectangle_t kernel_; kleidicv_point_t anchor_; BorderType border_type_; - kleidicv_border_values_t border_values_; + std::array border_value_; size_t iterations_; size_t type_size_; Rectangle image_size_; diff --git a/kleidicv/include/kleidicv/transform/remap.h b/kleidicv/include/kleidicv/transform/remap.h index 2d61e2b25..29182178b 100644 --- a/kleidicv/include/kleidicv/transform/remap.h +++ b/kleidicv/include/kleidicv/transform/remap.h @@ -53,7 +53,7 @@ kleidicv_error_t remap_s16(const T *src, size_t src_stride, size_t src_width, size_t dst_width, size_t dst_height, size_t channels, const int16_t *mapxy, size_t mapxy_stride, kleidicv_border_type_t border_type, - kleidicv_border_values_t border_values); + const T *border_value); template kleidicv_error_t remap_s16point5(const T *src, size_t src_stride, @@ -63,7 +63,7 @@ kleidicv_error_t remap_s16point5(const T *src, size_t src_stride, const int16_t *mapxy, size_t mapxy_stride, const uint16_t *mapfrac, size_t mapfrac_stride, kleidicv_border_type_t border_type, - kleidicv_border_values_t border_values); + const T *border_value); } // namespace neon @@ -75,7 +75,7 @@ kleidicv_error_t remap_s16(const T *src, size_t src_stride, size_t src_width, size_t dst_width, size_t dst_height, size_t channels, const int16_t *mapxy, size_t mapxy_stride, kleidicv_border_type_t border_type, - kleidicv_border_values_t border_values); + const T *border_value); template kleidicv_error_t remap_s16point5(const T *src, size_t src_stride, @@ -85,7 +85,7 @@ kleidicv_error_t remap_s16point5(const T *src, size_t src_stride, const int16_t *mapxy, size_t mapxy_stride, const uint16_t *mapfrac, size_t mapfrac_stride, kleidicv_border_type_t border_type, - kleidicv_border_values_t border_values); + const T *border_value); } // namespace sve2 @@ -99,7 +99,7 @@ kleidicv_error_t remap_s16point5(const T *src, size_t src_stride, const int16_t *mapxy, size_t mapxy_stride, const uint16_t *mapfrac, size_t mapfrac_stride, kleidicv_border_type_t border_type, - kleidicv_border_values_t border_values); + const T *border_value); } // namespace sme2 diff --git a/kleidicv/include/kleidicv/transform/warp_perspective.h b/kleidicv/include/kleidicv/transform/warp_perspective.h index 1314e3964..e5fe74526 100644 --- a/kleidicv/include/kleidicv/transform/warp_perspective.h +++ b/kleidicv/include/kleidicv/transform/warp_perspective.h @@ -22,7 +22,7 @@ KLEIDICV_API_DECLARATION(kleidicv_warp_perspective_stripe_u8, const float transformation[9], size_t channels, kleidicv_interpolation_type_t interpolation, kleidicv_border_type_t border_type, - kleidicv_border_values_t border_values); + const uint8_t *border_value); } namespace kleidicv { @@ -52,7 +52,7 @@ kleidicv_error_t warp_perspective_stripe( T *dst, size_t dst_stride, size_t dst_width, size_t dst_height, size_t y_begin, size_t y_end, const float transformation[9], size_t channels, kleidicv_interpolation_type_t interpolation, - kleidicv_border_type_t border_type, kleidicv_border_values_t border_values); + kleidicv_border_type_t border_type, const T *border_value); } // namespace neon namespace sve2 { @@ -63,7 +63,7 @@ kleidicv_error_t warp_perspective_stripe( T *dst, size_t dst_stride, size_t dst_width, size_t dst_height, size_t y_begin, size_t y_end, const float transformation[9], size_t channels, kleidicv_interpolation_type_t interpolation, - kleidicv_border_type_t border_type, kleidicv_border_values_t border_values); + kleidicv_border_type_t border_type, const T *border_value); } // namespace sve2 } // namespace kleidicv diff --git a/kleidicv/include/kleidicv/types.h b/kleidicv/include/kleidicv/types.h index ffa30bbf8..831507833 100644 --- a/kleidicv/include/kleidicv/types.h +++ b/kleidicv/include/kleidicv/types.h @@ -103,36 +103,6 @@ class Margin final { size_t bottom_; }; // end of class Margin -// Represents constant border values around a two dimensional area. -template -class Border final { - public: - Border() KLEIDICV_STREAMING_COMPATIBLE = default; - - Border(double left, double top, double right, - double bottom) KLEIDICV_STREAMING_COMPATIBLE - : left_{saturating_cast(left)}, - top_{saturating_cast(top)}, - right_{saturating_cast(right)}, - bottom_{saturating_cast(bottom)} {} - - explicit Border(kleidicv_border_values_t border_values) - KLEIDICV_STREAMING_COMPATIBLE - : Border(border_values.left, border_values.top, border_values.right, - border_values.bottom) {} - - T left() const KLEIDICV_STREAMING_COMPATIBLE { return left_; } - T top() const KLEIDICV_STREAMING_COMPATIBLE { return top_; } - T right() const KLEIDICV_STREAMING_COMPATIBLE { return right_; } - T bottom() const KLEIDICV_STREAMING_COMPATIBLE { return bottom_; } - - private: - T left_; - T top_; - T right_; - T bottom_; -}; // end of class Border - // Describes the layout of one row given by a base pointer and channel count. template class Columns final { diff --git a/kleidicv/src/morphology/morphology_api.cpp b/kleidicv/src/morphology/morphology_api.cpp index 42679e328..619224817 100644 --- a/kleidicv/src/morphology/morphology_api.cpp +++ b/kleidicv/src/morphology/morphology_api.cpp @@ -59,7 +59,7 @@ using KLEIDICV_TARGET_NAMESPACE::MorphologyWorkspace; kleidicv_error_t kleidicv_morphology_create( kleidicv_morphology_context_t **context, kleidicv_rectangle_t kernel, kleidicv_point_t anchor, kleidicv_border_type_t border_type, - kleidicv_border_values_t border_values, size_t channels, size_t iterations, + const uint8_t *border_value, size_t channels, size_t iterations, size_t type_size, kleidicv_rectangle_t image) { CHECK_POINTERS(context); *context = nullptr; @@ -83,7 +83,7 @@ kleidicv_error_t kleidicv_morphology_create( MorphologyWorkspace::Pointer workspace; if (kleidicv_error_t error = MorphologyWorkspace::create( - workspace, kernel, anchor, *morphology_border_type, border_values, + workspace, kernel, anchor, *morphology_border_type, border_value, channels, iterations, type_size, image)) { return error; } diff --git a/kleidicv/src/morphology/morphology_neon.cpp b/kleidicv/src/morphology/morphology_neon.cpp index 687a9309d..0beddd745 100644 --- a/kleidicv/src/morphology/morphology_neon.cpp +++ b/kleidicv/src/morphology/morphology_neon.cpp @@ -517,13 +517,12 @@ kleidicv_error_t dilate(const T *src, size_t src_stride, T *dst, Rows src_rows{src, src_stride, workspace->channels()}; Rows dst_rows{dst, dst_stride, workspace->channels()}; Margin margin{workspace->kernel(), workspace->anchor()}; - Border border{workspace->border_values()}; Rows current_src_rows = src_rows; Rows current_dst_rows = dst_rows; for (size_t iteration = 0; iteration < workspace->iterations(); ++iteration) { DilateOperation operation{kernel}; - workspace->process(rect, current_src_rows, current_dst_rows, margin, border, + workspace->process(rect, current_src_rows, current_dst_rows, margin, workspace->border_type(), operation); // Update source for the next iteration. current_src_rows = dst_rows; @@ -588,13 +587,12 @@ kleidicv_error_t erode(const T *src, size_t src_stride, T *dst, Rows src_rows{src, src_stride, workspace->channels()}; Rows dst_rows{dst, dst_stride, workspace->channels()}; Margin margin{workspace->kernel(), workspace->anchor()}; - Border border{workspace->border_values()}; Rows current_src_rows = src_rows; Rows current_dst_rows = dst_rows; for (size_t iteration = 0; iteration < workspace->iterations(); ++iteration) { ErodeOperation operation{kernel}; - workspace->process(rect, current_src_rows, current_dst_rows, margin, border, + workspace->process(rect, current_src_rows, current_dst_rows, margin, workspace->border_type(), operation); // Update source for the next iteration. current_src_rows = dst_rows; diff --git a/kleidicv/src/morphology/morphology_sc.h b/kleidicv/src/morphology/morphology_sc.h index bce498bb2..2a1021abc 100644 --- a/kleidicv/src/morphology/morphology_sc.h +++ b/kleidicv/src/morphology/morphology_sc.h @@ -502,13 +502,12 @@ static kleidicv_error_t dilate_sc( Rows src_rows{src, src_stride, workspace->channels()}; Rows dst_rows{dst, dst_stride, workspace->channels()}; Margin margin{workspace->kernel(), workspace->anchor()}; - Border border{workspace->border_values()}; Rows current_src_rows = src_rows; Rows current_dst_rows = dst_rows; for (size_t iteration = 0; iteration < workspace->iterations(); ++iteration) { DilateOperation operation{kernel}; - workspace->process(rect, current_src_rows, current_dst_rows, margin, border, + workspace->process(rect, current_src_rows, current_dst_rows, margin, workspace->border_type(), operation); // Update source for the next iteration. current_src_rows = dst_rows; @@ -575,13 +574,12 @@ static kleidicv_error_t erode_sc(const T *src, size_t src_stride, T *dst, Rows src_rows{src, src_stride, workspace->channels()}; Rows dst_rows{dst, dst_stride, workspace->channels()}; Margin margin{workspace->kernel(), workspace->anchor()}; - Border border{workspace->border_values()}; Rows current_src_rows = src_rows; Rows current_dst_rows = dst_rows; for (size_t iteration = 0; iteration < workspace->iterations(); ++iteration) { ErodeOperation operation{kernel}; - workspace->process(rect, current_src_rows, current_dst_rows, margin, border, + workspace->process(rect, current_src_rows, current_dst_rows, margin, workspace->border_type(), operation); // Update source for the next iteration. current_src_rows = dst_rows; diff --git a/kleidicv/src/transform/remap_neon.cpp b/kleidicv/src/transform/remap_neon.cpp index 327c23971..9d47bd7b7 100644 --- a/kleidicv/src/transform/remap_neon.cpp +++ b/kleidicv/src/transform/remap_neon.cpp @@ -73,12 +73,12 @@ class RemapS16 { }; // end of class RemapS16 template -kleidicv_error_t remap_s16( - const T *src, size_t src_stride, size_t src_width, size_t src_height, - T *dst, size_t dst_stride, size_t dst_width, size_t dst_height, - size_t channels, const int16_t *mapxy, size_t mapxy_stride, - kleidicv_border_type_t border_type, - [[maybe_unused]] kleidicv_border_values_t border_values) { +kleidicv_error_t remap_s16(const T *src, size_t src_stride, size_t src_width, + size_t src_height, T *dst, size_t dst_stride, + size_t dst_width, size_t dst_height, size_t channels, + const int16_t *mapxy, size_t mapxy_stride, + kleidicv_border_type_t border_type, + [[maybe_unused]] const T *border_value) { CHECK_POINTER_AND_STRIDE(src, src_stride, src_height); CHECK_POINTER_AND_STRIDE(dst, dst_stride, dst_height); CHECK_POINTER_AND_STRIDE(mapxy, mapxy_stride, dst_height); @@ -230,7 +230,7 @@ kleidicv_error_t remap_s16point5( size_t channels, const int16_t *mapxy, size_t mapxy_stride, const uint16_t *mapfrac, size_t mapfrac_stride, [[maybe_unused]] kleidicv_border_type_t border_type, - [[maybe_unused]] kleidicv_border_values_t border_values) { + [[maybe_unused]] const T *border_value) { CHECK_POINTER_AND_STRIDE(src, src_stride, src_height); CHECK_POINTER_AND_STRIDE(dst, dst_stride, dst_height); CHECK_POINTER_AND_STRIDE(mapxy, mapxy_stride, dst_height); @@ -257,8 +257,7 @@ kleidicv_error_t remap_s16point5( const type *src, size_t src_stride, size_t src_width, size_t src_height, \ type *dst, size_t dst_stride, size_t dst_width, size_t dst_height, \ size_t channels, const int16_t *mapxy, size_t mapxy_stride, \ - kleidicv_border_type_t border_type, \ - kleidicv_border_values_t border_values) + kleidicv_border_type_t border_type, const type *border_value) KLEIDICV_INSTANTIATE_TEMPLATE_REMAP_S16(uint8_t); @@ -268,8 +267,7 @@ KLEIDICV_INSTANTIATE_TEMPLATE_REMAP_S16(uint8_t); type *dst, size_t dst_stride, size_t dst_width, size_t dst_height, \ size_t channels, const int16_t *mapxy, size_t mapxy_stride, \ const uint16_t *mapfrac, size_t mapfrac_stride, \ - kleidicv_border_type_t border_type, \ - kleidicv_border_values_t border_values) + kleidicv_border_type_t border_type, const type *border_value) KLEIDICV_INSTANTIATE_TEMPLATE_REMAP_S16Point5(uint8_t); diff --git a/kleidicv/src/transform/remap_sc.h b/kleidicv/src/transform/remap_sc.h index 617016e71..3c204ac34 100644 --- a/kleidicv/src/transform/remap_sc.h +++ b/kleidicv/src/transform/remap_sc.h @@ -118,8 +118,7 @@ kleidicv_error_t remap_s16_sc(const T* src, size_t src_stride, size_t src_width, size_t dst_width, size_t dst_height, size_t channels, const int16_t* mapxy, size_t mapxy_stride, - kleidicv_border_type_t border_type, - kleidicv_border_values_t) { + kleidicv_border_type_t border_type, const T*) { CHECK_POINTER_AND_STRIDE(src, src_stride, src_height); CHECK_POINTER_AND_STRIDE(dst, dst_stride, dst_height); CHECK_POINTER_AND_STRIDE(mapxy, mapxy_stride, dst_height); @@ -396,7 +395,7 @@ kleidicv_error_t remap_s16point5_sc( T* dst, size_t dst_stride, size_t dst_width, size_t dst_height, size_t channels, const int16_t* mapxy, size_t mapxy_stride, const uint16_t* mapfrac, size_t mapfrac_stride, - kleidicv_border_type_t border_type, kleidicv_border_values_t) { + kleidicv_border_type_t border_type, const T*) { CHECK_POINTER_AND_STRIDE(src, src_stride, src_height); CHECK_POINTER_AND_STRIDE(dst, dst_stride, dst_height); CHECK_POINTER_AND_STRIDE(mapxy, mapxy_stride, dst_height); diff --git a/kleidicv/src/transform/remap_sme2.cpp b/kleidicv/src/transform/remap_sme2.cpp index e5c0d6a44..03280dc76 100644 --- a/kleidicv/src/transform/remap_sme2.cpp +++ b/kleidicv/src/transform/remap_sme2.cpp @@ -15,11 +15,11 @@ kleidicv_error_t remap_s16point5(const T *src, size_t src_stride, const int16_t *mapxy, size_t mapxy_stride, const uint16_t *mapfrac, size_t mapfrac_stride, kleidicv_border_type_t border_type, - kleidicv_border_values_t border_values) { - return remap_s16point5_sc( - src, src_stride, src_width, src_height, dst, dst_stride, dst_width, - dst_height, channels, mapxy, mapxy_stride, mapfrac, mapfrac_stride, - border_type, border_values); + const T *border_value) { + return remap_s16point5_sc(src, src_stride, src_width, src_height, + dst, dst_stride, dst_width, dst_height, + channels, mapxy, mapxy_stride, mapfrac, + mapfrac_stride, border_type, border_value); } #define KLEIDICV_INSTANTIATE_TEMPLATE_REMAP_S16Point5(type) \ @@ -28,8 +28,7 @@ kleidicv_error_t remap_s16point5(const T *src, size_t src_stride, type *dst, size_t dst_stride, size_t dst_width, size_t dst_height, \ size_t channels, const int16_t *mapxy, size_t mapxy_stride, \ const uint16_t *mapfrac, size_t mapfrac_stride, \ - kleidicv_border_type_t border_type, \ - kleidicv_border_values_t border_values) + kleidicv_border_type_t border_type, const type *border_value) KLEIDICV_INSTANTIATE_TEMPLATE_REMAP_S16Point5(uint8_t); diff --git a/kleidicv/src/transform/remap_sve2.cpp b/kleidicv/src/transform/remap_sve2.cpp index acf3c164f..13276480f 100644 --- a/kleidicv/src/transform/remap_sve2.cpp +++ b/kleidicv/src/transform/remap_sve2.cpp @@ -13,10 +13,10 @@ kleidicv_error_t remap_s16(const T *src, size_t src_stride, size_t src_width, size_t dst_width, size_t dst_height, size_t channels, const int16_t *mapxy, size_t mapxy_stride, kleidicv_border_type_t border_type, - kleidicv_border_values_t border_values) { + const T *border_value) { return remap_s16_sc(src, src_stride, src_width, src_height, dst, dst_stride, dst_width, dst_height, channels, - mapxy, mapxy_stride, border_type, border_values); + mapxy, mapxy_stride, border_type, border_value); } template @@ -27,11 +27,11 @@ kleidicv_error_t remap_s16point5(const T *src, size_t src_stride, const int16_t *mapxy, size_t mapxy_stride, const uint16_t *mapfrac, size_t mapfrac_stride, kleidicv_border_type_t border_type, - kleidicv_border_values_t border_values) { - return remap_s16point5_sc( - src, src_stride, src_width, src_height, dst, dst_stride, dst_width, - dst_height, channels, mapxy, mapxy_stride, mapfrac, mapfrac_stride, - border_type, border_values); + const T *border_value) { + return remap_s16point5_sc(src, src_stride, src_width, src_height, + dst, dst_stride, dst_width, dst_height, + channels, mapxy, mapxy_stride, mapfrac, + mapfrac_stride, border_type, border_value); } #define KLEIDICV_INSTANTIATE_TEMPLATE_REMAP_S16(type) \ @@ -39,8 +39,7 @@ kleidicv_error_t remap_s16point5(const T *src, size_t src_stride, const type *src, size_t src_stride, size_t src_width, size_t src_height, \ type *dst, size_t dst_stride, size_t dst_width, size_t dst_height, \ size_t channels, const int16_t *mapxy, size_t mapxy_stride, \ - kleidicv_border_type_t border_type, \ - kleidicv_border_values_t border_values) + kleidicv_border_type_t border_type, const type *border_value) KLEIDICV_INSTANTIATE_TEMPLATE_REMAP_S16(uint8_t); @@ -50,8 +49,7 @@ KLEIDICV_INSTANTIATE_TEMPLATE_REMAP_S16(uint8_t); type *dst, size_t dst_stride, size_t dst_width, size_t dst_height, \ size_t channels, const int16_t *mapxy, size_t mapxy_stride, \ const uint16_t *mapfrac, size_t mapfrac_stride, \ - kleidicv_border_type_t border_type, \ - kleidicv_border_values_t border_values) + kleidicv_border_type_t border_type, const type *border_value) KLEIDICV_INSTANTIATE_TEMPLATE_REMAP_S16Point5(uint8_t); diff --git a/kleidicv/src/transform/warp_perspective_api.cpp b/kleidicv/src/transform/warp_perspective_api.cpp index d4cae3760..797b603c4 100644 --- a/kleidicv/src/transform/warp_perspective_api.cpp +++ b/kleidicv/src/transform/warp_perspective_api.cpp @@ -20,8 +20,7 @@ kleidicv_error_t kleidicv_warp_perspective_u8( uint8_t *dst, size_t dst_stride, size_t dst_width, size_t dst_height, const float transformation[9], size_t channels, kleidicv_interpolation_type_t interpolation, - kleidicv_border_type_t border_type, - kleidicv_border_values_t border_values) { + kleidicv_border_type_t border_type, const uint8_t *border_value) { if (!kleidicv::warp_perspective_is_implemented( dst_width, interpolation, border_type, channels)) { return KLEIDICV_ERROR_NOT_IMPLEMENTED; @@ -30,7 +29,7 @@ kleidicv_error_t kleidicv_warp_perspective_u8( return kleidicv_warp_perspective_stripe_u8( src, src_stride, src_width, src_height, dst, dst_stride, dst_width, dst_height, 0, dst_height, transformation, 1, interpolation, border_type, - border_values); + border_value); } } // extern "C" diff --git a/kleidicv/src/transform/warp_perspective_neon.cpp b/kleidicv/src/transform/warp_perspective_neon.cpp index 67469f49b..7c84ee2d4 100644 --- a/kleidicv/src/transform/warp_perspective_neon.cpp +++ b/kleidicv/src/transform/warp_perspective_neon.cpp @@ -144,7 +144,7 @@ kleidicv_error_t warp_perspective_stripe( T *dst, size_t dst_stride, size_t dst_width, size_t dst_height, size_t y_begin, size_t y_end, const float transformation[9], size_t channels, kleidicv_interpolation_type_t, kleidicv_border_type_t, - kleidicv_border_values_t) { + const T *) { CHECK_POINTER_AND_STRIDE(src, src_stride, src_height); CHECK_POINTER_AND_STRIDE(dst, dst_stride, dst_height); CHECK_POINTERS(transformation); @@ -190,8 +190,7 @@ kleidicv_error_t warp_perspective_stripe( type *dst, size_t dst_stride, size_t dst_width, size_t dst_height, \ size_t y_begin, size_t y_end, const float transformation[9], \ size_t channels, kleidicv_interpolation_type_t interpolation, \ - kleidicv_border_type_t border_type, \ - kleidicv_border_values_t border_values) + kleidicv_border_type_t border_type, const type *border_value) KLEIDICV_INSTANTIATE_WARP_PERSPECTIVE(uint8_t); diff --git a/kleidicv/src/transform/warp_perspective_sc.h b/kleidicv/src/transform/warp_perspective_sc.h index 9df1c6318..aaa358ec7 100644 --- a/kleidicv/src/transform/warp_perspective_sc.h +++ b/kleidicv/src/transform/warp_perspective_sc.h @@ -190,7 +190,7 @@ KLEIDICV_LOCALLY_STREAMING kleidicv_error_t warp_perspective_stripe_sc( T *dst, size_t dst_stride, size_t dst_width, size_t dst_height, size_t y_begin, size_t y_end, const float transformation[9], size_t channels, kleidicv_interpolation_type_t, kleidicv_border_type_t, - kleidicv_border_values_t) { + const T *) { CHECK_POINTER_AND_STRIDE(src, src_stride, src_height); CHECK_POINTER_AND_STRIDE(dst, dst_stride, dst_height); CHECK_POINTERS(transformation); @@ -225,8 +225,7 @@ KLEIDICV_LOCALLY_STREAMING kleidicv_error_t warp_perspective_stripe_sc( type *dst, size_t dst_stride, size_t dst_width, size_t dst_height, \ size_t y_begin, size_t y_end, const float transformation[9], \ size_t channels, kleidicv_interpolation_type_t interpolation, \ - kleidicv_border_type_t border_type, \ - kleidicv_border_values_t border_values) + kleidicv_border_type_t border_type, const type *border_value) KLEIDICV_INSTANTIATE_WARP_PERSPECTIVE_SC(uint8_t); diff --git a/kleidicv/src/transform/warp_perspective_sve2.cpp b/kleidicv/src/transform/warp_perspective_sve2.cpp index 1bee07812..8310053d3 100644 --- a/kleidicv/src/transform/warp_perspective_sve2.cpp +++ b/kleidicv/src/transform/warp_perspective_sve2.cpp @@ -13,12 +13,11 @@ kleidicv_error_t warp_perspective_stripe( T *dst, size_t dst_stride, size_t dst_width, size_t dst_height, size_t y_begin, size_t y_end, const float transformation[9], size_t channels, kleidicv_interpolation_type_t interpolation, - kleidicv_border_type_t border_type, - kleidicv_border_values_t border_values) { + kleidicv_border_type_t border_type, const T *border_value) { return warp_perspective_stripe_sc( src, src_stride, src_width, src_height, dst, dst_stride, dst_width, dst_height, y_begin, y_end, transformation, channels, interpolation, - border_type, border_values); + border_type, border_value); } #define KLEIDICV_INSTANTIATE_WARP_PERSPECTIVE(type) \ @@ -28,8 +27,7 @@ kleidicv_error_t warp_perspective_stripe( type *dst, size_t dst_stride, size_t dst_width, size_t dst_height, \ size_t y_begin, size_t y_end, const float transformation[9], \ size_t channels, kleidicv_interpolation_type_t interpolation, \ - kleidicv_border_type_t border_type, \ - kleidicv_border_values_t border_values) + kleidicv_border_type_t border_type, const type *border_value) KLEIDICV_INSTANTIATE_WARP_PERSPECTIVE(uint8_t); diff --git a/kleidicv_thread/include/kleidicv_thread/kleidicv_thread.h b/kleidicv_thread/include/kleidicv_thread/kleidicv_thread.h index c9c04cc14..51189095f 100644 --- a/kleidicv_thread/include/kleidicv_thread/kleidicv_thread.h +++ b/kleidicv_thread/include/kleidicv_thread/kleidicv_thread.h @@ -389,7 +389,7 @@ kleidicv_error_t kleidicv_thread_remap_s16_u8( const uint8_t *src, size_t src_stride, size_t src_width, size_t src_height, uint8_t *dst, size_t dst_stride, size_t dst_width, size_t dst_height, size_t channels, const int16_t *mapxy, size_t mapxy_stride, - kleidicv_border_type_t border_type, kleidicv_border_values_t border_values, + kleidicv_border_type_t border_type, const uint8_t *border_value, kleidicv_thread_multithreading); /// Internal - not part of the public API and its direct use is not supported. @@ -401,7 +401,7 @@ kleidicv_error_t kleidicv_thread_remap_s16point5_u8( uint8_t *dst, size_t dst_stride, size_t dst_width, size_t dst_height, size_t channels, const int16_t *mapxy, size_t mapxy_stride, const uint16_t *mapfrac, size_t mapfrac_stride, - kleidicv_border_type_t border_type, kleidicv_border_values_t border_values, + kleidicv_border_type_t border_type, const uint8_t *border_value, kleidicv_thread_multithreading); /// Internal - not part of the public API and its direct use is not supported. @@ -413,7 +413,7 @@ kleidicv_error_t kleidicv_thread_warp_perspective_u8( uint8_t *dst, size_t dst_stride, size_t dst_width, size_t dst_height, const float transformation[9], size_t channels, kleidicv_interpolation_type_t interpolation, - kleidicv_border_type_t border_type, kleidicv_border_values_t border_values, + kleidicv_border_type_t border_type, const uint8_t *border_value, kleidicv_thread_multithreading); #ifdef __cplusplus diff --git a/kleidicv_thread/src/kleidicv_thread.cpp b/kleidicv_thread/src/kleidicv_thread.cpp index b1e34b4a5..5368e978a 100644 --- a/kleidicv_thread/src/kleidicv_thread.cpp +++ b/kleidicv_thread/src/kleidicv_thread.cpp @@ -660,7 +660,7 @@ kleidicv_error_t kleidicv_thread_remap_s16_u8( const uint8_t *src, size_t src_stride, size_t src_width, size_t src_height, uint8_t *dst, size_t dst_stride, size_t dst_width, size_t dst_height, size_t channels, const int16_t *mapxy, size_t mapxy_stride, - kleidicv_border_type_t border_type, kleidicv_border_values_t border_values, + kleidicv_border_type_t border_type, const uint8_t *border_value, kleidicv_thread_multithreading mt) { if (!kleidicv::remap_s16_is_implemented(dst_width, border_type, channels)) { @@ -671,7 +671,7 @@ kleidicv_error_t kleidicv_thread_remap_s16_u8( dst + begin * dst_stride / sizeof(uint8_t), dst_stride, dst_width, end - begin, channels, mapxy + begin * mapxy_stride / sizeof(int16_t), - mapxy_stride, border_type, border_values); + mapxy_stride, border_type, border_value); }; return parallel_batches(callback, mt, dst_height); } @@ -681,7 +681,7 @@ kleidicv_error_t kleidicv_thread_remap_s16point5_u8( uint8_t *dst, size_t dst_stride, size_t dst_width, size_t dst_height, size_t channels, const int16_t *mapxy, size_t mapxy_stride, const uint16_t *mapfrac, size_t mapfrac_stride, - kleidicv_border_type_t border_type, kleidicv_border_values_t border_values, + kleidicv_border_type_t border_type, const uint8_t *border_value, kleidicv_thread_multithreading mt) { if (!kleidicv::remap_s16point5_is_implemented(dst_width, border_type, channels)) { @@ -693,7 +693,7 @@ kleidicv_error_t kleidicv_thread_remap_s16point5_u8( dst + begin * dst_stride / sizeof(uint8_t), dst_stride, dst_width, end - begin, channels, mapxy + begin * mapxy_stride / sizeof(int16_t), mapxy_stride, mapfrac + begin * mapfrac_stride / sizeof(uint16_t), - mapfrac_stride, border_type, border_values); + mapfrac_stride, border_type, border_value); }; return parallel_batches(callback, mt, dst_height); } @@ -705,7 +705,7 @@ kleidicv_error_t kleidicv_thread_warp_perspective_u8( uint8_t *dst, size_t dst_stride, size_t dst_width, size_t dst_height, const float transformation[9], size_t channels, kleidicv_interpolation_type_t interpolation, - kleidicv_border_type_t border_type, kleidicv_border_values_t border_values, + kleidicv_border_type_t border_type, const uint8_t *border_value, kleidicv_thread_multithreading mt) { if (!kleidicv::warp_perspective_is_implemented( dst_width, interpolation, border_type, channels)) { @@ -716,7 +716,7 @@ kleidicv_error_t kleidicv_thread_warp_perspective_u8( return kleidicv_warp_perspective_stripe_u8( src, src_stride, src_width, src_height, dst, dst_stride, dst_width, dst_height, y_begin, std::min(dst_height, y_end + 1), - transformation, channels, interpolation, border_type, border_values); + transformation, channels, interpolation, border_type, border_value); }; return parallel_batches(callback, mt, dst_height); } diff --git a/test/api/test_blur_and_downsample.cpp b/test/api/test_blur_and_downsample.cpp index 1d36a65ff..305609bd0 100644 --- a/test/api/test_blur_and_downsample.cpp +++ b/test/api/test_blur_and_downsample.cpp @@ -62,7 +62,8 @@ class BlurAndDownsampleTest : public test::KernelTest { void test(const test::Array2D &mask) { test::Kernel kernel{mask}; // Create generators and execute test. - test::SequenceGenerator tested_border_values{test::default_border_values()}; + test::SequenceGenerator tested_border_values{ + test::default_border_values()}; test::PseudoRandomNumberGenerator element_generator; Base::test(kernel, array_layout_generator_, border_type_generator_, tested_border_values, element_generator); @@ -72,7 +73,7 @@ class BlurAndDownsampleTest : public test::KernelTest { kleidicv_error_t call_api(const test::Array2D *input, test::Array2D *output, kleidicv_border_type_t border_type, - kleidicv_border_values_t) override { + const InputType *) override { kleidicv_filter_context_t *context = nullptr; auto ret = kleidicv_filter_context_create( &context, input->channels(), kKernelSize, kKernelSize, diff --git a/test/api/test_gaussian_blur.cpp b/test/api/test_gaussian_blur.cpp index ca4d4fc2d..559301f8d 100644 --- a/test/api/test_gaussian_blur.cpp +++ b/test/api/test_gaussian_blur.cpp @@ -66,7 +66,8 @@ class GaussianBlurTest : public test::KernelTest { void test(const test::Array2D &mask) { test::Kernel kernel{mask}; // Create generators and execute test. - test::SequenceGenerator tested_border_values{test::default_border_values()}; + test::SequenceGenerator tested_border_values{ + test::default_border_values()}; test::PseudoRandomNumberGenerator element_generator; Base::test(kernel, array_layout_generator_, border_type_generator_, tested_border_values, element_generator); @@ -76,7 +77,7 @@ class GaussianBlurTest : public test::KernelTest { kleidicv_error_t call_api(const test::Array2D *input, test::Array2D *output, kleidicv_border_type_t border_type, - kleidicv_border_values_t) override { + const InputType *) override { kleidicv_filter_context_t *context = nullptr; auto ret = kleidicv_filter_context_create( &context, input->channels(), KernelTestParams::kKernelSize, diff --git a/test/api/test_morphology.cpp b/test/api/test_morphology.cpp index ffcde1243..50430eb92 100644 --- a/test/api/test_morphology.cpp +++ b/test/api/test_morphology.cpp @@ -10,6 +10,7 @@ #include "framework/array.h" #include "framework/generator.h" #include "framework/kernel.h" +#include "kleidicv/ctypes.h" #include "kleidicv/kleidicv.h" #include "test_config.h" @@ -56,9 +57,9 @@ static constexpr std::array kConstantBorder = { KLEIDICV_BORDER_TYPE_CONSTANT}; template -static const std::array &more_border_values() { +static const std::array, 4> &more_border_values() { using limit = std::numeric_limits; - static const std::array values = { + static const std::array, 4> values = { {{0, 0, 0, 0}, // default {7, 42, 99, 9}, {limit::min(), limit::max(), limit::min(), limit::max()}, @@ -70,7 +71,7 @@ template > + std::array, 1>> class MorphologyTest : public test::KernelTest { using Base = test::KernelTest; using typename Base::InputType; @@ -84,7 +85,8 @@ class MorphologyTest : public test::KernelTest { MorphologyKernelTestParams, size_t kernel_width, size_t kernel_height, ArrayLayoutsGetterType array_layouts_getter = test::small_array_layouts, BorderContainerType border_types = kDefaultBorder, - BorderValuesContainerType border_values = test::default_border_values()) + BorderValuesContainerType border_values = + test::default_border_values()) : kernel_width_{kernel_width}, kernel_height_{kernel_height}, mask_{kernel_width, kernel_height}, @@ -119,12 +121,12 @@ class MorphologyTest : public test::KernelTest { kleidicv_error_t call_api(const test::Array2D *input, test::Array2D *output, kleidicv_border_type_t border_type, - kleidicv_border_values_t border_values) override { + const InputType *border_value) override { kleidicv_morphology_context_t *context = nullptr; auto kernelRect = kleidicv_rectangle_t{kernel_width_, kernel_height_}; kleidicv_point_t anchor{kernel_.anchor().x, kernel_.anchor().y}; auto ret = kleidicv_morphology_create( - &context, kernelRect, anchor, border_type, border_values, + &context, kernelRect, anchor, border_type, border_value, input->channels(), iterations_, sizeof(InputType), kleidicv_rectangle_t{input->width() / input->channels(), input->height()}); @@ -146,14 +148,13 @@ class MorphologyTest : public test::KernelTest { void prepare_expected(const test::Kernel &kernel, const test::ArrayLayout &array_layout, kleidicv_border_type_t border_type, - kleidicv_border_values_t border_values) override { - Base::prepare_expected(kernel, array_layout, border_type, border_values); + const InputType *border_value) override { + Base::prepare_expected(kernel, array_layout, border_type, border_value); if (iterations_ > 1) { test::Array2D saved_input = this->input_; for (size_t i = 1; i < iterations_; ++i) { this->input_ = this->expected_; - Base::prepare_expected(kernel, array_layout, border_type, - border_values); + Base::prepare_expected(kernel, array_layout, border_type, border_value); } this->input_ = saved_input; } @@ -215,8 +216,8 @@ std::array get_large_array_layouts(size_t min_width, return {{ // clang-format off // width, height, padding, channels - { min_width * 8, min_height, 0, 8}, - { min_width * 8, min_height, vl, 8}, + { min_width * 4, min_height, 0, 4}, + { min_width * 4, min_height, vl, 4}, { min_width * 2, big_height, 0, 1}, { min_width * 2, big_height, vl, 1}, // clang-format on @@ -300,10 +301,10 @@ TYPED_TEST(Morphology, Anchors) { static kleidicv_error_t make_minimal_context( kleidicv_morphology_context_t **context, size_t type_size, kleidicv_border_type_t border = KLEIDICV_BORDER_TYPE_REPLICATE) { - return kleidicv_morphology_create(context, kleidicv_rectangle_t{1, 1}, - kleidicv_point_t{0, 0}, border, - kleidicv_border_values_t{0, 0, 1, 1}, 1, 1, - type_size, kleidicv_rectangle_t{1, 1}); + const uint8_t border_value[] = {0, 0, 1, 1}; + return kleidicv_morphology_create( + context, kleidicv_rectangle_t{1, 1}, kleidicv_point_t{0, 0}, border, + border_value, 1, 1, type_size, kleidicv_rectangle_t{1, 1}); } template @@ -311,7 +312,7 @@ static void test_valid_image_size(kleidicv_rectangle_t kernel, test::Array2D src) { size_t validSize = kernel.width - 1; kleidicv_rectangle_t image{validSize, validSize}; - kleidicv_border_values_t border_values{0, 0, 1, 1}; + const uint8_t border_value[] = {0, 0, 1, 1}; test::Array2D dst{validSize, validSize, test::Options::vector_length()}; @@ -326,7 +327,7 @@ static void test_valid_image_size(kleidicv_rectangle_t kernel, kleidicv_morphology_context_t *context = nullptr; ASSERT_EQ(KLEIDICV_OK, kleidicv_morphology_create(&context, kernel, anchor, border, - border_values, 1, 1, + border_value, 1, 1, sizeof(ElementType), image)); EXPECT_EQ(KLEIDICV_OK, ErodeParams::api()( @@ -352,11 +353,11 @@ static void test_undersize_image(kleidicv_rectangle_t kernel) { kleidicv_rectangle_t imageW{underSize, validHeight}; kleidicv_rectangle_t imageH{validWidth, underSize}; kleidicv_border_type_t border = KLEIDICV_BORDER_TYPE_REPLICATE; - kleidicv_border_values_t border_values{0, 0, 1, 1}; + const uint8_t border_value[] = {0, 0, 1, 1}; kleidicv_point_t anchor{1, 1}; ElementType src[1], dst[1]; ASSERT_EQ(KLEIDICV_OK, kleidicv_morphology_create( - &context, kernel, anchor, border, border_values, 1, + &context, kernel, anchor, border, border_value, 1, 1, sizeof(ElementType), image)); EXPECT_EQ(KLEIDICV_ERROR_NOT_IMPLEMENTED, ErodeParams::api()(src, sizeof(ElementType), dst, @@ -368,7 +369,7 @@ static void test_undersize_image(kleidicv_rectangle_t kernel) { underSize, context)); EXPECT_EQ(KLEIDICV_OK, kleidicv_morphology_release(context)); ASSERT_EQ(KLEIDICV_OK, kleidicv_morphology_create( - &context, kernel, anchor, border, border_values, 1, + &context, kernel, anchor, border, border_value, 1, 1, sizeof(ElementType), imageW)); EXPECT_EQ(KLEIDICV_ERROR_NOT_IMPLEMENTED, ErodeParams::api()(src, sizeof(ElementType), dst, @@ -380,7 +381,7 @@ static void test_undersize_image(kleidicv_rectangle_t kernel) { validHeight, context)); EXPECT_EQ(KLEIDICV_OK, kleidicv_morphology_release(context)); ASSERT_EQ(KLEIDICV_OK, kleidicv_morphology_create( - &context, kernel, anchor, border, border_values, 1, + &context, kernel, anchor, border, border_value, 1, 1, sizeof(ElementType), imageH)); EXPECT_EQ(KLEIDICV_ERROR_NOT_IMPLEMENTED, ErodeParams::api()(src, sizeof(ElementType), dst, @@ -413,7 +414,7 @@ TYPED_TEST(Morphology, UnsupportedSize) { kleidicv_rectangle_t small_rect{1, 1}; kleidicv_point_t anchor{0, 0}; kleidicv_border_type_t border = KLEIDICV_BORDER_TYPE_REPLICATE; - kleidicv_border_values_t border_values{0, 0, 1, 1}; + const uint8_t border_value[] = {0, 0, 1, 1}; for (kleidicv_rectangle_t bad_rect : { kleidicv_rectangle_t{KLEIDICV_MAX_IMAGE_PIXELS + 1, 1}, @@ -422,31 +423,45 @@ TYPED_TEST(Morphology, UnsupportedSize) { }) { EXPECT_EQ(KLEIDICV_ERROR_RANGE, kleidicv_morphology_create(&context, bad_rect, anchor, border, - border_values, 1, 1, sizeof(TypeParam), + border_value, 1, 1, sizeof(TypeParam), small_rect)); ASSERT_EQ(nullptr, context); EXPECT_EQ(KLEIDICV_ERROR_RANGE, kleidicv_morphology_create(&context, small_rect, anchor, border, - border_values, 1, 1, sizeof(TypeParam), + border_value, 1, 1, sizeof(TypeParam), bad_rect)); ASSERT_EQ(nullptr, context); } } +TYPED_TEST(Morphology, UnsupportedChannels) { + kleidicv_morphology_context_t *context = nullptr; + kleidicv_rectangle_t small_rect{1, 1}; + kleidicv_point_t anchor{0, 0}; + kleidicv_border_type_t border = KLEIDICV_BORDER_TYPE_REPLICATE; + const uint8_t border_value[] = {0, 0, 1, 1}; + + EXPECT_EQ(KLEIDICV_ERROR_RANGE, + kleidicv_morphology_create(&context, small_rect, anchor, border, + border_value, 5, 1, sizeof(TypeParam), + small_rect)); + ASSERT_EQ(nullptr, context); +} + #ifdef KLEIDICV_ALLOCATION_TESTS TYPED_TEST(Morphology, CannotAllocateImage) { MockMallocToFail::enable(); kleidicv_morphology_context_t *context = nullptr; kleidicv_rectangle_t kernel{3, 3}, image{3072, 2048}; kleidicv_border_type_t border = KLEIDICV_BORDER_TYPE_REPLICATE; - kleidicv_border_values_t border_values{0, 0, 1, 1}; + const uint8_t border_value[] = {0, 0, 1, 1}; kleidicv_point_t anchor{1, 1}; - EXPECT_EQ(KLEIDICV_ERROR_ALLOCATION, - kleidicv_morphology_create(&context, kernel, anchor, border, - border_values, 1, 1, sizeof(TypeParam), - image)); + EXPECT_EQ( + KLEIDICV_ERROR_ALLOCATION, + kleidicv_morphology_create(&context, kernel, anchor, border, border_value, + 1, 1, sizeof(TypeParam), image)); MockMallocToFail::disable(); } #endif @@ -455,61 +470,61 @@ TYPED_TEST(Morphology, OversizeImage) { kleidicv_morphology_context_t *context = nullptr; kleidicv_rectangle_t kernel{3, 1UL << 33}, image{1UL << 33, 100}; kleidicv_border_type_t border = KLEIDICV_BORDER_TYPE_REPLICATE; - kleidicv_border_values_t border_values{0, 0, 1, 1}; + const uint8_t border_value[] = {0, 0, 1, 1}; kleidicv_point_t anchor{1, 1}; - EXPECT_EQ(KLEIDICV_ERROR_RANGE, - kleidicv_morphology_create(&context, kernel, anchor, border, - border_values, 1, 1, sizeof(TypeParam), - image)); + EXPECT_EQ( + KLEIDICV_ERROR_RANGE, + kleidicv_morphology_create(&context, kernel, anchor, border, border_value, + 1, 1, sizeof(TypeParam), image)); } TYPED_TEST(Morphology, InvalidAnchors) { kleidicv_morphology_context_t *context = nullptr; kleidicv_rectangle_t kernel1{1, 1}, kernel2{6, 4}, image{20, 20}; kleidicv_border_type_t border = KLEIDICV_BORDER_TYPE_REPLICATE; - kleidicv_border_values_t border_values{0, 0, 1, 1}; + const uint8_t border_value[] = {0, 0, 1, 1}; kleidicv_point_t anchor1{1, 0}, anchor2{6, 3}, anchor3{5, 4}; - EXPECT_EQ(KLEIDICV_ERROR_RANGE, - kleidicv_morphology_create(&context, kernel1, anchor1, border, - border_values, 1, 1, sizeof(TypeParam), - image)); + EXPECT_EQ( + KLEIDICV_ERROR_RANGE, + kleidicv_morphology_create(&context, kernel1, anchor1, border, + border_value, 1, 1, sizeof(TypeParam), image)); ASSERT_EQ(nullptr, context); - EXPECT_EQ(KLEIDICV_ERROR_RANGE, - kleidicv_morphology_create(&context, kernel2, anchor2, border, - border_values, 1, 1, sizeof(TypeParam), - image)); + EXPECT_EQ( + KLEIDICV_ERROR_RANGE, + kleidicv_morphology_create(&context, kernel2, anchor2, border, + border_value, 1, 1, sizeof(TypeParam), image)); ASSERT_EQ(nullptr, context); - EXPECT_EQ(KLEIDICV_ERROR_RANGE, - kleidicv_morphology_create(&context, kernel2, anchor3, border, - border_values, 1, 1, sizeof(TypeParam), - image)); + EXPECT_EQ( + KLEIDICV_ERROR_RANGE, + kleidicv_morphology_create(&context, kernel2, anchor3, border, + border_value, 1, 1, sizeof(TypeParam), image)); ASSERT_EQ(nullptr, context); } TYPED_TEST(Morphology, InvalidTypeSize) { kleidicv_morphology_context_t *context = nullptr; + const uint8_t border_value[] = {}; - EXPECT_EQ( - KLEIDICV_ERROR_RANGE, - kleidicv_morphology_create( - &context, kleidicv_rectangle_t{1, 1}, kleidicv_point_t{0, 0}, - KLEIDICV_BORDER_TYPE_REPLICATE, kleidicv_border_values_t{0, 0, 1, 1}, - 1, 1, KLEIDICV_MAXIMUM_TYPE_SIZE + 1, kleidicv_rectangle_t{1, 1})); + EXPECT_EQ(KLEIDICV_ERROR_RANGE, + kleidicv_morphology_create( + &context, kleidicv_rectangle_t{1, 1}, kleidicv_point_t{0, 0}, + KLEIDICV_BORDER_TYPE_REPLICATE, border_value, 1, 1, + KLEIDICV_MAXIMUM_TYPE_SIZE + 1, kleidicv_rectangle_t{1, 1})); ASSERT_EQ(nullptr, context); } TYPED_TEST(Morphology, InvalidChannelNumber) { kleidicv_morphology_context_t *context = nullptr; + const uint8_t border_value[] = {}; EXPECT_EQ(KLEIDICV_ERROR_RANGE, - kleidicv_morphology_create(&context, kleidicv_rectangle_t{1, 1}, - kleidicv_point_t{0, 0}, - KLEIDICV_BORDER_TYPE_REPLICATE, - kleidicv_border_values_t{0, 0, 1, 1}, - KLEIDICV_MAXIMUM_CHANNEL_COUNT + 1, 1, 1, - kleidicv_rectangle_t{1, 1})); + kleidicv_morphology_create( + &context, kleidicv_rectangle_t{1, 1}, kleidicv_point_t{0, 0}, + KLEIDICV_BORDER_TYPE_REPLICATE, border_value, + KLEIDICV_MAXIMUM_CHANNEL_COUNT + 1, 1, 1, + kleidicv_rectangle_t{1, 1})); ASSERT_EQ(nullptr, context); } @@ -517,11 +532,11 @@ TYPED_TEST(Morphology, ImageBiggerThanContext) { kleidicv_morphology_context_t *context = nullptr; kleidicv_rectangle_t kernel{3, 3}, image{5, 5}; kleidicv_border_type_t border = KLEIDICV_BORDER_TYPE_REPLICATE; - kleidicv_border_values_t border_values{0, 0, 1, 1}; + const uint8_t border_value[] = {0, 0, 1, 1}; kleidicv_point_t anchor{1, 1}; EXPECT_EQ(KLEIDICV_OK, kleidicv_morphology_create(&context, kernel, anchor, - border, border_values, 1, 1, + border, border_value, 1, 1, sizeof(TypeParam), image)); const size_t w = 7, h = 7; TypeParam src[w * h], dst[w * h]; @@ -536,6 +551,19 @@ TYPED_TEST(Morphology, ImageBiggerThanContext) { EXPECT_EQ(KLEIDICV_OK, kleidicv_morphology_release(context)); } +TYPED_TEST(Morphology, CreateNullPointer) { + kleidicv_morphology_context_t *context = nullptr; + kleidicv_rectangle_t small_rect{1, 1}; + kleidicv_point_t anchor{0, 0}; + kleidicv_border_type_t border = KLEIDICV_BORDER_TYPE_CONSTANT; + + EXPECT_EQ( + KLEIDICV_ERROR_NULL_POINTER, + kleidicv_morphology_create(&context, small_rect, anchor, border, nullptr, + 4, 1, sizeof(TypeParam), small_rect)); + ASSERT_EQ(nullptr, context); +} + TYPED_TEST(Morphology, DilateNullPointer) { kleidicv_morphology_context_t *context = nullptr; ASSERT_EQ(KLEIDICV_OK, make_minimal_context(&context, sizeof(TypeParam))); @@ -593,23 +621,21 @@ TYPED_TEST(Morphology, ErodeMisalignment) { TYPED_TEST(Morphology, DilateZeroImageSize) { kleidicv_morphology_context_t *context = nullptr; TypeParam src[1], dst[1]; - ASSERT_EQ( - KLEIDICV_OK, - kleidicv_morphology_create( - &context, kleidicv_rectangle_t{1, 1}, kleidicv_point_t{0, 0}, - KLEIDICV_BORDER_TYPE_REPLICATE, kleidicv_border_values_t{0, 0, 1, 1}, - 1, 1, sizeof(TypeParam), kleidicv_rectangle_t{0, 1})); + ASSERT_EQ(KLEIDICV_OK, + kleidicv_morphology_create( + &context, kleidicv_rectangle_t{1, 1}, kleidicv_point_t{0, 0}, + KLEIDICV_BORDER_TYPE_REPLICATE, nullptr, 1, 1, + sizeof(TypeParam), kleidicv_rectangle_t{0, 1})); EXPECT_EQ(KLEIDICV_OK, DilateParams::api()(src, sizeof(TypeParam), dst, sizeof(TypeParam), 0, 1, context)); EXPECT_EQ(KLEIDICV_OK, kleidicv_morphology_release(context)); - ASSERT_EQ( - KLEIDICV_OK, - kleidicv_morphology_create( - &context, kleidicv_rectangle_t{1, 1}, kleidicv_point_t{0, 0}, - KLEIDICV_BORDER_TYPE_REPLICATE, kleidicv_border_values_t{0, 0, 1, 1}, - 1, 1, sizeof(TypeParam), kleidicv_rectangle_t{1, 0})); + ASSERT_EQ(KLEIDICV_OK, + kleidicv_morphology_create( + &context, kleidicv_rectangle_t{1, 1}, kleidicv_point_t{0, 0}, + KLEIDICV_BORDER_TYPE_REPLICATE, nullptr, 1, 1, + sizeof(TypeParam), kleidicv_rectangle_t{1, 0})); EXPECT_EQ(KLEIDICV_OK, DilateParams::api()(src, sizeof(TypeParam), dst, sizeof(TypeParam), 1, 0, context)); @@ -619,23 +645,21 @@ TYPED_TEST(Morphology, DilateZeroImageSize) { TYPED_TEST(Morphology, ErodeZeroImageSize) { kleidicv_morphology_context_t *context = nullptr; TypeParam src[1], dst[1]; - ASSERT_EQ( - KLEIDICV_OK, - kleidicv_morphology_create( - &context, kleidicv_rectangle_t{1, 1}, kleidicv_point_t{0, 0}, - KLEIDICV_BORDER_TYPE_REPLICATE, kleidicv_border_values_t{0, 0, 1, 1}, - 1, 1, sizeof(TypeParam), kleidicv_rectangle_t{0, 1})); + ASSERT_EQ(KLEIDICV_OK, + kleidicv_morphology_create( + &context, kleidicv_rectangle_t{1, 1}, kleidicv_point_t{0, 0}, + KLEIDICV_BORDER_TYPE_REPLICATE, nullptr, 1, 1, + sizeof(TypeParam), kleidicv_rectangle_t{0, 1})); EXPECT_EQ(KLEIDICV_OK, ErodeParams::api()(src, sizeof(TypeParam), dst, sizeof(TypeParam), 0, 1, context)); EXPECT_EQ(KLEIDICV_OK, kleidicv_morphology_release(context)); - ASSERT_EQ( - KLEIDICV_OK, - kleidicv_morphology_create( - &context, kleidicv_rectangle_t{1, 1}, kleidicv_point_t{0, 0}, - KLEIDICV_BORDER_TYPE_REPLICATE, kleidicv_border_values_t{0, 0, 1, 1}, - 1, 1, sizeof(TypeParam), kleidicv_rectangle_t{1, 0})); + ASSERT_EQ(KLEIDICV_OK, + kleidicv_morphology_create( + &context, kleidicv_rectangle_t{1, 1}, kleidicv_point_t{0, 0}, + KLEIDICV_BORDER_TYPE_REPLICATE, nullptr, 1, 1, + sizeof(TypeParam), kleidicv_rectangle_t{1, 0})); EXPECT_EQ(KLEIDICV_OK, ErodeParams::api()(src, sizeof(TypeParam), dst, sizeof(TypeParam), 1, 0, context)); @@ -737,12 +761,12 @@ TYPED_TEST(Morphology, ErodeOversizeImage) { } TEST(MorphologyCreate, NullPointer) { + const uint8_t border_value[4] = {}; EXPECT_EQ(KLEIDICV_ERROR_NULL_POINTER, - kleidicv_morphology_create(nullptr, kleidicv_rectangle_t{1, 1}, - kleidicv_point_t{0, 0}, - KLEIDICV_BORDER_TYPE_REPLICATE, - kleidicv_border_values_t{0, 0, 1, 1}, 1, - 1, 1, kleidicv_rectangle_t{1, 1})); + kleidicv_morphology_create( + nullptr, kleidicv_rectangle_t{1, 1}, kleidicv_point_t{0, 0}, + KLEIDICV_BORDER_TYPE_REPLICATE, border_value, 1, 1, 1, + kleidicv_rectangle_t{1, 1})); } TEST(MorphologyRelease, NullPointer) { EXPECT_EQ(KLEIDICV_ERROR_NULL_POINTER, kleidicv_morphology_release(nullptr)); diff --git a/test/api/test_remap.cpp b/test/api/test_remap.cpp index dd4b02548..a5a731e71 100644 --- a/test/api/test_remap.cpp +++ b/test/api/test_remap.cpp @@ -171,8 +171,7 @@ TYPED_TEST(RemapS16, NullPointer) { TypeParam dst[1]; int16_t mapxy[2] = {}; test::test_null_args(kleidicv_remap_s16_u8, src, 2, 2, 2, dst, 1, 1, 1, 1, - mapxy, 4, KLEIDICV_BORDER_TYPE_REPLICATE, - kleidicv_border_values_t{}); + mapxy, 4, KLEIDICV_BORDER_TYPE_REPLICATE, nullptr); } TYPED_TEST(RemapS16, ZeroImageSize) { @@ -182,12 +181,10 @@ TYPED_TEST(RemapS16, ZeroImageSize) { EXPECT_EQ(KLEIDICV_ERROR_NOT_IMPLEMENTED, kleidicv_remap_s16_u8(src, 1, 0, 1, dst, 1, 0, 1, 1, mapxy, 4, - KLEIDICV_BORDER_TYPE_REPLICATE, - kleidicv_border_values_t{})); + KLEIDICV_BORDER_TYPE_REPLICATE, nullptr)); EXPECT_EQ(KLEIDICV_ERROR_NOT_IMPLEMENTED, kleidicv_remap_s16_u8(src, 1, 1, 0, dst, 1, 1, 0, 1, mapxy, 4, - KLEIDICV_BORDER_TYPE_REPLICATE, - kleidicv_border_values_t{})); + KLEIDICV_BORDER_TYPE_REPLICATE, nullptr)); } TYPED_TEST(RemapS16, InvalidImageSize) { @@ -195,29 +192,27 @@ TYPED_TEST(RemapS16, InvalidImageSize) { TypeParam dst[8]; int16_t mapxy[16] = {}; + EXPECT_EQ(KLEIDICV_ERROR_RANGE, + kleidicv_remap_s16_u8(src, 1, KLEIDICV_MAX_IMAGE_PIXELS + 1, 1, dst, + 8, 8, 1, 1, mapxy, 4, + KLEIDICV_BORDER_TYPE_REPLICATE, nullptr)); + EXPECT_EQ( KLEIDICV_ERROR_RANGE, - kleidicv_remap_s16_u8(src, 1, KLEIDICV_MAX_IMAGE_PIXELS + 1, 1, dst, 8, 8, - 1, 1, mapxy, 4, KLEIDICV_BORDER_TYPE_REPLICATE, - kleidicv_border_values_t{})); + kleidicv_remap_s16_u8(src, 1, KLEIDICV_MAX_IMAGE_PIXELS, + KLEIDICV_MAX_IMAGE_PIXELS, dst, 8, 8, 1, 1, mapxy, + 4, KLEIDICV_BORDER_TYPE_REPLICATE, nullptr)); EXPECT_EQ(KLEIDICV_ERROR_RANGE, - kleidicv_remap_s16_u8(src, 1, KLEIDICV_MAX_IMAGE_PIXELS, - KLEIDICV_MAX_IMAGE_PIXELS, dst, 8, 8, 1, 1, - mapxy, 4, KLEIDICV_BORDER_TYPE_REPLICATE, - kleidicv_border_values_t{})); + kleidicv_remap_s16_u8(src, 1, 1, 1, dst, 1, + KLEIDICV_MAX_IMAGE_PIXELS + 1, 1, 1, mapxy, 4, + KLEIDICV_BORDER_TYPE_REPLICATE, nullptr)); EXPECT_EQ( KLEIDICV_ERROR_RANGE, - kleidicv_remap_s16_u8(src, 1, 1, 1, dst, 1, KLEIDICV_MAX_IMAGE_PIXELS + 1, - 1, 1, mapxy, 4, KLEIDICV_BORDER_TYPE_REPLICATE, - kleidicv_border_values_t{})); - - EXPECT_EQ(KLEIDICV_ERROR_RANGE, - kleidicv_remap_s16_u8( - src, 1, 1, 1, dst, 1, KLEIDICV_MAX_IMAGE_PIXELS, - KLEIDICV_MAX_IMAGE_PIXELS, 1, mapxy, 4, - KLEIDICV_BORDER_TYPE_REPLICATE, kleidicv_border_values_t{})); + kleidicv_remap_s16_u8(src, 1, 1, 1, dst, 1, KLEIDICV_MAX_IMAGE_PIXELS, + KLEIDICV_MAX_IMAGE_PIXELS, 1, mapxy, 4, + KLEIDICV_BORDER_TYPE_REPLICATE, nullptr)); } TYPED_TEST(RemapS16, UnsupportedTwoChannels) { @@ -227,8 +222,7 @@ TYPED_TEST(RemapS16, UnsupportedTwoChannels) { EXPECT_EQ(KLEIDICV_ERROR_NOT_IMPLEMENTED, kleidicv_remap_s16_u8(src, 1, 1, 1, dst, 8, 8, 1, 2, mapxy, 4, - KLEIDICV_BORDER_TYPE_REPLICATE, - kleidicv_border_values_t{})); + KLEIDICV_BORDER_TYPE_REPLICATE, nullptr)); } TYPED_TEST(RemapS16, UnsupportedBorderTypeConst) { @@ -238,8 +232,7 @@ TYPED_TEST(RemapS16, UnsupportedBorderTypeConst) { EXPECT_EQ(KLEIDICV_ERROR_NOT_IMPLEMENTED, kleidicv_remap_s16_u8(src, 1, 1, 1, dst, 8, 8, 1, 1, mapxy, 4, - KLEIDICV_BORDER_TYPE_CONSTANT, - kleidicv_border_values_t{})); + KLEIDICV_BORDER_TYPE_CONSTANT, src)); } TYPED_TEST(RemapS16, UnsupportedTooSmallImage) { @@ -249,8 +242,7 @@ TYPED_TEST(RemapS16, UnsupportedTooSmallImage) { EXPECT_EQ(KLEIDICV_ERROR_NOT_IMPLEMENTED, kleidicv_remap_s16_u8(src, 1, 1, 1, dst, 8, 7, 1, 1, mapxy, 4, - KLEIDICV_BORDER_TYPE_REPLICATE, - kleidicv_border_values_t{})); + KLEIDICV_BORDER_TYPE_REPLICATE, nullptr)); } template @@ -468,7 +460,7 @@ TYPED_TEST(RemapS16Point5, NullPointer) { uint16_t mapfrac[1] = {}; test::test_null_args(kleidicv_remap_s16point5_u8, src, 2, 2, 2, dst, 1, 1, 1, 1, mapxy, 4, mapfrac, 2, KLEIDICV_BORDER_TYPE_REPLICATE, - kleidicv_border_values_t{}); + nullptr); } TYPED_TEST(RemapS16Point5, ZeroImageSize) { @@ -480,11 +472,11 @@ TYPED_TEST(RemapS16Point5, ZeroImageSize) { EXPECT_EQ(KLEIDICV_ERROR_NOT_IMPLEMENTED, kleidicv_remap_s16point5_u8( src, 1, 0, 1, dst, 1, 0, 1, 1, mapxy, 4, mapfrac, 2, - KLEIDICV_BORDER_TYPE_REPLICATE, kleidicv_border_values_t{})); + KLEIDICV_BORDER_TYPE_REPLICATE, nullptr)); EXPECT_EQ(KLEIDICV_ERROR_NOT_IMPLEMENTED, kleidicv_remap_s16point5_u8( src, 1, 1, 0, dst, 1, 1, 0, 1, mapxy, 4, mapfrac, 2, - KLEIDICV_BORDER_TYPE_REPLICATE, kleidicv_border_values_t{})); + KLEIDICV_BORDER_TYPE_REPLICATE, nullptr)); } TYPED_TEST(RemapS16Point5, InvalidImageSize) { @@ -494,28 +486,26 @@ TYPED_TEST(RemapS16Point5, InvalidImageSize) { uint16_t mapfrac[1] = {}; EXPECT_EQ(KLEIDICV_ERROR_RANGE, - kleidicv_remap_s16point5_u8(src, 1, KLEIDICV_MAX_IMAGE_PIXELS + 1, - 1, dst, 1, 1, 1, 1, mapxy, 4, mapfrac, - 2, KLEIDICV_BORDER_TYPE_REPLICATE, - kleidicv_border_values_t{})); + kleidicv_remap_s16point5_u8( + src, 1, KLEIDICV_MAX_IMAGE_PIXELS + 1, 1, dst, 1, 1, 1, 1, + mapxy, 4, mapfrac, 2, KLEIDICV_BORDER_TYPE_REPLICATE, nullptr)); EXPECT_EQ(KLEIDICV_ERROR_RANGE, kleidicv_remap_s16point5_u8( src, 1, KLEIDICV_MAX_IMAGE_PIXELS, KLEIDICV_MAX_IMAGE_PIXELS, dst, 1, 1, 1, 1, mapxy, 4, mapfrac, 2, - KLEIDICV_BORDER_TYPE_REPLICATE, kleidicv_border_values_t{})); + KLEIDICV_BORDER_TYPE_REPLICATE, nullptr)); EXPECT_EQ(KLEIDICV_ERROR_RANGE, kleidicv_remap_s16point5_u8( src, 1, 1, 1, dst, 1, KLEIDICV_MAX_IMAGE_PIXELS + 1, 1, 1, - mapxy, 4, mapfrac, 2, KLEIDICV_BORDER_TYPE_REPLICATE, - kleidicv_border_values_t{})); + mapxy, 4, mapfrac, 2, KLEIDICV_BORDER_TYPE_REPLICATE, nullptr)); EXPECT_EQ(KLEIDICV_ERROR_RANGE, kleidicv_remap_s16point5_u8( src, 1, 1, 1, dst, 1, KLEIDICV_MAX_IMAGE_PIXELS, KLEIDICV_MAX_IMAGE_PIXELS, 1, mapxy, 4, mapfrac, 2, - KLEIDICV_BORDER_TYPE_REPLICATE, kleidicv_border_values_t{})); + KLEIDICV_BORDER_TYPE_REPLICATE, nullptr)); } TYPED_TEST(RemapS16Point5, UnsupportedTwoChannels) { @@ -527,7 +517,7 @@ TYPED_TEST(RemapS16Point5, UnsupportedTwoChannels) { EXPECT_EQ(KLEIDICV_ERROR_NOT_IMPLEMENTED, kleidicv_remap_s16point5_u8( src, 1, 1, 1, dst, 8, 8, 1, 2, mapxy, 4, mapfrac, 2, - KLEIDICV_BORDER_TYPE_REPLICATE, kleidicv_border_values_t{})); + KLEIDICV_BORDER_TYPE_REPLICATE, nullptr)); } TYPED_TEST(RemapS16Point5, UnsupportedBorderTypeConst) { @@ -537,9 +527,9 @@ TYPED_TEST(RemapS16Point5, UnsupportedBorderTypeConst) { uint16_t mapfrac[8] = {}; EXPECT_EQ(KLEIDICV_ERROR_NOT_IMPLEMENTED, - kleidicv_remap_s16point5_u8( - src, 1, 1, 1, dst, 8, 8, 1, 1, mapxy, 4, mapfrac, 2, - KLEIDICV_BORDER_TYPE_CONSTANT, kleidicv_border_values_t{})); + kleidicv_remap_s16point5_u8(src, 1, 1, 1, dst, 8, 8, 1, 1, mapxy, 4, + mapfrac, 2, + KLEIDICV_BORDER_TYPE_CONSTANT, src)); } TYPED_TEST(RemapS16Point5, UnsupportedTooSmallImage) { @@ -551,6 +541,6 @@ TYPED_TEST(RemapS16Point5, UnsupportedTooSmallImage) { EXPECT_EQ(KLEIDICV_ERROR_NOT_IMPLEMENTED, kleidicv_remap_s16point5_u8( src, 1, 1, 1, dst, 8, 7, 1, 1, mapxy, 4, mapfrac, 2, - KLEIDICV_BORDER_TYPE_REPLICATE, kleidicv_border_values_t{})); + KLEIDICV_BORDER_TYPE_REPLICATE, nullptr)); } #endif // KLEIDICV_EXPERIMENTAL_FEATURE_REMAP diff --git a/test/api/test_separable_filter_2d.cpp b/test/api/test_separable_filter_2d.cpp index 44862bc48..0b5d80274 100644 --- a/test/api/test_separable_filter_2d.cpp +++ b/test/api/test_separable_filter_2d.cpp @@ -81,7 +81,8 @@ class SeparableFilter2DTest : public test::KernelTest { void test(const test::Array2D &mask, InputType max_value) { test::Kernel kernel{mask}; // Create generators and execute test. - test::SequenceGenerator tested_border_values{test::default_border_values()}; + test::SequenceGenerator tested_border_values{ + test::default_border_values()}; test::PseudoRandomNumberGeneratorIntRange element_generator{ 0, max_value}; Base::test(kernel, array_layout_generator_, border_type_generator_, @@ -92,7 +93,7 @@ class SeparableFilter2DTest : public test::KernelTest { kleidicv_error_t call_api(const test::Array2D *input, test::Array2D *output, kleidicv_border_type_t border_type, - kleidicv_border_values_t) override { + const InputType *) override { kleidicv_filter_context_t *context = nullptr; auto ret = kleidicv_filter_context_create( &context, input->channels(), KernelTestParams::kKernelSize, diff --git a/test/api/test_sobel.cpp b/test/api/test_sobel.cpp index 03c6a153b..41bf00c47 100644 --- a/test/api/test_sobel.cpp +++ b/test/api/test_sobel.cpp @@ -51,7 +51,7 @@ class Sobel3x3Test : public test::KernelTest { kleidicv_error_t call_api(const test::Array2D *input, test::Array2D *output, kleidicv_border_type_t, - kleidicv_border_values_t) override { + const InputType *) override { auto api = KernelTestParams::kIsHorizontal ? sobel_3x3_horizontal() : sobel_3x3_vertical(); @@ -69,7 +69,8 @@ class Sobel3x3Test : public test::KernelTest { // Create generators and execute test. test::SequenceGenerator tested_array_layouts{array_layouts}; test::SequenceGenerator tested_borders{kSupportedBorders}; - test::SequenceGenerator tested_border_values{test::default_border_values()}; + test::SequenceGenerator tested_border_values{ + test::default_border_values()}; test::PseudoRandomNumberGenerator element_generator; Base::test(kernel, tested_array_layouts, tested_borders, tested_border_values, element_generator); diff --git a/test/api/test_thread.cpp b/test/api/test_thread.cpp index 443eb794c..e8c319ea0 100644 --- a/test/api/test_thread.cpp +++ b/test/api/test_thread.cpp @@ -597,54 +597,56 @@ TEST(ThreadSeparableFilter2D, NotImplemented) { #if KLEIDICV_EXPERIMENTAL_FEATURE_REMAP TEST_P(Thread, remap_s16_u8_border_replicate) { check_remap_s16(kleidicv_remap_s16_u8, kleidicv_thread_remap_s16_u8, - 1, KLEIDICV_BORDER_TYPE_REPLICATE, - kleidicv_border_values_t{}); + 1, KLEIDICV_BORDER_TYPE_REPLICATE, nullptr); } TEST_P(Thread, remap_s16_u8_not_implemented) { + const uint8_t border_value[4] = {}; check_remap_s16_not_implemented(kleidicv_thread_remap_s16_u8, 2, KLEIDICV_BORDER_TYPE_REPLICATE, - kleidicv_border_values_t{}); + border_value); check_remap_s16_not_implemented(kleidicv_thread_remap_s16_u8, 1, KLEIDICV_BORDER_TYPE_CONSTANT, - kleidicv_border_values_t{}); + border_value); } TEST_P(Thread, remap_s16point5_u8_border_replicate) { - kleidicv_border_values_t border_values = {}; check_remap_s16point5(kleidicv_remap_s16point5_u8, kleidicv_thread_remap_s16point5_u8, 1, - KLEIDICV_BORDER_TYPE_REPLICATE, border_values); + KLEIDICV_BORDER_TYPE_REPLICATE, nullptr); } TEST_P(Thread, remap_s16point5_u8_not_implemented) { + const uint8_t border_value[4] = {}; check_remap_s16point5_not_implemented( kleidicv_thread_remap_s16point5_u8, 2, KLEIDICV_BORDER_TYPE_REPLICATE, - kleidicv_border_values_t{}); + border_value); check_remap_s16point5_not_implemented( kleidicv_thread_remap_s16point5_u8, 1, KLEIDICV_BORDER_TYPE_CONSTANT, - kleidicv_border_values_t{}); + border_value); } #endif // KLEIDICV_EXPERIMENTAL_FEATURE_REMAP #if KLEIDICV_EXPERIMENTAL_FEATURE_WARP_PERSPECTIVE TEST_P(Thread, warp_perspective_u8_border_replicate) { - check_warp_perspective( - kleidicv_warp_perspective_u8, kleidicv_thread_warp_perspective_u8, 1, - KLEIDICV_INTERPOLATION_NEAREST, KLEIDICV_BORDER_TYPE_REPLICATE, - kleidicv_border_values_t{}); + const uint8_t border_value[4] = {}; + check_warp_perspective(kleidicv_warp_perspective_u8, + kleidicv_thread_warp_perspective_u8, 1, + KLEIDICV_INTERPOLATION_NEAREST, + KLEIDICV_BORDER_TYPE_REPLICATE, border_value); } TEST_P(Thread, warp_perspective_u8_not_implemented) { + const uint8_t border_value[4] = {}; check_warp_perspective_not_implemented( kleidicv_thread_warp_perspective_u8, 1, KLEIDICV_INTERPOLATION_LINEAR, - KLEIDICV_BORDER_TYPE_REPLICATE, kleidicv_border_values_t{}); + KLEIDICV_BORDER_TYPE_REPLICATE, border_value); check_warp_perspective_not_implemented( kleidicv_thread_warp_perspective_u8, 2, KLEIDICV_INTERPOLATION_NEAREST, - KLEIDICV_BORDER_TYPE_REPLICATE, kleidicv_border_values_t{}); + KLEIDICV_BORDER_TYPE_REPLICATE, border_value); check_warp_perspective_not_implemented( kleidicv_thread_warp_perspective_u8, 1, KLEIDICV_INTERPOLATION_NEAREST, - KLEIDICV_BORDER_TYPE_CONSTANT, kleidicv_border_values_t{}); + KLEIDICV_BORDER_TYPE_CONSTANT, border_value); } #endif // KLEIDICV_EXPERIMENTAL_FEATURE_WARP_PERSPECTIVE diff --git a/test/api/test_warp_perspective.cpp b/test/api/test_warp_perspective.cpp index f43ee2ed8..60c457f8f 100644 --- a/test/api/test_warp_perspective.cpp +++ b/test/api/test_warp_perspective.cpp @@ -288,8 +288,7 @@ TYPED_TEST(WarpPerspective, NullPointer) { TypeParam dst[8]; test::test_null_args(kleidicv_warp_perspective_u8, src, 2, 2, 2, dst, 8, 8, 1, transform_identity, 1, KLEIDICV_INTERPOLATION_NEAREST, - KLEIDICV_BORDER_TYPE_REPLICATE, - kleidicv_border_values_t{}); + KLEIDICV_BORDER_TYPE_REPLICATE, nullptr); } TYPED_TEST(WarpPerspective, ZeroImageSize) { @@ -300,12 +299,12 @@ TYPED_TEST(WarpPerspective, ZeroImageSize) { kleidicv_warp_perspective_u8( src, 1, 0, 1, dst, 1, 0, 1, transform_identity, 1, KLEIDICV_INTERPOLATION_NEAREST, KLEIDICV_BORDER_TYPE_REPLICATE, - kleidicv_border_values_t{})); + nullptr)); EXPECT_EQ(KLEIDICV_ERROR_NOT_IMPLEMENTED, kleidicv_warp_perspective_u8( src, 1, 1, 0, dst, 1, 1, 0, transform_identity, 1, KLEIDICV_INTERPOLATION_NEAREST, KLEIDICV_BORDER_TYPE_REPLICATE, - kleidicv_border_values_t{})); + nullptr)); } TYPED_TEST(WarpPerspective, InvalidImageSize) { @@ -316,27 +315,27 @@ TYPED_TEST(WarpPerspective, InvalidImageSize) { kleidicv_warp_perspective_u8( src, 1, KLEIDICV_MAX_IMAGE_PIXELS + 1, 1, dst, 8, 8, 1, transform_identity, 1, KLEIDICV_INTERPOLATION_NEAREST, - KLEIDICV_BORDER_TYPE_REPLICATE, kleidicv_border_values_t{})); + KLEIDICV_BORDER_TYPE_REPLICATE, nullptr)); EXPECT_EQ( KLEIDICV_ERROR_RANGE, kleidicv_warp_perspective_u8( src, 1, KLEIDICV_MAX_IMAGE_PIXELS, KLEIDICV_MAX_IMAGE_PIXELS, dst, 8, 8, 1, transform_identity, 1, KLEIDICV_INTERPOLATION_NEAREST, - KLEIDICV_BORDER_TYPE_REPLICATE, kleidicv_border_values_t{})); + KLEIDICV_BORDER_TYPE_REPLICATE, nullptr)); EXPECT_EQ(KLEIDICV_ERROR_RANGE, kleidicv_warp_perspective_u8( src, 1, 1, 1, dst, 1, KLEIDICV_MAX_IMAGE_PIXELS + 1, 1, transform_identity, 1, KLEIDICV_INTERPOLATION_NEAREST, - KLEIDICV_BORDER_TYPE_REPLICATE, kleidicv_border_values_t{})); + KLEIDICV_BORDER_TYPE_REPLICATE, nullptr)); EXPECT_EQ(KLEIDICV_ERROR_RANGE, kleidicv_warp_perspective_u8( src, 1, 1, 1, dst, 1, KLEIDICV_MAX_IMAGE_PIXELS, KLEIDICV_MAX_IMAGE_PIXELS, transform_identity, 1, KLEIDICV_INTERPOLATION_NEAREST, KLEIDICV_BORDER_TYPE_REPLICATE, - kleidicv_border_values_t{})); + nullptr)); } TYPED_TEST(WarpPerspective, UnsupportedTwoChannels) { @@ -347,7 +346,7 @@ TYPED_TEST(WarpPerspective, UnsupportedTwoChannels) { kleidicv_warp_perspective_u8( src, 1, 1, 1, dst, 8, 8, 1, transform_identity, 2, KLEIDICV_INTERPOLATION_NEAREST, KLEIDICV_BORDER_TYPE_REPLICATE, - kleidicv_border_values_t{})); + nullptr)); } TYPED_TEST(WarpPerspective, UnsupportedBorderTypeConst) { @@ -355,10 +354,10 @@ TYPED_TEST(WarpPerspective, UnsupportedBorderTypeConst) { TypeParam dst[8]; EXPECT_EQ(KLEIDICV_ERROR_NOT_IMPLEMENTED, - kleidicv_warp_perspective_u8( - src, 1, 1, 1, dst, 8, 8, 1, transform_identity, 1, - KLEIDICV_INTERPOLATION_NEAREST, KLEIDICV_BORDER_TYPE_CONSTANT, - kleidicv_border_values_t{})); + kleidicv_warp_perspective_u8(src, 1, 1, 1, dst, 8, 8, 1, + transform_identity, 1, + KLEIDICV_INTERPOLATION_NEAREST, + KLEIDICV_BORDER_TYPE_CONSTANT, src)); } TYPED_TEST(WarpPerspective, UnsupportedTooSmallImage) { @@ -369,7 +368,7 @@ TYPED_TEST(WarpPerspective, UnsupportedTooSmallImage) { kleidicv_warp_perspective_u8( src, 1, 1, 1, dst, 8, 7, 1, transform_identity, 1, KLEIDICV_INTERPOLATION_NEAREST, KLEIDICV_BORDER_TYPE_REPLICATE, - kleidicv_border_values_t{})); + nullptr)); } TYPED_TEST(WarpPerspective, UnsupportedInterpolation) { @@ -380,7 +379,7 @@ TYPED_TEST(WarpPerspective, UnsupportedInterpolation) { kleidicv_warp_perspective_u8( src, 1, 1, 1, dst, 8, 8, 1, transform_identity, 1, KLEIDICV_INTERPOLATION_LINEAR, KLEIDICV_BORDER_TYPE_REPLICATE, - kleidicv_border_values_t{})); + nullptr)); } TYPED_TEST(WarpPerspective, UnsupportedBigStride) { @@ -391,7 +390,7 @@ TYPED_TEST(WarpPerspective, UnsupportedBigStride) { kleidicv_warp_perspective_u8( src, 1UL << 32, 1, 1, dst, 8, 8, 1, transform_identity, 1, KLEIDICV_INTERPOLATION_NEAREST, KLEIDICV_BORDER_TYPE_REPLICATE, - kleidicv_border_values_t{})); + nullptr)); } TYPED_TEST(WarpPerspective, UnsupportedBigHeight) { @@ -402,7 +401,7 @@ TYPED_TEST(WarpPerspective, UnsupportedBigHeight) { kleidicv_warp_perspective_u8( src, 1, 1, 1UL << 24, dst, 8, 8, 1, transform_identity, 1, KLEIDICV_INTERPOLATION_NEAREST, KLEIDICV_BORDER_TYPE_REPLICATE, - kleidicv_border_values_t{})); + nullptr)); } TYPED_TEST(WarpPerspective, UnsupportedBigWidth) { @@ -413,6 +412,6 @@ TYPED_TEST(WarpPerspective, UnsupportedBigWidth) { kleidicv_warp_perspective_u8( src, 1, 1UL << 24, 1, dst, 8, 8, 1, transform_identity, 1, KLEIDICV_INTERPOLATION_NEAREST, KLEIDICV_BORDER_TYPE_REPLICATE, - kleidicv_border_values_t{})); + nullptr)); } #endif // KLEIDICV_EXPERIMENTAL_FEATURE_WARP_PERSPECTIVE diff --git a/test/framework/border.cpp b/test/framework/border.cpp index 6004471c0..3b16df08f 100644 --- a/test/framework/border.cpp +++ b/test/framework/border.cpp @@ -67,8 +67,7 @@ static void replicate(const Bordered *bordered, // | left border | elements | right border | // | X X | A B C D E | Y Y | template -static void constant(const Bordered *bordered, - kleidicv_border_values_t border_values, +static void constant(const Bordered *bordered, const ElementType *border_value, TwoDimensional *elements) { ASSERT_LE((bordered->left() + bordered->right()) * elements->channels(), elements->width()); @@ -80,7 +79,7 @@ static void constant(const Bordered *bordered, // 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; + elements->at(row, dst_column)[0] = border_value[channel]; } // Prepare right border columns. @@ -88,23 +87,29 @@ static void constant(const Bordered *bordered, size_t dst_column = elements->width() + (column - bordered->right()) * elements->channels() + channel; - elements->at(row, dst_column)[0] = border_values.right; + elements->at(row, dst_column)[0] = border_value[channel]; } } } // 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; + for (size_t column = 0; column < elements->width();) { + for (size_t channel = 0; channel < elements->channels(); + ++channel, ++column) { + elements->at(row, column)[0] = border_value[channel]; + } } } // 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) { - elements->at(row, column)[0] = border_values.bottom; + for (size_t column = 0; column < elements->width();) { + for (size_t channel = 0; channel < elements->channels(); + ++channel, ++column) { + elements->at(row, column)[0] = border_value[channel]; + } } } } @@ -281,8 +286,7 @@ static void reverse(const Bordered *bordered, template void prepare_borders(kleidicv_border_type_t border_type, - kleidicv_border_values_t border_values, - const Bordered *bordered, + const ElementType *border_value, const Bordered *bordered, TwoDimensional *elements) { ASSERT_NE(bordered, nullptr); ASSERT_NE(elements, nullptr); @@ -295,7 +299,7 @@ void prepare_borders(kleidicv_border_type_t border_type, return replicate(bordered, elements); case KLEIDICV_BORDER_TYPE_CONSTANT: - return constant(bordered, border_values, elements); + return constant(bordered, border_value, elements); case KLEIDICV_BORDER_TYPE_REFLECT: return reflect(bordered, elements); @@ -308,18 +312,15 @@ void prepare_borders(kleidicv_border_type_t border_type, } } -template void prepare_borders(kleidicv_border_type_t, - kleidicv_border_values_t, +template void prepare_borders(kleidicv_border_type_t, const uint8_t *, const Bordered *, TwoDimensional *); template void prepare_borders(kleidicv_border_type_t, - kleidicv_border_values_t, - const Bordered *, + const uint16_t *, const Bordered *, TwoDimensional *); -template void prepare_borders(kleidicv_border_type_t, - kleidicv_border_values_t, +template void prepare_borders(kleidicv_border_type_t, const int16_t *, const Bordered *, TwoDimensional *); diff --git a/test/framework/border.h b/test/framework/border.h index 3a4355185..0b499b855 100644 --- a/test/framework/border.h +++ b/test/framework/border.h @@ -13,8 +13,7 @@ namespace test { // Prepares bordering elements given a border type and bordering requirements. template void prepare_borders(kleidicv_border_type_t border_type, - kleidicv_border_values_t border_values, - const Bordered *bordered, + const ElementType *border_value, const Bordered *bordered, TwoDimensional *elements); } // namespace test diff --git a/test/framework/kernel.h b/test/framework/kernel.h index 8889ac962..4579cac9a 100644 --- a/test/framework/kernel.h +++ b/test/framework/kernel.h @@ -85,7 +85,7 @@ class KernelTest { void test(Generator>& kernel_generator, Generator& array_layout_generator, Generator& border_type_generator, - Generator& border_values_generator, + Generator>& border_values_generator, Generator& element_generator) { kernel_generator.reset(); @@ -100,7 +100,7 @@ class KernelTest { void test(const Kernel& kernel, Generator& array_layout_generator, Generator& border_type_generator, - Generator& border_values_generator, + Generator>& border_values_generator, Generator& element_generator) { array_layout_generator.reset(); @@ -118,7 +118,7 @@ class KernelTest { void test(const Kernel& kernel, ArrayLayout array_layout, Generator& border_type_generator, - Generator& border_values_generator, + Generator>& border_values_generator, Generator& element_generator) { border_type_generator.reset(); @@ -132,27 +132,25 @@ class KernelTest { void test(const Kernel& kernel, ArrayLayout array_layout, kleidicv_border_type_t border_type, - Generator& border_values_generator, + Generator>& border_values_generator, Generator& element_generator) { border_values_generator.reset(); - std::optional maybe_border_values; + std::optional> maybe_border_values; while ((maybe_border_values = border_values_generator.next()) != std::nullopt) { - test(kernel, array_layout, border_type, *maybe_border_values, + test(kernel, array_layout, border_type, maybe_border_values->data(), element_generator); ASSERT_NO_FAILURES(); } } void test(const Kernel& kernel, ArrayLayout array_layout, - kleidicv_border_type_t border_type, - kleidicv_border_values_t border_values, + kleidicv_border_type_t border_type, const InputType* border_value, Generator& element_generator) { prepare_source(element_generator); - prepare_expected(kernel, array_layout, border_type, border_values); + prepare_expected(kernel, array_layout, border_type, border_value); prepare_actual(); - check_results( - this->call_api(&input_, &actual_, border_type, border_values)); + check_results(this->call_api(&input_, &actual_, border_type, border_value)); } protected: @@ -162,7 +160,7 @@ class KernelTest { virtual kleidicv_error_t call_api(const Array2D* input, Array2D* output, kleidicv_border_type_t border_type, - kleidicv_border_values_t border_values) = 0; + const InputType* border_value) = 0; // Calculates the expected output. virtual void calculate_expected(const Kernel& kernel, @@ -236,7 +234,7 @@ class KernelTest { virtual void prepare_expected(const Kernel& kernel, const ArrayLayout& array_layout, kleidicv_border_type_t border_type, - kleidicv_border_values_t border_values) { + const InputType* border_value) { input_with_borders_.set(kernel.anchor().y, kernel.anchor().x * array_layout.channels, &input_); @@ -245,7 +243,7 @@ class KernelTest { dump(&input_with_borders_); } - prepare_borders(border_type, border_values, &kernel, + prepare_borders(border_type, border_value, &kernel, &input_with_borders_); if (debug_) { diff --git a/test/framework/test_border.cpp b/test/framework/test_border.cpp index c36431e94..6385745ef 100644 --- a/test/framework/test_border.cpp +++ b/test/framework/test_border.cpp @@ -48,8 +48,9 @@ TEST(Border, Replicate_1Ch_1Element) { expected.set(4, 0, {9, 9, 0, 1, 2, 2}); ImplementsBorder bordered{1, 1, 1, 1}; + const ElementType border_value[1] = {}; test::prepare_borders(KLEIDICV_BORDER_TYPE_REPLICATE, - {0, 0, 0, 0}, &bordered, &actual); + border_value, &bordered, &actual); EXPECT_EQ_ARRAY2D(expected, actual); } @@ -77,8 +78,9 @@ TEST(Border, Replicate_1Ch_2Elements) { expected.set(5, 0, {3, 3, 3, 4, 4, 4}); ImplementsBorder bordered{2, 2, 2, 2}; + const ElementType border_value[1] = {}; test::prepare_borders(KLEIDICV_BORDER_TYPE_REPLICATE, - {0, 0, 0, 0}, &bordered, &actual); + border_value, &bordered, &actual); EXPECT_EQ_ARRAY2D(expected, actual); } @@ -104,8 +106,9 @@ TEST(Border, Replicate_2Ch_1Element) { expected.set(4, 0, {9, 1, 9, 1, 2, 3, 2, 3}); ImplementsBorder bordered{1, 1, 1, 1}; + const ElementType border_value[channels] = {}; test::prepare_borders(KLEIDICV_BORDER_TYPE_REPLICATE, - {0, 0, 0, 0}, &bordered, &actual); + border_value, &bordered, &actual); EXPECT_EQ_ARRAY2D(expected, actual); } @@ -133,7 +136,8 @@ TEST(Border, Replicate_2Ch_2Elements) { expected.set(5, 0, {5, 6, 5, 6, 5, 6, 7, 8, 7, 8, 7, 8}); ImplementsBorder bordered{2, 2, 2, 2}; + const ElementType border_value[channels] = {}; test::prepare_borders(KLEIDICV_BORDER_TYPE_REPLICATE, - {0, 0, 0, 0}, &bordered, &actual); + border_value, &bordered, &actual); EXPECT_EQ_ARRAY2D(expected, actual); } diff --git a/test/framework/test_kernel.cpp b/test/framework/test_kernel.cpp index d3750e49c..31900ffce 100644 --- a/test/framework/test_kernel.cpp +++ b/test/framework/test_kernel.cpp @@ -96,16 +96,15 @@ class ExampleKernelTest : public test::KernelTest { kleidicv_error_t call_api(const test::Array2D *input, test::Array2D *output, kleidicv_border_type_t border_type, - kleidicv_border_values_t border_values) override { + const InputType *border_value) override { // Check the expected border type. EXPECT_EQ(border_type, kBorders[border_count_ % kBorders.size()]); // Check the expected border value. - auto act = border_values; - auto exp = kBorderValues[border_value_count_ % kBorderValues.size()]; - EXPECT_EQ(act.top, exp.top); - EXPECT_EQ(act.right, exp.right); - EXPECT_EQ(act.bottom, exp.bottom); - EXPECT_EQ(act.left, exp.left); + auto actual = border_value; + auto expected = kBorderValues[border_value_count_ % kBorderValues.size()]; + for (size_t i = 0; i < kBorderValues.size(); ++i) { + EXPECT_EQ(actual[i], expected[i]); + } // Check the expected layout. const test::ArrayLayout &expected_array_layout = @@ -152,7 +151,7 @@ class ExampleKernelTest : public test::KernelTest { static constexpr std::array kBorders = { KLEIDICV_BORDER_TYPE_REPLICATE, KLEIDICV_BORDER_TYPE_CONSTANT}; - static constexpr std::array kBorderValues = { + static constexpr std::array, 2> kBorderValues = { {{0, 0, 0, 0}, {1, 2, 3, 4}}}; size_t api_calls_{0}; diff --git a/test/framework/utils.cpp b/test/framework/utils.cpp index 2c1a8cf09..f51c2e6ca 100644 --- a/test/framework/utils.cpp +++ b/test/framework/utils.cpp @@ -59,12 +59,6 @@ template void dump(const TwoDimensional *); template void dump(const TwoDimensional *); template void dump(const TwoDimensional *); -const std::array &default_border_values() { - static const std::array kDefaultBorderValues{ - {{0, 0, 0, 0}}}; - return kDefaultBorderValues; -} - 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 656a2a02c..87bfcd70a 100644 --- a/test/framework/utils.h +++ b/test/framework/utils.h @@ -114,7 +114,12 @@ template void dump(const TwoDimensional *elements); // Returns default border values. -const std::array &default_border_values(); +template +const std::array, 1> &default_border_values() { + static std::array, 1> result{ + std::array{0, 0, 0, 0}}; + return result; +} // Returns an array of just a few small layouts. std::array small_array_layouts(size_t min_width, @@ -131,11 +136,15 @@ template class NullPointerTester { // Set the given argument to null and test that the function diagnoses the // error correctly. + // If the given argument is *already* set to null then this signals that it is + // valid for the argument to be null and the function is not called. template static typename std::enable_if>::type test_with_null_arg(Function f, Tuple t) { - std::get(t) = nullptr; - EXPECT_EQ(KLEIDICV_ERROR_NULL_POINTER, std::apply(f, t)); + if (nullptr != std::get(t)) { + std::get(t) = nullptr; + EXPECT_EQ(KLEIDICV_ERROR_NULL_POINTER, std::apply(f, t)); + } } // Skip arguments that aren't pointers. -- GitLab