diff --git a/conformity/opencv/CMakeLists.txt b/conformity/opencv/CMakeLists.txt index 3daa88943d04f4b1c5925056c99c1cc607f597eb..85111dc2f64327756a987e17dc5bc1c1355d8adc 100644 --- a/conformity/opencv/CMakeLists.txt +++ b/conformity/opencv/CMakeLists.txt @@ -32,6 +32,7 @@ add_executable( manager manager.cpp tests.cpp + utils.cpp ${conformity_test_sources} ) @@ -67,6 +68,7 @@ add_executable( subordinate subordinate.cpp tests.cpp + utils.cpp ${conformity_test_sources} ) diff --git a/conformity/opencv/test_gaussian_blur.cpp b/conformity/opencv/test_gaussian_blur.cpp index 776fbec2bf6499cedc230569d8d3e80d7e2e2226..8b189c74e1e286e43ffb37e48c0b643188f3e729 100644 --- a/conformity/opencv/test_gaussian_blur.cpp +++ b/conformity/opencv/test_gaussian_blur.cpp @@ -6,13 +6,13 @@ #include "tests.h" -template +template cv::Mat exec_gaussian_blur(cv::Mat& input) { double sigma = - *reinterpret_cast(&input.at(input.rows - 2, 0)); + *reinterpret_cast(&input.at(input.rows - 1, 0)); // clone is required, otherwise the result matrix is treated as part of a // bigger image, and it would have impact on what border types are supported - cv::Mat input_mat = input.rowRange(0, input.rows - 2).clone(); + cv::Mat input_mat = input.rowRange(0, input.rows - 1).clone(); cv::Size kernel(KernelSize, KernelSize); cv::Mat result; cv::GaussianBlur(input_mat, result, kernel, sigma, sigma, BorderType); @@ -20,54 +20,47 @@ cv::Mat exec_gaussian_blur(cv::Mat& input) { } #if MANAGER -template bool test_gaussian_blur(int index, RecreatedMessageQueue& request_queue, RecreatedMessageQueue& reply_queue) { cv::RNG rng(0); - size_t size_min = 5; - size_t size_max = 16; - if constexpr (KernelSize == 15) { - size_min = 14; - size_max = 32; - } + // Minimal width is sizeof(double) and one more row is allcated to place the + // value of sigma next to the real input + for (auto size : typical_test_sizes( + std::max(KernelSize - 1, static_cast(sizeof(double))), + KernelSize - 1)) { + cv::Mat input(size.height + 1, size.width, CV_8UC(Channels)); + rng.fill(input, cv::RNG::UNIFORM, 0, 255); + + double sigma = 0.0; + + if constexpr (!Binomial) { + // cv::rng returns [0,1) range in case of float or double, so it is + // multiplied by 10 + sigma = static_cast(rng) * 10; + } + + // sigma is embedded into the input matrix + *reinterpret_cast(&input.at(input.rows - 1, 0)) = sigma; + + cv::Mat actual = exec_gaussian_blur(input); + cv::Mat expected = + get_expected_from_subordinate(index, request_queue, reply_queue, input); + + uint8_t threshold = 0; + // There are currently rounding differences sometimes + // between the OpenCV and KleidiCV implementations that use + // the 15x15 kernel size, so we ignore any non-matching + // values that fall within the specified threshold. + if constexpr (KernelSize == 15) { + threshold = 2; + } - for (size_t y = size_min; y <= size_max; ++y) { - for (size_t x = size_min; x <= size_max; ++x) { - // Two extra lines allocated to be sure sigma can be placed next to the - // real input - cv::Mat input(y + 2, x, CV_8UC(Channels)); - rng.fill(input, cv::RNG::UNIFORM, 0, 255); - - double sigma = 0.0; - - if constexpr (!Binomial) { - // cv::rng returns [0,1) range in case of float or double, so it is - // multiplied by 10 - sigma = static_cast(rng) * 10; - } - - // sigma is embedded into the input matrix - *reinterpret_cast(&input.at(input.rows - 2, 0)) = sigma; - - cv::Mat actual = exec_gaussian_blur(input); - cv::Mat expected = get_expected_from_subordinate(index, request_queue, - reply_queue, input); - - uint8_t threshold = 0; - // There are currently rounding differences sometimes - // between the OpenCV and KleidiCV implementations that use - // the 15x15 kernel size, so we ignore any non-matching - // values that fall within the specified threshold. - if constexpr (KernelSize == 15) { - threshold = 2; - } - - if (are_matrices_different(threshold, actual, expected)) { - fail_print_matrices(y, x, input, actual, expected); - return true; - } + if (are_matrices_different(threshold, actual, expected)) { + fail_print_matrices(size.height, size.width, input, actual, expected); + return true; } } diff --git a/conformity/opencv/test_in_range.cpp b/conformity/opencv/test_in_range.cpp index 315474e050d907fc58f9b787180a0c7eae2fe928..e3b6aa94e9fb868fab327e4502fba161adb7ca2c 100644 --- a/conformity/opencv/test_in_range.cpp +++ b/conformity/opencv/test_in_range.cpp @@ -19,18 +19,16 @@ bool test_in_range(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, Format); - rng.fill(input, cv::RNG::UNIFORM, 0, 255); - cv::Mat actual = exec_in_range(input); - cv::Mat expected = get_expected_from_subordinate(index, request_queue, - reply_queue, input); + for (auto size : typical_test_sizes(1, 1)) { + cv::Mat input(size.height, size.width, Format); + rng.fill(input, cv::RNG::UNIFORM, 0, 255); + cv::Mat actual = exec_in_range(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; - } + if (are_matrices_different(0, actual, expected)) { + fail_print_matrices(size.height, size.width, input, actual, expected); + return true; } } diff --git a/conformity/opencv/test_rgb2yuv.cpp b/conformity/opencv/test_rgb2yuv.cpp index de27c48089aa1f32ae51ac4993a0f617c085cc07..9889f4b71f3e76fc7f89e5fd767555320dc64fe1 100644 --- a/conformity/opencv/test_rgb2yuv.cpp +++ b/conformity/opencv/test_rgb2yuv.cpp @@ -23,19 +23,17 @@ bool test_rgb2yuv(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_rgb2yuv(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; - } + for (auto size : typical_test_sizes(1, 1)) { + cv::Mat input(size.height, size.width, CV_8UC(Channels)); + rng.fill(input, cv::RNG::UNIFORM, 0, 255); + + cv::Mat actual = exec_rgb2yuv(input); + cv::Mat expected = + get_expected_from_subordinate(index, request_queue, reply_queue, input); + + if (are_matrices_different(0, actual, expected)) { + fail_print_matrices(size.height, size.width, input, actual, expected); + return true; } } diff --git a/conformity/opencv/test_scale.cpp b/conformity/opencv/test_scale.cpp index bc1614a1737cad6a1ff6f58b4ef01ae9c4ef4fe0..b82d882c444ee6c39a28a630e1d9a4fe38c7b672 100644 --- a/conformity/opencv/test_scale.cpp +++ b/conformity/opencv/test_scale.cpp @@ -22,24 +22,23 @@ bool test_scale(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_mat(x, y, Format); - rng.fill(input_mat, cv::RNG::NORMAL, 0.0, 1.0e10); - cv::Mat actual_mat = exec_scale(input_mat); - cv::Mat expected_mat = get_expected_from_subordinate( - index, request_queue, reply_queue, input_mat); + for (auto size : typical_test_sizes(1, 1)) { + cv::Mat input_mat(size.height, size.width, Format); + rng.fill(input_mat, cv::RNG::NORMAL, 0.0, 1.0e10); + cv::Mat actual_mat = exec_scale(input_mat); + cv::Mat expected_mat = get_expected_from_subordinate( + index, request_queue, reply_queue, input_mat); - bool success = - (CV_MAT_DEPTH(Format) == CV_32F && - !are_float_matrices_different(1e-5, actual_mat, - expected_mat)) || - (CV_MAT_DEPTH(Format) == CV_8U && - !are_matrices_different(0, actual_mat, expected_mat)); - if (!success) { - fail_print_matrices(x, y, input_mat, actual_mat, expected_mat); - return true; - } + bool success = + (CV_MAT_DEPTH(Format) == CV_32F && + !are_float_matrices_different(1e-5, actual_mat, + expected_mat)) || + (CV_MAT_DEPTH(Format) == CV_8U && + !are_matrices_different(0, actual_mat, expected_mat)); + if (!success) { + fail_print_matrices(size.height, size.width, input_mat, actual_mat, + expected_mat); + return true; } } diff --git a/conformity/opencv/test_separable_filter_2d.cpp b/conformity/opencv/test_separable_filter_2d.cpp index fda861ef8aac35c6a31ef434e109988a0bd99416..2979e94c7248f077af76aa2257f3b82f38a568b3 100644 --- a/conformity/opencv/test_separable_filter_2d.cpp +++ b/conformity/opencv/test_separable_filter_2d.cpp @@ -11,7 +11,7 @@ // specified InputType and KernelType. An exception should be thrown in case the // constraint in the HAL has not been met. // Returns a 1x1-sized boolean matrix. -template +template cv::Mat exec_separable_filter_2d_channel_check(cv::Mat& input) { cv::Mat kernel(KernelSize, 1, KernelType); cv::Mat result; @@ -24,7 +24,7 @@ cv::Mat exec_separable_filter_2d_channel_check(cv::Mat& input) { return cv::Mat(1, 1, CV_8UC1, cv::Scalar(0)); } -template +template cv::Mat exec_separable_filter_2d(cv::Mat& input) { uint32_t kernel_seed = *reinterpret_cast(&input.at(input.rows - 1, 0)); @@ -51,7 +51,7 @@ cv::Mat exec_separable_filter_2d(cv::Mat& input) { #if MANAGER // The purpose of this test is to check one of the initial constraints of the // Separable Filter 2D HAL, that the kernel can only have one channel. -template +template bool test_separable_filter_2d_channel_check( int index, RecreatedMessageQueue& request_queue, RecreatedMessageQueue& reply_queue) { @@ -81,53 +81,51 @@ bool test_separable_filter_2d_channel_check( return false; } -template bool test_separable_filter_2d(int index, RecreatedMessageQueue& request_queue, RecreatedMessageQueue& reply_queue) { cv::RNG rng(0); - for (size_t y = 5; y <= 16; ++y) { - for (size_t x = 5; x <= 16; ++x) { - // One extra line allocated to be sure the kernel seed can be placed next - // to the real input - cv::Mat input(y + 1, x, get_opencv_matrix_type()); - // use the minimum value 1 for the input in order to properly work around - // the potential OpenCV bug (mentioned lower) - rng.fill(input, cv::RNG::UNIFORM, 1, - std::numeric_limits::max()); - - uint32_t kernel_seed = rng.next(); - - // kernel seed is embedded into the input matrix - *reinterpret_cast(&input.at(input.rows - 1, 0)) = - kernel_seed; - - cv::Mat actual = - exec_separable_filter_2d(input); - cv::Mat expected = get_expected_from_subordinate(index, request_queue, - reply_queue, input); - - // bypass OpenCV bug/inconsistency where the output matrix contains 0's - // (or also the smallest negative value for signed types), whereas this - // should not be possible mathematically - for (size_t i = 0; i < (y * Channels); ++i) { - for (size_t j = 0; j < (x * Channels); ++j) { - if (expected.at(i, j) == - std::numeric_limits::lowest()) { - expected.at(i, j) = - std::numeric_limits::max(); - } else if (expected.at(i, j) == 0) { - expected.at(i, j) = - std::numeric_limits::max(); - } + // Minimal width is sizeof(uint32_t) and one more row is allcated to place + // the kernel seed next to the real input + for (auto size : typical_test_sizes( + std::max(KernelSize - 1, static_cast(sizeof(uint32_t))), + KernelSize - 1)) { + cv::Mat input(size.height + 1, size.width, + get_opencv_matrix_type()); + // use the minimum value 1 for the input in order to properly work around + // the potential OpenCV bug (mentioned lower) + rng.fill(input, cv::RNG::UNIFORM, 1, std::numeric_limits::max()); + + uint32_t kernel_seed = rng.next(); + + // kernel seed is embedded into the input matrix + *reinterpret_cast(&input.at(input.rows - 1, 0)) = + kernel_seed; + + cv::Mat actual = + exec_separable_filter_2d(input); + cv::Mat expected = + get_expected_from_subordinate(index, request_queue, reply_queue, input); + + // bypass OpenCV bug/inconsistency where the output matrix contains 0's + // (or also the smallest negative value for signed types), whereas this + // should not be possible mathematically + for (size_t i = 0; i < static_cast(size.height); ++i) { + for (size_t j = 0; j < (size.width * Channels); ++j) { + if (expected.at(i, j) == + std::numeric_limits::lowest()) { + expected.at(i, j) = std::numeric_limits::max(); + } else if (expected.at(i, j) == 0) { + expected.at(i, j) = std::numeric_limits::max(); } } + } - if (are_matrices_different(0, actual, expected)) { - fail_print_matrices(y, x, input, actual, expected); - return true; - } + if (are_matrices_different(0, actual, expected)) { + fail_print_matrices(size.height, size.width, input, actual, expected); + return true; } } diff --git a/conformity/opencv/test_sobel.cpp b/conformity/opencv/test_sobel.cpp index 807af0c8ac1be656abe55202019e31728b2be50d..99ad47aa66ae51a87ae0ea4924c5536f4c8a0314 100644 --- a/conformity/opencv/test_sobel.cpp +++ b/conformity/opencv/test_sobel.cpp @@ -23,19 +23,17 @@ bool test_sobel(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_sobel(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; - } + for (auto size : typical_test_sizes(2, 2)) { + cv::Mat input(size.height, size.width, CV_8UC(Channels)); + rng.fill(input, cv::RNG::UNIFORM, 0, 255); + + cv::Mat actual = exec_sobel(input); + cv::Mat expected = + get_expected_from_subordinate(index, request_queue, reply_queue, input); + + if (are_matrices_different(0, actual, expected)) { + fail_print_matrices(size.height, size.width, input, actual, expected); + return true; } } diff --git a/conformity/opencv/test_yuv2rgb.cpp b/conformity/opencv/test_yuv2rgb.cpp index 4a0573efc9a5a42f4cc76d0a6278ff88a13fe366..f0414c66d7fea98048c6d3966380b0e2f151d41a 100644 --- a/conformity/opencv/test_yuv2rgb.cpp +++ b/conformity/opencv/test_yuv2rgb.cpp @@ -23,19 +23,17 @@ bool test_yuv2rgb(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_8UC3); - rng.fill(input, cv::RNG::UNIFORM, 0, 255); - - cv::Mat actual = exec_yuv2rgb(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; - } + for (auto size : typical_test_sizes(1, 1)) { + cv::Mat input(size.height, size.width, CV_8UC3); + rng.fill(input, cv::RNG::UNIFORM, 0, 255); + + cv::Mat actual = exec_yuv2rgb(input); + cv::Mat expected = + get_expected_from_subordinate(index, request_queue, reply_queue, input); + + if (are_matrices_different(0, actual, expected)) { + fail_print_matrices(size.height, size.width, input, actual, expected); + return true; } } diff --git a/conformity/opencv/utils.cpp b/conformity/opencv/utils.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d7c25e69449710b9040935b31b1465db519ad4ca --- /dev/null +++ b/conformity/opencv/utils.cpp @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: 2024 Arm Limited and/or its affiliates +// +// SPDX-License-Identifier: Apache-2.0 + +#include "utils.h" + +#if MANAGER + +std::array typical_test_sizes(int min_width, int min_height) { + // Tests around the minimal width and height and some rudimentary sizes + return std::array{{{min_width, min_height}, + {min_width + 1, min_height + 1}, + {64 * 4, min_height * 2}, + {(64 * 4) + 1, min_height * 2}}}; +} + +#endif diff --git a/conformity/opencv/utils.h b/conformity/opencv/utils.h index c6778d4f04803067bed74e5d4809a24092a83e50..12f0af353a2678fb3dd5a27b3c2b4c426f8aad99 100644 --- a/conformity/opencv/utils.h +++ b/conformity/opencv/utils.h @@ -23,6 +23,8 @@ constexpr int get_opencv_matrix_type() { } #if MANAGER +std::array typical_test_sizes(int min_width, int min_height); + template static auto abs_diff(T a, T b) { return a > b ? a - b : b - a;