From cbf70954ee3c158f67c032526ca0cd6a71792397 Mon Sep 17 00:00:00 2001 From: Michael Platings Date: Wed, 8 May 2024 10:12:27 +0000 Subject: [PATCH] Re-enable cv_hal_minMaxIdx * Patch OpenCV 4.9 to only call cv_hal_minMaxIdx with a single channel. * Allow conformity tests to use matrices with higher dimensions. * Add min_max conformity tests. --- adapters/opencv/kleidicv_hal.h | 4 -- adapters/opencv/opencv-4.9.patch | 17 ++++- conformity/opencv/CMakeLists.txt | 2 + conformity/opencv/common.h | 26 ++++--- conformity/opencv/test_min_max.cpp | 106 +++++++++++++++++++++++++++++ conformity/opencv/test_min_max.h | 14 ++++ conformity/opencv/tests.cpp | 2 + doc/opencv.md | 12 ++++ 8 files changed, 168 insertions(+), 15 deletions(-) create mode 100644 conformity/opencv/test_min_max.cpp create mode 100644 conformity/opencv/test_min_max.h diff --git a/adapters/opencv/kleidicv_hal.h b/adapters/opencv/kleidicv_hal.h index 1df5b667f..a25be7719 100644 --- a/adapters/opencv/kleidicv_hal.h +++ b/adapters/opencv/kleidicv_hal.h @@ -288,9 +288,6 @@ static inline int kleidicv_transpose_with_fallback( #undef cv_hal_transpose2d #define cv_hal_transpose2d kleidicv_transpose_with_fallback -// cv_hal_minMaxIdx is unstable in OpenCV -// See https://github.com/opencv/opencv/issues/25540 -#if 0 // min_max_idx static inline int kleidicv_min_max_idx_with_fallback( const uchar *src_data, size_t src_stride, int width, int height, int depth, @@ -302,7 +299,6 @@ static inline int kleidicv_min_max_idx_with_fallback( } #undef cv_hal_minMaxIdx #define cv_hal_minMaxIdx kleidicv_min_max_idx_with_fallback -#endif // 0 #if defined(cv_hal_convertTo) static inline int kleidicv_convertTo_with_fallback( diff --git a/adapters/opencv/opencv-4.9.patch b/adapters/opencv/opencv-4.9.patch index 86f30a874..87ca8ad88 100644 --- a/adapters/opencv/opencv-4.9.patch +++ b/adapters/opencv/opencv-4.9.patch @@ -4,7 +4,7 @@ diff --git a/3rdparty/kleidicv/CMakeLists.txt b/3rdparty/kleidicv/CMakeLists.txt new file mode 100644 -index 0000000000..c0ffb73ad7 +index 0000000000..5105214af3 --- /dev/null +++ b/3rdparty/kleidicv/CMakeLists.txt @@ -0,0 +1,3 @@ @@ -96,7 +96,7 @@ index 345b4624cb..8698cc64bf 100644 if( dims <= 2 ) diff --git a/modules/core/src/hal_replacement.hpp b/modules/core/src/hal_replacement.hpp -index 1f2b259920..b8eb6e22b1 100644 +index 1f2b259920..0e19b24f7f 100644 --- a/modules/core/src/hal_replacement.hpp +++ b/modules/core/src/hal_replacement.hpp @@ -818,6 +818,35 @@ inline int hal_ni_rotate90(int src_type, const uchar* src_data, size_t src_step, @@ -148,6 +148,19 @@ index 5a80ac8ca7..bad17e7b6b 100644 CV_IPP_RUN_FAST(ipp_transpose(src, dst)) if( dst.data == src.data ) +diff --git a/modules/core/src/minmax.cpp b/modules/core/src/minmax.cpp +index ff3786886e..62db04f8eb 100644 +--- a/modules/core/src/minmax.cpp ++++ b/modules/core/src/minmax.cpp +@@ -1510,7 +1510,7 @@ void cv::minMaxIdx(InputArray _src, double* minVal, + + Mat src = _src.getMat(), mask = _mask.getMat(); + +- if (src.dims <= 2) ++ if (cn == 1 && src.dims <= 2) + CALL_HAL(minMaxIdx, cv_hal_minMaxIdx, src.data, src.step, src.cols, src.rows, src.depth(), minVal, maxVal, + minIdx, maxIdx, mask.data); + diff --git a/modules/imgproc/src/hal_replacement.hpp b/modules/imgproc/src/hal_replacement.hpp index c066f3d6f3..d8b58015f9 100644 --- a/modules/imgproc/src/hal_replacement.hpp diff --git a/conformity/opencv/CMakeLists.txt b/conformity/opencv/CMakeLists.txt index 74ea42d6e..06912c661 100644 --- a/conformity/opencv/CMakeLists.txt +++ b/conformity/opencv/CMakeLists.txt @@ -31,6 +31,7 @@ add_executable( manager.cpp tests.cpp test_gaussian_blur.cpp + test_min_max.cpp test_rgb2yuv.cpp test_sobel.cpp ) @@ -68,6 +69,7 @@ add_executable( subordinate.cpp tests.cpp test_gaussian_blur.cpp + test_min_max.cpp test_rgb2yuv.cpp test_sobel.cpp ) diff --git a/conformity/opencv/common.h b/conformity/opencv/common.h index b8f316b8d..b34720445 100644 --- a/conformity/opencv/common.h +++ b/conformity/opencv/common.h @@ -29,6 +29,8 @@ #define KLEIDICV_CONFORMITY_REPLY_MQ_ID \ "/opencv_kleidicv_conformity_reply_queue" +#define KLEIDICV_CONFORMITY_MAX_MAT_DIMENSIONS 4 + class ExceptionWithErrno : public std::exception { public: explicit ExceptionWithErrno(const std::string& msg) @@ -119,17 +121,18 @@ class SharedMemory { SharedMemory(SharedMemory const&) = delete; SharedMemory& operator=(SharedMemory) = delete; - cv::Mat cv_mat(int rows, int cols, int mat_type) { - size_t requested_size = rows * cols * cv::Mat(1, 1, mat_type).elemSize(); + cv::Mat cv_mat(int ndims, const int* sizes, int mat_type) { + size_t requested_size = cv::Mat(1, 1, mat_type).elemSize(); + for (int i = 0; i < ndims; ++i) requested_size *= sizes[i]; if (requested_size > size_) { throw std::runtime_error( "Requested matrix is bigger than the shared memory size"); } - return cv::Mat(rows, cols, mat_type, mem_); + return cv::Mat(ndims, sizes, mat_type, mem_); } void store_mat(const cv::Mat& mat) { - size_t matrix_size = mat.rows * mat.cols * mat.elemSize(); + size_t matrix_size = mat.total() * mat.elemSize(); if (matrix_size > size_) { throw std::runtime_error( "Input matrix is bigger than the shared memory size"); @@ -172,13 +175,18 @@ class MessageQueue { MessageQueue& operator=(MessageQueue) = delete; void request_exit() { - message m = {-1, 0, 0, 0}; + message m = {}; + m.cmd = -1; send(m); } void request_operation(int cmd, const cv::Mat& mat) { sm_.store_mat(mat); - message m = {cmd, mat.rows, mat.cols, mat.type()}; + message m; + m.cmd = cmd; + m.type = mat.type(); + m.ndims = mat.dims; + memcpy(m.sizes, mat.size.p, sizeof(int) * mat.dims); send(m); } @@ -205,16 +213,16 @@ class MessageQueue { int last_cmd() const { return last_message_.cmd; } cv::Mat cv_mat_from_last_msg() const { - return sm_.cv_mat(last_message_.rows, last_message_.cols, + return sm_.cv_mat(last_message_.ndims, last_message_.sizes, last_message_.type); } private: struct message { int cmd; - int rows; - int cols; int type; + int ndims; + int sizes[KLEIDICV_CONFORMITY_MAX_MAT_DIMENSIONS]; }; static mqd_t open(const std::string& id) { diff --git a/conformity/opencv/test_min_max.cpp b/conformity/opencv/test_min_max.cpp new file mode 100644 index 000000000..ce88bf03c --- /dev/null +++ b/conformity/opencv/test_min_max.cpp @@ -0,0 +1,106 @@ +// SPDX-FileCopyrightText: 2024 Arm Limited and/or its affiliates +// +// SPDX-License-Identifier: Apache-2.0 + +#include "test_min_max.h" + +#include +#include + +template +cv::Mat exec_min_max(cv::Mat& input) { + double minVal, maxVal; + if constexpr (GetIndex) { + int min_index[2], max_index[2]; + cv::minMaxIdx(input, &minVal, &maxVal, min_index, max_index); + cv::Mat result(6, 1, CV_32SC1); + result.at(0, 0) = minVal; + result.at(1, 0) = maxVal; + result.at(2, 0) = min_index[0]; + result.at(3, 0) = min_index[1]; + result.at(4, 0) = max_index[0]; + result.at(5, 0) = max_index[1]; + return result; + } else { + cv::minMaxIdx(input, &minVal, &maxVal); + cv::Mat result(2, 1, CV_32SC1); + result.at(0, 0) = minVal; + result.at(1, 0) = maxVal; + return result; + } +} + +#if MANAGER +template +bool test_min_max(int index, RecreatedMessageQueue& request_queue, + RecreatedMessageQueue& reply_queue) { + cv::RNG rng(0); + + constexpr size_t Channels = (Format >> CV_CN_SHIFT) + 1; + + // Test 2D matrices + for (size_t x = 5; x <= 16; ++x) { + for (size_t y = 5; y <= 16; ++y) { + cv::Mat input(x, y, Format, cv::Scalar::all(100)); + + // Add a few random values at random locations. + for (int i = 0; i < 3; ++i) { + input.at(rng.next() % x, rng.next() % y, rng.next() % Channels) = + rng.next(); + } + + cv::Mat actual = exec_min_max(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; + } + } + } + + // Test 3D matrix + const int x = 4, y = 3, z = 2; + cv::Mat input(std::vector{x, y, z}, Format); + rng.fill(input, cv::RNG::UNIFORM, std::numeric_limits::lowest(), + std::numeric_limits::max()); + cv::Mat actual = exec_min_max(input); + cv::Mat expected = + get_expected_from_subordinate(index, request_queue, reply_queue, input); + if (are_matrices_different(0, actual, expected)) { + std::cout << "[FAIL]\n"; + std::cout << "=== Input Matrix:\n"; + for (int i = 0; i < z; ++i) { + // Output a slice of the 3D matrix. + std::cout << cv::Mat(x, y, Format, + input.ptr() + input.step[0] * input.step[1] * i) + << "\n"; + } + std::cout << "\n=== Manager result (actual):\n" << actual; + std::cout << "\n\n=== Subordinate result (expected):\n" << expected; + std::cout << "\n" << std::endl; + return true; + } + + return false; +} +#endif + +std::vector& min_max_tests_get() { + // clang-format off + static std::vector tests = { + TEST("min_max_u8, 1 channel", (test_min_max), (exec_min_max)), + TEST("min_max_u8, 2 channel", (test_min_max), (exec_min_max)), + TEST("min_max_s8, 1 channel", (test_min_max), (exec_min_max)), + TEST("min_max_s8, 3 channel", (test_min_max), (exec_min_max)), + TEST("min_max_u16, 1 channel", (test_min_max), (exec_min_max)), + TEST("min_max_u16, 4 channel", (test_min_max), (exec_min_max)), + TEST("min_max_u16, 1 channel", (test_min_max), (exec_min_max)), + TEST("min_max_u16, 3 channel", (test_min_max), (exec_min_max)), + TEST("min_max_s32, 2 channel", (test_min_max), (exec_min_max)), + TEST("min_max_s32, 4 channel", (test_min_max), (exec_min_max)), + TEST("min_max_loc_u8", (test_min_max), (exec_min_max)), + }; + // clang-format on + return tests; +} diff --git a/conformity/opencv/test_min_max.h b/conformity/opencv/test_min_max.h new file mode 100644 index 000000000..df793b3cd --- /dev/null +++ b/conformity/opencv/test_min_max.h @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: 2024 Arm Limited and/or its affiliates +// +// SPDX-License-Identifier: Apache-2.0 + +#ifndef KLEIDICV_OPENCV_CONFORMITY_TEST_MIN_MAX_H_ +#define KLEIDICV_OPENCV_CONFORMITY_TEST_MIN_MAX_H_ + +#include + +#include "tests.h" + +std::vector& min_max_tests_get(); + +#endif // KLEIDICV_OPENCV_CONFORMITY_TEST_MIN_MAX_H_ diff --git a/conformity/opencv/tests.cpp b/conformity/opencv/tests.cpp index 2c02e39fb..257f00d7b 100644 --- a/conformity/opencv/tests.cpp +++ b/conformity/opencv/tests.cpp @@ -11,6 +11,7 @@ #include "opencv2/core.hpp" #include "opencv2/imgproc.hpp" #include "test_gaussian_blur.h" +#include "test_min_max.h" #include "test_rgb2yuv.h" #include "test_sobel.h" @@ -26,6 +27,7 @@ static std::vector merge_tests( std::vector all_tests = merge_tests({ gaussian_blur_tests_get, + min_max_tests_get, rgb2yuv_tests_get, sobel_tests_get, }); diff --git a/doc/opencv.md b/doc/opencv.md index ef17f76b6..8dccc17fa 100644 --- a/doc/opencv.md +++ b/doc/opencv.md @@ -154,5 +154,17 @@ Notes on parameters: + `uint8_t` + `uint16_t` +### `minMaxIdx` +Finds the minimum and maximum element values and their positions. + +Notes on parameters: +* `minIdx`,`maxIdx` - only supported for `depth == CV_8U` +* `depth` - supported element size (without specifying index): + + `CV_8S` + + `CV_8U` + + `CV_16S` + + `CV_16U` + + `CV_32S` + ### `convertTo` Currently converting to different data types is not supported. This function scales given input of `src_depth == CV_8U` using `scale` and `shift`. -- GitLab