From 4c7a70cb5f39d260d7f69b9b95120856c0ea3573 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Thu, 15 Feb 2024 07:18:52 +0000 Subject: [PATCH 01/94] video(test): filter very long debug tests --- modules/video/test/test_trackers.cpp | 27 ++++++++++++++++++----- modules/video/test/test_trackers.impl.hpp | 19 ++++++++++++++-- 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/modules/video/test/test_trackers.cpp b/modules/video/test/test_trackers.cpp index 7186d0fe6b..1628cecf92 100644 --- a/modules/video/test/test_trackers.cpp +++ b/modules/video/test/test_trackers.cpp @@ -21,25 +21,27 @@ const string FOLDER_OMIT_INIT = "initOmit"; #include "test_trackers.impl.hpp" //[TESTDATA] -PARAM_TEST_CASE(DistanceAndOverlap, string) +PARAM_TEST_CASE(DistanceAndOverlap, string, int) { string dataset; + int numFramesLimit; virtual void SetUp() { dataset = GET_PARAM(0); + numFramesLimit = GET_PARAM(1); } }; TEST_P(DistanceAndOverlap, MIL) { TrackerTest test(TrackerMIL::create(), dataset, 30, .65f, NoTransform); - test.run(); + test.run(numFramesLimit); } TEST_P(DistanceAndOverlap, Shifted_Data_MIL) { TrackerTest test(TrackerMIL::create(), dataset, 30, .6f, CenterShiftLeft); - test.run(); + test.run(numFramesLimit); } /***************************************************************************************/ @@ -48,7 +50,7 @@ TEST_P(DistanceAndOverlap, Shifted_Data_MIL) TEST_P(DistanceAndOverlap, Scaled_Data_MIL) { TrackerTest test(TrackerMIL::create(), dataset, 30, .7f, Scale_1_1); - test.run(); + test.run(numFramesLimit); } TEST_P(DistanceAndOverlap, GOTURN) @@ -59,10 +61,23 @@ TEST_P(DistanceAndOverlap, GOTURN) params.modelTxt = model; params.modelBin = weights; TrackerTest test(TrackerGOTURN::create(params), dataset, 35, .35f, NoTransform); - test.run(); + test.run(numFramesLimit); } -INSTANTIATE_TEST_CASE_P(Tracking, DistanceAndOverlap, TESTSET_NAMES); +INSTANTIATE_TEST_CASE_P(Tracking, DistanceAndOverlap, + testing::Combine( + TESTSET_NAMES, + testing::Values(0) + ) +); + +INSTANTIATE_TEST_CASE_P(Tracking5Frames, DistanceAndOverlap, + testing::Combine( + TESTSET_NAMES, + testing::Values(5) + ) +); + static bool checkIOU(const Rect& r0, const Rect& r1, double threshold) { diff --git a/modules/video/test/test_trackers.impl.hpp b/modules/video/test/test_trackers.impl.hpp index fc2315ced0..0d98772e3f 100644 --- a/modules/video/test/test_trackers.impl.hpp +++ b/modules/video/test/test_trackers.impl.hpp @@ -65,7 +65,7 @@ public: TrackerTest(const Ptr& tracker, const string& video, float distanceThreshold, float overlapThreshold, int shift = NoTransform, int segmentIdx = 1, int numSegments = 10); ~TrackerTest() {} - void run(); + void run(int numFramesLimit = 0); protected: void checkDataTest(); @@ -351,7 +351,7 @@ void TrackerTest::checkDataTest() } template -void TrackerTest::run() +void TrackerTest::run(int numFramesLimit) { srand(1); // FIXIT remove that, ensure that there is no "rand()" in implementation @@ -363,5 +363,20 @@ void TrackerTest::run() if (::testing::Test::HasFatalFailure()) return; + int numFrames = endFrame - startFrame; + std::cout << "Number of frames in test data: " << numFrames << std::endl; + + if (numFramesLimit > 0) + { + numFrames = std::min(numFrames, numFramesLimit); + endFrame = startFrame + numFramesLimit; + } + else + { + applyTestTag(CV_TEST_TAG_DEBUG_VERYLONG); + } + + std::cout << "Number of frames to test: " << numFrames << std::endl; + distanceAndOverlapTest(); } From 603344fa54253da19ddc09fa15ae2b64678ce7be Mon Sep 17 00:00:00 2001 From: gaohaoyuan Date: Thu, 11 Jul 2024 17:55:08 +0800 Subject: [PATCH 02/94] add API to reinterpret Mat type --- modules/core/include/opencv2/core/mat.hpp | 10 +++++++ modules/core/src/matrix.cpp | 10 +++++++ modules/core/src/matrix_wrap.cpp | 6 ++++ modules/core/test/test_mat.cpp | 36 +++++++++++++++++++++++ 4 files changed, 62 insertions(+) diff --git a/modules/core/include/opencv2/core/mat.hpp b/modules/core/include/opencv2/core/mat.hpp index 7af3c9cfc4..15b3177514 100644 --- a/modules/core/include/opencv2/core/mat.hpp +++ b/modules/core/include/opencv2/core/mat.hpp @@ -371,6 +371,7 @@ public: void release() const; void clear() const; void setTo(const _InputArray& value, const _InputArray & mask = _InputArray()) const; + Mat reinterpret( int type ) const; void assign(const UMat& u) const; void assign(const Mat& m) const; @@ -1322,6 +1323,15 @@ public: */ Mat reshape(int cn, const std::vector& newshape) const; + /** @brief Reset the type of matrix. + + The methods reset the data type of matrix. If the new type and the old type of the matrix + have the same element size, the current buffer can be reused. The method needs to consider whether the + current mat is a submatrix or has any references. + @param type New data type. + */ + Mat reinterpret( int type ) const; + /** @brief Transposes a matrix. The method performs matrix transposition by means of matrix expressions. It does not perform the diff --git a/modules/core/src/matrix.cpp b/modules/core/src/matrix.cpp index 1b11e12145..0453546c75 100644 --- a/modules/core/src/matrix.cpp +++ b/modules/core/src/matrix.cpp @@ -1261,6 +1261,16 @@ Mat Mat::reshape(int _cn, const std::vector& _newshape) const return reshape(_cn, (int)_newshape.size(), &_newshape[0]); } +Mat Mat::reinterpret(int type) const +{ + type = CV_MAT_TYPE(type); + CV_Assert(CV_ELEM_SIZE(this->type()) == CV_ELEM_SIZE(type)); + Mat m = *this; + m.flags = (m.flags & ~CV_MAT_TYPE_MASK) | type; + m.updateContinuityFlag(); + return m; +} + Mat Mat::diag(const Mat& d) { CV_Assert( d.cols == 1 || d.rows == 1 ); diff --git a/modules/core/src/matrix_wrap.cpp b/modules/core/src/matrix_wrap.cpp index b72fdbe784..4ddb89f638 100644 --- a/modules/core/src/matrix_wrap.cpp +++ b/modules/core/src/matrix_wrap.cpp @@ -1656,6 +1656,12 @@ void _OutputArray::create(int d, const int* sizes, int mtype, int i, CV_Error(Error::StsNotImplemented, "Unknown/unsupported array type"); } +Mat _OutputArray::reinterpret(int mtype) const +{ + mtype = CV_MAT_TYPE(mtype); + return getMat().reinterpret(mtype); +} + void _OutputArray::createSameSize(const _InputArray& arr, int mtype) const { int arrsz[CV_MAX_DIM], d = arr.sizend(arrsz); diff --git a/modules/core/test/test_mat.cpp b/modules/core/test/test_mat.cpp index e0ec60955c..f93505ffdc 100644 --- a/modules/core/test/test_mat.cpp +++ b/modules/core/test/test_mat.cpp @@ -1303,6 +1303,42 @@ TEST(Core_Mat, reshape_ndims_4) } } +TEST(Core_Mat, reinterpret_Mat_8UC3_8SC3) +{ + cv::Mat A(8, 16, CV_8UC3, cv::Scalar(1, 2, 3)); + cv::Mat B = A.reinterpret(CV_8SC3); + + EXPECT_EQ(A.data, B.data); + EXPECT_EQ(B.type(), CV_8SC3); +} + +TEST(Core_Mat, reinterpret_Mat_8UC4_32FC1) +{ + cv::Mat A(8, 16, CV_8UC4, cv::Scalar(1, 2, 3, 4)); + cv::Mat B = A.reinterpret(CV_32FC1); + + EXPECT_EQ(A.data, B.data); + EXPECT_EQ(B.type(), CV_32FC1); +} + +TEST(Core_Mat, reinterpret_OutputArray_8UC3_8SC3) { + cv::Mat A(8, 16, CV_8UC3, cv::Scalar(1, 2, 3)); + cv::OutputArray C(A); + cv::Mat B = C.reinterpret(CV_8SC3); + + EXPECT_EQ(A.data, B.data); + EXPECT_EQ(B.type(), CV_8SC3); +} + +TEST(Core_Mat, reinterpret_OutputArray_8UC4_32FC1) { + cv::Mat A(8, 16, CV_8UC4, cv::Scalar(1, 2, 3, 4)); + cv::OutputArray C(A); + cv::Mat B = C.reinterpret(CV_32FC1); + + EXPECT_EQ(A.data, B.data); + EXPECT_EQ(B.type(), CV_32FC1); +} + TEST(Core_Mat, push_back) { Mat a = (Mat_(1,2) << 3.4884074f, 1.4159607f); From e342d2f339a85f03fcf755b809684ae0d0b27cdb Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Tue, 11 Mar 2025 10:16:01 +0300 Subject: [PATCH 03/94] Local decolor pipeline optimization. --- modules/photo/src/contrast_preserve.hpp | 30 ++++++++++++++++--------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/modules/photo/src/contrast_preserve.hpp b/modules/photo/src/contrast_preserve.hpp index 4079272e99..3fb98e2093 100644 --- a/modules/photo/src/contrast_preserve.hpp +++ b/modules/photo/src/contrast_preserve.hpp @@ -113,14 +113,16 @@ Decolor::Decolor() sigma = 0.02f; } -vector Decolor::product(const vector &comb, const double initRGB[3]) +vector Decolor::product(const vector &comb, const double initRGB[3]) { vector res(comb.size()); for (size_t i=0;i &grad) const singleChannelGradx(img,dest); singleChannelGrady(img,dest1); - Mat d_trans=dest.t(); - Mat d1_trans=dest1.t(); - - const int height = d_trans.size().height; - const int width = d_trans.size().width; + // the function uses transposed dest and dest1 here and bellow + const int height = dest.size().width; + const int width = dest.size().height; grad.resize(width * height * 2); for(int i=0;i(i, j); + grad[i*width + j] = dest.at(j, i); const int offset = width * height; for(int i=0;i(i, j); + grad[offset + i * width + j] = dest1.at(j, i); } void Decolor::colorGrad(const Mat &img, vector &Cg) const @@ -277,7 +277,9 @@ void Decolor::grad_system(const Mat &im, vector < vector < double > > &polyGrad, int idx = 0, idx1 = 0; for(int r=0 ;r <=order; r++) + { for(int g=0; g<=order;g++) + { for(int b =0; b <=order;b++) { if((r+g+b)<=order && (r+g+b) > 0) @@ -293,6 +295,8 @@ void Decolor::grad_system(const Mat &im, vector < vector < double > > &polyGrad, add_to_vector_poly(polyGrad,curGrad,idx1); } } + } + } } void Decolor::wei_update_matrix(const vector < vector > &poly, const vector &Cg, Mat &X) @@ -305,7 +309,6 @@ void Decolor::wei_update_matrix(const vector < vector > &poly, const ve for (int j = 0; j < size0;j++) P.at(i,j) = static_cast(poly[i][j]); - const Mat P_trans = P.t(); Mat B = Mat(size, size0, CV_32FC1); for(int i =0;i < size;i++) { @@ -313,7 +316,8 @@ void Decolor::wei_update_matrix(const vector < vector > &poly, const ve B.at(i,j) = static_cast(poly[i][j] * Cg[j]); } - Mat A = P*P_trans; + Mat A; + mulTransposed(P, A, false); solve(A, B, X, DECOMP_NORMAL); } @@ -352,8 +356,11 @@ void Decolor::grayImContruct(vector &wei, const Mat &img, Mat &Gray) co int kk =0; for(int r =0;r<=order;r++) + { for(int g=0;g<=order;g++) + { for(int b=0;b<=order;b++) + { if((r + g + b) <=order && (r+g+b) > 0) { for(int i = 0;i &wei, const Mat &img, Mat &Gray) co kk=kk+1; } + } + } + } double minval, maxval; minMaxLoc(Gray, &minval, &maxval); From 60de3ff24f88b840fe95eab24e374d6e97ee76ee Mon Sep 17 00:00:00 2001 From: GenshinImpactStarts <147074368+GenshinImpactStarts@users.noreply.github.com> Date: Wed, 12 Mar 2025 13:34:27 +0800 Subject: [PATCH 04/94] Merge pull request #27015 from GenshinImpactStarts:sqrt [HAL RVV] impl sqrt and invSqrt #27015 Implement through the existing interfaces `cv_hal_sqrt32f`, `cv_hal_sqrt64f`, `cv_hal_invSqrt32f`, `cv_hal_invSqrt64f`. Perf test done on MUSE-PI and CanMV K230. Because the performance of scalar is much worse than universal intrinsic, only ui and hal rvv is compared. In RVV's UI, `invSqrt` is computed using `1 / sqrt()`. This patch first uses `frsqrt` and then applies the Newton-Raphson method to achieve higher precision. For the initial value, I tried using the famous [fast inverse square root algorithm](https://en.wikipedia.org/wiki/Fast_inverse_square_root), which involves one bit shift and one subtraction. However, on both MUSE-PI and CanMV K230, the performance was slightly lower (about 3%), so I chose to use `frsqrt` for the initial value instead. BTW, I think this patch can directly replace RVV's UI. **UPDATE**: Due to strange vector registers allocation strategy in clang, for `invSqrt`, clang use LMUL m4 while gcc use LMUL m8, which leads to some performance loss in clang. So the test for clang is appended. ```sh $ opencv_test_core --gtest_filter="Core_HAL/mathfuncs.*" $ opencv_perf_core --gtest_filter="SqrtFixture.*" --perf_min_samples=300 --perf_force_samples=300 ``` CanMV K230: ``` Name of Test ui rvv rvv vs ui (x-factor) Sqrt::SqrtFixture::(127x61, 5, false) 0.052 0.027 1.96 Sqrt::SqrtFixture::(127x61, 5, true) 0.101 0.026 3.80 Sqrt::SqrtFixture::(127x61, 6, false) 0.106 0.059 1.79 Sqrt::SqrtFixture::(127x61, 6, true) 0.207 0.058 3.55 Sqrt::SqrtFixture::(640x480, 5, false) 1.988 0.956 2.08 Sqrt::SqrtFixture::(640x480, 5, true) 3.920 0.948 4.13 Sqrt::SqrtFixture::(640x480, 6, false) 4.179 2.342 1.78 Sqrt::SqrtFixture::(640x480, 6, true) 8.220 2.290 3.59 Sqrt::SqrtFixture::(1280x720, 5, false) 5.969 2.881 2.07 Sqrt::SqrtFixture::(1280x720, 5, true) 11.731 2.857 4.11 Sqrt::SqrtFixture::(1280x720, 6, false) 12.533 7.031 1.78 Sqrt::SqrtFixture::(1280x720, 6, true) 24.643 6.917 3.56 Sqrt::SqrtFixture::(1920x1080, 5, false) 13.423 6.483 2.07 Sqrt::SqrtFixture::(1920x1080, 5, true) 26.379 6.436 4.10 Sqrt::SqrtFixture::(1920x1080, 6, false) 28.200 15.833 1.78 Sqrt::SqrtFixture::(1920x1080, 6, true) 55.434 15.565 3.56 ``` MUSE-PI: ``` GCC | clang Name of Test ui rvv rvv | ui rvv rvv vs | vs ui | ui (x-factor) | (x-factor) Sqrt::SqrtFixture::(127x61, 5, false) 0.027 0.018 1.46 | 0.027 0.016 1.65 Sqrt::SqrtFixture::(127x61, 5, true) 0.050 0.017 2.98 | 0.050 0.017 2.99 Sqrt::SqrtFixture::(127x61, 6, false) 0.053 0.031 1.72 | 0.052 0.032 1.64 Sqrt::SqrtFixture::(127x61, 6, true) 0.100 0.030 3.31 | 0.101 0.035 2.86 Sqrt::SqrtFixture::(640x480, 5, false) 0.955 0.483 1.98 | 0.959 0.499 1.92 Sqrt::SqrtFixture::(640x480, 5, true) 1.873 0.489 3.83 | 1.873 0.520 3.60 Sqrt::SqrtFixture::(640x480, 6, false) 2.027 1.163 1.74 | 2.037 1.218 1.67 Sqrt::SqrtFixture::(640x480, 6, true) 3.961 1.153 3.44 | 3.961 1.341 2.95 Sqrt::SqrtFixture::(1280x720, 5, false) 2.916 1.538 1.90 | 2.912 1.598 1.82 Sqrt::SqrtFixture::(1280x720, 5, true) 5.735 1.534 3.74 | 5.726 1.661 3.45 Sqrt::SqrtFixture::(1280x720, 6, false) 6.121 3.585 1.71 | 6.109 3.725 1.64 Sqrt::SqrtFixture::(1280x720, 6, true) 12.059 3.501 3.44 | 12.053 4.080 2.95 Sqrt::SqrtFixture::(1920x1080, 5, false) 6.540 3.535 1.85 | 6.540 3.643 1.80 Sqrt::SqrtFixture::(1920x1080, 5, true) 12.943 3.445 3.76 | 12.908 3.706 3.48 Sqrt::SqrtFixture::(1920x1080, 6, false) 13.714 8.062 1.70 | 13.711 8.376 1.64 Sqrt::SqrtFixture::(1920x1080, 6, true) 27.011 7.989 3.38 | 27.115 9.245 2.93 ``` ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [ ] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- 3rdparty/hal_rvv/hal_rvv.hpp | 1 + 3rdparty/hal_rvv/hal_rvv_1p0/sqrt.hpp | 122 ++++++++++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 3rdparty/hal_rvv/hal_rvv_1p0/sqrt.hpp diff --git a/3rdparty/hal_rvv/hal_rvv.hpp b/3rdparty/hal_rvv/hal_rvv.hpp index a4dd6c2326..57d2ccfee5 100644 --- a/3rdparty/hal_rvv/hal_rvv.hpp +++ b/3rdparty/hal_rvv/hal_rvv.hpp @@ -38,6 +38,7 @@ #include "hal_rvv_1p0/cholesky.hpp" // core #include "hal_rvv_1p0/qr.hpp" // core #include "hal_rvv_1p0/svd.hpp" // core +#include "hal_rvv_1p0/sqrt.hpp" // core #include "hal_rvv_1p0/filter.hpp" // imgproc #include "hal_rvv_1p0/pyramids.hpp" // imgproc diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/sqrt.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/sqrt.hpp new file mode 100644 index 0000000000..9a2e5d6bfe --- /dev/null +++ b/3rdparty/hal_rvv/hal_rvv_1p0/sqrt.hpp @@ -0,0 +1,122 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level +// directory of this distribution and at http://opencv.org/license.html. +#ifndef OPENCV_HAL_RVV_SQRT_HPP_INCLUDED +#define OPENCV_HAL_RVV_SQRT_HPP_INCLUDED + +#include +#include +#include "hal_rvv_1p0/types.hpp" + +namespace cv { namespace cv_hal_rvv { + +#undef cv_hal_sqrt32f +#undef cv_hal_sqrt64f +#undef cv_hal_invSqrt32f +#undef cv_hal_invSqrt64f + +#define cv_hal_sqrt32f cv::cv_hal_rvv::sqrt> +#define cv_hal_sqrt64f cv::cv_hal_rvv::sqrt> + +#ifdef __clang__ +// Strange bug in clang: invSqrt use 2 LMUL registers to store mask, which will cause memory access. +// So a smaller LMUL is used here. +# define cv_hal_invSqrt32f cv::cv_hal_rvv::invSqrt> +# define cv_hal_invSqrt64f cv::cv_hal_rvv::invSqrt> +#else +# define cv_hal_invSqrt32f cv::cv_hal_rvv::invSqrt> +# define cv_hal_invSqrt64f cv::cv_hal_rvv::invSqrt> +#endif + +namespace detail { + +// Newton-Raphson method +// Use 4 LMUL registers +template +inline VEC_T sqrt(VEC_T x, size_t vl) +{ + auto x2 = __riscv_vfmul(x, 0.5, vl); + auto y = __riscv_vfrsqrt7(x, vl); +#pragma unroll + for (size_t i = 0; i < iter_times; i++) + { + auto t = __riscv_vfmul(y, y, vl); + t = __riscv_vfmul(t, x2, vl); + t = __riscv_vfrsub(t, 1.5, vl); + y = __riscv_vfmul(t, y, vl); + } + // just to prevent the compiler from calculating mask before the invSqrt, which will run out + // of registers and cause memory access. + asm volatile("" ::: "memory"); + auto mask = __riscv_vmfne(x, 0.0, vl); + mask = __riscv_vmfne_mu(mask, mask, x, INFINITY, vl); + return __riscv_vfmul_mu(mask, x, x, y, vl); +} + +// Newton-Raphson method +// Use 3 LMUL registers and 1 mask register +template +inline VEC_T invSqrt(VEC_T x, size_t vl) +{ + auto mask = __riscv_vmfne(x, 0.0, vl); + mask = __riscv_vmfne_mu(mask, mask, x, INFINITY, vl); + auto x2 = __riscv_vfmul(x, 0.5, vl); + auto y = __riscv_vfrsqrt7(x, vl); +#pragma unroll + for (size_t i = 0; i < iter_times; i++) + { + auto t = __riscv_vfmul(y, y, vl); + t = __riscv_vfmul(t, x2, vl); + t = __riscv_vfrsub(t, 1.5, vl); + y = __riscv_vfmul_mu(mask, y, t, y, vl); + } + return y; +} + +} // namespace detail + +template +struct Sqrt32f +{ + using T = RVV_T; + static constexpr size_t iter_times = 2; +}; + +template +struct Sqrt64f +{ + using T = RVV_T; + static constexpr size_t iter_times = 3; +}; + +template +inline int sqrt(const Elem* src, Elem* dst, int _len) +{ + size_t vl; + for (size_t len = _len; len > 0; len -= vl, src += vl, dst += vl) + { + vl = SQRT_T::T::setvl(len); + auto x = SQRT_T::T::vload(src, vl); + SQRT_T::T::vstore(dst, detail::sqrt(x, vl), vl); + } + + return CV_HAL_ERROR_OK; +} + +template +inline int invSqrt(const Elem* src, Elem* dst, int _len) +{ + size_t vl; + for (size_t len = _len; len > 0; len -= vl, src += vl, dst += vl) + { + vl = SQRT_T::T::setvl(len); + auto x = SQRT_T::T::vload(src, vl); + SQRT_T::T::vstore(dst, detail::invSqrt(x, vl), vl); + } + + return CV_HAL_ERROR_OK; +} + +}} // namespace cv::cv_hal_rvv + +#endif // OPENCV_HAL_RVV_SQRT_HPP_INCLUDED From 46dbc57a865594a5f9141a3a3b6878764df6cc0d Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Wed, 12 Mar 2025 09:47:49 +0300 Subject: [PATCH 05/94] Merge pull request #26968 from MaximSmolskiy:fix-Aruco-marker-incorrect-detection-near-image-edge Fix Aruco marker incorrect detection near image edge #26968 ### Pull Request Readiness Checklist Fix #26922 As I understood the algorithm, at the first stage we search for the contours of the marker several times (adaptive threshold with different windows sizes). Therefore, for the same marker, we get several contours (inner and outer with different sizes due to the different windows sizes). In the second stage, we group the contours for the same marker into one group, from which we take the largest contour as the best candidate (which should best match the border of the marker). The problem is that using the `minDistanceToBorder` parameter, we discard contours at the first stage. Thus, we discard the best candidates most appropriate to the marker border, and inner contours may remain, representing a significantly smaller marker border (which we observe in the issue). But if we use the `minDistanceToBorder` parameter to discard the best candidate of the group at the second stage, then there will be no such problems and we will completely discard markers located too close to the border of the image. See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [x] The feature is well documented and sample code can be built with the project CMake --- .../objdetect/src/aruco/aruco_detector.cpp | 49 ++++++++++--------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/modules/objdetect/src/aruco/aruco_detector.cpp b/modules/objdetect/src/aruco/aruco_detector.cpp index 368625aa89..a0f5c64390 100644 --- a/modules/objdetect/src/aruco/aruco_detector.cpp +++ b/modules/objdetect/src/aruco/aruco_detector.cpp @@ -130,10 +130,10 @@ static void _threshold(InputArray _in, OutputArray _out, int winSize, double con static void _findMarkerContours(const Mat &in, vector > &candidates, vector > &contoursOut, double minPerimeterRate, double maxPerimeterRate, double accuracyRate, - double minCornerDistanceRate, int minDistanceToBorder, int minSize) { + double minCornerDistanceRate, int minSize) { CV_Assert(minPerimeterRate > 0 && maxPerimeterRate > 0 && accuracyRate > 0 && - minCornerDistanceRate >= 0 && minDistanceToBorder >= 0); + minCornerDistanceRate >= 0); // calculate maximum and minimum sizes in pixels unsigned int minPerimeterPixels = @@ -171,16 +171,6 @@ static void _findMarkerContours(const Mat &in, vector > &candida double minCornerDistancePixels = double(contours[i].size()) * minCornerDistanceRate; if(minDistSq < minCornerDistancePixels * minCornerDistancePixels) continue; - // check if it is too near to the image border - bool tooNearBorder = false; - for(int j = 0; j < 4; j++) { - if(approxCurve[j].x < minDistanceToBorder || approxCurve[j].y < minDistanceToBorder || - approxCurve[j].x > in.cols - 1 - minDistanceToBorder || - approxCurve[j].y > in.rows - 1 - minDistanceToBorder) - tooNearBorder = true; - } - if(tooNearBorder) continue; - // if it passes all the test, add to candidates vector vector currentCandidate; currentCandidate.resize(4); @@ -305,7 +295,7 @@ static void _detectInitialCandidates(const Mat &grey, vector > & _findMarkerContours(thresh, candidatesArrays[i], contoursArrays[i], params.minMarkerPerimeterRate, params.maxMarkerPerimeterRate, params.polygonalApproxAccuracyRate, params.minCornerDistanceRate, - params.minDistanceToBorder, params.minSideLengthCanonicalImg); + params.minSideLengthCanonicalImg); } }); // join candidates @@ -742,7 +732,7 @@ struct ArucoDetector::ArucoDetectorImpl { vector> rejectedImgPoints; if (DictionaryMode::Single == dictMode) { Dictionary& dictionary = dictionaries.at(0); - auto selectedCandidates = filterTooCloseCandidates(candidates, contours, dictionary.markerSize); + auto selectedCandidates = filterTooCloseCandidates(grey.size(), candidates, contours, dictionary.markerSize); candidates.clear(); contours.clear(); @@ -765,7 +755,7 @@ struct ArucoDetector::ArucoDetectorImpl { // copy candidates vector> candidatesCopy = candidates; vector > contoursCopy = contours; - candidatesTreeEntry.second = filterTooCloseCandidates(candidatesCopy, contoursCopy, candidatesTreeEntry.first); + candidatesTreeEntry.second = filterTooCloseCandidates(grey.size(), candidatesCopy, contoursCopy, candidatesTreeEntry.first); } candidates.clear(); contours.clear(); @@ -872,14 +862,14 @@ struct ArucoDetector::ArucoDetectorImpl { } /** - * @brief FILTER OUT NEAR CANDIDATE PAIRS + * @brief FILTER OUT NEAR CANDIDATES PAIRS AND TOO NEAR CANDIDATES TO IMAGE BORDER * - * save the outter/inner border (i.e. potential candidates) to vector, + * save the outer/inner border (i.e. potential candidates) to vector, * clear candidates and contours */ vector - filterTooCloseCandidates(vector > &candidates, vector > &contours, int markerSize) { - CV_Assert(detectorParams.minMarkerDistanceRate >= 0.); + filterTooCloseCandidates(const Size &imageSize, vector > &candidates, vector > &contours, int markerSize) { + CV_Assert(detectorParams.minMarkerDistanceRate >= 0. && detectorParams.minDistanceToBorder >= 0); vector candidateTree(candidates.size()); for(size_t i = 0ull; i < candidates.size(); i++) { candidateTree[i] = MarkerCandidateTree(std::move(candidates[i]), std::move(contours[i])); @@ -940,6 +930,20 @@ struct ArucoDetector::ArucoDetectorImpl { else // if detectInvertedMarker==false choose largest contours std::stable_sort(grouped.begin(), grouped.end()); size_t currId = grouped[0]; + // check if it is too near to the image border + bool tooNearBorder = false; + for (const auto& corner : candidateTree[currId].corners) { + if (corner.x < detectorParams.minDistanceToBorder || + corner.y < detectorParams.minDistanceToBorder || + corner.x > imageSize.width - 1 - detectorParams.minDistanceToBorder || + corner.y > imageSize.height - 1 - detectorParams.minDistanceToBorder) { + tooNearBorder = true; + break; + } + } + if (tooNearBorder) { + continue; + } isSelectedContours[currId] = true; for (size_t i = 1ull; i < grouped.size(); i++) { size_t id = grouped[i]; @@ -952,12 +956,11 @@ struct ArucoDetector::ArucoDetectorImpl { } } - vector selectedCandidates(groupedCandidates.size()); - size_t countSelectedContours = 0ull; + vector selectedCandidates; + selectedCandidates.reserve(groupedCandidates.size()); for (size_t i = 0ull; i < candidateTree.size(); i++) { if (isSelectedContours[i]) { - selectedCandidates[countSelectedContours] = std::move(candidateTree[i]); - countSelectedContours++; + selectedCandidates.push_back(std::move(candidateTree[i])); } } From 2969b67bd7ecd384bb58a96fad19f89d998627ef Mon Sep 17 00:00:00 2001 From: Liutong HAN Date: Wed, 12 Mar 2025 12:15:05 +0000 Subject: [PATCH 06/94] Fix 27003. --- .../include/opencv2/core/hal/intrin_rvv_scalable.hpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/modules/core/include/opencv2/core/hal/intrin_rvv_scalable.hpp b/modules/core/include/opencv2/core/hal/intrin_rvv_scalable.hpp index 85acd09d4b..67c9d12741 100644 --- a/modules/core/include/opencv2/core/hal/intrin_rvv_scalable.hpp +++ b/modules/core/include/opencv2/core/hal/intrin_rvv_scalable.hpp @@ -2130,7 +2130,17 @@ inline v_int64 v_dotprod_expand_fast(const v_int16& a, const v_int16& b, const v // 32 >> 64f #if CV_SIMD_SCALABLE_64F inline v_float64 v_dotprod_expand_fast(const v_int32& a, const v_int32& b) -{ return v_cvt_f64(v_dotprod_fast(a, b)); } +{ + vfloat64m1_t zero = __riscv_vfmv_v_f_f64m1(0, VTraits::vlanes()); + auto prod_i64 = __riscv_vwmul(a, b, VTraits::vlanes()); + // Convert to f64 before reduction to avoid overflow: #27003 + auto prod_f64 = __riscv_vfcvt_f(prod_i64, VTraits::vlanes()); + return __riscv_vset( // Needs v_float64 (vfloat64m2_t) here. + v_setall_f64(0.0f), // zero_f64m2 + 0, + __riscv_vfredusum_tu(zero, prod_f64, zero, VTraits::vlanes()) + ); +} inline v_float64 v_dotprod_expand_fast(const v_int32& a, const v_int32& b, const v_float64& c) { return v_add(v_dotprod_expand_fast(a, b) , c); } #endif From eefa327f30cc51372599d7afad4224d44cede71a Mon Sep 17 00:00:00 2001 From: Yuantao Feng Date: Wed, 12 Mar 2025 21:43:10 +0800 Subject: [PATCH 07/94] Merge pull request #27042 from fengyuentau:4x/core/normDiff_simd core: vectorize normDiff with universal intrinsics #27042 Merge with https://github.com/opencv/opencv_extra/pull/1242. Performance results on Desktop Intel i7-12700K, Apple M2, Jetson Orin and SpaceMIT K1: [perf-normDiff.zip](https://github.com/user-attachments/files/19178689/perf-normDiff.zip) ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- modules/core/perf/perf_norm.cpp | 4 +- modules/core/src/norm.dispatch.cpp | 145 +----- modules/core/src/norm.simd.hpp | 739 +++++++++++++++++++++++++++++ 3 files changed, 754 insertions(+), 134 deletions(-) diff --git a/modules/core/perf/perf_norm.cpp b/modules/core/perf/perf_norm.cpp index 07f989f21c..8bcf9ea224 100644 --- a/modules/core/perf/perf_norm.cpp +++ b/modules/core/perf/perf_norm.cpp @@ -59,7 +59,7 @@ PERF_TEST_P(Size_MatType_NormType, norm_mask, PERF_TEST_P(Size_MatType_NormType, norm2, testing::Combine( testing::Values(TYPICAL_MAT_SIZES), - testing::Values(TYPICAL_MAT_TYPES), + testing::Values(CV_8UC1, CV_8UC4, CV_8SC1, CV_16UC1, CV_16SC1, CV_32SC1, CV_32FC1, CV_64FC1), testing::Values((int)NORM_INF, (int)NORM_L1, (int)NORM_L2, (int)(NORM_RELATIVE+NORM_INF), (int)(NORM_RELATIVE+NORM_L1), (int)(NORM_RELATIVE+NORM_L2)) ) ) @@ -82,7 +82,7 @@ PERF_TEST_P(Size_MatType_NormType, norm2, PERF_TEST_P(Size_MatType_NormType, norm2_mask, testing::Combine( testing::Values(TYPICAL_MAT_SIZES), - testing::Values(TYPICAL_MAT_TYPES), + testing::Values(CV_8UC1, CV_8UC4, CV_8SC1, CV_16UC1, CV_16SC1, CV_32SC1, CV_32FC1, CV_64FC1), testing::Values((int)NORM_INF, (int)NORM_L1, (int)NORM_L2, (int)(NORM_RELATIVE|NORM_INF), (int)(NORM_RELATIVE|NORM_L1), (int)(NORM_RELATIVE|NORM_L2)) ) ) diff --git a/modules/core/src/norm.dispatch.cpp b/modules/core/src/norm.dispatch.cpp index a67df07eba..fed4896677 100644 --- a/modules/core/src/norm.dispatch.cpp +++ b/modules/core/src/norm.dispatch.cpp @@ -218,120 +218,9 @@ int normL1_(const uchar* a, const uchar* b, int n) //================================================================================================== -template int -normDiffInf_(const T* src1, const T* src2, const uchar* mask, ST* _result, int len, int cn) -{ - ST result = *_result; - if( !mask ) - { - result = std::max(result, normInf(src1, src2, len*cn)); - } - else - { - for( int i = 0; i < len; i++, src1 += cn, src2 += cn ) - if( mask[i] ) - { - for( int k = 0; k < cn; k++ ) - result = std::max(result, (ST)std::abs(src1[k] - src2[k])); - } - } - *_result = result; - return 0; -} - -template int -normDiffL1_(const T* src1, const T* src2, const uchar* mask, ST* _result, int len, int cn) -{ - ST result = *_result; - if( !mask ) - { - result += normL1(src1, src2, len*cn); - } - else - { - for( int i = 0; i < len; i++, src1 += cn, src2 += cn ) - if( mask[i] ) - { - for( int k = 0; k < cn; k++ ) - result += std::abs(src1[k] - src2[k]); - } - } - *_result = result; - return 0; -} - -template int -normDiffL2_(const T* src1, const T* src2, const uchar* mask, ST* _result, int len, int cn) -{ - ST result = *_result; - if( !mask ) - { - result += normL2Sqr(src1, src2, len*cn); - } - else - { - for( int i = 0; i < len; i++, src1 += cn, src2 += cn ) - if( mask[i] ) - { - for( int k = 0; k < cn; k++ ) - { - ST v = src1[k] - src2[k]; - result += v*v; - } - } - } - *_result = result; - return 0; -} - -#define CV_DEF_NORM_DIFF_FUNC(L, suffix, type, ntype) \ - static int normDiff##L##_##suffix(const type* src1, const type* src2, \ - const uchar* mask, ntype* r, int len, int cn) \ -{ return normDiff##L##_(src1, src2, mask, r, (int)len, cn); } - -#define CV_DEF_NORM_DIFF_ALL(suffix, type, inftype, l1type, l2type) \ - CV_DEF_NORM_DIFF_FUNC(Inf, suffix, type, inftype) \ - CV_DEF_NORM_DIFF_FUNC(L1, suffix, type, l1type) \ - CV_DEF_NORM_DIFF_FUNC(L2, suffix, type, l2type) - -CV_DEF_NORM_DIFF_ALL(8u, uchar, int, int, int) -CV_DEF_NORM_DIFF_ALL(8s, schar, int, int, int) -CV_DEF_NORM_DIFF_ALL(16u, ushort, int, int, double) -CV_DEF_NORM_DIFF_ALL(16s, short, int, int, double) -CV_DEF_NORM_DIFF_ALL(32s, int, int, double, double) -CV_DEF_NORM_DIFF_ALL(32f, float, float, double, double) -CV_DEF_NORM_DIFF_ALL(64f, double, double, double, double) - typedef int (*NormFunc)(const uchar*, const uchar*, uchar*, int, int); typedef int (*NormDiffFunc)(const uchar*, const uchar*, const uchar*, uchar*, int, int); -static NormDiffFunc getNormDiffFunc(int normType, int depth) -{ - static NormDiffFunc normDiffTab[3][8] = - { - { - (NormDiffFunc)GET_OPTIMIZED(normDiffInf_8u), (NormDiffFunc)normDiffInf_8s, - (NormDiffFunc)normDiffInf_16u, (NormDiffFunc)normDiffInf_16s, - (NormDiffFunc)normDiffInf_32s, (NormDiffFunc)GET_OPTIMIZED(normDiffInf_32f), - (NormDiffFunc)normDiffInf_64f, 0 - }, - { - (NormDiffFunc)GET_OPTIMIZED(normDiffL1_8u), (NormDiffFunc)normDiffL1_8s, - (NormDiffFunc)normDiffL1_16u, (NormDiffFunc)normDiffL1_16s, - (NormDiffFunc)normDiffL1_32s, (NormDiffFunc)GET_OPTIMIZED(normDiffL1_32f), - (NormDiffFunc)normDiffL1_64f, 0 - }, - { - (NormDiffFunc)GET_OPTIMIZED(normDiffL2_8u), (NormDiffFunc)normDiffL2_8s, - (NormDiffFunc)normDiffL2_16u, (NormDiffFunc)normDiffL2_16s, - (NormDiffFunc)normDiffL2_32s, (NormDiffFunc)GET_OPTIMIZED(normDiffL2_32f), - (NormDiffFunc)normDiffL2_64f, 0 - } - }; - - return normDiffTab[normType][depth]; -} - #ifdef HAVE_OPENCL static bool ocl_norm( InputArray _src, int normType, InputArray _mask, double & result ) @@ -520,6 +409,10 @@ static NormFunc getNormFunc(int normType, int depth) { CV_INSTRUMENT_REGION(); CV_CPU_DISPATCH(getNormFunc, (normType, depth), CV_CPU_DISPATCH_MODES_ALL); } +static NormDiffFunc getNormDiffFunc(int normType, int depth) { + CV_INSTRUMENT_REGION(); + CV_CPU_DISPATCH(getNormDiffFunc, (normType, depth), CV_CPU_DISPATCH_MODES_ALL); +} double norm( InputArray _src, int normType, InputArray _mask ) { @@ -1050,6 +943,9 @@ double norm( InputArray _src1, InputArray _src2, int normType, InputArray _mask normType == NORM_L2 || normType == NORM_L2SQR || ((normType == NORM_HAMMING || normType == NORM_HAMMING2) && src1.type() == CV_8U) ); + NormDiffFunc func = getNormDiffFunc(normType >> 1, depth == CV_16F ? CV_32F : depth); + CV_Assert( func != 0 ); + if( src1.isContinuous() && src2.isContinuous() && mask.empty() ) { size_t len = src1.total()*src1.channels(); @@ -1057,31 +953,19 @@ double norm( InputArray _src1, InputArray _src2, int normType, InputArray _mask { if( src1.depth() == CV_32F ) { - const float* data1 = src1.ptr(); - const float* data2 = src2.ptr(); + const uchar* data1 = src1.ptr(); + const uchar* data2 = src2.ptr(); - if( normType == NORM_L2 ) + if( normType == NORM_L2 || normType == NORM_L2SQR || normType == NORM_L1 ) { double result = 0; - GET_OPTIMIZED(normDiffL2_32f)(data1, data2, 0, &result, (int)len, 1); - return std::sqrt(result); - } - if( normType == NORM_L2SQR ) - { - double result = 0; - GET_OPTIMIZED(normDiffL2_32f)(data1, data2, 0, &result, (int)len, 1); - return result; - } - if( normType == NORM_L1 ) - { - double result = 0; - GET_OPTIMIZED(normDiffL1_32f)(data1, data2, 0, &result, (int)len, 1); - return result; + func(data1, data2, 0, (uchar*)&result, (int)len, 1); + return normType == NORM_L2 ? std::sqrt(result) : result; } if( normType == NORM_INF ) { float result = 0; - GET_OPTIMIZED(normDiffInf_32f)(data1, data2, 0, &result, (int)len, 1); + func(data1, data2, 0, (uchar*)&result, (int)len, 1); return result; } } @@ -1115,9 +999,6 @@ double norm( InputArray _src1, InputArray _src2, int normType, InputArray _mask return result; } - NormDiffFunc func = getNormDiffFunc(normType >> 1, depth == CV_16F ? CV_32F : depth); - CV_Assert( func != 0 ); - const Mat* arrays[] = {&src1, &src2, &mask, 0}; uchar* ptrs[3] = {}; union diff --git a/modules/core/src/norm.simd.hpp b/modules/core/src/norm.simd.hpp index fd7b658ba1..c2b72d2e13 100644 --- a/modules/core/src/norm.simd.hpp +++ b/modules/core/src/norm.simd.hpp @@ -11,10 +11,12 @@ namespace cv { using NormFunc = int (*)(const uchar*, const uchar*, uchar*, int, int); +using NormDiffFunc = int (*)(const uchar*, const uchar*, const uchar*, uchar*, int, int); CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN NormFunc getNormFunc(int normType, int depth); +NormDiffFunc getNormDiffFunc(int normType, int depth); #ifndef CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY @@ -52,6 +54,42 @@ struct NormL2_SIMD { } }; +template +struct NormDiffInf_SIMD { + inline ST operator() (const T* src1, const T* src2, int n) const { + ST s = 0; + for (int i = 0; i < n; i++) { + ST v = ST(src1[i] - src2[i]); + s = std::max(s, (ST)cv_abs(v)); + } + return s; + } +}; + +template +struct NormDiffL1_SIMD { + inline ST operator() (const T* src1, const T* src2, int n) const { + ST s = 0; + for (int i = 0; i < n; i++) { + ST v = ST(src1[i] - src2[i]); + s += cv_abs(v); + } + return s; + } +}; + +template +struct NormDiffL2_SIMD { + inline ST operator() (const T* src1, const T* src2, int n) const { + ST s = 0; + for (int i = 0; i < n; i++) { + ST v = ST(src1[i] - src2[i]); + s += v * v; + } + return s; + } +}; + #if (CV_SIMD || CV_SIMD_SCALABLE) template<> @@ -348,6 +386,367 @@ struct NormL2_SIMD { } }; +template<> +struct NormDiffInf_SIMD { + int operator() (const uchar* src1, const uchar* src2, int n) const { + int j = 0; + int s = 0; + v_uint8 r0 = vx_setzero_u8(), r1 = vx_setzero_u8(); + v_uint8 r2 = vx_setzero_u8(), r3 = vx_setzero_u8(); + for (; j <= n - 4 * VTraits::vlanes(); j += 4 * VTraits::vlanes()) { + v_uint8 v01 = vx_load(src1 + j), v02 = vx_load(src2 + j); + r0 = v_max(r0, v_absdiff(v01, v02)); + + v_uint8 v11 = vx_load(src1 + j + VTraits::vlanes()), + v12 = vx_load(src2 + j + VTraits::vlanes()); + r1 = v_max(r1, v_absdiff(v11, v12)); + + v_uint8 v21 = vx_load(src1 + j + 2 * VTraits::vlanes()), + v22 = vx_load(src2 + j + 2 * VTraits::vlanes()); + r2 = v_max(r2, v_absdiff(v21, v22)); + + v_uint8 v31 = vx_load(src1 + j + 3 * VTraits::vlanes()), + v32 = vx_load(src2 + j + 3 * VTraits::vlanes()); + r3 = v_max(r3, v_absdiff(v31, v32)); + } + s = (int)v_reduce_max(v_max(v_max(v_max(r0, r1), r2), r3)); + for (; j < n; j++) { + int v = src1[j] - src2[j]; + s = std::max(s, (int)cv_abs(v)); + } + return s; + } +}; + +template<> +struct NormDiffInf_SIMD { + int operator() (const schar* src1, const schar* src2, int n) const { + int j = 0; + int s = 0; + v_uint8 r0 = vx_setzero_u8(), r1 = vx_setzero_u8(); + v_uint8 r2 = vx_setzero_u8(), r3 = vx_setzero_u8(); + for (; j <= n - 4 * VTraits::vlanes(); j += 4 * VTraits::vlanes()) { + v_int8 v01 = vx_load(src1 + j), v02 = vx_load(src2 + j); + r0 = v_max(r0, v_absdiff(v01, v02)); + + v_int8 v11 = vx_load(src1 + j + VTraits::vlanes()), + v12 = vx_load(src2 + j + VTraits::vlanes()); + r1 = v_max(r1, v_absdiff(v11, v12)); + + v_int8 v21 = vx_load(src1 + j + 2 * VTraits::vlanes()), + v22 = vx_load(src2 + j + 2 * VTraits::vlanes()); + r2 = v_max(r2, v_absdiff(v21, v22)); + + v_int8 v31 = vx_load(src1 + j + 3 * VTraits::vlanes()), + v32 = vx_load(src2 + j + 3 * VTraits::vlanes()); + r3 = v_max(r3, v_absdiff(v31, v32)); + } + s = (int)v_reduce_max(v_max(v_max(v_max(r0, r1), r2), r3)); + for (; j < n; j++) { + int v = src1[j] - src2[j]; + s = std::max(s, (int)cv_abs(v)); + } + return s; + } +}; + +template<> +struct NormDiffInf_SIMD { + int operator() (const ushort* src1, const ushort* src2, int n) const { + int j = 0; + int s = 0; + v_uint16 r0 = vx_setzero_u16(), r1 = vx_setzero_u16(); + v_uint16 r2 = vx_setzero_u16(), r3 = vx_setzero_u16(); + for (; j <= n - 4 * VTraits::vlanes(); j += 4 * VTraits::vlanes()) { + v_uint16 v01 = vx_load(src1 + j), v02 = vx_load(src2 + j); + r0 = v_max(r0, v_absdiff(v01, v02)); + + v_uint16 v11 = vx_load(src1 + j + VTraits::vlanes()), + v12 = vx_load(src2 + j + VTraits::vlanes()); + r1 = v_max(r1, v_absdiff(v11, v12)); + + v_uint16 v21 = vx_load(src1 + j + 2 * VTraits::vlanes()), + v22 = vx_load(src2 + j + 2 * VTraits::vlanes()); + r2 = v_max(r2, v_absdiff(v21, v22)); + + v_uint16 v31 = vx_load(src1 + j + 3 * VTraits::vlanes()), + v32 = vx_load(src2 + j + 3 * VTraits::vlanes()); + r3 = v_max(r3, v_absdiff(v31, v32)); + } + s = (int)v_reduce_max(v_max(v_max(v_max(r0, r1), r2), r3)); + for (; j < n; j++) { + int v = src1[j] - src2[j]; + s = std::max(s, (int)cv_abs(v)); + } + return s; + } +}; + +template<> +struct NormDiffInf_SIMD { + int operator() (const short* src1, const short* src2, int n) const { + int j = 0; + int s = 0; + v_uint16 r0 = vx_setzero_u16(), r1 = vx_setzero_u16(); + v_uint16 r2 = vx_setzero_u16(), r3 = vx_setzero_u16(); + for (; j <= n - 4 * VTraits::vlanes(); j += 4 * VTraits::vlanes()) { + v_int16 v01 = vx_load(src1 + j), v02 = vx_load(src2 + j); + r0 = v_max(r0, v_absdiff(v01, v02)); + + v_int16 v11 = vx_load(src1 + j + VTraits::vlanes()), + v12 = vx_load(src2 + j + VTraits::vlanes()); + r1 = v_max(r1, v_absdiff(v11, v12)); + + v_int16 v21 = vx_load(src1 + j + 2 * VTraits::vlanes()), + v22 = vx_load(src2 + j + 2 * VTraits::vlanes()); + r2 = v_max(r2, v_absdiff(v21, v22)); + + v_int16 v31 = vx_load(src1 + j + 3 * VTraits::vlanes()), + v32 = vx_load(src2 + j + 3 * VTraits::vlanes()); + r3 = v_max(r3, v_absdiff(v31, v32)); + } + s = (int)v_reduce_max(v_max(v_max(v_max(r0, r1), r2), r3)); + for (; j < n; j++) { + int v = src1[j] - src2[j]; + s = std::max(s, (int)cv_abs(v)); + } + return s; + } +}; + +template<> +struct NormDiffInf_SIMD { + int operator() (const int* src1, const int* src2, int n) const { + int j = 0; + int s = 0; + v_uint32 r0 = vx_setzero_u32(), r1 = vx_setzero_u32(); + v_uint32 r2 = vx_setzero_u32(), r3 = vx_setzero_u32(); + for (; j <= n - 4 * VTraits::vlanes(); j += 4 * VTraits::vlanes()) { + v_int32 v01 = vx_load(src1 + j), v02 = vx_load(src2 + j); + r0 = v_max(r0, v_abs(v_sub(v01, v02))); + + v_int32 v11 = vx_load(src1 + j + VTraits::vlanes()), + v12 = vx_load(src2 + j + VTraits::vlanes()); + r1 = v_max(r1, v_abs(v_sub(v11, v12))); + + v_int32 v21 = vx_load(src1 + j + 2 * VTraits::vlanes()), + v22 = vx_load(src2 + j + 2 * VTraits::vlanes()); + r2 = v_max(r2, v_abs(v_sub(v21, v22))); + + v_int32 v31 = vx_load(src1 + j + 3 * VTraits::vlanes()), + v32 = vx_load(src2 + j + 3 * VTraits::vlanes()); + r3 = v_max(r3, v_abs(v_sub(v31, v32))); + } + s = (int)v_reduce_max(v_max(v_max(v_max(r0, r1), r2), r3)); + for (; j < n; j++) { + int v = src1[j] - src2[j]; + s = std::max(s, (int)cv_abs(v)); + } + return s; + } +}; + +template<> +struct NormDiffInf_SIMD { + float operator() (const float* src1, const float* src2, int n) const { + int j = 0; + float s = 0; + v_float32 r0 = vx_setzero_f32(), r1 = vx_setzero_f32(); + for (; j <= n - 2 * VTraits::vlanes(); j += 2 * VTraits::vlanes()) { + v_float32 v01 = vx_load(src1 + j), v02 = vx_load(src2 + j); + r0 = v_max(r0, v_absdiff(v01, v02)); + + v_float32 v11 = vx_load(src1 + j + VTraits::vlanes()), + v12 = vx_load(src2 + j + VTraits::vlanes()); + r1 = v_max(r1, v_absdiff(v11, v12)); + } + s = v_reduce_max(v_max(r0, r1)); + for (; j < n; j++) { + float v = src1[j] - src2[j]; + s = std::max(s, cv_abs(v)); + } + return s; + } +}; + +template<> +struct NormDiffL1_SIMD { + int operator() (const uchar* src1, const uchar* src2, int n) const { + int j = 0; + int s = 0; + v_uint32 r0 = vx_setzero_u32(), r1 = vx_setzero_u32(); + v_uint8 one = vx_setall_u8(1); + for (; j<= n - 2 * VTraits::vlanes(); j += 2 * VTraits::vlanes()) { + v_uint8 v01 = vx_load(src1 + j), v02 = vx_load(src2 + j); + r0 = v_dotprod_expand_fast(v_absdiff(v01, v02), one, r0); + + v_uint8 v11 = vx_load(src1 + j + VTraits::vlanes()), + v12 = vx_load(src2 + j + VTraits::vlanes()); + r1 = v_dotprod_expand_fast(v_absdiff(v11, v12), one, r1); + } + s += v_reduce_sum(v_add(r0, r1)); + for (; j < n; j++) { + int v = src1[j] - src2[j]; + s += (int)cv_abs(v); + } + return s; + } +}; + +template<> +struct NormDiffL1_SIMD { + int operator() (const schar* src1, const schar* src2, int n) const { + int j = 0; + int s = 0; + v_uint32 r0 = vx_setzero_u32(), r1 = vx_setzero_u32(); + v_uint8 one = vx_setall_u8(1); + for (; j<= n - 2 * VTraits::vlanes(); j += 2 * VTraits::vlanes()) { + v_int8 v01 = vx_load(src1 + j), v02 = vx_load(src2 + j); + r0 = v_dotprod_expand_fast(v_absdiff(v01, v02), one, r0); + + v_int8 v11 = vx_load(src1 + j + VTraits::vlanes()), + v12 = vx_load(src2 + j + VTraits::vlanes()); + r1 = v_dotprod_expand_fast(v_absdiff(v11, v12), one, r1); + } + s += v_reduce_sum(v_add(r0, r1)); + for (; j < n; j++) { + int v =src1[j] - src2[j]; + s += (int)cv_abs(v); + } + return s; + } +}; + +template<> +struct NormDiffL1_SIMD { + int operator() (const ushort* src1, const ushort* src2, int n) const { + int j = 0; + int s = 0; + v_uint32 r0 = vx_setzero_u32(), r1 = vx_setzero_u32(); + v_uint32 r2 = vx_setzero_u32(), r3 = vx_setzero_u32(); + for (; j<= n - 4 * VTraits::vlanes(); j += 4 * VTraits::vlanes()) { + v_uint16 v01 = vx_load(src1 + j), v02 = vx_load(src2 + j); + v_uint32 u00, u01; + v_expand(v_absdiff(v01, v02), u00, u01); + r0 = v_add(r0, v_add(u00, u01)); + + v_uint16 v11 = vx_load(src1 + j + VTraits::vlanes()), + v12 = vx_load(src2 + j + VTraits::vlanes()); + v_uint32 u10, u11; + v_expand(v_absdiff(v11, v12), u10, u11); + r1 = v_add(r1, v_add(u10, u11)); + + v_uint16 v21 = vx_load(src1 + j + 2 * VTraits::vlanes()), + v22 = vx_load(src2 + j + 2 * VTraits::vlanes()); + v_uint32 u20, u21; + v_expand(v_absdiff(v21, v22), u20, u21); + r2 = v_add(r2, v_add(u20, u21)); + + v_uint16 v31 = vx_load(src1 + j + 3 * VTraits::vlanes()), + v32 = vx_load(src2 + j + 3 * VTraits::vlanes()); + v_uint32 u30, u31; + v_expand(v_absdiff(v31, v32), u30, u31); + r3 = v_add(r3, v_add(u30, u31)); + } + s += (int)v_reduce_sum(v_add(v_add(v_add(r0, r1), r2), r3)); + for (; j < n; j++) { + int v = src1[j] - src2[j]; + s += (int)cv_abs(v); + } + return s; + } +}; + +template<> +struct NormDiffL1_SIMD { + int operator() (const short* src1, const short* src2, int n) const { + int j = 0; + int s = 0; + v_uint32 r0 = vx_setzero_u32(), r1 = vx_setzero_u32(); + v_uint32 r2 = vx_setzero_u32(), r3 = vx_setzero_u32(); + for (; j<= n - 4 * VTraits::vlanes(); j += 4 * VTraits::vlanes()) { + v_int16 v01 = vx_load(src1 + j), v02 = vx_load(src2 + j); + v_uint32 u00, u01; + v_expand(v_absdiff(v01, v02), u00, u01); + r0 = v_add(r0, v_add(u00, u01)); + + v_int16 v11 = vx_load(src1 + j + VTraits::vlanes()), + v12 = vx_load(src2 + j + VTraits::vlanes()); + v_uint32 u10, u11; + v_expand(v_absdiff(v11, v12), u10, u11); + r1 = v_add(r1, v_add(u10, u11)); + + v_int16 v21 = vx_load(src1 + j + 2 * VTraits::vlanes()), + v22 = vx_load(src2 + j + 2 * VTraits::vlanes()); + v_uint32 u20, u21; + v_expand(v_absdiff(v21, v22), u20, u21); + r2 = v_add(r2, v_add(u20, u21)); + + v_int16 v31 = vx_load(src1 + j + 3 * VTraits::vlanes()), + v32 = vx_load(src2 + j + 3 * VTraits::vlanes()); + v_uint32 u30, u31; + v_expand(v_absdiff(v31, v32), u30, u31); + r3 = v_add(r3, v_add(u30, u31)); + } + s += (int)v_reduce_sum(v_add(v_add(v_add(r0, r1), r2), r3)); + for (; j < n; j++) { + int v = src1[j] - src2[j]; + s += (int)cv_abs(v); + } + return s; + } +}; + +template<> +struct NormDiffL2_SIMD { + int operator() (const uchar* src1, const uchar* src2, int n) const { + int j = 0; + int s = 0; + v_uint32 r0 = vx_setzero_u32(), r1 = vx_setzero_u32(); + for (; j <= n - 2 * VTraits::vlanes(); j += 2 * VTraits::vlanes()) { + v_uint8 v01 = vx_load(src1 + j), v02 = vx_load(src2 + j); + v_uint8 v0 = v_absdiff(v01, v02); + r0 = v_dotprod_expand_fast(v0, v0, r0); + + v_uint8 v11 = vx_load(src1 + j + VTraits::vlanes()), + v12 = vx_load(src2 + j + VTraits::vlanes()); + v_uint8 v1 = v_absdiff(v11, v12); + r1 = v_dotprod_expand_fast(v1, v1, r1); + } + s += v_reduce_sum(v_add(r0, r1)); + for (; j < n; j++) { + int v = saturate_cast(src1[j] - src2[j]); + s += v * v; + } + return s; + } +}; + +template<> +struct NormDiffL2_SIMD { + int operator() (const schar* src1, const schar* src2, int n) const { + int j = 0; + int s = 0; + v_uint32 r0 = vx_setzero_u32(), r1 = vx_setzero_u32(); + for (; j <= n - 2 * VTraits::vlanes(); j += 2 * VTraits::vlanes()) { + v_int8 v01 = vx_load(src1 + j), v02 = vx_load(src2 + j); + v_uint8 v0 = v_absdiff(v01, v02); + r0 = v_dotprod_expand_fast(v0, v0, r0); + + v_int8 v11 = vx_load(src1 + j + VTraits::vlanes()), + v12 = vx_load(src2 + j + VTraits::vlanes()); + v_uint8 v1 = v_absdiff(v11, v12); + r1 = v_dotprod_expand_fast(v1, v1, r1); + } + s += v_reduce_sum(v_add(r0, r1)); + for (; j < n; j++) { + int v = saturate_cast(src1[j] - src2[j]); + s += v * v; + } + return s; + } +}; + #endif #if (CV_SIMD_64F || CV_SIMD_SCALABLE_64F) @@ -570,6 +969,257 @@ struct NormL2_SIMD { } }; +template<> +struct NormDiffInf_SIMD { + double operator() (const double* src1, const double* src2, int n) const { + int j = 0; + double s = 0; + v_float64 r0 = vx_setzero_f64(), r1 = vx_setzero_f64(); + for (; j <= n - 2 * VTraits::vlanes(); j += 2 * VTraits::vlanes()) { + v_float64 v01 = vx_load(src1 + j), v02 = vx_load(src2 + j); + r0 = v_max(r0, v_absdiff(v01, v02)); + + v_float64 v11 = vx_load(src1 + j + VTraits::vlanes()), + v12 = vx_load(src2 + j + VTraits::vlanes()); + r1 = v_max(r1, v_absdiff(v11, v12)); + } + // [TODO]: use v_reduce_max when it supports float64 + double t[VTraits::max_nlanes]; + vx_store(t, v_max(r0, r1)); + for (int i = 0; i < VTraits::vlanes(); i++) { + s = std::max(s, cv_abs(t[i])); + } + for (; j < n; j++) { + double v = src1[j] - src2[j]; + s = std::max(s, cv_abs(v)); + } + return s; + } +}; + +template<> +struct NormDiffL1_SIMD { + double operator() (const int* src1, const int* src2, int n) const { + int j = 0; + double s = 0.f; + v_float64 r0 = vx_setzero_f64(), r1 = vx_setzero_f64(); + v_float64 r2 = vx_setzero_f64(), r3 = vx_setzero_f64(); + for (; j <= n - 2 * VTraits::vlanes(); j += 2 * VTraits::vlanes()) { + v_int32 v01 = vx_load(src1 + j), v02 = vx_load(src2 + j); + v_float32 v0 = v_abs(v_cvt_f32(v_sub(v01, v02))); + r0 = v_add(r0, v_cvt_f64(v0)); r1 = v_add(r1, v_cvt_f64_high(v0)); + + v_int32 v11 = vx_load(src1 + j + VTraits::vlanes()), + v12 = vx_load(src2 + j + VTraits::vlanes()); + v_float32 v1 = v_abs(v_cvt_f32(v_sub(v11, v12))); + r2 = v_add(r2, v_cvt_f64(v1)); r3 = v_add(r3, v_cvt_f64_high(v1)); + } + s += v_reduce_sum(v_add(v_add(v_add(r0, r1), r2), r3)); + for (; j < n; j++) { + double v = src1[j] - src2[j]; + s += cv_abs(v); + } + return s; + } +}; + +template<> +struct NormDiffL1_SIMD { + double operator() (const float* src1, const float* src2, int n) const { + int j = 0; + double s = 0.f; + v_float64 r0 = vx_setzero_f64(), r1 = vx_setzero_f64(); + v_float64 r2 = vx_setzero_f64(), r3 = vx_setzero_f64(); + for (; j <= n - 2 * VTraits::vlanes(); j += 2 * VTraits::vlanes()) { + v_float32 v01 = vx_load(src1 + j), v02 = vx_load(src2 + j); + v_float32 v0 = v_absdiff(v01, v02); + r0 = v_add(r0, v_cvt_f64(v0)); r1 = v_add(r1, v_cvt_f64_high(v0)); + + v_float32 v11 = vx_load(src1 + j + VTraits::vlanes()), + v12 = vx_load(src2 + j + VTraits::vlanes()); + v_float32 v1 = v_absdiff(v11, v12); + r2 = v_add(r2, v_cvt_f64(v1)); r3 = v_add(r3, v_cvt_f64_high(v1)); + } + s += v_reduce_sum(v_add(v_add(v_add(r0, r1), r2), r3)); + for (; j < n; j++) { + double v = src1[j] - src2[j]; + s += cv_abs(v); + } + return s; + } +}; + +template<> +struct NormDiffL1_SIMD { + double operator() (const double* src1, const double* src2, int n) const { + int j = 0; + double s = 0.f; + v_float64 r0 = vx_setzero_f64(), r1 = vx_setzero_f64(); + for (; j <= n - 2 * VTraits::vlanes(); j += 2 * VTraits::vlanes()) { + v_float64 v01 = vx_load(src1 + j), v02 = vx_load(src2 + j); + r0 = v_add(r0, v_absdiff(v01, v02)); + + v_float64 v11 = vx_load(src1 + j + VTraits::vlanes()), + v12 = vx_load(src2 + j + VTraits::vlanes()); + r1 = v_add(r1, v_absdiff(v11, v12)); + } + s += v_reduce_sum(v_add(r0, r1)); + for (; j < n; j++) { + double v = src1[j] - src2[j]; + s += cv_abs(v); + } + return s; + } +}; + +template<> +struct NormDiffL2_SIMD { + double operator() (const ushort* src1, const ushort* src2, int n) const { + int j = 0; + double s = 0.f; + v_float64 r0 = vx_setzero_f64(), r1 = vx_setzero_f64(); + for (; j <= n - 2 * VTraits::vlanes(); j += 2 * VTraits::vlanes()) { + v_uint16 v01 = vx_load(src1 + j), v02 = vx_load(src2 + j); + v_uint16 v0 = v_absdiff(v01, v02); + v_uint64 u0 = v_dotprod_expand_fast(v0, v0); + r0 = v_add(r0, v_cvt_f64(v_reinterpret_as_s64(u0))); + + v_uint16 v11 = vx_load(src1 + j + VTraits::vlanes()), + v12 = vx_load(src2 + j + VTraits::vlanes()); + v_uint16 v1 = v_absdiff(v11, v12); + v_uint64 u1 = v_dotprod_expand_fast(v1, v1); + r1 = v_add(r1, v_cvt_f64(v_reinterpret_as_s64(u1))); + } + s += v_reduce_sum(v_add(r0, r1)); + for (; j < n; j++) { + double v = saturate_cast(src1[j] - src2[j]); + s += v * v; + } + return s; + } +}; + +template<> +struct NormDiffL2_SIMD { + double operator() (const short* src1, const short* src2, int n) const { + int j = 0; + double s = 0.f; + v_float64 r0 = vx_setzero_f64(), r1 = vx_setzero_f64(); + for (; j <= n - 2 * VTraits::vlanes(); j += 2 * VTraits::vlanes()) { + v_int16 v01 = vx_load(src1 + j), v02 = vx_load(src2 + j); + v_uint16 v0 = v_absdiff(v01, v02); + v_uint64 u0 = v_dotprod_expand_fast(v0, v0); + r0 = v_add(r0, v_cvt_f64(v_reinterpret_as_s64(u0))); + + v_int16 v11 = vx_load(src1 + j + VTraits::vlanes()), + v12 = vx_load(src2 + j + VTraits::vlanes()); + v_uint16 v1 = v_absdiff(v11, v12); + v_uint64 u1 = v_dotprod_expand_fast(v1, v1); + r1 = v_add(r1, v_cvt_f64(v_reinterpret_as_s64(u1))); + } + s += v_reduce_sum(v_add(r0, r1)); + for (; j < n; j++) { + double v = saturate_cast(src1[j] - src2[j]); + s += v * v; + } + return s; + } +}; + +template<> +struct NormDiffL2_SIMD { + double operator() (const int* src1, const int* src2, int n) const { + int j = 0; + double s = 0.f; + v_float64 r0 = vx_setzero_f64(), r1 = vx_setzero_f64(); + v_float64 r2 = vx_setzero_f64(), r3 = vx_setzero_f64(); + for (; j <= n - 2 * VTraits::vlanes(); j += 2 * VTraits::vlanes()) { + v_int32 v01 = vx_load(src1 + j), v02 = vx_load(src2 + j); + v_float32 v0 = v_abs(v_cvt_f32(v_sub(v01, v02))); + v_float64 f00, f01; + f00 = v_cvt_f64(v0); f01 = v_cvt_f64_high(v0); + r0 = v_fma(f00, f00, r0); r1 = v_fma(f01, f01, r1); + + v_int32 v11 = vx_load(src1 + j + VTraits::vlanes()), + v12 = vx_load(src2 + j + VTraits::vlanes()); + v_float32 v1 = v_abs(v_cvt_f32(v_sub(v11, v12))); + v_float64 f10, f11; + f10 = v_cvt_f64(v1); f11 = v_cvt_f64_high(v1); + r2 = v_fma(f10, f10, r2); r3 = v_fma(f11, f11, r3); + } + s += v_reduce_sum(v_add(v_add(v_add(r0, r1), r2), r3)); + for (; j < n; j++) { + double v = src1[j] - src2[j]; + s += v * v; + } + return s; + } +}; + +template<> +struct NormDiffL2_SIMD { + double operator() (const float* src1, const float* src2, int n) const { + int j = 0; + double s = 0.f; + v_float64 r0 = vx_setzero_f64(), r1 = vx_setzero_f64(); + v_float64 r2 = vx_setzero_f64(), r3 = vx_setzero_f64(); + for (; j <= n - 2 * VTraits::vlanes(); j += 2 * VTraits::vlanes()) { + v_float32 v01 = vx_load(src1 + j), v02 = vx_load(src2 + j); + v_float32 v0 = v_absdiff(v01, v02); + v_float64 f01 = v_cvt_f64(v0), f02 = v_cvt_f64_high(v0); + r0 = v_fma(f01, f01, r0); r1 = v_fma(f02, f02, r1); + + v_float32 v11 = vx_load(src1 + j + VTraits::vlanes()), + v12 = vx_load(src2 + j + VTraits::vlanes()); + v_float32 v1 = v_absdiff(v11, v12); + v_float64 f11 = v_cvt_f64(v1), f12 = v_cvt_f64_high(v1); + r2 = v_fma(f11, f11, r2); r3 = v_fma(f12, f12, r3); + } + s += v_reduce_sum(v_add(v_add(v_add(r0, r1), r2), r3)); + for (; j < n; j++) { + double v = src1[j] - src2[j]; + s += v * v; + } + return s; + } +}; + +template<> +struct NormDiffL2_SIMD { + double operator() (const double* src1, const double* src2, int n) const { + int j = 0; + double s = 0.f; + v_float64 r0 = vx_setzero_f64(), r1 = vx_setzero_f64(); + v_float64 r2 = vx_setzero_f64(), r3 = vx_setzero_f64(); + for (; j <= n - 4 * VTraits::vlanes(); j += 4 * VTraits::vlanes()) { + v_float64 v01 = vx_load(src1 + j), v02 = vx_load(src2 + j); + v_float64 v0 = v_absdiff(v01, v02); + r0 = v_fma(v0, v0, r0); + + v_float64 v11 = vx_load(src1 + j + VTraits::vlanes()), + v12 = vx_load(src2 + j + VTraits::vlanes()); + v_float64 v1 = v_absdiff(v11, v12); + r1 = v_fma(v1, v1, r1); + + v_float64 v21 = vx_load(src1 + j + 2 * VTraits::vlanes()), + v22 = vx_load(src2 + j + 2 * VTraits::vlanes()); + v_float64 v2 = v_absdiff(v21, v22); + r2 = v_fma(v2, v2, r2); + + v_float64 v31 = vx_load(src1 + j + 3 * VTraits::vlanes()), + v32 = vx_load(src2 + j + 3 * VTraits::vlanes()); + v_float64 v3 = v_absdiff(v31, v32); + r3 = v_fma(v3, v3, r3); + } + s += v_reduce_sum(v_add(v_add(v_add(r0, r1), r2), r3)); + for (; j < n; j++) { + double v = src1[j] - src2[j]; + s += v * v; + } + return s; + } +}; + #endif template int @@ -630,9 +1280,71 @@ normL2_(const T* src, const uchar* mask, ST* _result, int len, int cn) { return 0; } +template int +normDiffInf_(const T* src1, const T* src2, const uchar* mask, ST* _result, int len, int cn) { + ST result = *_result; + if( !mask ) { + NormDiffInf_SIMD op; + result = std::max(result, op(src1, src2, len*cn)); + } else { + for( int i = 0; i < len; i++, src1 += cn, src2 += cn ) { + if( mask[i] ) { + for( int k = 0; k < cn; k++ ) { + result = std::max(result, (ST)std::abs(src1[k] - src2[k])); + } + } + } + } + *_result = result; + return 0; +} + +template int +normDiffL1_(const T* src1, const T* src2, const uchar* mask, ST* _result, int len, int cn) { + ST result = *_result; + if( !mask ) { + NormDiffL1_SIMD op; + result += op(src1, src2, len*cn); + } + else { + for( int i = 0; i < len; i++, src1 += cn, src2 += cn ) { + if( mask[i] ) { + for( int k = 0; k < cn; k++ ) { + result += std::abs(src1[k] - src2[k]); + } + } + } + } + *_result = result; + return 0; +} + +template int +normDiffL2_(const T* src1, const T* src2, const uchar* mask, ST* _result, int len, int cn) { + ST result = *_result; + if( !mask ) { + NormDiffL2_SIMD op; + result += op(src1, src2, len*cn); + } else { + for( int i = 0; i < len; i++, src1 += cn, src2 += cn ) { + if( mask[i] ) { + for( int k = 0; k < cn; k++ ) { + ST v = src1[k] - src2[k]; + result += v*v; + } + } + } + } + *_result = result; + return 0; +} + #define CV_DEF_NORM_FUNC(L, suffix, type, ntype) \ static int norm##L##_##suffix(const type* src, const uchar* mask, ntype* r, int len, int cn) \ { CV_INSTRUMENT_REGION(); return norm##L##_(src, mask, r, len, cn); } \ + static int normDiff##L##_##suffix(const type* src1, const type* src2, \ + const uchar* mask, ntype* r, int len, int cn) \ +{ return normDiff##L##_(src1, src2, mask, r, (int)len, cn); } #define CV_DEF_NORM_ALL(suffix, type, inftype, l1type, l2type) \ CV_DEF_NORM_FUNC(Inf, suffix, type, inftype) \ @@ -669,6 +1381,33 @@ NormFunc getNormFunc(int normType, int depth) return normTab[normType][depth]; } +NormDiffFunc getNormDiffFunc(int normType, int depth) +{ + static NormDiffFunc normDiffTab[3][8] = + { + { + (NormDiffFunc)GET_OPTIMIZED(normDiffInf_8u), (NormDiffFunc)normDiffInf_8s, + (NormDiffFunc)normDiffInf_16u, (NormDiffFunc)normDiffInf_16s, + (NormDiffFunc)normDiffInf_32s, (NormDiffFunc)GET_OPTIMIZED(normDiffInf_32f), + (NormDiffFunc)normDiffInf_64f, 0 + }, + { + (NormDiffFunc)GET_OPTIMIZED(normDiffL1_8u), (NormDiffFunc)normDiffL1_8s, + (NormDiffFunc)normDiffL1_16u, (NormDiffFunc)normDiffL1_16s, + (NormDiffFunc)normDiffL1_32s, (NormDiffFunc)GET_OPTIMIZED(normDiffL1_32f), + (NormDiffFunc)normDiffL1_64f, 0 + }, + { + (NormDiffFunc)GET_OPTIMIZED(normDiffL2_8u), (NormDiffFunc)normDiffL2_8s, + (NormDiffFunc)normDiffL2_16u, (NormDiffFunc)normDiffL2_16s, + (NormDiffFunc)normDiffL2_32s, (NormDiffFunc)GET_OPTIMIZED(normDiffL2_32f), + (NormDiffFunc)normDiffL2_64f, 0 + } + }; + + return normDiffTab[normType][depth]; +} + #endif // CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY CV_CPU_OPTIMIZATION_NAMESPACE_END From 0db6a496baf5543a9b08004a13674ad3ba067403 Mon Sep 17 00:00:00 2001 From: Pierre Chatelier Date: Wed, 12 Mar 2025 15:55:07 +0100 Subject: [PATCH 08/94] Merge pull request #26842 from chacha21:threshold_with_mask Added optional mask to cv::threshold #26842 Proposal for #26777 To avoid code duplication, and keep performance when no mask is used, inner implementation always propagate the const cv::Mat& mask, but they use a template parameter that let the compiler optimize out unnecessary tests when the mask is not to be used. See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [X] The PR is proposed to the proper branch - [X] There is a reference to the original bug report and related work - [X] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- modules/imgproc/include/opencv2/imgproc.hpp | 21 +- modules/imgproc/src/opencl/threshold_mask.cl | 60 +++ modules/imgproc/src/thresh.cpp | 396 ++++++++++++++++--- modules/imgproc/test/ocl/test_imgproc.cpp | 51 +++ modules/imgproc/test/test_thresh.cpp | 92 +++++ 5 files changed, 574 insertions(+), 46 deletions(-) create mode 100644 modules/imgproc/src/opencl/threshold_mask.cl diff --git a/modules/imgproc/include/opencv2/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc.hpp index 4bc9d6a11c..cf222b9edd 100644 --- a/modules/imgproc/include/opencv2/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc.hpp @@ -3081,11 +3081,30 @@ types. @param type thresholding type (see #ThresholdTypes). @return the computed threshold value if Otsu's or Triangle methods used. -@sa adaptiveThreshold, findContours, compare, min, max +@sa thresholdWithMask, adaptiveThreshold, findContours, compare, min, max */ CV_EXPORTS_W double threshold( InputArray src, OutputArray dst, double thresh, double maxval, int type ); +/** @brief Same as #threshold, but with an optional mask + +@note If the mask is empty, #thresholdWithMask is equivalent to #threshold. +If the mask is not empty, dst *must* be of the same size and type as src, so that +outliers pixels are left as-is + +@param src input array (multiple-channel, 8-bit or 32-bit floating point). +@param dst output array of the same size and type and the same number of channels as src. +@param mask optional mask (same size as src, 8-bit). +@param thresh threshold value. +@param maxval maximum value to use with the #THRESH_BINARY and #THRESH_BINARY_INV thresholding +types. +@param type thresholding type (see #ThresholdTypes). +@return the computed threshold value if Otsu's or Triangle methods used. + +@sa threshold, adaptiveThreshold, findContours, compare, min, max +*/ +CV_EXPORTS_W double thresholdWithMask( InputArray src, InputOutputArray dst, InputArray mask, + double thresh, double maxval, int type ); /** @brief Applies an adaptive threshold to an array. diff --git a/modules/imgproc/src/opencl/threshold_mask.cl b/modules/imgproc/src/opencl/threshold_mask.cl new file mode 100644 index 0000000000..9e0ea603f0 --- /dev/null +++ b/modules/imgproc/src/opencl/threshold_mask.cl @@ -0,0 +1,60 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// @Authors +// Zhang Ying, zhangying913@gmail.com +// Pierre Chatelier, pierre@chachatelier.fr + +#ifdef DOUBLE_SUPPORT +#ifdef cl_amd_fp64 +#pragma OPENCL EXTENSION cl_amd_fp64:enable +#elif defined (cl_khr_fp64) +#pragma OPENCL EXTENSION cl_khr_fp64:enable +#endif +#endif + +__kernel void threshold_mask(__global const uchar * srcptr, int src_step, int src_offset, + __global uchar * dstptr, int dst_step, int dst_offset, int rows, int cols, + __global const uchar * maskptr, int mask_step, int mask_offset, + T1 thresh, T1 max_val, T1 min_val) +{ + int gx = get_global_id(0); + int gy = get_global_id(1) * STRIDE_SIZE; + + if (gx < cols) + { + int src_index = mad24(gy, src_step, mad24(gx, (int)sizeof(T), src_offset)); + int dst_index = mad24(gy, dst_step, mad24(gx, (int)sizeof(T), dst_offset)); + int mask_index = mad24(gy, mask_step, mad24(gx/CN, (int)sizeof(uchar), mask_offset)); + + #pragma unroll + for (int i = 0; i < STRIDE_SIZE; i++) + { + if (gy < rows) + { + T sdata = *(__global const T *)(srcptr + src_index); + const uchar mdata = *(maskptr + mask_index); + if (mdata != 0) + { + __global T * dst = (__global T *)(dstptr + dst_index); + + #ifdef THRESH_BINARY + dst[0] = sdata > (thresh) ? (T)(max_val) : (T)(0); + #elif defined THRESH_BINARY_INV + dst[0] = sdata > (thresh) ? (T)(0) : (T)(max_val); + #elif defined THRESH_TRUNC + dst[0] = clamp(sdata, (T)min_val, (T)(thresh)); + #elif defined THRESH_TOZERO + dst[0] = sdata > (thresh) ? sdata : (T)(0); + #elif defined THRESH_TOZERO_INV + dst[0] = sdata > (thresh) ? (T)(0) : sdata; + #endif + } + gy++; + src_index += src_step; + dst_index += dst_step; + mask_index += mask_step; + } + } + } +} diff --git a/modules/imgproc/src/thresh.cpp b/modules/imgproc/src/thresh.cpp index c92912d8dc..09266393e2 100644 --- a/modules/imgproc/src/thresh.cpp +++ b/modules/imgproc/src/thresh.cpp @@ -119,6 +119,65 @@ static void threshGeneric(Size roi, const T* src, size_t src_step, T* dst, } } +template +static void threshGenericWithMask(const Mat& _src, Mat& _dst, const Mat& _mask, + T thresh, T maxval, int type) +{ + Size roi = _src.size(); + const int cn = _src.channels(); + roi.width *= cn; + size_t src_step = _src.step/_src.elemSize1(); + size_t dst_step = _dst.step/_src.elemSize1(); + + const T* src = _src.ptr(0); + T* dst = _dst.ptr(0); + const unsigned char* mask = _mask.ptr(0); + size_t mask_step = _mask.step; + + int i = 0, j; + switch (type) + { + case THRESH_BINARY: + for (; i < roi.height; i++, src += src_step, dst += dst_step, mask += mask_step) + for (j = 0; j < roi.width; j++) + if (mask[j/cn] != 0) + dst[j] = threshBinary(src[j], thresh, maxval); + return; + + case THRESH_BINARY_INV: + for (; i < roi.height; i++, src += src_step, dst += dst_step, mask += mask_step) + for (j = 0; j < roi.width; j++) + if (mask[j/cn] != 0) + dst[j] = threshBinaryInv(src[j], thresh, maxval); + return; + + case THRESH_TRUNC: + for (; i < roi.height; i++, src += src_step, dst += dst_step, mask += mask_step) + for (j = 0; j < roi.width; j++) + if (mask[j/cn] != 0) + dst[j] = threshTrunc(src[j], thresh); + return; + + case THRESH_TOZERO: + for (; i < roi.height; i++, src += src_step, dst += dst_step, mask += mask_step) + for (j = 0; j < roi.width; j++) + if (mask[j/cn] != 0) + dst[j] = threshToZero(src[j], thresh); + return; + + case THRESH_TOZERO_INV: + for (; i < roi.height; i++, src += src_step, dst += dst_step, mask += mask_step) + for (j = 0; j < roi.width; j++) + if (mask[j/cn] != 0) + dst[j] = threshToZeroInv(src[j], thresh); + return; + + default: + CV_Error( cv::Error::StsBadArg, "" ); return; + } +} + + static void thresh_8u( const Mat& _src, Mat& _dst, uchar thresh, uchar maxval, int type ) { @@ -724,7 +783,6 @@ thresh_16s( const Mat& _src, Mat& _dst, short thresh, short maxval, int type ) #endif } - static void thresh_32f( const Mat& _src, Mat& _dst, float thresh, float maxval, int type ) { @@ -1121,8 +1179,8 @@ static bool ipp_getThreshVal_Otsu_8u( const unsigned char* _src, int step, Size } #endif -template -static double getThreshVal_Otsu( const Mat& _src, const Size& size) +template +static double getThreshVal_Otsu( const Mat& _src, const Mat& _mask, const Size& size ) { const int N = std::numeric_limits::max() + 1; int i, j; @@ -1136,24 +1194,51 @@ static double getThreshVal_Otsu( const Mat& _src, const Size& size) #if CV_ENABLE_UNROLLED int* h_unrolled[3] = {h + N, h + 2 * N, h + 3 * N }; #endif + int maskCount = 0; for( i = 0; i < size.height; i++ ) { const T* src = _src.ptr(i, 0); + const unsigned char* pMask = nullptr; + if ( useMask ) + pMask = _mask.ptr(i, 0); j = 0; #if CV_ENABLE_UNROLLED for( ; j <= size.width - 4; j += 4 ) { int v0 = src[j], v1 = src[j+1]; - h[v0]++; h_unrolled[0][v1]++; + if ( useMask ) + { + h[v0] += (pMask[j] != 0) ? ++maskCount,1 : 0; + h_unrolled[0][v1] += (pMask[j+1] != 0) ? ++maskCount,1 : 0; + } + else + { + h[v0]++; + h_unrolled[0][v1]++; + } v0 = src[j+2]; v1 = src[j+3]; - h_unrolled[1][v0]++; h_unrolled[2][v1]++; + if ( useMask ) + { + h_unrolled[1][v0] += (pMask[j+2] != 0) ? ++maskCount,1 : 0; + h_unrolled[2][v1] += (pMask[j+3] != 0) ? ++maskCount,1 : 0; + } + else + { + h_unrolled[1][v0]++; + h_unrolled[2][v1]++; + } } #endif for( ; j < size.width; j++ ) - h[src[j]]++; + { + if ( useMask ) + h[src[j]] += (pMask[j] != 0) ? ++maskCount,1 : 0; + else + h[src[j]]++; + } } - double mu = 0, scale = 1./(size.width*size.height); + double mu = 0, scale = 1./( useMask ? maskCount : ( size.width*size.height ) ); for( i = 0; i < N; i++ ) { #if CV_ENABLE_UNROLLED @@ -1191,46 +1276,56 @@ static double getThreshVal_Otsu( const Mat& _src, const Size& size) } static double -getThreshVal_Otsu_8u( const Mat& _src ) +getThreshVal_Otsu_8u( const Mat& _src, const Mat& _mask = cv::Mat()) { Size size = _src.size(); int step = (int) _src.step; - if( _src.isContinuous() ) + if( _src.isContinuous() && ( _mask.empty() || _mask.isContinuous() ) ) { size.width *= size.height; size.height = 1; step = size.width; } -#ifdef HAVE_IPP - unsigned char thresh = 0; - CV_IPP_RUN_FAST(ipp_getThreshVal_Otsu_8u(_src.ptr(), step, size, thresh), thresh); -#else - CV_UNUSED(step); -#endif + if (_mask.empty()) + { + #ifdef HAVE_IPP + unsigned char thresh = 0; + CV_IPP_RUN_FAST(ipp_getThreshVal_Otsu_8u(_src.ptr(), step, size, thresh), thresh); + #else + CV_UNUSED(step); + #endif + } - return getThreshVal_Otsu(_src, size); + if (!_mask.empty()) + return getThreshVal_Otsu(_src, _mask, size); + else + return getThreshVal_Otsu(_src, _mask, size); } static double -getThreshVal_Otsu_16u( const Mat& _src ) +getThreshVal_Otsu_16u( const Mat& _src, const Mat& _mask = cv::Mat() ) { Size size = _src.size(); - if( _src.isContinuous() ) + if( _src.isContinuous() && ( _mask.empty() || _mask.isContinuous() ) ) { size.width *= size.height; size.height = 1; } - return getThreshVal_Otsu(_src, size); + if (!_mask.empty()) + return getThreshVal_Otsu(_src, _mask, size); + else + return getThreshVal_Otsu(_src, _mask, size); } +template static double -getThreshVal_Triangle_8u( const Mat& _src ) +getThreshVal_Triangle_8u( const Mat& _src, const Mat& _mask = cv::Mat() ) { Size size = _src.size(); int step = (int) _src.step; - if( _src.isContinuous() ) + if( _src.isContinuous() && ( _mask.empty() || _mask.isContinuous() ) ) { size.width *= size.height; size.height = 1; @@ -1245,18 +1340,44 @@ getThreshVal_Triangle_8u( const Mat& _src ) for( i = 0; i < size.height; i++ ) { const uchar* src = _src.ptr() + step*i; + const uchar* pMask = nullptr; + if ( useMask ) + pMask = _mask.ptr(i); j = 0; #if CV_ENABLE_UNROLLED for( ; j <= size.width - 4; j += 4 ) { int v0 = src[j], v1 = src[j+1]; - h[v0]++; h_unrolled[0][v1]++; + if ( useMask ) + { + h[v0] += (pMask[j] != 0) ? 1 : 0; + h_unrolled[0][v1] += (pMask[j+1] != 0) ? 1 : 0; + } + else + { + h[v0]++; + h_unrolled[0][v1]++; + } v0 = src[j+2]; v1 = src[j+3]; - h_unrolled[1][v0]++; h_unrolled[2][v1]++; + if ( useMask ) + { + h_unrolled[1][v0] += (pMask[j+2] != 0) ? 1 : 0; + h_unrolled[2][v1] += (pMask[j+3] != 0) ? 1 : 0; + } + else + { + h_unrolled[1][v0]++; + h_unrolled[2][v1]++; + } } #endif for( ; j < size.width; j++ ) - h[src[j]]++; + { + if ( useMask ) + h[src[j]] += (pMask[j] != 0) ? 1 : 0; + else + h[src[j]]++; + } } int left_bound = 0, right_bound = 0, max_ind = 0, max = 0; @@ -1342,10 +1463,11 @@ getThreshVal_Triangle_8u( const Mat& _src ) class ThresholdRunner : public ParallelLoopBody { public: - ThresholdRunner(Mat _src, Mat _dst, double _thresh, double _maxval, int _thresholdType) + ThresholdRunner(Mat _src, Mat _dst, const Mat& _mask, double _thresh, double _maxval, int _thresholdType) { src = _src; dst = _dst; + mask = _mask; thresh = _thresh; maxval = _maxval; @@ -1360,35 +1482,56 @@ public: Mat srcStripe = src.rowRange(row0, row1); Mat dstStripe = dst.rowRange(row0, row1); - CALL_HAL(threshold, cv_hal_threshold, srcStripe.data, srcStripe.step, dstStripe.data, dstStripe.step, - srcStripe.cols, srcStripe.rows, srcStripe.depth(), srcStripe.channels(), - thresh, maxval, thresholdType); + const bool useMask = !mask.empty(); + + if ( !useMask ) + { + CALL_HAL(threshold, cv_hal_threshold, srcStripe.data, srcStripe.step, dstStripe.data, dstStripe.step, + srcStripe.cols, srcStripe.rows, srcStripe.depth(), srcStripe.channels(), + thresh, maxval, thresholdType); + } if (srcStripe.depth() == CV_8U) { - thresh_8u( srcStripe, dstStripe, (uchar)thresh, (uchar)maxval, thresholdType ); + if ( useMask ) + threshGenericWithMask( srcStripe, dstStripe, mask.rowRange(row0, row1), (uchar)thresh, (uchar)maxval, thresholdType ); + else + thresh_8u( srcStripe, dstStripe, (uchar)thresh, (uchar)maxval, thresholdType ); } else if( srcStripe.depth() == CV_16S ) { - thresh_16s( srcStripe, dstStripe, (short)thresh, (short)maxval, thresholdType ); + if ( useMask ) + threshGenericWithMask( srcStripe, dstStripe, mask.rowRange(row0, row1), (short)thresh, (short)maxval, thresholdType ); + else + thresh_16s( srcStripe, dstStripe, (short)thresh, (short)maxval, thresholdType ); } else if( srcStripe.depth() == CV_16U ) { - thresh_16u( srcStripe, dstStripe, (ushort)thresh, (ushort)maxval, thresholdType ); + if ( useMask ) + threshGenericWithMask( srcStripe, dstStripe, mask.rowRange(row0, row1), (ushort)thresh, (ushort)maxval, thresholdType ); + else + thresh_16u( srcStripe, dstStripe, (ushort)thresh, (ushort)maxval, thresholdType ); } else if( srcStripe.depth() == CV_32F ) { - thresh_32f( srcStripe, dstStripe, (float)thresh, (float)maxval, thresholdType ); + if ( useMask ) + threshGenericWithMask( srcStripe, dstStripe, mask.rowRange(row0, row1), (float)thresh, (float)maxval, thresholdType ); + else + thresh_32f( srcStripe, dstStripe, (float)thresh, (float)maxval, thresholdType ); } else if( srcStripe.depth() == CV_64F ) { - thresh_64f(srcStripe, dstStripe, thresh, maxval, thresholdType); + if ( useMask ) + threshGenericWithMask( srcStripe, dstStripe, mask.rowRange(row0, row1), thresh, maxval, thresholdType ); + else + thresh_64f(srcStripe, dstStripe, thresh, maxval, thresholdType); } } private: Mat src; Mat dst; + Mat mask; double thresh; double maxval; @@ -1397,7 +1540,7 @@ private: #ifdef HAVE_OPENCL -static bool ocl_threshold( InputArray _src, OutputArray _dst, double & thresh, double maxval, int thresh_type ) +static bool ocl_threshold( InputArray _src, OutputArray _dst, InputArray _mask, double & thresh, double maxval, int thresh_type ) { int type = _src.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type), kercn = ocl::predictOptimalVectorWidth(_src, _dst), ktype = CV_MAKE_TYPE(depth, kercn); @@ -1416,16 +1559,26 @@ static bool ocl_threshold( InputArray _src, OutputArray _dst, double & thresh, d ocl::Device dev = ocl::Device::getDefault(); int stride_size = dev.isIntel() && (dev.type() & ocl::Device::TYPE_GPU) ? 4 : 1; - ocl::Kernel k("threshold", ocl::imgproc::threshold_oclsrc, - format("-D %s -D T=%s -D T1=%s -D STRIDE_SIZE=%d%s", thresholdMap[thresh_type], - ocl::typeToStr(ktype), ocl::typeToStr(depth), stride_size, - doubleSupport ? " -D DOUBLE_SUPPORT" : "")); + const bool useMask = !_mask.empty(); + + ocl::Kernel k = + !useMask ? + ocl::Kernel("threshold", ocl::imgproc::threshold_oclsrc, + format("-D %s -D T=%s -D T1=%s -D STRIDE_SIZE=%d%s", thresholdMap[thresh_type], + ocl::typeToStr(ktype), ocl::typeToStr(depth), stride_size, + doubleSupport ? " -D DOUBLE_SUPPORT" : "")) : + ocl::Kernel("threshold_mask", ocl::imgproc::threshold_oclsrc, + format("-D %s -D T=%s -D T1=%s -D CN=%d -D STRIDE_SIZE=%d%s", thresholdMap[thresh_type], + ocl::typeToStr(ktype), ocl::typeToStr(depth), cn, stride_size, + doubleSupport ? " -D DOUBLE_SUPPORT" : "")); + if (k.empty()) return false; UMat src = _src.getUMat(); _dst.create(src.size(), type); UMat dst = _dst.getUMat(); + UMat mask = !useMask ? cv::UMat() : _mask.getUMat(); if (depth <= CV_32S) thresh = cvFloor(thresh); @@ -1433,10 +1586,17 @@ static bool ocl_threshold( InputArray _src, OutputArray _dst, double & thresh, d const double min_vals[] = { 0, CHAR_MIN, 0, SHRT_MIN, INT_MIN, -FLT_MAX, -DBL_MAX, 0 }; double min_val = min_vals[depth]; - k.args(ocl::KernelArg::ReadOnlyNoSize(src), ocl::KernelArg::WriteOnly(dst, cn, kercn), - ocl::KernelArg::Constant(Mat(1, 1, depth, Scalar::all(thresh))), - ocl::KernelArg::Constant(Mat(1, 1, depth, Scalar::all(maxval))), - ocl::KernelArg::Constant(Mat(1, 1, depth, Scalar::all(min_val)))); + if (!useMask) + k.args(ocl::KernelArg::ReadOnlyNoSize(src), ocl::KernelArg::WriteOnly(dst, cn, kercn), + ocl::KernelArg::Constant(Mat(1, 1, depth, Scalar::all(thresh))), + ocl::KernelArg::Constant(Mat(1, 1, depth, Scalar::all(maxval))), + ocl::KernelArg::Constant(Mat(1, 1, depth, Scalar::all(min_val)))); + else + k.args(ocl::KernelArg::ReadOnlyNoSize(src), ocl::KernelArg::WriteOnly(dst, cn, kercn), + ocl::KernelArg::ReadOnlyNoSize(mask), + ocl::KernelArg::Constant(Mat(1, 1, depth, Scalar::all(thresh))), + ocl::KernelArg::Constant(Mat(1, 1, depth, Scalar::all(maxval))), + ocl::KernelArg::Constant(Mat(1, 1, depth, Scalar::all(min_val)))); size_t globalsize[2] = { (size_t)dst.cols * cn / kercn, (size_t)dst.rows }; globalsize[1] = (globalsize[1] + stride_size - 1) / stride_size; @@ -1452,7 +1612,7 @@ double cv::threshold( InputArray _src, OutputArray _dst, double thresh, double m CV_INSTRUMENT_REGION(); CV_OCL_RUN_(_src.dims() <= 2 && _dst.isUMat(), - ocl_threshold(_src, _dst, thresh, maxval, type), thresh) + ocl_threshold(_src, _dst, cv::noArray(), thresh, maxval, type), thresh) const bool isDisabled = ((type & THRESH_DRYRUN) != 0); type &= ~THRESH_DRYRUN; @@ -1481,7 +1641,7 @@ double cv::threshold( InputArray _src, OutputArray _dst, double thresh, double m else if( automatic_thresh == cv::THRESH_TRIANGLE ) { CV_Assert( src.type() == CV_8UC1 ); - thresh = getThreshVal_Triangle_8u( src ); + thresh = getThreshVal_Triangle_8u( src ); } if( src.depth() == CV_8U ) @@ -1587,7 +1747,153 @@ double cv::threshold( InputArray _src, OutputArray _dst, double thresh, double m return thresh; parallel_for_(Range(0, dst.rows), - ThresholdRunner(src, dst, thresh, maxval, type), + ThresholdRunner(src, dst, cv::Mat(), thresh, maxval, type), + dst.total()/(double)(1<<16)); + return thresh; +} + +double cv::thresholdWithMask( InputArray _src, InputOutputArray _dst, InputArray _mask, double thresh, double maxval, int type ) +{ + CV_INSTRUMENT_REGION(); + CV_Assert( _mask.empty() || ( ( _dst.size() == _src.size() ) && ( _dst.type() == _src.type() ) ) ); + if ( _mask.empty() ) + return cv::threshold(_src, _dst, thresh, maxval, type); + + CV_OCL_RUN_(_src.dims() <= 2 && _dst.isUMat(), + ocl_threshold(_src, _dst, _mask, thresh, maxval, type), thresh) + + const bool isDisabled = ((type & THRESH_DRYRUN) != 0); + type &= ~THRESH_DRYRUN; + + Mat src = _src.getMat(); + Mat mask = _mask.getMat(); + + if (!isDisabled) + _dst.create( src.size(), src.type() ); + Mat dst = isDisabled ? cv::Mat() : _dst.getMat(); + + int automatic_thresh = (type & ~cv::THRESH_MASK); + type &= THRESH_MASK; + + CV_Assert( automatic_thresh != (cv::THRESH_OTSU | cv::THRESH_TRIANGLE) ); + if( automatic_thresh == cv::THRESH_OTSU ) + { + int src_type = src.type(); + CV_CheckType(src_type, src_type == CV_8UC1 || src_type == CV_16UC1, "THRESH_OTSU mode"); + + thresh = src.type() == CV_8UC1 ? getThreshVal_Otsu_8u( src, mask ) + : getThreshVal_Otsu_16u( src, mask ); + } + else if( automatic_thresh == cv::THRESH_TRIANGLE ) + { + CV_Assert( src.type() == CV_8UC1 ); + thresh = getThreshVal_Triangle_8u( src, mask ); + } + + if( src.depth() == CV_8U ) + { + int ithresh = cvFloor(thresh); + thresh = ithresh; + if (isDisabled) + return thresh; + + int imaxval = cvRound(maxval); + if( type == THRESH_TRUNC ) + imaxval = ithresh; + imaxval = saturate_cast(imaxval); + + if( ithresh < 0 || ithresh >= 255 ) + { + if( type == THRESH_BINARY || type == THRESH_BINARY_INV || + ((type == THRESH_TRUNC || type == THRESH_TOZERO_INV) && ithresh < 0) || + (type == THRESH_TOZERO && ithresh >= 255) ) + { + int v = type == THRESH_BINARY ? (ithresh >= 255 ? 0 : imaxval) : + type == THRESH_BINARY_INV ? (ithresh >= 255 ? imaxval : 0) : + /*type == THRESH_TRUNC ? imaxval :*/ 0; + dst.setTo(v); + } + else + src.copyTo(dst); + return thresh; + } + + thresh = ithresh; + maxval = imaxval; + } + else if( src.depth() == CV_16S ) + { + int ithresh = cvFloor(thresh); + thresh = ithresh; + if (isDisabled) + return thresh; + + int imaxval = cvRound(maxval); + if( type == THRESH_TRUNC ) + imaxval = ithresh; + imaxval = saturate_cast(imaxval); + + if( ithresh < SHRT_MIN || ithresh >= SHRT_MAX ) + { + if( type == THRESH_BINARY || type == THRESH_BINARY_INV || + ((type == THRESH_TRUNC || type == THRESH_TOZERO_INV) && ithresh < SHRT_MIN) || + (type == THRESH_TOZERO && ithresh >= SHRT_MAX) ) + { + int v = type == THRESH_BINARY ? (ithresh >= SHRT_MAX ? 0 : imaxval) : + type == THRESH_BINARY_INV ? (ithresh >= SHRT_MAX ? imaxval : 0) : + /*type == THRESH_TRUNC ? imaxval :*/ 0; + dst.setTo(v); + } + else + src.copyTo(dst); + return thresh; + } + thresh = ithresh; + maxval = imaxval; + } + else if (src.depth() == CV_16U ) + { + int ithresh = cvFloor(thresh); + thresh = ithresh; + if (isDisabled) + return thresh; + + int imaxval = cvRound(maxval); + if (type == THRESH_TRUNC) + imaxval = ithresh; + imaxval = saturate_cast(imaxval); + + int ushrt_min = 0; + if (ithresh < ushrt_min || ithresh >= (int)USHRT_MAX) + { + if (type == THRESH_BINARY || type == THRESH_BINARY_INV || + ((type == THRESH_TRUNC || type == THRESH_TOZERO_INV) && ithresh < ushrt_min) || + (type == THRESH_TOZERO && ithresh >= (int)USHRT_MAX)) + { + int v = type == THRESH_BINARY ? (ithresh >= (int)USHRT_MAX ? 0 : imaxval) : + type == THRESH_BINARY_INV ? (ithresh >= (int)USHRT_MAX ? imaxval : 0) : + /*type == THRESH_TRUNC ? imaxval :*/ 0; + dst.setTo(v); + } + else + src.copyTo(dst); + return thresh; + } + thresh = ithresh; + maxval = imaxval; + } + else if( src.depth() == CV_32F ) + ; + else if( src.depth() == CV_64F ) + ; + else + CV_Error( cv::Error::StsUnsupportedFormat, "" ); + + if (isDisabled) + return thresh; + + parallel_for_(Range(0, dst.rows), + ThresholdRunner(src, dst, mask, thresh, maxval, type), dst.total()/(double)(1<<16)); return thresh; } diff --git a/modules/imgproc/test/ocl/test_imgproc.cpp b/modules/imgproc/test/ocl/test_imgproc.cpp index d72000884a..8934be9d9f 100644 --- a/modules/imgproc/test/ocl/test_imgproc.cpp +++ b/modules/imgproc/test/ocl/test_imgproc.cpp @@ -420,6 +420,49 @@ OCL_TEST_P(Threshold_Dryrun, Mat) } } +struct Threshold_masked : + public ImgprocTestBase +{ + int thresholdType; + + virtual void SetUp() + { + type = GET_PARAM(0); + thresholdType = GET_PARAM(2); + useRoi = GET_PARAM(3); + } +}; + +OCL_TEST_P(Threshold_masked, Mat) +{ + for (int j = 0; j < test_loop_times; j++) + { + random_roi(); + + double maxVal = randomDouble(20.0, 127.0); + double thresh = randomDouble(0.0, maxVal); + + const int _thresholdType = thresholdType; + + cv::Size sz = src_roi.size(); + cv::Mat mask_roi = cv::Mat::zeros(sz, CV_8UC1); + cv::RotatedRect ellipseRect((cv::Point2f)cv::Point(sz.width/2, sz.height/2), (cv::Size2f)sz, 0); + cv::ellipse(mask_roi, ellipseRect, cv::Scalar::all(255), cv::FILLED);//for very different mask alignments + + cv::UMat umask_roi(mask_roi.size(), mask_roi.type()); + mask_roi.copyTo(umask_roi.getMat(cv::AccessFlag::ACCESS_WRITE)); + + src_roi.copyTo(dst_roi); + usrc_roi.copyTo(udst_roi); + + OCL_OFF(cv::thresholdWithMask(src_roi, dst_roi, mask_roi, thresh, maxVal, _thresholdType)); + OCL_ON(cv::thresholdWithMask(usrc_roi, udst_roi, umask_roi, thresh, maxVal, _thresholdType)); + + OCL_EXPECT_MATS_NEAR(dst, 0); + } +} + + /////////////////////////////////////////// CLAHE ////////////////////////////////////////////////// PARAM_TEST_CASE(CLAHETest, Size, double, bool) @@ -527,6 +570,14 @@ OCL_INSTANTIATE_TEST_CASE_P(Imgproc, Threshold_Dryrun, Combine( ThreshOp(THRESH_TOZERO), ThreshOp(THRESH_TOZERO_INV)), Bool())); +OCL_INSTANTIATE_TEST_CASE_P(Imgproc, Threshold_masked, Combine( + Values(CV_8UC1, CV_8UC3, CV_16SC1, CV_16SC3, CV_16UC1, CV_16UC3, CV_32FC1, CV_32FC3, CV_64FC1, CV_64FC3), + Values(0), + Values(ThreshOp(THRESH_BINARY), + ThreshOp(THRESH_BINARY_INV), ThreshOp(THRESH_TRUNC), + ThreshOp(THRESH_TOZERO), ThreshOp(THRESH_TOZERO_INV)), + Bool())); + OCL_INSTANTIATE_TEST_CASE_P(Imgproc, CLAHETest, Combine( Values(Size(4, 4), Size(32, 8), Size(8, 64)), Values(0.0, 10.0, 62.0, 300.0), diff --git a/modules/imgproc/test/test_thresh.cpp b/modules/imgproc/test/test_thresh.cpp index 90fa5cb9ff..da9d6efd5b 100644 --- a/modules/imgproc/test/test_thresh.cpp +++ b/modules/imgproc/test/test_thresh.cpp @@ -520,6 +520,98 @@ TEST(Imgproc_Threshold, threshold_dryrun) } } +typedef tuple < bool, int, int, int, int > Imgproc_Threshold_Masked_Params_t; + +typedef testing::TestWithParam< Imgproc_Threshold_Masked_Params_t > Imgproc_Threshold_Masked_Fixed; + +TEST_P(Imgproc_Threshold_Masked_Fixed, threshold_mask_fixed) +{ + bool useROI = get<0>(GetParam()); + int depth = get<1>(GetParam()); + int cn = get<2>(GetParam()); + int threshType = get<3>(GetParam()); + int threshFlag = get<4>(GetParam()); + + const int _threshType = threshType | threshFlag; + Size sz(127, 127); + Size wrapperSize = useROI ? Size(sz.width+4, sz.height+4) : sz; + Mat wrapper(wrapperSize, CV_MAKETYPE(depth, cn)); + Mat input = useROI ? Mat(wrapper, Rect(Point(), sz)) : wrapper; + cv::randu(input, cv::Scalar::all(0), cv::Scalar::all(255)); + + Mat mask = cv::Mat::zeros(sz, CV_8UC1); + cv::RotatedRect ellipseRect((cv::Point2f)cv::Point(sz.width/2, sz.height/2), (cv::Size2f)sz, 0); + cv::ellipse(mask, ellipseRect, cv::Scalar::all(255), cv::FILLED);//for very different mask alignments + + Mat output_with_mask = cv::Mat::zeros(sz, input.type()); + cv::thresholdWithMask(input, output_with_mask, mask, 127, 255, _threshType); + + cv::bitwise_not(mask, mask); + input.copyTo(output_with_mask, mask); + + Mat output_without_mask; + cv::threshold(input, output_without_mask, 127, 255, _threshType); + input.copyTo(output_without_mask, mask); + + EXPECT_MAT_NEAR(output_with_mask, output_without_mask, 0); +} + +INSTANTIATE_TEST_CASE_P(/*nothing*/, Imgproc_Threshold_Masked_Fixed, + testing::Combine( + testing::Values(false, true),//use roi + testing::Values(CV_8U, CV_16U, CV_16S, CV_32F, CV_64F),//depth + testing::Values(1, 3),//channels + testing::Values(THRESH_BINARY, THRESH_BINARY_INV, THRESH_TRUNC, THRESH_TOZERO, THRESH_TOZERO_INV),// threshTypes + testing::Values(0) + ) +); + +typedef testing::TestWithParam< Imgproc_Threshold_Masked_Params_t > Imgproc_Threshold_Masked_Auto; + +TEST_P(Imgproc_Threshold_Masked_Auto, threshold_mask_auto) +{ + bool useROI = get<0>(GetParam()); + int depth = get<1>(GetParam()); + int cn = get<2>(GetParam()); + int threshType = get<3>(GetParam()); + int threshFlag = get<4>(GetParam()); + + if (threshFlag == THRESH_TRIANGLE && depth != CV_8U) + throw SkipTestException("THRESH_TRIANGLE option supports CV_8UC1 input only"); + + const int _threshType = threshType | threshFlag; + Size sz(127, 127); + Size wrapperSize = useROI ? Size(sz.width+4, sz.height+4) : sz; + Mat wrapper(wrapperSize, CV_MAKETYPE(depth, cn)); + Mat input = useROI ? Mat(wrapper, Rect(Point(), sz)) : wrapper; + cv::randu(input, cv::Scalar::all(0), cv::Scalar::all(255)); + + //for OTSU and TRIANGLE, we use a rectangular mask that can be just cropped + //in order to compute the threshold of the non-masked version + Mat mask = cv::Mat::zeros(sz, CV_8UC1); + cv::Rect roiRect(sz.width/4, sz.height/4, sz.width/2, sz.height/2); + cv::rectangle(mask, roiRect, cv::Scalar::all(255), cv::FILLED); + + Mat output_with_mask = cv::Mat::zeros(sz, input.type()); + const double autoThreshWithMask = cv::thresholdWithMask(input, output_with_mask, mask, 127, 255, _threshType); + output_with_mask = Mat(output_with_mask, roiRect); + + Mat output_without_mask; + const double autoThresholdWithoutMask = cv::threshold(Mat(input, roiRect), output_without_mask, 127, 255, _threshType); + + ASSERT_EQ(autoThreshWithMask, autoThresholdWithoutMask); + EXPECT_MAT_NEAR(output_with_mask, output_without_mask, 0); +} + +INSTANTIATE_TEST_CASE_P(/*nothing*/, Imgproc_Threshold_Masked_Auto, + testing::Combine( + testing::Values(false, true),//use roi + testing::Values(CV_8U, CV_16U),//depth + testing::Values(1),//channels + testing::Values(THRESH_BINARY, THRESH_BINARY_INV, THRESH_TRUNC, THRESH_TOZERO, THRESH_TOZERO_INV),// threshTypes + testing::Values(THRESH_OTSU, THRESH_TRIANGLE) + ) +); TEST(Imgproc_Threshold, regression_THRESH_TOZERO_IPP_16085) { From d83df66ff074a46ab1940df059f88ec08c77d7a2 Mon Sep 17 00:00:00 2001 From: Pierre Chatelier Date: Wed, 12 Mar 2025 16:00:01 +0100 Subject: [PATCH 09/94] Merge pull request #26834 from chacha21:findContours_speedup Find contours speedup #26834 It is an attempt, as suggested by #26775, to restore lost speed when migrating `findContours()` implementation from C to C++ The patch adds an "Arena" (a pool) of pre-allocated memory so that contours points (and TreeNodes) can be picked from the Arena. The code of `findContours()` is mostly unchanged, the arena usage being implicit through a utility class Arena::Item that provides C++ overloaded operators and construct/destruct logic. As mentioned in #26775, the contour points are allocated and released in order, and can be represented by ranges of indices in their arena. No range subset will be released and drill a hole, that's why the internal representation as a range of indices makes sense. The TreeNodes use another Arena class that does not comply to that range logic. Currently, there is a significant improvement of the run-time on the test mentioned in #26775, but it is still far from the `findContours_legacy()` performance. - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [X] The PR is proposed to the proper branch - [X] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- modules/imgproc/src/contours_approx.cpp | 22 ++-- modules/imgproc/src/contours_blockstorage.hpp | 122 ++++++++++++++++++ modules/imgproc/src/contours_common.cpp | 19 ++- modules/imgproc/src/contours_common.hpp | 121 ++++++++++++++--- modules/imgproc/src/contours_link.cpp | 11 +- modules/imgproc/src/contours_new.cpp | 27 ++-- 6 files changed, 271 insertions(+), 51 deletions(-) create mode 100644 modules/imgproc/src/contours_blockstorage.hpp diff --git a/modules/imgproc/src/contours_approx.cpp b/modules/imgproc/src/contours_approx.cpp index 176c490468..bba45f48e7 100644 --- a/modules/imgproc/src/contours_approx.cpp +++ b/modules/imgproc/src/contours_approx.cpp @@ -31,7 +31,7 @@ static const Point chainCodeDeltas[8] = // Restores all the digital curve points from the chain code. // Removes the points (from the resultant polygon) // that have zero 1-curvature -static vector pass_0(const vector& chain, Point pt, bool isApprox, bool isFull) +static vector pass_0(const ContourCodesStorage& chain, Point pt, bool isApprox, bool isFull) { vector res; const size_t len = chain.size(); @@ -52,17 +52,14 @@ static vector pass_0(const vector& chain, Point pt, bool isAp return res; } -static vector gatherPoints(const vector& ares) +static void gatherPoints(const vector& ares, ContourPointsStorage& output) { - vector res; - res.reserve(ares.size() / 2); + output.clear(); for (const ApproxItem& item : ares) { - if (item.removed) - continue; - res.push_back(item.pt); + if (!item.removed) + output.push_back(item.pt); } - return res; } static size_t calc_support(const vector& ares, size_t i) @@ -273,11 +270,14 @@ static void pass_cleanup(vector& ares, size_t start_idx) } // namespace -vector cv::approximateChainTC89(vector chain, const Point& origin, const int method) +void cv::approximateChainTC89(const ContourCodesStorage& chain, const Point& origin, const int method, + ContourPointsStorage& output) { if (chain.size() == 0) { - return vector({origin}); + output.clear(); + output.push_back(origin); + return; } const bool isApprox = method == CHAIN_APPROX_TC89_L1 || method == CHAIN_APPROX_TC89_KCOS; @@ -349,5 +349,5 @@ vector cv::approximateChainTC89(vector chain, const Point& origin, } } - return gatherPoints(ares); + gatherPoints(ares, output); } diff --git a/modules/imgproc/src/contours_blockstorage.hpp b/modules/imgproc/src/contours_blockstorage.hpp new file mode 100644 index 0000000000..7b7c55a72d --- /dev/null +++ b/modules/imgproc/src/contours_blockstorage.hpp @@ -0,0 +1,122 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html + +#ifndef OPENCV_CONTOURS_BLOCKSTORAGE_HPP +#define OPENCV_CONTOURS_BLOCKSTORAGE_HPP + +#include "precomp.hpp" + +#include + +namespace cv { + +// BLOCK_SIZE_ELEM - number of elements in a block +// STATIC_CAPACITY_BYTES - static memory in bytes for preallocated blocks +template +class BlockStorage { + public: + using value_type = T; + typedef struct {value_type data[BLOCK_SIZE_ELEM];} block_type; + + BlockStorage() + { + const size_t minDynamicBlocks = !staticBlocksCount ? 1 : 0; + for(size_t i = 0 ; i operator*(void) const {return get();} + std::pair get(void) const { + const block_type& cur_block = + (blockIndex < owner->staticBlocksCount) ? owner->staticBlocks[blockIndex] : + *owner->dynamicBlocks[blockIndex-owner->staticBlocksCount]; + const value_type* rangeStart = cur_block.data+offset; + const size_t rangeLength = std::min(remaining, BLOCK_SIZE_ELEM-offset); + return std::make_pair(rangeStart, rangeLength); + } + RangeIterator& operator++() { + std::pair range = get(); + remaining -= range.second; + offset = 0; + ++blockIndex; + return *this; + } + }; + RangeIterator getRangeIterator(size_t first, size_t last) const { + return RangeIterator(this, first, last); + } + private: + std::array staticBlocks; + const size_t staticBlocksCount = STATIC_CAPACITY_BYTES/(BLOCK_SIZE_ELEM*sizeof(value_type)); + std::vector dynamicBlocks; + size_t sz = 0; +}; + +} // namespace cv + +#endif // OPENCV_CONTOURS_BLOCKSTORAGE_HPP diff --git a/modules/imgproc/src/contours_common.cpp b/modules/imgproc/src/contours_common.cpp index a8cb12c1a2..8fb1459fc9 100644 --- a/modules/imgproc/src/contours_common.cpp +++ b/modules/imgproc/src/contours_common.cpp @@ -22,12 +22,11 @@ void cv::contourTreeToResults(CTree& tree, return; } - // mapping for indexes (original -> resulting) - map index_mapping; - index_mapping[-1] = -1; - index_mapping[0] = -1; - CV_Assert(tree.size() < (size_t)numeric_limits::max()); + // mapping for indexes (original -> resulting) + // -1 - based indexing + vector index_mapping(tree.size() + 1, -1); + const int total = (int)tree.size() - 1; _contours.create(total, 1, 0, -1, true); { @@ -39,7 +38,7 @@ void cv::contourTreeToResults(CTree& tree, CV_Assert(elem.self() != -1); if (elem.self() == 0) continue; - index_mapping[elem.self()] = i; + index_mapping.at(elem.self() + 1) = i; CV_Assert(elem.body.size() < (size_t)numeric_limits::max()); const int sz = (int)elem.body.size(); _contours.create(sz, 1, res_type, i, true); @@ -65,10 +64,10 @@ void cv::contourTreeToResults(CTree& tree, if (elem.self() == 0) continue; Vec4i& h_vec = h_mat.at(i); - h_vec = Vec4i(index_mapping.at(elem.next), - index_mapping.at(elem.prev), - index_mapping.at(elem.first_child), - index_mapping.at(elem.parent)); + h_vec = Vec4i(index_mapping.at(elem.next + 1), + index_mapping.at(elem.prev + 1), + index_mapping.at(elem.first_child + 1), + index_mapping.at(elem.parent + 1)); ++i; } } diff --git a/modules/imgproc/src/contours_common.hpp b/modules/imgproc/src/contours_common.hpp index b22c5cfd0b..e02945fd01 100644 --- a/modules/imgproc/src/contours_common.hpp +++ b/modules/imgproc/src/contours_common.hpp @@ -6,7 +6,8 @@ #define OPENCV_CONTOURS_COMMON_HPP #include "precomp.hpp" -#include + +#include "contours_blockstorage.hpp" namespace cv { @@ -45,11 +46,15 @@ public: T body; public: - TreeNode(int self) : - self_(self), parent(-1), first_child(-1), prev(-1), next(-1), ctable_next(-1) + TreeNode(int self, T&& body_) : + self_(self), parent(-1), first_child(-1), prev(-1), next(-1), ctable_next(-1), body(std::move(body_)) { CV_Assert(self >= 0); } + TreeNode(const TreeNode&) = delete; + TreeNode(TreeNode&&) noexcept = default; + TreeNode& operator=(const TreeNode&) = delete; + TreeNode& operator=(TreeNode&&) noexcept = default; int self() const { return self_; @@ -59,15 +64,22 @@ public: template class Tree { +public: + Tree() {} + Tree(const Tree&) = delete; + Tree(Tree&&) = delete; + Tree& operator=(const Tree&) = delete; + Tree& operator=(Tree&&) = delete; + ~Tree() = default; private: std::vector> nodes; public: - TreeNode& newElem() + TreeNode& newElem(T && body_) { const size_t idx = nodes.size(); CV_DbgAssert(idx < (size_t)std::numeric_limits::max()); - nodes.push_back(TreeNode((int)idx)); + nodes.emplace_back(std::move(TreeNode((int)idx, std::move(body_)))); return nodes[idx]; } TreeNode& elem(int idx) @@ -101,7 +113,7 @@ public: child.parent = prev_item.parent; if (prev_item.next != -1) { - nodes[prev_item.next].prev = idx; + ((TreeNode&)nodes[prev_item.next]).prev = idx; child.next = prev_item.next; } child.prev = prev; @@ -159,23 +171,80 @@ public: } private: - std::stack levels; Tree& tree; + std::stack levels; }; //============================================================================== +template +class ContourDataStorage +{ +public: + typedef T data_storage_t; + typedef BlockStorage storage_t; +public: + ContourDataStorage(void) = delete; + ContourDataStorage(storage_t* _storage):storage(_storage) {} + ContourDataStorage(const ContourDataStorage&) = delete; + ContourDataStorage(ContourDataStorage&&) noexcept = default; + ~ContourDataStorage() = default; + ContourDataStorage& operator=(const ContourDataStorage&) = delete; + ContourDataStorage& operator=(ContourDataStorage&&) noexcept = default; +public: + typename storage_t::RangeIterator getRangeIterator(void) const {return storage->getRangeIterator(first, last);} +public: + bool empty(void) const {return first == last;} + size_t size(void) const {return last - first;} +public: + void clear(void) {first = last;} + bool resize(size_t newSize) + { + bool ok = (newSize <= size()); + if (ok) + last = first+newSize; + return ok; + } + void push_back(const data_storage_t& value) + { + if (empty()) + { + first = storage->size(); + } + storage->push_back(value); + last = storage->size(); + } + const data_storage_t& at(size_t index) const {return storage->at(first+index);} + data_storage_t& at(size_t index) {return storage->at(first+index);} + const data_storage_t& operator[](size_t index) const {return at(index);} + data_storage_t& operator[](size_t index) {return at(index);} +private: + storage_t* storage = nullptr; + size_t first = 0; + size_t last = 0; +}; + +typedef ContourDataStorage ContourPointsStorage; +typedef ContourDataStorage ContourCodesStorage; + class Contour { public: + ContourPointsStorage pts; cv::Rect brect; cv::Point origin; - std::vector pts; - std::vector codes; - bool isHole; - bool isChain; + ContourCodesStorage codes; + bool isHole = false; + bool isChain = false; - Contour() : isHole(false), isChain(false) {} + explicit Contour(ContourPointsStorage::storage_t* pointStorage_, + ContourCodesStorage::storage_t* codesStorage_) + :pts(pointStorage_),codes(codesStorage_) {} + Contour(const Contour&) = delete; + Contour(Contour&& other) noexcept = default; + Contour& operator=(const Contour&) = delete; + Contour& operator=(Contour&& other) noexcept = default; + ~Contour() = default; void updateBoundingRect() {} bool isEmpty() const { @@ -185,17 +254,37 @@ public: { return isChain ? codes.size() : pts.size(); } + void addPoint(const Point& pt) + { + pts.push_back(pt); + } void copyTo(void* data) const { // NOTE: Mat::copyTo doesn't work because it creates new Mat object // instead of reusing existing vector data if (isChain) { - memcpy(data, &codes[0], codes.size() * sizeof(codes[0])); + /*memcpy(data, codes.data(), codes.size() * sizeof(typename decltype(codes)::value_type));*/ + schar* dst = reinterpret_cast(data); + for(auto rangeIterator = codes.getRangeIterator() ; !rangeIterator.done() ; ++rangeIterator) + { + const auto range = *rangeIterator; + memcpy(dst, range.first, range.second*sizeof(schar)); + dst += range.second; + } } else { - memcpy(data, &pts[0], pts.size() * sizeof(pts[0])); + /*for (size_t i = 0, count = pts.size() ; i < count ; ++i) + ((Point*)data)[i] = pts.at(i); + */ + cv::Point* dst = reinterpret_cast(data); + for(auto rangeIterator = pts.getRangeIterator() ; !rangeIterator.done() ; ++rangeIterator) + { + const auto range = *rangeIterator; + memcpy(dst, range.first, range.second*sizeof(cv::Point)); + dst += range.second; + } } } }; @@ -211,8 +300,8 @@ void contourTreeToResults(CTree& tree, cv::OutputArray& _hierarchy); -std::vector - approximateChainTC89(std::vector chain, const Point& origin, const int method); +void approximateChainTC89(const ContourCodesStorage& chain, const Point& origin, const int method, + ContourPointsStorage& output); } // namespace cv diff --git a/modules/imgproc/src/contours_link.cpp b/modules/imgproc/src/contours_link.cpp index 8df88fc123..667db1be30 100644 --- a/modules/imgproc/src/contours_link.cpp +++ b/modules/imgproc/src/contours_link.cpp @@ -90,10 +90,13 @@ public: vector ext_rns; vector int_rns; + ContourPointsStorage::storage_t pointsStorage; + ContourCodesStorage::storage_t codesStorage; + public: - LinkRunner() + LinkRunner(void) { - tree.newElem(); + tree.newElem(Contour(&pointsStorage, &codesStorage)); rns.reserve(100); } void process(Mat& image); @@ -117,12 +120,12 @@ void LinkRunner::convertLinks(int& first, int& prev, bool isHole) if (rns[cur].link == -1) continue; - CNode& node = tree.newElem(); + CNode& node = tree.newElem(Contour(&pointsStorage, &codesStorage)); node.body.isHole = isHole; do { - node.body.pts.push_back(rns[cur].pt); + node.body.addPoint(rns[cur].pt); int p_temp = cur; cur = rns[cur].link; rns[p_temp].link = -1; diff --git a/modules/imgproc/src/contours_new.cpp b/modules/imgproc/src/contours_new.cpp index 9f8c716fbb..b2f67e13b1 100644 --- a/modules/imgproc/src/contours_new.cpp +++ b/modules/imgproc/src/contours_new.cpp @@ -197,7 +197,7 @@ static void icvFetchContourEx(Mat& image, Trait::setRightFlag(i0, i0, nbd); if (!res_contour.isChain) { - res_contour.pts.push_back(pt); + res_contour.addPoint(pt); } } else @@ -236,7 +236,7 @@ static void icvFetchContourEx(Mat& image, } else if (s != prev_s || isDirect) { - res_contour.pts.push_back(pt); + res_contour.addPoint(pt); } if (s != prev_s) @@ -281,6 +281,9 @@ static void icvFetchContourEx(Mat& image, // It supports both hierarchical and plane variants of Suzuki algorithm. struct ContourScanner_ { + ContourPointsStorage::storage_t& pointsStorage; + ContourCodesStorage::storage_t& codesStorage; + Mat image; Point offset; // ROI offset: coordinates, added to each contour point Point pt; // current scanner position @@ -293,7 +296,9 @@ struct ContourScanner_ array ctable; public: - ContourScanner_() {} + ContourScanner_(ContourPointsStorage::storage_t& _pointsStorage, + ContourCodesStorage::storage_t& _codesStorage) + :pointsStorage(_pointsStorage),codesStorage(_codesStorage) {} ~ContourScanner_() {} inline bool isInt() const { @@ -310,13 +315,13 @@ public: int findNextX(int x, int y, int& prev, int& p); bool findNext(); - static shared_ptr create(Mat img, int mode, int method, Point offset); + static shared_ptr create(ContourPointsStorage::storage_t& pointsStorage, ContourCodesStorage::storage_t& codesStorage, Mat img, int mode, int method, Point offset); }; // class ContourScanner_ typedef shared_ptr ContourScanner; -shared_ptr ContourScanner_::create(Mat img, int mode, int method, Point offset) +shared_ptr ContourScanner_::create(ContourPointsStorage::storage_t& pointsStorage, ContourCodesStorage::storage_t& codesStorage, Mat img, int mode, int method, Point offset) { if (mode == RETR_CCOMP && img.type() == CV_32SC1) mode = RETR_FLOODFILL; @@ -342,14 +347,14 @@ shared_ptr ContourScanner_::create(Mat img, int mode, int metho Size size = img.size(); CV_Assert(size.height >= 1); - shared_ptr scanner = make_shared(); + shared_ptr scanner = make_shared(pointsStorage, codesStorage); scanner->image = img; scanner->mode = mode; scanner->offset = offset; scanner->pt = Point(1, 1); scanner->lnbd = Point(0, 1); scanner->nbd = 2; - CNode& root = scanner->tree.newElem(); + CNode& root = scanner->tree.newElem(Contour(&scanner->pointsStorage, &scanner->codesStorage)); CV_Assert(root.self() == 0); root.body.isHole = true; root.body.brect = Rect(Point(0, 0), size); @@ -367,7 +372,7 @@ CNode& ContourScanner_::makeContour(schar& nbd_, const bool is_hole, const int x const Point start_pt(x - (is_hole ? 1 : 0), y); - CNode& res = tree.newElem(); + CNode& res = tree.newElem(Contour(&pointsStorage, &codesStorage)); res.body.isHole = is_hole; res.body.isChain = isChain; res.body.origin = start_pt + offset; @@ -403,7 +408,7 @@ CNode& ContourScanner_::makeContour(schar& nbd_, const bool is_hole, const int x if (this->approx_method1 != this->approx_method2) { CV_Assert(res.body.isChain); - res.body.pts = approximateChainTC89(res.body.codes, prev_origin, this->approx_method2); + approximateChainTC89(res.body.codes, prev_origin, this->approx_method2, res.body.pts); res.body.isChain = false; } return res; @@ -674,7 +679,9 @@ void cv::findContours(InputArray _image, threshold(image, image, 0, 1, THRESH_BINARY); // find contours - ContourScanner scanner = ContourScanner_::create(image, mode, method, offset + Point(-1, -1)); + ContourPointsStorage::storage_t pointsStorage; + ContourCodesStorage::storage_t codesStorage; + ContourScanner scanner = ContourScanner_::create(pointsStorage, codesStorage, image, mode, method, offset + Point(-1, -1)); while (scanner->findNext()) { } From 7481cb50b536e2e35afc3583adf309b56313059c Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov <2536374+asmorkalov@users.noreply.github.com> Date: Wed, 12 Mar 2025 18:10:06 +0300 Subject: [PATCH 10/94] Merge pull request #27013 from asmorkalov:as/imencode_animation Test for in-memory animation encoding and decoding #27013 Tests for https://github.com/opencv/opencv/pull/26964 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [ ] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- .../imgcodecs/include/opencv2/imgcodecs.hpp | 33 ++++ modules/imgcodecs/src/loadsave.cpp | 142 ++++++++++++++++++ modules/imgcodecs/test/test_animation.cpp | 48 ++++++ 3 files changed, 223 insertions(+) diff --git a/modules/imgcodecs/include/opencv2/imgcodecs.hpp b/modules/imgcodecs/include/opencv2/imgcodecs.hpp index 458e68483e..5bf850b69a 100644 --- a/modules/imgcodecs/include/opencv2/imgcodecs.hpp +++ b/modules/imgcodecs/include/opencv2/imgcodecs.hpp @@ -388,6 +388,19 @@ The function imreadanimation loads frames from an animated image file (e.g., GIF */ CV_EXPORTS_W bool imreadanimation(const String& filename, CV_OUT Animation& animation, int start = 0, int count = INT16_MAX); +/** @brief Loads frames from an animated image buffer into an Animation structure. + +The function imdecodeanimation loads frames from an animated image buffer (e.g., GIF, AVIF, APNG, WEBP) into the provided Animation struct. + +@param buf A reference to an InputArray containing the image buffer. +@param animation A reference to an Animation structure where the loaded frames will be stored. It should be initialized before the function is called. +@param start The index of the first frame to load. This is optional and defaults to 0. +@param count The number of frames to load. This is optional and defaults to 32767. + +@return Returns true if the buffer was successfully loaded and frames were extracted; returns false otherwise. +*/ +CV_EXPORTS_W bool imdecodeanimation(InputArray buf, CV_OUT Animation& animation, int start = 0, int count = INT16_MAX); + /** @brief Saves an Animation to a specified file. The function imwriteanimation saves the provided Animation data to the specified file in an animated format. @@ -402,6 +415,26 @@ These parameters are used to specify additional options for the encoding process */ CV_EXPORTS_W bool imwriteanimation(const String& filename, const Animation& animation, const std::vector& params = std::vector()); +/** @brief Encodes an Animation to a memory buffer. + +The function imencodeanimation encodes the provided Animation data into a memory +buffer in an animated format. Supported formats depend on the implementation and +may include formats like GIF, AVIF, APNG, or WEBP. + +@param ext The file extension that determines the format of the encoded data. +@param animation A constant reference to an Animation struct containing the +frames and metadata to be encoded. +@param buf A reference to a vector of unsigned chars where the encoded data will +be stored. +@param params Optional format-specific parameters encoded as pairs (paramId_1, +paramValue_1, paramId_2, paramValue_2, ...). These parameters are used to +specify additional options for the encoding process. Refer to `cv::ImwriteFlags` +for details on possible parameters. + +@return Returns true if the animation was successfully encoded; returns false otherwise. +*/ +CV_EXPORTS_W bool imencodeanimation(const String& ext, const Animation& animation, CV_OUT std::vector& buf, const std::vector& params = std::vector()); + /** @brief Returns the number of images inside the given file The function imcount returns the number of pages in a multi-page image (e.g. TIFF), the number of frames in an animation (e.g. AVIF), and 1 otherwise. diff --git a/modules/imgcodecs/src/loadsave.cpp b/modules/imgcodecs/src/loadsave.cpp index 42693effd9..0bf90f2aa2 100644 --- a/modules/imgcodecs/src/loadsave.cpp +++ b/modules/imgcodecs/src/loadsave.cpp @@ -811,6 +811,116 @@ bool imreadanimation(const String& filename, CV_OUT Animation& animation, int st return imreadanimation_(filename, IMREAD_UNCHANGED, start, count, animation); } +static bool imdecodeanimation_(InputArray buf, int flags, int start, int count, Animation& animation) +{ + bool success = false; + if (start < 0) { + start = 0; + } + if (count < 0) { + count = INT16_MAX; + } + + /// Search for the relevant decoder to handle the imagery + ImageDecoder decoder; + decoder = findDecoder(buf.getMat()); + + /// if no decoder was found, return false. + if (!decoder) { + CV_LOG_WARNING(NULL, "Decoder for buffer not found!\n"); + return false; + } + + /// set the filename in the driver + decoder->setSource(buf.getMat()); + // read the header to make sure it succeeds + try + { + // read the header to make sure it succeeds + if (!decoder->readHeader()) + return false; + } + catch (const cv::Exception& e) + { + CV_LOG_ERROR(NULL, "imdecodeanimation_(): can't read header: " << e.what()); + return false; + } + catch (...) + { + CV_LOG_ERROR(NULL, "imdecodeanimation_(): can't read header: unknown exception"); + return false; + } + + int current = 0; + int frame_count = (int)decoder->getFrameCount(); + count = count + start > frame_count ? frame_count - start : count; + + uint64 pixels = (uint64)decoder->width() * (uint64)decoder->height() * (uint64)(count + 4); + if (pixels > CV_IO_MAX_IMAGE_PIXELS) { + CV_LOG_WARNING(NULL, "\nyou are trying to read " << pixels << + " bytes that exceed CV_IO_MAX_IMAGE_PIXELS.\n"); + return false; + } + + while (current < start + count) + { + // grab the decoded type + const int type = calcType(decoder->type(), flags); + + // established the required input image size + Size size = validateInputImageSize(Size(decoder->width(), decoder->height())); + + // read the image data + Mat mat(size.height, size.width, type); + success = false; + try + { + if (decoder->readData(mat)) + success = true; + } + catch (const cv::Exception& e) + { + CV_LOG_ERROR(NULL, "imreadanimation_: can't read data: " << e.what()); + } + catch (...) + { + CV_LOG_ERROR(NULL, "imreadanimation_: can't read data: unknown exception"); + } + if (!success) + break; + + // optionally rotate the data if EXIF' orientation flag says so + if ((flags & IMREAD_IGNORE_ORIENTATION) == 0 && flags != IMREAD_UNCHANGED) + { + ApplyExifOrientation(decoder->getExifTag(ORIENTATION), mat); + } + + if (current >= start) + { + int duration = decoder->animation().durations.size() > 0 ? decoder->animation().durations.back() : 1000; + animation.durations.push_back(duration); + animation.frames.push_back(mat); + } + + if (!decoder->nextPage()) + { + break; + } + ++current; + } + animation.bgcolor = decoder->animation().bgcolor; + animation.loop_count = decoder->animation().loop_count; + + return success; +} + +bool imdecodeanimation(InputArray buf, Animation& animation, int start, int count) +{ + CV_TRACE_FUNCTION(); + + return imdecodeanimation_(buf, IMREAD_UNCHANGED, start, count, animation); +} + static size_t imcount_(const String& filename, int flags) { @@ -994,6 +1104,38 @@ bool imwriteanimation(const String& filename, const Animation& animation, const return imwriteanimation_(filename, animation, params); } +static bool imencodeanimation_(const String& ext, const Animation& animation, std::vector& buf, const std::vector& params) +{ + ImageEncoder encoder = findEncoder(ext); + if (!encoder) + CV_Error(Error::StsError, "could not find a writer for the specified extension"); + + encoder->setDestination(buf); + + bool code = false; + try + { + code = encoder->writeanimation(animation, params); + } + catch (const cv::Exception& e) + { + CV_LOG_ERROR(NULL, "imencodeanimation_('" << ext << "'): can't write data: " << e.what()); + } + catch (...) + { + CV_LOG_ERROR(NULL, "imencodeanimation_('" << ext << "'): can't write data: unknown exception"); + } + + return code; +} + +bool imencodeanimation(const String& ext, const Animation& animation, std::vector& buf, const std::vector& params) +{ + CV_Assert(!animation.frames.empty()); + CV_Assert(animation.frames.size() == animation.durations.size()); + return imencodeanimation_(ext, animation, buf, params); +} + static bool imdecode_( const Mat& buf, int flags, Mat& mat ) { diff --git a/modules/imgcodecs/test/test_animation.cpp b/modules/imgcodecs/test/test_animation.cpp index 5bfb3dc231..e566bcbd49 100644 --- a/modules/imgcodecs/test/test_animation.cpp +++ b/modules/imgcodecs/test/test_animation.cpp @@ -588,6 +588,54 @@ INSTANTIATE_TEST_CASE_P(/**/, Imgcodecs_ImageCollection, testing::ValuesIn(exts_multi)); +TEST(Imgcodecs_APNG, imdecode_animation) +{ + Animation gt_animation, mem_animation; + // Set the path to the test image directory and filename for loading. + const string root = cvtest::TS::ptr()->get_data_path(); + const string filename = root + "pngsuite/tp1n3p08.png"; + + EXPECT_TRUE(imreadanimation(filename, gt_animation)); + EXPECT_EQ(1000, gt_animation.durations.back()); + + std::vector buf; + readFileBytes(filename, buf); + EXPECT_TRUE(imdecodeanimation(buf, mem_animation)); + + EXPECT_EQ(mem_animation.frames.size(), gt_animation.frames.size()); + EXPECT_EQ(mem_animation.bgcolor, gt_animation.bgcolor); + EXPECT_EQ(mem_animation.loop_count, gt_animation.loop_count); + for (size_t i = 0; i < gt_animation.frames.size(); i++) + { + EXPECT_EQ(0, cvtest::norm(mem_animation.frames[i], gt_animation.frames[i], NORM_INF)); + EXPECT_EQ(mem_animation.durations[i], gt_animation.durations[i]); + } +} + +TEST(Imgcodecs_APNG, imencode_animation) +{ + Animation gt_animation, mem_animation; + // Set the path to the test image directory and filename for loading. + const string root = cvtest::TS::ptr()->get_data_path(); + const string filename = root + "pngsuite/tp1n3p08.png"; + + EXPECT_TRUE(imreadanimation(filename, gt_animation)); + EXPECT_EQ(1000, gt_animation.durations.back()); + + std::vector buf; + EXPECT_TRUE(imencodeanimation(".png", gt_animation, buf)); + EXPECT_TRUE(imdecodeanimation(buf, mem_animation)); + + EXPECT_EQ(mem_animation.frames.size(), gt_animation.frames.size()); + EXPECT_EQ(mem_animation.bgcolor, gt_animation.bgcolor); + EXPECT_EQ(mem_animation.loop_count, gt_animation.loop_count); + for (size_t i = 0; i < gt_animation.frames.size(); i++) + { + EXPECT_EQ(0, cvtest::norm(mem_animation.frames[i], gt_animation.frames[i], NORM_INF)); + EXPECT_EQ(mem_animation.durations[i], gt_animation.durations[i]); + } +} + #endif // HAVE_PNG }} // namespace From 71fe90312101c157702525e10296a4dcd4a15f3f Mon Sep 17 00:00:00 2001 From: Vincent Rabaud Date: Wed, 12 Mar 2025 19:06:01 +0100 Subject: [PATCH 11/94] Merge pull request #27040 from vrabaud:png_leak Make sure there are enough channels to check for opacity #27040 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- modules/imgcodecs/src/grfmt_png.cpp | 102 +++++++++++++++------------- 1 file changed, 54 insertions(+), 48 deletions(-) diff --git a/modules/imgcodecs/src/grfmt_png.cpp b/modules/imgcodecs/src/grfmt_png.cpp index dd5ac49a63..ffcc0f29be 100644 --- a/modules/imgcodecs/src/grfmt_png.cpp +++ b/modules/imgcodecs/src/grfmt_png.cpp @@ -42,6 +42,8 @@ #include "precomp.hpp" +#include + #ifdef HAVE_PNG /****************************************************************************************\ @@ -328,6 +330,8 @@ bool PngDecoder::readHeader() delay_den = png_get_uint_16(&chunk.p[30]); dop = chunk.p[32]; bop = chunk.p[33]; + if (dop > 2 || bop > 1) + return false; } if (id == id_PLTE || id == id_tRNS) @@ -480,6 +484,11 @@ bool PngDecoder::readData( Mat& img ) { return false; } + // Asking for blend over with no alpha is invalid. + if (bop == 1 && img.channels() != 4) + { + return false; + } memcpy(&m_chunkIHDR.p[8], &chunk.p[12], 8); return true; @@ -607,6 +616,15 @@ bool PngDecoder::nextPage() { void PngDecoder::compose_frame(std::vector& rows_dst, const std::vector& rows_src, unsigned char _bop, uint32_t x, uint32_t y, uint32_t w, uint32_t h, Mat& img) { + const size_t elem_size = img.elemSize(); + if (_bop == 0) { + // Overwrite mode: copy source row directly to destination + for(uint32_t j = 0; j < h; ++j) { + std::memcpy(rows_dst[j + y] + x * elem_size,rows_src[j], w * elem_size); + } + return; + } + int channels = img.channels(); if (img.depth() == CV_16U) cv::parallel_for_(cv::Range(0, h), [&](const cv::Range& range) { @@ -614,30 +632,24 @@ void PngDecoder::compose_frame(std::vector& rows_dst, const std::vect uint16_t* sp = reinterpret_cast(rows_src[j]); uint16_t* dp = reinterpret_cast(rows_dst[j + y]) + x * channels; - if (_bop == 0) { - // Overwrite mode: copy source row directly to destination - memcpy(dp, sp, w * channels * sizeof(uint16_t)); - } - else { - // Blending mode - for (unsigned int i = 0; i < w; i++, sp += channels, dp += channels) { - if (sp[3] == 65535) { // Fully opaque in 16-bit (max value) - memcpy(dp, sp, channels * sizeof(uint16_t)); + // Blending mode + for (unsigned int i = 0; i < w; i++, sp += channels, dp += channels) { + if (channels < 4 || sp[3] == 65535) { // Fully opaque in 16-bit (max value) + memcpy(dp, sp, elem_size); + } + else if (sp[3] != 0) { // Partially transparent + if (dp[3] != 0) { // Both source and destination have alpha + uint32_t u = sp[3] * 65535; // 16-bit max + uint32_t v = (65535 - sp[3]) * dp[3]; + uint32_t al = u + v; + dp[0] = static_cast((sp[0] * u + dp[0] * v) / al); // Red + dp[1] = static_cast((sp[1] * u + dp[1] * v) / al); // Green + dp[2] = static_cast((sp[2] * u + dp[2] * v) / al); // Blue + dp[3] = static_cast(al / 65535); // Alpha } - else if (sp[3] != 0) { // Partially transparent - if (dp[3] != 0) { // Both source and destination have alpha - uint32_t u = sp[3] * 65535; // 16-bit max - uint32_t v = (65535 - sp[3]) * dp[3]; - uint32_t al = u + v; - dp[0] = static_cast((sp[0] * u + dp[0] * v) / al); // Red - dp[1] = static_cast((sp[1] * u + dp[1] * v) / al); // Green - dp[2] = static_cast((sp[2] * u + dp[2] * v) / al); // Blue - dp[3] = static_cast(al / 65535); // Alpha - } - else { - // If destination alpha is 0, copy source pixel - memcpy(dp, sp, channels * sizeof(uint16_t)); - } + else { + // If destination alpha is 0, copy source pixel + memcpy(dp, sp, elem_size); } } } @@ -649,32 +661,26 @@ void PngDecoder::compose_frame(std::vector& rows_dst, const std::vect unsigned char* sp = rows_src[j]; unsigned char* dp = rows_dst[j + y] + x * channels; - if (_bop == 0) { - // Overwrite mode: copy source row directly to destination - memcpy(dp, sp, w * channels); - } - else { - // Blending mode - for (unsigned int i = 0; i < w; i++, sp += channels, dp += channels) { - if (sp[3] == 255) { - // Fully opaque: copy source pixel directly - memcpy(dp, sp, channels); + // Blending mode + for (unsigned int i = 0; i < w; i++, sp += channels, dp += channels) { + if (channels < 4 || sp[3] == 255) { + // Fully opaque: copy source pixel directly + memcpy(dp, sp, elem_size); + } + else if (sp[3] != 0) { + // Alpha blending + if (dp[3] != 0) { + int u = sp[3] * 255; + int v = (255 - sp[3]) * dp[3]; + int al = u + v; + dp[0] = (sp[0] * u + dp[0] * v) / al; // Red + dp[1] = (sp[1] * u + dp[1] * v) / al; // Green + dp[2] = (sp[2] * u + dp[2] * v) / al; // Blue + dp[3] = al / 255; // Alpha } - else if (sp[3] != 0) { - // Alpha blending - if (dp[3] != 0) { - int u = sp[3] * 255; - int v = (255 - sp[3]) * dp[3]; - int al = u + v; - dp[0] = (sp[0] * u + dp[0] * v) / al; // Red - dp[1] = (sp[1] * u + dp[1] * v) / al; // Green - dp[2] = (sp[2] * u + dp[2] * v) / al; // Blue - dp[3] = al / 255; // Alpha - } - else { - // If destination alpha is 0, copy source pixel - memcpy(dp, sp, channels); - } + else { + // If destination alpha is 0, copy source pixel + memcpy(dp, sp, elem_size); } } } From e30697fd42b36960ed0fcf5d2c927f11e6f191bc Mon Sep 17 00:00:00 2001 From: GenshinImpactStarts <147074368+GenshinImpactStarts@users.noreply.github.com> Date: Thu, 13 Mar 2025 13:34:11 +0800 Subject: [PATCH 12/94] Merge pull request #27002 from GenshinImpactStarts:magnitude [HAL RVV] impl magnitude | add perf test #27002 Implement through the existing `cv_hal_magnitude32f` and `cv_hal_magnitude64f` interfaces. **UPDATE**: UI is enabled. The only difference between UI and HAL now is HAL use a approximate `sqrt`. Perf test done on MUSE-PI. ```sh $ opencv_test_core --gtest_filter="*Magnitude*" $ opencv_perf_core --gtest_filter="*Magnitude*" --perf_min_samples=300 --perf_force_samples=300 ``` Test result between enabled UI and HAL: ``` Name of Test ui rvv rvv vs ui (x-factor) Magnitude::MagnitudeFixture::(127x61, 32FC1) 0.029 0.016 1.75 Magnitude::MagnitudeFixture::(127x61, 64FC1) 0.057 0.036 1.57 Magnitude::MagnitudeFixture::(640x480, 32FC1) 1.063 0.648 1.64 Magnitude::MagnitudeFixture::(640x480, 64FC1) 2.261 1.530 1.48 Magnitude::MagnitudeFixture::(1280x720, 32FC1) 3.261 2.118 1.54 Magnitude::MagnitudeFixture::(1280x720, 64FC1) 6.802 4.682 1.45 Magnitude::MagnitudeFixture::(1920x1080, 32FC1) 7.287 4.738 1.54 Magnitude::MagnitudeFixture::(1920x1080, 64FC1) 15.226 10.334 1.47 ``` Test result before and after enabling UI: ``` Name of Test orig pr pr vs orig (x-factor) Magnitude::MagnitudeFixture::(127x61, 32FC1) 0.032 0.029 1.11 Magnitude::MagnitudeFixture::(127x61, 64FC1) 0.067 0.057 1.17 Magnitude::MagnitudeFixture::(640x480, 32FC1) 1.228 1.063 1.16 Magnitude::MagnitudeFixture::(640x480, 64FC1) 2.786 2.261 1.23 Magnitude::MagnitudeFixture::(1280x720, 32FC1) 3.762 3.261 1.15 Magnitude::MagnitudeFixture::(1280x720, 64FC1) 8.549 6.802 1.26 Magnitude::MagnitudeFixture::(1920x1080, 32FC1) 8.408 7.287 1.15 Magnitude::MagnitudeFixture::(1920x1080, 64FC1) 18.884 15.226 1.24 ``` ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [ ] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- 3rdparty/hal_rvv/hal_rvv.hpp | 1 + 3rdparty/hal_rvv/hal_rvv_1p0/magnitude.hpp | 42 ++++++++++++++++++++++ 3rdparty/hal_rvv/hal_rvv_1p0/sqrt.hpp | 12 ++++--- modules/core/perf/perf_math.cpp | 21 +++++++++++ modules/core/src/mathfuncs_core.simd.hpp | 4 +-- 5 files changed, 73 insertions(+), 7 deletions(-) create mode 100644 3rdparty/hal_rvv/hal_rvv_1p0/magnitude.hpp diff --git a/3rdparty/hal_rvv/hal_rvv.hpp b/3rdparty/hal_rvv/hal_rvv.hpp index 57d2ccfee5..83b1ea272c 100644 --- a/3rdparty/hal_rvv/hal_rvv.hpp +++ b/3rdparty/hal_rvv/hal_rvv.hpp @@ -30,6 +30,7 @@ #include "hal_rvv_1p0/minmax.hpp" // core #include "hal_rvv_1p0/atan.hpp" // core #include "hal_rvv_1p0/split.hpp" // core +#include "hal_rvv_1p0/magnitude.hpp" // core #include "hal_rvv_1p0/flip.hpp" // core #include "hal_rvv_1p0/lut.hpp" // core #include "hal_rvv_1p0/exp.hpp" // core diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/magnitude.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/magnitude.hpp new file mode 100644 index 0000000000..eb814c1b77 --- /dev/null +++ b/3rdparty/hal_rvv/hal_rvv_1p0/magnitude.hpp @@ -0,0 +1,42 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +// Copyright (C) 2025, Institute of Software, Chinese Academy of Sciences. + +#ifndef OPENCV_HAL_RVV_MAGNITUDE_HPP_INCLUDED +#define OPENCV_HAL_RVV_MAGNITUDE_HPP_INCLUDED + +#include + +#include "hal_rvv_1p0/sqrt.hpp" +#include "hal_rvv_1p0/types.hpp" + +namespace cv { namespace cv_hal_rvv { + +#undef cv_hal_magnitude32f +#define cv_hal_magnitude32f cv::cv_hal_rvv::magnitude> +#undef cv_hal_magnitude64f +#define cv_hal_magnitude64f cv::cv_hal_rvv::magnitude> + +template +inline int magnitude(const T* x, const T* y, T* dst, int len) +{ + size_t vl; + for (; len > 0; len -= (int)vl, x += vl, y += vl, dst += vl) + { + vl = SQRT_T::T::setvl(len); + + auto vx = SQRT_T::T::vload(x, vl); + auto vy = SQRT_T::T::vload(y, vl); + + auto vmag = detail::sqrt(__riscv_vfmadd(vx, vx, __riscv_vfmul(vy, vy, vl), vl), vl); + SQRT_T::T::vstore(dst, vmag, vl); + } + + return CV_HAL_ERROR_OK; +} + +}} // namespace cv::cv_hal_rvv + +#endif // OPENCV_HAL_RVV_MAGNITUDE_HPP_INCLUDED diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/sqrt.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/sqrt.hpp index 9a2e5d6bfe..9ed72f6818 100644 --- a/3rdparty/hal_rvv/hal_rvv_1p0/sqrt.hpp +++ b/3rdparty/hal_rvv/hal_rvv_1p0/sqrt.hpp @@ -45,11 +45,12 @@ inline VEC_T sqrt(VEC_T x, size_t vl) t = __riscv_vfrsub(t, 1.5, vl); y = __riscv_vfmul(t, y, vl); } - // just to prevent the compiler from calculating mask before the invSqrt, which will run out + // just to prevent the compiler from calculating mask before the iteration, which will run out // of registers and cause memory access. asm volatile("" ::: "memory"); - auto mask = __riscv_vmfne(x, 0.0, vl); - mask = __riscv_vmfne_mu(mask, mask, x, INFINITY, vl); + auto classified = __riscv_vfclass(x, vl); + // block -0, +0, positive subnormal number, +inf + auto mask = __riscv_vmseq(__riscv_vand(classified, 0b10111000, vl), 0, vl); return __riscv_vfmul_mu(mask, x, x, y, vl); } @@ -58,8 +59,9 @@ inline VEC_T sqrt(VEC_T x, size_t vl) template inline VEC_T invSqrt(VEC_T x, size_t vl) { - auto mask = __riscv_vmfne(x, 0.0, vl); - mask = __riscv_vmfne_mu(mask, mask, x, INFINITY, vl); + auto classified = __riscv_vfclass(x, vl); + // block -0, +0, positive subnormal number, +inf + auto mask = __riscv_vmseq(__riscv_vand(classified, 0b10111000, vl), 0, vl); auto x2 = __riscv_vfmul(x, 0.5, vl); auto y = __riscv_vfrsqrt7(x, vl); #pragma unroll diff --git a/modules/core/perf/perf_math.cpp b/modules/core/perf/perf_math.cpp index fe947aec1a..c06fda44da 100644 --- a/modules/core/perf/perf_math.cpp +++ b/modules/core/perf/perf_math.cpp @@ -36,6 +36,27 @@ PERF_TEST_P(VectorLength, phase64f, testing::Values(128, 1000, 128*1024, 512*102 SANITY_CHECK(angle, 5e-5); } +///////////// Magnitude ///////////// + +typedef Size_MatType MagnitudeFixture; + +PERF_TEST_P(MagnitudeFixture, Magnitude, + testing::Combine(testing::Values(TYPICAL_MAT_SIZES), testing::Values(CV_32F, CV_64F))) +{ + cv::Size size = std::get<0>(GetParam()); + int type = std::get<1>(GetParam()); + + cv::Mat x(size, type); + cv::Mat y(size, type); + cv::Mat magnitude(size, type); + + declare.in(x, y, WARMUP_RNG).out(magnitude); + + TEST_CYCLE() cv::magnitude(x, y, magnitude); + + SANITY_CHECK_NOTHING(); +} + // generates random vectors, performs Gram-Schmidt orthogonalization on them Mat randomOrtho(int rows, int ftype, RNG& rng) { diff --git a/modules/core/src/mathfuncs_core.simd.hpp b/modules/core/src/mathfuncs_core.simd.hpp index 41a3261c64..0d9d9272e6 100644 --- a/modules/core/src/mathfuncs_core.simd.hpp +++ b/modules/core/src/mathfuncs_core.simd.hpp @@ -273,7 +273,7 @@ void magnitude32f(const float* x, const float* y, float* mag, int len) int i = 0; -#if CV_SIMD +#if (CV_SIMD || CV_SIMD_SCALABLE) const int VECSZ = VTraits::vlanes(); for( ; i < len; i += VECSZ*2 ) { @@ -306,7 +306,7 @@ void magnitude64f(const double* x, const double* y, double* mag, int len) int i = 0; -#if CV_SIMD_64F +#if (CV_SIMD_64F || CV_SIMD_SCALABLE_64F) const int VECSZ = VTraits::vlanes(); for( ; i < len; i += VECSZ*2 ) { From fd62bd0991e24fb9c71b84c0a873479e6eaed650 Mon Sep 17 00:00:00 2001 From: Liutong HAN Date: Thu, 13 Mar 2025 07:54:41 +0000 Subject: [PATCH 13/94] Relax the loop condition to process the final batch. --- modules/core/src/matmul.simd.hpp | 4 ++-- modules/imgproc/src/color_lab.cpp | 4 ++-- modules/imgproc/src/resize.cpp | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/modules/core/src/matmul.simd.hpp b/modules/core/src/matmul.simd.hpp index a64cd7ba6a..09847d4a4b 100644 --- a/modules/core/src/matmul.simd.hpp +++ b/modules/core/src/matmul.simd.hpp @@ -2544,7 +2544,7 @@ double dotProd_32s(const int* src1, const int* src2, int len) #if CV_SIMD_WIDTH == 16 const int wstep = step * 2; v_float64 v_sum1 = vx_setzero_f64(); - for (; i < len - wstep; i += wstep, src1 += wstep, src2 += wstep) + for (; i <= len - wstep; i += wstep, src1 += wstep, src2 += wstep) { v_int32 v_src10 = vx_load(src1); v_int32 v_src20 = vx_load(src2); @@ -2555,7 +2555,7 @@ double dotProd_32s(const int* src1, const int* src2, int len) } v_sum0 = v_add(v_sum0, v_sum1); #endif - for (; i < len - step; i += step, src1 += step, src2 += step) + for (; i <= len - step; i += step, src1 += step, src2 += step) { v_int32 v_src1 = vx_load(src1); v_int32 v_src2 = vx_load(src2); diff --git a/modules/imgproc/src/color_lab.cpp b/modules/imgproc/src/color_lab.cpp index dd6fb52949..fe9888e381 100644 --- a/modules/imgproc/src/color_lab.cpp +++ b/modules/imgproc/src/color_lab.cpp @@ -1953,7 +1953,7 @@ struct RGB2Lab_f { const int vsize = VTraits::vlanes(); static const int nPixels = vsize*2; - for(; i < n - 3*nPixels; i += 3*nPixels, src += scn*nPixels) + for(; i <= n - 3*nPixels; i += 3*nPixels, src += scn*nPixels) { v_float32 rvec0, gvec0, bvec0, rvec1, gvec1, bvec1; if(scn == 3) @@ -3297,7 +3297,7 @@ struct RGB2Luvinterpolate { const int vsize = VTraits::vlanes(); static const int nPixels = vsize*2; - for(; i < n - 3*nPixels; i += 3*nPixels, src += scn*nPixels) + for(; i <= n - 3*nPixels; i += 3*nPixels, src += scn*nPixels) { /* int R = src[bIdx], G = src[1], B = src[bIdx^2]; diff --git a/modules/imgproc/src/resize.cpp b/modules/imgproc/src/resize.cpp index 4b317c6a5a..92af9cacc4 100644 --- a/modules/imgproc/src/resize.cpp +++ b/modules/imgproc/src/resize.cpp @@ -1325,7 +1325,7 @@ struct VResizeLinearVec_32s8u v_store(dst + x, v_rshr_pack_u<2>(v_add(v_mul_hi(v_pack(v_shr<4>(vx_load(S0 + x)), v_shr<4>(vx_load(S0 + x + VTraits::vlanes()))), b0), v_mul_hi(v_pack(v_shr<4>(vx_load(S1 + x)), v_shr<4>(vx_load(S1 + x + VTraits::vlanes()))), b1)), v_add(v_mul_hi(v_pack(v_shr<4>(vx_load(S0 + x + 2 * VTraits::vlanes())), v_shr<4>(vx_load(S0 + x + 3 * VTraits::vlanes()))), b0), v_mul_hi(v_pack(v_shr<4>(vx_load(S1 + x + 2 * VTraits::vlanes())), v_shr<4>(vx_load(S1 + x + 3 * VTraits::vlanes()))), b1)))); - for( ; x < width - VTraits::vlanes(); x += VTraits::vlanes()) + for( ; x <= width - VTraits::vlanes(); x += VTraits::vlanes()) v_rshr_pack_u_store<2>(dst + x, v_add(v_mul_hi(v_pack(v_shr<4>(vx_load(S0 + x)), v_shr<4>(vx_load(S0 + x + VTraits::vlanes()))), b0), v_mul_hi(v_pack(v_shr<4>(vx_load(S1 + x)), v_shr<4>(vx_load(S1 + x + VTraits::vlanes()))), b1))); return x; @@ -1349,7 +1349,7 @@ struct VResizeLinearVec_32f16u for (; x <= width - VTraits::vlanes(); x += VTraits::vlanes()) v_store(dst + x, v_pack_u(v_round(v_muladd(vx_load(S0 + x ), b0, v_mul(vx_load(S1 + x), b1))), v_round(v_muladd(vx_load(S0 + x + VTraits::vlanes()), b0, v_mul(vx_load(S1 + x + VTraits::vlanes()), b1))))); - for( ; x < width - VTraits::vlanes(); x += VTraits::vlanes()) + for( ; x <= width - VTraits::vlanes(); x += VTraits::vlanes()) { v_int32 t0 = v_round(v_muladd(vx_load(S0 + x), b0, v_mul(vx_load(S1 + x), b1))); v_store_low(dst + x, v_pack_u(t0, t0)); @@ -1376,7 +1376,7 @@ struct VResizeLinearVec_32f16s for (; x <= width - VTraits::vlanes(); x += VTraits::vlanes()) v_store(dst + x, v_pack(v_round(v_muladd(vx_load(S0 + x ), b0, v_mul(vx_load(S1 + x), b1))), v_round(v_muladd(vx_load(S0 + x + VTraits::vlanes()), b0, v_mul(vx_load(S1 + x + VTraits::vlanes()), b1))))); - for( ; x < width - VTraits::vlanes(); x += VTraits::vlanes()) + for( ; x <= width - VTraits::vlanes(); x += VTraits::vlanes()) { v_int32 t0 = v_round(v_muladd(vx_load(S0 + x), b0, v_mul(vx_load(S1 + x), b1))); v_store_low(dst + x, v_pack(t0, t0)); From 186537a3154c2d596d993fedb5b597a2bdfcdd23 Mon Sep 17 00:00:00 2001 From: Vincent Rabaud Date: Thu, 13 Mar 2025 09:59:09 +0100 Subject: [PATCH 14/94] Move the CV_Assert above the << operation to not trigger the fuzzer --- modules/imgcodecs/src/grfmt_gif.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/imgcodecs/src/grfmt_gif.cpp b/modules/imgcodecs/src/grfmt_gif.cpp index 653b62ddc8..d33b600722 100644 --- a/modules/imgcodecs/src/grfmt_gif.cpp +++ b/modules/imgcodecs/src/grfmt_gif.cpp @@ -319,9 +319,9 @@ bool GifDecoder::lzwDecode() { lzwMinCodeSize = m_strm.getByte(); const int lzwMaxSize = (1 << 12); // 4096 is the maximum size of the LZW table (12 bits) int lzwCodeSize = lzwMinCodeSize + 1; + CV_Assert(lzwCodeSize > 2 && lzwCodeSize <= 12); int clearCode = 1 << lzwMinCodeSize; int exitCode = clearCode + 1; - CV_Assert(lzwCodeSize > 2 && lzwCodeSize <= 12); std::vector lzwExtraTable(lzwMaxSize + 1); int colorTableSize = clearCode; int lzwTableSize = exitCode; From 2a8d4b8e43f6e499c5553edd26056caed284d5a6 Mon Sep 17 00:00:00 2001 From: GenshinImpactStarts <147074368+GenshinImpactStarts@users.noreply.github.com> Date: Thu, 13 Mar 2025 20:56:56 +0800 Subject: [PATCH 15/94] Merge pull request #27000 from GenshinImpactStarts:cart_to_polar [HAL RVV] reuse atan | impl cart_to_polar | add perf test #27000 Implement through the existing `cv_hal_cartToPolar32f` and `cv_hal_cartToPolar64f` interfaces. Add `cartToPolar` performance tests. cv_hal_rvv::fast_atan is modified to make it more reusable because it's needed in cartToPolar. **UPDATE**: UI enabled. Since the vec type of RVV can't be stored in struct. UI implementation of `v_atan_f32` is modified. Both `fastAtan` and `cartToPolar` are affected so the test result for `atan` is also appended. I have tested the modified UI on RVV and AVX2 and no regressions appears. Perf test done on MUSE-PI. AVX2 test done on Intel(R) Xeon(R) Gold 6140 CPU @ 2.30GHz. ```sh $ opencv_test_core --gtest_filter="*CartToPolar*:*Core_CartPolar_reverse*:*Phase*" $ opencv_perf_core --gtest_filter="*CartToPolar*:*phase*" --perf_min_samples=300 --perf_force_samples=300 ``` Test result between enabled UI and HAL: ``` Name of Test ui rvv rvv vs ui (x-factor) CartToPolar::CartToPolarFixture::(127x61, 32FC1) 0.106 0.059 1.80 CartToPolar::CartToPolarFixture::(127x61, 64FC1) 0.155 0.070 2.20 CartToPolar::CartToPolarFixture::(640x480, 32FC1) 4.188 2.317 1.81 CartToPolar::CartToPolarFixture::(640x480, 64FC1) 6.593 2.889 2.28 CartToPolar::CartToPolarFixture::(1280x720, 32FC1) 12.600 7.057 1.79 CartToPolar::CartToPolarFixture::(1280x720, 64FC1) 19.860 8.797 2.26 CartToPolar::CartToPolarFixture::(1920x1080, 32FC1) 28.295 15.809 1.79 CartToPolar::CartToPolarFixture::(1920x1080, 64FC1) 44.573 19.398 2.30 phase32f::VectorLength::128 0.002 0.002 1.20 phase32f::VectorLength::1000 0.008 0.006 1.32 phase32f::VectorLength::131072 1.061 0.731 1.45 phase32f::VectorLength::524288 3.997 2.976 1.34 phase32f::VectorLength::1048576 8.001 5.959 1.34 phase64f::VectorLength::128 0.002 0.002 1.33 phase64f::VectorLength::1000 0.012 0.008 1.58 phase64f::VectorLength::131072 1.648 0.931 1.77 phase64f::VectorLength::524288 6.836 3.837 1.78 phase64f::VectorLength::1048576 14.060 7.540 1.86 ``` Test result before and after enabling UI on RVV: ``` Name of Test perf perf perf ui ui ui orig pr pr vs perf ui orig (x-factor) CartToPolar::CartToPolarFixture::(127x61, 32FC1) 0.141 0.106 1.33 CartToPolar::CartToPolarFixture::(127x61, 64FC1) 0.187 0.155 1.20 CartToPolar::CartToPolarFixture::(640x480, 32FC1) 5.990 4.188 1.43 CartToPolar::CartToPolarFixture::(640x480, 64FC1) 8.370 6.593 1.27 CartToPolar::CartToPolarFixture::(1280x720, 32FC1) 18.214 12.600 1.45 CartToPolar::CartToPolarFixture::(1280x720, 64FC1) 25.365 19.860 1.28 CartToPolar::CartToPolarFixture::(1920x1080, 32FC1) 40.437 28.295 1.43 CartToPolar::CartToPolarFixture::(1920x1080, 64FC1) 56.699 44.573 1.27 phase32f::VectorLength::128 0.003 0.002 1.54 phase32f::VectorLength::1000 0.016 0.008 1.90 phase32f::VectorLength::131072 2.048 1.061 1.93 phase32f::VectorLength::524288 8.219 3.997 2.06 phase32f::VectorLength::1048576 16.426 8.001 2.05 phase64f::VectorLength::128 0.003 0.002 1.44 phase64f::VectorLength::1000 0.020 0.012 1.60 phase64f::VectorLength::131072 2.621 1.648 1.59 phase64f::VectorLength::524288 10.780 6.836 1.58 phase64f::VectorLength::1048576 22.723 14.060 1.62 ``` Test result before and after modifying UI on AVX2: ``` Name of Test perf perf perf avx2 avx2 avx2 orig pr pr vs perf avx2 orig (x-factor) CartToPolar::CartToPolarFixture::(127x61, 32FC1) 0.006 0.005 1.14 CartToPolar::CartToPolarFixture::(127x61, 64FC1) 0.010 0.009 1.08 CartToPolar::CartToPolarFixture::(640x480, 32FC1) 0.273 0.264 1.03 CartToPolar::CartToPolarFixture::(640x480, 64FC1) 0.511 0.487 1.05 CartToPolar::CartToPolarFixture::(1280x720, 32FC1) 0.760 0.723 1.05 CartToPolar::CartToPolarFixture::(1280x720, 64FC1) 2.009 1.937 1.04 CartToPolar::CartToPolarFixture::(1920x1080, 32FC1) 1.996 1.923 1.04 CartToPolar::CartToPolarFixture::(1920x1080, 64FC1) 5.721 5.509 1.04 phase32f::VectorLength::128 0.000 0.000 0.98 phase32f::VectorLength::1000 0.001 0.001 0.97 phase32f::VectorLength::131072 0.105 0.111 0.95 phase32f::VectorLength::524288 0.402 0.402 1.00 phase32f::VectorLength::1048576 0.775 0.767 1.01 phase64f::VectorLength::128 0.000 0.000 1.00 phase64f::VectorLength::1000 0.001 0.001 1.01 phase64f::VectorLength::131072 0.163 0.162 1.01 phase64f::VectorLength::524288 0.669 0.653 1.02 phase64f::VectorLength::1048576 1.660 1.634 1.02 ``` ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [ ] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- 3rdparty/hal_rvv/hal_rvv.hpp | 1 + 3rdparty/hal_rvv/hal_rvv_1p0/atan.hpp | 122 +++++++++--------- .../hal_rvv/hal_rvv_1p0/cart_to_polar.hpp | 48 +++++++ modules/core/perf/perf_math.cpp | 22 ++++ modules/core/src/mathfuncs_core.simd.hpp | 76 +++++------ 5 files changed, 158 insertions(+), 111 deletions(-) create mode 100644 3rdparty/hal_rvv/hal_rvv_1p0/cart_to_polar.hpp diff --git a/3rdparty/hal_rvv/hal_rvv.hpp b/3rdparty/hal_rvv/hal_rvv.hpp index 83b1ea272c..f2f9aa68d4 100644 --- a/3rdparty/hal_rvv/hal_rvv.hpp +++ b/3rdparty/hal_rvv/hal_rvv.hpp @@ -31,6 +31,7 @@ #include "hal_rvv_1p0/atan.hpp" // core #include "hal_rvv_1p0/split.hpp" // core #include "hal_rvv_1p0/magnitude.hpp" // core +#include "hal_rvv_1p0/cart_to_polar.hpp" // core #include "hal_rvv_1p0/flip.hpp" // core #include "hal_rvv_1p0/lut.hpp" // core #include "hal_rvv_1p0/exp.hpp" // core diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/atan.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/atan.hpp index 2134d98a6e..2e4f9c2a67 100644 --- a/3rdparty/hal_rvv/hal_rvv_1p0/atan.hpp +++ b/3rdparty/hal_rvv/hal_rvv_1p0/atan.hpp @@ -13,67 +13,76 @@ #include -namespace cv::cv_hal_rvv { +namespace cv { namespace cv_hal_rvv { namespace detail { // ref: mathfuncs_core.simd.hpp static constexpr float pi = CV_PI; -static constexpr float atan2_p1 = 0.9997878412794807F * (180 / pi); -static constexpr float atan2_p3 = -0.3258083974640975F * (180 / pi); -static constexpr float atan2_p5 = 0.1555786518463281F * (180 / pi); -static constexpr float atan2_p7 = -0.04432655554792128F * (180 / pi); -__attribute__((always_inline)) inline vfloat32m4_t -rvv_atan_f32(vfloat32m4_t vy, vfloat32m4_t vx, size_t vl, float p7, - vfloat32m4_t vp5, vfloat32m4_t vp3, vfloat32m4_t vp1, - float angle_90_deg) { +struct AtanParams +{ + float p1, p3, p5, p7, angle_90; +}; + +static constexpr AtanParams atan_params_rad { + 0.9997878412794807F, + -0.3258083974640975F, + 0.1555786518463281F, + -0.04432655554792128F, + 90.F * (pi / 180.F)}; +static constexpr AtanParams atan_params_deg { + atan_params_rad.p1 * (180 / pi), + atan_params_rad.p3 * (180 / pi), + atan_params_rad.p5 * (180 / pi), + atan_params_rad.p7 * (180 / pi), + 90.F}; + +template +__attribute__((always_inline)) inline VEC_T + rvv_atan(VEC_T vy, VEC_T vx, size_t vl, const AtanParams& params) +{ const auto ax = __riscv_vfabs(vx, vl); const auto ay = __riscv_vfabs(vy, vl); - const auto c = __riscv_vfdiv( - __riscv_vfmin(ax, ay, vl), - __riscv_vfadd(__riscv_vfmax(ax, ay, vl), FLT_EPSILON, vl), vl); + // Reciprocal Estimate (vfrec7) is not accurate enough to pass the test of cartToPolar. + const auto c = __riscv_vfdiv(__riscv_vfmin(ax, ay, vl), + __riscv_vfadd(__riscv_vfmax(ax, ay, vl), FLT_EPSILON, vl), + vl); const auto c2 = __riscv_vfmul(c, c, vl); - auto a = __riscv_vfmadd(c2, p7, vp5, vl); - a = __riscv_vfmadd(a, c2, vp3, vl); - a = __riscv_vfmadd(a, c2, vp1, vl); + // Using vfmadd only results in about a 2% performance improvement, but it occupies 3 additional + // M4 registers. (Performance test on phase32f::VectorLength::1048576: time decreased + // from 5.952ms to 5.805ms on Muse Pi) + // Additionally, when registers are nearly fully utilized (though not yet exhausted), the + // compiler is likely to fail to optimize and may introduce slower memory access (e.g., in + // cv::cv_hal_rvv::fast_atan_64). + // Saving registers can also make this function more reusable in other contexts. + // Therefore, vfmadd is not used here. + auto a = __riscv_vfadd(__riscv_vfmul(c2, params.p7, vl), params.p5, vl); + a = __riscv_vfadd(__riscv_vfmul(c2, a, vl), params.p3, vl); + a = __riscv_vfadd(__riscv_vfmul(c2, a, vl), params.p1, vl); a = __riscv_vfmul(a, c, vl); - const auto mask = __riscv_vmflt(ax, ay, vl); - a = __riscv_vfrsub_mu(mask, a, a, angle_90_deg, vl); - - a = __riscv_vfrsub_mu(__riscv_vmflt(vx, 0.F, vl), a, a, angle_90_deg * 2, - vl); - a = __riscv_vfrsub_mu(__riscv_vmflt(vy, 0.F, vl), a, a, angle_90_deg * 4, - vl); + a = __riscv_vfrsub_mu(__riscv_vmflt(ax, ay, vl), a, a, params.angle_90, vl); + a = __riscv_vfrsub_mu(__riscv_vmflt(vx, 0.F, vl), a, a, params.angle_90 * 2, vl); + a = __riscv_vfrsub_mu(__riscv_vmflt(vy, 0.F, vl), a, a, params.angle_90 * 4, vl); return a; } -} // namespace detail +} // namespace detail -inline int fast_atan_32(const float *y, const float *x, float *dst, size_t n, - bool angle_in_deg) { - const float scale = angle_in_deg ? 1.f : CV_PI / 180.f; - const float p1 = detail::atan2_p1 * scale; - const float p3 = detail::atan2_p3 * scale; - const float p5 = detail::atan2_p5 * scale; - const float p7 = detail::atan2_p7 * scale; - const float angle_90_deg = 90.F * scale; +inline int fast_atan_32(const float* y, const float* x, float* dst, size_t n, bool angle_in_deg) +{ + auto atan_params = angle_in_deg ? detail::atan_params_deg : detail::atan_params_rad; - static size_t vlmax = __riscv_vsetvlmax_e32m4(); - auto vp1 = __riscv_vfmv_v_f_f32m4(p1, vlmax); - auto vp3 = __riscv_vfmv_v_f_f32m4(p3, vlmax); - auto vp5 = __riscv_vfmv_v_f_f32m4(p5, vlmax); - - for (size_t vl{}; n > 0; n -= vl) { + for (size_t vl = 0; n > 0; n -= vl) + { vl = __riscv_vsetvl_e32m4(n); auto vy = __riscv_vle32_v_f32m4(y, vl); auto vx = __riscv_vle32_v_f32m4(x, vl); - auto a = - detail::rvv_atan_f32(vy, vx, vl, p7, vp5, vp3, vp1, angle_90_deg); + auto a = detail::rvv_atan(vy, vx, vl, atan_params); __riscv_vse32(dst, a, vl); @@ -85,37 +94,22 @@ inline int fast_atan_32(const float *y, const float *x, float *dst, size_t n, return CV_HAL_ERROR_OK; } -inline int fast_atan_64(const double *y, const double *x, double *dst, size_t n, - bool angle_in_deg) { +inline int fast_atan_64(const double* y, const double* x, double* dst, size_t n, bool angle_in_deg) +{ // this also uses float32 version, ref: mathfuncs_core.simd.hpp - const float scale = angle_in_deg ? 1.f : CV_PI / 180.f; - const float p1 = detail::atan2_p1 * scale; - const float p3 = detail::atan2_p3 * scale; - const float p5 = detail::atan2_p5 * scale; - const float p7 = detail::atan2_p7 * scale; - const float angle_90_deg = 90.F * scale; + auto atan_params = angle_in_deg ? detail::atan_params_deg : detail::atan_params_rad; - static size_t vlmax = __riscv_vsetvlmax_e32m4(); - auto vp1 = __riscv_vfmv_v_f_f32m4(p1, vlmax); - auto vp3 = __riscv_vfmv_v_f_f32m4(p3, vlmax); - auto vp5 = __riscv_vfmv_v_f_f32m4(p5, vlmax); - - for (size_t vl{}; n > 0; n -= vl) { + for (size_t vl = 0; n > 0; n -= vl) + { vl = __riscv_vsetvl_e64m8(n); - auto wy = __riscv_vle64_v_f64m8(y, vl); - auto wx = __riscv_vle64_v_f64m8(x, vl); + auto vy = __riscv_vfncvt_f(__riscv_vle64_v_f64m8(y, vl), vl); + auto vx = __riscv_vfncvt_f(__riscv_vle64_v_f64m8(x, vl), vl); - auto vy = __riscv_vfncvt_f_f_w_f32m4(wy, vl); - auto vx = __riscv_vfncvt_f_f_w_f32m4(wx, vl); + auto a = detail::rvv_atan(vy, vx, vl, atan_params); - auto a = - detail::rvv_atan_f32(vy, vx, vl, p7, vp5, vp3, vp1, angle_90_deg); - - auto wa = __riscv_vfwcvt_f_f_v_f64m8(a, vl); - - __riscv_vse64(dst, wa, vl); + __riscv_vse64(dst, __riscv_vfwcvt_f(a, vl), vl); x += vl; y += vl; @@ -125,4 +119,4 @@ inline int fast_atan_64(const double *y, const double *x, double *dst, size_t n, return CV_HAL_ERROR_OK; } -} // namespace cv::cv_hal_rvv +}} // namespace cv::cv_hal_rvv diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/cart_to_polar.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/cart_to_polar.hpp new file mode 100644 index 0000000000..676133b668 --- /dev/null +++ b/3rdparty/hal_rvv/hal_rvv_1p0/cart_to_polar.hpp @@ -0,0 +1,48 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +// Copyright (C) 2025, Institute of Software, Chinese Academy of Sciences. + +#ifndef OPENCV_HAL_RVV_CART_TO_POLAR_HPP_INCLUDED +#define OPENCV_HAL_RVV_CART_TO_POLAR_HPP_INCLUDED + +#include + +#include "hal_rvv_1p0/atan.hpp" +#include "hal_rvv_1p0/sqrt.hpp" +#include "hal_rvv_1p0/types.hpp" + +namespace cv { namespace cv_hal_rvv { + +#undef cv_hal_cartToPolar32f +#define cv_hal_cartToPolar32f cv::cv_hal_rvv::cartToPolar +#undef cv_hal_cartToPolar64f +#define cv_hal_cartToPolar64f cv::cv_hal_rvv::cartToPolar + +template +inline int cartToPolar(const T* x, const T* y, T* mag, T* angle, int len, bool angleInDegrees) +{ + using CalType = RVV_SameLen; + auto atan_params = angleInDegrees ? detail::atan_params_deg : detail::atan_params_rad; + size_t vl; + for (; len > 0; len -= (int)vl, x += vl, y += vl, mag += vl, angle += vl) + { + vl = RVV_T::setvl(len); + + auto vx = CalType::cast(RVV_T::vload(x, vl), vl); + auto vy = CalType::cast(RVV_T::vload(y, vl), vl); + + auto vmag = detail::sqrt<2>(__riscv_vfmadd(vx, vx, __riscv_vfmul(vy, vy, vl), vl), vl); + RVV_T::vstore(mag, RVV_T::cast(vmag, vl), vl); + + auto vangle = detail::rvv_atan(vy, vx, vl, atan_params); + RVV_T::vstore(angle, RVV_T::cast(vangle, vl), vl); + } + + return CV_HAL_ERROR_OK; +} + +}} // namespace cv::cv_hal_rvv + +#endif // OPENCV_HAL_RVV_CART_TO_POLAR_HPP_INCLUDED diff --git a/modules/core/perf/perf_math.cpp b/modules/core/perf/perf_math.cpp index c06fda44da..398a3ad651 100644 --- a/modules/core/perf/perf_math.cpp +++ b/modules/core/perf/perf_math.cpp @@ -57,6 +57,28 @@ PERF_TEST_P(MagnitudeFixture, Magnitude, SANITY_CHECK_NOTHING(); } +///////////// Cart to Polar ///////////// + +typedef Size_MatType CartToPolarFixture; + +PERF_TEST_P(CartToPolarFixture, CartToPolar, + testing::Combine(testing::Values(TYPICAL_MAT_SIZES), testing::Values(CV_32F, CV_64F))) +{ + cv::Size size = std::get<0>(GetParam()); + int type = std::get<1>(GetParam()); + + cv::Mat x(size, type); + cv::Mat y(size, type); + cv::Mat magnitude(size, type); + cv::Mat angle(size, type); + + declare.in(x, y, WARMUP_RNG).out(magnitude, angle); + + TEST_CYCLE() cv::cartToPolar(x, y, magnitude, angle); + + SANITY_CHECK_NOTHING(); +} + // generates random vectors, performs Gram-Schmidt orthogonalization on them Mat randomOrtho(int rows, int ftype, RNG& rng) { diff --git a/modules/core/src/mathfuncs_core.simd.hpp b/modules/core/src/mathfuncs_core.simd.hpp index 0d9d9272e6..e9d57a5c4d 100644 --- a/modules/core/src/mathfuncs_core.simd.hpp +++ b/modules/core/src/mathfuncs_core.simd.hpp @@ -73,48 +73,30 @@ static inline float atan_f32(float y, float x) } #endif -#if CV_SIMD +#if (CV_SIMD || CV_SIMD_SCALABLE) -struct v_atan_f32 +v_float32 v_atan_f32(const v_float32& y, const v_float32& x) { - explicit v_atan_f32(const float& scale) - { - eps = vx_setall_f32((float)DBL_EPSILON); - z = vx_setzero_f32(); - p7 = vx_setall_f32(atan2_p7); - p5 = vx_setall_f32(atan2_p5); - p3 = vx_setall_f32(atan2_p3); - p1 = vx_setall_f32(atan2_p1); - val90 = vx_setall_f32(90.f); - val180 = vx_setall_f32(180.f); - val360 = vx_setall_f32(360.f); - s = vx_setall_f32(scale); - } + v_float32 eps = vx_setall_f32((float)DBL_EPSILON); + v_float32 z = vx_setzero_f32(); + v_float32 p7 = vx_setall_f32(atan2_p7); + v_float32 p5 = vx_setall_f32(atan2_p5); + v_float32 p3 = vx_setall_f32(atan2_p3); + v_float32 p1 = vx_setall_f32(atan2_p1); + v_float32 val90 = vx_setall_f32(90.f); + v_float32 val180 = vx_setall_f32(180.f); + v_float32 val360 = vx_setall_f32(360.f); - v_float32 compute(const v_float32& y, const v_float32& x) - { - v_float32 ax = v_abs(x); - v_float32 ay = v_abs(y); - v_float32 c = v_div(v_min(ax, ay), v_add(v_max(ax, ay), this->eps)); - v_float32 cc = v_mul(c, c); - v_float32 a = v_mul(v_fma(v_fma(v_fma(cc, this->p7, this->p5), cc, this->p3), cc, this->p1), c); - a = v_select(v_ge(ax, ay), a, v_sub(this->val90, a)); - a = v_select(v_lt(x, this->z), v_sub(this->val180, a), a); - a = v_select(v_lt(y, this->z), v_sub(this->val360, a), a); - return v_mul(a, this->s); - } - - v_float32 eps; - v_float32 z; - v_float32 p7; - v_float32 p5; - v_float32 p3; - v_float32 p1; - v_float32 val90; - v_float32 val180; - v_float32 val360; - v_float32 s; -}; + v_float32 ax = v_abs(x); + v_float32 ay = v_abs(y); + v_float32 c = v_div(v_min(ax, ay), v_add(v_max(ax, ay), eps)); + v_float32 cc = v_mul(c, c); + v_float32 a = v_mul(v_fma(v_fma(v_fma(cc, p7, p5), cc, p3), cc, p1), c); + a = v_select(v_ge(ax, ay), a, v_sub(val90, a)); + a = v_select(v_lt(x, z), v_sub(val180, a), a); + a = v_select(v_lt(y, z), v_sub(val360, a), a); + return a; +} #endif @@ -124,9 +106,9 @@ static void cartToPolar32f_(const float *X, const float *Y, float *mag, float *a { float scale = angleInDegrees ? 1.f : (float)(CV_PI/180); int i = 0; -#if CV_SIMD +#if (CV_SIMD || CV_SIMD_SCALABLE) const int VECSZ = VTraits::vlanes(); - v_atan_f32 v(scale); + v_float32 s = vx_setall_f32(scale); for( ; i < len; i += VECSZ*2 ) { @@ -148,8 +130,8 @@ static void cartToPolar32f_(const float *X, const float *Y, float *mag, float *a v_float32 m0 = v_sqrt(v_muladd(x0, x0, v_mul(y0, y0))); v_float32 m1 = v_sqrt(v_muladd(x1, x1, v_mul(y1, y1))); - v_float32 r0 = v.compute(y0, x0); - v_float32 r1 = v.compute(y1, x1); + v_float32 r0 = v_mul(v_atan_f32(y0, x0), s); + v_float32 r1 = v_mul(v_atan_f32(y1, x1), s); v_store(mag + i, m0); v_store(mag + i + VECSZ, m1); @@ -200,9 +182,9 @@ static void fastAtan32f_(const float *Y, const float *X, float *angle, int len, { float scale = angleInDegrees ? 1.f : (float)(CV_PI/180); int i = 0; -#if CV_SIMD +#if (CV_SIMD || CV_SIMD_SCALABLE) const int VECSZ = VTraits::vlanes(); - v_atan_f32 v(scale); + v_float32 s = vx_setall_f32(scale); for( ; i < len; i += VECSZ*2 ) { @@ -221,8 +203,8 @@ static void fastAtan32f_(const float *Y, const float *X, float *angle, int len, v_float32 y1 = vx_load(Y + i + VECSZ); v_float32 x1 = vx_load(X + i + VECSZ); - v_float32 r0 = v.compute(y0, x0); - v_float32 r1 = v.compute(y1, x1); + v_float32 r0 = v_mul(v_atan_f32(y0, x0), s); + v_float32 r1 = v_mul(v_atan_f32(y1, x1), s); v_store(angle + i, r0); v_store(angle + i + VECSZ, r1); From 2c16f3b7d2b28f6cac444046b8f95b40d9266a6a Mon Sep 17 00:00:00 2001 From: amane-ame Date: Fri, 14 Mar 2025 18:33:57 +0800 Subject: [PATCH 16/94] Optimize cv::sepFilter. Co-authored-by: Liutong HAN --- 3rdparty/hal_rvv/hal_rvv_1p0/filter.hpp | 250 +++++++++++------------- 1 file changed, 116 insertions(+), 134 deletions(-) diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/filter.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/filter.hpp index d4577c8893..412dfee4d4 100644 --- a/3rdparty/hal_rvv/hal_rvv_1p0/filter.hpp +++ b/3rdparty/hal_rvv/hal_rvv_1p0/filter.hpp @@ -38,10 +38,10 @@ private: }; template -static inline int invoke(int start, int end, std::function func, Args&&... args) +static inline int invoke(int height, std::function func, Args&&... args) { - cv::parallel_for_(Range(start + 1, end), FilterInvoker(func, std::forward(args)...), cv::getNumThreads()); - return func(start, start + 1, std::forward(args)...); + cv::parallel_for_(Range(1, height), FilterInvoker(func, std::forward(args)...), cv::getNumThreads()); + return func(0, 1, std::forward(args)...); } static inline int borderInterpolate( int p, int len, int borderType ) @@ -179,10 +179,10 @@ static void process5(int anchor, int left, int right, float delta, const float* auto v3 = __riscv_vfwcvt_f(__riscv_vwcvtu_x(__riscv_vget_v_u8m1x4_u8m1(src, 3), vl), vl); const uchar* extra = row + (i + vl - anchor) * 4; - s0 = addshift(s0, v0, k0, k1, k2, k3, k4, *(extra ), *(extra + 4), *(extra + 8), *(extra + 12)); - s1 = addshift(s1, v1, k0, k1, k2, k3, k4, *(extra + 1), *(extra + 5), *(extra + 9), *(extra + 13)); - s2 = addshift(s2, v2, k0, k1, k2, k3, k4, *(extra + 2), *(extra + 6), *(extra + 10), *(extra + 14)); - s3 = addshift(s3, v3, k0, k1, k2, k3, k4, *(extra + 3), *(extra + 7), *(extra + 11), *(extra + 15)); + s0 = addshift(s0, v0, k0, k1, k2, k3, k4, extra[0], extra[4], extra[ 8], extra[12]); + s1 = addshift(s1, v1, k0, k1, k2, k3, k4, extra[1], extra[5], extra[ 9], extra[13]); + s2 = addshift(s2, v2, k0, k1, k2, k3, k4, extra[2], extra[6], extra[10], extra[14]); + s3 = addshift(s3, v3, k0, k1, k2, k3, k4, extra[3], extra[7], extra[11], extra[15]); }; loadsrc(row0, kernel[ 0], kernel[ 1], kernel[ 2], kernel[ 3], kernel[ 4]); @@ -250,9 +250,9 @@ static inline int filter(int start, int end, Filter2D* data, const uchar* src_da dst_data[(x * width + y) * 4 + 3] = std::max(0, std::min((int)std::round(sum3), (int)std::numeric_limits::max())); }; + const int left = data->anchor_x, right = width - (ksize - 1 - data->anchor_x); for (int i = start; i < end; i++) { - const int left = ksize - 1, right = width - (ksize - 1); if (left >= right) { for (int j = 0; j < width; j++) @@ -293,10 +293,10 @@ inline int filter(cvhalFilter2D* context, uchar* src_data, size_t src_step, ucha switch (data->kernel_width) { case 3: - res = invoke(0, height, {filter<3>}, data, src_data, src_step, dst.data(), width, height, full_width, full_height, offset_x, offset_y); + res = invoke(height, {filter<3>}, data, src_data, src_step, dst.data(), width, height, full_width, full_height, offset_x, offset_y); break; case 5: - res = invoke(0, height, {filter<5>}, data, src_data, src_step, dst.data(), width, height, full_width, full_height, offset_x, offset_y); + res = invoke(height, {filter<5>}, data, src_data, src_step, dst.data(), width, height, full_width, full_height, offset_x, offset_y); break; } @@ -355,85 +355,10 @@ inline int sepFilterInit(cvhalFilter2D **context, int src_type, int dst_type, in // the algorithm is copied from 3rdparty/carotene/src/separable_filter.hpp, // in the functor RowFilter3x3S16Generic and ColFilter3x3S16Generic template -static inline int sepFilterRow(int start, int end, sepFilter2D* data, const uchar* src_data, size_t src_step, float* dst_data, int width, int full_width, int offset_x) +static inline int sepFilter(int start, int end, sepFilter2D* data, const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, int width, int height, int full_width, int full_height, int offset_x, int offset_y) { constexpr int noval = std::numeric_limits::max(); - auto access = [&](int y) { - int pj; - if (data->borderType & BORDER_ISOLATED) - { - pj = filter::borderInterpolate(y - data->anchor_x, width, data->borderType & ~BORDER_ISOLATED); - pj = pj < 0 ? noval : pj; - } - else - { - pj = filter::borderInterpolate(offset_x + y - data->anchor_x, full_width, data->borderType); - pj = pj < 0 ? noval : pj - offset_x; - } - return pj; - }; - - const float* kx = reinterpret_cast(data->kernelx_data); - auto process = [&](int x, int y) { - float sum = 0; - for (int i = 0; i < ksize; i++) - { - int p = access(y + i); - if (p != noval) - { - sum += kx[i] * src_data[x * src_step + p]; - } - } - dst_data[x * width + y] = sum; - }; - - for (int i = start; i < end; i++) - { - const int left = ksize - 1, right = width - (ksize - 1); - if (left >= right) - { - for (int j = 0; j < width; j++) - process(i, j); - } - else - { - for (int j = 0; j < left; j++) - process(i, j); - for (int j = right; j < width; j++) - process(i, j); - - int vl; - for (int j = left; j < right; j += vl) - { - vl = __riscv_vsetvl_e8m2(right - j); - const uchar* extra = src_data + i * src_step + j - data->anchor_x; - auto sum = __riscv_vfmv_v_f_f32m8(0, vl); - auto src = __riscv_vfwcvt_f(__riscv_vwcvtu_x(__riscv_vle8_v_u8m2(extra, vl), vl), vl); - sum = __riscv_vfmacc(sum, kx[0], src, vl); - src = __riscv_vfslide1down(src, extra[vl], vl); - sum = __riscv_vfmacc(sum, kx[1], src, vl); - src = __riscv_vfslide1down(src, extra[vl + 1], vl); - sum = __riscv_vfmacc(sum, kx[2], src, vl); - if (ksize == 5) - { - src = __riscv_vfslide1down(src, extra[vl + 2], vl); - sum = __riscv_vfmacc(sum, kx[3], src, vl); - src = __riscv_vfslide1down(src, extra[vl + 3], vl); - sum = __riscv_vfmacc(sum, kx[4], src, vl); - } - __riscv_vse32(dst_data + i * width + j, sum, vl); - } - } - } - - return CV_HAL_ERROR_OK; -} - -template -static inline int sepFilterCol(int start, int end, sepFilter2D* data, const float* src_data, uchar* dst_data, size_t dst_step, int width, int height, int full_height, int offset_y) -{ - constexpr int noval = std::numeric_limits::max(); - auto access = [&](int x) { + auto accessX = [&](int x) { int pi; if (data->borderType & BORDER_ISOLATED) { @@ -447,42 +372,115 @@ static inline int sepFilterCol(int start, int end, sepFilter2D* data, const floa } return pi; }; - - const float* ky = reinterpret_cast(data->kernely_data); - for (int i = start; i < end; i++) - { - const float* row0 = access(i ) == noval ? nullptr : src_data + access(i ) * width; - const float* row1 = access(i + 1) == noval ? nullptr : src_data + access(i + 1) * width; - const float* row2 = access(i + 2) == noval ? nullptr : src_data + access(i + 2) * width; - const float* row3, *row4; - if (ksize == 5) + auto accessY = [&](int y) { + int pj; + if (data->borderType & BORDER_ISOLATED) { - row3 = access(i + 3) == noval ? nullptr : src_data + access(i + 3) * width; - row4 = access(i + 4) == noval ? nullptr : src_data + access(i + 4) * width; + pj = filter::borderInterpolate(y - data->anchor_x, width, data->borderType & ~BORDER_ISOLATED); + pj = pj < 0 ? noval : pj; } - - int vl; - for (int j = 0; j < width; j += vl) + else { - vl = __riscv_vsetvl_e32m4(width - j); - auto v0 = row0 ? __riscv_vle32_v_f32m4(row0 + j, vl) : __riscv_vfmv_v_f_f32m4(0, vl); - auto v1 = row1 ? __riscv_vle32_v_f32m4(row1 + j, vl) : __riscv_vfmv_v_f_f32m4(0, vl); - auto v2 = row2 ? __riscv_vle32_v_f32m4(row2 + j, vl) : __riscv_vfmv_v_f_f32m4(0, vl); - auto sum = __riscv_vfmacc(__riscv_vfmacc(__riscv_vfmacc(__riscv_vfmv_v_f_f32m4(data->delta, vl), ky[0], v0, vl), ky[1], v1, vl), ky[2], v2, vl); + pj = filter::borderInterpolate(offset_x + y - data->anchor_x, full_width, data->borderType); + pj = pj < 0 ? noval : pj - offset_x; + } + return pj; + }; + auto p2idx = [&](int x, int y){ return (x + ksize) % ksize * width + y; }; - if (ksize == 5) + const float* kx = reinterpret_cast(data->kernelx_data); + const float* ky = reinterpret_cast(data->kernely_data); + std::vector res(width * ksize); + auto process = [&](int x, int y) { + float sum = 0; + for (int i = 0; i < ksize; i++) + { + int p = accessY(y + i); + if (p != noval) { - auto v3 = row3 ? __riscv_vle32_v_f32m4(row3 + j, vl) : __riscv_vfmv_v_f_f32m4(0, vl); - auto v4 = row4 ? __riscv_vle32_v_f32m4(row4 + j, vl) : __riscv_vfmv_v_f_f32m4(0, vl); - sum = __riscv_vfmacc(__riscv_vfmacc(sum, ky[3], v3, vl), ky[4], v4, vl); + sum += kx[i] * src_data[x * src_step + p]; } - if (data->dst_type == CV_16SC1) + } + res[p2idx(x, y)] = sum; + }; + + const int left = data->anchor_x, right = width - (ksize - 1 - data->anchor_x); + for (int i = start - data->anchor_y; i < end + (ksize - 1 - data->anchor_y); i++) + { + if (i + offset_y >= 0 && i + offset_y < full_height) + { + if (left >= right) { - __riscv_vse16(reinterpret_cast(dst_data + i * dst_step) + j, __riscv_vfncvt_x(sum, vl), vl); + for (int j = 0; j < width; j++) + process(i, j); } else { - __riscv_vse32(reinterpret_cast(dst_data + i * dst_step) + j, sum, vl); + for (int j = 0; j < left; j++) + process(i, j); + for (int j = right; j < width; j++) + process(i, j); + + int vl; + for (int j = left; j < right; j += vl) + { + vl = __riscv_vsetvl_e8m2(right - j); + const uchar* extra = src_data + i * src_step + j - data->anchor_x; + auto sum = __riscv_vfmv_v_f_f32m8(0, vl); + auto src = __riscv_vfwcvt_f(__riscv_vwcvtu_x(__riscv_vle8_v_u8m2(extra, vl), vl), vl); + sum = __riscv_vfmacc(sum, kx[0], src, vl); + src = __riscv_vfslide1down(src, extra[vl], vl); + sum = __riscv_vfmacc(sum, kx[1], src, vl); + src = __riscv_vfslide1down(src, extra[vl + 1], vl); + sum = __riscv_vfmacc(sum, kx[2], src, vl); + if (ksize == 5) + { + src = __riscv_vfslide1down(src, extra[vl + 2], vl); + sum = __riscv_vfmacc(sum, kx[3], src, vl); + src = __riscv_vfslide1down(src, extra[vl + 3], vl); + sum = __riscv_vfmacc(sum, kx[4], src, vl); + } + __riscv_vse32(res.data() + p2idx(i, j), sum, vl); + } + } + } + + int cur = i - (ksize - 1 - data->anchor_y); + if (cur >= start) + { + const float* row0 = accessX(cur ) == noval ? nullptr : res.data() + p2idx(accessX(cur ), 0); + const float* row1 = accessX(cur + 1) == noval ? nullptr : res.data() + p2idx(accessX(cur + 1), 0); + const float* row2 = accessX(cur + 2) == noval ? nullptr : res.data() + p2idx(accessX(cur + 2), 0); + const float* row3, *row4; + if (ksize == 5) + { + row3 = accessX(cur + 3) == noval ? nullptr : res.data() + p2idx(accessX(cur + 3), 0); + row4 = accessX(cur + 4) == noval ? nullptr : res.data() + p2idx(accessX(cur + 4), 0); + } + + int vl; + for (int j = 0; j < width; j += vl) + { + vl = __riscv_vsetvl_e32m4(width - j); + auto v0 = row0 ? __riscv_vle32_v_f32m4(row0 + j, vl) : __riscv_vfmv_v_f_f32m4(0, vl); + auto v1 = row1 ? __riscv_vle32_v_f32m4(row1 + j, vl) : __riscv_vfmv_v_f_f32m4(0, vl); + auto v2 = row2 ? __riscv_vle32_v_f32m4(row2 + j, vl) : __riscv_vfmv_v_f_f32m4(0, vl); + auto sum = __riscv_vfmacc(__riscv_vfmacc(__riscv_vfmacc(__riscv_vfmv_v_f_f32m4(data->delta, vl), ky[0], v0, vl), ky[1], v1, vl), ky[2], v2, vl); + + if (ksize == 5) + { + auto v3 = row3 ? __riscv_vle32_v_f32m4(row3 + j, vl) : __riscv_vfmv_v_f_f32m4(0, vl); + auto v4 = row4 ? __riscv_vle32_v_f32m4(row4 + j, vl) : __riscv_vfmv_v_f_f32m4(0, vl); + sum = __riscv_vfmacc(__riscv_vfmacc(sum, ky[3], v3, vl), ky[4], v4, vl); + } + if (data->dst_type == CV_16SC1) + { + __riscv_vse16(reinterpret_cast(dst_data + cur * dst_step) + j, __riscv_vfncvt_x(sum, vl), vl); + } + else + { + __riscv_vse32(reinterpret_cast(dst_data + cur * dst_step) + j, sum, vl); + } } } } @@ -493,29 +491,13 @@ static inline int sepFilterCol(int start, int end, sepFilter2D* data, const floa inline int sepFilter(cvhalFilter2D *context, uchar *src_data, size_t src_step, uchar* dst_data, size_t dst_step, int width, int height, int full_width, int full_height, int offset_x, int offset_y) { sepFilter2D* data = reinterpret_cast(context); - const int padding = data->kernelx_length - 1; - std::vector _result(width * (height + 2 * padding)); - float* result = _result.data() + width * padding; - - int res = CV_HAL_ERROR_NOT_IMPLEMENTED; - switch (data->kernelx_length) - { - case 3: - res = filter::invoke(-std::min(offset_y, padding), height + std::min(full_height - height - offset_y, padding), {sepFilterRow<3>}, data, src_data, src_step, result, width, full_width, offset_x); - break; - case 5: - res = filter::invoke(-std::min(offset_y, padding), height + std::min(full_height - height - offset_y, padding), {sepFilterRow<5>}, data, src_data, src_step, result, width, full_width, offset_x); - break; - } - if (res == CV_HAL_ERROR_NOT_IMPLEMENTED) - return CV_HAL_ERROR_NOT_IMPLEMENTED; switch (data->kernelx_length) { case 3: - return filter::invoke(0, height, {sepFilterCol<3>}, data, result, dst_data, dst_step, width, height, full_height, offset_y); + return filter::invoke(height, {sepFilter<3>}, data, src_data, src_step, dst_data, dst_step, width, height, full_width, full_height, offset_x, offset_y); case 5: - return filter::invoke(0, height, {sepFilterCol<5>}, data, result, dst_data, dst_step, width, height, full_height, offset_y); + return filter::invoke(height, {sepFilter<5>}, data, src_data, src_step, dst_data, dst_step, width, height, full_width, full_height, offset_x, offset_y); } return CV_HAL_ERROR_NOT_IMPLEMENTED; @@ -696,9 +678,9 @@ static inline int morph(int start, int end, Morph2D* data, const uchar* src_data } }; + const int left = data->anchor_x, right = width - (2 - data->anchor_x); for (int i = start; i < end; i++) { - const int left = 2, right = width - 2; if (left >= right) { for (int j = 0; j < width; j++) @@ -827,10 +809,10 @@ inline int morph(cvhalFilter2D* context, uchar *src_data, size_t src_step, uchar switch (data->operation) { case CV_HAL_MORPH_ERODE: - res = filter::invoke(0, height, {morph}, data, src_data, src_step, dst.data(), width, height, src_full_width, src_full_height, src_roi_x, src_roi_y); + res = filter::invoke(height, {morph}, data, src_data, src_step, dst.data(), width, height, src_full_width, src_full_height, src_roi_x, src_roi_y); break; case CV_HAL_MORPH_DILATE: - res = filter::invoke(0, height, {morph}, data, src_data, src_step, dst.data(), width, height, src_full_width, src_full_height, src_roi_x, src_roi_y); + res = filter::invoke(height, {morph}, data, src_data, src_step, dst.data(), width, height, src_full_width, src_full_height, src_roi_x, src_roi_y); break; } From 6eaaaa410ec63ecb66b48a23dd5396674e2f5125 Mon Sep 17 00:00:00 2001 From: Liutong HAN Date: Sat, 15 Mar 2025 22:25:31 +0800 Subject: [PATCH 17/94] Merge pull request #27056 from hanliutong:rvv-hal-copyright [RVV HAL] Add copyright and replace '#pragma once'. #27056 Add copyright and in RVV HAL, since other companies or teams may join the development and add their copyright. And the '#pragma once' are replaced. ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [ ] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- 3rdparty/hal_rvv/hal_rvv.hpp | 1 + 3rdparty/hal_rvv/hal_rvv_1p0/atan.hpp | 10 ++++++++-- 3rdparty/hal_rvv/hal_rvv_1p0/cholesky.hpp | 3 +++ 3rdparty/hal_rvv/hal_rvv_1p0/color.hpp | 3 +++ 3rdparty/hal_rvv/hal_rvv_1p0/convert_scale.hpp | 3 +++ 3rdparty/hal_rvv/hal_rvv_1p0/dxt.hpp | 3 +++ 3rdparty/hal_rvv/hal_rvv_1p0/exp.hpp | 8 +++++++- 3rdparty/hal_rvv/hal_rvv_1p0/filter.hpp | 3 +++ 3rdparty/hal_rvv/hal_rvv_1p0/flip.hpp | 9 ++++++++- 3rdparty/hal_rvv/hal_rvv_1p0/log.hpp | 8 +++++++- 3rdparty/hal_rvv/hal_rvv_1p0/lu.hpp | 3 +++ 3rdparty/hal_rvv/hal_rvv_1p0/lut.hpp | 8 +++++++- 3rdparty/hal_rvv/hal_rvv_1p0/mean.hpp | 3 +++ 3rdparty/hal_rvv/hal_rvv_1p0/merge.hpp | 3 +++ 3rdparty/hal_rvv/hal_rvv_1p0/minmax.hpp | 3 +++ 3rdparty/hal_rvv/hal_rvv_1p0/norm.hpp | 3 +++ 3rdparty/hal_rvv/hal_rvv_1p0/norm_diff.hpp | 3 +++ 3rdparty/hal_rvv/hal_rvv_1p0/norm_hamming.hpp | 8 +++++++- 3rdparty/hal_rvv/hal_rvv_1p0/pyramids.hpp | 3 +++ 3rdparty/hal_rvv/hal_rvv_1p0/qr.hpp | 3 +++ 3rdparty/hal_rvv/hal_rvv_1p0/sqrt.hpp | 3 +++ 3rdparty/hal_rvv/hal_rvv_1p0/svd.hpp | 3 +++ 3rdparty/hal_rvv/hal_rvv_1p0/types.hpp | 8 +++++++- 23 files changed, 97 insertions(+), 8 deletions(-) diff --git a/3rdparty/hal_rvv/hal_rvv.hpp b/3rdparty/hal_rvv/hal_rvv.hpp index f2f9aa68d4..92eea59aae 100644 --- a/3rdparty/hal_rvv/hal_rvv.hpp +++ b/3rdparty/hal_rvv/hal_rvv.hpp @@ -20,6 +20,7 @@ #endif #if defined(__riscv_v) && __riscv_v == 1000000 +#include "hal_rvv_1p0/types.hpp" #include "hal_rvv_1p0/merge.hpp" // core #include "hal_rvv_1p0/mean.hpp" // core #include "hal_rvv_1p0/dxt.hpp" // core diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/atan.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/atan.hpp index 2e4f9c2a67..b864fea2c1 100644 --- a/3rdparty/hal_rvv/hal_rvv_1p0/atan.hpp +++ b/3rdparty/hal_rvv/hal_rvv_1p0/atan.hpp @@ -1,7 +1,11 @@ // This file is part of OpenCV project. // It is subject to the license terms in the LICENSE file found in the top-level // directory of this distribution and at http://opencv.org/license.html. -#pragma once + +// Copyright (C) 2025, Institute of Software, Chinese Academy of Sciences. + +#ifndef OPENCV_HAL_RVV_ATAN_HPP_INCLUDED +#define OPENCV_HAL_RVV_ATAN_HPP_INCLUDED #undef cv_hal_fastAtan32f #define cv_hal_fastAtan32f cv::cv_hal_rvv::fast_atan_32 @@ -119,4 +123,6 @@ inline int fast_atan_64(const double* y, const double* x, double* dst, size_t n, return CV_HAL_ERROR_OK; } -}} // namespace cv::cv_hal_rvv +}} // namespace cv::cv_hal_rvv + +#endif //OPENCV_HAL_RVV_ATAN_HPP_INCLUDED diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/cholesky.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/cholesky.hpp index e519a1ad4a..b5d9d3e891 100644 --- a/3rdparty/hal_rvv/hal_rvv_1p0/cholesky.hpp +++ b/3rdparty/hal_rvv/hal_rvv_1p0/cholesky.hpp @@ -1,6 +1,9 @@ // This file is part of OpenCV project. // It is subject to the license terms in the LICENSE file found in the top-level directory // of this distribution and at http://opencv.org/license.html. + +// Copyright (C) 2025, Institute of Software, Chinese Academy of Sciences. + #ifndef OPENCV_HAL_RVV_CHOLESKY_HPP_INCLUDED #define OPENCV_HAL_RVV_CHOLESKY_HPP_INCLUDED diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/color.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/color.hpp index 08272d4272..db86c56723 100644 --- a/3rdparty/hal_rvv/hal_rvv_1p0/color.hpp +++ b/3rdparty/hal_rvv/hal_rvv_1p0/color.hpp @@ -1,6 +1,9 @@ // This file is part of OpenCV project. // It is subject to the license terms in the LICENSE file found in the top-level directory // of this distribution and at http://opencv.org/license.html. + +// Copyright (C) 2025, Institute of Software, Chinese Academy of Sciences. + #ifndef OPENCV_HAL_RVV_COLOR_HPP_INCLUDED #define OPENCV_HAL_RVV_COLOR_HPP_INCLUDED diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/convert_scale.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/convert_scale.hpp index 3a779f5cb3..2f28f20bfd 100644 --- a/3rdparty/hal_rvv/hal_rvv_1p0/convert_scale.hpp +++ b/3rdparty/hal_rvv/hal_rvv_1p0/convert_scale.hpp @@ -1,6 +1,9 @@ // This file is part of OpenCV project. // It is subject to the license terms in the LICENSE file found in the top-level directory // of this distribution and at http://opencv.org/license.html. + +// Copyright (C) 2025, Institute of Software, Chinese Academy of Sciences. + #ifndef OPENCV_HAL_RVV_CONVERT_SCALE_HPP_INCLUDED #define OPENCV_HAL_RVV_CONVERT_SCALE_HPP_INCLUDED diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/dxt.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/dxt.hpp index 11ac133760..25f4879532 100644 --- a/3rdparty/hal_rvv/hal_rvv_1p0/dxt.hpp +++ b/3rdparty/hal_rvv/hal_rvv_1p0/dxt.hpp @@ -1,6 +1,9 @@ // This file is part of OpenCV project. // It is subject to the license terms in the LICENSE file found in the top-level directory // of this distribution and at http://opencv.org/license.html. + +// Copyright (C) 2025, Institute of Software, Chinese Academy of Sciences. + #ifndef OPENCV_HAL_RVV_DXT_HPP_INCLUDED #define OPENCV_HAL_RVV_DXT_HPP_INCLUDED diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/exp.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/exp.hpp index 60efee2b8b..82690fb321 100644 --- a/3rdparty/hal_rvv/hal_rvv_1p0/exp.hpp +++ b/3rdparty/hal_rvv/hal_rvv_1p0/exp.hpp @@ -1,7 +1,11 @@ // This file is part of OpenCV project. // It is subject to the license terms in the LICENSE file found in the top-level // directory of this distribution and at http://opencv.org/license.html. -#pragma once + +// Copyright (C) 2025, Institute of Software, Chinese Academy of Sciences. + +#ifndef OPENCV_HAL_RVV_EXP_HPP_INCLUDED +#define OPENCV_HAL_RVV_EXP_HPP_INCLUDED #include @@ -200,3 +204,5 @@ inline int exp64f(const double* src, double* dst, int _len) } }} // namespace cv::cv_hal_rvv + +#endif //OPENCV_HAL_RVV_EXP_HPP_INCLUDED diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/filter.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/filter.hpp index d4577c8893..70e0b89a83 100644 --- a/3rdparty/hal_rvv/hal_rvv_1p0/filter.hpp +++ b/3rdparty/hal_rvv/hal_rvv_1p0/filter.hpp @@ -1,6 +1,9 @@ // This file is part of OpenCV project. // It is subject to the license terms in the LICENSE file found in the top-level directory // of this distribution and at http://opencv.org/license.html. + +// Copyright (C) 2025, Institute of Software, Chinese Academy of Sciences. + #ifndef OPENCV_HAL_RVV_FILTER_HPP_INCLUDED #define OPENCV_HAL_RVV_FILTER_HPP_INCLUDED diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/flip.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/flip.hpp index 95de5793ed..8d58532832 100644 --- a/3rdparty/hal_rvv/hal_rvv_1p0/flip.hpp +++ b/3rdparty/hal_rvv/hal_rvv_1p0/flip.hpp @@ -1,7 +1,12 @@ // This file is part of OpenCV project. // It is subject to the license terms in the LICENSE file found in the top-level directory // of this distribution and at http://opencv.org/license.html. -#pragma once + +// Copyright (C) 2025, Institute of Software, Chinese Academy of Sciences. + +#ifndef OPENCV_HAL_RVV_FLIP_HPP_INCLUDED +#define OPENCV_HAL_RVV_FLIP_HPP_INCLUDED + #include #include @@ -223,3 +228,5 @@ inline int flip(int src_type, } }} // namespace cv::cv_hal_rvv + +#endif //OPENCV_HAL_RVV_FLIP_HPP_INCLUDED diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/log.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/log.hpp index 02c62f4400..8df0761861 100644 --- a/3rdparty/hal_rvv/hal_rvv_1p0/log.hpp +++ b/3rdparty/hal_rvv/hal_rvv_1p0/log.hpp @@ -1,7 +1,11 @@ // This file is part of OpenCV project. // It is subject to the license terms in the LICENSE file found in the top-level // directory of this distribution and at http://opencv.org/license.html. -#pragma once + +// Copyright (C) 2025, Institute of Software, Chinese Academy of Sciences. + +#ifndef OPENCV_HAL_RVV_LOG_HPP_INCLUDED +#define OPENCV_HAL_RVV_LOG_HPP_INCLUDED #include @@ -379,3 +383,5 @@ inline int log64f(const double* src, double* dst, int _len) } }} // namespace cv::cv_hal_rvv + +#endif //OPENCV_HAL_RVV_LOG_HPP_INCLUDED diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/lu.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/lu.hpp index b90f6de53d..6de137fe82 100644 --- a/3rdparty/hal_rvv/hal_rvv_1p0/lu.hpp +++ b/3rdparty/hal_rvv/hal_rvv_1p0/lu.hpp @@ -1,6 +1,9 @@ // This file is part of OpenCV project. // It is subject to the license terms in the LICENSE file found in the top-level directory // of this distribution and at http://opencv.org/license.html. + +// Copyright (C) 2025, Institute of Software, Chinese Academy of Sciences. + #ifndef OPENCV_HAL_RVV_LU_HPP_INCLUDED #define OPENCV_HAL_RVV_LU_HPP_INCLUDED diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/lut.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/lut.hpp index e869731ce5..26e885632b 100644 --- a/3rdparty/hal_rvv/hal_rvv_1p0/lut.hpp +++ b/3rdparty/hal_rvv/hal_rvv_1p0/lut.hpp @@ -1,7 +1,11 @@ // This file is part of OpenCV project. // It is subject to the license terms in the LICENSE file found in the top-level directory // of this distribution and at http://opencv.org/license.html. -#pragma once + +// Copyright (C) 2025, Institute of Software, Chinese Academy of Sciences. + +#ifndef OPENCV_HAL_RVV_LUT_HPP_INCLUDED +#define OPENCV_HAL_RVV_LUT_HPP_INCLUDED #include #include @@ -188,3 +192,5 @@ inline int lut(const uchar* src_data, } }} // namespace cv::cv_hal_rvv + +#endif //OPENCV_HAL_RVV_LUT_HPP_INCLUDED diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/mean.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/mean.hpp index 4a9ffec500..e8156371b3 100644 --- a/3rdparty/hal_rvv/hal_rvv_1p0/mean.hpp +++ b/3rdparty/hal_rvv/hal_rvv_1p0/mean.hpp @@ -1,6 +1,9 @@ // This file is part of OpenCV project. // It is subject to the license terms in the LICENSE file found in the top-level directory // of this distribution and at http://opencv.org/license.html. + +// Copyright (C) 2025, Institute of Software, Chinese Academy of Sciences. + #ifndef OPENCV_HAL_RVV_MEANSTDDEV_HPP_INCLUDED #define OPENCV_HAL_RVV_MEANSTDDEV_HPP_INCLUDED diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/merge.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/merge.hpp index 760024f429..0b5f21de91 100644 --- a/3rdparty/hal_rvv/hal_rvv_1p0/merge.hpp +++ b/3rdparty/hal_rvv/hal_rvv_1p0/merge.hpp @@ -1,6 +1,9 @@ // This file is part of OpenCV project. // It is subject to the license terms in the LICENSE file found in the top-level directory // of this distribution and at http://opencv.org/license.html. + +// Copyright (C) 2025, Institute of Software, Chinese Academy of Sciences. + #ifndef OPENCV_HAL_RVV_MERGE_HPP_INCLUDED #define OPENCV_HAL_RVV_MERGE_HPP_INCLUDED diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/minmax.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/minmax.hpp index 2e03a3b338..c07a1ff6f7 100644 --- a/3rdparty/hal_rvv/hal_rvv_1p0/minmax.hpp +++ b/3rdparty/hal_rvv/hal_rvv_1p0/minmax.hpp @@ -1,6 +1,9 @@ // This file is part of OpenCV project. // It is subject to the license terms in the LICENSE file found in the top-level directory // of this distribution and at http://opencv.org/license.html. + +// Copyright (C) 2025, Institute of Software, Chinese Academy of Sciences. + #ifndef OPENCV_HAL_RVV_MINMAX_HPP_INCLUDED #define OPENCV_HAL_RVV_MINMAX_HPP_INCLUDED diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/norm.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/norm.hpp index 19577ab2b8..260978f6ee 100644 --- a/3rdparty/hal_rvv/hal_rvv_1p0/norm.hpp +++ b/3rdparty/hal_rvv/hal_rvv_1p0/norm.hpp @@ -1,6 +1,9 @@ // This file is part of OpenCV project. // It is subject to the license terms in the LICENSE file found in the top-level directory // of this distribution and at http://opencv.org/license.html. + +// Copyright (C) 2025, Institute of Software, Chinese Academy of Sciences. + #ifndef OPENCV_HAL_RVV_NORM_HPP_INCLUDED #define OPENCV_HAL_RVV_NORM_HPP_INCLUDED diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/norm_diff.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/norm_diff.hpp index 54125f6beb..cfb9fba6c7 100644 --- a/3rdparty/hal_rvv/hal_rvv_1p0/norm_diff.hpp +++ b/3rdparty/hal_rvv/hal_rvv_1p0/norm_diff.hpp @@ -1,6 +1,9 @@ // This file is part of OpenCV project. // It is subject to the license terms in the LICENSE file found in the top-level directory // of this distribution and at http://opencv.org/license.html. + +// Copyright (C) 2025, Institute of Software, Chinese Academy of Sciences. + #ifndef OPENCV_HAL_RVV_NORM_DIFF_HPP_INCLUDED #define OPENCV_HAL_RVV_NORM_DIFF_HPP_INCLUDED diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/norm_hamming.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/norm_hamming.hpp index 4fa2fe5da3..9c19f62b7e 100644 --- a/3rdparty/hal_rvv/hal_rvv_1p0/norm_hamming.hpp +++ b/3rdparty/hal_rvv/hal_rvv_1p0/norm_hamming.hpp @@ -1,7 +1,11 @@ // This file is part of OpenCV project. // It is subject to the license terms in the LICENSE file found in the top-level directory // of this distribution and at http://opencv.org/license.html. -#pragma once + +// Copyright (C) 2025, Institute of Software, Chinese Academy of Sciences. + +#ifndef OPENCV_HAL_RVV_NORM_HAMMING_HPP_INCLUDED +#define OPENCV_HAL_RVV_NORM_HAMMING_HPP_INCLUDED #include #include @@ -180,3 +184,5 @@ inline int normHammingDiff8u(const uchar* a, const uchar* b, int n, int cellSize } }} // namespace cv::cv_hal_rvv + +#endif //OPENCV_HAL_RVV_NORM_HAMMING_HPP_INCLUDED diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/pyramids.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/pyramids.hpp index 76f040fec8..1b07cabfb2 100644 --- a/3rdparty/hal_rvv/hal_rvv_1p0/pyramids.hpp +++ b/3rdparty/hal_rvv/hal_rvv_1p0/pyramids.hpp @@ -1,6 +1,9 @@ // This file is part of OpenCV project. // It is subject to the license terms in the LICENSE file found in the top-level directory // of this distribution and at http://opencv.org/license.html. + +// Copyright (C) 2025, Institute of Software, Chinese Academy of Sciences. + #ifndef OPENCV_HAL_RVV_PYRAMIDS_HPP_INCLUDED #define OPENCV_HAL_RVV_PYRAMIDS_HPP_INCLUDED diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/qr.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/qr.hpp index 58b6f770fc..a7085e062b 100644 --- a/3rdparty/hal_rvv/hal_rvv_1p0/qr.hpp +++ b/3rdparty/hal_rvv/hal_rvv_1p0/qr.hpp @@ -1,6 +1,9 @@ // This file is part of OpenCV project. // It is subject to the license terms in the LICENSE file found in the top-level directory // of this distribution and at http://opencv.org/license.html. + +// Copyright (C) 2025, Institute of Software, Chinese Academy of Sciences. + #ifndef OPENCV_HAL_RVV_QR_HPP_INCLUDED #define OPENCV_HAL_RVV_QR_HPP_INCLUDED diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/sqrt.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/sqrt.hpp index 9ed72f6818..7653cfc50d 100644 --- a/3rdparty/hal_rvv/hal_rvv_1p0/sqrt.hpp +++ b/3rdparty/hal_rvv/hal_rvv_1p0/sqrt.hpp @@ -1,6 +1,9 @@ // This file is part of OpenCV project. // It is subject to the license terms in the LICENSE file found in the top-level // directory of this distribution and at http://opencv.org/license.html. + +// Copyright (C) 2025, Institute of Software, Chinese Academy of Sciences. + #ifndef OPENCV_HAL_RVV_SQRT_HPP_INCLUDED #define OPENCV_HAL_RVV_SQRT_HPP_INCLUDED diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/svd.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/svd.hpp index 5d22d73227..53225c64a9 100644 --- a/3rdparty/hal_rvv/hal_rvv_1p0/svd.hpp +++ b/3rdparty/hal_rvv/hal_rvv_1p0/svd.hpp @@ -1,6 +1,9 @@ // This file is part of OpenCV project. // It is subject to the license terms in the LICENSE file found in the top-level directory // of this distribution and at http://opencv.org/license.html. + +// Copyright (C) 2025, Institute of Software, Chinese Academy of Sciences. + #ifndef OPENCV_HAL_RVV_SVD_HPP_INCLUDED #define OPENCV_HAL_RVV_SVD_HPP_INCLUDED diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/types.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/types.hpp index 9416e8cd6e..e6d4bd99fb 100644 --- a/3rdparty/hal_rvv/hal_rvv_1p0/types.hpp +++ b/3rdparty/hal_rvv/hal_rvv_1p0/types.hpp @@ -1,7 +1,11 @@ // This file is part of OpenCV project. // It is subject to the license terms in the LICENSE file found in the top-level // directory of this distribution and at http://opencv.org/license.html. -#pragma once + +// Copyright (C) 2025, Institute of Software, Chinese Academy of Sciences. + +#ifndef OPENCV_HAL_RVV_TYPES_HPP_INCLUDED +#define OPENCV_HAL_RVV_TYPES_HPP_INCLUDED #include #include @@ -483,3 +487,5 @@ HAL_RVV_CVT( uint8_t, int8_t, u8, i8, LMUL_f8, mf8) #undef HAL_RVV_CVT }} // namespace cv::cv_hal_rvv + +#endif //OPENCV_HAL_RVV_TYPES_HPP_INCLUDED From b6f213a8c774a946f63cede4221c919a132d62cd Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Mon, 17 Mar 2025 11:24:46 +0300 Subject: [PATCH 18/94] Merge pull request #27079 from MaximSmolskiy:add-test-for-ArucoDetector-detectMarkers Add test for ArucoDetector::detectMarkers #27079 ### Pull Request Readiness Checklist Related to #26968 and #26922 See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [x] The feature is well documented and sample code can be built with the project CMake --- .../objdetect/test/test_arucodetection.cpp | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/modules/objdetect/test/test_arucodetection.cpp b/modules/objdetect/test/test_arucodetection.cpp index fd957698b2..399aa36102 100644 --- a/modules/objdetect/test/test_arucodetection.cpp +++ b/modules/objdetect/test/test_arucodetection.cpp @@ -650,6 +650,40 @@ TEST(CV_ArucoDetectMarkers, regression_contour_24220) } } +TEST(CV_ArucoDetectMarkers, regression_26922) +{ + const auto arucoDict = aruco::getPredefinedDictionary(aruco::DICT_4X4_1000); + const aruco::GridBoard gridBoard(Size(19, 10), 1, 0.25, arucoDict); + + const Size imageSize(7200, 3825); + + Mat boardImage; + gridBoard.generateImage(imageSize, boardImage, 75, 1); + + const aruco::ArucoDetector detector(arucoDict); + + vector> corners; + vector ids; + detector.detectMarkers(boardImage, corners, ids); + + EXPECT_EQ(ids.size(), 190ull); + EXPECT_TRUE(find(ids.begin(), ids.end(), 76) != ids.end()); + EXPECT_TRUE(find(ids.begin(), ids.end(), 172) != ids.end()); + + float transformMatrixData[9] = {1, -0.2f, 300, 0.4f, 1, -1000, 0, 0, 1}; + const Mat transformMatrix(Size(3, 3), CV_32FC1, transformMatrixData); + + Mat warpedImage; + warpPerspective(boardImage, warpedImage, transformMatrix, imageSize); + + detector.detectMarkers(warpedImage, corners, ids); + + EXPECT_EQ(ids.size(), 133ull); + // markers with id 76 and 172 are on border and should not be detected + EXPECT_FALSE(find(ids.begin(), ids.end(), 76) != ids.end()); + EXPECT_FALSE(find(ids.begin(), ids.end(), 172) != ids.end()); +} + TEST(CV_ArucoMultiDict, setGetDictionaries) { vector dictionaries = {aruco::getPredefinedDictionary(aruco::DICT_4X4_50), aruco::getPredefinedDictionary(aruco::DICT_5X5_100)}; From 2090407002b4f91186d758f150e839d2c1a56677 Mon Sep 17 00:00:00 2001 From: GenshinImpactStarts <147074368+GenshinImpactStarts@users.noreply.github.com> Date: Mon, 17 Mar 2025 19:16:09 +0800 Subject: [PATCH 19/94] Merge pull request #26999 from GenshinImpactStarts:polar_to_cart [HAL RVV] unify and impl polar_to_cart | add perf test #26999 ### Summary 1. Implement through the existing `cv_hal_polarToCart32f` and `cv_hal_polarToCart64f` interfaces. 2. Add `polarToCart` performance tests 3. Make `cv::polarToCart` use CALL_HAL in the same way as `cv::cartToPolar` 4. To achieve the 3rd point, the original implementation was moved, and some modifications were made. Tested through: ```sh opencv_test_core --gtest_filter="*PolarToCart*:*Core_CartPolar_reverse*" opencv_perf_core --gtest_filter="*PolarToCart*" --perf_min_samples=300 --perf_force_samples=300 ``` ### HAL performance test ***UPDATE***: Current implementation is no more depending on vlen. **NOTE**: Due to the 4th point in the summary above, the `scalar` and `ui` test is based on the modified code of this PR. The impact of this patch on `scalar` and `ui` is evaluated in the next section, `Effect of Point 4`. Vlen 256 (Muse Pi): ``` Name of Test scalar ui rvv ui rvv vs vs scalar scalar (x-factor) (x-factor) PolarToCart::PolarToCartFixture::(127x61, 32FC1) 0.315 0.110 0.034 2.85 9.34 PolarToCart::PolarToCartFixture::(127x61, 64FC1) 0.423 0.163 0.045 2.59 9.34 PolarToCart::PolarToCartFixture::(640x480, 32FC1) 13.695 4.325 1.278 3.17 10.71 PolarToCart::PolarToCartFixture::(640x480, 64FC1) 17.719 7.118 2.105 2.49 8.42 PolarToCart::PolarToCartFixture::(1280x720, 32FC1) 40.678 13.114 3.977 3.10 10.23 PolarToCart::PolarToCartFixture::(1280x720, 64FC1) 53.124 21.298 6.519 2.49 8.15 PolarToCart::PolarToCartFixture::(1920x1080, 32FC1) 95.158 29.465 8.894 3.23 10.70 PolarToCart::PolarToCartFixture::(1920x1080, 64FC1) 119.262 47.743 14.129 2.50 8.44 ``` ### Effect of Point 4 To make `cv::polarToCart` behave the same as `cv::cartToPolar`, the implementation detail of the former has been moved to the latter's location (from `mathfuncs.cpp` to `mathfuncs_core.simd.hpp`). #### Reason for Changes: This function works as follows: $y = \text{mag} \times \sin(\text{angle})$ and $x = \text{mag} \times \cos(\text{angle})$. The original implementation first calculates the values of $\sin$ and $\cos$, storing the results in the output buffers $x$ and $y$, and then multiplies the result by $\text{mag}$. However, when the function is used as an in-place operation (one of the output buffers is also an input buffer), the original implementation allocates an extra buffer to store the $\sin$ and $\cos$ values in case the $\text{mag}$ value gets overwritten. This extra buffer allocation prevents `cv::polarToCart` from functioning in the same way as `cv::cartToPolar`. Therefore, the multiplication is now performed immediately without storing intermediate values. Since the original implementation also had AVX2 optimizations, I have applied the same optimizations to the AVX2 version of this implementation. ***UPDATE***: UI use v_sincos from #25892 now. The original implementation has AVX2 optimizations but is slower much than current UI so it's removed, and AVX2 perf test is below. Scalar implementation isn't changed because it's faster than using UI's method. #### Test Result `scalar` and `ui` test is done on Muse PI, and AVX2 test is done on Intel(R) Xeon(R) Gold 6140 CPU @ 2.30GHz. `scalar` test: ``` Name of Test orig pr pr vs orig (x-factor) PolarToCart::PolarToCartFixture::(127x61, 32FC1) 0.333 0.294 1.13 PolarToCart::PolarToCartFixture::(127x61, 64FC1) 0.385 0.403 0.96 PolarToCart::PolarToCartFixture::(640x480, 32FC1) 14.749 12.343 1.19 PolarToCart::PolarToCartFixture::(640x480, 64FC1) 19.419 16.743 1.16 PolarToCart::PolarToCartFixture::(1280x720, 32FC1) 44.155 37.822 1.17 PolarToCart::PolarToCartFixture::(1280x720, 64FC1) 62.108 50.358 1.23 PolarToCart::PolarToCartFixture::(1920x1080, 32FC1) 99.011 85.769 1.15 PolarToCart::PolarToCartFixture::(1920x1080, 64FC1) 127.740 112.874 1.13 ``` `ui` test: ``` Name of Test orig pr pr vs orig (x-factor) PolarToCart::PolarToCartFixture::(127x61, 32FC1) 0.306 0.110 2.77 PolarToCart::PolarToCartFixture::(127x61, 64FC1) 0.455 0.163 2.79 PolarToCart::PolarToCartFixture::(640x480, 32FC1) 13.381 4.325 3.09 PolarToCart::PolarToCartFixture::(640x480, 64FC1) 21.851 7.118 3.07 PolarToCart::PolarToCartFixture::(1280x720, 32FC1) 39.975 13.114 3.05 PolarToCart::PolarToCartFixture::(1280x720, 64FC1) 67.006 21.298 3.15 PolarToCart::PolarToCartFixture::(1920x1080, 32FC1) 90.362 29.465 3.07 PolarToCart::PolarToCartFixture::(1920x1080, 64FC1) 129.637 47.743 2.72 ``` AVX2 test: ``` Name of Test orig pr pr vs orig (x-factor) PolarToCart::PolarToCartFixture::(127x61, 32FC1) 0.019 0.009 2.11 PolarToCart::PolarToCartFixture::(127x61, 64FC1) 0.022 0.013 1.74 PolarToCart::PolarToCartFixture::(640x480, 32FC1) 0.788 0.355 2.22 PolarToCart::PolarToCartFixture::(640x480, 64FC1) 1.102 0.618 1.78 PolarToCart::PolarToCartFixture::(1280x720, 32FC1) 2.383 1.042 2.29 PolarToCart::PolarToCartFixture::(1280x720, 64FC1) 3.758 2.316 1.62 PolarToCart::PolarToCartFixture::(1920x1080, 32FC1) 5.577 2.559 2.18 PolarToCart::PolarToCartFixture::(1920x1080, 64FC1) 9.710 6.424 1.51 ``` A slight performance loss occurs because the check for whether $mag$ is nullptr is performed with every calculation, instead of being done once per batch. This is to reuse current `SinCos_32f` function. ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [ ] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- 3rdparty/hal_rvv/hal_rvv.hpp | 1 + .../hal_rvv/hal_rvv_1p0/polar_to_cart.hpp | 53 +++++ 3rdparty/hal_rvv/hal_rvv_1p0/pyramids.hpp | 4 +- 3rdparty/hal_rvv/hal_rvv_1p0/sincos.hpp | 72 ++++++ 3rdparty/hal_rvv/hal_rvv_1p0/types.hpp | 78 +++++- modules/core/perf/perf_math.cpp | 22 ++ modules/core/src/mathfuncs.cpp | 223 +----------------- modules/core/src/mathfuncs_core.dispatch.cpp | 20 ++ modules/core/src/mathfuncs_core.simd.hpp | 163 +++++++++++++ 9 files changed, 411 insertions(+), 225 deletions(-) create mode 100644 3rdparty/hal_rvv/hal_rvv_1p0/polar_to_cart.hpp create mode 100644 3rdparty/hal_rvv/hal_rvv_1p0/sincos.hpp diff --git a/3rdparty/hal_rvv/hal_rvv.hpp b/3rdparty/hal_rvv/hal_rvv.hpp index 92eea59aae..45c7da446d 100644 --- a/3rdparty/hal_rvv/hal_rvv.hpp +++ b/3rdparty/hal_rvv/hal_rvv.hpp @@ -33,6 +33,7 @@ #include "hal_rvv_1p0/split.hpp" // core #include "hal_rvv_1p0/magnitude.hpp" // core #include "hal_rvv_1p0/cart_to_polar.hpp" // core +#include "hal_rvv_1p0/polar_to_cart.hpp" // core #include "hal_rvv_1p0/flip.hpp" // core #include "hal_rvv_1p0/lut.hpp" // core #include "hal_rvv_1p0/exp.hpp" // core diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/polar_to_cart.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/polar_to_cart.hpp new file mode 100644 index 0000000000..feab2047e5 --- /dev/null +++ b/3rdparty/hal_rvv/hal_rvv_1p0/polar_to_cart.hpp @@ -0,0 +1,53 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +// Copyright (C) 2025, Institute of Software, Chinese Academy of Sciences. + +#ifndef OPENCV_HAL_RVV_POLAR_TO_CART_HPP_INCLUDED +#define OPENCV_HAL_RVV_POLAR_TO_CART_HPP_INCLUDED + +#include +#include "hal_rvv_1p0/sincos.hpp" +#include "hal_rvv_1p0/types.hpp" + +namespace cv { namespace cv_hal_rvv { + +#undef cv_hal_polarToCart32f +#define cv_hal_polarToCart32f cv::cv_hal_rvv::polarToCart +#undef cv_hal_polarToCart64f +#define cv_hal_polarToCart64f cv::cv_hal_rvv::polarToCart + +template +inline int + polarToCart(const Elem* mag, const Elem* angle, Elem* x, Elem* y, int len, bool angleInDegrees) +{ + using T = RVV_F32M4; + const auto sincos_scale = angleInDegrees ? detail::sincos_deg_scale : detail::sincos_rad_scale; + + size_t vl; + auto cos_p2 = T::vmv(detail::sincos_cos_p2, T::setvlmax()); + auto cos_p0 = T::vmv(detail::sincos_cos_p0, T::setvlmax()); + for (; len > 0; len -= (int)vl, angle += vl, x += vl, y += vl) + { + vl = RVV_T::setvl(len); + auto vangle = T::cast(RVV_T::vload(angle, vl), vl); + T::VecType vsin, vcos; + detail::SinCos32f(vangle, vsin, vcos, sincos_scale, cos_p2, cos_p0, vl); + if (mag) + { + auto vmag = T::cast(RVV_T::vload(mag, vl), vl); + vsin = __riscv_vfmul(vsin, vmag, vl); + vcos = __riscv_vfmul(vcos, vmag, vl); + mag += vl; + } + RVV_T::vstore(x, RVV_T::cast(vcos, vl), vl); + RVV_T::vstore(y, RVV_T::cast(vsin, vl), vl); + } + + return CV_HAL_ERROR_OK; +} + +}} // namespace cv::cv_hal_rvv + +#endif // OPENCV_HAL_RVV_POLAR_TO_CART_HPP_INCLUDED diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/pyramids.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/pyramids.hpp index 1b07cabfb2..a349d341c5 100644 --- a/3rdparty/hal_rvv/hal_rvv_1p0/pyramids.hpp +++ b/3rdparty/hal_rvv/hal_rvv_1p0/pyramids.hpp @@ -25,8 +25,8 @@ template<> struct rvv using WT = RVV_SameLen; using MT = RVV_SameLen; - static inline WT::VecType vcvt_T_WT(T::VecType a, size_t b) { return WT::cast(MT::cast(a, b), b); } - static inline T::VecType vcvt_WT_T(WT::VecType a, int b, size_t c) { return T::cast(MT::cast(__riscv_vsra(__riscv_vadd(a, 1 << (b - 1), c), b, c), c), c); } + static inline WT::VecType vcvt_T_WT(T::VecType a, size_t b) { return WT::reinterpret(MT::cast(a, b)); } + static inline T::VecType vcvt_WT_T(WT::VecType a, int b, size_t c) { return T::cast(MT::reinterpret(__riscv_vsra(__riscv_vadd(a, 1 << (b - 1), c), b, c)), c); } static inline WT::VecType down0(WT::VecType vec_src0, WT::VecType vec_src1, WT::VecType vec_src2, WT::VecType vec_src3, WT::VecType vec_src4, size_t vl) { return __riscv_vadd(__riscv_vadd(__riscv_vadd(vec_src0, vec_src4, vl), __riscv_vadd(vec_src2, vec_src2, vl), vl), __riscv_vsll(__riscv_vadd(__riscv_vadd(vec_src1, vec_src2, vl), vec_src3, vl), 2, vl), vl); diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/sincos.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/sincos.hpp new file mode 100644 index 0000000000..776d58f42c --- /dev/null +++ b/3rdparty/hal_rvv/hal_rvv_1p0/sincos.hpp @@ -0,0 +1,72 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level +// directory of this distribution and at http://opencv.org/license.html. + +// Copyright (C) 2025, Institute of Software, Chinese Academy of Sciences. + +#ifndef OPENCV_HAL_RVV_SINCOS_HPP_INCLUDED +#define OPENCV_HAL_RVV_SINCOS_HPP_INCLUDED + +#include +#include "hal_rvv_1p0/types.hpp" + +namespace cv { namespace cv_hal_rvv { namespace detail { + +static constexpr size_t sincos_mask = 0x3; + +static constexpr float sincos_rad_scale = 2.f / CV_PI; +static constexpr float sincos_deg_scale = 2.f / 180.f; + +// Taylor expansion coefficients for sin(x*pi/2) and cos(x*pi/2) +static constexpr double sincos_sin_p7 = -0.004681754135319; +static constexpr double sincos_sin_p5 = 0.079692626246167; +static constexpr double sincos_sin_p3 = -0.645964097506246; +static constexpr double sincos_sin_p1 = 1.570796326794897; + +static constexpr double sincos_cos_p8 = 0.000919260274839; +static constexpr double sincos_cos_p6 = -0.020863480763353; +static constexpr double sincos_cos_p4 = 0.253669507901048; +static constexpr double sincos_cos_p2 = -1.233700550136170; +static constexpr double sincos_cos_p0 = 1.000000000000000; + +// Taylor expansion and angle sum identity +// Use 7 LMUL registers (can be reduced to 5 by splitting fmadd to fadd and fmul) +template +static inline void + SinCos32f(T angle, T& sinval, T& cosval, float scale, T cos_p2, T cos_p0, size_t vl) +{ + angle = __riscv_vfmul(angle, scale, vl); + auto round_angle = RVV_ToInt::cast(angle, vl); + auto delta_angle = __riscv_vfsub(angle, RVV_T::cast(round_angle, vl), vl); + auto delta_angle2 = __riscv_vfmul(delta_angle, delta_angle, vl); + + auto sin = __riscv_vfadd(__riscv_vfmul(delta_angle2, sincos_sin_p7, vl), sincos_sin_p5, vl); + sin = __riscv_vfadd(__riscv_vfmul(delta_angle2, sin, vl), sincos_sin_p3, vl); + sin = __riscv_vfadd(__riscv_vfmul(delta_angle2, sin, vl), sincos_sin_p1, vl); + sin = __riscv_vfmul(delta_angle, sin, vl); + + auto cos = __riscv_vfadd(__riscv_vfmul(delta_angle2, sincos_cos_p8, vl), sincos_cos_p6, vl); + cos = __riscv_vfadd(__riscv_vfmul(delta_angle2, cos, vl), sincos_cos_p4, vl); + cos = __riscv_vfmadd(cos, delta_angle2, cos_p2, vl); + cos = __riscv_vfmadd(cos, delta_angle2, cos_p0, vl); + + // idx = 0: sinval = sin, cosval = cos + // idx = 1: sinval = cos, cosval = -sin + // idx = 2: sinval = -sin, cosval = -cos + // idx = 3: sinval = -cos, cosval = sin + auto idx = __riscv_vand(round_angle, sincos_mask, vl); + auto idx1 = __riscv_vmseq(idx, 1, vl); + auto idx2 = __riscv_vmseq(idx, 2, vl); + auto idx3 = __riscv_vmseq(idx, 3, vl); + + auto idx13 = __riscv_vmor(idx1, idx3, vl); + sinval = __riscv_vmerge(sin, cos, idx13, vl); + cosval = __riscv_vmerge(cos, sin, idx13, vl); + + sinval = __riscv_vfneg_mu(__riscv_vmor(idx2, idx3, vl), sinval, sinval, vl); + cosval = __riscv_vfneg_mu(__riscv_vmor(idx1, idx2, vl), cosval, cosval, vl); +} + +}}} // namespace cv::cv_hal_rvv::detail + +#endif // OPENCV_HAL_RVV_SINCOS_HPP_INCLUDED diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/types.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/types.hpp index e6d4bd99fb..71182e0e4f 100644 --- a/3rdparty/hal_rvv/hal_rvv_1p0/types.hpp +++ b/3rdparty/hal_rvv/hal_rvv_1p0/types.hpp @@ -96,6 +96,22 @@ template using RVV_SameLen = RVV; +template struct RVV_ToIntHelper; +template struct RVV_ToUintHelper; +template struct RVV_ToFloatHelper; + +template +using RVV_ToInt = + RVV::type, RVV_T::lmul>; + +template +using RVV_ToUint = + RVV::type, RVV_T::lmul>; + +template +using RVV_ToFloat = + RVV::type, RVV_T::lmul>; + template using RVV_BaseType = RVV; @@ -203,13 +219,15 @@ static inline BaseType vredmax(VecType vs2, BaseType vs1, size_t vl) { using BoolType = HAL_RVV_BOOL_TYPE(__VA_ARGS__); \ using BaseType = v##VEC_TYPE##m1_t; \ \ - static constexpr size_t lmul = LMUL_TYPE; \ + static constexpr RVV_LMUL lmul = LMUL_TYPE; \ \ HAL_RVV_SIZE_RELATED(EEW, TYPE, LMUL, __VA_ARGS__) \ HAL_RVV_SIZE_UNRELATED(__VA_ARGS__) \ \ template \ inline static VecType cast(FROM v, size_t vl); \ + template \ + inline static VecType reinterpret(FROM v); \ }; \ \ template <> \ @@ -304,6 +322,20 @@ HAL_RVV_DEFINE_ONE( float, float32, LMUL_f2, 32, f32, mf2, HAL_RVV_FLOAT_PARAM) // -------------------------------Define cast-------------------------------- +template <> struct RVV_ToIntHelper<1> {using type = int8_t;}; +template <> struct RVV_ToIntHelper<2> {using type = int16_t;}; +template <> struct RVV_ToIntHelper<4> {using type = int32_t;}; +template <> struct RVV_ToIntHelper<8> {using type = int64_t;}; + +template <> struct RVV_ToUintHelper<1> {using type = uint8_t;}; +template <> struct RVV_ToUintHelper<2> {using type = uint16_t;}; +template <> struct RVV_ToUintHelper<4> {using type = uint32_t;}; +template <> struct RVV_ToUintHelper<8> {using type = uint64_t;}; + +template <> struct RVV_ToFloatHelper<2> {using type = _Float16;}; +template <> struct RVV_ToFloatHelper<4> {using type = float;}; +template <> struct RVV_ToFloatHelper<8> {using type = double;}; + #define HAL_RVV_CVT(ONE, TWO) \ template <> \ inline ONE::VecType ONE::cast(TWO::VecType v, size_t vl) { return __riscv_vncvt_x(v, vl); } \ @@ -441,18 +473,52 @@ HAL_RVV_CVT(RVV_F32MF2, RVV_F64M1) #undef HAL_RVV_CVT -#define HAL_RVV_CVT(A, B, A_TYPE, B_TYPE, LMUL_TYPE, LMUL) \ +#define HAL_RVV_CVT(A, B, A_TYPE, B_TYPE, LMUL_TYPE, LMUL, IS_U) \ template <> \ inline RVV::VecType RVV::cast( \ - RVV::VecType v, [[maybe_unused]] size_t vl \ + RVV::VecType v, size_t vl \ ) { \ - return __riscv_vreinterpret_##A_TYPE##LMUL(v); \ + return __riscv_vfcvt_f_x##IS_U##_v_##A_TYPE##LMUL(v, vl); \ } \ template <> \ inline RVV::VecType RVV::cast( \ - RVV::VecType v, [[maybe_unused]] size_t vl \ + RVV::VecType v, size_t vl \ ) { \ - return __riscv_vreinterpret_##B_TYPE##LMUL(v); \ + return __riscv_vfcvt_x##IS_U##_f_v_##B_TYPE##LMUL(v, vl); \ + } + +HAL_RVV_CVT( float, int32_t, f32, i32, LMUL_1, m1, ) +HAL_RVV_CVT( float, int32_t, f32, i32, LMUL_2, m2, ) +HAL_RVV_CVT( float, int32_t, f32, i32, LMUL_4, m4, ) +HAL_RVV_CVT( float, int32_t, f32, i32, LMUL_8, m8, ) +HAL_RVV_CVT( float, int32_t, f32, i32, LMUL_f2, mf2, ) + +HAL_RVV_CVT( float, uint32_t, f32, u32, LMUL_1, m1, u) +HAL_RVV_CVT( float, uint32_t, f32, u32, LMUL_2, m2, u) +HAL_RVV_CVT( float, uint32_t, f32, u32, LMUL_4, m4, u) +HAL_RVV_CVT( float, uint32_t, f32, u32, LMUL_8, m8, u) +HAL_RVV_CVT( float, uint32_t, f32, u32, LMUL_f2, mf2, u) + +HAL_RVV_CVT(double, int64_t, f64, i64, LMUL_1, m1, ) +HAL_RVV_CVT(double, int64_t, f64, i64, LMUL_2, m2, ) +HAL_RVV_CVT(double, int64_t, f64, i64, LMUL_4, m4, ) +HAL_RVV_CVT(double, int64_t, f64, i64, LMUL_8, m8, ) + +HAL_RVV_CVT(double, uint64_t, f64, u64, LMUL_1, m1, u) +HAL_RVV_CVT(double, uint64_t, f64, u64, LMUL_2, m2, u) +HAL_RVV_CVT(double, uint64_t, f64, u64, LMUL_4, m4, u) +HAL_RVV_CVT(double, uint64_t, f64, u64, LMUL_8, m8, u) + +#undef HAL_RVV_CVT + +#define HAL_RVV_CVT(A, B, A_TYPE, B_TYPE, LMUL_TYPE, LMUL) \ + template <> \ + inline RVV::VecType RVV::reinterpret(RVV::VecType v) { \ + return __riscv_vreinterpret_##A_TYPE##LMUL(v); \ + } \ + template <> \ + inline RVV::VecType RVV::reinterpret(RVV::VecType v) { \ + return __riscv_vreinterpret_##B_TYPE##LMUL(v); \ } #define HAL_RVV_CVT2(A, B, A_TYPE, B_TYPE) \ diff --git a/modules/core/perf/perf_math.cpp b/modules/core/perf/perf_math.cpp index 398a3ad651..e06e281592 100644 --- a/modules/core/perf/perf_math.cpp +++ b/modules/core/perf/perf_math.cpp @@ -79,6 +79,28 @@ PERF_TEST_P(CartToPolarFixture, CartToPolar, SANITY_CHECK_NOTHING(); } +///////////// Polar to Cart ///////////// + +typedef Size_MatType PolarToCartFixture; + +PERF_TEST_P(PolarToCartFixture, PolarToCart, + testing::Combine(testing::Values(TYPICAL_MAT_SIZES), testing::Values(CV_32F, CV_64F))) +{ + cv::Size size = std::get<0>(GetParam()); + int type = std::get<1>(GetParam()); + + cv::Mat magnitude(size, type); + cv::Mat angle(size, type); + cv::Mat x(size, type); + cv::Mat y(size, type); + + declare.in(magnitude, angle, WARMUP_RNG).out(x, y); + + TEST_CYCLE() cv::polarToCart(magnitude, angle, x, y); + + SANITY_CHECK_NOTHING(); +} + // generates random vectors, performs Gram-Schmidt orthogonalization on them Mat randomOrtho(int rows, int ftype, RNG& rng) { diff --git a/modules/core/src/mathfuncs.cpp b/modules/core/src/mathfuncs.cpp index 2ba2a4001e..0403b75452 100644 --- a/modules/core/src/mathfuncs.cpp +++ b/modules/core/src/mathfuncs.cpp @@ -328,149 +328,6 @@ void cartToPolar( InputArray src1, InputArray src2, * Polar -> Cartezian * \****************************************************************************************/ -static void SinCos_32f( const float *angle, float *sinval, float* cosval, - int len, int angle_in_degrees ) -{ - const int N = 64; - - static const double sin_table[] = - { - 0.00000000000000000000, 0.09801714032956060400, - 0.19509032201612825000, 0.29028467725446233000, - 0.38268343236508978000, 0.47139673682599764000, - 0.55557023301960218000, 0.63439328416364549000, - 0.70710678118654746000, 0.77301045336273699000, - 0.83146961230254524000, 0.88192126434835494000, - 0.92387953251128674000, 0.95694033573220894000, - 0.98078528040323043000, 0.99518472667219682000, - 1.00000000000000000000, 0.99518472667219693000, - 0.98078528040323043000, 0.95694033573220894000, - 0.92387953251128674000, 0.88192126434835505000, - 0.83146961230254546000, 0.77301045336273710000, - 0.70710678118654757000, 0.63439328416364549000, - 0.55557023301960218000, 0.47139673682599786000, - 0.38268343236508989000, 0.29028467725446239000, - 0.19509032201612861000, 0.09801714032956082600, - 0.00000000000000012246, -0.09801714032956059000, - -0.19509032201612836000, -0.29028467725446211000, - -0.38268343236508967000, -0.47139673682599764000, - -0.55557023301960196000, -0.63439328416364527000, - -0.70710678118654746000, -0.77301045336273666000, - -0.83146961230254524000, -0.88192126434835494000, - -0.92387953251128652000, -0.95694033573220882000, - -0.98078528040323032000, -0.99518472667219693000, - -1.00000000000000000000, -0.99518472667219693000, - -0.98078528040323043000, -0.95694033573220894000, - -0.92387953251128663000, -0.88192126434835505000, - -0.83146961230254546000, -0.77301045336273688000, - -0.70710678118654768000, -0.63439328416364593000, - -0.55557023301960218000, -0.47139673682599792000, - -0.38268343236509039000, -0.29028467725446250000, - -0.19509032201612872000, -0.09801714032956050600, - }; - - static const double k2 = (2*CV_PI)/N; - - static const double sin_a0 = -0.166630293345647*k2*k2*k2; - static const double sin_a2 = k2; - - static const double cos_a0 = -0.499818138450326*k2*k2; - /*static const double cos_a2 = 1;*/ - - double k1; - int i = 0; - - if( !angle_in_degrees ) - k1 = N/(2*CV_PI); - else - k1 = N/360.; - -#if CV_AVX2 - if (USE_AVX2) - { - __m128d v_k1 = _mm_set1_pd(k1); - __m128d v_1 = _mm_set1_pd(1); - __m128i v_N1 = _mm_set1_epi32(N - 1); - __m128i v_N4 = _mm_set1_epi32(N >> 2); - __m128d v_sin_a0 = _mm_set1_pd(sin_a0); - __m128d v_sin_a2 = _mm_set1_pd(sin_a2); - __m128d v_cos_a0 = _mm_set1_pd(cos_a0); - - for ( ; i <= len - 4; i += 4) - { - __m128 v_angle = _mm_loadu_ps(angle + i); - - // 0-1 - __m128d v_t = _mm_mul_pd(_mm_cvtps_pd(v_angle), v_k1); - __m128i v_it = _mm_cvtpd_epi32(v_t); - v_t = _mm_sub_pd(v_t, _mm_cvtepi32_pd(v_it)); - - __m128i v_sin_idx = _mm_and_si128(v_it, v_N1); - __m128i v_cos_idx = _mm_and_si128(_mm_sub_epi32(v_N4, v_sin_idx), v_N1); - - __m128d v_t2 = _mm_mul_pd(v_t, v_t); - __m128d v_sin_b = _mm_mul_pd(_mm_add_pd(_mm_mul_pd(v_sin_a0, v_t2), v_sin_a2), v_t); - __m128d v_cos_b = _mm_add_pd(_mm_mul_pd(v_cos_a0, v_t2), v_1); - - __m128d v_sin_a = _mm_i32gather_pd(sin_table, v_sin_idx, 8); - __m128d v_cos_a = _mm_i32gather_pd(sin_table, v_cos_idx, 8); - - __m128d v_sin_val_0 = _mm_add_pd(_mm_mul_pd(v_sin_a, v_cos_b), - _mm_mul_pd(v_cos_a, v_sin_b)); - __m128d v_cos_val_0 = _mm_sub_pd(_mm_mul_pd(v_cos_a, v_cos_b), - _mm_mul_pd(v_sin_a, v_sin_b)); - - // 2-3 - v_t = _mm_mul_pd(_mm_cvtps_pd(_mm_castsi128_ps(_mm_srli_si128(_mm_castps_si128(v_angle), 8))), v_k1); - v_it = _mm_cvtpd_epi32(v_t); - v_t = _mm_sub_pd(v_t, _mm_cvtepi32_pd(v_it)); - - v_sin_idx = _mm_and_si128(v_it, v_N1); - v_cos_idx = _mm_and_si128(_mm_sub_epi32(v_N4, v_sin_idx), v_N1); - - v_t2 = _mm_mul_pd(v_t, v_t); - v_sin_b = _mm_mul_pd(_mm_add_pd(_mm_mul_pd(v_sin_a0, v_t2), v_sin_a2), v_t); - v_cos_b = _mm_add_pd(_mm_mul_pd(v_cos_a0, v_t2), v_1); - - v_sin_a = _mm_i32gather_pd(sin_table, v_sin_idx, 8); - v_cos_a = _mm_i32gather_pd(sin_table, v_cos_idx, 8); - - __m128d v_sin_val_1 = _mm_add_pd(_mm_mul_pd(v_sin_a, v_cos_b), - _mm_mul_pd(v_cos_a, v_sin_b)); - __m128d v_cos_val_1 = _mm_sub_pd(_mm_mul_pd(v_cos_a, v_cos_b), - _mm_mul_pd(v_sin_a, v_sin_b)); - - _mm_storeu_ps(sinval + i, _mm_movelh_ps(_mm_cvtpd_ps(v_sin_val_0), - _mm_cvtpd_ps(v_sin_val_1))); - _mm_storeu_ps(cosval + i, _mm_movelh_ps(_mm_cvtpd_ps(v_cos_val_0), - _mm_cvtpd_ps(v_cos_val_1))); - } - } -#endif - - for( ; i < len; i++ ) - { - double t = angle[i]*k1; - int it = cvRound(t); - t -= it; - int sin_idx = it & (N - 1); - int cos_idx = (N/4 - sin_idx) & (N - 1); - - double sin_b = (sin_a0*t*t + sin_a2)*t; - double cos_b = cos_a0*t*t + 1; - - double sin_a = sin_table[sin_idx]; - double cos_a = sin_table[cos_idx]; - - double sin_val = sin_a*cos_b + cos_a*sin_b; - double cos_val = cos_a*cos_b - sin_a*sin_b; - - sinval[i] = (float)sin_val; - cosval[i] = (float)cos_val; - } -} - - #ifdef HAVE_OPENCL static bool ocl_polarToCart( InputArray _mag, InputArray _angle, @@ -587,11 +444,13 @@ void polarToCart( InputArray src1, InputArray src2, CV_Assert(dst1.getObj() != dst2.getObj()); +#ifdef HAVE_IPP const bool isInPlace = (src1.getObj() == dst1.getObj()) || (src1.getObj() == dst2.getObj()) || (src2.getObj() == dst1.getObj()) || (src2.getObj() == dst2.getObj()); +#endif int type = src2.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type); CV_Assert((depth == CV_32F || depth == CV_64F) && (src1.empty() || src1.type() == type)); @@ -610,95 +469,25 @@ void polarToCart( InputArray src1, InputArray src2, const Mat* arrays[] = {&Mag, &Angle, &X, &Y, 0}; uchar* ptrs[4] = {}; NAryMatIterator it(arrays, ptrs); - cv::AutoBuffer _buf; - float* buf[2] = {0, 0}; - int j, k, total = (int)(it.size*cn), blockSize = std::min(total, ((BLOCK_SIZE+cn-1)/cn)*cn); + int j, total = (int)(it.size*cn), blockSize = std::min(total, ((BLOCK_SIZE+cn-1)/cn)*cn); size_t esz1 = Angle.elemSize1(); - if (( depth == CV_64F ) || isInPlace) - { - _buf.allocate(blockSize*2); - buf[0] = _buf.data(); - buf[1] = buf[0] + blockSize; - } - for( size_t i = 0; i < it.nplanes; i++, ++it ) { for( j = 0; j < total; j += blockSize ) { int len = std::min(total - j, blockSize); - if (( depth == CV_32F ) && !isInPlace) + if ( depth == CV_32F ) { const float *mag = (const float*)ptrs[0], *angle = (const float*)ptrs[1]; float *x = (float*)ptrs[2], *y = (float*)ptrs[3]; - - SinCos_32f( angle, y, x, len, angleInDegrees ); - if( mag ) - { - k = 0; - -#if (CV_SIMD || CV_SIMD_SCALABLE) - int cWidth = VTraits::vlanes(); - for( ; k <= len - cWidth; k += cWidth ) - { - v_float32 v_m = vx_load(mag + k); - v_store(x + k, v_mul(vx_load(x + k), v_m)); - v_store(y + k, v_mul(vx_load(y + k), v_m)); - } - vx_cleanup(); -#endif - - for( ; k < len; k++ ) - { - float m = mag[k]; - x[k] *= m; y[k] *= m; - } - } - } - else if (( depth == CV_32F ) && isInPlace) - { - const float *mag = (const float*)ptrs[0], *angle = (const float*)ptrs[1]; - float *x = (float*)ptrs[2], *y = (float*)ptrs[3]; - - for( k = 0; k < len; k++ ) - buf[0][k] = (float)angle[k]; - - SinCos_32f( buf[0], buf[1], buf[0], len, angleInDegrees ); - if( mag ) - for( k = 0; k < len; k++ ) - { - float m = mag[k]; - x[k] = buf[0][k]*m; y[k] = buf[1][k]*m; - } - else - { - std::memcpy(x, buf[0], sizeof(float) * len); - std::memcpy(y, buf[1], sizeof(float) * len); - } + hal::polarToCart32f( mag, angle, x, y, len, angleInDegrees ); } else { const double *mag = (const double*)ptrs[0], *angle = (const double*)ptrs[1]; double *x = (double*)ptrs[2], *y = (double*)ptrs[3]; - - for( k = 0; k < len; k++ ) - buf[0][k] = (float)angle[k]; - - SinCos_32f( buf[0], buf[1], buf[0], len, angleInDegrees ); - if( mag ) - for( k = 0; k < len; k++ ) - { - double m = mag[k]; - x[k] = buf[0][k]*m; y[k] = buf[1][k]*m; - } - else - { - for( k = 0; k < len; k++ ) - { - x[k] = buf[0][k]; - y[k] = buf[1][k]; - } - } + hal::polarToCart64f( mag, angle, x, y, len, angleInDegrees ); } if( ptrs[0] ) diff --git a/modules/core/src/mathfuncs_core.dispatch.cpp b/modules/core/src/mathfuncs_core.dispatch.cpp index 485eac27b4..84e4e6a652 100644 --- a/modules/core/src/mathfuncs_core.dispatch.cpp +++ b/modules/core/src/mathfuncs_core.dispatch.cpp @@ -29,6 +29,26 @@ void cartToPolar64f(const double* x, const double* y, double* mag, double* angle CV_CPU_DISPATCH_MODES_ALL); } +void polarToCart32f(const float* mag, const float* angle, float* x, float* y, int len, bool angleInDegrees) +{ + CV_INSTRUMENT_REGION(); + + CALL_HAL(polarToCart32f, cv_hal_polarToCart32f, mag, angle, x, y, len, angleInDegrees); + + CV_CPU_DISPATCH(polarToCart32f, (mag, angle, x, y, len, angleInDegrees), + CV_CPU_DISPATCH_MODES_ALL); +} + +void polarToCart64f(const double* mag, const double* angle, double* x, double* y, int len, bool angleInDegrees) +{ + CV_INSTRUMENT_REGION(); + + CALL_HAL(polarToCart64f, cv_hal_polarToCart64f, mag, angle, x, y, len, angleInDegrees); + + CV_CPU_DISPATCH(polarToCart64f, (mag, angle, x, y, len, angleInDegrees), + CV_CPU_DISPATCH_MODES_ALL); +} + void fastAtan32f(const float *Y, const float *X, float *angle, int len, bool angleInDegrees ) { CV_INSTRUMENT_REGION(); diff --git a/modules/core/src/mathfuncs_core.simd.hpp b/modules/core/src/mathfuncs_core.simd.hpp index e9d57a5c4d..d9289ecb4e 100644 --- a/modules/core/src/mathfuncs_core.simd.hpp +++ b/modules/core/src/mathfuncs_core.simd.hpp @@ -11,6 +11,8 @@ CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN // forward declarations void cartToPolar32f(const float *X, const float *Y, float* mag, float *angle, int len, bool angleInDegrees); void cartToPolar64f(const double *X, const double *Y, double* mag, double *angle, int len, bool angleInDegrees); +void polarToCart32f(const float *mag, const float *angle, float *X, float *Y, int len, bool angleInDegrees); +void polarToCart64f(const double *mag, const double *angle, double *X, double *Y, int len, bool angleInDegrees); void fastAtan32f(const float *Y, const float *X, float *angle, int len, bool angleInDegrees); void fastAtan64f(const double *Y, const double *X, double *angle, int len, bool angleInDegrees); void fastAtan2(const float *Y, const float *X, float *angle, int len, bool angleInDegrees); @@ -178,6 +180,167 @@ void cartToPolar64f(const double *X, const double *Y, double *mag, double *angle } } +namespace { + +static inline void SinCos_32f(const float* mag, const float* angle, float* cosval, float* sinval, int len, int angle_in_degrees) +{ + const int N = 64; + + static const double sin_table[] = + { + 0.00000000000000000000, 0.09801714032956060400, + 0.19509032201612825000, 0.29028467725446233000, + 0.38268343236508978000, 0.47139673682599764000, + 0.55557023301960218000, 0.63439328416364549000, + 0.70710678118654746000, 0.77301045336273699000, + 0.83146961230254524000, 0.88192126434835494000, + 0.92387953251128674000, 0.95694033573220894000, + 0.98078528040323043000, 0.99518472667219682000, + 1.00000000000000000000, 0.99518472667219693000, + 0.98078528040323043000, 0.95694033573220894000, + 0.92387953251128674000, 0.88192126434835505000, + 0.83146961230254546000, 0.77301045336273710000, + 0.70710678118654757000, 0.63439328416364549000, + 0.55557023301960218000, 0.47139673682599786000, + 0.38268343236508989000, 0.29028467725446239000, + 0.19509032201612861000, 0.09801714032956082600, + 0.00000000000000012246, -0.09801714032956059000, + -0.19509032201612836000, -0.29028467725446211000, + -0.38268343236508967000, -0.47139673682599764000, + -0.55557023301960196000, -0.63439328416364527000, + -0.70710678118654746000, -0.77301045336273666000, + -0.83146961230254524000, -0.88192126434835494000, + -0.92387953251128652000, -0.95694033573220882000, + -0.98078528040323032000, -0.99518472667219693000, + -1.00000000000000000000, -0.99518472667219693000, + -0.98078528040323043000, -0.95694033573220894000, + -0.92387953251128663000, -0.88192126434835505000, + -0.83146961230254546000, -0.77301045336273688000, + -0.70710678118654768000, -0.63439328416364593000, + -0.55557023301960218000, -0.47139673682599792000, + -0.38268343236509039000, -0.29028467725446250000, + -0.19509032201612872000, -0.09801714032956050600, + }; + + static const double k2 = (2*CV_PI)/N; + + static const double sin_a0 = -0.166630293345647*k2*k2*k2; + static const double sin_a2 = k2; + + static const double cos_a0 = -0.499818138450326*k2*k2; + /*static const double cos_a2 = 1;*/ + + double k1; + int i = 0; + + if( !angle_in_degrees ) + k1 = N/(2*CV_PI); + else + k1 = N/360.; + +#if (CV_SIMD || CV_SIMD_SCALABLE) + const int VECSZ = VTraits::vlanes(); + const v_float32 scale = vx_setall_f32(angle_in_degrees ? (float)CV_PI / 180.f : 1.f); + + for( ; i < len; i += VECSZ*2 ) + { + if( i + VECSZ*2 > len ) + { + // if it's inplace operation, we cannot repeatedly process + // the tail for the second time, so we have to use the + // scalar code + if( i == 0 || angle == cosval || angle == sinval || mag == cosval || mag == sinval ) + break; + i = len - VECSZ*2; + } + + v_float32 r0 = v_mul(vx_load(angle + i), scale); + v_float32 r1 = v_mul(vx_load(angle + i + VECSZ), scale); + + v_float32 c0, c1, s0, s1; + v_sincos(r0, s0, c0); + v_sincos(r1, s1, c1); + + if( mag ) + { + v_float32 m0 = vx_load(mag + i); + v_float32 m1 = vx_load(mag + i + VECSZ); + c0 = v_mul(c0, m0); + c1 = v_mul(c1, m1); + s0 = v_mul(s0, m0); + s1 = v_mul(s1, m1); + } + + v_store(cosval + i, c0); + v_store(cosval + i + VECSZ, c1); + + v_store(sinval + i, s0); + v_store(sinval + i + VECSZ, s1); + } + vx_cleanup(); +#endif + + for( ; i < len; i++ ) + { + double t = angle[i]*k1; + int it = cvRound(t); + t -= it; + int sin_idx = it & (N - 1); + int cos_idx = (N/4 - sin_idx) & (N - 1); + + double sin_b = (sin_a0*t*t + sin_a2)*t; + double cos_b = cos_a0*t*t + 1; + + double sin_a = sin_table[sin_idx]; + double cos_a = sin_table[cos_idx]; + + double sin_val = sin_a*cos_b + cos_a*sin_b; + double cos_val = cos_a*cos_b - sin_a*sin_b; + + if (mag) + { + double mag_val = mag[i]; + sin_val *= mag_val; + cos_val *= mag_val; + } + + sinval[i] = (float)sin_val; + cosval[i] = (float)cos_val; + } +} + +} // anonymous:: + +void polarToCart32f(const float *mag, const float *angle, float *X, float *Y, int len, bool angleInDegrees) +{ + CV_INSTRUMENT_REGION(); + SinCos_32f(mag, angle, X, Y, len, angleInDegrees); +} + +void polarToCart64f(const double *mag, const double *angle, double *X, double *Y, int len, bool angleInDegrees) +{ + CV_INSTRUMENT_REGION(); + + const int BLKSZ = 128; + float ybuf[BLKSZ], xbuf[BLKSZ], _mbuf[BLKSZ], abuf[BLKSZ]; + float* mbuf = mag ? _mbuf : nullptr; + for( int i = 0; i < len; i += BLKSZ ) + { + int j, blksz = std::min(BLKSZ, len - i); + for( j = 0; j < blksz; j++ ) + { + if (mbuf) + mbuf[j] = (float)mag[i + j]; + abuf[j] = (float)angle[i + j]; + } + SinCos_32f(mbuf, abuf, xbuf, ybuf, blksz, angleInDegrees); + for( j = 0; j < blksz; j++ ) + X[i + j] = xbuf[j]; + for( j = 0; j < blksz; j++ ) + Y[i + j] = ybuf[j]; + } +} + static void fastAtan32f_(const float *Y, const float *X, float *angle, int len, bool angleInDegrees ) { float scale = angleInDegrees ? 1.f : (float)(CV_PI/180); From 855f20fdfee3701796f2731eb42ce9f0984d9f05 Mon Sep 17 00:00:00 2001 From: iiiuhuy Date: Mon, 17 Mar 2025 16:36:06 +0000 Subject: [PATCH 20/94] Merge pull request #27082 from iiiuhuy:fix_bug displayOverlay doesn't disappear after timeout #27082 Fixes #26555 ### Expected Behaviour An overlay should be displayed atop an image and then disappear after `delayms` has timed out, but it doesn't. Also, `displayStatusBar` doesn't appear to set any text on the window. ### Actual Behaviour The overlay appears but doesn't disappear unless a mouse move event happens on the image. ### Changes - Fixed the issue with `displayOverlay` not disappearing after the timeout. ### Checklist - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV. - [x] The PR is proposed to the proper branch. - [x] There is a reference to the original bug report and related work. - [ ] There is accuracy test, performance test, and test data in the opencv_extra repository, if applicable. - [ ] The feature is well documented, and sample code can be built with the project CMake. --- modules/highgui/src/window_QT.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/highgui/src/window_QT.cpp b/modules/highgui/src/window_QT.cpp index 726cb69695..c8957d9536 100644 --- a/modules/highgui/src/window_QT.cpp +++ b/modules/highgui/src/window_QT.cpp @@ -2949,6 +2949,7 @@ void DefaultViewPort::stopDisplayInfo() { timerDisplay->stop(); drawInfo = false; + viewport()->update(); } From 0142231e4d14e2453e7a6f62d7ac4a91f634f08f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=A9=E9=9F=B3=E3=81=82=E3=82=81?= Date: Tue, 18 Mar 2025 14:24:00 +0800 Subject: [PATCH 21/94] Merge pull request #27072 from amane-ame:thresh_hal_rvv Add RISC-V HAL implementation for cv::threshold and cv::adaptiveThreshold #27072 This patch implements `cv_hal_threshold_otsu` and `cv_hal_adaptiveThreshold` using native intrinsics, optimizing the performance of `cv::threshold(THRESH_OTSU)` and `cv::adaptiveThreshold`. Since UI is as fast as HAL `cv_hal_rvv::threshold::threshold` so `cv_hal_threshold` is not redirected, but this part of HAL is keeped because `cv_hal_threshold_otsu` depends on it. Tested on MUSE-PI (Spacemit X60) for both gcc 14.2 and clang 20.0. ``` $ ./opencv_test_imgproc --gtest_filter="*thresh*:*Thresh*" $ ./opencv_perf_imgproc --gtest_filter="*otsu*:*adaptiveThreshold*" --perf_min_samples=1000 --perf_force_samples=1000 ``` ![image](https://github.com/user-attachments/assets/4bb953f8-8589-4af1-8f1c-99e2c506be3c) ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [ ] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- 3rdparty/hal_rvv/hal_rvv.hpp | 1 + 3rdparty/hal_rvv/hal_rvv_1p0/thresh.hpp | 482 ++++++++++++++++++++++++ 3rdparty/hal_rvv/hal_rvv_1p0/types.hpp | 6 + 3 files changed, 489 insertions(+) create mode 100644 3rdparty/hal_rvv/hal_rvv_1p0/thresh.hpp diff --git a/3rdparty/hal_rvv/hal_rvv.hpp b/3rdparty/hal_rvv/hal_rvv.hpp index 45c7da446d..2135cc355d 100644 --- a/3rdparty/hal_rvv/hal_rvv.hpp +++ b/3rdparty/hal_rvv/hal_rvv.hpp @@ -47,6 +47,7 @@ #include "hal_rvv_1p0/filter.hpp" // imgproc #include "hal_rvv_1p0/pyramids.hpp" // imgproc #include "hal_rvv_1p0/color.hpp" // imgproc +#include "hal_rvv_1p0/thresh.hpp" // imgproc #endif #endif diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/thresh.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/thresh.hpp new file mode 100644 index 0000000000..42b231b26d --- /dev/null +++ b/3rdparty/hal_rvv/hal_rvv_1p0/thresh.hpp @@ -0,0 +1,482 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +// Copyright (C) 2025, Institute of Software, Chinese Academy of Sciences. + +#ifndef OPENCV_HAL_RVV_THRESH_HPP_INCLUDED +#define OPENCV_HAL_RVV_THRESH_HPP_INCLUDED + +#include "hal_rvv_1p0/types.hpp" +#include + +namespace cv { namespace cv_hal_rvv { + +namespace threshold { +// disabled since UI is fast enough, only called in threshold_otsu +// #undef cv_hal_threshold +// #define cv_hal_threshold cv::cv_hal_rvv::threshold::threshold + +class ThresholdInvoker : public ParallelLoopBody +{ +public: + template + ThresholdInvoker(std::function _func, Args&&... args) + { + func = std::bind(_func, std::placeholders::_1, std::placeholders::_2, std::forward(args)...); + } + + virtual void operator()(const Range& range) const override + { + func(range.start, range.end); + } + +private: + std::function func; +}; + +template +static inline int invoke(int width, int height, std::function func, Args&&... args) +{ + cv::parallel_for_(Range(1, height), ThresholdInvoker(func, std::forward(args)...), static_cast((width - 1) * height) / (1 << 15)); + return func(0, 1, std::forward(args)...); +} + +template struct rvv; +template<> struct rvv +{ + static inline vuint8m4_t vmerge(vuint8m4_t a, uchar b, vbool2_t c, size_t d) { return __riscv_vmerge(a, b, c, d); } +}; +template<> struct rvv +{ + static inline vint16m4_t vmerge(vint16m4_t a, short b, vbool4_t c, size_t d) { return __riscv_vmerge(a, b, c, d); } +}; +template<> struct rvv +{ + static inline vfloat32m4_t vmerge(vfloat32m4_t a, float b, vbool8_t c, size_t d) { return __riscv_vfmerge(a, b, c, d); } +}; +template<> struct rvv +{ + static inline vfloat64m4_t vmerge(vfloat64m4_t a, double b, vbool16_t c, size_t d) { return __riscv_vfmerge(a, b, c, d); } +}; + +// the algorithm is copied from imgproc/src/thresh.cpp, +// in the functor ThresholdRunner +template +static inline int threshold(int start, int end, const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, int width, T tval, T mval) +{ + auto zero = helper::vmv(0, helper::setvlmax()); + for (int i = start; i < end; i++) + { + const T* src = reinterpret_cast(src_data + i * src_step); + T* dst = reinterpret_cast(dst_data + i * dst_step); + int vl0, vl1; + for (int j = 0; j < width; j += vl0 + vl1) + { + vl0 = helper::setvl(width - j); + vl1 = helper::setvl(width - j - vl0); + auto src0 = helper::vload(src + j, vl0); + auto src1 = helper::vload(src + j + vl0, vl1); + + typename helper::VecType dst0, dst1; + switch (type) + { + case CV_HAL_THRESH_BINARY: + dst0 = rvv::vmerge(zero, mval, helper::vmgt(src0, tval, vl0), vl0); + dst1 = rvv::vmerge(zero, mval, helper::vmgt(src1, tval, vl1), vl1); + break; + case CV_HAL_THRESH_BINARY_INV: + dst0 = rvv::vmerge(zero, mval, helper::vmle(src0, tval, vl0), vl0); + dst1 = rvv::vmerge(zero, mval, helper::vmle(src1, tval, vl1), vl1); + break; + case CV_HAL_THRESH_TRUNC: + dst0 = rvv::vmerge(src0, tval, helper::vmgt(src0, tval, vl0), vl0); + dst1 = rvv::vmerge(src1, tval, helper::vmgt(src1, tval, vl1), vl1); + break; + case CV_HAL_THRESH_TOZERO: + dst0 = rvv::vmerge(src0, 0, helper::vmle(src0, tval, vl0), vl0); + dst1 = rvv::vmerge(src1, 0, helper::vmle(src1, tval, vl1), vl1); + break; + case CV_HAL_THRESH_TOZERO_INV: + dst0 = rvv::vmerge(src0, 0, helper::vmgt(src0, tval, vl0), vl0); + dst1 = rvv::vmerge(src1, 0, helper::vmgt(src1, tval, vl1), vl1); + break; + } + helper::vstore(dst + j, dst0, vl0); + helper::vstore(dst + j + vl0, dst1, vl1); + } + } + + return CV_HAL_ERROR_OK; +} + +static inline int threshold_range(int start, int end, const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, int width, int depth, int cn, double thresh, double maxValue, int thresholdType) +{ + auto saturate_8u = [](double x){ return static_cast(std::min(std::max(x, static_cast(std::numeric_limits::lowest())), static_cast(std::numeric_limits::max()))); }; + auto saturate_16s = [](double x){ return static_cast(std::min(std::max(x, static_cast(std::numeric_limits::lowest())), static_cast(std::numeric_limits::max()))); }; + + width *= cn; + switch (depth) + { + case CV_8U: + switch (thresholdType) + { + case CV_HAL_THRESH_BINARY: + return threshold(start, end, src_data, src_step, dst_data, dst_step, width, saturate_8u(std::floor(thresh)), saturate_8u(std::round(maxValue))); + case CV_HAL_THRESH_BINARY_INV: + return threshold(start, end, src_data, src_step, dst_data, dst_step, width, saturate_8u(std::floor(thresh)), saturate_8u(std::round(maxValue))); + case CV_HAL_THRESH_TRUNC: + return threshold(start, end, src_data, src_step, dst_data, dst_step, width, saturate_8u(std::floor(thresh)), saturate_8u(std::round(maxValue))); + case CV_HAL_THRESH_TOZERO: + return threshold(start, end, src_data, src_step, dst_data, dst_step, width, saturate_8u(std::floor(thresh)), saturate_8u(std::round(maxValue))); + case CV_HAL_THRESH_TOZERO_INV: + return threshold(start, end, src_data, src_step, dst_data, dst_step, width, saturate_8u(std::floor(thresh)), saturate_8u(std::round(maxValue))); + } + break; + case CV_16S: + switch (thresholdType) + { + case CV_HAL_THRESH_BINARY: + return threshold(start, end, src_data, src_step, dst_data, dst_step, width, saturate_16s(std::floor(thresh)), saturate_16s(std::round(maxValue))); + case CV_HAL_THRESH_BINARY_INV: + return threshold(start, end, src_data, src_step, dst_data, dst_step, width, saturate_16s(std::floor(thresh)), saturate_16s(std::round(maxValue))); + case CV_HAL_THRESH_TRUNC: + return threshold(start, end, src_data, src_step, dst_data, dst_step, width, saturate_16s(std::floor(thresh)), saturate_16s(std::round(maxValue))); + case CV_HAL_THRESH_TOZERO: + return threshold(start, end, src_data, src_step, dst_data, dst_step, width, saturate_16s(std::floor(thresh)), saturate_16s(std::round(maxValue))); + case CV_HAL_THRESH_TOZERO_INV: + return threshold(start, end, src_data, src_step, dst_data, dst_step, width, saturate_16s(std::floor(thresh)), saturate_16s(std::round(maxValue))); + } + break; + case CV_32F: + switch (thresholdType) + { + case CV_HAL_THRESH_BINARY: + return threshold(start, end, src_data, src_step, dst_data, dst_step, width, static_cast(thresh), static_cast(maxValue)); + case CV_HAL_THRESH_BINARY_INV: + return threshold(start, end, src_data, src_step, dst_data, dst_step, width, static_cast(thresh), static_cast(maxValue)); + case CV_HAL_THRESH_TRUNC: + return threshold(start, end, src_data, src_step, dst_data, dst_step, width, static_cast(thresh), static_cast(maxValue)); + case CV_HAL_THRESH_TOZERO: + return threshold(start, end, src_data, src_step, dst_data, dst_step, width, static_cast(thresh), static_cast(maxValue)); + case CV_HAL_THRESH_TOZERO_INV: + return threshold(start, end, src_data, src_step, dst_data, dst_step, width, static_cast(thresh), static_cast(maxValue)); + } + break; + case CV_64F: + switch (thresholdType) + { + case CV_HAL_THRESH_BINARY: + return threshold(start, end, src_data, src_step, dst_data, dst_step, width, thresh, maxValue); + case CV_HAL_THRESH_BINARY_INV: + return threshold(start, end, src_data, src_step, dst_data, dst_step, width, thresh, maxValue); + case CV_HAL_THRESH_TRUNC: + return threshold(start, end, src_data, src_step, dst_data, dst_step, width, thresh, maxValue); + case CV_HAL_THRESH_TOZERO: + return threshold(start, end, src_data, src_step, dst_data, dst_step, width, thresh, maxValue); + case CV_HAL_THRESH_TOZERO_INV: + return threshold(start, end, src_data, src_step, dst_data, dst_step, width, thresh, maxValue); + } + break; + } + return CV_HAL_ERROR_NOT_IMPLEMENTED; +} + +inline int threshold(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, int width, int height, int depth, int cn, double thresh, double maxValue, int thresholdType) +{ + return threshold_range(0, height, src_data, src_step, dst_data, dst_step, width, depth, cn, thresh, maxValue, thresholdType); +} +} // cv::cv_hal_rvv::threshold + +namespace threshold_otsu { +#undef cv_hal_threshold_otsu +#define cv_hal_threshold_otsu cv::cv_hal_rvv::threshold_otsu::threshold_otsu + +static inline int otsu(int start, int end, const uchar* src_data, size_t src_step, int width, std::atomic* cnt, int N, int* h) +{ + const int c = cnt->fetch_add(1) % cv::getNumThreads(); + h += c * N; + + for (int i = start; i < end; i++) + { + for (int j = 0; j < width; j++) + h[src_data[i * src_step + j]]++; + } + return CV_HAL_ERROR_OK; +} + +// the algorithm is copied from imgproc/src/thresh.cpp, +// in the function template static double getThreshVal_Otsu +inline int threshold_otsu(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, int width, int height, int depth, double maxValue, int thresholdType, double* thresh) +{ + if (depth != CV_8UC1 || width * height < (1 << 15)) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + + const int N = std::numeric_limits::max() + 1; + const int nums = cv::getNumThreads(); + std::vector _h(N * nums, 0); + int* h = _h.data(); + + std::atomic cnt(0); + cv::parallel_for_(Range(0, height), threshold::ThresholdInvoker({otsu}, src_data, src_step, width, &cnt, N, h), nums); + for (int i = N; i < nums * N; i++) + { + h[i % N] += h[i]; + } + + double mu = 0, scale = 1. / (width*height); + for (int i = 0; i < N; i++) + { + mu += i*(double)h[i]; + } + + mu *= scale; + double mu1 = 0, q1 = 0; + double max_sigma = 0, max_val = 0; + + for (int i = 0; i < N; i++) + { + double p_i, q2, mu2, sigma; + + p_i = h[i]*scale; + mu1 *= q1; + q1 += p_i; + q2 = 1. - q1; + + if (std::min(q1,q2) < FLT_EPSILON || std::max(q1,q2) > 1. - FLT_EPSILON) + continue; + + mu1 = (mu1 + i*p_i)/q1; + mu2 = (mu - q1*mu1)/q2; + sigma = q1*q2*(mu1 - mu2)*(mu1 - mu2); + if (sigma > max_sigma) + { + max_sigma = sigma; + max_val = i; + } + } + + *thresh = max_val; + if (dst_data == nullptr) + return CV_HAL_ERROR_OK; + + return threshold::invoke(width, height, {threshold::threshold_range}, src_data, src_step, dst_data, dst_step, width, depth, 1, max_val, maxValue, thresholdType); +} +} // cv::cv_hal_rvv::threshold_otsu + +namespace adaptiveThreshold { +#undef cv_hal_adaptiveThreshold +#define cv_hal_adaptiveThreshold cv::cv_hal_rvv::adaptiveThreshold::adaptiveThreshold + +// the algorithm is copied from imgproc/src/thresh.cpp, +// in the function void cv::adaptiveThreshold +template +static inline int adaptiveThreshold(int start, int end, const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, int width, int height, double maxValue, double C) +{ + auto saturate = [](double x){ return static_cast(std::min(std::max(x, static_cast(std::numeric_limits::lowest())), static_cast(std::numeric_limits::max()))); }; + uchar mval = saturate(std::round(maxValue)); + + if (method == CV_HAL_ADAPTIVE_THRESH_MEAN_C) + { + int cval = static_cast(std::round(C)); + if (cval != C) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + + std::vector res(width * ksize); + auto process = [&](int x, int y) { + int sum = 0; + for (int i = 0; i < ksize; i++) + { + int q = std::min(std::max(y + i - ksize / 2, 0), width - 1); + sum += src_data[x * src_step + q]; + } + res[(x % ksize) * width + y] = sum; + }; + + const int left = ksize - 1, right = width - (ksize - 1); + for (int i = start - ksize / 2; i < end + ksize / 2; i++) + { + if (i >= 0 && i < height) + { + for (int j = 0; j < left; j++) + process(i, j); + for (int j = right; j < width; j++) + process(i, j); + + int vl; + for (int j = left; j < right; j += vl) + { + vl = __riscv_vsetvl_e8m4(right - j); + const uchar* row = src_data + i * src_step + j - ksize / 2; + auto src = __riscv_vreinterpret_v_u16m8_i16m8(__riscv_vzext_vf2(__riscv_vle8_v_u8m4(row, vl), vl)); + auto sum = src; + src = __riscv_vslide1down(src, row[vl], vl); + sum = __riscv_vadd(sum, src, vl); + src = __riscv_vslide1down(src, row[vl + 1], vl); + sum = __riscv_vadd(sum, src, vl); + if (ksize == 5) + { + src = __riscv_vslide1down(src, row[vl + 2], vl); + sum = __riscv_vadd(sum, src, vl); + src = __riscv_vslide1down(src, row[vl + 3], vl); + sum = __riscv_vadd(sum, src, vl); + } + __riscv_vse16(res.data() + (i % ksize) * width + j, sum, vl); + } + } + + int cur = i - ksize / 2; + if (cur >= start) + { + const short* row0 = res.data() + std::min(std::max(cur - ksize / 2, 0), height - 1) % ksize * width; + const short* row1 = res.data() + std::min(std::max(cur + 1 - ksize / 2, 0), height - 1) % ksize * width; + const short* row2 = res.data() + std::min(std::max(cur + 2 - ksize / 2, 0), height - 1) % ksize * width; + const short* row3 = res.data() + std::min(std::max(cur + 3 - ksize / 2, 0), height - 1) % ksize * width; + const short* row4 = res.data() + std::min(std::max(cur + 4 - ksize / 2, 0), height - 1) % ksize * width; + int vl; + for (int j = 0; j < width; j += vl) + { + vl = __riscv_vsetvl_e16m8(width - j); + auto sum = __riscv_vle16_v_i16m8(row0 + j, vl); + sum = __riscv_vadd(sum, __riscv_vle16_v_i16m8(row1 + j, vl), vl); + sum = __riscv_vadd(sum, __riscv_vle16_v_i16m8(row2 + j, vl), vl); + if (ksize == 5) + { + sum = __riscv_vadd(sum, __riscv_vle16_v_i16m8(row3 + j, vl), vl); + sum = __riscv_vadd(sum, __riscv_vle16_v_i16m8(row4 + j, vl), vl); + } + auto mean = __riscv_vsub(__riscv_vdiv(sum, ksize * ksize, vl), cval, vl); + auto cmp = __riscv_vmsgt(__riscv_vreinterpret_v_u16m8_i16m8(__riscv_vzext_vf2(__riscv_vle8_v_u8m4(src_data + cur * src_step + j, vl), vl)), mean, vl); + if (type == CV_HAL_THRESH_BINARY) + { + __riscv_vse8(dst_data + cur * dst_step + j, __riscv_vmerge(__riscv_vmv_v_x_u8m4(0, vl), mval, cmp, vl), vl); + } + else + { + __riscv_vse8(dst_data + cur * dst_step + j, __riscv_vmerge(__riscv_vmv_v_x_u8m4(mval, vl), 0, cmp, vl), vl); + } + } + } + } + } + else + { + constexpr float kernel[2][5] = {{0.25f, 0.5f, 0.25f}, {0.0625f, 0.25f, 0.375f, 0.25f, 0.0625f}}; + std::vector res(width * ksize); + auto process = [&](int x, int y) { + float sum = 0; + for (int i = 0; i < ksize; i++) + { + int q = std::min(std::max(y + i - ksize / 2, 0), width - 1); + sum += kernel[ksize == 5][i] * src_data[x * src_step + q]; + } + res[(x % ksize) * width + y] = sum; + }; + + const int left = ksize - 1, right = width - (ksize - 1); + for (int i = start - ksize / 2; i < end + ksize / 2; i++) + { + if (i >= 0 && i < height) + { + for (int j = 0; j < left; j++) + process(i, j); + for (int j = right; j < width; j++) + process(i, j); + + int vl; + for (int j = left; j < right; j += vl) + { + vl = __riscv_vsetvl_e8m2(right - j); + const uchar* row = src_data + i * src_step + j - ksize / 2; + auto src = __riscv_vfwcvt_f(__riscv_vzext_vf2(__riscv_vle8_v_u8m2(row, vl), vl), vl); + auto sum = __riscv_vfmul(src, kernel[ksize == 5][0], vl); + src = __riscv_vfslide1down(src, row[vl], vl); + sum = __riscv_vfmacc(sum, kernel[ksize == 5][1], src, vl); + src = __riscv_vfslide1down(src, row[vl + 1], vl); + sum = __riscv_vfmacc(sum, kernel[ksize == 5][2], src, vl); + if (ksize == 5) + { + src = __riscv_vfslide1down(src, row[vl + 2], vl); + sum = __riscv_vfmacc(sum, kernel[1][3], src, vl); + src = __riscv_vfslide1down(src, row[vl + 3], vl); + sum = __riscv_vfmacc(sum, kernel[1][4], src, vl); + } + __riscv_vse32(res.data() + (i % ksize) * width + j, sum, vl); + } + } + + int cur = i - ksize / 2; + if (cur >= start) + { + const float* row0 = res.data() + std::min(std::max(cur - ksize / 2, 0), height - 1) % ksize * width; + const float* row1 = res.data() + std::min(std::max(cur + 1 - ksize / 2, 0), height - 1) % ksize * width; + const float* row2 = res.data() + std::min(std::max(cur + 2 - ksize / 2, 0), height - 1) % ksize * width; + const float* row3 = res.data() + std::min(std::max(cur + 3 - ksize / 2, 0), height - 1) % ksize * width; + const float* row4 = res.data() + std::min(std::max(cur + 4 - ksize / 2, 0), height - 1) % ksize * width; + int vl; + for (int j = 0; j < width; j += vl) + { + vl = __riscv_vsetvl_e32m8(width - j); + auto sum = __riscv_vfmv_v_f_f32m8(-C, vl); + sum = __riscv_vfmacc(sum, kernel[ksize == 5][0], __riscv_vle32_v_f32m8(row0 + j, vl), vl); + sum = __riscv_vfmacc(sum, kernel[ksize == 5][1], __riscv_vle32_v_f32m8(row1 + j, vl), vl); + sum = __riscv_vfmacc(sum, kernel[ksize == 5][2], __riscv_vle32_v_f32m8(row2 + j, vl), vl); + if (ksize == 5) + { + sum = __riscv_vfmacc(sum, kernel[1][3], __riscv_vle32_v_f32m8(row3 + j, vl), vl); + sum = __riscv_vfmacc(sum, kernel[1][4], __riscv_vle32_v_f32m8(row4 + j, vl), vl); + } + auto mean = __riscv_vnclipu(__riscv_vfncvt_rtz_xu(sum, vl), 0, __RISCV_VXRM_RNU, vl); + auto cmp = __riscv_vmsgtu(__riscv_vle8_v_u8m2(src_data + cur * src_step + j, vl), mean, vl); + if (type == CV_HAL_THRESH_BINARY) + { + __riscv_vse8(dst_data + cur * dst_step + j, __riscv_vmerge(__riscv_vmv_v_x_u8m2(0, vl), mval, cmp, vl), vl); + } + else + { + __riscv_vse8(dst_data + cur * dst_step + j, __riscv_vmerge(__riscv_vmv_v_x_u8m2(mval, vl), 0, cmp, vl), vl); + } + } + } + } + } + + return CV_HAL_ERROR_OK; +} + +inline int adaptiveThreshold(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, int width, int height, double maxValue, int adaptiveMethod, int thresholdType, int blockSize, double C) +{ + if (thresholdType != CV_HAL_THRESH_BINARY && thresholdType != CV_HAL_THRESH_BINARY_INV) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + if (adaptiveMethod != CV_HAL_ADAPTIVE_THRESH_MEAN_C && adaptiveMethod != CV_HAL_ADAPTIVE_THRESH_GAUSSIAN_C) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + if ((blockSize != 3 && blockSize != 5) || width < blockSize * 2) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + + switch (blockSize*100 + adaptiveMethod*10 + thresholdType) + { + case 300 + CV_HAL_ADAPTIVE_THRESH_MEAN_C*10 + CV_HAL_THRESH_BINARY: + return threshold::invoke(width, height, {adaptiveThreshold<3, CV_HAL_ADAPTIVE_THRESH_MEAN_C, CV_HAL_THRESH_BINARY>}, src_data, src_step, dst_data, dst_step, width, height, maxValue, C); + case 300 + CV_HAL_ADAPTIVE_THRESH_MEAN_C*10 + CV_HAL_THRESH_BINARY_INV: + return threshold::invoke(width, height, {adaptiveThreshold<3, CV_HAL_ADAPTIVE_THRESH_MEAN_C, CV_HAL_THRESH_BINARY_INV>}, src_data, src_step, dst_data, dst_step, width, height, maxValue, C); + case 500 + CV_HAL_ADAPTIVE_THRESH_MEAN_C*10 + CV_HAL_THRESH_BINARY: + return threshold::invoke(width, height, {adaptiveThreshold<5, CV_HAL_ADAPTIVE_THRESH_MEAN_C, CV_HAL_THRESH_BINARY>}, src_data, src_step, dst_data, dst_step, width, height, maxValue, C); + case 500 + CV_HAL_ADAPTIVE_THRESH_MEAN_C*10 + CV_HAL_THRESH_BINARY_INV: + return threshold::invoke(width, height, {adaptiveThreshold<5, CV_HAL_ADAPTIVE_THRESH_MEAN_C, CV_HAL_THRESH_BINARY_INV>}, src_data, src_step, dst_data, dst_step, width, height, maxValue, C); + case 300 + CV_HAL_ADAPTIVE_THRESH_GAUSSIAN_C*10 + CV_HAL_THRESH_BINARY: + return threshold::invoke(width, height, {adaptiveThreshold<3, CV_HAL_ADAPTIVE_THRESH_GAUSSIAN_C, CV_HAL_THRESH_BINARY>}, src_data, src_step, dst_data, dst_step, width, height, maxValue, C); + case 300 + CV_HAL_ADAPTIVE_THRESH_GAUSSIAN_C*10 + CV_HAL_THRESH_BINARY_INV: + return threshold::invoke(width, height, {adaptiveThreshold<3, CV_HAL_ADAPTIVE_THRESH_GAUSSIAN_C, CV_HAL_THRESH_BINARY_INV>}, src_data, src_step, dst_data, dst_step, width, height, maxValue, C); + case 500 + CV_HAL_ADAPTIVE_THRESH_GAUSSIAN_C*10 + CV_HAL_THRESH_BINARY: + return threshold::invoke(width, height, {adaptiveThreshold<5, CV_HAL_ADAPTIVE_THRESH_GAUSSIAN_C, CV_HAL_THRESH_BINARY>}, src_data, src_step, dst_data, dst_step, width, height, maxValue, C); + case 500 + CV_HAL_ADAPTIVE_THRESH_GAUSSIAN_C*10 + CV_HAL_THRESH_BINARY_INV: + return threshold::invoke(width, height, {adaptiveThreshold<5, CV_HAL_ADAPTIVE_THRESH_GAUSSIAN_C, CV_HAL_THRESH_BINARY_INV>}, src_data, src_step, dst_data, dst_step, width, height, maxValue, C); + } + + return CV_HAL_ERROR_NOT_IMPLEMENTED; +} +} // cv::cv_hal_rvv::adaptiveThreshold + +}} + +#endif diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/types.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/types.hpp index 71182e0e4f..da916669fd 100644 --- a/3rdparty/hal_rvv/hal_rvv_1p0/types.hpp +++ b/3rdparty/hal_rvv/hal_rvv_1p0/types.hpp @@ -165,6 +165,12 @@ static inline BoolType vmle(VecType vs2, VecType vs1, size_t vl) { static inline BoolType vmgt(VecType vs2, VecType vs1, size_t vl) { \ return __riscv_vm##S_OR_F##gt##IS_U(vs2, vs1, vl); \ } \ +static inline BoolType vmle(VecType vs2, ElemType vs1, size_t vl) { \ + return __riscv_vm##S_OR_F##le##IS_U(vs2, vs1, vl); \ +} \ +static inline BoolType vmgt(VecType vs2, ElemType vs1, size_t vl) { \ + return __riscv_vm##S_OR_F##gt##IS_U(vs2, vs1, vl); \ +} \ static inline BoolType vmge(VecType vs2, VecType vs1, size_t vl) { \ return __riscv_vm##S_OR_F##ge##IS_U(vs2, vs1, vl); \ } \ From 8207549638c60d46ebe85af7b3a3f50bb5ef49d5 Mon Sep 17 00:00:00 2001 From: Yuantao Feng Date: Tue, 18 Mar 2025 14:42:55 +0800 Subject: [PATCH 22/94] Merge pull request #26991 from fengyuentau:4x/core/norm2hal_rvv core: improve norm of hal rvv #26991 Merge with https://github.com/opencv/opencv_extra/pull/1241 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- 3rdparty/hal_rvv/hal_rvv_1p0/common.hpp | 30 + 3rdparty/hal_rvv/hal_rvv_1p0/norm.hpp | 1528 ++++++++++++++++------- modules/core/perf/perf_norm.cpp | 4 +- modules/core/src/norm.rvv1p0.hpp | 200 --- modules/core/src/norm.simd.hpp | 86 +- 5 files changed, 1111 insertions(+), 737 deletions(-) create mode 100644 3rdparty/hal_rvv/hal_rvv_1p0/common.hpp delete mode 100644 modules/core/src/norm.rvv1p0.hpp diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/common.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/common.hpp new file mode 100644 index 0000000000..8db03267e1 --- /dev/null +++ b/3rdparty/hal_rvv/hal_rvv_1p0/common.hpp @@ -0,0 +1,30 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#ifndef OPENCV_HAL_RVV_COMMON_HPP_INCLUDED +#define OPENCV_HAL_RVV_COMMON_HPP_INCLUDED + +#include + +namespace cv { namespace cv_hal_rvv { namespace custom_intrin { + +#define CV_HAL_RVV_COMMON_CUSTOM_INTRIN_ABS(_Tpvs, _Tpvd, shift, suffix) \ + inline _Tpvd __riscv_vabs(const _Tpvs& v, const int vl) { \ + _Tpvs mask = __riscv_vsra(v, shift, vl); \ + _Tpvs v_xor = __riscv_vxor(v, mask, vl); \ + return __riscv_vreinterpret_##suffix( \ + __riscv_vsub(v_xor, mask, vl) \ + ); \ + } + +CV_HAL_RVV_COMMON_CUSTOM_INTRIN_ABS(vint8m2_t, vuint8m2_t, 7, u8m2) +CV_HAL_RVV_COMMON_CUSTOM_INTRIN_ABS(vint8m8_t, vuint8m8_t, 7, u8m8) +CV_HAL_RVV_COMMON_CUSTOM_INTRIN_ABS(vint16m4_t, vuint16m4_t, 15, u16m4) +CV_HAL_RVV_COMMON_CUSTOM_INTRIN_ABS(vint16m8_t, vuint16m8_t, 15, u16m8) +CV_HAL_RVV_COMMON_CUSTOM_INTRIN_ABS(vint32m4_t, vuint32m4_t, 31, u32m4) +CV_HAL_RVV_COMMON_CUSTOM_INTRIN_ABS(vint32m8_t, vuint32m8_t, 31, u32m8) + +}}} // cv::cv_hal_rvv::custom_intrin + +#endif diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/norm.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/norm.hpp index 260978f6ee..1e583f29da 100644 --- a/3rdparty/hal_rvv/hal_rvv_1p0/norm.hpp +++ b/3rdparty/hal_rvv/hal_rvv_1p0/norm.hpp @@ -1,520 +1,1094 @@ // This file is part of OpenCV project. // It is subject to the license terms in the LICENSE file found in the top-level directory // of this distribution and at http://opencv.org/license.html. - +// // Copyright (C) 2025, Institute of Software, Chinese Academy of Sciences. +// Copyright (C) 2025, SpaceMIT Inc., all rights reserved. +// Third party copyrights are property of their respective owners. #ifndef OPENCV_HAL_RVV_NORM_HPP_INCLUDED #define OPENCV_HAL_RVV_NORM_HPP_INCLUDED -#include +#include "common.hpp" namespace cv { namespace cv_hal_rvv { namespace norm { #undef cv_hal_norm #define cv_hal_norm cv::cv_hal_rvv::norm::norm -inline int normInf_8UC1(const uchar* src, size_t src_step, const uchar* mask, size_t mask_step, int width, int height, double* result) -{ - int vlmax = __riscv_vsetvlmax_e8m8(); - auto vec_max = __riscv_vmv_v_x_u8m8(0, vlmax); +namespace { - if (mask) - { - for (int i = 0; i < height; i++) - { - const uchar* src_row = src + i * src_step; - const uchar* mask_row = mask + i * mask_step; - int vl; - for (int j = 0; j < width; j += vl) - { - vl = __riscv_vsetvl_e8m8(width - j); - auto vec_src = __riscv_vle8_v_u8m8(src_row + j, vl); - auto vec_mask = __riscv_vle8_v_u8m8(mask_row + j, vl); - auto bool_mask = __riscv_vmsne(vec_mask, 0, vl); - vec_max = __riscv_vmaxu_tumu(bool_mask, vec_max, vec_max, vec_src, vl); +template +struct NormInf_RVV { + inline ST operator() (const T* src, int n) const { + ST s = 0; + for (int i = 0; i < n; i++) { + s = std::max(s, (ST)cv_abs(src[i])); + } + return s; + } +}; + +template +struct NormL1_RVV { + inline ST operator() (const T* src, int n) const { + ST s = 0; + for (int i = 0; i < n; i++) { + s += cv_abs(src[i]); + } + return s; + } +}; + +template +struct NormL2_RVV { + inline ST operator() (const T* src, int n) const { + ST s = 0; + for (int i = 0; i < n; i++) { + ST v = src[i]; + s += v * v; + } + return s; + } +}; + +template<> +struct NormInf_RVV { + int operator() (const uchar* src, int n) const { + int vlmax = __riscv_vsetvlmax_e8m8(); + auto s = __riscv_vmv_v_x_u8m8(0, vlmax); + int vl; + for (int i = 0; i < n; i += vl) { + vl = __riscv_vsetvl_e8m8(n - i); + auto v = __riscv_vle8_v_u8m8(src + i, vl); + s = __riscv_vmaxu_tu(s, s, v, vl); + } + return __riscv_vmv_x(__riscv_vredmaxu(s, __riscv_vmv_s_x_u8m1(0, __riscv_vsetvlmax_e8m1()), vlmax)); + } +}; + +template<> +struct NormInf_RVV { + int operator() (const schar* src, int n) const { + int vlmax = __riscv_vsetvlmax_e8m8(); + auto s = __riscv_vmv_v_x_u8m8(0, vlmax); + int vl; + for (int i = 0; i < n; i += vl) { + vl = __riscv_vsetvl_e8m8(n - i); + auto v = __riscv_vle8_v_i8m8(src + i, vl); + s = __riscv_vmaxu_tu(s, s, custom_intrin::__riscv_vabs(v, vl), vl); + } + return __riscv_vmv_x(__riscv_vredmaxu(s, __riscv_vmv_s_x_u8m1(0, __riscv_vsetvlmax_e8m1()), vlmax)); + } +}; + +template<> +struct NormInf_RVV { + int operator() (const ushort* src, int n) const { + int vlmax = __riscv_vsetvlmax_e16m8(); + auto s = __riscv_vmv_v_x_u16m8(0, vlmax); + int vl; + for (int i = 0; i < n; i += vl) { + vl = __riscv_vsetvl_e16m8(n - i); + auto v = __riscv_vle16_v_u16m8(src + i, vl); + s = __riscv_vmaxu_tu(s, s, v, vl); + } + return __riscv_vmv_x(__riscv_vredmaxu(s, __riscv_vmv_s_x_u16m1(0, __riscv_vsetvlmax_e16m1()), vlmax)); + } +}; + +template<> +struct NormInf_RVV { + int operator() (const short* src, int n) const { + int vlmax = __riscv_vsetvlmax_e16m8(); + auto s = __riscv_vmv_v_x_u16m8(0, vlmax); + int vl; + for (int i = 0; i < n; i += vl) { + vl = __riscv_vsetvl_e16m8(n - i); + auto v = __riscv_vle16_v_i16m8(src + i, vl); + s = __riscv_vmaxu_tu(s, s, custom_intrin::__riscv_vabs(v, vl), vl); + } + return __riscv_vmv_x(__riscv_vredmaxu(s, __riscv_vmv_s_x_u16m1(0, __riscv_vsetvlmax_e16m1()), vlmax)); + } +}; + +template<> +struct NormInf_RVV { + int operator() (const int* src, int n) const { + int vlmax = __riscv_vsetvlmax_e32m8(); + auto s = __riscv_vmv_v_x_u32m8(0, vlmax); + int vl; + for (int i = 0; i < n; i += vl) { + vl = __riscv_vsetvl_e32m8(n - i); + auto v = __riscv_vle32_v_i32m8(src + i, vl); + s = __riscv_vmaxu_tu(s, s, custom_intrin::__riscv_vabs(v, vl), vl); + } + return __riscv_vmv_x(__riscv_vredmaxu(s, __riscv_vmv_s_x_u32m1(0, __riscv_vsetvlmax_e32m1()), vlmax)); + } +}; + +template<> +struct NormInf_RVV { + float operator() (const float* src, int n) const { + int vlmax = __riscv_vsetvlmax_e32m8(); + auto s = __riscv_vfmv_v_f_f32m8(0, vlmax); + int vl; + for (int i = 0; i < n; i += vl) { + vl = __riscv_vsetvl_e32m8(n - i); + auto v = __riscv_vle32_v_f32m8(src + i, vl); + s = __riscv_vfmax_tu(s, s, __riscv_vfabs(v, vl), vl); + } + return __riscv_vfmv_f(__riscv_vfredmax(s, __riscv_vfmv_s_f_f32m1(0, __riscv_vsetvlmax_e32m1()), vlmax)); + } +}; + +template<> +struct NormInf_RVV { + double operator() (const double* src, int n) const { + int vlmax = __riscv_vsetvlmax_e64m8(); + auto s = __riscv_vfmv_v_f_f64m8(0, vlmax); + int vl; + for (int i = 0; i < n; i += vl) { + vl = __riscv_vsetvl_e64m8(n - i); + auto v = __riscv_vle64_v_f64m8(src + i, vl); + s = __riscv_vfmax_tu(s, s, __riscv_vfabs(v, vl), vl); + } + return __riscv_vfmv_f(__riscv_vfredmax(s, __riscv_vfmv_s_f_f64m1(0, __riscv_vsetvlmax_e64m1()), vlmax)); + } +}; + +template<> +struct NormL1_RVV { + int operator() (const uchar* src, int n) const { + auto s = __riscv_vmv_v_x_u32m1(0, __riscv_vsetvlmax_e32m1()); + auto zero = __riscv_vmv_v_x_u16m1(0, __riscv_vsetvlmax_e16m1()); + int vl; + for (int i = 0; i < n; i += vl) { + vl = __riscv_vsetvl_e8m8(n - i); + auto v = __riscv_vle8_v_u8m8(src + i, vl); + s = __riscv_vwredsumu(__riscv_vwredsumu_tu(zero, v, zero, vl), s, vl); + } + return __riscv_vmv_x(s); + } +}; + +template<> +struct NormL1_RVV { + int operator() (const schar* src, int n) const { + auto s = __riscv_vmv_v_x_u32m1(0, __riscv_vsetvlmax_e32m1()); + auto zero = __riscv_vmv_v_x_u16m1(0, __riscv_vsetvlmax_e16m1()); + int vl; + for (int i = 0; i < n; i += vl) { + vl = __riscv_vsetvl_e8m8(n - i); + auto v = custom_intrin::__riscv_vabs(__riscv_vle8_v_i8m8(src + i, vl), vl); + s = __riscv_vwredsumu(__riscv_vwredsumu_tu(zero, v, zero, vl), s, vl); + } + return __riscv_vmv_x(s); + } +}; + +template<> +struct NormL1_RVV { + int operator() (const ushort* src, int n) const { + auto s = __riscv_vmv_v_x_u32m1(0, __riscv_vsetvlmax_e32m1()); + int vl; + for (int i = 0; i < n; i += vl) { + vl = __riscv_vsetvl_e16m8(n - i); + auto v = __riscv_vle16_v_u16m8(src + i, vl); + s = __riscv_vwredsumu(v, s, vl); + } + return __riscv_vmv_x(s); + } +}; + +template<> +struct NormL1_RVV { + int operator() (const short* src, int n) const { + auto s = __riscv_vmv_v_x_u32m1(0, __riscv_vsetvlmax_e32m1()); + int vl; + for (int i = 0; i < n; i += vl) { + vl = __riscv_vsetvl_e16m8(n - i); + auto v = custom_intrin::__riscv_vabs(__riscv_vle16_v_i16m8(src + i, vl), vl); + s = __riscv_vwredsumu(v, s, vl); + } + return __riscv_vmv_x(s); + } +}; + +template<> +struct NormL1_RVV { + double operator() (const int* src, int n) const { + int vlmax = __riscv_vsetvlmax_e32m4(); + auto s = __riscv_vfmv_v_f_f64m8(0, vlmax); + int vl; + for (int i = 0; i < n; i += vl) { + vl = __riscv_vsetvl_e32m4(n - i); + auto v = custom_intrin::__riscv_vabs(__riscv_vle32_v_i32m4(src + i, vl), vl); + s = __riscv_vfadd_tu(s, s, __riscv_vfwcvt_f(v, vl), vl); + } + return __riscv_vfmv_f(__riscv_vfredosum(s, __riscv_vfmv_s_f_f64m1(0, __riscv_vsetvlmax_e64m1()), vlmax)); + } +}; + +template<> +struct NormL1_RVV { + double operator() (const float* src, int n) const { + int vlmax = __riscv_vsetvlmax_e32m4(); + auto s = __riscv_vfmv_v_f_f64m8(0, vlmax); + int vl; + for (int i = 0; i < n; i += vl) { + vl = __riscv_vsetvl_e32m4(n - i); + auto v = __riscv_vfabs(__riscv_vle32_v_f32m4(src + i, vl), vl); + s = __riscv_vfadd_tu(s, s, __riscv_vfwcvt_f(v, vl), vl); + } + return __riscv_vfmv_f(__riscv_vfredosum(s, __riscv_vfmv_s_f_f64m1(0, __riscv_vsetvlmax_e64m1()), vlmax)); + } +}; + +template<> +struct NormL1_RVV { + double operator() (const double* src, int n) const { + int vlmax = __riscv_vsetvlmax_e64m8(); + auto s = __riscv_vfmv_v_f_f64m8(0, vlmax); + int vl; + for (int i = 0; i < n; i += vl) { + vl = __riscv_vsetvl_e64m8(n - i); + auto v = __riscv_vle64_v_f64m8(src + i, vl); + s = __riscv_vfadd_tu(s, s, __riscv_vfabs(v, vl), vl); + } + return __riscv_vfmv_f(__riscv_vfredosum(s, __riscv_vfmv_s_f_f64m1(0, __riscv_vsetvlmax_e64m1()), vlmax)); + } +}; + +template<> +struct NormL2_RVV { + int operator() (const uchar* src, int n) const { + auto s = __riscv_vmv_v_x_u32m1(0, __riscv_vsetvlmax_e32m1()); + int vl; + for (int i = 0; i < n; i += vl) { + vl = __riscv_vsetvl_e8m4(n - i); + auto v = __riscv_vle8_v_u8m4(src + i, vl); + s = __riscv_vwredsumu(__riscv_vwmulu(v, v, vl), s, vl); + } + return __riscv_vmv_x(s); + } +}; + +template<> +struct NormL2_RVV { + int operator() (const schar* src, int n) const { + auto s = __riscv_vmv_v_x_i32m1(0, __riscv_vsetvlmax_e32m1()); + int vl; + for (int i = 0; i < n; i += vl) { + vl = __riscv_vsetvl_e8m4(n - i); + auto v = __riscv_vle8_v_i8m4(src + i, vl); + s = __riscv_vwredsum(__riscv_vwmul(v, v, vl), s, vl); + } + return __riscv_vmv_x(s); + } +}; + +template<> +struct NormL2_RVV { + double operator() (const ushort* src, int n) const { + int vlmax = __riscv_vsetvlmax_e16m2(); + auto s = __riscv_vfmv_v_f_f64m8(0, vlmax); + int vl; + for (int i = 0; i < n; i += vl) { + vl = __riscv_vsetvl_e16m2(n - i); + auto v = __riscv_vle16_v_u16m2(src + i, vl); + auto v_mul = __riscv_vwmulu(v, v, vl); + s = __riscv_vfadd_tu(s, s, __riscv_vfwcvt_f(v_mul, vl), vl); + } + return __riscv_vfmv_f(__riscv_vfredosum(s, __riscv_vfmv_s_f_f64m1(0, __riscv_vsetvlmax_e64m1()), vlmax)); + } +}; + +template<> +struct NormL2_RVV { + double operator() (const short* src, int n) const { + int vlmax = __riscv_vsetvlmax_e16m2(); + auto s = __riscv_vfmv_v_f_f64m8(0, vlmax); + int vl; + for (int i = 0; i < n; i += vl) { + vl = __riscv_vsetvl_e16m2(n - i); + auto v = __riscv_vle16_v_i16m2(src + i, vl); + auto v_mul = __riscv_vwmul(v, v, vl); + s = __riscv_vfadd_tu(s, s, __riscv_vfwcvt_f(v_mul, vl), vl); + } + return __riscv_vfmv_f(__riscv_vfredosum(s, __riscv_vfmv_s_f_f64m1(0, __riscv_vsetvlmax_e32m1()), vlmax)); + } +}; + +template<> +struct NormL2_RVV { + double operator() (const int* src, int n) const { + int vlmax = __riscv_vsetvlmax_e32m4(); + auto s = __riscv_vfmv_v_f_f64m8(0, vlmax); + int vl; + for (int i = 0; i < n; i += vl) { + vl = __riscv_vsetvl_e32m4(n - i); + auto v = __riscv_vle32_v_i32m4(src + i, vl); + auto v_mul = __riscv_vwmul(v, v, vl); + s = __riscv_vfadd_tu(s, s, __riscv_vfcvt_f(v_mul, vl), vl); + } + return __riscv_vfmv_f(__riscv_vfredosum(s, __riscv_vfmv_s_f_f64m1(0, __riscv_vsetvlmax_e64m1()), vlmax)); + } +}; + +template<> +struct NormL2_RVV { + double operator() (const float* src, int n) const { + int vlmax = __riscv_vsetvlmax_e32m4(); + auto s = __riscv_vfmv_v_f_f64m8(0, vlmax); + int vl; + for (int i = 0; i < n; i += vl) { + vl = __riscv_vsetvl_e32m4(n - i); + auto v = __riscv_vle32_v_f32m4(src + i, vl); + auto v_mul = __riscv_vfwmul(v, v, vl); + s = __riscv_vfadd_tu(s, s, v_mul, vl); + } + return __riscv_vfmv_f(__riscv_vfredosum(s, __riscv_vfmv_s_f_f64m1(0, __riscv_vsetvlmax_e64m1()), vlmax)); + } +}; + +template<> +struct NormL2_RVV { + double operator() (const double* src, int n) const { + int vlmax = __riscv_vsetvlmax_e64m8(); + auto s = __riscv_vfmv_v_f_f64m8(0, vlmax); + int vl; + for (int i = 0; i < n; i += vl) { + vl = __riscv_vsetvl_e64m8(n - i); + auto v = __riscv_vle64_v_f64m8(src + i, vl); + auto v_mul = __riscv_vfmul(v, v, vl); + s = __riscv_vfadd_tu(s, s, v_mul, vl); + } + return __riscv_vfmv_f(__riscv_vfredosum(s, __riscv_vfmv_s_f_f64m1(0, __riscv_vsetvlmax_e64m1()), vlmax)); + } +}; + +// Norm with mask + +template +struct MaskedNormInf_RVV { + inline ST operator() (const T* src, const uchar* mask, int len, int cn) const { + ST s = 0; + for( int i = 0; i < len; i++, src += cn ) { + if( mask[i] ) { + for( int k = 0; k < cn; k++ ) { + s = std::max(s, ST(cv_abs(src[k]))); + } } } + return s; } - else - { - for (int i = 0; i < height; i++) - { - const uchar* src_row = src + i * src_step; - int vl; - for (int j = 0; j < width; j += vl) - { - vl = __riscv_vsetvl_e8m8(width - j); - auto vec_src = __riscv_vle8_v_u8m8(src_row + j, vl); - vec_max = __riscv_vmaxu_tu(vec_max, vec_max, vec_src, vl); +}; + +template +struct MaskedNormL1_RVV { + inline ST operator() (const T* src, const uchar* mask, int len, int cn) const { + ST s = 0; + for( int i = 0; i < len; i++, src += cn ) { + if( mask[i] ) { + for( int k = 0; k < cn; k++ ) { + s += cv_abs(src[k]); + } } } + return s; } - auto sc_max = __riscv_vmv_s_x_u8m1(0, vlmax); - sc_max = __riscv_vredmaxu(vec_max, sc_max, vlmax); - *result = __riscv_vmv_x(sc_max); +}; - return CV_HAL_ERROR_OK; +template +struct MaskedNormL2_RVV { + inline ST operator() (const T* src, const uchar* mask, int len, int cn) const { + ST s = 0; + for( int i = 0; i < len; i++, src += cn ) { + if( mask[i] ) { + for( int k = 0; k < cn; k++ ) { + T v = src[k]; + s += (ST)v*v; + } + } + } + return s; + } +}; + +template<> +struct MaskedNormInf_RVV { + int operator() (const uchar* src, const uchar* mask, int len, int cn) const { + int vlmax = __riscv_vsetvlmax_e8m8(); + auto s = __riscv_vmv_v_x_u8m8(0, vlmax); + if (cn == 1) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e8m8(len - i); + auto v = __riscv_vle8_v_u8m8(src + i, vl); + auto m = __riscv_vle8_v_u8m8(mask + i, vl); + auto b = __riscv_vmsne(m, 0, vl); + s = __riscv_vmaxu_tumu(b, s, s, v, vl); + } + } else if (cn == 4) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e8m2(len - i); + auto v = __riscv_vle8_v_u8m8(src + i * 4, vl * 4); + auto m = __riscv_vle8_v_u8m2(mask + i, vl); + auto b = __riscv_vmsne(__riscv_vreinterpret_u8m8(__riscv_vmul(__riscv_vzext_vf4(__riscv_vminu(m, 1, vl), vl), 0x01010101, vl)), 0, vl * 4); + s = __riscv_vmaxu_tumu(b, s, s, v, vl * 4); + } + } else { + for (int cn_index = 0; cn_index < cn; cn_index++) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e8m8(len - i); + auto v = __riscv_vlse8_v_u8m8(src + cn * i + cn_index, sizeof(uchar) * cn, vl); + auto m = __riscv_vle8_v_u8m8(mask + i, vl); + auto b = __riscv_vmsne(m, 0, vl); + s = __riscv_vmaxu_tumu(b, s, s, v, vl); + } + } + } + return __riscv_vmv_x(__riscv_vredmaxu(s, __riscv_vmv_s_x_u8m1(0, __riscv_vsetvlmax_e8m1()), vlmax)); + } +}; + +template<> +struct MaskedNormL1_RVV { + int operator() (const uchar* src, const uchar* mask, int len, int cn) const { + auto s = __riscv_vmv_v_x_u32m1(0, __riscv_vsetvlmax_e32m1()); + auto zero = __riscv_vmv_v_x_u16m1(0, __riscv_vsetvlmax_e16m1()); + if (cn == 1) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e8m8(len - i); + auto v = __riscv_vle8_v_u8m8(src + i, vl); + auto m = __riscv_vle8_v_u8m8(mask + i, vl); + auto b = __riscv_vmsne(m, 0, vl); + s = __riscv_vwredsumu(__riscv_vwredsumu_tum(b, zero, v, zero, vl), s, __riscv_vsetvlmax_e16m1()); + } + } else if (cn == 4) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e8m2(len - i); + auto v = __riscv_vle8_v_u8m8(src + i * 4, vl * 4); + auto m = __riscv_vle8_v_u8m2(mask + i, vl); + auto b = __riscv_vmsne(__riscv_vreinterpret_u8m8(__riscv_vmul(__riscv_vzext_vf4(__riscv_vminu(m, 1, vl), vl), 0x01010101, vl)), 0, vl * 4); + s = __riscv_vwredsumu(__riscv_vwredsumu_tum(b, zero, v, zero, vl * 4), s, __riscv_vsetvlmax_e16m1()); + } + } else { + for (int cn_index = 0; cn_index < cn; cn_index++) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e8m8(len - i); + auto v = __riscv_vlse8_v_u8m8(src + cn * i + cn_index, sizeof(uchar) * cn, vl); + auto m = __riscv_vle8_v_u8m8(mask + i, vl); + auto b = __riscv_vmsne(m, 0, vl); + s = __riscv_vwredsumu(__riscv_vwredsumu_tum(b, zero, v, zero, vl), s, __riscv_vsetvlmax_e16m1()); + } + } + } + return __riscv_vmv_x(s); + } +}; + +template<> +struct MaskedNormL2_RVV { + int operator() (const uchar* src, const uchar* mask, int len, int cn) const { + auto s = __riscv_vmv_v_x_u32m1(0, __riscv_vsetvlmax_e32m1()); + if (cn == 1) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e8m4(len - i); + auto v = __riscv_vle8_v_u8m4(src + i, vl); + auto m = __riscv_vle8_v_u8m4(mask + i, vl); + auto b = __riscv_vmsne(m, 0, vl); + s = __riscv_vwredsumu(b, __riscv_vwmulu(b, v, v, vl), s, vl); + } + } else if (cn == 4) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e8m1(len - i); + auto v = __riscv_vle8_v_u8m4(src + i * 4, vl * 4); + auto m = __riscv_vle8_v_u8m1(mask + i, vl); + auto b = __riscv_vmsne(__riscv_vreinterpret_u8m4(__riscv_vmul(__riscv_vzext_vf4(__riscv_vminu(m, 1, vl), vl), 0x01010101, vl)), 0, vl * 4); + s = __riscv_vwredsumu(b, __riscv_vwmulu(b, v, v, vl * 4), s, vl * 4); + } + } else { + for (int cn_index = 0; cn_index < cn; cn_index++) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e8m4(len - i); + auto v = __riscv_vlse8_v_u8m4(src + cn * i + cn_index, sizeof(uchar) * cn, vl); + auto m = __riscv_vle8_v_u8m4(mask + i, vl); + auto b = __riscv_vmsne(m, 0, vl); + s = __riscv_vwredsumu(b, __riscv_vwmulu(b, v, v, vl), s, vl); + } + } + } + return __riscv_vmv_x(s); + } +}; + +template<> +struct MaskedNormInf_RVV { + int operator() (const schar* src, const uchar* mask, int len, int cn) const { + int vlmax = __riscv_vsetvlmax_e8m8(); + auto s = __riscv_vmv_v_x_u8m8(0, vlmax); + for (int cn_index = 0; cn_index < cn; cn_index++) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e8m8(len - i); + auto v = __riscv_vlse8_v_i8m8(src + cn * i + cn_index, sizeof(schar) * cn, vl); + auto m = __riscv_vle8_v_u8m8(mask + i, vl); + auto b = __riscv_vmsne(m, 0, vl); + s = __riscv_vmaxu_tumu(b, s, s, custom_intrin::__riscv_vabs(v, vl), vl); + } + } + return __riscv_vmv_x(__riscv_vredmaxu(s, __riscv_vmv_s_x_u8m1(0, __riscv_vsetvlmax_e8m1()), vlmax)); + } +}; + +template<> +struct MaskedNormL1_RVV { + int operator() (const schar* src, const uchar* mask, int len, int cn) const { + auto s = __riscv_vmv_v_x_u32m1(0, __riscv_vsetvlmax_e32m1()); + auto zero = __riscv_vmv_v_x_u16m1(0, __riscv_vsetvlmax_e16m1()); + for (int cn_index = 0; cn_index < cn; cn_index++) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e8m8(len - i); + auto v = custom_intrin::__riscv_vabs(__riscv_vlse8_v_i8m8(src + cn * i + cn_index, sizeof(schar) * cn, vl), vl); + auto m = __riscv_vle8_v_u8m8(mask + i, vl); + auto b = __riscv_vmsne(m, 0, vl); + s = __riscv_vwredsumu(__riscv_vwredsumu_tum(b, zero, v, zero, vl), s, __riscv_vsetvlmax_e16m1()); + } + } + return __riscv_vmv_x(s); + } +}; + +template<> +struct MaskedNormL2_RVV { + int operator() (const schar* src, const uchar* mask, int len, int cn) const { + auto s = __riscv_vmv_v_x_i32m1(0, __riscv_vsetvlmax_e32m1()); + for (int cn_index = 0; cn_index < cn; cn_index++) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e8m4(len - i); + auto v = __riscv_vlse8_v_i8m4(src + cn * i + cn_index, sizeof(schar) * cn, vl); + auto m = __riscv_vle8_v_u8m4(mask + i, vl); + auto b = __riscv_vmsne(m, 0, vl); + s = __riscv_vwredsum(b, __riscv_vwmul(b, v, v, vl), s, vl); + } + } + return __riscv_vmv_x(s); + } +}; + +template<> +struct MaskedNormInf_RVV { + int operator() (const ushort* src, const uchar* mask, int len, int cn) const { + int vlmax = __riscv_vsetvlmax_e16m8(); + auto s = __riscv_vmv_v_x_u16m8(0, vlmax); + for (int cn_index = 0; cn_index < cn; cn_index++) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e16m8(len - i); + auto v = __riscv_vlse16_v_u16m8(src + cn * i + cn_index, sizeof(ushort) * cn, vl); + auto m = __riscv_vle8_v_u8m4(mask + i, vl); + auto b = __riscv_vmsne(m, 0, vl); + s = __riscv_vmaxu_tumu(b, s, s, v, vl); + } + } + return __riscv_vmv_x(__riscv_vredmaxu(s, __riscv_vmv_s_x_u16m1(0, __riscv_vsetvlmax_e16m1()), vlmax)); + } +}; + +template<> +struct MaskedNormL1_RVV { + int operator() (const ushort* src, const uchar* mask, int len, int cn) const { + auto s = __riscv_vmv_v_x_u32m1(0, __riscv_vsetvlmax_e32m1()); + for (int cn_index = 0; cn_index < cn; cn_index++) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e8m4(len - i); + auto v = __riscv_vlse16_v_u16m8(src + cn * i + cn_index, sizeof(ushort) * cn, vl); + auto m = __riscv_vle8_v_u8m4(mask + i, vl); + auto b = __riscv_vmsne(m, 0, vl); + s = __riscv_vwredsumu_tum(b, s, v, s, vl); + } + } + return __riscv_vmv_x(s); + } +}; + +template<> +struct MaskedNormL2_RVV { + double operator() (const ushort* src, const uchar* mask, int len, int cn) const { + int vlmax = __riscv_vsetvlmax_e16m2(); + auto s = __riscv_vfmv_v_f_f64m8(0, vlmax); + for (int cn_index = 0; cn_index < cn; cn_index++) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e16m2(len - i); + auto v = __riscv_vlse16_v_u16m2(src + cn * i + cn_index, sizeof(ushort) * cn, vl); + auto m = __riscv_vle8_v_u8m1(mask + i, vl); + auto b = __riscv_vmsne(m, 0, vl); + auto v_mul = __riscv_vwmulu(b, v, v, vl); + s = __riscv_vfadd_tumu(b, s, s, __riscv_vfwcvt_f(b, v_mul, vl), vl); + } + } + return __riscv_vfmv_f(__riscv_vfredosum(s, __riscv_vfmv_s_f_f64m1(0, __riscv_vsetvlmax_e64m1()), vlmax)); + } +}; + +template<> +struct MaskedNormInf_RVV { + int operator() (const short* src, const uchar* mask, int len, int cn) const { + int vlmax = __riscv_vsetvlmax_e16m8(); + auto s = __riscv_vmv_v_x_u16m8(0, vlmax); + for (int cn_index = 0; cn_index < cn; cn_index++) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e16m8(len - i); + auto v = __riscv_vlse16_v_i16m8(src + cn * i + cn_index, sizeof(short) * cn, vl); + auto m = __riscv_vle8_v_u8m4(mask + i, vl); + auto b = __riscv_vmsne(m, 0, vl); + s = __riscv_vmaxu_tumu(b, s, s, custom_intrin::__riscv_vabs(v, vl), vl); + } + } + return __riscv_vmv_x(__riscv_vredmaxu(s, __riscv_vmv_s_x_u16m1(0, __riscv_vsetvlmax_e16m1()), vlmax)); + } +}; + +template<> +struct MaskedNormL1_RVV { + int operator() (const short* src, const uchar* mask, int len, int cn) const { + auto s = __riscv_vmv_v_x_u32m1(0, __riscv_vsetvlmax_e32m1()); + for (int cn_index = 0; cn_index < cn; cn_index++) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e8m4(len - i); + auto v = custom_intrin::__riscv_vabs(__riscv_vlse16_v_i16m8(src + cn * i + cn_index, sizeof(short) * cn, vl), vl); + auto m = __riscv_vle8_v_u8m4(mask + i, vl); + auto b = __riscv_vmsne(m, 0, vl); + s = __riscv_vwredsumu_tum(b, s, v, s, vl); + } + } + return __riscv_vmv_x(s); + } +}; + +template<> +struct MaskedNormL2_RVV { + double operator() (const short* src, const uchar* mask, int len, int cn) const { + int vlmax = __riscv_vsetvlmax_e16m2(); + auto s = __riscv_vfmv_v_f_f64m8(0, vlmax); + for (int cn_index = 0; cn_index < cn; cn_index++) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e16m2(len - i); + auto v = __riscv_vlse16_v_i16m2(src + cn * i + cn_index, sizeof(short) * cn, vl); + auto m = __riscv_vle8_v_u8m1(mask + i, vl); + auto b = __riscv_vmsne(m, 0, vl); + auto v_mul = __riscv_vwmul(b, v, v, vl); + s = __riscv_vfadd_tumu(b, s, s, __riscv_vfwcvt_f(b, v_mul, vl), vl); + } + } + return __riscv_vfmv_f(__riscv_vfredosum(s, __riscv_vfmv_s_f_f64m1(0, __riscv_vsetvlmax_e32m1()), vlmax)); + } +}; + +template<> +struct MaskedNormInf_RVV { + int operator() (const int* src, const uchar* mask, int len, int cn) const { + int vlmax = __riscv_vsetvlmax_e32m8(); + auto s = __riscv_vmv_v_x_u32m8(0, vlmax); + for (int cn_index = 0; cn_index < cn; cn_index++) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e32m8(len - i); + auto v = __riscv_vlse32_v_i32m8(src + cn * i + cn_index, sizeof(int) * cn, vl); + auto m = __riscv_vle8_v_u8m2(mask + i, vl); + auto b = __riscv_vmsne(m, 0, vl); + s = __riscv_vmaxu_tumu(b, s, s, custom_intrin::__riscv_vabs(v, vl), vl); + } + } + return __riscv_vmv_x(__riscv_vredmaxu(s, __riscv_vmv_s_x_u32m1(0, __riscv_vsetvlmax_e32m1()), vlmax)); + } +}; + +template<> +struct MaskedNormL1_RVV { + double operator() (const int* src, const uchar* mask, int len, int cn) const { + int vlmax = __riscv_vsetvlmax_e32m4(); + auto s = __riscv_vfmv_v_f_f64m8(0, vlmax); + for (int cn_index = 0; cn_index < cn; cn_index++) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e32m4(len - i); + auto v = __riscv_vlse32_v_i32m4(src + cn * i + cn_index, sizeof(int) * cn, vl); + auto m = __riscv_vle8_v_u8m1(mask + i, vl); + auto b = __riscv_vmsne(m, 0, vl); + s = __riscv_vfadd_tumu(b, s, s, __riscv_vfwcvt_f(b, custom_intrin::__riscv_vabs(v, vl), vl), vl); + } + } + return __riscv_vfmv_f(__riscv_vfredosum(s, __riscv_vfmv_s_f_f64m1(0, __riscv_vsetvlmax_e64m1()), vlmax)); + } +}; + +template<> +struct MaskedNormL2_RVV { + double operator() (const int* src, const uchar* mask, int len, int cn) const { + int vlmax = __riscv_vsetvlmax_e32m4(); + auto s = __riscv_vfmv_v_f_f64m8(0, vlmax); + for (int cn_index = 0; cn_index < cn; cn_index++) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e16m2(len - i); + auto v = __riscv_vlse32_v_i32m4(src + cn * i + cn_index, sizeof(int) * cn, vl); + auto m = __riscv_vle8_v_u8m1(mask + i, vl); + auto b = __riscv_vmsne(m, 0, vl); + auto v_mul = __riscv_vwmul(b, v, v, vl); + s = __riscv_vfadd_tumu(b, s, s, __riscv_vfcvt_f(b, v_mul, vl), vl); + } + } + return __riscv_vfmv_f(__riscv_vfredosum(s, __riscv_vfmv_s_f_f64m1(0, __riscv_vsetvlmax_e64m1()), vlmax)); + } +}; + +template<> +struct MaskedNormInf_RVV { + float operator() (const float* src, const uchar* mask, int len, int cn) const { + int vlmax = __riscv_vsetvlmax_e32m8(); + auto s = __riscv_vfmv_v_f_f32m8(0, vlmax); + if (cn == 1) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e32m8(len - i); + auto v = __riscv_vle32_v_f32m8(src + i, vl); + auto m = __riscv_vle8_v_u8m2(mask + i, vl); + auto b = __riscv_vmsne(m, 0, vl); + s = __riscv_vfmax_tumu(b, s, s, __riscv_vfabs(v, vl), vl); + } + } else { + for (int cn_index = 0; cn_index < cn; cn_index++) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e32m8(len - i); + auto v = __riscv_vlse32_v_f32m8(src + cn * i + cn_index, sizeof(float) * cn, vl); + auto m = __riscv_vle8_v_u8m2(mask + i, vl); + auto b = __riscv_vmsne(m, 0, vl); + s = __riscv_vfmax_tumu(b, s, s, __riscv_vfabs(v, vl), vl); + } + } + } + return __riscv_vfmv_f(__riscv_vfredmax(s, __riscv_vfmv_s_f_f32m1(0, __riscv_vsetvlmax_e32m1()), vlmax)); + } +}; + +template<> +struct MaskedNormL1_RVV { + double operator() (const float* src, const uchar* mask, int len, int cn) const { + int vlmax = __riscv_vsetvlmax_e32m4(); + auto s = __riscv_vfmv_v_f_f64m8(0, vlmax); + if (cn == 1) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e32m4(len - i); + auto v = __riscv_vle32_v_f32m4(src + i, vl); + auto m = __riscv_vle8_v_u8m1(mask + i, vl); + auto b = __riscv_vmsne(m, 0, vl); + s = __riscv_vfadd_tumu(b, s, s, __riscv_vfwcvt_f(b, __riscv_vfabs(v, vl), vl), vl); + } + } else { + for (int cn_index = 0; cn_index < cn; cn_index++) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e32m4(len - i); + auto v = __riscv_vlse32_v_f32m4(src + cn * i + cn_index, sizeof(float) * cn, vl); + auto m = __riscv_vle8_v_u8m1(mask + i, vl); + auto b = __riscv_vmsne(m, 0, vl); + s = __riscv_vfadd_tumu(b, s, s, __riscv_vfwcvt_f(b, __riscv_vfabs(v, vl), vl), vl); + } + } + } + return __riscv_vfmv_f(__riscv_vfredosum(s, __riscv_vfmv_s_f_f64m1(0, __riscv_vsetvlmax_e64m1()), vlmax)); + } +}; + +template<> +struct MaskedNormL2_RVV { + double operator() (const float* src, const uchar* mask, int len, int cn) const { + int vlmax = __riscv_vsetvlmax_e32m4(); + auto s = __riscv_vfmv_v_f_f64m8(0, vlmax); + if (cn == 1) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e32m4(len - i); + auto v = __riscv_vle32_v_f32m4(src + i, vl); + auto m = __riscv_vle8_v_u8m1(mask + i, vl); + auto b = __riscv_vmsne(m, 0, vl); + auto v_mul = __riscv_vfwmul(b, v, v, vl); + s = __riscv_vfadd_tumu(b, s, s, v_mul, vl); + } + } else { + for (int cn_index = 0; cn_index < cn; cn_index++) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e32m4(len - i); + auto v = __riscv_vlse32_v_f32m4(src + cn * i + cn_index, sizeof(float) * cn, vl); + auto m = __riscv_vle8_v_u8m1(mask + i, vl); + auto b = __riscv_vmsne(m, 0, vl); + auto v_mul = __riscv_vfwmul(b, v, v, vl); + s = __riscv_vfadd_tumu(b, s, s, v_mul, vl); + } + } + } + return __riscv_vfmv_f(__riscv_vfredosum(s, __riscv_vfmv_s_f_f64m1(0, __riscv_vsetvlmax_e64m1()), vlmax)); + } +}; + +template<> +struct MaskedNormInf_RVV { + double operator() (const double* src, const uchar* mask, int len, int cn) const { + int vlmax = __riscv_vsetvlmax_e64m8(); + auto s = __riscv_vfmv_v_f_f64m8(0, vlmax); + for (int cn_index = 0; cn_index < cn; cn_index++) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e64m8(len - i); + auto v = __riscv_vlse64_v_f64m8(src + cn * i + cn_index, sizeof(double) * cn, vl); + auto m = __riscv_vle8_v_u8m1(mask + i, vl); + auto b = __riscv_vmsne(m, 0, vl); + s = __riscv_vfmax_tumu(b, s, s, __riscv_vfabs(v, vl), vl); + } + } + return __riscv_vfmv_f(__riscv_vfredmax(s, __riscv_vfmv_s_f_f64m1(0, __riscv_vsetvlmax_e64m1()), vlmax)); + } +}; + +template<> +struct MaskedNormL1_RVV { + double operator() (const double* src, const uchar* mask, int len, int cn) const { + int vlmax = __riscv_vsetvlmax_e64m8(); + auto s = __riscv_vfmv_v_f_f64m8(0, vlmax); + for (int cn_index = 0; cn_index < cn; cn_index++) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e64m8(len - i); + auto v = __riscv_vlse64_v_f64m8(src + cn * i + cn_index, sizeof(double) * cn, vl); + auto m = __riscv_vle8_v_u8m1(mask + i, vl); + auto b = __riscv_vmsne(m, 0, vl); + s = __riscv_vfadd_tumu(b, s, s, __riscv_vfabs(v, vl), vl); + } + } + return __riscv_vfmv_f(__riscv_vfredosum(s, __riscv_vfmv_s_f_f64m1(0, __riscv_vsetvlmax_e64m1()), vlmax)); + } +}; + +template<> +struct MaskedNormL2_RVV { + double operator() (const double* src, const uchar* mask, int len, int cn) const { + int vlmax = __riscv_vsetvlmax_e64m8(); + auto s = __riscv_vfmv_v_f_f64m8(0, vlmax); + for (int cn_index = 0; cn_index < cn; cn_index++) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e64m8(len - i); + auto v = __riscv_vlse64_v_f64m8(src + cn * i + cn_index, sizeof(double) * cn, vl); + auto m = __riscv_vle8_v_u8m1(mask + i, vl); + auto b = __riscv_vmsne(m, 0, vl); + auto v_mul = __riscv_vfmul(b, v, v, vl); + s = __riscv_vfadd_tumu(b, s, s, v_mul, vl); + } + } + return __riscv_vfmv_f(__riscv_vfredosum(s, __riscv_vfmv_s_f_f64m1(0, __riscv_vsetvlmax_e64m1()), vlmax)); + } +}; + +template int +normInf_(const T* src, const uchar* mask, ST* _result, int len, int cn) { + ST result = *_result; + if( !mask ) { + NormInf_RVV op; + result = std::max(result, op(src, len*cn)); + } else { + MaskedNormInf_RVV op; + result = std::max(result, op(src, mask, len, cn)); + } + *_result = result; + return 0; } -inline int normL1_8UC1(const uchar* src, size_t src_step, const uchar* mask, size_t mask_step, int width, int height, double* result) -{ - int vlmax = __riscv_vsetvlmax_e8m2(); - auto vec_sum = __riscv_vmv_v_x_u32m8(0, vlmax); - - if (mask) - { - for (int i = 0; i < height; i++) - { - const uchar* src_row = src + i * src_step; - const uchar* mask_row = mask + i * mask_step; - int vl; - for (int j = 0; j < width; j += vl) - { - vl = __riscv_vsetvl_e8m2(width - j); - auto vec_src = __riscv_vle8_v_u8m2(src_row + j, vl); - auto vec_mask = __riscv_vle8_v_u8m2(mask_row + j, vl); - auto bool_mask = __riscv_vmsne(vec_mask, 0, vl); - auto vec_zext = __riscv_vzext_vf4_u32m8_m(bool_mask, vec_src, vl); - vec_sum = __riscv_vadd_tumu(bool_mask, vec_sum, vec_sum, vec_zext, vl); - } - } +template int +normL1_(const T* src, const uchar* mask, ST* _result, int len, int cn) { + ST result = *_result; + if( !mask ) { + NormL1_RVV op; + result += op(src, len*cn); + } else { + MaskedNormL1_RVV op; + result += op(src, mask, len, cn); } - else - { - for (int i = 0; i < height; i++) - { - const uchar* src_row = src + i * src_step; - int vl; - for (int j = 0; j < width; j += vl) - { - vl = __riscv_vsetvl_e8m2(width - j); - auto vec_src = __riscv_vle8_v_u8m2(src_row + j, vl); - auto vec_zext = __riscv_vzext_vf4(vec_src, vl); - vec_sum = __riscv_vadd_tu(vec_sum, vec_sum, vec_zext, vl); - } - } - } - auto sc_sum = __riscv_vmv_s_x_u32m1(0, vlmax); - sc_sum = __riscv_vredsum(vec_sum, sc_sum, vlmax); - *result = __riscv_vmv_x(sc_sum); - - return CV_HAL_ERROR_OK; + *_result = result; + return 0; } -inline int normL2Sqr_8UC1(const uchar* src, size_t src_step, const uchar* mask, size_t mask_step, int width, int height, double* result) -{ - int vlmax = __riscv_vsetvlmax_e8m2(); - auto vec_sum = __riscv_vmv_v_x_u32m8(0, vlmax); - int cnt = 0; - auto reduce = [&](int vl) { - if ((cnt += vl) < (1 << 16)) - return; - cnt = vl; - for (int i = 0; i < vlmax; i++) - { - *result += __riscv_vmv_x(vec_sum); - vec_sum = __riscv_vslidedown(vec_sum, 1, vlmax); - } - vec_sum = __riscv_vmv_v_x_u32m8(0, vlmax); - }; - - *result = 0; - if (mask) - { - for (int i = 0; i < height; i++) - { - const uchar* src_row = src + i * src_step; - const uchar* mask_row = mask + i * mask_step; - int vl; - for (int j = 0; j < width; j += vl) - { - vl = __riscv_vsetvl_e8m2(width - j); - reduce(vl); - - auto vec_src = __riscv_vle8_v_u8m2(src_row + j, vl); - auto vec_mask = __riscv_vle8_v_u8m2(mask_row + j, vl); - auto bool_mask = __riscv_vmsne(vec_mask, 0, vl); - auto vec_mul = __riscv_vwmulu_vv_u16m4_m(bool_mask, vec_src, vec_src, vl); - auto vec_zext = __riscv_vzext_vf2_u32m8_m(bool_mask, vec_mul, vl); - vec_sum = __riscv_vadd_tumu(bool_mask, vec_sum, vec_sum, vec_zext, vl); - } - } +template int +normL2_(const T* src, const uchar* mask, ST* _result, int len, int cn) { + ST result = *_result; + if( !mask ) { + NormL2_RVV op; + result += op(src, len*cn); + } else { + MaskedNormL2_RVV op; + result += op(src, mask, len, cn); } - else - { - for (int i = 0; i < height; i++) - { - const uchar* src_row = src + i * src_step; - int vl; - for (int j = 0; j < width; j += vl) - { - vl = __riscv_vsetvl_e8m2(width - j); - reduce(vl); - - auto vec_src = __riscv_vle8_v_u8m2(src_row + j, vl); - auto vec_mul = __riscv_vwmulu(vec_src, vec_src, vl); - auto vec_zext = __riscv_vzext_vf2(vec_mul, vl); - vec_sum = __riscv_vadd_tu(vec_sum, vec_sum, vec_zext, vl); - } - } - } - reduce(1 << 16); - - return CV_HAL_ERROR_OK; + *_result = result; + return 0; } -inline int normInf_8UC4(const uchar* src, size_t src_step, const uchar* mask, size_t mask_step, int width, int height, double* result) -{ - int vlmax = __riscv_vsetvlmax_e8m8(); - auto vec_max = __riscv_vmv_v_x_u8m8(0, vlmax); +#define CV_HAL_RVV_DEF_NORM_FUNC(L, suffix, type, ntype) \ + static int norm##L##_##suffix(const type* src, const uchar* mask, ntype* r, int len, int cn) \ + { return norm##L##_(src, mask, r, len, cn); } - if (mask) - { - for (int i = 0; i < height; i++) - { - const uchar* src_row = src + i * src_step; - const uchar* mask_row = mask + i * mask_step; - int vl, vlm; - for (int j = 0, jm = 0; j < width * 4; j += vl, jm += vlm) - { - vl = __riscv_vsetvl_e8m8(width * 4 - j); - vlm = __riscv_vsetvl_e8m2(width - jm); - auto vec_src = __riscv_vle8_v_u8m8(src_row + j, vl); - auto vec_mask = __riscv_vle8_v_u8m2(mask_row + jm, vlm); - auto vec_mask_ext = __riscv_vmul(__riscv_vzext_vf4(__riscv_vminu(vec_mask, 1, vlm), vlm), 0x01010101, vlm); - auto bool_mask_ext = __riscv_vmsne(__riscv_vreinterpret_u8m8(vec_mask_ext), 0, vl); - vec_max = __riscv_vmaxu_tumu(bool_mask_ext, vec_max, vec_max, vec_src, vl); - } - } - } - else - { - for (int i = 0; i < height; i++) - { - const uchar* src_row = src + i * src_step; - int vl; - for (int j = 0; j < width * 4; j += vl) - { - vl = __riscv_vsetvl_e8m8(width * 4 - j); - auto vec_src = __riscv_vle8_v_u8m8(src_row + j, vl); - vec_max = __riscv_vmaxu_tu(vec_max, vec_max, vec_src, vl); - } - } - } - auto sc_max = __riscv_vmv_s_x_u8m1(0, vlmax); - sc_max = __riscv_vredmaxu(vec_max, sc_max, vlmax); - *result = __riscv_vmv_x(sc_max); +#define CV_HAL_RVV_DEF_NORM_ALL(suffix, type, inftype, l1type, l2type) \ + CV_HAL_RVV_DEF_NORM_FUNC(Inf, suffix, type, inftype) \ + CV_HAL_RVV_DEF_NORM_FUNC(L1, suffix, type, l1type) \ + CV_HAL_RVV_DEF_NORM_FUNC(L2, suffix, type, l2type) + +CV_HAL_RVV_DEF_NORM_ALL(8u, uchar, int, int, int) +CV_HAL_RVV_DEF_NORM_ALL(8s, schar, int, int, int) +CV_HAL_RVV_DEF_NORM_ALL(16u, ushort, int, int, double) +CV_HAL_RVV_DEF_NORM_ALL(16s, short, int, int, double) +CV_HAL_RVV_DEF_NORM_ALL(32s, int, int, double, double) +CV_HAL_RVV_DEF_NORM_ALL(32f, float, float, double, double) +CV_HAL_RVV_DEF_NORM_ALL(64f, double, double, double, double) - return CV_HAL_ERROR_OK; -} - -inline int normL1_8UC4(const uchar* src, size_t src_step, const uchar* mask, size_t mask_step, int width, int height, double* result) -{ - int vlmax = __riscv_vsetvlmax_e8m2(); - auto vec_sum = __riscv_vmv_v_x_u32m8(0, vlmax); - - if (mask) - { - for (int i = 0; i < height; i++) - { - const uchar* src_row = src + i * src_step; - const uchar* mask_row = mask + i * mask_step; - int vl, vlm; - for (int j = 0, jm = 0; j < width * 4; j += vl, jm += vlm) - { - vl = __riscv_vsetvl_e8m2(width * 4 - j); - vlm = __riscv_vsetvl_e8mf2(width - jm); - auto vec_src = __riscv_vle8_v_u8m2(src_row + j, vl); - auto vec_mask = __riscv_vle8_v_u8mf2(mask_row + jm, vlm); - auto vec_mask_ext = __riscv_vmul(__riscv_vzext_vf4(__riscv_vminu(vec_mask, 1, vlm), vlm), 0x01010101, vlm); - auto bool_mask_ext = __riscv_vmsne(__riscv_vreinterpret_u8m2(vec_mask_ext), 0, vl); - auto vec_zext = __riscv_vzext_vf4_u32m8_m(bool_mask_ext, vec_src, vl); - vec_sum = __riscv_vadd_tumu(bool_mask_ext, vec_sum, vec_sum, vec_zext, vl); - } - } - } - else - { - for (int i = 0; i < height; i++) - { - const uchar* src_row = src + i * src_step; - int vl; - for (int j = 0; j < width * 4; j += vl) - { - vl = __riscv_vsetvl_e8m2(width * 4 - j); - auto vec_src = __riscv_vle8_v_u8m2(src_row + j, vl); - auto vec_zext = __riscv_vzext_vf4(vec_src, vl); - vec_sum = __riscv_vadd_tu(vec_sum, vec_sum, vec_zext, vl); - } - } - } - auto sc_sum = __riscv_vmv_s_x_u32m1(0, vlmax); - sc_sum = __riscv_vredsum(vec_sum, sc_sum, vlmax); - *result = __riscv_vmv_x(sc_sum); - - return CV_HAL_ERROR_OK; -} - -inline int normL2Sqr_8UC4(const uchar* src, size_t src_step, const uchar* mask, size_t mask_step, int width, int height, double* result) -{ - int vlmax = __riscv_vsetvlmax_e8m2(); - auto vec_sum = __riscv_vmv_v_x_u32m8(0, vlmax); - int cnt = 0; - auto reduce = [&](int vl) { - if ((cnt += vl) < (1 << 16)) - return; - cnt = vl; - for (int i = 0; i < vlmax; i++) - { - *result += __riscv_vmv_x(vec_sum); - vec_sum = __riscv_vslidedown(vec_sum, 1, vlmax); - } - vec_sum = __riscv_vmv_v_x_u32m8(0, vlmax); - }; - - *result = 0; - if (mask) - { - for (int i = 0; i < height; i++) - { - const uchar* src_row = src + i * src_step; - const uchar* mask_row = mask + i * mask_step; - int vl, vlm; - for (int j = 0, jm = 0; j < width * 4; j += vl, jm += vlm) - { - vl = __riscv_vsetvl_e8m2(width * 4 - j); - vlm = __riscv_vsetvl_e8mf2(width - jm); - reduce(vl); - - auto vec_src = __riscv_vle8_v_u8m2(src_row + j, vl); - auto vec_mask = __riscv_vle8_v_u8mf2(mask_row + jm, vlm); - auto vec_mask_ext = __riscv_vmul(__riscv_vzext_vf4(__riscv_vminu(vec_mask, 1, vlm), vlm), 0x01010101, vlm); - auto bool_mask_ext = __riscv_vmsne(__riscv_vreinterpret_u8m2(vec_mask_ext), 0, vl); - auto vec_mul = __riscv_vwmulu_vv_u16m4_m(bool_mask_ext, vec_src, vec_src, vl); - auto vec_zext = __riscv_vzext_vf2_u32m8_m(bool_mask_ext, vec_mul, vl); - vec_sum = __riscv_vadd_tumu(bool_mask_ext, vec_sum, vec_sum, vec_zext, vl); - } - } - } - else - { - for (int i = 0; i < height; i++) - { - const uchar* src_row = src + i * src_step; - int vl; - for (int j = 0; j < width * 4; j += vl) - { - vl = __riscv_vsetvl_e8m2(width * 4 - j); - reduce(vl); - - auto vec_src = __riscv_vle8_v_u8m2(src_row + j, vl); - auto vec_mul = __riscv_vwmulu(vec_src, vec_src, vl); - auto vec_zext = __riscv_vzext_vf2(vec_mul, vl); - vec_sum = __riscv_vadd_tu(vec_sum, vec_sum, vec_zext, vl); - } - } - } - reduce(1 << 16); - - return CV_HAL_ERROR_OK; -} - -inline int normInf_32FC1(const uchar* src, size_t src_step, const uchar* mask, size_t mask_step, int width, int height, double* result) -{ - int vlmax = __riscv_vsetvlmax_e32m8(); - auto vec_max = __riscv_vfmv_v_f_f32m8(0, vlmax); - - if (mask) - { - for (int i = 0; i < height; i++) - { - const float* src_row = reinterpret_cast(src + i * src_step); - const uchar* mask_row = mask + i * mask_step; - int vl; - for (int j = 0; j < width; j += vl) - { - vl = __riscv_vsetvl_e32m8(width - j); - auto vec_src = __riscv_vle32_v_f32m8(src_row + j, vl); - auto vec_mask = __riscv_vle8_v_u8m2(mask_row + j, vl); - auto bool_mask = __riscv_vmsne(vec_mask, 0, vl); - auto vec_abs = __riscv_vfabs_v_f32m8_m(bool_mask, vec_src, vl); - vec_max = __riscv_vfmax_tumu(bool_mask, vec_max, vec_max, vec_abs, vl); - } - } - } - else - { - for (int i = 0; i < height; i++) - { - const float* src_row = reinterpret_cast(src + i * src_step); - int vl; - for (int j = 0; j < width; j += vl) - { - vl = __riscv_vsetvl_e32m8(width - j); - auto vec_src = __riscv_vle32_v_f32m8(src_row + j, vl); - auto vec_abs = __riscv_vfabs(vec_src, vl); - vec_max = __riscv_vfmax_tu(vec_max, vec_max, vec_abs, vl); - } - } - } - auto sc_max = __riscv_vfmv_s_f_f32m1(0, vlmax); - sc_max = __riscv_vfredmax(vec_max, sc_max, vlmax); - *result = __riscv_vfmv_f(sc_max); - - return CV_HAL_ERROR_OK; -} - -inline int normL1_32FC1(const uchar* src, size_t src_step, const uchar* mask, size_t mask_step, int width, int height, double* result) -{ - int vlmax = __riscv_vsetvlmax_e32m4(); - auto vec_sum = __riscv_vfmv_v_f_f64m8(0, vlmax); - - if (mask) - { - for (int i = 0; i < height; i++) - { - const float* src_row = reinterpret_cast(src + i * src_step); - const uchar* mask_row = mask + i * mask_step; - int vl; - for (int j = 0; j < width; j += vl) - { - vl = __riscv_vsetvl_e32m4(width - j); - auto vec_src = __riscv_vle32_v_f32m4(src_row + j, vl); - auto vec_mask = __riscv_vle8_v_u8m1(mask_row + j, vl); - auto bool_mask = __riscv_vmsne(vec_mask, 0, vl); - auto vec_abs = __riscv_vfabs_v_f32m4_m(bool_mask, vec_src, vl); - auto vec_fext = __riscv_vfwcvt_f_f_v_f64m8_m(bool_mask, vec_abs, vl); - vec_sum = __riscv_vfadd_tumu(bool_mask, vec_sum, vec_sum, vec_fext, vl); - } - } - } - else - { - for (int i = 0; i < height; i++) - { - const float* src_row = reinterpret_cast(src + i * src_step); - int vl; - for (int j = 0; j < width; j += vl) - { - vl = __riscv_vsetvl_e32m4(width - j); - auto vec_src = __riscv_vle32_v_f32m4(src_row + j, vl); - auto vec_abs = __riscv_vfabs(vec_src, vl); - auto vec_fext = __riscv_vfwcvt_f_f_v_f64m8(vec_abs, vl); - vec_sum = __riscv_vfadd_tu(vec_sum, vec_sum, vec_fext, vl); - } - } - } - auto sc_sum = __riscv_vfmv_s_f_f64m1(0, vlmax); - sc_sum = __riscv_vfredosum(vec_sum, sc_sum, vlmax); - *result = __riscv_vfmv_f(sc_sum); - - return CV_HAL_ERROR_OK; -} - -inline int normL2Sqr_32FC1(const uchar* src, size_t src_step, const uchar* mask, size_t mask_step, int width, int height, double* result) -{ - int vlmax = __riscv_vsetvlmax_e32m4(); - auto vec_sum = __riscv_vfmv_v_f_f64m8(0, vlmax); - - if (mask) - { - for (int i = 0; i < height; i++) - { - const float* src_row = reinterpret_cast(src + i * src_step); - const uchar* mask_row = mask + i * mask_step; - int vl; - for (int j = 0; j < width; j += vl) - { - vl = __riscv_vsetvl_e32m4(width - j); - auto vec_src = __riscv_vle32_v_f32m4(src_row + j, vl); - auto vec_mask = __riscv_vle8_v_u8m1(mask_row + j, vl); - auto bool_mask = __riscv_vmsne(vec_mask, 0, vl); - auto vec_mul = __riscv_vfwmul_vv_f64m8_m(bool_mask, vec_src, vec_src, vl); - vec_sum = __riscv_vfadd_tumu(bool_mask, vec_sum, vec_sum, vec_mul, vl); - } - } - } - else - { - for (int i = 0; i < height; i++) - { - const float* src_row = reinterpret_cast(src + i * src_step); - int vl; - for (int j = 0; j < width; j += vl) - { - vl = __riscv_vsetvl_e32m4(width - j); - auto vec_src = __riscv_vle32_v_f32m4(src_row + j, vl); - auto vec_mul = __riscv_vfwmul(vec_src, vec_src, vl); - vec_sum = __riscv_vfadd_tu(vec_sum, vec_sum, vec_mul, vl); - } - } - } - auto sc_sum = __riscv_vfmv_s_f_f64m1(0, vlmax); - sc_sum = __riscv_vfredosum(vec_sum, sc_sum, vlmax); - *result = __riscv_vfmv_f(sc_sum); - - return CV_HAL_ERROR_OK; } +using NormFunc = int (*)(const uchar*, const uchar*, uchar*, int, int); inline int norm(const uchar* src, size_t src_step, const uchar* mask, size_t mask_step, int width, - int height, int type, int norm_type, double* result) -{ - if (!result) - return CV_HAL_ERROR_OK; + int height, int type, int norm_type, double* result) { + int depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type); - switch (type) - { - case CV_8UC1: - switch (norm_type) - { - case NORM_INF: - return normInf_8UC1(src, src_step, mask, mask_step, width, height, result); - case NORM_L1: - return normL1_8UC1(src, src_step, mask, mask_step, width, height, result); - case NORM_L2SQR: - return normL2Sqr_8UC1(src, src_step, mask, mask_step, width, height, result); - case NORM_L2: - int ret = normL2Sqr_8UC1(src, src_step, mask, mask_step, width, height, result); - *result = std::sqrt(*result); - return ret; - } - return CV_HAL_ERROR_NOT_IMPLEMENTED; - case CV_8UC4: - switch (norm_type) - { - case NORM_INF: - return normInf_8UC4(src, src_step, mask, mask_step, width, height, result); - case NORM_L1: - return normL1_8UC4(src, src_step, mask, mask_step, width, height, result); - case NORM_L2SQR: - return normL2Sqr_8UC4(src, src_step, mask, mask_step, width, height, result); - case NORM_L2: - int ret = normL2Sqr_8UC4(src, src_step, mask, mask_step, width, height, result); - *result = std::sqrt(*result); - return ret; - } - return CV_HAL_ERROR_NOT_IMPLEMENTED; - case CV_32FC1: - switch (norm_type) - { - case NORM_INF: - return normInf_32FC1(src, src_step, mask, mask_step, width, height, result); - case NORM_L1: - return normL1_32FC1(src, src_step, mask, mask_step, width, height, result); - case NORM_L2SQR: - return normL2Sqr_32FC1(src, src_step, mask, mask_step, width, height, result); - case NORM_L2: - int ret = normL2Sqr_32FC1(src, src_step, mask, mask_step, width, height, result); - *result = std::sqrt(*result); - return ret; - } + if (result == nullptr || depth == CV_16F || norm_type > NORM_L2SQR) { return CV_HAL_ERROR_NOT_IMPLEMENTED; } - return CV_HAL_ERROR_NOT_IMPLEMENTED; + // [FIXME] append 0's when merging to 5.x + static NormFunc norm_tab[3][CV_DEPTH_MAX] = { + { + (NormFunc)(normInf_8u), (NormFunc)(normInf_8s), + (NormFunc)(normInf_16u), (NormFunc)(normInf_16s), + (NormFunc)(normInf_32s), (NormFunc)(normInf_32f), + (NormFunc)(normInf_64f), 0, + }, + { + (NormFunc)(normL1_8u), (NormFunc)(normL1_8s), + (NormFunc)(normL1_16u), (NormFunc)(normL1_16s), + (NormFunc)(normL1_32s), (NormFunc)(normL1_32f), + (NormFunc)(normL1_64f), 0, + }, + { + (NormFunc)(normL2_8u), (NormFunc)(normL2_8s), + (NormFunc)(normL2_16u), (NormFunc)(normL2_16s), + (NormFunc)(normL2_32s), (NormFunc)(normL2_32f), + (NormFunc)(normL2_64f), 0, + }, + }; + + static const size_t elem_size_tab[CV_DEPTH_MAX] = { + sizeof(uchar), sizeof(schar), + sizeof(ushort), sizeof(short), + sizeof(int), sizeof(float), + sizeof(int64_t), 0, + }; + + bool src_continuous = (src_step == width * elem_size_tab[depth] * cn || (src_step != width * elem_size_tab[depth] * cn && height == 1)); + bool mask_continuous = (mask_step == width); + size_t nplanes = 1; + size_t size = width * height; + if ((mask && (!src_continuous || !mask_continuous)) || !src_continuous) { + nplanes = height; + size = width; + } + + NormFunc func = norm_tab[norm_type >> 1][depth]; + if (func == nullptr) { + return CV_HAL_ERROR_NOT_IMPLEMENTED; + } + + // Handle overflow + union { + double d; + int i; + float f; + } res; + res.d = 0; + if ((norm_type == NORM_L1 && depth <= CV_16S) || + ((norm_type == NORM_L2 || norm_type == NORM_L2SQR) && depth <= CV_8S)) { + const size_t esz = elem_size_tab[depth] * cn; + const int total = (int)size; + const int intSumBlockSize = (norm_type == NORM_L1 && depth <= CV_8S ? (1 << 23) : (1 << 15))/cn; + const int blockSize = std::min(total, intSumBlockSize); + int isum = 0; + int count = 0; + auto _src = src; + auto _mask = mask; + for (size_t i = 0; i < nplanes; i++) { + if ((mask && (!src_continuous || !mask_continuous)) || !src_continuous) { + _src = src + src_step * i; + _mask = mask + mask_step * i; + } + for (int j = 0; j < total; j += blockSize) { + int bsz = std::min(total - j, blockSize); + func(_src, _mask, (uchar*)&isum, bsz, cn); + count += bsz; + if (count + blockSize >= intSumBlockSize || (i + 1 >= nplanes && j + bsz >= total)) { + res.d += isum; + isum = 0; + count = 0; + } + _src += bsz * esz; + if (mask) { + _mask += bsz; + } + } + } + } else { + auto _src = src; + auto _mask = mask; + for (size_t i = 0; i < nplanes; i++) { + if ((mask && (!src_continuous || !mask_continuous)) || !src_continuous) { + _src = src + src_step * i; + _mask = mask + mask_step * i; + } + func(_src, _mask, (uchar*)&res, (int)size, cn); + } + } + + if (norm_type == NORM_INF) { + if (depth == CV_64F) { + *result = res.d; + } else if (depth == CV_32F) { + *result = res.f; + } else { + *result = res.i; + } + } else if (norm_type == NORM_L2) { + *result = std::sqrt(res.d); + } else { + *result = res.d; + } + + return CV_HAL_ERROR_OK; } -}}} +}}} // cv::cv_hal_rvv::norm #endif diff --git a/modules/core/perf/perf_norm.cpp b/modules/core/perf/perf_norm.cpp index 8bcf9ea224..c47398f8fc 100644 --- a/modules/core/perf/perf_norm.cpp +++ b/modules/core/perf/perf_norm.cpp @@ -14,7 +14,7 @@ typedef perf::TestBaseWithParam Size_MatType_NormType; PERF_TEST_P(Size_MatType_NormType, norm, testing::Combine( testing::Values(TYPICAL_MAT_SIZES), - testing::Values(TYPICAL_MAT_TYPES), + testing::Values(CV_8UC1, CV_8UC4, CV_8SC1, CV_16UC1, CV_16SC1, CV_32SC1, CV_32FC1, CV_64FC1), testing::Values((int)NORM_INF, (int)NORM_L1, (int)NORM_L2) ) ) @@ -36,7 +36,7 @@ PERF_TEST_P(Size_MatType_NormType, norm, PERF_TEST_P(Size_MatType_NormType, norm_mask, testing::Combine( testing::Values(TYPICAL_MAT_SIZES), - testing::Values(TYPICAL_MAT_TYPES), + testing::Values(CV_8UC1, CV_8UC4, CV_8SC1, CV_16UC1, CV_16SC1, CV_32SC1, CV_32FC1, CV_64FC1), testing::Values((int)NORM_INF, (int)NORM_L1, (int)NORM_L2) ) ) diff --git a/modules/core/src/norm.rvv1p0.hpp b/modules/core/src/norm.rvv1p0.hpp deleted file mode 100644 index 3db05c50a4..0000000000 --- a/modules/core/src/norm.rvv1p0.hpp +++ /dev/null @@ -1,200 +0,0 @@ -// This file is part of OpenCV project. -// It is subject to the license terms in the LICENSE file found in the top-level directory -// of this distribution and at http://opencv.org/license.html. -// -// Copytright (C) 2025, SpaceMIT Inc., all rights reserved. - -#include "opencv2/core/hal/intrin.hpp" - -namespace cv { - -namespace { - -// [TODO] Drop this until rvv has dedicated intrinsics for abs on integers. -template inline ST __riscv_vabs(const T&); - -template<> inline -vuint8m1_t __riscv_vabs(const vint8m1_t& v) { - const int vle8m1 = __riscv_vsetvlmax_e8m1(); - vint8m1_t mask = __riscv_vsra_vx_i8m1(v, 7, vle8m1); - vint8m1_t v_xor = __riscv_vxor_vv_i8m1(v, mask, vle8m1); - return __riscv_vreinterpret_v_i8m1_u8m1( - __riscv_vsub_vv_i8m1(v_xor, mask, vle8m1) - ); -} - -template<> inline -vuint16m1_t __riscv_vabs(const vint16m1_t& v) { - const int vle16m1 = __riscv_vsetvlmax_e16m1(); - vint16m1_t mask = __riscv_vsra_vx_i16m1(v, 15, vle16m1); - vint16m1_t v_xor = __riscv_vxor_vv_i16m1(v, mask, vle16m1); - return __riscv_vreinterpret_v_i16m1_u16m1( - __riscv_vsub_vv_i16m1(v_xor, mask, vle16m1) - ); -} - -template<> inline -vuint32m1_t __riscv_vabs(const vint32m1_t& v) { - const int vle32m1 = __riscv_vsetvlmax_e32m1(); - vint32m1_t mask = __riscv_vsra_vx_i32m1(v, 31, vle32m1); - vint32m1_t v_xor = __riscv_vxor_vv_i32m1(v, mask, vle32m1); - return __riscv_vreinterpret_v_i32m1_u32m1( - __riscv_vsub_vv_i32m1(v_xor, mask, vle32m1) - ); -} -} - -CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN - -template inline -ST normInf_rvv(const T* src, int n, int& j); - -template<> inline -int normInf_rvv(const int* src, int n, int& j) { - const int vle32m1 = __riscv_vsetvlmax_e32m1(); - vuint32m1_t r0 = __riscv_vmv_v_x_u32m1(0, vle32m1); - vuint32m1_t r1 = __riscv_vmv_v_x_u32m1(0, vle32m1); - for (; j <= n - 2 * vle32m1; j += 2 * vle32m1) { - vuint32m1_t v0 = __riscv_vabs(__riscv_vle32_v_i32m1(src + j, vle32m1)); - r0 = __riscv_vmaxu(r0, v0, vle32m1); - - vuint32m1_t v1 = __riscv_vabs(__riscv_vle32_v_i32m1(src + j + vle32m1, vle32m1)); - r1 = __riscv_vmaxu(r1, v1, vle32m1); - } - r0 = __riscv_vmaxu(r0, r1, vle32m1); - return (int)__riscv_vmv_x(__riscv_vredmaxu(r0, __riscv_vmv_v_x_u32m1(0, vle32m1), vle32m1)); -} - -template inline -ST normL1_rvv(const T* src, int n, int& j); - -template<> inline -int normL1_rvv(const schar* src, int n, int& j) { - const int vle8m1 = __riscv_vsetvlmax_e8m1(); - const int vle16m1 = __riscv_vsetvlmax_e16m1(); - const int vle32m1 = __riscv_vsetvlmax_e32m1(); - vuint32m1_t r0 = __riscv_vmv_v_x_u32m1(0, vle32m1); - vuint32m1_t r1 = __riscv_vmv_v_x_u32m1(0, vle32m1); - vuint16m1_t zero = __riscv_vmv_v_x_u16m1(0, vle16m1); - for (; j <= n - 2 * vle8m1; j += 2 * vle8m1) { - vuint8m1_t v0 = __riscv_vabs(__riscv_vle8_v_i8m1(src + j, vle8m1)); - vuint16m1_t u0 = __riscv_vwredsumu_tu(zero, v0, zero, vle8m1); - r0 = __riscv_vwredsumu(u0, r0, vle16m1); - - vuint8m1_t v1 = __riscv_vabs(__riscv_vle8_v_i8m1(src + j + vle8m1, vle8m1)); - vuint16m1_t u1 = __riscv_vwredsumu_tu(zero, v1, zero, vle8m1); - r1 = __riscv_vwredsumu(u1, r1, vle16m1); - } - return (int)__riscv_vmv_x(__riscv_vadd(r0, r1, vle32m1)); -} - -template<> inline -int normL1_rvv(const ushort* src, int n, int& j) { - const int vle16m1 = __riscv_vsetvlmax_e16m1(); - const int vle32m1 = __riscv_vsetvlmax_e32m1(); - vuint32m1_t r0 = __riscv_vmv_v_x_u32m1(0, vle32m1); - vuint32m1_t r1 = __riscv_vmv_v_x_u32m1(0, vle32m1); - for (; j <= n - 2 * vle16m1; j += 2 * vle16m1) { - vuint16m1_t v0 = __riscv_vle16_v_u16m1(src + j, vle16m1); - r0 = __riscv_vwredsumu(v0, r0, vle16m1); - - vuint16m1_t v1 = __riscv_vle16_v_u16m1(src + j + vle16m1, vle16m1); - r1 = __riscv_vwredsumu(v1, r1, vle16m1); - } - return (int)__riscv_vmv_x(__riscv_vadd(r0, r1, vle32m1)); -} - -template<> inline -int normL1_rvv(const short* src, int n, int& j) { - const int vle16m1 = __riscv_vsetvlmax_e16m1(); - const int vle32m1 = __riscv_vsetvlmax_e32m1(); - vuint32m1_t r0 = __riscv_vmv_v_x_u32m1(0, vle32m1); - vuint32m1_t r1 = __riscv_vmv_v_x_u32m1(0, vle32m1); - for (; j<= n - 2 * vle16m1; j += 2 * vle16m1) { - vuint16m1_t v0 = __riscv_vabs(__riscv_vle16_v_i16m1(src + j, vle16m1)); - r0 = __riscv_vwredsumu(v0, r0, vle16m1); - - vuint16m1_t v1 = __riscv_vabs(__riscv_vle16_v_i16m1(src + j + vle16m1, vle16m1)); - r1 = __riscv_vwredsumu(v1, r1, vle16m1); - } - return (int)__riscv_vmv_x(__riscv_vadd(r0, r1, vle32m1)); -} - -template<> inline -double normL1_rvv(const double* src, int n, int& j) { - const int vle64m1 = __riscv_vsetvlmax_e64m1(); - vfloat64m1_t r0 = __riscv_vfmv_v_f_f64m1(0.f, vle64m1); - vfloat64m1_t r1 = __riscv_vfmv_v_f_f64m1(0.f, vle64m1); - for (; j <= n - 2 * vle64m1; j += 2 * vle64m1) { - vfloat64m1_t v0 = __riscv_vle64_v_f64m1(src + j, vle64m1); - v0 = __riscv_vfabs(v0, vle64m1); - r0 = __riscv_vfadd(r0, v0, vle64m1); - - vfloat64m1_t v1 = __riscv_vle64_v_f64m1(src + j + vle64m1, vle64m1); - v1 = __riscv_vfabs(v1, vle64m1); - r1 = __riscv_vfadd(r1, v1, vle64m1); - } - r0 = __riscv_vfadd(r0, r1, vle64m1); - return __riscv_vfmv_f(__riscv_vfredusum(r0, __riscv_vfmv_v_f_f64m1(0.f, vle64m1), vle64m1)); -} - -template inline -ST normL2_rvv(const T* src, int n, int& j); - -template<> inline -int normL2_rvv(const uchar* src, int n, int& j) { - const int vle8m1 = __riscv_vsetvlmax_e8m1(); - const int vle16m1 = __riscv_vsetvlmax_e16m1(); - const int vle32m1 = __riscv_vsetvlmax_e32m1(); - vuint32m1_t r0 = __riscv_vmv_v_x_u32m1(0, vle32m1); - vuint32m1_t r1 = __riscv_vmv_v_x_u32m1(0, vle32m1); - for (; j <= n - 2 * vle8m1; j += 2 * vle8m1) { - vuint8m1_t v0 = __riscv_vle8_v_u8m1(src + j, vle8m1); - vuint16m2_t u0 = __riscv_vwmulu(v0, v0, vle8m1); - r0 = __riscv_vwredsumu(u0, r0, vle16m1 * 2); - - vuint8m1_t v1 = __riscv_vle8_v_u8m1(src + j + vle8m1, vle8m1); - vuint16m2_t u1 = __riscv_vwmulu(v1, v1, vle8m1); - r1 = __riscv_vwredsumu(u1, r1, vle16m1 * 2); - } - return (int)__riscv_vmv_x(__riscv_vadd(r0, r1, vle32m1)); -} - -template<> inline -int normL2_rvv(const schar* src, int n, int& j) { - const int vle8m1 = __riscv_vsetvlmax_e8m1(); - const int vle16m1 = __riscv_vsetvlmax_e16m1(); - const int vle32m1 = __riscv_vsetvlmax_e32m1(); - vint32m1_t r0 = __riscv_vmv_v_x_i32m1(0, vle32m1); - vint32m1_t r1 = __riscv_vmv_v_x_i32m1(0, vle32m1); - for (; j <= n - 2 * vle8m1; j += 2 * vle8m1) { - vint8m1_t v0 = __riscv_vle8_v_i8m1(src + j, vle8m1); - vint16m2_t u0 = __riscv_vwmul(v0, v0, vle8m1); - r0 = __riscv_vwredsum(u0, r0, vle16m1 * 2); - - vint8m1_t v1 = __riscv_vle8_v_i8m1(src + j + vle8m1, vle8m1); - vint16m2_t u1 = __riscv_vwmul(v1, v1, vle8m1); - r1 = __riscv_vwredsum(u1, r1, vle16m1 * 2); - } - return __riscv_vmv_x(__riscv_vadd(r0, r1, vle32m1)); -} - -template<> inline -double normL2_rvv(const double* src, int n, int& j) { - const int vle64m1 = __riscv_vsetvlmax_e64m1(); - vfloat64m1_t r0 = __riscv_vfmv_v_f_f64m1(0.f, vle64m1); - vfloat64m1_t r1 = __riscv_vfmv_v_f_f64m1(0.f, vle64m1); - for (; j <= n - 2 * vle64m1; j += 2 * vle64m1) { - vfloat64m1_t v0 = __riscv_vle64_v_f64m1(src + j, vle64m1); - r0 = __riscv_vfmacc(r0, v0, v0, vle64m1); - - vfloat64m1_t v1 = __riscv_vle64_v_f64m1(src + j + vle64m1, vle64m1); - r1 = __riscv_vfmacc(r1, v1, v1, vle64m1); - } - r0 = __riscv_vfadd(r0, r1, vle64m1); - return __riscv_vfmv_f(__riscv_vfredusum(r0, __riscv_vfmv_v_f_f64m1(0.f, vle64m1), vle64m1)); -} - -CV_CPU_OPTIMIZATION_NAMESPACE_END - -} // cv:: diff --git a/modules/core/src/norm.simd.hpp b/modules/core/src/norm.simd.hpp index c2b72d2e13..0c3cd5d995 100644 --- a/modules/core/src/norm.simd.hpp +++ b/modules/core/src/norm.simd.hpp @@ -4,10 +4,6 @@ #include "precomp.hpp" -#if CV_RVV -#include "norm.rvv1p0.hpp" -#endif - namespace cv { using NormFunc = int (*)(const uchar*, const uchar*, uchar*, int, int); @@ -181,9 +177,6 @@ struct NormInf_SIMD { int operator() (const int* src, int n) const { int j = 0; int s = 0; -#if CV_RVV - s = normInf_rvv(src, n, j); -#else v_uint32 r0 = vx_setzero_u32(), r1 = vx_setzero_u32(); v_uint32 r2 = vx_setzero_u32(), r3 = vx_setzero_u32(); for (; j <= n - 4 * VTraits::vlanes(); j += 4 * VTraits::vlanes()) { @@ -194,7 +187,6 @@ struct NormInf_SIMD { } r0 = v_max(r0, v_max(r1, v_max(r2, r3))); s = std::max(s, saturate_cast(v_reduce_max(r0))); -#endif for (; j < n; j++) { s = std::max(s, cv_abs(src[j])); } @@ -250,9 +242,6 @@ struct NormL1_SIMD { int operator() (const schar* src, int n) const { int j = 0; int s = 0; -#if CV_RVV - s = normL1_rvv(src, n, j); -#else v_uint32 r0 = vx_setzero_u32(), r1 = vx_setzero_u32(); v_uint8 one = vx_setall_u8(1); for (; j<= n - 2 * VTraits::vlanes(); j += 2 * VTraits::vlanes()) { @@ -263,7 +252,6 @@ struct NormL1_SIMD { r1 = v_dotprod_expand_fast(v1, one, r1); } s += v_reduce_sum(v_add(r0, r1)); -#endif for (; j < n; j++) { s += saturate_cast(cv_abs(src[j])); } @@ -276,9 +264,6 @@ struct NormL1_SIMD { int operator() (const ushort* src, int n) const { int j = 0; int s = 0; -#if CV_RVV - s = normL1_rvv(src, n, j); -#else v_uint32 r00 = vx_setzero_u32(), r01 = vx_setzero_u32(); v_uint32 r10 = vx_setzero_u32(), r11 = vx_setzero_u32(); for (; j<= n - 2 * VTraits::vlanes(); j += 2 * VTraits::vlanes()) { @@ -295,7 +280,6 @@ struct NormL1_SIMD { r11 = v_add(r11, v11); } s += (int)v_reduce_sum(v_add(v_add(v_add(r00, r01), r10), r11)); -#endif for (; j < n; j++) { s += src[j]; } @@ -308,9 +292,6 @@ struct NormL1_SIMD { int operator() (const short* src, int n) const { int j = 0; int s = 0; -#if CV_RVV - s = normL1_rvv(src, n, j); -#else v_uint32 r00 = vx_setzero_u32(), r01 = vx_setzero_u32(); v_uint32 r10 = vx_setzero_u32(), r11 = vx_setzero_u32(); for (; j<= n - 2 * VTraits::vlanes(); j += 2 * VTraits::vlanes()) { @@ -327,7 +308,6 @@ struct NormL1_SIMD { r11 = v_add(r11, v11); } s += (int)v_reduce_sum(v_add(v_add(v_add(r00, r01), r10), r11)); -#endif for (; j < n; j++) { s += saturate_cast(cv_abs(src[j])); } @@ -340,9 +320,6 @@ struct NormL2_SIMD { int operator() (const uchar* src, int n) const { int j = 0; int s = 0; -#if CV_RVV - s = normL2_rvv(src, n, j); -#else v_uint32 r0 = vx_setzero_u32(), r1 = vx_setzero_u32(); for (; j <= n - 2 * VTraits::vlanes(); j += 2 * VTraits::vlanes()) { v_uint8 v0 = vx_load(src + j); @@ -352,7 +329,6 @@ struct NormL2_SIMD { r1 = v_dotprod_expand_fast(v1, v1, r1); } s += v_reduce_sum(v_add(r0, r1)); -#endif for (; j < n; j++) { int v = saturate_cast(src[j]); s += v * v; @@ -366,9 +342,6 @@ struct NormL2_SIMD { int operator() (const schar* src, int n) const { int j = 0; int s = 0; -#if CV_RVV - s = normL2_rvv(src, n, j); -#else v_int32 r0 = vx_setzero_s32(), r1 = vx_setzero_s32(); for (; j <= n - 2 * VTraits::vlanes(); j += 2 * VTraits::vlanes()) { v_int8 v0 = vx_load(src + j); @@ -377,7 +350,6 @@ struct NormL2_SIMD { r1 = v_dotprod_expand_fast(v1, v1, r1); } s += v_reduce_sum(v_add(r0, r1)); -#endif for (; j < n; j++) { int v = saturate_cast(src[j]); s += v * v; @@ -825,31 +797,6 @@ struct NormL1_SIMD { } }; -template<> -struct NormL1_SIMD { - double operator() (const double* src, int n) const { - int j = 0; - double s = 0.f; -#if CV_RVV // This is introduced to workaround the accuracy issue on ci - s = normL1_rvv(src, n, j); -#else - v_float64 r00 = vx_setzero_f64(), r01 = vx_setzero_f64(); - v_float64 r10 = vx_setzero_f64(), r11 = vx_setzero_f64(); - for (; j <= n - 4 * VTraits::vlanes(); j += 4 * VTraits::vlanes()) { - r00 = v_add(r00, v_abs(vx_load(src + j ))); - r01 = v_add(r01, v_abs(vx_load(src + j + VTraits::vlanes()))); - r10 = v_add(r10, v_abs(vx_load(src + j + 2 * VTraits::vlanes()))); - r11 = v_add(r11, v_abs(vx_load(src + j + 3 * VTraits::vlanes()))); - } - s += v_reduce_sum(v_add(v_add(v_add(r00, r01), r10), r11)); -#endif - for (; j < n; j++) { - s += cv_abs(src[j]); - } - return s; - } -}; - template<> struct NormL2_SIMD { double operator() (const ushort* src, int n) const { @@ -941,14 +888,36 @@ struct NormL2_SIMD { } }; +#endif + +#if CV_SIMD_64F // CV_SIMD_SCALABLE_64F has accuracy problem with the following kernels on ci + +template<> +struct NormL1_SIMD { + double operator() (const double* src, int n) const { + int j = 0; + double s = 0.f; + v_float64 r00 = vx_setzero_f64(), r01 = vx_setzero_f64(); + v_float64 r10 = vx_setzero_f64(), r11 = vx_setzero_f64(); + for (; j <= n - 4 * VTraits::vlanes(); j += 4 * VTraits::vlanes()) { + r00 = v_add(r00, v_abs(vx_load(src + j ))); + r01 = v_add(r01, v_abs(vx_load(src + j + VTraits::vlanes()))); + r10 = v_add(r10, v_abs(vx_load(src + j + 2 * VTraits::vlanes()))); + r11 = v_add(r11, v_abs(vx_load(src + j + 3 * VTraits::vlanes()))); + } + s += v_reduce_sum(v_add(v_add(v_add(r00, r01), r10), r11)); + for (; j < n; j++) { + s += cv_abs(src[j]); + } + return s; + } +}; + template<> struct NormL2_SIMD { double operator() (const double* src, int n) const { int j = 0; double s = 0.f; -#if CV_RVV // This is introduced to workaround the accuracy issue on ci - s = normL2_rvv(src, n, j); -#else v_float64 r00 = vx_setzero_f64(), r01 = vx_setzero_f64(); v_float64 r10 = vx_setzero_f64(), r11 = vx_setzero_f64(); for (; j <= n - 4 * VTraits::vlanes(); j += 4 * VTraits::vlanes()) { @@ -960,7 +929,6 @@ struct NormL2_SIMD { r10 = v_fma(v10, v10, r10); r11 = v_fma(v11, v11, r11); } s += v_reduce_sum(v_add(v_add(v_add(r00, r01), r10), r11)); -#endif for (; j < n; j++) { double v = src[j]; s += v * v; @@ -1362,7 +1330,9 @@ CV_DEF_NORM_ALL(64f, double, double, double, double) NormFunc getNormFunc(int normType, int depth) { CV_INSTRUMENT_REGION(); - static NormFunc normTab[3][8] = + + // [FIXME] append 0's when merging to 5.x + static NormFunc normTab[3][CV_DEPTH_MAX] = { { (NormFunc)GET_OPTIMIZED(normInf_8u), (NormFunc)GET_OPTIMIZED(normInf_8s), (NormFunc)GET_OPTIMIZED(normInf_16u), (NormFunc)GET_OPTIMIZED(normInf_16s), From b902a8e792e1702b40f19dbd48dff0bfdca8b36d Mon Sep 17 00:00:00 2001 From: amane-ame Date: Tue, 18 Mar 2025 15:53:05 +0800 Subject: [PATCH 23/94] Add equalize_hist. Co-authored-by: Liutong HAN --- 3rdparty/hal_rvv/hal_rvv.hpp | 1 + 3rdparty/hal_rvv/hal_rvv_1p0/histogram.hpp | 108 +++++++++++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 3rdparty/hal_rvv/hal_rvv_1p0/histogram.hpp diff --git a/3rdparty/hal_rvv/hal_rvv.hpp b/3rdparty/hal_rvv/hal_rvv.hpp index 2135cc355d..2a76f6d54d 100644 --- a/3rdparty/hal_rvv/hal_rvv.hpp +++ b/3rdparty/hal_rvv/hal_rvv.hpp @@ -48,6 +48,7 @@ #include "hal_rvv_1p0/pyramids.hpp" // imgproc #include "hal_rvv_1p0/color.hpp" // imgproc #include "hal_rvv_1p0/thresh.hpp" // imgproc +#include "hal_rvv_1p0/histogram.hpp" // imgproc #endif #endif diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/histogram.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/histogram.hpp new file mode 100644 index 0000000000..48f6123b0d --- /dev/null +++ b/3rdparty/hal_rvv/hal_rvv_1p0/histogram.hpp @@ -0,0 +1,108 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +// Copyright (C) 2025, Institute of Software, Chinese Academy of Sciences. + +#ifndef OPENCV_HAL_RVV_HISTOGRAM_HPP_INCLUDED +#define OPENCV_HAL_RVV_HISTOGRAM_HPP_INCLUDED + +#include + +namespace cv { namespace cv_hal_rvv { + +namespace equalize_hist { +#undef cv_hal_equalize_hist +#define cv_hal_equalize_hist cv::cv_hal_rvv::equalize_hist::equalize_hist + +class HistogramInvoker : public ParallelLoopBody +{ +public: + template + HistogramInvoker(std::function _func, Args&&... args) + { + func = std::bind(_func, std::placeholders::_1, std::placeholders::_2, std::forward(args)...); + } + + virtual void operator()(const Range& range) const override + { + func(range.start, range.end); + } + +private: + std::function func; +}; + +constexpr int HIST_SZ = std::numeric_limits::max() + 1; + +static inline void hist_invoke(int start, int end, const uchar* src_data, size_t src_step, int width, int* hist, std::mutex* m) +{ + int h[HIST_SZ] = {0}; + for (int i = start; i < end; i++) + { + const uchar* src = src_data + i * src_step; + int j; + for (j = 0; j + 3 < width; j += 4) + { + int t0 = src[j], t1 = src[j+1]; + h[t0]++; h[t1]++; + t0 = src[j+2]; t1 = src[j+3]; + h[t0]++; h[t1]++; + } + for (; j < width; j++) + { + h[src[j]]++; + } + } + + std::lock_guard lk(*m); + for (int i = 0; i < HIST_SZ; i++) + { + hist[i] += h[i]; + } +} + +static inline void lut_invoke(int start, int end, const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, int width, const uchar* lut) +{ + for (int i = start; i < end; i++) + { + int vl; + for (int j = 0; j < width; j += vl) + { + vl = __riscv_vsetvl_e8m8(width - j); + auto src = __riscv_vle8_v_u8m8(src_data + i * src_step + j, vl); + auto dst = __riscv_vloxei8_v_u8m8(lut, src, vl); + __riscv_vse8(dst_data + i * dst_step + j, dst, vl); + } + } +} + +// the algorithm is copied from imgproc/src/histogram.cpp, +// in the function void cv::equalizeHist +inline int equalize_hist(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, int width, int height) +{ + int hist[HIST_SZ] = {0}; + uchar lut[HIST_SZ]; + + std::mutex m; + cv::parallel_for_(Range(0, height), HistogramInvoker({hist_invoke}, src_data, src_step, width, reinterpret_cast(hist), &m), static_cast(width * height) / (1 << 15)); + + int i = 0; + while (!hist[i]) ++i; + + float scale = (HIST_SZ - 1.f)/(width * height - hist[i]); + int sum = 0; + for (lut[i++] = 0; i < HIST_SZ; i++) + { + sum += hist[i]; + lut[i] = std::min(std::max(static_cast(std::round(sum * scale)), 0), HIST_SZ - 1); + } + cv::parallel_for_(Range(0, height), HistogramInvoker({lut_invoke}, src_data, src_step, dst_data, dst_step, width, reinterpret_cast(lut)), static_cast(width * height) / (1 << 15)); + + return CV_HAL_ERROR_OK; +} +} // cv::cv_hal_rvv::equalize_hist + +}} + +#endif From 3e43d0cfca9753bcc4983f610b75d70c3f25f0cd Mon Sep 17 00:00:00 2001 From: Kumataro Date: Wed, 19 Mar 2025 20:24:08 +0900 Subject: [PATCH 24/94] Merge pull request #26971 from Kumataro:fix26970 imgcodecs: gif: support animated gif without loop #26971 Close #26970 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [x] The feature is well documented and sample code can be built with the project CMake --- .../imgcodecs/include/opencv2/imgcodecs.hpp | 14 ++- modules/imgcodecs/src/grfmt_gif.cpp | 83 +++++++++----- modules/imgcodecs/src/grfmt_gif.hpp | 7 +- modules/imgcodecs/test/test_gif.cpp | 104 ++++++++++++++++++ 4 files changed, 172 insertions(+), 36 deletions(-) diff --git a/modules/imgcodecs/include/opencv2/imgcodecs.hpp b/modules/imgcodecs/include/opencv2/imgcodecs.hpp index 5bf850b69a..b78b641121 100644 --- a/modules/imgcodecs/include/opencv2/imgcodecs.hpp +++ b/modules/imgcodecs/include/opencv2/imgcodecs.hpp @@ -118,8 +118,8 @@ enum ImwriteFlags { IMWRITE_JPEGXL_EFFORT = 641,//!< For JPEG XL, encoder effort/speed level without affecting decoding speed; it is between 1 (fastest) and 10 (slowest). Default is 7. IMWRITE_JPEGXL_DISTANCE = 642,//!< For JPEG XL, distance level for lossy compression: target max butteraugli distance, lower = higher quality, 0 = lossless; range: 0 .. 25. Default is 1. IMWRITE_JPEGXL_DECODING_SPEED = 643,//!< For JPEG XL, decoding speed tier for the provided options; minimum is 0 (slowest to decode, best quality/density), and maximum is 4 (fastest to decode, at the cost of some quality/density). Default is 0. - IMWRITE_GIF_LOOP = 1024,//!< For GIF, it can be a loop flag from 0 to 65535. Default is 0 - loop forever. - IMWRITE_GIF_SPEED = 1025,//!< For GIF, it is between 1 (slowest) and 100 (fastest). Default is 96. + IMWRITE_GIF_LOOP = 1024, //!< Not functional since 4.12.0. Replaced by cv::Animation::loop_count. + IMWRITE_GIF_SPEED = 1025, //!< Not functional since 4.12.0. Replaced by cv::Animation::durations. IMWRITE_GIF_QUALITY = 1026, //!< For GIF, it can be a quality from 1 to 8. Default is 2. See cv::ImwriteGifCompressionFlags. IMWRITE_GIF_DITHER = 1027, //!< For GIF, it can be a quality from -1(most dither) to 3(no dither). Default is 0. IMWRITE_GIF_TRANSPARENCY = 1028, //!< For GIF, the alpha channel lower than this will be set to transparent. Default is 1. @@ -260,10 +260,20 @@ It provides support for looping, background color settings, frame timing, and fr struct CV_EXPORTS_W_SIMPLE Animation { //! Number of times the animation should loop. 0 means infinite looping. + /*! @note At some file format, when N is set, whether it is displayed N or N+1 times depends on the implementation of the user application. This loop times behaviour has not been documented clearly. + * - (GIF) See https://issues.chromium.org/issues/40459899 + * And animated GIF with loop is extended with the Netscape Application Block(NAB), which it not a part of GIF89a specification. See https://en.wikipedia.org/wiki/GIF#Animated_GIF . + * - (WebP) See https://issues.chromium.org/issues/41276895 + */ CV_PROP_RW int loop_count; //! Background color of the animation in BGRA format. CV_PROP_RW Scalar bgcolor; //! Duration for each frame in milliseconds. + /*! @note (GIF) Due to file format limitation + * - Durations must be multiples of 10 milliseconds. Any provided value will be rounded down to the nearest 10ms (e.g., 88ms → 80ms). + * - 0ms(or smaller than expected in user application) duration may cause undefined behavior, e.g. it is handled with default duration. + * - Over 65535 * 10 milliseconds duration is not supported. + */ CV_PROP_RW std::vector durations; //! Vector of frames, where each Mat represents a single frame. CV_PROP_RW std::vector frames; diff --git a/modules/imgcodecs/src/grfmt_gif.cpp b/modules/imgcodecs/src/grfmt_gif.cpp index d33b600722..3a70dcc568 100644 --- a/modules/imgcodecs/src/grfmt_gif.cpp +++ b/modules/imgcodecs/src/grfmt_gif.cpp @@ -47,8 +47,8 @@ bool GifDecoder::readHeader() { return false; } - String signature(6, ' '); - m_strm.getBytes((uchar*)signature.data(), 6); + std::string signature(6, ' '); + m_strm.getBytes((uchar*)signature.c_str(), 6); CV_Assert(signature == R"(GIF87a)" || signature == R"(GIF89a)"); // #1: read logical screen descriptor @@ -428,6 +428,7 @@ void GifDecoder::close() { bool GifDecoder::getFrameCount_() { m_frame_count = 0; + m_animation.loop_count = 1; auto type = (uchar)m_strm.getByte(); while (type != 0x3B) { if (!(type ^ 0x21)) { @@ -436,11 +437,18 @@ bool GifDecoder::getFrameCount_() { // Application Extension need to be handled for the loop count if (extension == 0xFF) { int len = m_strm.getByte(); + bool isFoundNetscape = false; while (len) { - // TODO: In strictly, Application Identifier and Authentication Code should be checked. - if (len == 3) { - if (m_strm.getByte() == 0x01) { - m_animation.loop_count = m_strm.getWord(); + if (len == 11) { + std::string app_auth_code(len, ' '); + m_strm.getBytes(const_cast(static_cast(app_auth_code.c_str())), len); + isFoundNetscape = (app_auth_code == R"(NETSCAPE2.0)"); + } else if (len == 3) { + if (isFoundNetscape && (m_strm.getByte() == 0x01)) { + int loop_count = m_strm.getWord(); + // If loop_count == 0, it means loop forever. + // Otherwise, the loop is displayed extra one time than it is written in the data. + m_animation.loop_count = (loop_count == 0) ? 0 : loop_count + 1; } else { // this branch should not be reached in normal cases m_strm.skip(2); @@ -505,8 +513,8 @@ bool GifDecoder::getFrameCount_() { } bool GifDecoder::skipHeader() { - String signature(6, ' '); - m_strm.getBytes((uchar *) signature.data(), 6); + std::string signature(6, ' '); + m_strm.getBytes((uchar *) signature.c_str(), 6); // skip height and width m_strm.skip(4); char flags = (char) m_strm.getByte(); @@ -538,9 +546,7 @@ GifEncoder::GifEncoder() { // default value of the params fast = true; - loopCount = 0; // infinite loops by default criticalTransparency = 1; // critical transparency, default 1, range from 0 to 255, 0 means no transparency - frameDelay = 5; // 20fps by default, 10ms per unit bitDepth = 8; // the number of bits per pixel, default 8, currently it is a constant number lzwMinCodeSize = 8; // the minimum code size, default 8, this changes as the color number changes colorNum = 256; // the number of colors in the color table, default 256 @@ -566,16 +572,14 @@ bool GifEncoder::writeanimation(const Animation& animation, const std::vector(); } -bool GifEncoder::writeFrame(const Mat &img) { +bool GifEncoder::writeFrame(const Mat &img, const int frameDelay10ms) { if (img.empty()) { return false; } + height = m_height, width = m_width; // graphic control extension @@ -681,7 +699,7 @@ bool GifEncoder::writeFrame(const Mat &img) { const int gcePackedFields = static_cast(GIF_DISPOSE_RESTORE_PREVIOUS << GIF_DISPOSE_METHOD_SHIFT) | static_cast(criticalTransparency ? GIF_TRANSPARENT_INDEX_GIVEN : GIF_TRANSPARENT_INDEX_NOT_GIVEN); strm.putByte(gcePackedFields); - strm.putWord(frameDelay); + strm.putWord(frameDelay10ms); strm.putByte(transparentColor); strm.putByte(0x00); // end of the extension @@ -796,7 +814,7 @@ bool GifEncoder::lzwEncode() { return true; } -bool GifEncoder::writeHeader(const std::vector& img_vec) { +bool GifEncoder::writeHeader(const std::vector& img_vec, const int loopCount) { strm.putBytes(fmtGifHeader, (int)strlen(fmtGifHeader)); if (img_vec[0].empty()) { @@ -821,16 +839,23 @@ bool GifEncoder::writeHeader(const std::vector& img_vec) { strm.putBytes(globalColorTable.data(), globalColorTableSize * 3); } + if ( loopCount != 1 ) // If no-loop, Netscape Application Block is unnecessary. + { + // loopCount 0 means loop forever. + // Otherwise, most browsers(Edge, Chrome, Firefox...) will loop with extra 1 time. + // GIF data should be written with loop count decreased by 1. + const int _loopCount = ( loopCount == 0 ) ? loopCount : loopCount - 1; - // add application extension to set the loop count - strm.putByte(0x21); // GIF extension code - strm.putByte(0xFF); // application extension table - strm.putByte(0x0B); // length of application block, in decimal is 11 - strm.putBytes(R"(NETSCAPE2.0)", 11); // application authentication code - strm.putByte(0x03); // length of application block, in decimal is 3 - strm.putByte(0x01); // identifier - strm.putWord(loopCount); - strm.putByte(0x00); // end of the extension + // add Netscape Application Block to set the loop count in application extension. + strm.putByte(0x21); // GIF extension code + strm.putByte(0xFF); // application extension table + strm.putByte(0x0B); // length of application block, in decimal is 11 + strm.putBytes(R"(NETSCAPE2.0)", 11); // application authentication code + strm.putByte(0x03); // length of application block, in decimal is 3 + strm.putByte(0x01); // identifier + strm.putWord(_loopCount); + strm.putByte(0x00); // end of the extension + } return true; } diff --git a/modules/imgcodecs/src/grfmt_gif.hpp b/modules/imgcodecs/src/grfmt_gif.hpp index 106cc52186..af1f794ca9 100644 --- a/modules/imgcodecs/src/grfmt_gif.hpp +++ b/modules/imgcodecs/src/grfmt_gif.hpp @@ -157,7 +157,6 @@ private: int globalColorTableSize; int localColorTableSize; - uchar opMode; uchar criticalTransparency; uchar transparentColor; Vec3b transparentRGB; @@ -173,8 +172,6 @@ private: std::vector localColorTable; // params - int loopCount; - int frameDelay; int colorNum; int bitDepth; int dithering; @@ -182,8 +179,8 @@ private: bool fast; bool writeFrames(const std::vector& img_vec, const std::vector& params); - bool writeHeader(const std::vector& img_vec); - bool writeFrame(const Mat& img); + bool writeHeader(const std::vector& img_vec, const int loopCount); + bool writeFrame(const Mat& img, const int frameDelay); bool pixel2code(const Mat& img); void getColorTable(const std::vector& img_vec, bool isGlobal); static int ditheringKernel(const Mat &img, Mat &img_, int depth, uchar transparency); diff --git a/modules/imgcodecs/test/test_gif.cpp b/modules/imgcodecs/test/test_gif.cpp index a7c5ce8264..1ceef24637 100644 --- a/modules/imgcodecs/test/test_gif.cpp +++ b/modules/imgcodecs/test/test_gif.cpp @@ -414,6 +414,110 @@ TEST(Imgcodecs_Gif, decode_disposal_method) } } +// See https://github.com/opencv/opencv/issues/26970 +typedef testing::TestWithParam Imgcodecs_Gif_loop_count; +TEST_P(Imgcodecs_Gif_loop_count, imwriteanimation) +{ + const string gif_filename = cv::tempfile(".gif"); + + int loopCount = GetParam(); + cv::Animation anim(loopCount); + + vector src; + for(int n = 1; n <= 5 ; n ++ ) + { + cv::Mat frame(64, 64, CV_8UC3, cv::Scalar::all(0)); + cv::putText(frame, cv::format("%d", n), cv::Point(0,64), cv::FONT_HERSHEY_PLAIN, 4.0, cv::Scalar::all(255)); + anim.frames.push_back(frame); + anim.durations.push_back(1000 /* ms */); + } + + bool ret = false; +#if 0 + // To output gif image for test. + EXPECT_NO_THROW(ret = imwriteanimation(cv::format("gif_loop-%d.gif", loopCount), anim)); + EXPECT_TRUE(ret); +#endif + EXPECT_NO_THROW(ret = imwriteanimation(gif_filename, anim)); + EXPECT_TRUE(ret); + + // Read raw GIF data. + std::ifstream ifs(gif_filename); + std::stringstream ss; + ss << ifs.rdbuf(); + string tmp = ss.str(); + std::vector buf(tmp.begin(), tmp.end()); + + std::vector netscape = {0x21, 0xFF, 0x0B, 'N','E','T','S','C','A','P','E','2','.','0'}; + auto pos = std::search(buf.begin(), buf.end(), netscape.begin(), netscape.end()); + if(loopCount == 1) { + EXPECT_EQ(pos, buf.end()) << "Netscape Application Block should not be included if Animation.loop_count == 1"; + } else { + EXPECT_NE(pos, buf.end()) << "Netscape Application Block should be included if Animation.loop_count != 1"; + } + + remove(gif_filename.c_str()); +} + +INSTANTIATE_TEST_CASE_P(/*nothing*/, + Imgcodecs_Gif_loop_count, + testing::Values( + -1, + 0, // Default, loop-forever + 1, + 2, + 65534, + 65535, // Maximum Limit + 65536 + ) +); + +typedef testing::TestWithParam Imgcodecs_Gif_duration; +TEST_P(Imgcodecs_Gif_duration, imwriteanimation) +{ + const string gif_filename = cv::tempfile(".gif"); + + cv::Animation anim; + + int duration = GetParam(); + vector src; + for(int n = 1; n <= 5 ; n ++ ) + { + cv::Mat frame(64, 64, CV_8UC3, cv::Scalar::all(0)); + cv::putText(frame, cv::format("%d", n), cv::Point(0,64), cv::FONT_HERSHEY_PLAIN, 4.0, cv::Scalar::all(255)); + anim.frames.push_back(frame); + anim.durations.push_back(duration /* ms */); + } + + bool ret = false; +#if 0 + // To output gif image for test. + EXPECT_NO_THROW(ret = imwriteanimation(cv::format("gif_duration-%d.gif", duration), anim)); + EXPECT_EQ(ret, ( (0 <= duration) && (duration <= 655350) ) ); +#endif + EXPECT_NO_THROW(ret = imwriteanimation(gif_filename, anim)); + EXPECT_EQ(ret, ( (0 <= duration) && (duration <= 655350) ) ); + + remove(gif_filename.c_str()); +} + +INSTANTIATE_TEST_CASE_P(/*nothing*/, + Imgcodecs_Gif_duration, + testing::Values( + -1, // Unsupported + 0, // Undefined Behaviour + 1, + 9, + 10, + 50, + 100, // 10 FPS + 1000, // 1 FPS + 655340, + 655350, // Maximum Limit + 655360 // Unsupported + ) +); + }//opencv_test }//namespace From 259ec3674db64bf69c0668785d160829ab1a3853 Mon Sep 17 00:00:00 2001 From: JavaTypedScript Date: Wed, 19 Mar 2025 21:38:08 +0530 Subject: [PATCH 25/94] fixed typo --- modules/imgproc/doc/colors.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/imgproc/doc/colors.markdown b/modules/imgproc/doc/colors.markdown index 97d0907a62..be2dcd1c50 100644 --- a/modules/imgproc/doc/colors.markdown +++ b/modules/imgproc/doc/colors.markdown @@ -187,7 +187,7 @@ The Bayer pattern is widely used in CCD and CMOS cameras. It enables you to get from a single plane where R, G, and B pixels (sensors of a particular component) are interleaved as follows: -![Bayer patterns (BGGR, GBRG, GRGB, RGGB)](pics/Bayer_patterns.png) +![Bayer patterns (BGGR, GBRG, GRBG, RGGB)](pics/Bayer_patterns.png) The output RGB components of a pixel are interpolated from 1, 2, or 4 neighbors of the pixel having the same color. From 0ed5556ceea4ab737a891dbcdf86f8061aad1d89 Mon Sep 17 00:00:00 2001 From: Suleyman TURKMEN Date: Tue, 18 Mar 2025 01:46:52 +0300 Subject: [PATCH 26/94] Add a test to check whether cv::imread successfully reads 16-bit APNG images. Make proper fixes to pass the test --- modules/imgcodecs/src/grfmt_png.cpp | 11 ++++- modules/imgcodecs/src/loadsave.cpp | 2 +- modules/imgcodecs/test/test_animation.cpp | 59 ++++++++++++++++++++++- 3 files changed, 69 insertions(+), 3 deletions(-) diff --git a/modules/imgcodecs/src/grfmt_png.cpp b/modules/imgcodecs/src/grfmt_png.cpp index ffcc0f29be..1001ff6eec 100644 --- a/modules/imgcodecs/src/grfmt_png.cpp +++ b/modules/imgcodecs/src/grfmt_png.cpp @@ -451,6 +451,9 @@ bool PngDecoder::readData( Mat& img ) delay_den = 100; m_animation.durations.push_back(cvRound(1000.*delay_num/delay_den)); + if (mat_cur.depth() == CV_16U && img.depth() == CV_8U) + mat_cur.convertTo(mat_cur, CV_8U, 1. / 255); + if (mat_cur.channels() == img.channels()) mat_cur.copyTo(img); else if (img.channels() == 1) @@ -485,7 +488,7 @@ bool PngDecoder::readData( Mat& img ) return false; } // Asking for blend over with no alpha is invalid. - if (bop == 1 && img.channels() != 4) + if (bop == 1 && mat_cur.channels() != 4) { return false; } @@ -514,6 +517,9 @@ bool PngDecoder::readData( Mat& img ) delay_den = 100; m_animation.durations.push_back(cvRound(1000.*delay_num/delay_den)); + if (mat_cur.depth() == CV_16U && img.depth() == CV_8U) + mat_cur.convertTo(mat_cur, CV_8U, 1. / 255); + if (mat_cur.channels() == img.channels()) mat_cur.copyTo(img); else if (img.channels() == 1) @@ -775,6 +781,9 @@ bool PngDecoder::processing_start(void* frame_ptr, const Mat& img) else png_set_rgb_to_gray(m_png_ptr, 1, 0.299, 0.587); // RGB->Gray + if (!isBigEndian() && m_bit_depth == 16) + png_set_swap(m_png_ptr); + for (size_t i = 0; i < m_chunksInfo.size(); i++) png_process_data(m_png_ptr, m_info_ptr, m_chunksInfo[i].p.data(), m_chunksInfo[i].p.size()); diff --git a/modules/imgcodecs/src/loadsave.cpp b/modules/imgcodecs/src/loadsave.cpp index 0bf90f2aa2..fd547a378a 100644 --- a/modules/imgcodecs/src/loadsave.cpp +++ b/modules/imgcodecs/src/loadsave.cpp @@ -505,7 +505,7 @@ imread_( const String& filename, int flags, OutputArray mat ) { if (decoder->readData(real_mat)) { - CV_CheckTrue(original_ptr == real_mat.data, "Internal imread issue"); + CV_CheckTrue((decoder->getFrameCount() > 1) || original_ptr == real_mat.data, "Internal imread issue"); success = true; } } diff --git a/modules/imgcodecs/test/test_animation.cpp b/modules/imgcodecs/test/test_animation.cpp index e566bcbd49..ece0d19d29 100644 --- a/modules/imgcodecs/test/test_animation.cpp +++ b/modules/imgcodecs/test/test_animation.cpp @@ -626,7 +626,7 @@ TEST(Imgcodecs_APNG, imencode_animation) EXPECT_TRUE(imencodeanimation(".png", gt_animation, buf)); EXPECT_TRUE(imdecodeanimation(buf, mem_animation)); - EXPECT_EQ(mem_animation.frames.size(), gt_animation.frames.size()); + EXPECT_EQ(mem_animation.frames.size(), gt_animation.frames.size()); EXPECT_EQ(mem_animation.bgcolor, gt_animation.bgcolor); EXPECT_EQ(mem_animation.loop_count, gt_animation.loop_count); for (size_t i = 0; i < gt_animation.frames.size(); i++) @@ -638,4 +638,61 @@ TEST(Imgcodecs_APNG, imencode_animation) #endif // HAVE_PNG +#if defined(HAVE_PNG) || defined(HAVE_SPNG) + +TEST(Imgcodecs_APNG, imread_animation_16u) +{ + // Set the path to the test image directory and filename for loading. + const string root = cvtest::TS::ptr()->get_data_path(); + const string filename = root + "readwrite/033.png"; + + Mat img = imread(filename, IMREAD_UNCHANGED); + ASSERT_FALSE(img.empty()); + EXPECT_TRUE(img.type() == CV_16UC4); + EXPECT_EQ(0, img.at(0, 0)); + EXPECT_EQ(0, img.at(0, 1)); + EXPECT_EQ(65280, img.at(0, 2)); + EXPECT_EQ(65535, img.at(0, 3)); + + img = imread(filename, IMREAD_GRAYSCALE); + ASSERT_FALSE(img.empty()); + EXPECT_TRUE(img.type() == CV_8UC1); + EXPECT_EQ(76, img.at(0, 0)); + + img = imread(filename, IMREAD_COLOR); + ASSERT_FALSE(img.empty()); + EXPECT_TRUE(img.type() == CV_8UC3); + EXPECT_EQ(0, img.at(0, 0)); + EXPECT_EQ(0, img.at(0, 1)); + EXPECT_EQ(255, img.at(0, 2)); + + img = imread(filename, IMREAD_COLOR_RGB); + ASSERT_FALSE(img.empty()); + EXPECT_TRUE(img.type() == CV_8UC3); + EXPECT_EQ(255, img.at(0, 0)); + EXPECT_EQ(0, img.at(0, 1)); + EXPECT_EQ(0, img.at(0, 2)); + + img = imread(filename, IMREAD_ANYDEPTH); + ASSERT_FALSE(img.empty()); + EXPECT_TRUE(img.type() == CV_16UC1); + EXPECT_EQ(19519, img.at(0, 0)); + + img = imread(filename, IMREAD_COLOR | IMREAD_ANYDEPTH); + ASSERT_FALSE(img.empty()); + EXPECT_TRUE(img.type() == CV_16UC3); + EXPECT_EQ(0, img.at(0, 0)); + EXPECT_EQ(0, img.at(0, 1)); + EXPECT_EQ(65280, img.at(0, 2)); + + img = imread(filename, IMREAD_COLOR_RGB | IMREAD_ANYDEPTH); + ASSERT_FALSE(img.empty()); + EXPECT_TRUE(img.type() == CV_16UC3); + EXPECT_EQ(65280, img.at(0, 0)); + EXPECT_EQ(0, img.at(0, 1)); + EXPECT_EQ(0, img.at(0, 2)); +} + +#endif // HAVE_PNG || HAVE_SPNG + }} // namespace From 46fbe1895a1ffcaead08a339f6cd5d0902c7dc17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=A9=E9=9F=B3=E3=81=82=E3=82=81?= Date: Thu, 20 Mar 2025 15:50:06 +0800 Subject: [PATCH 27/94] Merge pull request #27096 from amane-ame:moments_hal_rvv Add RISC-V HAL implementation for cv::moments #27096 This patch implements `cv_hal_imageMoments` using native intrinsics, optimizing the performance of `cv::moments` for data types `CV_16U/CV_16S/CV_32F/CV_64F`. Tested on MUSE-PI (Spacemit X60) for both gcc 14.2 and clang 20.0. ``` $ ./opencv_test_imgproc --gtest_filter="*Moments*" $ ./opencv_perf_imgproc --gtest_filter="*Moments*" --perf_min_samples=1000 --perf_force_samples=1000 ``` ![image](https://github.com/user-attachments/assets/0efbae10-c022-4f15-a81c-682514cdb372) ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [ ] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- 3rdparty/hal_rvv/hal_rvv.hpp | 1 + 3rdparty/hal_rvv/hal_rvv_1p0/moments.hpp | 191 +++++++++++++++++++++++ 3rdparty/hal_rvv/hal_rvv_1p0/types.hpp | 39 ++++- 3 files changed, 224 insertions(+), 7 deletions(-) create mode 100644 3rdparty/hal_rvv/hal_rvv_1p0/moments.hpp diff --git a/3rdparty/hal_rvv/hal_rvv.hpp b/3rdparty/hal_rvv/hal_rvv.hpp index 2135cc355d..9a465c4f05 100644 --- a/3rdparty/hal_rvv/hal_rvv.hpp +++ b/3rdparty/hal_rvv/hal_rvv.hpp @@ -44,6 +44,7 @@ #include "hal_rvv_1p0/svd.hpp" // core #include "hal_rvv_1p0/sqrt.hpp" // core +#include "hal_rvv_1p0/moments.hpp" // imgproc #include "hal_rvv_1p0/filter.hpp" // imgproc #include "hal_rvv_1p0/pyramids.hpp" // imgproc #include "hal_rvv_1p0/color.hpp" // imgproc diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/moments.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/moments.hpp new file mode 100644 index 0000000000..f0db8b3a17 --- /dev/null +++ b/3rdparty/hal_rvv/hal_rvv_1p0/moments.hpp @@ -0,0 +1,191 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +// Copyright (C) 2025, Institute of Software, Chinese Academy of Sciences. + +#ifndef OPENCV_HAL_RVV_MOMENTS_HPP_INCLUDED +#define OPENCV_HAL_RVV_MOMENTS_HPP_INCLUDED + +#include + +namespace cv { namespace cv_hal_rvv { + +namespace imageMoments { +#undef cv_hal_imageMoments +#define cv_hal_imageMoments cv::cv_hal_rvv::imageMoments::imageMoments + +class MomentsInvoker : public ParallelLoopBody +{ +public: + template + MomentsInvoker(std::function _func, Args&&... args) + { + func = std::bind(_func, std::placeholders::_1, std::placeholders::_2, std::forward(args)...); + } + + virtual void operator()(const Range& range) const override + { + func(range.start, range.end); + } + +private: + std::function func; +}; + +template +static inline int invoke(int width, int height, std::function func, Args&&... args) +{ + cv::parallel_for_(Range(1, height), MomentsInvoker(func, std::forward(args)...), static_cast((width - 1) * height) / (1 << 10)); + return func(0, 1, std::forward(args)...); +} + +template struct rvv; +template<> struct rvv +{ + static inline vuint8mf2_t vid(size_t a) { return __riscv_vid_v_u8mf2(a); } + static inline RVV_U32M2::VecType vcvt(vuint8mf2_t a, size_t b) { return __riscv_vzext_vf4(a, b); } +}; +template<> struct rvv +{ + static inline vuint8m1_t vid(size_t a) { return __riscv_vid_v_u8m1(a); } + static inline RVV_U32M4::VecType vcvt(vuint8m1_t a, size_t b) { return __riscv_vzext_vf4(a, b); } +}; +template<> struct rvv +{ + static inline vuint8mf2_t vid(size_t a) { return __riscv_vid_v_u8mf2(a); } + static inline RVV_I32M2::VecType vcvt(vuint8mf2_t a, size_t b) { return RVV_I32M2::reinterpret(__riscv_vzext_vf4(a, b)); } +}; +template<> struct rvv +{ + static inline vuint8mf2_t vid(size_t a) { return __riscv_vid_v_u8mf2(a); } + static inline RVV_F64M4::VecType vcvt(vuint8mf2_t a, size_t b) { return __riscv_vfcvt_f(__riscv_vzext_vf8(a, b), b); } +}; + +constexpr int TILE_SIZE = 32; + +template +static inline int imageMoments(int start, int end, const uchar* src_data, size_t src_step, int full_width, int full_height, double* m, std::mutex* mt) +{ + double mm[10] = {0}; + for (int yy = start; yy < end; yy++) + { + const int y = yy * TILE_SIZE; + const int height = std::min(TILE_SIZE, full_height - y); + for (int x = 0; x < full_width; x += TILE_SIZE) + { + const int width = std::min(TILE_SIZE, full_width - x); + double mom[10] = {0}; + + for (int i = 0; i < height; i++) + { + auto id = rvv::vid(helperT::setvlmax()); + auto v0 = helperWT::vmv(0, helperWT::setvlmax()); + auto v1 = helperWT::vmv(0, helperWT::setvlmax()); + auto v2 = helperWT::vmv(0, helperWT::setvlmax()); + auto v3 = helperMT::vmv(0, helperMT::setvlmax()); + + int vl; + for (int j = 0; j < width; j += vl) + { + vl = helperT::setvl(width - j); + typename helperWT::VecType p; + if (binary) + { + auto src = RVV_SameLen::vload(reinterpret_cast(src_data + (i + y) * src_step) + j + x, vl); + p = __riscv_vmerge(helperWT::vmv(0, vl), helperWT::vmv(255, vl), RVV_SameLen::vmne(src, 0, vl), vl); + } + else + { + p = helperWT::cast(helperT::vload(reinterpret_cast(src_data + (i + y) * src_step) + j + x, vl), vl); + } + auto xx = rvv::vcvt(id, vl); + auto xp = helperWT::vmul(xx, p, vl); + v0 = helperWT::vadd_tu(v0, v0, p, vl); + v1 = helperWT::vadd_tu(v1, v1, xp, vl); + auto xxp = helperWT::vmul(xx, xp, vl); + v2 = helperWT::vadd_tu(v2, v2, xxp, vl); + v3 = helperMT::vadd_tu(v3, v3, helperMT::vmul(helperMT::cast(xx, vl), helperMT::cast(xxp, vl), vl), vl); + id = __riscv_vadd(id, vl, vl); + } + + auto x0 = RVV_BaseType::vmv_x(helperWT::vredsum(v0, RVV_BaseType::vmv_s(0, RVV_BaseType::setvlmax()), helperWT::setvlmax())); + auto x1 = RVV_BaseType::vmv_x(helperWT::vredsum(v1, RVV_BaseType::vmv_s(0, RVV_BaseType::setvlmax()), helperWT::setvlmax())); + auto x2 = RVV_BaseType::vmv_x(helperWT::vredsum(v2, RVV_BaseType::vmv_s(0, RVV_BaseType::setvlmax()), helperWT::setvlmax())); + auto x3 = RVV_BaseType::vmv_x(helperMT::vredsum(v3, RVV_BaseType::vmv_s(0, RVV_BaseType::setvlmax()), helperMT::setvlmax())); + typename helperWT::ElemType py = i * x0, sy = i*i; + + mom[9] += static_cast(py) * sy; + mom[8] += static_cast(x1) * sy; + mom[7] += static_cast(x2) * i; + mom[6] += x3; + mom[5] += x0 * sy; + mom[4] += x1 * i; + mom[3] += x2; + mom[2] += py; + mom[1] += x1; + mom[0] += x0; + } + + if (binary) + { + mom[0] /= 255, mom[1] /= 255, mom[2] /= 255, mom[3] /= 255, mom[4] /= 255; + mom[5] /= 255, mom[6] /= 255, mom[7] /= 255, mom[8] /= 255, mom[9] /= 255; + } + double xm = x * mom[0], ym = y * mom[0]; + mm[0] += mom[0]; + mm[1] += mom[1] + xm; + mm[2] += mom[2] + ym; + mm[3] += mom[3] + x * (mom[1] * 2 + xm); + mm[4] += mom[4] + x * (mom[2] + ym) + y * mom[1]; + mm[5] += mom[5] + y * (mom[2] * 2 + ym); + mm[6] += mom[6] + x * (3. * mom[3] + x * (3. * mom[1] + xm)); + mm[7] += mom[7] + x * (2 * (mom[4] + y * mom[1]) + x * (mom[2] + ym)) + y * mom[3]; + mm[8] += mom[8] + y * (2 * (mom[4] + x * mom[2]) + y * (mom[1] + xm)) + x * mom[5]; + mm[9] += mom[9] + y * (3. * mom[5] + y * (3. * mom[2] + ym)); + } + } + + std::lock_guard lk(*mt); + for (int i = 0; i < 10; i++) + m[i] += mm[i]; + return CV_HAL_ERROR_OK; +} + +// the algorithm is copied from imgproc/src/moments.cpp, +// in the function cv::Moments cv::moments +inline int imageMoments(const uchar* src_data, size_t src_step, int src_type, int width, int height, bool binary, double m[10]) +{ + if (src_type != CV_16UC1 && src_type != CV_16SC1 && src_type != CV_32FC1 && src_type != CV_64FC1) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + + std::fill(m, m + 10, 0); + const int cnt = (height + TILE_SIZE - 1) / TILE_SIZE; + std::mutex mt; + switch (static_cast(binary)*100 + src_type) + { + case CV_16UC1: + return invoke(width, cnt, {imageMoments}, src_data, src_step, width, height, m, &mt); + case CV_16SC1: + return invoke(width, cnt, {imageMoments}, src_data, src_step, width, height, m, &mt); + case CV_32FC1: + return invoke(width, cnt, {imageMoments}, src_data, src_step, width, height, m, &mt); + case CV_64FC1: + return invoke(width, cnt, {imageMoments}, src_data, src_step, width, height, m, &mt); + case 100 + CV_16UC1: + return invoke(width, cnt, {imageMoments}, src_data, src_step, width, height, m, &mt); + case 100 + CV_16SC1: + return invoke(width, cnt, {imageMoments}, src_data, src_step, width, height, m, &mt); + case 100 + CV_32FC1: + return invoke(width, cnt, {imageMoments}, src_data, src_step, width, height, m, &mt); + case 100 + CV_64FC1: + return invoke(width, cnt, {imageMoments}, src_data, src_step, width, height, m, &mt); + } + + return CV_HAL_ERROR_NOT_IMPLEMENTED; +} +} // cv::cv_hal_rvv::imageMoments + +}} + +#endif diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/types.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/types.hpp index da916669fd..05c1e84951 100644 --- a/3rdparty/hal_rvv/hal_rvv_1p0/types.hpp +++ b/3rdparty/hal_rvv/hal_rvv_1p0/types.hpp @@ -94,7 +94,7 @@ using RVV_F64M8 = struct RVV; // Only for dst type lmul >= 1 template using RVV_SameLen = - RVV; + RVV; template struct RVV_ToIntHelper; template struct RVV_ToUintHelper; @@ -117,7 +117,7 @@ using RVV_BaseType = RVV; // -------------------------------Supported operations-------------------------------- -#define HAL_RVV_SIZE_RELATED(EEW, TYPE, LMUL, S_OR_F, X_OR_F, IS_U, IS_F) \ +#define HAL_RVV_SIZE_RELATED(EEW, TYPE, LMUL, S_OR_F, X_OR_F, IS_U, IS_F, IS_O) \ static inline size_t setvlmax() { return __riscv_vsetvlmax_e##EEW##LMUL(); } \ static inline size_t setvl(size_t vl) { return __riscv_vsetvl_e##EEW##LMUL(vl); } \ static inline VecType vload(const ElemType* ptr, size_t vl) { \ @@ -153,7 +153,7 @@ static inline VecType vmv_s(ElemType a, size_t vl) { } \ HAL_RVV_SIZE_RELATED_CUSTOM(EEW, TYPE, LMUL) -#define HAL_RVV_SIZE_UNRELATED(S_OR_F, X_OR_F, IS_U, IS_F) \ +#define HAL_RVV_SIZE_UNRELATED(S_OR_F, X_OR_F, IS_U, IS_F, IS_O) \ static inline ElemType vmv_x(VecType vs2) { return __riscv_v##IS_F##mv_##X_OR_F(vs2); } \ \ static inline BoolType vmlt(VecType vs2, VecType vs1, size_t vl) { \ @@ -174,6 +174,12 @@ static inline BoolType vmgt(VecType vs2, ElemType vs1, size_t vl) { static inline BoolType vmge(VecType vs2, VecType vs1, size_t vl) { \ return __riscv_vm##S_OR_F##ge##IS_U(vs2, vs1, vl); \ } \ +static inline BoolType vmeq(VecType vs2, ElemType vs1, size_t vl) { \ + return __riscv_vm##S_OR_F##eq(vs2, vs1, vl); \ +} \ +static inline BoolType vmne(VecType vs2, ElemType vs1, size_t vl) { \ + return __riscv_vm##S_OR_F##ne(vs2, vs1, vl); \ +} \ static inline BoolType vmlt_mu(BoolType vm, BoolType vd, VecType vs2, VecType vs1, size_t vl) { \ return __riscv_vm##S_OR_F##lt##IS_U##_mu(vm, vd, vs2, vs1, vl); \ } \ @@ -187,6 +193,22 @@ static inline BoolType vmge_mu(BoolType vm, BoolType vd, VecType vs2, VecType vs return __riscv_vm##S_OR_F##ge##IS_U##_mu(vm, vd, vs2, vs1, vl); \ } \ \ +static inline VecType vadd(VecType vs2, VecType vs1, size_t vl) { \ + return __riscv_v##IS_F##add(vs2, vs1, vl); \ +} \ +static inline VecType vsub(VecType vs2, VecType vs1, size_t vl) { \ + return __riscv_v##IS_F##sub(vs2, vs1, vl); \ +} \ +static inline VecType vadd_tu(VecType vd, VecType vs2, VecType vs1, size_t vl) { \ + return __riscv_v##IS_F##add_tu(vd, vs2, vs1, vl); \ +} \ +static inline VecType vsub_tu(VecType vd, VecType vs2, VecType vs1, size_t vl) { \ + return __riscv_v##IS_F##sub_tu(vd, vs2, vs1, vl); \ +} \ +static inline VecType vmul(VecType vs2, VecType vs1, size_t vl) { \ + return __riscv_v##IS_F##mul(vs2, vs1, vl); \ +} \ + \ static inline VecType vmin(VecType vs2, VecType vs1, size_t vl) { \ return __riscv_v##IS_F##min##IS_U(vs2, vs1, vl); \ } \ @@ -211,9 +233,12 @@ static inline BaseType vredmin(VecType vs2, BaseType vs1, size_t vl) { } \ static inline BaseType vredmax(VecType vs2, BaseType vs1, size_t vl) { \ return __riscv_v##IS_F##redmax##IS_U(vs2, vs1, vl); \ +} \ +static inline BaseType vredsum(VecType vs2, BaseType vs1, size_t vl) { \ + return __riscv_v##IS_F##red##IS_O##sum(vs2, vs1, vl); \ } -#define HAL_RVV_BOOL_TYPE(S_OR_F, X_OR_F, IS_U, IS_F) \ +#define HAL_RVV_BOOL_TYPE(S_OR_F, X_OR_F, IS_U, IS_F, IS_O) \ decltype(__riscv_vm##S_OR_F##eq(std::declval(), std::declval(), 0)) #define HAL_RVV_DEFINE_ONE(ELEM_TYPE, VEC_TYPE, LMUL_TYPE, \ @@ -259,9 +284,9 @@ static inline BaseType vredmax(VecType vs2, BaseType vs1, size_t vl) { HAL_RVV_DEFINE_ONE(ELEM_TYPE, VEC_TYPE, LMUL_8, \ EEW, TYPE, m8, __VA_ARGS__) -#define HAL_RVV_SIGNED_PARAM s,x, , -#define HAL_RVV_UNSIGNED_PARAM s,x,u, -#define HAL_RVV_FLOAT_PARAM f,f, ,f +#define HAL_RVV_SIGNED_PARAM s,x, , , +#define HAL_RVV_UNSIGNED_PARAM s,x,u, , +#define HAL_RVV_FLOAT_PARAM f,f, ,f,o // -------------------------------Define Unsigned Integer-------------------------------- From ec5f7bb9f1d26fd2a78cf124a1c43f50b7de121f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=A9=E9=9F=B3=E3=81=82=E3=82=81?= Date: Thu, 20 Mar 2025 17:59:59 +0800 Subject: [PATCH 28/94] Merge pull request #27097 from amane-ame:blur_hal_rvv Add RISC-V HAL implementation for cv::blur series #27097 This patch implements `cv_hal_gaussianBlurBinomial`, `cv_hal_medianBlur`, `cv_hal_boxFilter` and `cv_hal_bilateralFilter` using native intrinsics, optimizing the performance of `cv::GaussianBlur/cv::medianBlur/cv::boxFilter/cv::bilateralFilter` for `3x3/5x5` kernels. Tested on MUSE-PI (Spacemit X60) for both gcc 14.2 and clang 20.0. ``` $ ./opencv_test_imgproc --gtest_filter="*Filter*:*Blur*" $ ./opencv_perf_imgproc --gtest_filter="*gauss*:*box*:*Bilateral*:*median*" --perf_min_samples=2000 --perf_force_samples=2000 ``` View the full perf table here: [hal_rvv_blur.pdf](https://github.com/user-attachments/files/19335582/hal_rvv_blur.pdf) ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [ ] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- 3rdparty/hal_rvv/hal_rvv_1p0/filter.hpp | 1827 ++++++++++++++++++++++- 3rdparty/hal_rvv/hal_rvv_1p0/types.hpp | 7 + 2 files changed, 1791 insertions(+), 43 deletions(-) diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/filter.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/filter.hpp index a457ccf0e0..f978cd172e 100644 --- a/3rdparty/hal_rvv/hal_rvv_1p0/filter.hpp +++ b/3rdparty/hal_rvv/hal_rvv_1p0/filter.hpp @@ -49,25 +49,32 @@ static inline int invoke(int height, std::function func, static inline int borderInterpolate( int p, int len, int borderType ) { - if( (unsigned)p < (unsigned)len ) + if ((unsigned)p < (unsigned)len) ; - else if( borderType == BORDER_REPLICATE ) + else if (borderType == BORDER_REPLICATE) p = p < 0 ? 0 : len - 1; - else if( borderType == BORDER_REFLECT || borderType == BORDER_REFLECT_101 ) + else if (borderType == BORDER_REFLECT || borderType == BORDER_REFLECT_101) { int delta = borderType == BORDER_REFLECT_101; - if( len == 1 ) + if (len == 1) return 0; do { - if( p < 0 ) + if (p < 0) p = -p - 1 + delta; else p = len - 1 - (p - len) - delta; } while( (unsigned)p >= (unsigned)len ); } - else if( borderType == BORDER_CONSTANT ) + else if (borderType == BORDER_WRAP) + { + if (p < 0) + p -= ((p-len+1)/len)*len; + if (p >= len) + p %= len; + } + else if (borderType == BORDER_CONSTANT) p = -1; return p; } @@ -132,10 +139,11 @@ static void process3(int anchor, int left, int right, float delta, const float* auto v2 = __riscv_vfwcvt_f(__riscv_vwcvtu_x(__riscv_vget_v_u8m1x4_u8m1(src, 2), vl), vl); auto v3 = __riscv_vfwcvt_f(__riscv_vwcvtu_x(__riscv_vget_v_u8m1x4_u8m1(src, 3), vl), vl); - s0 = addshift(s0, v0, k0, k1, k2, extra[vl * 4 ], extra[vl * 4 + 4]); - s1 = addshift(s1, v1, k0, k1, k2, extra[vl * 4 + 1], extra[vl * 4 + 5]); - s2 = addshift(s2, v2, k0, k1, k2, extra[vl * 4 + 2], extra[vl * 4 + 6]); - s3 = addshift(s3, v3, k0, k1, k2, extra[vl * 4 + 3], extra[vl * 4 + 7]); + extra += vl * 4; + s0 = addshift(s0, v0, k0, k1, k2, extra[0], extra[4]); + s1 = addshift(s1, v1, k0, k1, k2, extra[1], extra[5]); + s2 = addshift(s2, v2, k0, k1, k2, extra[2], extra[6]); + s3 = addshift(s3, v3, k0, k1, k2, extra[3], extra[7]); }; loadsrc(row0, kernel[0], kernel[1], kernel[2]); @@ -175,13 +183,14 @@ static void process5(int anchor, int left, int right, float delta, const float* auto loadsrc = [&](const uchar* row, float k0, float k1, float k2, float k3, float k4) { if (!row) return; - auto src = __riscv_vlseg4e8_v_u8m1x4(row + (i - anchor) * 4, vl); + const uchar* extra = row + (i - anchor) * 4; + auto src = __riscv_vlseg4e8_v_u8m1x4(extra, vl); auto v0 = __riscv_vfwcvt_f(__riscv_vwcvtu_x(__riscv_vget_v_u8m1x4_u8m1(src, 0), vl), vl); auto v1 = __riscv_vfwcvt_f(__riscv_vwcvtu_x(__riscv_vget_v_u8m1x4_u8m1(src, 1), vl), vl); auto v2 = __riscv_vfwcvt_f(__riscv_vwcvtu_x(__riscv_vget_v_u8m1x4_u8m1(src, 2), vl), vl); auto v3 = __riscv_vfwcvt_f(__riscv_vwcvtu_x(__riscv_vget_v_u8m1x4_u8m1(src, 3), vl), vl); - const uchar* extra = row + (i + vl - anchor) * 4; + extra += vl * 4; s0 = addshift(s0, v0, k0, k1, k2, k3, k4, extra[0], extra[4], extra[ 8], extra[12]); s1 = addshift(s1, v1, k0, k1, k2, k3, k4, extra[1], extra[5], extra[ 9], extra[13]); s2 = addshift(s2, v2, k0, k1, k2, k3, k4, extra[2], extra[6], extra[10], extra[14]); @@ -304,7 +313,7 @@ inline int filter(cvhalFilter2D* context, uchar* src_data, size_t src_step, ucha } for (int i = 0; i < height; i++) - std::copy(dst.data() + i * width * 4, dst.data() + (i + 1) * width * 4, dst_data + i * dst_step); + memcpy(dst_data + i * dst_step, dst.data() + i * width * 4, width * 4); return res; } @@ -338,13 +347,15 @@ struct sepFilter2D int borderType; }; -inline int sepFilterInit(cvhalFilter2D **context, int src_type, int dst_type, int kernel_type, uchar *kernelx_data, int kernelx_length, uchar *kernely_data, int kernely_length, int anchor_x, int anchor_y, double delta, int borderType) +inline int sepFilterInit(cvhalFilter2D **context, int src_type, int dst_type, int kernel_type, uchar* kernelx_data, int kernelx_length, uchar* kernely_data, int kernely_length, int anchor_x, int anchor_y, double delta, int borderType) { - if (kernel_type != CV_32FC1 || src_type != CV_8UC1 || (dst_type != CV_16SC1 && dst_type != CV_32FC1)) + if (kernel_type != CV_32FC1) return CV_HAL_ERROR_NOT_IMPLEMENTED; - if (kernelx_length != kernely_length) + if (src_type != CV_8UC1 && src_type != CV_16SC1 && src_type != CV_32FC1) return CV_HAL_ERROR_NOT_IMPLEMENTED; - if (kernelx_length != 3 && kernelx_length != 5) + if (dst_type != CV_16SC1 && dst_type != CV_32FC1) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + if ((kernelx_length != 3 && kernelx_length != 5) || kernelx_length != kernely_length) return CV_HAL_ERROR_NOT_IMPLEMENTED; if ((borderType & ~BORDER_ISOLATED) == BORDER_WRAP) return CV_HAL_ERROR_NOT_IMPLEMENTED; @@ -357,7 +368,7 @@ inline int sepFilterInit(cvhalFilter2D **context, int src_type, int dst_type, in // the algorithm is copied from 3rdparty/carotene/src/separable_filter.hpp, // in the functor RowFilter3x3S16Generic and ColFilter3x3S16Generic -template +template static inline int sepFilter(int start, int end, sepFilter2D* data, const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, int width, int height, int full_width, int full_height, int offset_x, int offset_y) { constexpr int noval = std::numeric_limits::max(); @@ -401,7 +412,7 @@ static inline int sepFilter(int start, int end, sepFilter2D* data, const uchar* int p = accessY(y + i); if (p != noval) { - sum += kx[i] * src_data[x * src_step + p]; + sum += kx[i] * reinterpret_cast(src_data + x * src_step)[p]; } } res[p2idx(x, y)] = sum; @@ -428,19 +439,32 @@ static inline int sepFilter(int start, int end, sepFilter2D* data, const uchar* for (int j = left; j < right; j += vl) { vl = __riscv_vsetvl_e8m2(right - j); - const uchar* extra = src_data + i * src_step + j - data->anchor_x; - auto sum = __riscv_vfmv_v_f_f32m8(0, vl); - auto src = __riscv_vfwcvt_f(__riscv_vwcvtu_x(__riscv_vle8_v_u8m2(extra, vl), vl), vl); - sum = __riscv_vfmacc(sum, kx[0], src, vl); - src = __riscv_vfslide1down(src, extra[vl], vl); + const T* extra = reinterpret_cast(src_data + i * src_step) + j - data->anchor_x; + vfloat32m8_t src; + if (std::is_same::value) + { + src = __riscv_vfwcvt_f(__riscv_vwcvtu_x(__riscv_vle8_v_u8m2(reinterpret_cast(extra), vl), vl), vl); + } + else if (std::is_same::value) + { + src = __riscv_vfwcvt_f(__riscv_vle16_v_i16m4(reinterpret_cast(extra), vl), vl); + } + else + { + src = __riscv_vle32_v_f32m8(reinterpret_cast(extra), vl); + } + + extra += vl; + auto sum = __riscv_vfmul(src, kx[0], vl); + src = __riscv_vfslide1down(src, extra[0], vl); sum = __riscv_vfmacc(sum, kx[1], src, vl); - src = __riscv_vfslide1down(src, extra[vl + 1], vl); + src = __riscv_vfslide1down(src, extra[1], vl); sum = __riscv_vfmacc(sum, kx[2], src, vl); if (ksize == 5) { - src = __riscv_vfslide1down(src, extra[vl + 2], vl); + src = __riscv_vfslide1down(src, extra[2], vl); sum = __riscv_vfmacc(sum, kx[3], src, vl); - src = __riscv_vfslide1down(src, extra[vl + 3], vl); + src = __riscv_vfslide1down(src, extra[3], vl); sum = __riscv_vfmacc(sum, kx[4], src, vl); } __riscv_vse32(res.data() + p2idx(i, j), sum, vl); @@ -454,7 +478,7 @@ static inline int sepFilter(int start, int end, sepFilter2D* data, const uchar* const float* row0 = accessX(cur ) == noval ? nullptr : res.data() + p2idx(accessX(cur ), 0); const float* row1 = accessX(cur + 1) == noval ? nullptr : res.data() + p2idx(accessX(cur + 1), 0); const float* row2 = accessX(cur + 2) == noval ? nullptr : res.data() + p2idx(accessX(cur + 2), 0); - const float* row3, *row4; + const float* row3 = nullptr, *row4 = nullptr; if (ksize == 5) { row3 = accessX(cur + 3) == noval ? nullptr : res.data() + p2idx(accessX(cur + 3), 0); @@ -476,6 +500,7 @@ static inline int sepFilter(int start, int end, sepFilter2D* data, const uchar* auto v4 = row4 ? __riscv_vle32_v_f32m4(row4 + j, vl) : __riscv_vfmv_v_f_f32m4(0, vl); sum = __riscv_vfmacc(__riscv_vfmacc(sum, ky[3], v3, vl), ky[4], v4, vl); } + if (data->dst_type == CV_16SC1) { __riscv_vse16(reinterpret_cast(dst_data + cur * dst_step) + j, __riscv_vfncvt_x(sum, vl), vl); @@ -491,19 +516,62 @@ static inline int sepFilter(int start, int end, sepFilter2D* data, const uchar* return CV_HAL_ERROR_OK; } -inline int sepFilter(cvhalFilter2D *context, uchar *src_data, size_t src_step, uchar* dst_data, size_t dst_step, int width, int height, int full_width, int full_height, int offset_x, int offset_y) +inline int sepFilter(cvhalFilter2D *context, uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, int width, int height, int full_width, int full_height, int offset_x, int offset_y) { sepFilter2D* data = reinterpret_cast(context); - switch (data->kernelx_length) + uchar* _dst_data = dst_data; + size_t _dst_step = dst_step; + size_t size = sizeof(char); + switch (data->dst_type) { - case 3: - return filter::invoke(height, {sepFilter<3>}, data, src_data, src_step, dst_data, dst_step, width, height, full_width, full_height, offset_x, offset_y); - case 5: - return filter::invoke(height, {sepFilter<5>}, data, src_data, src_step, dst_data, dst_step, width, height, full_width, full_height, offset_x, offset_y); + case CV_16SC1: + size = sizeof(short); + break; + case CV_32FC1: + size = sizeof(float); + break; + } + std::vector dst; + if (src_data == _dst_data) + { + dst = std::vector(width * height * size); + dst_data = dst.data(); + dst_step = width * size; } - return CV_HAL_ERROR_NOT_IMPLEMENTED; + int res = CV_HAL_ERROR_NOT_IMPLEMENTED; + switch (data->kernelx_length*100 + data->src_type) + { + case 300 + CV_8UC1: + res = filter::invoke(height, {sepFilter<3, uchar>}, data, src_data, src_step, dst_data, dst_step, width, height, full_width, full_height, offset_x, offset_y); + break; + case 500 + CV_8UC1: + res = filter::invoke(height, {sepFilter<5, uchar>}, data, src_data, src_step, dst_data, dst_step, width, height, full_width, full_height, offset_x, offset_y); + break; + case 300 + CV_16SC1: + res = filter::invoke(height, {sepFilter<3, short>}, data, src_data, src_step, dst_data, dst_step, width, height, full_width, full_height, offset_x, offset_y); + break; + case 500 + CV_16SC1: + res = filter::invoke(height, {sepFilter<5, short>}, data, src_data, src_step, dst_data, dst_step, width, height, full_width, full_height, offset_x, offset_y); + break; + case 300 + CV_32FC1: + res = filter::invoke(height, {sepFilter<3, float>}, data, src_data, src_step, dst_data, dst_step, width, height, full_width, full_height, offset_x, offset_y); + break; + case 500 + CV_32FC1: + res = filter::invoke(height, {sepFilter<5, float>}, data, src_data, src_step, dst_data, dst_step, width, height, full_width, full_height, offset_x, offset_y); + break; + } + if (res == CV_HAL_ERROR_NOT_IMPLEMENTED) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + + if (src_data == _dst_data) + { + for (int i = 0; i < height; i++) + memcpy(_dst_data + i * _dst_step, dst.data() + i * dst_step, dst_step); + } + + return res; } inline int sepFilterFree(cvhalFilter2D* context) @@ -527,7 +595,7 @@ struct Morph2D int src_type; int dst_type; int kernel_type; - uchar *kernel_data; + uchar* kernel_data; size_t kernel_step; int kernel_width; int kernel_height; @@ -537,7 +605,7 @@ struct Morph2D const uchar* borderValue; }; -inline int morphInit(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 borderType, const double borderValue[4], int iterations, bool /*allowSubmatrix*/, bool /*allowInplace*/) +inline int morphInit(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 borderType, const double borderValue[4], int iterations, bool /*allowSubmatrix*/, bool /*allowInplace*/) { if (kernel_type != CV_8UC1 || src_type != dst_type) return CV_HAL_ERROR_NOT_IMPLEMENTED; @@ -774,10 +842,11 @@ static inline int morph(int start, int end, Morph2D* data, const uchar* src_data v2 = __riscv_vset_v_u8m2_u8m4(v2, 1, __riscv_vget_v_u8m2x4_u8m2(src, 2)); v3 = __riscv_vset_v_u8m2_u8m4(v3, 1, __riscv_vget_v_u8m2x4_u8m2(src, 3)); - m0 = opshift(m0, v0, k0, k1, k2, extra[vl * 4 ], extra[vl * 4 + 4]); - m1 = opshift(m1, v1, k0, k1, k2, extra[vl * 4 + 1], extra[vl * 4 + 5]); - m2 = opshift(m2, v2, k0, k1, k2, extra[vl * 4 + 2], extra[vl * 4 + 6]); - m3 = opshift(m3, v3, k0, k1, k2, extra[vl * 4 + 3], extra[vl * 4 + 7]); + extra += vl * 4; + m0 = opshift(m0, v0, k0, k1, k2, extra[0], extra[4]); + m1 = opshift(m1, v1, k0, k1, k2, extra[1], extra[5]); + m2 = opshift(m2, v2, k0, k1, k2, extra[2], extra[6]); + m3 = opshift(m3, v3, k0, k1, k2, extra[3], extra[7]); }; loadsrc(row0, kernel[0], kernel[1], kernel[2]); @@ -802,7 +871,7 @@ static inline int morph(int start, int end, Morph2D* data, const uchar* src_data return CV_HAL_ERROR_OK; } -inline int morph(cvhalFilter2D* context, uchar *src_data, size_t src_step, uchar *dst_data, size_t dst_step, int width, int height, int src_full_width, int src_full_height, int src_roi_x, int src_roi_y, int /*dst_full_width*/, int /*dst_full_height*/, int /*dst_roi_x*/, int /*dst_roi_y*/) +inline int morph(cvhalFilter2D* context, uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, int width, int height, int src_full_width, int src_full_height, int src_roi_x, int src_roi_y, int /*dst_full_width*/, int /*dst_full_height*/, int /*dst_roi_x*/, int /*dst_roi_y*/) { Morph2D* data = reinterpret_cast(context); int cn = data->src_type == CV_8UC1 ? 1 : 4; @@ -820,7 +889,7 @@ inline int morph(cvhalFilter2D* context, uchar *src_data, size_t src_step, uchar } for (int i = 0; i < height; i++) - std::copy(dst.data() + i * width * cn, dst.data() + (i + 1) * width * cn, dst_data + i * dst_step); + memcpy(dst_data + i * dst_step, dst.data() + i * width * cn, width * cn); return res; } @@ -832,6 +901,1678 @@ inline int morphFree(cvhalFilter2D* context) } } // cv::cv_hal_rvv::morph +namespace gaussianBlurBinomial { +#undef cv_hal_gaussianBlurBinomial +#define cv_hal_gaussianBlurBinomial cv::cv_hal_rvv::gaussianBlurBinomial::gaussianBlurBinomial + +// the algorithm is same as cv_hal_sepFilter +template +static inline int gaussianBlurC1(int start, int end, const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, int width, int full_width, int full_height, int offset_x, int offset_y, int border_type) +{ + using T = typename helperT::ElemType; + using WT = typename helperWT::ElemType; + + constexpr int noval = std::numeric_limits::max(); + auto accessX = [&](int x) { + int pi = filter::borderInterpolate(offset_y + x - ksize / 2, full_height, border_type); + return pi < 0 ? noval : pi - offset_y; + }; + auto accessY = [&](int y) { + int pj = filter::borderInterpolate(offset_x + y - ksize / 2, full_width, border_type); + return pj < 0 ? noval : pj - offset_x; + }; + auto p2idx = [&](int x, int y){ return (x + ksize) % ksize * width + y; }; + + constexpr uint kernel[2][5] = {{1, 2, 1}, {1, 4, 6, 4, 1}}; + std::vector res(width * ksize); + auto process = [&](int x, int y) { + WT sum = 0; + for (int i = 0; i < ksize; i++) + { + int p = accessY(y + i); + if (p != noval) + { + sum += kernel[ksize == 5][i] * static_cast(reinterpret_cast(src_data + x * src_step)[p]); + } + } + res[p2idx(x, y)] = sum; + }; + + const int left = ksize / 2, right = width - ksize / 2; + for (int i = start - ksize / 2; i < end + ksize / 2; i++) + { + if (i + offset_y >= 0 && i + offset_y < full_height) + { + if (left >= right) + { + for (int j = 0; j < width; j++) + process(i, j); + } + else + { + for (int j = 0; j < left; j++) + process(i, j); + for (int j = right; j < width; j++) + process(i, j); + + int vl; + for (int j = left; j < right; j += vl) + { + vl = helperT::setvl(right - j); + const T* extra = reinterpret_cast(src_data + i * src_step) + j - ksize / 2; + auto src = __riscv_vzext_vf2(helperT::vload(extra, vl), vl); + + extra += vl; + auto sum = src; + if (ksize == 3) + { + src = __riscv_vslide1down(src, extra[0], vl); + sum = __riscv_vadd(sum, __riscv_vsll(src, 1, vl), vl); + src = __riscv_vslide1down(src, extra[1], vl); + sum = __riscv_vadd(sum, src, vl); + } + else + { + src = __riscv_vslide1down(src, extra[0], vl); + sum = __riscv_vadd(sum, __riscv_vsll(src, 2, vl), vl); + src = __riscv_vslide1down(src, extra[1], vl); + sum = __riscv_vadd(sum, __riscv_vadd(__riscv_vsll(src, 1, vl), __riscv_vsll(src, 2, vl), vl), vl); + src = __riscv_vslide1down(src, extra[2], vl); + sum = __riscv_vadd(sum, __riscv_vsll(src, 2, vl), vl); + src = __riscv_vslide1down(src, extra[3], vl); + sum = __riscv_vadd(sum, src, vl); + } + helperWT::vstore(res.data() + p2idx(i, j), sum, vl); + } + } + } + + int cur = i - ksize / 2; + if (cur >= start) + { + const WT* row0 = accessX(cur ) == noval ? nullptr : res.data() + p2idx(accessX(cur ), 0); + const WT* row1 = accessX(cur + 1) == noval ? nullptr : res.data() + p2idx(accessX(cur + 1), 0); + const WT* row2 = accessX(cur + 2) == noval ? nullptr : res.data() + p2idx(accessX(cur + 2), 0); + const WT* row3 = nullptr, *row4 = nullptr; + if (ksize == 5) + { + row3 = accessX(cur + 3) == noval ? nullptr : res.data() + p2idx(accessX(cur + 3), 0); + row4 = accessX(cur + 4) == noval ? nullptr : res.data() + p2idx(accessX(cur + 4), 0); + } + + int vl; + for (int j = 0; j < width; j += vl) + { + vl = helperWT::setvl(width - j); + auto v0 = row0 ? helperWT::vload(row0 + j, vl) : helperWT::vmv(0, vl); + auto v1 = row1 ? helperWT::vload(row1 + j, vl) : helperWT::vmv(0, vl); + auto v2 = row2 ? helperWT::vload(row2 + j, vl) : helperWT::vmv(0, vl); + typename helperWT::VecType sum; + if (ksize == 3) + { + sum = __riscv_vadd(__riscv_vadd(v0, v2, vl), __riscv_vsll(v1, 1, vl), vl); + } + else + { + sum = __riscv_vadd(v0, __riscv_vadd(__riscv_vsll(v2, 1, vl), __riscv_vsll(v2, 2, vl), vl), vl); + auto v3 = row3 ? helperWT::vload(row3 + j, vl) : helperWT::vmv(0, vl); + sum = __riscv_vadd(sum, __riscv_vsll(__riscv_vadd(v1, v3, vl), 2, vl), vl); + auto v4 = row4 ? helperWT::vload(row4 + j, vl) : helperWT::vmv(0, vl); + sum = __riscv_vadd(sum, v4, vl); + } + helperT::vstore(reinterpret_cast(dst_data + cur * dst_step) + j, __riscv_vnclipu(sum, ksize == 5 ? 8 : 4, __RISCV_VXRM_RNU, vl), vl); + } + } + } + + return CV_HAL_ERROR_OK; +} + +template +static inline int gaussianBlurC4(int start, int end, const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, int width, int full_width, int full_height, int offset_x, int offset_y, int border_type) +{ + constexpr int noval = std::numeric_limits::max(); + auto accessX = [&](int x) { + int pi = filter::borderInterpolate(offset_y + x - ksize / 2, full_height, border_type); + return pi < 0 ? noval : pi - offset_y; + }; + auto accessY = [&](int y) { + int pj = filter::borderInterpolate(offset_x + y - ksize / 2, full_width, border_type); + return pj < 0 ? noval : pj - offset_x; + }; + auto p2idx = [&](int x, int y){ return ((x + ksize) % ksize * width + y) * 4; }; + + constexpr uint kernel[2][5] = {{1, 2, 1}, {1, 4, 6, 4, 1}}; + std::vector res(width * ksize * 4); + auto process = [&](int x, int y) { + ushort sum0, sum1, sum2, sum3; + sum0 = sum1 = sum2 = sum3 = 0; + for (int i = 0; i < ksize; i++) + { + int p = accessY(y + i); + if (p != noval) + { + sum0 += kernel[ksize == 5][i] * static_cast((src_data + x * src_step)[p * 4 ]); + sum1 += kernel[ksize == 5][i] * static_cast((src_data + x * src_step)[p * 4 + 1]); + sum2 += kernel[ksize == 5][i] * static_cast((src_data + x * src_step)[p * 4 + 2]); + sum3 += kernel[ksize == 5][i] * static_cast((src_data + x * src_step)[p * 4 + 3]); + } + } + res[p2idx(x, y) ] = sum0; + res[p2idx(x, y) + 1] = sum1; + res[p2idx(x, y) + 2] = sum2; + res[p2idx(x, y) + 3] = sum3; + }; + + const int left = ksize / 2, right = width - ksize / 2; + for (int i = start - ksize / 2; i < end + ksize / 2; i++) + { + if (i + offset_y >= 0 && i + offset_y < full_height) + { + if (left >= right) + { + for (int j = 0; j < width; j++) + process(i, j); + } + else + { + for (int j = 0; j < left; j++) + process(i, j); + for (int j = right; j < width; j++) + process(i, j); + + int vl; + for (int j = left; j < right; j += vl) + { + vl = __riscv_vsetvl_e8m1(right - j); + const uchar* extra = src_data + i * src_step + (j - ksize / 2) * 4; + auto src = __riscv_vlseg4e8_v_u8m1x4(extra, vl); + auto src0 = __riscv_vzext_vf2(__riscv_vget_v_u8m1x4_u8m1(src, 0), vl); + auto src1 = __riscv_vzext_vf2(__riscv_vget_v_u8m1x4_u8m1(src, 1), vl); + auto src2 = __riscv_vzext_vf2(__riscv_vget_v_u8m1x4_u8m1(src, 2), vl); + auto src3 = __riscv_vzext_vf2(__riscv_vget_v_u8m1x4_u8m1(src, 3), vl); + + extra += vl * 4; + auto sum0 = src0, sum1 = src1, sum2 = src2, sum3 = src3; + if (ksize == 3) + { + src0 = __riscv_vslide1down(src0, extra[0], vl); + src1 = __riscv_vslide1down(src1, extra[1], vl); + src2 = __riscv_vslide1down(src2, extra[2], vl); + src3 = __riscv_vslide1down(src3, extra[3], vl); + sum0 = __riscv_vadd(sum0, __riscv_vsll(src0, 1, vl), vl); + sum1 = __riscv_vadd(sum1, __riscv_vsll(src1, 1, vl), vl); + sum2 = __riscv_vadd(sum2, __riscv_vsll(src2, 1, vl), vl); + sum3 = __riscv_vadd(sum3, __riscv_vsll(src3, 1, vl), vl); + src0 = __riscv_vslide1down(src0, extra[4], vl); + src1 = __riscv_vslide1down(src1, extra[5], vl); + src2 = __riscv_vslide1down(src2, extra[6], vl); + src3 = __riscv_vslide1down(src3, extra[7], vl); + sum0 = __riscv_vadd(sum0, src0, vl); + sum1 = __riscv_vadd(sum1, src1, vl); + sum2 = __riscv_vadd(sum2, src2, vl); + sum3 = __riscv_vadd(sum3, src3, vl); + } + else + { + src0 = __riscv_vslide1down(src0, extra[0], vl); + src1 = __riscv_vslide1down(src1, extra[1], vl); + src2 = __riscv_vslide1down(src2, extra[2], vl); + src3 = __riscv_vslide1down(src3, extra[3], vl); + sum0 = __riscv_vadd(sum0, __riscv_vsll(src0, 2, vl), vl); + sum1 = __riscv_vadd(sum1, __riscv_vsll(src1, 2, vl), vl); + sum2 = __riscv_vadd(sum2, __riscv_vsll(src2, 2, vl), vl); + sum3 = __riscv_vadd(sum3, __riscv_vsll(src3, 2, vl), vl); + src0 = __riscv_vslide1down(src0, extra[4], vl); + src1 = __riscv_vslide1down(src1, extra[5], vl); + src2 = __riscv_vslide1down(src2, extra[6], vl); + src3 = __riscv_vslide1down(src3, extra[7], vl); + sum0 = __riscv_vadd(sum0, __riscv_vadd(__riscv_vsll(src0, 1, vl), __riscv_vsll(src0, 2, vl), vl), vl); + sum1 = __riscv_vadd(sum1, __riscv_vadd(__riscv_vsll(src1, 1, vl), __riscv_vsll(src1, 2, vl), vl), vl); + sum2 = __riscv_vadd(sum2, __riscv_vadd(__riscv_vsll(src2, 1, vl), __riscv_vsll(src2, 2, vl), vl), vl); + sum3 = __riscv_vadd(sum3, __riscv_vadd(__riscv_vsll(src3, 1, vl), __riscv_vsll(src3, 2, vl), vl), vl); + src0 = __riscv_vslide1down(src0, extra[ 8], vl); + src1 = __riscv_vslide1down(src1, extra[ 9], vl); + src2 = __riscv_vslide1down(src2, extra[10], vl); + src3 = __riscv_vslide1down(src3, extra[11], vl); + sum0 = __riscv_vadd(sum0, __riscv_vsll(src0, 2, vl), vl); + sum1 = __riscv_vadd(sum1, __riscv_vsll(src1, 2, vl), vl); + sum2 = __riscv_vadd(sum2, __riscv_vsll(src2, 2, vl), vl); + sum3 = __riscv_vadd(sum3, __riscv_vsll(src3, 2, vl), vl); + src0 = __riscv_vslide1down(src0, extra[12], vl); + src1 = __riscv_vslide1down(src1, extra[13], vl); + src2 = __riscv_vslide1down(src2, extra[14], vl); + src3 = __riscv_vslide1down(src3, extra[15], vl); + sum0 = __riscv_vadd(sum0, src0, vl); + sum1 = __riscv_vadd(sum1, src1, vl); + sum2 = __riscv_vadd(sum2, src2, vl); + sum3 = __riscv_vadd(sum3, src3, vl); + } + + vuint16m2x4_t dst{}; + dst = __riscv_vset_v_u16m2_u16m2x4(dst, 0, sum0); + dst = __riscv_vset_v_u16m2_u16m2x4(dst, 1, sum1); + dst = __riscv_vset_v_u16m2_u16m2x4(dst, 2, sum2); + dst = __riscv_vset_v_u16m2_u16m2x4(dst, 3, sum3); + __riscv_vsseg4e16(res.data() + p2idx(i, j), dst, vl); + } + } + } + + int cur = i - ksize / 2; + if (cur >= start) + { + const ushort* row0 = accessX(cur ) == noval ? nullptr : res.data() + p2idx(accessX(cur ), 0); + const ushort* row1 = accessX(cur + 1) == noval ? nullptr : res.data() + p2idx(accessX(cur + 1), 0); + const ushort* row2 = accessX(cur + 2) == noval ? nullptr : res.data() + p2idx(accessX(cur + 2), 0); + const ushort* row3 = nullptr, *row4 = nullptr; + if (ksize == 5) + { + row3 = accessX(cur + 3) == noval ? nullptr : res.data() + p2idx(accessX(cur + 3), 0); + row4 = accessX(cur + 4) == noval ? nullptr : res.data() + p2idx(accessX(cur + 4), 0); + } + + int vl; + for (int j = 0; j < width; j += vl) + { + vl = __riscv_vsetvl_e16m2(width - j); + vuint16m2_t sum0, sum1, sum2, sum3, src0{}, src1{}, src2{}, src3{}; + sum0 = sum1 = sum2 = sum3 = __riscv_vmv_v_x_u16m2(0, vl); + + auto loadres = [&](const ushort* row) { + auto src = __riscv_vlseg4e16_v_u16m2x4(row + j * 4, vl); + src0 = __riscv_vget_v_u16m2x4_u16m2(src, 0); + src1 = __riscv_vget_v_u16m2x4_u16m2(src, 1); + src2 = __riscv_vget_v_u16m2x4_u16m2(src, 2); + src3 = __riscv_vget_v_u16m2x4_u16m2(src, 3); + }; + if (row0) + { + loadres(row0); + sum0 = src0; + sum1 = src1; + sum2 = src2; + sum3 = src3; + } + if (row1) + { + loadres(row1); + sum0 = __riscv_vadd(sum0, __riscv_vsll(src0, ksize == 5 ? 2 : 1, vl), vl); + sum1 = __riscv_vadd(sum1, __riscv_vsll(src1, ksize == 5 ? 2 : 1, vl), vl); + sum2 = __riscv_vadd(sum2, __riscv_vsll(src2, ksize == 5 ? 2 : 1, vl), vl); + sum3 = __riscv_vadd(sum3, __riscv_vsll(src3, ksize == 5 ? 2 : 1, vl), vl); + } + if (row2) + { + loadres(row2); + if (ksize == 5) + { + src0 = __riscv_vadd(__riscv_vsll(src0, 1, vl), __riscv_vsll(src0, 2, vl), vl); + src1 = __riscv_vadd(__riscv_vsll(src1, 1, vl), __riscv_vsll(src1, 2, vl), vl); + src2 = __riscv_vadd(__riscv_vsll(src2, 1, vl), __riscv_vsll(src2, 2, vl), vl); + src3 = __riscv_vadd(__riscv_vsll(src3, 1, vl), __riscv_vsll(src3, 2, vl), vl); + } + sum0 = __riscv_vadd(sum0, src0, vl); + sum1 = __riscv_vadd(sum1, src1, vl); + sum2 = __riscv_vadd(sum2, src2, vl); + sum3 = __riscv_vadd(sum3, src3, vl); + } + if (row3) + { + loadres(row3); + sum0 = __riscv_vadd(sum0, __riscv_vsll(src0, 2, vl), vl); + sum1 = __riscv_vadd(sum1, __riscv_vsll(src1, 2, vl), vl); + sum2 = __riscv_vadd(sum2, __riscv_vsll(src2, 2, vl), vl); + sum3 = __riscv_vadd(sum3, __riscv_vsll(src3, 2, vl), vl); + } + if (row4) + { + loadres(row4); + sum0 = __riscv_vadd(sum0, src0, vl); + sum1 = __riscv_vadd(sum1, src1, vl); + sum2 = __riscv_vadd(sum2, src2, vl); + sum3 = __riscv_vadd(sum3, src3, vl); + } + + vuint8m1x4_t dst{}; + dst = __riscv_vset_v_u8m1_u8m1x4(dst, 0, __riscv_vnclipu(sum0, ksize == 5 ? 8 : 4, __RISCV_VXRM_RNU, vl)); + dst = __riscv_vset_v_u8m1_u8m1x4(dst, 1, __riscv_vnclipu(sum1, ksize == 5 ? 8 : 4, __RISCV_VXRM_RNU, vl)); + dst = __riscv_vset_v_u8m1_u8m1x4(dst, 2, __riscv_vnclipu(sum2, ksize == 5 ? 8 : 4, __RISCV_VXRM_RNU, vl)); + dst = __riscv_vset_v_u8m1_u8m1x4(dst, 3, __riscv_vnclipu(sum3, ksize == 5 ? 8 : 4, __RISCV_VXRM_RNU, vl)); + __riscv_vsseg4e8(dst_data + cur * dst_step + j * 4, dst, vl); + } + } + } + + return CV_HAL_ERROR_OK; +} + +inline int gaussianBlurBinomial(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, int width, int height, int depth, int cn, size_t margin_left, size_t margin_top, size_t margin_right, size_t margin_bottom, size_t ksize, int border_type) +{ + const int type = CV_MAKETYPE(depth, cn); + if ((type != CV_8UC1 && type != CV_8UC4 && type != CV_16UC1) || src_data == dst_data) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + if ((ksize != 3 && ksize != 5) || border_type & BORDER_ISOLATED || border_type == BORDER_WRAP) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + + switch (ksize*100 + type) + { + case 300 + CV_8UC1: + return filter::invoke(height, {gaussianBlurC1<3, RVV_U8M4, RVV_U16M8>}, src_data, src_step, dst_data, dst_step, width, margin_left + width + margin_right, margin_top + height + margin_bottom, margin_left, margin_top, border_type); + case 500 + CV_8UC1: + return filter::invoke(height, {gaussianBlurC1<5, RVV_U8M4, RVV_U16M8>}, src_data, src_step, dst_data, dst_step, width, margin_left + width + margin_right, margin_top + height + margin_bottom, margin_left, margin_top, border_type); + case 300 + CV_16UC1: + return filter::invoke(height, {gaussianBlurC1<3, RVV_U16M4, RVV_U32M8>}, src_data, src_step, dst_data, dst_step, width, margin_left + width + margin_right, margin_top + height + margin_bottom, margin_left, margin_top, border_type); + case 500 + CV_16UC1: + return filter::invoke(height, {gaussianBlurC1<5, RVV_U16M4, RVV_U32M8>}, src_data, src_step, dst_data, dst_step, width, margin_left + width + margin_right, margin_top + height + margin_bottom, margin_left, margin_top, border_type); + case 300 + CV_8UC4: + return filter::invoke(height, {gaussianBlurC4<3>}, src_data, src_step, dst_data, dst_step, width, margin_left + width + margin_right, margin_top + height + margin_bottom, margin_left, margin_top, border_type); + case 500 + CV_8UC4: + return filter::invoke(height, {gaussianBlurC4<5>}, src_data, src_step, dst_data, dst_step, width, margin_left + width + margin_right, margin_top + height + margin_bottom, margin_left, margin_top, border_type); + } + + return CV_HAL_ERROR_NOT_IMPLEMENTED; +} +} // cv::cv_hal_rvv::gaussianBlurBinomial + +namespace medianBlur { +#undef cv_hal_medianBlur +#define cv_hal_medianBlur cv::cv_hal_rvv::medianBlur::medianBlur + +// the algorithm is copied from imgproc/src/median_blur.simd.cpp +// in the function template static void medianBlur_SortNet +template +static inline int medianBlurC1(int start, int end, const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, int width, int height) +{ + using T = typename helper::ElemType; + using VT = typename helper::VecType; + + for (int i = start; i < end; i++) + { + const T* row0 = reinterpret_cast(src_data + std::min(std::max(i - ksize / 2, 0), height - 1) * src_step); + const T* row1 = reinterpret_cast(src_data + std::min(std::max(i + 1 - ksize / 2, 0), height - 1) * src_step); + const T* row2 = reinterpret_cast(src_data + std::min(std::max(i + 2 - ksize / 2, 0), height - 1) * src_step); + const T* row3 = reinterpret_cast(src_data + std::min(std::max(i + 3 - ksize / 2, 0), height - 1) * src_step); + const T* row4 = reinterpret_cast(src_data + std::min(std::max(i + 4 - ksize / 2, 0), height - 1) * src_step); + int vl; + auto vop = [&vl](VT& a, VT& b) { + auto t = a; + a = helper::vmin(a, b, vl); + b = helper::vmax(t, b, vl); + }; + + for (int j = 0; j < width; j += vl) + { + vl = helper::setvl(width - j); + if (ksize == 3) + { + VT p0, p1, p2; + VT p3, p4, p5; + VT p6, p7, p8; + if (j != 0) + { + p0 = helper::vload(row0 + j - 1, vl); + p3 = helper::vload(row1 + j - 1, vl); + p6 = helper::vload(row2 + j - 1, vl); + } + else + { + p0 = helper::vslide1up(helper::vload(row0, vl), row0[0], vl); + p3 = helper::vslide1up(helper::vload(row1, vl), row1[0], vl); + p6 = helper::vslide1up(helper::vload(row2, vl), row2[0], vl); + } + p1 = helper::vslide1down(p0, row0[j + vl - 1], vl); + p4 = helper::vslide1down(p3, row1[j + vl - 1], vl); + p7 = helper::vslide1down(p6, row2[j + vl - 1], vl); + p2 = helper::vslide1down(p1, row0[std::min(width - 1, j + vl)], vl); + p5 = helper::vslide1down(p4, row1[std::min(width - 1, j + vl)], vl); + p8 = helper::vslide1down(p7, row2[std::min(width - 1, j + vl)], vl); + + vop(p1, p2); vop(p4, p5); vop(p7, p8); vop(p0, p1); + vop(p3, p4); vop(p6, p7); vop(p1, p2); vop(p4, p5); + vop(p7, p8); vop(p0, p3); vop(p5, p8); vop(p4, p7); + vop(p3, p6); vop(p1, p4); vop(p2, p5); vop(p4, p7); + vop(p4, p2); vop(p6, p4); vop(p4, p2); + helper::vstore(reinterpret_cast(dst_data + i * dst_step) + j, p4, vl); + } + else + { + VT p0, p1, p2, p3, p4; + VT p5, p6, p7, p8, p9; + VT p10, p11, p12, p13, p14; + VT p15, p16, p17, p18, p19; + VT p20, p21, p22, p23, p24; + if (j >= 2) + { + p0 = helper::vload(row0 + j - 2, vl); + p5 = helper::vload(row1 + j - 2, vl); + p10 = helper::vload(row2 + j - 2, vl); + p15 = helper::vload(row3 + j - 2, vl); + p20 = helper::vload(row4 + j - 2, vl); + } + else + { + p0 = helper::vslide1up(helper::vload(row0, vl), row0[0], vl); + p5 = helper::vslide1up(helper::vload(row1, vl), row1[0], vl); + p10 = helper::vslide1up(helper::vload(row2, vl), row2[0], vl); + p15 = helper::vslide1up(helper::vload(row3, vl), row3[0], vl); + p20 = helper::vslide1up(helper::vload(row4, vl), row4[0], vl); + if (j == 0) + { + p0 = helper::vslide1up(p0, row0[0], vl); + p5 = helper::vslide1up(p5, row1[0], vl); + p10 = helper::vslide1up(p10, row2[0], vl); + p15 = helper::vslide1up(p15, row3[0], vl); + p20 = helper::vslide1up(p20, row4[0], vl); + } + } + p1 = helper::vslide1down(p0, row0[j + vl - 2], vl); + p6 = helper::vslide1down(p5, row1[j + vl - 2], vl); + p11 = helper::vslide1down(p10, row2[j + vl - 2], vl); + p16 = helper::vslide1down(p15, row3[j + vl - 2], vl); + p21 = helper::vslide1down(p20, row4[j + vl - 2], vl); + p2 = helper::vslide1down(p1, row0[j + vl - 1], vl); + p7 = helper::vslide1down(p6, row1[j + vl - 1], vl); + p12 = helper::vslide1down(p11, row2[j + vl - 1], vl); + p17 = helper::vslide1down(p16, row3[j + vl - 1], vl); + p22 = helper::vslide1down(p21, row4[j + vl - 1], vl); + p3 = helper::vslide1down(p2, row0[std::min(width - 1, j + vl)], vl); + p8 = helper::vslide1down(p7, row1[std::min(width - 1, j + vl)], vl); + p13 = helper::vslide1down(p12, row2[std::min(width - 1, j + vl)], vl); + p18 = helper::vslide1down(p17, row3[std::min(width - 1, j + vl)], vl); + p23 = helper::vslide1down(p22, row4[std::min(width - 1, j + vl)], vl); + p4 = helper::vslide1down(p3, row0[std::min(width - 1, j + vl + 1)], vl); + p9 = helper::vslide1down(p8, row1[std::min(width - 1, j + vl + 1)], vl); + p14 = helper::vslide1down(p13, row2[std::min(width - 1, j + vl + 1)], vl); + p19 = helper::vslide1down(p18, row3[std::min(width - 1, j + vl + 1)], vl); + p24 = helper::vslide1down(p23, row4[std::min(width - 1, j + vl + 1)], vl); + + vop(p1, p2); vop(p0, p1); vop(p1, p2); vop(p4, p5); vop(p3, p4); + vop(p4, p5); vop(p0, p3); vop(p2, p5); vop(p2, p3); vop(p1, p4); + vop(p1, p2); vop(p3, p4); vop(p7, p8); vop(p6, p7); vop(p7, p8); + vop(p10, p11); vop(p9, p10); vop(p10, p11); vop(p6, p9); vop(p8, p11); + vop(p8, p9); vop(p7, p10); vop(p7, p8); vop(p9, p10); vop(p0, p6); + vop(p4, p10); vop(p4, p6); vop(p2, p8); vop(p2, p4); vop(p6, p8); + vop(p1, p7); vop(p5, p11); vop(p5, p7); vop(p3, p9); vop(p3, p5); + vop(p7, p9); vop(p1, p2); vop(p3, p4); vop(p5, p6); vop(p7, p8); + vop(p9, p10); vop(p13, p14); vop(p12, p13); vop(p13, p14); vop(p16, p17); + vop(p15, p16); vop(p16, p17); vop(p12, p15); vop(p14, p17); vop(p14, p15); + vop(p13, p16); vop(p13, p14); vop(p15, p16); vop(p19, p20); vop(p18, p19); + vop(p19, p20); vop(p21, p22); vop(p23, p24); vop(p21, p23); vop(p22, p24); + vop(p22, p23); vop(p18, p21); vop(p20, p23); vop(p20, p21); vop(p19, p22); + vop(p22, p24); vop(p19, p20); vop(p21, p22); vop(p23, p24); vop(p12, p18); + vop(p16, p22); vop(p16, p18); vop(p14, p20); vop(p20, p24); vop(p14, p16); + vop(p18, p20); vop(p22, p24); vop(p13, p19); vop(p17, p23); vop(p17, p19); + vop(p15, p21); vop(p15, p17); vop(p19, p21); vop(p13, p14); vop(p15, p16); + vop(p17, p18); vop(p19, p20); vop(p21, p22); vop(p23, p24); vop(p0, p12); + vop(p8, p20); vop(p8, p12); vop(p4, p16); vop(p16, p24); vop(p12, p16); + vop(p2, p14); vop(p10, p22); vop(p10, p14); vop(p6, p18); vop(p6, p10); + vop(p10, p12); vop(p1, p13); vop(p9, p21); vop(p9, p13); vop(p5, p17); + vop(p13, p17); vop(p3, p15); vop(p11, p23); vop(p11, p15); vop(p7, p19); + vop(p7, p11); vop(p11, p13); vop(p11, p12); + helper::vstore(reinterpret_cast(dst_data + i * dst_step) + j, p12, vl); + } + } + } + + return CV_HAL_ERROR_OK; +} + +template +static inline int medianBlurC4(int start, int end, const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, int width, int height) +{ + for (int i = start; i < end; i++) + { + const uchar* row0 = src_data + std::min(std::max(i - ksize / 2, 0), height - 1) * src_step; + const uchar* row1 = src_data + std::min(std::max(i + 1 - ksize / 2, 0), height - 1) * src_step; + const uchar* row2 = src_data + std::min(std::max(i + 2 - ksize / 2, 0), height - 1) * src_step; + const uchar* row3 = src_data + std::min(std::max(i + 3 - ksize / 2, 0), height - 1) * src_step; + const uchar* row4 = src_data + std::min(std::max(i + 4 - ksize / 2, 0), height - 1) * src_step; + int vl; + for (int j = 0; j < width; j += vl) + { + if (ksize == 3) + { + vl = __riscv_vsetvl_e8m1(width - j); + vuint8m1_t p00, p01, p02; + vuint8m1_t p03, p04, p05; + vuint8m1_t p06, p07, p08; + vuint8m1_t p10, p11, p12; + vuint8m1_t p13, p14, p15; + vuint8m1_t p16, p17, p18; + vuint8m1_t p20, p21, p22; + vuint8m1_t p23, p24, p25; + vuint8m1_t p26, p27, p28; + vuint8m1_t p30, p31, p32; + vuint8m1_t p33, p34, p35; + vuint8m1_t p36, p37, p38; + auto loadsrc = [&vl](const uchar* row, vuint8m1_t& p0, vuint8m1_t& p1, vuint8m1_t& p2, vuint8m1_t& p3) { + auto src = __riscv_vlseg4e8_v_u8m1x4(row, vl); + p0 = __riscv_vget_v_u8m1x4_u8m1(src, 0); + p1 = __riscv_vget_v_u8m1x4_u8m1(src, 1); + p2 = __riscv_vget_v_u8m1x4_u8m1(src, 2); + p3 = __riscv_vget_v_u8m1x4_u8m1(src, 3); + }; + if (j != 0) + { + loadsrc(row0 + (j - 1) * 4, p00, p10, p20, p30); + loadsrc(row1 + (j - 1) * 4, p03, p13, p23, p33); + loadsrc(row2 + (j - 1) * 4, p06, p16, p26, p36); + } + else + { + loadsrc(row0, p00, p10, p20, p30); + loadsrc(row1, p03, p13, p23, p33); + loadsrc(row2, p06, p16, p26, p36); + p00 = __riscv_vslide1up(p00, row0[0], vl); + p10 = __riscv_vslide1up(p10, row0[1], vl); + p20 = __riscv_vslide1up(p20, row0[2], vl); + p30 = __riscv_vslide1up(p30, row0[3], vl); + p03 = __riscv_vslide1up(p03, row1[0], vl); + p13 = __riscv_vslide1up(p13, row1[1], vl); + p23 = __riscv_vslide1up(p23, row1[2], vl); + p33 = __riscv_vslide1up(p33, row1[3], vl); + p06 = __riscv_vslide1up(p06, row2[0], vl); + p16 = __riscv_vslide1up(p16, row2[1], vl); + p26 = __riscv_vslide1up(p26, row2[2], vl); + p36 = __riscv_vslide1up(p36, row2[3], vl); + } + p01 = __riscv_vslide1down(p00, row0[(j + vl - 1) * 4 ], vl); + p11 = __riscv_vslide1down(p10, row0[(j + vl - 1) * 4 + 1], vl); + p21 = __riscv_vslide1down(p20, row0[(j + vl - 1) * 4 + 2], vl); + p31 = __riscv_vslide1down(p30, row0[(j + vl - 1) * 4 + 3], vl); + p04 = __riscv_vslide1down(p03, row1[(j + vl - 1) * 4 ], vl); + p14 = __riscv_vslide1down(p13, row1[(j + vl - 1) * 4 + 1], vl); + p24 = __riscv_vslide1down(p23, row1[(j + vl - 1) * 4 + 2], vl); + p34 = __riscv_vslide1down(p33, row1[(j + vl - 1) * 4 + 3], vl); + p07 = __riscv_vslide1down(p06, row2[(j + vl - 1) * 4 ], vl); + p17 = __riscv_vslide1down(p16, row2[(j + vl - 1) * 4 + 1], vl); + p27 = __riscv_vslide1down(p26, row2[(j + vl - 1) * 4 + 2], vl); + p37 = __riscv_vslide1down(p36, row2[(j + vl - 1) * 4 + 3], vl); + p02 = __riscv_vslide1down(p01, row0[std::min(width - 1, j + vl) * 4 ], vl); + p12 = __riscv_vslide1down(p11, row0[std::min(width - 1, j + vl) * 4 + 1], vl); + p22 = __riscv_vslide1down(p21, row0[std::min(width - 1, j + vl) * 4 + 2], vl); + p32 = __riscv_vslide1down(p31, row0[std::min(width - 1, j + vl) * 4 + 3], vl); + p05 = __riscv_vslide1down(p04, row1[std::min(width - 1, j + vl) * 4 ], vl); + p15 = __riscv_vslide1down(p14, row1[std::min(width - 1, j + vl) * 4 + 1], vl); + p25 = __riscv_vslide1down(p24, row1[std::min(width - 1, j + vl) * 4 + 2], vl); + p35 = __riscv_vslide1down(p34, row1[std::min(width - 1, j + vl) * 4 + 3], vl); + p08 = __riscv_vslide1down(p07, row2[std::min(width - 1, j + vl) * 4 ], vl); + p18 = __riscv_vslide1down(p17, row2[std::min(width - 1, j + vl) * 4 + 1], vl); + p28 = __riscv_vslide1down(p27, row2[std::min(width - 1, j + vl) * 4 + 2], vl); + p38 = __riscv_vslide1down(p37, row2[std::min(width - 1, j + vl) * 4 + 3], vl); + + auto vop = [&vl](vuint8m1_t& a, vuint8m1_t& b) { + auto t = a; + a = __riscv_vminu(a, b, vl); + b = __riscv_vmaxu(t, b, vl); + }; + vuint8m1x4_t dst{}; + vop(p01, p02); vop(p04, p05); vop(p07, p08); vop(p00, p01); + vop(p03, p04); vop(p06, p07); vop(p01, p02); vop(p04, p05); + vop(p07, p08); vop(p00, p03); vop(p05, p08); vop(p04, p07); + vop(p03, p06); vop(p01, p04); vop(p02, p05); vop(p04, p07); + vop(p04, p02); vop(p06, p04); vop(p04, p02); + dst = __riscv_vset_v_u8m1_u8m1x4(dst, 0, p04); + vop(p11, p12); vop(p14, p15); vop(p17, p18); vop(p10, p11); + vop(p13, p14); vop(p16, p17); vop(p11, p12); vop(p14, p15); + vop(p17, p18); vop(p10, p13); vop(p15, p18); vop(p14, p17); + vop(p13, p16); vop(p11, p14); vop(p12, p15); vop(p14, p17); + vop(p14, p12); vop(p16, p14); vop(p14, p12); + dst = __riscv_vset_v_u8m1_u8m1x4(dst, 1, p14); + vop(p21, p22); vop(p24, p25); vop(p27, p28); vop(p20, p21); + vop(p23, p24); vop(p26, p27); vop(p21, p22); vop(p24, p25); + vop(p27, p28); vop(p20, p23); vop(p25, p28); vop(p24, p27); + vop(p23, p26); vop(p21, p24); vop(p22, p25); vop(p24, p27); + vop(p24, p22); vop(p26, p24); vop(p24, p22); + dst = __riscv_vset_v_u8m1_u8m1x4(dst, 2, p24); + vop(p31, p32); vop(p34, p35); vop(p37, p38); vop(p30, p31); + vop(p33, p34); vop(p36, p37); vop(p31, p32); vop(p34, p35); + vop(p37, p38); vop(p30, p33); vop(p35, p38); vop(p34, p37); + vop(p33, p36); vop(p31, p34); vop(p32, p35); vop(p34, p37); + vop(p34, p32); vop(p36, p34); vop(p34, p32); + dst = __riscv_vset_v_u8m1_u8m1x4(dst, 3, p34); + __riscv_vsseg4e8(dst_data + i * dst_step + j * 4, dst, vl); + } + else + { + vl = __riscv_vsetvl_e8m2(width - j); + vuint8m2_t p00, p01, p02, p03, p04; + vuint8m2_t p05, p06, p07, p08, p09; + vuint8m2_t p010, p011, p012, p013, p014; + vuint8m2_t p015, p016, p017, p018, p019; + vuint8m2_t p020, p021, p022, p023, p024; + vuint8m2_t p10, p11, p12, p13, p14; + vuint8m2_t p15, p16, p17, p18, p19; + vuint8m2_t p110, p111, p112, p113, p114; + vuint8m2_t p115, p116, p117, p118, p119; + vuint8m2_t p120, p121, p122, p123, p124; + vuint8m2_t p20, p21, p22, p23, p24; + vuint8m2_t p25, p26, p27, p28, p29; + vuint8m2_t p210, p211, p212, p213, p214; + vuint8m2_t p215, p216, p217, p218, p219; + vuint8m2_t p220, p221, p222, p223, p224; + vuint8m2_t p30, p31, p32, p33, p34; + vuint8m2_t p35, p36, p37, p38, p39; + vuint8m2_t p310, p311, p312, p313, p314; + vuint8m2_t p315, p316, p317, p318, p319; + vuint8m2_t p320, p321, p322, p323, p324; + auto loadsrc = [&vl](const uchar* row, vuint8m2_t& p0, vuint8m2_t& p1, vuint8m2_t& p2, vuint8m2_t& p3) { + auto src = __riscv_vlseg4e8_v_u8m2x4(row, vl); + p0 = __riscv_vget_v_u8m2x4_u8m2(src, 0); + p1 = __riscv_vget_v_u8m2x4_u8m2(src, 1); + p2 = __riscv_vget_v_u8m2x4_u8m2(src, 2); + p3 = __riscv_vget_v_u8m2x4_u8m2(src, 3); + }; + if (j >= 2) + { + loadsrc(row0 + (j - 2) * 4, p00, p10, p20, p30); + loadsrc(row1 + (j - 2) * 4, p05, p15, p25, p35); + loadsrc(row2 + (j - 2) * 4, p010, p110, p210, p310); + loadsrc(row3 + (j - 2) * 4, p015, p115, p215, p315); + loadsrc(row4 + (j - 2) * 4, p020, p120, p220, p320); + } + else + { + loadsrc(row0, p00, p10, p20, p30); + loadsrc(row1, p05, p15, p25, p35); + loadsrc(row2, p010, p110, p210, p310); + loadsrc(row3, p015, p115, p215, p315); + loadsrc(row4, p020, p120, p220, p320); + auto slideup = [&] { + p00 = __riscv_vslide1up(p00, row0[0], vl); + p10 = __riscv_vslide1up(p10, row0[1], vl); + p20 = __riscv_vslide1up(p20, row0[2], vl); + p30 = __riscv_vslide1up(p30, row0[3], vl); + p05 = __riscv_vslide1up(p05, row1[0], vl); + p15 = __riscv_vslide1up(p15, row1[1], vl); + p25 = __riscv_vslide1up(p25, row1[2], vl); + p35 = __riscv_vslide1up(p35, row1[3], vl); + p010 = __riscv_vslide1up(p010, row2[0], vl); + p110 = __riscv_vslide1up(p110, row2[1], vl); + p210 = __riscv_vslide1up(p210, row2[2], vl); + p310 = __riscv_vslide1up(p310, row2[3], vl); + p015 = __riscv_vslide1up(p015, row3[0], vl); + p115 = __riscv_vslide1up(p115, row3[1], vl); + p215 = __riscv_vslide1up(p215, row3[2], vl); + p315 = __riscv_vslide1up(p315, row3[3], vl); + p020 = __riscv_vslide1up(p020, row4[0], vl); + p120 = __riscv_vslide1up(p120, row4[1], vl); + p220 = __riscv_vslide1up(p220, row4[2], vl); + p320 = __riscv_vslide1up(p320, row4[3], vl); + }; + slideup(); + if (j == 0) + { + slideup(); + } + } + p01 = __riscv_vslide1down(p00, row0[(j + vl - 2) * 4 ], vl); + p11 = __riscv_vslide1down(p10, row0[(j + vl - 2) * 4 + 1], vl); + p21 = __riscv_vslide1down(p20, row0[(j + vl - 2) * 4 + 2], vl); + p31 = __riscv_vslide1down(p30, row0[(j + vl - 2) * 4 + 3], vl); + p06 = __riscv_vslide1down(p05, row1[(j + vl - 2) * 4 ], vl); + p16 = __riscv_vslide1down(p15, row1[(j + vl - 2) * 4 + 1], vl); + p26 = __riscv_vslide1down(p25, row1[(j + vl - 2) * 4 + 2], vl); + p36 = __riscv_vslide1down(p35, row1[(j + vl - 2) * 4 + 3], vl); + p011 = __riscv_vslide1down(p010, row2[(j + vl - 2) * 4 ], vl); + p111 = __riscv_vslide1down(p110, row2[(j + vl - 2) * 4 + 1], vl); + p211 = __riscv_vslide1down(p210, row2[(j + vl - 2) * 4 + 2], vl); + p311 = __riscv_vslide1down(p310, row2[(j + vl - 2) * 4 + 3], vl); + p016 = __riscv_vslide1down(p015, row3[(j + vl - 2) * 4 ], vl); + p116 = __riscv_vslide1down(p115, row3[(j + vl - 2) * 4 + 1], vl); + p216 = __riscv_vslide1down(p215, row3[(j + vl - 2) * 4 + 2], vl); + p316 = __riscv_vslide1down(p315, row3[(j + vl - 2) * 4 + 3], vl); + p021 = __riscv_vslide1down(p020, row4[(j + vl - 2) * 4 ], vl); + p121 = __riscv_vslide1down(p120, row4[(j + vl - 2) * 4 + 1], vl); + p221 = __riscv_vslide1down(p220, row4[(j + vl - 2) * 4 + 2], vl); + p321 = __riscv_vslide1down(p320, row4[(j + vl - 2) * 4 + 3], vl); + p02 = __riscv_vslide1down(p01, row0[(j + vl - 1) * 4 ], vl); + p12 = __riscv_vslide1down(p11, row0[(j + vl - 1) * 4 + 1], vl); + p22 = __riscv_vslide1down(p21, row0[(j + vl - 1) * 4 + 2], vl); + p32 = __riscv_vslide1down(p31, row0[(j + vl - 1) * 4 + 3], vl); + p07 = __riscv_vslide1down(p06, row1[(j + vl - 1) * 4 ], vl); + p17 = __riscv_vslide1down(p16, row1[(j + vl - 1) * 4 + 1], vl); + p27 = __riscv_vslide1down(p26, row1[(j + vl - 1) * 4 + 2], vl); + p37 = __riscv_vslide1down(p36, row1[(j + vl - 1) * 4 + 3], vl); + p012 = __riscv_vslide1down(p011, row2[(j + vl - 1) * 4 ], vl); + p112 = __riscv_vslide1down(p111, row2[(j + vl - 1) * 4 + 1], vl); + p212 = __riscv_vslide1down(p211, row2[(j + vl - 1) * 4 + 2], vl); + p312 = __riscv_vslide1down(p311, row2[(j + vl - 1) * 4 + 3], vl); + p017 = __riscv_vslide1down(p016, row3[(j + vl - 1) * 4 ], vl); + p117 = __riscv_vslide1down(p116, row3[(j + vl - 1) * 4 + 1], vl); + p217 = __riscv_vslide1down(p216, row3[(j + vl - 1) * 4 + 2], vl); + p317 = __riscv_vslide1down(p316, row3[(j + vl - 1) * 4 + 3], vl); + p022 = __riscv_vslide1down(p021, row4[(j + vl - 1) * 4 ], vl); + p122 = __riscv_vslide1down(p121, row4[(j + vl - 1) * 4 + 1], vl); + p222 = __riscv_vslide1down(p221, row4[(j + vl - 1) * 4 + 2], vl); + p322 = __riscv_vslide1down(p321, row4[(j + vl - 1) * 4 + 3], vl); + p03 = __riscv_vslide1down(p02, row0[std::min(width - 1, j + vl) * 4 ], vl); + p13 = __riscv_vslide1down(p12, row0[std::min(width - 1, j + vl) * 4 + 1], vl); + p23 = __riscv_vslide1down(p22, row0[std::min(width - 1, j + vl) * 4 + 2], vl); + p33 = __riscv_vslide1down(p32, row0[std::min(width - 1, j + vl) * 4 + 3], vl); + p08 = __riscv_vslide1down(p07, row1[std::min(width - 1, j + vl) * 4 ], vl); + p18 = __riscv_vslide1down(p17, row1[std::min(width - 1, j + vl) * 4 + 1], vl); + p28 = __riscv_vslide1down(p27, row1[std::min(width - 1, j + vl) * 4 + 2], vl); + p38 = __riscv_vslide1down(p37, row1[std::min(width - 1, j + vl) * 4 + 3], vl); + p013 = __riscv_vslide1down(p012, row2[std::min(width - 1, j + vl) * 4 ], vl); + p113 = __riscv_vslide1down(p112, row2[std::min(width - 1, j + vl) * 4 + 1], vl); + p213 = __riscv_vslide1down(p212, row2[std::min(width - 1, j + vl) * 4 + 2], vl); + p313 = __riscv_vslide1down(p312, row2[std::min(width - 1, j + vl) * 4 + 3], vl); + p018 = __riscv_vslide1down(p017, row3[std::min(width - 1, j + vl) * 4 ], vl); + p118 = __riscv_vslide1down(p117, row3[std::min(width - 1, j + vl) * 4 + 1], vl); + p218 = __riscv_vslide1down(p217, row3[std::min(width - 1, j + vl) * 4 + 2], vl); + p318 = __riscv_vslide1down(p317, row3[std::min(width - 1, j + vl) * 4 + 3], vl); + p023 = __riscv_vslide1down(p022, row4[std::min(width - 1, j + vl) * 4 ], vl); + p123 = __riscv_vslide1down(p122, row4[std::min(width - 1, j + vl) * 4 + 1], vl); + p223 = __riscv_vslide1down(p222, row4[std::min(width - 1, j + vl) * 4 + 2], vl); + p323 = __riscv_vslide1down(p322, row4[std::min(width - 1, j + vl) * 4 + 3], vl); + p04 = __riscv_vslide1down(p03, row0[std::min(width - 1, j + vl + 1) * 4 ], vl); + p14 = __riscv_vslide1down(p13, row0[std::min(width - 1, j + vl + 1) * 4 + 1], vl); + p24 = __riscv_vslide1down(p23, row0[std::min(width - 1, j + vl + 1) * 4 + 2], vl); + p34 = __riscv_vslide1down(p33, row0[std::min(width - 1, j + vl + 1) * 4 + 3], vl); + p09 = __riscv_vslide1down(p08, row1[std::min(width - 1, j + vl + 1) * 4 ], vl); + p19 = __riscv_vslide1down(p18, row1[std::min(width - 1, j + vl + 1) * 4 + 1], vl); + p29 = __riscv_vslide1down(p28, row1[std::min(width - 1, j + vl + 1) * 4 + 2], vl); + p39 = __riscv_vslide1down(p38, row1[std::min(width - 1, j + vl + 1) * 4 + 3], vl); + p014 = __riscv_vslide1down(p013, row2[std::min(width - 1, j + vl + 1) * 4 ], vl); + p114 = __riscv_vslide1down(p113, row2[std::min(width - 1, j + vl + 1) * 4 + 1], vl); + p214 = __riscv_vslide1down(p213, row2[std::min(width - 1, j + vl + 1) * 4 + 2], vl); + p314 = __riscv_vslide1down(p313, row2[std::min(width - 1, j + vl + 1) * 4 + 3], vl); + p019 = __riscv_vslide1down(p018, row3[std::min(width - 1, j + vl + 1) * 4 ], vl); + p119 = __riscv_vslide1down(p118, row3[std::min(width - 1, j + vl + 1) * 4 + 1], vl); + p219 = __riscv_vslide1down(p218, row3[std::min(width - 1, j + vl + 1) * 4 + 2], vl); + p319 = __riscv_vslide1down(p318, row3[std::min(width - 1, j + vl + 1) * 4 + 3], vl); + p024 = __riscv_vslide1down(p023, row4[std::min(width - 1, j + vl + 1) * 4 ], vl); + p124 = __riscv_vslide1down(p123, row4[std::min(width - 1, j + vl + 1) * 4 + 1], vl); + p224 = __riscv_vslide1down(p223, row4[std::min(width - 1, j + vl + 1) * 4 + 2], vl); + p324 = __riscv_vslide1down(p323, row4[std::min(width - 1, j + vl + 1) * 4 + 3], vl); + + auto vop = [&vl](vuint8m2_t& a, vuint8m2_t& b) { + auto t = a; + a = __riscv_vminu(a, b, vl); + b = __riscv_vmaxu(t, b, vl); + }; + vuint8m2x4_t dst{}; + vop(p01, p02); vop(p00, p01); vop(p01, p02); vop(p04, p05); vop(p03, p04); + vop(p04, p05); vop(p00, p03); vop(p02, p05); vop(p02, p03); vop(p01, p04); + vop(p01, p02); vop(p03, p04); vop(p07, p08); vop(p06, p07); vop(p07, p08); + vop(p010, p011); vop(p09, p010); vop(p010, p011); vop(p06, p09); vop(p08, p011); + vop(p08, p09); vop(p07, p010); vop(p07, p08); vop(p09, p010); vop(p00, p06); + vop(p04, p010); vop(p04, p06); vop(p02, p08); vop(p02, p04); vop(p06, p08); + vop(p01, p07); vop(p05, p011); vop(p05, p07); vop(p03, p09); vop(p03, p05); + vop(p07, p09); vop(p01, p02); vop(p03, p04); vop(p05, p06); vop(p07, p08); + vop(p09, p010); vop(p013, p014); vop(p012, p013); vop(p013, p014); vop(p016, p017); + vop(p015, p016); vop(p016, p017); vop(p012, p015); vop(p014, p017); vop(p014, p015); + vop(p013, p016); vop(p013, p014); vop(p015, p016); vop(p019, p020); vop(p018, p019); + vop(p019, p020); vop(p021, p022); vop(p023, p024); vop(p021, p023); vop(p022, p024); + vop(p022, p023); vop(p018, p021); vop(p020, p023); vop(p020, p021); vop(p019, p022); + vop(p022, p024); vop(p019, p020); vop(p021, p022); vop(p023, p024); vop(p012, p018); + vop(p016, p022); vop(p016, p018); vop(p014, p020); vop(p020, p024); vop(p014, p016); + vop(p018, p020); vop(p022, p024); vop(p013, p019); vop(p017, p023); vop(p017, p019); + vop(p015, p021); vop(p015, p017); vop(p019, p021); vop(p013, p014); vop(p015, p016); + vop(p017, p018); vop(p019, p020); vop(p021, p022); vop(p023, p024); vop(p00, p012); + vop(p08, p020); vop(p08, p012); vop(p04, p016); vop(p016, p024); vop(p012, p016); + vop(p02, p014); vop(p010, p022); vop(p010, p014); vop(p06, p018); vop(p06, p010); + vop(p010, p012); vop(p01, p013); vop(p09, p021); vop(p09, p013); vop(p05, p017); + vop(p013, p017); vop(p03, p015); vop(p011, p023); vop(p011, p015); vop(p07, p019); + vop(p07, p011); vop(p011, p013); vop(p011, p012); + dst = __riscv_vset_v_u8m2_u8m2x4(dst, 0, p012); + vop(p11, p12); vop(p10, p11); vop(p11, p12); vop(p14, p15); vop(p13, p14); + vop(p14, p15); vop(p10, p13); vop(p12, p15); vop(p12, p13); vop(p11, p14); + vop(p11, p12); vop(p13, p14); vop(p17, p18); vop(p16, p17); vop(p17, p18); + vop(p110, p111); vop(p19, p110); vop(p110, p111); vop(p16, p19); vop(p18, p111); + vop(p18, p19); vop(p17, p110); vop(p17, p18); vop(p19, p110); vop(p10, p16); + vop(p14, p110); vop(p14, p16); vop(p12, p18); vop(p12, p14); vop(p16, p18); + vop(p11, p17); vop(p15, p111); vop(p15, p17); vop(p13, p19); vop(p13, p15); + vop(p17, p19); vop(p11, p12); vop(p13, p14); vop(p15, p16); vop(p17, p18); + vop(p19, p110); vop(p113, p114); vop(p112, p113); vop(p113, p114); vop(p116, p117); + vop(p115, p116); vop(p116, p117); vop(p112, p115); vop(p114, p117); vop(p114, p115); + vop(p113, p116); vop(p113, p114); vop(p115, p116); vop(p119, p120); vop(p118, p119); + vop(p119, p120); vop(p121, p122); vop(p123, p124); vop(p121, p123); vop(p122, p124); + vop(p122, p123); vop(p118, p121); vop(p120, p123); vop(p120, p121); vop(p119, p122); + vop(p122, p124); vop(p119, p120); vop(p121, p122); vop(p123, p124); vop(p112, p118); + vop(p116, p122); vop(p116, p118); vop(p114, p120); vop(p120, p124); vop(p114, p116); + vop(p118, p120); vop(p122, p124); vop(p113, p119); vop(p117, p123); vop(p117, p119); + vop(p115, p121); vop(p115, p117); vop(p119, p121); vop(p113, p114); vop(p115, p116); + vop(p117, p118); vop(p119, p120); vop(p121, p122); vop(p123, p124); vop(p10, p112); + vop(p18, p120); vop(p18, p112); vop(p14, p116); vop(p116, p124); vop(p112, p116); + vop(p12, p114); vop(p110, p122); vop(p110, p114); vop(p16, p118); vop(p16, p110); + vop(p110, p112); vop(p11, p113); vop(p19, p121); vop(p19, p113); vop(p15, p117); + vop(p113, p117); vop(p13, p115); vop(p111, p123); vop(p111, p115); vop(p17, p119); + vop(p17, p111); vop(p111, p113); vop(p111, p112); + dst = __riscv_vset_v_u8m2_u8m2x4(dst, 1, p112); + vop(p21, p22); vop(p20, p21); vop(p21, p22); vop(p24, p25); vop(p23, p24); + vop(p24, p25); vop(p20, p23); vop(p22, p25); vop(p22, p23); vop(p21, p24); + vop(p21, p22); vop(p23, p24); vop(p27, p28); vop(p26, p27); vop(p27, p28); + vop(p210, p211); vop(p29, p210); vop(p210, p211); vop(p26, p29); vop(p28, p211); + vop(p28, p29); vop(p27, p210); vop(p27, p28); vop(p29, p210); vop(p20, p26); + vop(p24, p210); vop(p24, p26); vop(p22, p28); vop(p22, p24); vop(p26, p28); + vop(p21, p27); vop(p25, p211); vop(p25, p27); vop(p23, p29); vop(p23, p25); + vop(p27, p29); vop(p21, p22); vop(p23, p24); vop(p25, p26); vop(p27, p28); + vop(p29, p210); vop(p213, p214); vop(p212, p213); vop(p213, p214); vop(p216, p217); + vop(p215, p216); vop(p216, p217); vop(p212, p215); vop(p214, p217); vop(p214, p215); + vop(p213, p216); vop(p213, p214); vop(p215, p216); vop(p219, p220); vop(p218, p219); + vop(p219, p220); vop(p221, p222); vop(p223, p224); vop(p221, p223); vop(p222, p224); + vop(p222, p223); vop(p218, p221); vop(p220, p223); vop(p220, p221); vop(p219, p222); + vop(p222, p224); vop(p219, p220); vop(p221, p222); vop(p223, p224); vop(p212, p218); + vop(p216, p222); vop(p216, p218); vop(p214, p220); vop(p220, p224); vop(p214, p216); + vop(p218, p220); vop(p222, p224); vop(p213, p219); vop(p217, p223); vop(p217, p219); + vop(p215, p221); vop(p215, p217); vop(p219, p221); vop(p213, p214); vop(p215, p216); + vop(p217, p218); vop(p219, p220); vop(p221, p222); vop(p223, p224); vop(p20, p212); + vop(p28, p220); vop(p28, p212); vop(p24, p216); vop(p216, p224); vop(p212, p216); + vop(p22, p214); vop(p210, p222); vop(p210, p214); vop(p26, p218); vop(p26, p210); + vop(p210, p212); vop(p21, p213); vop(p29, p221); vop(p29, p213); vop(p25, p217); + vop(p213, p217); vop(p23, p215); vop(p211, p223); vop(p211, p215); vop(p27, p219); + vop(p27, p211); vop(p211, p213); vop(p211, p212); + dst = __riscv_vset_v_u8m2_u8m2x4(dst, 2, p212); + vop(p31, p32); vop(p30, p31); vop(p31, p32); vop(p34, p35); vop(p33, p34); + vop(p34, p35); vop(p30, p33); vop(p32, p35); vop(p32, p33); vop(p31, p34); + vop(p31, p32); vop(p33, p34); vop(p37, p38); vop(p36, p37); vop(p37, p38); + vop(p310, p311); vop(p39, p310); vop(p310, p311); vop(p36, p39); vop(p38, p311); + vop(p38, p39); vop(p37, p310); vop(p37, p38); vop(p39, p310); vop(p30, p36); + vop(p34, p310); vop(p34, p36); vop(p32, p38); vop(p32, p34); vop(p36, p38); + vop(p31, p37); vop(p35, p311); vop(p35, p37); vop(p33, p39); vop(p33, p35); + vop(p37, p39); vop(p31, p32); vop(p33, p34); vop(p35, p36); vop(p37, p38); + vop(p39, p310); vop(p313, p314); vop(p312, p313); vop(p313, p314); vop(p316, p317); + vop(p315, p316); vop(p316, p317); vop(p312, p315); vop(p314, p317); vop(p314, p315); + vop(p313, p316); vop(p313, p314); vop(p315, p316); vop(p319, p320); vop(p318, p319); + vop(p319, p320); vop(p321, p322); vop(p323, p324); vop(p321, p323); vop(p322, p324); + vop(p322, p323); vop(p318, p321); vop(p320, p323); vop(p320, p321); vop(p319, p322); + vop(p322, p324); vop(p319, p320); vop(p321, p322); vop(p323, p324); vop(p312, p318); + vop(p316, p322); vop(p316, p318); vop(p314, p320); vop(p320, p324); vop(p314, p316); + vop(p318, p320); vop(p322, p324); vop(p313, p319); vop(p317, p323); vop(p317, p319); + vop(p315, p321); vop(p315, p317); vop(p319, p321); vop(p313, p314); vop(p315, p316); + vop(p317, p318); vop(p319, p320); vop(p321, p322); vop(p323, p324); vop(p30, p312); + vop(p38, p320); vop(p38, p312); vop(p34, p316); vop(p316, p324); vop(p312, p316); + vop(p32, p314); vop(p310, p322); vop(p310, p314); vop(p36, p318); vop(p36, p310); + vop(p310, p312); vop(p31, p313); vop(p39, p321); vop(p39, p313); vop(p35, p317); + vop(p313, p317); vop(p33, p315); vop(p311, p323); vop(p311, p315); vop(p37, p319); + vop(p37, p311); vop(p311, p313); vop(p311, p312); + dst = __riscv_vset_v_u8m2_u8m2x4(dst, 3, p312); + __riscv_vsseg4e8(dst_data + i * dst_step + j * 4, dst, vl); + } + } + } + + return CV_HAL_ERROR_OK; +} + +inline int medianBlur(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, int width, int height, int depth, int cn, int ksize) +{ + const int type = CV_MAKETYPE(depth, cn); + if (type != CV_8UC1 && type != CV_8UC4 && type != CV_16UC1 && type != CV_16SC1 && type != CV_32FC1) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + if ((ksize != 3 && ksize != 5) || src_data == dst_data) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + + switch (ksize*100 + type) + { + case 300 + CV_8UC1: + return filter::invoke(height, {medianBlurC1<3, RVV_U8M4>}, src_data, src_step, dst_data, dst_step, width, height); + case 300 + CV_16UC1: + return filter::invoke(height, {medianBlurC1<3, RVV_U16M4>}, src_data, src_step, dst_data, dst_step, width, height); + case 300 + CV_16SC1: + return filter::invoke(height, {medianBlurC1<3, RVV_I16M4>}, src_data, src_step, dst_data, dst_step, width, height); + case 300 + CV_32FC1: + return filter::invoke(height, {medianBlurC1<3, RVV_F32M4>}, src_data, src_step, dst_data, dst_step, width, height); + case 500 + CV_8UC1: + return filter::invoke(height, {medianBlurC1<5, RVV_U8M1>}, src_data, src_step, dst_data, dst_step, width, height); + case 500 + CV_16UC1: + return filter::invoke(height, {medianBlurC1<5, RVV_U16M1>}, src_data, src_step, dst_data, dst_step, width, height); + case 500 + CV_16SC1: + return filter::invoke(height, {medianBlurC1<5, RVV_I16M1>}, src_data, src_step, dst_data, dst_step, width, height); + case 500 + CV_32FC1: + return filter::invoke(height, {medianBlurC1<5, RVV_F32M1>}, src_data, src_step, dst_data, dst_step, width, height); + + case 300 + CV_8UC4: + return filter::invoke(height, {medianBlurC4<3>}, src_data, src_step, dst_data, dst_step, width, height); + case 500 + CV_8UC4: + return filter::invoke(height, {medianBlurC4<5>}, src_data, src_step, dst_data, dst_step, width, height); + } + + return CV_HAL_ERROR_NOT_IMPLEMENTED; +} +} // cv::cv_hal_rvv::medianBlur + +namespace boxFilter { +#undef cv_hal_boxFilter +#define cv_hal_boxFilter cv::cv_hal_rvv::boxFilter::boxFilter + +template struct rvv; +template<> struct rvv +{ + static inline vuint16m8_t vcvt0(vuint8m4_t a, size_t b) { return __riscv_vzext_vf2(a, b); } + static inline vuint8m4_t vcvt1(vuint16m8_t a, size_t b) { return __riscv_vnclipu(a, 0, __RISCV_VXRM_RNU, b); } + static inline vuint16m8_t vdiv(vuint16m8_t a, ushort b, size_t c) { return __riscv_vdivu(__riscv_vadd(a, b / 2, c), b, c); } +}; +template<> struct rvv +{ + static inline vint32m8_t vcvt0(vint16m4_t a, size_t b) { return __riscv_vsext_vf2(a, b); } + static inline vint16m4_t vcvt1(vint32m8_t a, size_t b) { return __riscv_vnclip(a, 0, __RISCV_VXRM_RNU, b); } + static inline vint32m8_t vdiv(vint32m8_t a, int b, size_t c) { return __riscv_vdiv(__riscv_vadd(a, b / 2, c), b, c); } +}; +template<> struct rvv +{ + static inline vint32m8_t vcvt0(vint32m8_t a, size_t) { return a; } + static inline vint32m8_t vcvt1(vint32m8_t a, size_t) { return a; } + static inline vint32m8_t vdiv(vint32m8_t a, int b, size_t c) { return __riscv_vdiv(__riscv_vadd(a, b / 2, c), b, c); } +}; +template<> struct rvv +{ + static inline vfloat32m8_t vcvt0(vfloat32m8_t a, size_t) { return a; } + static inline vfloat32m8_t vcvt1(vfloat32m8_t a, size_t) { return a; } + static inline vfloat32m8_t vdiv(vfloat32m8_t a, float b, size_t c) { return __riscv_vfdiv(a, b, c); } +}; + +// the algorithm is same as cv_hal_sepFilter +template +static inline int boxFilterC1(int start, int end, const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, int width, int full_width, int full_height, int offset_x, int offset_y, int anchor_x, int anchor_y, bool normalize, int border_type) +{ + using T = typename helperT::ElemType; + using WT = typename helperWT::ElemType; + + constexpr int noval = std::numeric_limits::max(); + auto accessX = [&](int x) { + int pi = filter::borderInterpolate(offset_y + x - anchor_y, full_height, border_type); + return pi < 0 ? noval : pi - offset_y; + }; + auto accessY = [&](int y) { + int pj = filter::borderInterpolate(offset_x + y - anchor_x, full_width, border_type); + return pj < 0 ? noval : pj - offset_x; + }; + auto p2idx = [&](int x, int y){ return (x + ksize) % ksize * width + y; }; + + std::vector res(width * ksize); + auto process = [&](int x, int y) { + WT sum = 0; + for (int i = 0; i < ksize; i++) + { + int p = accessY(y + i); + if (p != noval) + { + sum += reinterpret_cast(src_data + x * src_step)[p]; + } + } + res[p2idx(x, y)] = sum; + }; + + const int left = anchor_x, right = width - (ksize - 1 - anchor_x); + for (int i = start - anchor_y; i < end + (ksize - 1 - anchor_y); i++) + { + if (i + offset_y >= 0 && i + offset_y < full_height) + { + if (left >= right) + { + for (int j = 0; j < width; j++) + process(i, j); + } + else + { + for (int j = 0; j < left; j++) + process(i, j); + for (int j = right; j < width; j++) + process(i, j); + + int vl; + for (int j = left; j < right; j += vl) + { + vl = helperT::setvl(right - j); + const T* extra = reinterpret_cast(src_data + i * src_step) + j - anchor_x; + auto src = rvv::vcvt0(helperT::vload(extra, vl), vl); + + extra += vl; + auto sum = src; + src = helperWT::vslide1down(src, extra[0], vl); + sum = helperWT::vadd(sum, src, vl); + src = helperWT::vslide1down(src, extra[1], vl); + sum = helperWT::vadd(sum, src, vl); + if (ksize == 5) + { + src = helperWT::vslide1down(src, extra[2], vl); + sum = helperWT::vadd(sum, src, vl); + src = helperWT::vslide1down(src, extra[3], vl); + sum = helperWT::vadd(sum, src, vl); + } + helperWT::vstore(res.data() + p2idx(i, j), sum, vl); + } + } + } + + int cur = i - (ksize - 1 - anchor_y); + if (cur >= start) + { + const WT* row0 = accessX(cur ) == noval ? nullptr : res.data() + p2idx(accessX(cur ), 0); + const WT* row1 = accessX(cur + 1) == noval ? nullptr : res.data() + p2idx(accessX(cur + 1), 0); + const WT* row2 = accessX(cur + 2) == noval ? nullptr : res.data() + p2idx(accessX(cur + 2), 0); + const WT* row3 = nullptr, *row4 = nullptr; + if (ksize == 5) + { + row3 = accessX(cur + 3) == noval ? nullptr : res.data() + p2idx(accessX(cur + 3), 0); + row4 = accessX(cur + 4) == noval ? nullptr : res.data() + p2idx(accessX(cur + 4), 0); + } + + int vl; + for (int j = 0; j < width; j += vl) + { + vl = helperWT::setvl(width - j); + auto sum = row0 ? helperWT::vload(row0 + j, vl) : helperWT::vmv(0, vl); + if (row1) sum = helperWT::vadd(sum, helperWT::vload(row1 + j, vl), vl); + if (row2) sum = helperWT::vadd(sum, helperWT::vload(row2 + j, vl), vl); + if (row3) sum = helperWT::vadd(sum, helperWT::vload(row3 + j, vl), vl); + if (row4) sum = helperWT::vadd(sum, helperWT::vload(row4 + j, vl), vl); + if (normalize) sum = rvv::vdiv(sum, ksize * ksize, vl); + + if (cast) + { + helperT::vstore(reinterpret_cast(dst_data + cur * dst_step) + j, rvv::vcvt1(sum, vl), vl); + } + else + { + helperWT::vstore(reinterpret_cast(dst_data + cur * dst_step) + j, sum, vl); + } + } + } + } + + return CV_HAL_ERROR_OK; +} + +template +static inline int boxFilterC3(int start, int end, const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, int width, int full_width, int full_height, int offset_x, int offset_y, int anchor_x, int anchor_y, bool normalize, int border_type) +{ + constexpr int noval = std::numeric_limits::max(); + auto accessX = [&](int x) { + int pi = filter::borderInterpolate(offset_y + x - anchor_y, full_height, border_type); + return pi < 0 ? noval : pi - offset_y; + }; + auto accessY = [&](int y) { + int pj = filter::borderInterpolate(offset_x + y - anchor_x, full_width, border_type); + return pj < 0 ? noval : pj - offset_x; + }; + auto p2idx = [&](int x, int y){ return ((x + ksize) % ksize * width + y) * 3; }; + + std::vector res(width * ksize * 3); + auto process = [&](int x, int y) { + float sum0, sum1, sum2; + sum0 = sum1 = sum2 = 0; + for (int i = 0; i < ksize; i++) + { + int p = accessY(y + i); + if (p != noval) + { + sum0 += reinterpret_cast(src_data + x * src_step)[p * 3 ]; + sum1 += reinterpret_cast(src_data + x * src_step)[p * 3 + 1]; + sum2 += reinterpret_cast(src_data + x * src_step)[p * 3 + 2]; + } + } + res[p2idx(x, y) ] = sum0; + res[p2idx(x, y) + 1] = sum1; + res[p2idx(x, y) + 2] = sum2; + }; + + const int left = anchor_x, right = width - (ksize - 1 - anchor_x); + for (int i = start - anchor_y; i < end + (ksize - 1 - anchor_y); i++) + { + if (i + offset_y >= 0 && i + offset_y < full_height) + { + if (left >= right) + { + for (int j = 0; j < width; j++) + process(i, j); + } + else + { + for (int j = 0; j < left; j++) + process(i, j); + for (int j = right; j < width; j++) + process(i, j); + + int vl; + for (int j = left; j < right; j += vl) + { + vl = __riscv_vsetvl_e32m2(right - j); + const float* extra = reinterpret_cast(src_data + i * src_step) + (j - anchor_x) * 3; + auto src = __riscv_vlseg3e32_v_f32m2x3(extra, vl); + auto src0 = __riscv_vget_v_f32m2x3_f32m2(src, 0); + auto src1 = __riscv_vget_v_f32m2x3_f32m2(src, 1); + auto src2 = __riscv_vget_v_f32m2x3_f32m2(src, 2); + + extra += vl * 3; + auto sum0 = src0, sum1 = src1, sum2 = src2; + src0 = __riscv_vfslide1down(src0, extra[0], vl); + src1 = __riscv_vfslide1down(src1, extra[1], vl); + src2 = __riscv_vfslide1down(src2, extra[2], vl); + sum0 = __riscv_vfadd(sum0, src0, vl); + sum1 = __riscv_vfadd(sum1, src1, vl); + sum2 = __riscv_vfadd(sum2, src2, vl); + src0 = __riscv_vfslide1down(src0, extra[3], vl); + src1 = __riscv_vfslide1down(src1, extra[4], vl); + src2 = __riscv_vfslide1down(src2, extra[5], vl); + sum0 = __riscv_vfadd(sum0, src0, vl); + sum1 = __riscv_vfadd(sum1, src1, vl); + sum2 = __riscv_vfadd(sum2, src2, vl); + if (ksize == 5) + { + src0 = __riscv_vfslide1down(src0, extra[6], vl); + src1 = __riscv_vfslide1down(src1, extra[7], vl); + src2 = __riscv_vfslide1down(src2, extra[8], vl); + sum0 = __riscv_vfadd(sum0, src0, vl); + sum1 = __riscv_vfadd(sum1, src1, vl); + sum2 = __riscv_vfadd(sum2, src2, vl); + src0 = __riscv_vfslide1down(src0, extra[ 9], vl); + src1 = __riscv_vfslide1down(src1, extra[10], vl); + src2 = __riscv_vfslide1down(src2, extra[11], vl); + sum0 = __riscv_vfadd(sum0, src0, vl); + sum1 = __riscv_vfadd(sum1, src1, vl); + sum2 = __riscv_vfadd(sum2, src2, vl); + } + + vfloat32m2x3_t dst{}; + dst = __riscv_vset_v_f32m2_f32m2x3(dst, 0, sum0); + dst = __riscv_vset_v_f32m2_f32m2x3(dst, 1, sum1); + dst = __riscv_vset_v_f32m2_f32m2x3(dst, 2, sum2); + __riscv_vsseg3e32(res.data() + p2idx(i, j), dst, vl); + } + } + } + + int cur = i - (ksize - 1 - anchor_y); + if (cur >= start) + { + const float* row0 = accessX(cur ) == noval ? nullptr : res.data() + p2idx(accessX(cur ), 0); + const float* row1 = accessX(cur + 1) == noval ? nullptr : res.data() + p2idx(accessX(cur + 1), 0); + const float* row2 = accessX(cur + 2) == noval ? nullptr : res.data() + p2idx(accessX(cur + 2), 0); + const float* row3 = nullptr, *row4 = nullptr; + if (ksize == 5) + { + row3 = accessX(cur + 3) == noval ? nullptr : res.data() + p2idx(accessX(cur + 3), 0); + row4 = accessX(cur + 4) == noval ? nullptr : res.data() + p2idx(accessX(cur + 4), 0); + } + + int vl; + for (int j = 0; j < width; j += vl) + { + vl = __riscv_vsetvl_e32m2(width - j); + vfloat32m2_t sum0, sum1, sum2; + sum0 = sum1 = sum2 = __riscv_vfmv_v_f_f32m2(0, vl); + auto loadres = [&](const float* row) { + if (!row) return; + auto src = __riscv_vlseg3e32_v_f32m2x3(row + j * 3, vl); + sum0 = __riscv_vfadd(sum0, __riscv_vget_v_f32m2x3_f32m2(src, 0), vl); + sum1 = __riscv_vfadd(sum1, __riscv_vget_v_f32m2x3_f32m2(src, 1), vl); + sum2 = __riscv_vfadd(sum2, __riscv_vget_v_f32m2x3_f32m2(src, 2), vl); + }; + loadres(row0); + loadres(row1); + loadres(row2); + loadres(row3); + loadres(row4); + if (normalize) + { + sum0 = __riscv_vfdiv(sum0, ksize * ksize, vl); + sum1 = __riscv_vfdiv(sum1, ksize * ksize, vl); + sum2 = __riscv_vfdiv(sum2, ksize * ksize, vl); + } + + vfloat32m2x3_t dst{}; + dst = __riscv_vset_v_f32m2_f32m2x3(dst, 0, sum0); + dst = __riscv_vset_v_f32m2_f32m2x3(dst, 1, sum1); + dst = __riscv_vset_v_f32m2_f32m2x3(dst, 2, sum2); + __riscv_vsseg3e32(reinterpret_cast(dst_data + cur * dst_step) + j * 3, dst, vl); + } + } + } + + return CV_HAL_ERROR_OK; +} + +inline int boxFilter(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, int width, int height, int src_depth, int dst_depth, int cn, int margin_left, int margin_top, int margin_right, int margin_bottom, size_t ksize_width, size_t ksize_height, int anchor_x, int anchor_y, bool normalize, int border_type) +{ + const int src_type = CV_MAKETYPE(src_depth, cn), dst_type = CV_MAKETYPE(dst_depth, cn); + if (ksize_width != ksize_height || (ksize_width != 3 && ksize_width != 5)) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + if (border_type & BORDER_ISOLATED || border_type == BORDER_WRAP) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + + uchar* _dst_data = dst_data; + size_t _dst_step = dst_step; + size_t size = cn; + switch (dst_depth) + { + case CV_8U: + case CV_8S: + size *= sizeof(char); + break; + case CV_16U: + case CV_16S: + size *= sizeof(short); + break; + case CV_32F: + case CV_32S: + size *= sizeof(float); + break; + } + std::vector dst; + if (src_data == _dst_data) + { + dst = std::vector(width * height * size); + dst_data = dst.data(); + dst_step = width * size; + } + + int res = CV_HAL_ERROR_NOT_IMPLEMENTED; + anchor_x = anchor_x < 0 ? ksize_width / 2 : anchor_x; + anchor_y = anchor_y < 0 ? ksize_height / 2 : anchor_y; + if (src_type != dst_type) + { + if (src_type == CV_8UC1 && dst_type == CV_16UC1) + { + if (ksize_width == 3) + { + res = filter::invoke(height, {boxFilterC1<3, RVV_U8M4, RVV_U16M8, false>}, src_data, src_step, dst_data, dst_step, width, margin_left + width + margin_right, margin_top + height + margin_bottom, margin_left, margin_top, anchor_x, anchor_y, normalize, border_type); + } + if (ksize_width == 5) + { + res = filter::invoke(height, {boxFilterC1<5, RVV_U8M4, RVV_U16M8, false>}, src_data, src_step, dst_data, dst_step, width, margin_left + width + margin_right, margin_top + height + margin_bottom, margin_left, margin_top, anchor_x, anchor_y, normalize, border_type); + } + } + } + else + { + switch (ksize_width*100 + src_type) + { + case 300 + CV_8UC1: + res = filter::invoke(height, {boxFilterC1<3, RVV_U8M4, RVV_U16M8, true>}, src_data, src_step, dst_data, dst_step, width, margin_left + width + margin_right, margin_top + height + margin_bottom, margin_left, margin_top, anchor_x, anchor_y, normalize, border_type); + break; + case 500 + CV_8UC1: + res = filter::invoke(height, {boxFilterC1<5, RVV_U8M4, RVV_U16M8, true>}, src_data, src_step, dst_data, dst_step, width, margin_left + width + margin_right, margin_top + height + margin_bottom, margin_left, margin_top, anchor_x, anchor_y, normalize, border_type); + break; + case 300 + CV_16SC1: + res = filter::invoke(height, {boxFilterC1<3, RVV_I16M4, RVV_I32M8, true>}, src_data, src_step, dst_data, dst_step, width, margin_left + width + margin_right, margin_top + height + margin_bottom, margin_left, margin_top, anchor_x, anchor_y, normalize, border_type); + break; + case 500 + CV_16SC1: + res = filter::invoke(height, {boxFilterC1<5, RVV_I16M4, RVV_I32M8, true>}, src_data, src_step, dst_data, dst_step, width, margin_left + width + margin_right, margin_top + height + margin_bottom, margin_left, margin_top, anchor_x, anchor_y, normalize, border_type); + break; + case 300 + CV_32SC1: + res = filter::invoke(height, {boxFilterC1<3, RVV_I32M8, RVV_I32M8, true>}, src_data, src_step, dst_data, dst_step, width, margin_left + width + margin_right, margin_top + height + margin_bottom, margin_left, margin_top, anchor_x, anchor_y, normalize, border_type); + break; + case 500 + CV_32SC1: + res = filter::invoke(height, {boxFilterC1<5, RVV_I32M8, RVV_I32M8, true>}, src_data, src_step, dst_data, dst_step, width, margin_left + width + margin_right, margin_top + height + margin_bottom, margin_left, margin_top, anchor_x, anchor_y, normalize, border_type); + break; + case 300 + CV_32FC1: + res = filter::invoke(height, {boxFilterC1<3, RVV_F32M8, RVV_F32M8, true>}, src_data, src_step, dst_data, dst_step, width, margin_left + width + margin_right, margin_top + height + margin_bottom, margin_left, margin_top, anchor_x, anchor_y, normalize, border_type); + break; + case 500 + CV_32FC1: + res = filter::invoke(height, {boxFilterC1<5, RVV_F32M8, RVV_F32M8, true>}, src_data, src_step, dst_data, dst_step, width, margin_left + width + margin_right, margin_top + height + margin_bottom, margin_left, margin_top, anchor_x, anchor_y, normalize, border_type); + break; + case 300 + CV_32FC3: + res = filter::invoke(height, {boxFilterC3<3>}, src_data, src_step, dst_data, dst_step, width, margin_left + width + margin_right, margin_top + height + margin_bottom, margin_left, margin_top, anchor_x, anchor_y, normalize, border_type); + break; + case 500 + CV_32FC3: + res = filter::invoke(height, {boxFilterC3<5>}, src_data, src_step, dst_data, dst_step, width, margin_left + width + margin_right, margin_top + height + margin_bottom, margin_left, margin_top, anchor_x, anchor_y, normalize, border_type); + break; + } + } + if (res == CV_HAL_ERROR_NOT_IMPLEMENTED) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + + if (src_data == _dst_data) + { + for (int i = 0; i < height; i++) + memcpy(_dst_data + i * _dst_step, dst.data() + i * dst_step, dst_step); + } + + return res; +} +} // cv::cv_hal_rvv::boxFilter + +namespace bilateralFilter { +#undef cv_hal_bilateralFilter +#define cv_hal_bilateralFilter cv::cv_hal_rvv::bilateralFilter::bilateralFilter + +// the algorithm is copied from imgproc/src/bilateral_filter.simd.cpp +// in the functor BilateralFilter_8u_Invoker +static inline int bilateralFilter8UC1(int start, int end, const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, int width, int radius, int maxk, const int* space_ofs, const float* space_weight, const float* color_weight) +{ + constexpr int align = 31; + std::vector _sum(width + align), _wsum(width + align); + float* sum = reinterpret_cast(((size_t)_sum.data() + align) & ~align); + float* wsum = reinterpret_cast(((size_t)_wsum.data() + align) & ~align); + + for (int i = start; i < end; i++) + { + const uchar* sptr = src_data + (i+radius) * src_step + radius; + memset(sum, 0, sizeof(float) * width); + memset(wsum, 0, sizeof(float) * width); + for(int k = 0; k < maxk; k++) + { + const uchar* ksptr = sptr + space_ofs[k]; + int vl; + for (int j = 0; j < width; j += vl) + { + vl = __riscv_vsetvl_e8m2(width - j); + auto src = __riscv_vle8_v_u8m2(sptr + j, vl); + auto ksrc = __riscv_vle8_v_u8m2(ksptr + j, vl); + auto diff = __riscv_vsub(__riscv_vmaxu(src, ksrc, vl), __riscv_vminu(src, ksrc, vl), vl); + auto w = __riscv_vloxei16_v_f32m8(color_weight, __riscv_vmul(__riscv_vzext_vf2(diff, vl), sizeof(float), vl), vl); + w = __riscv_vfmul(w, space_weight[k], vl); + + __riscv_vse32(wsum + j, __riscv_vfadd(w, __riscv_vle32_v_f32m8(wsum + j, vl), vl), vl); + __riscv_vse32(sum + j, __riscv_vfmadd(w, __riscv_vfwcvt_f(__riscv_vzext_vf2(ksrc, vl), vl), __riscv_vle32_v_f32m8(sum + j, vl), vl), vl); + } + } + + int vl; + for (int j = 0; j < width; j += vl) + { + vl = __riscv_vsetvl_e8m2(width - j); + auto dst = __riscv_vfncvt_xu(__riscv_vfdiv(__riscv_vle32_v_f32m8(sum + j, vl), __riscv_vle32_v_f32m8(wsum + j, vl), vl), vl); + __riscv_vse8(dst_data + i * dst_step + j, __riscv_vncvt_x(dst, vl), vl); + } + } + + return CV_HAL_ERROR_OK; +} + +static inline int bilateralFilter8UC3(int start, int end, const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, int width, int radius, int maxk, const int* space_ofs, const float* space_weight, const float* color_weight) +{ + constexpr int align = 31; + std::vector _sum_b(width + align), _sum_g(width + align), _sum_r(width + align), _wsum(width + align); + float* sum_b = reinterpret_cast(((size_t)_sum_b.data() + align) & ~align); + float* sum_g = reinterpret_cast(((size_t)_sum_g.data() + align) & ~align); + float* sum_r = reinterpret_cast(((size_t)_sum_r.data() + align) & ~align); + float* wsum = reinterpret_cast(((size_t)_wsum.data() + align) & ~align); + + for (int i = start; i < end; i++) + { + const uchar* sptr = src_data + (i+radius) * src_step + radius*3; + memset(sum_b, 0, sizeof(float) * width); + memset(sum_g, 0, sizeof(float) * width); + memset(sum_r, 0, sizeof(float) * width); + memset(wsum, 0, sizeof(float) * width); + for(int k = 0; k < maxk; k++) + { + const uchar* ksptr = sptr + space_ofs[k]; + int vl; + for (int j = 0; j < width; j += vl) + { + vl = __riscv_vsetvl_e8m2(width - j); + auto src = __riscv_vlseg3e8_v_u8m2x3(sptr + j * 3, vl); + auto src0 = __riscv_vget_v_u8m2x3_u8m2(src, 0); + auto src1 = __riscv_vget_v_u8m2x3_u8m2(src, 1); + auto src2 = __riscv_vget_v_u8m2x3_u8m2(src, 2); + src = __riscv_vlseg3e8_v_u8m2x3(ksptr + j * 3, vl); + auto ksrc0 = __riscv_vget_v_u8m2x3_u8m2(src, 0); + auto ksrc1 = __riscv_vget_v_u8m2x3_u8m2(src, 1); + auto ksrc2 = __riscv_vget_v_u8m2x3_u8m2(src, 2); + + auto diff0 = __riscv_vsub(__riscv_vmaxu(src0, ksrc0, vl), __riscv_vminu(src0, ksrc0, vl), vl); + auto diff1 = __riscv_vsub(__riscv_vmaxu(src1, ksrc1, vl), __riscv_vminu(src1, ksrc1, vl), vl); + auto diff2 = __riscv_vsub(__riscv_vmaxu(src2, ksrc2, vl), __riscv_vminu(src2, ksrc2, vl), vl); + auto w = __riscv_vloxei16_v_f32m8(color_weight, __riscv_vmul(__riscv_vadd(__riscv_vadd(__riscv_vzext_vf2(diff0, vl), __riscv_vzext_vf2(diff1, vl), vl), __riscv_vzext_vf2(diff2, vl), vl), sizeof(float), vl), vl); + w = __riscv_vfmul(w, space_weight[k], vl); + + __riscv_vse32(wsum + j, __riscv_vfadd(w, __riscv_vle32_v_f32m8(wsum + j, vl), vl), vl); + __riscv_vse32(sum_b + j, __riscv_vfmadd(w, __riscv_vfwcvt_f(__riscv_vzext_vf2(ksrc0, vl), vl), __riscv_vle32_v_f32m8(sum_b + j, vl), vl), vl); + __riscv_vse32(sum_g + j, __riscv_vfmadd(w, __riscv_vfwcvt_f(__riscv_vzext_vf2(ksrc1, vl), vl), __riscv_vle32_v_f32m8(sum_g + j, vl), vl), vl); + __riscv_vse32(sum_r + j, __riscv_vfmadd(w, __riscv_vfwcvt_f(__riscv_vzext_vf2(ksrc2, vl), vl), __riscv_vle32_v_f32m8(sum_r + j, vl), vl), vl); + } + } + + int vl; + for (int j = 0; j < width; j += vl) + { + vl = __riscv_vsetvl_e8m2(width - j); + auto w = __riscv_vfrdiv(__riscv_vle32_v_f32m8(wsum + j, vl), 1.0f, vl); + vuint8m2x3_t dst{}; + dst = __riscv_vset_v_u8m2_u8m2x3(dst, 0,__riscv_vncvt_x(__riscv_vfncvt_xu(__riscv_vfmul(__riscv_vle32_v_f32m8(sum_b + j, vl), w, vl), vl), vl)); + dst = __riscv_vset_v_u8m2_u8m2x3(dst, 1,__riscv_vncvt_x(__riscv_vfncvt_xu(__riscv_vfmul(__riscv_vle32_v_f32m8(sum_g + j, vl), w, vl), vl), vl)); + dst = __riscv_vset_v_u8m2_u8m2x3(dst, 2,__riscv_vncvt_x(__riscv_vfncvt_xu(__riscv_vfmul(__riscv_vle32_v_f32m8(sum_r + j, vl), w, vl), vl), vl)); + __riscv_vsseg3e8(dst_data + i * dst_step + j * 3, dst, vl); + } + } + + return CV_HAL_ERROR_OK; +} + +// the algorithm is copied from imgproc/src/bilateral_filter.simd.cpp +// in the functor BilateralFilter_32f_Invoker +static inline int bilateralFilter32FC1(int start, int end, const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, int width, int radius, int maxk, const int* space_ofs, const float* space_weight, const float* expLUT, float scale_index) +{ + constexpr int align = 31; + std::vector _sum(width + align), _wsum(width + align); + float* sum = reinterpret_cast(((size_t)_sum.data() + align) & ~align); + float* wsum = reinterpret_cast(((size_t)_wsum.data() + align) & ~align); + + for (int i = start; i < end; i++) + { + const float* sptr = reinterpret_cast(src_data + (i+radius) * src_step) + radius; + memset(sum, 0, sizeof(float) * width); + memset(wsum, 0, sizeof(float) * width); + for(int k = 0; k < maxk; k++) + { + const float* ksptr = sptr + space_ofs[k]; + int vl; + for (int j = 0; j < width; j += vl) + { + vl = __riscv_vsetvl_e32m4(width - j); + auto src = __riscv_vle32_v_f32m4(sptr + j, vl); + auto ksrc = __riscv_vle32_v_f32m4(ksptr + j, vl); + auto diff = __riscv_vfmul(__riscv_vfabs(__riscv_vfsub(src, ksrc, vl), vl), scale_index, vl); + auto idx = __riscv_vfcvt_rtz_x(diff, vl); + auto alpha = __riscv_vfsub(diff, __riscv_vfcvt_f(idx, vl), vl); + + auto exp = __riscv_vloxseg2ei32_v_f32m4x2(expLUT, __riscv_vreinterpret_v_i32m4_u32m4(__riscv_vmul(idx, sizeof(float), vl)), vl); + auto w = __riscv_vfmadd(alpha, __riscv_vfsub(__riscv_vget_v_f32m4x2_f32m4(exp, 1), __riscv_vget_v_f32m4x2_f32m4(exp, 0), vl), __riscv_vget_v_f32m4x2_f32m4(exp, 0), vl); + w = __riscv_vfmul(w, space_weight[k], vl); + + __riscv_vse32(wsum + j, __riscv_vfadd(w, __riscv_vle32_v_f32m4(wsum + j, vl), vl), vl); + __riscv_vse32(sum + j, __riscv_vfmadd(w, ksrc, __riscv_vle32_v_f32m4(sum + j, vl), vl), vl); + } + } + + int vl; + for (int j = 0; j < width; j += vl) + { + vl = __riscv_vsetvl_e32m4(width - j); + auto src = __riscv_vle32_v_f32m4(sptr + j, vl); + auto dst = __riscv_vfdiv(__riscv_vfadd(__riscv_vle32_v_f32m4(sum + j, vl), src, vl), __riscv_vfadd(__riscv_vle32_v_f32m4(wsum + j, vl), 1, vl), vl); + __riscv_vse32(reinterpret_cast(dst_data + i * dst_step) + j, dst, vl); + } + } + + return CV_HAL_ERROR_OK; +} + +static inline int bilateralFilter32FC3(int start, int end, const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, int width, int radius, int maxk, const int* space_ofs, const float* space_weight, const float* expLUT, float scale_index) +{ + constexpr int align = 31; + std::vector _sum_b(width + align), _sum_g(width + align), _sum_r(width + align), _wsum(width + align); + float* sum_b = reinterpret_cast(((size_t)_sum_b.data() + align) & ~align); + float* sum_g = reinterpret_cast(((size_t)_sum_g.data() + align) & ~align); + float* sum_r = reinterpret_cast(((size_t)_sum_r.data() + align) & ~align); + float* wsum = reinterpret_cast(((size_t)_wsum.data() + align) & ~align); + + for (int i = start; i < end; i++) + { + const float* sptr = reinterpret_cast(src_data + (i+radius) * src_step) + radius*3; + memset(sum_b, 0, sizeof(float) * width); + memset(sum_g, 0, sizeof(float) * width); + memset(sum_r, 0, sizeof(float) * width); + memset(wsum, 0, sizeof(float) * width); + for(int k = 0; k < maxk; k++) + { + const float* ksptr = sptr + space_ofs[k]; + int vl; + for (int j = 0; j < width; j += vl) + { + vl = __riscv_vsetvl_e32m2(width - j); + auto src = __riscv_vlseg3e32_v_f32m2x3(sptr + j * 3, vl); + auto src0 = __riscv_vget_v_f32m2x3_f32m2(src, 0); + auto src1 = __riscv_vget_v_f32m2x3_f32m2(src, 1); + auto src2 = __riscv_vget_v_f32m2x3_f32m2(src, 2); + src = __riscv_vlseg3e32_v_f32m2x3(ksptr + j * 3, vl); + auto ksrc0 = __riscv_vget_v_f32m2x3_f32m2(src, 0); + auto ksrc1 = __riscv_vget_v_f32m2x3_f32m2(src, 1); + auto ksrc2 = __riscv_vget_v_f32m2x3_f32m2(src, 2); + + auto diff = __riscv_vfmul(__riscv_vfadd(__riscv_vfadd(__riscv_vfabs(__riscv_vfsub(src0, ksrc0, vl), vl), __riscv_vfabs(__riscv_vfsub(src1, ksrc1, vl), vl), vl), __riscv_vfabs(__riscv_vfsub(src2, ksrc2, vl), vl), vl), scale_index, vl); + auto idx = __riscv_vfcvt_rtz_x(diff, vl); + auto alpha = __riscv_vfsub(diff, __riscv_vfcvt_f(idx, vl), vl); + + auto exp = __riscv_vloxseg2ei32_v_f32m2x2(expLUT, __riscv_vreinterpret_v_i32m2_u32m2(__riscv_vmul(idx, sizeof(float), vl)), vl); + auto w = __riscv_vfmadd(alpha, __riscv_vfsub(__riscv_vget_v_f32m2x2_f32m2(exp, 1), __riscv_vget_v_f32m2x2_f32m2(exp, 0), vl), __riscv_vget_v_f32m2x2_f32m2(exp, 0), vl); + w = __riscv_vfmul(w, space_weight[k], vl); + + __riscv_vse32(wsum + j, __riscv_vfadd(w, __riscv_vle32_v_f32m2(wsum + j, vl), vl), vl); + __riscv_vse32(sum_b + j, __riscv_vfmadd(w, ksrc0, __riscv_vle32_v_f32m2(sum_b + j, vl), vl), vl); + __riscv_vse32(sum_g + j, __riscv_vfmadd(w, ksrc1, __riscv_vle32_v_f32m2(sum_g + j, vl), vl), vl); + __riscv_vse32(sum_r + j, __riscv_vfmadd(w, ksrc2, __riscv_vle32_v_f32m2(sum_r + j, vl), vl), vl); + } + } + + int vl; + for (int j = 0; j < width; j += vl) + { + vl = __riscv_vsetvl_e32m2(width - j); + auto w = __riscv_vfrdiv(__riscv_vfadd(__riscv_vle32_v_f32m2(wsum + j, vl), 1, vl), 1, vl); + auto src = __riscv_vlseg3e32_v_f32m2x3(sptr + j * 3, vl); + auto src0 = __riscv_vget_v_f32m2x3_f32m2(src, 0); + auto src1 = __riscv_vget_v_f32m2x3_f32m2(src, 1); + auto src2 = __riscv_vget_v_f32m2x3_f32m2(src, 2); + + vfloat32m2x3_t dst{}; + dst = __riscv_vset_v_f32m2_f32m2x3(dst, 0, __riscv_vfmul(w, __riscv_vfadd(__riscv_vle32_v_f32m2(sum_b + j, vl), src0, vl), vl)); + dst = __riscv_vset_v_f32m2_f32m2x3(dst, 1, __riscv_vfmul(w, __riscv_vfadd(__riscv_vle32_v_f32m2(sum_g + j, vl), src1, vl), vl)); + dst = __riscv_vset_v_f32m2_f32m2x3(dst, 2, __riscv_vfmul(w, __riscv_vfadd(__riscv_vle32_v_f32m2(sum_r + j, vl), src2, vl), vl)); + __riscv_vsseg3e32(reinterpret_cast(dst_data + i * dst_step) + j * 3, dst, vl); + } + } + + return CV_HAL_ERROR_OK; +} + +// the algorithm is copied from imgproc/src/bilateral_filter.dispatch.cpp +// in the function static void bilateralFilter_8u and bilateralFilter_32f +inline int bilateralFilter(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, + int width, int height, int depth, int cn, int d, double sigma_color, double sigma_space, int border_type) +{ + const int type = CV_MAKETYPE(depth, cn); + if (type != CV_8UC1 && type != CV_8UC3 && type != CV_32FC1 && type != CV_32FC3) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + if (type == CV_32FC1 && width * height > 1 << 20) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + if (src_data == dst_data || border_type & BORDER_ISOLATED) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + + sigma_color = sigma_color <= 0 ? 1 : sigma_color; + sigma_space = sigma_space <= 0 ? 1 : sigma_space; + double gauss_color_coeff = -0.5/(sigma_color*sigma_color); + double gauss_space_coeff = -0.5/(sigma_space*sigma_space); + int radius = d <= 0 ? std::round(sigma_space*1.5) : d/2; + radius = std::max(radius, 1); + d = radius*2 + 1; + + const int size = depth == CV_32F ? cn * sizeof(float) : cn; + const int temp_step = (width + radius * 2) * size; + std::vector _temp((width + radius * 2) * (height + radius * 2) * size, 0); + uchar* temp = _temp.data(); + std::vector width_interpolate(radius * 2); + for (int j = 0; j < radius; j++) + { + width_interpolate[j] = filter::borderInterpolate(j - radius, width, border_type); + width_interpolate[j + radius] = filter::borderInterpolate(width + j, width, border_type); + } + for (int i = 0; i < height + radius * 2; i++) + { + int x = filter::borderInterpolate(i - radius, height, border_type); + if (x != -1) + { + for (int j = 0; j < radius; j++) + { + int y = width_interpolate[j]; + if (y != -1) + memcpy(temp + i * temp_step + j * size, src_data + x * src_step + y * size, size); + y = width_interpolate[j + radius]; + if (y != -1) + memcpy(temp + i * temp_step + (width + j + radius) * size, src_data + x * src_step + y * size, size); + } + memcpy(temp + i * temp_step + radius * size, src_data + x * src_step, width * size); + } + } + + std::vector _space_weight(d*d); + std::vector _space_ofs(d*d); + float* space_weight = _space_weight.data(); + int* space_ofs = _space_ofs.data(); + int maxk = 0; + for (int i = -radius; i <= radius; i++) + { + for (int j = -radius; j <= radius; j++) + { + double r = std::sqrt((double)i*i + (double)j*j); + if (r <= radius && (depth == CV_8U || i != 0 || j != 0)) + { + space_weight[maxk] = static_cast(r*r*gauss_space_coeff); + space_ofs[maxk++] = (i * (temp_step / size) + j) * cn; + } + } + } + cv::cv_hal_rvv::exp32f(space_weight, space_weight, maxk); + + if (depth == CV_8U) + { + std::vector _color_weight(cn*256); + float* color_weight = _color_weight.data(); + for (int i = 0; i < 256*cn; i++) + color_weight[i] = static_cast(i*i*gauss_color_coeff); + cv::cv_hal_rvv::exp32f(color_weight, color_weight, 256*cn); + + switch (cn) + { + case 1: + return filter::invoke(height, {bilateralFilter8UC1}, temp, temp_step, dst_data, dst_step, width, radius, maxk, space_ofs, space_weight, color_weight); + case 3: + return filter::invoke(height, {bilateralFilter8UC3}, temp, temp_step, dst_data, dst_step, width, radius, maxk, space_ofs, space_weight, color_weight); + } + } + else + { + double minValSrc = -1, maxValSrc = 1; + cv::cv_hal_rvv::minmax::minMaxIdx(src_data, src_step, width * cn, height, CV_32F, &minValSrc, &maxValSrc, nullptr, nullptr, nullptr); + if(std::abs(minValSrc - maxValSrc) < FLT_EPSILON) + { + for (int i = 0; i < width; i++) + memcpy(dst_data + i * dst_step, src_data + i * src_step, width * size); + return CV_HAL_ERROR_OK; + } + + const int kExpNumBinsPerChannel = 1 << 12; + const int kExpNumBins = kExpNumBinsPerChannel * cn; + const float scale_index = kExpNumBins / static_cast((maxValSrc - minValSrc) * cn); + std::vector _expLUT(kExpNumBins+2, 0); + float* expLUT = _expLUT.data(); + for (int i = 0; i < kExpNumBins+2; i++) + { + double val = i / scale_index; + expLUT[i] = static_cast(val * val * gauss_color_coeff); + } + cv::cv_hal_rvv::exp32f(expLUT, expLUT, kExpNumBins+2); + + switch (cn) + { + case 1: + return filter::invoke(height, {bilateralFilter32FC1}, temp, temp_step, dst_data, dst_step, width, radius, maxk, space_ofs, space_weight, expLUT, scale_index); + case 3: + return filter::invoke(height, {bilateralFilter32FC3}, temp, temp_step, dst_data, dst_step, width, radius, maxk, space_ofs, space_weight, expLUT, scale_index); + } + } + + return CV_HAL_ERROR_NOT_IMPLEMENTED; +} +} // cv::cv_hal_rvv::bilateralFilter + }} #endif diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/types.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/types.hpp index 05c1e84951..79db847eb5 100644 --- a/3rdparty/hal_rvv/hal_rvv_1p0/types.hpp +++ b/3rdparty/hal_rvv/hal_rvv_1p0/types.hpp @@ -209,6 +209,13 @@ static inline VecType vmul(VecType vs2, VecType vs1, size_t vl) { return __riscv_v##IS_F##mul(vs2, vs1, vl); \ } \ \ +static inline VecType vslide1down(VecType vs2, ElemType vs1, size_t vl) { \ + return __riscv_v##IS_F##slide1down(vs2, vs1, vl); \ +} \ +static inline VecType vslide1up(VecType vs2, ElemType vs1, size_t vl) { \ + return __riscv_v##IS_F##slide1up(vs2, vs1, vl); \ +} \ + \ static inline VecType vmin(VecType vs2, VecType vs1, size_t vl) { \ return __riscv_v##IS_F##min##IS_U(vs2, vs1, vl); \ } \ From c10b80083719d5862ce4e37572b43803b94fe86b Mon Sep 17 00:00:00 2001 From: Vincent Rabaud Date: Thu, 20 Mar 2025 15:31:47 +0100 Subject: [PATCH 29/94] Merge pull request #27081 from vrabaud:lzw GIF: Make sure to resize lzwExtraTable before each block #27081 This fixes https://g-issues.oss-fuzz.com/issues/403364362 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- modules/imgcodecs/src/grfmt_gif.cpp | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/modules/imgcodecs/src/grfmt_gif.cpp b/modules/imgcodecs/src/grfmt_gif.cpp index 3a70dcc568..74418fe6e3 100644 --- a/modules/imgcodecs/src/grfmt_gif.cpp +++ b/modules/imgcodecs/src/grfmt_gif.cpp @@ -320,11 +320,18 @@ bool GifDecoder::lzwDecode() { const int lzwMaxSize = (1 << 12); // 4096 is the maximum size of the LZW table (12 bits) int lzwCodeSize = lzwMinCodeSize + 1; CV_Assert(lzwCodeSize > 2 && lzwCodeSize <= 12); - int clearCode = 1 << lzwMinCodeSize; - int exitCode = clearCode + 1; + const int clearCode = 1 << lzwMinCodeSize; + const int exitCode = clearCode + 1; std::vector lzwExtraTable(lzwMaxSize + 1); - int colorTableSize = clearCode; + const int colorTableSize = clearCode; int lzwTableSize = exitCode; + auto clear = [&]() { + lzwExtraTable.clear(); + lzwExtraTable.resize(lzwMaxSize + 1); + // reset the code size, the same as that in the initialization part + lzwCodeSize = lzwMinCodeSize + 1; + lzwTableSize = exitCode; + }; idx = 0; int leftBits = 0; @@ -345,18 +352,12 @@ bool GifDecoder::lzwDecode() { // clear code if (!(code ^ clearCode)) { - lzwExtraTable.clear(); - lzwExtraTable.resize(lzwMaxSize + 1); - // reset the code size, the same as that in the initialization part - lzwCodeSize = lzwMinCodeSize + 1; - lzwTableSize = exitCode; + clear(); continue; } // end of information if (!(code ^ exitCode)) { - lzwExtraTable.clear(); - lzwCodeSize = lzwMinCodeSize + 1; - lzwTableSize = exitCode; + clear(); break; } From 2e9345570f3f5b81b6ebc095a0a9803f57f24c3c Mon Sep 17 00:00:00 2001 From: Scorpion1234567 <1971208858@qq.com> Date: Thu, 20 Mar 2025 22:46:18 +0800 Subject: [PATCH 30/94] Merge pull request #27108 from Scorpion1234567:Multithreading-wrapPolar When WARP_INVERSE_MAP is used, accelerate the calculation with multi-threading #27108 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [ ] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- modules/imgproc/src/imgwarp.cpp | 47 ++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/modules/imgproc/src/imgwarp.cpp b/modules/imgproc/src/imgwarp.cpp index d512bbf887..38f61333cf 100644 --- a/modules/imgproc/src/imgwarp.cpp +++ b/modules/imgproc/src/imgwarp.cpp @@ -3737,7 +3737,6 @@ void cv::warpPolar(InputArray _src, OutputArray _dst, Size dsize, else Kmag = maxRadius / ssize.width; - int x, y; Mat bufx, bufy, bufp, bufa; bufx = Mat(1, dsize.width, CV_32F); @@ -3745,33 +3744,39 @@ void cv::warpPolar(InputArray _src, OutputArray _dst, Size dsize, bufp = Mat(1, dsize.width, CV_32F); bufa = Mat(1, dsize.width, CV_32F); - for (x = 0; x < dsize.width; x++) + for (int x = 0; x < dsize.width; x++) bufx.at(0, x) = (float)x - center.x; - for (y = 0; y < dsize.height; y++) - { - float* mx = (float*)(mapx.data + y*mapx.step); - float* my = (float*)(mapy.data + y*mapy.step); + cv::parallel_for_(cv::Range(0, dsize.height), [&](const cv::Range& range) { + for (int y = range.start; y < range.end; ++y) { + Mat local_bufx = bufx.clone(); + Mat local_bufy = Mat(1, dsize.width, CV_32F); + Mat local_bufp = Mat(1, dsize.width, CV_32F); + Mat local_bufa = Mat(1, dsize.width, CV_32F); - for (x = 0; x < dsize.width; x++) - bufy.at(0, x) = (float)y - center.y; + for (int x = 0; x < dsize.width; x++) { + local_bufy.at(0, x) = static_cast(y) - center.y; + } - cartToPolar(bufx, bufy, bufp, bufa, 0); + cartToPolar(local_bufx, local_bufy, local_bufp, local_bufa, false); - if (semiLog) - { - bufp += 1.f; - log(bufp, bufp); + if (semiLog) { + local_bufp += 1.f; + log(local_bufp, local_bufp); + } + + float* mx = (float*)(mapx.data + y * mapx.step); + float* my = (float*)(mapy.data + y * mapy.step); + + for (int x = 0; x < dsize.width; x++) { + double rho = local_bufp.at(0, x) / Kmag; + double phi = local_bufa.at(0, x) / Kangle; + mx[x] = static_cast(rho); + my[x] = static_cast(phi) + ANGLE_BORDER; + } } + }); - for (x = 0; x < dsize.width; x++) - { - double rho = bufp.at(0, x) / Kmag; - double phi = bufa.at(0, x) / Kangle; - mx[x] = (float)rho; - my[x] = (float)phi + ANGLE_BORDER; - } - } remap(src, _dst, mapx, mapy, flags & cv::INTER_MAX, (flags & cv::WARP_FILL_OUTLIERS) ? cv::BORDER_CONSTANT : cv::BORDER_TRANSPARENT); } From 4e488a0b161a503de678bf0e47062984e8060785 Mon Sep 17 00:00:00 2001 From: Ali Saleem <86358454+CyberWarrior5466@users.noreply.github.com> Date: Thu, 20 Mar 2025 19:45:58 +0000 Subject: [PATCH 31/94] Update old URL --- .../js_imgproc/js_watershed/js_watershed.markdown | 2 +- .../py_imgproc/py_watershed/py_watershed.markdown | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/js_tutorials/js_imgproc/js_watershed/js_watershed.markdown b/doc/js_tutorials/js_imgproc/js_watershed/js_watershed.markdown index 1554744052..59421dd24a 100644 --- a/doc/js_tutorials/js_imgproc/js_watershed/js_watershed.markdown +++ b/doc/js_tutorials/js_imgproc/js_watershed/js_watershed.markdown @@ -17,7 +17,7 @@ nearby, water from different valleys, obviously with different colors will start that, you build barriers in the locations where water merges. You continue the work of filling water and building barriers until all the peaks are under water. Then the barriers you created gives you the segmentation result. This is the "philosophy" behind the watershed. You can visit the [CMM -webpage on watershed](http://cmm.ensmp.fr/~beucher/wtshed.html) to understand it with the help of +webpage on watershed](https://people.cmm.minesparis.psl.eu/users/beucher/wtshed.html) to understand it with the help of some animations. But this approach gives you oversegmented result due to noise or any other irregularities in the diff --git a/doc/py_tutorials/py_imgproc/py_watershed/py_watershed.markdown b/doc/py_tutorials/py_imgproc/py_watershed/py_watershed.markdown index 9536bf3e30..63410095e6 100644 --- a/doc/py_tutorials/py_imgproc/py_watershed/py_watershed.markdown +++ b/doc/py_tutorials/py_imgproc/py_watershed/py_watershed.markdown @@ -18,7 +18,7 @@ nearby, water from different valleys, obviously with different colors will start that, you build barriers in the locations where water merges. You continue the work of filling water and building barriers until all the peaks are under water. Then the barriers you created gives you the segmentation result. This is the "philosophy" behind the watershed. You can visit the [CMM -webpage on watershed](http://cmm.ensmp.fr/~beucher/wtshed.html) to understand it with the help of +webpage on watershed](https://people.cmm.minesparis.psl.eu/users/beucher/wtshed.html) to understand it with the help of some animations. But this approach gives you oversegmented result due to noise or any other irregularities in the @@ -140,7 +140,7 @@ some, they are not. Additional Resources -------------------- --# CMM page on [Watershed Transformation](http://cmm.ensmp.fr/~beucher/wtshed.html) +-# CMM page on [Watershed Transformation](https://people.cmm.minesparis.psl.eu/users/beucher/wtshed.html) Exercises --------- From 46bd22abad28cb74d6ea9702dccd246a22a50fa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=A9=E9=9F=B3=E3=81=82=E3=82=81?= Date: Fri, 21 Mar 2025 15:18:51 +0800 Subject: [PATCH 32/94] Fix RISC-V HAL solve:SVD and BGRtoLab (#27046) Fix RISC-V HAL solve/SVD and BGRtoLab #27046 Closes #27044. Also suppressed some warnings in other HAL. ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [ ] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- 3rdparty/hal_rvv/CMakeLists.txt | 2 +- 3rdparty/hal_rvv/hal_rvv.hpp | 1 + 3rdparty/hal_rvv/hal_rvv_1p0/color.hpp | 185 +++++++++++++++++------- 3rdparty/hal_rvv/hal_rvv_1p0/filter.hpp | 1 - 3rdparty/hal_rvv/hal_rvv_1p0/lut.hpp | 16 +- 3rdparty/hal_rvv/hal_rvv_1p0/merge.hpp | 4 +- 3rdparty/hal_rvv/hal_rvv_1p0/norm.hpp | 2 +- 3rdparty/hal_rvv/hal_rvv_1p0/sqrt.hpp | 4 + 3rdparty/hal_rvv/hal_rvv_1p0/svd.hpp | 4 +- 3rdparty/hal_rvv/hal_rvv_1p0/thresh.hpp | 2 +- 10 files changed, 152 insertions(+), 69 deletions(-) diff --git a/3rdparty/hal_rvv/CMakeLists.txt b/3rdparty/hal_rvv/CMakeLists.txt index 814af987d9..8c19800053 100644 --- a/3rdparty/hal_rvv/CMakeLists.txt +++ b/3rdparty/hal_rvv/CMakeLists.txt @@ -6,4 +6,4 @@ set(RVV_HAL_FOUND TRUE CACHE INTERNAL "") set(RVV_HAL_VERSION "0.0.1" CACHE INTERNAL "") set(RVV_HAL_LIBRARIES ${HAL_LIB_NAME} CACHE INTERNAL "") set(RVV_HAL_HEADERS "hal_rvv.hpp" CACHE INTERNAL "") -set(RVV_HAL_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}" CACHE INTERNAL "") +set(RVV_HAL_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_SOURCE_DIR}/modules/imgproc/include" CACHE INTERNAL "") diff --git a/3rdparty/hal_rvv/hal_rvv.hpp b/3rdparty/hal_rvv/hal_rvv.hpp index 8b15f8cd18..a0c1d0e52f 100644 --- a/3rdparty/hal_rvv/hal_rvv.hpp +++ b/3rdparty/hal_rvv/hal_rvv.hpp @@ -6,6 +6,7 @@ #define OPENCV_HAL_RVV_HPP_INCLUDED #include "opencv2/core/hal/interface.h" +#include "opencv2/imgproc/hal/interface.h" #ifndef CV_HAL_RVV_071_ENABLED # if defined(__GNUC__) && __GNUC__ == 10 && __GNUC_MINOR__ == 4 && defined(__THEAD_VERSION__) && defined(__riscv_v) && __riscv_v == 7000 diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/color.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/color.hpp index db86c56723..c715c6ad38 100644 --- a/3rdparty/hal_rvv/hal_rvv_1p0/color.hpp +++ b/3rdparty/hal_rvv/hal_rvv_1p0/color.hpp @@ -36,6 +36,11 @@ namespace color { cv::parallel_for_(Range(1, height), ColorInvoker(func, std::forward(args)...), (width - 1) * height / static_cast(1 << 15)); return func(0, 1, std::forward(args)...); } + + template T rint(T val) + { + return val - std::remainder(val, 1.0); + } } // cv::cv_hal_rvv::color namespace BGRtoBGR { @@ -2246,7 +2251,7 @@ namespace LabTable for (int i = 0; i < 3072; i++) { float x = i * 1.0f / (255*8); - LabCbrtTab_b[i] = (ushort)std::rint((1 << 15) * applyCbrt(x)); + LabCbrtTab_b[i] = (ushort)color::rint((1 << 15) * applyCbrt(x)); } // tweak to imitate the error of cv::softfloat, or bitExactness tests won't pass LabCbrtTab_b[324] -= 1, LabCbrtTab_b[2079] -= 1; @@ -2254,12 +2259,12 @@ namespace LabTable for (int i = 0; i < 256; i++) { float x = i / 255.0f; - sRGBGammaTab_b[i] = (ushort)std::rint(2040 * applyGamma(x)); + sRGBGammaTab_b[i] = (ushort)color::rint(2040 * applyGamma(x)); } for (int i = 0; i < INV_GAMMA_TAB_SIZE; i++) { float x = i * 1.0f / INV_GAMMA_TAB_SIZE; - sRGBInvGammaTab_b[i] = (ushort)std::rint(255 * applyInvGamma(x)); + sRGBInvGammaTab_b[i] = (ushort)color::rint(255 * applyInvGamma(x)); } for (int i = 0; i < 256; i++) @@ -2275,8 +2280,8 @@ namespace LabTable fy = (li + 16.0f) / 116.0f; yy = fy * fy * fy; } - LabToYF_b[i*2 ] = (short)std::rint(yy * LAB_BASE); - LabToYF_b[i*2+1] = (short)std::rint(fy * LAB_BASE); + LabToYF_b[i*2 ] = (short)color::rint(yy * LAB_BASE); + LabToYF_b[i*2+1] = (short)color::rint(fy * LAB_BASE); } for (int LL = 0; LL < 256; LL++) @@ -2286,7 +2291,7 @@ namespace LabTable { float u = uu*354.0f/255 - 134; float up = 9.0f*(u + L*2.5719122887f); - LuToUp_b[LL*256+uu] = (int)std::rint(up*float(LAB_BASE/1024)); + LuToUp_b[LL*256+uu] = (int)color::rint(up*float(LAB_BASE/1024)); } for (int vv = 0; vv < 256; vv++) { @@ -2294,7 +2299,7 @@ namespace LabTable float vp = 0.25f/(v + L*6.0884485245f); if (vp > 0.25f) vp = 0.25f; if (vp < -0.25f) vp = -0.25f; - LvToVp_b[LL*256+vv] = (int)std::rint(vp*float(LAB_BASE*1024)); + LvToVp_b[LL*256+vv] = (int)color::rint(vp*float(LAB_BASE*1024)); } } // tweak @@ -2307,13 +2312,19 @@ namespace LabTable LvToVp_b[error1[i]] += error1[i + 1]; #endif + static constexpr float BGR2XYZ[] = + { + 0.180423f / 0.950456f, 0.357580f / 0.950456f, 0.412453f / 0.950456f, + 0.072169f , 0.715160f , 0.212671f , + 0.950227f / 1.088754f, 0.119193f / 1.088754f, 0.019334f / 1.088754f + }; static constexpr float BGR2XYZ_D65[] = { 0.180423f, 0.357580f, 0.412453f, 0.072169f, 0.715160f, 0.212671f, 0.950227f, 0.119193f, 0.019334f }; - short RGB2Luvprev[LAB_LUT_DIM*LAB_LUT_DIM*LAB_LUT_DIM*3]; + short RGB2Labprev[LAB_LUT_DIM*LAB_LUT_DIM*LAB_LUT_DIM*3], RGB2Luvprev[LAB_LUT_DIM*LAB_LUT_DIM*LAB_LUT_DIM*3]; for (int p = 0; p < LAB_LUT_DIM; p++) { for (int q = 0; q < LAB_LUT_DIM; q++) @@ -2325,28 +2336,51 @@ namespace LabTable float G = applyGamma(q / 32.0f); float B = applyGamma(r / 32.0f); - float X = R*BGR2XYZ_D65[0] + G*BGR2XYZ_D65[1] + B*BGR2XYZ_D65[2]; - float Y = R*BGR2XYZ_D65[3] + G*BGR2XYZ_D65[4] + B*BGR2XYZ_D65[5]; - float Z = R*BGR2XYZ_D65[6] + G*BGR2XYZ_D65[7] + B*BGR2XYZ_D65[8]; + { + float X = R*BGR2XYZ[0] + G*BGR2XYZ[1] + B*BGR2XYZ[2]; + float Y = R*BGR2XYZ[3] + G*BGR2XYZ[4] + B*BGR2XYZ[5]; + float Z = R*BGR2XYZ[6] + G*BGR2XYZ[7] + B*BGR2XYZ[8]; - float L = applyCbrt(Y); - L = L*116.0f - 16.0f; + float FX = applyCbrt(X); + float FY = applyCbrt(Y); + float FZ = applyCbrt(Z); - float d = 52.0f/std::max(X + 15.0f * Y + 3.0f * Z, FLT_EPSILON); - float u = L*(X*d - 2.5719122887f); - float v = L*(2.25f*Y*d - 6.0884485245f); + float L = Y > 0.008856f ? (116.0f*FY - 16.0f) : (903.3f*Y); + float a = 500.0f * (FX - FY); + float b = 200.0f * (FY - FZ); - RGB2Luvprev[idx ] = (short)std::rint(LAB_BASE*L/100.0f); - RGB2Luvprev[idx+1] = (short)std::rint(LAB_BASE*(u+134.0f)/354.0f); - RGB2Luvprev[idx+2] = (short)std::rint(LAB_BASE*(v+140.0f)/262.0f); + RGB2Labprev[idx] = (short)(color::rint(LAB_BASE*L/100.0f)); + RGB2Labprev[idx+1] = (short)(color::rint(LAB_BASE*(a+128.0f)/256.0f)); + RGB2Labprev[idx+2] = (short)(color::rint(LAB_BASE*(b+128.0f)/256.0f)); + } + { + float X = R*BGR2XYZ_D65[0] + G*BGR2XYZ_D65[1] + B*BGR2XYZ_D65[2]; + float Y = R*BGR2XYZ_D65[3] + G*BGR2XYZ_D65[4] + B*BGR2XYZ_D65[5]; + float Z = R*BGR2XYZ_D65[6] + G*BGR2XYZ_D65[7] + B*BGR2XYZ_D65[8]; + + float L = applyCbrt(Y); + L = L*116.0f - 16.0f; + + float d = 52.0f/std::max(X + 15.0f * Y + 3.0f * Z, FLT_EPSILON); + float u = L*(X*d - 2.5719122887f); + float v = L*(2.25f*Y*d - 6.0884485245f); + + RGB2Luvprev[idx ] = (short)color::rint(LAB_BASE*L/100.0f); + RGB2Luvprev[idx+1] = (short)color::rint(LAB_BASE*(u+134.0f)/354.0f); + RGB2Luvprev[idx+2] = (short)color::rint(LAB_BASE*(v+140.0f)/262.0f); + } } } } // tweak - static constexpr int error2[] = {32,-1,5246,-1,6662,-1,7837,1,8625,-1,11969,1,15290,1,19142,1,19588,1,21707,-1,22731,-1,24291,-1,25922,-1,27402,-1,28485,-1,29878,-1,32405,-1,36227,1,38265,-1,38296,1,38403,-1,41795,1,41867,1,43796,1,48096,-1,50562,-1,51054,-1,54496,1,55328,-1,56973,-1,58594,1,61568,1,66512,-1,68543,-1,68615,1,70105,-1,70692,-1,74924,1,76336,-1,78781,1,79259,-1,80855,1,81662,1,82290,-1,83208,-1,84370,1,86293,1,87263,-1,87939,-1,89942,-1,90258,-1,92101,-1,92325,-1,95244,-1,97556,1,97758,-1,97769,1,98455,1,104087,-1,106997,-1}; + static constexpr int error2[] = {37,-1,124,-1,503,-1,4150,1,5548,1,6544,1,6659,1,8625,-1,11704,1,16108,-1,16347,-1,16446,-1,18148,1,19624,-1,22731,-1,23479,1,24001,1,24291,-1,25199,-1,25352,-1,27402,-1,28485,-1,29788,1,29807,-1,32149,-1,33451,-1,33974,-1,38265,-1,38403,-1,41038,-1,41279,1,41824,-1,42856,-1,48096,-1,49201,-1,50562,-1,51054,-1,51550,-1,51821,1,56973,-1,57283,1,62335,-1,67867,-1,70692,-1,71194,-1,71662,1,71815,1,72316,-1,73487,1,75722,-1,75959,1,82290,-1,82868,-1,83208,-1,83534,-1,84217,-1,85793,1,86683,-1,87939,-1,89143,1,90258,-1,91432,-1,92302,1,92325,-1,92572,1,93143,-1,93731,-1,94142,-1,95244,-1,96025,-1,96950,-1,97758,-1,102409,-1,104165,-1}; + static constexpr int error3[] = {32,-1,5246,-1,6662,-1,7837,1,8625,-1,11969,1,15290,1,19142,1,19588,1,21707,-1,22731,-1,24291,-1,25922,-1,27402,-1,28485,-1,29878,-1,32405,-1,36227,1,38265,-1,38296,1,38403,-1,41795,1,41867,1,43796,1,48096,-1,50562,-1,51054,-1,54496,1,55328,-1,56973,-1,58594,1,61568,1,66512,-1,68543,-1,68615,1,70105,-1,70692,-1,74924,1,76336,-1,78781,1,79259,-1,80855,1,81662,1,82290,-1,83208,-1,84370,1,86293,1,87263,-1,87939,-1,89942,-1,90258,-1,92101,-1,92325,-1,95244,-1,97556,1,97758,-1,97769,1,98455,1,104087,-1,106997,-1}; for (size_t i = 0; i < sizeof(error2) / sizeof(int); i += 2) - RGB2Luvprev[error2[i]] += error2[i + 1]; + RGB2Labprev[error2[i]] += error2[i + 1]; + for (size_t i = 0; i < sizeof(error3) / sizeof(int); i += 2) + RGB2Luvprev[error3[i]] += error3[i + 1]; #ifdef __clang__ + RGB2Labprev[9704] -= 1, RGB2Labprev[41279] -= 1, RGB2Labprev[71194] += 1, RGB2Labprev[73487] -= 1, RGB2Labprev[85793] -= 1; RGB2Luvprev[36227] -= 1, RGB2Luvprev[38587] += 1; #endif for (int p = 0; p < LAB_LUT_DIM; p++) @@ -2360,6 +2394,9 @@ namespace LabTable idxold += std::min(q+q_, (int)(LAB_LUT_DIM-1))*LAB_LUT_DIM*3; idxold += std::min(r+r_, (int)(LAB_LUT_DIM-1))*LAB_LUT_DIM*LAB_LUT_DIM*3; int idxnew = p*3*8 + q*LAB_LUT_DIM*3*8 + r*LAB_LUT_DIM*LAB_LUT_DIM*3*8+4*p_+2*q_+r_; + RGB2LabLUT[idxnew] = RGB2Labprev[idxold]; + RGB2LabLUT[idxnew+8] = RGB2Labprev[idxold+1]; + RGB2LabLUT[idxnew+16] = RGB2Labprev[idxold+2]; RGB2LuvLUT[idxnew] = RGB2Luvprev[idxold]; RGB2LuvLUT[idxnew+8] = RGB2Luvprev[idxold+1]; RGB2LuvLUT[idxnew+16] = RGB2Luvprev[idxold+2]; @@ -2438,7 +2475,8 @@ namespace LabTable ushort sRGBGammaTab_b[256], sRGBInvGammaTab_b[INV_GAMMA_TAB_SIZE]; short LabToYF_b[256*2]; int LuToUp_b[256*256], LvToVp_b[256*256]; - short RGB2LuvLUT[LAB_LUT_DIM*LAB_LUT_DIM*LAB_LUT_DIM*3*8], trilinearLUT[TRILINEAR_BASE*TRILINEAR_BASE*TRILINEAR_BASE*8]; + short RGB2LabLUT[LAB_LUT_DIM*LAB_LUT_DIM*LAB_LUT_DIM*3*8], RGB2LuvLUT[LAB_LUT_DIM*LAB_LUT_DIM*LAB_LUT_DIM*3*8]; + short trilinearLUT[TRILINEAR_BASE*TRILINEAR_BASE*TRILINEAR_BASE*8]; static Tab& instance() { @@ -2473,15 +2511,15 @@ inline int cvtLabtoBGR(int start, int end, const uchar * src, size_t src_ { static const int XYZ2BGR[] = { - (int)std::rint((1 << 12) * 0.055648f * 0.950456f), (int)std::rint((1 << 12) * -0.204043f), (int)std::rint((1 << 12) * 1.057311f * 1.088754f), - (int)std::rint((1 << 12) * -0.969256f * 0.950456f), (int)std::rint((1 << 12) * 1.875991f), (int)std::rint((1 << 12) * 0.041556f * 1.088754f), - (int)std::rint((1 << 12) * 3.240479f * 0.950456f), (int)std::rint((1 << 12) * -1.53715f ), (int)std::rint((1 << 12) * -0.498535f * 1.088754f) + (int)color::rint((1 << 12) * 0.055648f * 0.950456f), (int)color::rint((1 << 12) * -0.204043f), (int)color::rint((1 << 12) * 1.057311f * 1.088754f), + (int)color::rint((1 << 12) * -0.969256f * 0.950456f), (int)color::rint((1 << 12) * 1.875991f), (int)color::rint((1 << 12) * 0.041556f * 1.088754f), + (int)color::rint((1 << 12) * 3.240479f * 0.950456f), (int)color::rint((1 << 12) * -1.53715f ), (int)color::rint((1 << 12) * -0.498535f * 1.088754f) }; static const int XYZ2BGR_D65[] = { - (int)std::rint((1 << 12) * 0.055648f), (int)std::rint((1 << 12) * -0.204043f), (int)std::rint((1 << 12) * 1.057311f), - (int)std::rint((1 << 12) * -0.969256f), (int)std::rint((1 << 12) * 1.875991f), (int)std::rint((1 << 12) * 0.041556f), - (int)std::rint((1 << 12) * 3.240479f), (int)std::rint((1 << 12) * -1.53715f ), (int)std::rint((1 << 12) * -0.498535f) + (int)color::rint((1 << 12) * 0.055648f), (int)color::rint((1 << 12) * -0.204043f), (int)color::rint((1 << 12) * 1.057311f), + (int)color::rint((1 << 12) * -0.969256f), (int)color::rint((1 << 12) * 1.875991f), (int)color::rint((1 << 12) * 0.041556f), + (int)color::rint((1 << 12) * 3.240479f), (int)color::rint((1 << 12) * -1.53715f ), (int)color::rint((1 << 12) * -0.498535f) }; const int* XYZtab = isLab ? XYZ2BGR : XYZ2BGR_D65; @@ -2734,9 +2772,9 @@ template struct rvv : rvv_base { static const ushort BGR2XYZ[] = { - (ushort)std::rint((1 << 12) * 0.180423f / 0.950456f), (ushort)std::rint((1 << 12) * 0.357580f / 0.950456f), (ushort)std::rint((1 << 12) * 0.412453f / 0.950456f), - (ushort)std::rint((1 << 12) * 0.072169f ), (ushort)std::rint((1 << 12) * 0.715160f ), (ushort)std::rint((1 << 12) * 0.212671f ), - (ushort)std::rint((1 << 12) * 0.950227f / 1.088754f), (ushort)std::rint((1 << 12) * 0.119193f / 1.088754f), (ushort)std::rint((1 << 12) * 0.019334f / 1.088754f) + (ushort)color::rint((1 << 12) * 0.180423f / 0.950456f), (ushort)color::rint((1 << 12) * 0.357580f / 0.950456f), (ushort)color::rint((1 << 12) * 0.412453f / 0.950456f), + (ushort)color::rint((1 << 12) * 0.072169f ), (ushort)color::rint((1 << 12) * 0.715160f ), (ushort)color::rint((1 << 12) * 0.212671f ), + (ushort)color::rint((1 << 12) * 0.950227f / 1.088754f), (ushort)color::rint((1 << 12) * 0.119193f / 1.088754f), (ushort)color::rint((1 << 12) * 0.019334f / 1.088754f) }; vuint16m2_t bb, gg, rr; @@ -2938,40 +2976,79 @@ static inline int cvtBGRtoLab_f(int start, int end, const float * src, size_t sr auto t = b; b = r, r = t; } - b = __riscv_vfmin(__riscv_vfmax(b, 0.0f, vl), 1.0f, vl); g = __riscv_vfmin(__riscv_vfmax(g, 0.0f, vl), 1.0f, vl); r = __riscv_vfmin(__riscv_vfmax(r, 0.0f, vl), 1.0f, vl); - if (srgb) + + vfloat32m2_t lo, ao, bo; + if (isLab && srgb) { - b = LabTable::Tab::splineInterpolate(vl, __riscv_vfmul(b, LabTable::Tab::GAMMA_TAB_SIZE, vl), LabTable::Tab::instance().sRGBGammaTab, LabTable::Tab::GAMMA_TAB_SIZE); - g = LabTable::Tab::splineInterpolate(vl, __riscv_vfmul(g, LabTable::Tab::GAMMA_TAB_SIZE, vl), LabTable::Tab::instance().sRGBGammaTab, LabTable::Tab::GAMMA_TAB_SIZE); - r = LabTable::Tab::splineInterpolate(vl, __riscv_vfmul(r, LabTable::Tab::GAMMA_TAB_SIZE, vl), LabTable::Tab::instance().sRGBGammaTab, LabTable::Tab::GAMMA_TAB_SIZE); - } + auto ib = __riscv_vfcvt_xu(__riscv_vfmul(b, LabTable::Tab::LAB_BASE, vl), vl); + auto ig = __riscv_vfcvt_xu(__riscv_vfmul(g, LabTable::Tab::LAB_BASE, vl), vl); + auto ir = __riscv_vfcvt_xu(__riscv_vfmul(r, LabTable::Tab::LAB_BASE, vl), vl); - auto x = __riscv_vfmadd(b, BGRtab[0], __riscv_vfmadd(g, BGRtab[1], __riscv_vfmul(r, BGRtab[2], vl), vl), vl); - auto y = __riscv_vfmadd(b, BGRtab[3], __riscv_vfmadd(g, BGRtab[4], __riscv_vfmul(r, BGRtab[5], vl), vl), vl); - auto z = __riscv_vfmadd(b, BGRtab[6], __riscv_vfmadd(g, BGRtab[7], __riscv_vfmul(r, BGRtab[8], vl), vl), vl); - auto fy = LabTable::Tab::splineInterpolate(vl, __riscv_vfmul(y, LabTable::Tab::GAMMA_TAB_SIZE, vl), LabTable::Tab::instance().LabCbrtTab, LabTable::Tab::GAMMA_TAB_SIZE); + auto x = __riscv_vand(__riscv_vsrl(ib, 5, vl), 15, vl), y = __riscv_vand(__riscv_vsrl(ig, 5, vl), 15, vl), z = __riscv_vand(__riscv_vsrl(ir, 5, vl), 15, vl); + auto base = __riscv_vmul(__riscv_vmacc(__riscv_vmacc(__riscv_vmul(x, 8, vl), 8*LabTable::Tab::TRILINEAR_BASE, y, vl), 8*LabTable::Tab::TRILINEAR_BASE*LabTable::Tab::TRILINEAR_BASE, z, vl), sizeof(short), vl); + auto tab = __riscv_vloxseg8ei32_v_i16m1x8(LabTable::Tab::instance().trilinearLUT, base, vl); + auto w0 = __riscv_vget_v_i16m1x8_i16m1(tab, 0); + auto w1 = __riscv_vget_v_i16m1x8_i16m1(tab, 1); + auto w2 = __riscv_vget_v_i16m1x8_i16m1(tab, 2); + auto w3 = __riscv_vget_v_i16m1x8_i16m1(tab, 3); + auto w4 = __riscv_vget_v_i16m1x8_i16m1(tab, 4); + auto w5 = __riscv_vget_v_i16m1x8_i16m1(tab, 5); + auto w6 = __riscv_vget_v_i16m1x8_i16m1(tab, 6); + auto w7 = __riscv_vget_v_i16m1x8_i16m1(tab, 7); - auto lo = __riscv_vfmadd(fy, 116.0f, __riscv_vfmv_v_f_f32m2(-16.0f, vl), vl); - vfloat32m2_t ao, bo; - if (isLab) - { - x = LabTable::Tab::splineInterpolate(vl, __riscv_vfmul(x, LabTable::Tab::GAMMA_TAB_SIZE, vl), LabTable::Tab::instance().LabCbrtTab, LabTable::Tab::GAMMA_TAB_SIZE); - z = LabTable::Tab::splineInterpolate(vl, __riscv_vfmul(z, LabTable::Tab::GAMMA_TAB_SIZE, vl), LabTable::Tab::instance().LabCbrtTab, LabTable::Tab::GAMMA_TAB_SIZE); + auto tx = __riscv_vsrl(ib, 9, vl), ty = __riscv_vsrl(ig, 9, vl), tz = __riscv_vsrl(ir, 9, vl); + base = __riscv_vmul(__riscv_vmacc(__riscv_vmacc(__riscv_vmul(tx, 3*8, vl), 3*8*LabTable::Tab::LAB_LUT_DIM, ty, vl), 3*8*LabTable::Tab::LAB_LUT_DIM*LabTable::Tab::LAB_LUT_DIM, tz, vl), sizeof(short), vl); + auto interpolate = [&](vuint32m2_t p) { + tab = __riscv_vloxseg8ei32_v_i16m1x8(LabTable::Tab::instance().RGB2LabLUT, p, vl); + auto a0 = __riscv_vget_v_i16m1x8_i16m1(tab, 0); + auto a1 = __riscv_vget_v_i16m1x8_i16m1(tab, 1); + auto a2 = __riscv_vget_v_i16m1x8_i16m1(tab, 2); + auto a3 = __riscv_vget_v_i16m1x8_i16m1(tab, 3); + auto a4 = __riscv_vget_v_i16m1x8_i16m1(tab, 4); + auto a5 = __riscv_vget_v_i16m1x8_i16m1(tab, 5); + auto a6 = __riscv_vget_v_i16m1x8_i16m1(tab, 6); + auto a7 = __riscv_vget_v_i16m1x8_i16m1(tab, 7); + return __riscv_vwmacc(__riscv_vwmacc(__riscv_vwmacc(__riscv_vwmacc(__riscv_vwmacc(__riscv_vwmacc(__riscv_vwmacc(__riscv_vwmul(a0, w0, vl), a1, w1, vl), a2, w2, vl), a3, w3, vl), a4, w4, vl), a5, w5, vl), a6, w6, vl), a7, w7, vl); + }; - lo = __riscv_vmerge(__riscv_vfmul(y, 903.3f, vl), lo, __riscv_vmfgt(y, 0.008856f, vl), vl); - ao = __riscv_vfmul(__riscv_vfsub(x, fy, vl), 500.0f, vl); - bo = __riscv_vfmul(__riscv_vfsub(fy, z, vl), 200.0f, vl); + lo = __riscv_vfmul(__riscv_vfcvt_f(__riscv_vssra(interpolate(base), 12, __RISCV_VXRM_RNU, vl), vl), 100.0f / LabTable::Tab::LAB_BASE, vl); + ao = __riscv_vfmadd(__riscv_vfcvt_f(__riscv_vssra(interpolate(__riscv_vadd(base, 8 * sizeof(short), vl)), 12, __RISCV_VXRM_RNU, vl), vl), 256.0f / LabTable::Tab::LAB_BASE, __riscv_vfmv_v_f_f32m2(-128.0f, vl), vl); + bo = __riscv_vfmadd(__riscv_vfcvt_f(__riscv_vssra(interpolate(__riscv_vadd(base, 16 * sizeof(short), vl)), 12, __RISCV_VXRM_RNU, vl), vl), 256.0f / LabTable::Tab::LAB_BASE, __riscv_vfmv_v_f_f32m2(-128.0f, vl), vl); } else { - auto d = __riscv_vfrdiv(__riscv_vfmax(__riscv_vfmadd(y, 15.0f, __riscv_vfmadd(z, 3.0f, x, vl), vl), FLT_EPSILON, vl), 52.0f, vl); - ao = __riscv_vfmul(__riscv_vfmadd(x, d, __riscv_vfmv_v_f_f32m2(-2.5719122887f, vl), vl), lo, vl); - bo = __riscv_vfmul(__riscv_vfmadd(__riscv_vfmul(y, 2.25f, vl), d, __riscv_vfmv_v_f_f32m2(-6.0884485245f, vl), vl), lo, vl); - } + if (srgb) + { + b = LabTable::Tab::splineInterpolate(vl, __riscv_vfmul(b, LabTable::Tab::GAMMA_TAB_SIZE, vl), LabTable::Tab::instance().sRGBGammaTab, LabTable::Tab::GAMMA_TAB_SIZE); + g = LabTable::Tab::splineInterpolate(vl, __riscv_vfmul(g, LabTable::Tab::GAMMA_TAB_SIZE, vl), LabTable::Tab::instance().sRGBGammaTab, LabTable::Tab::GAMMA_TAB_SIZE); + r = LabTable::Tab::splineInterpolate(vl, __riscv_vfmul(r, LabTable::Tab::GAMMA_TAB_SIZE, vl), LabTable::Tab::instance().sRGBGammaTab, LabTable::Tab::GAMMA_TAB_SIZE); + } + auto x = __riscv_vfmadd(b, BGRtab[0], __riscv_vfmadd(g, BGRtab[1], __riscv_vfmul(r, BGRtab[2], vl), vl), vl); + auto y = __riscv_vfmadd(b, BGRtab[3], __riscv_vfmadd(g, BGRtab[4], __riscv_vfmul(r, BGRtab[5], vl), vl), vl); + auto z = __riscv_vfmadd(b, BGRtab[6], __riscv_vfmadd(g, BGRtab[7], __riscv_vfmul(r, BGRtab[8], vl), vl), vl); + auto fy = LabTable::Tab::splineInterpolate(vl, __riscv_vfmul(y, LabTable::Tab::GAMMA_TAB_SIZE, vl), LabTable::Tab::instance().LabCbrtTab, LabTable::Tab::GAMMA_TAB_SIZE); + + lo = __riscv_vfmadd(fy, 116.0f, __riscv_vfmv_v_f_f32m2(-16.0f, vl), vl); + if (isLab) + { + x = LabTable::Tab::splineInterpolate(vl, __riscv_vfmul(x, LabTable::Tab::GAMMA_TAB_SIZE, vl), LabTable::Tab::instance().LabCbrtTab, LabTable::Tab::GAMMA_TAB_SIZE); + z = LabTable::Tab::splineInterpolate(vl, __riscv_vfmul(z, LabTable::Tab::GAMMA_TAB_SIZE, vl), LabTable::Tab::instance().LabCbrtTab, LabTable::Tab::GAMMA_TAB_SIZE); + + lo = __riscv_vmerge(__riscv_vfmul(y, 903.3f, vl), lo, __riscv_vmfgt(y, 0.008856f, vl), vl); + ao = __riscv_vfmul(__riscv_vfsub(x, fy, vl), 500.0f, vl); + bo = __riscv_vfmul(__riscv_vfsub(fy, z, vl), 200.0f, vl); + } + else + { + auto d = __riscv_vfrdiv(__riscv_vfmax(__riscv_vfmadd(y, 15.0f, __riscv_vfmadd(z, 3.0f, x, vl), vl), FLT_EPSILON, vl), 52.0f, vl); + ao = __riscv_vfmul(__riscv_vfmadd(x, d, __riscv_vfmv_v_f_f32m2(-2.5719122887f, vl), vl), lo, vl); + bo = __riscv_vfmul(__riscv_vfmadd(__riscv_vfmul(y, 2.25f, vl), d, __riscv_vfmv_v_f_f32m2(-6.0884485245f, vl), vl), lo, vl); + } + } vfloat32m2x3_t vec_dst{}; vec_dst = __riscv_vset_v_f32m2_f32m2x3(vec_dst, 0, lo); vec_dst = __riscv_vset_v_f32m2_f32m2x3(vec_dst, 1, ao); diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/filter.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/filter.hpp index f978cd172e..97e6edd1a9 100644 --- a/3rdparty/hal_rvv/hal_rvv_1p0/filter.hpp +++ b/3rdparty/hal_rvv/hal_rvv_1p0/filter.hpp @@ -7,7 +7,6 @@ #ifndef OPENCV_HAL_RVV_FILTER_HPP_INCLUDED #define OPENCV_HAL_RVV_FILTER_HPP_INCLUDED -#include "../../imgproc/include/opencv2/imgproc/hal/interface.h" #include struct cvhalFilter2D; diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/lut.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/lut.hpp index 26e885632b..c13a5b2f0a 100644 --- a/3rdparty/hal_rvv/hal_rvv_1p0/lut.hpp +++ b/3rdparty/hal_rvv/hal_rvv_1p0/lut.hpp @@ -82,14 +82,14 @@ public: size_t dst_step; size_t width; - LUTParallelBody(const uchar* src_data, - size_t src_step, - const uchar* lut_data, - uchar* dst_data, - size_t dst_step, - size_t width) : - src_data(src_data), lut_data(lut_data), dst_data(dst_data), src_step(src_step), - dst_step(dst_step), width(width) + LUTParallelBody(const uchar* _src_data, + size_t _src_step, + const uchar* _lut_data, + uchar* _dst_data, + size_t _dst_step, + size_t _width) : + src_data(_src_data), lut_data(_lut_data), dst_data(_dst_data), src_step(_src_step), + dst_step(_dst_step), width(_width) { } diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/merge.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/merge.hpp index 0b5f21de91..b1da204b39 100644 --- a/3rdparty/hal_rvv/hal_rvv_1p0/merge.hpp +++ b/3rdparty/hal_rvv/hal_rvv_1p0/merge.hpp @@ -214,7 +214,7 @@ inline int merge16u(const ushort** src, ushort* dst, int len, int cn ) { return CV_HAL_ERROR_OK; } -#if defined __GNUC__ +#if defined __GNUC__ && !defined(__clang__) __attribute__((optimize("no-tree-vectorize"))) #endif inline int merge32s(const int** src, int* dst, int len, int cn ) { @@ -284,7 +284,7 @@ inline int merge32s(const int** src, int* dst, int len, int cn ) { return CV_HAL_ERROR_OK; } -#if defined __GNUC__ +#if defined __GNUC__ && !defined(__clang__) __attribute__((optimize("no-tree-vectorize"))) #endif inline int merge64s(const int64** src, int64* dst, int len, int cn ) { diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/norm.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/norm.hpp index 1e583f29da..9e79940390 100644 --- a/3rdparty/hal_rvv/hal_rvv_1p0/norm.hpp +++ b/3rdparty/hal_rvv/hal_rvv_1p0/norm.hpp @@ -1010,7 +1010,7 @@ inline int norm(const uchar* src, size_t src_step, const uchar* mask, size_t mas }; bool src_continuous = (src_step == width * elem_size_tab[depth] * cn || (src_step != width * elem_size_tab[depth] * cn && height == 1)); - bool mask_continuous = (mask_step == width); + bool mask_continuous = (mask_step == static_cast(width)); size_t nplanes = 1; size_t size = width * height; if ((mask && (!src_continuous || !mask_continuous)) || !src_continuous) { diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/sqrt.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/sqrt.hpp index 7653cfc50d..b87998d637 100644 --- a/3rdparty/hal_rvv/hal_rvv_1p0/sqrt.hpp +++ b/3rdparty/hal_rvv/hal_rvv_1p0/sqrt.hpp @@ -40,7 +40,9 @@ inline VEC_T sqrt(VEC_T x, size_t vl) { auto x2 = __riscv_vfmul(x, 0.5, vl); auto y = __riscv_vfrsqrt7(x, vl); +#ifdef __clang__ #pragma unroll +#endif for (size_t i = 0; i < iter_times; i++) { auto t = __riscv_vfmul(y, y, vl); @@ -67,7 +69,9 @@ inline VEC_T invSqrt(VEC_T x, size_t vl) auto mask = __riscv_vmseq(__riscv_vand(classified, 0b10111000, vl), 0, vl); auto x2 = __riscv_vfmul(x, 0.5, vl); auto y = __riscv_vfrsqrt7(x, vl); +#ifdef __clang__ #pragma unroll +#endif for (size_t i = 0; i < iter_times; i++) { auto t = __riscv_vfmul(y, y, vl); diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/svd.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/svd.hpp index 53225c64a9..dfe644ad06 100644 --- a/3rdparty/hal_rvv/hal_rvv_1p0/svd.hpp +++ b/3rdparty/hal_rvv/hal_rvv_1p0/svd.hpp @@ -191,6 +191,8 @@ inline int SVD(T* src, size_t src_step, T* w, T*, size_t, T* vt, size_t vt_step, if( !vt ) return CV_HAL_ERROR_OK; + uint64 rng = 0x12345678; + auto next = [&rng]{ return (unsigned)(rng = (uint64)(unsigned)rng * 4164903690U + (unsigned)(rng >> 32)); }; for( i = 0; i < n1; i++ ) { sd = i < n ? W[i] : 0; @@ -203,7 +205,7 @@ inline int SVD(T* src, size_t src_step, T* w, T*, size_t, T* vt, size_t vt_step, const T val0 = (T)(1./m); for( k = 0; k < m; k++ ) { - T val = (rand() & 256) != 0 ? val0 : -val0; + T val = (next() & 256) != 0 ? val0 : -val0; src[i*src_step + k] = val; } for( iter = 0; iter < 2; iter++ ) diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/thresh.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/thresh.hpp index 42b231b26d..5842540c35 100644 --- a/3rdparty/hal_rvv/hal_rvv_1p0/thresh.hpp +++ b/3rdparty/hal_rvv/hal_rvv_1p0/thresh.hpp @@ -7,7 +7,7 @@ #ifndef OPENCV_HAL_RVV_THRESH_HPP_INCLUDED #define OPENCV_HAL_RVV_THRESH_HPP_INCLUDED -#include "hal_rvv_1p0/types.hpp" +#include #include namespace cv { namespace cv_hal_rvv { From 1d9dda3f09d164a6da66f37f622f51dd6c8b085c Mon Sep 17 00:00:00 2001 From: cudawarped <12133430+cudawarped@users.noreply.github.com> Date: Fri, 21 Mar 2025 13:41:49 +0200 Subject: [PATCH 33/94] Merge pull request #27112 from cudawarped:add_cuda_c++17 cuda: Force C++17 Standard for CUDA targets when CUDA Toolkit >=12.8 #27112 Fix https://github.com/opencv/opencv/issues/27095. ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [ ] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- cmake/OpenCVDetectCUDA.cmake | 8 +++++--- cmake/OpenCVDetectCUDALanguage.cmake | 6 ++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/cmake/OpenCVDetectCUDA.cmake b/cmake/OpenCVDetectCUDA.cmake index 06998400d7..c5ce8762f2 100644 --- a/cmake/OpenCVDetectCUDA.cmake +++ b/cmake/OpenCVDetectCUDA.cmake @@ -151,12 +151,14 @@ macro(ocv_cuda_compile VAR) ocv_check_windows_crt_linkage() ocv_nvcc_flags() - if(UNIX OR APPLE) - if(NOT " ${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_RELEASE} ${CMAKE_CXX_FLAGS_DEBUG} ${CUDA_NVCC_FLAGS}" MATCHES "-std=") + if(NOT " ${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_RELEASE} ${CMAKE_CXX_FLAGS_DEBUG} ${CUDA_NVCC_FLAGS}" MATCHES "-std=") + if(UNIX OR APPLE) if(CUDA_VERSION VERSION_LESS "11.0") list(APPEND CUDA_NVCC_FLAGS "--std=c++11") - else() + elseif(CUDA_VERSION VERSION_LESS "12.8") list(APPEND CUDA_NVCC_FLAGS "--std=c++14") + elseif(CUDA_VERSION VERSION_GREATER_EQUAL "12.8") + list(APPEND CUDA_NVCC_FLAGS "--std=c++17") endif() endif() endif() diff --git a/cmake/OpenCVDetectCUDALanguage.cmake b/cmake/OpenCVDetectCUDALanguage.cmake index 0eeea77f2c..bc40134180 100644 --- a/cmake/OpenCVDetectCUDALanguage.cmake +++ b/cmake/OpenCVDetectCUDALanguage.cmake @@ -33,10 +33,12 @@ if(CMAKE_CUDA_COMPILER AND CUDAToolkit_FOUND) set(CUDA_TOOLKIT_INCLUDE ${CUDAToolkit_INCLUDE_DIRS}) set(CUDA_VERSION_STRING ${CUDAToolkit_VERSION}) set(CUDA_VERSION ${CUDAToolkit_VERSION}) - if(NOT CUDA_VERSION VERSION_LESS 11.0) + if(CUDA_VERSION VERSION_LESS 11.0) + set(CMAKE_CUDA_STANDARD 11) + elseif(CUDA_VERSION VERSION_LESS 12.8) set(CMAKE_CUDA_STANDARD 14) else() - set(CMAKE_CUDA_STANDARD 11) + set(CMAKE_CUDA_STANDARD 17) endif() if(UNIX AND NOT BUILD_SHARED_LIBS) set(CUDA_LIB_EXT "_static") From 01ef38dcad65a119204e6d70f553767a7d2ab614 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov <2536374+asmorkalov@users.noreply.github.com> Date: Sat, 22 Mar 2025 09:31:42 +0300 Subject: [PATCH 34/94] Merge pull request #26880 from asmorkalov:as/ipp_hal Initial version of IPP-based HAL for x86 and x86_64 platforms #26880 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [ ] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- 3rdparty/ipphal/CMakeLists.txt | 34 ++++ 3rdparty/ipphal/include/ipp_hal_core.hpp | 15 ++ 3rdparty/ipphal/include/ipp_utils.hpp | 24 +++ 3rdparty/ipphal/src/mean_ipp.cpp | 206 ++++++++++++++++++++ CMakeLists.txt | 12 ++ modules/core/src/mean.dispatch.cpp | 232 ----------------------- 6 files changed, 291 insertions(+), 232 deletions(-) create mode 100644 3rdparty/ipphal/CMakeLists.txt create mode 100644 3rdparty/ipphal/include/ipp_hal_core.hpp create mode 100644 3rdparty/ipphal/include/ipp_utils.hpp create mode 100644 3rdparty/ipphal/src/mean_ipp.cpp diff --git a/3rdparty/ipphal/CMakeLists.txt b/3rdparty/ipphal/CMakeLists.txt new file mode 100644 index 0000000000..6d0f368957 --- /dev/null +++ b/3rdparty/ipphal/CMakeLists.txt @@ -0,0 +1,34 @@ +project(ipphal) + +set(IPP_HAL_VERSION 0.0.1 CACHE INTERNAL "") +set(IPP_HAL_LIBRARIES "ipphal" CACHE INTERNAL "") +set(IPP_HAL_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/include" CACHE INTERNAL "") +set(IPP_HAL_HEADERS + "${CMAKE_CURRENT_SOURCE_DIR}/include/ipp_hal_core.hpp" + CACHE INTERNAL "") + +add_library(ipphal STATIC "${CMAKE_CURRENT_SOURCE_DIR}/src/mean_ipp.cpp") + +if(HAVE_IPP_ICV) + target_compile_definitions(ipphal PRIVATE HAVE_IPP_ICV) +endif() + +target_include_directories(ipphal PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/include") + +target_include_directories(ipphal PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/src" + ${CMAKE_SOURCE_DIR}/modules/core/include + ${IPP_INCLUDE_DIRS} +) + +target_link_libraries(ipphal PUBLIC ${IPP_IW_LIBRARY} ${IPP_LIBRARIES}) + +set_target_properties(ipphal PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${3P_LIBRARY_OUTPUT_PATH}) + +if(NOT BUILD_SHARED_LIBS) + ocv_install_target(ipphal EXPORT OpenCVModules ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT dev) +endif() + +if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(ipphal PROPERTIES FOLDER "3rdparty") +endif() diff --git a/3rdparty/ipphal/include/ipp_hal_core.hpp b/3rdparty/ipphal/include/ipp_hal_core.hpp new file mode 100644 index 0000000000..ad9c3984a6 --- /dev/null +++ b/3rdparty/ipphal/include/ipp_hal_core.hpp @@ -0,0 +1,15 @@ +#ifndef __IPP_HAL_CORE_HPP__ +#define __IPP_HAL_CORE_HPP__ + +#include +#include "ipp_utils.hpp" + +#if (IPP_VERSION_X100 >= 700) +int ipp_hal_meanStdDev(const uchar* src_data, size_t src_step, int width, int height, int src_type, + double* mean_val, double* stddev_val, uchar* mask, size_t mask_step); + +#undef cv_hal_meanStdDev +#define cv_hal_meanStdDev ipp_hal_meanStdDev +#endif + +#endif diff --git a/3rdparty/ipphal/include/ipp_utils.hpp b/3rdparty/ipphal/include/ipp_utils.hpp new file mode 100644 index 0000000000..26ae75affd --- /dev/null +++ b/3rdparty/ipphal/include/ipp_utils.hpp @@ -0,0 +1,24 @@ +#ifndef __IPP_HAL_UTILS_HPP__ +#define __IPP_HAL_UTILS_HPP__ + +#include "ippversion.h" +#ifndef IPP_VERSION_UPDATE // prior to 7.1 +#define IPP_VERSION_UPDATE 0 +#endif + +#define IPP_VERSION_X100 (IPP_VERSION_MAJOR * 100 + IPP_VERSION_MINOR*10 + IPP_VERSION_UPDATE) + +#ifdef HAVE_IPP_ICV +# define ICV_BASE +#if IPP_VERSION_X100 >= 201700 +# include "ippicv.h" +#else +# include "ipp.h" +#endif +#else +# include "ipp.h" +#endif + +#define CV_INSTRUMENT_FUN_IPP(FUN, ...) ((FUN)(__VA_ARGS__)) + +#endif diff --git a/3rdparty/ipphal/src/mean_ipp.cpp b/3rdparty/ipphal/src/mean_ipp.cpp new file mode 100644 index 0000000000..63abe6eb9d --- /dev/null +++ b/3rdparty/ipphal/src/mean_ipp.cpp @@ -0,0 +1,206 @@ +#include "ipp_hal_core.hpp" + +#include +#include + +#if IPP_VERSION_X100 >= 700 + +static int ipp_mean(const uchar* src_data, size_t src_step, int width, int height, + int src_type, double* mean_val, uchar* mask, size_t mask_step) +{ + int cn = CV_MAT_CN(src_type); + if (cn > 4) + { + return CV_HAL_ERROR_NOT_IMPLEMENTED; + } + + if((src_step == 1 || src_step == static_cast(width)) && (mask_step == 1 || mask_step == static_cast(width))) + { + IppiSize sz = { width, height }; + if( mask ) + { + typedef IppStatus (CV_STDCALL* ippiMaskMeanFuncC1)(const void *, int, const void *, int, IppiSize, Ipp64f *); + ippiMaskMeanFuncC1 ippiMean_C1MR = + src_type == CV_8UC1 ? (ippiMaskMeanFuncC1)ippiMean_8u_C1MR : + src_type == CV_16UC1 ? (ippiMaskMeanFuncC1)ippiMean_16u_C1MR : + src_type == CV_32FC1 ? (ippiMaskMeanFuncC1)ippiMean_32f_C1MR : + 0; + if( ippiMean_C1MR ) + { + if( CV_INSTRUMENT_FUN_IPP(ippiMean_C1MR, src_data, (int)src_step, mask, (int)mask_step, sz, mean_val) >= 0 ) + { + return CV_HAL_ERROR_OK; + } + } + typedef IppStatus (CV_STDCALL* ippiMaskMeanFuncC3)(const void *, int, const void *, int, IppiSize, int, Ipp64f *); + ippiMaskMeanFuncC3 ippiMean_C3MR = + src_type == CV_8UC3 ? (ippiMaskMeanFuncC3)ippiMean_8u_C3CMR : + src_type == CV_16UC3 ? (ippiMaskMeanFuncC3)ippiMean_16u_C3CMR : + src_type == CV_32FC3 ? (ippiMaskMeanFuncC3)ippiMean_32f_C3CMR : + 0; + if( ippiMean_C3MR ) + { + if( CV_INSTRUMENT_FUN_IPP(ippiMean_C3MR, src_data, (int)src_step, mask, (int)mask_step, sz, 1, &mean_val[0]) >= 0 && + CV_INSTRUMENT_FUN_IPP(ippiMean_C3MR, src_data, (int)src_step, mask, (int)mask_step, sz, 2, &mean_val[1]) >= 0 && + CV_INSTRUMENT_FUN_IPP(ippiMean_C3MR, src_data, (int)src_step, mask, (int)mask_step, sz, 3, &mean_val[2]) >= 0 ) + { + return CV_HAL_ERROR_OK; + } + } + } + else + { + typedef IppStatus (CV_STDCALL* ippiMeanFuncHint)(const void*, int, IppiSize, double *, IppHintAlgorithm); + typedef IppStatus (CV_STDCALL* ippiMeanFuncNoHint)(const void*, int, IppiSize, double *); + ippiMeanFuncHint ippiMeanHint = + src_type == CV_32FC1 ? (ippiMeanFuncHint)ippiMean_32f_C1R : + src_type == CV_32FC3 ? (ippiMeanFuncHint)ippiMean_32f_C3R : + src_type == CV_32FC4 ? (ippiMeanFuncHint)ippiMean_32f_C4R : + 0; + ippiMeanFuncNoHint ippiMean = + src_type == CV_8UC1 ? (ippiMeanFuncNoHint)ippiMean_8u_C1R : + src_type == CV_8UC3 ? (ippiMeanFuncNoHint)ippiMean_8u_C3R : + src_type == CV_8UC4 ? (ippiMeanFuncNoHint)ippiMean_8u_C4R : + src_type == CV_16UC1 ? (ippiMeanFuncNoHint)ippiMean_16u_C1R : + src_type == CV_16UC3 ? (ippiMeanFuncNoHint)ippiMean_16u_C3R : + src_type == CV_16UC4 ? (ippiMeanFuncNoHint)ippiMean_16u_C4R : + src_type == CV_16SC1 ? (ippiMeanFuncNoHint)ippiMean_16s_C1R : + src_type == CV_16SC3 ? (ippiMeanFuncNoHint)ippiMean_16s_C3R : + src_type == CV_16SC4 ? (ippiMeanFuncNoHint)ippiMean_16s_C4R : + 0; + + // Make sure only zero or one version of the function pointer is valid + CV_Assert(!ippiMeanHint || !ippiMean); + if( ippiMeanHint || ippiMean ) + { + IppStatus status = ippiMeanHint ? CV_INSTRUMENT_FUN_IPP(ippiMeanHint, src_data, (int)src_step, sz, mean_val, ippAlgHintAccurate) : + CV_INSTRUMENT_FUN_IPP(ippiMean, src_data, (int)src_step, sz, mean_val); + if( status >= 0 ) + { + return CV_HAL_ERROR_OK; + } + } + } + } + + return CV_HAL_ERROR_NOT_IMPLEMENTED; +} + + + +static int ipp_meanStdDev(const uchar* src_data, size_t src_step, int width, int height, + int src_type, double* mean_val, double* stddev_val, uchar* mask, size_t mask_step) +{ + int cn = CV_MAT_CN(src_type); + + if((src_step == 1 || src_step == static_cast(width)) && (mask_step == 1 || mask_step == static_cast(width))) + { + Ipp64f mean_temp[3]; + Ipp64f stddev_temp[3]; + Ipp64f *pmean = &mean_temp[0]; + Ipp64f *pstddev = &stddev_temp[0]; + int dcn_mean = -1; + if( mean_val ) + { + dcn_mean = cn; + pmean = mean_val; + } + int dcn_stddev = -1; + if( stddev_val ) + { + dcn_stddev = cn; + pstddev = stddev_val; + } + + for( int c = cn; c < dcn_mean; c++ ) + pmean[c] = 0; + for( int c = cn; c < dcn_stddev; c++ ) + pstddev[c] = 0; + + IppiSize sz = { width, height }; + if( !mask ) + { + typedef IppStatus (CV_STDCALL* ippiMaskMeanStdDevFuncC1)(const void *, int, const void *, int, IppiSize, Ipp64f *, Ipp64f *); + ippiMaskMeanStdDevFuncC1 ippiMean_StdDev_C1MR = + src_type == CV_8UC1 ? (ippiMaskMeanStdDevFuncC1)ippiMean_StdDev_8u_C1MR : + src_type == CV_16UC1 ? (ippiMaskMeanStdDevFuncC1)ippiMean_StdDev_16u_C1MR : + src_type == CV_32FC1 ? (ippiMaskMeanStdDevFuncC1)ippiMean_StdDev_32f_C1MR : + nullptr; + if( ippiMean_StdDev_C1MR ) + { + if( CV_INSTRUMENT_FUN_IPP(ippiMean_StdDev_C1MR, src_data, (int)src_step, mask, (int)mask_step, sz, pmean, pstddev) >= 0 ) + { + return CV_HAL_ERROR_OK; + } + } + + typedef IppStatus (CV_STDCALL* ippiMaskMeanStdDevFuncC3)(const void *, int, const void *, int, IppiSize, int, Ipp64f *, Ipp64f *); + ippiMaskMeanStdDevFuncC3 ippiMean_StdDev_C3CMR = + src_type == CV_8UC3 ? (ippiMaskMeanStdDevFuncC3)ippiMean_StdDev_8u_C3CMR : + src_type == CV_16UC3 ? (ippiMaskMeanStdDevFuncC3)ippiMean_StdDev_16u_C3CMR : + src_type == CV_32FC3 ? (ippiMaskMeanStdDevFuncC3)ippiMean_StdDev_32f_C3CMR : + nullptr; + if( ippiMean_StdDev_C3CMR ) + { + if( CV_INSTRUMENT_FUN_IPP(ippiMean_StdDev_C3CMR, src_data, (int)src_step, mask, (int)mask_step, sz, 1, &pmean[0], &pstddev[0]) >= 0 && + CV_INSTRUMENT_FUN_IPP(ippiMean_StdDev_C3CMR, src_data, (int)src_step, mask, (int)mask_step, sz, 2, &pmean[1], &pstddev[1]) >= 0 && + CV_INSTRUMENT_FUN_IPP(ippiMean_StdDev_C3CMR, src_data, (int)src_step, mask, (int)mask_step, sz, 3, &pmean[2], &pstddev[2]) >= 0 ) + { + return CV_HAL_ERROR_OK; + } + } + } + else + { + typedef IppStatus (CV_STDCALL* ippiMeanStdDevFuncC1)(const void *, int, IppiSize, Ipp64f *, Ipp64f *); + ippiMeanStdDevFuncC1 ippiMean_StdDev_C1R = + src_type == CV_8UC1 ? (ippiMeanStdDevFuncC1)ippiMean_StdDev_8u_C1R : + src_type == CV_16UC1 ? (ippiMeanStdDevFuncC1)ippiMean_StdDev_16u_C1R : + #if (IPP_VERSION_X100 >= 810) + src_type == CV_32FC1 ? (ippiMeanStdDevFuncC1)ippiMean_StdDev_32f_C1R ://Aug 2013: bug in IPP 7.1, 8.0 + #endif + nullptr; + if( ippiMean_StdDev_C1R ) + { + if( CV_INSTRUMENT_FUN_IPP(ippiMean_StdDev_C1R, src_data, (int)src_step, sz, pmean, pstddev) >= 0 ) + { + return CV_HAL_ERROR_OK; + } + } + + typedef IppStatus (CV_STDCALL* ippiMeanStdDevFuncC3)(const void *, int, IppiSize, int, Ipp64f *, Ipp64f *); + ippiMeanStdDevFuncC3 ippiMean_StdDev_C3CR = + src_type == CV_8UC3 ? (ippiMeanStdDevFuncC3)ippiMean_StdDev_8u_C3CR : + src_type == CV_16UC3 ? (ippiMeanStdDevFuncC3)ippiMean_StdDev_16u_C3CR : + src_type == CV_32FC3 ? (ippiMeanStdDevFuncC3)ippiMean_StdDev_32f_C3CR : + nullptr; + if( ippiMean_StdDev_C3CR ) + { + if( CV_INSTRUMENT_FUN_IPP(ippiMean_StdDev_C3CR, src_data, (int)src_step, sz, 1, &pmean[0], &pstddev[0]) >= 0 && + CV_INSTRUMENT_FUN_IPP(ippiMean_StdDev_C3CR, src_data, (int)src_step, sz, 2, &pmean[1], &pstddev[1]) >= 0 && + CV_INSTRUMENT_FUN_IPP(ippiMean_StdDev_C3CR, src_data, (int)src_step, sz, 3, &pmean[2], &pstddev[2]) >= 0 ) + { + return CV_HAL_ERROR_OK; + } + } + } + } + + return CV_HAL_ERROR_NOT_IMPLEMENTED; +} + +int ipp_hal_meanStdDev(const uchar* src_data, size_t src_step, int width, int height, int src_type, + double* mean_val, double* stddev_val, uchar* mask, size_t mask_step) +{ + if (stddev_val) + { + return ipp_meanStdDev(src_data, src_step, width, height, src_type, mean_val, stddev_val, mask, mask_step); + } + else + { + return ipp_mean(src_data, src_step, width, height, src_type, mean_val, mask, mask_step); + } +} + + +#endif // IPP_VERSION_X100 >= 700 diff --git a/CMakeLists.txt b/CMakeLists.txt index 6b607b943f..8fbfd0563d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -952,7 +952,15 @@ if(NOT DEFINED OpenCV_HAL) set(OpenCV_HAL "OpenCV_HAL") endif() +if(HAVE_IPP) + ocv_debug_message(STATUS "Enable IPP acceleration") + if(NOT ";${OpenCV_HAL};" MATCHES ";ipp;") + set(OpenCV_HAL "ipp;${OpenCV_HAL}") + endif() +endif() + if(HAVE_OPENVX) + ocv_debug_message(STATUS "Enable OpenVX acceleration") if(NOT ";${OpenCV_HAL};" MATCHES ";openvx;") set(OpenCV_HAL "openvx;${OpenCV_HAL}") endif() @@ -1030,6 +1038,10 @@ foreach(hal ${OpenCV_HAL}) else() message(STATUS "HAL RVV: RVV is not available, disabling halrvv...") endif() + elseif(hal STREQUAL "ipp") + add_subdirectory(3rdparty/ipphal) + ocv_hal_register(IPP_HAL_LIBRARIES IPP_HAL_HEADERS IPP_HAL_INCLUDE_DIRS) + list(APPEND OpenCV_USED_HAL "ipp (ver ${IPP_HAL_VERSION})") elseif(hal STREQUAL "openvx") add_subdirectory(3rdparty/openvx) ocv_hal_register(OPENVX_HAL_LIBRARIES OPENVX_HAL_HEADERS OPENVX_HAL_INCLUDE_DIRS) diff --git a/modules/core/src/mean.dispatch.cpp b/modules/core/src/mean.dispatch.cpp index 8712cf8707..1e547bd0db 100644 --- a/modules/core/src/mean.dispatch.cpp +++ b/modules/core/src/mean.dispatch.cpp @@ -28,99 +28,6 @@ namespace cv { -#if defined HAVE_IPP -static bool ipp_mean( Mat &src, Mat &mask, Scalar &ret ) -{ - CV_INSTRUMENT_REGION_IPP(); - -#if IPP_VERSION_X100 >= 700 - size_t total_size = src.total(); - int cn = src.channels(); - if (cn > 4) - return false; - int rows = src.size[0], cols = rows ? (int)(total_size/rows) : 0; - if( src.dims == 2 || (src.isContinuous() && mask.isContinuous() && cols > 0 && (size_t)rows*cols == total_size) ) - { - IppiSize sz = { cols, rows }; - int type = src.type(); - if( !mask.empty() ) - { - typedef IppStatus (CV_STDCALL* ippiMaskMeanFuncC1)(const void *, int, const void *, int, IppiSize, Ipp64f *); - ippiMaskMeanFuncC1 ippiMean_C1MR = - type == CV_8UC1 ? (ippiMaskMeanFuncC1)ippiMean_8u_C1MR : - type == CV_16UC1 ? (ippiMaskMeanFuncC1)ippiMean_16u_C1MR : - type == CV_32FC1 ? (ippiMaskMeanFuncC1)ippiMean_32f_C1MR : - 0; - if( ippiMean_C1MR ) - { - Ipp64f res; - if( CV_INSTRUMENT_FUN_IPP(ippiMean_C1MR, src.ptr(), (int)src.step[0], mask.ptr(), (int)mask.step[0], sz, &res) >= 0 ) - { - ret = Scalar(res); - return true; - } - } - typedef IppStatus (CV_STDCALL* ippiMaskMeanFuncC3)(const void *, int, const void *, int, IppiSize, int, Ipp64f *); - ippiMaskMeanFuncC3 ippiMean_C3MR = - type == CV_8UC3 ? (ippiMaskMeanFuncC3)ippiMean_8u_C3CMR : - type == CV_16UC3 ? (ippiMaskMeanFuncC3)ippiMean_16u_C3CMR : - type == CV_32FC3 ? (ippiMaskMeanFuncC3)ippiMean_32f_C3CMR : - 0; - if( ippiMean_C3MR ) - { - Ipp64f res1, res2, res3; - if( CV_INSTRUMENT_FUN_IPP(ippiMean_C3MR, src.ptr(), (int)src.step[0], mask.ptr(), (int)mask.step[0], sz, 1, &res1) >= 0 && - CV_INSTRUMENT_FUN_IPP(ippiMean_C3MR, src.ptr(), (int)src.step[0], mask.ptr(), (int)mask.step[0], sz, 2, &res2) >= 0 && - CV_INSTRUMENT_FUN_IPP(ippiMean_C3MR, src.ptr(), (int)src.step[0], mask.ptr(), (int)mask.step[0], sz, 3, &res3) >= 0 ) - { - ret = Scalar(res1, res2, res3); - return true; - } - } - } - else - { - typedef IppStatus (CV_STDCALL* ippiMeanFuncHint)(const void*, int, IppiSize, double *, IppHintAlgorithm); - typedef IppStatus (CV_STDCALL* ippiMeanFuncNoHint)(const void*, int, IppiSize, double *); - ippiMeanFuncHint ippiMeanHint = - type == CV_32FC1 ? (ippiMeanFuncHint)ippiMean_32f_C1R : - type == CV_32FC3 ? (ippiMeanFuncHint)ippiMean_32f_C3R : - type == CV_32FC4 ? (ippiMeanFuncHint)ippiMean_32f_C4R : - 0; - ippiMeanFuncNoHint ippiMean = - type == CV_8UC1 ? (ippiMeanFuncNoHint)ippiMean_8u_C1R : - type == CV_8UC3 ? (ippiMeanFuncNoHint)ippiMean_8u_C3R : - type == CV_8UC4 ? (ippiMeanFuncNoHint)ippiMean_8u_C4R : - type == CV_16UC1 ? (ippiMeanFuncNoHint)ippiMean_16u_C1R : - type == CV_16UC3 ? (ippiMeanFuncNoHint)ippiMean_16u_C3R : - type == CV_16UC4 ? (ippiMeanFuncNoHint)ippiMean_16u_C4R : - type == CV_16SC1 ? (ippiMeanFuncNoHint)ippiMean_16s_C1R : - type == CV_16SC3 ? (ippiMeanFuncNoHint)ippiMean_16s_C3R : - type == CV_16SC4 ? (ippiMeanFuncNoHint)ippiMean_16s_C4R : - 0; - // Make sure only zero or one version of the function pointer is valid - CV_Assert(!ippiMeanHint || !ippiMean); - if( ippiMeanHint || ippiMean ) - { - Ipp64f res[4]; - IppStatus status = ippiMeanHint ? CV_INSTRUMENT_FUN_IPP(ippiMeanHint, src.ptr(), (int)src.step[0], sz, res, ippAlgHintAccurate) : - CV_INSTRUMENT_FUN_IPP(ippiMean, src.ptr(), (int)src.step[0], sz, res); - if( status >= 0 ) - { - for( int i = 0; i < cn; i++ ) - ret[i] = res[i]; - return true; - } - } - } - } - return false; -#else - return false; -#endif -} -#endif - Scalar mean(InputArray _src, InputArray _mask) { CV_INSTRUMENT_REGION(); @@ -327,143 +234,6 @@ static bool ocl_meanStdDev( InputArray _src, OutputArray _mean, OutputArray _sdv } #endif -#ifdef HAVE_IPP -static bool ipp_meanStdDev(Mat& src, OutputArray _mean, OutputArray _sdv, Mat& mask) -{ - CV_INSTRUMENT_REGION_IPP(); - -#if IPP_VERSION_X100 >= 700 - int cn = src.channels(); - -#if IPP_VERSION_X100 < 201801 - // IPP_DISABLE: C3C functions can read outside of allocated memory - if (cn > 1) - return false; -#endif -#if IPP_VERSION_X100 >= 201900 && IPP_VERSION_X100 < 201901 - // IPP_DISABLE: 32f C3C functions can read outside of allocated memory - if (cn > 1 && src.depth() == CV_32F) - return false; - - // SSE4.2 buffer overrun -#if defined(_WIN32) && !defined(_WIN64) - // IPPICV doesn't have AVX2 in 32-bit builds - // However cv::ipp::getIppTopFeatures() may return AVX2 value on AVX2 capable H/W - // details #12959 -#else - if (cv::ipp::getIppTopFeatures() == ippCPUID_SSE42) // Linux x64 + OPENCV_IPP=SSE42 is affected too -#endif - { - if (src.depth() == CV_32F && src.dims > 1 && src.size[src.dims - 1] == 6) - return false; - } -#endif - - size_t total_size = src.total(); - int rows = src.size[0], cols = rows ? (int)(total_size/rows) : 0; - if( src.dims == 2 || (src.isContinuous() && mask.isContinuous() && cols > 0 && (size_t)rows*cols == total_size) ) - { - Ipp64f mean_temp[3]; - Ipp64f stddev_temp[3]; - Ipp64f *pmean = &mean_temp[0]; - Ipp64f *pstddev = &stddev_temp[0]; - Mat mean, stddev; - int dcn_mean = -1; - if( _mean.needed() ) - { - if( !_mean.fixedSize() ) - _mean.create(cn, 1, CV_64F, -1, true); - mean = _mean.getMat(); - dcn_mean = (int)mean.total(); - pmean = mean.ptr(); - } - int dcn_stddev = -1; - if( _sdv.needed() ) - { - if( !_sdv.fixedSize() ) - _sdv.create(cn, 1, CV_64F, -1, true); - stddev = _sdv.getMat(); - dcn_stddev = (int)stddev.total(); - pstddev = stddev.ptr(); - } - for( int c = cn; c < dcn_mean; c++ ) - pmean[c] = 0; - for( int c = cn; c < dcn_stddev; c++ ) - pstddev[c] = 0; - IppiSize sz = { cols, rows }; - int type = src.type(); - if( !mask.empty() ) - { - typedef IppStatus (CV_STDCALL* ippiMaskMeanStdDevFuncC1)(const void *, int, const void *, int, IppiSize, Ipp64f *, Ipp64f *); - ippiMaskMeanStdDevFuncC1 ippiMean_StdDev_C1MR = - type == CV_8UC1 ? (ippiMaskMeanStdDevFuncC1)ippiMean_StdDev_8u_C1MR : - type == CV_16UC1 ? (ippiMaskMeanStdDevFuncC1)ippiMean_StdDev_16u_C1MR : - type == CV_32FC1 ? (ippiMaskMeanStdDevFuncC1)ippiMean_StdDev_32f_C1MR : - 0; - if( ippiMean_StdDev_C1MR ) - { - if( CV_INSTRUMENT_FUN_IPP(ippiMean_StdDev_C1MR, src.ptr(), (int)src.step[0], mask.ptr(), (int)mask.step[0], sz, pmean, pstddev) >= 0 ) - { - return true; - } - } - typedef IppStatus (CV_STDCALL* ippiMaskMeanStdDevFuncC3)(const void *, int, const void *, int, IppiSize, int, Ipp64f *, Ipp64f *); - ippiMaskMeanStdDevFuncC3 ippiMean_StdDev_C3CMR = - type == CV_8UC3 ? (ippiMaskMeanStdDevFuncC3)ippiMean_StdDev_8u_C3CMR : - type == CV_16UC3 ? (ippiMaskMeanStdDevFuncC3)ippiMean_StdDev_16u_C3CMR : - type == CV_32FC3 ? (ippiMaskMeanStdDevFuncC3)ippiMean_StdDev_32f_C3CMR : - 0; - if( ippiMean_StdDev_C3CMR ) - { - if( CV_INSTRUMENT_FUN_IPP(ippiMean_StdDev_C3CMR, src.ptr(), (int)src.step[0], mask.ptr(), (int)mask.step[0], sz, 1, &pmean[0], &pstddev[0]) >= 0 && - CV_INSTRUMENT_FUN_IPP(ippiMean_StdDev_C3CMR, src.ptr(), (int)src.step[0], mask.ptr(), (int)mask.step[0], sz, 2, &pmean[1], &pstddev[1]) >= 0 && - CV_INSTRUMENT_FUN_IPP(ippiMean_StdDev_C3CMR, src.ptr(), (int)src.step[0], mask.ptr(), (int)mask.step[0], sz, 3, &pmean[2], &pstddev[2]) >= 0 ) - { - return true; - } - } - } - else - { - typedef IppStatus (CV_STDCALL* ippiMeanStdDevFuncC1)(const void *, int, IppiSize, Ipp64f *, Ipp64f *); - ippiMeanStdDevFuncC1 ippiMean_StdDev_C1R = - type == CV_8UC1 ? (ippiMeanStdDevFuncC1)ippiMean_StdDev_8u_C1R : - type == CV_16UC1 ? (ippiMeanStdDevFuncC1)ippiMean_StdDev_16u_C1R : -#if (IPP_VERSION_X100 >= 810) - type == CV_32FC1 ? (ippiMeanStdDevFuncC1)ippiMean_StdDev_32f_C1R ://Aug 2013: bug in IPP 7.1, 8.0 -#endif - 0; - if( ippiMean_StdDev_C1R ) - { - if( CV_INSTRUMENT_FUN_IPP(ippiMean_StdDev_C1R, src.ptr(), (int)src.step[0], sz, pmean, pstddev) >= 0 ) - { - return true; - } - } - typedef IppStatus (CV_STDCALL* ippiMeanStdDevFuncC3)(const void *, int, IppiSize, int, Ipp64f *, Ipp64f *); - ippiMeanStdDevFuncC3 ippiMean_StdDev_C3CR = - type == CV_8UC3 ? (ippiMeanStdDevFuncC3)ippiMean_StdDev_8u_C3CR : - type == CV_16UC3 ? (ippiMeanStdDevFuncC3)ippiMean_StdDev_16u_C3CR : - type == CV_32FC3 ? (ippiMeanStdDevFuncC3)ippiMean_StdDev_32f_C3CR : - 0; - if( ippiMean_StdDev_C3CR ) - { - if( CV_INSTRUMENT_FUN_IPP(ippiMean_StdDev_C3CR, src.ptr(), (int)src.step[0], sz, 1, &pmean[0], &pstddev[0]) >= 0 && - CV_INSTRUMENT_FUN_IPP(ippiMean_StdDev_C3CR, src.ptr(), (int)src.step[0], sz, 2, &pmean[1], &pstddev[1]) >= 0 && - CV_INSTRUMENT_FUN_IPP(ippiMean_StdDev_C3CR, src.ptr(), (int)src.step[0], sz, 3, &pmean[2], &pstddev[2]) >= 0 ) - { - return true; - } - } - } - } -#else - CV_UNUSED(src); CV_UNUSED(_mean); CV_UNUSED(_sdv); CV_UNUSED(mask); -#endif - return false; -} -#endif - void meanStdDev(InputArray _src, OutputArray _mean, OutputArray _sdv, InputArray _mask) { CV_INSTRUMENT_REGION(); @@ -478,8 +248,6 @@ void meanStdDev(InputArray _src, OutputArray _mean, OutputArray _sdv, InputArray CV_Assert(mask.empty() || src.size == mask.size); - CV_IPP_RUN(IPP_VERSION_X100 >= 700, ipp_meanStdDev(src, _mean, _sdv, mask)); - int k, cn = src.channels(), depth = src.depth(); Mat mean_mat, stddev_mat; From 64535757dff0a54dd4a2ffa7b0d00982e0f5cf69 Mon Sep 17 00:00:00 2001 From: Aditya Jha Date: Sun, 23 Mar 2025 13:18:17 +0530 Subject: [PATCH 35/94] Add documentation for StereoBM parameters (fixes #26816) --- modules/calib3d/include/opencv2/calib3d.hpp | 119 +++++++++++++++++--- 1 file changed, 102 insertions(+), 17 deletions(-) diff --git a/modules/calib3d/include/opencv2/calib3d.hpp b/modules/calib3d/include/opencv2/calib3d.hpp index 6e3ed8c3b5..31961a369a 100644 --- a/modules/calib3d/include/opencv2/calib3d.hpp +++ b/modules/calib3d/include/opencv2/calib3d.hpp @@ -3346,52 +3346,137 @@ public: }; -/** @brief Class for computing stereo correspondence using the block matching algorithm, introduced and -contributed to OpenCV by K. Konolige. +/** + * @class StereoBM + * @brief Class for computing stereo correspondence using the block matching algorithm, introduced and contributed to OpenCV by K. Konolige. + * @details This class implements a block matching algorithm for stereo correspondence, which is used to compute disparity maps from stereo image pairs. It provides methods to fine-tune parameters such as pre-filtering, texture thresholds, uniqueness ratios, and regions of interest (ROIs) to optimize performance and accuracy. */ class CV_EXPORTS_W StereoBM : public StereoMatcher { public: - enum { PREFILTER_NORMALIZED_RESPONSE = 0, - PREFILTER_XSOBEL = 1 - }; + /** + * @brief Pre-filter types for the stereo matching algorithm. + * @details These constants define the type of pre-filtering applied to the images before computing the disparity map. + * - PREFILTER_NORMALIZED_RESPONSE: Uses normalized response for pre-filtering. + * - PREFILTER_XSOBEL: Uses the X-Sobel operator for pre-filtering. + */ + enum { + PREFILTER_NORMALIZED_RESPONSE = 0, ///< Normalized response pre-filter + PREFILTER_XSOBEL = 1 ///< X-Sobel pre-filter + }; + /** + * @brief Gets the type of pre-filtering currently used in the algorithm. + * @return The current pre-filter type: 0 for PREFILTER_NORMALIZED_RESPONSE or 1 for PREFILTER_XSOBEL. + */ CV_WRAP virtual int getPreFilterType() const = 0; + + /** + * @brief Sets the type of pre-filtering used in the algorithm. + * @param preFilterType The type of pre-filter to use. Possible values are: + * - PREFILTER_NORMALIZED_RESPONSE (0): Uses normalized response for pre-filtering. + * - PREFILTER_XSOBEL (1): Uses the X-Sobel operator for pre-filtering. + * @details The pre-filter type affects how the images are prepared before computing the disparity map. Different pre-filtering methods can enhance specific image features or reduce noise, influencing the quality of the disparity map. + */ CV_WRAP virtual void setPreFilterType(int preFilterType) = 0; + /** + * @brief Gets the current size of the pre-filter kernel. + * @return The current pre-filter size. + */ CV_WRAP virtual int getPreFilterSize() const = 0; + + /** + * @brief Sets the size of the pre-filter kernel. + * @param preFilterSize The size of the pre-filter kernel. Must be an odd integer, typically between 5 and 255. + * @details The pre-filter size determines the spatial extent of the pre-filtering operation, which prepares the images for disparity computation by normalizing brightness and enhancing texture. Larger sizes reduce noise but may blur details, while smaller sizes preserve details but are more susceptible to noise. + */ CV_WRAP virtual void setPreFilterSize(int preFilterSize) = 0; + /** + * @brief Gets the current truncation value for prefiltered pixels. + * @return The current pre-filter cap value. + */ CV_WRAP virtual int getPreFilterCap() const = 0; + + /** + * @brief Sets the truncation value for prefiltered pixels. + * @param preFilterCap The truncation value. Typically in the range [1, 63]. + * @details This value caps the output of the pre-filter to [-preFilterCap, preFilterCap], helping to reduce the impact of noise and outliers in the pre-filtered image. + */ CV_WRAP virtual void setPreFilterCap(int preFilterCap) = 0; + /** + * @brief Gets the current texture threshold value. + * @return The current texture threshold. + */ CV_WRAP virtual int getTextureThreshold() const = 0; + + /** + * @brief Sets the threshold for filtering low-texture regions. + * @param textureThreshold The threshold value. Must be non-negative. + * @details This parameter filters out regions with low texture, where establishing correspondences is difficult, thus reducing noise in the disparity map. Higher values filter more aggressively but may discard valid information. + */ CV_WRAP virtual void setTextureThreshold(int textureThreshold) = 0; + /** + * @brief Gets the current uniqueness ratio value. + * @return The current uniqueness ratio. + */ CV_WRAP virtual int getUniquenessRatio() const = 0; + + /** + * @brief Sets the uniqueness ratio for filtering ambiguous matches. + * @param uniquenessRatio The uniqueness ratio value. Typically in the range [5, 15], but can be from 0 to 100. + * @details This parameter ensures that the best match is sufficiently better than the next best match, reducing false positives. Higher values are stricter but may filter out valid matches in difficult regions. + */ CV_WRAP virtual void setUniquenessRatio(int uniquenessRatio) = 0; + /** + * @brief Gets the current size of the smaller block used for texture check. + * @return The current smaller block size. + */ CV_WRAP virtual int getSmallerBlockSize() const = 0; + + /** + * @brief Sets the size of the smaller block used for texture check. + * @param blockSize The size of the smaller block. Must be an odd integer between 5 and 255. + * @details This parameter determines the size of the block used to compute texture variance. Smaller blocks capture finer details but are more sensitive to noise, while larger blocks are more robust but may miss fine details. + */ CV_WRAP virtual void setSmallerBlockSize(int blockSize) = 0; + /** + * @brief Gets the current Region of Interest (ROI) for the left image. + * @return The current ROI for the left image. + */ CV_WRAP virtual Rect getROI1() const = 0; + + /** + * @brief Sets the Region of Interest (ROI) for the left image. + * @param roi1 The ROI rectangle for the left image. + * @details By setting the ROI, the stereo matching computation is limited to the specified region, improving performance and potentially accuracy by focusing on relevant parts of the image. + */ CV_WRAP virtual void setROI1(Rect roi1) = 0; + /** + * @brief Gets the current Region of Interest (ROI) for the right image. + * @return The current ROI for the right image. + */ CV_WRAP virtual Rect getROI2() const = 0; + + /** + * @brief Sets the Region of Interest (ROI) for the right image. + * @param roi2 The ROI rectangle for the right image. + * @details Similar to setROI1, this limits the computation to the specified region in the right image. + */ CV_WRAP virtual void setROI2(Rect roi2) = 0; - /** @brief Creates StereoBM object - - @param numDisparities the disparity search range. For each pixel algorithm will find the best - disparity from 0 (default minimum disparity) to numDisparities. The search range can then be - shifted by changing the minimum disparity. - @param blockSize the linear size of the blocks compared by the algorithm. The size should be odd - (as the block is centered at the current pixel). Larger block size implies smoother, though less - accurate disparity map. Smaller block size gives more detailed disparity map, but there is higher - chance for algorithm to find a wrong correspondence. - - The function create StereoBM object. You can then call StereoBM::compute() to compute disparity for - a specific stereo pair. + /** + * @brief Creates StereoBM object + * @param numDisparities The disparity search range. For each pixel, the algorithm will find the best disparity from 0 (default minimum disparity) to numDisparities. The search range can be shifted by changing the minimum disparity. + * @param blockSize The linear size of the blocks compared by the algorithm. The size should be odd (as the block is centered at the current pixel). Larger block size implies smoother, though less accurate disparity map. Smaller block size gives more detailed disparity map, but there is a higher chance for the algorithm to find a wrong correspondence. + * @return A pointer to the created StereoBM object. + * @details The function creates a StereoBM object. You can then call StereoBM::compute() to compute disparity for a specific stereo pair. */ CV_WRAP static Ptr create(int numDisparities = 0, int blockSize = 21); }; From 5db60e16210f55e2d322c2a7b6f1f8eaa4db7363 Mon Sep 17 00:00:00 2001 From: MaximSmolskiy Date: Sun, 23 Mar 2025 18:24:10 +0300 Subject: [PATCH 36/94] Add planar accuracy tests for solvePnPRansac --- modules/calib3d/test/test_solvepnp_ransac.cpp | 31 ++++++++++++++++--- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/modules/calib3d/test/test_solvepnp_ransac.cpp b/modules/calib3d/test/test_solvepnp_ransac.cpp index a16928c738..99fa451ef5 100644 --- a/modules/calib3d/test/test_solvepnp_ransac.cpp +++ b/modules/calib3d/test/test_solvepnp_ransac.cpp @@ -209,9 +209,27 @@ public: eps[SOLVEPNP_AP3P] = 1.0e-2; eps[SOLVEPNP_DLS] = 1.0e-2; eps[SOLVEPNP_UPNP] = 1.0e-2; + eps[SOLVEPNP_IPPE] = 1.0e-2; + eps[SOLVEPNP_IPPE_SQUARE] = 1.0e-2; eps[SOLVEPNP_SQPNP] = 1.0e-2; - totalTestsCount = 10; - pointsCount = 500; + + totalTestsCount = 1000; + + if (planar || planarTag) + { + if (planarTag) + { + pointsCount = 4; + } + else + { + pointsCount = 30; + } + } + else + { + pointsCount = 500; + } } ~CV_solvePnPRansac_Test() {} protected: @@ -320,17 +338,20 @@ protected: vector projectedPoints; projectedPoints.resize(points.size()); projectPoints(points, trueRvec, trueTvec, intrinsics, distCoeffs, projectedPoints); + + size_t numOutliers = 0; for (size_t i = 0; i < projectedPoints.size(); i++) { - if (i % 20 == 0) + if (!planarTag && rng.uniform(0., 1.) > 0.95) { projectedPoints[i] = projectedPoints[rng.uniform(0,(int)points.size()-1)]; + numOutliers++; } } solvePnPRansac(points, projectedPoints, intrinsics, distCoeffs, rvec, tvec, false, pointsCount, 0.5f, 0.99, inliers, method); - bool isTestSuccess = inliers.size() >= points.size()*0.95; + bool isTestSuccess = inliers.size() + numOutliers >= points.size(); double rvecDiff = cvtest::norm(rvec, trueRvec, NORM_L2), tvecDiff = cvtest::norm(tvec, trueTvec, NORM_L2); isTestSuccess = isTestSuccess && rvecDiff < eps[method] && tvecDiff < eps[method]; @@ -703,6 +724,8 @@ protected: TEST(Calib3d_SolveP3P, accuracy) { CV_solveP3P_Test test; test.safe_run();} TEST(Calib3d_SolvePnPRansac, accuracy) { CV_solvePnPRansac_Test test; test.safe_run(); } +TEST(Calib3d_SolvePnPRansac, accuracy_planar) { CV_solvePnPRansac_Test test(true); test.safe_run(); } +TEST(Calib3d_SolvePnPRansac, accuracy_planar_tag) { CV_solvePnPRansac_Test test(true, true); test.safe_run(); } TEST(Calib3d_SolvePnP, accuracy) { CV_solvePnP_Test test; test.safe_run(); } TEST(Calib3d_SolvePnP, accuracy_planar) { CV_solvePnP_Test test(true); test.safe_run(); } TEST(Calib3d_SolvePnP, accuracy_planar_tag) { CV_solvePnP_Test test(true, true); test.safe_run(); } From ef474e06fc64bd48a4ff8ef021ba88907b57096c Mon Sep 17 00:00:00 2001 From: shyama7004 Date: Sun, 23 Mar 2025 23:38:33 +0530 Subject: [PATCH 37/94] minor changes : Replace ndarray.ptp() with np.ptp() for NumPy 2.0 Compatibility --- samples/python/mosse.py | 2 +- samples/python/turing.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/python/mosse.py b/samples/python/mosse.py index e11e9215c6..04d3cf8763 100755 --- a/samples/python/mosse.py +++ b/samples/python/mosse.py @@ -107,7 +107,7 @@ class MOSSE: h, w = f.shape f = np.roll(f, -h//2, 0) f = np.roll(f, -w//2, 1) - kernel = np.uint8( (f-f.min()) / f.ptp()*255 ) + kernel = np.uint8( (f-f.min()) / np.ptp(f)*255 ) resp = self.last_resp resp = np.uint8(np.clip(resp/resp.max(), 0, 1)*255) vis = np.hstack([self.last_img, kernel, resp]) diff --git a/samples/python/turing.py b/samples/python/turing.py index dc920d1295..d8f517cf9f 100755 --- a/samples/python/turing.py +++ b/samples/python/turing.py @@ -62,7 +62,7 @@ def main(): vs.append(v) mi = np.argmin(vs, 0) a += np.choose(mi, ms) * 0.025 - a = (a-a.min()) / a.ptp() + a = (a-a.min()) / np.ptp(a) if out: out.write(a) From 0944f7ad260bbc46883ea54835a0b4a9080806d8 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov <2536374+asmorkalov@users.noreply.github.com> Date: Mon, 24 Mar 2025 09:17:22 +0300 Subject: [PATCH 38/94] Merge pull request #27128 from asmorkalov:as/ipp_norm Move IPP norm and normDiff to HAL #27128 Continues https://github.com/opencv/opencv/pull/26880 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [ ] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- 3rdparty/ipphal/CMakeLists.txt | 4 +- 3rdparty/ipphal/include/ipp_hal_core.hpp | 16 + 3rdparty/ipphal/src/norm_ipp.cpp | 343 +++++++++++++++++++++ modules/core/src/norm.dispatch.cpp | 377 +---------------------- 4 files changed, 364 insertions(+), 376 deletions(-) create mode 100644 3rdparty/ipphal/src/norm_ipp.cpp diff --git a/3rdparty/ipphal/CMakeLists.txt b/3rdparty/ipphal/CMakeLists.txt index 6d0f368957..dc1ec627cb 100644 --- a/3rdparty/ipphal/CMakeLists.txt +++ b/3rdparty/ipphal/CMakeLists.txt @@ -7,7 +7,9 @@ set(IPP_HAL_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/include/ipp_hal_core.hpp" CACHE INTERNAL "") -add_library(ipphal STATIC "${CMAKE_CURRENT_SOURCE_DIR}/src/mean_ipp.cpp") +add_library(ipphal STATIC + "${CMAKE_CURRENT_SOURCE_DIR}/src/mean_ipp.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/norm_ipp.cpp") if(HAVE_IPP_ICV) target_compile_definitions(ipphal PRIVATE HAVE_IPP_ICV) diff --git a/3rdparty/ipphal/include/ipp_hal_core.hpp b/3rdparty/ipphal/include/ipp_hal_core.hpp index ad9c3984a6..beb1524ac7 100644 --- a/3rdparty/ipphal/include/ipp_hal_core.hpp +++ b/3rdparty/ipphal/include/ipp_hal_core.hpp @@ -10,6 +10,22 @@ int ipp_hal_meanStdDev(const uchar* src_data, size_t src_step, int width, int he #undef cv_hal_meanStdDev #define cv_hal_meanStdDev ipp_hal_meanStdDev + +#define IPP_DISABLE_NORM_8U 1 // accuracy difference in perf test sanity check + +int ipp_hal_norm(const uchar* src, size_t src_step, const uchar* mask, size_t mask_step, + int width, int height, int type, int norm_type, double* result); + +#undef cv_hal_norm +#define cv_hal_norm ipp_hal_norm + + +int ipp_hal_normDiff(const uchar* src1, size_t src1_step, const uchar* src2, size_t src2_step, const uchar* mask, + size_t mask_step, int width, int height, int type, int norm_type, double* result); + +#undef cv_hal_normDiff +#define cv_hal_normDiff ipp_hal_normDiff + #endif #endif diff --git a/3rdparty/ipphal/src/norm_ipp.cpp b/3rdparty/ipphal/src/norm_ipp.cpp new file mode 100644 index 0000000000..5fc4609edb --- /dev/null +++ b/3rdparty/ipphal/src/norm_ipp.cpp @@ -0,0 +1,343 @@ +#include "ipp_hal_core.hpp" + +#include +#include + +#if IPP_VERSION_X100 >= 700 + +int ipp_hal_norm(const uchar* src, size_t src_step, const uchar* mask, size_t mask_step, + int width, int height, int type, int norm_type, double* result) +{ + if( mask ) + { + IppiSize sz = { width, height }; + + typedef IppStatus (CV_STDCALL* ippiMaskNormFuncC1)(const void *, int, const void *, int, IppiSize, Ipp64f *); + ippiMaskNormFuncC1 ippiNorm_C1MR = + norm_type == cv::NORM_INF ? + (type == CV_8UC1 ? (ippiMaskNormFuncC1)ippiNorm_Inf_8u_C1MR : + type == CV_16UC1 ? (ippiMaskNormFuncC1)ippiNorm_Inf_16u_C1MR : + type == CV_32FC1 ? (ippiMaskNormFuncC1)ippiNorm_Inf_32f_C1MR : + 0) : + norm_type == cv::NORM_L1 ? + (type == CV_8UC1 ? (ippiMaskNormFuncC1)ippiNorm_L1_8u_C1MR : + type == CV_16UC1 ? (ippiMaskNormFuncC1)ippiNorm_L1_16u_C1MR : + type == CV_32FC1 ? (ippiMaskNormFuncC1)ippiNorm_L1_32f_C1MR : + 0) : + norm_type == cv::NORM_L2 || norm_type == cv::NORM_L2SQR ? + (type == CV_8UC1 ? (ippiMaskNormFuncC1)ippiNorm_L2_8u_C1MR : + type == CV_16UC1 ? (ippiMaskNormFuncC1)ippiNorm_L2_16u_C1MR : + type == CV_32FC1 ? (ippiMaskNormFuncC1)ippiNorm_L2_32f_C1MR : + 0) : 0; + if( ippiNorm_C1MR ) + { + Ipp64f norm; + if( CV_INSTRUMENT_FUN_IPP(ippiNorm_C1MR, src, (int)src_step, mask, (int)mask_step, sz, &norm) >= 0 ) + { + *result = (norm_type == cv::NORM_L2SQR ? (double)(norm * norm) : (double)norm); + return CV_HAL_ERROR_OK; + } + } + typedef IppStatus (CV_STDCALL* ippiMaskNormFuncC3)(const void *, int, const void *, int, IppiSize, int, Ipp64f *); + ippiMaskNormFuncC3 ippiNorm_C3CMR = + norm_type == cv::NORM_INF ? + (type == CV_8UC3 ? (ippiMaskNormFuncC3)ippiNorm_Inf_8u_C3CMR : + type == CV_16UC3 ? (ippiMaskNormFuncC3)ippiNorm_Inf_16u_C3CMR : + type == CV_32FC3 ? (ippiMaskNormFuncC3)ippiNorm_Inf_32f_C3CMR : + 0) : + norm_type == cv::NORM_L1 ? + (type == CV_8UC3 ? (ippiMaskNormFuncC3)ippiNorm_L1_8u_C3CMR : + type == CV_16UC3 ? (ippiMaskNormFuncC3)ippiNorm_L1_16u_C3CMR : + type == CV_32FC3 ? (ippiMaskNormFuncC3)ippiNorm_L1_32f_C3CMR : + 0) : + norm_type == cv::NORM_L2 || norm_type == cv::NORM_L2SQR ? + (type == CV_8UC3 ? (ippiMaskNormFuncC3)ippiNorm_L2_8u_C3CMR : + type == CV_16UC3 ? (ippiMaskNormFuncC3)ippiNorm_L2_16u_C3CMR : + type == CV_32FC3 ? (ippiMaskNormFuncC3)ippiNorm_L2_32f_C3CMR : + 0) : 0; + if( ippiNorm_C3CMR ) + { + Ipp64f norm1, norm2, norm3; + if( CV_INSTRUMENT_FUN_IPP(ippiNorm_C3CMR, src, (int)src_step, mask, (int)mask_step, sz, 1, &norm1) >= 0 && + CV_INSTRUMENT_FUN_IPP(ippiNorm_C3CMR, src, (int)src_step, mask, (int)mask_step, sz, 2, &norm2) >= 0 && + CV_INSTRUMENT_FUN_IPP(ippiNorm_C3CMR, src, (int)src_step, mask, (int)mask_step, sz, 3, &norm3) >= 0) + { + Ipp64f norm = + norm_type == cv::NORM_INF ? std::max(std::max(norm1, norm2), norm3) : + norm_type == cv::NORM_L1 ? norm1 + norm2 + norm3 : + norm_type == cv::NORM_L2 || norm_type == cv::NORM_L2SQR ? std::sqrt(norm1 * norm1 + norm2 * norm2 + norm3 * norm3) : + 0; + *result = (norm_type == cv::NORM_L2SQR ? (double)(norm * norm) : (double)norm); + return CV_HAL_ERROR_OK; + } + } + } + else + { + int cn = CV_MAT_CN(type); + IppiSize sz = { width*cn, height }; + + typedef IppStatus (CV_STDCALL* ippiNormFuncHint)(const void *, int, IppiSize, Ipp64f *, IppHintAlgorithm hint); + typedef IppStatus (CV_STDCALL* ippiNormFuncNoHint)(const void *, int, IppiSize, Ipp64f *); + ippiNormFuncHint ippiNormHint = + norm_type == cv::NORM_L1 ? + (type == CV_32FC1 ? (ippiNormFuncHint)ippiNorm_L1_32f_C1R : + 0) : + norm_type == cv::NORM_L2 || norm_type == cv::NORM_L2SQR ? + (type == CV_32FC1 ? (ippiNormFuncHint)ippiNorm_L2_32f_C1R : + 0) : 0; + ippiNormFuncNoHint ippiNorm = + norm_type == cv::NORM_INF ? + (type == CV_8UC1 ? (ippiNormFuncNoHint)ippiNorm_Inf_8u_C1R : + type == CV_16UC1 ? (ippiNormFuncNoHint)ippiNorm_Inf_16u_C1R : + type == CV_16SC1 ? (ippiNormFuncNoHint)ippiNorm_Inf_16s_C1R : + type == CV_32FC1 ? (ippiNormFuncNoHint)ippiNorm_Inf_32f_C1R : + 0) : + norm_type == cv::NORM_L1 ? + (type == CV_8UC1 ? (ippiNormFuncNoHint)ippiNorm_L1_8u_C1R : + type == CV_16UC1 ? (ippiNormFuncNoHint)ippiNorm_L1_16u_C1R : + type == CV_16SC1 ? (ippiNormFuncNoHint)ippiNorm_L1_16s_C1R : + 0) : + norm_type == cv::NORM_L2 || norm_type == cv::NORM_L2SQR ? + ( + #if (!IPP_DISABLE_NORM_8U) + type == CV_8UC1 ? (ippiNormFuncNoHint)ippiNorm_L2_8u_C1R : + #endif + type == CV_16UC1 ? (ippiNormFuncNoHint)ippiNorm_L2_16u_C1R : + type == CV_16SC1 ? (ippiNormFuncNoHint)ippiNorm_L2_16s_C1R : + 0) : 0; + if( ippiNormHint || ippiNorm ) + { + Ipp64f norm; + IppStatus ret = ippiNormHint ? CV_INSTRUMENT_FUN_IPP(ippiNormHint, src, (int)src_step, sz, &norm, ippAlgHintAccurate) : + CV_INSTRUMENT_FUN_IPP(ippiNorm, src, (int)src_step, sz, &norm); + if( ret >= 0 ) + { + *result = (norm_type == cv::NORM_L2SQR) ? norm * norm : norm; + return CV_HAL_ERROR_OK; + } + } + } + + return CV_HAL_ERROR_NOT_IMPLEMENTED; +} + +int ipp_hal_normDiff(const uchar* src1, size_t src1_step, const uchar* src2, size_t src2_step, const uchar* mask, + size_t mask_step, int width, int height, int type, int norm_type, double* result) +{ + if( norm_type & cv::NORM_RELATIVE ) + { + norm_type &= cv::NORM_TYPE_MASK; + + if( mask ) + { + IppiSize sz = { width, height }; + + typedef IppStatus (CV_STDCALL* ippiMaskNormDiffFuncC1)(const void *, int, const void *, int, const void *, int, IppiSize, Ipp64f *); + ippiMaskNormDiffFuncC1 ippiNormRel_C1MR = + norm_type == cv::NORM_INF ? + (type == CV_8UC1 ? (ippiMaskNormDiffFuncC1)ippiNormRel_Inf_8u_C1MR : + type == CV_16UC1 ? (ippiMaskNormDiffFuncC1)ippiNormRel_Inf_16u_C1MR : + type == CV_32FC1 ? (ippiMaskNormDiffFuncC1)ippiNormRel_Inf_32f_C1MR : + 0) : + norm_type == cv::NORM_L1 ? + (type == CV_8UC1 ? (ippiMaskNormDiffFuncC1)ippiNormRel_L1_8u_C1MR : + type == CV_16UC1 ? (ippiMaskNormDiffFuncC1)ippiNormRel_L1_16u_C1MR : + type == CV_32FC1 ? (ippiMaskNormDiffFuncC1)ippiNormRel_L1_32f_C1MR : + 0) : + norm_type == cv::NORM_L2 || norm_type == cv::NORM_L2SQR ? + (type == CV_8UC1 ? (ippiMaskNormDiffFuncC1)ippiNormRel_L2_8u_C1MR : + type == CV_16UC1 ? (ippiMaskNormDiffFuncC1)ippiNormRel_L2_16u_C1MR : + type == CV_32FC1 ? (ippiMaskNormDiffFuncC1)ippiNormRel_L2_32f_C1MR : + 0) : 0; + if( ippiNormRel_C1MR ) + { + Ipp64f norm; + if( CV_INSTRUMENT_FUN_IPP(ippiNormRel_C1MR, src1, (int)src1_step, src2, (int)src2_step, mask, (int)mask_step, sz, &norm) >= 0 ) + { + *result = (norm_type == cv::NORM_L2SQR ? (double)(norm * norm) : (double)norm); + return CV_HAL_ERROR_OK; + } + } + } + else + { + int cn = CV_MAT_CN(type); + type = CV_MAT_DEPTH(type); + IppiSize sz = { width*cn, height }; + + typedef IppStatus (CV_STDCALL* ippiNormRelFuncHint)(const void *, int, const void *, int, IppiSize, Ipp64f *, IppHintAlgorithm hint); + typedef IppStatus (CV_STDCALL* ippiNormRelFuncNoHint)(const void *, int, const void *, int, IppiSize, Ipp64f *); + ippiNormRelFuncHint ippiNormRelHint = + norm_type == cv::NORM_L1 ? + (type == CV_32F ? (ippiNormRelFuncHint)ippiNormRel_L1_32f_C1R : + 0) : + norm_type == cv::NORM_L2 || norm_type == cv::NORM_L2SQR ? + (type == CV_32F ? (ippiNormRelFuncHint)ippiNormRel_L2_32f_C1R : + 0) : 0; + ippiNormRelFuncNoHint ippiNormRel = + norm_type == cv::NORM_INF ? + ( + #if (!IPP_DISABLE_NORM_8U) + type == CV_8U ? (ippiNormRelFuncNoHint)ippiNormRel_Inf_8u_C1R : + #endif + type == CV_16U ? (ippiNormRelFuncNoHint)ippiNormRel_Inf_16u_C1R : + type == CV_16S ? (ippiNormRelFuncNoHint)ippiNormRel_Inf_16s_C1R : + type == CV_32F ? (ippiNormRelFuncNoHint)ippiNormRel_Inf_32f_C1R : + 0) : + norm_type == cv::NORM_L1 ? + ( + #if (!IPP_DISABLE_NORM_8U) + type == CV_8U ? (ippiNormRelFuncNoHint)ippiNormRel_L1_8u_C1R : + #endif + type == CV_16U ? (ippiNormRelFuncNoHint)ippiNormRel_L1_16u_C1R : + type == CV_16S ? (ippiNormRelFuncNoHint)ippiNormRel_L1_16s_C1R : + 0) : + norm_type == cv::NORM_L2 || norm_type == cv::NORM_L2SQR ? + ( + #if (!IPP_DISABLE_NORM_8U) + type == CV_8U ? (ippiNormRelFuncNoHint)ippiNormRel_L2_8u_C1R : + #endif + type == CV_16U ? (ippiNormRelFuncNoHint)ippiNormRel_L2_16u_C1R : + type == CV_16S ? (ippiNormRelFuncNoHint)ippiNormRel_L2_16s_C1R : + 0) : 0; + if( ippiNormRelHint || ippiNormRel ) + { + Ipp64f norm; + IppStatus ret = ippiNormRelHint ? CV_INSTRUMENT_FUN_IPP(ippiNormRelHint, src1, (int)src1_step, src2, (int)src2_step, sz, &norm, ippAlgHintAccurate) : + CV_INSTRUMENT_FUN_IPP(ippiNormRel, src1, (int)src1_step, src2, (int)src2_step, sz, &norm); + if( ret >= 0 ) + { + *result = (norm_type == cv::NORM_L2SQR) ? norm * norm : norm; + return CV_HAL_ERROR_OK; + } + } + } + return CV_HAL_ERROR_NOT_IMPLEMENTED; + } + + norm_type &= cv::NORM_TYPE_MASK; + + if( mask ) + { + IppiSize sz = { width, height }; + + typedef IppStatus (CV_STDCALL* ippiMaskNormDiffFuncC1)(const void *, int, const void *, int, const void *, int, IppiSize, Ipp64f *); + ippiMaskNormDiffFuncC1 ippiNormDiff_C1MR = + norm_type == cv::NORM_INF ? + (type == CV_8UC1 ? (ippiMaskNormDiffFuncC1)ippiNormDiff_Inf_8u_C1MR : + type == CV_16UC1 ? (ippiMaskNormDiffFuncC1)ippiNormDiff_Inf_16u_C1MR : + type == CV_32FC1 ? (ippiMaskNormDiffFuncC1)ippiNormDiff_Inf_32f_C1MR : + 0) : + norm_type == cv::NORM_L1 ? + (type == CV_8UC1 ? (ippiMaskNormDiffFuncC1)ippiNormDiff_L1_8u_C1MR : + type == CV_16UC1 ? (ippiMaskNormDiffFuncC1)ippiNormDiff_L1_16u_C1MR : + type == CV_32FC1 ? (ippiMaskNormDiffFuncC1)ippiNormDiff_L1_32f_C1MR : + 0) : + norm_type == cv::NORM_L2 || norm_type == cv::NORM_L2SQR ? + (type == CV_8UC1 ? (ippiMaskNormDiffFuncC1)ippiNormDiff_L2_8u_C1MR : + type == CV_16UC1 ? (ippiMaskNormDiffFuncC1)ippiNormDiff_L2_16u_C1MR : + type == CV_32FC1 ? (ippiMaskNormDiffFuncC1)ippiNormDiff_L2_32f_C1MR : + 0) : 0; + if( ippiNormDiff_C1MR ) + { + Ipp64f norm; + if( CV_INSTRUMENT_FUN_IPP(ippiNormDiff_C1MR, src1, (int)src1_step, src2, (int)src2_step, mask, (int)mask_step, sz, &norm) >= 0 ) + { + *result = (norm_type == cv::NORM_L2SQR ? (double)(norm * norm) : (double)norm); + return CV_HAL_ERROR_OK; + } + } + typedef IppStatus (CV_STDCALL* ippiMaskNormDiffFuncC3)(const void *, int, const void *, int, const void *, int, IppiSize, int, Ipp64f *); + ippiMaskNormDiffFuncC3 ippiNormDiff_C3CMR = + norm_type == cv::NORM_INF ? + (type == CV_8UC3 ? (ippiMaskNormDiffFuncC3)ippiNormDiff_Inf_8u_C3CMR : + type == CV_16UC3 ? (ippiMaskNormDiffFuncC3)ippiNormDiff_Inf_16u_C3CMR : + type == CV_32FC3 ? (ippiMaskNormDiffFuncC3)ippiNormDiff_Inf_32f_C3CMR : + 0) : + norm_type == cv::NORM_L1 ? + (type == CV_8UC3 ? (ippiMaskNormDiffFuncC3)ippiNormDiff_L1_8u_C3CMR : + type == CV_16UC3 ? (ippiMaskNormDiffFuncC3)ippiNormDiff_L1_16u_C3CMR : + type == CV_32FC3 ? (ippiMaskNormDiffFuncC3)ippiNormDiff_L1_32f_C3CMR : + 0) : + norm_type == cv::NORM_L2 || norm_type == cv::NORM_L2SQR ? + (type == CV_8UC3 ? (ippiMaskNormDiffFuncC3)ippiNormDiff_L2_8u_C3CMR : + type == CV_16UC3 ? (ippiMaskNormDiffFuncC3)ippiNormDiff_L2_16u_C3CMR : + type == CV_32FC3 ? (ippiMaskNormDiffFuncC3)ippiNormDiff_L2_32f_C3CMR : + 0) : 0; + if( ippiNormDiff_C3CMR ) + { + Ipp64f norm1, norm2, norm3; + if( CV_INSTRUMENT_FUN_IPP(ippiNormDiff_C3CMR, src1, (int)src1_step, src2, (int)src2_step, mask, (int)mask_step, sz, 1, &norm1) >= 0 && + CV_INSTRUMENT_FUN_IPP(ippiNormDiff_C3CMR, src1, (int)src1_step, src2, (int)src2_step, mask, (int)mask_step, sz, 2, &norm2) >= 0 && + CV_INSTRUMENT_FUN_IPP(ippiNormDiff_C3CMR, src1, (int)src1_step, src2, (int)src2_step, mask, (int)mask_step, sz, 3, &norm3) >= 0) + { + Ipp64f norm = + norm_type == cv::NORM_INF ? std::max(std::max(norm1, norm2), norm3) : + norm_type == cv::NORM_L1 ? norm1 + norm2 + norm3 : + norm_type == cv::NORM_L2 || norm_type == cv::NORM_L2SQR ? std::sqrt(norm1 * norm1 + norm2 * norm2 + norm3 * norm3) : + 0; + *result = (norm_type == cv::NORM_L2SQR ? (double)(norm * norm) : (double)norm); + return CV_HAL_ERROR_OK; + } + } + } + else + { + int cn = CV_MAT_CN(type); + type = CV_MAT_DEPTH(type); + IppiSize sz = { width*cn, height }; + + typedef IppStatus (CV_STDCALL* ippiNormDiffFuncHint)(const void *, int, const void *, int, IppiSize, Ipp64f *, IppHintAlgorithm hint); + typedef IppStatus (CV_STDCALL* ippiNormDiffFuncNoHint)(const void *, int, const void *, int, IppiSize, Ipp64f *); + ippiNormDiffFuncHint ippiNormDiffHint = + norm_type == cv::NORM_L1 ? + (type == CV_32F ? (ippiNormDiffFuncHint)ippiNormDiff_L1_32f_C1R : + 0) : + norm_type == cv::NORM_L2 || norm_type == cv::NORM_L2SQR ? + (type == CV_32F ? (ippiNormDiffFuncHint)ippiNormDiff_L2_32f_C1R : + 0) : 0; + ippiNormDiffFuncNoHint ippiNormDiff = + norm_type == cv::NORM_INF ? + ( + #if (!IPP_DISABLE_NORM_8U) + type == CV_8U ? (ippiNormDiffFuncNoHint)ippiNormDiff_Inf_8u_C1R : + #endif + type == CV_16U ? (ippiNormDiffFuncNoHint)ippiNormDiff_Inf_16u_C1R : + type == CV_16S ? (ippiNormDiffFuncNoHint)ippiNormDiff_Inf_16s_C1R : + type == CV_32F ? (ippiNormDiffFuncNoHint)ippiNormDiff_Inf_32f_C1R : + 0) : + norm_type == cv::NORM_L1 ? + ( + #if (!IPP_DISABLE_NORM_8U) + type == CV_8U ? (ippiNormDiffFuncNoHint)ippiNormDiff_L1_8u_C1R : + #endif + type == CV_16U ? (ippiNormDiffFuncNoHint)ippiNormDiff_L1_16u_C1R : + type == CV_16S ? (ippiNormDiffFuncNoHint)ippiNormDiff_L1_16s_C1R : + 0) : + norm_type == cv::NORM_L2 || norm_type == cv::NORM_L2SQR ? + ( + #if (!IPP_DISABLE_NORM_8U) + type == CV_8U ? (ippiNormDiffFuncNoHint)ippiNormDiff_L2_8u_C1R : + #endif + type == CV_16U ? (ippiNormDiffFuncNoHint)ippiNormDiff_L2_16u_C1R : + type == CV_16S ? (ippiNormDiffFuncNoHint)ippiNormDiff_L2_16s_C1R : + 0) : 0; + if( ippiNormDiffHint || ippiNormDiff ) + { + Ipp64f norm; + IppStatus ret = ippiNormDiffHint ? CV_INSTRUMENT_FUN_IPP(ippiNormDiffHint, src1, (int)src1_step, src2, (int)src2_step, sz, &norm, ippAlgHintAccurate) : + CV_INSTRUMENT_FUN_IPP(ippiNormDiff, src1, (int)src1_step, src2, (int)src2_step, sz, &norm); + if( ret >= 0 ) + { + *result = (norm_type == cv::NORM_L2SQR) ? norm * norm : norm; + return CV_HAL_ERROR_OK; + } + } + } + + return CV_HAL_ERROR_NOT_IMPLEMENTED; +} + + +#endif diff --git a/modules/core/src/norm.dispatch.cpp b/modules/core/src/norm.dispatch.cpp index fed4896677..9f02df47e7 100644 --- a/modules/core/src/norm.dispatch.cpp +++ b/modules/core/src/norm.dispatch.cpp @@ -274,137 +274,6 @@ static bool ocl_norm( InputArray _src, int normType, InputArray _mask, double & #endif -#ifdef HAVE_IPP -static bool ipp_norm(Mat &src, int normType, Mat &mask, double &result) -{ - CV_INSTRUMENT_REGION_IPP(); - -#if IPP_VERSION_X100 >= 700 - size_t total_size = src.total(); - int rows = src.size[0], cols = rows ? (int)(total_size/rows) : 0; - - if( (src.dims == 2 || (src.isContinuous() && mask.isContinuous())) - && cols > 0 && (size_t)rows*cols == total_size ) - { - if( !mask.empty() ) - { - IppiSize sz = { cols, rows }; - int type = src.type(); - - typedef IppStatus (CV_STDCALL* ippiMaskNormFuncC1)(const void *, int, const void *, int, IppiSize, Ipp64f *); - ippiMaskNormFuncC1 ippiNorm_C1MR = - normType == NORM_INF ? - (type == CV_8UC1 ? (ippiMaskNormFuncC1)ippiNorm_Inf_8u_C1MR : - type == CV_16UC1 ? (ippiMaskNormFuncC1)ippiNorm_Inf_16u_C1MR : - type == CV_32FC1 ? (ippiMaskNormFuncC1)ippiNorm_Inf_32f_C1MR : - 0) : - normType == NORM_L1 ? - (type == CV_8UC1 ? (ippiMaskNormFuncC1)ippiNorm_L1_8u_C1MR : - type == CV_16UC1 ? (ippiMaskNormFuncC1)ippiNorm_L1_16u_C1MR : - type == CV_32FC1 ? (ippiMaskNormFuncC1)ippiNorm_L1_32f_C1MR : - 0) : - normType == NORM_L2 || normType == NORM_L2SQR ? - (type == CV_8UC1 ? (ippiMaskNormFuncC1)ippiNorm_L2_8u_C1MR : - type == CV_16UC1 ? (ippiMaskNormFuncC1)ippiNorm_L2_16u_C1MR : - type == CV_32FC1 ? (ippiMaskNormFuncC1)ippiNorm_L2_32f_C1MR : - 0) : 0; - if( ippiNorm_C1MR ) - { - Ipp64f norm; - if( CV_INSTRUMENT_FUN_IPP(ippiNorm_C1MR, src.ptr(), (int)src.step[0], mask.ptr(), (int)mask.step[0], sz, &norm) >= 0 ) - { - result = (normType == NORM_L2SQR ? (double)(norm * norm) : (double)norm); - return true; - } - } - typedef IppStatus (CV_STDCALL* ippiMaskNormFuncC3)(const void *, int, const void *, int, IppiSize, int, Ipp64f *); - ippiMaskNormFuncC3 ippiNorm_C3CMR = - normType == NORM_INF ? - (type == CV_8UC3 ? (ippiMaskNormFuncC3)ippiNorm_Inf_8u_C3CMR : - type == CV_16UC3 ? (ippiMaskNormFuncC3)ippiNorm_Inf_16u_C3CMR : - type == CV_32FC3 ? (ippiMaskNormFuncC3)ippiNorm_Inf_32f_C3CMR : - 0) : - normType == NORM_L1 ? - (type == CV_8UC3 ? (ippiMaskNormFuncC3)ippiNorm_L1_8u_C3CMR : - type == CV_16UC3 ? (ippiMaskNormFuncC3)ippiNorm_L1_16u_C3CMR : - type == CV_32FC3 ? (ippiMaskNormFuncC3)ippiNorm_L1_32f_C3CMR : - 0) : - normType == NORM_L2 || normType == NORM_L2SQR ? - (type == CV_8UC3 ? (ippiMaskNormFuncC3)ippiNorm_L2_8u_C3CMR : - type == CV_16UC3 ? (ippiMaskNormFuncC3)ippiNorm_L2_16u_C3CMR : - type == CV_32FC3 ? (ippiMaskNormFuncC3)ippiNorm_L2_32f_C3CMR : - 0) : 0; - if( ippiNorm_C3CMR ) - { - Ipp64f norm1, norm2, norm3; - if( CV_INSTRUMENT_FUN_IPP(ippiNorm_C3CMR, src.data, (int)src.step[0], mask.data, (int)mask.step[0], sz, 1, &norm1) >= 0 && - CV_INSTRUMENT_FUN_IPP(ippiNorm_C3CMR, src.data, (int)src.step[0], mask.data, (int)mask.step[0], sz, 2, &norm2) >= 0 && - CV_INSTRUMENT_FUN_IPP(ippiNorm_C3CMR, src.data, (int)src.step[0], mask.data, (int)mask.step[0], sz, 3, &norm3) >= 0) - { - Ipp64f norm = - normType == NORM_INF ? std::max(std::max(norm1, norm2), norm3) : - normType == NORM_L1 ? norm1 + norm2 + norm3 : - normType == NORM_L2 || normType == NORM_L2SQR ? std::sqrt(norm1 * norm1 + norm2 * norm2 + norm3 * norm3) : - 0; - result = (normType == NORM_L2SQR ? (double)(norm * norm) : (double)norm); - return true; - } - } - } - else - { - IppiSize sz = { cols*src.channels(), rows }; - int type = src.depth(); - - typedef IppStatus (CV_STDCALL* ippiNormFuncHint)(const void *, int, IppiSize, Ipp64f *, IppHintAlgorithm hint); - typedef IppStatus (CV_STDCALL* ippiNormFuncNoHint)(const void *, int, IppiSize, Ipp64f *); - ippiNormFuncHint ippiNormHint = - normType == NORM_L1 ? - (type == CV_32FC1 ? (ippiNormFuncHint)ippiNorm_L1_32f_C1R : - 0) : - normType == NORM_L2 || normType == NORM_L2SQR ? - (type == CV_32FC1 ? (ippiNormFuncHint)ippiNorm_L2_32f_C1R : - 0) : 0; - ippiNormFuncNoHint ippiNorm = - normType == NORM_INF ? - (type == CV_8UC1 ? (ippiNormFuncNoHint)ippiNorm_Inf_8u_C1R : - type == CV_16UC1 ? (ippiNormFuncNoHint)ippiNorm_Inf_16u_C1R : - type == CV_16SC1 ? (ippiNormFuncNoHint)ippiNorm_Inf_16s_C1R : - type == CV_32FC1 ? (ippiNormFuncNoHint)ippiNorm_Inf_32f_C1R : - 0) : - normType == NORM_L1 ? - (type == CV_8UC1 ? (ippiNormFuncNoHint)ippiNorm_L1_8u_C1R : - type == CV_16UC1 ? (ippiNormFuncNoHint)ippiNorm_L1_16u_C1R : - type == CV_16SC1 ? (ippiNormFuncNoHint)ippiNorm_L1_16s_C1R : - 0) : - normType == NORM_L2 || normType == NORM_L2SQR ? - ( - #if !IPP_DISABLE_NORM_8U - type == CV_8UC1 ? (ippiNormFuncNoHint)ippiNorm_L2_8u_C1R : - #endif - type == CV_16UC1 ? (ippiNormFuncNoHint)ippiNorm_L2_16u_C1R : - type == CV_16SC1 ? (ippiNormFuncNoHint)ippiNorm_L2_16s_C1R : - 0) : 0; - if( ippiNormHint || ippiNorm ) - { - Ipp64f norm; - IppStatus ret = ippiNormHint ? CV_INSTRUMENT_FUN_IPP(ippiNormHint, src.ptr(), (int)src.step[0], sz, &norm, ippAlgHintAccurate) : - CV_INSTRUMENT_FUN_IPP(ippiNorm, src.ptr(), (int)src.step[0], sz, &norm); - if( ret >= 0 ) - { - result = (normType == NORM_L2SQR) ? norm * norm : norm; - return true; - } - } - } - } -#else - CV_UNUSED(src); CV_UNUSED(normType); CV_UNUSED(mask); CV_UNUSED(result); -#endif - return false; -} // ipp_norm() -#endif // HAVE_IPP - static NormFunc getNormFunc(int normType, int depth) { CV_INSTRUMENT_REGION(); CV_CPU_DISPATCH(getNormFunc, (normType, depth), CV_CPU_DISPATCH_MODES_ALL); @@ -423,7 +292,7 @@ double norm( InputArray _src, int normType, InputArray _mask ) normType == NORM_L2 || normType == NORM_L2SQR || ((normType == NORM_HAMMING || normType == NORM_HAMMING2) && _src.type() == CV_8U) ); -#if defined HAVE_OPENCL || defined HAVE_IPP +#if defined HAVE_OPENCL double _result = 0; #endif @@ -446,8 +315,6 @@ double norm( InputArray _src, int normType, InputArray _mask ) CALL_HAL_RET(norm, cv_hal_norm, result, src.data, 0, mask.data, 0, (int)src.total(), 1, src.type(), normType); } - CV_IPP_RUN(IPP_VERSION_X100 >= 700, ipp_norm(src, normType, mask, _result), _result); - NormFunc func = getNormFunc(normType >> 1, depth == CV_16F ? CV_32F : depth); CV_Assert( func != 0 ); @@ -663,244 +530,6 @@ static bool ocl_norm( InputArray _src1, InputArray _src2, int normType, InputArr } // ocl_norm() #endif // HAVE_OPENCL -#ifdef HAVE_IPP -static bool ipp_norm(InputArray _src1, InputArray _src2, int normType, InputArray _mask, double &result) -{ - CV_INSTRUMENT_REGION_IPP(); - -#if IPP_VERSION_X100 >= 700 - Mat src1 = _src1.getMat(), src2 = _src2.getMat(), mask = _mask.getMat(); - - if( normType & CV_RELATIVE ) - { - normType &= NORM_TYPE_MASK; - - size_t total_size = src1.total(); - int rows = src1.size[0], cols = rows ? (int)(total_size/rows) : 0; - if( (src1.dims == 2 || (src1.isContinuous() && src2.isContinuous() && mask.isContinuous())) - && cols > 0 && (size_t)rows*cols == total_size ) - { - if( !mask.empty() ) - { - IppiSize sz = { cols, rows }; - int type = src1.type(); - - typedef IppStatus (CV_STDCALL* ippiMaskNormDiffFuncC1)(const void *, int, const void *, int, const void *, int, IppiSize, Ipp64f *); - ippiMaskNormDiffFuncC1 ippiNormRel_C1MR = - normType == NORM_INF ? - (type == CV_8UC1 ? (ippiMaskNormDiffFuncC1)ippiNormRel_Inf_8u_C1MR : - type == CV_16UC1 ? (ippiMaskNormDiffFuncC1)ippiNormRel_Inf_16u_C1MR : - type == CV_32FC1 ? (ippiMaskNormDiffFuncC1)ippiNormRel_Inf_32f_C1MR : - 0) : - normType == NORM_L1 ? - (type == CV_8UC1 ? (ippiMaskNormDiffFuncC1)ippiNormRel_L1_8u_C1MR : - type == CV_16UC1 ? (ippiMaskNormDiffFuncC1)ippiNormRel_L1_16u_C1MR : - type == CV_32FC1 ? (ippiMaskNormDiffFuncC1)ippiNormRel_L1_32f_C1MR : - 0) : - normType == NORM_L2 || normType == NORM_L2SQR ? - (type == CV_8UC1 ? (ippiMaskNormDiffFuncC1)ippiNormRel_L2_8u_C1MR : - type == CV_16UC1 ? (ippiMaskNormDiffFuncC1)ippiNormRel_L2_16u_C1MR : - type == CV_32FC1 ? (ippiMaskNormDiffFuncC1)ippiNormRel_L2_32f_C1MR : - 0) : 0; - if( ippiNormRel_C1MR ) - { - Ipp64f norm; - if( CV_INSTRUMENT_FUN_IPP(ippiNormRel_C1MR, src1.ptr(), (int)src1.step[0], src2.ptr(), (int)src2.step[0], mask.ptr(), (int)mask.step[0], sz, &norm) >= 0 ) - { - result = (normType == NORM_L2SQR ? (double)(norm * norm) : (double)norm); - return true; - } - } - } - else - { - IppiSize sz = { cols*src1.channels(), rows }; - int type = src1.depth(); - - typedef IppStatus (CV_STDCALL* ippiNormRelFuncHint)(const void *, int, const void *, int, IppiSize, Ipp64f *, IppHintAlgorithm hint); - typedef IppStatus (CV_STDCALL* ippiNormRelFuncNoHint)(const void *, int, const void *, int, IppiSize, Ipp64f *); - ippiNormRelFuncHint ippiNormRelHint = - normType == NORM_L1 ? - (type == CV_32F ? (ippiNormRelFuncHint)ippiNormRel_L1_32f_C1R : - 0) : - normType == NORM_L2 || normType == NORM_L2SQR ? - (type == CV_32F ? (ippiNormRelFuncHint)ippiNormRel_L2_32f_C1R : - 0) : 0; - ippiNormRelFuncNoHint ippiNormRel = - normType == NORM_INF ? - ( - #if !IPP_DISABLE_NORM_8U - type == CV_8U ? (ippiNormRelFuncNoHint)ippiNormRel_Inf_8u_C1R : - #endif - type == CV_16U ? (ippiNormRelFuncNoHint)ippiNormRel_Inf_16u_C1R : - type == CV_16S ? (ippiNormRelFuncNoHint)ippiNormRel_Inf_16s_C1R : - type == CV_32F ? (ippiNormRelFuncNoHint)ippiNormRel_Inf_32f_C1R : - 0) : - normType == NORM_L1 ? - ( - #if !IPP_DISABLE_NORM_8U - type == CV_8U ? (ippiNormRelFuncNoHint)ippiNormRel_L1_8u_C1R : - #endif - type == CV_16U ? (ippiNormRelFuncNoHint)ippiNormRel_L1_16u_C1R : - type == CV_16S ? (ippiNormRelFuncNoHint)ippiNormRel_L1_16s_C1R : - 0) : - normType == NORM_L2 || normType == NORM_L2SQR ? - ( - #if !IPP_DISABLE_NORM_8U - type == CV_8U ? (ippiNormRelFuncNoHint)ippiNormRel_L2_8u_C1R : - #endif - type == CV_16U ? (ippiNormRelFuncNoHint)ippiNormRel_L2_16u_C1R : - type == CV_16S ? (ippiNormRelFuncNoHint)ippiNormRel_L2_16s_C1R : - 0) : 0; - if( ippiNormRelHint || ippiNormRel ) - { - Ipp64f norm; - IppStatus ret = ippiNormRelHint ? CV_INSTRUMENT_FUN_IPP(ippiNormRelHint, src1.ptr(), (int)src1.step[0], src2.ptr(), (int)src2.step[0], sz, &norm, ippAlgHintAccurate) : - CV_INSTRUMENT_FUN_IPP(ippiNormRel, src1.ptr(), (int)src1.step[0], src2.ptr(), (int)src2.step[0], sz, &norm); - if( ret >= 0 ) - { - result = (normType == NORM_L2SQR) ? norm * norm : norm; - return true; - } - } - } - } - return false; - } - - normType &= NORM_TYPE_MASK; - - size_t total_size = src1.total(); - int rows = src1.size[0], cols = rows ? (int)(total_size/rows) : 0; - if( (src1.dims == 2 || (src1.isContinuous() && src2.isContinuous() && mask.isContinuous())) - && cols > 0 && (size_t)rows*cols == total_size ) - { - if( !mask.empty() ) - { - IppiSize sz = { cols, rows }; - int type = src1.type(); - - typedef IppStatus (CV_STDCALL* ippiMaskNormDiffFuncC1)(const void *, int, const void *, int, const void *, int, IppiSize, Ipp64f *); - ippiMaskNormDiffFuncC1 ippiNormDiff_C1MR = - normType == NORM_INF ? - (type == CV_8UC1 ? (ippiMaskNormDiffFuncC1)ippiNormDiff_Inf_8u_C1MR : - type == CV_16UC1 ? (ippiMaskNormDiffFuncC1)ippiNormDiff_Inf_16u_C1MR : - type == CV_32FC1 ? (ippiMaskNormDiffFuncC1)ippiNormDiff_Inf_32f_C1MR : - 0) : - normType == NORM_L1 ? - (type == CV_8UC1 ? (ippiMaskNormDiffFuncC1)ippiNormDiff_L1_8u_C1MR : - type == CV_16UC1 ? (ippiMaskNormDiffFuncC1)ippiNormDiff_L1_16u_C1MR : - type == CV_32FC1 ? (ippiMaskNormDiffFuncC1)ippiNormDiff_L1_32f_C1MR : - 0) : - normType == NORM_L2 || normType == NORM_L2SQR ? - (type == CV_8UC1 ? (ippiMaskNormDiffFuncC1)ippiNormDiff_L2_8u_C1MR : - type == CV_16UC1 ? (ippiMaskNormDiffFuncC1)ippiNormDiff_L2_16u_C1MR : - type == CV_32FC1 ? (ippiMaskNormDiffFuncC1)ippiNormDiff_L2_32f_C1MR : - 0) : 0; - if( ippiNormDiff_C1MR ) - { - Ipp64f norm; - if( CV_INSTRUMENT_FUN_IPP(ippiNormDiff_C1MR, src1.ptr(), (int)src1.step[0], src2.ptr(), (int)src2.step[0], mask.ptr(), (int)mask.step[0], sz, &norm) >= 0 ) - { - result = (normType == NORM_L2SQR ? (double)(norm * norm) : (double)norm); - return true; - } - } - typedef IppStatus (CV_STDCALL* ippiMaskNormDiffFuncC3)(const void *, int, const void *, int, const void *, int, IppiSize, int, Ipp64f *); - ippiMaskNormDiffFuncC3 ippiNormDiff_C3CMR = - normType == NORM_INF ? - (type == CV_8UC3 ? (ippiMaskNormDiffFuncC3)ippiNormDiff_Inf_8u_C3CMR : - type == CV_16UC3 ? (ippiMaskNormDiffFuncC3)ippiNormDiff_Inf_16u_C3CMR : - type == CV_32FC3 ? (ippiMaskNormDiffFuncC3)ippiNormDiff_Inf_32f_C3CMR : - 0) : - normType == NORM_L1 ? - (type == CV_8UC3 ? (ippiMaskNormDiffFuncC3)ippiNormDiff_L1_8u_C3CMR : - type == CV_16UC3 ? (ippiMaskNormDiffFuncC3)ippiNormDiff_L1_16u_C3CMR : - type == CV_32FC3 ? (ippiMaskNormDiffFuncC3)ippiNormDiff_L1_32f_C3CMR : - 0) : - normType == NORM_L2 || normType == NORM_L2SQR ? - (type == CV_8UC3 ? (ippiMaskNormDiffFuncC3)ippiNormDiff_L2_8u_C3CMR : - type == CV_16UC3 ? (ippiMaskNormDiffFuncC3)ippiNormDiff_L2_16u_C3CMR : - type == CV_32FC3 ? (ippiMaskNormDiffFuncC3)ippiNormDiff_L2_32f_C3CMR : - 0) : 0; - if( ippiNormDiff_C3CMR ) - { - Ipp64f norm1, norm2, norm3; - if( CV_INSTRUMENT_FUN_IPP(ippiNormDiff_C3CMR, src1.data, (int)src1.step[0], src2.data, (int)src2.step[0], mask.data, (int)mask.step[0], sz, 1, &norm1) >= 0 && - CV_INSTRUMENT_FUN_IPP(ippiNormDiff_C3CMR, src1.data, (int)src1.step[0], src2.data, (int)src2.step[0], mask.data, (int)mask.step[0], sz, 2, &norm2) >= 0 && - CV_INSTRUMENT_FUN_IPP(ippiNormDiff_C3CMR, src1.data, (int)src1.step[0], src2.data, (int)src2.step[0], mask.data, (int)mask.step[0], sz, 3, &norm3) >= 0) - { - Ipp64f norm = - normType == NORM_INF ? std::max(std::max(norm1, norm2), norm3) : - normType == NORM_L1 ? norm1 + norm2 + norm3 : - normType == NORM_L2 || normType == NORM_L2SQR ? std::sqrt(norm1 * norm1 + norm2 * norm2 + norm3 * norm3) : - 0; - result = (normType == NORM_L2SQR ? (double)(norm * norm) : (double)norm); - return true; - } - } - } - else - { - IppiSize sz = { cols*src1.channels(), rows }; - int type = src1.depth(); - - typedef IppStatus (CV_STDCALL* ippiNormDiffFuncHint)(const void *, int, const void *, int, IppiSize, Ipp64f *, IppHintAlgorithm hint); - typedef IppStatus (CV_STDCALL* ippiNormDiffFuncNoHint)(const void *, int, const void *, int, IppiSize, Ipp64f *); - ippiNormDiffFuncHint ippiNormDiffHint = - normType == NORM_L1 ? - (type == CV_32F ? (ippiNormDiffFuncHint)ippiNormDiff_L1_32f_C1R : - 0) : - normType == NORM_L2 || normType == NORM_L2SQR ? - (type == CV_32F ? (ippiNormDiffFuncHint)ippiNormDiff_L2_32f_C1R : - 0) : 0; - ippiNormDiffFuncNoHint ippiNormDiff = - normType == NORM_INF ? - ( - #if !IPP_DISABLE_NORM_8U - type == CV_8U ? (ippiNormDiffFuncNoHint)ippiNormDiff_Inf_8u_C1R : - #endif - type == CV_16U ? (ippiNormDiffFuncNoHint)ippiNormDiff_Inf_16u_C1R : - type == CV_16S ? (ippiNormDiffFuncNoHint)ippiNormDiff_Inf_16s_C1R : - type == CV_32F ? (ippiNormDiffFuncNoHint)ippiNormDiff_Inf_32f_C1R : - 0) : - normType == NORM_L1 ? - ( - #if !IPP_DISABLE_NORM_8U - type == CV_8U ? (ippiNormDiffFuncNoHint)ippiNormDiff_L1_8u_C1R : - #endif - type == CV_16U ? (ippiNormDiffFuncNoHint)ippiNormDiff_L1_16u_C1R : - type == CV_16S ? (ippiNormDiffFuncNoHint)ippiNormDiff_L1_16s_C1R : - 0) : - normType == NORM_L2 || normType == NORM_L2SQR ? - ( - #if !IPP_DISABLE_NORM_8U - type == CV_8U ? (ippiNormDiffFuncNoHint)ippiNormDiff_L2_8u_C1R : - #endif - type == CV_16U ? (ippiNormDiffFuncNoHint)ippiNormDiff_L2_16u_C1R : - type == CV_16S ? (ippiNormDiffFuncNoHint)ippiNormDiff_L2_16s_C1R : - 0) : 0; - if( ippiNormDiffHint || ippiNormDiff ) - { - Ipp64f norm; - IppStatus ret = ippiNormDiffHint ? CV_INSTRUMENT_FUN_IPP(ippiNormDiffHint, src1.ptr(), (int)src1.step[0], src2.ptr(), (int)src2.step[0], sz, &norm, ippAlgHintAccurate) : - CV_INSTRUMENT_FUN_IPP(ippiNormDiff, src1.ptr(), (int)src1.step[0], src2.ptr(), (int)src2.step[0], sz, &norm); - if( ret >= 0 ) - { - result = (normType == NORM_L2SQR) ? norm * norm : norm; - return true; - } - } - } - } -#else - CV_UNUSED(_src1); CV_UNUSED(_src2); CV_UNUSED(normType); CV_UNUSED(_mask); CV_UNUSED(result); -#endif - return false; -} // ipp_norm -#endif // HAVE_IPP - - double norm( InputArray _src1, InputArray _src2, int normType, InputArray _mask ) { CV_INSTRUMENT_REGION(); @@ -908,7 +537,7 @@ double norm( InputArray _src1, InputArray _src2, int normType, InputArray _mask CV_CheckTypeEQ(_src1.type(), _src2.type(), "Input type mismatch"); CV_Assert(_src1.sameSize(_src2)); -#if defined HAVE_OPENCL || defined HAVE_IPP +#if defined HAVE_OPENCL double _result = 0; #endif @@ -931,8 +560,6 @@ double norm( InputArray _src1, InputArray _src2, int normType, InputArray _mask CALL_HAL_RET(normDiff, cv_hal_normDiff, result, src1.data, 0, src2.data, 0, mask.data, 0, (int)src1.total(), 1, src1.type(), normType); } - CV_IPP_RUN(IPP_VERSION_X100 >= 700, ipp_norm(_src1, _src2, normType, _mask, _result), _result); - if( normType & CV_RELATIVE ) { return norm(_src1, _src2, normType & ~CV_RELATIVE, _mask)/(norm(_src2, normType, _mask) + DBL_EPSILON); From a77623a32be00be52073134184ce8ee6d181a1b1 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Sat, 22 Mar 2025 10:43:25 +0300 Subject: [PATCH 39/94] Move IPP minMaxIdx to HAL. --- 3rdparty/ipphal/CMakeLists.txt | 6 +- 3rdparty/ipphal/include/ipp_hal_core.hpp | 6 + 3rdparty/ipphal/src/minmax_ipp.cpp | 252 ++++++++++++++++++ modules/core/src/mean.dispatch.cpp | 17 -- modules/core/src/minmax.cpp | 312 ----------------------- 5 files changed, 262 insertions(+), 331 deletions(-) create mode 100644 3rdparty/ipphal/src/minmax_ipp.cpp diff --git a/3rdparty/ipphal/CMakeLists.txt b/3rdparty/ipphal/CMakeLists.txt index dc1ec627cb..5e69b0511e 100644 --- a/3rdparty/ipphal/CMakeLists.txt +++ b/3rdparty/ipphal/CMakeLists.txt @@ -8,8 +8,10 @@ set(IPP_HAL_HEADERS CACHE INTERNAL "") add_library(ipphal STATIC - "${CMAKE_CURRENT_SOURCE_DIR}/src/mean_ipp.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/src/norm_ipp.cpp") + "${CMAKE_CURRENT_SOURCE_DIR}/src/mean_ipp.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/minmax_ipp.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/norm_ipp.cpp" +) if(HAVE_IPP_ICV) target_compile_definitions(ipphal PRIVATE HAVE_IPP_ICV) diff --git a/3rdparty/ipphal/include/ipp_hal_core.hpp b/3rdparty/ipphal/include/ipp_hal_core.hpp index beb1524ac7..441a8bb207 100644 --- a/3rdparty/ipphal/include/ipp_hal_core.hpp +++ b/3rdparty/ipphal/include/ipp_hal_core.hpp @@ -11,6 +11,12 @@ int ipp_hal_meanStdDev(const uchar* src_data, size_t src_step, int width, int he #undef cv_hal_meanStdDev #define cv_hal_meanStdDev ipp_hal_meanStdDev +int ipp_hal_minMaxIdxMaskStep(const uchar* src_data, size_t src_step, int width, int height, int depth, + double* _minVal, double* _maxVal, int* _minIdx, int* _maxIdx, uchar* mask, size_t mask_step); + +#undef cv_hal_minMaxIdxMaskStep +#define cv_hal_minMaxIdxMaskStep ipp_hal_minMaxIdxMaskStep + #define IPP_DISABLE_NORM_8U 1 // accuracy difference in perf test sanity check int ipp_hal_norm(const uchar* src, size_t src_step, const uchar* mask, size_t mask_step, diff --git a/3rdparty/ipphal/src/minmax_ipp.cpp b/3rdparty/ipphal/src/minmax_ipp.cpp new file mode 100644 index 0000000000..a87d5825e9 --- /dev/null +++ b/3rdparty/ipphal/src/minmax_ipp.cpp @@ -0,0 +1,252 @@ +#include "ipp_hal_core.hpp" + +#include +#include + +#define IPP_DISABLE_MINMAXIDX_MANY_ROWS 1 // see Core_MinMaxIdx.rows_overflow test + +static inline IppDataType ippiGetDataType(int depth) +{ + depth = CV_MAT_DEPTH(depth); + return depth == CV_8U ? ipp8u : + depth == CV_8S ? ipp8s : + depth == CV_16U ? ipp16u : + depth == CV_16S ? ipp16s : + depth == CV_32S ? ipp32s : + depth == CV_32F ? ipp32f : + depth == CV_64F ? ipp64f : + (IppDataType)-1; +} + +#if IPP_VERSION_X100 >= 700 + + +static IppStatus ipp_minMaxIndex_wrap(const void* pSrc, int srcStep, IppiSize size, IppDataType dataType, + float* pMinVal, float* pMaxVal, IppiPoint* pMinIndex, IppiPoint* pMaxIndex, const Ipp8u*, int) +{ + switch(dataType) + { + case ipp8u: return CV_INSTRUMENT_FUN_IPP(ippiMinMaxIndx_8u_C1R, (const Ipp8u*)pSrc, srcStep, size, pMinVal, pMaxVal, pMinIndex, pMaxIndex); + case ipp16u: return CV_INSTRUMENT_FUN_IPP(ippiMinMaxIndx_16u_C1R, (const Ipp16u*)pSrc, srcStep, size, pMinVal, pMaxVal, pMinIndex, pMaxIndex); + case ipp32f: return CV_INSTRUMENT_FUN_IPP(ippiMinMaxIndx_32f_C1R, (const Ipp32f*)pSrc, srcStep, size, pMinVal, pMaxVal, pMinIndex, pMaxIndex); + default: return ippStsDataTypeErr; + } +} + +static IppStatus ipp_minMaxIndexMask_wrap(const void* pSrc, int srcStep, IppiSize size, IppDataType dataType, + float* pMinVal, float* pMaxVal, IppiPoint* pMinIndex, IppiPoint* pMaxIndex, const Ipp8u* pMask, int maskStep) +{ + switch(dataType) + { + case ipp8u: return CV_INSTRUMENT_FUN_IPP(ippiMinMaxIndx_8u_C1MR, (const Ipp8u*)pSrc, srcStep, pMask, maskStep, size, pMinVal, pMaxVal, pMinIndex, pMaxIndex); + case ipp16u: return CV_INSTRUMENT_FUN_IPP(ippiMinMaxIndx_16u_C1MR, (const Ipp16u*)pSrc, srcStep, pMask, maskStep, size, pMinVal, pMaxVal, pMinIndex, pMaxIndex); + case ipp32f: return CV_INSTRUMENT_FUN_IPP(ippiMinMaxIndx_32f_C1MR, (const Ipp32f*)pSrc, srcStep, pMask, maskStep, size, pMinVal, pMaxVal, pMinIndex, pMaxIndex); + default: return ippStsDataTypeErr; + } +} + +static IppStatus ipp_minMax_wrap(const void* pSrc, int srcStep, IppiSize size, IppDataType dataType, + float* pMinVal, float* pMaxVal, IppiPoint*, IppiPoint*, const Ipp8u*, int) +{ + IppStatus status; + + switch(dataType) + { +#if IPP_VERSION_X100 > 201701 // wrong min values + case ipp8u: + { + Ipp8u val[2]; + status = CV_INSTRUMENT_FUN_IPP(ippiMinMax_8u_C1R, (const Ipp8u*)pSrc, srcStep, size, &val[0], &val[1]); + *pMinVal = val[0]; + *pMaxVal = val[1]; + return status; + } +#endif + case ipp16u: + { + Ipp16u val[2]; + status = CV_INSTRUMENT_FUN_IPP(ippiMinMax_16u_C1R, (const Ipp16u*)pSrc, srcStep, size, &val[0], &val[1]); + *pMinVal = val[0]; + *pMaxVal = val[1]; + return status; + } + case ipp16s: + { + Ipp16s val[2]; + status = CV_INSTRUMENT_FUN_IPP(ippiMinMax_16s_C1R, (const Ipp16s*)pSrc, srcStep, size, &val[0], &val[1]); + *pMinVal = val[0]; + *pMaxVal = val[1]; + return status; + } + case ipp32f: return CV_INSTRUMENT_FUN_IPP(ippiMinMax_32f_C1R, (const Ipp32f*)pSrc, srcStep, size, pMinVal, pMaxVal); + default: return ipp_minMaxIndex_wrap(pSrc, srcStep, size, dataType, pMinVal, pMaxVal, NULL, NULL, NULL, 0); + } +} + +static IppStatus ipp_minIdx_wrap(const void* pSrc, int srcStep, IppiSize size, IppDataType dataType, + float* pMinVal, float*, IppiPoint* pMinIndex, IppiPoint*, const Ipp8u*, int) +{ + IppStatus status; + + switch(dataType) + { + case ipp8u: + { + Ipp8u val; + status = CV_INSTRUMENT_FUN_IPP(ippiMinIndx_8u_C1R, (const Ipp8u*)pSrc, srcStep, size, &val, &pMinIndex->x, &pMinIndex->y); + *pMinVal = val; + return status; + } + case ipp16u: + { + Ipp16u val; + status = CV_INSTRUMENT_FUN_IPP(ippiMinIndx_16u_C1R, (const Ipp16u*)pSrc, srcStep, size, &val, &pMinIndex->x, &pMinIndex->y); + *pMinVal = val; + return status; + } + case ipp16s: + { + Ipp16s val; + status = CV_INSTRUMENT_FUN_IPP(ippiMinIndx_16s_C1R, (const Ipp16s*)pSrc, srcStep, size, &val, &pMinIndex->x, &pMinIndex->y); + *pMinVal = val; + return status; + } + case ipp32f: return CV_INSTRUMENT_FUN_IPP(ippiMinIndx_32f_C1R, (const Ipp32f*)pSrc, srcStep, size, pMinVal, &pMinIndex->x, &pMinIndex->y); + default: return ipp_minMaxIndex_wrap(pSrc, srcStep, size, dataType, pMinVal, NULL, pMinIndex, NULL, NULL, 0); + } +} + +static IppStatus ipp_maxIdx_wrap(const void* pSrc, int srcStep, IppiSize size, IppDataType dataType, + float*, float* pMaxVal, IppiPoint*, IppiPoint* pMaxIndex, const Ipp8u*, int) +{ + IppStatus status; + + switch(dataType) + { + case ipp8u: + { + Ipp8u val; + status = CV_INSTRUMENT_FUN_IPP(ippiMaxIndx_8u_C1R, (const Ipp8u*)pSrc, srcStep, size, &val, &pMaxIndex->x, &pMaxIndex->y); + *pMaxVal = val; + return status; + } + case ipp16u: + { + Ipp16u val; + status = CV_INSTRUMENT_FUN_IPP(ippiMaxIndx_16u_C1R, (const Ipp16u*)pSrc, srcStep, size, &val, &pMaxIndex->x, &pMaxIndex->y); + *pMaxVal = val; + return status; + } + case ipp16s: + { + Ipp16s val; + status = CV_INSTRUMENT_FUN_IPP(ippiMaxIndx_16s_C1R, (const Ipp16s*)pSrc, srcStep, size, &val, &pMaxIndex->x, &pMaxIndex->y); + *pMaxVal = val; + return status; + } + case ipp32f: return CV_INSTRUMENT_FUN_IPP(ippiMaxIndx_32f_C1R, (const Ipp32f*)pSrc, srcStep, size, pMaxVal, &pMaxIndex->x, &pMaxIndex->y); + default: return ipp_minMaxIndex_wrap(pSrc, srcStep, size, dataType, NULL, pMaxVal, NULL, pMaxIndex, NULL, 0); + } +} + +typedef IppStatus (*IppMinMaxSelector)(const void* pSrc, int srcStep, IppiSize size, IppDataType dataType, + float* pMinVal, float* pMaxVal, IppiPoint* pMinIndex, IppiPoint* pMaxIndex, const Ipp8u* pMask, int maskStep); + + +int ipp_hal_minMaxIdxMaskStep(const uchar* src_data, size_t src_step, int width, int height, int depth, + double* _minVal, double* _maxVal, int* _minIdx, int* _maxIdx, uchar* mask, size_t mask_step) +{ +#if IPP_VERSION_X100 < 201800 + // cv::minMaxIdx problem with NaN input + // Disable 32F processing only + if(depth == CV_32F && cv::ipp::getIppTopFeatures() == ippCPUID_SSE42) + return CV_HAL_ERROR_NOT_IMPLEMENTED; +#endif + +#if IPP_VERSION_X100 < 201801 + // cv::minMaxIdx problem with index positions on AVX + if(mask && _maxIdx && cv::ipp::getIppTopFeatures() != ippCPUID_SSE42) + return CV_HAL_ERROR_NOT_IMPLEMENTED; +#endif + + IppStatus status; + IppDataType dataType = ippiGetDataType(depth); + float minVal = 0; + float maxVal = 0; + IppiPoint minIdx = {-1, -1}; + IppiPoint maxIdx = {-1, -1}; + + float *pMinVal = (_minVal || _minIdx)?&minVal:NULL; + float *pMaxVal = (_maxVal || _maxIdx)?&maxVal:NULL; + IppiPoint *pMinIdx = (_minIdx)?&minIdx:NULL; + IppiPoint *pMaxIdx = (_maxIdx)?&maxIdx:NULL; + + IppMinMaxSelector ippMinMaxFun = ipp_minMaxIndexMask_wrap; + if(!mask) + { + if(_maxVal && _maxIdx && !_minVal && !_minIdx) + ippMinMaxFun = ipp_maxIdx_wrap; + else if(!_maxVal && !_maxIdx && _minVal && _minIdx) + ippMinMaxFun = ipp_minIdx_wrap; + else if(_maxVal && !_maxIdx && _minVal && !_minIdx) + ippMinMaxFun = ipp_minMax_wrap; + else if(!_maxVal && !_maxIdx && !_minVal && !_minIdx) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + else + ippMinMaxFun = ipp_minMaxIndex_wrap; + } + + IppiSize size = { width, height }; +#if defined(_WIN32) && !defined(_WIN64) && IPP_VERSION_X100 == 201900 && IPP_DISABLE_MINMAXIDX_MANY_ROWS + if (size.height > 65536) + return CV_HAL_ERROR_NOT_IMPLEMENTED; // test: Core_MinMaxIdx.rows_overflow +#endif + + status = ippMinMaxFun(src_data, (int)src_step, size, dataType, pMinVal, pMaxVal, pMinIdx, pMaxIdx, (Ipp8u*)mask, (int)mask_step); + if(status < 0) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + if(_minVal) + *_minVal = minVal; + if(_maxVal) + *_maxVal = maxVal; + if(_minIdx) + { +#if IPP_VERSION_X100 < 201801 + // Should be just ippStsNoOperation check, but there is a bug in the function so we need additional checks + if(status == ippStsNoOperation && mask && !pMinIdx->x && !pMinIdx->y) +#else + if(status == ippStsNoOperation) +#endif + { + _minIdx[0] = -1; + _minIdx[1] = -1; + } + else + { + _minIdx[0] = minIdx.y; + _minIdx[1] = minIdx.x; + } + } + if(_maxIdx) + { +#if IPP_VERSION_X100 < 201801 + // Should be just ippStsNoOperation check, but there is a bug in the function so we need additional checks + if(status == ippStsNoOperation && mask && !pMaxIdx->x && !pMaxIdx->y) +#else + if(status == ippStsNoOperation) +#endif + { + _maxIdx[0] = -1; + _maxIdx[1] = -1; + } + else + { + _maxIdx[0] = maxIdx.y; + _maxIdx[1] = maxIdx.x; + } + } + + return CV_HAL_ERROR_OK; +} + +#endif // IPP_VERSION_X100 >= 700 diff --git a/modules/core/src/mean.dispatch.cpp b/modules/core/src/mean.dispatch.cpp index 1e547bd0db..3a38377de9 100644 --- a/modules/core/src/mean.dispatch.cpp +++ b/modules/core/src/mean.dispatch.cpp @@ -7,24 +7,9 @@ #include "opencl_kernels_core.hpp" #include "stat.hpp" -#ifndef OPENCV_IPP_MEAN -#undef HAVE_IPP -#undef CV_IPP_RUN_FAST -#define CV_IPP_RUN_FAST(f, ...) -#undef CV_IPP_RUN -#define CV_IPP_RUN(c, f, ...) -#endif // OPENCV_IPP_MEAN - #include "mean.simd.hpp" #include "mean.simd_declarations.hpp" // defines CV_CPU_DISPATCH_MODES_ALL=AVX2,...,BASELINE based on CMakeLists.txt content -#ifndef OPENCV_IPP_MEAN -#undef HAVE_IPP -#undef CV_IPP_RUN_FAST -#define CV_IPP_RUN_FAST(f, ...) -#undef CV_IPP_RUN -#define CV_IPP_RUN(c, f, ...) -#endif // OPENCV_IPP_MEAN namespace cv { @@ -40,8 +25,6 @@ Scalar mean(InputArray _src, InputArray _mask) CV_Assert( cn <= 4 ); - CV_IPP_RUN(IPP_VERSION_X100 >= 700, ipp_mean(src, mask, s), s) - if (src.isContinuous() && mask.isContinuous()) { CALL_HAL_RET2(meanStdDev, cv_hal_meanStdDev, s, src.data, 0, (int)src.total(), 1, src.type(), diff --git a/modules/core/src/minmax.cpp b/modules/core/src/minmax.cpp index 5c819fcd61..876735d85d 100644 --- a/modules/core/src/minmax.cpp +++ b/modules/core/src/minmax.cpp @@ -10,16 +10,6 @@ #include -#ifndef OPENCV_IPP_MINMAX -#undef HAVE_IPP -#undef CV_IPP_RUN_FAST -#define CV_IPP_RUN_FAST(f, ...) -#undef CV_IPP_RUN -#define CV_IPP_RUN(c, f, ...) -#endif // OPENCV_IPP_MINMAX - -#define IPP_DISABLE_MINMAXIDX_MANY_ROWS 1 // see Core_MinMaxIdx.rows_overflow test - /****************************************************************************************\ * minMaxLoc * \****************************************************************************************/ @@ -1118,306 +1108,6 @@ bool ocl_minMaxIdx( InputArray _src, double* minVal, double* maxVal, int* minLoc #endif -#ifdef HAVE_IPP -static IppStatus ipp_minMaxIndex_wrap(const void* pSrc, int srcStep, IppiSize size, IppDataType dataType, - float* pMinVal, float* pMaxVal, IppiPoint* pMinIndex, IppiPoint* pMaxIndex, const Ipp8u*, int) -{ - switch(dataType) - { - case ipp8u: return CV_INSTRUMENT_FUN_IPP(ippiMinMaxIndx_8u_C1R, (const Ipp8u*)pSrc, srcStep, size, pMinVal, pMaxVal, pMinIndex, pMaxIndex); - case ipp16u: return CV_INSTRUMENT_FUN_IPP(ippiMinMaxIndx_16u_C1R, (const Ipp16u*)pSrc, srcStep, size, pMinVal, pMaxVal, pMinIndex, pMaxIndex); - case ipp32f: return CV_INSTRUMENT_FUN_IPP(ippiMinMaxIndx_32f_C1R, (const Ipp32f*)pSrc, srcStep, size, pMinVal, pMaxVal, pMinIndex, pMaxIndex); - default: return ippStsDataTypeErr; - } -} - -static IppStatus ipp_minMaxIndexMask_wrap(const void* pSrc, int srcStep, IppiSize size, IppDataType dataType, - float* pMinVal, float* pMaxVal, IppiPoint* pMinIndex, IppiPoint* pMaxIndex, const Ipp8u* pMask, int maskStep) -{ - switch(dataType) - { - case ipp8u: return CV_INSTRUMENT_FUN_IPP(ippiMinMaxIndx_8u_C1MR, (const Ipp8u*)pSrc, srcStep, pMask, maskStep, size, pMinVal, pMaxVal, pMinIndex, pMaxIndex); - case ipp16u: return CV_INSTRUMENT_FUN_IPP(ippiMinMaxIndx_16u_C1MR, (const Ipp16u*)pSrc, srcStep, pMask, maskStep, size, pMinVal, pMaxVal, pMinIndex, pMaxIndex); - case ipp32f: return CV_INSTRUMENT_FUN_IPP(ippiMinMaxIndx_32f_C1MR, (const Ipp32f*)pSrc, srcStep, pMask, maskStep, size, pMinVal, pMaxVal, pMinIndex, pMaxIndex); - default: return ippStsDataTypeErr; - } -} - -static IppStatus ipp_minMax_wrap(const void* pSrc, int srcStep, IppiSize size, IppDataType dataType, - float* pMinVal, float* pMaxVal, IppiPoint*, IppiPoint*, const Ipp8u*, int) -{ - IppStatus status; - - switch(dataType) - { -#if IPP_VERSION_X100 > 201701 // wrong min values - case ipp8u: - { - Ipp8u val[2]; - status = CV_INSTRUMENT_FUN_IPP(ippiMinMax_8u_C1R, (const Ipp8u*)pSrc, srcStep, size, &val[0], &val[1]); - *pMinVal = val[0]; - *pMaxVal = val[1]; - return status; - } -#endif - case ipp16u: - { - Ipp16u val[2]; - status = CV_INSTRUMENT_FUN_IPP(ippiMinMax_16u_C1R, (const Ipp16u*)pSrc, srcStep, size, &val[0], &val[1]); - *pMinVal = val[0]; - *pMaxVal = val[1]; - return status; - } - case ipp16s: - { - Ipp16s val[2]; - status = CV_INSTRUMENT_FUN_IPP(ippiMinMax_16s_C1R, (const Ipp16s*)pSrc, srcStep, size, &val[0], &val[1]); - *pMinVal = val[0]; - *pMaxVal = val[1]; - return status; - } - case ipp32f: return CV_INSTRUMENT_FUN_IPP(ippiMinMax_32f_C1R, (const Ipp32f*)pSrc, srcStep, size, pMinVal, pMaxVal); - default: return ipp_minMaxIndex_wrap(pSrc, srcStep, size, dataType, pMinVal, pMaxVal, NULL, NULL, NULL, 0); - } -} - -static IppStatus ipp_minIdx_wrap(const void* pSrc, int srcStep, IppiSize size, IppDataType dataType, - float* pMinVal, float*, IppiPoint* pMinIndex, IppiPoint*, const Ipp8u*, int) -{ - IppStatus status; - - switch(dataType) - { - case ipp8u: - { - Ipp8u val; - status = CV_INSTRUMENT_FUN_IPP(ippiMinIndx_8u_C1R, (const Ipp8u*)pSrc, srcStep, size, &val, &pMinIndex->x, &pMinIndex->y); - *pMinVal = val; - return status; - } - case ipp16u: - { - Ipp16u val; - status = CV_INSTRUMENT_FUN_IPP(ippiMinIndx_16u_C1R, (const Ipp16u*)pSrc, srcStep, size, &val, &pMinIndex->x, &pMinIndex->y); - *pMinVal = val; - return status; - } - case ipp16s: - { - Ipp16s val; - status = CV_INSTRUMENT_FUN_IPP(ippiMinIndx_16s_C1R, (const Ipp16s*)pSrc, srcStep, size, &val, &pMinIndex->x, &pMinIndex->y); - *pMinVal = val; - return status; - } - case ipp32f: return CV_INSTRUMENT_FUN_IPP(ippiMinIndx_32f_C1R, (const Ipp32f*)pSrc, srcStep, size, pMinVal, &pMinIndex->x, &pMinIndex->y); - default: return ipp_minMaxIndex_wrap(pSrc, srcStep, size, dataType, pMinVal, NULL, pMinIndex, NULL, NULL, 0); - } -} - -static IppStatus ipp_maxIdx_wrap(const void* pSrc, int srcStep, IppiSize size, IppDataType dataType, - float*, float* pMaxVal, IppiPoint*, IppiPoint* pMaxIndex, const Ipp8u*, int) -{ - IppStatus status; - - switch(dataType) - { - case ipp8u: - { - Ipp8u val; - status = CV_INSTRUMENT_FUN_IPP(ippiMaxIndx_8u_C1R, (const Ipp8u*)pSrc, srcStep, size, &val, &pMaxIndex->x, &pMaxIndex->y); - *pMaxVal = val; - return status; - } - case ipp16u: - { - Ipp16u val; - status = CV_INSTRUMENT_FUN_IPP(ippiMaxIndx_16u_C1R, (const Ipp16u*)pSrc, srcStep, size, &val, &pMaxIndex->x, &pMaxIndex->y); - *pMaxVal = val; - return status; - } - case ipp16s: - { - Ipp16s val; - status = CV_INSTRUMENT_FUN_IPP(ippiMaxIndx_16s_C1R, (const Ipp16s*)pSrc, srcStep, size, &val, &pMaxIndex->x, &pMaxIndex->y); - *pMaxVal = val; - return status; - } - case ipp32f: return CV_INSTRUMENT_FUN_IPP(ippiMaxIndx_32f_C1R, (const Ipp32f*)pSrc, srcStep, size, pMaxVal, &pMaxIndex->x, &pMaxIndex->y); - default: return ipp_minMaxIndex_wrap(pSrc, srcStep, size, dataType, NULL, pMaxVal, NULL, pMaxIndex, NULL, 0); - } -} - -typedef IppStatus (*IppMinMaxSelector)(const void* pSrc, int srcStep, IppiSize size, IppDataType dataType, - float* pMinVal, float* pMaxVal, IppiPoint* pMinIndex, IppiPoint* pMaxIndex, const Ipp8u* pMask, int maskStep); - -static bool ipp_minMaxIdx(Mat &src, double* _minVal, double* _maxVal, int* _minIdx, int* _maxIdx, Mat &mask) -{ -#if IPP_VERSION_X100 >= 700 - CV_INSTRUMENT_REGION_IPP(); - -#if IPP_VERSION_X100 < 201800 - // cv::minMaxIdx problem with NaN input - // Disable 32F processing only - if(src.depth() == CV_32F && cv::ipp::getIppTopFeatures() == ippCPUID_SSE42) - return false; -#endif - -#if IPP_VERSION_X100 < 201801 - // cv::minMaxIdx problem with index positions on AVX - if(!mask.empty() && _maxIdx && cv::ipp::getIppTopFeatures() != ippCPUID_SSE42) - return false; -#endif - - IppStatus status; - IppDataType dataType = ippiGetDataType(src.depth()); - float minVal = 0; - float maxVal = 0; - IppiPoint minIdx = {-1, -1}; - IppiPoint maxIdx = {-1, -1}; - - float *pMinVal = (_minVal || _minIdx)?&minVal:NULL; - float *pMaxVal = (_maxVal || _maxIdx)?&maxVal:NULL; - IppiPoint *pMinIdx = (_minIdx)?&minIdx:NULL; - IppiPoint *pMaxIdx = (_maxIdx)?&maxIdx:NULL; - - IppMinMaxSelector ippMinMaxFun = ipp_minMaxIndexMask_wrap; - if(mask.empty()) - { - if(_maxVal && _maxIdx && !_minVal && !_minIdx) - ippMinMaxFun = ipp_maxIdx_wrap; - else if(!_maxVal && !_maxIdx && _minVal && _minIdx) - ippMinMaxFun = ipp_minIdx_wrap; - else if(_maxVal && !_maxIdx && _minVal && !_minIdx) - ippMinMaxFun = ipp_minMax_wrap; - else if(!_maxVal && !_maxIdx && !_minVal && !_minIdx) - return false; - else - ippMinMaxFun = ipp_minMaxIndex_wrap; - } - - if(src.dims <= 2) - { - IppiSize size = ippiSize(src.size()); -#if defined(_WIN32) && !defined(_WIN64) && IPP_VERSION_X100 == 201900 && IPP_DISABLE_MINMAXIDX_MANY_ROWS - if (size.height > 65536) - return false; // test: Core_MinMaxIdx.rows_overflow -#endif - size.width *= src.channels(); - - status = ippMinMaxFun(src.ptr(), (int)src.step, size, dataType, pMinVal, pMaxVal, pMinIdx, pMaxIdx, (Ipp8u*)mask.ptr(), (int)mask.step); - if(status < 0) - return false; - if(_minVal) - *_minVal = minVal; - if(_maxVal) - *_maxVal = maxVal; - if(_minIdx) - { -#if IPP_VERSION_X100 < 201801 - // Should be just ippStsNoOperation check, but there is a bug in the function so we need additional checks - if(status == ippStsNoOperation && !mask.empty() && !pMinIdx->x && !pMinIdx->y) -#else - if(status == ippStsNoOperation) -#endif - { - _minIdx[0] = -1; - _minIdx[1] = -1; - } - else - { - _minIdx[0] = minIdx.y; - _minIdx[1] = minIdx.x; - } - } - if(_maxIdx) - { -#if IPP_VERSION_X100 < 201801 - // Should be just ippStsNoOperation check, but there is a bug in the function so we need additional checks - if(status == ippStsNoOperation && !mask.empty() && !pMaxIdx->x && !pMaxIdx->y) -#else - if(status == ippStsNoOperation) -#endif - { - _maxIdx[0] = -1; - _maxIdx[1] = -1; - } - else - { - _maxIdx[0] = maxIdx.y; - _maxIdx[1] = maxIdx.x; - } - } - } - else - { - const Mat *arrays[] = {&src, mask.empty()?NULL:&mask, NULL}; - uchar *ptrs[3] = {NULL}; - NAryMatIterator it(arrays, ptrs); - IppiSize size = ippiSize(it.size*src.channels(), 1); - int srcStep = (int)(size.width*src.elemSize1()); - int maskStep = size.width; - size_t idxPos = 1; - size_t minIdxAll = 0; - size_t maxIdxAll = 0; - float minValAll = IPP_MAXABS_32F; - float maxValAll = -IPP_MAXABS_32F; - - for(size_t i = 0; i < it.nplanes; i++, ++it, idxPos += size.width) - { - status = ippMinMaxFun(ptrs[0], srcStep, size, dataType, pMinVal, pMaxVal, pMinIdx, pMaxIdx, ptrs[1], maskStep); - if(status < 0) - return false; -#if IPP_VERSION_X100 > 201701 - // Zero-mask check, function should return ippStsNoOperation warning - if(status == ippStsNoOperation) - continue; -#else - // Crude zero-mask check, waiting for fix in IPP function - if(ptrs[1]) - { - Mat localMask(Size(size.width, 1), CV_8U, ptrs[1], maskStep); - if(!cv::countNonZero(localMask)) - continue; - } -#endif - - if(_minVal && minVal < minValAll) - { - minValAll = minVal; - minIdxAll = idxPos+minIdx.x; - } - if(_maxVal && maxVal > maxValAll) - { - maxValAll = maxVal; - maxIdxAll = idxPos+maxIdx.x; - } - } - if(!src.empty() && mask.empty()) - { - if(minIdxAll == 0) - minIdxAll = 1; - if(maxValAll == 0) - maxValAll = 1; - } - - if(_minVal) - *_minVal = minValAll; - if(_maxVal) - *_maxVal = maxValAll; - if(_minIdx) - ofs2idx(src, minIdxAll, _minIdx); - if(_maxIdx) - ofs2idx(src, maxIdxAll, _maxIdx); - } - - return true; -#else - CV_UNUSED(src); CV_UNUSED(minVal); CV_UNUSED(maxVal); CV_UNUSED(minIdx); CV_UNUSED(maxIdx); CV_UNUSED(mask); - return false; -#endif -} -#endif - } void cv::minMaxIdx(InputArray _src, double* minVal, @@ -1467,8 +1157,6 @@ void cv::minMaxIdx(InputArray _src, double* minVal, } } - CV_IPP_RUN_FAST(ipp_minMaxIdx(src, minVal, maxVal, minIdx, maxIdx, mask)) - MinMaxIdxFunc func = getMinmaxTab(depth); CV_Assert( func != 0 ); From a2a2f37ebbfb4c93dcfd20d28ca9b80bac11c887 Mon Sep 17 00:00:00 2001 From: Yuantao Feng Date: Tue, 25 Mar 2025 12:59:59 +0800 Subject: [PATCH 40/94] Merge pull request #27115 from fengyuentau:4x/hal_rvv/normDiff core: refactored normDiff in hal_rvv and extended with support of more data types #27115 Merge wtih https://github.com/opencv/opencv_extra/pull/1246. ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- 3rdparty/hal_rvv/hal_rvv.hpp | 1 + 3rdparty/hal_rvv/hal_rvv_1p0/common.hpp | 22 + 3rdparty/hal_rvv/hal_rvv_1p0/norm.hpp | 5 +- 3rdparty/hal_rvv/hal_rvv_1p0/norm_diff.hpp | 1720 +++++++++++++------- modules/core/src/norm.simd.hpp | 112 +- 5 files changed, 1235 insertions(+), 625 deletions(-) diff --git a/3rdparty/hal_rvv/hal_rvv.hpp b/3rdparty/hal_rvv/hal_rvv.hpp index a0c1d0e52f..8287ebeda9 100644 --- a/3rdparty/hal_rvv/hal_rvv.hpp +++ b/3rdparty/hal_rvv/hal_rvv.hpp @@ -5,6 +5,7 @@ #ifndef OPENCV_HAL_RVV_HPP_INCLUDED #define OPENCV_HAL_RVV_HPP_INCLUDED +#include "opencv2/core/base.hpp" #include "opencv2/core/hal/interface.h" #include "opencv2/imgproc/hal/interface.h" diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/common.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/common.hpp index 8db03267e1..9fc01d2897 100644 --- a/3rdparty/hal_rvv/hal_rvv_1p0/common.hpp +++ b/3rdparty/hal_rvv/hal_rvv_1p0/common.hpp @@ -1,6 +1,9 @@ // This file is part of OpenCV project. // It is subject to the license terms in the LICENSE file found in the top-level directory // of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2025, SpaceMIT Inc., all rights reserved. +// Third party copyrights are property of their respective owners. #ifndef OPENCV_HAL_RVV_COMMON_HPP_INCLUDED #define OPENCV_HAL_RVV_COMMON_HPP_INCLUDED @@ -9,6 +12,8 @@ namespace cv { namespace cv_hal_rvv { namespace custom_intrin { +#define CV_HAL_RVV_NOOP(a) (a) + #define CV_HAL_RVV_COMMON_CUSTOM_INTRIN_ABS(_Tpvs, _Tpvd, shift, suffix) \ inline _Tpvd __riscv_vabs(const _Tpvs& v, const int vl) { \ _Tpvs mask = __riscv_vsra(v, shift, vl); \ @@ -25,6 +30,23 @@ CV_HAL_RVV_COMMON_CUSTOM_INTRIN_ABS(vint16m8_t, vuint16m8_t, 15, u16m8) CV_HAL_RVV_COMMON_CUSTOM_INTRIN_ABS(vint32m4_t, vuint32m4_t, 31, u32m4) CV_HAL_RVV_COMMON_CUSTOM_INTRIN_ABS(vint32m8_t, vuint32m8_t, 31, u32m8) +#define CV_HAL_RVV_COMMON_CUSTOM_INTRIN_ABSDIFF(_Tpvs, _Tpvd, cast, sub, max, min) \ + inline _Tpvd __riscv_vabd(const _Tpvs& v1, const _Tpvs& v2, const int vl) { \ + return cast(__riscv_##sub(__riscv_##max(v1, v2, vl), __riscv_##min(v1, v2, vl), vl)); \ + } + +CV_HAL_RVV_COMMON_CUSTOM_INTRIN_ABSDIFF(vuint8m4_t, vuint8m4_t, CV_HAL_RVV_NOOP, vsub, vmaxu, vminu) +CV_HAL_RVV_COMMON_CUSTOM_INTRIN_ABSDIFF(vuint8m8_t, vuint8m8_t, CV_HAL_RVV_NOOP, vsub, vmaxu, vminu) +CV_HAL_RVV_COMMON_CUSTOM_INTRIN_ABSDIFF(vuint16m2_t, vuint16m2_t, CV_HAL_RVV_NOOP, vsub, vmaxu, vminu) +CV_HAL_RVV_COMMON_CUSTOM_INTRIN_ABSDIFF(vuint16m8_t, vuint16m8_t, CV_HAL_RVV_NOOP, vsub, vmaxu, vminu) + +CV_HAL_RVV_COMMON_CUSTOM_INTRIN_ABSDIFF(vint8m4_t, vuint8m4_t, __riscv_vreinterpret_u8m4, vsub, vmax, vmin) +CV_HAL_RVV_COMMON_CUSTOM_INTRIN_ABSDIFF(vint8m8_t, vuint8m8_t, __riscv_vreinterpret_u8m8, vsub, vmax, vmin) +CV_HAL_RVV_COMMON_CUSTOM_INTRIN_ABSDIFF(vint16m2_t, vuint16m2_t, __riscv_vreinterpret_u16m2, vsub, vmax, vmin) +CV_HAL_RVV_COMMON_CUSTOM_INTRIN_ABSDIFF(vint16m8_t, vuint16m8_t, __riscv_vreinterpret_u16m8, vsub, vmax, vmin) +CV_HAL_RVV_COMMON_CUSTOM_INTRIN_ABSDIFF(vint32m4_t, vuint32m4_t, __riscv_vreinterpret_u32m4, vsub, vmax, vmin) +CV_HAL_RVV_COMMON_CUSTOM_INTRIN_ABSDIFF(vint32m8_t, vuint32m8_t, __riscv_vreinterpret_u32m8, vsub, vmax, vmin) + }}} // cv::cv_hal_rvv::custom_intrin #endif diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/norm.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/norm.hpp index 9e79940390..c35c0a3bd5 100644 --- a/3rdparty/hal_rvv/hal_rvv_1p0/norm.hpp +++ b/3rdparty/hal_rvv/hal_rvv_1p0/norm.hpp @@ -166,7 +166,7 @@ struct NormL1_RVV { for (int i = 0; i < n; i += vl) { vl = __riscv_vsetvl_e8m8(n - i); auto v = __riscv_vle8_v_u8m8(src + i, vl); - s = __riscv_vwredsumu(__riscv_vwredsumu_tu(zero, v, zero, vl), s, vl); + s = __riscv_vwredsumu(__riscv_vwredsumu_tu(zero, v, zero, vl), s, __riscv_vsetvlmax_e16m1()); } return __riscv_vmv_x(s); } @@ -181,7 +181,7 @@ struct NormL1_RVV { for (int i = 0; i < n; i += vl) { vl = __riscv_vsetvl_e8m8(n - i); auto v = custom_intrin::__riscv_vabs(__riscv_vle8_v_i8m8(src + i, vl), vl); - s = __riscv_vwredsumu(__riscv_vwredsumu_tu(zero, v, zero, vl), s, vl); + s = __riscv_vwredsumu(__riscv_vwredsumu_tu(zero, v, zero, vl), s, __riscv_vsetvlmax_e16m1()); } return __riscv_vmv_x(s); } @@ -1008,6 +1008,7 @@ inline int norm(const uchar* src, size_t src_step, const uchar* mask, size_t mas sizeof(int), sizeof(float), sizeof(int64_t), 0, }; + CV_Assert(elem_size_tab[depth]); bool src_continuous = (src_step == width * elem_size_tab[depth] * cn || (src_step != width * elem_size_tab[depth] * cn && height == 1)); bool mask_continuous = (mask_step == static_cast(width)); diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/norm_diff.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/norm_diff.hpp index cfb9fba6c7..d70a50a987 100644 --- a/3rdparty/hal_rvv/hal_rvv_1p0/norm_diff.hpp +++ b/3rdparty/hal_rvv/hal_rvv_1p0/norm_diff.hpp @@ -1,606 +1,1216 @@ // This file is part of OpenCV project. // It is subject to the license terms in the LICENSE file found in the top-level directory // of this distribution and at http://opencv.org/license.html. - +// // Copyright (C) 2025, Institute of Software, Chinese Academy of Sciences. +// Copyright (C) 2025, SpaceMIT Inc., all rights reserved. +// Third party copyrights are property of their respective owners. #ifndef OPENCV_HAL_RVV_NORM_DIFF_HPP_INCLUDED #define OPENCV_HAL_RVV_NORM_DIFF_HPP_INCLUDED -#include +#include "common.hpp" namespace cv { namespace cv_hal_rvv { namespace norm_diff { #undef cv_hal_normDiff #define cv_hal_normDiff cv::cv_hal_rvv::norm_diff::normDiff -inline int normDiffInf_8UC1(const uchar* src1, size_t src1_step, const uchar* src2, size_t src2_step, const uchar* mask, size_t mask_step, int width, int height, double* result) -{ - int vlmax = __riscv_vsetvlmax_e8m8(); - auto vec_max = __riscv_vmv_v_x_u8m8(0, vlmax); +namespace { - if (mask) - { - for (int i = 0; i < height; i++) - { - const uchar* src1_row = src1 + i * src1_step; - const uchar* src2_row = src2 + i * src2_step; - const uchar* mask_row = mask + i * mask_step; - int vl; - for (int j = 0; j < width; j += vl) - { - vl = __riscv_vsetvl_e8m8(width - j); - auto vec_src1 = __riscv_vle8_v_u8m8(src1_row + j, vl); - auto vec_src2 = __riscv_vle8_v_u8m8(src2_row + j, vl); - auto vec_mask = __riscv_vle8_v_u8m8(mask_row + j, vl); - auto bool_mask = __riscv_vmsne(vec_mask, 0, vl); - auto vec_src = __riscv_vsub_vv_u8m8_m(bool_mask, __riscv_vmaxu_vv_u8m8_m(bool_mask, vec_src1, vec_src2, vl), - __riscv_vminu_vv_u8m8_m(bool_mask, vec_src1, vec_src2, vl), vl); - vec_max = __riscv_vmaxu_tumu(bool_mask, vec_max, vec_max, vec_src, vl); +template +struct NormDiffInf_RVV { + inline ST operator() (const T* src1, const T* src2, int n) const { + ST s = 0; + for (int i = 0; i < n; i++) { + s = std::max(s, (ST)std::abs(src1[i] - src2[i])); + } + return s; + } +}; + +template +struct NormDiffL1_RVV { + inline ST operator() (const T* src1, const T* src2, int n) const { + ST s = 0; + for (int i = 0; i < n; i++) { + s += std::abs(src1[i] - src2[i]); + } + return s; + } +}; + +template +struct NormDiffL2_RVV { + inline ST operator() (const T* src1, const T* src2, int n) const { + ST s = 0; + for (int i = 0; i < n; i++) { + ST v = (ST)src1[i] - (ST)src2[i]; + s += v * v; + } + return s; + } +}; + +template<> +struct NormDiffInf_RVV { + int operator() (const uchar* src1, const uchar* src2, int n) const { + int vlmax = __riscv_vsetvlmax_e8m8(); + auto s = __riscv_vmv_v_x_u8m8(0, vlmax); + int vl; + for (int i = 0; i < n; i += vl) { + vl = __riscv_vsetvl_e8m8(n - i); + auto v1 = __riscv_vle8_v_u8m8(src1 + i, vl); + auto v2 = __riscv_vle8_v_u8m8(src2 + i, vl); + auto v = custom_intrin::__riscv_vabd(v1, v2, vl); + s = __riscv_vmaxu_tu(s, s, v, vl); + } + return __riscv_vmv_x(__riscv_vredmaxu(s, __riscv_vmv_s_x_u8m1(0, __riscv_vsetvlmax_e8m1()), vlmax)); + } +}; + +template<> +struct NormDiffInf_RVV { + int operator() (const schar* src1, const schar* src2, int n) const { + int vlmax = __riscv_vsetvlmax_e8m8(); + auto s = __riscv_vmv_v_x_u8m8(0, vlmax); + int vl; + for (int i = 0; i < n; i += vl) { + vl = __riscv_vsetvl_e8m8(n - i); + auto v1 = __riscv_vle8_v_i8m8(src1 + i, vl); + auto v2 = __riscv_vle8_v_i8m8(src2 + i, vl); + auto v = custom_intrin::__riscv_vabd(v1, v2, vl); + s = __riscv_vmaxu_tu(s, s, v, vl); + } + return __riscv_vmv_x(__riscv_vredmaxu(s, __riscv_vmv_s_x_u8m1(0, __riscv_vsetvlmax_e8m1()), vlmax)); + } +}; + +template<> +struct NormDiffInf_RVV { + int operator() (const ushort* src1, const ushort* src2, int n) const { + int vlmax = __riscv_vsetvlmax_e16m8(); + auto s = __riscv_vmv_v_x_u16m8(0, vlmax); + int vl; + for (int i = 0; i < n; i += vl) { + vl = __riscv_vsetvl_e16m8(n - i); + auto v1 = __riscv_vle16_v_u16m8(src1 + i, vl); + auto v2 = __riscv_vle16_v_u16m8(src2 + i, vl); + auto v = custom_intrin::__riscv_vabd(v1, v2, vl); + s = __riscv_vmaxu_tu(s, s, v, vl); + } + return __riscv_vmv_x(__riscv_vredmaxu(s, __riscv_vmv_s_x_u16m1(0, __riscv_vsetvlmax_e16m1()), vlmax)); + } +}; + +template<> +struct NormDiffInf_RVV { + int operator() (const short* src1, const short* src2, int n) const { + int vlmax = __riscv_vsetvlmax_e16m8(); + auto s = __riscv_vmv_v_x_u16m8(0, vlmax); + int vl; + for (int i = 0; i < n; i += vl) { + vl = __riscv_vsetvl_e16m8(n - i); + auto v1 = __riscv_vle16_v_i16m8(src1 + i, vl); + auto v2 = __riscv_vle16_v_i16m8(src2 + i, vl); + auto v = custom_intrin::__riscv_vabd(v1, v2, vl); + s = __riscv_vmaxu_tu(s, s, v, vl); + } + return __riscv_vmv_x(__riscv_vredmaxu(s, __riscv_vmv_s_x_u16m1(0, __riscv_vsetvlmax_e16m1()), vlmax)); + } +}; + +template<> +struct NormDiffInf_RVV { + int operator() (const int* src1, const int* src2, int n) const { + int vlmax = __riscv_vsetvlmax_e32m8(); + auto s = __riscv_vmv_v_x_u32m8(0, vlmax); + int vl; + for (int i = 0; i < n; i += vl) { + vl = __riscv_vsetvl_e32m8(n - i); + auto v1 = __riscv_vle32_v_i32m8(src1 + i, vl); + auto v2 = __riscv_vle32_v_i32m8(src2 + i, vl); + auto v = custom_intrin::__riscv_vabd(v1, v2, vl); + s = __riscv_vmaxu_tu(s, s, v, vl); + } + return __riscv_vmv_x(__riscv_vredmaxu(s, __riscv_vmv_s_x_u32m1(0, __riscv_vsetvlmax_e32m1()), vlmax)); + } +}; + +template<> +struct NormDiffInf_RVV { + float operator() (const float* src1, const float* src2, int n) const { + int vlmax = __riscv_vsetvlmax_e32m8(); + auto s = __riscv_vfmv_v_f_f32m8(0, vlmax); + int vl; + for (int i = 0; i < n; i += vl) { + vl = __riscv_vsetvl_e32m8(n - i); + auto v1 = __riscv_vle32_v_f32m8(src1 + i, vl); + auto v2 = __riscv_vle32_v_f32m8(src2 + i, vl); + auto v = __riscv_vfabs(__riscv_vfsub(v1, v2, vl), vl); + s = __riscv_vfmax_tu(s, s, v, vl); + } + return __riscv_vfmv_f(__riscv_vfredmax(s, __riscv_vfmv_s_f_f32m1(0, __riscv_vsetvlmax_e32m1()), vlmax)); + } +}; + +template<> +struct NormDiffInf_RVV { + double operator() (const double* src1, const double* src2, int n) const { + int vlmax = __riscv_vsetvlmax_e64m8(); + auto s = __riscv_vfmv_v_f_f64m8(0, vlmax); + int vl; + for (int i = 0; i < n; i += vl) { + vl = __riscv_vsetvl_e64m8(n - i); + auto v1 = __riscv_vle64_v_f64m8(src1 + i, vl); + auto v2 = __riscv_vle64_v_f64m8(src2 + i, vl); + auto v = __riscv_vfabs(__riscv_vfsub(v1, v2, vl), vl); + s = __riscv_vfmax_tu(s, s, v, vl); + } + return __riscv_vfmv_f(__riscv_vfredmax(s, __riscv_vfmv_s_f_f64m1(0, __riscv_vsetvlmax_e64m1()), vlmax)); + } +}; + +template<> +struct NormDiffL1_RVV { + int operator() (const uchar* src1, const uchar* src2, int n) const { + auto s = __riscv_vmv_v_x_u32m1(0, __riscv_vsetvlmax_e32m1()); + auto zero = __riscv_vmv_v_x_u16m1(0, __riscv_vsetvlmax_e16m1()); + int vl; + for (int i = 0; i < n; i += vl) { + vl = __riscv_vsetvl_e8m8(n - i); + auto v1 = __riscv_vle8_v_u8m8(src1 + i, vl); + auto v2 = __riscv_vle8_v_u8m8(src2 + i, vl); + auto v = custom_intrin::__riscv_vabd(v1, v2, vl); + s = __riscv_vwredsumu(__riscv_vwredsumu_tu(zero, v, zero, vl), s, __riscv_vsetvlmax_e16m1()); + } + return __riscv_vmv_x(s); + } +}; + +template<> +struct NormDiffL1_RVV { + int operator() (const schar* src1, const schar* src2, int n) const { + auto s = __riscv_vmv_v_x_u32m1(0, __riscv_vsetvlmax_e32m1()); + auto zero = __riscv_vmv_v_x_u16m1(0, __riscv_vsetvlmax_e16m1()); + int vl; + for (int i = 0; i < n; i += vl) { + vl = __riscv_vsetvl_e8m8(n - i); + auto v1 = __riscv_vle8_v_i8m8(src1 + i, vl); + auto v2 = __riscv_vle8_v_i8m8(src2 + i, vl); + auto v = custom_intrin::__riscv_vabd(v1, v2, vl); + s = __riscv_vwredsumu(__riscv_vwredsumu_tu(zero, v, zero, vl), s, __riscv_vsetvlmax_e16m1()); + } + return __riscv_vmv_x(s); + } +}; + +template<> +struct NormDiffL1_RVV { + int operator() (const ushort* src1, const ushort* src2, int n) const { + auto s = __riscv_vmv_v_x_u32m1(0, __riscv_vsetvlmax_e32m1()); + int vl; + for (int i = 0; i < n; i += vl) { + vl = __riscv_vsetvl_e16m8(n - i); + auto v1 = __riscv_vle16_v_u16m8(src1 + i, vl); + auto v2 = __riscv_vle16_v_u16m8(src2 + i, vl); + auto v = custom_intrin::__riscv_vabd(v1, v2, vl); + s = __riscv_vwredsumu(v, s, vl); + } + return __riscv_vmv_x(s); + } +}; + +template<> +struct NormDiffL1_RVV { + int operator() (const short* src1, const short* src2, int n) const { + auto s = __riscv_vmv_v_x_u32m1(0, __riscv_vsetvlmax_e32m1()); + int vl; + for (int i = 0; i < n; i += vl) { + vl = __riscv_vsetvl_e16m8(n - i); + auto v1 = __riscv_vle16_v_i16m8(src1 + i, vl); + auto v2 = __riscv_vle16_v_i16m8(src2 + i, vl); + auto v = custom_intrin::__riscv_vabd(v1, v2, vl); + s = __riscv_vwredsumu(v, s, vl); + } + return __riscv_vmv_x(s); + } +}; + +template<> +struct NormDiffL1_RVV { + double operator() (const int* src1, const int* src2, int n) const { + int vlmax = __riscv_vsetvlmax_e32m4(); + auto s = __riscv_vfmv_v_f_f64m8(0, vlmax); + int vl; + for (int i = 0; i < n; i += vl) { + vl = __riscv_vsetvl_e32m4(n - i); + auto v1 = __riscv_vle32_v_i32m4(src1 + i, vl); + auto v2 = __riscv_vle32_v_i32m4(src2 + i, vl); + auto v = custom_intrin::__riscv_vabd(v1, v2, vl); + s = __riscv_vfadd_tu(s, s, __riscv_vfwcvt_f(v, vl), vl); + } + return __riscv_vfmv_f(__riscv_vfredosum(s, __riscv_vfmv_s_f_f64m1(0, __riscv_vsetvlmax_e64m1()), vlmax)); + } +}; + +template<> +struct NormDiffL1_RVV { + double operator() (const float* src1, const float* src2, int n) const { + int vlmax = __riscv_vsetvlmax_e32m4(); + auto s = __riscv_vfmv_v_f_f64m8(0, vlmax); + int vl; + for (int i = 0; i < n; i += vl) { + vl = __riscv_vsetvl_e32m4(n - i); + auto v1 = __riscv_vle32_v_f32m4(src1 + i, vl); + auto v2 = __riscv_vle32_v_f32m4(src2 + i, vl); + auto v = __riscv_vfabs(__riscv_vfsub(v1, v2, vl), vl); + s = __riscv_vfadd_tu(s, s, __riscv_vfwcvt_f(v, vl), vl); + } + return __riscv_vfmv_f(__riscv_vfredosum(s, __riscv_vfmv_s_f_f64m1(0, __riscv_vsetvlmax_e64m1()), vlmax)); + } +}; + +template<> +struct NormDiffL1_RVV { + double operator() (const double* src1, const double* src2, int n) const { + int vlmax = __riscv_vsetvlmax_e64m8(); + auto s = __riscv_vfmv_v_f_f64m8(0, vlmax); + int vl; + for (int i = 0; i < n; i += vl) { + vl = __riscv_vsetvl_e64m8(n - i); + auto v1 = __riscv_vle64_v_f64m8(src1 + i, vl); + auto v2 = __riscv_vle64_v_f64m8(src2 + i, vl); + auto v = __riscv_vfabs(__riscv_vfsub(v1, v2, vl), vl); + s = __riscv_vfadd_tu(s, s, v, vl); + } + return __riscv_vfmv_f(__riscv_vfredosum(s, __riscv_vfmv_s_f_f64m1(0, __riscv_vsetvlmax_e64m1()), vlmax)); + } +}; + +template<> +struct NormDiffL2_RVV { + int operator() (const uchar* src1, const uchar* src2, int n) const { + auto s = __riscv_vmv_v_x_u32m1(0, __riscv_vsetvlmax_e32m1()); + int vl; + for (int i = 0; i < n; i += vl) { + vl = __riscv_vsetvl_e8m4(n - i); + auto v1 = __riscv_vle8_v_u8m4(src1 + i, vl); + auto v2 = __riscv_vle8_v_u8m4(src2 + i, vl); + auto v = custom_intrin::__riscv_vabd(v1, v2, vl); + s = __riscv_vwredsumu(__riscv_vwmulu(v, v, vl), s, vl); + } + return __riscv_vmv_x(s); + } +}; + +template<> +struct NormDiffL2_RVV { + int operator() (const schar* src1, const schar* src2, int n) const { + auto s = __riscv_vmv_v_x_u32m1(0, __riscv_vsetvlmax_e32m1()); + int vl; + for (int i = 0; i < n; i += vl) { + vl = __riscv_vsetvl_e8m4(n - i); + auto v1 = __riscv_vle8_v_i8m4(src1 + i, vl); + auto v2 = __riscv_vle8_v_i8m4(src2 + i, vl); + auto v = custom_intrin::__riscv_vabd(v1, v2, vl); + s = __riscv_vwredsumu(__riscv_vwmulu(v, v, vl), s, vl); + } + return __riscv_vmv_x(s); + } +}; + +template<> +struct NormDiffL2_RVV { + double operator() (const ushort* src1, const ushort* src2, int n) const { + int vlmax = __riscv_vsetvlmax_e16m2(); + auto s = __riscv_vfmv_v_f_f64m8(0, vlmax); + int vl; + for (int i = 0; i < n; i += vl) { + vl = __riscv_vsetvl_e16m2(n - i); + auto v1 = __riscv_vle16_v_u16m2(src1 + i, vl); + auto v2 = __riscv_vle16_v_u16m2(src2 + i, vl); + auto v = custom_intrin::__riscv_vabd(v1, v2, vl); + auto v_mul = __riscv_vwmulu(v, v, vl); + s = __riscv_vfadd_tu(s, s, __riscv_vfwcvt_f(v_mul, vl), vl); + } + return __riscv_vfmv_f(__riscv_vfredosum(s, __riscv_vfmv_s_f_f64m1(0, __riscv_vsetvlmax_e64m1()), vlmax)); + } +}; + +template<> +struct NormDiffL2_RVV { + double operator() (const short* src1, const short* src2, int n) const { + int vlmax = __riscv_vsetvlmax_e16m2(); + auto s = __riscv_vfmv_v_f_f64m8(0, vlmax); + int vl; + for (int i = 0; i < n; i += vl) { + vl = __riscv_vsetvl_e16m2(n - i); + auto v1 = __riscv_vle16_v_i16m2(src1 + i, vl); + auto v2 = __riscv_vle16_v_i16m2(src2 + i, vl); + auto v = custom_intrin::__riscv_vabd(v1, v2, vl); + auto v_mul = __riscv_vwmulu(v, v, vl); + s = __riscv_vfadd_tu(s, s, __riscv_vfwcvt_f(v_mul, vl), vl); + } + return __riscv_vfmv_f(__riscv_vfredosum(s, __riscv_vfmv_s_f_f64m1(0, __riscv_vsetvlmax_e64m1()), vlmax)); + } +}; + +template<> +struct NormDiffL2_RVV { + double operator() (const int* src1, const int* src2, int n) const { + int vlmax = __riscv_vsetvlmax_e32m4(); + auto s = __riscv_vfmv_v_f_f64m8(0, vlmax); + int vl; + for (int i = 0; i < n; i += vl) { + vl = __riscv_vsetvl_e32m4(n - i); + auto v1 = __riscv_vle32_v_i32m4(src1 + i, vl); + auto v2 = __riscv_vle32_v_i32m4(src2 + i, vl); + auto v = custom_intrin::__riscv_vabd(v1, v2, vl); + auto v_mul = __riscv_vwmulu(v, v, vl); + s = __riscv_vfadd_tu(s, s, __riscv_vfcvt_f(v_mul, vl), vl); + } + return __riscv_vfmv_f(__riscv_vfredosum(s, __riscv_vfmv_s_f_f64m1(0, __riscv_vsetvlmax_e64m1()), vlmax)); + } +}; + +template<> +struct NormDiffL2_RVV { + double operator() (const float* src1, const float* src2, int n) const { + int vlmax = __riscv_vsetvlmax_e32m4(); + auto s = __riscv_vfmv_v_f_f64m8(0, vlmax); + int vl; + for (int i = 0; i < n; i += vl) { + vl = __riscv_vsetvl_e32m4(n - i); + auto v1 = __riscv_vle32_v_f32m4(src1 + i, vl); + auto v2 = __riscv_vle32_v_f32m4(src2 + i, vl); + auto v = __riscv_vfsub(v1, v2, vl); + auto v_mul = __riscv_vfwmul(v, v, vl); + s = __riscv_vfadd_tu(s, s, v_mul, vl); + } + return __riscv_vfmv_f(__riscv_vfredosum(s, __riscv_vfmv_s_f_f64m1(0, __riscv_vsetvlmax_e64m1()), vlmax)); + } +}; + +template<> +struct NormDiffL2_RVV { + double operator() (const double* src1, const double* src2, int n) const { + int vlmax = __riscv_vsetvlmax_e64m8(); + auto s = __riscv_vfmv_v_f_f64m8(0, vlmax); + int vl; + for (int i = 0; i < n; i += vl) { + vl = __riscv_vsetvl_e64m8(n - i); + auto v1 = __riscv_vle64_v_f64m8(src1 + i, vl); + auto v2 = __riscv_vle64_v_f64m8(src2 + i, vl); + auto v = __riscv_vfsub(v1, v2, vl); + auto v_mul = __riscv_vfmul(v, v, vl); + s = __riscv_vfadd_tu(s, s, v_mul, vl); + } + return __riscv_vfmv_f(__riscv_vfredosum(s, __riscv_vfmv_s_f_f64m1(0, __riscv_vsetvlmax_e64m1()), vlmax)); + } +}; + +// Norm with mask + +template +struct MaskedNormDiffInf_RVV { + inline ST operator() (const T* src1, const T* src2, const uchar* mask, int len, int cn) const { + ST s = 0; + for( int i = 0; i < len; i++, src1 += cn, src2 += cn ) { + if( mask[i] ) { + for( int k = 0; k < cn; k++ ) { + s = std::max(s, (ST)std::abs(src1[k] - src2[k])); + } } } + return s; } - else - { - for (int i = 0; i < height; i++) - { - const uchar* src1_row = src1 + i * src1_step; - const uchar* src2_row = src2 + i * src2_step; - int vl; - for (int j = 0; j < width; j += vl) - { - vl = __riscv_vsetvl_e8m8(width - j); - auto vec_src1 = __riscv_vle8_v_u8m8(src1_row + j, vl); - auto vec_src2 = __riscv_vle8_v_u8m8(src2_row + j, vl); - auto vec_src = __riscv_vsub(__riscv_vmaxu(vec_src1, vec_src2, vl), __riscv_vminu(vec_src1, vec_src2, vl), vl); - vec_max = __riscv_vmaxu_tu(vec_max, vec_max, vec_src, vl); +}; + +template +struct MaskedNormDiffL1_RVV { + inline ST operator() (const T* src1, const T* src2, const uchar* mask, int len, int cn) const { + ST s = 0; + for( int i = 0; i < len; i++, src1 += cn, src2 += cn ) { + if( mask[i] ) { + for( int k = 0; k < cn; k++ ) { + s += std::abs(src1[k] - src2[k]); + } } } + return s; } - auto sc_max = __riscv_vmv_s_x_u8m1(0, vlmax); - sc_max = __riscv_vredmaxu(vec_max, sc_max, vlmax); - *result = __riscv_vmv_x(sc_max); +}; - return CV_HAL_ERROR_OK; +template +struct MaskedNormDiffL2_RVV { + inline ST operator() (const T* src1, const T* src2, const uchar* mask, int len, int cn) const { + ST s = 0; + for( int i = 0; i < len; i++, src1 += cn, src2 += cn ) { + if( mask[i] ) { + for( int k = 0; k < cn; k++ ) { + ST v = (ST)src1[k] - (ST)src2[k]; + s += v * v; + } + } + } + return s; + } +}; + +template<> +struct MaskedNormDiffInf_RVV { + int operator() (const uchar* src1, const uchar* src2, const uchar* mask, int len, int cn) const { + int vlmax = __riscv_vsetvlmax_e8m8(); + auto s = __riscv_vmv_v_x_u8m8(0, vlmax); + if (cn == 1) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e8m8(len - i); + auto v1 = __riscv_vle8_v_u8m8(src1 + i, vl); + auto v2 = __riscv_vle8_v_u8m8(src2 + i, vl); + auto v = custom_intrin::__riscv_vabd(v1, v2, vl); + auto m = __riscv_vle8_v_u8m8(mask + i, vl); + auto b = __riscv_vmsne(m, 0, vl); + s = __riscv_vmaxu_tumu(b, s, s, v, vl); + } + } else if (cn == 4) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e8m2(len - i); + auto v1 = __riscv_vle8_v_u8m8(src1 + i * 4, vl * 4); + auto v2 = __riscv_vle8_v_u8m8(src2 + i * 4, vl * 4); + auto v = custom_intrin::__riscv_vabd(v1, v2, vl * 4); + auto m = __riscv_vle8_v_u8m2(mask + i, vl); + auto b = __riscv_vmsne(__riscv_vreinterpret_u8m8(__riscv_vmul(__riscv_vzext_vf4(__riscv_vminu(m, 1, vl), vl), 0x01010101, vl)), 0, vl * 4); + s = __riscv_vmaxu_tumu(b, s, s, v, vl * 4); + } + } else { + for (int cn_index = 0; cn_index < cn; cn_index++) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e8m8(len - i); + auto v1 = __riscv_vlse8_v_u8m8(src1 + cn * i + cn_index, sizeof(uchar) * cn, vl); + auto v2 = __riscv_vlse8_v_u8m8(src2 + cn * i + cn_index, sizeof(uchar) * cn, vl); + auto v = custom_intrin::__riscv_vabd(v1, v2, vl); + auto m = __riscv_vle8_v_u8m8(mask + i, vl); + auto b = __riscv_vmsne(m, 0, vl); + s = __riscv_vmaxu_tumu(b, s, s, v, vl); + } + } + } + return __riscv_vmv_x(__riscv_vredmaxu(s, __riscv_vmv_s_x_u8m1(0, __riscv_vsetvlmax_e8m1()), vlmax)); + } +}; + +template<> +struct MaskedNormDiffInf_RVV { + int operator() (const schar* src1, const schar* src2, const uchar* mask, int len, int cn) const { + int vlmax = __riscv_vsetvlmax_e8m8(); + auto s = __riscv_vmv_v_x_u8m8(0, vlmax); + for (int cn_index = 0; cn_index < cn; cn_index++) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e8m8(len - i); + auto v1 = __riscv_vlse8_v_i8m8(src1 + cn * i + cn_index, sizeof(schar) * cn, vl); + auto v2 = __riscv_vlse8_v_i8m8(src2 + cn * i + cn_index, sizeof(schar) * cn, vl); + auto v = custom_intrin::__riscv_vabd(v1, v2, vl); + auto m = __riscv_vle8_v_u8m8(mask + i, vl); + auto b = __riscv_vmsne(m, 0, vl); + s = __riscv_vmaxu_tumu(b, s, s, v, vl); + } + } + return __riscv_vmv_x(__riscv_vredmaxu(s, __riscv_vmv_s_x_u8m1(0, __riscv_vsetvlmax_e8m1()), vlmax)); + } +}; + +template<> +struct MaskedNormDiffInf_RVV { + int operator() (const ushort* src1, const ushort* src2, const uchar* mask, int len, int cn) const { + int vlmax = __riscv_vsetvlmax_e16m8(); + auto s = __riscv_vmv_v_x_u16m8(0, vlmax); + for (int cn_index = 0; cn_index < cn; cn_index++) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e16m8(len - i); + auto v1 = __riscv_vlse16_v_u16m8(src1 + cn * i + cn_index, sizeof(ushort) * cn, vl); + auto v2 = __riscv_vlse16_v_u16m8(src2 + cn * i + cn_index, sizeof(ushort) * cn, vl); + auto v = custom_intrin::__riscv_vabd(v1, v2, vl); + auto m = __riscv_vle8_v_u8m4(mask + i, vl); + auto b = __riscv_vmsne(m, 0, vl); + s = __riscv_vmaxu_tumu(b, s, s, v, vl); + } + } + return __riscv_vmv_x(__riscv_vredmaxu(s, __riscv_vmv_s_x_u16m1(0, __riscv_vsetvlmax_e16m1()), vlmax)); + } +}; + +template<> +struct MaskedNormDiffInf_RVV { + int operator() (const short* src1, const short* src2, const uchar* mask, int len, int cn) const { + int vlmax = __riscv_vsetvlmax_e16m8(); + auto s = __riscv_vmv_v_x_u16m8(0, vlmax); + for (int cn_index = 0; cn_index < cn; cn_index++) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e16m8(len - i); + auto v1 = __riscv_vlse16_v_i16m8(src1 + cn * i + cn_index, sizeof(short) * cn, vl); + auto v2 = __riscv_vlse16_v_i16m8(src2 + cn * i + cn_index, sizeof(short) * cn, vl); + auto v = custom_intrin::__riscv_vabd(v1, v2, vl); + auto m = __riscv_vle8_v_u8m4(mask + i, vl); + auto b = __riscv_vmsne(m, 0, vl); + s = __riscv_vmaxu_tumu(b, s, s, v, vl); + } + } + return __riscv_vmv_x(__riscv_vredmaxu(s, __riscv_vmv_s_x_u16m1(0, __riscv_vsetvlmax_e16m1()), vlmax)); + } +}; + +template<> +struct MaskedNormDiffInf_RVV { + int operator() (const int* src1, const int* src2, const uchar* mask, int len, int cn) const { + int vlmax = __riscv_vsetvlmax_e32m8(); + auto s = __riscv_vmv_v_x_u32m8(0, vlmax); + for (int cn_index = 0; cn_index < cn; cn_index++) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e32m8(len - i); + auto v1 = __riscv_vlse32_v_i32m8(src1 + cn * i + cn_index, sizeof(int) * cn, vl); + auto v2 = __riscv_vlse32_v_i32m8(src2 + cn * i + cn_index, sizeof(int) * cn, vl); + auto v = custom_intrin::__riscv_vabd(v1, v2, vl); + auto m = __riscv_vle8_v_u8m2(mask + i, vl); + auto b = __riscv_vmsne(m, 0, vl); + s = __riscv_vmaxu_tumu(b, s, s, v, vl); + } + } + return __riscv_vmv_x(__riscv_vredmaxu(s, __riscv_vmv_s_x_u32m1(0, __riscv_vsetvlmax_e32m1()), vlmax)); + } +}; + +template<> +struct MaskedNormDiffInf_RVV { + float operator() (const float* src1, const float* src2, const uchar* mask, int len, int cn) const { + int vlmax = __riscv_vsetvlmax_e32m8(); + auto s = __riscv_vfmv_v_f_f32m8(0, vlmax); + if (cn == 1) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e32m8(len - i); + auto v1 = __riscv_vle32_v_f32m8(src1 + i, vl); + auto v2 = __riscv_vle32_v_f32m8(src2 + i, vl); + auto v = __riscv_vfabs(__riscv_vfsub(v1, v2, vl), vl); + auto m = __riscv_vle8_v_u8m2(mask + i, vl); + auto b = __riscv_vmsne(m, 0, vl); + s = __riscv_vfmax_tumu(b, s, s, v, vl); + } + } else { + for (int cn_index = 0; cn_index < cn; cn_index++) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e32m8(len - i); + auto v1 = __riscv_vlse32_v_f32m8(src1 + cn * i + cn_index, sizeof(float) * cn, vl); + auto v2 = __riscv_vlse32_v_f32m8(src2 + cn * i + cn_index, sizeof(float) * cn, vl); + auto v = __riscv_vfabs(__riscv_vfsub(v1, v2, vl), vl); + auto m = __riscv_vle8_v_u8m2(mask + i, vl); + auto b = __riscv_vmsne(m, 0, vl); + s = __riscv_vfmax_tumu(b, s, s, v, vl); + } + } + } + return __riscv_vfmv_f(__riscv_vfredmax(s, __riscv_vfmv_s_f_f32m1(0, __riscv_vsetvlmax_e32m1()), vlmax)); + } +}; + +template<> +struct MaskedNormDiffInf_RVV { + double operator() (const double* src1, const double* src2, const uchar* mask, int len, int cn) const { + int vlmax = __riscv_vsetvlmax_e64m8(); + auto s = __riscv_vfmv_v_f_f64m8(0, vlmax); + for (int cn_index = 0; cn_index < cn; cn_index++) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e64m8(len - i); + auto v1 = __riscv_vlse64_v_f64m8(src1 + cn * i + cn_index, sizeof(double) * cn, vl); + auto v2 = __riscv_vlse64_v_f64m8(src2 + cn * i + cn_index, sizeof(double) * cn, vl); + auto v = __riscv_vfabs(__riscv_vfsub(v1, v2, vl), vl); + auto m = __riscv_vle8_v_u8m1(mask + i, vl); + auto b = __riscv_vmsne(m, 0, vl); + s = __riscv_vfmax_tumu(b, s, s, __riscv_vfabs(v, vl), vl); + } + } + return __riscv_vfmv_f(__riscv_vfredmax(s, __riscv_vfmv_s_f_f64m1(0, __riscv_vsetvlmax_e64m1()), vlmax)); + } +}; + +template<> +struct MaskedNormDiffL1_RVV { + int operator() (const uchar* src1, const uchar* src2, const uchar* mask, int len, int cn) const { + auto s = __riscv_vmv_v_x_u32m1(0, __riscv_vsetvlmax_e32m1()); + auto zero = __riscv_vmv_v_x_u16m1(0, __riscv_vsetvlmax_e16m1()); + if (cn == 1) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e8m8(len - i); + auto v1 = __riscv_vle8_v_u8m8(src1 + i, vl); + auto v2 = __riscv_vle8_v_u8m8(src2 + i, vl); + auto v = custom_intrin::__riscv_vabd(v1, v2, vl); + auto m = __riscv_vle8_v_u8m8(mask + i, vl); + auto b = __riscv_vmsne(m, 0, vl); + s = __riscv_vwredsumu(__riscv_vwredsumu_tum(b, zero, v, zero, vl), s, __riscv_vsetvlmax_e16m1()); + } + } else if (cn == 4) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e8m2(len - i); + auto v1 = __riscv_vle8_v_u8m8(src1 + i * 4, vl * 4); + auto v2 = __riscv_vle8_v_u8m8(src2 + i * 4, vl * 4); + auto v = custom_intrin::__riscv_vabd(v1, v2, vl * 4); + auto m = __riscv_vle8_v_u8m2(mask + i, vl); + auto b = __riscv_vmsne(__riscv_vreinterpret_u8m8(__riscv_vmul(__riscv_vzext_vf4(__riscv_vminu(m, 1, vl), vl), 0x01010101, vl)), 0, vl * 4); + s = __riscv_vwredsumu(__riscv_vwredsumu_tum(b, zero, v, zero, vl * 4), s, __riscv_vsetvlmax_e16m1()); + } + } else { + for (int cn_index = 0; cn_index < cn; cn_index++) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e8m8(len - i); + auto v1 = __riscv_vlse8_v_u8m8(src1 + cn * i + cn_index, sizeof(uchar) * cn, vl); + auto v2 = __riscv_vlse8_v_u8m8(src2 + cn * i + cn_index, sizeof(uchar) * cn, vl); + auto v = custom_intrin::__riscv_vabd(v1, v2, vl); + auto m = __riscv_vle8_v_u8m8(mask + i, vl); + auto b = __riscv_vmsne(m, 0, vl); + s = __riscv_vwredsumu(__riscv_vwredsumu_tum(b, zero, v, zero, vl), s, __riscv_vsetvlmax_e16m1()); + } + } + } + return __riscv_vmv_x(s); + } +}; + +template<> +struct MaskedNormDiffL1_RVV { + int operator() (const schar* src1, const schar* src2, const uchar* mask, int len, int cn) const { + auto s = __riscv_vmv_v_x_u32m1(0, __riscv_vsetvlmax_e32m1()); + auto zero = __riscv_vmv_v_x_u16m1(0, __riscv_vsetvlmax_e16m1()); + for (int cn_index = 0; cn_index < cn; cn_index++) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e8m8(len - i); + auto v1 = __riscv_vlse8_v_i8m8(src1 + cn * i + cn_index, sizeof(schar) * cn, vl); + auto v2 = __riscv_vlse8_v_i8m8(src2 + cn * i + cn_index, sizeof(schar) * cn, vl); + auto v = custom_intrin::__riscv_vabd(v1, v2, vl); + auto m = __riscv_vle8_v_u8m8(mask + i, vl); + auto b = __riscv_vmsne(m, 0, vl); + s = __riscv_vwredsumu(__riscv_vwredsumu_tum(b, zero, v, zero, vl), s, __riscv_vsetvlmax_e16m1()); + } + } + return __riscv_vmv_x(s); + } +}; + +template<> +struct MaskedNormDiffL1_RVV { + int operator() (const ushort* src1, const ushort* src2, const uchar* mask, int len, int cn) const { + auto s = __riscv_vmv_v_x_u32m1(0, __riscv_vsetvlmax_e32m1()); + for (int cn_index = 0; cn_index < cn; cn_index++) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e8m4(len - i); + auto v1 = __riscv_vlse16_v_u16m8(src1 + cn * i + cn_index, sizeof(ushort) * cn, vl); + auto v2 = __riscv_vlse16_v_u16m8(src2 + cn * i + cn_index, sizeof(ushort) * cn, vl); + auto v = custom_intrin::__riscv_vabd(v1, v2, vl); + auto m = __riscv_vle8_v_u8m4(mask + i, vl); + auto b = __riscv_vmsne(m, 0, vl); + s = __riscv_vwredsumu_tum(b, s, v, s, vl); + } + } + return __riscv_vmv_x(s); + } +}; + +template<> +struct MaskedNormDiffL1_RVV { + int operator() (const short* src1, const short* src2, const uchar* mask, int len, int cn) const { + auto s = __riscv_vmv_v_x_u32m1(0, __riscv_vsetvlmax_e32m1()); + for (int cn_index = 0; cn_index < cn; cn_index++) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e8m4(len - i); + auto v1 = __riscv_vlse16_v_i16m8(src1 + cn * i + cn_index, sizeof(short) * cn, vl); + auto v2 = __riscv_vlse16_v_i16m8(src2 + cn * i + cn_index, sizeof(short) * cn, vl); + auto v = custom_intrin::__riscv_vabd(v1, v2, vl); + auto m = __riscv_vle8_v_u8m4(mask + i, vl); + auto b = __riscv_vmsne(m, 0, vl); + s = __riscv_vwredsumu_tum(b, s, v, s, vl); + } + } + return __riscv_vmv_x(s); + } +}; + +template<> +struct MaskedNormDiffL1_RVV { + double operator() (const int* src1, const int* src2, const uchar* mask, int len, int cn) const { + int vlmax = __riscv_vsetvlmax_e32m4(); + auto s = __riscv_vfmv_v_f_f64m8(0, vlmax); + for (int cn_index = 0; cn_index < cn; cn_index++) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e32m4(len - i); + auto v1 = __riscv_vlse32_v_i32m4(src1 + cn * i + cn_index, sizeof(int) * cn, vl); + auto v2 = __riscv_vlse32_v_i32m4(src2 + cn * i + cn_index, sizeof(int) * cn, vl); + auto v = custom_intrin::__riscv_vabd(v1, v2, vl); + auto m = __riscv_vle8_v_u8m1(mask + i, vl); + auto b = __riscv_vmsne(m, 0, vl); + s = __riscv_vfadd_tumu(b, s, s, __riscv_vfwcvt_f(b, v, vl), vl); + } + } + return __riscv_vfmv_f(__riscv_vfredosum(s, __riscv_vfmv_s_f_f64m1(0, __riscv_vsetvlmax_e64m1()), vlmax)); + } +}; + +template<> +struct MaskedNormDiffL1_RVV { + double operator() (const float* src1, const float* src2, const uchar* mask, int len, int cn) const { + int vlmax = __riscv_vsetvlmax_e32m4(); + auto s = __riscv_vfmv_v_f_f64m8(0, vlmax); + if (cn == 1) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e32m4(len - i); + auto v1 = __riscv_vle32_v_f32m4(src1 + i, vl); + auto v2 = __riscv_vle32_v_f32m4(src2 + i, vl); + auto v = __riscv_vfabs(__riscv_vfsub(v1, v2, vl), vl); + auto m = __riscv_vle8_v_u8m1(mask + i, vl); + auto b = __riscv_vmsne(m, 0, vl); + s = __riscv_vfadd_tumu(b, s, s, __riscv_vfwcvt_f(b, v, vl), vl); + } + } else { + for (int cn_index = 0; cn_index < cn; cn_index++) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e32m4(len - i); + auto v1 = __riscv_vlse32_v_f32m4(src1 + cn * i + cn_index, sizeof(float) * cn, vl); + auto v2 = __riscv_vlse32_v_f32m4(src2 + cn * i + cn_index, sizeof(float) * cn, vl); + auto v = __riscv_vfabs(__riscv_vfsub(v1, v2, vl), vl); + auto m = __riscv_vle8_v_u8m1(mask + i, vl); + auto b = __riscv_vmsne(m, 0, vl); + s = __riscv_vfadd_tumu(b, s, s, __riscv_vfwcvt_f(b, v, vl), vl); + } + } + } + return __riscv_vfmv_f(__riscv_vfredosum(s, __riscv_vfmv_s_f_f64m1(0, __riscv_vsetvlmax_e64m1()), vlmax)); + } +}; + +template<> +struct MaskedNormDiffL1_RVV { + double operator() (const double* src1, const double* src2, const uchar* mask, int len, int cn) const { + int vlmax = __riscv_vsetvlmax_e64m8(); + auto s = __riscv_vfmv_v_f_f64m8(0, vlmax); + for (int cn_index = 0; cn_index < cn; cn_index++) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e64m8(len - i); + auto v1 = __riscv_vlse64_v_f64m8(src1 + cn * i + cn_index, sizeof(double) * cn, vl); + auto v2 = __riscv_vlse64_v_f64m8(src2 + cn * i + cn_index, sizeof(double) * cn, vl); + auto v = __riscv_vfabs(__riscv_vfsub(v1, v2, vl), vl); + auto m = __riscv_vle8_v_u8m1(mask + i, vl); + auto b = __riscv_vmsne(m, 0, vl); + s = __riscv_vfadd_tumu(b, s, s, __riscv_vfabs(v, vl), vl); + } + } + return __riscv_vfmv_f(__riscv_vfredosum(s, __riscv_vfmv_s_f_f64m1(0, __riscv_vsetvlmax_e64m1()), vlmax)); + } +}; + +template<> +struct MaskedNormDiffL2_RVV { + int operator() (const uchar* src1, const uchar* src2, const uchar* mask, int len, int cn) const { + auto s = __riscv_vmv_v_x_u32m1(0, __riscv_vsetvlmax_e32m1()); + if (cn == 1) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e8m4(len - i); + auto v1 = __riscv_vle8_v_u8m4(src1 + i, vl); + auto v2 = __riscv_vle8_v_u8m4(src2 + i, vl); + auto v = custom_intrin::__riscv_vabd(v1, v2, vl); + auto m = __riscv_vle8_v_u8m4(mask + i, vl); + auto b = __riscv_vmsne(m, 0, vl); + s = __riscv_vwredsumu(b, __riscv_vwmulu(b, v, v, vl), s, vl); + } + } else if (cn == 4) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e8m1(len - i); + auto v1 = __riscv_vle8_v_u8m4(src1 + i * 4, vl * 4); + auto v2 = __riscv_vle8_v_u8m4(src2 + i * 4, vl * 4); + auto v = custom_intrin::__riscv_vabd(v1, v2, vl * 4); + auto m = __riscv_vle8_v_u8m1(mask + i, vl); + auto b = __riscv_vmsne(__riscv_vreinterpret_u8m4(__riscv_vmul(__riscv_vzext_vf4(__riscv_vminu(m, 1, vl), vl), 0x01010101, vl)), 0, vl * 4); + s = __riscv_vwredsumu(b, __riscv_vwmulu(b, v, v, vl * 4), s, vl * 4); + } + } else { + for (int cn_index = 0; cn_index < cn; cn_index++) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e8m4(len - i); + auto v1 = __riscv_vlse8_v_u8m4(src1 + cn * i + cn_index, sizeof(uchar) * cn, vl); + auto v2 = __riscv_vlse8_v_u8m4(src2 + cn * i + cn_index, sizeof(uchar) * cn, vl); + auto v = custom_intrin::__riscv_vabd(v1, v2, vl); + auto m = __riscv_vle8_v_u8m4(mask + i, vl); + auto b = __riscv_vmsne(m, 0, vl); + s = __riscv_vwredsumu(b, __riscv_vwmulu(b, v, v, vl), s, vl); + } + } + } + return __riscv_vmv_x(s); + } +}; + +template<> +struct MaskedNormDiffL2_RVV { + int operator() (const schar* src1, const schar* src2, const uchar* mask, int len, int cn) const { + auto s = __riscv_vmv_v_x_u32m1(0, __riscv_vsetvlmax_e32m1()); + for (int cn_index = 0; cn_index < cn; cn_index++) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e8m4(len - i); + auto v1 = __riscv_vlse8_v_i8m4(src1 + cn * i + cn_index, sizeof(schar) * cn, vl); + auto v2 = __riscv_vlse8_v_i8m4(src2 + cn * i + cn_index, sizeof(schar) * cn, vl); + auto v = custom_intrin::__riscv_vabd(v1, v2, vl); + auto m = __riscv_vle8_v_u8m4(mask + i, vl); + auto b = __riscv_vmsne(m, 0, vl); + s = __riscv_vwredsumu(b, __riscv_vwmulu(b, v, v, vl), s, vl); + } + } + return __riscv_vmv_x(s); + } +}; + +template<> +struct MaskedNormDiffL2_RVV { + double operator() (const ushort* src1, const ushort* src2, const uchar* mask, int len, int cn) const { + int vlmax = __riscv_vsetvlmax_e16m2(); + auto s = __riscv_vfmv_v_f_f64m8(0, vlmax); + for (int cn_index = 0; cn_index < cn; cn_index++) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e16m2(len - i); + auto v1 = __riscv_vlse16_v_u16m2(src1 + cn * i + cn_index, sizeof(ushort) * cn, vl); + auto v2 = __riscv_vlse16_v_u16m2(src2 + cn * i + cn_index, sizeof(ushort) * cn, vl); + auto v = custom_intrin::__riscv_vabd(v1, v2, vl); + auto m = __riscv_vle8_v_u8m1(mask + i, vl); + auto b = __riscv_vmsne(m, 0, vl); + auto v_mul = __riscv_vwmulu(b, v, v, vl); + s = __riscv_vfadd_tumu(b, s, s, __riscv_vfwcvt_f(b, v_mul, vl), vl); + } + } + return __riscv_vfmv_f(__riscv_vfredosum(s, __riscv_vfmv_s_f_f64m1(0, __riscv_vsetvlmax_e64m1()), vlmax)); + } +}; + +template<> +struct MaskedNormDiffL2_RVV { + double operator() (const short* src1, const short* src2, const uchar* mask, int len, int cn) const { + int vlmax = __riscv_vsetvlmax_e16m2(); + auto s = __riscv_vfmv_v_f_f64m8(0, vlmax); + for (int cn_index = 0; cn_index < cn; cn_index++) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e16m2(len - i); + auto v1 = __riscv_vlse16_v_i16m2(src1 + cn * i + cn_index, sizeof(short) * cn, vl); + auto v2 = __riscv_vlse16_v_i16m2(src2 + cn * i + cn_index, sizeof(short) * cn, vl); + auto v = custom_intrin::__riscv_vabd(v1, v2, vl); + auto m = __riscv_vle8_v_u8m1(mask + i, vl); + auto b = __riscv_vmsne(m, 0, vl); + auto v_mul = __riscv_vwmulu(b, v, v, vl); + s = __riscv_vfadd_tumu(b, s, s, __riscv_vfwcvt_f(b, v_mul, vl), vl); + } + } + return __riscv_vfmv_f(__riscv_vfredosum(s, __riscv_vfmv_s_f_f64m1(0, __riscv_vsetvlmax_e64m1()), vlmax)); + } +}; + +template<> +struct MaskedNormDiffL2_RVV { + double operator() (const int* src1, const int* src2, const uchar* mask, int len, int cn) const { + int vlmax = __riscv_vsetvlmax_e32m4(); + auto s = __riscv_vfmv_v_f_f64m8(0, vlmax); + for (int cn_index = 0; cn_index < cn; cn_index++) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e16m2(len - i); + auto v1 = __riscv_vlse32_v_i32m4(src1 + cn * i + cn_index, sizeof(int) * cn, vl); + auto v2 = __riscv_vlse32_v_i32m4(src2 + cn * i + cn_index, sizeof(int) * cn, vl); + auto v = custom_intrin::__riscv_vabd(v1, v2, vl); + auto m = __riscv_vle8_v_u8m1(mask + i, vl); + auto b = __riscv_vmsne(m, 0, vl); + auto v_mul = __riscv_vwmulu(b, v, v, vl); + s = __riscv_vfadd_tumu(b, s, s, __riscv_vfcvt_f(b, v_mul, vl), vl); + } + } + return __riscv_vfmv_f(__riscv_vfredosum(s, __riscv_vfmv_s_f_f64m1(0, __riscv_vsetvlmax_e64m1()), vlmax)); + } +}; + +template<> +struct MaskedNormDiffL2_RVV { + double operator() (const float* src1, const float* src2, const uchar* mask, int len, int cn) const { + int vlmax = __riscv_vsetvlmax_e32m4(); + auto s = __riscv_vfmv_v_f_f64m8(0, vlmax); + if (cn == 1) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e32m4(len - i); + auto v1 = __riscv_vle32_v_f32m4(src1 + i, vl); + auto v2 = __riscv_vle32_v_f32m4(src2 + i, vl); + auto v = __riscv_vfsub(v1, v2, vl); + auto m = __riscv_vle8_v_u8m1(mask + i, vl); + auto b = __riscv_vmsne(m, 0, vl); + auto v_mul = __riscv_vfwmul(b, v, v, vl); + s = __riscv_vfadd_tumu(b, s, s, v_mul, vl); + } + } else { + for (int cn_index = 0; cn_index < cn; cn_index++) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e32m4(len - i); + auto v1 = __riscv_vlse32_v_f32m4(src1 + cn * i + cn_index, sizeof(float) * cn, vl); + auto v2 = __riscv_vlse32_v_f32m4(src2 + cn * i + cn_index, sizeof(float) * cn, vl); + auto v = __riscv_vfsub(v1, v2, vl); + auto m = __riscv_vle8_v_u8m1(mask + i, vl); + auto b = __riscv_vmsne(m, 0, vl); + auto v_mul = __riscv_vfwmul(b, v, v, vl); + s = __riscv_vfadd_tumu(b, s, s, v_mul, vl); + } + } + } + return __riscv_vfmv_f(__riscv_vfredosum(s, __riscv_vfmv_s_f_f64m1(0, __riscv_vsetvlmax_e64m1()), vlmax)); + } +}; + +template<> +struct MaskedNormDiffL2_RVV { + double operator() (const double* src1, const double* src2, const uchar* mask, int len, int cn) const { + int vlmax = __riscv_vsetvlmax_e64m8(); + auto s = __riscv_vfmv_v_f_f64m8(0, vlmax); + for (int cn_index = 0; cn_index < cn; cn_index++) { + int vl; + for (int i = 0; i < len; i += vl) { + vl = __riscv_vsetvl_e64m8(len - i); + auto v1 = __riscv_vlse64_v_f64m8(src1 + cn * i + cn_index, sizeof(double) * cn, vl); + auto v2 = __riscv_vlse64_v_f64m8(src2 + cn * i + cn_index, sizeof(double) * cn, vl); + auto v = __riscv_vfsub(v1, v2, vl); + auto m = __riscv_vle8_v_u8m1(mask + i, vl); + auto b = __riscv_vmsne(m, 0, vl); + auto v_mul = __riscv_vfmul(b, v, v, vl); + s = __riscv_vfadd_tumu(b, s, s, v_mul, vl); + } + } + return __riscv_vfmv_f(__riscv_vfredosum(s, __riscv_vfmv_s_f_f64m1(0, __riscv_vsetvlmax_e64m1()), vlmax)); + } +}; + +template int +normDiffInf_(const T* src1, const T* src2, const uchar* mask, ST* _result, int len, int cn) { + ST result = *_result; + if( !mask ) { + NormDiffInf_RVV op; + result = std::max(result, op(src1, src2, len*cn)); + } else { + MaskedNormDiffInf_RVV op; + result = std::max(result, op(src1, src2, mask, len, cn)); + } + *_result = result; + return 0; } -inline int normDiffL1_8UC1(const uchar* src1, size_t src1_step, const uchar* src2, size_t src2_step, const uchar* mask, size_t mask_step, int width, int height, double* result) -{ - int vlmax = __riscv_vsetvlmax_e8m2(); - auto vec_sum = __riscv_vmv_v_x_u32m8(0, vlmax); - - if (mask) - { - for (int i = 0; i < height; i++) - { - const uchar* src1_row = src1 + i * src1_step; - const uchar* src2_row = src2 + i * src2_step; - const uchar* mask_row = mask + i * mask_step; - int vl; - for (int j = 0; j < width; j += vl) - { - vl = __riscv_vsetvl_e8m2(width - j); - auto vec_src1 = __riscv_vle8_v_u8m2(src1_row + j, vl); - auto vec_src2 = __riscv_vle8_v_u8m2(src2_row + j, vl); - auto vec_mask = __riscv_vle8_v_u8m2(mask_row + j, vl); - auto bool_mask = __riscv_vmsne(vec_mask, 0, vl); - auto vec_src = __riscv_vsub_vv_u8m2_m(bool_mask, __riscv_vmaxu_vv_u8m2_m(bool_mask, vec_src1, vec_src2, vl), - __riscv_vminu_vv_u8m2_m(bool_mask, vec_src1, vec_src2, vl), vl); - auto vec_zext = __riscv_vzext_vf4_u32m8_m(bool_mask, vec_src, vl); - vec_sum = __riscv_vadd_tumu(bool_mask, vec_sum, vec_sum, vec_zext, vl); - } - } +template int +normDiffL1_(const T* src1, const T* src2, const uchar* mask, ST* _result, int len, int cn) { + ST result = *_result; + if( !mask ) { + NormDiffL1_RVV op; + result += op(src1, src2, len*cn); + } else { + MaskedNormDiffL1_RVV op; + result += op(src1, src2, mask, len, cn); } - else - { - for (int i = 0; i < height; i++) - { - const uchar* src1_row = src1 + i * src1_step; - const uchar* src2_row = src2 + i * src2_step; - int vl; - for (int j = 0; j < width; j += vl) - { - vl = __riscv_vsetvl_e8m2(width - j); - auto vec_src1 = __riscv_vle8_v_u8m2(src1_row + j, vl); - auto vec_src2 = __riscv_vle8_v_u8m2(src2_row + j, vl); - auto vec_src = __riscv_vsub(__riscv_vmaxu(vec_src1, vec_src2, vl), __riscv_vminu(vec_src1, vec_src2, vl), vl); - auto vec_zext = __riscv_vzext_vf4(vec_src, vl); - vec_sum = __riscv_vadd_tu(vec_sum, vec_sum, vec_zext, vl); - } - } - } - auto sc_sum = __riscv_vmv_s_x_u32m1(0, vlmax); - sc_sum = __riscv_vredsum(vec_sum, sc_sum, vlmax); - *result = __riscv_vmv_x(sc_sum); - - return CV_HAL_ERROR_OK; + *_result = result; + return 0; } -inline int normDiffL2Sqr_8UC1(const uchar* src1, size_t src1_step, const uchar* src2, size_t src2_step, const uchar* mask, size_t mask_step, int width, int height, double* result) -{ - int vlmax = __riscv_vsetvlmax_e8m2(); - auto vec_sum = __riscv_vmv_v_x_u32m8(0, vlmax); - int cnt = 0; - auto reduce = [&](int vl) { - if ((cnt += vl) < (1 << 16)) - return; - cnt = vl; - for (int i = 0; i < vlmax; i++) - { - *result += __riscv_vmv_x(vec_sum); - vec_sum = __riscv_vslidedown(vec_sum, 1, vlmax); - } - vec_sum = __riscv_vmv_v_x_u32m8(0, vlmax); - }; - - *result = 0; - if (mask) - { - for (int i = 0; i < height; i++) - { - const uchar* src1_row = src1 + i * src1_step; - const uchar* src2_row = src2 + i * src2_step; - const uchar* mask_row = mask + i * mask_step; - int vl; - for (int j = 0; j < width; j += vl) - { - vl = __riscv_vsetvl_e8m2(width - j); - reduce(vl); - - auto vec_src1 = __riscv_vle8_v_u8m2(src1_row + j, vl); - auto vec_src2 = __riscv_vle8_v_u8m2(src2_row + j, vl); - auto vec_mask = __riscv_vle8_v_u8m2(mask_row + j, vl); - auto bool_mask = __riscv_vmsne(vec_mask, 0, vl); - auto vec_src = __riscv_vsub_vv_u8m2_m(bool_mask, __riscv_vmaxu_vv_u8m2_m(bool_mask, vec_src1, vec_src2, vl), - __riscv_vminu_vv_u8m2_m(bool_mask, vec_src1, vec_src2, vl), vl); - auto vec_mul = __riscv_vwmulu_vv_u16m4_m(bool_mask, vec_src, vec_src, vl); - auto vec_zext = __riscv_vzext_vf2_u32m8_m(bool_mask, vec_mul, vl); - vec_sum = __riscv_vadd_tumu(bool_mask, vec_sum, vec_sum, vec_zext, vl); - } - } +template int +normDiffL2_(const T* src1, const T* src2, const uchar* mask, ST* _result, int len, int cn) { + ST result = *_result; + if( !mask ) { + NormDiffL2_RVV op; + result += op(src1, src2, len*cn); + } else { + MaskedNormDiffL2_RVV op; + result += op(src1, src2, mask, len, cn); } - else - { - for (int i = 0; i < height; i++) - { - const uchar* src1_row = src1 + i * src1_step; - const uchar* src2_row = src2 + i * src2_step; - int vl; - for (int j = 0; j < width; j += vl) - { - vl = __riscv_vsetvl_e8m2(width - j); - reduce(vl); - - auto vec_src1 = __riscv_vle8_v_u8m2(src1_row + j, vl); - auto vec_src2 = __riscv_vle8_v_u8m2(src2_row + j, vl); - auto vec_src = __riscv_vsub(__riscv_vmaxu(vec_src1, vec_src2, vl), __riscv_vminu(vec_src1, vec_src2, vl), vl); - auto vec_mul = __riscv_vwmulu(vec_src, vec_src, vl); - auto vec_zext = __riscv_vzext_vf2(vec_mul, vl); - vec_sum = __riscv_vadd_tu(vec_sum, vec_sum, vec_zext, vl); - } - } - } - reduce(1 << 16); - - return CV_HAL_ERROR_OK; + *_result = result; + return 0; } -inline int normDiffInf_8UC4(const uchar* src1, size_t src1_step, const uchar* src2, size_t src2_step, const uchar* mask, size_t mask_step, int width, int height, double* result) -{ - int vlmax = __riscv_vsetvlmax_e8m8(); - auto vec_max = __riscv_vmv_v_x_u8m8(0, vlmax); +#define CV_HAL_RVV_DEF_NORM_DIFF_FUNC(L, suffix, type, ntype) \ + static int normDiff##L##_##suffix(const type* src1, const type* src2, const uchar* mask, ntype* r, int len, int cn) \ + { return normDiff##L##_(src1, src2, mask, r, len, cn); } - if (mask) - { - for (int i = 0; i < height; i++) - { - const uchar* src1_row = src1 + i * src1_step; - const uchar* src2_row = src2 + i * src2_step; - const uchar* mask_row = mask + i * mask_step; - int vl, vlm; - for (int j = 0, jm = 0; j < width * 4; j += vl, jm += vlm) - { - vl = __riscv_vsetvl_e8m8(width * 4 - j); - vlm = __riscv_vsetvl_e8m2(width - jm); - auto vec_src1 = __riscv_vle8_v_u8m8(src1_row + j, vl); - auto vec_src2 = __riscv_vle8_v_u8m8(src2_row + j, vl); - auto vec_mask = __riscv_vle8_v_u8m2(mask_row + jm, vlm); - auto vec_mask_ext = __riscv_vmul(__riscv_vzext_vf4(__riscv_vminu(vec_mask, 1, vlm), vlm), 0x01010101, vlm); - auto bool_mask_ext = __riscv_vmsne(__riscv_vreinterpret_u8m8(vec_mask_ext), 0, vl); - auto vec_src = __riscv_vsub_vv_u8m8_m(bool_mask_ext, __riscv_vmaxu_vv_u8m8_m(bool_mask_ext, vec_src1, vec_src2, vl), - __riscv_vminu_vv_u8m8_m(bool_mask_ext, vec_src1, vec_src2, vl), vl); - vec_max = __riscv_vmaxu_tumu(bool_mask_ext, vec_max, vec_max, vec_src, vl); - } - } - } - else - { - for (int i = 0; i < height; i++) - { - const uchar* src1_row = src1 + i * src1_step; - const uchar* src2_row = src2 + i * src2_step; - int vl; - for (int j = 0; j < width * 4; j += vl) - { - vl = __riscv_vsetvl_e8m8(width * 4 - j); - auto vec_src1 = __riscv_vle8_v_u8m8(src1_row + j, vl); - auto vec_src2 = __riscv_vle8_v_u8m8(src2_row + j, vl); - auto vec_src = __riscv_vsub(__riscv_vmaxu(vec_src1, vec_src2, vl), __riscv_vminu(vec_src1, vec_src2, vl), vl); - vec_max = __riscv_vmaxu_tu(vec_max, vec_max, vec_src, vl); - } - } - } - auto sc_max = __riscv_vmv_s_x_u8m1(0, vlmax); - sc_max = __riscv_vredmaxu(vec_max, sc_max, vlmax); - *result = __riscv_vmv_x(sc_max); +#define CV_HAL_RVV_DEF_NORM_DIFF_ALL(suffix, type, inftype, l1type, l2type) \ + CV_HAL_RVV_DEF_NORM_DIFF_FUNC(Inf, suffix, type, inftype) \ + CV_HAL_RVV_DEF_NORM_DIFF_FUNC(L1, suffix, type, l1type) \ + CV_HAL_RVV_DEF_NORM_DIFF_FUNC(L2, suffix, type, l2type) + +CV_HAL_RVV_DEF_NORM_DIFF_ALL(8u, uchar, int, int, int) +CV_HAL_RVV_DEF_NORM_DIFF_ALL(8s, schar, int, int, int) +CV_HAL_RVV_DEF_NORM_DIFF_ALL(16u, ushort, int, int, double) +CV_HAL_RVV_DEF_NORM_DIFF_ALL(16s, short, int, int, double) +CV_HAL_RVV_DEF_NORM_DIFF_ALL(32s, int, int, double, double) +CV_HAL_RVV_DEF_NORM_DIFF_ALL(32f, float, float, double, double) +CV_HAL_RVV_DEF_NORM_DIFF_ALL(64f, double, double, double, double) + +#undef CV_HAL_RVV_DEF_NORM_DIFF_ALL +#undef CV_HAL_RVV_DEF_NORM_DIFF_FUNC - return CV_HAL_ERROR_OK; -} - -inline int normDiffL1_8UC4(const uchar* src1, size_t src1_step, const uchar* src2, size_t src2_step, const uchar* mask, size_t mask_step, int width, int height, double* result) -{ - int vlmax = __riscv_vsetvlmax_e8m2(); - auto vec_sum = __riscv_vmv_v_x_u32m8(0, vlmax); - - if (mask) - { - for (int i = 0; i < height; i++) - { - const uchar* src1_row = src1 + i * src1_step; - const uchar* src2_row = src2 + i * src2_step; - const uchar* mask_row = mask + i * mask_step; - int vl, vlm; - for (int j = 0, jm = 0; j < width * 4; j += vl, jm += vlm) - { - vl = __riscv_vsetvl_e8m2(width * 4 - j); - vlm = __riscv_vsetvl_e8mf2(width - jm); - auto vec_src1 = __riscv_vle8_v_u8m2(src1_row + j, vl); - auto vec_src2 = __riscv_vle8_v_u8m2(src2_row + j, vl); - auto vec_mask = __riscv_vle8_v_u8mf2(mask_row + jm, vlm); - auto vec_mask_ext = __riscv_vmul(__riscv_vzext_vf4(__riscv_vminu(vec_mask, 1, vlm), vlm), 0x01010101, vlm); - auto bool_mask_ext = __riscv_vmsne(__riscv_vreinterpret_u8m2(vec_mask_ext), 0, vl); - auto vec_src = __riscv_vsub_vv_u8m2_m(bool_mask_ext, __riscv_vmaxu_vv_u8m2_m(bool_mask_ext, vec_src1, vec_src2, vl), - __riscv_vminu_vv_u8m2_m(bool_mask_ext, vec_src1, vec_src2, vl), vl); - auto vec_zext = __riscv_vzext_vf4_u32m8_m(bool_mask_ext, vec_src, vl); - vec_sum = __riscv_vadd_tumu(bool_mask_ext, vec_sum, vec_sum, vec_zext, vl); - } - } - } - else - { - for (int i = 0; i < height; i++) - { - const uchar* src1_row = src1 + i * src1_step; - const uchar* src2_row = src2 + i * src2_step; - int vl; - for (int j = 0; j < width * 4; j += vl) - { - vl = __riscv_vsetvl_e8m2(width * 4 - j); - auto vec_src1 = __riscv_vle8_v_u8m2(src1_row + j, vl); - auto vec_src2 = __riscv_vle8_v_u8m2(src2_row + j, vl); - auto vec_src = __riscv_vsub(__riscv_vmaxu(vec_src1, vec_src2, vl), __riscv_vminu(vec_src1, vec_src2, vl), vl); - auto vec_zext = __riscv_vzext_vf4(vec_src, vl); - vec_sum = __riscv_vadd_tu(vec_sum, vec_sum, vec_zext, vl); - } - } - } - auto sc_sum = __riscv_vmv_s_x_u32m1(0, vlmax); - sc_sum = __riscv_vredsum(vec_sum, sc_sum, vlmax); - *result = __riscv_vmv_x(sc_sum); - - return CV_HAL_ERROR_OK; -} - -inline int normDiffL2Sqr_8UC4(const uchar* src1, size_t src1_step, const uchar* src2, size_t src2_step, const uchar* mask, size_t mask_step, int width, int height, double* result) -{ - int vlmax = __riscv_vsetvlmax_e8m2(); - auto vec_sum = __riscv_vmv_v_x_u32m8(0, vlmax); - int cnt = 0; - auto reduce = [&](int vl) { - if ((cnt += vl) < (1 << 16)) - return; - cnt = vl; - for (int i = 0; i < vlmax; i++) - { - *result += __riscv_vmv_x(vec_sum); - vec_sum = __riscv_vslidedown(vec_sum, 1, vlmax); - } - vec_sum = __riscv_vmv_v_x_u32m8(0, vlmax); - }; - - *result = 0; - if (mask) - { - for (int i = 0; i < height; i++) - { - const uchar* src1_row = src1 + i * src1_step; - const uchar* src2_row = src2 + i * src2_step; - const uchar* mask_row = mask + i * mask_step; - int vl, vlm; - for (int j = 0, jm = 0; j < width * 4; j += vl, jm += vlm) - { - vl = __riscv_vsetvl_e8m2(width * 4 - j); - vlm = __riscv_vsetvl_e8mf2(width - jm); - reduce(vl); - - auto vec_src1 = __riscv_vle8_v_u8m2(src1_row + j, vl); - auto vec_src2 = __riscv_vle8_v_u8m2(src2_row + j, vl); - auto vec_mask = __riscv_vle8_v_u8mf2(mask_row + jm, vlm); - auto vec_mask_ext = __riscv_vmul(__riscv_vzext_vf4(__riscv_vminu(vec_mask, 1, vlm), vlm), 0x01010101, vlm); - auto bool_mask_ext = __riscv_vmsne(__riscv_vreinterpret_u8m2(vec_mask_ext), 0, vl); - auto vec_src = __riscv_vsub_vv_u8m2_m(bool_mask_ext, __riscv_vmaxu_vv_u8m2_m(bool_mask_ext, vec_src1, vec_src2, vl), - __riscv_vminu_vv_u8m2_m(bool_mask_ext, vec_src1, vec_src2, vl), vl); - auto vec_mul = __riscv_vwmulu_vv_u16m4_m(bool_mask_ext, vec_src, vec_src, vl); - auto vec_zext = __riscv_vzext_vf2_u32m8_m(bool_mask_ext, vec_mul, vl); - vec_sum = __riscv_vadd_tumu(bool_mask_ext, vec_sum, vec_sum, vec_zext, vl); - } - } - } - else - { - for (int i = 0; i < height; i++) - { - const uchar* src1_row = src1 + i * src1_step; - const uchar* src2_row = src2 + i * src2_step; - int vl; - for (int j = 0; j < width * 4; j += vl) - { - vl = __riscv_vsetvl_e8m2(width * 4 - j); - reduce(vl); - - auto vec_src1 = __riscv_vle8_v_u8m2(src1_row + j, vl); - auto vec_src2 = __riscv_vle8_v_u8m2(src2_row + j, vl); - auto vec_src = __riscv_vsub(__riscv_vmaxu(vec_src1, vec_src2, vl), __riscv_vminu(vec_src1, vec_src2, vl), vl); - auto vec_mul = __riscv_vwmulu(vec_src, vec_src, vl); - auto vec_zext = __riscv_vzext_vf2(vec_mul, vl); - vec_sum = __riscv_vadd_tu(vec_sum, vec_sum, vec_zext, vl); - } - } - } - reduce(1 << 16); - - return CV_HAL_ERROR_OK; -} - -inline int normDiffInf_32FC1(const uchar* src1, size_t src1_step, const uchar* src2, size_t src2_step, const uchar* mask, size_t mask_step, int width, int height, double* result) -{ - int vlmax = __riscv_vsetvlmax_e32m8(); - auto vec_max = __riscv_vfmv_v_f_f32m8(0, vlmax); - - if (mask) - { - for (int i = 0; i < height; i++) - { - const float* src1_row = reinterpret_cast(src1 + i * src1_step); - const float* src2_row = reinterpret_cast(src2 + i * src2_step); - const uchar* mask_row = mask + i * mask_step; - int vl; - for (int j = 0; j < width; j += vl) - { - vl = __riscv_vsetvl_e32m8(width - j); - auto vec_src1 = __riscv_vle32_v_f32m8(src1_row + j, vl); - auto vec_src2 = __riscv_vle32_v_f32m8(src2_row + j, vl); - auto vec_mask = __riscv_vle8_v_u8m2(mask_row + j, vl); - auto bool_mask = __riscv_vmsne(vec_mask, 0, vl); - auto vec_src = __riscv_vfsub_vv_f32m8_m(bool_mask, vec_src1, vec_src2, vl); - auto vec_abs = __riscv_vfabs_v_f32m8_m(bool_mask, vec_src, vl); - vec_max = __riscv_vfmax_tumu(bool_mask, vec_max, vec_max, vec_abs, vl); - } - } - } - else - { - for (int i = 0; i < height; i++) - { - const float* src1_row = reinterpret_cast(src1 + i * src1_step); - const float* src2_row = reinterpret_cast(src2 + i * src2_step); - int vl; - for (int j = 0; j < width; j += vl) - { - vl = __riscv_vsetvl_e32m8(width - j); - auto vec_src1 = __riscv_vle32_v_f32m8(src1_row + j, vl); - auto vec_src2 = __riscv_vle32_v_f32m8(src2_row + j, vl); - auto vec_src = __riscv_vfsub(vec_src1, vec_src2, vl); - auto vec_abs = __riscv_vfabs(vec_src, vl); - vec_max = __riscv_vfmax_tu(vec_max, vec_max, vec_abs, vl); - } - } - } - auto sc_max = __riscv_vfmv_s_f_f32m1(0, vlmax); - sc_max = __riscv_vfredmax(vec_max, sc_max, vlmax); - *result = __riscv_vfmv_f(sc_max); - - return CV_HAL_ERROR_OK; -} - -inline int normDiffL1_32FC1(const uchar* src1, size_t src1_step, const uchar* src2, size_t src2_step, const uchar* mask, size_t mask_step, int width, int height, double* result) -{ - int vlmax = __riscv_vsetvlmax_e32m4(); - auto vec_sum = __riscv_vfmv_v_f_f64m8(0, vlmax); - - if (mask) - { - for (int i = 0; i < height; i++) - { - const float* src1_row = reinterpret_cast(src1 + i * src1_step); - const float* src2_row = reinterpret_cast(src2 + i * src2_step); - const uchar* mask_row = mask + i * mask_step; - int vl; - for (int j = 0; j < width; j += vl) - { - vl = __riscv_vsetvl_e32m4(width - j); - auto vec_src1 = __riscv_vle32_v_f32m4(src1_row + j, vl); - auto vec_src2 = __riscv_vle32_v_f32m4(src2_row + j, vl); - auto vec_mask = __riscv_vle8_v_u8m1(mask_row + j, vl); - auto bool_mask = __riscv_vmsne(vec_mask, 0, vl); - auto vec_src = __riscv_vfsub_vv_f32m4_m(bool_mask, vec_src1, vec_src2, vl); - auto vec_abs = __riscv_vfabs_v_f32m4_m(bool_mask, vec_src, vl); - auto vec_fext = __riscv_vfwcvt_f_f_v_f64m8_m(bool_mask, vec_abs, vl); - vec_sum = __riscv_vfadd_tumu(bool_mask, vec_sum, vec_sum, vec_fext, vl); - } - } - } - else - { - for (int i = 0; i < height; i++) - { - const float* src1_row = reinterpret_cast(src1 + i * src1_step); - const float* src2_row = reinterpret_cast(src2 + i * src2_step); - int vl; - for (int j = 0; j < width; j += vl) - { - vl = __riscv_vsetvl_e32m4(width - j); - auto vec_src1 = __riscv_vle32_v_f32m4(src1_row + j, vl); - auto vec_src2 = __riscv_vle32_v_f32m4(src2_row + j, vl); - auto vec_src = __riscv_vfsub(vec_src1, vec_src2, vl); - auto vec_abs = __riscv_vfabs(vec_src, vl); - auto vec_fext = __riscv_vfwcvt_f_f_v_f64m8(vec_abs, vl); - vec_sum = __riscv_vfadd_tu(vec_sum, vec_sum, vec_fext, vl); - } - } - } - auto sc_sum = __riscv_vfmv_s_f_f64m1(0, vlmax); - sc_sum = __riscv_vfredosum(vec_sum, sc_sum, vlmax); - *result = __riscv_vfmv_f(sc_sum); - - return CV_HAL_ERROR_OK; -} - -inline int normDiffL2Sqr_32FC1(const uchar* src1, size_t src1_step, const uchar* src2, size_t src2_step, const uchar* mask, size_t mask_step, int width, int height, double* result) -{ - int vlmax = __riscv_vsetvlmax_e32m4(); - auto vec_sum = __riscv_vfmv_v_f_f64m8(0, vlmax); - - if (mask) - { - for (int i = 0; i < height; i++) - { - const float* src1_row = reinterpret_cast(src1 + i * src1_step); - const float* src2_row = reinterpret_cast(src2 + i * src2_step); - const uchar* mask_row = mask + i * mask_step; - int vl; - for (int j = 0; j < width; j += vl) - { - vl = __riscv_vsetvl_e32m4(width - j); - auto vec_src1 = __riscv_vle32_v_f32m4(src1_row + j, vl); - auto vec_src2 = __riscv_vle32_v_f32m4(src2_row + j, vl); - auto vec_mask = __riscv_vle8_v_u8m1(mask_row + j, vl); - auto bool_mask = __riscv_vmsne(vec_mask, 0, vl); - auto vec_src = __riscv_vfsub_vv_f32m4_m(bool_mask, vec_src1, vec_src2, vl); - auto vec_mul = __riscv_vfwmul_vv_f64m8_m(bool_mask, vec_src, vec_src, vl); - vec_sum = __riscv_vfadd_tumu(bool_mask, vec_sum, vec_sum, vec_mul, vl); - } - } - } - else - { - for (int i = 0; i < height; i++) - { - const float* src1_row = reinterpret_cast(src1 + i * src1_step); - const float* src2_row = reinterpret_cast(src2 + i * src2_step); - int vl; - for (int j = 0; j < width; j += vl) - { - vl = __riscv_vsetvl_e32m4(width - j); - auto vec_src1 = __riscv_vle32_v_f32m4(src1_row + j, vl); - auto vec_src2 = __riscv_vle32_v_f32m4(src2_row + j, vl); - auto vec_src = __riscv_vfsub(vec_src1, vec_src2, vl); - auto vec_mul = __riscv_vfwmul(vec_src, vec_src, vl); - vec_sum = __riscv_vfadd_tu(vec_sum, vec_sum, vec_mul, vl); - } - } - } - auto sc_sum = __riscv_vfmv_s_f_f64m1(0, vlmax); - sc_sum = __riscv_vfredosum(vec_sum, sc_sum, vlmax); - *result = __riscv_vfmv_f(sc_sum); - - return CV_HAL_ERROR_OK; } +using NormDiffFunc = int (*)(const uchar*, const uchar*, const uchar*, uchar*, int, int); inline int normDiff(const uchar* src1, size_t src1_step, const uchar* src2, size_t src2_step, const uchar* mask, size_t mask_step, int width, int height, int type, int norm_type, double* result) { - if (!result) - return CV_HAL_ERROR_OK; + int depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type); - int ret; - switch (type) - { - case CV_8UC1: - switch (norm_type & ~NORM_RELATIVE) - { - case NORM_INF: - ret = normDiffInf_8UC1(src1, src1_step, src2, src2_step, mask, mask_step, width, height, result); - break; - case NORM_L1: - ret = normDiffL1_8UC1(src1, src1_step, src2, src2_step, mask, mask_step, width, height, result); - break; - case NORM_L2SQR: - ret = normDiffL2Sqr_8UC1(src1, src1_step, src2, src2_step, mask, mask_step, width, height, result); - break; - case NORM_L2: - ret = normDiffL2Sqr_8UC1(src1, src1_step, src2, src2_step, mask, mask_step, width, height, result); - *result = std::sqrt(*result); - break; - default: - ret = CV_HAL_ERROR_NOT_IMPLEMENTED; - } - break; - case CV_8UC4: - switch (norm_type & ~NORM_RELATIVE) - { - case NORM_INF: - ret = normDiffInf_8UC4(src1, src1_step, src2, src2_step, mask, mask_step, width, height, result); - break; - case NORM_L1: - ret = normDiffL1_8UC4(src1, src1_step, src2, src2_step, mask, mask_step, width, height, result); - break; - case NORM_L2SQR: - ret = normDiffL2Sqr_8UC4(src1, src1_step, src2, src2_step, mask, mask_step, width, height, result); - break; - case NORM_L2: - ret = normDiffL2Sqr_8UC4(src1, src1_step, src2, src2_step, mask, mask_step, width, height, result); - *result = std::sqrt(*result); - break; - default: - ret = CV_HAL_ERROR_NOT_IMPLEMENTED; - } - break; - case CV_32FC1: - switch (norm_type & ~NORM_RELATIVE) - { - case NORM_INF: - ret = normDiffInf_32FC1(src1, src1_step, src2, src2_step, mask, mask_step, width, height, result); - break; - case NORM_L1: - ret = normDiffL1_32FC1(src1, src1_step, src2, src2_step, mask, mask_step, width, height, result); - break; - case NORM_L2SQR: - ret = normDiffL2Sqr_32FC1(src1, src1_step, src2, src2_step, mask, mask_step, width, height, result); - break; - case NORM_L2: - ret = normDiffL2Sqr_32FC1(src1, src1_step, src2, src2_step, mask, mask_step, width, height, result); - *result = std::sqrt(*result); - break; - default: - ret = CV_HAL_ERROR_NOT_IMPLEMENTED; - } - break; - default: - ret = CV_HAL_ERROR_NOT_IMPLEMENTED; + bool relative = norm_type & NORM_RELATIVE; + norm_type &= ~NORM_RELATIVE; + + if (result == nullptr || depth == CV_16F || (norm_type > NORM_L2SQR && !relative)) { + return CV_HAL_ERROR_NOT_IMPLEMENTED; } - if(ret == CV_HAL_ERROR_OK && (norm_type & NORM_RELATIVE)) + // [FIXME] append 0's when merging to 5.x + static NormDiffFunc norm_diff_tab[3][CV_DEPTH_MAX] = { + { + (NormDiffFunc)(normDiffInf_8u), (NormDiffFunc)(normDiffInf_8s), + (NormDiffFunc)(normDiffInf_16u), (NormDiffFunc)(normDiffInf_16s), + (NormDiffFunc)(normDiffInf_32s), (NormDiffFunc)(normDiffInf_32f), + (NormDiffFunc)(normDiffInf_64f), 0, + }, + { + (NormDiffFunc)(normDiffL1_8u), (NormDiffFunc)(normDiffL1_8s), + (NormDiffFunc)(normDiffL1_16u), (NormDiffFunc)(normDiffL1_16s), + (NormDiffFunc)(normDiffL1_32s), (NormDiffFunc)(normDiffL1_32f), + (NormDiffFunc)(normDiffL1_64f), 0, + }, + { + (NormDiffFunc)(normDiffL2_8u), (NormDiffFunc)(normDiffL2_8s), + (NormDiffFunc)(normDiffL2_16u), (NormDiffFunc)(normDiffL2_16s), + (NormDiffFunc)(normDiffL2_32s), (NormDiffFunc)(normDiffL2_32f), + (NormDiffFunc)(normDiffL2_64f), 0, + }, + }; + + static const size_t elem_size_tab[CV_DEPTH_MAX] = { + sizeof(uchar), sizeof(schar), + sizeof(ushort), sizeof(short), + sizeof(int), sizeof(float), + sizeof(int64_t), 0, + }; + CV_Assert(elem_size_tab[depth]); + + bool src_continuous = (src1_step == width * elem_size_tab[depth] * cn || (src1_step != width * elem_size_tab[depth] * cn && height == 1)); + src_continuous &= (src2_step == width * elem_size_tab[depth] * cn || (src2_step != width * elem_size_tab[depth] * cn && height == 1)); + bool mask_continuous = (mask_step == width); + size_t nplanes = 1; + size_t size = width * height; + if ((mask && (!src_continuous || !mask_continuous)) || !src_continuous) { + nplanes = height; + size = width; + } + + NormDiffFunc func = norm_diff_tab[norm_type >> 1][depth]; + if (func == nullptr) { + return CV_HAL_ERROR_NOT_IMPLEMENTED; + } + + // Handle overflow + union { + double d; + float f; + unsigned u; + } res; + res.d = 0; + if ((norm_type == NORM_L1 && depth <= CV_16S) || + ((norm_type == NORM_L2 || norm_type == NORM_L2SQR) && depth <= CV_8S)) { + const size_t esz = elem_size_tab[depth] * cn; + const int total = (int)size; + const int intSumBlockSize = (norm_type == NORM_L1 && depth <= CV_8S ? (1 << 23) : (1 << 15))/cn; + const int blockSize = std::min(total, intSumBlockSize); + int isum = 0; + int count = 0; + auto _src1 = src1, _src2 = src2; + auto _mask = mask; + for (size_t i = 0; i < nplanes; i++) { + if ((mask && (!src_continuous || !mask_continuous)) || !src_continuous) { + _src1 = src1 + src1_step * i; + _src2 = src2 + src2_step * i; + _mask = mask + mask_step * i; + } + for (int j = 0; j < total; j += blockSize) { + int bsz = std::min(total - j, blockSize); + func(_src1, _src2, _mask, (uchar*)&isum, bsz, cn); + count += bsz; + if (count + blockSize >= intSumBlockSize || (i + 1 >= nplanes && j + bsz >= total)) { + res.d += isum; + isum = 0; + count = 0; + } + _src1 += bsz * esz; + _src2 += bsz * esz; + if (mask) { + _mask += bsz; + } + } + } + } else { + auto _src1 = src1, _src2 = src2; + auto _mask = mask; + for (size_t i = 0; i < nplanes; i++) { + if ((mask && (!src_continuous || !mask_continuous)) || !src_continuous) { + _src1 = src1 + src1_step * i; + _src2 = src2 + src2_step * i; + _mask = mask + mask_step * i; + } + func(_src1, _src2, _mask, (uchar*)&res, (int)size, cn); + } + } + + if (norm_type == NORM_INF) { + if (depth == CV_64F) { + *result = res.d; + } else if (depth == CV_32F) { + *result = res.f; + } else { + *result = res.u; + } + } else if (norm_type == NORM_L2) { + *result = std::sqrt(res.d); + } else { + *result = res.d; + } + + if(relative) { double result_; - ret = cv::cv_hal_rvv::norm::norm(src2, src2_step, mask, mask_step, width, height, type, norm_type & ~NORM_RELATIVE, &result_); + int ret = cv::cv_hal_rvv::norm::norm(src2, src2_step, mask, mask_step, width, height, type, norm_type, &result_); if(ret == CV_HAL_ERROR_OK) { *result /= result_ + DBL_EPSILON; } } - return ret; + return CV_HAL_ERROR_OK; } }}} diff --git a/modules/core/src/norm.simd.hpp b/modules/core/src/norm.simd.hpp index 0c3cd5d995..05227dde90 100644 --- a/modules/core/src/norm.simd.hpp +++ b/modules/core/src/norm.simd.hpp @@ -1,6 +1,9 @@ // This file is part of OpenCV project. // It is subject to the license terms in the LICENSE file found in the top-level directory // of this distribution and at http://opencv.org/license.html +// +// Copyright (C) 2025, SpaceMIT Inc., all rights reserved. +// Third party copyrights are property of their respective owners. #include "precomp.hpp" @@ -55,8 +58,7 @@ struct NormDiffInf_SIMD { inline ST operator() (const T* src1, const T* src2, int n) const { ST s = 0; for (int i = 0; i < n; i++) { - ST v = ST(src1[i] - src2[i]); - s = std::max(s, (ST)cv_abs(v)); + s = std::max(s, (ST)std::abs(src1[i] - src2[i])); } return s; } @@ -67,8 +69,7 @@ struct NormDiffL1_SIMD { inline ST operator() (const T* src1, const T* src2, int n) const { ST s = 0; for (int i = 0; i < n; i++) { - ST v = ST(src1[i] - src2[i]); - s += cv_abs(v); + s += std::abs(src1[i] - src2[i]); } return s; } @@ -79,7 +80,7 @@ struct NormDiffL2_SIMD { inline ST operator() (const T* src1, const T* src2, int n) const { ST s = 0; for (int i = 0; i < n; i++) { - ST v = ST(src1[i] - src2[i]); + ST v = (ST)src1[i] - (ST)src2[i]; s += v * v; } return s; @@ -383,8 +384,7 @@ struct NormDiffInf_SIMD { } s = (int)v_reduce_max(v_max(v_max(v_max(r0, r1), r2), r3)); for (; j < n; j++) { - int v = src1[j] - src2[j]; - s = std::max(s, (int)cv_abs(v)); + s = std::max(s, (int)std::abs(src1[j] - src2[j])); } return s; } @@ -415,8 +415,7 @@ struct NormDiffInf_SIMD { } s = (int)v_reduce_max(v_max(v_max(v_max(r0, r1), r2), r3)); for (; j < n; j++) { - int v = src1[j] - src2[j]; - s = std::max(s, (int)cv_abs(v)); + s = std::max(s, (int)std::abs(src1[j] - src2[j])); } return s; } @@ -447,8 +446,7 @@ struct NormDiffInf_SIMD { } s = (int)v_reduce_max(v_max(v_max(v_max(r0, r1), r2), r3)); for (; j < n; j++) { - int v = src1[j] - src2[j]; - s = std::max(s, (int)cv_abs(v)); + s = std::max(s, (int)std::abs(src1[j] - src2[j])); } return s; } @@ -479,8 +477,7 @@ struct NormDiffInf_SIMD { } s = (int)v_reduce_max(v_max(v_max(v_max(r0, r1), r2), r3)); for (; j < n; j++) { - int v = src1[j] - src2[j]; - s = std::max(s, (int)cv_abs(v)); + s = std::max(s, (int)std::abs(src1[j] - src2[j])); } return s; } @@ -511,8 +508,7 @@ struct NormDiffInf_SIMD { } s = (int)v_reduce_max(v_max(v_max(v_max(r0, r1), r2), r3)); for (; j < n; j++) { - int v = src1[j] - src2[j]; - s = std::max(s, (int)cv_abs(v)); + s = std::max(s, (int)std::abs(src1[j] - src2[j])); } return s; } @@ -534,8 +530,7 @@ struct NormDiffInf_SIMD { } s = v_reduce_max(v_max(r0, r1)); for (; j < n; j++) { - float v = src1[j] - src2[j]; - s = std::max(s, cv_abs(v)); + s = std::max(s, (float)std::abs(src1[j] - src2[j])); } return s; } @@ -558,8 +553,7 @@ struct NormDiffL1_SIMD { } s += v_reduce_sum(v_add(r0, r1)); for (; j < n; j++) { - int v = src1[j] - src2[j]; - s += (int)cv_abs(v); + s += std::abs(src1[j] - src2[j]); } return s; } @@ -582,8 +576,7 @@ struct NormDiffL1_SIMD { } s += v_reduce_sum(v_add(r0, r1)); for (; j < n; j++) { - int v =src1[j] - src2[j]; - s += (int)cv_abs(v); + s += std::abs(src1[j] - src2[j]); } return s; } @@ -622,8 +615,7 @@ struct NormDiffL1_SIMD { } s += (int)v_reduce_sum(v_add(v_add(v_add(r0, r1), r2), r3)); for (; j < n; j++) { - int v = src1[j] - src2[j]; - s += (int)cv_abs(v); + s += std::abs(src1[j] - src2[j]); } return s; } @@ -662,8 +654,7 @@ struct NormDiffL1_SIMD { } s += (int)v_reduce_sum(v_add(v_add(v_add(r0, r1), r2), r3)); for (; j < n; j++) { - int v = src1[j] - src2[j]; - s += (int)cv_abs(v); + s += std::abs(src1[j] - src2[j]); } return s; } @@ -687,7 +678,7 @@ struct NormDiffL2_SIMD { } s += v_reduce_sum(v_add(r0, r1)); for (; j < n; j++) { - int v = saturate_cast(src1[j] - src2[j]); + int v = (int)src1[j] - (int)src2[j]; s += v * v; } return s; @@ -712,7 +703,7 @@ struct NormDiffL2_SIMD { } s += v_reduce_sum(v_add(r0, r1)); for (; j < n; j++) { - int v = saturate_cast(src1[j] - src2[j]); + int v = (int)src1[j] - (int)src2[j]; s += v * v; } return s; @@ -955,11 +946,10 @@ struct NormDiffInf_SIMD { double t[VTraits::max_nlanes]; vx_store(t, v_max(r0, r1)); for (int i = 0; i < VTraits::vlanes(); i++) { - s = std::max(s, cv_abs(t[i])); + s = std::max(s, std::abs(t[i])); } for (; j < n; j++) { - double v = src1[j] - src2[j]; - s = std::max(s, cv_abs(v)); + s = std::max(s, (double)std::abs(src1[j] - src2[j])); } return s; } @@ -971,21 +961,17 @@ struct NormDiffL1_SIMD { int j = 0; double s = 0.f; v_float64 r0 = vx_setzero_f64(), r1 = vx_setzero_f64(); - v_float64 r2 = vx_setzero_f64(), r3 = vx_setzero_f64(); - for (; j <= n - 2 * VTraits::vlanes(); j += 2 * VTraits::vlanes()) { + for (; j <= n - VTraits::vlanes(); j += VTraits::vlanes()) { v_int32 v01 = vx_load(src1 + j), v02 = vx_load(src2 + j); - v_float32 v0 = v_abs(v_cvt_f32(v_sub(v01, v02))); - r0 = v_add(r0, v_cvt_f64(v0)); r1 = v_add(r1, v_cvt_f64_high(v0)); - - v_int32 v11 = vx_load(src1 + j + VTraits::vlanes()), - v12 = vx_load(src2 + j + VTraits::vlanes()); - v_float32 v1 = v_abs(v_cvt_f32(v_sub(v11, v12))); - r2 = v_add(r2, v_cvt_f64(v1)); r3 = v_add(r3, v_cvt_f64_high(v1)); + v_uint32 v0 = v_abs(v_sub(v01, v02)); + v_uint64 ev0, ev1; + v_expand(v0, ev0, ev1); + r0 = v_add(r0, v_cvt_f64(v_reinterpret_as_s64(ev0))); + r1 = v_add(r1, v_cvt_f64(v_reinterpret_as_s64(ev1))); } - s += v_reduce_sum(v_add(v_add(v_add(r0, r1), r2), r3)); + s += v_reduce_sum(v_add(r0, r1)); for (; j < n; j++) { - double v = src1[j] - src2[j]; - s += cv_abs(v); + s += std::abs(src1[j] - src2[j]); } return s; } @@ -1010,8 +996,7 @@ struct NormDiffL1_SIMD { } s += v_reduce_sum(v_add(v_add(v_add(r0, r1), r2), r3)); for (; j < n; j++) { - double v = src1[j] - src2[j]; - s += cv_abs(v); + s += std::abs(src1[j] - src2[j]); } return s; } @@ -1033,8 +1018,7 @@ struct NormDiffL1_SIMD { } s += v_reduce_sum(v_add(r0, r1)); for (; j < n; j++) { - double v = src1[j] - src2[j]; - s += cv_abs(v); + s += std::abs(src1[j] - src2[j]); } return s; } @@ -1060,7 +1044,7 @@ struct NormDiffL2_SIMD { } s += v_reduce_sum(v_add(r0, r1)); for (; j < n; j++) { - double v = saturate_cast(src1[j] - src2[j]); + double v = (double)src1[j] - (double)src2[j]; s += v * v; } return s; @@ -1087,7 +1071,7 @@ struct NormDiffL2_SIMD { } s += v_reduce_sum(v_add(r0, r1)); for (; j < n; j++) { - double v = saturate_cast(src1[j] - src2[j]); + double v = (double)src1[j] - (double)src2[j]; s += v * v; } return s; @@ -1100,24 +1084,17 @@ struct NormDiffL2_SIMD { int j = 0; double s = 0.f; v_float64 r0 = vx_setzero_f64(), r1 = vx_setzero_f64(); - v_float64 r2 = vx_setzero_f64(), r3 = vx_setzero_f64(); - for (; j <= n - 2 * VTraits::vlanes(); j += 2 * VTraits::vlanes()) { + for (; j <= n - VTraits::vlanes(); j += VTraits::vlanes()) { v_int32 v01 = vx_load(src1 + j), v02 = vx_load(src2 + j); - v_float32 v0 = v_abs(v_cvt_f32(v_sub(v01, v02))); - v_float64 f00, f01; - f00 = v_cvt_f64(v0); f01 = v_cvt_f64_high(v0); - r0 = v_fma(f00, f00, r0); r1 = v_fma(f01, f01, r1); - - v_int32 v11 = vx_load(src1 + j + VTraits::vlanes()), - v12 = vx_load(src2 + j + VTraits::vlanes()); - v_float32 v1 = v_abs(v_cvt_f32(v_sub(v11, v12))); - v_float64 f10, f11; - f10 = v_cvt_f64(v1); f11 = v_cvt_f64_high(v1); - r2 = v_fma(f10, f10, r2); r3 = v_fma(f11, f11, r3); + v_uint32 v0 = v_absdiff(v01, v02); + v_uint64 ev0, ev1; + v_expand(v0, ev0, ev1); + v_float64 f0 = v_cvt_f64(v_reinterpret_as_s64(ev0)), f1 = v_cvt_f64(v_reinterpret_as_s64(ev1)); + r0 = v_fma(f0, f0, r0); r1 = v_fma(f1, f1, r1); } - s += v_reduce_sum(v_add(v_add(v_add(r0, r1), r2), r3)); + s += v_reduce_sum(v_add(r0, r1)); for (; j < n; j++) { - double v = src1[j] - src2[j]; + double v = (double)src1[j] - (double)src2[j]; s += v * v; } return s; @@ -1145,7 +1122,7 @@ struct NormDiffL2_SIMD { } s += v_reduce_sum(v_add(v_add(v_add(r0, r1), r2), r3)); for (; j < n; j++) { - double v = src1[j] - src2[j]; + double v = (double)src1[j] - (double)src2[j]; s += v * v; } return s; @@ -1181,7 +1158,7 @@ struct NormDiffL2_SIMD { } s += v_reduce_sum(v_add(v_add(v_add(r0, r1), r2), r3)); for (; j < n; j++) { - double v = src1[j] - src2[j]; + double v = (double)src1[j] - (double)src2[j]; s += v * v; } return s; @@ -1297,7 +1274,7 @@ normDiffL2_(const T* src1, const T* src2, const uchar* mask, ST* _result, int le for( int i = 0; i < len; i++, src1 += cn, src2 += cn ) { if( mask[i] ) { for( int k = 0; k < cn; k++ ) { - ST v = src1[k] - src2[k]; + ST v = (ST)src1[k] - (ST)src2[k]; result += v*v; } } @@ -1331,7 +1308,6 @@ NormFunc getNormFunc(int normType, int depth) { CV_INSTRUMENT_REGION(); - // [FIXME] append 0's when merging to 5.x static NormFunc normTab[3][CV_DEPTH_MAX] = { { @@ -1353,7 +1329,7 @@ NormFunc getNormFunc(int normType, int depth) NormDiffFunc getNormDiffFunc(int normType, int depth) { - static NormDiffFunc normDiffTab[3][8] = + static NormDiffFunc normDiffTab[3][CV_DEPTH_MAX] = { { (NormDiffFunc)GET_OPTIMIZED(normDiffInf_8u), (NormDiffFunc)normDiffInf_8s, From 931af518d9a179b469265d70fdc417faf62a0d1b Mon Sep 17 00:00:00 2001 From: Gianluca Nordio <96429106+GianlucaNordio@users.noreply.github.com> Date: Tue, 25 Mar 2025 07:30:56 +0100 Subject: [PATCH 41/94] Merge pull request #27139 from GianlucaNordio:patch-4 Fix minAreaRect and boxPoints docs (#26799) #27139 As requested from issue #26799 the docs regarding minAreaRect and boxPoints are extended specifying the order of the corners for boxPoints and the way the angle is computed for the rotated rect returned by minAreaRect --- modules/imgproc/include/opencv2/imgproc.hpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/modules/imgproc/include/opencv2/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc.hpp index cf222b9edd..14f07e78ad 100644 --- a/modules/imgproc/include/opencv2/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc.hpp @@ -4160,7 +4160,11 @@ CV_EXPORTS_W double contourArea( InputArray contour, bool oriented = false ); /** @brief Finds a rotated rectangle of the minimum area enclosing the input 2D point set. The function calculates and returns the minimum-area bounding rectangle (possibly rotated) for a -specified point set. Developer should keep in mind that the returned RotatedRect can contain negative +specified point set. The angle of rotation represents the angle between the line connecting the starting +and ending points (based on the clockwise order with greatest index for the corner with greatest \f$y\f$) +and the horizontal axis. This angle always falls between \f$[-90, 0)\f$ because, if the object +rotates more than a rect angle, the next edge is used to measure the angle. The starting and ending points change +as the object rotates.Developer should keep in mind that the returned RotatedRect can contain negative indices when data is close to the containing Mat element boundary. @param points Input vector of 2D points, stored in std::vector\<\> or Mat @@ -4169,7 +4173,9 @@ CV_EXPORTS_W RotatedRect minAreaRect( InputArray points ); /** @brief Finds the four vertices of a rotated rect. Useful to draw the rotated rectangle. -The function finds the four vertices of a rotated rectangle. This function is useful to draw the +The function finds the four vertices of a rotated rectangle. The four vertices are returned +in clockwise order starting from the point with greatest \f$y\f$. If two points have the +same \f$y\f$ coordinate the rightmost is the starting point. This function is useful to draw the rectangle. In C++, instead of using this function, you can directly use RotatedRect::points method. Please visit the @ref tutorial_bounding_rotated_ellipses "tutorial on Creating Bounding rotated boxes and ellipses for contours" for more information. From fa58c1205b5a3149e9b75f74c796593debad319d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=A9=E9=9F=B3=E3=81=82=E3=82=81?= Date: Tue, 25 Mar 2025 16:57:47 +0800 Subject: [PATCH 42/94] Merge pull request #27119 from amane-ame:warp_hal_rvv Add RISC-V HAL implementation for cv::warp series #27119 This patch implements `cv_hal_remap`, `cv_hal_warpAffine` and `cv_hal_warpPerspective` using native intrinsics, optimizing the performance of `cv::remap/cv::warpAffine/cv::warpPerspective` for `CV_HAL_INTER_NEAREST/CV_HAL_INTER_LINEAR/CV_HAL_INTER_CUBIC/CV_HAL_INTER_LANCZOS4` modes. Tested on MUSE-PI (Spacemit X60) for both gcc 14.2 and clang 20.0. ``` $ ./opencv_test_imgproc --gtest_filter="*Remap*:*Warp*" $ ./opencv_perf_imgproc --gtest_filter="*Remap*:*remap*:*Warp*" --perf_min_samples=200 --perf_force_samples=200 ``` View the full perf table here: [hal_rvv_warp.pdf](https://github.com/user-attachments/files/19403718/hal_rvv_warp.pdf) ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [ ] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- 3rdparty/hal_rvv/hal_rvv.hpp | 1 + 3rdparty/hal_rvv/hal_rvv_1p0/types.hpp | 6 +- 3rdparty/hal_rvv/hal_rvv_1p0/warp.hpp | 1208 +++++++++++++++++++++++ modules/imgproc/src/hal_replacement.hpp | 49 + modules/imgproc/src/imgwarp.cpp | 10 + 5 files changed, 1272 insertions(+), 2 deletions(-) create mode 100644 3rdparty/hal_rvv/hal_rvv_1p0/warp.hpp diff --git a/3rdparty/hal_rvv/hal_rvv.hpp b/3rdparty/hal_rvv/hal_rvv.hpp index 8287ebeda9..4a10906a33 100644 --- a/3rdparty/hal_rvv/hal_rvv.hpp +++ b/3rdparty/hal_rvv/hal_rvv.hpp @@ -50,6 +50,7 @@ #include "hal_rvv_1p0/filter.hpp" // imgproc #include "hal_rvv_1p0/pyramids.hpp" // imgproc #include "hal_rvv_1p0/color.hpp" // imgproc +#include "hal_rvv_1p0/warp.hpp" // imgproc #include "hal_rvv_1p0/thresh.hpp" // imgproc #include "hal_rvv_1p0/histogram.hpp" // imgproc #endif diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/types.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/types.hpp index 79db847eb5..8c8ad23787 100644 --- a/3rdparty/hal_rvv/hal_rvv_1p0/types.hpp +++ b/3rdparty/hal_rvv/hal_rvv_1p0/types.hpp @@ -91,10 +91,12 @@ using RVV_F64M2 = struct RVV; using RVV_F64M4 = struct RVV; using RVV_F64M8 = struct RVV; -// Only for dst type lmul >= 1 template using RVV_SameLen = - RVV; + RVV((RVV_T::lmul <= 8 ? RVV_T::lmul * static_cast(sizeof(Dst_T)) : RVV_T::lmul == 9 ? static_cast(sizeof(Dst_T)) / 2 : RVV_T::lmul == 10 ? static_cast(sizeof(Dst_T)) / 4 : static_cast(sizeof(Dst_T)) / 8) / sizeof(typename RVV_T::ElemType) == 0.5 ? 9 : \ + (RVV_T::lmul <= 8 ? RVV_T::lmul * static_cast(sizeof(Dst_T)) : RVV_T::lmul == 9 ? static_cast(sizeof(Dst_T)) / 2 : RVV_T::lmul == 10 ? static_cast(sizeof(Dst_T)) / 4 : static_cast(sizeof(Dst_T)) / 8) / sizeof(typename RVV_T::ElemType) == 0.25 ? 10 : \ + (RVV_T::lmul <= 8 ? RVV_T::lmul * static_cast(sizeof(Dst_T)) : RVV_T::lmul == 9 ? static_cast(sizeof(Dst_T)) / 2 : RVV_T::lmul == 10 ? static_cast(sizeof(Dst_T)) / 4 : static_cast(sizeof(Dst_T)) / 8) / sizeof(typename RVV_T::ElemType) == 0.125 ? 11 : \ + (RVV_T::lmul <= 8 ? RVV_T::lmul * static_cast(sizeof(Dst_T)) : RVV_T::lmul == 9 ? static_cast(sizeof(Dst_T)) / 2 : RVV_T::lmul == 10 ? static_cast(sizeof(Dst_T)) / 4 : static_cast(sizeof(Dst_T)) / 8) / sizeof(typename RVV_T::ElemType)))>; template struct RVV_ToIntHelper; template struct RVV_ToUintHelper; diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/warp.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/warp.hpp new file mode 100644 index 0000000000..d9fcf9c109 --- /dev/null +++ b/3rdparty/hal_rvv/hal_rvv_1p0/warp.hpp @@ -0,0 +1,1208 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +// Copyright (C) 2025, Institute of Software, Chinese Academy of Sciences. + +#ifndef OPENCV_HAL_RVV_WARP_HPP_INCLUDED +#define OPENCV_HAL_RVV_WARP_HPP_INCLUDED + +#include + +namespace cv { namespace cv_hal_rvv { + +namespace remap { +#undef cv_hal_remap32f +#define cv_hal_remap32f cv::cv_hal_rvv::remap::remap32f +#undef cv_hal_remap32fc2 +#define cv_hal_remap32fc2 cv::cv_hal_rvv::remap::remap32fc2 +#undef cv_hal_remap16s +#define cv_hal_remap16s cv::cv_hal_rvv::remap::remap16s + +class RemapInvoker : public ParallelLoopBody +{ +public: + template + RemapInvoker(std::function _func, Args&&... args) + { + func = std::bind(_func, std::placeholders::_1, std::placeholders::_2, std::forward(args)...); + } + + virtual void operator()(const Range& range) const override + { + func(range.start, range.end); + } + +private: + std::function func; +}; + +template +static inline int invoke(int width, int height, std::function func, Args&&... args) +{ + cv::parallel_for_(Range(1, height), RemapInvoker(func, std::forward(args)...), static_cast((width - 1) * height) / (1 << 15)); + return func(0, 1, std::forward(args)...); +} + +template struct rvv; +// NN & LINEAR +template<> struct rvv +{ + static inline vfloat32m8_t vcvt0(vuint8m2_t a, size_t b) { return __riscv_vfcvt_f(__riscv_vzext_vf4(a, b), b); } + static inline vuint8m2_t vcvt1(vfloat32m8_t a, size_t b) { return __riscv_vnclipu(__riscv_vfncvt_xu(a, b), 0, __RISCV_VXRM_RNU, b); } + static inline vuint8m2_t vloxei(const uchar* a, vuint32m8_t b, size_t c) { return __riscv_vloxei32_v_u8m2(a, b, c); } +}; +template<> struct rvv +{ + static inline vfloat32m8_t vcvt0(vuint16m4_t a, size_t b) { return __riscv_vfwcvt_f(a, b); } + static inline vuint16m4_t vcvt1(vfloat32m8_t a, size_t b) { return __riscv_vfncvt_xu(a, b); } + static inline vuint16m4_t vloxei(const ushort* a, vuint32m8_t b, size_t c) { return __riscv_vloxei32_v_u16m4(a, b, c); } +}; +template<> struct rvv +{ + static inline vfloat32m8_t vcvt0(vint16m4_t a, size_t b) { return __riscv_vfwcvt_f(a, b); } + static inline vint16m4_t vcvt1(vfloat32m8_t a, size_t b) { return __riscv_vfncvt_x(a, b); } + static inline vint16m4_t vloxei(const short* a, vuint32m8_t b, size_t c) { return __riscv_vloxei32_v_i16m4(a, b, c); } +}; +template<> struct rvv +{ + static inline vfloat32m8_t vcvt0(vfloat32m8_t a, size_t) { return a; } + static inline vfloat32m8_t vcvt1(vfloat32m8_t a, size_t) { return a; } + static inline vfloat32m8_t vloxei(const float* a, vuint32m8_t b, size_t c) { return __riscv_vloxei32_v_f32m8(a, b, c); } +}; +// CUBIC +template<> struct rvv +{ + static inline vfloat32m1_t vcvt0(vuint8mf4_t a, size_t b) { return __riscv_vfcvt_f(__riscv_vzext_vf4(a, b), b); } + static inline vuint8mf4_t vcvt1(vfloat32m1_t a, size_t b) { return __riscv_vnclipu(__riscv_vfncvt_xu(a, b), 0, __RISCV_VXRM_RNU, b); } + static inline vuint8mf4_t vloxei(const uchar* a, vuint32m1_t b, size_t c) { return __riscv_vloxei32_v_u8mf4(a, b, c); } +}; +template<> struct rvv +{ + static inline vfloat32m1_t vcvt0(vuint16mf2_t a, size_t b) { return __riscv_vfwcvt_f(a, b); } + static inline vuint16mf2_t vcvt1(vfloat32m1_t a, size_t b) { return __riscv_vfncvt_xu(a, b); } + static inline vuint16mf2_t vloxei(const ushort* a, vuint32m1_t b, size_t c) { return __riscv_vloxei32_v_u16mf2(a, b, c); } +}; +template<> struct rvv +{ + static inline vfloat32m1_t vcvt0(vint16mf2_t a, size_t b) { return __riscv_vfwcvt_f(a, b); } + static inline vint16mf2_t vcvt1(vfloat32m1_t a, size_t b) { return __riscv_vfncvt_x(a, b); } + static inline vint16mf2_t vloxei(const short* a, vuint32m1_t b, size_t c) { return __riscv_vloxei32_v_i16mf2(a, b, c); } +}; +template<> struct rvv +{ + static inline vfloat32m1_t vcvt0(vfloat32m1_t a, size_t) { return a; } + static inline vfloat32m1_t vcvt1(vfloat32m1_t a, size_t) { return a; } + static inline vfloat32m1_t vloxei(const float* a, vuint32m1_t b, size_t c) { return __riscv_vloxei32_v_f32m1(a, b, c); } +}; +// LANCZOS4 +template<> struct rvv +{ + static inline vfloat32m2_t vcvt0(vuint8mf2_t a, size_t b) { return __riscv_vfcvt_f(__riscv_vzext_vf4(a, b), b); } + static inline vuint8mf2_t vcvt1(vfloat32m2_t a, size_t b) { return __riscv_vnclipu(__riscv_vfncvt_xu(a, b), 0, __RISCV_VXRM_RNU, b); } + static inline vuint8mf2_t vloxei(const uchar* a, vuint32m2_t b, size_t c) { return __riscv_vloxei32_v_u8mf2(a, b, c); } +}; +template<> struct rvv +{ + static inline vfloat32m2_t vcvt0(vuint16m1_t a, size_t b) { return __riscv_vfwcvt_f(a, b); } + static inline vuint16m1_t vcvt1(vfloat32m2_t a, size_t b) { return __riscv_vfncvt_xu(a, b); } + static inline vuint16m1_t vloxei(const ushort* a, vuint32m2_t b, size_t c) { return __riscv_vloxei32_v_u16m1(a, b, c); } +}; +template<> struct rvv +{ + static inline vfloat32m2_t vcvt0(vint16m1_t a, size_t b) { return __riscv_vfwcvt_f(a, b); } + static inline vint16m1_t vcvt1(vfloat32m2_t a, size_t b) { return __riscv_vfncvt_x(a, b); } + static inline vint16m1_t vloxei(const short* a, vuint32m2_t b, size_t c) { return __riscv_vloxei32_v_i16m1(a, b, c); } +}; +template<> struct rvv +{ + static inline vfloat32m2_t vcvt0(vfloat32m2_t a, size_t) { return a; } + static inline vfloat32m2_t vcvt1(vfloat32m2_t a, size_t) { return a; } + static inline vfloat32m2_t vloxei(const float* a, vuint32m2_t b, size_t c) { return __riscv_vloxei32_v_f32m2(a, b, c); } +}; + +template +static inline int remap32fC1(int start, int end, bool s16, const uchar *src_data, size_t src_step, int src_width, int src_height, + uchar *dst_data, size_t dst_step, int dst_width, + const float* mapx, size_t mapx_step, const float* mapy, size_t mapy_step, + int interpolation, int border_type, const double* border_value) +{ + using T = typename helper::ElemType; + const int mode = interpolation & ~CV_HAL_WARP_RELATIVE_MAP; + + for (int i = start; i < end; i++) + { + int vl; + for (int j = 0; j < dst_width; j += vl) + { + vl = helper::setvl(dst_width - j); + typename RVV_SameLen::VecType mx, my; + if (s16) + { + auto map = __riscv_vlseg2e16_v_i16m4x2(reinterpret_cast(mapx) + i * mapx_step + j * 2, vl); + mx = __riscv_vfwcvt_f(__riscv_vget_v_i16m4x2_i16m4(map, 0), vl); + my = __riscv_vfwcvt_f(__riscv_vget_v_i16m4x2_i16m4(map, 1), vl); + } + else + { + if (mapy == nullptr) + { + mx = RVV_SameLen::vload_stride(mapx + i * mapx_step + j * 2 , sizeof(float) * 2, vl); + my = RVV_SameLen::vload_stride(mapx + i * mapx_step + j * 2 + 1, sizeof(float) * 2, vl); + } + else + { + mx = RVV_SameLen::vload(mapx + i * mapx_step + j, vl); + my = RVV_SameLen::vload(mapy + i * mapy_step + j, vl); + } + } + if (interpolation & CV_HAL_WARP_RELATIVE_MAP) + { + mx = __riscv_vfadd(mx, __riscv_vfcvt_f(__riscv_vadd(RVV_SameLen::vid(vl), j, vl), vl), vl); + my = __riscv_vfadd(my, i, vl); + } + + auto access = [&](typename RVV_SameLen::VecType ix, typename RVV_SameLen::VecType iy) { + auto ux = RVV_SameLen::reinterpret(__riscv_vmin(__riscv_vmax(ix, 0, vl), src_width - 1, vl)); + auto uy = RVV_SameLen::reinterpret(__riscv_vmin(__riscv_vmax(iy, 0, vl), src_height - 1, vl)); + auto src = rvv::vloxei(reinterpret_cast(src_data), __riscv_vmadd(uy, src_step, __riscv_vmul(ux, sizeof(T), vl), vl), vl); + if (border_type == CV_HAL_BORDER_CONSTANT) + { + auto mask = __riscv_vmor(__riscv_vmsne(ix, RVV_SameLen::reinterpret(ux), vl), __riscv_vmsne(iy, RVV_SameLen::reinterpret(uy), vl), vl); + src = __riscv_vmerge(src, helper::vmv(border_value[0], vl), mask, vl); + } + return src; + }; + if (mode == CV_HAL_INTER_NEAREST) + { + auto ix = __riscv_vfcvt_x(mx, vl), iy = __riscv_vfcvt_x(my, vl); + helper::vstore(reinterpret_cast(dst_data + i * dst_step) + j, access(ix, iy), vl); + } + else if (mode == CV_HAL_INTER_LINEAR) + { + typename RVV_SameLen::VecType ix0, iy0; + if (s16) + { + ix0 = __riscv_vfcvt_x(mx, vl); + iy0 = __riscv_vfcvt_x(my, vl); + auto md = __riscv_vle16_v_u16m4(reinterpret_cast(mapy) + i * mapy_step + j, vl); + mx = __riscv_vfdiv(__riscv_vfwcvt_f(__riscv_vand(md, 31, vl), vl), 32, vl); + my = __riscv_vfdiv(__riscv_vfwcvt_f(__riscv_vand(__riscv_vsrl(md, 5, vl), 31, vl), vl), 32, vl); + } + else + { + auto imx = __riscv_vfcvt_x(__riscv_vfmul(mx, 32, vl), vl); + auto imy = __riscv_vfcvt_x(__riscv_vfmul(my, 32, vl), vl); + ix0 = __riscv_vsra(imx, 5, vl); + iy0 = __riscv_vsra(imy, 5, vl); + mx = __riscv_vfdiv(__riscv_vfcvt_f(__riscv_vand(imx, 31, vl), vl), 32, vl); + my = __riscv_vfdiv(__riscv_vfcvt_f(__riscv_vand(imy, 31, vl), vl), 32, vl); + } + auto ix1 = __riscv_vadd(ix0, 1, vl), iy1 = __riscv_vadd(iy0, 1, vl); + auto v0 = rvv::vcvt0(access(ix0, iy0), vl); + auto v1 = rvv::vcvt0(access(ix1, iy0), vl); + auto v2 = rvv::vcvt0(access(ix0, iy1), vl); + auto v3 = rvv::vcvt0(access(ix1, iy1), vl); + + v0 = __riscv_vfmacc(v0, mx, __riscv_vfsub(v1, v0, vl), vl); + v2 = __riscv_vfmacc(v2, mx, __riscv_vfsub(v3, v2, vl), vl); + v0 = __riscv_vfmacc(v0, my, __riscv_vfsub(v2, v0, vl), vl); + helper::vstore(reinterpret_cast(dst_data + i * dst_step) + j, rvv::vcvt1(v0, vl), vl); + } + else + { + return CV_HAL_ERROR_NOT_IMPLEMENTED; + } + } + } + + return CV_HAL_ERROR_OK; +} + +class RemapTable +{ +private: + RemapTable() + { + // the algorithm is copied from imgproc/src/imgwarp.cpp, + // in the function static void interpolateLanczos4 + constexpr double s45 = 0.70710678118654752440084436210485; + constexpr double cs[][2] = {{1, 0}, {-s45, -s45}, {0, 1}, {s45, -s45}, {-1, 0}, {s45, s45}, {0, -1}, {-s45, s45}}; + + for (int t = 0; t < 32; t++) + { + float x = t / 32.0f; + if (x < FLT_EPSILON) + { + for (int i = 0; i < 8; i++) + coeffs[t*8+i] = 0; + coeffs[t*8+3] = 1; + return; + } + + float sum = 0; + double y0=-(x+3)*CV_PI*0.25, s0 = std::sin(y0), c0= std::cos(y0); + for (int i = 0; i < 8; i++) + { + double y = -(x+3-i)*CV_PI*0.25; + coeffs[t*8+i] = (float)((cs[i][0]*s0 + cs[i][1]*c0)/(y*y)); + sum += coeffs[t*8+i]; + } + + sum = 1.f/sum; + for (int i = 0; i < 8; i++) + coeffs[t*8+i] *= sum; + } + } + +public: + float coeffs[32 * 8]; + + static RemapTable& instance() + { + static RemapTable tab; + return tab; + } +}; + +template +static inline int remap32fCubic(int start, int end, bool s16, const uchar *src_data, size_t src_step, int src_width, int src_height, + uchar *dst_data, size_t dst_step, int dst_width, + const float* mapx, size_t mapx_step, const float* mapy, size_t mapy_step, + int interpolation, int border_type, const double* border_value) +{ + using T = typename helper::ElemType; + + for (int i = start; i < end; i++) + { + int vl; + for (int j = 0; j < dst_width; j += vl) + { + vl = helper::setvl(dst_width - j); + typename RVV_SameLen::VecType mx, my; + if (s16) + { + auto map = __riscv_vlseg2e16_v_i16mf2x2(reinterpret_cast(mapx) + i * mapx_step + j * 2, vl); + mx = __riscv_vfwcvt_f(__riscv_vget_v_i16mf2x2_i16mf2(map, 0), vl); + my = __riscv_vfwcvt_f(__riscv_vget_v_i16mf2x2_i16mf2(map, 1), vl); + } + else + { + if (mapy == nullptr) + { + auto map = __riscv_vlseg2e32_v_f32m1x2(mapx + i * mapx_step + j * 2, vl); + mx = __riscv_vget_v_f32m1x2_f32m1(map, 0); + my = __riscv_vget_v_f32m1x2_f32m1(map, 1); + } + else + { + mx = RVV_SameLen::vload(mapx + i * mapx_step + j, vl); + my = RVV_SameLen::vload(mapy + i * mapy_step + j, vl); + } + } + if (interpolation & CV_HAL_WARP_RELATIVE_MAP) + { + mx = __riscv_vfadd(mx, __riscv_vfcvt_f(__riscv_vadd(RVV_SameLen::vid(vl), j, vl), vl), vl); + my = __riscv_vfadd(my, i, vl); + } + + auto access = [&](typename RVV_SameLen::VecType ix, typename RVV_SameLen::VecType iy) { + auto ux = RVV_SameLen::reinterpret(__riscv_vmin(__riscv_vmax(ix, 0, vl), src_width - 1, vl)); + auto uy = RVV_SameLen::reinterpret(__riscv_vmin(__riscv_vmax(iy, 0, vl), src_height - 1, vl)); + auto src = rvv::vloxei(reinterpret_cast(src_data), __riscv_vmadd(uy, src_step, __riscv_vmul(ux, sizeof(T), vl), vl), vl); + if (border_type == CV_HAL_BORDER_CONSTANT) + { + auto mask = __riscv_vmor(__riscv_vmsne(ix, RVV_SameLen::reinterpret(ux), vl), __riscv_vmsne(iy, RVV_SameLen::reinterpret(uy), vl), vl); + src = __riscv_vmerge(src, helper::vmv(border_value[0], vl), mask, vl); + } + return src; + }; + + typename RVV_SameLen::VecType ix1, iy1; + if (s16) + { + ix1 = __riscv_vfcvt_x(mx, vl); + iy1 = __riscv_vfcvt_x(my, vl); + auto md = __riscv_vle16_v_u16mf2(reinterpret_cast(mapy) + i * mapy_step + j, vl); + mx = __riscv_vfdiv(__riscv_vfwcvt_f(__riscv_vand(md, 31, vl), vl), 32, vl); + my = __riscv_vfdiv(__riscv_vfwcvt_f(__riscv_vand(__riscv_vsrl(md, 5, vl), 31, vl), vl), 32, vl); + } + else + { + auto imx = __riscv_vfcvt_x(__riscv_vfmul(mx, 32, vl), vl); + auto imy = __riscv_vfcvt_x(__riscv_vfmul(my, 32, vl), vl); + ix1 = __riscv_vsra(imx, 5, vl); + iy1 = __riscv_vsra(imy, 5, vl); + mx = __riscv_vfdiv(__riscv_vfcvt_f(__riscv_vand(imx, 31, vl), vl), 32, vl); + my = __riscv_vfdiv(__riscv_vfcvt_f(__riscv_vand(imy, 31, vl), vl), 32, vl); + } + auto ix0 = __riscv_vsub(ix1, 1, vl), iy0 = __riscv_vsub(iy1, 1, vl); + auto ix2 = __riscv_vadd(ix1, 1, vl), iy2 = __riscv_vadd(iy1, 1, vl); + auto ix3 = __riscv_vadd(ix1, 2, vl), iy3 = __riscv_vadd(iy1, 2, vl); + + // the algorithm is copied from imgproc/src/imgwarp.cpp, + // in the function static void interpolateCubic + typename RVV_SameLen::VecType c0, c1, c2, c3; + auto intertab = [&](typename RVV_SameLen::VecType x) { + constexpr float A = -0.75f; + x = __riscv_vfadd(x, 1, vl); + c0 = __riscv_vfmadd(__riscv_vfmadd(__riscv_vfmadd(x, A, RVV_SameLen::vmv(-5 * A, vl), vl), x, RVV_SameLen::vmv(8 * A, vl), vl), x, RVV_SameLen::vmv(-4 * A, vl), vl); + x = __riscv_vfsub(x, 1, vl); + c1 = __riscv_vfmadd(__riscv_vfmul(__riscv_vfmadd(x, A + 2, RVV_SameLen::vmv(-(A + 3), vl), vl), x, vl), x, RVV_SameLen::vmv(1, vl), vl); + x = __riscv_vfrsub(x, 1, vl); + c2 = __riscv_vfmadd(__riscv_vfmul(__riscv_vfmadd(x, A + 2, RVV_SameLen::vmv(-(A + 3), vl), vl), x, vl), x, RVV_SameLen::vmv(1, vl), vl); + c3 = __riscv_vfsub(__riscv_vfsub(__riscv_vfrsub(c0, 1, vl), c1, vl), c2, vl); + }; + + intertab(mx); + auto v0 = rvv::vcvt0(access(ix0, iy0), vl); + auto v1 = rvv::vcvt0(access(ix1, iy0), vl); + auto v2 = rvv::vcvt0(access(ix2, iy0), vl); + auto v3 = rvv::vcvt0(access(ix3, iy0), vl); + auto k0 = __riscv_vfmacc(__riscv_vfmacc(__riscv_vfmacc(__riscv_vfmul(v0, c0, vl), v1, c1, vl), v2, c2, vl), v3, c3, vl); + v0 = rvv::vcvt0(access(ix0, iy1), vl); + v1 = rvv::vcvt0(access(ix1, iy1), vl); + v2 = rvv::vcvt0(access(ix2, iy1), vl); + v3 = rvv::vcvt0(access(ix3, iy1), vl); + auto k1 = __riscv_vfmacc(__riscv_vfmacc(__riscv_vfmacc(__riscv_vfmul(v0, c0, vl), v1, c1, vl), v2, c2, vl), v3, c3, vl); + v0 = rvv::vcvt0(access(ix0, iy2), vl); + v1 = rvv::vcvt0(access(ix1, iy2), vl); + v2 = rvv::vcvt0(access(ix2, iy2), vl); + v3 = rvv::vcvt0(access(ix3, iy2), vl); + auto k2 = __riscv_vfmacc(__riscv_vfmacc(__riscv_vfmacc(__riscv_vfmul(v0, c0, vl), v1, c1, vl), v2, c2, vl), v3, c3, vl); + v0 = rvv::vcvt0(access(ix0, iy3), vl); + v1 = rvv::vcvt0(access(ix1, iy3), vl); + v2 = rvv::vcvt0(access(ix2, iy3), vl); + v3 = rvv::vcvt0(access(ix3, iy3), vl); + auto k3 = __riscv_vfmacc(__riscv_vfmacc(__riscv_vfmacc(__riscv_vfmul(v0, c0, vl), v1, c1, vl), v2, c2, vl), v3, c3, vl); + + intertab(my); + k0 = __riscv_vfmacc(__riscv_vfmacc(__riscv_vfmacc(__riscv_vfmul(k0, c0, vl), k1, c1, vl), k2, c2, vl), k3, c3, vl); + + helper::vstore(reinterpret_cast(dst_data + i * dst_step) + j, rvv::vcvt1(k0, vl), vl); + } + } + + return CV_HAL_ERROR_OK; +} + +template +static inline int remap32fLanczos4(int start, int end, const uchar *src_data, size_t src_step, int src_width, int src_height, + uchar *dst_data, size_t dst_step, int dst_width, + const float* mapx, size_t mapx_step, const float* mapy, size_t mapy_step, + int interpolation, int border_type, const double* border_value) +{ + using T = typename helper::ElemType; + + for (int i = start; i < end; i++) + { + int vl; + for (int j = 0; j < dst_width; j += vl) + { + vl = helper::setvl(dst_width - j); + typename RVV_SameLen::VecType mx, my; + if (s16) + { + auto map = __riscv_vlseg2e16_v_i16m1x2(reinterpret_cast(mapx) + i * mapx_step + j * 2, vl); + mx = __riscv_vfwcvt_f(__riscv_vget_v_i16m1x2_i16m1(map, 0), vl); + my = __riscv_vfwcvt_f(__riscv_vget_v_i16m1x2_i16m1(map, 1), vl); + } + else + { + if (mapy == nullptr) + { + auto map = __riscv_vlseg2e32_v_f32m2x2(mapx + i * mapx_step + j * 2, vl); + mx = __riscv_vget_v_f32m2x2_f32m2(map, 0); + my = __riscv_vget_v_f32m2x2_f32m2(map, 1); + } + else + { + mx = RVV_SameLen::vload(mapx + i * mapx_step + j, vl); + my = RVV_SameLen::vload(mapy + i * mapy_step + j, vl); + } + } + if (interpolation & CV_HAL_WARP_RELATIVE_MAP) + { + mx = __riscv_vfadd(mx, __riscv_vfcvt_f(__riscv_vadd(RVV_SameLen::vid(vl), j, vl), vl), vl); + my = __riscv_vfadd(my, i, vl); + } + + auto access = [&](typename RVV_SameLen::VecType ix, typename RVV_SameLen::VecType iy) { + auto ux = RVV_SameLen::reinterpret(__riscv_vmin(__riscv_vmax(ix, 0, vl), src_width - 1, vl)); + auto uy = RVV_SameLen::reinterpret(__riscv_vmin(__riscv_vmax(iy, 0, vl), src_height - 1, vl)); + auto src = rvv::vloxei(reinterpret_cast(src_data), __riscv_vmadd(uy, src_step, __riscv_vmul(ux, sizeof(T), vl), vl), vl); + if (border_type == CV_HAL_BORDER_CONSTANT) + { + auto mask = __riscv_vmor(__riscv_vmsne(ix, RVV_SameLen::reinterpret(ux), vl), __riscv_vmsne(iy, RVV_SameLen::reinterpret(uy), vl), vl); + src = __riscv_vmerge(src, helper::vmv(border_value[0], vl), mask, vl); + } + return src; + }; + + typename RVV_SameLen::VecType ix3, iy3; + typename RVV_SameLen::VecType imx, imy; + if (s16) + { + ix3 = __riscv_vfcvt_x(mx, vl); + iy3 = __riscv_vfcvt_x(my, vl); + auto md = __riscv_vle16_v_u16m1(reinterpret_cast(mapy) + i * mapy_step + j, vl); + imx = __riscv_vand(md, 31, vl); + imy = __riscv_vand(__riscv_vsrl(md, 5, vl), 31, vl); + } + else + { + auto dmx = __riscv_vfcvt_x(__riscv_vfmul(mx, 32, vl), vl); + auto dmy = __riscv_vfcvt_x(__riscv_vfmul(my, 32, vl), vl); + ix3 = __riscv_vsra(dmx, 5, vl); + iy3 = __riscv_vsra(dmy, 5, vl); + imx = __riscv_vncvt_x(__riscv_vreinterpret_v_i32m2_u32m2(__riscv_vand(dmx, 31, vl)), vl); + imy = __riscv_vncvt_x(__riscv_vreinterpret_v_i32m2_u32m2(__riscv_vand(dmy, 31, vl)), vl); + } + auto ix0 = __riscv_vsub(ix3, 3, vl), iy0 = __riscv_vsub(iy3, 3, vl); + auto ix1 = __riscv_vsub(ix3, 2, vl), iy1 = __riscv_vsub(iy3, 2, vl); + auto ix2 = __riscv_vsub(ix3, 1, vl), iy2 = __riscv_vsub(iy3, 1, vl); + auto ix4 = __riscv_vadd(ix3, 1, vl), iy4 = __riscv_vadd(iy3, 1, vl); + auto ix5 = __riscv_vadd(ix3, 2, vl), iy5 = __riscv_vadd(iy3, 2, vl); + auto ix6 = __riscv_vadd(ix3, 3, vl), iy6 = __riscv_vadd(iy3, 3, vl); + auto ix7 = __riscv_vadd(ix3, 4, vl), iy7 = __riscv_vadd(iy3, 4, vl); + + typename RVV_SameLen::VecType c0, c1, c2, c3, c4, c5, c6, c7; + auto intertab = [&](typename RVV_SameLen::VecType x) { + x = __riscv_vmul(x, sizeof(float) * 8, vl); + auto val = __riscv_vloxseg4ei16_v_f32m2x4(RemapTable::instance().coeffs, x, vl); + c0 = __riscv_vget_v_f32m2x4_f32m2(val, 0); + c1 = __riscv_vget_v_f32m2x4_f32m2(val, 1); + c2 = __riscv_vget_v_f32m2x4_f32m2(val, 2); + c3 = __riscv_vget_v_f32m2x4_f32m2(val, 3); + val = __riscv_vloxseg4ei16_v_f32m2x4(RemapTable::instance().coeffs, __riscv_vadd(x, sizeof(float) * 4, vl), vl); + c4 = __riscv_vget_v_f32m2x4_f32m2(val, 0); + c5 = __riscv_vget_v_f32m2x4_f32m2(val, 1); + c6 = __riscv_vget_v_f32m2x4_f32m2(val, 2); + c7 = __riscv_vget_v_f32m2x4_f32m2(val, 3); + }; + + intertab(imx); + auto v0 = rvv::vcvt0(access(ix0, iy0), vl); + auto v1 = rvv::vcvt0(access(ix1, iy0), vl); + auto v2 = rvv::vcvt0(access(ix2, iy0), vl); + auto v3 = rvv::vcvt0(access(ix3, iy0), vl); + auto v4 = rvv::vcvt0(access(ix4, iy0), vl); + auto v5 = rvv::vcvt0(access(ix5, iy0), vl); + auto v6 = rvv::vcvt0(access(ix6, iy0), vl); + auto v7 = rvv::vcvt0(access(ix7, iy0), vl); + auto k0 = __riscv_vfmacc(__riscv_vfmacc(__riscv_vfmacc(__riscv_vfmacc(__riscv_vfmacc(__riscv_vfmacc(__riscv_vfmacc(__riscv_vfmul(v0, c0, vl), v1, c1, vl), v2, c2, vl), v3, c3, vl), v4, c4, vl), v5, c5, vl), v6, c6, vl), v7, c7, vl); + v0 = rvv::vcvt0(access(ix0, iy1), vl); + v1 = rvv::vcvt0(access(ix1, iy1), vl); + v2 = rvv::vcvt0(access(ix2, iy1), vl); + v3 = rvv::vcvt0(access(ix3, iy1), vl); + v4 = rvv::vcvt0(access(ix4, iy1), vl); + v5 = rvv::vcvt0(access(ix5, iy1), vl); + v6 = rvv::vcvt0(access(ix6, iy1), vl); + v7 = rvv::vcvt0(access(ix7, iy1), vl); + auto k1 = __riscv_vfmacc(__riscv_vfmacc(__riscv_vfmacc(__riscv_vfmacc(__riscv_vfmacc(__riscv_vfmacc(__riscv_vfmacc(__riscv_vfmul(v0, c0, vl), v1, c1, vl), v2, c2, vl), v3, c3, vl), v4, c4, vl), v5, c5, vl), v6, c6, vl), v7, c7, vl); + v0 = rvv::vcvt0(access(ix0, iy2), vl); + v1 = rvv::vcvt0(access(ix1, iy2), vl); + v2 = rvv::vcvt0(access(ix2, iy2), vl); + v3 = rvv::vcvt0(access(ix3, iy2), vl); + v4 = rvv::vcvt0(access(ix4, iy2), vl); + v5 = rvv::vcvt0(access(ix5, iy2), vl); + v6 = rvv::vcvt0(access(ix6, iy2), vl); + v7 = rvv::vcvt0(access(ix7, iy2), vl); + auto k2 = __riscv_vfmacc(__riscv_vfmacc(__riscv_vfmacc(__riscv_vfmacc(__riscv_vfmacc(__riscv_vfmacc(__riscv_vfmacc(__riscv_vfmul(v0, c0, vl), v1, c1, vl), v2, c2, vl), v3, c3, vl), v4, c4, vl), v5, c5, vl), v6, c6, vl), v7, c7, vl); + v0 = rvv::vcvt0(access(ix0, iy3), vl); + v1 = rvv::vcvt0(access(ix1, iy3), vl); + v2 = rvv::vcvt0(access(ix2, iy3), vl); + v3 = rvv::vcvt0(access(ix3, iy3), vl); + v4 = rvv::vcvt0(access(ix4, iy3), vl); + v5 = rvv::vcvt0(access(ix5, iy3), vl); + v6 = rvv::vcvt0(access(ix6, iy3), vl); + v7 = rvv::vcvt0(access(ix7, iy3), vl); + auto k3 = __riscv_vfmacc(__riscv_vfmacc(__riscv_vfmacc(__riscv_vfmacc(__riscv_vfmacc(__riscv_vfmacc(__riscv_vfmacc(__riscv_vfmul(v0, c0, vl), v1, c1, vl), v2, c2, vl), v3, c3, vl), v4, c4, vl), v5, c5, vl), v6, c6, vl), v7, c7, vl); + v0 = rvv::vcvt0(access(ix0, iy4), vl); + v1 = rvv::vcvt0(access(ix1, iy4), vl); + v2 = rvv::vcvt0(access(ix2, iy4), vl); + v3 = rvv::vcvt0(access(ix3, iy4), vl); + v4 = rvv::vcvt0(access(ix4, iy4), vl); + v5 = rvv::vcvt0(access(ix5, iy4), vl); + v6 = rvv::vcvt0(access(ix6, iy4), vl); + v7 = rvv::vcvt0(access(ix7, iy4), vl); + auto k4 = __riscv_vfmacc(__riscv_vfmacc(__riscv_vfmacc(__riscv_vfmacc(__riscv_vfmacc(__riscv_vfmacc(__riscv_vfmacc(__riscv_vfmul(v0, c0, vl), v1, c1, vl), v2, c2, vl), v3, c3, vl), v4, c4, vl), v5, c5, vl), v6, c6, vl), v7, c7, vl); + v0 = rvv::vcvt0(access(ix0, iy5), vl); + v1 = rvv::vcvt0(access(ix1, iy5), vl); + v2 = rvv::vcvt0(access(ix2, iy5), vl); + v3 = rvv::vcvt0(access(ix3, iy5), vl); + v4 = rvv::vcvt0(access(ix4, iy5), vl); + v5 = rvv::vcvt0(access(ix5, iy5), vl); + v6 = rvv::vcvt0(access(ix6, iy5), vl); + v7 = rvv::vcvt0(access(ix7, iy5), vl); + auto k5 = __riscv_vfmacc(__riscv_vfmacc(__riscv_vfmacc(__riscv_vfmacc(__riscv_vfmacc(__riscv_vfmacc(__riscv_vfmacc(__riscv_vfmul(v0, c0, vl), v1, c1, vl), v2, c2, vl), v3, c3, vl), v4, c4, vl), v5, c5, vl), v6, c6, vl), v7, c7, vl); + v0 = rvv::vcvt0(access(ix0, iy6), vl); + v1 = rvv::vcvt0(access(ix1, iy6), vl); + v2 = rvv::vcvt0(access(ix2, iy6), vl); + v3 = rvv::vcvt0(access(ix3, iy6), vl); + v4 = rvv::vcvt0(access(ix4, iy6), vl); + v5 = rvv::vcvt0(access(ix5, iy6), vl); + v6 = rvv::vcvt0(access(ix6, iy6), vl); + v7 = rvv::vcvt0(access(ix7, iy6), vl); + auto k6 = __riscv_vfmacc(__riscv_vfmacc(__riscv_vfmacc(__riscv_vfmacc(__riscv_vfmacc(__riscv_vfmacc(__riscv_vfmacc(__riscv_vfmul(v0, c0, vl), v1, c1, vl), v2, c2, vl), v3, c3, vl), v4, c4, vl), v5, c5, vl), v6, c6, vl), v7, c7, vl); + v0 = rvv::vcvt0(access(ix0, iy7), vl); + v1 = rvv::vcvt0(access(ix1, iy7), vl); + v2 = rvv::vcvt0(access(ix2, iy7), vl); + v3 = rvv::vcvt0(access(ix3, iy7), vl); + v4 = rvv::vcvt0(access(ix4, iy7), vl); + v5 = rvv::vcvt0(access(ix5, iy7), vl); + v6 = rvv::vcvt0(access(ix6, iy7), vl); + v7 = rvv::vcvt0(access(ix7, iy7), vl); + auto k7 = __riscv_vfmacc(__riscv_vfmacc(__riscv_vfmacc(__riscv_vfmacc(__riscv_vfmacc(__riscv_vfmacc(__riscv_vfmacc(__riscv_vfmul(v0, c0, vl), v1, c1, vl), v2, c2, vl), v3, c3, vl), v4, c4, vl), v5, c5, vl), v6, c6, vl), v7, c7, vl); + + intertab(imy); + k0 = __riscv_vfmacc(__riscv_vfmacc(__riscv_vfmacc(__riscv_vfmacc(__riscv_vfmacc(__riscv_vfmacc(__riscv_vfmacc(__riscv_vfmul(k0, c0, vl), k1, c1, vl), k2, c2, vl), k3, c3, vl), k4, c4, vl), k5, c5, vl), k6, c6, vl), k7, c7, vl); + + helper::vstore(reinterpret_cast(dst_data + i * dst_step) + j, rvv::vcvt1(k0, vl), vl); + } + } + + return CV_HAL_ERROR_OK; +} + +static inline int remap32fC3(int start, int end, const uchar *src_data, size_t src_step, int src_width, int src_height, + uchar *dst_data, size_t dst_step, int dst_width, + const float* mapx, size_t mapx_step, const float* mapy, size_t mapy_step, + int interpolation, int border_type, const double* border_value) +{ + for (int i = start; i < end; i++) + { + int vl; + for (int j = 0; j < dst_width; j += vl) + { + vl = __riscv_vsetvl_e8mf2(dst_width - j); + vfloat32m2_t mx, my; + if (mapy == nullptr) + { + auto map = __riscv_vlseg2e32_v_f32m2x2(mapx + i * mapx_step + j * 2, vl); + mx = __riscv_vget_v_f32m2x2_f32m2(map, 0); + my = __riscv_vget_v_f32m2x2_f32m2(map, 1); + } + else + { + mx = __riscv_vle32_v_f32m2(mapx + i * mapx_step + j, vl); + my = __riscv_vle32_v_f32m2(mapy + i * mapy_step + j, vl); + } + if (interpolation & CV_HAL_WARP_RELATIVE_MAP) + { + mx = __riscv_vfadd(mx, __riscv_vfcvt_f(__riscv_vadd(__riscv_vid_v_u32m2(vl), j, vl), vl), vl); + my = __riscv_vfadd(my, i, vl); + } + + auto access = [&](vint32m2_t ix, vint32m2_t iy, vuint8mf2_t& src0, vuint8mf2_t& src1, vuint8mf2_t& src2) { + auto ux = __riscv_vreinterpret_v_i32m2_u32m2(__riscv_vmin(__riscv_vmax(ix, 0, vl), src_width - 1, vl)); + auto uy = __riscv_vreinterpret_v_i32m2_u32m2(__riscv_vmin(__riscv_vmax(iy, 0, vl), src_height - 1, vl)); + auto src = __riscv_vloxseg3ei32_v_u8mf2x3(src_data, __riscv_vmadd(uy, src_step, __riscv_vmul(ux, 3, vl), vl), vl); + src0 = __riscv_vget_v_u8mf2x3_u8mf2(src, 0); + src1 = __riscv_vget_v_u8mf2x3_u8mf2(src, 1); + src2 = __riscv_vget_v_u8mf2x3_u8mf2(src, 2); + if (border_type == CV_HAL_BORDER_CONSTANT) + { + auto mask = __riscv_vmor(__riscv_vmsne(ix, __riscv_vreinterpret_v_u32m2_i32m2(ux), vl), __riscv_vmsne(iy, __riscv_vreinterpret_v_u32m2_i32m2(uy), vl), vl); + src0 = __riscv_vmerge(src0, border_value[0], mask, vl); + src1 = __riscv_vmerge(src1, border_value[1], mask, vl); + src2 = __riscv_vmerge(src2, border_value[2], mask, vl); + } + }; + if ((interpolation & ~CV_HAL_WARP_RELATIVE_MAP) == CV_HAL_INTER_NEAREST) + { + auto ix = __riscv_vfcvt_x(mx, vl), iy = __riscv_vfcvt_x(my, vl); + vuint8mf2_t src0, src1, src2; + access(ix, iy, src0, src1, src2); + vuint8mf2x3_t dst{}; + dst = __riscv_vset_v_u8mf2_u8mf2x3(dst, 0, src0); + dst = __riscv_vset_v_u8mf2_u8mf2x3(dst, 1, src1); + dst = __riscv_vset_v_u8mf2_u8mf2x3(dst, 2, src2); + __riscv_vsseg3e8(dst_data + i * dst_step + j * 3, dst, vl); + } + else + { + auto imx = __riscv_vfcvt_x(__riscv_vfmul(mx, 32, vl), vl); + auto imy = __riscv_vfcvt_x(__riscv_vfmul(my, 32, vl), vl); + auto ix0 = __riscv_vsra(imx, 5, vl); + auto iy0 = __riscv_vsra(imy, 5, vl); + auto ix1 = __riscv_vadd(ix0, 1, vl), iy1 = __riscv_vadd(iy0, 1, vl); + mx = __riscv_vfdiv(__riscv_vfcvt_f(__riscv_vand(imx, 31, vl), vl), 32, vl); + my = __riscv_vfdiv(__riscv_vfcvt_f(__riscv_vand(imy, 31, vl), vl), 32, vl); + + vfloat32m2_t v00, v10, v20; + vfloat32m2_t v01, v11, v21; + vfloat32m2_t v02, v12, v22; + vfloat32m2_t v03, v13, v23; + vuint8mf2_t src0, src1, src2; + access(ix0, iy0, src0, src1, src2); + v00 = __riscv_vfcvt_f(__riscv_vzext_vf4(src0, vl), vl); + v10 = __riscv_vfcvt_f(__riscv_vzext_vf4(src1, vl), vl); + v20 = __riscv_vfcvt_f(__riscv_vzext_vf4(src2, vl), vl); + access(ix1, iy0, src0, src1, src2); + v01 = __riscv_vfcvt_f(__riscv_vzext_vf4(src0, vl), vl); + v11 = __riscv_vfcvt_f(__riscv_vzext_vf4(src1, vl), vl); + v21 = __riscv_vfcvt_f(__riscv_vzext_vf4(src2, vl), vl); + access(ix0, iy1, src0, src1, src2); + v02 = __riscv_vfcvt_f(__riscv_vzext_vf4(src0, vl), vl); + v12 = __riscv_vfcvt_f(__riscv_vzext_vf4(src1, vl), vl); + v22 = __riscv_vfcvt_f(__riscv_vzext_vf4(src2, vl), vl); + access(ix1, iy1, src0, src1, src2); + v03 = __riscv_vfcvt_f(__riscv_vzext_vf4(src0, vl), vl); + v13 = __riscv_vfcvt_f(__riscv_vzext_vf4(src1, vl), vl); + v23 = __riscv_vfcvt_f(__riscv_vzext_vf4(src2, vl), vl); + + v00 = __riscv_vfmacc(v00, mx, __riscv_vfsub(v01, v00, vl), vl); + v02 = __riscv_vfmacc(v02, mx, __riscv_vfsub(v03, v02, vl), vl); + v00 = __riscv_vfmacc(v00, my, __riscv_vfsub(v02, v00, vl), vl); + v10 = __riscv_vfmacc(v10, mx, __riscv_vfsub(v11, v10, vl), vl); + v12 = __riscv_vfmacc(v12, mx, __riscv_vfsub(v13, v12, vl), vl); + v10 = __riscv_vfmacc(v10, my, __riscv_vfsub(v12, v10, vl), vl); + v20 = __riscv_vfmacc(v20, mx, __riscv_vfsub(v21, v20, vl), vl); + v22 = __riscv_vfmacc(v22, mx, __riscv_vfsub(v23, v22, vl), vl); + v20 = __riscv_vfmacc(v20, my, __riscv_vfsub(v22, v20, vl), vl); + vuint8mf2x3_t dst{}; + dst = __riscv_vset_v_u8mf2_u8mf2x3(dst, 0, __riscv_vnclipu(__riscv_vfncvt_xu(v00, vl), 0, __RISCV_VXRM_RNU, vl)); + dst = __riscv_vset_v_u8mf2_u8mf2x3(dst, 1, __riscv_vnclipu(__riscv_vfncvt_xu(v10, vl), 0, __RISCV_VXRM_RNU, vl)); + dst = __riscv_vset_v_u8mf2_u8mf2x3(dst, 2, __riscv_vnclipu(__riscv_vfncvt_xu(v20, vl), 0, __RISCV_VXRM_RNU, vl)); + __riscv_vsseg3e8(dst_data + i * dst_step + j * 3, dst, vl); + } + } + } + + return CV_HAL_ERROR_OK; +} + +static inline int remap32fC4(int start, int end, const uchar *src_data, size_t src_step, int src_width, int src_height, + uchar *dst_data, size_t dst_step, int dst_width, + const float* mapx, size_t mapx_step, const float* mapy, size_t mapy_step, + int interpolation, int border_type, const double* border_value) +{ + for (int i = start; i < end; i++) + { + int vl; + for (int j = 0; j < dst_width; j += vl) + { + vl = __riscv_vsetvl_e8mf2(dst_width - j); + vfloat32m2_t mx, my; + if (mapy == nullptr) + { + auto map = __riscv_vlseg2e32_v_f32m2x2(mapx + i * mapx_step + j * 2, vl); + mx = __riscv_vget_v_f32m2x2_f32m2(map, 0); + my = __riscv_vget_v_f32m2x2_f32m2(map, 1); + } + else + { + mx = __riscv_vle32_v_f32m2(mapx + i * mapx_step + j, vl); + my = __riscv_vle32_v_f32m2(mapy + i * mapy_step + j, vl); + } + if (interpolation & CV_HAL_WARP_RELATIVE_MAP) + { + mx = __riscv_vfadd(mx, __riscv_vfcvt_f(__riscv_vadd(__riscv_vid_v_u32m2(vl), j, vl), vl), vl); + my = __riscv_vfadd(my, i, vl); + } + + auto access = [&](vint32m2_t ix, vint32m2_t iy, vuint8mf2_t& src0, vuint8mf2_t& src1, vuint8mf2_t& src2, vuint8mf2_t& src3) { + auto ux = __riscv_vreinterpret_v_i32m2_u32m2(__riscv_vmin(__riscv_vmax(ix, 0, vl), src_width - 1, vl)); + auto uy = __riscv_vreinterpret_v_i32m2_u32m2(__riscv_vmin(__riscv_vmax(iy, 0, vl), src_height - 1, vl)); + auto src = __riscv_vloxseg4ei32_v_u8mf2x4(src_data, __riscv_vmadd(uy, src_step, __riscv_vmul(ux, 4, vl), vl), vl); + src0 = __riscv_vget_v_u8mf2x4_u8mf2(src, 0); + src1 = __riscv_vget_v_u8mf2x4_u8mf2(src, 1); + src2 = __riscv_vget_v_u8mf2x4_u8mf2(src, 2); + src3 = __riscv_vget_v_u8mf2x4_u8mf2(src, 3); + if (border_type == CV_HAL_BORDER_CONSTANT) + { + auto mask = __riscv_vmor(__riscv_vmsne(ix, __riscv_vreinterpret_v_u32m2_i32m2(ux), vl), __riscv_vmsne(iy, __riscv_vreinterpret_v_u32m2_i32m2(uy), vl), vl); + src0 = __riscv_vmerge(src0, border_value[0], mask, vl); + src1 = __riscv_vmerge(src1, border_value[1], mask, vl); + src2 = __riscv_vmerge(src2, border_value[2], mask, vl); + src3 = __riscv_vmerge(src3, border_value[3], mask, vl); + } + }; + if ((interpolation & ~CV_HAL_WARP_RELATIVE_MAP) == CV_HAL_INTER_NEAREST) + { + auto ix = __riscv_vfcvt_x(mx, vl), iy = __riscv_vfcvt_x(my, vl); + vuint8mf2_t src0, src1, src2, src3; + access(ix, iy, src0, src1, src2, src3); + vuint8mf2x4_t dst{}; + dst = __riscv_vset_v_u8mf2_u8mf2x4(dst, 0, src0); + dst = __riscv_vset_v_u8mf2_u8mf2x4(dst, 1, src1); + dst = __riscv_vset_v_u8mf2_u8mf2x4(dst, 2, src2); + dst = __riscv_vset_v_u8mf2_u8mf2x4(dst, 3, src3); + __riscv_vsseg4e8(dst_data + i * dst_step + j * 4, dst, vl); + } + else + { + auto imx = __riscv_vfcvt_x(__riscv_vfmul(mx, 32, vl), vl); + auto imy = __riscv_vfcvt_x(__riscv_vfmul(my, 32, vl), vl); + auto ix0 = __riscv_vsra(imx, 5, vl); + auto iy0 = __riscv_vsra(imy, 5, vl); + auto ix1 = __riscv_vadd(ix0, 1, vl), iy1 = __riscv_vadd(iy0, 1, vl); + mx = __riscv_vfdiv(__riscv_vfcvt_f(__riscv_vand(imx, 31, vl), vl), 32, vl); + my = __riscv_vfdiv(__riscv_vfcvt_f(__riscv_vand(imy, 31, vl), vl), 32, vl); + + vfloat32m2_t v00, v10, v20, v30; + vfloat32m2_t v01, v11, v21, v31; + vfloat32m2_t v02, v12, v22, v32; + vfloat32m2_t v03, v13, v23, v33; + vuint8mf2_t src0, src1, src2, src3; + access(ix0, iy0, src0, src1, src2, src3); + v00 = __riscv_vfcvt_f(__riscv_vzext_vf4(src0, vl), vl); + v10 = __riscv_vfcvt_f(__riscv_vzext_vf4(src1, vl), vl); + v20 = __riscv_vfcvt_f(__riscv_vzext_vf4(src2, vl), vl); + v30 = __riscv_vfcvt_f(__riscv_vzext_vf4(src3, vl), vl); + access(ix1, iy0, src0, src1, src2, src3); + v01 = __riscv_vfcvt_f(__riscv_vzext_vf4(src0, vl), vl); + v11 = __riscv_vfcvt_f(__riscv_vzext_vf4(src1, vl), vl); + v21 = __riscv_vfcvt_f(__riscv_vzext_vf4(src2, vl), vl); + v31 = __riscv_vfcvt_f(__riscv_vzext_vf4(src3, vl), vl); + access(ix0, iy1, src0, src1, src2, src3); + v02 = __riscv_vfcvt_f(__riscv_vzext_vf4(src0, vl), vl); + v12 = __riscv_vfcvt_f(__riscv_vzext_vf4(src1, vl), vl); + v22 = __riscv_vfcvt_f(__riscv_vzext_vf4(src2, vl), vl); + v32 = __riscv_vfcvt_f(__riscv_vzext_vf4(src3, vl), vl); + access(ix1, iy1, src0, src1, src2, src3); + v03 = __riscv_vfcvt_f(__riscv_vzext_vf4(src0, vl), vl); + v13 = __riscv_vfcvt_f(__riscv_vzext_vf4(src1, vl), vl); + v23 = __riscv_vfcvt_f(__riscv_vzext_vf4(src2, vl), vl); + v33 = __riscv_vfcvt_f(__riscv_vzext_vf4(src3, vl), vl); + + v00 = __riscv_vfmacc(v00, mx, __riscv_vfsub(v01, v00, vl), vl); + v02 = __riscv_vfmacc(v02, mx, __riscv_vfsub(v03, v02, vl), vl); + v00 = __riscv_vfmacc(v00, my, __riscv_vfsub(v02, v00, vl), vl); + v10 = __riscv_vfmacc(v10, mx, __riscv_vfsub(v11, v10, vl), vl); + v12 = __riscv_vfmacc(v12, mx, __riscv_vfsub(v13, v12, vl), vl); + v10 = __riscv_vfmacc(v10, my, __riscv_vfsub(v12, v10, vl), vl); + v20 = __riscv_vfmacc(v20, mx, __riscv_vfsub(v21, v20, vl), vl); + v22 = __riscv_vfmacc(v22, mx, __riscv_vfsub(v23, v22, vl), vl); + v20 = __riscv_vfmacc(v20, my, __riscv_vfsub(v22, v20, vl), vl); + v30 = __riscv_vfmacc(v30, mx, __riscv_vfsub(v31, v30, vl), vl); + v32 = __riscv_vfmacc(v32, mx, __riscv_vfsub(v33, v32, vl), vl); + v30 = __riscv_vfmacc(v30, my, __riscv_vfsub(v32, v30, vl), vl); + vuint8mf2x4_t dst{}; + dst = __riscv_vset_v_u8mf2_u8mf2x4(dst, 0, __riscv_vnclipu(__riscv_vfncvt_xu(v00, vl), 0, __RISCV_VXRM_RNU, vl)); + dst = __riscv_vset_v_u8mf2_u8mf2x4(dst, 1, __riscv_vnclipu(__riscv_vfncvt_xu(v10, vl), 0, __RISCV_VXRM_RNU, vl)); + dst = __riscv_vset_v_u8mf2_u8mf2x4(dst, 2, __riscv_vnclipu(__riscv_vfncvt_xu(v20, vl), 0, __RISCV_VXRM_RNU, vl)); + dst = __riscv_vset_v_u8mf2_u8mf2x4(dst, 3, __riscv_vnclipu(__riscv_vfncvt_xu(v30, vl), 0, __RISCV_VXRM_RNU, vl)); + __riscv_vsseg4e8(dst_data + i * dst_step + j * 4, dst, vl); + } + } + } + + return CV_HAL_ERROR_OK; +} + +// the algorithm is copied from 3rdparty/carotene/src/remap.cpp, +// in the function void CAROTENE_NS::remapNearestNeighbor and void CAROTENE_NS::remapLinear +template +inline int remap32f(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, + float* mapx, size_t mapx_step, float* mapy, size_t mapy_step, + int interpolation, int border_type, const double border_value[4]) +{ + if (src_type != CV_8UC1 && src_type != CV_8UC3 && src_type != CV_8UC4 && src_type != CV_16UC1 && src_type != CV_16SC1 && src_type != CV_32FC1) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + if (border_type != CV_HAL_BORDER_CONSTANT && border_type != CV_HAL_BORDER_REPLICATE) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + + const int mode = interpolation & ~CV_HAL_WARP_RELATIVE_MAP; + if (mode != CV_HAL_INTER_NEAREST && mode != CV_HAL_INTER_LINEAR && mode != CV_HAL_INTER_CUBIC && mode != CV_HAL_INTER_LANCZOS4) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + if ((mode == CV_HAL_INTER_CUBIC || mode == CV_HAL_INTER_LANCZOS4) && CV_MAKETYPE(src_type, 1) != src_type) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + + mapx_step /= s16 ? sizeof(short) : sizeof(float); + mapy_step /= s16 ? sizeof(ushort) : sizeof(float); + switch (src_type) + { + case CV_8UC3: + return invoke(dst_width, dst_height, {remap32fC3}, src_data, src_step, src_width, src_height, dst_data, dst_step, dst_width, mapx, mapx_step, mapy, mapy_step, interpolation, border_type, border_value); + case CV_8UC4: + return invoke(dst_width, dst_height, {remap32fC4}, src_data, src_step, src_width, src_height, dst_data, dst_step, dst_width, mapx, mapx_step, mapy, mapy_step, interpolation, border_type, border_value); + } + switch (mode*100 + src_type) + { + case CV_HAL_INTER_NEAREST*100 + CV_8UC1: + case CV_HAL_INTER_LINEAR*100 + CV_8UC1: + return invoke(dst_width, dst_height, {remap32fC1}, s16, src_data, src_step, src_width, src_height, dst_data, dst_step, dst_width, mapx, mapx_step, mapy, mapy_step, interpolation, border_type, border_value); + case CV_HAL_INTER_NEAREST*100 + CV_16UC1: + case CV_HAL_INTER_LINEAR*100 + CV_16UC1: + return invoke(dst_width, dst_height, {remap32fC1}, s16, src_data, src_step, src_width, src_height, dst_data, dst_step, dst_width, mapx, mapx_step, mapy, mapy_step, interpolation, border_type, border_value); + case CV_HAL_INTER_NEAREST*100 + CV_16SC1: + case CV_HAL_INTER_LINEAR*100 + CV_16SC1: + return invoke(dst_width, dst_height, {remap32fC1}, s16, src_data, src_step, src_width, src_height, dst_data, dst_step, dst_width, mapx, mapx_step, mapy, mapy_step, interpolation, border_type, border_value); + case CV_HAL_INTER_NEAREST*100 + CV_32FC1: + case CV_HAL_INTER_LINEAR*100 + CV_32FC1: + return invoke(dst_width, dst_height, {remap32fC1}, s16, src_data, src_step, src_width, src_height, dst_data, dst_step, dst_width, mapx, mapx_step, mapy, mapy_step, interpolation, border_type, border_value); + + case CV_HAL_INTER_CUBIC*100 + CV_8UC1: + return invoke(dst_width, dst_height, {remap32fCubic}, s16, src_data, src_step, src_width, src_height, dst_data, dst_step, dst_width, mapx, mapx_step, mapy, mapy_step, interpolation, border_type, border_value); + case CV_HAL_INTER_CUBIC*100 + CV_16UC1: + return invoke(dst_width, dst_height, {remap32fCubic}, s16, src_data, src_step, src_width, src_height, dst_data, dst_step, dst_width, mapx, mapx_step, mapy, mapy_step, interpolation, border_type, border_value); + case CV_HAL_INTER_CUBIC*100 + CV_16SC1: + return invoke(dst_width, dst_height, {remap32fCubic}, s16, src_data, src_step, src_width, src_height, dst_data, dst_step, dst_width, mapx, mapx_step, mapy, mapy_step, interpolation, border_type, border_value); + case CV_HAL_INTER_CUBIC*100 + CV_32FC1: + return invoke(dst_width, dst_height, {remap32fCubic}, s16, src_data, src_step, src_width, src_height, dst_data, dst_step, dst_width, mapx, mapx_step, mapy, mapy_step, interpolation, border_type, border_value); + + // Lanczos4 is disabled in clang since register allocation strategy is buggy in clang 20.0 + // remove this #ifndef in the future if possible +#ifndef __clang__ + case CV_HAL_INTER_LANCZOS4*100 + CV_8UC1: + return invoke(dst_width, dst_height, {remap32fLanczos4}, src_data, src_step, src_width, src_height, dst_data, dst_step, dst_width, mapx, mapx_step, mapy, mapy_step, interpolation, border_type, border_value); + // disabled since UI is fast enough + // case CV_HAL_INTER_LANCZOS4*100 + CV_16UC1: + // return invoke(dst_width, dst_height, {remap32fLanczos4}, src_data, src_step, src_width, src_height, dst_data, dst_step, dst_width, mapx, mapx_step, mapy, mapy_step, interpolation, border_type, border_value); + case CV_HAL_INTER_LANCZOS4*100 + CV_16SC1: + return invoke(dst_width, dst_height, {remap32fLanczos4}, src_data, src_step, src_width, src_height, dst_data, dst_step, dst_width, mapx, mapx_step, mapy, mapy_step, interpolation, border_type, border_value); + case CV_HAL_INTER_LANCZOS4*100 + CV_32FC1: + return invoke(dst_width, dst_height, {remap32fLanczos4}, src_data, src_step, src_width, src_height, dst_data, dst_step, dst_width, mapx, mapx_step, mapy, mapy_step, interpolation, border_type, border_value); +#endif + } + + return CV_HAL_ERROR_NOT_IMPLEMENTED; +} + +inline int remap32fc2(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, + float* map, size_t map_step, int interpolation, int border_type, const double border_value[4]) +{ + return remap32f(src_type, src_data, src_step, src_width, src_height, dst_data, dst_step, dst_width, dst_height, map, map_step, nullptr, 0, interpolation, border_type, border_value); +} + +inline int remap16s(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, + short* mapx, size_t mapx_step, ushort* mapy, size_t mapy_step, + int interpolation, int border_type, const double border_value[4]) +{ + if (CV_MAKETYPE(src_type, 1) != src_type) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + return remap32f(src_type, src_data, src_step, src_width, src_height, dst_data, dst_step, dst_width, dst_height, reinterpret_cast(mapx), mapx_step, reinterpret_cast(mapy), mapy_step, interpolation, border_type, border_value); +} +} // cv::cv_hal_rvv::remap + +namespace warp { +#undef cv_hal_warpAffine +#define cv_hal_warpAffine cv::cv_hal_rvv::warp::warpAffine +#undef cv_hal_warpPerspective +#define cv_hal_warpPerspective cv::cv_hal_rvv::warp::warpPerspective + +template +static inline int warpC1(int start, int end, const uchar *src_data, size_t src_step, int src_width, int src_height, uchar *dst_data, size_t dst_step, int dst_width, const double* M, int interpolation, int borderType, const double* borderValue) +{ + for (int i = start; i < end; i++) + { + int vl; + for (int j = 0; j < dst_width; j += vl) + { + vl = __riscv_vsetvl_e8m1(dst_width - j); + auto access = [&](vint32m4_t ix, vint32m4_t iy) { + auto ux = __riscv_vreinterpret_v_i32m4_u32m4(__riscv_vmin(__riscv_vmax(ix, 0, vl), src_width - 1, vl)); + auto uy = __riscv_vreinterpret_v_i32m4_u32m4(__riscv_vmin(__riscv_vmax(iy, 0, vl), src_height - 1, vl)); + auto src = __riscv_vloxei32_v_u8m1(src_data, __riscv_vmadd(uy, src_step, ux, vl), vl); + if (borderType == CV_HAL_BORDER_CONSTANT) + { + auto mask = __riscv_vmor(__riscv_vmsne(ix, __riscv_vreinterpret_v_u32m4_i32m4(ux), vl), __riscv_vmsne(iy, __riscv_vreinterpret_v_u32m4_i32m4(uy), vl), vl); + src = __riscv_vmerge(src, borderValue[0], mask, vl); + } + return src; + }; + + auto id = __riscv_vfcvt_f(__riscv_vadd(__riscv_vid_v_u32m4(vl), j, vl), vl); + auto mx = __riscv_vfmadd(id, M[0], __riscv_vfmadd(__riscv_vfmv_v_f_f32m4(i, vl), M[1], __riscv_vfmv_v_f_f32m4(M[2], vl), vl), vl); + auto my = __riscv_vfmadd(id, M[3], __riscv_vfmadd(__riscv_vfmv_v_f_f32m4(i, vl), M[4], __riscv_vfmv_v_f_f32m4(M[5], vl), vl), vl); + if (perspective) + { + auto md = __riscv_vfrdiv(__riscv_vfmadd(id, M[6], __riscv_vfmadd(__riscv_vfmv_v_f_f32m4(i, vl), M[7], __riscv_vfmv_v_f_f32m4(M[8], vl), vl), vl), 1, vl); + mx = __riscv_vfmul(mx, md, vl); + my = __riscv_vfmul(my, md, vl); + } + + if (interpolation == CV_HAL_INTER_NEAREST) + { + auto ix = __riscv_vfcvt_x(mx, vl), iy = __riscv_vfcvt_x(my, vl); + __riscv_vse8(dst_data + i * dst_step + j, access(ix, iy), vl); + } + else + { + auto ix = __riscv_vfcvt_x(__riscv_vfmadd(mx, 1 << 10, __riscv_vfmv_v_f_f32m4(1 << 4, vl), vl), vl); + auto iy = __riscv_vfcvt_x(__riscv_vfmadd(my, 1 << 10, __riscv_vfmv_v_f_f32m4(1 << 4, vl), vl), vl); + auto ix0 = __riscv_vsra(ix, 10, vl), iy0 = __riscv_vsra(iy, 10, vl); + auto ix1 = __riscv_vadd(ix0, 1, vl), iy1 = __riscv_vadd(iy0, 1, vl); + + auto v0 = __riscv_vzext_vf4(access(ix0, iy0), vl); + auto v1 = __riscv_vzext_vf4(access(ix1, iy0), vl); + auto v2 = __riscv_vzext_vf4(access(ix0, iy1), vl); + auto v3 = __riscv_vzext_vf4(access(ix1, iy1), vl); + + auto rx = __riscv_vreinterpret_v_i32m4_u32m4(__riscv_vand(__riscv_vsra(ix, 5, vl), (1 << 5) - 1, vl)); + auto ry = __riscv_vreinterpret_v_i32m4_u32m4(__riscv_vand(__riscv_vsra(iy, 5, vl), (1 << 5) - 1, vl)); + v0 = __riscv_vmacc(__riscv_vmul(v0, 1 << 5, vl), rx, __riscv_vsub(v1, v0, vl), vl); + v2 = __riscv_vmacc(__riscv_vmul(v2, 1 << 5, vl), rx, __riscv_vsub(v3, v2, vl), vl); + v0 = __riscv_vmacc(__riscv_vmul(v0, 1 << 5, vl), ry, __riscv_vsub(v2, v0, vl), vl); + __riscv_vse8(dst_data + i * dst_step + j, __riscv_vnclipu(__riscv_vnclipu(v0, 10, __RISCV_VXRM_RNU, vl), 0, __RISCV_VXRM_RNU, vl), vl); + } + } + } + + return CV_HAL_ERROR_OK; +} + +template +static inline int warpC3(int start, int end, const uchar *src_data, size_t src_step, int src_width, int src_height, uchar *dst_data, size_t dst_step, int dst_width, const double* M, int interpolation, int borderType, const double* borderValue) +{ + for (int i = start; i < end; i++) + { + int vl; + for (int j = 0; j < dst_width; j += vl) + { + vl = __riscv_vsetvl_e8mf2(dst_width - j); + auto access = [&](vint32m2_t ix, vint32m2_t iy, vuint8mf2_t& src0, vuint8mf2_t& src1, vuint8mf2_t& src2) { + auto ux = __riscv_vreinterpret_v_i32m2_u32m2(__riscv_vmin(__riscv_vmax(ix, 0, vl), src_width - 1, vl)); + auto uy = __riscv_vreinterpret_v_i32m2_u32m2(__riscv_vmin(__riscv_vmax(iy, 0, vl), src_height - 1, vl)); + auto src = __riscv_vloxseg3ei32_v_u8mf2x3(src_data, __riscv_vmadd(uy, src_step, __riscv_vmul(ux, 3, vl), vl), vl); + src0 = __riscv_vget_v_u8mf2x3_u8mf2(src, 0); + src1 = __riscv_vget_v_u8mf2x3_u8mf2(src, 1); + src2 = __riscv_vget_v_u8mf2x3_u8mf2(src, 2); + if (borderType == CV_HAL_BORDER_CONSTANT) + { + auto mask = __riscv_vmor(__riscv_vmsne(ix, __riscv_vreinterpret_v_u32m2_i32m2(ux), vl), __riscv_vmsne(iy, __riscv_vreinterpret_v_u32m2_i32m2(uy), vl), vl); + src0 = __riscv_vmerge(src0, borderValue[0], mask, vl); + src1 = __riscv_vmerge(src1, borderValue[1], mask, vl); + src2 = __riscv_vmerge(src2, borderValue[2], mask, vl); + } + }; + + auto id = __riscv_vfcvt_f(__riscv_vadd(__riscv_vid_v_u32m2(vl), j, vl), vl); + auto mx = __riscv_vfmadd(id, M[0], __riscv_vfmadd(__riscv_vfmv_v_f_f32m2(i, vl), M[1], __riscv_vfmv_v_f_f32m2(M[2], vl), vl), vl); + auto my = __riscv_vfmadd(id, M[3], __riscv_vfmadd(__riscv_vfmv_v_f_f32m2(i, vl), M[4], __riscv_vfmv_v_f_f32m2(M[5], vl), vl), vl); + if (perspective) + { + auto md = __riscv_vfrdiv(__riscv_vfmadd(id, M[6], __riscv_vfmadd(__riscv_vfmv_v_f_f32m2(i, vl), M[7], __riscv_vfmv_v_f_f32m2(M[8], vl), vl), vl), 1, vl); + mx = __riscv_vfmul(mx, md, vl); + my = __riscv_vfmul(my, md, vl); + } + + if (interpolation == CV_HAL_INTER_NEAREST) + { + auto ix = __riscv_vfcvt_x(mx, vl), iy = __riscv_vfcvt_x(my, vl); + vuint8mf2_t src0, src1, src2; + access(ix, iy, src0, src1, src2); + vuint8mf2x3_t dst{}; + dst = __riscv_vset_v_u8mf2_u8mf2x3(dst, 0, src0); + dst = __riscv_vset_v_u8mf2_u8mf2x3(dst, 1, src1); + dst = __riscv_vset_v_u8mf2_u8mf2x3(dst, 2, src2); + __riscv_vsseg3e8(dst_data + i * dst_step + j * 3, dst, vl); + } + else + { + auto ix = __riscv_vfcvt_x(__riscv_vfmadd(mx, 1 << 10, __riscv_vfmv_v_f_f32m2(1 << 4, vl), vl), vl); + auto iy = __riscv_vfcvt_x(__riscv_vfmadd(my, 1 << 10, __riscv_vfmv_v_f_f32m2(1 << 4, vl), vl), vl); + auto ix0 = __riscv_vsra(ix, 10, vl), iy0 = __riscv_vsra(iy, 10, vl); + auto ix1 = __riscv_vadd(ix0, 1, vl), iy1 = __riscv_vadd(iy0, 1, vl); + + vuint32m2_t v00, v10, v20; + vuint32m2_t v01, v11, v21; + vuint32m2_t v02, v12, v22; + vuint32m2_t v03, v13, v23; + vuint8mf2_t src0, src1, src2; + access(ix0, iy0, src0, src1, src2); + v00 = __riscv_vzext_vf4(src0, vl); + v10 = __riscv_vzext_vf4(src1, vl); + v20 = __riscv_vzext_vf4(src2, vl); + access(ix1, iy0, src0, src1, src2); + v01 = __riscv_vzext_vf4(src0, vl); + v11 = __riscv_vzext_vf4(src1, vl); + v21 = __riscv_vzext_vf4(src2, vl); + access(ix0, iy1, src0, src1, src2); + v02 = __riscv_vzext_vf4(src0, vl); + v12 = __riscv_vzext_vf4(src1, vl); + v22 = __riscv_vzext_vf4(src2, vl); + access(ix1, iy1, src0, src1, src2); + v03 = __riscv_vzext_vf4(src0, vl); + v13 = __riscv_vzext_vf4(src1, vl); + v23 = __riscv_vzext_vf4(src2, vl); + + auto rx = __riscv_vreinterpret_v_i32m2_u32m2(__riscv_vand(__riscv_vsra(ix, 5, vl), (1 << 5) - 1, vl)); + auto ry = __riscv_vreinterpret_v_i32m2_u32m2(__riscv_vand(__riscv_vsra(iy, 5, vl), (1 << 5) - 1, vl)); + v00 = __riscv_vmacc(__riscv_vmul(v00, 1 << 5, vl), rx, __riscv_vsub(v01, v00, vl), vl); + v02 = __riscv_vmacc(__riscv_vmul(v02, 1 << 5, vl), rx, __riscv_vsub(v03, v02, vl), vl); + v00 = __riscv_vmacc(__riscv_vmul(v00, 1 << 5, vl), ry, __riscv_vsub(v02, v00, vl), vl); + v10 = __riscv_vmacc(__riscv_vmul(v10, 1 << 5, vl), rx, __riscv_vsub(v11, v10, vl), vl); + v12 = __riscv_vmacc(__riscv_vmul(v12, 1 << 5, vl), rx, __riscv_vsub(v13, v12, vl), vl); + v10 = __riscv_vmacc(__riscv_vmul(v10, 1 << 5, vl), ry, __riscv_vsub(v12, v10, vl), vl); + v20 = __riscv_vmacc(__riscv_vmul(v20, 1 << 5, vl), rx, __riscv_vsub(v21, v20, vl), vl); + v22 = __riscv_vmacc(__riscv_vmul(v22, 1 << 5, vl), rx, __riscv_vsub(v23, v22, vl), vl); + v20 = __riscv_vmacc(__riscv_vmul(v20, 1 << 5, vl), ry, __riscv_vsub(v22, v20, vl), vl); + vuint8mf2x3_t dst{}; + dst = __riscv_vset_v_u8mf2_u8mf2x3(dst, 0, __riscv_vnclipu(__riscv_vnclipu(v00, 10, __RISCV_VXRM_RNU, vl), 0, __RISCV_VXRM_RNU, vl)); + dst = __riscv_vset_v_u8mf2_u8mf2x3(dst, 1, __riscv_vnclipu(__riscv_vnclipu(v10, 10, __RISCV_VXRM_RNU, vl), 0, __RISCV_VXRM_RNU, vl)); + dst = __riscv_vset_v_u8mf2_u8mf2x3(dst, 2, __riscv_vnclipu(__riscv_vnclipu(v20, 10, __RISCV_VXRM_RNU, vl), 0, __RISCV_VXRM_RNU, vl)); + __riscv_vsseg3e8(dst_data + i * dst_step + j * 3, dst, vl); + } + } + } + + return CV_HAL_ERROR_OK; +} + +template +static inline int warpC4(int start, int end, const uchar *src_data, size_t src_step, int src_width, int src_height, uchar *dst_data, size_t dst_step, int dst_width, const double* M, int interpolation, int borderType, const double* borderValue) +{ + for (int i = start; i < end; i++) + { + int vl; + for (int j = 0; j < dst_width; j += vl) + { + vl = __riscv_vsetvl_e8mf2(dst_width - j); + auto access = [&](vint32m2_t ix, vint32m2_t iy, vuint8mf2_t& src0, vuint8mf2_t& src1, vuint8mf2_t& src2, vuint8mf2_t& src3) { + auto ux = __riscv_vreinterpret_v_i32m2_u32m2(__riscv_vmin(__riscv_vmax(ix, 0, vl), src_width - 1, vl)); + auto uy = __riscv_vreinterpret_v_i32m2_u32m2(__riscv_vmin(__riscv_vmax(iy, 0, vl), src_height - 1, vl)); + auto src = __riscv_vloxseg4ei32_v_u8mf2x4(src_data, __riscv_vmadd(uy, src_step, __riscv_vmul(ux, 4, vl), vl), vl); + src0 = __riscv_vget_v_u8mf2x4_u8mf2(src, 0); + src1 = __riscv_vget_v_u8mf2x4_u8mf2(src, 1); + src2 = __riscv_vget_v_u8mf2x4_u8mf2(src, 2); + src3 = __riscv_vget_v_u8mf2x4_u8mf2(src, 3); + if (borderType == CV_HAL_BORDER_CONSTANT) + { + auto mask = __riscv_vmor(__riscv_vmsne(ix, __riscv_vreinterpret_v_u32m2_i32m2(ux), vl), __riscv_vmsne(iy, __riscv_vreinterpret_v_u32m2_i32m2(uy), vl), vl); + src0 = __riscv_vmerge(src0, borderValue[0], mask, vl); + src1 = __riscv_vmerge(src1, borderValue[1], mask, vl); + src2 = __riscv_vmerge(src2, borderValue[2], mask, vl); + src3 = __riscv_vmerge(src3, borderValue[3], mask, vl); + } + }; + + auto id = __riscv_vfcvt_f(__riscv_vadd(__riscv_vid_v_u32m2(vl), j, vl), vl); + auto mx = __riscv_vfmadd(id, M[0], __riscv_vfmadd(__riscv_vfmv_v_f_f32m2(i, vl), M[1], __riscv_vfmv_v_f_f32m2(M[2], vl), vl), vl); + auto my = __riscv_vfmadd(id, M[3], __riscv_vfmadd(__riscv_vfmv_v_f_f32m2(i, vl), M[4], __riscv_vfmv_v_f_f32m2(M[5], vl), vl), vl); + if (perspective) + { + auto md = __riscv_vfrdiv(__riscv_vfmadd(id, M[6], __riscv_vfmadd(__riscv_vfmv_v_f_f32m2(i, vl), M[7], __riscv_vfmv_v_f_f32m2(M[8], vl), vl), vl), 1, vl); + mx = __riscv_vfmul(mx, md, vl); + my = __riscv_vfmul(my, md, vl); + } + + if (interpolation == CV_HAL_INTER_NEAREST) + { + auto ix = __riscv_vfcvt_x(mx, vl), iy = __riscv_vfcvt_x(my, vl); + vuint8mf2_t src0, src1, src2, src3; + access(ix, iy, src0, src1, src2, src3); + vuint8mf2x4_t dst{}; + dst = __riscv_vset_v_u8mf2_u8mf2x4(dst, 0, src0); + dst = __riscv_vset_v_u8mf2_u8mf2x4(dst, 1, src1); + dst = __riscv_vset_v_u8mf2_u8mf2x4(dst, 2, src2); + dst = __riscv_vset_v_u8mf2_u8mf2x4(dst, 3, src3); + __riscv_vsseg4e8(dst_data + i * dst_step + j * 4, dst, vl); + } + else + { + auto ix = __riscv_vfcvt_x(__riscv_vfmadd(mx, 1 << 10, __riscv_vfmv_v_f_f32m2(1 << 4, vl), vl), vl); + auto iy = __riscv_vfcvt_x(__riscv_vfmadd(my, 1 << 10, __riscv_vfmv_v_f_f32m2(1 << 4, vl), vl), vl); + auto ix0 = __riscv_vsra(ix, 10, vl), iy0 = __riscv_vsra(iy, 10, vl); + auto ix1 = __riscv_vadd(ix0, 1, vl), iy1 = __riscv_vadd(iy0, 1, vl); + + vuint32m2_t v00, v10, v20, v30; + vuint32m2_t v01, v11, v21, v31; + vuint32m2_t v02, v12, v22, v32; + vuint32m2_t v03, v13, v23, v33; + vuint8mf2_t src0, src1, src2, src3; + access(ix0, iy0, src0, src1, src2, src3); + v00 = __riscv_vzext_vf4(src0, vl); + v10 = __riscv_vzext_vf4(src1, vl); + v20 = __riscv_vzext_vf4(src2, vl); + v30 = __riscv_vzext_vf4(src3, vl); + access(ix1, iy0, src0, src1, src2, src3); + v01 = __riscv_vzext_vf4(src0, vl); + v11 = __riscv_vzext_vf4(src1, vl); + v21 = __riscv_vzext_vf4(src2, vl); + v31 = __riscv_vzext_vf4(src3, vl); + access(ix0, iy1, src0, src1, src2, src3); + v02 = __riscv_vzext_vf4(src0, vl); + v12 = __riscv_vzext_vf4(src1, vl); + v22 = __riscv_vzext_vf4(src2, vl); + v32 = __riscv_vzext_vf4(src3, vl); + access(ix1, iy1, src0, src1, src2, src3); + v03 = __riscv_vzext_vf4(src0, vl); + v13 = __riscv_vzext_vf4(src1, vl); + v23 = __riscv_vzext_vf4(src2, vl); + v33 = __riscv_vzext_vf4(src3, vl); + + auto rx = __riscv_vreinterpret_v_i32m2_u32m2(__riscv_vand(__riscv_vsra(ix, 5, vl), (1 << 5) - 1, vl)); + auto ry = __riscv_vreinterpret_v_i32m2_u32m2(__riscv_vand(__riscv_vsra(iy, 5, vl), (1 << 5) - 1, vl)); + v00 = __riscv_vmacc(__riscv_vmul(v00, 1 << 5, vl), rx, __riscv_vsub(v01, v00, vl), vl); + v02 = __riscv_vmacc(__riscv_vmul(v02, 1 << 5, vl), rx, __riscv_vsub(v03, v02, vl), vl); + v00 = __riscv_vmacc(__riscv_vmul(v00, 1 << 5, vl), ry, __riscv_vsub(v02, v00, vl), vl); + v10 = __riscv_vmacc(__riscv_vmul(v10, 1 << 5, vl), rx, __riscv_vsub(v11, v10, vl), vl); + v12 = __riscv_vmacc(__riscv_vmul(v12, 1 << 5, vl), rx, __riscv_vsub(v13, v12, vl), vl); + v10 = __riscv_vmacc(__riscv_vmul(v10, 1 << 5, vl), ry, __riscv_vsub(v12, v10, vl), vl); + v20 = __riscv_vmacc(__riscv_vmul(v20, 1 << 5, vl), rx, __riscv_vsub(v21, v20, vl), vl); + v22 = __riscv_vmacc(__riscv_vmul(v22, 1 << 5, vl), rx, __riscv_vsub(v23, v22, vl), vl); + v20 = __riscv_vmacc(__riscv_vmul(v20, 1 << 5, vl), ry, __riscv_vsub(v22, v20, vl), vl); + v30 = __riscv_vmacc(__riscv_vmul(v30, 1 << 5, vl), rx, __riscv_vsub(v31, v30, vl), vl); + v32 = __riscv_vmacc(__riscv_vmul(v32, 1 << 5, vl), rx, __riscv_vsub(v33, v32, vl), vl); + v30 = __riscv_vmacc(__riscv_vmul(v30, 1 << 5, vl), ry, __riscv_vsub(v32, v30, vl), vl); + vuint8mf2x4_t dst{}; + dst = __riscv_vset_v_u8mf2_u8mf2x4(dst, 0, __riscv_vnclipu(__riscv_vnclipu(v00, 10, __RISCV_VXRM_RNU, vl), 0, __RISCV_VXRM_RNU, vl)); + dst = __riscv_vset_v_u8mf2_u8mf2x4(dst, 1, __riscv_vnclipu(__riscv_vnclipu(v10, 10, __RISCV_VXRM_RNU, vl), 0, __RISCV_VXRM_RNU, vl)); + dst = __riscv_vset_v_u8mf2_u8mf2x4(dst, 2, __riscv_vnclipu(__riscv_vnclipu(v20, 10, __RISCV_VXRM_RNU, vl), 0, __RISCV_VXRM_RNU, vl)); + dst = __riscv_vset_v_u8mf2_u8mf2x4(dst, 3, __riscv_vnclipu(__riscv_vnclipu(v30, 10, __RISCV_VXRM_RNU, vl), 0, __RISCV_VXRM_RNU, vl)); + __riscv_vsseg4e8(dst_data + i * dst_step + j * 4, dst, vl); + } + } + } + + return CV_HAL_ERROR_OK; +} + +// the algorithm is copied from 3rdparty/carotene/src/warp_affine.cpp, +// in the function void CAROTENE_NS::warpAffineNearestNeighbor and void CAROTENE_NS::warpAffineLinear +inline int warpAffine(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 M[6], int interpolation, int borderType, const double borderValue[4]) +{ + if (src_type != CV_8UC1 && src_type != CV_8UC3 && src_type != CV_8UC4) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + if (borderType != CV_HAL_BORDER_CONSTANT && borderType != CV_HAL_BORDER_REPLICATE) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + if (interpolation != CV_HAL_INTER_NEAREST && interpolation != CV_HAL_INTER_LINEAR) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + + switch (src_type) + { + case CV_8UC1: + return remap::invoke(dst_width, dst_height, {warpC1}, src_data, src_step, src_width, src_height, dst_data, dst_step, dst_width, M, interpolation, borderType, borderValue); + case CV_8UC3: + return remap::invoke(dst_width, dst_height, {warpC3}, src_data, src_step, src_width, src_height, dst_data, dst_step, dst_width, M, interpolation, borderType, borderValue); + case CV_8UC4: + return remap::invoke(dst_width, dst_height, {warpC4}, src_data, src_step, src_width, src_height, dst_data, dst_step, dst_width, M, interpolation, borderType, borderValue); + } + + return CV_HAL_ERROR_NOT_IMPLEMENTED; +} + +// the algorithm is copied from 3rdparty/carotene/src/warp_perspective.cpp, +// in the function void CAROTENE_NS::warpPerspectiveNearestNeighbor and void CAROTENE_NS::warpPerspectiveLinear +inline int warpPerspective(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 M[9], int interpolation, int borderType, const double borderValue[4]) +{ + if (src_type != CV_8UC1 && src_type != CV_8UC3 && src_type != CV_8UC4) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + if (borderType != CV_HAL_BORDER_CONSTANT && borderType != CV_HAL_BORDER_REPLICATE) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + if (interpolation != CV_HAL_INTER_NEAREST && interpolation != CV_HAL_INTER_LINEAR) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + + switch (src_type) + { + case CV_8UC1: + return remap::invoke(dst_width, dst_height, {warpC1}, src_data, src_step, src_width, src_height, dst_data, dst_step, dst_width, M, interpolation, borderType, borderValue); + case CV_8UC3: + return remap::invoke(dst_width, dst_height, {warpC3}, src_data, src_step, src_width, src_height, dst_data, dst_step, dst_width, M, interpolation, borderType, borderValue); + case CV_8UC4: + return remap::invoke(dst_width, dst_height, {warpC4}, src_data, src_step, src_width, src_height, dst_data, dst_step, dst_width, M, interpolation, borderType, borderValue); + } + + return CV_HAL_ERROR_NOT_IMPLEMENTED; +} +} // cv::cv_hal_rvv::warp + +}} + +#endif diff --git a/modules/imgproc/src/hal_replacement.hpp b/modules/imgproc/src/hal_replacement.hpp index 26aa58e77e..645e9557ed 100644 --- a/modules/imgproc/src/hal_replacement.hpp +++ b/modules/imgproc/src/hal_replacement.hpp @@ -373,9 +373,58 @@ inline int hal_ni_remap32f(int src_type, const uchar *src_data, size_t src_step, float* mapx, size_t mapx_step, float* mapy, size_t mapy_step, int interpolation, int border_type, const double border_value[4]) { return CV_HAL_ERROR_NOT_IMPLEMENTED; } +/** + @brief hal_remap with floating point maps + @param src_type source and destination image type + @param src_data source image data + @param src_step source image step + @param src_width source image width + @param src_height source image height + @param dst_data destination image data + @param dst_step destination image step + @param dst_width destination image width + @param dst_height destination image height + @param map map for xy values + @param map_step map matrix step + @param interpolation interpolation mode (CV_HAL_INTER_NEAREST, ...) + @param border_type border processing mode (CV_HAL_BORDER_REFLECT, ...) + @param border_value values to use for CV_HAL_BORDER_CONSTANT mode + @sa cv::remap + */ +inline int hal_ni_remap32fc2(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, + float* map, size_t map_step, int interpolation, int border_type, const double border_value[4]) +{ return CV_HAL_ERROR_NOT_IMPLEMENTED; } +/** + @brief hal_remap with fixed-point maps + @param src_type source and destination image type + @param src_data source image data + @param src_step source image step + @param src_width source image width + @param src_height source image height + @param dst_data destination image data + @param dst_step destination image step + @param dst_width destination image width + @param dst_height destination image height + @param mapx map for x values + @param mapx_step mapx matrix step + @param mapy map for y values + @param mapy_step mapy matrix step + @param interpolation interpolation mode (CV_HAL_INTER_NEAREST, ...) + @param border_type border processing mode (CV_HAL_BORDER_REFLECT, ...) + @param border_value values to use for CV_HAL_BORDER_CONSTANT mode + @sa cv::remap + */ +inline int hal_ni_remap16s(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, + short* mapx, size_t mapx_step, ushort* mapy, size_t mapy_step, + int interpolation, int border_type, const double border_value[4]) +{ return CV_HAL_ERROR_NOT_IMPLEMENTED; } //! @cond IGNORED #define cv_hal_remap32f hal_ni_remap32f +#define cv_hal_remap32fc2 hal_ni_remap32fc2 +#define cv_hal_remap16s hal_ni_remap16s //! @endcond /** diff --git a/modules/imgproc/src/imgwarp.cpp b/modules/imgproc/src/imgwarp.cpp index 38f61333cf..f348860549 100644 --- a/modules/imgproc/src/imgwarp.cpp +++ b/modules/imgproc/src/imgwarp.cpp @@ -1720,6 +1720,16 @@ void cv::remap( InputArray _src, OutputArray _dst, CALL_HAL(remap32f, cv_hal_remap32f, src.type(), src.data, src.step, src.cols, src.rows, dst.data, dst.step, dst.cols, dst.rows, map1.ptr(), map1.step, map2.ptr(), map2.step, interpolation, borderType, borderValue.val); } + if ((map1.type() == CV_32FC2) && map2.empty()) + { + CALL_HAL(remap32fc2, cv_hal_remap32fc2, src.type(), src.data, src.step, src.cols, src.rows, dst.data, dst.step, dst.cols, dst.rows, + map1.ptr(), map1.step, interpolation, borderType, borderValue.val); + } + if ((map1.type() == CV_16SC2) && (map2.empty() || map2.type() == CV_16UC1)) + { + CALL_HAL(remap16s, cv_hal_remap16s, src.type(), src.data, src.step, src.cols, src.rows, dst.data, dst.step, dst.cols, dst.rows, + map1.ptr(), map1.step, map2.ptr(), map2.step, interpolation, borderType, borderValue.val); + } interpolation &= ~WARP_RELATIVE_MAP; if( interpolation == INTER_AREA ) From ae443a904b4cb30c7ef4c1310000fb5b6b281e3d Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Tue, 25 Mar 2025 12:33:58 +0300 Subject: [PATCH 43/94] Fixed JavaDoc generation for StereoBM. --- modules/calib3d/include/opencv2/calib3d.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/calib3d/include/opencv2/calib3d.hpp b/modules/calib3d/include/opencv2/calib3d.hpp index 31961a369a..8b87a3cb68 100644 --- a/modules/calib3d/include/opencv2/calib3d.hpp +++ b/modules/calib3d/include/opencv2/calib3d.hpp @@ -3347,7 +3347,6 @@ public: /** - * @class StereoBM * @brief Class for computing stereo correspondence using the block matching algorithm, introduced and contributed to OpenCV by K. Konolige. * @details This class implements a block matching algorithm for stereo correspondence, which is used to compute disparity maps from stereo image pairs. It provides methods to fine-tune parameters such as pre-filtering, texture thresholds, uniqueness ratios, and regions of interest (ROIs) to optimize performance and accuracy. */ From 68a595d88bb79e3ba9f8df5ac98f7da5794d5676 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Tue, 25 Mar 2025 07:57:03 +0300 Subject: [PATCH 44/94] Set C++ standard for all CUDA configurations. --- cmake/OpenCVDetectCUDA.cmake | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/cmake/OpenCVDetectCUDA.cmake b/cmake/OpenCVDetectCUDA.cmake index c5ce8762f2..c2179da804 100644 --- a/cmake/OpenCVDetectCUDA.cmake +++ b/cmake/OpenCVDetectCUDA.cmake @@ -152,14 +152,15 @@ macro(ocv_cuda_compile VAR) ocv_nvcc_flags() if(NOT " ${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_RELEASE} ${CMAKE_CXX_FLAGS_DEBUG} ${CUDA_NVCC_FLAGS}" MATCHES "-std=") - if(UNIX OR APPLE) - if(CUDA_VERSION VERSION_LESS "11.0") + if(CUDA_VERSION VERSION_LESS "11.0") + # Windows version does not support --std option + if(UNIX OR APPLE) list(APPEND CUDA_NVCC_FLAGS "--std=c++11") - elseif(CUDA_VERSION VERSION_LESS "12.8") - list(APPEND CUDA_NVCC_FLAGS "--std=c++14") - elseif(CUDA_VERSION VERSION_GREATER_EQUAL "12.8") - list(APPEND CUDA_NVCC_FLAGS "--std=c++17") endif() + elseif(CUDA_VERSION VERSION_LESS "12.8") + list(APPEND CUDA_NVCC_FLAGS "--std=c++14") + elseif(CUDA_VERSION VERSION_GREATER_EQUAL "12.8") + list(APPEND CUDA_NVCC_FLAGS "--std=c++17") endif() endif() From 8948faa394272f6167737ef084867f418cb8ab7d Mon Sep 17 00:00:00 2001 From: Kumataro Date: Tue, 25 Mar 2025 23:11:17 +0900 Subject: [PATCH 45/94] doc: unwrap promise-typed cv object --- doc/js_tutorials/js_assets/js_setup_usage.html | 3 ++- .../js_setup/js_setup/js_setup.markdown | 14 ++++---------- .../js_setup/js_usage/js_usage.markdown | 8 ++++++-- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/doc/js_tutorials/js_assets/js_setup_usage.html b/doc/js_tutorials/js_assets/js_setup_usage.html index 4f00dc3bd9..6c3bbba6ca 100644 --- a/doc/js_tutorials/js_assets/js_setup_usage.html +++ b/doc/js_tutorials/js_assets/js_setup_usage.html @@ -36,7 +36,8 @@ inputElement.addEventListener('change', (e) => { imgElement.src = URL.createObjectURL(e.target.files[0]); }, false); -imgElement.onload = function() { +imgElement.onload = async function() { + cv = (cv instanceof Promise) ? await cv : cv; let mat = cv.imread(imgElement); cv.imshow('canvasOutput', mat); mat.delete(); diff --git a/doc/js_tutorials/js_setup/js_setup/js_setup.markdown b/doc/js_tutorials/js_setup/js_setup/js_setup.markdown index 87a32a78cb..198cc74a24 100644 --- a/doc/js_tutorials/js_setup/js_setup/js_setup.markdown +++ b/doc/js_tutorials/js_setup/js_setup/js_setup.markdown @@ -73,6 +73,10 @@ Building OpenCV.js from Source --------------------------------------- -# To build `opencv.js`, execute python script `/platforms/js/build_js.py `. + The build script builds WebAssembly version by default(`--build_wasm` switch is kept by back-compatibility reason). + By default everything is bundled into one JavaScript file by `base64` encoding the WebAssembly code. For production + builds you can add `--disable_single_file` which will reduce total size by writing the WebAssembly code + to a dedicated `.wasm` file which the generated JavaScript file will automatically load. For example, to build in `build_js` directory: @code{.bash} @@ -82,16 +86,6 @@ Building OpenCV.js from Source @note It requires `python` and `cmake` installed in your development environment. --# The build script builds asm.js version by default. To build WebAssembly version, append `--build_wasm` switch. - By default everything is bundled into one JavaScript file by `base64` encoding the WebAssembly code. For production - builds you can add `--disable_single_file` which will reduce total size by writing the WebAssembly code - to a dedicated `.wasm` file which the generated JavaScript file will automatically load. - - For example, to build wasm version in `build_wasm` directory: - @code{.bash} - emcmake python ./opencv/platforms/js/build_js.py build_wasm --build_wasm - @endcode - -# [Optional] To build the OpenCV.js loader, append `--build_loader`. For example: diff --git a/doc/js_tutorials/js_setup/js_usage/js_usage.markdown b/doc/js_tutorials/js_setup/js_usage/js_usage.markdown index 4034977f08..ba124468f1 100644 --- a/doc/js_tutorials/js_setup/js_usage/js_usage.markdown +++ b/doc/js_tutorials/js_setup/js_usage/js_usage.markdown @@ -63,13 +63,16 @@ Example for asynchronous loading ### Use OpenCV.js Once `opencv.js` is ready, you can access OpenCV objects and functions through `cv` object. +The promise-typed `cv` object should be unwrap with `await` operator. +See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await . For example, you can create a cv.Mat from an image by cv.imread. @note Because image loading is asynchronous, you need to put cv.Mat creation inside the `onload` callback. @code{.js} -imgElement.onload = function() { +imgElement.onload = await function() { + cv = (cv instanceof Promise) ? await cv : cv; let mat = cv.imread(imgElement); } @endcode @@ -116,7 +119,8 @@ inputElement.addEventListener('change', (e) => { imgElement.src = URL.createObjectURL(e.target.files[0]); }, false); -imgElement.onload = function() { +imgElement.onload = async function() { + cv = (cv instanceof Promise) ? await cv : cv; let mat = cv.imread(imgElement); cv.imshow('canvasOutput', mat); mat.delete(); From 42a132088cfc2060e9ae816bbcf7ebfcab4f1de8 Mon Sep 17 00:00:00 2001 From: Vincent Rabaud Date: Wed, 26 Mar 2025 15:14:50 +0100 Subject: [PATCH 46/94] Merge pull request #27138 from vrabaud:lzw Fix heap buffer overflow and use after free in imgcodecs #27138 This fixes: - https://g-issues.oss-fuzz.com/issues/405243132 - https://g-issues.oss-fuzz.com/issues/405456349 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- modules/imgcodecs/src/grfmt_gif.cpp | 2 ++ modules/imgcodecs/src/grfmt_png.cpp | 51 +++++++++++++++++++---------- 2 files changed, 35 insertions(+), 18 deletions(-) diff --git a/modules/imgcodecs/src/grfmt_gif.cpp b/modules/imgcodecs/src/grfmt_gif.cpp index 74418fe6e3..4490e72309 100644 --- a/modules/imgcodecs/src/grfmt_gif.cpp +++ b/modules/imgcodecs/src/grfmt_gif.cpp @@ -392,6 +392,8 @@ bool GifDecoder::lzwDecode() { if (code < colorTableSize) { imgCodeStream[idx++] = (uchar)code; } else { + CV_LOG_WARNING(NULL, "Too long LZW length in GIF."); + CV_Assert(idx + lzwExtraTable[code].length <= width * height); for (int i = 0; i < lzwExtraTable[code].length - 1; i++) { imgCodeStream[idx++] = lzwExtraTable[code].prefix[i]; } diff --git a/modules/imgcodecs/src/grfmt_png.cpp b/modules/imgcodecs/src/grfmt_png.cpp index 1001ff6eec..7febc3153b 100644 --- a/modules/imgcodecs/src/grfmt_png.cpp +++ b/modules/imgcodecs/src/grfmt_png.cpp @@ -401,7 +401,7 @@ bool PngDecoder::readData( Mat& img ) Mat mat_cur = Mat::zeros(img.rows, img.cols, m_type); uint32_t id = 0; uint32_t j = 0; - uint32_t imagesize = m_width * m_height * mat_cur.channels(); + uint32_t imagesize = m_width * m_height * (uint32_t)mat_cur.elemSize(); m_is_IDAT_loaded = false; if (m_frame_no == 0) @@ -451,15 +451,26 @@ bool PngDecoder::readData( Mat& img ) delay_den = 100; m_animation.durations.push_back(cvRound(1000.*delay_num/delay_den)); - if (mat_cur.depth() == CV_16U && img.depth() == CV_8U) - mat_cur.convertTo(mat_cur, CV_8U, 1. / 255); - if (mat_cur.channels() == img.channels()) - mat_cur.copyTo(img); - else if (img.channels() == 1) - cvtColor(mat_cur, img, COLOR_BGRA2GRAY); - else if (img.channels() == 3) - cvtColor(mat_cur, img, COLOR_BGRA2BGR); + { + if (mat_cur.depth() == CV_16U && img.depth() == CV_8U) + mat_cur.convertTo(img, CV_8U, 1. / 255); + else + mat_cur.copyTo(img); + } + else + { + Mat mat_cur_scaled; + if (mat_cur.depth() == CV_16U && img.depth() == CV_8U) + mat_cur.convertTo(mat_cur_scaled, CV_8U, 1. / 255); + else + mat_cur_scaled = mat_cur; + + if (img.channels() == 1) + cvtColor(mat_cur_scaled, img, COLOR_BGRA2GRAY); + else if (img.channels() == 3) + cvtColor(mat_cur_scaled, img, COLOR_BGRA2BGR); + } if (dop != 2) { @@ -517,15 +528,19 @@ bool PngDecoder::readData( Mat& img ) delay_den = 100; m_animation.durations.push_back(cvRound(1000.*delay_num/delay_den)); - if (mat_cur.depth() == CV_16U && img.depth() == CV_8U) - mat_cur.convertTo(mat_cur, CV_8U, 1. / 255); - - if (mat_cur.channels() == img.channels()) - mat_cur.copyTo(img); - else if (img.channels() == 1) - cvtColor(mat_cur, img, COLOR_BGRA2GRAY); - else if (img.channels() == 3) - cvtColor(mat_cur, img, COLOR_BGRA2BGR); + if (mat_cur.depth() == CV_16U && img.depth() == CV_8U && mat_cur.channels() == img.channels()) + mat_cur.convertTo(img, CV_8U, 1. / 255); + else + { + if (mat_cur.depth() == CV_16U && img.depth() == CV_8U) + mat_cur.convertTo(mat_cur, CV_8U, 1. / 255); + if (mat_cur.channels() == img.channels()) + mat_cur.copyTo(img); + else if (img.channels() == 1) + cvtColor(mat_cur, img, COLOR_BGRA2GRAY); + else if (img.channels() == 3) + cvtColor(mat_cur, img, COLOR_BGRA2BGR); + } } else return false; From 3d5ab56a684b1c4980ae5f155cc3e64d69a47adb Mon Sep 17 00:00:00 2001 From: cudawarped <12133430+cudawarped@users.noreply.github.com> Date: Thu, 27 Mar 2025 16:40:01 +0200 Subject: [PATCH 47/94] Add comment for CMake 3.18+: if CMAKE_CUDA_ARCHITECTURES is empty enable_language(CUDA) sets it to the default architecture chosen by the compiler, to trigger the OpenCV custom CUDA architecture search an empty value needs to be respected see https://github.com/opencv/opencv/pull/25941. --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8fbfd0563d..7985623ffb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -684,6 +684,7 @@ if(ENABLE_CUDA_FIRST_CLASS_LANGUAGE) cmake_policy(SET CMP0092 NEW) # CMake 3.15+: leave warning flags out of default CMAKE__FLAGS flags. if(CMAKE_CUDA_COMPILER) + # CMake 3.18+: if CMAKE_CUDA_ARCHITECTURES is empty enable_language(CUDA) sets it to the default architecture chosen by the compiler, to trigger the OpenCV custom CUDA architecture search an empty value needs to be respected see https://github.com/opencv/opencv/pull/25941. if(CMAKE_CUDA_ARCHITECTURES) set(USER_DEFINED_CMAKE_CUDA_ARCHITECTURES TRUE) endif() From 6b4ef1dccd8b2d5f1727081f3eb53a1c4493a44a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=98ystein=20Walle?= Date: Fri, 28 Mar 2025 14:27:17 +0100 Subject: [PATCH 48/94] Fix closing of windows when using the Qt backend Notify the main GUI thread upon receiving a close event instead of when the windows is destroyed. Additionally there was a logic error in in GuiReceiver::isLastWindow() that is corrected. Fixes #6479 and #20822 --- modules/highgui/src/window_QT.cpp | 18 ++++++++++-------- modules/highgui/src/window_QT.h | 2 +- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/modules/highgui/src/window_QT.cpp b/modules/highgui/src/window_QT.cpp index c8957d9536..f7cd08bc88 100644 --- a/modules/highgui/src/window_QT.cpp +++ b/modules/highgui/src/window_QT.cpp @@ -859,7 +859,7 @@ void GuiReceiver::isLastWindow() delete guiMainThread;//delete global_control_panel too guiMainThread = NULL; - if (!doesExternalQAppExist) + if (doesExternalQAppExist) { qApp->quit(); } @@ -1739,13 +1739,6 @@ CvWindow::CvWindow(QString name, int arg2) } -CvWindow::~CvWindow() -{ - if (guiMainThread) - guiMainThread->isLastWindow(); -} - - void CvWindow::setMouseCallBack(CvMouseCallback callback, void* param) { myView->setMouseCallBack(callback, param); @@ -2259,6 +2252,15 @@ void CvWindow::keyPressEvent(QKeyEvent *evnt) } +void CvWindow::closeEvent(QCloseEvent* evnt) +{ + QWidget::closeEvent(evnt); + + if (guiMainThread) + guiMainThread->isLastWindow(); +} + + void CvWindow::icvLoadControlPanel() { QSettings settings("OpenCV2", QFileInfo(QApplication::applicationFilePath()).fileName() + " control panel"); diff --git a/modules/highgui/src/window_QT.h b/modules/highgui/src/window_QT.h index b93b9ba597..1224ba9d32 100644 --- a/modules/highgui/src/window_QT.h +++ b/modules/highgui/src/window_QT.h @@ -298,7 +298,6 @@ class CvWindow : public CvWinModel Q_OBJECT public: CvWindow(QString arg2, int flag = CV_WINDOW_NORMAL); - ~CvWindow(); void setMouseCallBack(CvMouseCallback m, void* param); @@ -349,6 +348,7 @@ public: protected: virtual void keyPressEvent(QKeyEvent* event) CV_OVERRIDE; + virtual void closeEvent(QCloseEvent* event) CV_OVERRIDE; private: From afc7c0a89c75968a6d32d14a22a9a62b2fb2d21c Mon Sep 17 00:00:00 2001 From: Ryan Wong Date: Sun, 30 Mar 2025 06:17:07 -0700 Subject: [PATCH 49/94] Merge pull request #27154 from kinchungwong:logging_callback_simple_c User-defined logger callback, C-style. #27154 This is a competing PR, an alternative to #27140 Both functions accept C-style pointer to static functions. Both functions allow restoring the OpenCV built-in implementation by passing in a nullptr. - replaceWriteLogMessage - replaceWriteLogMessageEx This implementation is not compatible with C++ log handler objects. This implementation has minimal thread safety, in the sense that the function pointer are stored and read atomically. But otherwise, the user-defined static functions must accept calls at all times, even after having been deregistered, because some log calls may have started before deregistering. ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [ ] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- .../include/opencv2/core/utils/logger.hpp | 38 ++++++ modules/core/src/logger.cpp | 41 +++++++ modules/core/test/test_logger_replace.cpp | 115 ++++++++++++++++++ 3 files changed, 194 insertions(+) create mode 100644 modules/core/test/test_logger_replace.cpp diff --git a/modules/core/include/opencv2/core/utils/logger.hpp b/modules/core/include/opencv2/core/utils/logger.hpp index accb860ada..e5bf455125 100644 --- a/modules/core/include/opencv2/core/utils/logger.hpp +++ b/modules/core/include/opencv2/core/utils/logger.hpp @@ -43,6 +43,44 @@ CV_EXPORTS void writeLogMessage(LogLevel logLevel, const char* message); /** Write log message */ CV_EXPORTS void writeLogMessageEx(LogLevel logLevel, const char* tag, const char* file, int line, const char* func, const char* message); +/** + * @brief Function pointer type for writeLogMessage. Used by replaceWriteLogMessage. + */ +typedef void (*WriteLogMessageFuncType)(LogLevel, const char*); + +/** + * @brief Function pointer type for writeLogMessageEx. Used by replaceWriteLogMessageEx. + */ +typedef void (*WriteLogMessageExFuncType)(LogLevel, const char*, const char*, int, const char*, const char*); + +/** + * @brief Replaces the OpenCV writeLogMessage function with a user-defined function. + * @note The user-defined function must have the same signature as writeLogMessage. + * @note The user-defined function must accept arguments that can be potentially null. + * @note The user-defined function must be thread-safe, as OpenCV logging may be called + * from multiple threads. + * @note The user-defined function must not perform any action that can trigger + * deadlocks or infinite loop. Many OpenCV functions are not re-entrant. + * @note Once replaced, logs will not go through the OpenCV writeLogMessage function. + * @note To restore, call this function with a nullptr. + */ +CV_EXPORTS void replaceWriteLogMessage(WriteLogMessageFuncType f); + +/** + * @brief Replaces the OpenCV writeLogMessageEx function with a user-defined function. + * @note The user-defined function must have the same signature as writeLogMessage. + * @note The user-defined function must accept arguments that can be potentially null. + * @note The user-defined function must be thread-safe, as OpenCV logging may be called + * from multiple threads. + * @note The user-defined function must not perform any action that can trigger + * deadlocks or infinite loop. Many OpenCV functions are not re-entrant. + * @note Once replaced, logs will not go through any of the OpenCV logging functions + * such as writeLogMessage or writeLogMessageEx, until their respective restore + * methods are called. + * @note To restore, call this function with a nullptr. + */ +CV_EXPORTS void replaceWriteLogMessageEx(WriteLogMessageExFuncType f); + } // namespace struct LogTagAuto diff --git a/modules/core/src/logger.cpp b/modules/core/src/logger.cpp index 7e3f8aa29d..32183a59a0 100644 --- a/modules/core/src/logger.cpp +++ b/modules/core/src/logger.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #ifdef __ANDROID__ # include @@ -181,6 +182,12 @@ LogLevel getLogLevel() namespace internal { +namespace //unnamed +{ + std::atomic stc_userWriteLogMessageFunc{}; + std::atomic stc_userWriteLogMessageExFunc{}; +} //unnamed + static int getShowTimestampMode() { static bool param_timestamp_enable = utils::getConfigurationParameterBool("OPENCV_LOG_TIMESTAMP", true); @@ -190,6 +197,13 @@ static int getShowTimestampMode() void writeLogMessage(LogLevel logLevel, const char* message) { + WriteLogMessageFuncType userFunc = stc_userWriteLogMessageFunc.load(); + if (userFunc && userFunc != writeLogMessage) + { + (*userFunc)(logLevel, message); + return; + } + const int threadID = cv::utils::getThreadID(); std::string message_id; @@ -230,7 +244,9 @@ void writeLogMessage(LogLevel logLevel, const char* message) std::ostream* out = (logLevel <= LOG_LEVEL_WARNING) ? &std::cerr : &std::cout; (*out) << ss.str(); if (logLevel <= LOG_LEVEL_WARNING) + { (*out) << std::flush; + } } static const char* stripSourceFilePathPrefix(const char* file) @@ -252,6 +268,13 @@ static const char* stripSourceFilePathPrefix(const char* file) void writeLogMessageEx(LogLevel logLevel, const char* tag, const char* file, int line, const char* func, const char* message) { + WriteLogMessageExFuncType userFunc = stc_userWriteLogMessageExFunc.load(); + if (userFunc && userFunc != writeLogMessageEx) + { + (*userFunc)(logLevel, tag, file, line, func, message); + return; + } + std::ostringstream strm; if (tag) { @@ -274,6 +297,24 @@ void writeLogMessageEx(LogLevel logLevel, const char* tag, const char* file, int writeLogMessage(logLevel, strm.str().c_str()); } +void replaceWriteLogMessage(WriteLogMessageFuncType f) +{ + if (f == writeLogMessage) + { + f = nullptr; + } + stc_userWriteLogMessageFunc.store(f); +} + +void replaceWriteLogMessageEx(WriteLogMessageExFuncType f) +{ + if (f == writeLogMessageEx) + { + f = nullptr; + } + stc_userWriteLogMessageExFunc.store(f); +} + } // namespace }}} // namespace diff --git a/modules/core/test/test_logger_replace.cpp b/modules/core/test/test_logger_replace.cpp new file mode 100644 index 0000000000..d3c4a0308d --- /dev/null +++ b/modules/core/test/test_logger_replace.cpp @@ -0,0 +1,115 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#include "test_precomp.hpp" +#include +#include + +namespace opencv_test { +namespace { + +using namespace cv::utils::logging; +using namespace cv::utils::logging::internal; + +TEST(Core_Logger_Replace, WriteLogMessageRestoreCallWithNullOk) +{ + replaceWriteLogMessage(nullptr); + writeLogMessage(cv::utils::logging::LOG_LEVEL_DEBUG, "msg"); + SUCCEED(); +} + +TEST(Core_Logger_Replace, WriteLogMessageExRestoreCallWithNullOk) +{ + replaceWriteLogMessageEx(nullptr); + writeLogMessageEx(cv::utils::logging::LOG_LEVEL_DEBUG, "tag", "file", 1000, "func", "msg"); + SUCCEED(); +} + +std::atomic& getCallFlagger() +{ + static std::atomic callFlagger(0); + return callFlagger; +} + +std::atomic& getCallCounter() +{ + static std::atomic callCounter(0); + return callCounter; +} + +void myWriteLogMessage(LogLevel, const char*) +{ + getCallFlagger().fetch_or(1024u); + getCallCounter().fetch_add(1u); +} + +void myWriteLogMessageEx(LogLevel, const char*, const char*, int, const char*, const char*) +{ + getCallFlagger().fetch_or(2048u); + getCallCounter().fetch_add(1u); +} + +TEST(Core_Logger_Replace, WriteLogMessageReplaceRestore) +{ + uint32_t step_0 = getCallCounter().load(); + writeLogMessage(cv::utils::logging::LOG_LEVEL_DEBUG, "msg"); + uint32_t step_1 = getCallCounter().load(); + EXPECT_EQ(step_0, step_1); + replaceWriteLogMessage(nullptr); + uint32_t step_2 = getCallCounter().load(); + EXPECT_EQ(step_1, step_2); + writeLogMessage(cv::utils::logging::LOG_LEVEL_DEBUG, "msg"); + uint32_t step_3 = getCallCounter().load(); + EXPECT_EQ(step_2, step_3); + replaceWriteLogMessage(myWriteLogMessage); + uint32_t step_4 = getCallCounter().load(); + EXPECT_EQ(step_3, step_4); + writeLogMessage(cv::utils::logging::LOG_LEVEL_DEBUG, "msg"); + uint32_t step_5 = getCallCounter().load(); + EXPECT_EQ(step_4 + 1, step_5); + writeLogMessage(cv::utils::logging::LOG_LEVEL_DEBUG, "msg"); + uint32_t step_6 = getCallCounter().load(); + EXPECT_EQ(step_5 + 1, step_6); + replaceWriteLogMessage(nullptr); + uint32_t step_7 = getCallCounter().load(); + EXPECT_EQ(step_6, step_7); + writeLogMessage(cv::utils::logging::LOG_LEVEL_DEBUG, "msg"); + uint32_t step_8 = getCallCounter().load(); + EXPECT_EQ(step_7, step_8); + uint32_t flags = getCallFlagger().load(); + EXPECT_NE(flags & 1024u, 0u); +} + +TEST(Core_Logger_Replace, WriteLogMessageExReplaceRestore) +{ + uint32_t step_0 = getCallCounter().load(); + writeLogMessageEx(cv::utils::logging::LOG_LEVEL_DEBUG, "tag", "file", 0, "func", "msg"); + uint32_t step_1 = getCallCounter().load(); + EXPECT_EQ(step_0, step_1); + replaceWriteLogMessageEx(nullptr); + uint32_t step_2 = getCallCounter().load(); + EXPECT_EQ(step_1, step_2); + writeLogMessageEx(cv::utils::logging::LOG_LEVEL_DEBUG, "tag", "file", 0, "func", "msg"); + uint32_t step_3 = getCallCounter().load(); + EXPECT_EQ(step_2, step_3); + replaceWriteLogMessageEx(myWriteLogMessageEx); + uint32_t step_4 = getCallCounter().load(); + EXPECT_EQ(step_3, step_4); + writeLogMessageEx(cv::utils::logging::LOG_LEVEL_DEBUG, "tag", "file", 0, "func", "msg"); + uint32_t step_5 = getCallCounter().load(); + EXPECT_EQ(step_4 + 1, step_5); + writeLogMessageEx(cv::utils::logging::LOG_LEVEL_DEBUG, "tag", "file", 0, "func", "msg"); + uint32_t step_6 = getCallCounter().load(); + EXPECT_EQ(step_5 + 1, step_6); + replaceWriteLogMessageEx(nullptr); + uint32_t step_7 = getCallCounter().load(); + EXPECT_EQ(step_6, step_7); + writeLogMessageEx(cv::utils::logging::LOG_LEVEL_DEBUG, "tag", "file", 0, "func", "msg"); + uint32_t step_8 = getCallCounter().load(); + EXPECT_EQ(step_7, step_8); + uint32_t flags = getCallFlagger().load(); + EXPECT_NE(flags & 2048u, 0u); +} + +}} // namespace \ No newline at end of file From 289884adc59122251bf30134912d7b3d37f947a8 Mon Sep 17 00:00:00 2001 From: MaximSmolskiy Date: Sun, 30 Mar 2025 17:00:11 +0300 Subject: [PATCH 50/94] More elegant skipping SOLVEPNP_IPPE* methods in non-planar accuracy tests for solvePnP* --- modules/calib3d/test/test_solvepnp_ransac.cpp | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/modules/calib3d/test/test_solvepnp_ransac.cpp b/modules/calib3d/test/test_solvepnp_ransac.cpp index 99fa451ef5..66412bcfca 100644 --- a/modules/calib3d/test/test_solvepnp_ransac.cpp +++ b/modules/calib3d/test/test_solvepnp_ransac.cpp @@ -308,11 +308,6 @@ protected: virtual bool runTest(RNG& rng, int mode, int method, const vector& points, double& errorTrans, double& errorRot) { - if ((!planar && method == SOLVEPNP_IPPE) || method == SOLVEPNP_IPPE_SQUARE) - { - return true; - } - Mat rvec, tvec; vector inliers; Mat trueRvec, trueTvec; @@ -383,6 +378,22 @@ protected: { for (int method = 0; method < SOLVEPNP_MAX_COUNT; method++) { + // SOLVEPNP_IPPE need planar object + if (!planar && method == SOLVEPNP_IPPE) + { + cout << "mode: " << printMode(mode) << ", method: " << printMethod(method) << " -> " + << "Skip for non-planar object" << endl; + continue; + } + + // SOLVEPNP_IPPE_SQUARE need planar tag object + if (!planarTag && method == SOLVEPNP_IPPE_SQUARE) + { + cout << "mode: " << printMode(mode) << ", method: " << printMethod(method) << " -> " + << "Skip for non-planar tag object" << endl; + continue; + } + //To get the same input for each methods RNG rngCopy = rng; std::vector vec_errorTrans, vec_errorRot; @@ -486,15 +497,6 @@ public: protected: virtual bool runTest(RNG& rng, int mode, int method, const vector& points, double& errorTrans, double& errorRot) { - if ((!planar && (method == SOLVEPNP_IPPE || method == SOLVEPNP_IPPE_SQUARE)) || - (!planarTag && method == SOLVEPNP_IPPE_SQUARE)) - { - errorTrans = -1; - errorRot = -1; - //SOLVEPNP_IPPE and SOLVEPNP_IPPE_SQUARE need planar object - return true; - } - //Tune thresholds... double epsilon_trans[SOLVEPNP_MAX_COUNT]; memcpy(epsilon_trans, eps, SOLVEPNP_MAX_COUNT * sizeof(*epsilon_trans)); From 14e1f6ce96d0479dbc28440ccea1b249803b2f44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=A9=E9=9F=B3=E3=81=82=E3=82=81?= Date: Mon, 31 Mar 2025 13:19:18 +0800 Subject: [PATCH 51/94] Merge pull request #27160 from amane-ame:resize_hal_rvv Add RISC-V HAL implementation for cv::resize #27160 This patch implements `cv_hal_resize` using native intrinsics, optimizing the performance of `cv::resize` for `CV_INTER_NEAREST/CV_INTER_NEAREST_EXACT/CV_INTER_LINEAR/CV_INTER_LINEAR_EXACT/CV_INTER_AREA` modes. Tested on MUSE-PI (Spacemit X60) for both gcc 14.2 and clang 20.1. ``` $ ./opencv_test_imgproc --gtest_filter="*Resize*:*resize*" $ ./opencv_perf_imgproc --gtest_filter="*Resize*:*resize*" --perf_min_samples=300 --perf_force_samples=300 ``` View the full perf table here: [hal_rvv_resize.pdf](https://github.com/user-attachments/files/19480756/hal_rvv_resize.pdf) ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [ ] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- 3rdparty/hal_rvv/hal_rvv.hpp | 1 + 3rdparty/hal_rvv/hal_rvv_1p0/filter.hpp | 28 +- 3rdparty/hal_rvv/hal_rvv_1p0/norm_diff.hpp | 2 +- 3rdparty/hal_rvv/hal_rvv_1p0/resize.hpp | 1006 ++++++++++++++++++++ 4 files changed, 1010 insertions(+), 27 deletions(-) create mode 100644 3rdparty/hal_rvv/hal_rvv_1p0/resize.hpp diff --git a/3rdparty/hal_rvv/hal_rvv.hpp b/3rdparty/hal_rvv/hal_rvv.hpp index 4a10906a33..c04d3b9899 100644 --- a/3rdparty/hal_rvv/hal_rvv.hpp +++ b/3rdparty/hal_rvv/hal_rvv.hpp @@ -53,6 +53,7 @@ #include "hal_rvv_1p0/warp.hpp" // imgproc #include "hal_rvv_1p0/thresh.hpp" // imgproc #include "hal_rvv_1p0/histogram.hpp" // imgproc +#include "hal_rvv_1p0/resize.hpp" // imgproc #endif #endif diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/filter.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/filter.hpp index 97e6edd1a9..85949137e3 100644 --- a/3rdparty/hal_rvv/hal_rvv_1p0/filter.hpp +++ b/3rdparty/hal_rvv/hal_rvv_1p0/filter.hpp @@ -521,16 +521,7 @@ inline int sepFilter(cvhalFilter2D *context, uchar* src_data, size_t src_step, u uchar* _dst_data = dst_data; size_t _dst_step = dst_step; - size_t size = sizeof(char); - switch (data->dst_type) - { - case CV_16SC1: - size = sizeof(short); - break; - case CV_32FC1: - size = sizeof(float); - break; - } + const size_t size = CV_ELEM_SIZE(data->dst_type); std::vector dst; if (src_data == _dst_data) { @@ -2136,22 +2127,7 @@ inline int boxFilter(const uchar* src_data, size_t src_step, uchar* dst_data, si uchar* _dst_data = dst_data; size_t _dst_step = dst_step; - size_t size = cn; - switch (dst_depth) - { - case CV_8U: - case CV_8S: - size *= sizeof(char); - break; - case CV_16U: - case CV_16S: - size *= sizeof(short); - break; - case CV_32F: - case CV_32S: - size *= sizeof(float); - break; - } + const size_t size = CV_ELEM_SIZE(dst_type); std::vector dst; if (src_data == _dst_data) { diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/norm_diff.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/norm_diff.hpp index d70a50a987..1bc0f31075 100644 --- a/3rdparty/hal_rvv/hal_rvv_1p0/norm_diff.hpp +++ b/3rdparty/hal_rvv/hal_rvv_1p0/norm_diff.hpp @@ -1121,7 +1121,7 @@ inline int normDiff(const uchar* src1, size_t src1_step, const uchar* src2, size bool src_continuous = (src1_step == width * elem_size_tab[depth] * cn || (src1_step != width * elem_size_tab[depth] * cn && height == 1)); src_continuous &= (src2_step == width * elem_size_tab[depth] * cn || (src2_step != width * elem_size_tab[depth] * cn && height == 1)); - bool mask_continuous = (mask_step == width); + bool mask_continuous = (mask_step == static_cast(width)); size_t nplanes = 1; size_t size = width * height; if ((mask && (!src_continuous || !mask_continuous)) || !src_continuous) { diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/resize.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/resize.hpp new file mode 100644 index 0000000000..d18db5f058 --- /dev/null +++ b/3rdparty/hal_rvv/hal_rvv_1p0/resize.hpp @@ -0,0 +1,1006 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +// Copyright (C) 2025, Institute of Software, Chinese Academy of Sciences. + +#ifndef OPENCV_HAL_RVV_RESIZE_HPP_INCLUDED +#define OPENCV_HAL_RVV_RESIZE_HPP_INCLUDED + +#include +#include + +namespace cv { namespace cv_hal_rvv { + +namespace resize { +#undef cv_hal_resize +#define cv_hal_resize cv::cv_hal_rvv::resize::resize + +class ResizeInvoker : public ParallelLoopBody +{ +public: + template + ResizeInvoker(std::function _func, Args&&... args) + { + func = std::bind(_func, std::placeholders::_1, std::placeholders::_2, std::forward(args)...); + } + + virtual void operator()(const Range& range) const override + { + func(range.start, range.end); + } + +private: + std::function func; +}; + +template +static inline int invoke(int height, std::function func, Args&&... args) +{ + cv::parallel_for_(Range(1, height), ResizeInvoker(func, std::forward(args)...), cv::getNumThreads()); + return func(0, 1, std::forward(args)...); +} + +template +static inline int resizeNN(int start, int end, const uchar *src_data, size_t src_step, int src_height, uchar *dst_data, size_t dst_step, int dst_width, int dst_height, double scale_y, int interpolation, const ushort* x_ofs) +{ + const int ify = ((src_height << 16) + dst_height / 2) / dst_height; + const int ify0 = ify / 2 - src_height % 2; + + for (int i = start; i < end; i++) + { + int y_ofs = interpolation == CV_HAL_INTER_NEAREST ? static_cast(std::floor(i * scale_y)) : (ify * i + ify0) >> 16; + y_ofs = std::min(y_ofs, src_height - 1); + + int vl; + switch (cn) + { + case 1: + for (int j = 0; j < dst_width; j += vl) + { + vl = __riscv_vsetvl_e8m4(dst_width - j); + auto ptr = __riscv_vle16_v_u16m8(x_ofs + j, vl); + auto src = __riscv_vloxei16_v_u8m4(src_data + y_ofs * src_step, ptr, vl); + __riscv_vse8(dst_data + i * dst_step + j, src, vl); + } + break; + case 2: + for (int j = 0; j < dst_width; j += vl) + { + vl = __riscv_vsetvl_e8m4(dst_width - j); + auto ptr = __riscv_vle16_v_u16m8(x_ofs + j, vl); + auto src = __riscv_vloxei16_v_u16m8(reinterpret_cast(src_data + y_ofs * src_step), ptr, vl); + __riscv_vse16(reinterpret_cast(dst_data + i * dst_step) + j, src, vl); + } + break; + case 3: + for (int j = 0; j < dst_width; j += vl) + { + vl = __riscv_vsetvl_e8m2(dst_width - j); + auto ptr = __riscv_vle16_v_u16m4(x_ofs + j, vl); + auto src = __riscv_vloxseg3ei16_v_u8m2x3(src_data + y_ofs * src_step, ptr, vl); + __riscv_vsseg3e8(dst_data + i * dst_step + j * 3, src, vl); + } + break; + case 4: + for (int j = 0; j < dst_width; j += vl) + { + vl = __riscv_vsetvl_e8m2(dst_width - j); + auto ptr = __riscv_vle16_v_u16m4(x_ofs + j, vl); + auto src = __riscv_vloxei16_v_u32m8(reinterpret_cast(src_data + y_ofs * src_step), ptr, vl); + __riscv_vse32(reinterpret_cast(dst_data + i * dst_step) + j, src, vl); + } + break; + default: + return CV_HAL_ERROR_NOT_IMPLEMENTED; + } + } + + return CV_HAL_ERROR_OK; +} + +template struct rvv; +template<> struct rvv +{ + static inline vfloat32m4_t vcvt0(vuint8m1_t a, size_t b) { return __riscv_vfcvt_f(__riscv_vzext_vf4(a, b), b); } + static inline vuint8m1_t vcvt1(vfloat32m4_t a, size_t b) { return __riscv_vnclipu(__riscv_vfncvt_xu(a, b), 0, __RISCV_VXRM_RNU, b); } + static inline vuint8m1_t vloxei(const uchar* a, vuint16m2_t b, size_t c) { return __riscv_vloxei16_v_u8m1(a, b, c); } + static inline void vloxseg2ei(const uchar* a, vuint16m2_t b, size_t c, vuint8m1_t& x, vuint8m1_t& y) { auto src = __riscv_vloxseg2ei16_v_u8m1x2(a, b, c); x = __riscv_vget_v_u8m1x2_u8m1(src, 0); y = __riscv_vget_v_u8m1x2_u8m1(src, 1); } + static inline void vloxseg3ei(const uchar* a, vuint16m2_t b, size_t c, vuint8m1_t& x, vuint8m1_t& y, vuint8m1_t& z) { auto src = __riscv_vloxseg3ei16_v_u8m1x3(a, b, c); x = __riscv_vget_v_u8m1x3_u8m1(src, 0); y = __riscv_vget_v_u8m1x3_u8m1(src, 1); z = __riscv_vget_v_u8m1x3_u8m1(src, 2); } + static inline void vloxseg4ei(const uchar* a, vuint16m2_t b, size_t c, vuint8m1_t& x, vuint8m1_t& y, vuint8m1_t& z, vuint8m1_t& w) { auto src = __riscv_vloxseg4ei16_v_u8m1x4(a, b, c); x = __riscv_vget_v_u8m1x4_u8m1(src, 0); y = __riscv_vget_v_u8m1x4_u8m1(src, 1); z = __riscv_vget_v_u8m1x4_u8m1(src, 2); w = __riscv_vget_v_u8m1x4_u8m1(src, 3); } + static inline void vsseg2e(uchar* a, size_t b, vuint8m1_t x, vuint8m1_t y) { vuint8m1x2_t dst{}; dst = __riscv_vset_v_u8m1_u8m1x2(dst, 0, x); dst = __riscv_vset_v_u8m1_u8m1x2(dst, 1, y); __riscv_vsseg2e8(a, dst, b); } + static inline void vsseg3e(uchar* a, size_t b, vuint8m1_t x, vuint8m1_t y, vuint8m1_t z) { vuint8m1x3_t dst{}; dst = __riscv_vset_v_u8m1_u8m1x3(dst, 0, x); dst = __riscv_vset_v_u8m1_u8m1x3(dst, 1, y); dst = __riscv_vset_v_u8m1_u8m1x3(dst, 2, z); __riscv_vsseg3e8(a, dst, b); } + static inline void vsseg4e(uchar* a, size_t b, vuint8m1_t x, vuint8m1_t y, vuint8m1_t z, vuint8m1_t w) { vuint8m1x4_t dst{}; dst = __riscv_vset_v_u8m1_u8m1x4(dst, 0, x); dst = __riscv_vset_v_u8m1_u8m1x4(dst, 1, y); dst = __riscv_vset_v_u8m1_u8m1x4(dst, 2, z); dst = __riscv_vset_v_u8m1_u8m1x4(dst, 3, w); __riscv_vsseg4e8(a, dst, b); } + + static inline void vlseg2e(const uchar* a, size_t b, vuint8m1_t& x, vuint8m1_t& y) { auto src = __riscv_vlseg2e8_v_u8m1x2(a, b); x = __riscv_vget_v_u8m1x2_u8m1(src, 0); y = __riscv_vget_v_u8m1x2_u8m1(src, 1); } + static inline void vlsseg2e(const uchar* a, ptrdiff_t b, size_t c, vuint8m1_t& x, vuint8m1_t& y) { auto src = __riscv_vlsseg2e8_v_u8m1x2(a, b, c); x = __riscv_vget_v_u8m1x2_u8m1(src, 0); y = __riscv_vget_v_u8m1x2_u8m1(src, 1); } + static inline void vlsseg3e(const uchar* a, ptrdiff_t b, size_t c, vuint8m1_t& x, vuint8m1_t& y, vuint8m1_t& z) { auto src = __riscv_vlsseg3e8_v_u8m1x3(a, b, c); x = __riscv_vget_v_u8m1x3_u8m1(src, 0); y = __riscv_vget_v_u8m1x3_u8m1(src, 1); z = __riscv_vget_v_u8m1x3_u8m1(src, 2); } + static inline void vlsseg4e(const uchar* a, ptrdiff_t b, size_t c, vuint8m1_t& x, vuint8m1_t& y, vuint8m1_t& z, vuint8m1_t& w) { auto src = __riscv_vlsseg4e8_v_u8m1x4(a, b, c); x = __riscv_vget_v_u8m1x4_u8m1(src, 0); y = __riscv_vget_v_u8m1x4_u8m1(src, 1); z = __riscv_vget_v_u8m1x4_u8m1(src, 2); w = __riscv_vget_v_u8m1x4_u8m1(src, 3); } +}; +template<> struct rvv +{ + static inline vfloat32m4_t vcvt0(vuint16m2_t a, size_t b) { return __riscv_vfwcvt_f(a, b); } + static inline vuint16m2_t vcvt1(vfloat32m4_t a, size_t b) { return __riscv_vfncvt_xu(a, b); } + static inline vuint16m2_t vloxei(const ushort* a, vuint16m2_t b, size_t c) { return __riscv_vloxei16_v_u16m2(a, b, c); } + static inline void vloxseg2ei(const ushort* a, vuint16m2_t b, size_t c, vuint16m2_t& x, vuint16m2_t& y) { auto src = __riscv_vloxseg2ei16_v_u16m2x2(a, b, c); x = __riscv_vget_v_u16m2x2_u16m2(src, 0); y = __riscv_vget_v_u16m2x2_u16m2(src, 1); } + static inline void vloxseg3ei(const ushort* a, vuint16m2_t b, size_t c, vuint16m2_t& x, vuint16m2_t& y, vuint16m2_t& z) { auto src = __riscv_vloxseg3ei16_v_u16m2x3(a, b, c); x = __riscv_vget_v_u16m2x3_u16m2(src, 0); y = __riscv_vget_v_u16m2x3_u16m2(src, 1); z = __riscv_vget_v_u16m2x3_u16m2(src, 2); } + static inline void vloxseg4ei(const ushort* a, vuint16m2_t b, size_t c, vuint16m2_t& x, vuint16m2_t& y, vuint16m2_t& z, vuint16m2_t& w) { auto src = __riscv_vloxseg4ei16_v_u16m2x4(a, b, c); x = __riscv_vget_v_u16m2x4_u16m2(src, 0); y = __riscv_vget_v_u16m2x4_u16m2(src, 1); z = __riscv_vget_v_u16m2x4_u16m2(src, 2); w = __riscv_vget_v_u16m2x4_u16m2(src, 3); } + static inline void vsseg2e(ushort* a, size_t b, vuint16m2_t x, vuint16m2_t y) { vuint16m2x2_t dst{}; dst = __riscv_vset_v_u16m2_u16m2x2(dst, 0, x); dst = __riscv_vset_v_u16m2_u16m2x2(dst, 1, y); __riscv_vsseg2e16(a, dst, b); } + static inline void vsseg3e(ushort* a, size_t b, vuint16m2_t x, vuint16m2_t y, vuint16m2_t z) { vuint16m2x3_t dst{}; dst = __riscv_vset_v_u16m2_u16m2x3(dst, 0, x); dst = __riscv_vset_v_u16m2_u16m2x3(dst, 1, y); dst = __riscv_vset_v_u16m2_u16m2x3(dst, 2, z); __riscv_vsseg3e16(a, dst, b); } + static inline void vsseg4e(ushort* a, size_t b, vuint16m2_t x, vuint16m2_t y, vuint16m2_t z, vuint16m2_t w) { vuint16m2x4_t dst{}; dst = __riscv_vset_v_u16m2_u16m2x4(dst, 0, x); dst = __riscv_vset_v_u16m2_u16m2x4(dst, 1, y); dst = __riscv_vset_v_u16m2_u16m2x4(dst, 2, z); dst = __riscv_vset_v_u16m2_u16m2x4(dst, 3, w); __riscv_vsseg4e16(a, dst, b); } + + static inline void vlseg2e(const ushort* a, size_t b, vuint16m2_t& x, vuint16m2_t& y) { auto src = __riscv_vlseg2e16_v_u16m2x2(a, b); x = __riscv_vget_v_u16m2x2_u16m2(src, 0); y = __riscv_vget_v_u16m2x2_u16m2(src, 1); } + static inline void vlsseg2e(const ushort* a, ptrdiff_t b, size_t c, vuint16m2_t& x, vuint16m2_t& y) { auto src = __riscv_vlsseg2e16_v_u16m2x2(a, b, c); x = __riscv_vget_v_u16m2x2_u16m2(src, 0); y = __riscv_vget_v_u16m2x2_u16m2(src, 1); } + static inline void vlsseg3e(const ushort* a, ptrdiff_t b, size_t c, vuint16m2_t& x, vuint16m2_t& y, vuint16m2_t& z) { auto src = __riscv_vlsseg3e16_v_u16m2x3(a, b, c); x = __riscv_vget_v_u16m2x3_u16m2(src, 0); y = __riscv_vget_v_u16m2x3_u16m2(src, 1); z = __riscv_vget_v_u16m2x3_u16m2(src, 2); } + static inline void vlsseg4e(const ushort* a, ptrdiff_t b, size_t c, vuint16m2_t& x, vuint16m2_t& y, vuint16m2_t& z, vuint16m2_t& w) { auto src = __riscv_vlsseg4e16_v_u16m2x4(a, b, c); x = __riscv_vget_v_u16m2x4_u16m2(src, 0); y = __riscv_vget_v_u16m2x4_u16m2(src, 1); z = __riscv_vget_v_u16m2x4_u16m2(src, 2); w = __riscv_vget_v_u16m2x4_u16m2(src, 3); } +}; +template<> struct rvv +{ + static inline vfloat32m4_t vcvt0(vfloat32m4_t a, size_t) { return a; } + static inline vfloat32m4_t vcvt1(vfloat32m4_t a, size_t) { return a; } + static inline vfloat32m4_t vloxei(const float* a, vuint16m2_t b, size_t c) { return __riscv_vloxei16_v_f32m4(a, b, c); } + static inline void vloxseg2ei(const float* a, vuint16m2_t b, size_t c, vfloat32m4_t& x, vfloat32m4_t& y) { auto src = __riscv_vloxseg2ei16_v_f32m4x2(a, b, c); x = __riscv_vget_v_f32m4x2_f32m4(src, 0); y = __riscv_vget_v_f32m4x2_f32m4(src, 1); } + static inline void vloxseg3ei(const float*, vuint16m2_t, size_t, vfloat32m4_t&, vfloat32m4_t&, vfloat32m4_t&) { /*NOTREACHED*/ } + static inline void vloxseg4ei(const float*, vuint16m2_t, size_t, vfloat32m4_t&, vfloat32m4_t&, vfloat32m4_t&, vfloat32m4_t&) { /*NOTREACHED*/ } + static inline void vsseg2e(float* a, size_t b, vfloat32m4_t x, vfloat32m4_t y) { vfloat32m4x2_t dst{}; dst = __riscv_vset_v_f32m4_f32m4x2(dst, 0, x); dst = __riscv_vset_v_f32m4_f32m4x2(dst, 1, y); __riscv_vsseg2e32(a, dst, b); } + static inline void vsseg3e(float*, size_t, vfloat32m4_t, vfloat32m4_t, vfloat32m4_t) { /*NOTREACHED*/ } + static inline void vsseg4e(float*, size_t, vfloat32m4_t, vfloat32m4_t, vfloat32m4_t, vfloat32m4_t) { /*NOTREACHED*/ } +}; +template<> struct rvv +{ + static inline vfloat32m2_t vcvt0(vuint8mf2_t a, size_t b) { return __riscv_vfcvt_f(__riscv_vzext_vf4(a, b), b); } + static inline vuint8mf2_t vcvt1(vfloat32m2_t a, size_t b) { return __riscv_vnclipu(__riscv_vfncvt_xu(a, b), 0, __RISCV_VXRM_RNU, b); } + static inline vuint8mf2_t vloxei(const uchar* a, vuint16m1_t b, size_t c) { return __riscv_vloxei16_v_u8mf2(a, b, c); } + static inline void vloxseg2ei(const uchar* a, vuint16m1_t b, size_t c, vuint8mf2_t& x, vuint8mf2_t& y) { auto src = __riscv_vloxseg2ei16_v_u8mf2x2(a, b, c); x = __riscv_vget_v_u8mf2x2_u8mf2(src, 0); y = __riscv_vget_v_u8mf2x2_u8mf2(src, 1); } + static inline void vloxseg3ei(const uchar* a, vuint16m1_t b, size_t c, vuint8mf2_t& x, vuint8mf2_t& y, vuint8mf2_t& z) { auto src = __riscv_vloxseg3ei16_v_u8mf2x3(a, b, c); x = __riscv_vget_v_u8mf2x3_u8mf2(src, 0); y = __riscv_vget_v_u8mf2x3_u8mf2(src, 1); z = __riscv_vget_v_u8mf2x3_u8mf2(src, 2); } + static inline void vloxseg4ei(const uchar* a, vuint16m1_t b, size_t c, vuint8mf2_t& x, vuint8mf2_t& y, vuint8mf2_t& z, vuint8mf2_t& w) { auto src = __riscv_vloxseg4ei16_v_u8mf2x4(a, b, c); x = __riscv_vget_v_u8mf2x4_u8mf2(src, 0); y = __riscv_vget_v_u8mf2x4_u8mf2(src, 1); z = __riscv_vget_v_u8mf2x4_u8mf2(src, 2); w = __riscv_vget_v_u8mf2x4_u8mf2(src, 3); } + static inline void vsseg2e(uchar* a, size_t b, vuint8mf2_t x, vuint8mf2_t y) { vuint8mf2x2_t dst{}; dst = __riscv_vset_v_u8mf2_u8mf2x2(dst, 0, x); dst = __riscv_vset_v_u8mf2_u8mf2x2(dst, 1, y); __riscv_vsseg2e8(a, dst, b); } + static inline void vsseg3e(uchar* a, size_t b, vuint8mf2_t x, vuint8mf2_t y, vuint8mf2_t z) { vuint8mf2x3_t dst{}; dst = __riscv_vset_v_u8mf2_u8mf2x3(dst, 0, x); dst = __riscv_vset_v_u8mf2_u8mf2x3(dst, 1, y); dst = __riscv_vset_v_u8mf2_u8mf2x3(dst, 2, z); __riscv_vsseg3e8(a, dst, b); } + static inline void vsseg4e(uchar* a, size_t b, vuint8mf2_t x, vuint8mf2_t y, vuint8mf2_t z, vuint8mf2_t w) { vuint8mf2x4_t dst{}; dst = __riscv_vset_v_u8mf2_u8mf2x4(dst, 0, x); dst = __riscv_vset_v_u8mf2_u8mf2x4(dst, 1, y); dst = __riscv_vset_v_u8mf2_u8mf2x4(dst, 2, z); dst = __riscv_vset_v_u8mf2_u8mf2x4(dst, 3, w); __riscv_vsseg4e8(a, dst, b); } +}; +template<> struct rvv +{ + static inline vfloat32m2_t vcvt0(vuint16m1_t a, size_t b) { return __riscv_vfwcvt_f(a, b); } + static inline vuint16m1_t vcvt1(vfloat32m2_t a, size_t b) { return __riscv_vfncvt_xu(a, b); } + static inline vuint16m1_t vloxei(const ushort* a, vuint16m1_t b, size_t c) { return __riscv_vloxei16_v_u16m1(a, b, c); } + static inline void vloxseg2ei(const ushort* a, vuint16m1_t b, size_t c, vuint16m1_t& x, vuint16m1_t& y) { auto src = __riscv_vloxseg2ei16_v_u16m1x2(a, b, c); x = __riscv_vget_v_u16m1x2_u16m1(src, 0); y = __riscv_vget_v_u16m1x2_u16m1(src, 1); } + static inline void vloxseg3ei(const ushort* a, vuint16m1_t b, size_t c, vuint16m1_t& x, vuint16m1_t& y, vuint16m1_t& z) { auto src = __riscv_vloxseg3ei16_v_u16m1x3(a, b, c); x = __riscv_vget_v_u16m1x3_u16m1(src, 0); y = __riscv_vget_v_u16m1x3_u16m1(src, 1); z = __riscv_vget_v_u16m1x3_u16m1(src, 2); } + static inline void vloxseg4ei(const ushort* a, vuint16m1_t b, size_t c, vuint16m1_t& x, vuint16m1_t& y, vuint16m1_t& z, vuint16m1_t& w) { auto src = __riscv_vloxseg4ei16_v_u16m1x4(a, b, c); x = __riscv_vget_v_u16m1x4_u16m1(src, 0); y = __riscv_vget_v_u16m1x4_u16m1(src, 1); z = __riscv_vget_v_u16m1x4_u16m1(src, 2); w = __riscv_vget_v_u16m1x4_u16m1(src, 3); } + static inline void vsseg2e(ushort* a, size_t b, vuint16m1_t x, vuint16m1_t y) { vuint16m1x2_t dst{}; dst = __riscv_vset_v_u16m1_u16m1x2(dst, 0, x); dst = __riscv_vset_v_u16m1_u16m1x2(dst, 1, y); __riscv_vsseg2e16(a, dst, b); } + static inline void vsseg3e(ushort* a, size_t b, vuint16m1_t x, vuint16m1_t y, vuint16m1_t z) { vuint16m1x3_t dst{}; dst = __riscv_vset_v_u16m1_u16m1x3(dst, 0, x); dst = __riscv_vset_v_u16m1_u16m1x3(dst, 1, y); dst = __riscv_vset_v_u16m1_u16m1x3(dst, 2, z); __riscv_vsseg3e16(a, dst, b); } + static inline void vsseg4e(ushort* a, size_t b, vuint16m1_t x, vuint16m1_t y, vuint16m1_t z, vuint16m1_t w) { vuint16m1x4_t dst{}; dst = __riscv_vset_v_u16m1_u16m1x4(dst, 0, x); dst = __riscv_vset_v_u16m1_u16m1x4(dst, 1, y); dst = __riscv_vset_v_u16m1_u16m1x4(dst, 2, z); dst = __riscv_vset_v_u16m1_u16m1x4(dst, 3, w); __riscv_vsseg4e16(a, dst, b); } +}; +template<> struct rvv +{ + static inline vfloat32m2_t vcvt0(vfloat32m2_t a, size_t) { return a; } + static inline vfloat32m2_t vcvt1(vfloat32m2_t a, size_t) { return a; } + static inline vfloat32m2_t vloxei(const float* a, vuint16m1_t b, size_t c) { return __riscv_vloxei16_v_f32m2(a, b, c); } + static inline void vloxseg2ei(const float* a, vuint16m1_t b, size_t c, vfloat32m2_t& x, vfloat32m2_t& y) { auto src = __riscv_vloxseg2ei16_v_f32m2x2(a, b, c); x = __riscv_vget_v_f32m2x2_f32m2(src, 0); y = __riscv_vget_v_f32m2x2_f32m2(src, 1); } + static inline void vloxseg3ei(const float* a, vuint16m1_t b, size_t c, vfloat32m2_t& x, vfloat32m2_t& y, vfloat32m2_t& z) { auto src = __riscv_vloxseg3ei16_v_f32m2x3(a, b, c); x = __riscv_vget_v_f32m2x3_f32m2(src, 0); y = __riscv_vget_v_f32m2x3_f32m2(src, 1); z = __riscv_vget_v_f32m2x3_f32m2(src, 2); } + static inline void vloxseg4ei(const float* a, vuint16m1_t b, size_t c, vfloat32m2_t& x, vfloat32m2_t& y, vfloat32m2_t& z, vfloat32m2_t& w) { auto src = __riscv_vloxseg4ei16_v_f32m2x4(a, b, c); x = __riscv_vget_v_f32m2x4_f32m2(src, 0); y = __riscv_vget_v_f32m2x4_f32m2(src, 1); z = __riscv_vget_v_f32m2x4_f32m2(src, 2); w = __riscv_vget_v_f32m2x4_f32m2(src, 3); } + static inline void vsseg2e(float* a, size_t b, vfloat32m2_t x, vfloat32m2_t y) { vfloat32m2x2_t dst{}; dst = __riscv_vset_v_f32m2_f32m2x2(dst, 0, x); dst = __riscv_vset_v_f32m2_f32m2x2(dst, 1, y); __riscv_vsseg2e32(a, dst, b); } + static inline void vsseg3e(float* a, size_t b, vfloat32m2_t x, vfloat32m2_t y, vfloat32m2_t z) { vfloat32m2x3_t dst{}; dst = __riscv_vset_v_f32m2_f32m2x3(dst, 0, x); dst = __riscv_vset_v_f32m2_f32m2x3(dst, 1, y); dst = __riscv_vset_v_f32m2_f32m2x3(dst, 2, z); __riscv_vsseg3e32(a, dst, b); } + static inline void vsseg4e(float* a, size_t b, vfloat32m2_t x, vfloat32m2_t y, vfloat32m2_t z, vfloat32m2_t w) { vfloat32m2x4_t dst{}; dst = __riscv_vset_v_f32m2_f32m2x4(dst, 0, x); dst = __riscv_vset_v_f32m2_f32m2x4(dst, 1, y); dst = __riscv_vset_v_f32m2_f32m2x4(dst, 2, z); dst = __riscv_vset_v_f32m2_f32m2x4(dst, 3, w); __riscv_vsseg4e32(a, dst, b); } +}; + +template +static inline int resizeLinear(int start, int end, const uchar *src_data, size_t src_step, int src_height, uchar *dst_data, size_t dst_step, int dst_width, double scale_y, const ushort* x_ofs0, const ushort* x_ofs1, const float* x_val) +{ + using T = typename helper::ElemType; + + for (int i = start; i < end; i++) + { + float my = (i + 0.5) * scale_y - 0.5; + int y_ofs = static_cast(std::floor(my)); + my -= y_ofs; + + int y_ofs0 = std::min(std::max(y_ofs , 0), src_height - 1); + int y_ofs1 = std::min(std::max(y_ofs + 1, 0), src_height - 1); + + int vl; + switch (cn) + { + case 1: + for (int j = 0; j < dst_width; j += vl) + { + vl = helper::setvl(dst_width - j); + auto ptr0 = RVV_SameLen::vload(x_ofs0 + j, vl); + auto ptr1 = RVV_SameLen::vload(x_ofs1 + j, vl); + + auto v0 = rvv::vcvt0(rvv::vloxei(reinterpret_cast(src_data + y_ofs0 * src_step), ptr0, vl), vl); + auto v1 = rvv::vcvt0(rvv::vloxei(reinterpret_cast(src_data + y_ofs0 * src_step), ptr1, vl), vl); + auto v2 = rvv::vcvt0(rvv::vloxei(reinterpret_cast(src_data + y_ofs1 * src_step), ptr0, vl), vl); + auto v3 = rvv::vcvt0(rvv::vloxei(reinterpret_cast(src_data + y_ofs1 * src_step), ptr1, vl), vl); + + auto mx = RVV_SameLen::vload(x_val + j, vl); + v0 = __riscv_vfmadd(__riscv_vfsub(v1, v0, vl), mx, v0, vl); + v2 = __riscv_vfmadd(__riscv_vfsub(v3, v2, vl), mx, v2, vl); + v0 = __riscv_vfmadd(__riscv_vfsub(v2, v0, vl), my, v0, vl); + helper::vstore(reinterpret_cast(dst_data + i * dst_step) + j, rvv::vcvt1(v0, vl), vl); + } + break; + case 2: + for (int j = 0; j < dst_width; j += vl) + { + vl = helper::setvl(dst_width - j); + auto ptr0 = RVV_SameLen::vload(x_ofs0 + j, vl); + auto ptr1 = RVV_SameLen::vload(x_ofs1 + j, vl); + + typename helper::VecType s0, s1; + rvv::vloxseg2ei(reinterpret_cast(src_data + y_ofs0 * src_step), ptr0, vl, s0, s1); + auto v00 = rvv::vcvt0(s0, vl), v10 = rvv::vcvt0(s1, vl); + rvv::vloxseg2ei(reinterpret_cast(src_data + y_ofs0 * src_step), ptr1, vl, s0, s1); + auto v01 = rvv::vcvt0(s0, vl), v11 = rvv::vcvt0(s1, vl); + rvv::vloxseg2ei(reinterpret_cast(src_data + y_ofs1 * src_step), ptr0, vl, s0, s1); + auto v02 = rvv::vcvt0(s0, vl), v12 = rvv::vcvt0(s1, vl); + rvv::vloxseg2ei(reinterpret_cast(src_data + y_ofs1 * src_step), ptr1, vl, s0, s1); + auto v03 = rvv::vcvt0(s0, vl), v13 = rvv::vcvt0(s1, vl); + + auto mx = RVV_SameLen::vload(x_val + j, vl); + v00 = __riscv_vfmadd(__riscv_vfsub(v01, v00, vl), mx, v00, vl); + v02 = __riscv_vfmadd(__riscv_vfsub(v03, v02, vl), mx, v02, vl); + v00 = __riscv_vfmadd(__riscv_vfsub(v02, v00, vl), my, v00, vl); + v10 = __riscv_vfmadd(__riscv_vfsub(v11, v10, vl), mx, v10, vl); + v12 = __riscv_vfmadd(__riscv_vfsub(v13, v12, vl), mx, v12, vl); + v10 = __riscv_vfmadd(__riscv_vfsub(v12, v10, vl), my, v10, vl); + rvv::vsseg2e(reinterpret_cast(dst_data + i * dst_step) + j * 2, vl, rvv::vcvt1(v00, vl), rvv::vcvt1(v10, vl)); + } + break; + case 3: + for (int j = 0; j < dst_width; j += vl) + { + vl = helper::setvl(dst_width - j); + auto ptr0 = RVV_SameLen::vload(x_ofs0 + j, vl); + auto ptr1 = RVV_SameLen::vload(x_ofs1 + j, vl); + + typename helper::VecType s0, s1, s2; + rvv::vloxseg3ei(reinterpret_cast(src_data + y_ofs0 * src_step), ptr0, vl, s0, s1, s2); + auto v00 = rvv::vcvt0(s0, vl), v10 = rvv::vcvt0(s1, vl), v20 = rvv::vcvt0(s2, vl); + rvv::vloxseg3ei(reinterpret_cast(src_data + y_ofs0 * src_step), ptr1, vl, s0, s1, s2); + auto v01 = rvv::vcvt0(s0, vl), v11 = rvv::vcvt0(s1, vl), v21 = rvv::vcvt0(s2, vl); + rvv::vloxseg3ei(reinterpret_cast(src_data + y_ofs1 * src_step), ptr0, vl, s0, s1, s2); + auto v02 = rvv::vcvt0(s0, vl), v12 = rvv::vcvt0(s1, vl), v22 = rvv::vcvt0(s2, vl); + rvv::vloxseg3ei(reinterpret_cast(src_data + y_ofs1 * src_step), ptr1, vl, s0, s1, s2); + auto v03 = rvv::vcvt0(s0, vl), v13 = rvv::vcvt0(s1, vl), v23 = rvv::vcvt0(s2, vl); + + auto mx = RVV_SameLen::vload(x_val + j, vl); + v00 = __riscv_vfmadd(__riscv_vfsub(v01, v00, vl), mx, v00, vl); + v02 = __riscv_vfmadd(__riscv_vfsub(v03, v02, vl), mx, v02, vl); + v00 = __riscv_vfmadd(__riscv_vfsub(v02, v00, vl), my, v00, vl); + v10 = __riscv_vfmadd(__riscv_vfsub(v11, v10, vl), mx, v10, vl); + v12 = __riscv_vfmadd(__riscv_vfsub(v13, v12, vl), mx, v12, vl); + v10 = __riscv_vfmadd(__riscv_vfsub(v12, v10, vl), my, v10, vl); + v20 = __riscv_vfmadd(__riscv_vfsub(v21, v20, vl), mx, v20, vl); + v22 = __riscv_vfmadd(__riscv_vfsub(v23, v22, vl), mx, v22, vl); + v20 = __riscv_vfmadd(__riscv_vfsub(v22, v20, vl), my, v20, vl); + rvv::vsseg3e(reinterpret_cast(dst_data + i * dst_step) + j * 3, vl, rvv::vcvt1(v00, vl), rvv::vcvt1(v10, vl), rvv::vcvt1(v20, vl)); + } + break; + case 4: + for (int j = 0; j < dst_width; j += vl) + { + vl = helper::setvl(dst_width - j); + auto ptr0 = RVV_SameLen::vload(x_ofs0 + j, vl); + auto ptr1 = RVV_SameLen::vload(x_ofs1 + j, vl); + + typename helper::VecType s0, s1, s2, s3; + rvv::vloxseg4ei(reinterpret_cast(src_data + y_ofs0 * src_step), ptr0, vl, s0, s1, s2, s3); + auto v00 = rvv::vcvt0(s0, vl), v10 = rvv::vcvt0(s1, vl), v20 = rvv::vcvt0(s2, vl), v30 = rvv::vcvt0(s3, vl); + rvv::vloxseg4ei(reinterpret_cast(src_data + y_ofs0 * src_step), ptr1, vl, s0, s1, s2, s3); + auto v01 = rvv::vcvt0(s0, vl), v11 = rvv::vcvt0(s1, vl), v21 = rvv::vcvt0(s2, vl), v31 = rvv::vcvt0(s3, vl); + rvv::vloxseg4ei(reinterpret_cast(src_data + y_ofs1 * src_step), ptr0, vl, s0, s1, s2, s3); + auto v02 = rvv::vcvt0(s0, vl), v12 = rvv::vcvt0(s1, vl), v22 = rvv::vcvt0(s2, vl), v32 = rvv::vcvt0(s3, vl); + rvv::vloxseg4ei(reinterpret_cast(src_data + y_ofs1 * src_step), ptr1, vl, s0, s1, s2, s3); + auto v03 = rvv::vcvt0(s0, vl), v13 = rvv::vcvt0(s1, vl), v23 = rvv::vcvt0(s2, vl), v33 = rvv::vcvt0(s3, vl); + + auto mx = RVV_SameLen::vload(x_val + j, vl); + v00 = __riscv_vfmadd(__riscv_vfsub(v01, v00, vl), mx, v00, vl); + v02 = __riscv_vfmadd(__riscv_vfsub(v03, v02, vl), mx, v02, vl); + v00 = __riscv_vfmadd(__riscv_vfsub(v02, v00, vl), my, v00, vl); + v10 = __riscv_vfmadd(__riscv_vfsub(v11, v10, vl), mx, v10, vl); + v12 = __riscv_vfmadd(__riscv_vfsub(v13, v12, vl), mx, v12, vl); + v10 = __riscv_vfmadd(__riscv_vfsub(v12, v10, vl), my, v10, vl); + v20 = __riscv_vfmadd(__riscv_vfsub(v21, v20, vl), mx, v20, vl); + v22 = __riscv_vfmadd(__riscv_vfsub(v23, v22, vl), mx, v22, vl); + v20 = __riscv_vfmadd(__riscv_vfsub(v22, v20, vl), my, v20, vl); + v30 = __riscv_vfmadd(__riscv_vfsub(v31, v30, vl), mx, v30, vl); + v32 = __riscv_vfmadd(__riscv_vfsub(v33, v32, vl), mx, v32, vl); + v30 = __riscv_vfmadd(__riscv_vfsub(v32, v30, vl), my, v30, vl); + rvv::vsseg4e(reinterpret_cast(dst_data + i * dst_step) + j * 4, vl, rvv::vcvt1(v00, vl), rvv::vcvt1(v10, vl), rvv::vcvt1(v20, vl), rvv::vcvt1(v30, vl)); + } + break; + default: + return CV_HAL_ERROR_NOT_IMPLEMENTED; + } + } + + return CV_HAL_ERROR_OK; +} + +template +static inline int resizeLinearExact(int start, int end, const uchar *src_data, size_t src_step, int src_height, uchar *dst_data, size_t dst_step, int dst_width, double scale_y, const ushort* x_ofs0, const ushort* x_ofs1, const ushort* x_val) +{ + for (int i = start; i < end; i++) + { + double y_val = (i + 0.5) * scale_y - 0.5; + int y_ofs = static_cast(std::floor(y_val)); + y_val -= y_ofs; + + int y_ofs0 = std::min(std::max(y_ofs , 0), src_height - 1); + int y_ofs1 = std::min(std::max(y_ofs + 1, 0), src_height - 1); + ushort my = static_cast(y_val * 256 - std::remainder(y_val * 256, 1)); + + int vl; + switch (cn) + { + case 1: + for (int j = 0; j < dst_width; j += vl) + { + vl = __riscv_vsetvl_e8m1(dst_width - j); + auto ptr0 = __riscv_vle16_v_u16m2(x_ofs0 + j, vl); + auto ptr1 = __riscv_vle16_v_u16m2(x_ofs1 + j, vl); + + auto v0 = __riscv_vzext_vf2(__riscv_vloxei16_v_u8m1(src_data + y_ofs0 * src_step, ptr0, vl), vl); + auto v1 = __riscv_vzext_vf2(__riscv_vloxei16_v_u8m1(src_data + y_ofs0 * src_step, ptr1, vl), vl); + auto v2 = __riscv_vzext_vf2(__riscv_vloxei16_v_u8m1(src_data + y_ofs1 * src_step, ptr0, vl), vl); + auto v3 = __riscv_vzext_vf2(__riscv_vloxei16_v_u8m1(src_data + y_ofs1 * src_step, ptr1, vl), vl); + + auto mx = __riscv_vle16_v_u16m2(x_val + j, vl); + v0 = __riscv_vmadd(v0, __riscv_vrsub(mx, 256, vl), __riscv_vmul(v1, mx, vl), vl); + v2 = __riscv_vmadd(v2, __riscv_vrsub(mx, 256, vl), __riscv_vmul(v3, mx, vl), vl); + auto d0 = __riscv_vwmaccu(__riscv_vwmulu(v2, my, vl), 256 - my, v0, vl); + __riscv_vse8(dst_data + i * dst_step + j, __riscv_vnclipu(__riscv_vnclipu(d0, 16, __RISCV_VXRM_RNU, vl), 0, __RISCV_VXRM_RNU, vl), vl); + } + break; + case 2: + for (int j = 0; j < dst_width; j += vl) + { + vl = __riscv_vsetvl_e8m1(dst_width - j); + auto ptr0 = __riscv_vle16_v_u16m2(x_ofs0 + j, vl); + auto ptr1 = __riscv_vle16_v_u16m2(x_ofs1 + j, vl); + + auto src = __riscv_vloxseg2ei16_v_u8m1x2(src_data + y_ofs0 * src_step, ptr0, vl); + auto v00 = __riscv_vzext_vf2(__riscv_vget_v_u8m1x2_u8m1(src, 0), vl), v10 = __riscv_vzext_vf2(__riscv_vget_v_u8m1x2_u8m1(src, 1), vl); + src = __riscv_vloxseg2ei16_v_u8m1x2(src_data + y_ofs0 * src_step, ptr1, vl); + auto v01 = __riscv_vzext_vf2(__riscv_vget_v_u8m1x2_u8m1(src, 0), vl), v11 = __riscv_vzext_vf2(__riscv_vget_v_u8m1x2_u8m1(src, 1), vl); + src = __riscv_vloxseg2ei16_v_u8m1x2(src_data + y_ofs1 * src_step, ptr0, vl); + auto v02 = __riscv_vzext_vf2(__riscv_vget_v_u8m1x2_u8m1(src, 0), vl), v12 = __riscv_vzext_vf2(__riscv_vget_v_u8m1x2_u8m1(src, 1), vl); + src = __riscv_vloxseg2ei16_v_u8m1x2(src_data + y_ofs1 * src_step, ptr1, vl); + auto v03 = __riscv_vzext_vf2(__riscv_vget_v_u8m1x2_u8m1(src, 0), vl), v13 = __riscv_vzext_vf2(__riscv_vget_v_u8m1x2_u8m1(src, 1), vl); + + auto mx = __riscv_vle16_v_u16m2(x_val + j, vl); + v00 = __riscv_vmadd(v00, __riscv_vrsub(mx, 256, vl), __riscv_vmul(v01, mx, vl), vl); + v02 = __riscv_vmadd(v02, __riscv_vrsub(mx, 256, vl), __riscv_vmul(v03, mx, vl), vl); + auto d00 = __riscv_vwmaccu(__riscv_vwmulu(v02, my, vl), 256 - my, v00, vl); + v10 = __riscv_vmadd(v10, __riscv_vrsub(mx, 256, vl), __riscv_vmul(v11, mx, vl), vl); + v12 = __riscv_vmadd(v12, __riscv_vrsub(mx, 256, vl), __riscv_vmul(v13, mx, vl), vl); + auto d10 = __riscv_vwmaccu(__riscv_vwmulu(v12, my, vl), 256 - my, v10, vl); + + vuint8m1x2_t dst{}; + dst = __riscv_vset_v_u8m1_u8m1x2(dst, 0, __riscv_vnclipu(__riscv_vnclipu(d00, 16, __RISCV_VXRM_RNU, vl), 0, __RISCV_VXRM_RNU, vl)); + dst = __riscv_vset_v_u8m1_u8m1x2(dst, 1, __riscv_vnclipu(__riscv_vnclipu(d10, 16, __RISCV_VXRM_RNU, vl), 0, __RISCV_VXRM_RNU, vl)); + __riscv_vsseg2e8(dst_data + i * dst_step + j * 2, dst, vl); + } + break; + case 3: + for (int j = 0; j < dst_width; j += vl) + { + vl = __riscv_vsetvl_e8mf2(dst_width - j); + auto ptr0 = __riscv_vle16_v_u16m1(x_ofs0 + j, vl); + auto ptr1 = __riscv_vle16_v_u16m1(x_ofs1 + j, vl); + + auto src = __riscv_vloxseg3ei16_v_u8mf2x3(src_data + y_ofs0 * src_step, ptr0, vl); + auto v00 = __riscv_vzext_vf2(__riscv_vget_v_u8mf2x3_u8mf2(src, 0), vl), v10 = __riscv_vzext_vf2(__riscv_vget_v_u8mf2x3_u8mf2(src, 1), vl), v20 = __riscv_vzext_vf2(__riscv_vget_v_u8mf2x3_u8mf2(src, 2), vl); + src = __riscv_vloxseg3ei16_v_u8mf2x3(src_data + y_ofs0 * src_step, ptr1, vl); + auto v01 = __riscv_vzext_vf2(__riscv_vget_v_u8mf2x3_u8mf2(src, 0), vl), v11 = __riscv_vzext_vf2(__riscv_vget_v_u8mf2x3_u8mf2(src, 1), vl), v21 = __riscv_vzext_vf2(__riscv_vget_v_u8mf2x3_u8mf2(src, 2), vl); + src = __riscv_vloxseg3ei16_v_u8mf2x3(src_data + y_ofs1 * src_step, ptr0, vl); + auto v02 = __riscv_vzext_vf2(__riscv_vget_v_u8mf2x3_u8mf2(src, 0), vl), v12 = __riscv_vzext_vf2(__riscv_vget_v_u8mf2x3_u8mf2(src, 1), vl), v22 = __riscv_vzext_vf2(__riscv_vget_v_u8mf2x3_u8mf2(src, 2), vl); + src = __riscv_vloxseg3ei16_v_u8mf2x3(src_data + y_ofs1 * src_step, ptr1, vl); + auto v03 = __riscv_vzext_vf2(__riscv_vget_v_u8mf2x3_u8mf2(src, 0), vl), v13 = __riscv_vzext_vf2(__riscv_vget_v_u8mf2x3_u8mf2(src, 1), vl), v23 = __riscv_vzext_vf2(__riscv_vget_v_u8mf2x3_u8mf2(src, 2), vl); + + auto mx = __riscv_vle16_v_u16m1(x_val + j, vl); + v00 = __riscv_vmadd(v00, __riscv_vrsub(mx, 256, vl), __riscv_vmul(v01, mx, vl), vl); + v02 = __riscv_vmadd(v02, __riscv_vrsub(mx, 256, vl), __riscv_vmul(v03, mx, vl), vl); + auto d00 = __riscv_vwmaccu(__riscv_vwmulu(v02, my, vl), 256 - my, v00, vl); + v10 = __riscv_vmadd(v10, __riscv_vrsub(mx, 256, vl), __riscv_vmul(v11, mx, vl), vl); + v12 = __riscv_vmadd(v12, __riscv_vrsub(mx, 256, vl), __riscv_vmul(v13, mx, vl), vl); + auto d10 = __riscv_vwmaccu(__riscv_vwmulu(v12, my, vl), 256 - my, v10, vl); + v20 = __riscv_vmadd(v20, __riscv_vrsub(mx, 256, vl), __riscv_vmul(v21, mx, vl), vl); + v22 = __riscv_vmadd(v22, __riscv_vrsub(mx, 256, vl), __riscv_vmul(v23, mx, vl), vl); + auto d20 = __riscv_vwmaccu(__riscv_vwmulu(v22, my, vl), 256 - my, v20, vl); + + vuint8mf2x3_t dst{}; + dst = __riscv_vset_v_u8mf2_u8mf2x3(dst, 0, __riscv_vnclipu(__riscv_vnclipu(d00, 16, __RISCV_VXRM_RNU, vl), 0, __RISCV_VXRM_RNU, vl)); + dst = __riscv_vset_v_u8mf2_u8mf2x3(dst, 1, __riscv_vnclipu(__riscv_vnclipu(d10, 16, __RISCV_VXRM_RNU, vl), 0, __RISCV_VXRM_RNU, vl)); + dst = __riscv_vset_v_u8mf2_u8mf2x3(dst, 2, __riscv_vnclipu(__riscv_vnclipu(d20, 16, __RISCV_VXRM_RNU, vl), 0, __RISCV_VXRM_RNU, vl)); + __riscv_vsseg3e8(dst_data + i * dst_step + j * 3, dst, vl); + } + break; + case 4: + for (int j = 0; j < dst_width; j += vl) + { + vl = __riscv_vsetvl_e8mf2(dst_width - j); + auto ptr0 = __riscv_vle16_v_u16m1(x_ofs0 + j, vl); + auto ptr1 = __riscv_vle16_v_u16m1(x_ofs1 + j, vl); + + auto src = __riscv_vloxseg4ei16_v_u8mf2x4(src_data + y_ofs0 * src_step, ptr0, vl); + auto v00 = __riscv_vzext_vf2(__riscv_vget_v_u8mf2x4_u8mf2(src, 0), vl), v10 = __riscv_vzext_vf2(__riscv_vget_v_u8mf2x4_u8mf2(src, 1), vl), v20 = __riscv_vzext_vf2(__riscv_vget_v_u8mf2x4_u8mf2(src, 2), vl), v30 = __riscv_vzext_vf2(__riscv_vget_v_u8mf2x4_u8mf2(src, 3), vl); + src = __riscv_vloxseg4ei16_v_u8mf2x4(src_data + y_ofs0 * src_step, ptr1, vl); + auto v01 = __riscv_vzext_vf2(__riscv_vget_v_u8mf2x4_u8mf2(src, 0), vl), v11 = __riscv_vzext_vf2(__riscv_vget_v_u8mf2x4_u8mf2(src, 1), vl), v21 = __riscv_vzext_vf2(__riscv_vget_v_u8mf2x4_u8mf2(src, 2), vl), v31 = __riscv_vzext_vf2(__riscv_vget_v_u8mf2x4_u8mf2(src, 3), vl); + src = __riscv_vloxseg4ei16_v_u8mf2x4(src_data + y_ofs1 * src_step, ptr0, vl); + auto v02 = __riscv_vzext_vf2(__riscv_vget_v_u8mf2x4_u8mf2(src, 0), vl), v12 = __riscv_vzext_vf2(__riscv_vget_v_u8mf2x4_u8mf2(src, 1), vl), v22 = __riscv_vzext_vf2(__riscv_vget_v_u8mf2x4_u8mf2(src, 2), vl), v32 = __riscv_vzext_vf2(__riscv_vget_v_u8mf2x4_u8mf2(src, 3), vl); + src = __riscv_vloxseg4ei16_v_u8mf2x4(src_data + y_ofs1 * src_step, ptr1, vl); + auto v03 = __riscv_vzext_vf2(__riscv_vget_v_u8mf2x4_u8mf2(src, 0), vl), v13 = __riscv_vzext_vf2(__riscv_vget_v_u8mf2x4_u8mf2(src, 1), vl), v23 = __riscv_vzext_vf2(__riscv_vget_v_u8mf2x4_u8mf2(src, 2), vl), v33 = __riscv_vzext_vf2(__riscv_vget_v_u8mf2x4_u8mf2(src, 3), vl); + + auto mx = __riscv_vle16_v_u16m1(x_val + j, vl); + v00 = __riscv_vmadd(v00, __riscv_vrsub(mx, 256, vl), __riscv_vmul(v01, mx, vl), vl); + v02 = __riscv_vmadd(v02, __riscv_vrsub(mx, 256, vl), __riscv_vmul(v03, mx, vl), vl); + auto d00 = __riscv_vwmaccu(__riscv_vwmulu(v02, my, vl), 256 - my, v00, vl); + v10 = __riscv_vmadd(v10, __riscv_vrsub(mx, 256, vl), __riscv_vmul(v11, mx, vl), vl); + v12 = __riscv_vmadd(v12, __riscv_vrsub(mx, 256, vl), __riscv_vmul(v13, mx, vl), vl); + auto d10 = __riscv_vwmaccu(__riscv_vwmulu(v12, my, vl), 256 - my, v10, vl); + v20 = __riscv_vmadd(v20, __riscv_vrsub(mx, 256, vl), __riscv_vmul(v21, mx, vl), vl); + v22 = __riscv_vmadd(v22, __riscv_vrsub(mx, 256, vl), __riscv_vmul(v23, mx, vl), vl); + auto d20 = __riscv_vwmaccu(__riscv_vwmulu(v22, my, vl), 256 - my, v20, vl); + v30 = __riscv_vmadd(v30, __riscv_vrsub(mx, 256, vl), __riscv_vmul(v31, mx, vl), vl); + v32 = __riscv_vmadd(v32, __riscv_vrsub(mx, 256, vl), __riscv_vmul(v33, mx, vl), vl); + auto d30 = __riscv_vwmaccu(__riscv_vwmulu(v32, my, vl), 256 - my, v30, vl); + + vuint8mf2x4_t dst{}; + dst = __riscv_vset_v_u8mf2_u8mf2x4(dst, 0, __riscv_vnclipu(__riscv_vnclipu(d00, 16, __RISCV_VXRM_RNU, vl), 0, __RISCV_VXRM_RNU, vl)); + dst = __riscv_vset_v_u8mf2_u8mf2x4(dst, 1, __riscv_vnclipu(__riscv_vnclipu(d10, 16, __RISCV_VXRM_RNU, vl), 0, __RISCV_VXRM_RNU, vl)); + dst = __riscv_vset_v_u8mf2_u8mf2x4(dst, 2, __riscv_vnclipu(__riscv_vnclipu(d20, 16, __RISCV_VXRM_RNU, vl), 0, __RISCV_VXRM_RNU, vl)); + dst = __riscv_vset_v_u8mf2_u8mf2x4(dst, 3, __riscv_vnclipu(__riscv_vnclipu(d30, 16, __RISCV_VXRM_RNU, vl), 0, __RISCV_VXRM_RNU, vl)); + __riscv_vsseg4e8(dst_data + i * dst_step + j * 4, dst, vl); + } + break; + default: + return CV_HAL_ERROR_NOT_IMPLEMENTED; + } + } + + return CV_HAL_ERROR_OK; +} + +static int computeResizeAreaTab(int ssize, int dsize, double scale, ushort* stab, ushort* dtab, float* vtab) +{ + int k = 0; + for (int dx = 0; dx < dsize; dx++) + { + double fsx1 = dx * scale; + double fsx2 = fsx1 + scale; + double cellWidth = std::min(scale, ssize - fsx1); + + int sx1 = std::ceil(fsx1), sx2 = std::floor(fsx2); + + sx2 = std::min(sx2, ssize - 1); + sx1 = std::min(sx1, sx2); + + if (sx1 - fsx1 > 1e-3) + { + dtab[k] = dx; + stab[k] = sx1 - 1; + vtab[k++] = static_cast((sx1 - fsx1) / cellWidth); + } + + for (int sx = sx1; sx < sx2; sx++) + { + dtab[k] = dx; + stab[k] = sx; + vtab[k++] = static_cast(1.0 / cellWidth); + } + + if (fsx2 - sx2 > 1e-3) + { + dtab[k] = dx; + stab[k] = sx2; + vtab[k++] = static_cast(std::min(std::min(fsx2 - sx2, 1.), cellWidth) / cellWidth); + } + } + return k; +} + +template +static inline int resizeAreaFast(int start, int end, const uchar *src_data, size_t src_step, uchar *dst_data, size_t dst_step, int dst_width) +{ + using T = typename helper::ElemType; + + for (int i = start; i < end; i++) + { + int y_ofs0 = i * 2; + int y_ofs1 = i * 2 + 1; + + int vl; + switch (cn) + { + case 1: + for (int j = 0; j < dst_width; j += vl) + { + vl = helper::setvl(dst_width - j); + + typename helper::VecType s0, s1; + rvv::vlseg2e(reinterpret_cast(src_data + y_ofs0 * src_step) + j * 2, vl, s0, s1); + auto v0 = __riscv_vzext_vf2(s0, vl), v1 = __riscv_vzext_vf2(s1, vl); + rvv::vlseg2e(reinterpret_cast(src_data + y_ofs1 * src_step) + j * 2, vl, s0, s1); + auto v2 = __riscv_vzext_vf2(s0, vl), v3 = __riscv_vzext_vf2(s1, vl); + + v0 = __riscv_vadd(__riscv_vadd(v0, v1, vl), __riscv_vadd(v2, v3, vl), vl); + helper::vstore(reinterpret_cast(dst_data + i * dst_step) + j, __riscv_vnclipu(v0, 2, __RISCV_VXRM_RNU, vl), vl); + } + break; + case 2: + for (int j = 0; j < dst_width; j += vl) + { + vl = helper::setvl(dst_width - j); + + typename helper::VecType s0, s1; + rvv::vlsseg2e(reinterpret_cast(src_data + y_ofs0 * src_step) + j * 4, 4 * sizeof(T), vl, s0, s1); + auto v00 = __riscv_vzext_vf2(s0, vl), v10 = __riscv_vzext_vf2(s1, vl); + rvv::vlsseg2e(reinterpret_cast(src_data + y_ofs0 * src_step) + j * 4 + 2, 4 * sizeof(T), vl, s0, s1); + auto v01 = __riscv_vzext_vf2(s0, vl), v11 = __riscv_vzext_vf2(s1, vl); + rvv::vlsseg2e(reinterpret_cast(src_data + y_ofs1 * src_step) + j * 4, 4 * sizeof(T), vl, s0, s1); + auto v02 = __riscv_vzext_vf2(s0, vl), v12 = __riscv_vzext_vf2(s1, vl); + rvv::vlsseg2e(reinterpret_cast(src_data + y_ofs1 * src_step) + j * 4 + 2, 4 * sizeof(T), vl, s0, s1); + auto v03 = __riscv_vzext_vf2(s0, vl), v13 = __riscv_vzext_vf2(s1, vl); + + v00 = __riscv_vadd(__riscv_vadd(v00, v01, vl), __riscv_vadd(v02, v03, vl), vl); + v10 = __riscv_vadd(__riscv_vadd(v10, v11, vl), __riscv_vadd(v12, v13, vl), vl); + rvv::vsseg2e(reinterpret_cast(dst_data + i * dst_step) + j * 2, vl, __riscv_vnclipu(v00, 2, __RISCV_VXRM_RNU, vl), __riscv_vnclipu(v10, 2, __RISCV_VXRM_RNU, vl)); + } + break; + case 3: + for (int j = 0; j < dst_width; j += vl) + { + vl = helper::setvl(dst_width - j); + + typename helper::VecType s0, s1, s2; + rvv::vlsseg3e(reinterpret_cast(src_data + y_ofs0 * src_step) + j * 6, 6 * sizeof(T), vl, s0, s1, s2); + auto v00 = __riscv_vzext_vf2(s0, vl), v10 = __riscv_vzext_vf2(s1, vl), v20 = __riscv_vzext_vf2(s2, vl); + rvv::vlsseg3e(reinterpret_cast(src_data + y_ofs0 * src_step) + j * 6 + 3, 6 * sizeof(T), vl, s0, s1, s2); + auto v01 = __riscv_vzext_vf2(s0, vl), v11 = __riscv_vzext_vf2(s1, vl), v21 = __riscv_vzext_vf2(s2, vl); + rvv::vlsseg3e(reinterpret_cast(src_data + y_ofs1 * src_step) + j * 6, 6 * sizeof(T), vl, s0, s1, s2); + auto v02 = __riscv_vzext_vf2(s0, vl), v12 = __riscv_vzext_vf2(s1, vl), v22 = __riscv_vzext_vf2(s2, vl); + rvv::vlsseg3e(reinterpret_cast(src_data + y_ofs1 * src_step) + j * 6 + 3, 6 * sizeof(T), vl, s0, s1, s2); + auto v03 = __riscv_vzext_vf2(s0, vl), v13 = __riscv_vzext_vf2(s1, vl), v23 = __riscv_vzext_vf2(s2, vl); + + v00 = __riscv_vadd(__riscv_vadd(v00, v01, vl), __riscv_vadd(v02, v03, vl), vl); + v10 = __riscv_vadd(__riscv_vadd(v10, v11, vl), __riscv_vadd(v12, v13, vl), vl); + v20 = __riscv_vadd(__riscv_vadd(v20, v21, vl), __riscv_vadd(v22, v23, vl), vl); + rvv::vsseg3e(reinterpret_cast(dst_data + i * dst_step) + j * 3, vl, __riscv_vnclipu(v00, 2, __RISCV_VXRM_RNU, vl), __riscv_vnclipu(v10, 2, __RISCV_VXRM_RNU, vl), __riscv_vnclipu(v20, 2, __RISCV_VXRM_RNU, vl)); + } + break; + case 4: + for (int j = 0; j < dst_width; j += vl) + { + vl = helper::setvl(dst_width - j); + + typename helper::VecType s0, s1, s2, s3; + rvv::vlsseg4e(reinterpret_cast(src_data + y_ofs0 * src_step) + j * 8, 8 * sizeof(T), vl, s0, s1, s2, s3); + auto v00 = __riscv_vzext_vf2(s0, vl), v10 = __riscv_vzext_vf2(s1, vl), v20 = __riscv_vzext_vf2(s2, vl), v30 = __riscv_vzext_vf2(s3, vl); + rvv::vlsseg4e(reinterpret_cast(src_data + y_ofs0 * src_step) + j * 8 + 4, 8 * sizeof(T), vl, s0, s1, s2, s3); + auto v01 = __riscv_vzext_vf2(s0, vl), v11 = __riscv_vzext_vf2(s1, vl), v21 = __riscv_vzext_vf2(s2, vl), v31 = __riscv_vzext_vf2(s3, vl); + rvv::vlsseg4e(reinterpret_cast(src_data + y_ofs1 * src_step) + j * 8, 8 * sizeof(T), vl, s0, s1, s2, s3); + auto v02 = __riscv_vzext_vf2(s0, vl), v12 = __riscv_vzext_vf2(s1, vl), v22 = __riscv_vzext_vf2(s2, vl), v32 = __riscv_vzext_vf2(s3, vl); + rvv::vlsseg4e(reinterpret_cast(src_data + y_ofs1 * src_step) + j * 8 + 4, 8 * sizeof(T), vl, s0, s1, s2, s3); + auto v03 = __riscv_vzext_vf2(s0, vl), v13 = __riscv_vzext_vf2(s1, vl), v23 = __riscv_vzext_vf2(s2, vl), v33 = __riscv_vzext_vf2(s3, vl); + + v00 = __riscv_vadd(__riscv_vadd(v00, v01, vl), __riscv_vadd(v02, v03, vl), vl); + v10 = __riscv_vadd(__riscv_vadd(v10, v11, vl), __riscv_vadd(v12, v13, vl), vl); + v20 = __riscv_vadd(__riscv_vadd(v20, v21, vl), __riscv_vadd(v22, v23, vl), vl); + v30 = __riscv_vadd(__riscv_vadd(v30, v31, vl), __riscv_vadd(v32, v33, vl), vl); + rvv::vsseg4e(reinterpret_cast(dst_data + i * dst_step) + j * 4, vl, __riscv_vnclipu(v00, 2, __RISCV_VXRM_RNU, vl), __riscv_vnclipu(v10, 2, __RISCV_VXRM_RNU, vl), __riscv_vnclipu(v20, 2, __RISCV_VXRM_RNU, vl), __riscv_vnclipu(v30, 2, __RISCV_VXRM_RNU, vl)); + } + break; + default: + return CV_HAL_ERROR_NOT_IMPLEMENTED; + } + } + + return CV_HAL_ERROR_OK; +} + +template +static inline int resizeArea(int start, int end, const uchar *src_data, size_t src_step, uchar *dst_data, size_t dst_step, int dst_width, int xtab_size, int xtab_part, const ushort* x_stab, const ushort* x_dtab, const float* x_vtab, const ushort* y_stab, const ushort* y_dtab, const float* y_vtab, const ushort* tabofs) +{ + const int n = dst_width * cn; + std::vector _buf(n, 0), _sum(n, 0); + float* buf = _buf.data(), *sum = _sum.data(); + + start = tabofs[start]; + end = tabofs[end]; + int prev = y_dtab[start], vl; + for (int i = start; i < end; i++) + { + memset(buf, 0, sizeof(float) * n); + switch (cn) + { + case 1: + for (int j = 0; j < xtab_part; j += vl) + { + vl = __riscv_vsetvl_e8m1(xtab_part - j); + auto sxn = __riscv_vle16_v_u16m2(x_stab + j, vl); + auto dxn = __riscv_vle16_v_u16m2(x_dtab + j, vl); + auto vxn = __riscv_vle32_v_f32m4(x_vtab + j, vl); + + auto val = __riscv_vloxei16_v_f32m4(buf, dxn, vl); + auto src = __riscv_vfcvt_f(__riscv_vzext_vf4(__riscv_vloxei16_v_u8m1(src_data + y_stab[i] * src_step, sxn, vl), vl), vl); + __riscv_vsoxei16(buf, dxn, __riscv_vfmacc(val, src, vxn, vl), vl); + } + for (int j = xtab_part; j < xtab_size; j++) + { + buf[x_dtab[j]] += src_data[y_stab[i] * src_step + x_stab[j]] * x_vtab[j]; + } + break; + case 2: + for (int j = 0; j < xtab_part; j += vl) + { + vl = __riscv_vsetvl_e8m1(xtab_part - j); + auto sxn = __riscv_vle16_v_u16m2(x_stab + j, vl); + auto dxn = __riscv_vle16_v_u16m2(x_dtab + j, vl); + auto vxn = __riscv_vle32_v_f32m4(x_vtab + j, vl); + + auto val = __riscv_vloxseg2ei16_v_f32m4x2(buf, dxn, vl); + auto src = __riscv_vloxseg2ei16_v_u8m1x2(src_data + y_stab[i] * src_step, sxn, vl); + + vfloat32m4x2_t dst{}; + dst = __riscv_vset_v_f32m4_f32m4x2(dst, 0, __riscv_vfmacc(__riscv_vget_v_f32m4x2_f32m4(val, 0), __riscv_vfcvt_f(__riscv_vzext_vf4(__riscv_vget_v_u8m1x2_u8m1(src, 0), vl), vl), vxn, vl)); + dst = __riscv_vset_v_f32m4_f32m4x2(dst, 1, __riscv_vfmacc(__riscv_vget_v_f32m4x2_f32m4(val, 1), __riscv_vfcvt_f(__riscv_vzext_vf4(__riscv_vget_v_u8m1x2_u8m1(src, 1), vl), vl), vxn, vl)); + __riscv_vsoxseg2ei16(buf, dxn, dst, vl); + } + for (int j = xtab_part; j < xtab_size; j++) + { + buf[x_dtab[j] ] += src_data[y_stab[i] * src_step + x_stab[j] ] * x_vtab[j]; + buf[x_dtab[j] + 1] += src_data[y_stab[i] * src_step + x_stab[j] + 1] * x_vtab[j]; + } + break; + case 3: + for (int j = 0; j < xtab_part; j += vl) + { + vl = __riscv_vsetvl_e8mf2(xtab_part - j); + auto sxn = __riscv_vle16_v_u16m1(x_stab + j, vl); + auto dxn = __riscv_vle16_v_u16m1(x_dtab + j, vl); + auto vxn = __riscv_vle32_v_f32m2(x_vtab + j, vl); + + auto val = __riscv_vloxseg3ei16_v_f32m2x3(buf, dxn, vl); + auto src = __riscv_vloxseg3ei16_v_u8mf2x3(src_data + y_stab[i] * src_step, sxn, vl); + + vfloat32m2x3_t dst{}; + dst = __riscv_vset_v_f32m2_f32m2x3(dst, 0, __riscv_vfmacc(__riscv_vget_v_f32m2x3_f32m2(val, 0), __riscv_vfcvt_f(__riscv_vzext_vf4(__riscv_vget_v_u8mf2x3_u8mf2(src, 0), vl), vl), vxn, vl)); + dst = __riscv_vset_v_f32m2_f32m2x3(dst, 1, __riscv_vfmacc(__riscv_vget_v_f32m2x3_f32m2(val, 1), __riscv_vfcvt_f(__riscv_vzext_vf4(__riscv_vget_v_u8mf2x3_u8mf2(src, 1), vl), vl), vxn, vl)); + dst = __riscv_vset_v_f32m2_f32m2x3(dst, 2, __riscv_vfmacc(__riscv_vget_v_f32m2x3_f32m2(val, 2), __riscv_vfcvt_f(__riscv_vzext_vf4(__riscv_vget_v_u8mf2x3_u8mf2(src, 2), vl), vl), vxn, vl)); + __riscv_vsoxseg3ei16(buf, dxn, dst, vl); + } + for (int j = xtab_part; j < xtab_size; j++) + { + buf[x_dtab[j] ] += src_data[y_stab[i] * src_step + x_stab[j] ] * x_vtab[j]; + buf[x_dtab[j] + 1] += src_data[y_stab[i] * src_step + x_stab[j] + 1] * x_vtab[j]; + buf[x_dtab[j] + 2] += src_data[y_stab[i] * src_step + x_stab[j] + 2] * x_vtab[j]; + } + break; + case 4: + for (int j = 0; j < xtab_part; j += vl) + { + vl = __riscv_vsetvl_e8mf2(xtab_part - j); + auto sxn = __riscv_vle16_v_u16m1(x_stab + j, vl); + auto dxn = __riscv_vle16_v_u16m1(x_dtab + j, vl); + auto vxn = __riscv_vle32_v_f32m2(x_vtab + j, vl); + + auto val = __riscv_vloxseg4ei16_v_f32m2x4(buf, dxn, vl); + auto src = __riscv_vloxseg4ei16_v_u8mf2x4(src_data + y_stab[i] * src_step, sxn, vl); + + vfloat32m2x4_t dst{}; + dst = __riscv_vset_v_f32m2_f32m2x4(dst, 0, __riscv_vfmacc(__riscv_vget_v_f32m2x4_f32m2(val, 0), __riscv_vfcvt_f(__riscv_vzext_vf4(__riscv_vget_v_u8mf2x4_u8mf2(src, 0), vl), vl), vxn, vl)); + dst = __riscv_vset_v_f32m2_f32m2x4(dst, 1, __riscv_vfmacc(__riscv_vget_v_f32m2x4_f32m2(val, 1), __riscv_vfcvt_f(__riscv_vzext_vf4(__riscv_vget_v_u8mf2x4_u8mf2(src, 1), vl), vl), vxn, vl)); + dst = __riscv_vset_v_f32m2_f32m2x4(dst, 2, __riscv_vfmacc(__riscv_vget_v_f32m2x4_f32m2(val, 2), __riscv_vfcvt_f(__riscv_vzext_vf4(__riscv_vget_v_u8mf2x4_u8mf2(src, 2), vl), vl), vxn, vl)); + dst = __riscv_vset_v_f32m2_f32m2x4(dst, 3, __riscv_vfmacc(__riscv_vget_v_f32m2x4_f32m2(val, 3), __riscv_vfcvt_f(__riscv_vzext_vf4(__riscv_vget_v_u8mf2x4_u8mf2(src, 3), vl), vl), vxn, vl)); + __riscv_vsoxseg4ei16(buf, dxn, dst, vl); + } + for (int j = xtab_part; j < xtab_size; j++) + { + buf[x_dtab[j] ] += src_data[y_stab[i] * src_step + x_stab[j] ] * x_vtab[j]; + buf[x_dtab[j] + 1] += src_data[y_stab[i] * src_step + x_stab[j] + 1] * x_vtab[j]; + buf[x_dtab[j] + 2] += src_data[y_stab[i] * src_step + x_stab[j] + 2] * x_vtab[j]; + buf[x_dtab[j] + 3] += src_data[y_stab[i] * src_step + x_stab[j] + 3] * x_vtab[j]; + } + break; + default: + return CV_HAL_ERROR_NOT_IMPLEMENTED; + } + + if (y_dtab[i] != prev) + { + for (int j = 0; j < n; j += vl) + { + vl = __riscv_vsetvl_e32m4(n - j); + auto val = __riscv_vle32_v_f32m4(sum + j, vl); + __riscv_vse8(dst_data + prev * dst_step + j, __riscv_vnclipu(__riscv_vfncvt_xu(val, vl), 0, __RISCV_VXRM_RNU, vl), vl); + __riscv_vse32(sum + j, __riscv_vfmul(__riscv_vle32_v_f32m4(buf + j, vl), y_vtab[i], vl), vl); + } + prev = y_dtab[i]; + } + else + { + for (int j = 0; j < n; j += vl) + { + vl = __riscv_vsetvl_e32m4(n - j); + __riscv_vse32(sum + j, __riscv_vfmacc(__riscv_vle32_v_f32m4(sum + j, vl), y_vtab[i], __riscv_vle32_v_f32m4(buf + j, vl), vl), vl); + } + } + } + for (int j = 0; j < n; j += vl) + { + vl = __riscv_vsetvl_e32m4(n - j); + auto val = __riscv_vle32_v_f32m4(sum + j, vl); + __riscv_vse8(dst_data + prev * dst_step + j, __riscv_vnclipu(__riscv_vfncvt_xu(val, vl), 0, __RISCV_VXRM_RNU, vl), vl); + } + + return CV_HAL_ERROR_OK; +} + +// the algorithm is copied from imgproc/src/resize.cpp, +// in the function static void resizeNN and static void resizeNN_bitexact +static inline int resizeNN(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, double scale_x, double scale_y, int interpolation) +{ + const int cn = CV_ELEM_SIZE(src_type); + if (cn * src_width > std::numeric_limits::max()) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + + std::vector x_ofs(dst_width); + const int ifx = ((src_width << 16) + dst_width / 2) / dst_width; + const int ifx0 = ifx / 2 - src_width % 2; + for (int i = 0; i < dst_width; i++) + { + x_ofs[i] = interpolation == CV_HAL_INTER_NEAREST ? static_cast(std::floor(i * scale_x)) : (ifx * i + ifx0) >> 16; + x_ofs[i] = std::min(x_ofs[i], static_cast(src_width - 1)) * cn; + } + + switch (src_type) + { + case CV_8UC1: + return invoke(dst_height, {resizeNN<1>}, src_data, src_step, src_height, dst_data, dst_step, dst_width, dst_height, scale_y, interpolation, x_ofs.data()); + case CV_8UC2: + return invoke(dst_height, {resizeNN<2>}, src_data, src_step, src_height, dst_data, dst_step, dst_width, dst_height, scale_y, interpolation, x_ofs.data()); + case CV_8UC3: + return invoke(dst_height, {resizeNN<3>}, src_data, src_step, src_height, dst_data, dst_step, dst_width, dst_height, scale_y, interpolation, x_ofs.data()); + case CV_8UC4: + return invoke(dst_height, {resizeNN<4>}, src_data, src_step, src_height, dst_data, dst_step, dst_width, dst_height, scale_y, interpolation, x_ofs.data()); + } + return CV_HAL_ERROR_NOT_IMPLEMENTED; +} + +// the algorithm is copied from imgproc/src/resize.cpp, +// in the functor HResizeLinear, VResizeLinear and resize_bitExact +static inline int resizeLinear(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, double scale_x, double scale_y, int interpolation) +{ + const int cn = CV_ELEM_SIZE(src_type); + if (cn * src_width > std::numeric_limits::max()) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + + std::vector x_ofs0(dst_width), x_ofs1(dst_width); + if (interpolation == CV_HAL_INTER_LINEAR_EXACT) + { + std::vector x_val(dst_width); + for (int i = 0; i < dst_width; i++) + { + double val = (i + 0.5) * scale_x - 0.5; + int x_ofs = static_cast(std::floor(val)); + val -= x_ofs; + + x_val[i] = static_cast(val * 256 - std::remainder(val * 256, 1)); + x_ofs0[i] = static_cast(std::min(std::max(x_ofs , 0), src_width - 1)) * cn; + x_ofs1[i] = static_cast(std::min(std::max(x_ofs + 1, 0), src_width - 1)) * cn; + } + + switch (src_type) + { + case CV_8UC1: + return invoke(dst_height, {resizeLinearExact<1>}, src_data, src_step, src_height, dst_data, dst_step, dst_width, scale_y, x_ofs0.data(), x_ofs1.data(), x_val.data()); + case CV_8UC2: + return invoke(dst_height, {resizeLinearExact<2>}, src_data, src_step, src_height, dst_data, dst_step, dst_width, scale_y, x_ofs0.data(), x_ofs1.data(), x_val.data()); + case CV_8UC3: + return invoke(dst_height, {resizeLinearExact<3>}, src_data, src_step, src_height, dst_data, dst_step, dst_width, scale_y, x_ofs0.data(), x_ofs1.data(), x_val.data()); + case CV_8UC4: + return invoke(dst_height, {resizeLinearExact<4>}, src_data, src_step, src_height, dst_data, dst_step, dst_width, scale_y, x_ofs0.data(), x_ofs1.data(), x_val.data()); + } + } + else + { + std::vector x_val(dst_width); + for (int i = 0; i < dst_width; i++) + { + x_val[i] = (i + 0.5) * scale_x - 0.5; + int x_ofs = static_cast(std::floor(x_val[i])); + x_val[i] -= x_ofs; + + x_ofs0[i] = static_cast(std::min(std::max(x_ofs , 0), src_width - 1)) * cn; + x_ofs1[i] = static_cast(std::min(std::max(x_ofs + 1, 0), src_width - 1)) * cn; + } + + switch (src_type) + { + case CV_8UC1: + return invoke(dst_height, {resizeLinear}, src_data, src_step, src_height, dst_data, dst_step, dst_width, scale_y, x_ofs0.data(), x_ofs1.data(), x_val.data()); + case CV_16UC1: + return invoke(dst_height, {resizeLinear}, src_data, src_step, src_height, dst_data, dst_step, dst_width, scale_y, x_ofs0.data(), x_ofs1.data(), x_val.data()); + case CV_32FC1: + return invoke(dst_height, {resizeLinear}, src_data, src_step, src_height, dst_data, dst_step, dst_width, scale_y, x_ofs0.data(), x_ofs1.data(), x_val.data()); + case CV_8UC2: + return invoke(dst_height, {resizeLinear}, src_data, src_step, src_height, dst_data, dst_step, dst_width, scale_y, x_ofs0.data(), x_ofs1.data(), x_val.data()); + case CV_16UC2: + return invoke(dst_height, {resizeLinear}, src_data, src_step, src_height, dst_data, dst_step, dst_width, scale_y, x_ofs0.data(), x_ofs1.data(), x_val.data()); + case CV_32FC2: + return invoke(dst_height, {resizeLinear}, src_data, src_step, src_height, dst_data, dst_step, dst_width, scale_y, x_ofs0.data(), x_ofs1.data(), x_val.data()); + + case CV_8UC3: + return invoke(dst_height, {resizeLinear}, src_data, src_step, src_height, dst_data, dst_step, dst_width, scale_y, x_ofs0.data(), x_ofs1.data(), x_val.data()); + case CV_16UC3: + return invoke(dst_height, {resizeLinear}, src_data, src_step, src_height, dst_data, dst_step, dst_width, scale_y, x_ofs0.data(), x_ofs1.data(), x_val.data()); + case CV_32FC3: + return invoke(dst_height, {resizeLinear}, src_data, src_step, src_height, dst_data, dst_step, dst_width, scale_y, x_ofs0.data(), x_ofs1.data(), x_val.data()); + case CV_8UC4: + return invoke(dst_height, {resizeLinear}, src_data, src_step, src_height, dst_data, dst_step, dst_width, scale_y, x_ofs0.data(), x_ofs1.data(), x_val.data()); + case CV_16UC4: + return invoke(dst_height, {resizeLinear}, src_data, src_step, src_height, dst_data, dst_step, dst_width, scale_y, x_ofs0.data(), x_ofs1.data(), x_val.data()); + case CV_32FC4: + return invoke(dst_height, {resizeLinear}, src_data, src_step, src_height, dst_data, dst_step, dst_width, scale_y, x_ofs0.data(), x_ofs1.data(), x_val.data()); + } + } + + return CV_HAL_ERROR_NOT_IMPLEMENTED; +} + +// the algorithm is copied from imgproc/src/resize.cpp, +// in the function template static void resizeArea_ and template static void resizeAreaFast_ +static inline int resizeArea(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, double scale_x, double scale_y) +{ + if (scale_x < 1 || scale_y < 1) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + + if (dst_width * 2 == src_width && dst_height * 2 == src_height) + { + switch (src_type) + { + case CV_8UC1: + return invoke(dst_height, {resizeAreaFast}, src_data, src_step, dst_data, dst_step, dst_width); + case CV_16UC1: + return invoke(dst_height, {resizeAreaFast}, src_data, src_step, dst_data, dst_step, dst_width); + case CV_8UC2: + return invoke(dst_height, {resizeAreaFast}, src_data, src_step, dst_data, dst_step, dst_width); + case CV_16UC2: + return invoke(dst_height, {resizeAreaFast}, src_data, src_step, dst_data, dst_step, dst_width); + case CV_8UC3: + return invoke(dst_height, {resizeAreaFast}, src_data, src_step, dst_data, dst_step, dst_width); + case CV_16UC3: + return invoke(dst_height, {resizeAreaFast}, src_data, src_step, dst_data, dst_step, dst_width); + case CV_8UC4: + return invoke(dst_height, {resizeAreaFast}, src_data, src_step, dst_data, dst_step, dst_width); + case CV_16UC4: + return invoke(dst_height, {resizeAreaFast}, src_data, src_step, dst_data, dst_step, dst_width); + } + } + else + { + const int cn = CV_ELEM_SIZE(src_type); + if (cn * sizeof(float) * src_width > std::numeric_limits::max()) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + + std::vector x_stab(src_width * 2), x_dtab(src_width * 2), y_stab(src_height * 2), y_dtab(src_height * 2); + std::vector x_vtab(src_width * 2), y_vtab(src_height * 2); + int xtab_size = computeResizeAreaTab(src_width , dst_width , scale_x, x_stab.data(), x_dtab.data(), x_vtab.data()); + int ytab_size = computeResizeAreaTab(src_height, dst_height, scale_y, y_stab.data(), y_dtab.data(), y_vtab.data()); + + // reorder xtab to avoid data dependency between __riscv_vloxei and __riscv_vsoxei + int range = __riscv_vlenb() * 4 / sizeof(float); + std::vector> idx(dst_width); + for (int i = 0; i < xtab_size; i++) + { + idx[x_dtab[i]].push_back(i); + } + std::list remain; + for (int i = 0; i < dst_width; i++) + { + remain.push_back(i); + } + + std::vector list; + for (int i = 0; i < xtab_size / range; i++) + { + auto it = remain.begin(); + int j; + for (j = 0; j < range; j++) + { + if (it == remain.end()) + break; + ushort val = *it; + + list.push_back(idx[val].back()); + idx[val].pop_back(); + it = idx[val].empty() ? remain.erase(it) : ++it; + } + if (j < range) + break; + } + + int xtab_part = list.size(); + for (auto val : remain) + { + for (ushort id : idx[val]) + { + list.push_back(id); + } + } + for (int i = 0; i < xtab_size; i++) + { + ushort stab = x_stab[i], dtab = x_dtab[i]; + float vtab = x_vtab[i]; + int j = i; + while (true) + { + int k = list[j]; + list[j] = j; + if (k == i) + break; + x_stab[j] = x_stab[k]; + x_dtab[j] = x_dtab[k]; + x_vtab[j] = x_vtab[k]; + j = k; + } + x_stab[j] = stab; + x_dtab[j] = dtab; + x_vtab[j] = vtab; + } + for (int i = 0; i < xtab_size; i++) + { + x_stab[i] *= cn; + x_dtab[i] *= cn; + if (i < xtab_part) + { + x_dtab[i] *= sizeof(float); + } + } + // reorder done + + std::vector tabofs; + for (int i = 0; i < ytab_size; i++) + { + if (i == 0 || y_dtab[i] != y_dtab[i - 1]) + { + tabofs.push_back(i); + } + } + tabofs.push_back(ytab_size); + + switch (src_type) + { + case CV_8UC1: + return invoke(dst_height, {resizeArea<1>}, src_data, src_step, dst_data, dst_step, dst_width, xtab_size, xtab_part, x_stab.data(), x_dtab.data(), x_vtab.data(), y_stab.data(), y_dtab.data(), y_vtab.data(), tabofs.data()); + case CV_8UC2: + return invoke(dst_height, {resizeArea<2>}, src_data, src_step, dst_data, dst_step, dst_width, xtab_size, xtab_part, x_stab.data(), x_dtab.data(), x_vtab.data(), y_stab.data(), y_dtab.data(), y_vtab.data(), tabofs.data()); + case CV_8UC3: + return invoke(dst_height, {resizeArea<3>}, src_data, src_step, dst_data, dst_step, dst_width, xtab_size, xtab_part, x_stab.data(), x_dtab.data(), x_vtab.data(), y_stab.data(), y_dtab.data(), y_vtab.data(), tabofs.data()); + case CV_8UC4: + return invoke(dst_height, {resizeArea<4>}, src_data, src_step, dst_data, dst_step, dst_width, xtab_size, xtab_part, x_stab.data(), x_dtab.data(), x_vtab.data(), y_stab.data(), y_dtab.data(), y_vtab.data(), tabofs.data()); + } + } + + return CV_HAL_ERROR_NOT_IMPLEMENTED; +} + +inline int resize(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, double inv_scale_x, double inv_scale_y, int interpolation) +{ + inv_scale_x = 1 / inv_scale_x; + inv_scale_y = 1 / inv_scale_y; + if (interpolation == CV_HAL_INTER_NEAREST || interpolation == CV_HAL_INTER_NEAREST_EXACT) + return resizeNN(src_type, src_data, src_step, src_width, src_height, dst_data, dst_step, dst_width, dst_height, inv_scale_x, inv_scale_y, interpolation); + if (interpolation == CV_HAL_INTER_LINEAR || interpolation == CV_HAL_INTER_LINEAR_EXACT) + return resizeLinear(src_type, src_data, src_step, src_width, src_height, dst_data, dst_step, dst_width, dst_height, inv_scale_x, inv_scale_y, interpolation); + if (interpolation == CV_HAL_INTER_AREA) + return resizeArea(src_type, src_data, src_step, src_width, src_height, dst_data, dst_step, dst_width, dst_height, inv_scale_x, inv_scale_y); + + return CV_HAL_ERROR_NOT_IMPLEMENTED; +} +} // cv::cv_hal_rvv::resize + +}} + +#endif From 8c7288676e47aebecc423758fb90a2fd00b75154 Mon Sep 17 00:00:00 2001 From: sujal <156937690+5usu@users.noreply.github.com> Date: Mon, 31 Mar 2025 12:17:34 +0530 Subject: [PATCH 52/94] Merge pull request #26682 from 5usu:4.x Adding AddRgbFeature(), and improving robustness in ComputeRgbDistance(). ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- .../components/ot/mtt/objects_associator.cpp | 6 ++++++ .../vasot/src/components/ot/tracklet.cpp | 19 +++++++++---------- .../vasot/src/components/ot/tracklet.hpp | 5 +++++ 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/modules/gapi/src/3rdparty/vasot/src/components/ot/mtt/objects_associator.cpp b/modules/gapi/src/3rdparty/vasot/src/components/ot/mtt/objects_associator.cpp index f2ad032dcb..9a4c3da433 100644 --- a/modules/gapi/src/3rdparty/vasot/src/components/ot/mtt/objects_associator.cpp +++ b/modules/gapi/src/3rdparty/vasot/src/components/ot/mtt/objects_associator.cpp @@ -141,6 +141,12 @@ ObjectsAssociator::ComputeRgbDistance(const std::vector &detections, if (tracking_per_class_ && (detections[d].class_label != tracklets[t]->label)) continue; + // Check if RGB features are available + auto t_rgb_features = tracklets[t]->GetRgbFeatures(); + if (!t_rgb_features || t_rgb_features->empty()) { + continue; // Skip if no RGB features are available + } + // Find best match in rgb feature history float min_dist = 1000.0f; for (const auto &t_rgb_feature : *(tracklets[t]->GetRgbFeatures())) { diff --git a/modules/gapi/src/3rdparty/vasot/src/components/ot/tracklet.cpp b/modules/gapi/src/3rdparty/vasot/src/components/ot/tracklet.cpp index 62e8d10cf6..7327325a4a 100644 --- a/modules/gapi/src/3rdparty/vasot/src/components/ot/tracklet.cpp +++ b/modules/gapi/src/3rdparty/vasot/src/components/ot/tracklet.cpp @@ -7,13 +7,15 @@ #include "tracklet.hpp" #include +#include namespace vas { namespace ot { Tracklet::Tracklet() : id(0), label(-1), association_idx(kNoMatchDetection), status(ST_DEAD), age(0), confidence(0.f), - occlusion_ratio(0.f), association_delta_t(0.f), association_fail_count(0) { + occlusion_ratio(0.f), association_delta_t(0.f), association_fail_count(0), + rgb_features_(std::make_shared>()) { } Tracklet::~Tracklet() { @@ -45,12 +47,13 @@ void Tracklet::RenewTrajectory(const cv::Rect2f &bounding_box) { trajectory_filtered.push_back(bounding_box); } -#define DEFINE_STRING_VAR(var_name, value) \ - std::stringstream __##var_name; \ - __##var_name << value; \ - std::string var_name = __##var_name.str(); +std::deque *Tracklet::GetRgbFeatures() { + return rgb_features_.get(); // Return the raw pointer from the shared_ptr +} -#define ROUND_F(value, scale) (round((value)*scale) / scale) +void Tracklet::AddRgbFeature(const cv::Mat &feature) { + rgb_features_->push_back(feature); +} std::string Tracklet::Serialize() const { #ifdef DUMP_OTAV @@ -97,10 +100,6 @@ std::string Tracklet::Serialize() const { #endif } -std::deque *Tracklet::GetRgbFeatures() { - return nullptr; -} - ZeroTermImagelessTracklet::ZeroTermImagelessTracklet() : Tracklet(), birth_count(1) { } diff --git a/modules/gapi/src/3rdparty/vasot/src/components/ot/tracklet.hpp b/modules/gapi/src/3rdparty/vasot/src/components/ot/tracklet.hpp index 762e3f6ea6..4ed9042d7a 100644 --- a/modules/gapi/src/3rdparty/vasot/src/components/ot/tracklet.hpp +++ b/modules/gapi/src/3rdparty/vasot/src/components/ot/tracklet.hpp @@ -13,6 +13,7 @@ #include #include +#include namespace vas { namespace ot { @@ -45,6 +46,7 @@ class Tracklet { virtual void RenewTrajectory(const cv::Rect2f &bounding_box); virtual std::deque *GetRgbFeatures(); + void AddRgbFeature(const cv::Mat &feature); virtual std::string Serialize() const; // Returns key:value with comma separated format public: @@ -63,6 +65,9 @@ class Tracklet { std::deque trajectory_filtered; cv::Rect2f predicted; // Result from Kalman prediction. It is for debugging (OTAV) mutable std::vector otav_msg; // Messages for OTAV + + private: + std::shared_ptr> rgb_features_; }; class ZeroTermImagelessTracklet : public Tracklet { From ec1cbe294a05e89535a64923a3873c1a94e5f699 Mon Sep 17 00:00:00 2001 From: Yuantao Feng Date: Mon, 31 Mar 2025 15:49:37 +0800 Subject: [PATCH 53/94] Merge pull request #27162 from fengyuentau:4x/hal_rvv/copyMask HAL: added copyToMask and implemented in hal_rvv #27162 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- 3rdparty/hal_rvv/hal_rvv.hpp | 1 + 3rdparty/hal_rvv/hal_rvv_1p0/copy_mask.hpp | 194 +++++++++++++++++++++ modules/core/src/copy.cpp | 12 ++ modules/core/src/hal_replacement.hpp | 16 ++ 4 files changed, 223 insertions(+) create mode 100644 3rdparty/hal_rvv/hal_rvv_1p0/copy_mask.hpp diff --git a/3rdparty/hal_rvv/hal_rvv.hpp b/3rdparty/hal_rvv/hal_rvv.hpp index c04d3b9899..f1d8ccb2e4 100644 --- a/3rdparty/hal_rvv/hal_rvv.hpp +++ b/3rdparty/hal_rvv/hal_rvv.hpp @@ -45,6 +45,7 @@ #include "hal_rvv_1p0/qr.hpp" // core #include "hal_rvv_1p0/svd.hpp" // core #include "hal_rvv_1p0/sqrt.hpp" // core +#include "hal_rvv_1p0/copy_mask.hpp" // core #include "hal_rvv_1p0/moments.hpp" // imgproc #include "hal_rvv_1p0/filter.hpp" // imgproc diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/copy_mask.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/copy_mask.hpp new file mode 100644 index 0000000000..f13b8bc22e --- /dev/null +++ b/3rdparty/hal_rvv/hal_rvv_1p0/copy_mask.hpp @@ -0,0 +1,194 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2025, SpaceMIT Inc., all rights reserved. +// Third party copyrights are property of their respective owners. + +#ifndef OPENCV_HAL_RVV_COPY_MASK_HPP_INCLUDED +#define OPENCV_HAL_RVV_COPY_MASK_HPP_INCLUDED + +#include + +namespace cv { namespace cv_hal_rvv { + +#undef cv_hal_copyToMasked +#define cv_hal_copyToMasked cv::cv_hal_rvv::copyToMasked + +namespace { + +#define CV_HAL_RVV_COPY_MASK_eXc1(X, mask_lmul) \ +static int copyToMasked_e##X##c1(const uchar *src_data, size_t src_step, const uchar *mask_data, size_t mask_step, \ + uchar *dst_data, size_t dst_step, int width, int height) { \ + for (; height--; mask_data += mask_step, src_data += src_step, dst_data += dst_step) { \ + const uint##X##_t *src = (const uint##X##_t*)src_data; \ + uint##X##_t *dst = (uint##X##_t*)dst_data; \ + int vl; \ + for (int i = 0; i < width; i += vl) { \ + vl = __riscv_vsetvl_e8m##mask_lmul(width - i); \ + auto m = __riscv_vmsne(__riscv_vle8_v_u8m##mask_lmul(mask_data + i, vl), 0, vl); \ + auto v = __riscv_vle##X##_v_u##X##m8_m(m, src + i, vl); \ + __riscv_vse##X##_v_u##X##m8_m(m, dst + i, v, vl); \ + } \ + } \ + return CV_HAL_ERROR_OK; \ +} + +CV_HAL_RVV_COPY_MASK_eXc1(8, 8) +CV_HAL_RVV_COPY_MASK_eXc1(16, 4) +CV_HAL_RVV_COPY_MASK_eXc1(32, 2) +CV_HAL_RVV_COPY_MASK_eXc1(64, 1) + +#define CV_HAL_RVV_COPY_MASK_eXc3(X, mask_lmul) \ +static int copyToMasked_e##X##c3(const uchar *src_data, size_t src_step, const uchar *mask_data, size_t mask_step, \ + uchar *dst_data, size_t dst_step, int width, int height) { \ + for (; height--; mask_data += mask_step, src_data += src_step, dst_data += dst_step) { \ + const uint##X##_t *src = (const uint##X##_t*)src_data; \ + uint##X##_t *dst = (uint##X##_t*)dst_data; \ + int vl; \ + for (int i = 0; i < width; i += vl) { \ + vl = __riscv_vsetvl_e8m##mask_lmul(width - i); \ + auto m = __riscv_vmsne(__riscv_vle8_v_u8m##mask_lmul(mask_data + i, vl), 0, vl); \ + auto v = __riscv_vlseg3e##X##_v_u##X##m2x3_m(m, src + 3 * i, vl); \ + __riscv_vsseg3e##X##_v_u##X##m2x3_m(m, dst + 3 * i, v, vl); \ + } \ + } \ + return CV_HAL_ERROR_OK; \ +} + +CV_HAL_RVV_COPY_MASK_eXc3(8, 2) +CV_HAL_RVV_COPY_MASK_eXc3(16, 1) +CV_HAL_RVV_COPY_MASK_eXc3(32, f2) +CV_HAL_RVV_COPY_MASK_eXc3(64, f4) + +static int copyToMasked_e64c2(const uchar *src_data, size_t src_step, + const uchar *mask_data, size_t mask_step, + uchar *dst_data, size_t dst_step, int width, + int height) { + for (; height--; mask_data += mask_step, src_data += src_step, dst_data += dst_step) { + const uint64_t *src = (const uint64_t *)src_data; + uint64_t *dst = (uint64_t *)dst_data; + int vl; + for (int i = 0; i < width; i += vl) { + vl = __riscv_vsetvl_e8mf2(width - i); + auto m = __riscv_vmsne(__riscv_vle8_v_u8mf2(mask_data + i, vl), 0, vl); + auto v = __riscv_vlseg2e64_v_u64m4x2_m(m, src + 2 * i, vl); + __riscv_vsseg2e64_v_u64m4x2_m(m, dst + 2 * i, v, vl); + } + } + return CV_HAL_ERROR_OK; +} + +static int copyToMasked_e64c4(const uchar *src_data, size_t src_step, + const uchar *mask_data, size_t mask_step, + uchar *dst_data, size_t dst_step, int width, + int height) { + for (; height--; mask_data += mask_step, src_data += src_step, dst_data += dst_step) { + const uint64_t *src = (const uint64_t *)src_data; + uint64_t *dst = (uint64_t *)dst_data; + int vl; + for (int i = 0; i < width; i += vl) { + vl = __riscv_vsetvl_e8mf4(width - i); + auto m = __riscv_vmsne(__riscv_vle8_v_u8mf4(mask_data + i, vl), 0, vl); + auto v = __riscv_vlseg4e64_v_u64m2x4_m(m, src + 4 * i, vl); + __riscv_vsseg4e64_v_u64m2x4_m(m, dst + 4 * i, v, vl); + } + } + return CV_HAL_ERROR_OK; +} + +} // anonymous + +using CopyToMaskedFunc = int (*)(const uchar*, size_t, const uchar*, size_t, uchar*, size_t, int, int); +inline int copyToMasked(const uchar *src_data, size_t src_step, uchar *dst_data, size_t dst_step, int width, int height, + int type, const uchar *mask_data, size_t mask_step, int mask_type) { + int depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type); + int mdepth = CV_MAT_DEPTH(mask_type), mcn = CV_MAT_CN(mask_type); + + if (mcn > 1 || mdepth != CV_8U) { + return CV_HAL_ERROR_NOT_IMPLEMENTED; + } + + CopyToMaskedFunc func = nullptr; + switch (depth) { + case CV_8U: {} + case CV_8S: switch (cn) { + case 1: func = copyToMasked_e8c1; break; + case 2: func = copyToMasked_e16c1; break; + case 3: func = copyToMasked_e8c3; break; + case 4: func = copyToMasked_e32c1; break; + case 6: func = copyToMasked_e16c3; break; + case 8: func = copyToMasked_e64c1; break; + default: func = nullptr; + }; break; + case CV_16U: {} + case CV_16S: switch (cn) { + case 1: func = copyToMasked_e16c1; break; + case 2: func = copyToMasked_e32c1; break; + case 3: func = copyToMasked_e16c3; break; + case 4: func = copyToMasked_e64c1; break; + case 6: func = copyToMasked_e32c3; break; + case 8: func = copyToMasked_e64c2; break; + default: func = nullptr; break; + }; break; + case CV_32S: {} + case CV_32F: switch (cn) { + case 1: func = copyToMasked_e32c1; break; + case 2: func = copyToMasked_e64c1; break; + case 3: func = copyToMasked_e32c3; break; + case 4: func = copyToMasked_e64c2; break; + case 6: func = copyToMasked_e64c3; break; + case 8: func = copyToMasked_e64c4; break; + default: func = nullptr; break; + }; break; + case CV_64F: switch (cn) { + case 1: func = copyToMasked_e64c1; break; + case 2: func = copyToMasked_e64c2; break; + case 3: func = copyToMasked_e64c3; break; + case 4: func = copyToMasked_e64c4; break; + default: func = nullptr; break; + }; break; + default: func = nullptr; + } + + if (func == nullptr) { + return CV_HAL_ERROR_NOT_IMPLEMENTED; + } + + static const size_t elem_size_tab[CV_DEPTH_MAX] = { + sizeof(uchar), sizeof(schar), + sizeof(ushort), sizeof(short), + sizeof(int), sizeof(float), + sizeof(int64_t), 0, + }; + CV_Assert(elem_size_tab[depth]); + + bool src_continuous = (src_step == width * elem_size_tab[depth] * cn || (src_step != width * elem_size_tab[depth] * cn && height == 1)); + bool dst_continuous = (dst_step == width * elem_size_tab[depth] * cn || (dst_step != width * elem_size_tab[depth] * cn && height == 1)); + bool mask_continuous = (mask_step == static_cast(width)); + size_t nplanes = 1; + int _width = width, _height = height; + if (!src_continuous || !dst_continuous || !mask_continuous) { + nplanes = height; + _width = width * mcn; + _height = 1; + } + + auto _src = src_data; + auto _mask = mask_data; + auto _dst = dst_data; + for (size_t i = 0; i < nplanes; i++) { + if (!src_continuous || !dst_continuous || !mask_continuous) { + _src = src_data + src_step * i; + _mask = mask_data + mask_step * i; + _dst = dst_data + dst_step * i; + } + func(_src, src_step, _mask, mask_step, _dst, dst_step, _width, _height); + } + + return CV_HAL_ERROR_OK; +} + +}} // cv::cv_hal_rvv + +#endif diff --git a/modules/core/src/copy.cpp b/modules/core/src/copy.cpp index 5c8af185b5..9f4fa8604a 100644 --- a/modules/core/src/copy.cpp +++ b/modules/core/src/copy.cpp @@ -459,6 +459,18 @@ void Mat::copyTo( OutputArray _dst, InputArray _mask ) const } CV_IPP_RUN_FAST(ipp_copyTo(*this, dst, mask)) + if ( this->dims <= 2 ) { + if ( this->size() == dst.size() && this->size() == dst.size() ) { + CALL_HAL(copyToMask, cv_hal_copyToMasked, this->data, this->step, dst.data, dst.step, this->cols, this->rows, this->type(), mask.data, mask.step, mask.type()); + } + } + else if ( this->isContinuous() && dst.isContinuous() && mask.isContinuous() ) + { + size_t sz = this->total(); + if (sz < INT_MAX) { + CALL_HAL(copyToMask, cv_hal_copyToMasked, this->data, 0, dst.data, 0, (int)sz, 1, this->type(), mask.data, 0, mask.type()); + } + } size_t esz = colorMask ? elemSize1() : elemSize(); BinaryFunc copymask = getCopyMaskFunc(esz); diff --git a/modules/core/src/hal_replacement.hpp b/modules/core/src/hal_replacement.hpp index 517e9b8f0b..8e566174f8 100644 --- a/modules/core/src/hal_replacement.hpp +++ b/modules/core/src/hal_replacement.hpp @@ -1107,6 +1107,22 @@ inline int hal_ni_transpose2d(const uchar* src_data, size_t src_step, uchar* dst #define cv_hal_transpose2d hal_ni_transpose2d //! @endcond +/** + @brief copyTo with mask + @param src_data, src_step Source image + @param dst_data, dst_step Destination image + @param width, height Image dimensions of source, destination and mask + @param type Type of source and destination images, for example CV_8UC1 or CV_32FC3 + @param mask_data, mask_step, mask_type Mask +*/ +inline int hal_ni_copyToMasked(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, int width, int height, + int type, const uchar* mask_data, size_t mask_step, int mask_type) +{ return CV_HAL_ERROR_NOT_IMPLEMENTED; } + +//! @cond IGNORED +#define cv_hal_copyToMasked hal_ni_copyToMasked +//! @endcond + //! @} From 1f468dd586804a2648a9a007a7b62b0f652bd2db Mon Sep 17 00:00:00 2001 From: Kumataro Date: Mon, 31 Mar 2025 17:01:22 +0900 Subject: [PATCH 54/94] Merge pull request #27169 from Kumataro:fix27168 Imgcodec: gif: remove unnecessary warning #27169 Close https://github.com/opencv/opencv/issues/27168 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- modules/imgcodecs/src/grfmt_gif.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/imgcodecs/src/grfmt_gif.cpp b/modules/imgcodecs/src/grfmt_gif.cpp index 4490e72309..d4c0099f1f 100644 --- a/modules/imgcodecs/src/grfmt_gif.cpp +++ b/modules/imgcodecs/src/grfmt_gif.cpp @@ -392,8 +392,7 @@ bool GifDecoder::lzwDecode() { if (code < colorTableSize) { imgCodeStream[idx++] = (uchar)code; } else { - CV_LOG_WARNING(NULL, "Too long LZW length in GIF."); - CV_Assert(idx + lzwExtraTable[code].length <= width * height); + CV_Check(idx, idx + lzwExtraTable[code].length <= width * height, "Too long LZW length in GIF."); for (int i = 0; i < lzwExtraTable[code].length - 1; i++) { imgCodeStream[idx++] = lzwExtraTable[code].prefix[i]; } From 09c71aed141210bf2b14582974ed9d231c24edd5 Mon Sep 17 00:00:00 2001 From: Kumataro Date: Mon, 31 Mar 2025 17:31:14 +0900 Subject: [PATCH 55/94] Merge pull request #27107 from Kumataro:fix27105 build: Check supported C++ standard features and user setting #27107 Close #27105 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [x] The feature is well documented and sample code can be built with the project CMake --- cmake/OpenCVDetectCXXCompiler.cmake | 31 +++++++++++-------- cmake/checks/cxx11.cpp | 13 -------- .../config_reference.markdown | 15 +++++++++ 3 files changed, 33 insertions(+), 26 deletions(-) delete mode 100644 cmake/checks/cxx11.cpp diff --git a/cmake/OpenCVDetectCXXCompiler.cmake b/cmake/OpenCVDetectCXXCompiler.cmake index 54cec78ba3..a8a6966108 100644 --- a/cmake/OpenCVDetectCXXCompiler.cmake +++ b/cmake/OpenCVDetectCXXCompiler.cmake @@ -207,30 +207,35 @@ if(CMAKE_VERSION VERSION_LESS "3.1") endforeach() endif() +# See https://github.com/opencv/opencv/issues/27105 +# - CMAKE_COMPILE_FEATURES is used to detect what features are available by the compiler. +# - CMAKE_CXX_STANDARD is used to detect what features are available in this configuration. if(NOT OPENCV_SKIP_CMAKE_CXX_STANDARD) + if(DEFINED CMAKE_CXX_STANDARD AND ((CMAKE_CXX_STANDARD EQUAL 98) OR (CMAKE_CXX_STANDARD LESS 11))) + message(FATAL_ERROR "OpenCV 4.x requires C++11, but your configuration does not enable(CMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}).") + endif() + ocv_update(CMAKE_CXX_STANDARD 11) ocv_update(CMAKE_CXX_STANDARD_REQUIRED TRUE) ocv_update(CMAKE_CXX_EXTENSIONS OFF) # use -std=c++11 instead of -std=gnu++11 - if(CMAKE_CXX11_COMPILE_FEATURES) +endif() + +# Meta-feature "cxx_std_XX" in CMAKE_CXX_COMPILE_FEATURES are supported in CMake 3.8+. +# - See https://cmake.org/cmake/help/latest/release/3.8.html +# For CMake 3.7-, use CMAKE_CXXxx_COMPILE_FEATURES instead of it. +if(CMAKE_CXX11_COMPILE_FEATURES OR ("cxx_std_11" IN_LIST CMAKE_CXX_COMPILE_FEATURES)) + if((NOT DEFINED CMAKE_CXX_STANDARD) OR (CMAKE_CXX_STANDARD GREATER_EQUAL 11)) set(HAVE_CXX11 ON) endif() - if(CMAKE_CXX17_COMPILE_FEATURES) - set(HAVE_CXX17 ON) - endif() endif() -if(NOT HAVE_CXX11) - ocv_check_compiler_flag(CXX "" HAVE_CXX11 "${OpenCV_SOURCE_DIR}/cmake/checks/cxx11.cpp") - if(NOT HAVE_CXX11) - ocv_check_compiler_flag(CXX "-std=c++11" HAVE_STD_CXX11 "${OpenCV_SOURCE_DIR}/cmake/checks/cxx11.cpp") - if(HAVE_STD_CXX11) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") - set(HAVE_CXX11 ON) - endif() +if(CMAKE_CXX17_COMPILE_FEATURES OR ("cxx_std_17" IN_LIST CMAKE_CXX_COMPILE_FEATURES)) + if((NOT DEFINED CMAKE_CXX_STANDARD) OR (CMAKE_CXX_STANDARD GREATER_EQUAL 17)) + set(HAVE_CXX17 ON) endif() endif() if(NOT HAVE_CXX11) - message(FATAL_ERROR "OpenCV 4.x requires C++11") + message(FATAL_ERROR "OpenCV 4.x requires C++11, but your compiler does not support it") endif() set(__OPENCV_ENABLE_ATOMIC_LONG_LONG OFF) diff --git a/cmake/checks/cxx11.cpp b/cmake/checks/cxx11.cpp deleted file mode 100644 index ea719d6248..0000000000 --- a/cmake/checks/cxx11.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1600) -// OK -#else -#error "C++11 is not supported" -#endif - -static int test() { return 0; } - -int main() -{ - auto res = test(); - return res; -} diff --git a/doc/tutorials/introduction/config_reference/config_reference.markdown b/doc/tutorials/introduction/config_reference/config_reference.markdown index 91e1067ceb..67a2c60863 100644 --- a/doc/tutorials/introduction/config_reference/config_reference.markdown +++ b/doc/tutorials/introduction/config_reference/config_reference.markdown @@ -64,6 +64,21 @@ Only 0- and 1-level deep module locations are supported, following command will cmake -DOPENCV_EXTRA_MODULES_PATH=../opencv_contrib ../opencv ``` +## Build with C++ Standard setting {#tutorial_config_reference_general_cxx_standard} + +`CMAKE_CXX_STANDARD` option can be used to set C++ standard settings for OpenCV building. + +```.sh +cmake -DCMAKE_CXX_STANDARD=17 ../opencv +cmake --build . +``` + +- C++11 is default/required/recommended for OpenCV 4.x. C++17 is default/required/recomended for OpenCV 5.x. +- If your compiler does not support required C++ Standard features, OpenCV configuration should be fail. +- If you set older C++ Standard than required, OpenCV configuration should be fail. + For workaround, `OPENCV_SKIP_CMAKE_CXX_STANDARD` option can be used to skip `CMAKE_CXX_STANDARD` version check. +- If you set newer C++ Standard than recomended, numerous warnings may appear or OpenCV build may fail. + ## Debug build {#tutorial_config_reference_general_debug} From 009fdbbea2c2a926f169c2be8a34de22bdae373c Mon Sep 17 00:00:00 2001 From: Maksim Shabunin Date: Sat, 8 Mar 2025 15:00:37 +0300 Subject: [PATCH 56/94] build: fix OpenBLAS detection on Linux --- cmake/OpenCVFindLAPACK.cmake | 88 +++++++++++---------- cmake/OpenCVFindOpenBLAS.cmake | 139 +++++++++------------------------ 2 files changed, 85 insertions(+), 142 deletions(-) diff --git a/cmake/OpenCVFindLAPACK.cmake b/cmake/OpenCVFindLAPACK.cmake index ba682c1c04..2ec188916c 100644 --- a/cmake/OpenCVFindLAPACK.cmake +++ b/cmake/OpenCVFindLAPACK.cmake @@ -44,6 +44,14 @@ macro(_find_header_file_in_dirs VAR NAME) endmacro() macro(ocv_lapack_check) + cmake_parse_arguments(LAPACK "" "IMPL;CBLAS_H;LAPACKE_H" "INCLUDE_DIR;LIBRARIES" ${ARGN}) + + ocv_debug_message("LAPACK_IMPL=${LAPACK_IMPL}") + ocv_debug_message("LAPACK_CBLAS_H=${LAPACK_CBLAS_H}") + ocv_debug_message("LAPACK_LAPACKE_H=${LAPACK_LAPACKE_H}") + ocv_debug_message("LAPACK_INCLUDE_DIR=${LAPACK_INCLUDE_DIR}") + ocv_debug_message("LAPACK_LIBRARIES=${LAPACK_LIBRARIES}") + string(REGEX REPLACE "[^a-zA-Z0-9_]" "_" _lapack_impl "${LAPACK_IMPL}") message(STATUS "LAPACK(${LAPACK_IMPL}): LAPACK_LIBRARIES: ${LAPACK_LIBRARIES}") _find_header_file_in_dirs(OPENCV_CBLAS_H_PATH_${_lapack_impl} "${LAPACK_CBLAS_H}" "${LAPACK_INCLUDE_DIR}") @@ -169,34 +177,31 @@ if(WITH_LAPACK) if(NOT LAPACK_LIBRARIES AND NOT OPENCV_LAPACK_DISABLE_MKL) include(cmake/OpenCVFindMKL.cmake) if(HAVE_MKL) - set(LAPACK_INCLUDE_DIR ${MKL_INCLUDE_DIRS}) - set(LAPACK_LIBRARIES ${MKL_LIBRARIES}) - set(LAPACK_CBLAS_H "mkl_cblas.h") - set(LAPACK_LAPACKE_H "mkl_lapack.h") - set(LAPACK_IMPL "MKL") - ocv_lapack_check() + ocv_lapack_check(IMPL "MKL" + CBLAS_H "mkl_cblas.h" + LAPACKE_H "mkl_lapack.h" + INCLUDE_DIR "${MKL_INCLUDE_DIRS}" + LIBRARIES "${MKL_LIBRARIES}") endif() endif() if(NOT LAPACK_LIBRARIES) include(cmake/OpenCVFindOpenBLAS.cmake) if(OpenBLAS_FOUND) - set(LAPACK_INCLUDE_DIR ${OpenBLAS_INCLUDE_DIR}) - set(LAPACK_LIBRARIES ${OpenBLAS_LIB}) - set(LAPACK_CBLAS_H "cblas.h") - set(LAPACK_LAPACKE_H "lapacke.h") - set(LAPACK_IMPL "OpenBLAS") - ocv_lapack_check() + ocv_lapack_check(IMPL "OpenBLAS" + CBLAS_H "cblas.h" + LAPACKE_H "lapacke.h" + INCLUDE_DIR "${OpenBLAS_INCLUDE_DIRS}" + LIBRARIES "${OpenBLAS_LIBRARIES}") endif() endif() if(NOT LAPACK_LIBRARIES AND UNIX) include(cmake/OpenCVFindAtlas.cmake) if(ATLAS_FOUND) - set(LAPACK_INCLUDE_DIR ${Atlas_INCLUDE_DIR}) - set(LAPACK_LIBRARIES ${Atlas_LIBRARIES}) - set(LAPACK_CBLAS_H "cblas.h") - set(LAPACK_LAPACKE_H "lapacke.h") - set(LAPACK_IMPL "Atlas") - ocv_lapack_check() + ocv_lapack_check(IMPL "Atlas" + CBLAS_H "cblas.h" + LAPACKE_H "lapacke.h" + INCLUDE_DIR "${Atlas_INCLUDE_DIR}" + LIBRARIES "${Atlas_LIBRARIES}") endif() endif() endif() @@ -214,24 +219,26 @@ if(WITH_LAPACK) find_path(MKL_LAPACKE_INCLUDE_DIR "mkl_lapack.h") endif() if(MKL_LAPACKE_INCLUDE_DIR AND NOT OPENCV_LAPACK_DISABLE_MKL) - set(LAPACK_INCLUDE_DIR ${MKL_LAPACKE_INCLUDE_DIR}) - set(LAPACK_CBLAS_H "mkl_cblas.h") - set(LAPACK_LAPACKE_H "mkl_lapack.h") - set(LAPACK_IMPL "LAPACK/MKL") - ocv_lapack_check() + ocv_lapack_check(IMPL "LAPACK/MKL" + CBLAS_H "mkl_cblas.h" + LAPACKE_H "mkl_lapack.h" + INCLUDE_DIR "${MKL_LAPACKE_INCLUDE_DIR}" + LIBRARIES "${LAPACK_LIBRARIES}") endif() if(NOT HAVE_LAPACK) - if(LAPACKE_INCLUDE_DIR) - set(LAPACK_INCLUDE_DIR ${LAPACKE_INCLUDE_DIR}) - set(LAPACK_CBLAS_H "cblas.h") - set(LAPACK_LAPACKE_H "lapacke.h") - set(LAPACK_IMPL "LAPACK/Generic") - ocv_lapack_check() + if(NOT DEFINED CBLAS_INCLUDE_DIR) + find_path(CBLAS_INCLUDE_DIR "cblas.h") + endif() + if(CBLAS_INCLUDE_DIR AND LAPACKE_INCLUDE_DIR) + ocv_lapack_check(IMPL "LAPACK/Generic" + CBLAS_H "cblas.h" + LAPACKE_H "lapacke.h" + INCLUDE_DIR "${CBLAS_INCLUDE_DIR}" "${LAPACKE_INCLUDE_DIR}" + LIBRARIES "${LAPACK_LIBRARIES}") elseif(APPLE) - set(LAPACK_CBLAS_H "Accelerate/Accelerate.h") - set(LAPACK_LAPACKE_H "Accelerate/Accelerate.h") - set(LAPACK_IMPL "LAPACK/Apple") - ocv_lapack_check() + ocv_lapack_check(IMPL "LAPACK/Apple" + CBLAS_H "Accelerate/Accelerate.h" + LAPACKE_H "Accelerate/Accelerate.h") endif() endif() endif() @@ -242,16 +249,17 @@ if(WITH_LAPACK) endif() if(NOT LAPACK_LIBRARIES AND APPLE AND NOT OPENCV_LAPACK_FIND_PACKAGE_ONLY) - set(LAPACK_INCLUDE_DIR "") - set(LAPACK_LIBRARIES "-framework Accelerate") - set(LAPACK_CBLAS_H "Accelerate/Accelerate.h") - set(LAPACK_LAPACKE_H "Accelerate/Accelerate.h") - set(LAPACK_IMPL "Apple") - ocv_lapack_check() + ocv_lapack_check(IMPL "Apple" + CBLAS_H "Accelerate/Accelerate.h" + LAPACKE_H "Accelerate/Accelerate.h" + LIBRARIES "-framework Accelerate") endif() if(NOT HAVE_LAPACK AND LAPACK_LIBRARIES AND LAPACK_CBLAS_H AND LAPACK_LAPACKE_H) - ocv_lapack_check() + ocv_lapack_check(IMPL "Unknown" + CBLAS_H ${LAPACK_CBLAS_H} + LAPACKE_H ${LAPACK_LAPACKE_H} + LIBRARIES "${LAPACK_LIBRARIES}") endif() set(LAPACK_INCLUDE_DIR ${LAPACK_INCLUDE_DIR} CACHE PATH "Path to BLAS include dir" FORCE) diff --git a/cmake/OpenCVFindOpenBLAS.cmake b/cmake/OpenCVFindOpenBLAS.cmake index 4e3f0cc210..15771f7ebb 100644 --- a/cmake/OpenCVFindOpenBLAS.cmake +++ b/cmake/OpenCVFindOpenBLAS.cmake @@ -1,107 +1,42 @@ -#COPYRIGHT -# -#All contributions by the University of California: -#Copyright (c) 2014, 2015, The Regents of the University of California (Regents) -#All rights reserved. -# -#All other contributions: -#Copyright (c) 2014, 2015, the respective contributors -#All rights reserved. -# -#Caffe uses a shared copyright model: each contributor holds copyright over -#their contributions to Caffe. The project versioning records all such -#contribution and copyright details. If a contributor wants to further mark -#their specific copyright on a particular contribution, they should indicate -#their copyright solely in the commit message of the change when it is -#committed. -# -#LICENSE -# -#Redistribution and use in source and binary forms, with or without -#modification, are permitted provided that the following conditions are met: -# -#1. Redistributions of source code must retain the above copyright notice, this -# list of conditions and the following disclaimer. -#2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -#THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -#ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -#WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -#DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -#ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -#(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -#LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -#ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -#(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -#SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# -#CONTRIBUTION AGREEMENT -# -#By contributing to the BVLC/caffe repository through pull-request, comment, -#or otherwise, the contributor releases their content to the -#license and copyright terms herein. +# Search for OpenBLAS library -SET(Open_BLAS_INCLUDE_SEARCH_PATHS - $ENV{OpenBLAS_HOME} - $ENV{OpenBLAS_HOME}/include - $ENV{OpenBLAS_HOME}/include/openblas - /opt/OpenBLAS/include - /usr/local/include/openblas - /usr/include/openblas - /usr/local/include/openblas-base - /usr/include/openblas-base - /usr/local/include - /usr/include -) +if(NOT OpenBLAS_FOUND AND NOT SKIP_OPENBLAS_PACKAGE) + find_package(OpenBLAS QUIET) + if(OpenBLAS_FOUND) + message(STATUS "Found OpenBLAS package") + endif() +endif() -SET(Open_BLAS_LIB_SEARCH_PATHS - $ENV{OpenBLAS} - $ENV{OpenBLAS}/lib - $ENV{OpenBLAS_HOME} - $ENV{OpenBLAS_HOME}/lib - /opt/OpenBLAS/lib - /usr/local/lib64 - /usr/local/lib - /lib/openblas-base - /lib64/ - /lib/ - /usr/lib/openblas-base - /usr/lib64 - /usr/lib - ) +if(NOT OpenBLAS_FOUND) + find_library(OpenBLAS_LIBRARIES NAMES openblas PATHS ENV "OpenBLAS" ENV "OpenBLAS_HOME" PATH_SUFFIXES "lib" NO_DEFAULT_PATH) + find_path(OpenBLAS_INCLUDE_DIRS NAMES cblas.h PATHS ENV "OpenBLAS" ENV "OpenBLAS_HOME" PATH_SUFFIXES "include" NO_DEFAULT_PATH) + find_path(OpenBLAS_LAPACKE_DIR NAMES lapacke.h PATHS "${OpenBLAS_INCLUDE_DIRS}" ENV "OpenBLAS" ENV "OpenBLAS_HOME" PATH_SUFFIXES "include" NO_DEFAULT_PATH) + if(OpenBLAS_LIBRARIES AND OpenBLAS_INCLUDE_DIRS) + message(STATUS "Found OpenBLAS using environment hint") + set(OpenBLAS_FOUND ON) + else() + ocv_clear_vars(OpenBLAS_LIBRARIES OpenBLAS_INCLUDE_DIRS) + endif() +endif() -FIND_PATH(OpenBLAS_INCLUDE_DIR NAMES cblas.h PATHS ${Open_BLAS_INCLUDE_SEARCH_PATHS} NO_DEFAULT_PATH) -FIND_LIBRARY(OpenBLAS_LIB NAMES openblas libopenblas PATHS ${Open_BLAS_LIB_SEARCH_PATHS} NO_DEFAULT_PATH) +if(NOT OpenBLAS_FOUND) + find_library(OpenBLAS_LIBRARIES NAMES openblas) + find_path(OpenBLAS_INCLUDE_DIRS NAMES cblas.h) + find_path(OpenBLAS_LAPACKE_DIR NAMES lapacke.h PATHS "${OpenBLAS_INCLUDE_DIRS}") + if(OpenBLAS_LIBRARIES AND OpenBLAS_INCLUDE_DIRS) + message(STATUS "Found OpenBLAS in the system") + set(OpenBLAS_FOUND ON) + else() + ocv_clear_vars(OpenBLAS_LIBRARIES OpenBLAS_INCLUDE_DIRS) + endif() +endif() -SET(OpenBLAS_FOUND ON) +if(OpenBLAS_FOUND) + if(OpenBLAS_LAPACKE_DIR) + set(OpenBLAS_INCLUDE_DIRS "${OpenBLAS_INCLUDE_DIRS};${OpenBLAS_LAPACKE_DIR}") + endif() + message(STATUS "OpenBLAS_LIBRARIES=${OpenBLAS_LIBRARIES}") + message(STATUS "OpenBLAS_INCLUDE_DIRS=${OpenBLAS_INCLUDE_DIRS}") +endif() -# Check include files -IF(NOT OpenBLAS_INCLUDE_DIR) - SET(OpenBLAS_FOUND OFF) - MESSAGE(STATUS "Could not find OpenBLAS include. Turning OpenBLAS_FOUND off") -ENDIF() - -# Check libraries -IF(NOT OpenBLAS_LIB) - SET(OpenBLAS_FOUND OFF) - MESSAGE(STATUS "Could not find OpenBLAS lib. Turning OpenBLAS_FOUND off") -ENDIF() - -IF (OpenBLAS_FOUND) - IF (NOT OpenBLAS_FIND_QUIETLY) - MESSAGE(STATUS "Found OpenBLAS libraries: ${OpenBLAS_LIB}") - MESSAGE(STATUS "Found OpenBLAS include: ${OpenBLAS_INCLUDE_DIR}") - ENDIF (NOT OpenBLAS_FIND_QUIETLY) -ELSE (OpenBLAS_FOUND) - IF (OpenBLAS_FIND_REQUIRED) - MESSAGE(FATAL_ERROR "Could not find OpenBLAS") - ENDIF (OpenBLAS_FIND_REQUIRED) -ENDIF (OpenBLAS_FOUND) - -MARK_AS_ADVANCED( - OpenBLAS_INCLUDE_DIR - OpenBLAS_LIB - OpenBLAS -) +mark_as_advanced(OpenBLAS_LIBRARIES OpenBLAS_INCLUDE_DIRS OpenBLAS_LAPACKE_DIR) From c8e88d898437f43c714c89e752189b2873a5a4bf Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Wed, 2 Apr 2025 21:21:56 +0300 Subject: [PATCH 57/94] Merge pull request #27185 from MaximSmolskiy:specify_dls_and_upnp_mappings_to_epnp_in_all_places_for_solvepnp_tests Specify DLS and UPnP mappings to EPnP in all places for solvePnP* tests #27185 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [x] The feature is well documented and sample code can be built with the project CMake --- modules/calib3d/test/test_solvepnp_ransac.cpp | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/modules/calib3d/test/test_solvepnp_ransac.cpp b/modules/calib3d/test/test_solvepnp_ransac.cpp index 66412bcfca..5bd2a37ca1 100644 --- a/modules/calib3d/test/test_solvepnp_ransac.cpp +++ b/modules/calib3d/test/test_solvepnp_ransac.cpp @@ -182,9 +182,9 @@ static std::string printMethod(int method) case 2: return "SOLVEPNP_P3P"; case 3: - return "SOLVEPNP_DLS (remaped to SOLVEPNP_EPNP)"; + return "SOLVEPNP_DLS (remapped to SOLVEPNP_EPNP)"; case 4: - return "SOLVEPNP_UPNP (remaped to SOLVEPNP_EPNP)"; + return "SOLVEPNP_UPNP (remapped to SOLVEPNP_EPNP)"; case 5: return "SOLVEPNP_AP3P"; case 6: @@ -207,8 +207,8 @@ public: eps[SOLVEPNP_EPNP] = 1.0e-2; eps[SOLVEPNP_P3P] = 1.0e-2; eps[SOLVEPNP_AP3P] = 1.0e-2; - eps[SOLVEPNP_DLS] = 1.0e-2; - eps[SOLVEPNP_UPNP] = 1.0e-2; + eps[SOLVEPNP_DLS] = 1.0e-2; // DLS is remapped to EPnP, so we use the same threshold + eps[SOLVEPNP_UPNP] = 1.0e-2; // UPnP is remapped to EPnP, so we use the same threshold eps[SOLVEPNP_IPPE] = 1.0e-2; eps[SOLVEPNP_IPPE_SQUARE] = 1.0e-2; eps[SOLVEPNP_SQPNP] = 1.0e-2; @@ -468,8 +468,8 @@ public: eps[SOLVEPNP_EPNP] = 1.0e-6; eps[SOLVEPNP_P3P] = 2.0e-4; eps[SOLVEPNP_AP3P] = 1.0e-4; - eps[SOLVEPNP_DLS] = 1.0e-6; //DLS is remapped to EPnP, so we use the same threshold - eps[SOLVEPNP_UPNP] = 1.0e-6; //UPnP is remapped to EPnP, so we use the same threshold + eps[SOLVEPNP_DLS] = 1.0e-6; // DLS is remapped to EPnP, so we use the same threshold + eps[SOLVEPNP_UPNP] = 1.0e-6; // UPnP is remapped to EPnP, so we use the same threshold eps[SOLVEPNP_IPPE] = 1.0e-6; eps[SOLVEPNP_IPPE_SQUARE] = 1.0e-6; eps[SOLVEPNP_SQPNP] = 1.0e-6; @@ -509,19 +509,19 @@ protected: if (mode == 0) { epsilon_trans[SOLVEPNP_EPNP] = 5.0e-3; - epsilon_trans[SOLVEPNP_DLS] = 5.0e-3; - epsilon_trans[SOLVEPNP_UPNP] = 5.0e-3; + epsilon_trans[SOLVEPNP_DLS] = 5.0e-3; // DLS is remapped to EPnP, so we use the same threshold + epsilon_trans[SOLVEPNP_UPNP] = 5.0e-3; // UPnP is remapped to EPnP, so we use the same threshold epsilon_rot[SOLVEPNP_EPNP] = 5.0e-3; - epsilon_rot[SOLVEPNP_DLS] = 5.0e-3; - epsilon_rot[SOLVEPNP_UPNP] = 5.0e-3; + epsilon_rot[SOLVEPNP_DLS] = 5.0e-3; // DLS is remapped to EPnP, so we use the same threshold + epsilon_rot[SOLVEPNP_UPNP] = 5.0e-3; // UPnP is remapped to EPnP, so we use the same threshold } else { epsilon_trans[SOLVEPNP_ITERATIVE] = 1e-4; epsilon_trans[SOLVEPNP_EPNP] = 5e-3; - epsilon_trans[SOLVEPNP_DLS] = 5e-3; - epsilon_trans[SOLVEPNP_UPNP] = 5e-3; + epsilon_trans[SOLVEPNP_DLS] = 5e-3; // DLS is remapped to EPnP, so we use the same threshold + epsilon_trans[SOLVEPNP_UPNP] = 5e-3; // UPnP is remapped to EPnP, so we use the same threshold epsilon_trans[SOLVEPNP_P3P] = 1e-4; epsilon_trans[SOLVEPNP_AP3P] = 1e-4; epsilon_trans[SOLVEPNP_IPPE] = 1e-4; @@ -529,8 +529,8 @@ protected: epsilon_rot[SOLVEPNP_ITERATIVE] = 1e-4; epsilon_rot[SOLVEPNP_EPNP] = 5e-3; - epsilon_rot[SOLVEPNP_DLS] = 5e-3; - epsilon_rot[SOLVEPNP_UPNP] = 5e-3; + epsilon_rot[SOLVEPNP_DLS] = 5e-3; // DLS is remapped to EPnP, so we use the same threshold + epsilon_rot[SOLVEPNP_UPNP] = 5e-3; // UPnP is remapped to EPnP, so we use the same threshold epsilon_rot[SOLVEPNP_P3P] = 1e-4; epsilon_rot[SOLVEPNP_AP3P] = 1e-4; epsilon_rot[SOLVEPNP_IPPE] = 1e-4; From cb8030809e0278d02fa335cc1f5ec7c3c17548e0 Mon Sep 17 00:00:00 2001 From: Dan Dennedy Date: Wed, 2 Apr 2025 13:45:08 -0700 Subject: [PATCH 58/94] Fix configuring with CMake version 4 fixes #27122 --- CMakeLists.txt | 2 +- cmake/OpenCVGenPkgconfig.cmake | 2 +- .../introduction/linux_gcc_cmake/linux_gcc_cmake.markdown | 2 +- modules/python/CMakeLists.txt | 2 +- platforms/android/build-tests/test_cmake_build.py | 2 +- samples/CMakeLists.example.in | 2 +- samples/CMakeLists.txt | 2 +- samples/cpp/example_cmake/CMakeLists.txt | 2 +- samples/cpp/tutorial_code/gpu/gpu-thrust-interop/CMakeLists.txt | 2 +- samples/hal/c_hal/CMakeLists.txt | 2 +- samples/hal/slow_hal/CMakeLists.txt | 2 +- samples/openvx/CMakeLists.txt | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7985623ffb..ca402b65c9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,7 +16,7 @@ endif() include(cmake/OpenCVMinDepVersions.cmake) if(CMAKE_SYSTEM_NAME MATCHES WindowsPhone OR CMAKE_SYSTEM_NAME MATCHES WindowsStore) - cmake_minimum_required(VERSION 3.1 FATAL_ERROR) + cmake_minimum_required(VERSION 3.5 FATAL_ERROR) #Required to resolve linker error issues due to incompatibility with CMake v3.0+ policies. #CMake fails to find _fseeko() which leads to subsequent linker error. #See details here: http://www.cmake.org/Wiki/CMake/Policies diff --git a/cmake/OpenCVGenPkgconfig.cmake b/cmake/OpenCVGenPkgconfig.cmake index 4fc80f5e4b..3532260efb 100644 --- a/cmake/OpenCVGenPkgconfig.cmake +++ b/cmake/OpenCVGenPkgconfig.cmake @@ -110,7 +110,7 @@ endif() # ============================================================================= else() # DEFINED CMAKE_HELPER_SCRIPT -cmake_minimum_required(VERSION 2.8.12.2) +cmake_minimum_required(VERSION 3.5) cmake_policy(SET CMP0012 NEW) include("${CMAKE_HELPER_SCRIPT}") include("${OpenCV_SOURCE_DIR}/cmake/OpenCVUtils.cmake") diff --git a/doc/tutorials/introduction/linux_gcc_cmake/linux_gcc_cmake.markdown b/doc/tutorials/introduction/linux_gcc_cmake/linux_gcc_cmake.markdown index 7cfbb31777..75bdc2ef42 100644 --- a/doc/tutorials/introduction/linux_gcc_cmake/linux_gcc_cmake.markdown +++ b/doc/tutorials/introduction/linux_gcc_cmake/linux_gcc_cmake.markdown @@ -63,7 +63,7 @@ int main(int argc, char** argv ) Now you have to create your CMakeLists.txt file. It should look like this: @code{.cmake} -cmake_minimum_required(VERSION 2.8) +cmake_minimum_required(VERSION 3.5) project( DisplayImage ) find_package( OpenCV REQUIRED ) include_directories( ${OpenCV_INCLUDE_DIRS} ) diff --git a/modules/python/CMakeLists.txt b/modules/python/CMakeLists.txt index 93eab8c94d..ec30f42150 100644 --- a/modules/python/CMakeLists.txt +++ b/modules/python/CMakeLists.txt @@ -35,7 +35,7 @@ add_subdirectory(python3) else() # standalone build -cmake_minimum_required(VERSION 2.8.12.2) +cmake_minimum_required(VERSION 3.5) project(OpenCVPython CXX C) include("./standalone.cmake") diff --git a/platforms/android/build-tests/test_cmake_build.py b/platforms/android/build-tests/test_cmake_build.py index 25d185b8e5..51affd1f3b 100644 --- a/platforms/android/build-tests/test_cmake_build.py +++ b/platforms/android/build-tests/test_cmake_build.py @@ -7,7 +7,7 @@ import logging as log log.basicConfig(format='%(message)s', level=log.DEBUG) CMAKE_TEMPLATE='''\ -CMAKE_MINIMUM_REQUIRED(VERSION 2.8) +CMAKE_MINIMUM_REQUIRED(VERSION 3.5) # Enable C++11 set(CMAKE_CXX_STANDARD 11) diff --git a/samples/CMakeLists.example.in b/samples/CMakeLists.example.in index 7cf20d5e44..59a133bc9d 100644 --- a/samples/CMakeLists.example.in +++ b/samples/CMakeLists.example.in @@ -1,5 +1,5 @@ # cmake needs this line -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.5) if(NOT DEFINED EXAMPLE_NAME) message(FATAL_ERROR "Invalid build script: missing EXAMPLE_NAME") diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index 6a18b61afa..dabe07747f 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -62,7 +62,7 @@ else() # Standalone mode # #=================================================================================================== -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.5) project(samples C CXX) option(BUILD_EXAMPLES "Build samples" ON) diff --git a/samples/cpp/example_cmake/CMakeLists.txt b/samples/cpp/example_cmake/CMakeLists.txt index 8d5cd98af2..f17aca4b54 100644 --- a/samples/cpp/example_cmake/CMakeLists.txt +++ b/samples/cpp/example_cmake/CMakeLists.txt @@ -1,5 +1,5 @@ # cmake needs this line -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.5) # Define project name project(opencv_example_project) diff --git a/samples/cpp/tutorial_code/gpu/gpu-thrust-interop/CMakeLists.txt b/samples/cpp/tutorial_code/gpu/gpu-thrust-interop/CMakeLists.txt index 037d508569..1a35085aa2 100644 --- a/samples/cpp/tutorial_code/gpu/gpu-thrust-interop/CMakeLists.txt +++ b/samples/cpp/tutorial_code/gpu/gpu-thrust-interop/CMakeLists.txt @@ -1,4 +1,4 @@ -CMAKE_MINIMUM_REQUIRED(VERSION 2.8) +CMAKE_MINIMUM_REQUIRED(VERSION 3.5) FIND_PACKAGE(CUDA REQUIRED) INCLUDE_DIRECTORIES(${CUDA_INCLUDE_DIRS}) diff --git a/samples/hal/c_hal/CMakeLists.txt b/samples/hal/c_hal/CMakeLists.txt index 8cf78aa5ff..72dfef01c3 100644 --- a/samples/hal/c_hal/CMakeLists.txt +++ b/samples/hal/c_hal/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12.2 FATAL_ERROR) +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) set(PROJECT_NAME "c_hal") set(HAL_LIB_NAME "c_hal") diff --git a/samples/hal/slow_hal/CMakeLists.txt b/samples/hal/slow_hal/CMakeLists.txt index 1ffa4670b6..a20596ea2a 100644 --- a/samples/hal/slow_hal/CMakeLists.txt +++ b/samples/hal/slow_hal/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12.2 FATAL_ERROR) +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) set(PROJECT_NAME "slow_hal") set(HAL_LIB_NAME "slow_hal") diff --git a/samples/openvx/CMakeLists.txt b/samples/openvx/CMakeLists.txt index fd04e6b9e2..c5efdf9ad0 100644 --- a/samples/openvx/CMakeLists.txt +++ b/samples/openvx/CMakeLists.txt @@ -1,6 +1,6 @@ ocv_install_example_src(cpp *.cpp *.hpp CMakeLists.txt) -cmake_minimum_required(VERSION 2.8.12.2) +cmake_minimum_required(VERSION 3.5) set(OPENCV_OPENVX_SAMPLE_REQUIRED_DEPS opencv_core From 3f9ed93da2318907e0d1d5b722b9b5f1df9b2537 Mon Sep 17 00:00:00 2001 From: Maksim Shabunin Date: Thu, 3 Apr 2025 07:03:32 +0300 Subject: [PATCH 59/94] videoio: fixed V4L frame size for non-BGR output --- modules/videoio/src/cap_v4l.cpp | 100 ++++++++++++++++++++++++----- modules/videoio/test/test_v4l2.cpp | 6 +- 2 files changed, 86 insertions(+), 20 deletions(-) diff --git a/modules/videoio/src/cap_v4l.cpp b/modules/videoio/src/cap_v4l.cpp index b7cc1e423d..5575b099e2 100644 --- a/modules/videoio/src/cap_v4l.cpp +++ b/modules/videoio/src/cap_v4l.cpp @@ -428,6 +428,7 @@ struct CvCaptureCAM_V4L CV_FINAL : public IVideoCapture bool tryIoctl(unsigned long ioctlCode, void *parameter, bool failIfBusy = true, int attempts = 10) const; bool controlInfo(int property_id, __u32 &v4l2id, cv::Range &range) const; bool icvControl(__u32 v4l2id, int &value, bool isSet) const; + void initFrameNonBGR(); bool icvSetFrameSize(int _width, int _height); bool v4l2_reset(); @@ -1427,18 +1428,13 @@ void CvCaptureCAM_V4L::convertToRgb(const Buffer ¤tBuffer) imageSize = cv::Size(form.fmt.pix.width, form.fmt.pix.height); } - // Not found conversion - switch (palette) - { + + frame.create(imageSize, CV_8UC3); + + switch (palette) { case V4L2_PIX_FMT_YUV411P: - frame.create(imageSize, CV_8UC3); yuv411p_to_rgb24(imageSize.width, imageSize.height, start, frame.data); return; - default: - break; - } - // Converted by cvtColor or imdecode - switch (palette) { case V4L2_PIX_FMT_YVU420: cv::cvtColor(cv::Mat(imageSize.height * 3 / 2, imageSize.width, CV_8U, start), frame, COLOR_YUV2BGR_YV12); @@ -1537,15 +1533,15 @@ void CvCaptureCAM_V4L::convertToRgb(const Buffer ¤tBuffer) } case V4L2_PIX_FMT_GREY: cv::cvtColor(cv::Mat(imageSize, CV_8UC1, start), frame, COLOR_GRAY2BGR); - break; + return; case V4L2_PIX_FMT_XBGR32: case V4L2_PIX_FMT_ABGR32: cv::cvtColor(cv::Mat(imageSize, CV_8UC4, start), frame, COLOR_BGRA2BGR); - break; + return; case V4L2_PIX_FMT_BGR24: default: - Mat(1, currentBuffer.bytesused, CV_8U, start).copyTo(frame); - break; + Mat(1, currentBuffer.bytesused, CV_8U, start).reshape(frame.channels(), frame.rows).copyTo(frame); + return; } } @@ -1914,11 +1910,13 @@ bool CvCaptureCAM_V4L::setProperty( int property_id, double _value ) } case cv::CAP_PROP_FOURCC: { - if (palette == static_cast<__u32>(value)) + __u32 new_palette = static_cast<__u32>(_value); + if (palette == new_palette) return true; __u32 old_palette = palette; - palette = static_cast<__u32>(value); + palette = new_palette; + if (v4l2_reset()) return true; @@ -2033,12 +2031,80 @@ bool CvCaptureCAM_V4L::streaming(bool startStream) return startStream; } +void CvCaptureCAM_V4L::initFrameNonBGR() +{ + Size size; + if (V4L2_TYPE_IS_MULTIPLANAR(type)) { + CV_Assert(form.fmt.pix_mp.width <= (uint)std::numeric_limits::max()); + CV_Assert(form.fmt.pix_mp.height <= (uint)std::numeric_limits::max()); + size = Size{(int)form.fmt.pix_mp.width, (int)form.fmt.pix_mp.height}; + } else { + CV_Assert(form.fmt.pix.width <= (uint)std::numeric_limits::max()); + CV_Assert(form.fmt.pix.height <= (uint)std::numeric_limits::max()); + size = Size{(int)form.fmt.pix.width, (int)form.fmt.pix.height}; + } + + int image_type = CV_8UC3; + switch (palette) { + case V4L2_PIX_FMT_BGR24: + case V4L2_PIX_FMT_RGB24: + image_type = CV_8UC3; + break; + case V4L2_PIX_FMT_XBGR32: + case V4L2_PIX_FMT_ABGR32: + image_type = CV_8UC4; + break; + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_UYVY: + image_type = CV_8UC2; + break; + case V4L2_PIX_FMT_YVU420: + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + image_type = CV_8UC1; + size.height = size.height * 3 / 2; // "1.5" channels + break; + case V4L2_PIX_FMT_Y16: + case V4L2_PIX_FMT_Y16_BE: + case V4L2_PIX_FMT_Y12: + case V4L2_PIX_FMT_Y10: + image_type = CV_16UC1; + break; + case V4L2_PIX_FMT_GREY: + image_type = CV_8UC1; + break; + default: + image_type = CV_8UC1; + if(bufferIndex < 0) + size = Size(buffers[MAX_V4L_BUFFERS].memories[MEMORY_ORIG].length, 1); + else { + __u32 bytesused = 0; + if (V4L2_TYPE_IS_MULTIPLANAR(type)) { + __u32 data_offset; + for (unsigned char n_planes = 0; n_planes < num_planes; n_planes++) { + data_offset = buffers[bufferIndex].planes[n_planes].data_offset; + bytesused += buffers[bufferIndex].planes[n_planes].bytesused - data_offset; + } + } else { + bytesused = buffers[bufferIndex].buffer.bytesused; + } + size = Size(bytesused, 1); + } + break; + } + frame.create(size, image_type); +} + bool CvCaptureCAM_V4L::retrieveFrame(int, OutputArray ret) { havePendingFrame = false; // unlock .grab() if (bufferIndex < 0) + { frame.copyTo(ret); + return true; + } /* Now get what has already been captured as a IplImage return */ const Buffer ¤tBuffer = buffers[bufferIndex]; @@ -2068,8 +2134,8 @@ bool CvCaptureCAM_V4L::retrieveFrame(int, OutputArray ret) std::min(currentBuffer.memories[n_planes].length, (size_t)cur_plane.bytesused)); } } else { - const Size sz(std::min(buffers[MAX_V4L_BUFFERS].memories[MEMORY_ORIG].length, (size_t)currentBuffer.buffer.bytesused), 1); - frame = Mat(sz, CV_8U, currentBuffer.memories[MEMORY_ORIG].start); + initFrameNonBGR(); + Mat(frame.size(), frame.type(), currentBuffer.memories[MEMORY_ORIG].start).copyTo(frame); } } //Revert buffer to the queue diff --git a/modules/videoio/test/test_v4l2.cpp b/modules/videoio/test/test_v4l2.cpp index b336a6fd8a..6ea1f67e6d 100644 --- a/modules/videoio/test/test_v4l2.cpp +++ b/modules/videoio/test/test_v4l2.cpp @@ -116,7 +116,7 @@ TEST_P(videoio_v4l2, formats) EXPECT_EQ(3, img.channels()); EXPECT_EQ(CV_8U, img.depth()); #ifdef DUMP_CAMERA_FRAME - std::string img_name = "frame_" + fourccToString(params.pixel_format); + std::string img_name = "frame_" + fourccToStringSafe(params.pixel_format); // V4L2 flag for big-endian formats if(params.pixel_format & (1 << 31)) img_name += "-BE"; @@ -147,8 +147,8 @@ vector all_params = { { V4L2_PIX_FMT_Y10, 1, CV_16U, 1.f, 1.f }, { V4L2_PIX_FMT_GREY, 1, CV_8U, 1.f, 1.f }, { V4L2_PIX_FMT_BGR24, 3, CV_8U, 1.f, 1.f }, - { V4L2_PIX_FMT_XBGR32, 3, CV_8U, 1.f, 1.f }, - { V4L2_PIX_FMT_ABGR32, 3, CV_8U, 1.f, 1.f }, + { V4L2_PIX_FMT_XBGR32, 4, CV_8U, 1.f, 1.f }, + { V4L2_PIX_FMT_ABGR32, 4, CV_8U, 1.f, 1.f }, }; inline static std::string param_printer(const testing::TestParamInfo& info) From 55a4a713fda029efc31a6aa77be77a4fd83cfc7a Mon Sep 17 00:00:00 2001 From: FurkanTahaSaranda <145482625+FurkanTahaSaranda@users.noreply.github.com> Date: Fri, 4 Apr 2025 17:36:42 +0300 Subject: [PATCH 60/94] Update js_houghcircles_HoughCirclesP.html --- doc/js_tutorials/js_assets/js_houghcircles_HoughCirclesP.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/js_tutorials/js_assets/js_houghcircles_HoughCirclesP.html b/doc/js_tutorials/js_assets/js_houghcircles_HoughCirclesP.html index f707adb514..2e4a3e1c4f 100644 --- a/doc/js_tutorials/js_assets/js_houghcircles_HoughCirclesP.html +++ b/doc/js_tutorials/js_assets/js_houghcircles_HoughCirclesP.html @@ -47,7 +47,7 @@ let color = new cv.Scalar(255, 0, 0); cv.cvtColor(src, src, cv.COLOR_RGBA2GRAY, 0); // You can try more different parameters cv.HoughCircles(src, circles, cv.HOUGH_GRADIENT, - 1, 45, 75, 40, 0, 0); + 1, 45, 175, 40, 0, 0); // draw circles for (let i = 0; i < circles.cols; ++i) { let x = circles.data32F[i * 3]; From 81859255ca072f73331d1efb8c1582785bf6c6d3 Mon Sep 17 00:00:00 2001 From: Yuantao Feng Date: Mon, 7 Apr 2025 15:36:19 +0800 Subject: [PATCH 61/94] Merge pull request #27175 from fengyuentau:4x/hal_rvv/div_recip HAL: implemented cv_hal_div* and cv_hal_recip* in hal_rvv #27175 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- 3rdparty/hal_rvv/hal_rvv.hpp | 1 + 3rdparty/hal_rvv/hal_rvv_1p0/div.hpp | 268 +++++++++++++++++++++++++++ 2 files changed, 269 insertions(+) create mode 100644 3rdparty/hal_rvv/hal_rvv_1p0/div.hpp diff --git a/3rdparty/hal_rvv/hal_rvv.hpp b/3rdparty/hal_rvv/hal_rvv.hpp index f1d8ccb2e4..b766f3c660 100644 --- a/3rdparty/hal_rvv/hal_rvv.hpp +++ b/3rdparty/hal_rvv/hal_rvv.hpp @@ -46,6 +46,7 @@ #include "hal_rvv_1p0/svd.hpp" // core #include "hal_rvv_1p0/sqrt.hpp" // core #include "hal_rvv_1p0/copy_mask.hpp" // core +#include "hal_rvv_1p0/div.hpp" // core #include "hal_rvv_1p0/moments.hpp" // imgproc #include "hal_rvv_1p0/filter.hpp" // imgproc diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/div.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/div.hpp new file mode 100644 index 0000000000..ccbeb6403d --- /dev/null +++ b/3rdparty/hal_rvv/hal_rvv_1p0/div.hpp @@ -0,0 +1,268 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2025, SpaceMIT Inc., all rights reserved. +// Third party copyrights are property of their respective owners. + +#ifndef OPENCV_HAL_RVV_DIV_HPP_INCLUDED +#define OPENCV_HAL_RVV_DIV_HPP_INCLUDED + +#include +#include + +namespace cv { namespace cv_hal_rvv { namespace div { + +namespace { + + inline size_t setvl(int l) { return __riscv_vsetvl_e8m2(l); } + + inline vuint8m2_t vle(const uint8_t *p, int vl) { return __riscv_vle8_v_u8m2(p, vl); } + inline vint8m2_t vle(const int8_t *p, int vl) { return __riscv_vle8_v_i8m2(p, vl); } + inline vuint16m4_t vle(const uint16_t *p, int vl) { return __riscv_vle16_v_u16m4(p, vl); } + inline vint16m4_t vle(const int16_t *p, int vl) { return __riscv_vle16_v_i16m4(p, vl); } + inline vint32m8_t vle(const int *p, int vl) { return __riscv_vle32_v_i32m8(p, vl); } + inline vfloat32m8_t vle(const float *p, int vl) { return __riscv_vle32_v_f32m8(p, vl); } + + inline void vse(uint8_t *p, const vuint8m2_t &v, int vl) { __riscv_vse8(p, v, vl); } + inline void vse(int8_t *p, const vint8m2_t &v, int vl) { __riscv_vse8(p, v, vl); } + inline void vse(uint16_t *p, const vuint16m4_t &v, int vl) { __riscv_vse16(p, v, vl); } + inline void vse(int16_t *p, const vint16m4_t &v, int vl) { __riscv_vse16(p, v, vl); } + inline void vse(int *p, const vint32m8_t &v, int vl) { __riscv_vse32(p, v, vl); } + inline void vse(float *p, const vfloat32m8_t &v, int vl) { __riscv_vse32(p, v, vl); } + + inline vuint16m4_t ext(const vuint8m2_t &v, const int vl) { return __riscv_vzext_vf2(v, vl); } + inline vint16m4_t ext(const vint8m2_t &v, const int vl) { return __riscv_vsext_vf2(v, vl); } + inline vuint32m8_t ext(const vuint16m4_t &v, const int vl) { return __riscv_vzext_vf2(v, vl); } + inline vint32m8_t ext(const vint16m4_t &v, const int vl) { return __riscv_vsext_vf2(v, vl); } + + inline vuint8m2_t nclip(const vuint16m4_t &v, const int vl) { return __riscv_vnclipu(v, 0, __RISCV_VXRM_RNU, vl); } + inline vint8m2_t nclip(const vint16m4_t &v, const int vl) { return __riscv_vnclip(v, 0, __RISCV_VXRM_RNU, vl); } + inline vuint16m4_t nclip(const vuint32m8_t &v, const int vl) { return __riscv_vnclipu(v, 0, __RISCV_VXRM_RNU, vl); } + inline vint16m4_t nclip(const vint32m8_t &v, const int vl) { return __riscv_vnclip(v, 0, __RISCV_VXRM_RNU, vl); } + + template inline + VT div_sat(const VT &v1, const VT &v2, const float scale, const int vl) { + return nclip(div_sat(ext(v1, vl), ext(v2, vl), scale, vl), vl); + } + template <> inline + vint32m8_t div_sat(const vint32m8_t &v1, const vint32m8_t &v2, const float scale, const int vl) { + auto f1 = __riscv_vfcvt_f(v1, vl); + auto f2 = __riscv_vfcvt_f(v2, vl); + auto res = __riscv_vfmul(f1, __riscv_vfrdiv(f2, scale, vl), vl); + return __riscv_vfcvt_x(res, vl); + } + template <> inline + vuint32m8_t div_sat(const vuint32m8_t &v1, const vuint32m8_t &v2, const float scale, const int vl) { + auto f1 = __riscv_vfcvt_f(v1, vl); + auto f2 = __riscv_vfcvt_f(v2, vl); + auto res = __riscv_vfmul(f1, __riscv_vfrdiv(f2, scale, vl), vl); + return __riscv_vfcvt_xu(res, vl); + } + + template inline + VT recip_sat(const VT &v, const float scale, const int vl) { + return nclip(recip_sat(ext(v, vl), scale, vl), vl); + } + template <> inline + vint32m8_t recip_sat(const vint32m8_t &v, const float scale, const int vl) { + auto f = __riscv_vfcvt_f(v, vl); + auto res = __riscv_vfrdiv(f, scale, vl); + return __riscv_vfcvt_x(res, vl); + } + template <> inline + vuint32m8_t recip_sat(const vuint32m8_t &v, const float scale, const int vl) { + auto f = __riscv_vfcvt_f(v, vl); + auto res = __riscv_vfrdiv(f, scale, vl); + return __riscv_vfcvt_xu(res, vl); + } + +} // anonymous + +#undef cv_hal_div8u +#define cv_hal_div8u cv::cv_hal_rvv::div::div +#undef cv_hal_div8s +#define cv_hal_div8s cv::cv_hal_rvv::div::div +#undef cv_hal_div16u +#define cv_hal_div16u cv::cv_hal_rvv::div::div +#undef cv_hal_div16s +#define cv_hal_div16s cv::cv_hal_rvv::div::div +#undef cv_hal_div32s +#define cv_hal_div32s cv::cv_hal_rvv::div::div +#undef cv_hal_div32f +#define cv_hal_div32f cv::cv_hal_rvv::div::div +// #undef cv_hal_div64f +// #define cv_hal_div64f cv::cv_hal_rvv::div::div + +template inline +int div(const ST *src1, size_t step1, const ST *src2, size_t step2, + ST *dst, size_t step, int width, int height, float scale) { + if (scale == 0.f || + (scale * static_cast(std::numeric_limits::max())) < 1.f && + (scale * static_cast(std::numeric_limits::max())) > -1.f) { + for (int h = 0; h < height; h++) { + ST *dst_h = reinterpret_cast((uchar*)dst + h * step); + std::memset(dst_h, 0, sizeof(ST) * width); + } + return CV_HAL_ERROR_OK; + } + + for (int h = 0; h < height; h++) { + const ST *src1_h = reinterpret_cast((const uchar*)src1 + h * step1); + const ST *src2_h = reinterpret_cast((const uchar*)src2 + h * step2); + ST *dst_h = reinterpret_cast((uchar*)dst + h * step); + + int vl; + for (int w = 0; w < width; w += vl) { + vl = setvl(width - w); + + auto v1 = vle(src1_h + w, vl); + auto v2 = vle(src2_h + w, vl); + + auto mask = __riscv_vmseq(v2, 0, vl); + vse(dst_h + w, __riscv_vmerge(div_sat(v1, v2, scale, vl), 0, mask, vl), vl); + } + } + + return CV_HAL_ERROR_OK; +} + +template <> inline +int div(const float *src1, size_t step1, const float *src2, size_t step2, + float *dst, size_t step, int width, int height, float scale) { + if (scale == 0.f) { + for (int h = 0; h < height; h++) { + float *dst_h = reinterpret_cast((uchar*)dst + h * step); + std::memset(dst_h, 0, sizeof(float) * width); + } + return CV_HAL_ERROR_OK; + } + + if (std::fabs(scale - 1.f) < FLT_EPSILON) { + for (int h = 0; h < height; h++) { + const float *src1_h = reinterpret_cast((const uchar*)src1 + h * step1); + const float *src2_h = reinterpret_cast((const uchar*)src2 + h * step2); + float *dst_h = reinterpret_cast((uchar*)dst + h * step); + + int vl; + for (int w = 0; w < width; w += vl) { + vl = setvl(width - w); + + auto v1 = vle(src1_h + w, vl); + auto v2 = vle(src2_h + w, vl); + + vse(dst_h + w, __riscv_vfmul(v1, __riscv_vfrdiv(v2, 1.f, vl), vl), vl); + } + } + } else { + for (int h = 0; h < height; h++) { + const float *src1_h = reinterpret_cast((const uchar*)src1 + h * step1); + const float *src2_h = reinterpret_cast((const uchar*)src2 + h * step2); + float *dst_h = reinterpret_cast((uchar*)dst + h * step); + + int vl; + for (int w = 0; w < width; w += vl) { + vl = setvl(width - w); + + auto v1 = vle(src1_h + w, vl); + auto v2 = vle(src2_h + w, vl); + + vse(dst_h + w, __riscv_vfmul(v1, __riscv_vfrdiv(v2, scale, vl), vl), vl); + } + } + } + + return CV_HAL_ERROR_OK; +} + +#undef cv_hal_recip8u +#define cv_hal_recip8u cv::cv_hal_rvv::div::recip +#undef cv_hal_recip8s +#define cv_hal_recip8s cv::cv_hal_rvv::div::recip +#undef cv_hal_recip16u +#define cv_hal_recip16u cv::cv_hal_rvv::div::recip +#undef cv_hal_recip16s +#define cv_hal_recip16s cv::cv_hal_rvv::div::recip +#undef cv_hal_recip32s +#define cv_hal_recip32s cv::cv_hal_rvv::div::recip +#undef cv_hal_recip32f +#define cv_hal_recip32f cv::cv_hal_rvv::div::recip +// #undef cv_hal_recip64f +// #define cv_hal_recip64f cv::cv_hal_rvv::div::recip + +template inline +int recip(const ST *src_data, size_t src_step, ST *dst_data, size_t dst_step, + int width, int height, float scale) { + if (scale == 0.f || scale < 1.f && scale > -1.f) { + for (int h = 0; h < height; h++) { + ST *dst_h = reinterpret_cast((uchar*)dst_data + h * dst_step); + std::memset(dst_h, 0, sizeof(ST) * width); + } + return CV_HAL_ERROR_OK; + } + + for (int h = 0; h < height; h++) { + const ST *src_h = reinterpret_cast((const uchar*)src_data + h * src_step); + ST *dst_h = reinterpret_cast((uchar*)dst_data + h * dst_step); + + int vl; + for (int w = 0; w < width; w += vl) { + vl = setvl(width - w); + + auto v = vle(src_h + w, vl); + + auto mask = __riscv_vmseq(v, 0, vl); + vse(dst_h + w, __riscv_vmerge(recip_sat(v, scale, vl), 0, mask, vl), vl); + } + } + + return CV_HAL_ERROR_OK; +} + +template <> inline +int recip(const float *src_data, size_t src_step, float *dst_data, size_t dst_step, + int width, int height, float scale) { + if (scale == 0.f) { + for (int h = 0; h < height; h++) { + float *dst_h = reinterpret_cast((uchar*)dst_data + h * dst_step); + std::memset(dst_h, 0, sizeof(float) * width); + } + return CV_HAL_ERROR_OK; + } + + if (std::fabs(scale - 1.f) < FLT_EPSILON) { + for (int h = 0; h < height; h++) { + const float *src_h = reinterpret_cast((const uchar*)src_data + h * src_step); + float *dst_h = reinterpret_cast((uchar*)dst_data + h * dst_step); + + int vl; + for (int w = 0; w < width; w += vl) { + vl = setvl(width - w); + + auto v = vle(src_h + w, vl); + + vse(dst_h + w, __riscv_vfrdiv(v, 1.f, vl), vl); + } + } + } else { + for (int h = 0; h < height; h++) { + const float *src_h = reinterpret_cast((const uchar*)src_data + h * src_step); + float *dst_h = reinterpret_cast((uchar*)dst_data + h * dst_step); + + int vl; + for (int w = 0; w < width; w += vl) { + vl = setvl(width - w); + + auto v = vle(src_h + w, vl); + + vse(dst_h + w, __riscv_vfrdiv(v, scale, vl), vl); + } + } + } + + return CV_HAL_ERROR_OK; +} + +}}} // cv::cv_hal_rvv::div + +#endif // OPENCV_HAL_RVV_DIV_HPP_INCLUDED From 1b3db545a30f9b2df7331bdee9570f913c1217a9 Mon Sep 17 00:00:00 2001 From: Yuantao Feng Date: Mon, 7 Apr 2025 15:56:02 +0800 Subject: [PATCH 62/94] Merge pull request #27145 from fengyuentau:4x/core/copyMask-simd core: further vectorize copyTo with mask #27145 Merge with https://github.com/opencv/opencv_extra/pull/1247. ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- modules/core/perf/perf_mat.cpp | 2 +- modules/core/src/copy.cpp | 122 +++++++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+), 1 deletion(-) diff --git a/modules/core/perf/perf_mat.cpp b/modules/core/perf/perf_mat.cpp index a179483503..277eb92c21 100644 --- a/modules/core/perf/perf_mat.cpp +++ b/modules/core/perf/perf_mat.cpp @@ -99,7 +99,7 @@ PERF_TEST_P(Size_MatType, Mat_Clone_Roi, PERF_TEST_P(Size_MatType, Mat_CopyToWithMask, testing::Combine(testing::Values(::perf::sz1080p, ::perf::szODD), - testing::Values(CV_8UC1, CV_8UC2, CV_8UC3, CV_16UC1, CV_32SC1, CV_32FC4)) + testing::Values(CV_8UC1, CV_8UC2, CV_8UC3, CV_16UC1, CV_16UC3, CV_32SC1, CV_32SC2, CV_32FC4)) ) { const Size_MatType_t params = GetParam(); diff --git a/modules/core/src/copy.cpp b/modules/core/src/copy.cpp index 9f4fa8604a..68d6b938e7 100644 --- a/modules/core/src/copy.cpp +++ b/modules/core/src/copy.cpp @@ -12,6 +12,7 @@ // Copyright (C) 2000-2008, Intel Corporation, all rights reserved. // Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved. // Copyright (C) 2014, Itseez Inc., all rights reserved. +// Copyright (C) 2025, SpaceMIT Inc., all rights reserved. // Third party copyrights are property of their respective owners. // // Redistribution and use in source and binary forms, with or without modification, @@ -178,6 +179,41 @@ copyMask_(const uchar* _src, size_t sstep, const uchar* mask, size_t mste } } +template<> void +copyMask_(const uchar* _src, size_t sstep, const uchar* mask, size_t mstep, uchar* _dst, size_t dstep, Size size) +{ + for( ; size.height--; mask += mstep, _src += sstep, _dst += dstep ) + { + const uchar* src = (const uchar*)_src; + uchar* dst = (uchar*)_dst; + int x = 0; +#if (CV_SIMD || CV_SIMD_SCALABLE) + for( ; x <= size.width - VTraits::vlanes(); x += VTraits::vlanes() ) + { + v_uint8 v_nmask = v_eq(vx_load(mask + x), vx_setzero_u8()); + + v_uint8 v_src0, v_src1, v_src2; + v_uint8 v_dst0, v_dst1, v_dst2; + v_load_deinterleave(src + 3 * x, v_src0, v_src1, v_src2); + v_load_deinterleave(dst + 3 * x, v_dst0, v_dst1, v_dst2); + + v_dst0 = v_select(v_nmask, v_dst0, v_src0); + v_dst1 = v_select(v_nmask, v_dst1, v_src1); + v_dst2 = v_select(v_nmask, v_dst2, v_src2); + + v_store_interleave(dst + 3 * x, v_dst0, v_dst1, v_dst2); + } + vx_cleanup(); +#endif + for( ; x < size.width; x++ ) + if( mask[x] ) { + dst[3 * x] = src[3 * x]; + dst[3 * x + 1] = src[3 * x + 1]; + dst[3 * x + 2] = src[3 * x + 2]; + } + } +} + template<> void copyMask_(const uchar* _src, size_t sstep, const uchar* mask, size_t mstep, uchar* _dst, size_t dstep, Size size) { @@ -215,6 +251,92 @@ copyMask_(const uchar* _src, size_t sstep, const uchar* mask, size_t mst } } +template<> void +copyMask_(const uchar* _src, size_t sstep, const uchar* mask, size_t mstep, uchar* _dst, size_t dstep, Size size) +{ + for( ; size.height--; mask += mstep, _src += sstep, _dst += dstep ) + { + const ushort* src = (const ushort*)_src; + ushort* dst = (ushort*)_dst; + int x = 0; +#if (CV_SIMD || CV_SIMD_SCALABLE) + for( ; x <= size.width - VTraits::vlanes(); x += VTraits::vlanes() ) + { + v_uint8 v_nmask = v_eq(vx_load(mask + x), vx_setzero_u8()); + v_uint8 v_nmask0, v_nmask1; + v_zip(v_nmask, v_nmask, v_nmask0, v_nmask1); + + v_uint16 v_src0, v_src1, v_src2; + v_uint16 v_dst0, v_dst1, v_dst2; + v_load_deinterleave(src + 3 * x, v_src0, v_src1, v_src2); + v_load_deinterleave(dst + 3 * x, v_dst0, v_dst1, v_dst2); + v_uint16 v_src3, v_src4, v_src5; + v_uint16 v_dst3, v_dst4, v_dst5; + v_load_deinterleave(src + 3 * (x + VTraits::vlanes()), v_src3, v_src4, v_src5); + v_load_deinterleave(dst + 3 * (x + VTraits::vlanes()), v_dst3, v_dst4, v_dst5); + + v_dst0 = v_select(v_reinterpret_as_u16(v_nmask0), v_dst0, v_src0); + v_dst1 = v_select(v_reinterpret_as_u16(v_nmask0), v_dst1, v_src1); + v_dst2 = v_select(v_reinterpret_as_u16(v_nmask0), v_dst2, v_src2); + v_dst3 = v_select(v_reinterpret_as_u16(v_nmask1), v_dst3, v_src3); + v_dst4 = v_select(v_reinterpret_as_u16(v_nmask1), v_dst4, v_src4); + v_dst5 = v_select(v_reinterpret_as_u16(v_nmask1), v_dst5, v_src5); + + v_store_interleave(dst + 3 * x, v_dst0, v_dst1, v_dst2); + v_store_interleave(dst + 3 * (x + VTraits::vlanes()), v_dst3, v_dst4, v_dst5); + } + vx_cleanup(); +#endif + for( ; x < size.width; x++ ) + if( mask[x] ) { + dst[3 * x] = src[3 * x]; + dst[3 * x + 1] = src[3 * x + 1]; + dst[3 * x + 2] = src[3 * x + 2]; + } + } +} + +template<> void +copyMask_(const uchar* _src, size_t sstep, const uchar* mask, size_t mstep, uchar* _dst, size_t dstep, Size size) +{ + for( ; size.height--; mask += mstep, _src += sstep, _dst += dstep ) + { + const int* src = (const int*)_src; + int* dst = (int*)_dst; + int x = 0; +#if (CV_SIMD || CV_SIMD_SCALABLE) + for (; x <= size.width - VTraits::vlanes(); x += VTraits::vlanes()) + { + v_int32 v_src0 = vx_load(src + x), v_dst0 = vx_load(dst + x); + v_int32 v_src1 = vx_load(src + x + VTraits::vlanes()), v_dst1 = vx_load(dst + x + VTraits::vlanes()); + v_int32 v_src2 = vx_load(src + x + 2 * VTraits::vlanes()), v_dst2 = vx_load(dst + x + 2 * VTraits::vlanes()); + v_int32 v_src3 = vx_load(src + x + 3 * VTraits::vlanes()), v_dst3 = vx_load(dst + x + 3 * VTraits::vlanes()); + + v_uint8 v_nmask = v_eq(vx_load(mask + x), vx_setzero_u8()); + v_uint8 v_nmask0, v_nmask1; + v_zip(v_nmask, v_nmask, v_nmask0, v_nmask1); + v_uint8 v_nmask00, v_nmask01, v_nmask10, v_nmask11; + v_zip(v_nmask0, v_nmask0, v_nmask00, v_nmask01); + v_zip(v_nmask1, v_nmask1, v_nmask10, v_nmask11); + + v_dst0 = v_select(v_reinterpret_as_s32(v_nmask00), v_dst0, v_src0); + v_dst1 = v_select(v_reinterpret_as_s32(v_nmask01), v_dst1, v_src1); + v_dst2 = v_select(v_reinterpret_as_s32(v_nmask10), v_dst2, v_src2); + v_dst3 = v_select(v_reinterpret_as_s32(v_nmask11), v_dst3, v_src3); + + vx_store(dst + x, v_dst0); + vx_store(dst + x + VTraits::vlanes(), v_dst1); + vx_store(dst + x + 2 * VTraits::vlanes(), v_dst2); + vx_store(dst + x + 3 * VTraits::vlanes(), v_dst3); + } + vx_cleanup(); +#endif + for (; x < size.width; x++) + if ( mask[x] ) + dst[x] = src[x]; + } +} + static void copyMaskGeneric(const uchar* _src, size_t sstep, const uchar* mask, size_t mstep, uchar* _dst, size_t dstep, Size size, void* _esz) { From 91e078be932726e2d3ad03954590aa0cc438670b Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Mon, 7 Apr 2025 14:11:13 +0300 Subject: [PATCH 63/94] Dropped inefficient (disabled) IPP integration for LUT. --- modules/core/include/opencv2/core/private.hpp | 1 - modules/core/src/lut.cpp | 182 ------------------ 2 files changed, 183 deletions(-) diff --git a/modules/core/include/opencv2/core/private.hpp b/modules/core/include/opencv2/core/private.hpp index 230f692c92..140264086f 100644 --- a/modules/core/include/opencv2/core/private.hpp +++ b/modules/core/include/opencv2/core/private.hpp @@ -213,7 +213,6 @@ T* allocSingletonNew() { return new(allocSingletonNewBuffer(sizeof(T))) T(); } // Temporary disabled named IPP region. Performance #define IPP_DISABLE_PERF_COPYMAKE 1 // performance variations -#define IPP_DISABLE_PERF_LUT 1 // there are no performance benefits (PR #2653) #define IPP_DISABLE_PERF_TRUE_DIST_MT 1 // cv::distanceTransform OpenCV MT performance is better #define IPP_DISABLE_PERF_CANNY_MT 1 // cv::Canny OpenCV MT performance is better diff --git a/modules/core/src/lut.cpp b/modules/core/src/lut.cpp index 20e12e4b92..090ba50d5e 100644 --- a/modules/core/src/lut.cpp +++ b/modules/core/src/lut.cpp @@ -104,184 +104,6 @@ static bool ocl_LUT(InputArray _src, InputArray _lut, OutputArray _dst) #endif -#if defined(HAVE_IPP) -#if !IPP_DISABLE_PERF_LUT // there are no performance benefits (PR #2653) -namespace ipp { - -class IppLUTParallelBody_LUTC1 : public ParallelLoopBody -{ -public: - bool* ok; - const Mat& src_; - const Mat& lut_; - Mat& dst_; - - int width; - size_t elemSize1; - - IppLUTParallelBody_LUTC1(const Mat& src, const Mat& lut, Mat& dst, bool* _ok) - : ok(_ok), src_(src), lut_(lut), dst_(dst) - { - width = dst.cols * dst.channels(); - elemSize1 = CV_ELEM_SIZE1(dst.depth()); - - CV_DbgAssert(elemSize1 == 1 || elemSize1 == 4); - *ok = true; - } - - void operator()( const cv::Range& range ) const - { - if (!*ok) - return; - - const int row0 = range.start; - const int row1 = range.end; - - Mat src = src_.rowRange(row0, row1); - Mat dst = dst_.rowRange(row0, row1); - - IppiSize sz = { width, dst.rows }; - - if (elemSize1 == 1) - { - if (CV_INSTRUMENT_FUN_IPP(ippiLUTPalette_8u_C1R, (const Ipp8u*)src.data, (int)src.step[0], dst.data, (int)dst.step[0], sz, lut_.data, 8) >= 0) - return; - } - else if (elemSize1 == 4) - { - if (CV_INSTRUMENT_FUN_IPP(ippiLUTPalette_8u32u_C1R, (const Ipp8u*)src.data, (int)src.step[0], (Ipp32u*)dst.data, (int)dst.step[0], sz, (Ipp32u*)lut_.data, 8) >= 0) - return; - } - *ok = false; - } -private: - IppLUTParallelBody_LUTC1(const IppLUTParallelBody_LUTC1&); - IppLUTParallelBody_LUTC1& operator=(const IppLUTParallelBody_LUTC1&); -}; - -class IppLUTParallelBody_LUTCN : public ParallelLoopBody -{ -public: - bool *ok; - const Mat& src_; - const Mat& lut_; - Mat& dst_; - - int lutcn; - - uchar* lutBuffer; - uchar* lutTable[4]; - - IppLUTParallelBody_LUTCN(const Mat& src, const Mat& lut, Mat& dst, bool* _ok) - : ok(_ok), src_(src), lut_(lut), dst_(dst), lutBuffer(NULL) - { - lutcn = lut.channels(); - IppiSize sz256 = {256, 1}; - - size_t elemSize1 = dst.elemSize1(); - CV_DbgAssert(elemSize1 == 1); - lutBuffer = (uchar*)CV_IPP_MALLOC(256 * (int)elemSize1 * 4); - lutTable[0] = lutBuffer + 0; - lutTable[1] = lutBuffer + 1 * 256 * elemSize1; - lutTable[2] = lutBuffer + 2 * 256 * elemSize1; - lutTable[3] = lutBuffer + 3 * 256 * elemSize1; - - CV_DbgAssert(lutcn == 3 || lutcn == 4); - if (lutcn == 3) - { - IppStatus status = CV_INSTRUMENT_FUN_IPP(ippiCopy_8u_C3P3R, lut.ptr(), (int)lut.step[0], lutTable, (int)lut.step[0], sz256); - if (status < 0) - return; - } - else if (lutcn == 4) - { - IppStatus status = CV_INSTRUMENT_FUN_IPP(ippiCopy_8u_C4P4R, lut.ptr(), (int)lut.step[0], lutTable, (int)lut.step[0], sz256); - if (status < 0) - return; - } - - *ok = true; - } - - ~IppLUTParallelBody_LUTCN() - { - if (lutBuffer != NULL) - ippFree(lutBuffer); - lutBuffer = NULL; - lutTable[0] = NULL; - } - - void operator()( const cv::Range& range ) const - { - if (!*ok) - return; - - const int row0 = range.start; - const int row1 = range.end; - - Mat src = src_.rowRange(row0, row1); - Mat dst = dst_.rowRange(row0, row1); - - if (lutcn == 3) - { - if (CV_INSTRUMENT_FUN_IPP(ippiLUTPalette_8u_C3R, src.ptr(), (int)src.step[0], dst.ptr(), (int)dst.step[0], ippiSize(dst.size()), lutTable, 8) >= 0) - return; - } - else if (lutcn == 4) - { - if (CV_INSTRUMENT_FUN_IPP(ippiLUTPalette_8u_C4R, src.ptr(), (int)src.step[0], dst.ptr(), (int)dst.step[0], ippiSize(dst.size()), lutTable, 8) >= 0) - return; - } - *ok = false; - } -private: - IppLUTParallelBody_LUTCN(const IppLUTParallelBody_LUTCN&); - IppLUTParallelBody_LUTCN& operator=(const IppLUTParallelBody_LUTCN&); -}; -} // namespace ipp - -static bool ipp_lut(Mat &src, Mat &lut, Mat &dst) -{ - CV_INSTRUMENT_REGION_IPP(); - - int lutcn = lut.channels(); - - if(src.dims > 2) - return false; - - bool ok = false; - Ptr body; - - size_t elemSize1 = CV_ELEM_SIZE1(dst.depth()); - - if (lutcn == 1) - { - ParallelLoopBody* p = new ipp::IppLUTParallelBody_LUTC1(src, lut, dst, &ok); - body.reset(p); - } - else if ((lutcn == 3 || lutcn == 4) && elemSize1 == 1) - { - ParallelLoopBody* p = new ipp::IppLUTParallelBody_LUTCN(src, lut, dst, &ok); - body.reset(p); - } - - if (body != NULL && ok) - { - Range all(0, dst.rows); - if (dst.total()>>18) - parallel_for_(all, *body, (double)std::max((size_t)1, dst.total()>>16)); - else - (*body)(all); - if (ok) - return true; - } - - return false; -} - -#endif -#endif // IPP - class LUTParallelBody : public ParallelLoopBody { public: @@ -348,10 +170,6 @@ void cv::LUT( InputArray _src, InputArray _lut, OutputArray _dst ) CALL_HAL(LUT, cv_hal_lut, src.data, src.step, src.type(), lut.data, lut.elemSize1(), lutcn, dst.data, dst.step, src.cols, src.rows); -#if !IPP_DISABLE_PERF_LUT - CV_IPP_RUN(_src.dims() <= 2, ipp_lut(src, lut, dst)); -#endif - if (_src.dims() <= 2) { bool ok = false; From 8f74086d3f518199a3114b4786f5b0e98f9dc0df Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Mon, 7 Apr 2025 14:18:37 +0300 Subject: [PATCH 64/94] Drop commented out convertTo impl with IPP. --- modules/core/src/convert.simd.hpp | 96 ------------------------------- 1 file changed, 96 deletions(-) diff --git a/modules/core/src/convert.simd.hpp b/modules/core/src/convert.simd.hpp index 841cb6f684..183c4686df 100644 --- a/modules/core/src/convert.simd.hpp +++ b/modules/core/src/convert.simd.hpp @@ -275,102 +275,6 @@ static void cvt32s(const uchar* src, size_t sstep, const uchar*, size_t, uchar* static void cvt64s(const uchar* src, size_t sstep, const uchar*, size_t, uchar* dst, size_t dstep, Size size, void*) { CV_INSTRUMENT_REGION(); cvtCopy((const uchar*)src, sstep, (uchar*)dst, dstep, size, 8); } - -/* [TODO] Recover IPP calls -#if defined(HAVE_IPP) -#define DEF_CVT_FUNC_F(suffix, stype, dtype, ippFavor) \ -static void cvt##suffix( const stype* src, size_t sstep, const uchar*, size_t, \ - dtype* dst, size_t dstep, Size size, double*) \ -{ \ - CV_IPP_RUN(src && dst, CV_INSTRUMENT_FUN_IPP(ippiConvert_##ippFavor, src, (int)sstep, dst, (int)dstep, ippiSize(size.width, size.height)) >= 0) \ - cvt_(src, sstep, dst, dstep, size); \ -} - -#define DEF_CVT_FUNC_F2(suffix, stype, dtype, ippFavor) \ -static void cvt##suffix( const stype* src, size_t sstep, const uchar*, size_t, \ - dtype* dst, size_t dstep, Size size, double*) \ -{ \ - CV_IPP_RUN(src && dst, CV_INSTRUMENT_FUN_IPP(ippiConvert_##ippFavor, src, (int)sstep, dst, (int)dstep, ippiSize(size.width, size.height), ippRndFinancial, 0) >= 0) \ - cvt_(src, sstep, dst, dstep, size); \ -} -#else -#define DEF_CVT_FUNC_F(suffix, stype, dtype, ippFavor) \ -static void cvt##suffix( const stype* src, size_t sstep, const uchar*, size_t, \ - dtype* dst, size_t dstep, Size size, double*) \ -{ \ - cvt_(src, sstep, dst, dstep, size); \ -} -#define DEF_CVT_FUNC_F2 DEF_CVT_FUNC_F -#endif - -#define DEF_CVT_FUNC(suffix, stype, dtype) \ -static void cvt##suffix( const stype* src, size_t sstep, const uchar*, size_t, \ - dtype* dst, size_t dstep, Size size, double*) \ -{ \ - cvt_(src, sstep, dst, dstep, size); \ -} - -#define DEF_CPY_FUNC(suffix, stype) \ -static void cvt##suffix( const stype* src, size_t sstep, const uchar*, size_t, \ - stype* dst, size_t dstep, Size size, double*) \ -{ \ - cpy_(src, sstep, dst, dstep, size); \ -} - -DEF_CPY_FUNC(8u, uchar) -DEF_CVT_FUNC_F(8s8u, schar, uchar, 8s8u_C1Rs) -DEF_CVT_FUNC_F(16u8u, ushort, uchar, 16u8u_C1R) -DEF_CVT_FUNC_F(16s8u, short, uchar, 16s8u_C1R) -DEF_CVT_FUNC_F(32s8u, int, uchar, 32s8u_C1R) -DEF_CVT_FUNC_F2(32f8u, float, uchar, 32f8u_C1RSfs) -DEF_CVT_FUNC(64f8u, double, uchar) - -DEF_CVT_FUNC_F2(8u8s, uchar, schar, 8u8s_C1RSfs) -DEF_CVT_FUNC_F2(16u8s, ushort, schar, 16u8s_C1RSfs) -DEF_CVT_FUNC_F2(16s8s, short, schar, 16s8s_C1RSfs) -DEF_CVT_FUNC_F(32s8s, int, schar, 32s8s_C1R) -DEF_CVT_FUNC_F2(32f8s, float, schar, 32f8s_C1RSfs) -DEF_CVT_FUNC(64f8s, double, schar) - -DEF_CVT_FUNC_F(8u16u, uchar, ushort, 8u16u_C1R) -DEF_CVT_FUNC_F(8s16u, schar, ushort, 8s16u_C1Rs) -DEF_CPY_FUNC(16u, ushort) -DEF_CVT_FUNC_F(16s16u, short, ushort, 16s16u_C1Rs) -DEF_CVT_FUNC_F2(32s16u, int, ushort, 32s16u_C1RSfs) -DEF_CVT_FUNC_F2(32f16u, float, ushort, 32f16u_C1RSfs) -DEF_CVT_FUNC(64f16u, double, ushort) - -DEF_CVT_FUNC_F(8u16s, uchar, short, 8u16s_C1R) -DEF_CVT_FUNC_F(8s16s, schar, short, 8s16s_C1R) -DEF_CVT_FUNC_F2(16u16s, ushort, short, 16u16s_C1RSfs) -DEF_CVT_FUNC_F2(32s16s, int, short, 32s16s_C1RSfs) -DEF_CVT_FUNC(32f16s, float, short) -DEF_CVT_FUNC(64f16s, double, short) - -DEF_CVT_FUNC_F(8u32s, uchar, int, 8u32s_C1R) -DEF_CVT_FUNC_F(8s32s, schar, int, 8s32s_C1R) -DEF_CVT_FUNC_F(16u32s, ushort, int, 16u32s_C1R) -DEF_CVT_FUNC_F(16s32s, short, int, 16s32s_C1R) -DEF_CPY_FUNC(32s, int) -DEF_CVT_FUNC_F2(32f32s, float, int, 32f32s_C1RSfs) -DEF_CVT_FUNC(64f32s, double, int) - -DEF_CVT_FUNC_F(8u32f, uchar, float, 8u32f_C1R) -DEF_CVT_FUNC_F(8s32f, schar, float, 8s32f_C1R) -DEF_CVT_FUNC_F(16u32f, ushort, float, 16u32f_C1R) -DEF_CVT_FUNC_F(16s32f, short, float, 16s32f_C1R) -DEF_CVT_FUNC_F(32s32f, int, float, 32s32f_C1R) -DEF_CVT_FUNC(64f32f, double, float) - -DEF_CVT_FUNC(8u64f, uchar, double) -DEF_CVT_FUNC(8s64f, schar, double) -DEF_CVT_FUNC(16u64f, ushort, double) -DEF_CVT_FUNC(16s64f, short, double) -DEF_CVT_FUNC(32s64f, int, double) -DEF_CVT_FUNC(32f64f, float, double) -DEF_CPY_FUNC(64s, int64) -*/ - BinaryFunc getConvertFunc(int sdepth, int ddepth) { static BinaryFunc cvtTab[CV_DEPTH_MAX][CV_DEPTH_MAX] = From 78662ac085e7190103d7ff131ccaf114274c9238 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Wed, 9 Apr 2025 17:37:15 +0300 Subject: [PATCH 65/94] Transfer IPP polarToCart to HAL. --- 3rdparty/ipphal/CMakeLists.txt | 1 + 3rdparty/ipphal/include/ipp_hal_core.hpp | 8 +++ 3rdparty/ipphal/src/cart_polar_ipp.cpp | 28 ++++++++++ modules/core/src/mathfuncs.cpp | 69 ------------------------ 4 files changed, 37 insertions(+), 69 deletions(-) create mode 100644 3rdparty/ipphal/src/cart_polar_ipp.cpp diff --git a/3rdparty/ipphal/CMakeLists.txt b/3rdparty/ipphal/CMakeLists.txt index 5e69b0511e..376bf7d386 100644 --- a/3rdparty/ipphal/CMakeLists.txt +++ b/3rdparty/ipphal/CMakeLists.txt @@ -11,6 +11,7 @@ add_library(ipphal STATIC "${CMAKE_CURRENT_SOURCE_DIR}/src/mean_ipp.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/src/minmax_ipp.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/src/norm_ipp.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/cart_polar_ipp.cpp" ) if(HAVE_IPP_ICV) diff --git a/3rdparty/ipphal/include/ipp_hal_core.hpp b/3rdparty/ipphal/include/ipp_hal_core.hpp index 441a8bb207..ac2ac868f0 100644 --- a/3rdparty/ipphal/include/ipp_hal_core.hpp +++ b/3rdparty/ipphal/include/ipp_hal_core.hpp @@ -34,4 +34,12 @@ int ipp_hal_normDiff(const uchar* src1, size_t src1_step, const uchar* src2, siz #endif +int ipp_hal_polarToCart32f(const float* mag, const float* angle, float* x, float* y, int len, bool angleInDegrees); +int ipp_hal_polarToCart64f(const double* mag, const double* angle, double* x, double* y, int len, bool angleInDegrees); + +#undef cv_hal_polarToCart32f +#define cv_hal_polarToCart32f ipp_hal_polarToCart32f +#undef cv_hal_polarToCart64f +#define cv_hal_polarToCart64f ipp_hal_polarToCart64f + #endif diff --git a/3rdparty/ipphal/src/cart_polar_ipp.cpp b/3rdparty/ipphal/src/cart_polar_ipp.cpp new file mode 100644 index 0000000000..39f4d4a53a --- /dev/null +++ b/3rdparty/ipphal/src/cart_polar_ipp.cpp @@ -0,0 +1,28 @@ +#include "ipp_hal_core.hpp" + +#include +#include + +int ipp_hal_polarToCart32f(const float* mag, const float* angle, float* x, float* y, int len, bool angleInDegrees) +{ + const bool isInPlace = (x == mag) || (x == angle) || (y == mag) || (y == angle); + if (isInPlace || angleInDegrees) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + + if (CV_INSTRUMENT_FUN_IPP(ippsPolarToCart_32f, mag, angle, x, y, len) < 0) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + + return CV_HAL_ERROR_OK; +} + +int ipp_hal_polarToCart64f(const double* mag, const double* angle, double* x, double* y, int len, bool angleInDegrees) +{ + const bool isInPlace = (x == mag) || (x == angle) || (y == mag) || (y == angle); + if (isInPlace || angleInDegrees) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + + if (CV_INSTRUMENT_FUN_IPP(ippsPolarToCart_64f, mag, angle, x, y, len) < 0) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + + return CV_HAL_ERROR_OK; +} diff --git a/modules/core/src/mathfuncs.cpp b/modules/core/src/mathfuncs.cpp index 0403b75452..0604d2c33f 100644 --- a/modules/core/src/mathfuncs.cpp +++ b/modules/core/src/mathfuncs.cpp @@ -378,65 +378,6 @@ static bool ocl_polarToCart( InputArray _mag, InputArray _angle, #endif -#ifdef HAVE_IPP -static bool ipp_polarToCart(Mat &mag, Mat &angle, Mat &x, Mat &y) -{ - CV_INSTRUMENT_REGION_IPP(); - - int depth = angle.depth(); - if(depth != CV_32F && depth != CV_64F) - return false; - - if(angle.dims <= 2) - { - int len = (int)(angle.cols*angle.channels()); - - if(depth == CV_32F) - { - for (int h = 0; h < angle.rows; h++) - { - if(CV_INSTRUMENT_FUN_IPP(ippsPolarToCart_32f, (const float*)mag.ptr(h), (const float*)angle.ptr(h), (float*)x.ptr(h), (float*)y.ptr(h), len) < 0) - return false; - } - } - else - { - for (int h = 0; h < angle.rows; h++) - { - if(CV_INSTRUMENT_FUN_IPP(ippsPolarToCart_64f, (const double*)mag.ptr(h), (const double*)angle.ptr(h), (double*)x.ptr(h), (double*)y.ptr(h), len) < 0) - return false; - } - } - return true; - } - else - { - const Mat *arrays[] = {&mag, &angle, &x, &y, NULL}; - uchar *ptrs[4] = {NULL}; - NAryMatIterator it(arrays, ptrs); - int len = (int)(it.size*angle.channels()); - - if(depth == CV_32F) - { - for (size_t i = 0; i < it.nplanes; i++, ++it) - { - if(CV_INSTRUMENT_FUN_IPP(ippsPolarToCart_32f, (const float*)ptrs[0], (const float*)ptrs[1], (float*)ptrs[2], (float*)ptrs[3], len) < 0) - return false; - } - } - else - { - for (size_t i = 0; i < it.nplanes; i++, ++it) - { - if(CV_INSTRUMENT_FUN_IPP(ippsPolarToCart_64f, (const double*)ptrs[0], (const double*)ptrs[1], (double*)ptrs[2], (double*)ptrs[3], len) < 0) - return false; - } - } - return true; - } -} -#endif - void polarToCart( InputArray src1, InputArray src2, OutputArray dst1, OutputArray dst2, bool angleInDegrees ) { @@ -444,14 +385,6 @@ void polarToCart( InputArray src1, InputArray src2, CV_Assert(dst1.getObj() != dst2.getObj()); -#ifdef HAVE_IPP - const bool isInPlace = - (src1.getObj() == dst1.getObj()) || - (src1.getObj() == dst2.getObj()) || - (src2.getObj() == dst1.getObj()) || - (src2.getObj() == dst2.getObj()); -#endif - int type = src2.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type); CV_Assert((depth == CV_32F || depth == CV_64F) && (src1.empty() || src1.type() == type)); @@ -464,8 +397,6 @@ void polarToCart( InputArray src1, InputArray src2, dst2.create( Angle.dims, Angle.size, type ); Mat X = dst1.getMat(), Y = dst2.getMat(); - CV_IPP_RUN(!angleInDegrees && !isInPlace, ipp_polarToCart(Mag, Angle, X, Y)); - const Mat* arrays[] = {&Mag, &Angle, &X, &Y, 0}; uchar* ptrs[4] = {}; NAryMatIterator it(arrays, ptrs); From 0d092c7b1e87f0cfd34535c10f94c56298415928 Mon Sep 17 00:00:00 2001 From: Xue Zhang Date: Wed, 12 Mar 2025 14:33:30 +0530 Subject: [PATCH 66/94] Add SVD into HAL --- 3rdparty/fastcv/include/fastcv_hal_core.hpp | 33 +++++++++++- 3rdparty/fastcv/src/fastcv_hal_core.cpp | 58 +++++++++++++++++++-- 2 files changed, 87 insertions(+), 4 deletions(-) diff --git a/3rdparty/fastcv/include/fastcv_hal_core.hpp b/3rdparty/fastcv/include/fastcv_hal_core.hpp index 03c17dc6b5..c950ac7959 100644 --- a/3rdparty/fastcv/include/fastcv_hal_core.hpp +++ b/3rdparty/fastcv/include/fastcv_hal_core.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ @@ -32,6 +32,9 @@ #define cv_hal_mul16s fastcv_hal_mul16s #undef cv_hal_mul32f #define cv_hal_mul32f fastcv_hal_mul32f +#undef cv_hal_SVD32f +#define cv_hal_SVD32f fastcv_hal_SVD32f + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// @brief look-up table transform of an array. @@ -219,4 +222,32 @@ int fastcv_hal_mul32f( int height, double scale); +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/// Performs singular value decomposition of \f$M\times N\f$(\f$M>N\f$) matrix \f$A = U*\Sigma*V^T\f$. +/// +/// @param src Pointer to input MxN matrix A stored in column major order. +/// After finish of work src will be filled with rows of U or not modified (depends of flag CV_HAL_SVD_MODIFY_A). +/// @param src_step Number of bytes between two consequent columns of matrix A. +/// @param w Pointer to array for singular values of matrix A (i. e. first N diagonal elements of matrix \f$\Sigma\f$). +/// @param u Pointer to output MxN or MxM matrix U (size depends of flags). +/// Pointer must be valid if flag CV_HAL_SVD_MODIFY_A not used. +/// @param u_step Number of bytes between two consequent rows of matrix U. +/// @param vt Pointer to array for NxN matrix V^T. +/// @param vt_step Number of bytes between two consequent rows of matrix V^T. +/// @param m Number fo rows in matrix A. +/// @param n Number of columns in matrix A. +/// @param flags Algorithm options (combination of CV_HAL_SVD_FULL_UV, ...). +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +int fastcv_hal_SVD32f( + float* src, + size_t src_step, + float* w, + float* u, + size_t u_step, + float* vt, + size_t vt_step, + int m, + int n, + int flags); + #endif diff --git a/3rdparty/fastcv/src/fastcv_hal_core.cpp b/3rdparty/fastcv/src/fastcv_hal_core.cpp index d46bf9a172..0b35f2f91a 100644 --- a/3rdparty/fastcv/src/fastcv_hal_core.cpp +++ b/3rdparty/fastcv/src/fastcv_hal_core.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ @@ -399,7 +399,7 @@ int fastcv_hal_mul8u( int8_t sF; if(FCV_CMP_EQ(scale,1.0)) { sF = 0; } - else if(scale > 1.0) + else if(scale > 1.0) { if(FCV_CMP_EQ(scale,2.0)) { sF = -1; } else if(FCV_CMP_EQ(scale,4.0)) { sF = -2; } @@ -471,7 +471,7 @@ int fastcv_hal_mul16s( int8_t sF; if(FCV_CMP_EQ(scale,1.0)) { sF = 0; } - else if(scale > 1.0) + else if(scale > 1.0) { if(FCV_CMP_EQ(scale,2.0)) { sF = -1; } else if(FCV_CMP_EQ(scale,4.0)) { sF = -2; } @@ -571,4 +571,56 @@ int fastcv_hal_mul32f( fcvStatus status = FASTCV_SUCCESS; CV_HAL_RETURN(status, hal_mul32f); +} + +int fastcv_hal_SVD32f( + float* src, + size_t src_step, + float* w, + float* u, + size_t u_step, + float* vt, + size_t vt_step, + int m, + int n, + int flags) +{ + if (n * sizeof(float) != src_step) + CV_HAL_RETURN_NOT_IMPLEMENTED("step is not supported"); + + INITIALIZATION_CHECK; + + fcvStatus status = FASTCV_SUCCESS; + + cv::Mat tmpU(m, n, CV_32F); + cv::Mat tmpV(n, n, CV_32F); + + switch (flags) + { + case CV_HAL_SVD_NO_UV: + { + status = fcvSVDf32_v2(src, m, n, w, u, vt, (float32_t *)tmpU.data, (float32_t *)tmpV.data, false); + break; + } + case CV_HAL_SVD_SHORT_UV: + { + if ((n * sizeof(float) == u_step) && (n * sizeof(float) == vt_step)) + status = fcvSVDf32_v2(src, m, n, w, u, vt, (float32_t *)tmpU.data, (float32_t *)tmpV.data, false); + else + CV_HAL_RETURN_NOT_IMPLEMENTED("step is not supported"); + break; + } + case CV_HAL_SVD_FULL_UV: + { + if ((n * sizeof(float) == u_step) && (n * sizeof(float) == vt_step)) + status = fcvSVDf32_v2(src, m, n, w, u, vt, (float32_t *)tmpU.data, (float32_t *)tmpV.data, true); + else + CV_HAL_RETURN_NOT_IMPLEMENTED("step is not supported"); + break; + } + default: + CV_HAL_RETURN_NOT_IMPLEMENTED(cv::format("Flags:%d is not supported", flags)); + } + + CV_HAL_RETURN(status, fastcv_hal_SVD32f); } \ No newline at end of file From fa7a0c1e12892726e30863218a2554b6f34814d2 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Thu, 3 Apr 2025 11:51:02 +0300 Subject: [PATCH 67/94] Migrated IPP impl for flip and transpose to HAL. --- 3rdparty/ipphal/CMakeLists.txt | 10 ++ 3rdparty/ipphal/include/ipp_hal_core.hpp | 14 +++ 3rdparty/ipphal/src/mean_ipp.cpp | 2 +- 3rdparty/ipphal/src/minmax_ipp.cpp | 2 +- 3rdparty/ipphal/src/norm_ipp.cpp | 2 +- 3rdparty/ipphal/src/transforms_ipp.cpp | 142 +++++++++++++++++++++++ modules/core/src/matrix_transform.cpp | 114 ------------------ 7 files changed, 169 insertions(+), 117 deletions(-) create mode 100644 3rdparty/ipphal/src/transforms_ipp.cpp diff --git a/3rdparty/ipphal/CMakeLists.txt b/3rdparty/ipphal/CMakeLists.txt index 376bf7d386..c80e76bfed 100644 --- a/3rdparty/ipphal/CMakeLists.txt +++ b/3rdparty/ipphal/CMakeLists.txt @@ -12,13 +12,23 @@ add_library(ipphal STATIC "${CMAKE_CURRENT_SOURCE_DIR}/src/minmax_ipp.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/src/norm_ipp.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/src/cart_polar_ipp.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/transforms_ipp.cpp" ) +#TODO: HAVE_IPP_ICV and HAVE_IPP_IW added as private macro till OpenCV itself is +# source of IPP and public definitions lead to redefinition warning +# The macro should be redefined as PUBLIC when IPP part is removed from core +# to make HAL the source of IPP integration if(HAVE_IPP_ICV) target_compile_definitions(ipphal PRIVATE HAVE_IPP_ICV) endif() +if(HAVE_IPP_IW) + target_compile_definitions(ipphal PRIVATE HAVE_IPP_IW) +endif() + target_include_directories(ipphal PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/include") +ocv_warnings_disable(CMAKE_CXX_FLAGS -Wno-suggest-override) target_include_directories(ipphal PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/src" diff --git a/3rdparty/ipphal/include/ipp_hal_core.hpp b/3rdparty/ipphal/include/ipp_hal_core.hpp index ac2ac868f0..6707db7290 100644 --- a/3rdparty/ipphal/include/ipp_hal_core.hpp +++ b/3rdparty/ipphal/include/ipp_hal_core.hpp @@ -42,4 +42,18 @@ int ipp_hal_polarToCart64f(const double* mag, const double* angle, double* x, do #undef cv_hal_polarToCart64f #define cv_hal_polarToCart64f ipp_hal_polarToCart64f +#ifdef HAVE_IPP_IW +int ipp_hal_flip(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 flip_mode); + +#undef cv_hal_flip +#define cv_hal_flip ipp_hal_flip +#endif + +int ipp_hal_transpose2d(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, int src_width, + int src_height, int element_size); + +#undef cv_hal_transpose2d +#define cv_hal_transpose2d ipp_hal_transpose2d + #endif diff --git a/3rdparty/ipphal/src/mean_ipp.cpp b/3rdparty/ipphal/src/mean_ipp.cpp index 63abe6eb9d..38412271b5 100644 --- a/3rdparty/ipphal/src/mean_ipp.cpp +++ b/3rdparty/ipphal/src/mean_ipp.cpp @@ -1,6 +1,6 @@ #include "ipp_hal_core.hpp" -#include +#include #include #if IPP_VERSION_X100 >= 700 diff --git a/3rdparty/ipphal/src/minmax_ipp.cpp b/3rdparty/ipphal/src/minmax_ipp.cpp index a87d5825e9..a8d7b7cad8 100644 --- a/3rdparty/ipphal/src/minmax_ipp.cpp +++ b/3rdparty/ipphal/src/minmax_ipp.cpp @@ -1,6 +1,6 @@ #include "ipp_hal_core.hpp" -#include +#include #include #define IPP_DISABLE_MINMAXIDX_MANY_ROWS 1 // see Core_MinMaxIdx.rows_overflow test diff --git a/3rdparty/ipphal/src/norm_ipp.cpp b/3rdparty/ipphal/src/norm_ipp.cpp index 5fc4609edb..16b0d9bd91 100644 --- a/3rdparty/ipphal/src/norm_ipp.cpp +++ b/3rdparty/ipphal/src/norm_ipp.cpp @@ -1,6 +1,6 @@ #include "ipp_hal_core.hpp" -#include +#include #include #if IPP_VERSION_X100 >= 700 diff --git a/3rdparty/ipphal/src/transforms_ipp.cpp b/3rdparty/ipphal/src/transforms_ipp.cpp new file mode 100644 index 0000000000..ae38310051 --- /dev/null +++ b/3rdparty/ipphal/src/transforms_ipp.cpp @@ -0,0 +1,142 @@ +#include "ipp_hal_core.hpp" + +#include +#include +#ifdef HAVE_IPP_IW +#include "iw++/iw.hpp" +#endif + +// HACK: Should be removed, when IPP management moved to HAL +namespace cv +{ + namespace ipp + { + unsigned long long getIppTopFeatures(); // Returns top major enabled IPP feature flag + } +} + +//bool ipp_transpose( Mat &src, Mat &dst ) +int ipp_hal_transpose2d(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, int src_width, + int src_height, int element_size) +{ + typedef IppStatus (CV_STDCALL * IppiTranspose)(const void * pSrc, int srcStep, void * pDst, int dstStep, IppiSize roiSize); + typedef IppStatus (CV_STDCALL * IppiTransposeI)(const void * pSrcDst, int srcDstStep, IppiSize roiSize); + IppiTranspose ippiTranspose = nullptr; + IppiTransposeI ippiTranspose_I = nullptr; + + if (dst_data == src_data && src_width == src_height) + { + CV_SUPPRESS_DEPRECATED_START + ippiTranspose_I = + element_size == 1*sizeof(char) ? (IppiTransposeI)ippiTranspose_8u_C1IR : + element_size == 3*sizeof(char) ? (IppiTransposeI)ippiTranspose_8u_C3IR : + element_size == 1*sizeof(short) ? (IppiTransposeI)ippiTranspose_16u_C1IR : + element_size == 4*sizeof(char) ? (IppiTransposeI)ippiTranspose_8u_C4IR : + element_size == 3*sizeof(short) ? (IppiTransposeI)ippiTranspose_16u_C3IR : + element_size == 4*sizeof(short) ? (IppiTransposeI)ippiTranspose_16u_C4IR : + element_size == 3*sizeof(int) ? (IppiTransposeI)ippiTranspose_32s_C3IR : + element_size == 4*sizeof(int) ? (IppiTransposeI)ippiTranspose_32s_C4IR : 0; + CV_SUPPRESS_DEPRECATED_END + } + else + { + ippiTranspose = + element_size == 1*sizeof(char) ? (IppiTranspose)ippiTranspose_8u_C1R : + element_size == 3*sizeof(char) ? (IppiTranspose)ippiTranspose_8u_C3R : + element_size == 4*sizeof(char) ? (IppiTranspose)ippiTranspose_8u_C4R : + element_size == 1*sizeof(short) ? (IppiTranspose)ippiTranspose_16u_C1R : + element_size == 3*sizeof(short) ? (IppiTranspose)ippiTranspose_16u_C3R : + element_size == 4*sizeof(short) ? (IppiTranspose)ippiTranspose_16u_C4R : + element_size == 1*sizeof(int) ? (IppiTranspose)ippiTranspose_32s_C1R : + element_size == 3*sizeof(int) ? (IppiTranspose)ippiTranspose_32s_C3R : + element_size == 4*sizeof(int) ? (IppiTranspose)ippiTranspose_32s_C4R : 0; + } + + IppiSize roiSize = { src_width, src_height }; + if (ippiTranspose != 0) + { + if (CV_INSTRUMENT_FUN_IPP(ippiTranspose, src_data, (int)src_step, dst_data, (int)dst_step, roiSize) >= 0) + return CV_HAL_ERROR_OK; + } + else if (ippiTranspose_I != 0) + { + if (CV_INSTRUMENT_FUN_IPP(ippiTranspose_I, dst_data, (int)dst_step, roiSize) >= 0) + return CV_HAL_ERROR_OK; + } + return CV_HAL_ERROR_NOT_IMPLEMENTED; +} + +#ifdef HAVE_IPP_IW + +static inline IppDataType ippiGetDataType(int depth) +{ + depth = CV_MAT_DEPTH(depth); + return depth == CV_8U ? ipp8u : + depth == CV_8S ? ipp8s : + depth == CV_16U ? ipp16u : + depth == CV_16S ? ipp16s : + depth == CV_32S ? ipp32s : + depth == CV_32F ? ipp32f : + depth == CV_64F ? ipp64f : + (IppDataType)-1; +} + +static inline ::ipp::IwiImage ippiGetImage(int src_type, const uchar* src_data, size_t src_step, int src_width, int src_height) +{ + ::ipp::IwiImage dst; + ::ipp::IwiBorderSize inMemBorder; +// if(src.isSubmatrix()) // already have physical border +// { +// cv::Size origSize; +// cv::Point offset; +// src.locateROI(origSize, offset); +// +// inMemBorder.left = (IwSize)offset.x; +// inMemBorder.top = (IwSize)offset.y; +// inMemBorder.right = (IwSize)(origSize.width - src.cols - offset.x); +// inMemBorder.bottom = (IwSize)(origSize.height - src.rows - offset.y); +// } + + dst.Init({src_width, src_height}, ippiGetDataType(CV_MAT_DEPTH(src_type)), + CV_MAT_CN(src_type), inMemBorder, (void*)src_data, src_step); + + return dst; +} + +int ipp_hal_flip(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 flip_mode) + +{ + int64_t total = src_step*src_height*CV_ELEM_SIZE(src_type); + // Details: https://github.com/opencv/opencv/issues/12943 + if (flip_mode <= 0 /* swap rows */ + && total > 0 && total >= CV_BIG_INT(0x80000000)/*2Gb*/ + && cv::ipp::getIppTopFeatures() != ippCPUID_SSE42) + { + return CV_HAL_ERROR_NOT_IMPLEMENTED; + } + + IppiAxis ippMode; + if(flip_mode < 0) + ippMode = ippAxsBoth; + else if(flip_mode == 0) + ippMode = ippAxsHorizontal; + else + ippMode = ippAxsVertical; + + try + { + ::ipp::IwiImage iwSrc = ippiGetImage(src_type, src_data, src_step, src_width, src_height); + ::ipp::IwiImage iwDst = ippiGetImage(src_type, dst_data, dst_step, src_width, src_height); + + CV_INSTRUMENT_FUN_IPP(::ipp::iwiMirror, iwSrc, iwDst, ippMode); + } + catch(const ::ipp::IwException &) + { + return CV_HAL_ERROR_NOT_IMPLEMENTED; + } + + return CV_HAL_ERROR_OK; +} + +#endif diff --git a/modules/core/src/matrix_transform.cpp b/modules/core/src/matrix_transform.cpp index bad17e7b6b..c52fcc1e4f 100644 --- a/modules/core/src/matrix_transform.cpp +++ b/modules/core/src/matrix_transform.cpp @@ -173,74 +173,6 @@ static bool ocl_transpose( InputArray _src, OutputArray _dst ) #endif -#ifdef HAVE_IPP -static bool ipp_transpose( Mat &src, Mat &dst ) -{ - CV_INSTRUMENT_REGION_IPP(); - - int type = src.type(); - typedef IppStatus (CV_STDCALL * IppiTranspose)(const void * pSrc, int srcStep, void * pDst, int dstStep, IppiSize roiSize); - typedef IppStatus (CV_STDCALL * IppiTransposeI)(const void * pSrcDst, int srcDstStep, IppiSize roiSize); - IppiTranspose ippiTranspose = 0; - IppiTransposeI ippiTranspose_I = 0; - - if (dst.data == src.data && dst.cols == dst.rows) - { - CV_SUPPRESS_DEPRECATED_START - ippiTranspose_I = - type == CV_8UC1 ? (IppiTransposeI)ippiTranspose_8u_C1IR : - type == CV_8UC3 ? (IppiTransposeI)ippiTranspose_8u_C3IR : - type == CV_8UC4 ? (IppiTransposeI)ippiTranspose_8u_C4IR : - type == CV_16UC1 ? (IppiTransposeI)ippiTranspose_16u_C1IR : - type == CV_16UC3 ? (IppiTransposeI)ippiTranspose_16u_C3IR : - type == CV_16UC4 ? (IppiTransposeI)ippiTranspose_16u_C4IR : - type == CV_16SC1 ? (IppiTransposeI)ippiTranspose_16s_C1IR : - type == CV_16SC3 ? (IppiTransposeI)ippiTranspose_16s_C3IR : - type == CV_16SC4 ? (IppiTransposeI)ippiTranspose_16s_C4IR : - type == CV_32SC1 ? (IppiTransposeI)ippiTranspose_32s_C1IR : - type == CV_32SC3 ? (IppiTransposeI)ippiTranspose_32s_C3IR : - type == CV_32SC4 ? (IppiTransposeI)ippiTranspose_32s_C4IR : - type == CV_32FC1 ? (IppiTransposeI)ippiTranspose_32f_C1IR : - type == CV_32FC3 ? (IppiTransposeI)ippiTranspose_32f_C3IR : - type == CV_32FC4 ? (IppiTransposeI)ippiTranspose_32f_C4IR : 0; - CV_SUPPRESS_DEPRECATED_END - } - else - { - ippiTranspose = - type == CV_8UC1 ? (IppiTranspose)ippiTranspose_8u_C1R : - type == CV_8UC3 ? (IppiTranspose)ippiTranspose_8u_C3R : - type == CV_8UC4 ? (IppiTranspose)ippiTranspose_8u_C4R : - type == CV_16UC1 ? (IppiTranspose)ippiTranspose_16u_C1R : - type == CV_16UC3 ? (IppiTranspose)ippiTranspose_16u_C3R : - type == CV_16UC4 ? (IppiTranspose)ippiTranspose_16u_C4R : - type == CV_16SC1 ? (IppiTranspose)ippiTranspose_16s_C1R : - type == CV_16SC3 ? (IppiTranspose)ippiTranspose_16s_C3R : - type == CV_16SC4 ? (IppiTranspose)ippiTranspose_16s_C4R : - type == CV_32SC1 ? (IppiTranspose)ippiTranspose_32s_C1R : - type == CV_32SC3 ? (IppiTranspose)ippiTranspose_32s_C3R : - type == CV_32SC4 ? (IppiTranspose)ippiTranspose_32s_C4R : - type == CV_32FC1 ? (IppiTranspose)ippiTranspose_32f_C1R : - type == CV_32FC3 ? (IppiTranspose)ippiTranspose_32f_C3R : - type == CV_32FC4 ? (IppiTranspose)ippiTranspose_32f_C4R : 0; - } - - IppiSize roiSize = { src.cols, src.rows }; - if (ippiTranspose != 0) - { - if (CV_INSTRUMENT_FUN_IPP(ippiTranspose, src.ptr(), (int)src.step, dst.ptr(), (int)dst.step, roiSize) >= 0) - return true; - } - else if (ippiTranspose_I != 0) - { - if (CV_INSTRUMENT_FUN_IPP(ippiTranspose_I, dst.ptr(), (int)dst.step, roiSize) >= 0) - return true; - } - return false; -} -#endif - - void transpose( InputArray _src, OutputArray _dst ) { CV_INSTRUMENT_REGION(); @@ -271,8 +203,6 @@ void transpose( InputArray _src, OutputArray _dst ) CALL_HAL(transpose2d, cv_hal_transpose2d, src.data, src.step, dst.data, dst.step, src.cols, src.rows, esz); - CV_IPP_RUN_FAST(ipp_transpose(src, dst)) - if( dst.data == src.data ) { TransposeInplaceFunc func = transposeInplaceTab[esz]; @@ -735,48 +665,6 @@ static bool ocl_flip(InputArray _src, OutputArray _dst, int flipCode ) #endif -#if defined HAVE_IPP -static bool ipp_flip(Mat &src, Mat &dst, int flip_mode) -{ -#ifdef HAVE_IPP_IW - CV_INSTRUMENT_REGION_IPP(); - - // Details: https://github.com/opencv/opencv/issues/12943 - if (flip_mode <= 0 /* swap rows */ - && cv::ipp::getIppTopFeatures() != ippCPUID_SSE42 - && (int64_t)(src.total()) * src.elemSize() >= CV_BIG_INT(0x80000000)/*2Gb*/ - ) - return false; - - IppiAxis ippMode; - if(flip_mode < 0) - ippMode = ippAxsBoth; - else if(flip_mode == 0) - ippMode = ippAxsHorizontal; - else - ippMode = ippAxsVertical; - - try - { - ::ipp::IwiImage iwSrc = ippiGetImage(src); - ::ipp::IwiImage iwDst = ippiGetImage(dst); - - CV_INSTRUMENT_FUN_IPP(::ipp::iwiMirror, iwSrc, iwDst, ippMode); - } - catch(const ::ipp::IwException &) - { - return false; - } - - return true; -#else - CV_UNUSED(src); CV_UNUSED(dst); CV_UNUSED(flip_mode); - return false; -#endif -} -#endif - - void flip( InputArray _src, OutputArray _dst, int flip_mode ) { CV_INSTRUMENT_REGION(); @@ -808,8 +696,6 @@ void flip( InputArray _src, OutputArray _dst, int flip_mode ) CALL_HAL(flip, cv_hal_flip, type, src.ptr(), src.step, src.cols, src.rows, dst.ptr(), dst.step, flip_mode); - CV_IPP_RUN_FAST(ipp_flip(src, dst, flip_mode)); - size_t esz = CV_ELEM_SIZE(type); if( flip_mode <= 0 ) From 70ab545b903f7a77dfc41dfb458e9780e4f58be5 Mon Sep 17 00:00:00 2001 From: Frank Liu Date: Wed, 9 Apr 2025 22:57:08 -0700 Subject: [PATCH 68/94] upgrade tbb to version 2022.1.0 upgrade tbb from version 2021.11.0 to 2022.1.0 to fix https://github.com/opencv/opencv/issues/25187 --- 3rdparty/tbb/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/3rdparty/tbb/CMakeLists.txt b/3rdparty/tbb/CMakeLists.txt index 2083415c61..10f60094ae 100644 --- a/3rdparty/tbb/CMakeLists.txt +++ b/3rdparty/tbb/CMakeLists.txt @@ -5,8 +5,8 @@ if (WIN32 AND NOT ARM) message(FATAL_ERROR "BUILD_TBB option supports Windows on ARM only!\nUse regular official TBB build instead of the BUILD_TBB option!") endif() -ocv_update(OPENCV_TBB_RELEASE "v2021.11.0") -ocv_update(OPENCV_TBB_RELEASE_MD5 "b301151120b08a17e98dcdda6e4f6011") +ocv_update(OPENCV_TBB_RELEASE "v2022.1.0") +ocv_update(OPENCV_TBB_RELEASE_MD5 "cce28e6cb1ceae14a93848990c98cb6b") ocv_update(OPENCV_TBB_FILENAME "${OPENCV_TBB_RELEASE}.tar.gz") string(REGEX REPLACE "^v" "" OPENCV_TBB_RELEASE_ "${OPENCV_TBB_RELEASE}") #ocv_update(OPENCV_TBB_SUBDIR ...) From c1d71d53757aaed5906431017daeaef1e747b377 Mon Sep 17 00:00:00 2001 From: Kumataro Date: Thu, 10 Apr 2025 23:24:01 +0900 Subject: [PATCH 69/94] Merge pull request #27220 from Kumataro:fix24757 imgproc: disable SIMD for compareHist(INTERSECT) if f64 is unsupported #27220 Close https://github.com/opencv/opencv/issues/24757 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- modules/imgproc/src/histogram.cpp | 3 ++- modules/imgproc/test/test_histograms.cpp | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/modules/imgproc/src/histogram.cpp b/modules/imgproc/src/histogram.cpp index c90039a0f3..b2df07cd27 100644 --- a/modules/imgproc/src/histogram.cpp +++ b/modules/imgproc/src/histogram.cpp @@ -2132,7 +2132,8 @@ double cv::compareHist( InputArray _H1, InputArray _H2, int method ) v_result = v_add(v_result, v_add(v_cvt_f64(v_src), v_cvt_f64_high(v_src))); } result += v_reduce_sum(v_result); -#elif CV_SIMD +#elif CV_SIMD && 0 // Disable vectorization for CV_COMP_INTERSECT if f64 is unsupported due to low precision + // See https://github.com/opencv/opencv/issues/24757 v_float32 v_result = vx_setzero_f32(); for (; j <= len - VTraits::vlanes(); j += VTraits::vlanes()) { diff --git a/modules/imgproc/test/test_histograms.cpp b/modules/imgproc/test/test_histograms.cpp index 2460fd92f0..f31bb34bed 100644 --- a/modules/imgproc/test/test_histograms.cpp +++ b/modules/imgproc/test/test_histograms.cpp @@ -2096,5 +2096,20 @@ INSTANTIATE_TEST_CASE_P(Imgproc_Hist, Imgproc_Equalize_Hist, ::testing::Combine( ::testing::Values(cv::Size(123, 321), cv::Size(256, 256), cv::Size(1024, 768)), ::testing::Range(0, 10))); +// See https://github.com/opencv/opencv/issues/24757 +TEST(Imgproc_Hist_Compare, intersect_regression_24757) +{ + cv::Mat src1 = cv::Mat::zeros(128,1, CV_32FC1); + cv::Mat src2 = cv::Mat(128,1, CV_32FC1, cv::Scalar(std::numeric_limits::max())); + + // Ideal result Wrong result + src1.at(32 * 0,0) = +1.0f; // work = +1.0 +1.0 + src1.at(32 * 1,0) = +55555555.5f; // work = +55555556.5 +55555555.5 + src1.at(32 * 2,0) = -55555555.5f; // work = +1.0 0.0 + src1.at(32 * 3,0) = -1.0f; // work = 0.0 -1.0 + + EXPECT_DOUBLE_EQ(compareHist(src1, src2, cv::HISTCMP_INTERSECT), 0.0); +} + }} // namespace /* End Of File */ From 7f7be9bab01105095aba24fdbcdb3956108314a0 Mon Sep 17 00:00:00 2001 From: Souriya Trinh Date: Sun, 13 Apr 2025 22:15:45 +0200 Subject: [PATCH 70/94] Add additional information about homogeneous transformations. Add quick formulas for conversions between physical focal length, sensor size, fov and camera intrinsic params. --- .../pinhole_homogeneous_transformation.png | Bin 0 -> 79039 bytes modules/calib3d/include/opencv2/calib3d.hpp | 101 ++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100644 modules/calib3d/doc/pics/pinhole_homogeneous_transformation.png diff --git a/modules/calib3d/doc/pics/pinhole_homogeneous_transformation.png b/modules/calib3d/doc/pics/pinhole_homogeneous_transformation.png new file mode 100644 index 0000000000000000000000000000000000000000..f98e4ba21746d3a1e3e3ee9674e77044937bbb0b GIT binary patch literal 79039 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�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 literal 0 HcmV?d00001 diff --git a/modules/calib3d/include/opencv2/calib3d.hpp b/modules/calib3d/include/opencv2/calib3d.hpp index 8b87a3cb68..76249112dc 100644 --- a/modules/calib3d/include/opencv2/calib3d.hpp +++ b/modules/calib3d/include/opencv2/calib3d.hpp @@ -383,6 +383,107 @@ R & t \\ 0 & 1 \end{bmatrix} P_{h_0}.\f] + Homogeneous Transformations, Object frame / Camera frame
+Change of basis or computing the 3D coordinates from one frame to another frame can be achieved easily using +the following notation: + +\f[ +\mathbf{X}_c = \hspace{0.2em} +{}^{c}\mathbf{T}_o \hspace{0.2em} \mathbf{X}_o +\f] + +\f[ +\begin{bmatrix} +X_c \\ +Y_c \\ +Z_c \\ +1 +\end{bmatrix} = +\begin{bmatrix} +{}^{c}\mathbf{R}_o & {}^{c}\mathbf{t}_o \\ +0_{1 \times 3} & 1 +\end{bmatrix} +\begin{bmatrix} +X_o \\ +Y_o \\ +Z_o \\ +1 +\end{bmatrix} +\f] + +For a 3D points (\f$ \mathbf{X}_o \f$) expressed in the object frame, the homogeneous transformation matrix +\f$ {}^{c}\mathbf{T}_o \f$ allows computing the corresponding coordinate (\f$ \mathbf{X}_c \f$) in the camera frame. +This transformation matrix is composed of a 3x3 rotation matrix \f$ {}^{c}\mathbf{R}_o \f$ and a 3x1 translation vector +\f$ {}^{c}\mathbf{t}_o \f$. +The 3x1 translation vector \f$ {}^{c}\mathbf{t}_o \f$ is the position of the object frame in the camera frame and the +3x3 rotation matrix \f$ {}^{c}\mathbf{R}_o \f$ the orientation of the object frame in the camera frame. + +With this simple notation, it is easy to chain the transformations. For instance, to compute the 3D coordinates of a point +expressed in the object frame in the world frame can be done with: + +\f[ +\mathbf{X}_w = \hspace{0.2em} +{}^{w}\mathbf{T}_c \hspace{0.2em} {}^{c}\mathbf{T}_o \hspace{0.2em} +\mathbf{X}_o = +{}^{w}\mathbf{T}_o \hspace{0.2em} \mathbf{X}_o +\f] + +Similarly, computing the inverse transformation can be done with: + +\f[ +\mathbf{X}_o = \hspace{0.2em} +{}^{o}\mathbf{T}_c \hspace{0.2em} \mathbf{X}_c = +\left( {}^{c}\mathbf{T}_o \right)^{-1} \hspace{0.2em} \mathbf{X}_c +\f] + +The inverse of an homogeneous transformation matrix is then: + +\f[ +{}^{o}\mathbf{T}_c = \left( {}^{c}\mathbf{T}_o \right)^{-1} = +\begin{bmatrix} +{}^{c}\mathbf{R}^{\top}_o & - \hspace{0.2em} {}^{c}\mathbf{R}^{\top}_o \hspace{0.2em} {}^{c}\mathbf{t}_o \\ +0_{1 \times 3} & 1 +\end{bmatrix} +\f] + +One can note that the inverse of a 3x3 rotation matrix is directly its matrix transpose. + +![Perspective projection, from object to camera frame](pics/pinhole_homogeneous_transformation.png) + +This figure summarizes the whole process. The object pose returned for instance by the @ref solvePnP function +or pose from fiducial marker detection is this \f$ {}^{c}\mathbf{T}_o \f$ transformation. + +The camera intrinsic matrix \f$ \mathbf{K} \f$ allows projecting the 3D point expressed in the camera frame onto the image plane +assuming a perspective projection model (pinhole camera model). Image coordinates extracted from classical image processing functions +assume a (u,v) top-left coordinates frame. + +\note +- for an online video course on this topic, see for instance: + - ["3.3.1. Homogeneous Transformation Matrices", Modern Robotics, Kevin M. Lynch and Frank C. Park](https://modernrobotics.northwestern.edu/nu-gm-book-resource/3-3-1-homogeneous-transformation-matrices/) +- the 3x3 rotation matrix is composed of 9 values but describes a 3 dof transformation +- some additional properties of the 3x3 rotation matrix are: + - \f$ \mathrm{det} \left( \mathbf{R} \right) = 1 \f$ + - \f$ \mathbf{R} \mathbf{R}^{\top} = \mathbf{R}^{\top} \mathbf{R} = \mathrm{I}_{3 \times 3} \f$ + - interpolating rotation can be done using the [Slerp (spherical linear interpolation)](https://en.wikipedia.org/wiki/Slerp) method +- quick conversions between the different rotation formalisms can be done using this [online tool](https://www.andre-gaschler.com/rotationconverter/) + + Intrinsic parameters from camera lens specifications
+When dealing with industrial cameras, the camera intrinsic matrix or more precisely \f$ \left(f_x, f_y \right) \f$ +can be deduced, approximated from the camera specifications: + +\f[ +f_x = \frac{f_{\text{mm}}}{\text{pixel_size_in_mm}} = \frac{f_{\text{mm}}}{\text{sensor_size_in_mm} / \text{nb_pixels}} +\f] + +In a same way, the physical focal length can be deduced from the angular field of view: + +\f[ +f_{\text{mm}} = \frac{\text{sensor_size_in_mm}}{2 \times \tan{\frac{\text{fov}}{2}}} +\f] + +This latter conversion can be useful when using a rendering software to mimic a physical camera device. + + Additional references, notes
@note - Many functions in this module take a camera intrinsic matrix as an input parameter. Although all functions assume the same structure of this parameter, they may name it differently. The From ba6eb8d95292f4631a3b8de09bfaa59e43c17226 Mon Sep 17 00:00:00 2001 From: adsha-quic Date: Wed, 16 Apr 2025 18:54:40 +0530 Subject: [PATCH 71/94] Merge pull request #27214 from CodeLinaro:fastcv_lib_hash_update Adding latest FastCV static libs updated libs PR: [opencv/opencv_3rdparty/pull/94](https://github.com/opencv/opencv_3rdparty/pull/94) ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [ ] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- 3rdparty/fastcv/fastcv.cmake | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/3rdparty/fastcv/fastcv.cmake b/3rdparty/fastcv/fastcv.cmake index 5c81c11300..6fee4ce4ce 100644 --- a/3rdparty/fastcv/fastcv.cmake +++ b/3rdparty/fastcv/fastcv.cmake @@ -1,23 +1,23 @@ function(download_fastcv root_dir) # Commit SHA in the opencv_3rdparty repo - set(FASTCV_COMMIT "f4413cc2ab7233fdfc383a4cded402c072677fb0") + set(FASTCV_COMMIT "8d86e68dad8b80b8575a8d3cf401d3ee96c24148") # Define actual FastCV versions if(ANDROID) if(AARCH64) message(STATUS "Download FastCV for Android aarch64") - set(FCV_PACKAGE_NAME "fastcv_android_aarch64_2024_12_11.tgz") - set(FCV_PACKAGE_HASH "9dac41e86597305f846212dae31a4a88") + set(FCV_PACKAGE_NAME "fastcv_android_aarch64_2025_04_08.tgz") + set(FCV_PACKAGE_HASH "e028966a1d1b2f3f0bc5967d316e8b64") else() message(STATUS "Download FastCV for Android armv7") - set(FCV_PACKAGE_NAME "fastcv_android_arm32_2024_12_11.tgz") - set(FCV_PACKAGE_HASH "fe2d30334180b17e3031eee92aac43b6") + set(FCV_PACKAGE_NAME "fastcv_android_arm32_2025_04_08.tgz") + set(FCV_PACKAGE_HASH "6fc1e812a4b3ef392469d2283e037ffe") endif() elseif(UNIX AND NOT APPLE AND NOT IOS AND NOT XROS) if(AARCH64) - set(FCV_PACKAGE_NAME "fastcv_linux_aarch64_2025_02_12.tgz") - set(FCV_PACKAGE_HASH "33ac2a59cf3e7d6402eee2e010de1202") + set(FCV_PACKAGE_NAME "fastcv_linux_aarch64_2025_04_08.tgz") + set(FCV_PACKAGE_HASH "062a26639cd2788beee2e0dd8743d680") else() message("FastCV: fastcv lib for 32-bit Linux is not supported for now!") endif() From 250ea3d7c6fc0b83e485c771f745f748d99d65f0 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Thu, 10 Apr 2025 18:21:25 +0300 Subject: [PATCH 72/94] Fixed Android build with FastCV. --- 3rdparty/fastcv/CMakeLists.txt | 2 +- cmake/OpenCVFindLibsPerf.cmake | 15 +++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/3rdparty/fastcv/CMakeLists.txt b/3rdparty/fastcv/CMakeLists.txt index ab17375902..5556e1d436 100644 --- a/3rdparty/fastcv/CMakeLists.txt +++ b/3rdparty/fastcv/CMakeLists.txt @@ -9,7 +9,7 @@ if(HAVE_FASTCV) file(GLOB FASTCV_HAL_FILES "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp") - add_library(fastcv_hal STATIC ${FASTCV_HAL_FILES}) + add_library(fastcv_hal STATIC ${OPENCV_3RDPARTY_EXCLUDE_FROM_ALL} ${FASTCV_HAL_FILES}) target_include_directories(fastcv_hal PRIVATE ${CMAKE_SOURCE_DIR}/modules/core/include diff --git a/cmake/OpenCVFindLibsPerf.cmake b/cmake/OpenCVFindLibsPerf.cmake index c5fb628c44..012b80a182 100644 --- a/cmake/OpenCVFindLibsPerf.cmake +++ b/cmake/OpenCVFindLibsPerf.cmake @@ -197,13 +197,16 @@ if(WITH_FASTCV) set(FastCV_INCLUDE_PATH "${FCV_ROOT_DIR}/inc" CACHE PATH "FastCV includes directory") set(FastCV_LIB_PATH "${FCV_ROOT_DIR}/libs" CACHE PATH "FastCV library directory") ocv_install_3rdparty_licenses(FastCV "${OpenCV_BINARY_DIR}/3rdparty/fastcv/LICENSE") - if(ANDROID) - set(FASTCV_LIBRARY "${FastCV_LIB_PATH}/libfastcvopt.so" CACHE PATH "FastCV library") - install(FILES "${FASTCV_LIBRARY}" DESTINATION "${OPENCV_LIB_INSTALL_PATH}" COMPONENT "bin") - else() - set(FASTCV_LIBRARY "${FastCV_LIB_PATH}/libfastcv.a" CACHE PATH "FastCV library") - install(FILES "${FASTCV_LIBRARY}" DESTINATION "${OPENCV_LIB_INSTALL_PATH}" COMPONENT "dev") + add_library(fastcv STATIC IMPORTED) + set_target_properties(fastcv PROPERTIES + IMPORTED_LINK_INTERFACE_LIBRARIES "" + IMPORTED_LOCATION "${FastCV_LIB_PATH}/libfastcv.a" + ) + if (NOT BUILD_SHARED_LIBS) + install(FILES "${FastCV_LIB_PATH}/libfastcv.a" DESTINATION "${OPENCV_3P_LIB_INSTALL_PATH}" COMPONENT "dev") endif() + set(FASTCV_LIBRARY "fastcv" CACHE PATH "FastCV library") + list(APPEND OPENCV_LINKER_LIBS ${FASTCV_LIBRARY}) else() set(HAVE_FASTCV FALSE CACHE BOOL "FastCV status") endif() From 6ffc515b2ab01beddfe758834e985e98c9b25c52 Mon Sep 17 00:00:00 2001 From: adsha-quic Date: Wed, 16 Apr 2025 21:03:38 +0530 Subject: [PATCH 73/94] Merge pull request #27182 from CodeLinaro:boxFilter_hal_changes Parallel_for in box Filter and support for 32f box filter in Fastcv hal #27182 Added parallel_for in box filter hal and support for 32f box filter ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [ ] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- 3rdparty/fastcv/src/fastcv_hal_imgproc.cpp | 141 +++++++++++++++------ modules/imgproc/perf/perf_blur.cpp | 2 +- 2 files changed, 106 insertions(+), 37 deletions(-) diff --git a/3rdparty/fastcv/src/fastcv_hal_imgproc.cpp b/3rdparty/fastcv/src/fastcv_hal_imgproc.cpp index 922361e84d..84ee5c607e 100644 --- a/3rdparty/fastcv/src/fastcv_hal_imgproc.cpp +++ b/3rdparty/fastcv/src/fastcv_hal_imgproc.cpp @@ -314,6 +314,69 @@ int fastcv_hal_sobel( CV_HAL_RETURN(status, hal_sobel); } +class FcvBoxLoop_Invoker : public cv::ParallelLoopBody +{ +public: + + FcvBoxLoop_Invoker(cv::Mat src_, int width_, int height_, cv::Mat dst_, int bdr_, int knl_, int normalize_, int stripeHeight_, int nStripes_, int depth_) : + cv::ParallelLoopBody(), src(src_), width(width_), height(height_), dst(dst_), bdr(bdr_), knl(knl_), normalize(normalize_), stripeHeight(stripeHeight_), nStripes(nStripes_), depth(depth_) + { + } + + virtual void operator()(const cv::Range& range) const CV_OVERRIDE + { + int height_ = stripeHeight * (range.end - range.start); + int width_ = width; + cv::Mat src_; + int n = knl/2; + + if(range.end == nStripes) + height_ += (height - range.end * stripeHeight); + + src_ = cv::Mat(height_ + 2*n, width_ + 2*n, depth); + + if(range.start == 0 && range.end == nStripes) + cv::copyMakeBorder(src(cv::Rect(0, 0, width_, height_)), src_, n, n, n, n, bdr); + else if(range.start == 0) + cv::copyMakeBorder(src(cv::Rect(0, 0, width_, height_ + n)), src_, n, 0, n, n, bdr); + else if(range.end == nStripes) + cv::copyMakeBorder(src(cv::Rect(0, range.start * stripeHeight - n, width_, height_ + n)), src_, 0, n, n, n, bdr); + else + cv::copyMakeBorder(src(cv::Rect(0, range.start * stripeHeight - n, width_, height_ + 2*n)), src_, 0, 0, n, n, bdr); + + cv::Mat dst_padded = cv::Mat(height_ + 2*n, width_ + 2*n, depth); + if(depth == CV_32F) + fcvBoxFilterNxNf32((float*)src_.data, width_ + 2*n, height_ + 2*n, (width_ + 2*n)*sizeof(float), + knl, (float*)dst_padded.data, dst_padded.step[0]); + else + { + auto func = knl == 3 ? fcvBoxFilter3x3u8_v3 : fcvBoxFilter5x5u8_v2; + + func(src_.data, width_ + 2*n, height_ + 2*n, width_ + 2*n, + dst_padded.data, dst_padded.step[0], normalize, FASTCV_BORDER_UNDEFINED, 0); + } + int start_val = stripeHeight * range.start; + cv::Mat dst_temp1 = dst_padded(cv::Rect(n, n, width_, height_)); + cv::Mat dst_temp2 = dst(cv::Rect(0, start_val, width_, height_)); + dst_temp1.copyTo(dst_temp2); + } + +private: + cv::Mat src; + const int width; + const int height; + cv::Mat dst; + const int bdr; + const int knl; + const int normalize; + const int stripeHeight; + const int nStripes; + int depth; + + FcvBoxLoop_Invoker(const FcvBoxLoop_Invoker &); // = delete; + const FcvBoxLoop_Invoker& operator= (const FcvBoxLoop_Invoker &); // = delete; +}; + int fastcv_hal_boxFilter( const uchar* src_data, size_t src_step, @@ -335,15 +398,7 @@ int fastcv_hal_boxFilter( bool normalize, int border_type) { - if((width*height) < (320*240)) - { - CV_HAL_RETURN_NOT_IMPLEMENTED("input size not supported"); - } - else if(src_data == dst_data) - { - CV_HAL_RETURN_NOT_IMPLEMENTED("in-place processing not supported"); - } - else if(src_depth != CV_8U || cn != 1) + if((src_depth != CV_8U && src_depth != CV_32F) || cn != 1) { CV_HAL_RETURN_NOT_IMPLEMENTED("src type not supported"); } @@ -351,8 +406,7 @@ int fastcv_hal_boxFilter( { CV_HAL_RETURN_NOT_IMPLEMENTED("same src and dst type supported"); } - else if(ksize_width != ksize_height || - (ksize_width != 3 && ksize_width != 5)) + else if(ksize_width != ksize_height) { CV_HAL_RETURN_NOT_IMPLEMENTED("kernel size not supported"); } @@ -363,37 +417,52 @@ int fastcv_hal_boxFilter( CV_HAL_RETURN_NOT_IMPLEMENTED("ROI not supported"); } + if(src_depth == CV_32F && normalize != 1) + CV_HAL_RETURN_NOT_IMPLEMENTED("normalized kernel supported for float types"); + + if(src_depth == CV_32F && (height < 5 || width < 5 || ksize_height < 5)) + CV_HAL_RETURN_NOT_IMPLEMENTED("size not supported"); + + if(src_depth == CV_8U && (ksize_width != 3 && ksize_width != 5)) + CV_HAL_RETURN_NOT_IMPLEMENTED("kernel size not supported"); + INITIALIZATION_CHECK; - fcvBorderType bdr; - uint8_t bdrVal = 0; - switch(border_type) + cv::Mat dst_temp; + bool inPlace = src_data == dst_data ? true : false ; + + int nThreads = cv::getNumThreads(); + + cv::Mat src = cv::Mat(height, width, src_depth, (void*)src_data, src_step); + + if(inPlace) + dst_temp = cv::Mat(height, width, src_depth); + else + dst_temp = cv::Mat(height, width, src_depth, (void*)dst_data, dst_step); + + int nStripes, stripeHeight = src.rows/nThreads; + + if((size_t)src.rows < ksize_height || stripeHeight < 5 || nThreads <= 1) { - case cv::BORDER_REPLICATE: - bdr = FASTCV_BORDER_REPLICATE; - break; - case cv::BORDER_REFLECT: - bdr = FASTCV_BORDER_REFLECT; - break; - case cv::BORDER_REFLECT101: // cv::BORDER_REFLECT_101, BORDER_DEFAULT - bdr = FASTCV_BORDER_REFLECT_V2; - break; - default: - CV_HAL_RETURN_NOT_IMPLEMENTED("border type not supported"); + nStripes = 1; + stripeHeight = src.rows; + } + else + { + nStripes = nThreads; + stripeHeight = src.rows/nThreads; + } + + cv::parallel_for_(cv::Range(0, nStripes), + FcvBoxLoop_Invoker(src, width, height, dst_temp, border_type, ksize_width, normalize, stripeHeight, nStripes, src_depth), nStripes); + + if(inPlace) + { + cv::Mat dst = cv::Mat(height, width, src_depth, (void*)dst_data, dst_step); + dst_temp.copyTo(dst); } fcvStatus status = FASTCV_SUCCESS; - if(ksize_width == 3) - { - status = fcvBoxFilter3x3u8_v3(src_data, width, height, src_step, - dst_data, dst_step, normalize, bdr, bdrVal); - } - else if(ksize_width == 5) - { - status = fcvBoxFilter5x5u8_v2(src_data, width, height, src_step, - dst_data, dst_step, normalize, bdr, bdrVal); - } - CV_HAL_RETURN(status,hal_boxFilter); } diff --git a/modules/imgproc/perf/perf_blur.cpp b/modules/imgproc/perf/perf_blur.cpp index a0904156a0..fd649b08c6 100644 --- a/modules/imgproc/perf/perf_blur.cpp +++ b/modules/imgproc/perf/perf_blur.cpp @@ -104,7 +104,7 @@ PERF_TEST_P(Size_MatType_BorderType, blur16x16, Size size = get<0>(GetParam()); int type = get<1>(GetParam()); BorderType btype = get<2>(GetParam()); - double eps = 1e-3; + double eps = 1.25e-3; eps = CV_MAT_DEPTH(type) <= CV_32S ? 1 : eps; From b5d38ea4cbfdb911155ed674b7a535839bc3d6f8 Mon Sep 17 00:00:00 2001 From: quic-xuezha Date: Thu, 17 Apr 2025 14:56:58 +0800 Subject: [PATCH 74/94] Merge pull request #27217 from CodeLinaro:gaussianBlur_hal_fix Optimize gaussian blur performance in FastCV HAL #27217 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [ ] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- 3rdparty/fastcv/src/fastcv_hal_imgproc.cpp | 113 ++++++++++++++++----- 1 file changed, 88 insertions(+), 25 deletions(-) diff --git a/3rdparty/fastcv/src/fastcv_hal_imgproc.cpp b/3rdparty/fastcv/src/fastcv_hal_imgproc.cpp index 84ee5c607e..c04998cc44 100644 --- a/3rdparty/fastcv/src/fastcv_hal_imgproc.cpp +++ b/3rdparty/fastcv/src/fastcv_hal_imgproc.cpp @@ -536,30 +536,88 @@ class FcvGaussianBlurLoop_Invoker : public cv::ParallelLoopBody { public: - FcvGaussianBlurLoop_Invoker(const cv::Mat& _src, cv::Mat& _dst, int _ksize, int _borderType, int _fcvBorderValue) : - cv::ParallelLoopBody(), src(_src),dst(_dst), ksize(_ksize), borderType(_borderType), fcvBorderValue(_fcvBorderValue) + FcvGaussianBlurLoop_Invoker(const cv::Mat& _src, cv::Mat& _dst, int _ksize, int _borderType) : + cv::ParallelLoopBody(), src(_src),dst(_dst), ksize(_ksize), borderType(_borderType) { width = src.cols; height = src.rows; halfKSize = ksize / 2; fcvFuncType = FCV_MAKETYPE(ksize, src.depth()); + + switch (borderType) + { + case cv::BorderTypes::BORDER_REPLICATE: + { + fcvBorder = fcvBorderType::FASTCV_BORDER_REPLICATE; + break; + } + // For constant border, there are no border value, OpenCV default value is 0 + case cv::BorderTypes::BORDER_CONSTANT: + { + fcvBorder = fcvBorderType::FASTCV_BORDER_CONSTANT; + break; + } + case cv::BorderTypes::BORDER_REFLECT: + { + fcvBorder = fcvBorderType::FASTCV_BORDER_REFLECT; + break; + } + case cv::BorderTypes::BORDER_REFLECT_101: + { + fcvBorder = fcvBorderType::FASTCV_BORDER_REFLECT_V2; + break; + } + } } virtual void operator()(const cv::Range& range) const CV_OVERRIDE { - int rangeHeight = range.end - range.start; - int paddedHeight = rangeHeight + halfKSize * 2; - int paddedWidth = width; + int topLines = 0; + int bottomLines = 0; + int rangeHeight = range.end-range.start; + int paddedHeight = rangeHeight; - cv::Mat srcPadded = src(cv::Rect(0, range.start, paddedWidth, paddedHeight)); - cv::Mat dstPadded = dst(cv::Rect(0, range.start, paddedWidth, paddedHeight)); + // Need additional lines to be border. + if(range.start > 0) + { + topLines = MIN(range.start, halfKSize); + paddedHeight += topLines; + } + + if(range.end < height) + { + bottomLines = MIN(height-range.end, halfKSize); + paddedHeight += bottomLines; + } if (fcvFuncType == FCV_MAKETYPE(3,CV_8U)) - fcvFilterGaussian3x3u8_v4(srcPadded.data, paddedWidth, paddedHeight, srcPadded.step, dstPadded.data, dstPadded.step, - fcvBorderType::FASTCV_BORDER_UNDEFINED, fcvBorderValue); + { + cv::Mat srcPadded = src(cv::Rect(0, range.start - topLines, width, paddedHeight)); + cv::Mat dstPadded = cv::Mat(paddedHeight, width, CV_8UC1); + fcvFilterGaussian3x3u8_v4(srcPadded.data, width, paddedHeight, srcPadded.step, dstPadded.data, dstPadded.step, + fcvBorder, 0); + + // Only copy center part back to output image and ignore the padded lines + cv::Mat temp1 = dstPadded(cv::Rect(0, topLines, width, rangeHeight)); + cv::Mat temp2 = dst(cv::Rect(0, range.start, width, rangeHeight)); + temp1.copyTo(temp2); + } else if (fcvFuncType == FCV_MAKETYPE(5,CV_8U)) - fcvFilterGaussian5x5u8_v3(srcPadded.data, paddedWidth, paddedHeight, srcPadded.step, dstPadded.data, dstPadded.step, - fcvBorderType::FASTCV_BORDER_UNDEFINED, fcvBorderValue); + { + int width_ = width + ksize - 1; + int height_ = rangeHeight + ksize - 1; + cv::Mat srcPadded = cv::Mat(height_, width_, CV_8UC1); + cv::Mat dstPadded = cv::Mat(height_, width_, CV_8UC1); + cv::copyMakeBorder(src(cv::Rect(0, range.start - topLines, width, paddedHeight)), srcPadded, + halfKSize - topLines, halfKSize - bottomLines, halfKSize, halfKSize, borderType); + fcvFilterGaussian5x5u8_v3(srcPadded.data, width_, height_, srcPadded.step, dstPadded.data, dstPadded.step, + fcvBorderType::FASTCV_BORDER_UNDEFINED, 0); + + // Only copy center part back to output image and ignore the padded lines + cv::Mat temp1 = dstPadded(cv::Rect(halfKSize, halfKSize, width, rangeHeight)); + cv::Mat temp2 = dst(cv::Rect(0, range.start, width, rangeHeight)); + temp1.copyTo(temp2); + } } private: @@ -569,9 +627,9 @@ class FcvGaussianBlurLoop_Invoker : public cv::ParallelLoopBody int height; const int ksize; int halfKSize; - int fcvFuncType; int borderType; - int fcvBorderValue; + int fcvFuncType; + fcvBorderType fcvBorder; FcvGaussianBlurLoop_Invoker(const FcvGaussianBlurLoop_Invoker &); // = delete; const FcvGaussianBlurLoop_Invoker& operator= (const FcvGaussianBlurLoop_Invoker &); // = delete; @@ -597,9 +655,9 @@ int fastcv_hal_gaussianBlurBinomial( if (src_data == dst_data) CV_HAL_RETURN_NOT_IMPLEMENTED("Inplace is not supported"); - // The pixels of input image should larger than 320*240 - if((width*height) < (320*240)) - CV_HAL_RETURN_NOT_IMPLEMENTED("Input image size should be larger than 320*240"); + // The input image width and height should greater than kernel size + if (((size_t)height <= ksize) || ((size_t)width <= ksize)) + CV_HAL_RETURN_NOT_IMPLEMENTED("Input image size should be larger than kernel size"); // The input channel should be 1 if (cn != 1) @@ -609,26 +667,31 @@ int fastcv_hal_gaussianBlurBinomial( if((margin_left!=0) || (margin_top != 0) || (margin_right != 0) || (margin_bottom !=0)) CV_HAL_RETURN_NOT_IMPLEMENTED("ROI is not supported"); + // Border type check + if( border_type != cv::BorderTypes::BORDER_CONSTANT && + border_type != cv::BorderTypes::BORDER_REPLICATE && + border_type != cv::BorderTypes::BORDER_REFLECT && + border_type != cv::BorderTypes::BORDER_REFLECT101) + CV_HAL_RETURN_NOT_IMPLEMENTED(cv::format("Border type:%s is not supported", borderToString(border_type))); + INITIALIZATION_CHECK; fcvStatus status = FASTCV_SUCCESS; - int fcvFuncType = FCV_MAKETYPE(ksize, depth); + int fcvFuncType = FCV_MAKETYPE(ksize, depth); int nThreads = cv::getNumThreads(); - int nStripes = (nThreads > 1) ? ((height > 60) ? 3 * nThreads : 1) : 1; + // In each stripe, the height should be equal or larger than ksize. + // Use 3*nThreads stripes to avoid too many threads. + int nStripes = nThreads > 1 ? MIN(height / (int)ksize, 3 * nThreads) : 1; switch (fcvFuncType) { case FCV_MAKETYPE(3,CV_8U): case FCV_MAKETYPE(5,CV_8U): { - cv::Mat src = cv::Mat(height, width, CV_8UC1, (void *)src_data, src_step); - cv::Mat dst = cv::Mat(height, width, CV_8UC1, (void *)dst_data, dst_step); - cv::Mat src_tmp = cv::Mat(height + ksize - 1, width + ksize - 1, CV_8UC1); - cv::Mat dst_tmp = cv::Mat(height + ksize - 1, width + ksize - 1, CV_8UC1); - cv::copyMakeBorder(src, src_tmp, ksize / 2, ksize / 2, ksize / 2, ksize / 2, border_type); - cv::parallel_for_(cv::Range(0, height), FcvGaussianBlurLoop_Invoker(src_tmp, dst_tmp, ksize, border_type, 0), nStripes); - dst_tmp(cv::Rect(ksize / 2, ksize / 2, width, height)).copyTo(dst); + cv::Mat src = cv::Mat(height, width, CV_8UC1, (void*)src_data, src_step); + cv::Mat dst = cv::Mat(height, width, CV_8UC1, (void*)dst_data, dst_step); + cv::parallel_for_(cv::Range(0, height), FcvGaussianBlurLoop_Invoker(src, dst, ksize, border_type), nStripes); break; } default: From d42b6f438e04d4b516d2cab65ad1d328343ccc3d Mon Sep 17 00:00:00 2001 From: utibenkei Date: Sun, 20 Apr 2025 05:27:24 +0900 Subject: [PATCH 75/94] Enable Java bindings for SimpleBlobDetector::blobColor The C++ uchar type is properly mapped to Java byte type. --- modules/features2d/misc/java/gen_dict.json | 6 ++++++ .../misc/java/test/SIMPLEBLOBFeatureDetectorTest.java | 3 +-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/modules/features2d/misc/java/gen_dict.json b/modules/features2d/misc/java/gen_dict.json index 950fc8a7a1..cceec2dfff 100644 --- a/modules/features2d/misc/java/gen_dict.json +++ b/modules/features2d/misc/java/gen_dict.json @@ -7,6 +7,12 @@ "jni_var": "Feature2D %(n)s", "suffix": "J", "j_import": "org.opencv.features2d.Feature2D" + }, + "uchar": { + "j_type": "byte", + "jn_type": "byte", + "jni_type": "jbyte", + "suffix": "B" } } } diff --git a/modules/features2d/misc/java/test/SIMPLEBLOBFeatureDetectorTest.java b/modules/features2d/misc/java/test/SIMPLEBLOBFeatureDetectorTest.java index 75817ca6b1..d1ca3d371c 100644 --- a/modules/features2d/misc/java/test/SIMPLEBLOBFeatureDetectorTest.java +++ b/modules/features2d/misc/java/test/SIMPLEBLOBFeatureDetectorTest.java @@ -108,8 +108,7 @@ public class SIMPLEBLOBFeatureDetectorTest extends OpenCVTestCase { assertEquals(2, params.get_minRepeatability()); assertEquals(10.0f, params.get_minDistBetweenBlobs()); assertEquals(true, params.get_filterByColor()); - // FIXME: blobColor field has uchar type in C++ and cannot be automatically wrapped to Java as it does not support unsigned types - //assertEquals(0, params.get_blobColor()); + assertEquals(0, params.get_blobColor()); assertEquals(true, params.get_filterByArea()); assertEquals(800f, params.get_minArea()); assertEquals(6000f, params.get_maxArea()); From 11e46cda86ea6fab0a69288937ab5df85feea5b6 Mon Sep 17 00:00:00 2001 From: Yuantao Feng Date: Mon, 21 Apr 2025 14:05:52 +0800 Subject: [PATCH 76/94] Merge pull request #27201 from fengyuentau:4x/hal_rvv/dotprod HAL: implemented cv_hal_dotProduct in hal_rvv #27201 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- 3rdparty/hal_rvv/hal_rvv.hpp | 1 + 3rdparty/hal_rvv/hal_rvv_1p0/dotprod.hpp | 234 +++++++++++++++++++++++ 2 files changed, 235 insertions(+) create mode 100644 3rdparty/hal_rvv/hal_rvv_1p0/dotprod.hpp diff --git a/3rdparty/hal_rvv/hal_rvv.hpp b/3rdparty/hal_rvv/hal_rvv.hpp index b766f3c660..e86c974574 100644 --- a/3rdparty/hal_rvv/hal_rvv.hpp +++ b/3rdparty/hal_rvv/hal_rvv.hpp @@ -47,6 +47,7 @@ #include "hal_rvv_1p0/sqrt.hpp" // core #include "hal_rvv_1p0/copy_mask.hpp" // core #include "hal_rvv_1p0/div.hpp" // core +#include "hal_rvv_1p0/dotprod.hpp" // core #include "hal_rvv_1p0/moments.hpp" // imgproc #include "hal_rvv_1p0/filter.hpp" // imgproc diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/dotprod.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/dotprod.hpp new file mode 100644 index 0000000000..1f53aa56b1 --- /dev/null +++ b/3rdparty/hal_rvv/hal_rvv_1p0/dotprod.hpp @@ -0,0 +1,234 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2025, SpaceMIT Inc., all rights reserved. +// Third party copyrights are property of their respective owners. + + +#ifndef OPENCV_HAL_RVV_DOTPROD_HPP_INCLUDED +#define OPENCV_HAL_RVV_DOTPROD_HPP_INCLUDED + +#include +#include + +namespace cv { namespace cv_hal_rvv { namespace dotprod { + +#undef cv_hal_dotProduct +#define cv_hal_dotProduct cv::cv_hal_rvv::dotprod::dotprod + +namespace { + +double dotProd_8u(const uchar *a, const uchar *b, int len) { + constexpr int block_size0 = (1 << 15); + + double r = 0; + int i = 0; + while (i < len) { + int block_size = std::min(block_size0, len - i); + + vuint32m1_t s = __riscv_vmv_v_x_u32m1(0, __riscv_vsetvlmax_e32m1()); + int vl; + for (int j = 0; j < block_size; j += vl) { + vl = __riscv_vsetvl_e8m4(block_size - j); + + auto va = __riscv_vle8_v_u8m4(a + j, vl); + auto vb = __riscv_vle8_v_u8m4(b + j, vl); + + s = __riscv_vwredsumu(__riscv_vwmulu(va, vb, vl), s, vl); + } + r += (double)__riscv_vmv_x(s); + + i += block_size; + a += block_size; + b += block_size; + } + + return r; +} + +double dotProd_8s(const schar *a, const schar *b, int len) { + constexpr int block_size0 = (1 << 14); + + double r = 0; + int i = 0; + while (i < len) { + int block_size = std::min(block_size0, len - i); + + vint32m1_t s = __riscv_vmv_v_x_i32m1(0, __riscv_vsetvlmax_e32m1()); + int vl; + for (int j = 0; j < block_size; j += vl) { + vl = __riscv_vsetvl_e8m4(block_size - j); + + auto va = __riscv_vle8_v_i8m4(a + j, vl); + auto vb = __riscv_vle8_v_i8m4(b + j, vl); + + s = __riscv_vwredsum(__riscv_vwmul(va, vb, vl), s, vl); + } + r += (double)__riscv_vmv_x(s); + + i += block_size; + a += block_size; + b += block_size; + } + + return r; +} + +double dotProd_16u(const ushort *a, const ushort *b, int len) { + constexpr int block_size0 = (1 << 24); + + double r = 0; + int i = 0; + while (i < len) { + int block_size = std::min(block_size0, len - i); + + vuint64m1_t s = __riscv_vmv_v_x_u64m1(0, __riscv_vsetvlmax_e64m1()); + int vl; + for (int j = 0; j < block_size; j += vl) { + vl = __riscv_vsetvl_e16m4(block_size - j); + + auto va = __riscv_vle16_v_u16m4(a + j, vl); + auto vb = __riscv_vle16_v_u16m4(b + j, vl); + + s = __riscv_vwredsumu(__riscv_vwmulu(va, vb, vl), s, vl); + } + r += (double)__riscv_vmv_x(s); + + i += block_size; + a += block_size; + b += block_size; + } + + return r; +} + +double dotProd_16s(const short *a, const short *b, int len) { + constexpr int block_size0 = (1 << 24); + + double r = 0; + int i = 0; + while (i < len) { + int block_size = std::min(block_size0, len - i); + + vint64m1_t s = __riscv_vmv_v_x_i64m1(0, __riscv_vsetvlmax_e64m1()); + int vl; + for (int j = 0; j < block_size; j += vl) { + vl = __riscv_vsetvl_e16m4(block_size - j); + + auto va = __riscv_vle16_v_i16m4(a + j, vl); + auto vb = __riscv_vle16_v_i16m4(b + j, vl); + + s = __riscv_vwredsum(__riscv_vwmul(va, vb, vl), s, vl); + } + r += (double)__riscv_vmv_x(s); + + i += block_size; + a += block_size; + b += block_size; + } + + return r; +} + +double dotProd_32s(const int *a, const int *b, int len) { + double r = 0; + + vfloat64m8_t s = __riscv_vfmv_v_f_f64m8(0.f, __riscv_vsetvlmax_e64m8()); + int vl; + for (int j = 0; j < len; j += vl) { + vl = __riscv_vsetvl_e32m4(len - j); + + auto va = __riscv_vle32_v_i32m4(a + j, vl); + auto vb = __riscv_vle32_v_i32m4(b + j, vl); + + s = __riscv_vfadd(s, __riscv_vfcvt_f(__riscv_vwmul(va, vb, vl), vl), vl); + } + r = __riscv_vfmv_f(__riscv_vfredosum(s, __riscv_vfmv_v_f_f64m1(0.f, __riscv_vsetvlmax_e64m1()), __riscv_vsetvlmax_e64m8())); + + return r; +} + +double dotProd_32f(const float *a, const float *b, int len) { + constexpr int block_size0 = (1 << 11); + + double r = 0.f; + int i = 0; + while (i < len) { + int block_size = std::min(block_size0, len - i); + + vfloat32m4_t s = __riscv_vfmv_v_f_f32m4(0.f, __riscv_vsetvlmax_e32m4()); + int vl; + for (int j = 0; j < block_size; j += vl) { + vl = __riscv_vsetvl_e32m4(block_size - j); + + auto va = __riscv_vle32_v_f32m4(a + j, vl); + auto vb = __riscv_vle32_v_f32m4(b + j, vl); + + s = __riscv_vfmacc(s, va, vb, vl); + } + r += (double)__riscv_vfmv_f(__riscv_vfredusum(s, __riscv_vfmv_v_f_f32m1(0.f, __riscv_vsetvlmax_e32m1()), __riscv_vsetvlmax_e32m4())); + + i += block_size; + a += block_size; + b += block_size; + } + + return r; +} + +} // anonymous + +using DotProdFunc = double (*)(const uchar *a, const uchar *b, int len); +inline int dotprod(const uchar *a_data, size_t a_step, const uchar *b_data, size_t b_step, + int width, int height, int type, double *dot_val) { + int depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type); + + static DotProdFunc dotprod_tab[CV_DEPTH_MAX] = { + (DotProdFunc)dotProd_8u, (DotProdFunc)dotProd_8s, + (DotProdFunc)dotProd_16u, (DotProdFunc)dotProd_16s, + (DotProdFunc)dotProd_32s, (DotProdFunc)dotProd_32f, + nullptr, nullptr + }; + DotProdFunc func = dotprod_tab[depth]; + if (func == nullptr) { + return CV_HAL_ERROR_NOT_IMPLEMENTED; + } + + static const size_t elem_size_tab[CV_DEPTH_MAX] = { + sizeof(uchar), sizeof(schar), + sizeof(ushort), sizeof(short), + sizeof(int), sizeof(float), + sizeof(int64_t), 0, + }; + CV_Assert(elem_size_tab[depth]); + + bool a_continuous = (a_step == width * elem_size_tab[depth] * cn); + bool b_continuous = (b_step == width * elem_size_tab[depth] * cn); + size_t nplanes = 1; + size_t len = width * height; + if (!a_continuous || !b_continuous) { + nplanes = height; + len = width; + } + len *= cn; + + double r = 0; + auto _a = a_data; + auto _b = b_data; + for (size_t i = 0; i < nplanes; i++) { + if (!a_continuous || !b_continuous) { + _a = a_data + a_step * i; + _b = b_data + b_step * i; + } + r += func(_a, _b, len); + } + *dot_val = r; + + return CV_HAL_ERROR_OK; +} + +}}} // cv::cv_hal_rvv::dotprod + +#endif // OPENCV_HAL_RVV_DOTPROD_HPP_INCLUDED + From f20facc60a3e79394c3d5214751fb05f141eb628 Mon Sep 17 00:00:00 2001 From: YooLc <52441312+YooLc@users.noreply.github.com> Date: Mon, 21 Apr 2025 14:50:13 +0800 Subject: [PATCH 77/94] Merge pull request #27060 from YooLc:hal-rvv-integral MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [hal_rvv] Add cv::integral implementation and more types of input for test #27060 This patch introduces an RVV-optimized implementation of `cv::integral()` in hal_rvv, along with performance and accuracy tests for all valid input/output type combinations specified in `modules/imgproc/src/hal_replacement.hpp`: https://github.com/opencv/opencv/blob/2a8d4b8e43f6e499c5553edd26056caed284d5a6/modules/imgproc/src/hal_replacement.hpp#L960-L974 The vectorized prefix sum algorithm follows the approach described in [Prefix Sum with SIMD - Algorithmica](https://en.algorithmica.org/hpc/algorithms/prefix/). I intentionally omitted support for the following cases by returning `CV_HAL_ERROR_NOT_IMPLEMENTED`, as they are harder to implement or show limited performance gains: 1. **Tilted Sum**: The data access pattern for tilted sums requires multi-row operations, making effective vectorization difficult. 2. **3-channel images (`cn == 3`)**: Current implementation requires `VLEN/SEW` (a.k.a. number of elements in a vector register) to be a multiple of channel count, which 3-channel formats typically cannot satisfy. - Support for 1, 2 and 4 channel images is implemented 4. **Small images (`!(width >> 8 || height >> 8)`)**: The scalar implementation demonstrates better performance for images with limited dimensions. - This is the same as `3rdparty/ndsrvp/src/integral.cpp` https://github.com/opencv/opencv/blob/09c71aed141210bf2b14582974ed9d231c24edd5/3rdparty/ndsrvp/src/integral.cpp#L24-L26 Test configuration: - Platform: SpacemiT Muse Pi (K1 @ 1.60 Ghz) - Toolchain: GCC 14.2.0 - `integral_sqsum_full` test is disabled by default, so `--gtest_also_run_disabled_tests` is needed Test results: ```plaintext Geometric mean (ms) Name of Test imgproc-gcc-scalar imgproc-gcc-hal imgproc-gcc-hal vs imgproc-gcc-scalar (x-factor) integral::Size_MatType_OutMatDepth::(640x480, 8UC1, CV_32F) 1.973 1.415 1.39 integral::Size_MatType_OutMatDepth::(640x480, 8UC1, CV_32S) 1.343 1.351 0.99 integral::Size_MatType_OutMatDepth::(640x480, 8UC1, CV_64F) 2.021 2.756 0.73 integral::Size_MatType_OutMatDepth::(640x480, 8UC2, CV_32F) 4.695 2.874 1.63 integral::Size_MatType_OutMatDepth::(640x480, 8UC2, CV_32S) 4.028 2.801 1.44 integral::Size_MatType_OutMatDepth::(640x480, 8UC2, CV_64F) 5.965 4.926 1.21 integral::Size_MatType_OutMatDepth::(640x480, 8UC4, CV_32F) 9.970 4.440 2.25 integral::Size_MatType_OutMatDepth::(640x480, 8UC4, CV_32S) 7.934 4.244 1.87 integral::Size_MatType_OutMatDepth::(640x480, 8UC4, CV_64F) 14.696 8.431 1.74 integral::Size_MatType_OutMatDepth::(1280x720, 8UC1, CV_32F) 5.949 4.108 1.45 integral::Size_MatType_OutMatDepth::(1280x720, 8UC1, CV_32S) 4.064 4.080 1.00 integral::Size_MatType_OutMatDepth::(1280x720, 8UC1, CV_64F) 6.137 7.975 0.77 integral::Size_MatType_OutMatDepth::(1280x720, 8UC2, CV_32F) 13.896 8.721 1.59 integral::Size_MatType_OutMatDepth::(1280x720, 8UC2, CV_32S) 10.948 8.513 1.29 integral::Size_MatType_OutMatDepth::(1280x720, 8UC2, CV_64F) 18.046 15.234 1.18 integral::Size_MatType_OutMatDepth::(1280x720, 8UC4, CV_32F) 35.105 13.778 2.55 integral::Size_MatType_OutMatDepth::(1280x720, 8UC4, CV_32S) 27.135 13.417 2.02 integral::Size_MatType_OutMatDepth::(1280x720, 8UC4, CV_64F) 43.477 25.616 1.70 integral::Size_MatType_OutMatDepth::(1920x1080, 8UC1, CV_32F) 13.386 9.281 1.44 integral::Size_MatType_OutMatDepth::(1920x1080, 8UC1, CV_32S) 9.159 9.194 1.00 integral::Size_MatType_OutMatDepth::(1920x1080, 8UC1, CV_64F) 13.776 17.836 0.77 integral::Size_MatType_OutMatDepth::(1920x1080, 8UC2, CV_32F) 31.943 19.435 1.64 integral::Size_MatType_OutMatDepth::(1920x1080, 8UC2, CV_32S) 24.747 18.946 1.31 integral::Size_MatType_OutMatDepth::(1920x1080, 8UC2, CV_64F) 35.925 33.943 1.06 integral::Size_MatType_OutMatDepth::(1920x1080, 8UC4, CV_32F) 66.493 29.692 2.24 integral::Size_MatType_OutMatDepth::(1920x1080, 8UC4, CV_32S) 54.737 28.250 1.94 integral::Size_MatType_OutMatDepth::(1920x1080, 8UC4, CV_64F) 91.880 57.495 1.60 integral_sqsum::Size_MatType_OutMatDepth::(640x480, 8UC1, CV_32F) 4.384 4.016 1.09 integral_sqsum::Size_MatType_OutMatDepth::(640x480, 8UC1, CV_32S) 3.676 3.960 0.93 integral_sqsum::Size_MatType_OutMatDepth::(640x480, 8UC1, CV_64F) 5.620 5.224 1.08 integral_sqsum::Size_MatType_OutMatDepth::(640x480, 8UC2, CV_32F) 9.971 7.696 1.30 integral_sqsum::Size_MatType_OutMatDepth::(640x480, 8UC2, CV_32S) 8.934 7.632 1.17 integral_sqsum::Size_MatType_OutMatDepth::(640x480, 8UC2, CV_64F) 9.927 9.759 1.02 integral_sqsum::Size_MatType_OutMatDepth::(640x480, 8UC4, CV_32F) 21.556 12.288 1.75 integral_sqsum::Size_MatType_OutMatDepth::(640x480, 8UC4, CV_32S) 21.261 12.089 1.76 integral_sqsum::Size_MatType_OutMatDepth::(640x480, 8UC4, CV_64F) 23.989 16.278 1.47 integral_sqsum::Size_MatType_OutMatDepth::(1280x720, 8UC1, CV_32F) 15.232 11.752 1.30 integral_sqsum::Size_MatType_OutMatDepth::(1280x720, 8UC1, CV_32S) 12.976 11.721 1.11 integral_sqsum::Size_MatType_OutMatDepth::(1280x720, 8UC1, CV_64F) 16.450 15.627 1.05 integral_sqsum::Size_MatType_OutMatDepth::(1280x720, 8UC2, CV_32F) 25.932 23.243 1.12 integral_sqsum::Size_MatType_OutMatDepth::(1280x720, 8UC2, CV_32S) 24.750 23.019 1.08 integral_sqsum::Size_MatType_OutMatDepth::(1280x720, 8UC2, CV_64F) 28.228 29.605 0.95 integral_sqsum::Size_MatType_OutMatDepth::(1280x720, 8UC4, CV_32F) 61.665 37.477 1.65 integral_sqsum::Size_MatType_OutMatDepth::(1280x720, 8UC4, CV_32S) 61.536 37.126 1.66 integral_sqsum::Size_MatType_OutMatDepth::(1280x720, 8UC4, CV_64F) 73.989 48.994 1.51 integral_sqsum::Size_MatType_OutMatDepth::(1920x1080, 8UC1, CV_32F) 49.640 26.529 1.87 integral_sqsum::Size_MatType_OutMatDepth::(1920x1080, 8UC1, CV_32S) 35.869 26.417 1.36 integral_sqsum::Size_MatType_OutMatDepth::(1920x1080, 8UC1, CV_64F) 34.378 35.056 0.98 integral_sqsum::Size_MatType_OutMatDepth::(1920x1080, 8UC2, CV_32F) 82.138 52.661 1.56 integral_sqsum::Size_MatType_OutMatDepth::(1920x1080, 8UC2, CV_32S) 54.644 52.089 1.05 integral_sqsum::Size_MatType_OutMatDepth::(1920x1080, 8UC2, CV_64F) 75.073 66.670 1.13 integral_sqsum::Size_MatType_OutMatDepth::(1920x1080, 8UC4, CV_32F) 143.283 83.943 1.71 integral_sqsum::Size_MatType_OutMatDepth::(1920x1080, 8UC4, CV_32S) 156.851 82.378 1.90 integral_sqsum::Size_MatType_OutMatDepth::(1920x1080, 8UC4, CV_64F) 521.594 111.375 4.68 integral_sqsum_full::Size_MatType_OutMatDepthArray::(640x480, (8UC1, DEPTH_32F_32F)) 3.529 2.787 1.27 integral_sqsum_full::Size_MatType_OutMatDepthArray::(640x480, (8UC1, DEPTH_32F_64F)) 4.396 3.998 1.10 integral_sqsum_full::Size_MatType_OutMatDepthArray::(640x480, (8UC1, DEPTH_32S_32F)) 3.229 2.774 1.16 integral_sqsum_full::Size_MatType_OutMatDepthArray::(640x480, (8UC1, DEPTH_32S_32S)) 2.945 2.780 1.06 integral_sqsum_full::Size_MatType_OutMatDepthArray::(640x480, (8UC1, DEPTH_32S_64F)) 3.857 3.995 0.97 integral_sqsum_full::Size_MatType_OutMatDepthArray::(640x480, (8UC1, DEPTH_64F_64F)) 5.872 5.228 1.12 integral_sqsum_full::Size_MatType_OutMatDepthArray::(640x480, (16UC1, DEPTH_64F_64F)) 6.075 5.277 1.15 integral_sqsum_full::Size_MatType_OutMatDepthArray::(640x480, (16SC1, DEPTH_64F_64F)) 5.680 5.296 1.07 integral_sqsum_full::Size_MatType_OutMatDepthArray::(640x480, (32FC1, DEPTH_32F_32F)) 3.355 2.896 1.16 integral_sqsum_full::Size_MatType_OutMatDepthArray::(640x480, (32FC1, DEPTH_32F_64F)) 4.183 4.000 1.05 integral_sqsum_full::Size_MatType_OutMatDepthArray::(640x480, (32FC1, DEPTH_64F_64F)) 6.237 5.143 1.21 integral_sqsum_full::Size_MatType_OutMatDepthArray::(640x480, (64FC1, DEPTH_64F_64F)) 4.753 4.783 0.99 integral_sqsum_full::Size_MatType_OutMatDepthArray::(640x480, (8UC2, DEPTH_32F_32F)) 8.021 5.793 1.38 integral_sqsum_full::Size_MatType_OutMatDepthArray::(640x480, (8UC2, DEPTH_32F_64F)) 9.963 7.704 1.29 integral_sqsum_full::Size_MatType_OutMatDepthArray::(640x480, (8UC2, DEPTH_32S_32F)) 7.864 5.720 1.37 integral_sqsum_full::Size_MatType_OutMatDepthArray::(640x480, (8UC2, DEPTH_32S_32S)) 7.141 5.699 1.25 integral_sqsum_full::Size_MatType_OutMatDepthArray::(640x480, (8UC2, DEPTH_32S_64F)) 9.228 7.646 1.21 integral_sqsum_full::Size_MatType_OutMatDepthArray::(640x480, (8UC2, DEPTH_64F_64F)) 9.940 9.759 1.02 integral_sqsum_full::Size_MatType_OutMatDepthArray::(640x480, (16UC2, DEPTH_64F_64F)) 10.606 9.716 1.09 integral_sqsum_full::Size_MatType_OutMatDepthArray::(640x480, (16SC2, DEPTH_64F_64F)) 9.933 9.751 1.02 integral_sqsum_full::Size_MatType_OutMatDepthArray::(640x480, (32FC2, DEPTH_32F_32F)) 7.986 5.962 1.34 integral_sqsum_full::Size_MatType_OutMatDepthArray::(640x480, (32FC2, DEPTH_32F_64F)) 9.243 7.598 1.22 integral_sqsum_full::Size_MatType_OutMatDepthArray::(640x480, (32FC2, DEPTH_64F_64F)) 10.573 9.425 1.12 integral_sqsum_full::Size_MatType_OutMatDepthArray::(640x480, (64FC2, DEPTH_64F_64F)) 11.029 8.977 1.23 integral_sqsum_full::Size_MatType_OutMatDepthArray::(640x480, (8UC4, DEPTH_32F_32F)) 17.236 8.881 1.94 integral_sqsum_full::Size_MatType_OutMatDepthArray::(640x480, (8UC4, DEPTH_32F_64F)) 20.905 12.322 1.70 integral_sqsum_full::Size_MatType_OutMatDepthArray::(640x480, (8UC4, DEPTH_32S_32F)) 16.011 8.666 1.85 integral_sqsum_full::Size_MatType_OutMatDepthArray::(640x480, (8UC4, DEPTH_32S_32S)) 15.932 8.507 1.87 integral_sqsum_full::Size_MatType_OutMatDepthArray::(640x480, (8UC4, DEPTH_32S_64F)) 20.713 12.115 1.71 integral_sqsum_full::Size_MatType_OutMatDepthArray::(640x480, (8UC4, DEPTH_64F_64F)) 23.953 16.284 1.47 integral_sqsum_full::Size_MatType_OutMatDepthArray::(640x480, (16UC4, DEPTH_64F_64F)) 25.127 16.341 1.54 integral_sqsum_full::Size_MatType_OutMatDepthArray::(640x480, (16SC4, DEPTH_64F_64F)) 24.950 16.441 1.52 integral_sqsum_full::Size_MatType_OutMatDepthArray::(640x480, (32FC4, DEPTH_32F_32F)) 17.261 8.906 1.94 integral_sqsum_full::Size_MatType_OutMatDepthArray::(640x480, (32FC4, DEPTH_32F_64F)) 21.944 12.073 1.82 integral_sqsum_full::Size_MatType_OutMatDepthArray::(640x480, (32FC4, DEPTH_64F_64F)) 25.921 15.539 1.67 integral_sqsum_full::Size_MatType_OutMatDepthArray::(640x480, (64FC4, DEPTH_64F_64F)) 27.938 14.824 1.88 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1280x720, (8UC1, DEPTH_32F_32F)) 11.156 8.260 1.35 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1280x720, (8UC1, DEPTH_32F_64F)) 14.777 11.869 1.24 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1280x720, (8UC1, DEPTH_32S_32F)) 9.693 8.221 1.18 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1280x720, (8UC1, DEPTH_32S_32S)) 9.023 8.256 1.09 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1280x720, (8UC1, DEPTH_32S_64F)) 13.276 11.821 1.12 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1280x720, (8UC1, DEPTH_64F_64F)) 15.406 15.618 0.99 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1280x720, (16UC1, DEPTH_64F_64F)) 16.799 15.749 1.07 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1280x720, (16SC1, DEPTH_64F_64F)) 15.054 15.806 0.95 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1280x720, (32FC1, DEPTH_32F_32F)) 10.055 7.999 1.26 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1280x720, (32FC1, DEPTH_32F_64F)) 13.506 11.253 1.20 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1280x720, (32FC1, DEPTH_64F_64F)) 14.952 15.021 1.00 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1280x720, (64FC1, DEPTH_64F_64F)) 13.761 14.002 0.98 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1280x720, (8UC2, DEPTH_32F_32F)) 22.677 17.330 1.31 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1280x720, (8UC2, DEPTH_32F_64F)) 26.283 23.237 1.13 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1280x720, (8UC2, DEPTH_32S_32F)) 20.126 17.118 1.18 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1280x720, (8UC2, DEPTH_32S_32S)) 19.337 17.041 1.13 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1280x720, (8UC2, DEPTH_32S_64F)) 24.973 23.004 1.09 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1280x720, (8UC2, DEPTH_64F_64F)) 29.959 29.585 1.01 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1280x720, (16UC2, DEPTH_64F_64F)) 33.598 29.599 1.14 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1280x720, (16SC2, DEPTH_64F_64F)) 46.213 29.741 1.55 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1280x720, (32FC2, DEPTH_32F_32F)) 33.077 17.556 1.88 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1280x720, (32FC2, DEPTH_32F_64F)) 33.960 22.991 1.48 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1280x720, (32FC2, DEPTH_64F_64F)) 41.792 28.803 1.45 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1280x720, (64FC2, DEPTH_64F_64F)) 34.660 28.532 1.21 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1280x720, (8UC4, DEPTH_32F_32F)) 52.989 27.659 1.92 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1280x720, (8UC4, DEPTH_32F_64F)) 62.418 37.515 1.66 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1280x720, (8UC4, DEPTH_32S_32F)) 50.902 27.310 1.86 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1280x720, (8UC4, DEPTH_32S_32S)) 47.301 27.019 1.75 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1280x720, (8UC4, DEPTH_32S_64F)) 61.982 37.140 1.67 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1280x720, (8UC4, DEPTH_64F_64F)) 79.403 49.041 1.62 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1280x720, (16UC4, DEPTH_64F_64F)) 86.550 49.180 1.76 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1280x720, (16SC4, DEPTH_64F_64F)) 85.715 49.468 1.73 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1280x720, (32FC4, DEPTH_32F_32F)) 63.932 28.019 2.28 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1280x720, (32FC4, DEPTH_32F_64F)) 68.180 36.858 1.85 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1280x720, (32FC4, DEPTH_64F_64F)) 83.063 46.483 1.79 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1280x720, (64FC4, DEPTH_64F_64F)) 91.990 44.545 2.07 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1920x1080, (8UC1, DEPTH_32F_32F)) 25.503 18.609 1.37 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1920x1080, (8UC1, DEPTH_32F_64F)) 29.544 26.635 1.11 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1920x1080, (8UC1, DEPTH_32S_32F)) 22.581 18.514 1.22 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1920x1080, (8UC1, DEPTH_32S_32S)) 20.860 18.547 1.12 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1920x1080, (8UC1, DEPTH_32S_64F)) 26.046 26.373 0.99 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1920x1080, (8UC1, DEPTH_64F_64F)) 34.831 34.997 1.00 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1920x1080, (16UC1, DEPTH_64F_64F)) 36.428 35.214 1.03 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1920x1080, (16SC1, DEPTH_64F_64F)) 32.435 35.314 0.92 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1920x1080, (32FC1, DEPTH_32F_32F)) 22.548 18.845 1.20 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1920x1080, (32FC1, DEPTH_32F_64F)) 28.589 25.790 1.11 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1920x1080, (32FC1, DEPTH_64F_64F)) 32.625 33.791 0.97 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1920x1080, (64FC1, DEPTH_64F_64F)) 30.158 31.889 0.95 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1920x1080, (8UC2, DEPTH_32F_32F)) 53.374 38.938 1.37 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1920x1080, (8UC2, DEPTH_32F_64F)) 73.892 52.747 1.40 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1920x1080, (8UC2, DEPTH_32S_32F)) 47.392 38.572 1.23 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1920x1080, (8UC2, DEPTH_32S_32S)) 45.638 38.225 1.19 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1920x1080, (8UC2, DEPTH_32S_64F)) 69.966 52.156 1.34 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1920x1080, (8UC2, DEPTH_64F_64F)) 68.560 66.963 1.02 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1920x1080, (16UC2, DEPTH_64F_64F)) 71.487 65.420 1.09 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1920x1080, (16SC2, DEPTH_64F_64F)) 68.127 65.718 1.04 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1920x1080, (32FC2, DEPTH_32F_32F)) 72.967 39.987 1.82 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1920x1080, (32FC2, DEPTH_32F_64F)) 63.933 51.408 1.24 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1920x1080, (32FC2, DEPTH_64F_64F)) 73.334 63.354 1.16 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1920x1080, (64FC2, DEPTH_64F_64F)) 80.983 60.778 1.33 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1920x1080, (8UC4, DEPTH_32F_32F)) 116.981 59.908 1.95 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1920x1080, (8UC4, DEPTH_32F_64F)) 155.085 83.974 1.85 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1920x1080, (8UC4, DEPTH_32S_32F)) 109.567 58.525 1.87 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1920x1080, (8UC4, DEPTH_32S_32S)) 105.457 57.124 1.85 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1920x1080, (8UC4, DEPTH_32S_64F)) 157.325 82.485 1.91 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1920x1080, (8UC4, DEPTH_64F_64F)) 265.776 111.577 2.38 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1920x1080, (16UC4, DEPTH_64F_64F)) 585.218 110.583 5.29 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1920x1080, (16SC4, DEPTH_64F_64F)) 585.418 111.302 5.26 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1920x1080, (32FC4, DEPTH_32F_32F)) 126.456 60.415 2.09 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1920x1080, (32FC4, DEPTH_32F_64F)) 169.278 81.460 2.08 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1920x1080, (32FC4, DEPTH_64F_64F)) 281.256 104.732 2.69 integral_sqsum_full::Size_MatType_OutMatDepthArray::(1920x1080, (64FC4, DEPTH_64F_64F)) 620.885 99.953 6.21 ``` The vectorized implementation shows progressively better acceleration for larger image sizes and higher channel counts, achieving up to 6.21× speedup for 64FC4 (1920×1080) inputs with `DEPTH_64F_64F` configuration. This is my first time proposing patch for the OpenCV Project 🥹, if there's anything that can be improved, please tell me. ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [x] The feature is well documented and sample code can be built with the project CMake --- 3rdparty/hal_rvv/hal_rvv.hpp | 1 + 3rdparty/hal_rvv/hal_rvv_1p0/integral.hpp | 173 +++++++++++++ 3rdparty/hal_rvv/hal_rvv_1p0/types.hpp | 281 ++++++++++++++++++++- modules/imgproc/perf/perf_integral.cpp | 37 ++- modules/imgproc/src/sumpixels.dispatch.cpp | 3 +- modules/imgproc/test/test_filter.cpp | 35 ++- 6 files changed, 510 insertions(+), 20 deletions(-) create mode 100644 3rdparty/hal_rvv/hal_rvv_1p0/integral.hpp diff --git a/3rdparty/hal_rvv/hal_rvv.hpp b/3rdparty/hal_rvv/hal_rvv.hpp index e86c974574..c18d7949ce 100644 --- a/3rdparty/hal_rvv/hal_rvv.hpp +++ b/3rdparty/hal_rvv/hal_rvv.hpp @@ -57,6 +57,7 @@ #include "hal_rvv_1p0/thresh.hpp" // imgproc #include "hal_rvv_1p0/histogram.hpp" // imgproc #include "hal_rvv_1p0/resize.hpp" // imgproc +#include "hal_rvv_1p0/integral.hpp" // imgproc #endif #endif diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/integral.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/integral.hpp new file mode 100644 index 0000000000..a3ea0b5557 --- /dev/null +++ b/3rdparty/hal_rvv/hal_rvv_1p0/integral.hpp @@ -0,0 +1,173 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +// Copyright (C) 2025, Institute of Software, Chinese Academy of Sciences. + +#ifndef OPENCV_HAL_RVV_INTEGRAL_HPP_INCLUDED +#define OPENCV_HAL_RVV_INTEGRAL_HPP_INCLUDED + +#include +#include "types.hpp" + +namespace cv { namespace cv_hal_rvv { + +#undef cv_hal_integral +#define cv_hal_integral cv::cv_hal_rvv::integral + +template +inline typename vec_t::VecType repeat_last_n(typename vec_t::VecType vs, int n, size_t vl) { + auto v_last = vec_t::vslidedown(vs, vl - n, vl); + if (n == 1) return vec_t::vmv(vec_t::vmv_x(v_last), vl); + for (size_t offset = n; offset < vl; offset <<= 1) { + v_last = vec_t::vslideup(v_last, v_last, offset, vl); + } + return v_last; +} + +template +inline int integral_inner(const uchar* src_data, size_t src_step, + uchar* sum_data, size_t sum_step, + int width, int height, int cn) { + using data_t = typename data_vec_t::ElemType; + using acc_t = typename acc_vec_t::ElemType; + + for (int y = 0; y < height; y++) { + const data_t* src = reinterpret_cast(src_data + src_step * y); + acc_t* prev = reinterpret_cast(sum_data + sum_step * y); + acc_t* curr = reinterpret_cast(sum_data + sum_step * (y + 1)); + memset(curr, 0, cn * sizeof(acc_t)); + + size_t vl = acc_vec_t::setvlmax(); + auto sum = acc_vec_t::vmv(0, vl); + for (size_t x = 0; x < static_cast(width); x += vl) { + vl = acc_vec_t::setvl(width - x); + __builtin_prefetch(&src[x + vl], 0); + __builtin_prefetch(&prev[x + cn], 0); + + auto v_src = data_vec_t::vload(&src[x], vl); + auto acc = acc_vec_t::cast(v_src, vl); + + if (sqsum) { // Squared Sum + acc = acc_vec_t::vmul(acc, acc, vl); + } + + auto v_zero = acc_vec_t::vmv(0, vl); + for (size_t offset = cn; offset < vl; offset <<= 1) { + auto v_shift = acc_vec_t::vslideup(v_zero, acc, offset, vl); + acc = acc_vec_t::vadd(acc, v_shift, vl); + } + auto last_n = repeat_last_n(acc, cn, vl); + + auto v_prev = acc_vec_t::vload(&prev[x + cn], vl); + acc = acc_vec_t::vadd(acc, v_prev, vl); + acc = acc_vec_t::vadd(acc, sum, vl); + sum = acc_vec_t::vadd(sum, last_n, vl); + + acc_vec_t::vstore(&curr[x + cn], acc, vl); + } + } + + return CV_HAL_ERROR_OK; +} + +template +inline int integral(const uchar* src_data, size_t src_step, uchar* sum_data, size_t sum_step, uchar* sqsum_data, size_t sqsum_step, int width, int height, int cn) { + memset(sum_data, 0, (sum_step) * sizeof(uchar)); + + int result = CV_HAL_ERROR_NOT_IMPLEMENTED; + if (sqsum_data == nullptr) { + result = integral_inner(src_data, src_step, sum_data, sum_step, width, height, cn); + } else { + result = integral_inner(src_data, src_step, sum_data, sum_step, width, height, cn); + memset(sqsum_data, 0, (sqsum_step) * sizeof(uchar)); + if (result != CV_HAL_ERROR_OK) return result; + result = integral_inner(src_data, src_step, sqsum_data, sqsum_step, width, height, cn); + } + return result; +} + +/** + @brief Calculate integral image + @param depth Depth of source image + @param sdepth Depth of sum image + @param sqdepth Depth of square sum image + @param src_data Source image data + @param src_step Source image step + @param sum_data Sum image data + @param sum_step Sum image step + @param sqsum_data Square sum image data + @param sqsum_step Square sum image step + @param tilted_data Tilted sum image data + @param tilted_step Tilted sum image step + @param width Source image width + @param height Source image height + @param cn Number of channels + @note Following combinations of image depths are used: + Source | Sum | Square sum + -------|-----|----------- + CV_8U | CV_32S | CV_64F + CV_8U | CV_32S | CV_32F + CV_8U | CV_32S | CV_32S + CV_8U | CV_32F | CV_64F + CV_8U | CV_32F | CV_32F + CV_8U | CV_64F | CV_64F + CV_16U | CV_64F | CV_64F + CV_16S | CV_64F | CV_64F + CV_32F | CV_32F | CV_64F + CV_32F | CV_32F | CV_32F + CV_32F | CV_64F | CV_64F + CV_64F | CV_64F | CV_64F +*/ +inline int integral(int depth, int sdepth, int sqdepth, + const uchar* src_data, size_t src_step, + uchar* sum_data, size_t sum_step, + uchar* sqsum_data, size_t sqsum_step, + uchar* tilted_data, [[maybe_unused]] size_t tilted_step, + int width, int height, int cn) { + // tilted sum and cn == 3 cases are not supported + if (tilted_data || cn == 3) { + return CV_HAL_ERROR_NOT_IMPLEMENTED; + } + + // Skip images that are too small + if (!(width >> 8 || height >> 8)) { + return CV_HAL_ERROR_NOT_IMPLEMENTED; + } + + int result = CV_HAL_ERROR_NOT_IMPLEMENTED; + + width *= cn; + + if( depth == CV_8U && sdepth == CV_32S && sqdepth == CV_64F ) + result = integral, RVV, RVV>(src_data, src_step, sum_data, sum_step, sqsum_data, sqsum_step, width, height, cn); + else if( depth == CV_8U && sdepth == CV_32S && sqdepth == CV_32F ) + result = integral, RVV, RVV>(src_data, src_step, sum_data, sum_step, sqsum_data, sqsum_step, width, height, cn); + else if( depth == CV_8U && sdepth == CV_32S && sqdepth == CV_32S ) + result = integral, RVV, RVV>(src_data, src_step, sum_data, sum_step, sqsum_data, sqsum_step, width, height, cn); + else if( depth == CV_8U && sdepth == CV_32F && sqdepth == CV_64F ) + result = integral, RVV, RVV>(src_data, src_step, sum_data, sum_step, sqsum_data, sqsum_step, width, height, cn); + else if( depth == CV_8U && sdepth == CV_32F && sqdepth == CV_32F ) + result = integral, RVV, RVV>(src_data, src_step, sum_data, sum_step, sqsum_data, sqsum_step, width, height, cn); + else if( depth == CV_8U && sdepth == CV_64F && sqdepth == CV_64F ) + result = integral, RVV, RVV>(src_data, src_step, sum_data, sum_step, sqsum_data, sqsum_step, width, height, cn); + else if( depth == CV_16U && sdepth == CV_64F && sqdepth == CV_64F ) + result = integral, RVV, RVV>(src_data, src_step, sum_data, sum_step, sqsum_data, sqsum_step, width, height, cn); + else if( depth == CV_16S && sdepth == CV_64F && sqdepth == CV_64F ) + result = integral, RVV, RVV>(src_data, src_step, sum_data, sum_step, sqsum_data, sqsum_step, width, height, cn); + else if( depth == CV_32F && sdepth == CV_32F && sqdepth == CV_64F ) + result = integral, RVV, RVV>(src_data, src_step, sum_data, sum_step, sqsum_data, sqsum_step, width, height, cn); + else if( depth == CV_32F && sdepth == CV_32F && sqdepth == CV_32F ) + result = integral, RVV, RVV>(src_data, src_step, sum_data, sum_step, sqsum_data, sqsum_step, width, height, cn); + else if( depth == CV_32F && sdepth == CV_64F && sqdepth == CV_64F ) + result = integral, RVV, RVV>(src_data, src_step, sum_data, sum_step, sqsum_data, sqsum_step, width, height, cn); + else if( depth == CV_64F && sdepth == CV_64F && sqdepth == CV_64F ) { + result = integral, RVV, RVV>(src_data, src_step, sum_data, sum_step, sqsum_data, sqsum_step, width, height, cn); + } + + return result; +} + +}} + +#endif diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/types.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/types.hpp index 8c8ad23787..6613a018fc 100644 --- a/3rdparty/hal_rvv/hal_rvv_1p0/types.hpp +++ b/3rdparty/hal_rvv/hal_rvv_1p0/types.hpp @@ -153,6 +153,12 @@ static inline VecType vmv(ElemType a, size_t vl) { static inline VecType vmv_s(ElemType a, size_t vl) { \ return __riscv_v##IS_F##mv_s_##X_OR_F##_##TYPE##LMUL(a, vl); \ } \ +static inline VecType vslideup(VecType vs2, VecType vs1, size_t n, size_t vl) { \ + return __riscv_vslideup_vx_##TYPE##LMUL(vs2, vs1, n, vl); \ +} \ +static inline VecType vslidedown(VecType vs, size_t n, size_t vl) { \ + return __riscv_vslidedown_vx_##TYPE##LMUL(vs, n, vl); \ +} \ HAL_RVV_SIZE_RELATED_CUSTOM(EEW, TYPE, LMUL) #define HAL_RVV_SIZE_UNRELATED(S_OR_F, X_OR_F, IS_U, IS_F, IS_O) \ @@ -380,7 +386,7 @@ template <> struct RVV_ToFloatHelper<8> {using type = double;}; template <> \ inline ONE::VecType ONE::cast(TWO::VecType v, size_t vl) { return __riscv_vncvt_x(v, vl); } \ template <> \ - inline TWO::VecType TWO::cast(ONE::VecType v, size_t vl) { return __riscv_vwcvt_x(v, vl); } + inline TWO::VecType TWO::cast(ONE::VecType v, size_t vl) { return __riscv_vsext_vf2(v, vl); } HAL_RVV_CVT(RVV_I8M4, RVV_I16M8) HAL_RVV_CVT(RVV_I8M2, RVV_I16M4) @@ -406,7 +412,7 @@ HAL_RVV_CVT(RVV_I32MF2, RVV_I64M1) template <> \ inline ONE::VecType ONE::cast(TWO::VecType v, size_t vl) { return __riscv_vncvt_x(v, vl); } \ template <> \ - inline TWO::VecType TWO::cast(ONE::VecType v, size_t vl) { return __riscv_vwcvtu_x(v, vl); } + inline TWO::VecType TWO::cast(ONE::VecType v, size_t vl) { return __riscv_vzext_vf2(v, vl); } HAL_RVV_CVT(RVV_U8M4, RVV_U16M8) HAL_RVV_CVT(RVV_U8M2, RVV_U16M4) @@ -592,6 +598,277 @@ HAL_RVV_CVT( uint8_t, int8_t, u8, i8, LMUL_f8, mf8) #undef HAL_RVV_CVT +#define HAL_RVV_CVT(A, B, A_TYPE, B_TYPE, LMUL_TYPE, LMUL) \ + template <> \ + inline RVV::VecType RVV::cast(RVV::VecType v, [[maybe_unused]] size_t vl) { \ + return __riscv_vreinterpret_##A_TYPE##LMUL(v); \ + } \ + template <> \ + inline RVV::VecType RVV::cast(RVV::VecType v, [[maybe_unused]] size_t vl) { \ + return __riscv_vreinterpret_##B_TYPE##LMUL(v); \ + } + +#define HAL_RVV_CVT2(A, B, A_TYPE, B_TYPE) \ + HAL_RVV_CVT(A, B, A_TYPE, B_TYPE, LMUL_1, m1) \ + HAL_RVV_CVT(A, B, A_TYPE, B_TYPE, LMUL_2, m2) \ + HAL_RVV_CVT(A, B, A_TYPE, B_TYPE, LMUL_4, m4) \ + HAL_RVV_CVT(A, B, A_TYPE, B_TYPE, LMUL_8, m8) + +HAL_RVV_CVT2( uint8_t, int8_t, u8, i8) +HAL_RVV_CVT2(uint16_t, int16_t, u16, i16) +HAL_RVV_CVT2(uint32_t, int32_t, u32, i32) +HAL_RVV_CVT2(uint64_t, int64_t, u64, i64) + +#undef HAL_RVV_CVT2 +#undef HAL_RVV_CVT + +#define HAL_RVV_CVT(FROM, INTERMEDIATE, TO) \ + template <> \ + inline TO::VecType TO::cast(FROM::VecType v, size_t vl) { \ + return TO::cast(INTERMEDIATE::cast(v, vl), vl); \ + } \ + template <> \ + inline FROM::VecType FROM::cast(TO::VecType v, size_t vl) { \ + return FROM::cast(INTERMEDIATE::cast(v, vl), vl); \ + } + +// Integer and Float conversions +HAL_RVV_CVT(RVV_I8M1, RVV_I32M4, RVV_F32M4) +HAL_RVV_CVT(RVV_I8M2, RVV_I32M8, RVV_F32M8) +HAL_RVV_CVT(RVV_I8M1, RVV_I64M8, RVV_F64M8) + +HAL_RVV_CVT(RVV_I16M1, RVV_I32M2, RVV_F32M2) +HAL_RVV_CVT(RVV_I16M2, RVV_I32M4, RVV_F32M4) +HAL_RVV_CVT(RVV_I16M4, RVV_I32M8, RVV_F32M8) +HAL_RVV_CVT(RVV_I16M1, RVV_I64M4, RVV_F64M4) +HAL_RVV_CVT(RVV_I16M2, RVV_I64M8, RVV_F64M8) + +HAL_RVV_CVT(RVV_I32M1, RVV_I64M2, RVV_F64M2) +HAL_RVV_CVT(RVV_I32M2, RVV_I64M4, RVV_F64M4) +HAL_RVV_CVT(RVV_I32M4, RVV_I64M8, RVV_F64M8) + +HAL_RVV_CVT(RVV_U8M1, RVV_U32M4, RVV_F32M4) +HAL_RVV_CVT(RVV_U8M2, RVV_U32M8, RVV_F32M8) +HAL_RVV_CVT(RVV_U8M1, RVV_U64M8, RVV_F64M8) + +HAL_RVV_CVT(RVV_U16M1, RVV_U32M2, RVV_F32M2) +HAL_RVV_CVT(RVV_U16M2, RVV_U32M4, RVV_F32M4) +HAL_RVV_CVT(RVV_U16M4, RVV_U32M8, RVV_F32M8) +HAL_RVV_CVT(RVV_U16M1, RVV_U64M4, RVV_F64M4) +HAL_RVV_CVT(RVV_U16M2, RVV_U64M8, RVV_F64M8) + +HAL_RVV_CVT(RVV_U32M1, RVV_U64M2, RVV_F64M2) +HAL_RVV_CVT(RVV_U32M2, RVV_U64M4, RVV_F64M4) +HAL_RVV_CVT(RVV_U32M4, RVV_U64M8, RVV_F64M8) + +// Signed and Unsigned conversions +HAL_RVV_CVT(RVV_U8M1, RVV_U16M2, RVV_I16M2) +HAL_RVV_CVT(RVV_U8M2, RVV_U16M4, RVV_I16M4) +HAL_RVV_CVT(RVV_U8M4, RVV_U16M8, RVV_I16M8) + +HAL_RVV_CVT(RVV_U8M1, RVV_U32M4, RVV_I32M4) +HAL_RVV_CVT(RVV_U8M2, RVV_U32M8, RVV_I32M8) + +HAL_RVV_CVT(RVV_U8M1, RVV_U64M8, RVV_I64M8) + +#undef HAL_RVV_CVT + +// ---------------------------- Define Register Group Operations ------------------------------- + +#if defined(__clang__) && __clang_major__ <= 17 +#define HAL_RVV_GROUP(ONE, TWO, TYPE, ONE_LMUL, TWO_LMUL) \ + template \ + inline ONE::VecType vget(TWO::VecType v) { \ + return __riscv_vget_v_##TYPE##TWO_LMUL##_##TYPE##ONE_LMUL(v, idx); \ + } \ + template \ + inline void vset(TWO::VecType v, ONE::VecType val) { \ + __riscv_vset_v_##TYPE##ONE_LMUL##_##TYPE##TWO_LMUL(v, idx, val); \ + } \ + inline TWO::VecType vcreate(ONE::VecType v0, ONE::VecType v1) { \ + TWO::VecType v{}; \ + v = __riscv_vset_v_##TYPE##ONE_LMUL##_##TYPE##TWO_LMUL(v, 0, v0); \ + v = __riscv_vset_v_##TYPE##ONE_LMUL##_##TYPE##TWO_LMUL(v, 1, v1); \ + return v; \ + } +#else +#define HAL_RVV_GROUP(ONE, TWO, TYPE, ONE_LMUL, TWO_LMUL) \ + template \ + inline ONE::VecType vget(TWO::VecType v) { \ + return __riscv_vget_v_##TYPE##TWO_LMUL##_##TYPE##ONE_LMUL(v, idx); \ + } \ + template \ + inline void vset(TWO::VecType v, ONE::VecType val) { \ + __riscv_vset_v_##TYPE##ONE_LMUL##_##TYPE##TWO_LMUL(v, idx, val); \ + } \ + inline TWO::VecType vcreate(ONE::VecType v0, ONE::VecType v1) { \ + return __riscv_vcreate_v_##TYPE##ONE_LMUL##_##TYPE##TWO_LMUL(v0, v1); \ + } +#endif + +HAL_RVV_GROUP(RVV_I8M1, RVV_I8M2, i8, m1, m2) +HAL_RVV_GROUP(RVV_I8M2, RVV_I8M4, i8, m2, m4) +HAL_RVV_GROUP(RVV_I8M4, RVV_I8M8, i8, m4, m8) + +HAL_RVV_GROUP(RVV_I16M1, RVV_I16M2, i16, m1, m2) +HAL_RVV_GROUP(RVV_I16M2, RVV_I16M4, i16, m2, m4) +HAL_RVV_GROUP(RVV_I16M4, RVV_I16M8, i16, m4, m8) + +HAL_RVV_GROUP(RVV_I32M1, RVV_I32M2, i32, m1, m2) +HAL_RVV_GROUP(RVV_I32M2, RVV_I32M4, i32, m2, m4) +HAL_RVV_GROUP(RVV_I32M4, RVV_I32M8, i32, m4, m8) + +HAL_RVV_GROUP(RVV_I64M1, RVV_I64M2, i64, m1, m2) +HAL_RVV_GROUP(RVV_I64M2, RVV_I64M4, i64, m2, m4) +HAL_RVV_GROUP(RVV_I64M4, RVV_I64M8, i64, m4, m8) + +HAL_RVV_GROUP(RVV_U8M1, RVV_U8M2, u8, m1, m2) +HAL_RVV_GROUP(RVV_U8M2, RVV_U8M4, u8, m2, m4) +HAL_RVV_GROUP(RVV_U8M4, RVV_U8M8, u8, m4, m8) + +HAL_RVV_GROUP(RVV_U16M1, RVV_U16M2, u16, m1, m2) +HAL_RVV_GROUP(RVV_U16M2, RVV_U16M4, u16, m2, m4) +HAL_RVV_GROUP(RVV_U16M4, RVV_U16M8, u16, m4, m8) + +HAL_RVV_GROUP(RVV_U32M1, RVV_U32M2, u32, m1, m2) +HAL_RVV_GROUP(RVV_U32M2, RVV_U32M4, u32, m2, m4) +HAL_RVV_GROUP(RVV_U32M4, RVV_U32M8, u32, m4, m8) + +HAL_RVV_GROUP(RVV_U64M1, RVV_U64M2, u64, m1, m2) +HAL_RVV_GROUP(RVV_U64M2, RVV_U64M4, u64, m2, m4) +HAL_RVV_GROUP(RVV_U64M4, RVV_U64M8, u64, m4, m8) + +HAL_RVV_GROUP(RVV_F32M1, RVV_F32M2, f32, m1, m2) +HAL_RVV_GROUP(RVV_F32M2, RVV_F32M4, f32, m2, m4) +HAL_RVV_GROUP(RVV_F32M4, RVV_F32M8, f32, m4, m8) + +HAL_RVV_GROUP(RVV_F64M1, RVV_F64M2, f64, m1, m2) +HAL_RVV_GROUP(RVV_F64M2, RVV_F64M4, f64, m2, m4) +HAL_RVV_GROUP(RVV_F64M4, RVV_F64M8, f64, m4, m8) + +#undef HAL_RVV_GROUP + +#if defined(__clang__) && __clang_major__ <= 17 +#define HAL_RVV_GROUP(ONE, FOUR, TYPE, ONE_LMUL, FOUR_LMUL) \ + template \ + inline ONE::VecType vget(FOUR::VecType v) { \ + return __riscv_vget_v_##TYPE##FOUR_LMUL##_##TYPE##ONE_LMUL(v, idx); \ + } \ + template \ + inline void vset(FOUR::VecType v, ONE::VecType val) { \ + __riscv_vset_v_##TYPE##ONE_LMUL##_##TYPE##FOUR_LMUL(v, idx, val); \ + } \ + inline FOUR::VecType vcreate(ONE::VecType v0, ONE::VecType v1, ONE::VecType v2, ONE::VecType v3) { \ + FOUR::VecType v{}; \ + v = __riscv_vset_v_##TYPE##ONE_LMUL##_##TYPE##FOUR_LMUL(v, 0, v0); \ + v = __riscv_vset_v_##TYPE##ONE_LMUL##_##TYPE##FOUR_LMUL(v, 1, v1); \ + v = __riscv_vset_v_##TYPE##ONE_LMUL##_##TYPE##FOUR_LMUL(v, 2, v2); \ + v = __riscv_vset_v_##TYPE##ONE_LMUL##_##TYPE##FOUR_LMUL(v, 3, v3); \ + return v; \ + } +#else +#define HAL_RVV_GROUP(ONE, FOUR, TYPE, ONE_LMUL, FOUR_LMUL) \ + template \ + inline ONE::VecType vget(FOUR::VecType v) { \ + return __riscv_vget_v_##TYPE##FOUR_LMUL##_##TYPE##ONE_LMUL(v, idx); \ + } \ + template \ + inline void vset(FOUR::VecType v, ONE::VecType val) { \ + __riscv_vset_v_##TYPE##ONE_LMUL##_##TYPE##FOUR_LMUL(v, idx, val); \ + } \ + inline FOUR::VecType vcreate(ONE::VecType v0, ONE::VecType v1, ONE::VecType v2, ONE::VecType v3) { \ + return __riscv_vcreate_v_##TYPE##ONE_LMUL##_##TYPE##FOUR_LMUL(v0, v1, v2, v3); \ + } +#endif + +HAL_RVV_GROUP(RVV_I8M1, RVV_I8M4, i8, m1, m4) +HAL_RVV_GROUP(RVV_I8M2, RVV_I8M8, i8, m2, m8) + +HAL_RVV_GROUP(RVV_U8M1, RVV_U8M4, u8, m1, m4) +HAL_RVV_GROUP(RVV_U8M2, RVV_U8M8, u8, m2, m8) + +HAL_RVV_GROUP(RVV_I16M1, RVV_I16M4, i16, m1, m4) +HAL_RVV_GROUP(RVV_I16M2, RVV_I16M8, i16, m2, m8) + +HAL_RVV_GROUP(RVV_U16M1, RVV_U16M4, u16, m1, m4) +HAL_RVV_GROUP(RVV_U16M2, RVV_U16M8, u16, m2, m8) + +HAL_RVV_GROUP(RVV_I32M1, RVV_I32M4, i32, m1, m4) +HAL_RVV_GROUP(RVV_I32M2, RVV_I32M8, i32, m2, m8) + +HAL_RVV_GROUP(RVV_U32M1, RVV_U32M4, u32, m1, m4) +HAL_RVV_GROUP(RVV_U32M2, RVV_U32M8, u32, m2, m8) + +HAL_RVV_GROUP(RVV_I64M1, RVV_I64M4, i64, m1, m4) +HAL_RVV_GROUP(RVV_I64M2, RVV_I64M8, i64, m2, m8) + +HAL_RVV_GROUP(RVV_U64M1, RVV_U64M4, u64, m1, m4) +HAL_RVV_GROUP(RVV_U64M2, RVV_U64M8, u64, m2, m8) + +HAL_RVV_GROUP(RVV_F32M1, RVV_F32M4, f32, m1, m4) +HAL_RVV_GROUP(RVV_F32M2, RVV_F32M8, f32, m2, m8) + +HAL_RVV_GROUP(RVV_F64M1, RVV_F64M4, f64, m1, m4) +HAL_RVV_GROUP(RVV_F64M2, RVV_F64M8, f64, m2, m8) + +#undef HAL_RVV_GROUP + +#if defined(__clang__) && __clang_major__ <= 17 +#define HAL_RVV_GROUP(ONE, EIGHT, TYPE, ONE_LMUL, EIGHT_LMUL) \ + template \ + inline ONE::VecType vget(EIGHT::VecType v) { \ + return __riscv_vget_v_##TYPE##EIGHT_LMUL##_##TYPE##ONE_LMUL(v, idx); \ + } \ + template \ + inline void vset(EIGHT::VecType v, ONE::VecType val) { \ + __riscv_vset_v_##TYPE##ONE_LMUL##_##TYPE##EIGHT_LMUL(v, idx, val); \ + } \ + inline EIGHT::VecType vcreate(ONE::VecType v0, ONE::VecType v1, ONE::VecType v2, ONE::VecType v3, \ + ONE::VecType v4, ONE::VecType v5, ONE::VecType v6, ONE::VecType v7) { \ + EIGHT::VecType v{}; \ + v = __riscv_vset_v_##TYPE##ONE_LMUL##_##TYPE##EIGHT_LMUL(v, 0, v0); \ + v = __riscv_vset_v_##TYPE##ONE_LMUL##_##TYPE##EIGHT_LMUL(v, 1, v1); \ + v = __riscv_vset_v_##TYPE##ONE_LMUL##_##TYPE##EIGHT_LMUL(v, 2, v2); \ + v = __riscv_vset_v_##TYPE##ONE_LMUL##_##TYPE##EIGHT_LMUL(v, 3, v3); \ + v = __riscv_vset_v_##TYPE##ONE_LMUL##_##TYPE##EIGHT_LMUL(v, 4, v4); \ + v = __riscv_vset_v_##TYPE##ONE_LMUL##_##TYPE##EIGHT_LMUL(v, 5, v5); \ + v = __riscv_vset_v_##TYPE##ONE_LMUL##_##TYPE##EIGHT_LMUL(v, 6, v6); \ + v = __riscv_vset_v_##TYPE##ONE_LMUL##_##TYPE##EIGHT_LMUL(v, 7, v7); \ + return v; \ + } +#else +#define HAL_RVV_GROUP(ONE, EIGHT, TYPE, ONE_LMUL, EIGHT_LMUL) \ + template \ + inline ONE::VecType vget(EIGHT::VecType v) { \ + return __riscv_vget_v_##TYPE##EIGHT_LMUL##_##TYPE##ONE_LMUL(v, idx); \ + } \ + template \ + inline void vset(EIGHT::VecType v, ONE::VecType val) { \ + __riscv_vset_v_##TYPE##ONE_LMUL##_##TYPE##EIGHT_LMUL(v, idx, val); \ + } \ + inline EIGHT::VecType vcreate(ONE::VecType v0, ONE::VecType v1, ONE::VecType v2, ONE::VecType v3, \ + ONE::VecType v4, ONE::VecType v5, ONE::VecType v6, ONE::VecType v7) { \ + return __riscv_vcreate_v_##TYPE##ONE_LMUL##_##TYPE##EIGHT_LMUL(v0, v1, v2, v3, v4, v5, v6, v7); \ + } +#endif + +HAL_RVV_GROUP(RVV_I8M1, RVV_I8M8, i8, m1, m8) +HAL_RVV_GROUP(RVV_U8M1, RVV_U8M8, u8, m1, m8) + +HAL_RVV_GROUP(RVV_I16M1, RVV_I16M8, i16, m1, m8) +HAL_RVV_GROUP(RVV_U16M1, RVV_U16M8, u16, m1, m8) + +HAL_RVV_GROUP(RVV_I32M1, RVV_I32M8, i32, m1, m8) +HAL_RVV_GROUP(RVV_U32M1, RVV_U32M8, u32, m1, m8) + +HAL_RVV_GROUP(RVV_I64M1, RVV_I64M8, i64, m1, m8) +HAL_RVV_GROUP(RVV_U64M1, RVV_U64M8, u64, m1, m8) + +HAL_RVV_GROUP(RVV_F32M1, RVV_F32M8, f32, m1, m8) +HAL_RVV_GROUP(RVV_F64M1, RVV_F64M8, f64, m1, m8) + +#undef HAL_RVV_GROUP + }} // namespace cv::cv_hal_rvv #endif //OPENCV_HAL_RVV_TYPES_HPP_INCLUDED diff --git a/modules/imgproc/perf/perf_integral.cpp b/modules/imgproc/perf/perf_integral.cpp index 0a4fc49329..23ab10b57f 100644 --- a/modules/imgproc/perf/perf_integral.cpp +++ b/modules/imgproc/perf/perf_integral.cpp @@ -20,7 +20,7 @@ static int extraOutputDepths[6][2] = {{CV_32S, CV_32S}, {CV_32S, CV_32F}, {CV_32 typedef tuple Size_MatType_OutMatDepth_t; typedef perf::TestBaseWithParam Size_MatType_OutMatDepth; -typedef tuple Size_MatType_OutMatDepthArray_t; +typedef tuple> Size_MatType_OutMatDepthArray_t; typedef perf::TestBaseWithParam Size_MatType_OutMatDepthArray; PERF_TEST_P(Size_MatType_OutMatDepth, integral, @@ -83,19 +83,42 @@ PERF_TEST_P(Size_MatType_OutMatDepth, integral_sqsum, SANITY_CHECK(sqsum, 1e-6); } +static std::vector> GetFullSqsumDepthPairs() { + static int extraDepths[12][2] = { + {CV_8U, DEPTH_32S_64F}, + {CV_8U, DEPTH_32S_32F}, + {CV_8U, DEPTH_32S_32S}, + {CV_8U, DEPTH_32F_64F}, + {CV_8U, DEPTH_32F_32F}, + {CV_8U, DEPTH_64F_64F}, + {CV_16U, DEPTH_64F_64F}, + {CV_16S, DEPTH_64F_64F}, + {CV_32F, DEPTH_32F_64F}, + {CV_32F, DEPTH_32F_32F}, + {CV_32F, DEPTH_64F_64F}, + {CV_64F, DEPTH_64F_64F} + }; + std::vector> valid_pairs; + for (size_t i = 0; i < 12; i++) { + for (int cn = 1; cn <= 4; cn++) { + valid_pairs.emplace_back(CV_MAKETYPE(extraDepths[i][0], cn), extraDepths[i][1]); + } + } + return valid_pairs; +} + PERF_TEST_P(Size_MatType_OutMatDepthArray, DISABLED_integral_sqsum_full, testing::Combine( testing::Values(TYPICAL_MAT_SIZES), - testing::Values(CV_8UC1, CV_8UC2, CV_8UC3, CV_8UC4), - testing::Values(DEPTH_32S_32S, DEPTH_32S_32F, DEPTH_32S_64F, DEPTH_32F_32F, DEPTH_32F_64F, DEPTH_64F_64F) + testing::ValuesIn(GetFullSqsumDepthPairs()) ) ) { Size sz = get<0>(GetParam()); - int matType = get<1>(GetParam()); - int *outputDepths = (int *)extraOutputDepths[get<2>(GetParam())]; - int sdepth = outputDepths[0]; - int sqdepth = outputDepths[1]; + auto depths = get<1>(GetParam()); + int matType = get<0>(depths); + int sdepth = extraOutputDepths[get<1>(depths)][0]; + int sqdepth = extraOutputDepths[get<1>(depths)][1]; Mat src(sz, matType); Mat sum(sz, sdepth); diff --git a/modules/imgproc/src/sumpixels.dispatch.cpp b/modules/imgproc/src/sumpixels.dispatch.cpp index b828ec70c0..dc000df6eb 100644 --- a/modules/imgproc/src/sumpixels.dispatch.cpp +++ b/modules/imgproc/src/sumpixels.dispatch.cpp @@ -486,7 +486,8 @@ cvIntegral( const CvArr* image, CvArr* sumImage, ptilted = &tilted; } cv::integral( src, sum, psqsum ? cv::_OutputArray(*psqsum) : cv::_OutputArray(), - ptilted ? cv::_OutputArray(*ptilted) : cv::_OutputArray(), sum.depth() ); + ptilted ? cv::_OutputArray(*ptilted) : cv::_OutputArray(), sum.depth(), + psqsum ? psqsum->depth() : -1 ); CV_Assert( sum.data == sum0.data && sqsum.data == sqsum0.data && tilted.data == tilted0.data ); } diff --git a/modules/imgproc/test/test_filter.cpp b/modules/imgproc/test/test_filter.cpp index 685743630f..46164aed21 100644 --- a/modules/imgproc/test/test_filter.cpp +++ b/modules/imgproc/test/test_filter.cpp @@ -1684,19 +1684,34 @@ void CV_IntegralTest::get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ) { RNG& rng = ts->get_rng(); - int depth = cvtest::randInt(rng) % 2, sum_depth; int cn = cvtest::randInt(rng) % 4 + 1; cvtest::ArrayTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); Size sum_size; - depth = depth == 0 ? CV_8U : CV_32F; - int b = (cvtest::randInt(rng) & 1) != 0; - sum_depth = depth == CV_8U && b ? CV_32S : b ? CV_32F : CV_64F; + const int depths[12][3] = { + {CV_8U, CV_32S, CV_64F}, + {CV_8U, CV_32S, CV_32F}, + {CV_8U, CV_32S, CV_32S}, + {CV_8U, CV_32F, CV_64F}, + {CV_8U, CV_32F, CV_32F}, + {CV_8U, CV_64F, CV_64F}, + {CV_16U, CV_64F, CV_64F}, + {CV_16S, CV_64F, CV_64F}, + {CV_32F, CV_32F, CV_64F}, + {CV_32F, CV_32F, CV_32F}, + {CV_32F, CV_64F, CV_64F}, + {CV_64F, CV_64F, CV_64F}, + }; - types[INPUT][0] = CV_MAKETYPE(depth,cn); + int random_choice = cvtest::randInt(rng) % 12; + int depth = depths[random_choice][0]; + int sum_depth = depths[random_choice][1]; + int sqsum_depth = depths[random_choice][2]; + + types[INPUT][0] = CV_MAKETYPE(depth, cn); types[OUTPUT][0] = types[REF_OUTPUT][0] = types[OUTPUT][2] = types[REF_OUTPUT][2] = CV_MAKETYPE(sum_depth, cn); - types[OUTPUT][1] = types[REF_OUTPUT][1] = CV_MAKETYPE(CV_64F, cn); + types[OUTPUT][1] = types[REF_OUTPUT][1] = CV_MAKETYPE(sqsum_depth, cn); sum_size.width = sizes[INPUT][0].width + 1; sum_size.height = sizes[INPUT][0].height + 1; @@ -1738,7 +1753,7 @@ void CV_IntegralTest::run_func() static void test_integral( const Mat& img, Mat* sum, Mat* sqsum, Mat* tilted ) { - CV_Assert( img.depth() == CV_32F ); + CV_Assert( img.depth() == CV_64F ); sum->create(img.rows+1, img.cols+1, CV_64F); if( sqsum ) @@ -1746,7 +1761,7 @@ static void test_integral( const Mat& img, Mat* sum, Mat* sqsum, Mat* tilted ) if( tilted ) tilted->create(img.rows+1, img.cols+1, CV_64F); - const float* data = img.ptr(); + const double* data = img.ptr(); double* sdata = sum->ptr(); double* sqdata = sqsum ? sqsum->ptr() : 0; double* tdata = tilted ? tilted->ptr() : 0; @@ -1788,7 +1803,7 @@ static void test_integral( const Mat& img, Mat* sum, Mat* sqsum, Mat* tilted ) else { ts += tdata[x-tstep-1]; - if( data > img.ptr() ) + if( data > img.ptr() ) { ts += data[x-step-1]; if( x < size.width ) @@ -1824,7 +1839,7 @@ void CV_IntegralTest::prepare_to_validation( int /*test_case_idx*/ ) { if( cn > 1 ) cvtest::extract(src, plane, i); - plane.convertTo(srcf, CV_32F); + plane.convertTo(srcf, CV_64F); test_integral( srcf, &psum, sqsum0 ? &psqsum : 0, tsum0 ? &ptsum : 0 ); psum.convertTo(psum2, sum0->depth()); From 8eb9d27a31b91fa249b63f92b9f583fc236a4563 Mon Sep 17 00:00:00 2001 From: fengyuentau Date: Fri, 11 Apr 2025 18:50:38 +0800 Subject: [PATCH 78/94] implemented cv_hal_cmp* in hal_rvv --- 3rdparty/hal_rvv/hal_rvv.hpp | 1 + 3rdparty/hal_rvv/hal_rvv_1p0/compare.hpp | 126 +++++++++++++++++++++++ 2 files changed, 127 insertions(+) create mode 100644 3rdparty/hal_rvv/hal_rvv_1p0/compare.hpp diff --git a/3rdparty/hal_rvv/hal_rvv.hpp b/3rdparty/hal_rvv/hal_rvv.hpp index c18d7949ce..9c6cd94623 100644 --- a/3rdparty/hal_rvv/hal_rvv.hpp +++ b/3rdparty/hal_rvv/hal_rvv.hpp @@ -48,6 +48,7 @@ #include "hal_rvv_1p0/copy_mask.hpp" // core #include "hal_rvv_1p0/div.hpp" // core #include "hal_rvv_1p0/dotprod.hpp" // core +#include "hal_rvv_1p0/compare.hpp" // core #include "hal_rvv_1p0/moments.hpp" // imgproc #include "hal_rvv_1p0/filter.hpp" // imgproc diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/compare.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/compare.hpp new file mode 100644 index 0000000000..6efd92e18a --- /dev/null +++ b/3rdparty/hal_rvv/hal_rvv_1p0/compare.hpp @@ -0,0 +1,126 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2025, SpaceMIT Inc., all rights reserved. +// Third party copyrights are property of their respective owners. + +#ifndef OPENCV_HAL_RVV_COMPARE_HPP_INCLUDED +#define OPENCV_HAL_RVV_COMPARE_HPP_INCLUDED + +#include "types.hpp" + +namespace cv { namespace cv_hal_rvv { namespace compare { + +namespace { + +constexpr RVV_LMUL getLMUL(size_t sz) { + // c++11 only allows exactly one return statement inside the function body modified by constexpr + return sz == 1 ? LMUL_8 : (sz == 2 ? LMUL_4 : (sz == 4 ? LMUL_2 : LMUL_1)); +} + +static inline vbool1_t vlt(const vuint8m8_t &a, const vuint8m8_t &b, const int vl) { return __riscv_vmsltu(a, b, vl); } +static inline vbool1_t vlt(const vint8m8_t &a, const vint8m8_t &b, const int vl) { return __riscv_vmslt(a, b, vl); } +static inline vbool2_t vlt(const vuint16m8_t &a, const vuint16m8_t &b, const int vl) { return __riscv_vmsltu(a, b, vl); } +static inline vbool2_t vlt(const vint16m8_t &a, const vint16m8_t &b, const int vl) { return __riscv_vmslt(a, b, vl); } +static inline vbool4_t vlt(const vint32m8_t &a, const vint32m8_t &b, const int vl) { return __riscv_vmslt(a, b, vl); } +static inline vbool4_t vlt(const vfloat32m8_t &a, const vfloat32m8_t &b, const int vl) { return __riscv_vmflt(a, b, vl); } + +static inline vbool1_t vle(const vuint8m8_t &a, const vuint8m8_t &b, const int vl) { return __riscv_vmsleu(a, b, vl); } +static inline vbool1_t vle(const vint8m8_t &a, const vint8m8_t &b, const int vl) { return __riscv_vmsle(a, b, vl); } +static inline vbool2_t vle(const vuint16m8_t &a, const vuint16m8_t &b, const int vl) { return __riscv_vmsleu(a, b, vl); } +static inline vbool2_t vle(const vint16m8_t &a, const vint16m8_t &b, const int vl) { return __riscv_vmsle(a, b, vl); } +static inline vbool4_t vle(const vint32m8_t &a, const vint32m8_t &b, const int vl) { return __riscv_vmsle(a, b, vl); } +static inline vbool4_t vle(const vfloat32m8_t &a, const vfloat32m8_t &b, const int vl) { return __riscv_vmfle(a, b, vl); } + +static inline vbool1_t veq(const vuint8m8_t &a, const vuint8m8_t &b, const int vl) { return __riscv_vmseq(a, b, vl); } +static inline vbool1_t veq(const vint8m8_t &a, const vint8m8_t &b, const int vl) { return __riscv_vmseq(a, b, vl); } +static inline vbool2_t veq(const vuint16m8_t &a, const vuint16m8_t &b, const int vl) { return __riscv_vmseq(a, b, vl); } +static inline vbool2_t veq(const vint16m8_t &a, const vint16m8_t &b, const int vl) { return __riscv_vmseq(a, b, vl); } +static inline vbool4_t veq(const vint32m8_t &a, const vint32m8_t &b, const int vl) { return __riscv_vmseq(a, b, vl); } +static inline vbool4_t veq(const vfloat32m8_t &a, const vfloat32m8_t &b, const int vl) { return __riscv_vmfeq(a, b, vl); } + +static inline vbool1_t vne(const vuint8m8_t &a, const vuint8m8_t &b, const int vl) { return __riscv_vmsne(a, b, vl); } +static inline vbool1_t vne(const vint8m8_t &a, const vint8m8_t &b, const int vl) { return __riscv_vmsne(a, b, vl); } +static inline vbool2_t vne(const vuint16m8_t &a, const vuint16m8_t &b, const int vl) { return __riscv_vmsne(a, b, vl); } +static inline vbool2_t vne(const vint16m8_t &a, const vint16m8_t &b, const int vl) { return __riscv_vmsne(a, b, vl); } +static inline vbool4_t vne(const vint32m8_t &a, const vint32m8_t &b, const int vl) { return __riscv_vmsne(a, b, vl); } +static inline vbool4_t vne(const vfloat32m8_t &a, const vfloat32m8_t &b, const int vl) { return __riscv_vmfne(a, b, vl); } + +#define CV_HAL_RVV_COMPARE_OP(op_name) \ +template \ +struct op_name { \ + using in = RVV<_Tps, LMUL_8>; \ + using out = RVV; \ + constexpr static uint8_t one = 255; \ + static inline void run(const _Tps *src1, const _Tps *src2, uchar *dst, const int len) { \ + auto zero = out::vmv(0, out::setvlmax()); \ + int vl; \ + for (int i = 0; i < len; i += vl) { \ + vl = in::setvl(len - i); \ + auto v1 = in::vload(src1 + i, vl); \ + auto v2 = in::vload(src2 + i, vl); \ + auto m = v##op_name(v1, v2, vl); \ + out::vstore(dst + i, __riscv_vmerge(zero, one, m, vl), vl); \ + } \ + } \ +}; + +CV_HAL_RVV_COMPARE_OP(lt) +CV_HAL_RVV_COMPARE_OP(le) +CV_HAL_RVV_COMPARE_OP(eq) +CV_HAL_RVV_COMPARE_OP(ne) + +template class op, typename _Tps> static inline +int compare_impl(const _Tps *src1_data, size_t src1_step, const _Tps *src2_data, size_t src2_step, + uchar *dst_data, size_t dst_step, int width, int height) { + if (src1_step == src2_step && src1_step == dst_step && src1_step == width * sizeof(_Tps)) { + width *= height; + height = 1; + } + + for (int h = 0; h < height; h++) { + const _Tps *src1 = reinterpret_cast((const uchar*)src1_data + h * src1_step); + const _Tps *src2 = reinterpret_cast((const uchar*)src2_data + h * src2_step); + uchar *dst = dst_data + h * dst_step; + + op<_Tps>::run(src1, src2, dst, width); + } + + return CV_HAL_ERROR_OK; +} + +} // anonymous + +#undef cv_hal_cmp8u +#define cv_hal_cmp8u cv::cv_hal_rvv::compare::compare +#undef cv_hal_cmp8s +#define cv_hal_cmp8s cv::cv_hal_rvv::compare::compare +#undef cv_hal_cmp16u +#define cv_hal_cmp16u cv::cv_hal_rvv::compare::compare +#undef cv_hal_cmp16s +#define cv_hal_cmp16s cv::cv_hal_rvv::compare::compare +#undef cv_hal_cmp32s +#define cv_hal_cmp32s cv::cv_hal_rvv::compare::compare +#undef cv_hal_cmp32f +#define cv_hal_cmp32f cv::cv_hal_rvv::compare::compare +// #undef cv_hal_cmp64f +// #define cv_hal_cmp64f cv::cv_hal_rvv::compare::compare + +template inline +int compare(const _Tps *src1_data, size_t src1_step, const _Tps *src2_data, size_t src2_step, + uchar *dst_data, size_t dst_step, int width, int height, int operation) { + switch (operation) { + case CMP_LT: return compare_impl(src1_data, src1_step, src2_data, src2_step, dst_data, dst_step, width, height); + case CMP_GT: return compare_impl(src2_data, src2_step, src1_data, src1_step, dst_data, dst_step, width, height); + case CMP_LE: return compare_impl(src1_data, src1_step, src2_data, src2_step, dst_data, dst_step, width, height); + case CMP_GE: return compare_impl(src2_data, src2_step, src1_data, src1_step, dst_data, dst_step, width, height); + case CMP_EQ: return compare_impl(src1_data, src1_step, src2_data, src2_step, dst_data, dst_step, width, height); + case CMP_NE: return compare_impl(src1_data, src1_step, src2_data, src2_step, dst_data, dst_step, width, height); + default: return CV_HAL_ERROR_NOT_IMPLEMENTED; + } +} + +}}} // cv::cv_hal_rvv::compare + +#endif // OPENCV_HAL_RVV_COMPARE_HPP_INCLUDED From a8a3b93043a1cbcf392cbef568e92ecc0a78d0ce Mon Sep 17 00:00:00 2001 From: Ivan Avdeev Date: Mon, 21 Apr 2025 17:33:14 +0300 Subject: [PATCH 79/94] Merge pull request #27239 from avdivan:4.x Android-SDK: check flag IPP package #27239 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [ ] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- platforms/android/build_sdk.py | 42 +++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/platforms/android/build_sdk.py b/platforms/android/build_sdk.py index 3a920c78f1..be78722334 100755 --- a/platforms/android/build_sdk.py +++ b/platforms/android/build_sdk.py @@ -380,7 +380,40 @@ def get_ndk_dir(): return android_sdk_ndk_bundle return None +def check_cmake_flag_enabled(cmake_file, flag_name, strict=True): + print(f"Checking build flag '{flag_name}' in: {cmake_file}") + + if not os.path.isfile(cmake_file): + msg = f"ERROR: File {cmake_file} does not exist." + if strict: + print(msg) + sys.exit(1) + else: + print("WARNING:", msg) + return + with open(cmake_file, 'r') as file: + for line in file: + if line.strip().startswith(f"{flag_name}="): + value = line.strip().split('=')[1] + if value == '1': + print(f"{flag_name}=1 found. Support is enabled.") + return + else: + msg = f"ERROR: {flag_name} is set to {value}, expected 1." + if strict: + print(msg) + sys.exit(1) + else: + print("WARNING:", msg) + return + msg = f"ERROR: {flag_name} not found in {os.path.basename(cmake_file)}." + if strict: + print(msg) + sys.exit(1) + else: + print("WARNING:", msg) + #=================================================================================================== if __name__ == "__main__": @@ -407,6 +440,7 @@ if __name__ == "__main__": parser.add_argument('--no_media_ndk', action="store_true", help="Do not link Media NDK (required for video I/O support)") parser.add_argument('--hwasan', action="store_true", help="Enable Hardware Address Sanitizer on ARM64") parser.add_argument('--disable', metavar='FEATURE', default=[], action='append', help='OpenCV features to disable (add WITH_*=OFF). To disable multiple, specify this flag again, e.g. "--disable TBB --disable OPENMP"') + parser.add_argument('--no-strict-dependencies',action='store_false',dest='strict_dependencies',help='Disable strict dependency checking (default: strict mode ON)') args = parser.parse_args() log.basicConfig(format='%(message)s', level=log.DEBUG) @@ -485,9 +519,15 @@ if __name__ == "__main__": os.chdir(builder.libdest) builder.clean_library_build_dir() builder.build_library(abi, do_install, args.no_media_ndk) + + #Check HAVE_IPP x86 / x86_64 + if abi.haveIPP(): + log.info("Checking HAVE_IPP for ABI: %s", abi.name) + check_cmake_flag_enabled(os.path.join(builder.libdest,"CMakeVars.txt"), "HAVE_IPP", strict=args.strict_dependencies) builder.gather_results() - + + if args.build_doc: builder.build_javadoc() From a08b1b656630c561327920b6034647aef370f6bc Mon Sep 17 00:00:00 2001 From: Adrian Kretz Date: Mon, 21 Apr 2025 16:40:33 +0200 Subject: [PATCH 80/94] Merge pull request #27244 from akretz:fix_issue_27183 Fix QR code encoder with autoversion #27244 The autodetected version is not honored in the `QRCodeEncoderImpl::encode*` methods. This fixes #27183 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- modules/objdetect/src/qrcode_encoder.cpp | 2 ++ modules/objdetect/test/test_qrcode_encode.cpp | 35 +++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/modules/objdetect/src/qrcode_encoder.cpp b/modules/objdetect/src/qrcode_encoder.cpp index d1c05d2525..14b51c254d 100644 --- a/modules/objdetect/src/qrcode_encoder.cpp +++ b/modules/objdetect/src/qrcode_encoder.cpp @@ -397,7 +397,9 @@ void QRCodeEncoderImpl::generateQR(const std::string &input) original = Mat(Size(version_size, version_size), CV_8UC1, Scalar(255)); masked_data = original.clone(); Mat qrcode = masked_data.clone(); + std::swap(version_level, tmp_version_level); generatingProcess(input_info, qrcode); + std::swap(version_level, tmp_version_level); final_qrcodes.push_back(qrcode); } } diff --git a/modules/objdetect/test/test_qrcode_encode.cpp b/modules/objdetect/test/test_qrcode_encode.cpp index 87142e4690..f6cf1c069f 100644 --- a/modules/objdetect/test/test_qrcode_encode.cpp +++ b/modules/objdetect/test/test_qrcode_encode.cpp @@ -590,4 +590,39 @@ INSTANTIATE_TEST_CASE_P(/**/, Objdetect_QRCode_decoding, testing::ValuesIn(std:: {"err_correct_2L.png", "Version 2 QR Code Test Image"}, })); +TEST(Objdetect_QRCode_Encode_Decode_Long_Text, regression_issue27183) +{ + const int len = 135; + Ptr encoder = QRCodeEncoder::create(); + + std::string input; + input.resize(len); + cv::randu(Mat(1, len, CV_8U, &input[0]), 'a', 'z' + 1); + Mat qrcode; + encoder->encode(input, qrcode); + + std::vector corners(4); + corners[0] = Point2f(border_width, border_width); + corners[1] = Point2f(qrcode.cols * 1.0f - border_width, border_width); + corners[2] = Point2f(qrcode.cols * 1.0f - border_width, qrcode.rows * 1.0f - border_width); + corners[3] = Point2f(border_width, qrcode.rows * 1.0f - border_width); + + Mat resized_src; + resize(qrcode, resized_src, fixed_size, 0, 0, INTER_AREA); + float width_ratio = resized_src.cols * 1.0f / qrcode.cols; + float height_ratio = resized_src.rows * 1.0f / qrcode.rows; + for(size_t j = 0; j < corners.size(); j++) + { + corners[j].x = corners[j].x * width_ratio; + corners[j].y = corners[j].y * height_ratio; + } + + QRCodeDetector detector; + cv::String decoded_msg; + Mat straight_barcode; + EXPECT_NO_THROW(decoded_msg = detector.decode(resized_src, corners, straight_barcode)); + ASSERT_FALSE(straight_barcode.empty()); + EXPECT_EQ(input, decoded_msg); +} + }} // namespace From 97f73ba0b544d2b034629f05bac040dfe2ad7450 Mon Sep 17 00:00:00 2001 From: utibenkei Date: Tue, 22 Apr 2025 02:51:38 +0900 Subject: [PATCH 81/94] Merge pull request #27228 from utibenkei:fix_java_enum_wrapper Explicitly specify enum type scopes to improve Java wrapper generation #27228 Changed DataLayout and ImagePaddingMode to dnn::DataLayout and dnn::ImagePaddingMode to explicitly specify their scopes. This allows gen_java.py to correctly register disc_type, preventing constructors and methods using these enum types from being skipped during Java wrapper generation. Similarly updated QRCodeEncoder::CorrectionLevel and QRCodeEncoder::EncodeMode with explicit scope declarations. Also added a new Java test class `DnnBlobFromImageWithParamsTest` based on: https://github.com/opencv/opencv/blob/4.x/modules/dnn/test/test_misc.cpp#L133-L243 Related issues #23753 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- modules/dnn/include/opencv2/dnn/dnn.hpp | 8 +- .../test/DnnBlobFromImageWithParamsTest.java | 134 ++++++++++++++++++ .../objdetect/include/opencv2/objdetect.hpp | 4 +- 3 files changed, 140 insertions(+), 6 deletions(-) create mode 100644 modules/dnn/misc/java/test/DnnBlobFromImageWithParamsTest.java diff --git a/modules/dnn/include/opencv2/dnn/dnn.hpp b/modules/dnn/include/opencv2/dnn/dnn.hpp index 0077ae4853..779318c33f 100644 --- a/modules/dnn/include/opencv2/dnn/dnn.hpp +++ b/modules/dnn/include/opencv2/dnn/dnn.hpp @@ -1219,16 +1219,16 @@ CV__DNN_INLINE_NS_BEGIN { CV_WRAP Image2BlobParams(); CV_WRAP Image2BlobParams(const Scalar& scalefactor, const Size& size = Size(), const Scalar& mean = Scalar(), - bool swapRB = false, int ddepth = CV_32F, DataLayout datalayout = DNN_LAYOUT_NCHW, - ImagePaddingMode mode = DNN_PMODE_NULL, Scalar borderValue = 0.0); + bool swapRB = false, int ddepth = CV_32F, dnn::DataLayout datalayout = DNN_LAYOUT_NCHW, + ImagePaddingMode mode = dnn::DNN_PMODE_NULL, Scalar borderValue = 0.0); CV_PROP_RW Scalar scalefactor; //!< scalefactor multiplier for input image values. CV_PROP_RW Size size; //!< Spatial size for output image. CV_PROP_RW Scalar mean; //!< Scalar with mean values which are subtracted from channels. CV_PROP_RW bool swapRB; //!< Flag which indicates that swap first and last channels CV_PROP_RW int ddepth; //!< Depth of output blob. Choose CV_32F or CV_8U. - CV_PROP_RW DataLayout datalayout; //!< Order of output dimensions. Choose DNN_LAYOUT_NCHW or DNN_LAYOUT_NHWC. - CV_PROP_RW ImagePaddingMode paddingmode; //!< Image padding mode. @see ImagePaddingMode. + CV_PROP_RW dnn::DataLayout datalayout; //!< Order of output dimensions. Choose DNN_LAYOUT_NCHW or DNN_LAYOUT_NHWC. + CV_PROP_RW dnn::ImagePaddingMode paddingmode; //!< Image padding mode. @see ImagePaddingMode. CV_PROP_RW Scalar borderValue; //!< Value used in padding mode for padding. /** @brief Get rectangle coordinates in original image system from rectangle in blob coordinates. diff --git a/modules/dnn/misc/java/test/DnnBlobFromImageWithParamsTest.java b/modules/dnn/misc/java/test/DnnBlobFromImageWithParamsTest.java new file mode 100644 index 0000000000..f55576eb61 --- /dev/null +++ b/modules/dnn/misc/java/test/DnnBlobFromImageWithParamsTest.java @@ -0,0 +1,134 @@ +package org.opencv.test.dnn; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.opencv.core.Core; +import org.opencv.core.CvType; +import org.opencv.core.Mat; +import org.opencv.core.Scalar; +import org.opencv.core.Size; +import org.opencv.core.Range; +import org.opencv.dnn.Dnn; +import org.opencv.dnn.Image2BlobParams; +import org.opencv.test.OpenCVTestCase; + +public class DnnBlobFromImageWithParamsTest extends OpenCVTestCase { + + public void testBlobFromImageWithParamsNHWCScalarScale() + { + Mat img = new Mat(10, 10, CvType.CV_8UC4, new Scalar(0, 1, 2, 3)); + Scalar scalefactor = new Scalar(0.1, 0.2, 0.3, 0.4); + + Image2BlobParams params = new Image2BlobParams(); + params.set_scalefactor(scalefactor); + params.set_datalayout(Dnn.DNN_LAYOUT_NHWC); + + Mat blob = Dnn.blobFromImageWithParams(img, params); // [1, 10, 10, 4] + + float[] expectedValues = { (float)scalefactor.val[0] * 0, (float)scalefactor.val[1] * 1, (float)scalefactor.val[2] * 2, (float)scalefactor.val[3] * 3 }; // Target Value. + for (int h = 0; h < 10; h++) + { + for (int w = 0; w < 10; w++) + { + float[] actualValues = new float[4]; + blob.get(new int[]{0, h, w, 0}, actualValues); + for (int c = 0; c < 4; c++) + { + // Check equal + assertEquals(expectedValues[c], actualValues[c]); + } + } + } + } + + public void testBlobFromImageWithParamsCustomPaddingLetterBox() + { + Mat img = new Mat(40, 20, CvType.CV_8UC4, new Scalar(0, 1, 2, 3)); + + // Custom padding value that you have added + Scalar customPaddingValue = new Scalar(5, 6, 7, 8); // Example padding value + Size targetSize = new Size(20, 20); + + Mat targetImg = img.clone(); + Core.copyMakeBorder(targetImg, targetImg, 0, 0, (int)targetSize.width / 2, (int)targetSize.width / 2, Core.BORDER_CONSTANT, customPaddingValue); + + // Set up Image2BlobParams with your new functionality + Image2BlobParams params = new Image2BlobParams(); + params.set_size(targetSize); + params.set_paddingmode(Dnn.DNN_PMODE_LETTERBOX); + params.set_borderValue(customPaddingValue); // Use your new feature here + + // Create blob with custom padding + Mat blob = Dnn.blobFromImageWithParams(img, params); + + // Create target blob for comparison + Mat targetBlob = Dnn.blobFromImage(targetImg, 1.0, targetSize); + + assertEquals(0, Core.norm(targetBlob, blob, Core.NORM_INF), EPS); + } + + public void testBlobFromImageWithParams4chLetterBox() + { + Mat img = new Mat(40, 20, CvType.CV_8UC4, new Scalar(0, 1, 2, 3)); + + // Construct target mat. + Mat[] targetChannels = new Mat[4]; + + // The letterbox will add zero at the left and right of output blob. + // After the letterbox, every row data would have same value showing as valVec. + byte[] valVec = { 0,0,0,0,0, 1,1,1,1,1,1,1,1,1,1, 0,0,0,0,0}; + + Mat rowM = new Mat(1, 20, CvType.CV_8UC1); + rowM.put(0, 0, valVec); + for (int i = 0; i < 4; i++) { + Core.multiply(rowM, new Scalar(i), targetChannels[i] = new Mat()); + } + + Mat targetImg = new Mat(); + Core.merge(Arrays.asList(targetChannels), targetImg); + Size targetSize = new Size(20, 20); + + Image2BlobParams params = new Image2BlobParams(); + params.set_size(targetSize); + params.set_paddingmode(Dnn.DNN_PMODE_LETTERBOX); + Mat blob = Dnn.blobFromImageWithParams(img, params); + Mat targetBlob = Dnn.blobFromImage(targetImg, 1.0, targetSize); // only convert data from uint8 to float32. + + assertEquals(0, Core.norm(targetBlob, blob, Core.NORM_INF), EPS); + } + + public void testBlobFromImageWithParams4chMultiImage() + { + Mat img = new Mat(10, 10, CvType.CV_8UC4, new Scalar(0, 1, 2, 3)); + + Scalar scalefactor = new Scalar(0.1, 0.2, 0.3, 0.4); + + Image2BlobParams param = new Image2BlobParams(); + param.set_scalefactor(scalefactor); + param.set_datalayout(Dnn.DNN_LAYOUT_NHWC); + + List images = new ArrayList<>(); + images.add(img); + Mat img2 = new Mat(); + Core.multiply(img, Scalar.all(2), img2); + images.add(img2); + + Mat blobs = Dnn.blobFromImagesWithParams(images, param); + + Range[] ranges = new Range[4]; + ranges[0] = new Range(0, 1); + ranges[1] = new Range(0, blobs.size(1)); + ranges[2] = new Range(0, blobs.size(2)); + ranges[3] = new Range(0, blobs.size(3)); + + Mat blob0 = blobs.submat(ranges).clone(); + + ranges[0] = new Range(1, 2); + Mat blob1 = blobs.submat(ranges).clone(); + + Core.multiply(blob0, Scalar.all(2), blob0); + + assertEquals(0, Core.norm(blob0, blob1, Core.NORM_INF), EPS); + } +} diff --git a/modules/objdetect/include/opencv2/objdetect.hpp b/modules/objdetect/include/opencv2/objdetect.hpp index 7f11890608..ed0d6f76ac 100644 --- a/modules/objdetect/include/opencv2/objdetect.hpp +++ b/modules/objdetect/include/opencv2/objdetect.hpp @@ -741,10 +741,10 @@ public: CV_PROP_RW int version; //! The optional level of error correction (by default - the lowest). - CV_PROP_RW CorrectionLevel correction_level; + CV_PROP_RW QRCodeEncoder::CorrectionLevel correction_level; //! The optional encoding mode - Numeric, Alphanumeric, Byte, Kanji, ECI or Structured Append. - CV_PROP_RW EncodeMode mode; + CV_PROP_RW QRCodeEncoder::EncodeMode mode; //! The optional number of QR codes to generate in Structured Append mode. CV_PROP_RW int structure_number; From e37819c2ac1b8ec2beac917bba0d885c79195603 Mon Sep 17 00:00:00 2001 From: Skreg <85214856+shyama7004@users.noreply.github.com> Date: Mon, 21 Apr 2025 23:45:37 +0530 Subject: [PATCH 82/94] Merge pull request #27221 from shyama7004:docChanges Minor changes in calib3d docs for clarity #27221 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- .../camera_calibration/camera_calibration.markdown | 4 ++-- .../camera_calibration_pattern.markdown | 10 +++++----- .../camera_calibration_square_chess.markdown | 7 ++++++- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/doc/tutorials/calib3d/camera_calibration/camera_calibration.markdown b/doc/tutorials/calib3d/camera_calibration/camera_calibration.markdown index 4f899f94ba..0126829f48 100644 --- a/doc/tutorials/calib3d/camera_calibration/camera_calibration.markdown +++ b/doc/tutorials/calib3d/camera_calibration/camera_calibration.markdown @@ -129,7 +129,7 @@ Explanation -# **Find the pattern in the current input** The formation of the equations I mentioned above aims - to finding major patterns in the input: in case of the chessboard this are corners of the + to finding major patterns in the input: in case of the chessboard these are corners of the squares and for the circles, well, the circles themselves. ChArUco board is equivalent to chessboard, but corners are matched by ArUco markers. The position of these will form the result which will be written into the *pointBuf* vector. @@ -140,7 +140,7 @@ Explanation of the patterns. cv::findChessboardCorners and cv::findCirclesGrid return a boolean variable which states if the pattern was found in the input (we only need to take into account those images where this is true!). `CharucoDetector::detectBoard` may detect partially visible - pattern and returns coordunates and ids of visible inner corners. + pattern and returns coordinates and ids of visible inner corners. @note Board size and amount of matched points is different for chessboard, circles grid and ChArUco. All chessboard related algorithm expects amount of inner corners as board width and height. diff --git a/doc/tutorials/calib3d/camera_calibration_pattern/camera_calibration_pattern.markdown b/doc/tutorials/calib3d/camera_calibration_pattern/camera_calibration_pattern.markdown index 54b0c0e1cd..7b5a92b417 100644 --- a/doc/tutorials/calib3d/camera_calibration_pattern/camera_calibration_pattern.markdown +++ b/doc/tutorials/calib3d/camera_calibration_pattern/camera_calibration_pattern.markdown @@ -11,7 +11,7 @@ Create calibration pattern {#tutorial_camera_calibration_pattern} | Compatibility | OpenCV >= 3.0 | -The goal of this tutorial is to learn how to create calibration pattern. +The goal of this tutorial is to learn how to create a calibration pattern. You can find a chessboard pattern in https://github.com/opencv/opencv/blob/4.x/doc/pattern.png @@ -47,14 +47,14 @@ create a ChAruco board pattern in charuco_board.svg with 7 rows, 5 columns, squa python gen_pattern.py -o charuco_board.svg --rows 7 --columns 5 -T charuco_board --square_size 30 --marker_size 15 -f DICT_5X5_100.json.gz -If you want to change unit use -u option (mm inches, px, m) +If you want to change the measurement units, use the -u option (e.g. mm, inches, px, m) -If you want to change page size use -w and -h options +If you want to change the page size, use the -w (width) and -h (height) options -If you want to use your own dictionary for ChAruco board your should write name of file with your dictionary. For example +If you want to use your own dictionary for the ChAruco board, specify the name of your dictionary file. For example python gen_pattern.py -o charuco_board.svg --rows 7 --columns 5 -T charuco_board -f my_dictionary.json -You can generate your dictionary in my_dictionary.json file with number of markers 30 and markers size 5 bits by using opencv/samples/cpp/aruco_dict_utils.cpp. +You can generate your dictionary in the file my_dictionary.json with 30 markers and a marker size of 5 bits using the utility provided in opencv/samples/cpp/aruco_dict_utils.cpp. bin/example_cpp_aruco_dict_utils.exe my_dict.json -nMarkers=30 -markerSize=5 diff --git a/doc/tutorials/calib3d/camera_calibration_square_chess/camera_calibration_square_chess.markdown b/doc/tutorials/calib3d/camera_calibration_square_chess/camera_calibration_square_chess.markdown index b278bb87ac..ddd7dbae79 100644 --- a/doc/tutorials/calib3d/camera_calibration_square_chess/camera_calibration_square_chess.markdown +++ b/doc/tutorials/calib3d/camera_calibration_square_chess/camera_calibration_square_chess.markdown @@ -63,4 +63,9 @@ image. opencv/samples/cpp/calibration.cpp, function computeReprojectionErrors). Question: how would you calculate distance from the camera origin to any one of the corners? -Answer: As our image lies in a 3D space, firstly we would calculate the relative camera pose. This would give us 3D to 2D correspondences. Next, we can apply a simple L2 norm to calculate distance between any point (end point for corners). +Answer: After obtaining the camera pose using solvePnP, the rotation (rvec) and translation (tvec) vectors define the transformation between the world (chessboard) coordinates and the camera coordinate system. To calculate the distance from the camera’s origin to any chessboard corner, first transform the 3D point from the chessboard coordinate system to the camera coordinate system (if not already done) and then compute its Euclidean distance using the L2 norm, for example: + + // assuming 'point' is the 3D position of a chessboard corner in the camera coordinate system + double distance = norm(point); + +This is equivalent to applying the L2 norm on the 3D point’s coordinates (x, y, z). \ No newline at end of file From a7749c38138a6cb17d4189a28e3ab28db97f47a1 Mon Sep 17 00:00:00 2001 From: fengyuentau Date: Tue, 22 Apr 2025 14:44:42 +0800 Subject: [PATCH 83/94] aligned behavior in normDiff in hal_rvv for 4.x --- 3rdparty/hal_rvv/hal_rvv_1p0/norm_diff.hpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/norm_diff.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/norm_diff.hpp index 1bc0f31075..1ffa42f15d 100644 --- a/3rdparty/hal_rvv/hal_rvv_1p0/norm_diff.hpp +++ b/3rdparty/hal_rvv/hal_rvv_1p0/norm_diff.hpp @@ -130,7 +130,8 @@ struct NormDiffInf_RVV { vl = __riscv_vsetvl_e32m8(n - i); auto v1 = __riscv_vle32_v_i32m8(src1 + i, vl); auto v2 = __riscv_vle32_v_i32m8(src2 + i, vl); - auto v = custom_intrin::__riscv_vabd(v1, v2, vl); + // auto v = custom_intrin::__riscv_vabd(v1, v2, vl); // 5.x + auto v = custom_intrin::__riscv_vabs(__riscv_vsub(v1, v2, vl), vl); // 4.x s = __riscv_vmaxu_tu(s, s, v, vl); } return __riscv_vmv_x(__riscv_vredmaxu(s, __riscv_vmv_s_x_u32m1(0, __riscv_vsetvlmax_e32m1()), vlmax)); @@ -247,7 +248,8 @@ struct NormDiffL1_RVV { vl = __riscv_vsetvl_e32m4(n - i); auto v1 = __riscv_vle32_v_i32m4(src1 + i, vl); auto v2 = __riscv_vle32_v_i32m4(src2 + i, vl); - auto v = custom_intrin::__riscv_vabd(v1, v2, vl); + // auto v = custom_intrin::__riscv_vabd(v1, v2, vl); // 5.x + auto v = custom_intrin::__riscv_vabs(__riscv_vsub(v1, v2, vl), vl); // 4.x s = __riscv_vfadd_tu(s, s, __riscv_vfwcvt_f(v, vl), vl); } return __riscv_vfmv_f(__riscv_vfredosum(s, __riscv_vfmv_s_f_f64m1(0, __riscv_vsetvlmax_e64m1()), vlmax)); @@ -577,7 +579,8 @@ struct MaskedNormDiffInf_RVV { vl = __riscv_vsetvl_e32m8(len - i); auto v1 = __riscv_vlse32_v_i32m8(src1 + cn * i + cn_index, sizeof(int) * cn, vl); auto v2 = __riscv_vlse32_v_i32m8(src2 + cn * i + cn_index, sizeof(int) * cn, vl); - auto v = custom_intrin::__riscv_vabd(v1, v2, vl); + // auto v = custom_intrin::__riscv_vabd(v1, v2, vl); // 5.x + auto v = custom_intrin::__riscv_vabs(__riscv_vsub(v1, v2, vl), vl); // 4.x auto m = __riscv_vle8_v_u8m2(mask + i, vl); auto b = __riscv_vmsne(m, 0, vl); s = __riscv_vmaxu_tumu(b, s, s, v, vl); @@ -759,7 +762,8 @@ struct MaskedNormDiffL1_RVV { vl = __riscv_vsetvl_e32m4(len - i); auto v1 = __riscv_vlse32_v_i32m4(src1 + cn * i + cn_index, sizeof(int) * cn, vl); auto v2 = __riscv_vlse32_v_i32m4(src2 + cn * i + cn_index, sizeof(int) * cn, vl); - auto v = custom_intrin::__riscv_vabd(v1, v2, vl); + // auto v = custom_intrin::__riscv_vabd(v1, v2, vl); // 5.x + auto v = custom_intrin::__riscv_vabs(__riscv_vsub(v1, v2, vl), vl); // 4.x auto m = __riscv_vle8_v_u8m1(mask + i, vl); auto b = __riscv_vmsne(m, 0, vl); s = __riscv_vfadd_tumu(b, s, s, __riscv_vfwcvt_f(b, v, vl), vl); From 325e59bd4c23bf78d6d64e1140bad5f0cb514cc9 Mon Sep 17 00:00:00 2001 From: Yuantao Feng Date: Tue, 22 Apr 2025 16:03:26 +0800 Subject: [PATCH 84/94] Merge pull request #27229 from fengyuentau:4x/hal_rvv/transpose HAL: implemented cv_hal_transpose in hal_rvv #27229 Checklists: - [x] transpose2d_8u - [x] transpose2d_16u - [ ] ~transpose2d_8uC3~ - [x] transpose2d_32s - [ ] ~transpose2d_16uC3~ - [x] transpose2d_32sC2 - [ ] ~transpose_32sC3~ - [ ] ~transpose_32sC4~ - [ ] ~transpose_32sC6~ - [ ] ~transpose_32sC8~ - [ ] ~inplace transpose~ ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- 3rdparty/hal_rvv/hal_rvv.hpp | 1 + 3rdparty/hal_rvv/hal_rvv_1p0/transpose.hpp | 220 +++++++++++++++++++++ modules/core/perf/perf_arithm.cpp | 13 ++ 3 files changed, 234 insertions(+) create mode 100644 3rdparty/hal_rvv/hal_rvv_1p0/transpose.hpp diff --git a/3rdparty/hal_rvv/hal_rvv.hpp b/3rdparty/hal_rvv/hal_rvv.hpp index 9c6cd94623..8fe78bd8b9 100644 --- a/3rdparty/hal_rvv/hal_rvv.hpp +++ b/3rdparty/hal_rvv/hal_rvv.hpp @@ -49,6 +49,7 @@ #include "hal_rvv_1p0/div.hpp" // core #include "hal_rvv_1p0/dotprod.hpp" // core #include "hal_rvv_1p0/compare.hpp" // core +#include "hal_rvv_1p0/transpose.hpp" // core #include "hal_rvv_1p0/moments.hpp" // imgproc #include "hal_rvv_1p0/filter.hpp" // imgproc diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/transpose.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/transpose.hpp new file mode 100644 index 0000000000..10bf9b4d3e --- /dev/null +++ b/3rdparty/hal_rvv/hal_rvv_1p0/transpose.hpp @@ -0,0 +1,220 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2025, SpaceMIT Inc., all rights reserved. +// Third party copyrights are property of their respective owners. + +#ifndef OPENCV_HAL_RVV_TRANSPOSE_HPP_INCLUDED +#define OPENCV_HAL_RVV_TRANSPOSE_HPP_INCLUDED + +#include + +namespace cv { namespace cv_hal_rvv { namespace transpose { + +#if defined (__clang__) && __clang_major__ < 18 +#define OPENCV_HAL_IMPL_RVV_VCREATE_x4(suffix, width, v0, v1, v2, v3) \ + __riscv_vset_v_##suffix##m##width##_##suffix##m##width##x4(v, 0, v0); \ + v = __riscv_vset(v, 1, v1); \ + v = __riscv_vset(v, 2, v2); \ + v = __riscv_vset(v, 3, v3); + +#define OPENCV_HAL_IMPL_RVV_VCREATE_x8(suffix, width, v0, v1, v2, v3, v4, v5, v6, v7) \ + __riscv_vset_v_##suffix##m##width##_##suffix##m##width##x8(v, 0, v0); \ + v = __riscv_vset(v, 1, v1); \ + v = __riscv_vset(v, 2, v2); \ + v = __riscv_vset(v, 3, v3); \ + v = __riscv_vset(v, 4, v4); \ + v = __riscv_vset(v, 5, v5); \ + v = __riscv_vset(v, 6, v6); \ + v = __riscv_vset(v, 7, v7); + +#define __riscv_vcreate_v_u8m1x8(v0, v1, v2, v3, v4, v5, v6, v7) OPENCV_HAL_IMPL_RVV_VCREATE_x8(u8, 1, v0, v1, v2, v3, v4, v5, v6, v7) +#define __riscv_vcreate_v_u16m1x8(v0, v1, v2, v3, v4, v5, v6, v7) OPENCV_HAL_IMPL_RVV_VCREATE_x8(u16, 1, v0, v1, v2, v3, v4, v5, v6, v7) +#define __riscv_vcreate_v_i32m1x4(v0, v1, v2, v3) OPENCV_HAL_IMPL_RVV_VCREATE_x4(i32, 1, v0, v1, v2, v3) +#define __riscv_vcreate_v_i64m1x8(v0, v1, v2, v3, v4, v5, v6, v7) OPENCV_HAL_IMPL_RVV_VCREATE_x8(i64, 1, v0, v1, v2, v3, v4, v5, v6, v7) +#endif + +static void transpose2d_8u(const uchar *src_data, size_t src_step, uchar *dst_data, size_t dst_step, int src_width, int src_height) { + auto transpose_8u_8xVl = [](const uchar *src, size_t src_step, uchar *dst, size_t dst_step, const int vl) { + auto v0 = __riscv_vle8_v_u8m1(src, vl); + auto v1 = __riscv_vle8_v_u8m1(src + src_step, vl); + auto v2 = __riscv_vle8_v_u8m1(src + 2 * src_step, vl); + auto v3 = __riscv_vle8_v_u8m1(src + 3 * src_step, vl); + auto v4 = __riscv_vle8_v_u8m1(src + 4 * src_step, vl); + auto v5 = __riscv_vle8_v_u8m1(src + 5 * src_step, vl); + auto v6 = __riscv_vle8_v_u8m1(src + 6 * src_step, vl); + auto v7 = __riscv_vle8_v_u8m1(src + 7 * src_step, vl); + vuint8m1x8_t v = __riscv_vcreate_v_u8m1x8(v0, v1, v2, v3, v4, v5, v6, v7); + __riscv_vssseg8e8(dst, dst_step, v, vl); + }; + + int h = 0, w = 0; + for (; h <= src_height - 8; h += 8) { + const uchar *src = src_data + h * src_step; + uchar *dst = dst_data + h; + int vl; + for (w = 0; w < src_width; w += vl) { + vl = __riscv_vsetvl_e8m1(src_width - w); + transpose_8u_8xVl(src + w, src_step, dst + w * dst_step, dst_step, vl); + } + } + for (; h < src_height; h++) { + const uchar *src = src_data + h * src_step; + uchar *dst = dst_data + h; + int vl; + for (w = 0; w < src_width; w += vl) { + vl = __riscv_vsetvl_e8m8(src_width - w); + auto v = __riscv_vle8_v_u8m8(src + w, vl); + __riscv_vsse8(dst + w * dst_step, dst_step, v, vl); + } + } +} + +static void transpose2d_16u(const uchar *src_data, size_t src_step, uchar *dst_data, size_t dst_step, int src_width, int src_height) { + auto transpose_16u_8xVl = [](const ushort *src, size_t src_step, ushort *dst, size_t dst_step, const int vl) { + auto v0 = __riscv_vle16_v_u16m1(src, vl); + auto v1 = __riscv_vle16_v_u16m1(src + src_step, vl); + auto v2 = __riscv_vle16_v_u16m1(src + 2 * src_step, vl); + auto v3 = __riscv_vle16_v_u16m1(src + 3 * src_step, vl); + auto v4 = __riscv_vle16_v_u16m1(src + 4 * src_step, vl); + auto v5 = __riscv_vle16_v_u16m1(src + 5 * src_step, vl); + auto v6 = __riscv_vle16_v_u16m1(src + 6 * src_step, vl); + auto v7 = __riscv_vle16_v_u16m1(src + 7 * src_step, vl); + vuint16m1x8_t v = __riscv_vcreate_v_u16m1x8(v0, v1, v2, v3, v4, v5, v6, v7); + __riscv_vssseg8e16(dst, dst_step, v, vl); + }; + + size_t src_step_base = src_step / sizeof(ushort); + size_t dst_step_base = dst_step / sizeof(ushort); + + int h = 0, w = 0; + for (; h <= src_height - 8; h += 8) { + const ushort *src = (const ushort*)(src_data) + h * src_step_base; + ushort *dst = (ushort*)(dst_data) + h; + int vl; + for (w = 0; w < src_width; w += vl) { + vl = __riscv_vsetvl_e16m1(src_width - w); + transpose_16u_8xVl(src + w, src_step_base, dst + w * dst_step_base, dst_step, vl); + } + } + for (; h < src_height; h++) { + const ushort *src = (const ushort*)(src_data) + h * src_step_base; + ushort *dst = (ushort*)(dst_data) + h; + int vl; + for (w = 0; w < src_width; w += vl) { + vl = __riscv_vsetvl_e16m8(src_width - w); + auto v = __riscv_vle16_v_u16m8(src + w, vl); + __riscv_vsse16(dst + w * dst_step_base, dst_step, v, vl); + } + } +} + +static void transpose2d_32s(const uchar *src_data, size_t src_step, uchar *dst_data, size_t dst_step, int src_width, int src_height) { + auto transpose_32s_4xVl = [](const int *src, size_t src_step, int *dst, size_t dst_step, const int vl) { + auto v0 = __riscv_vle32_v_i32m1(src, vl); + auto v1 = __riscv_vle32_v_i32m1(src + src_step, vl); + auto v2 = __riscv_vle32_v_i32m1(src + 2 * src_step, vl); + auto v3 = __riscv_vle32_v_i32m1(src + 3 * src_step, vl); + vint32m1x4_t v = __riscv_vcreate_v_i32m1x4(v0, v1, v2, v3); + __riscv_vssseg4e32(dst, dst_step, v, vl); + }; + + size_t src_step_base = src_step / sizeof(int); + size_t dst_step_base = dst_step / sizeof(int); + + int h = 0, w = 0; + for (; h <= src_height - 4; h += 4) { + const int *src = (const int*)(src_data) + h * src_step_base; + int *dst = (int*)(dst_data) + h; + int vl; + for (w = 0; w < src_width; w += vl) { + vl = __riscv_vsetvl_e32m1(src_width - w); + transpose_32s_4xVl(src + w, src_step_base, dst + w * dst_step_base, dst_step, vl); + } + } + for (; h < src_height; h++) { + const int *src = (const int*)(src_data) + h * src_step_base; + int *dst = (int*)(dst_data) + h; + int vl; + for (w = 0; w < src_width; w += vl) { + vl = __riscv_vsetvl_e32m8(src_width - w); + auto v = __riscv_vle32_v_i32m8(src + w, vl); + __riscv_vsse32(dst + w * dst_step_base, dst_step, v, vl); + } + } +} + +static void transpose2d_32sC2(const uchar *src_data, size_t src_step, uchar *dst_data, size_t dst_step, int src_width, int src_height) { + auto transpose_64s_8xVl = [](const int64_t *src, size_t src_step, int64_t *dst, size_t dst_step, const int vl) { + auto v0 = __riscv_vle64_v_i64m1(src, vl); + auto v1 = __riscv_vle64_v_i64m1(src + src_step, vl); + auto v2 = __riscv_vle64_v_i64m1(src + 2 * src_step, vl); + auto v3 = __riscv_vle64_v_i64m1(src + 3 * src_step, vl); + auto v4 = __riscv_vle64_v_i64m1(src + 4 * src_step, vl); + auto v5 = __riscv_vle64_v_i64m1(src + 5 * src_step, vl); + auto v6 = __riscv_vle64_v_i64m1(src + 6 * src_step, vl); + auto v7 = __riscv_vle64_v_i64m1(src + 7 * src_step, vl); + vint64m1x8_t v = __riscv_vcreate_v_i64m1x8(v0, v1, v2, v3, v4, v5, v6, v7); + __riscv_vssseg8e64(dst, dst_step, v, vl); + }; + + size_t src_step_base = src_step / sizeof(int64_t); + size_t dst_step_base = dst_step / sizeof(int64_t); + + int h = 0, w = 0; + for (; h <= src_height - 8; h += 8) { + const int64_t *src = (const int64_t*)(src_data) + h * src_step_base; + int64_t *dst = (int64_t*)(dst_data) + h; + int vl; + for (w = 0; w < src_width; w += vl) { + vl = __riscv_vsetvl_e64m1(src_width - w); + transpose_64s_8xVl(src + w, src_step_base, dst + w * dst_step_base, dst_step, vl); + } + } + for (; h < src_height; h++) { + const int64_t *src = (const int64_t*)(src_data) + h * src_step_base; + int64_t *dst = (int64_t*)(dst_data) + h; + int vl; + for (w = 0; w < src_width; w += vl) { + vl = __riscv_vsetvl_e64m8(src_width - w); + auto v = __riscv_vle64_v_i64m8(src + w, vl); + __riscv_vsse64(dst + w * dst_step_base, dst_step, v, vl); + } + } +} + +#undef cv_hal_transpose2d +#define cv_hal_transpose2d cv::cv_hal_rvv::transpose::transpose2d + +using Transpose2dFunc = void (*)(const uchar*, size_t, uchar*, size_t, int, int); +inline int transpose2d(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, + int src_width, int src_height, int element_size) { + if (src_data == dst_data) { + return CV_HAL_ERROR_NOT_IMPLEMENTED; + } + + static Transpose2dFunc tab[] = { + 0, transpose2d_8u, transpose2d_16u, 0, + transpose2d_32s, 0, 0, 0, + transpose2d_32sC2, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0 + }; + Transpose2dFunc func = tab[element_size]; + if (!func) { + return CV_HAL_ERROR_NOT_IMPLEMENTED; + } + + func(src_data, src_step, dst_data, dst_step, src_width, src_height); + + return CV_HAL_ERROR_OK; +} + +}}} // cv::cv_hal_rvv::transpose + +#endif // OPENCV_HAL_RVV_TRANSPOSE_HPP_INCLUDED diff --git a/modules/core/perf/perf_arithm.cpp b/modules/core/perf/perf_arithm.cpp index 242d323565..cd5a750cc5 100644 --- a/modules/core/perf/perf_arithm.cpp +++ b/modules/core/perf/perf_arithm.cpp @@ -422,6 +422,19 @@ PERF_TEST_P_(BinaryOpTest, reciprocal) SANITY_CHECK_NOTHING(); } +PERF_TEST_P_(BinaryOpTest, transpose2d) +{ + Size sz = get<0>(GetParam()); + int type = get<1>(GetParam()); + Size tsz = Size(sz.height, sz.width); + cv::Mat a(sz, type), b(tsz, type);; + + declare.in(a, WARMUP_RNG).out(b); + + TEST_CYCLE() cv::transpose(a, b); + + SANITY_CHECK_NOTHING(); +} PERF_TEST_P_(BinaryOpTest, transposeND) { From 9241e0a9f62200e69b6c782c3a2f6061e9847b29 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Thu, 24 Apr 2025 07:30:16 +0300 Subject: [PATCH 85/94] Added KleidiCV check for Android SDK release builds. --- platforms/android/build_sdk.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/platforms/android/build_sdk.py b/platforms/android/build_sdk.py index be78722334..9de579e5fc 100755 --- a/platforms/android/build_sdk.py +++ b/platforms/android/build_sdk.py @@ -139,6 +139,8 @@ class ABI: return "%s (%s)" % (self.name, self.toolchain) def haveIPP(self): return self.name == "x86" or self.name == "x86_64" + def haveKleidiCV(self): + return self.name == "arm64-v8a" #=================================================================================================== @@ -382,7 +384,7 @@ def get_ndk_dir(): def check_cmake_flag_enabled(cmake_file, flag_name, strict=True): print(f"Checking build flag '{flag_name}' in: {cmake_file}") - + if not os.path.isfile(cmake_file): msg = f"ERROR: File {cmake_file} does not exist." if strict: @@ -396,7 +398,7 @@ def check_cmake_flag_enabled(cmake_file, flag_name, strict=True): for line in file: if line.strip().startswith(f"{flag_name}="): value = line.strip().split('=')[1] - if value == '1': + if value == '1' or value == 'ON': print(f"{flag_name}=1 found. Support is enabled.") return else: @@ -413,7 +415,7 @@ def check_cmake_flag_enabled(cmake_file, flag_name, strict=True): sys.exit(1) else: print("WARNING:", msg) - + #=================================================================================================== if __name__ == "__main__": @@ -519,15 +521,19 @@ if __name__ == "__main__": os.chdir(builder.libdest) builder.clean_library_build_dir() builder.build_library(abi, do_install, args.no_media_ndk) - + #Check HAVE_IPP x86 / x86_64 if abi.haveIPP(): log.info("Checking HAVE_IPP for ABI: %s", abi.name) check_cmake_flag_enabled(os.path.join(builder.libdest,"CMakeVars.txt"), "HAVE_IPP", strict=args.strict_dependencies) + #Check HAVE_KLEIDICV for armv8 + if abi.haveKleidiCV(): + log.info("Checking HAVE_KLEIDICV for ABI: %s", abi.name) + check_cmake_flag_enabled(os.path.join(builder.libdest,"CMakeVars.txt"), "HAVE_KLEIDICV", strict=args.strict_dependencies) + builder.gather_results() - - + if args.build_doc: builder.build_javadoc() From 6f8f846288b3582a323d7b684192625715d6fefc Mon Sep 17 00:00:00 2001 From: nina16448 Date: Fri, 25 Apr 2025 15:39:50 +0800 Subject: [PATCH 86/94] Update houghcircles.py --- samples/python/houghcircles.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/python/houghcircles.py b/samples/python/houghcircles.py index 60a36714fe..9d63f6ca1f 100755 --- a/samples/python/houghcircles.py +++ b/samples/python/houghcircles.py @@ -27,7 +27,7 @@ def main(): img = cv.medianBlur(img, 5) cimg = src.copy() # numpy function - circles = cv.HoughCircles(img, cv.HOUGH_GRADIENT, 1, 10, np.array([]), 100, 30, 1, 30) + circles = cv.HoughCircles(img, cv.HOUGH_GRADIENT, 1, 10, np.array([]), 200, 30, 5, 30) if circles is not None: # Check if circles have been found and only then iterate over these and add them to the image circles = np.uint16(np.around(circles)) From 485c7d5be73d443b2d59336571fd2c124c33ee0d Mon Sep 17 00:00:00 2001 From: sirudoi <107246340+sirudoi@users.noreply.github.com> Date: Fri, 25 Apr 2025 16:04:19 +0800 Subject: [PATCH 87/94] Merge pull request #27230 from sirudoi:4.x MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit videoio: add Orbbec Gemini 330 camera support #27230 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] The feature is well documented and sample code can be built with the project CMake ### Description of Changes #### motivated: - Orbbec has launched a new RGB-D camera — the Gemini 330. To fully leverage the capabilities of the Gemini 330, Orbbec simultaneously released version 2 of the open-source OrbbecSDK. This PR adapts the support for the Gemini 330 series cameras to better meet and respond to users’ application requirements. #### change: - Add support for the Orbbec Gemini330 camera. - Fixed an issue with Femto Mega on Windows 10/11; for details, see [issue](https://github.com/opencv/opencv/pull/23237#issuecomment-2242347295). - When enabling `HAVE_OBSENSOR_ORBBEC_SDK`, the build now fetches version 2 of the OrbbecSDK, and the sample API calls have been updated to the v2 format. ### Testing | OS | Compiler | Camera | Result | |:----------:|:---------------------------------------:|:-----------------:|:------:| | Windows 11 | (VS2022) MSVC runtime library version 14.40 | Gemini 335/336L | Pass | | Windows 11 | (VS2022) MSVC runtime library version 14.19 | Gemini 335/336L | Pass | | Ubuntu22.04| GCC 11.4 | Gemini 335/336L | Pass | | Ubuntu18.04| GCC 7.5 | Gemini 335/336L | Pass | ### Acknowledgements Thank you to the OpenCV team for the continuous support and for creating such a robust open source project. I appreciate the valuable feedback from the community and reviewers, which has helped improve the quality of this contribution! --- doc/tutorials/app/orbbec_uvc.markdown | 3 +- modules/videoio/include/opencv2/videoio.hpp | 2 +- .../obsensor_stream_channel_interface.hpp | 15 +++++ .../obsensor_stream_channel_msmf.cpp | 18 ++++-- .../obsensor_stream_channel_v4l2.cpp | 4 +- .../obsensor_uvc_stream_channel.cpp | 58 ++++++++++++++++++- .../obsensor_uvc_stream_channel.hpp | 14 ++++- modules/videoio/src/cap_obsensor_capture.cpp | 31 +++++----- 8 files changed, 118 insertions(+), 27 deletions(-) diff --git a/doc/tutorials/app/orbbec_uvc.markdown b/doc/tutorials/app/orbbec_uvc.markdown index 1b043f2579..214ef6afc7 100644 --- a/doc/tutorials/app/orbbec_uvc.markdown +++ b/doc/tutorials/app/orbbec_uvc.markdown @@ -123,4 +123,5 @@ This tutorial code's is shown lines below. You can also download it from ![BGR And DEPTH And DepthToColor frame](images/orbbec_uvc_cpp.jpg) ### Note -Mac users need sudo privileges to execute the code. + - Mac users need sudo privileges to execute the code. + - **Firmware**: If you’re using an Orbbec UVC 3D camera, please ensure your camera’s firmware is updated to the latest version to avoid potential compatibility issues. For more details, see [Orbbec’s Release Notes](https://github.com/orbbec/OrbbecSDK_v2/releases). diff --git a/modules/videoio/include/opencv2/videoio.hpp b/modules/videoio/include/opencv2/videoio.hpp index c323e57eb7..6b5c1b6cc4 100644 --- a/modules/videoio/include/opencv2/videoio.hpp +++ b/modules/videoio/include/opencv2/videoio.hpp @@ -128,7 +128,7 @@ enum VideoCaptureAPIs { CAP_INTEL_MFX = 2300, //!< Intel MediaSDK CAP_XINE = 2400, //!< XINE engine (Linux) CAP_UEYE = 2500, //!< uEye Camera API - CAP_OBSENSOR = 2600, //!< For Orbbec 3D-Sensor device/module (Astra+, Femto, Astra2, Gemini2, Gemini2L, Gemini2XL, Femto Mega) attention: Astra2 cameras currently only support Windows and Linux kernel versions no higher than 4.15, and higher versions of Linux kernel may have exceptions. + CAP_OBSENSOR = 2600, //!< For Orbbec 3D-Sensor device/module (Astra+, Femto, Astra2, Gemini2, Gemini2L, Gemini2XL, Gemini330, Femto Mega) attention: Astra2 cameras currently only support Windows and Linux kernel versions no higher than 4.15, and higher versions of Linux kernel may have exceptions. }; diff --git a/modules/videoio/src/cap_obsensor/obsensor_stream_channel_interface.hpp b/modules/videoio/src/cap_obsensor/obsensor_stream_channel_interface.hpp index 7337452359..878de60f21 100644 --- a/modules/videoio/src/cap_obsensor/obsensor_stream_channel_interface.hpp +++ b/modules/videoio/src/cap_obsensor/obsensor_stream_channel_interface.hpp @@ -39,6 +39,21 @@ namespace obsensor { #define OBSENSOR_FEMTO_MEGA_PID 0x0669 // pid of Orbbec Femto Mega Camera #define OBSENSOR_GEMINI2L_PID 0x0673 // pid of Orbbec Gemini 2 L Camera #define OBSENSOR_GEMINI2XL_PID 0x0671 // pid of Orbbec Gemini 2 XL Camera +#define OBSENSOR_GEMINI335_PID 0x0800 // pid of Orbbec Gemini 335 Camera +#define OBSENSOR_GEMINI330_PID 0x0801 // pid of Orbbec Gemini 330 Camera +#define OBSENSOR_GEMINI336_PID 0x0803 // pid of Orbbec Gemini 336 Camera +#define OBSENSOR_GEMINI335L_PID 0x0804 // pid of Orbbec Gemini 335L Camera +#define OBSENSOR_GEMINI330L_PID 0x0805 // pid of Orbbec Gemini 330L Camera +#define OBSENSOR_GEMINI336L_PID 0x0807 // pid of Orbbec Gemini 336L Camera + +#define IS_OBSENSOR_GEMINI330_SHORT_PID(pid) \ + ((pid) == OBSENSOR_GEMINI335_PID || (pid) == OBSENSOR_GEMINI330_PID || (pid) == OBSENSOR_GEMINI336_PID) + +#define IS_OBSENSOR_GEMINI330_LONG_PID(pid) \ + ((pid) == OBSENSOR_GEMINI335L_PID || (pid) == OBSENSOR_GEMINI330L_PID || (pid) == OBSENSOR_GEMINI336L_PID) + +#define IS_OBSENSOR_GEMINI330_PID(pid) \ + (IS_OBSENSOR_GEMINI330_SHORT_PID(pid) || IS_OBSENSOR_GEMINI330_LONG_PID(pid)) enum StreamType { diff --git a/modules/videoio/src/cap_obsensor/obsensor_stream_channel_msmf.cpp b/modules/videoio/src/cap_obsensor/obsensor_stream_channel_msmf.cpp index 5de686430f..18c6f4782d 100644 --- a/modules/videoio/src/cap_obsensor/obsensor_stream_channel_msmf.cpp +++ b/modules/videoio/src/cap_obsensor/obsensor_stream_channel_msmf.cpp @@ -228,8 +228,8 @@ MSMFStreamChannel::MSMFStreamChannel(const UvcDeviceInfo& devInfo) : }) delete[] buffer; HR_FAILED_RETURN(MFCreateDeviceSource(deviceAttrs_.Get(), &deviceSource_)); - HR_FAILED_RETURN(deviceSource_->QueryInterface(__uuidof(IAMCameraControl), reinterpret_cast(&cameraControl_))); - HR_FAILED_RETURN(deviceSource_->QueryInterface(__uuidof(IAMVideoProcAmp), reinterpret_cast(&videoProcAmp_))); + HR_FAILED_LOG(deviceSource_->QueryInterface(__uuidof(IAMCameraControl), reinterpret_cast(&cameraControl_))); + HR_FAILED_LOG(deviceSource_->QueryInterface(__uuidof(IAMVideoProcAmp), reinterpret_cast(&videoProcAmp_))); HR_FAILED_RETURN(MFCreateAttributes(&readerAttrs_, 3)); HR_FAILED_RETURN(readerAttrs_->SetUINT32(MF_SOURCE_READER_DISCONNECT_MEDIASOURCE_ON_SHUTDOWN, false)); @@ -314,7 +314,7 @@ void MSMFStreamChannel::start(const StreamProfile& profile, FrameCallback frameC currentProfile_ = profile; currentStreamIndex_ = -1; - for (uint8_t index = 0; index <= 5; index++) + for (uint8_t index = 0; index < 5; index++) { for (uint32_t k = 0;; k++) { @@ -341,6 +341,12 @@ void MSMFStreamChannel::start(const StreamProfile& profile, FrameCallback frameC fps == profile.fps && frameFourccToFormat(device_fourcc) == profile.format) { + for (uint8_t i = 0; i < 5; ++i) { + if (index == i) + continue; + + streamReader_->SetStreamSelection(i, FALSE); + } HR_FAILED_RETURN(streamReader_->SetCurrentMediaType(index, nullptr, mediaType.Get())); HR_FAILED_RETURN(streamReader_->SetStreamSelection(index, true)); streamReader_->ReadSample(index, 0, nullptr, nullptr, nullptr, nullptr); @@ -391,9 +397,9 @@ bool MSMFStreamChannel::setXu(uint8_t ctrl, const uint8_t* data, uint32_t len) } memcpy(xuSendBuf_.data(), data, len); - KSP_NODE node; + KSP_NODE node; memset(&node, 0, sizeof(KSP_NODE)); - node.Property.Set = { 0xA55751A1, 0xF3C5, 0x4A5E, {0x8D, 0x5A, 0x68, 0x54, 0xB8, 0xFA, 0x27, 0x16} }; + node.Property.Set = reinterpret_cast(xuUnit_.id); node.Property.Id = ctrl; node.Property.Flags = KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_TOPOLOGY; node.NodeId = xuNodeId_; @@ -412,7 +418,7 @@ bool MSMFStreamChannel::getXu(uint8_t ctrl, uint8_t** data, uint32_t* len) } KSP_NODE node; memset(&node, 0, sizeof(KSP_NODE)); - node.Property.Set = { 0xA55751A1, 0xF3C5, 0x4A5E, {0x8D, 0x5A, 0x68, 0x54, 0xB8, 0xFA, 0x27, 0x16} }; + node.Property.Set = reinterpret_cast(xuUnit_.id); node.Property.Id = ctrl; node.Property.Flags = KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_TOPOLOGY; node.NodeId = xuNodeId_; diff --git a/modules/videoio/src/cap_obsensor/obsensor_stream_channel_v4l2.cpp b/modules/videoio/src/cap_obsensor/obsensor_stream_channel_v4l2.cpp index 96e78e160b..0764ac3ace 100644 --- a/modules/videoio/src/cap_obsensor/obsensor_stream_channel_v4l2.cpp +++ b/modules/videoio/src/cap_obsensor/obsensor_stream_channel_v4l2.cpp @@ -314,7 +314,7 @@ bool V4L2StreamChannel::setXu(uint8_t ctrl, const uint8_t* data, uint32_t len) } memcpy(xuSendBuf_.data(), data, len); struct uvc_xu_control_query xu_ctrl_query = { - .unit = XU_UNIT_ID, + .unit = xuUnit_.unit, .selector = ctrl, .query = UVC_SET_CUR, .size = (__u16)(ctrl == 1 ? 512 : (ctrl == 2 ? 64 : 1024)), @@ -333,7 +333,7 @@ bool V4L2StreamChannel::getXu(uint8_t ctrl, uint8_t** data, uint32_t* len) xuRecvBuf_.resize(XU_MAX_DATA_LENGTH); } struct uvc_xu_control_query xu_ctrl_query = { - .unit = XU_UNIT_ID, + .unit = xuUnit_.unit, .selector = ctrl, .query = UVC_GET_CUR, .size = (__u16)(ctrl == 1 ? 512 : (ctrl == 2 ? 64 : 1024)), diff --git a/modules/videoio/src/cap_obsensor/obsensor_uvc_stream_channel.cpp b/modules/videoio/src/cap_obsensor/obsensor_uvc_stream_channel.cpp index 76a963748b..e10948efd6 100644 --- a/modules/videoio/src/cap_obsensor/obsensor_uvc_stream_channel.cpp +++ b/modules/videoio/src/cap_obsensor/obsensor_uvc_stream_channel.cpp @@ -35,19 +35,23 @@ namespace cv { namespace obsensor { +const ObExtensionUnit OBSENSOR_COMMON_XU_UNIT = { XU_UNIT_ID_COMMON, { 0xA55751A1, 0xF3C5, 0x4A5E, { 0x8D, 0x5A, 0x68, 0x54, 0xB8, 0xFA, 0x27, 0x16 } } }; +const ObExtensionUnit OBSENSOR_G330_XU_UNIT = { XU_UNIT_ID_G330, { 0xC9606CCB, 0x594C, 0x4D25, { 0xaf, 0x47, 0xcc, 0xc4, 0x96, 0x43, 0x59, 0x95 } } }; + const uint8_t OB_EXT_CMD0[16] = { 0x47, 0x4d, 0x04, 0x00, 0x02, 0x00, 0x52, 0x00, 0x5B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }; const uint8_t OB_EXT_CMD1[16] = { 0x47, 0x4d, 0x04, 0x00, 0x02, 0x00, 0x54, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; const uint8_t OB_EXT_CMD2[16] = { 0x47, 0x4d, 0x04, 0x00, 0x02, 0x00, 0x56, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }; const uint8_t OB_EXT_CMD3[16] = { 0x47, 0x4d, 0x04, 0x00, 0x02, 0x00, 0x58, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }; const uint8_t OB_EXT_CMD4[16] = { 0x47, 0x4d, 0x02, 0x00, 0x03, 0x00, 0x60, 0x00, 0xed, 0x03, 0x00, 0x00 }; const uint8_t OB_EXT_CMD5[16] = { 0x47, 0x4d, 0x02, 0x00, 0x03, 0x00, 0x62, 0x00, 0xe9, 0x03, 0x00, 0x00 }; -const uint8_t OB_EXT_CMD6[16] = { 0x47, 0x4d, 0x04, 0x00, 0x02, 0x00, 0x7c, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00}; +const uint8_t OB_EXT_CMD6[16] = { 0x47, 0x4d, 0x04, 0x00, 0x02, 0x00, 0x7c, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }; const uint8_t OB_EXT_CMD7[16] = { 0x47, 0x4d, 0x04, 0x00, 0x02, 0x00, 0xfe, 0x12, 0x55, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }; const uint8_t OB_EXT_CMD8[16] = { 0x47, 0x4d, 0x04, 0x00, 0x02, 0x00, 0xfe, 0x13, 0x3f, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }; const uint8_t OB_EXT_CMD9[16] = { 0x47, 0x4d, 0x04, 0x00, 0x02, 0x00, 0xfa, 0x13, 0x4b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }; const uint8_t OB_EXT_CMD11[16] = { 0x47, 0x4d, 0x04, 0x00, 0x02, 0x00, 0xfe, 0x13, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; const uint8_t OB_EXT_CMD12[16] = { 0x47, 0x4d, 0x04, 0x00, 0x02, 0x00, 0xfe, 0x13, 0x3f, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00 }; const uint8_t OB_EXT_CMD13[16] = { 0x47, 0x4d, 0x04, 0x00, 0x02, 0x00, 0xfa, 0x13, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +const uint8_t OB_EXT_CMD14[16] = { 0x47, 0x4d, 0x04, 0x00, 0x02, 0x00, 0xfa, 0x14, 0xd3, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }; #if defined(HAVE_OBSENSOR_V4L2) #define fourCc2Int(a, b, c, d) \ @@ -62,6 +66,7 @@ const std::map fourccToOBFormat = { {fourCc2Int('M', 'J', 'P', 'G'), FRAME_FORMAT_MJPG}, {fourCc2Int('Y', '1', '6', ' '), FRAME_FORMAT_Y16}, {fourCc2Int('Y', '1', '4', ' '), FRAME_FORMAT_Y14}, + {fourCc2Int('Z', '1', '6', ' '), FRAME_FORMAT_Y16} }; StreamType parseUvcDeviceNameToStreamType(const std::string& devName) @@ -204,7 +209,6 @@ DepthFrameUnpacker::~DepthFrameUnpacker() { delete[] outputDataBuf_; } - #define ON_BITS(count) ((1 << count) - 1) #define CREATE_MASK(count, offset) (ON_BITS(count) << offset) #define TAKE_BITS(source, count, offset) ((source & CREATE_MASK(count, offset)) >> offset) @@ -245,6 +249,7 @@ void DepthFrameUnpacker::process(Frame *frame){ IUvcStreamChannel::IUvcStreamChannel(const UvcDeviceInfo& devInfo) : devInfo_(devInfo), + xuUnit_(IS_OBSENSOR_GEMINI330_PID(devInfo.pid) ? OBSENSOR_G330_XU_UNIT : OBSENSOR_COMMON_XU_UNIT), streamType_(parseUvcDeviceNameToStreamType(devInfo_.name)) { @@ -286,6 +291,11 @@ bool IUvcStreamChannel::setProperty(int propId, const uint8_t* /*data*/, uint32_ rst &= getXu(2, &rcvData, &rcvLen); rst &= setXu(2, OB_EXT_CMD6, sizeof(OB_EXT_CMD6)); rst &= getXu(2, &rcvData, &rcvLen); + }else if(IS_OBSENSOR_GEMINI330_PID(devInfo_.pid)) { + rst &= setXu(2, OB_EXT_CMD6, sizeof(OB_EXT_CMD6)); + rst &= getXu(2, &rcvData, &rcvLen); + rst &= setXu(2, OB_EXT_CMD14, sizeof(OB_EXT_CMD14)); + rst &= getXu(2, &rcvData, &rcvLen); }else{ rst &= setXu(2, OB_EXT_CMD0, sizeof(OB_EXT_CMD0)); rst &= getXu(2, &rcvData, &rcvLen); @@ -400,6 +410,42 @@ bool IUvcStreamChannel::getProperty(int propId, uint8_t* recvData, uint32_t* rec *recvDataSize = sizeof(CameraParam); memcpy(recvData, ¶m, *recvDataSize); } + else if(IS_OBSENSOR_GEMINI330_SHORT_PID(devInfo_.pid)){ + // return default param + CameraParam param; + param.p0[0] = 460.656f; + param.p0[1] = 460.782f; + param.p0[2] = 320.985f; + param.p0[3] = 233.921f; + param.p1[0] = 460.656f; + param.p1[1] = 460.782f; + param.p1[2] = 320.985f; + param.p1[3] = 233.921f; + param.p6[0] = 640; + param.p6[1] = 480; + param.p7[0] = 640; + param.p7[1] = 480; + *recvDataSize = sizeof(CameraParam); + memcpy(recvData, ¶m, *recvDataSize); + } + else if(IS_OBSENSOR_GEMINI330_LONG_PID(devInfo_.pid)){ + // return default param + CameraParam param; + param.p0[0] = 366.751f; + param.p0[1] = 365.782f; + param.p0[2] = 319.893f; + param.p0[3] = 243.415f; + param.p1[0] = 366.751f; + param.p1[1] = 365.782f; + param.p1[2] = 319.893f; + param.p1[3] = 243.415f; + param.p6[0] = 640; + param.p6[1] = 480; + param.p7[0] = 640; + param.p7[1] = 480; + *recvDataSize = sizeof(CameraParam); + memcpy(recvData, ¶m, *recvDataSize); + } else{ rst &= setXu(2, OB_EXT_CMD5, sizeof(OB_EXT_CMD5)); rst &= getXu(2, &rcvData, &rcvLen); @@ -453,7 +499,15 @@ bool IUvcStreamChannel::initDepthFrameProcessor() setXu(2, OB_EXT_CMD13, sizeof(OB_EXT_CMD13)); getXu(2, &rcvData, &rcvLen); + return true; + } + else if(IS_OBSENSOR_GEMINI330_PID(devInfo_.pid)) + { + uint8_t* rcvData; + uint32_t rcvLen; + setXu(2, OB_EXT_CMD7, sizeof(OB_EXT_CMD7)); + getXu(2, &rcvData, &rcvLen); return true; } else if(streamType_ == OBSENSOR_STREAM_DEPTH && setXu(2, OB_EXT_CMD4, sizeof(OB_EXT_CMD4))) diff --git a/modules/videoio/src/cap_obsensor/obsensor_uvc_stream_channel.hpp b/modules/videoio/src/cap_obsensor/obsensor_uvc_stream_channel.hpp index caff38efea..d967f928d8 100644 --- a/modules/videoio/src/cap_obsensor/obsensor_uvc_stream_channel.hpp +++ b/modules/videoio/src/cap_obsensor/obsensor_uvc_stream_channel.hpp @@ -27,7 +27,8 @@ namespace cv { namespace obsensor { #define XU_MAX_DATA_LENGTH 1024 -#define XU_UNIT_ID 4 +#define XU_UNIT_ID_COMMON 4 +#define XU_UNIT_ID_G330 3 struct UvcDeviceInfo { @@ -46,6 +47,16 @@ enum StreamState STREAM_STARTED = 2, STREAM_STOPPING = 3, }; +struct Guid { + uint32_t data1; + uint16_t data2, data3; + uint8_t data4[8]; +}; + +struct ObExtensionUnit { + uint8_t unit; + Guid id; +}; StreamType parseUvcDeviceNameToStreamType(const std::string& devName); FrameFormat frameFourccToFormat(uint32_t fourcc); @@ -104,6 +115,7 @@ protected: protected: const UvcDeviceInfo devInfo_; + const ObExtensionUnit xuUnit_; StreamType streamType_; Ptr depthFrameProcessor_; }; diff --git a/modules/videoio/src/cap_obsensor_capture.cpp b/modules/videoio/src/cap_obsensor_capture.cpp index 4c64faee11..10e457f7fd 100644 --- a/modules/videoio/src/cap_obsensor_capture.cpp +++ b/modules/videoio/src/cap_obsensor_capture.cpp @@ -34,16 +34,16 @@ Ptr create_obsensor_capture(int index) VideoCapture_obsensor::VideoCapture_obsensor(int index) : isOpened_(false) { static const obsensor::StreamProfile colorProfile = { 640, 480, 30, obsensor::FRAME_FORMAT_MJPG }; - static const obsensor::StreamProfile depthProfile = {640, 480, 30, obsensor::FRAME_FORMAT_Y16}; - static const obsensor::StreamProfile gemini2DepthProfile = {1280, 800, 30, obsensor::FRAME_FORMAT_Y16}; - static const obsensor::StreamProfile astra2ColorProfile = {800, 600, 30, obsensor::FRAME_FORMAT_MJPG}; - static const obsensor::StreamProfile astra2DepthProfile = {800, 600, 30, obsensor::FRAME_FORMAT_Y14}; - static const obsensor::StreamProfile megaColorProfile = {1280, 720, 30, obsensor::FRAME_FORMAT_MJPG}; - static const obsensor::StreamProfile megaDepthProfile = {640, 576, 30, obsensor::FRAME_FORMAT_Y16}; - static const obsensor::StreamProfile gemini2lColorProfile = { 1280, 720, 30, obsensor::FRAME_FORMAT_MJPG}; - static const obsensor::StreamProfile gemini2lDepthProfile = {1280, 800, 30, obsensor::FRAME_FORMAT_Y16}; - static const obsensor::StreamProfile gemini2XlColorProfile = { 1280, 800, 10, obsensor::FRAME_FORMAT_MJPG}; - static const obsensor::StreamProfile gemini2XlDepthProfile = {1280, 800, 10, obsensor::FRAME_FORMAT_Y16}; + static const obsensor::StreamProfile depthProfile = { 640, 480, 30, obsensor::FRAME_FORMAT_Y16 }; + static const obsensor::StreamProfile gemini2DepthProfile = { 1280, 800, 30, obsensor::FRAME_FORMAT_Y16 }; + static const obsensor::StreamProfile astra2ColorProfile = { 800, 600, 30, obsensor::FRAME_FORMAT_MJPG }; + static const obsensor::StreamProfile astra2DepthProfile = { 800, 600, 30, obsensor::FRAME_FORMAT_Y14 }; + static const obsensor::StreamProfile megaColorProfile = { 1280, 720, 30, obsensor::FRAME_FORMAT_MJPG }; + static const obsensor::StreamProfile megaDepthProfile = { 640, 576, 30, obsensor::FRAME_FORMAT_Y16 }; + static const obsensor::StreamProfile gemini2lColorProfile = { 1280, 720, 30, obsensor::FRAME_FORMAT_MJPG }; + static const obsensor::StreamProfile gemini2lDepthProfile = { 1280, 800, 30, obsensor::FRAME_FORMAT_Y16 }; + static const obsensor::StreamProfile gemini2XlColorProfile = { 1280, 800, 10, obsensor::FRAME_FORMAT_MJPG }; + static const obsensor::StreamProfile gemini2XlDepthProfile = { 1280, 800, 10, obsensor::FRAME_FORMAT_Y16 }; streamChannelGroup_ = obsensor::getStreamChannelGroup(index); if (!streamChannelGroup_.empty()) @@ -80,11 +80,9 @@ VideoCapture_obsensor::VideoCapture_obsensor(int index) : isOpened_(false) obsensor::StreamProfile profile = depthProfile; if(OBSENSOR_GEMINI2_PID == channel->getPid()){ profile = gemini2DepthProfile; - } - else if(OBSENSOR_ASTRA2_PID == channel->getPid()){ + }else if(OBSENSOR_ASTRA2_PID == channel->getPid()){ profile = astra2DepthProfile; - } - else if(OBSENSOR_FEMTO_MEGA_PID == channel->getPid()){ + }else if(OBSENSOR_FEMTO_MEGA_PID == channel->getPid()){ profile = megaDepthProfile; }else if(OBSENSOR_GEMINI2L_PID == channel->getPid()){ profile = gemini2lDepthProfile; @@ -164,6 +162,11 @@ bool VideoCapture_obsensor::retrieveFrame(int outputType, OutputArray frame) grabbedDepthFrame_(rect).copyTo(frame); }else if(OBSENSOR_GEMINI2XL_PID == streamChannelGroup_.front()->getPid()){ grabbedDepthFrame_.copyTo(frame); + }else if(IS_OBSENSOR_GEMINI330_PID(streamChannelGroup_.front()->getPid())){ + const double DepthValueScaleG300 = 1.0; + grabbedDepthFrame_ = grabbedDepthFrame_*DepthValueScaleG300; + Rect rect(0, 0, 640, 480); + grabbedDepthFrame_(rect).copyTo(frame); }else{ grabbedDepthFrame_.copyTo(frame); } From edccfa7961c61adebe2f8c0bb9c934ec783c170a Mon Sep 17 00:00:00 2001 From: adsha-quic Date: Fri, 25 Apr 2025 13:37:26 +0530 Subject: [PATCH 88/94] Merge pull request #27184 from CodeLinaro:gemm_fastcv_hal FastCV gemm hal #27184 FastCV hal for gemm 32f ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [ ] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- 3rdparty/fastcv/include/fastcv_hal_core.hpp | 19 +++- 3rdparty/fastcv/src/fastcv_hal_core.cpp | 115 ++++++++++++++++++++ modules/core/test/test_operations.cpp | 2 +- 3 files changed, 134 insertions(+), 2 deletions(-) diff --git a/3rdparty/fastcv/include/fastcv_hal_core.hpp b/3rdparty/fastcv/include/fastcv_hal_core.hpp index c950ac7959..8c9970bedd 100644 --- a/3rdparty/fastcv/include/fastcv_hal_core.hpp +++ b/3rdparty/fastcv/include/fastcv_hal_core.hpp @@ -34,7 +34,8 @@ #define cv_hal_mul32f fastcv_hal_mul32f #undef cv_hal_SVD32f #define cv_hal_SVD32f fastcv_hal_SVD32f - +#undef cv_hal_gemm32f +#define cv_hal_gemm32f fastcv_hal_gemm32f //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// @brief look-up table transform of an array. @@ -250,4 +251,20 @@ int fastcv_hal_SVD32f( int n, int flags); +int fastcv_hal_gemm32f( + const float* src1, + size_t src1_step, + const float* src2, + size_t src2_step, + float alpha, + const float* src3, + size_t src3_step, + float beta, + float* dst, + size_t dst_step, + int m, + int n, + int k, + int flags); + #endif diff --git a/3rdparty/fastcv/src/fastcv_hal_core.cpp b/3rdparty/fastcv/src/fastcv_hal_core.cpp index 0b35f2f91a..bc7459bcf4 100644 --- a/3rdparty/fastcv/src/fastcv_hal_core.cpp +++ b/3rdparty/fastcv/src/fastcv_hal_core.cpp @@ -623,4 +623,119 @@ int fastcv_hal_SVD32f( } CV_HAL_RETURN(status, fastcv_hal_SVD32f); +} + +int fastcv_hal_gemm32f( + const float* src1, + size_t src1_step, + const float* src2, + size_t src2_step, + float alpha, + const float* src3, + size_t src3_step, + float beta, + float* dst, + size_t dst_step, + int m, + int n, + int k, + int flags) +{ + cv::Mat src1_t, src2_t, src3_t, dst_temp1; + int height_a = m, width_a = n, width_d = k; + const float *src1p = src1, *src2p = src2, *src3p = src3; + + INITIALIZATION_CHECK; + + if((flags & (cv::GEMM_1_T)) && (flags & (cv::GEMM_2_T))) + { + height_a = n; width_a = m; + } + else if(flags & (cv::GEMM_1_T)) + { + src1_t = cv::Mat(width_a, height_a, CV_32FC1); + fcvTransposef32_v2(src1, width_a, height_a, src1_step, src1_t.ptr(), src1_t.step[0]); + src1p = src1_t.ptr(); + src1_step = src1_t.step[0]; + height_a = n; width_a = m; + } + else if(flags & (cv::GEMM_2_T)) + { + src2_t = cv::Mat(width_a, width_d, CV_32FC1); + fcvTransposef32_v2(src2, width_a, width_d, src2_step, src2_t.ptr(), src2_t.step[0]); + src2p = src2_t.ptr(); + src2_step = src2_t.step[0]; + } + + if((flags & cv::GEMM_3_T) && beta != 0.0 && src3 != NULL) + { + src3_t = cv::Mat(height_a, width_d, CV_32FC1); + fcvTransposef32_v2(src3, height_a, width_d, src3_step, src3_t.ptr(), src3_t.step[0]); + src3p = src3_t.ptr(); + src3_step = src3_t.step[0]; + } + + bool inplace = false; + size_t dst_stride; + float *dstp = NULL; + + if(src1 == dst || src2 == dst || src3 == dst) + { + dst_temp1 = cv::Mat(height_a, width_d, CV_32FC1); + dstp = dst_temp1.ptr(); + inplace = true; + dst_stride = dst_temp1.step[0]; + } + else + { + dstp = dst; + dst_stride = dst_step; + } + + float *dstp1 = dstp; + + fcvStatus status = FASTCV_SUCCESS; + + if(alpha != 0.0) + { + if((flags & (cv::GEMM_1_T)) && (flags & (cv::GEMM_2_T))) + { + cv::Mat dst_temp2 = cv::Mat(k, n, CV_32FC1); + fcvMatrixMultiplyf32_v2(src2p, m, k, src2_step, src1p, n, src1_step, + dst_temp2.ptr(), dst_temp2.step[0]); + fcvTransposef32_v2(dst_temp2.ptr(), n, k, dst_temp2.step[0], dstp, dst_stride); + + } + else + { + status = fcvMatrixMultiplyf32_v2(src1p, width_a, height_a, src1_step, src2p, width_d, + src2_step, dstp, dst_stride); + } + } + + if(alpha != 1.0 && alpha != 0.0 && status == FASTCV_SUCCESS) + { + status = fcvMultiplyScalarf32(dstp, width_d, height_a, dst_stride, alpha, dstp1, dst_stride); + } + + if(src3 != NULL && beta != 0.0 && status == FASTCV_SUCCESS) + { + cv::Mat dst3 = cv::Mat(height_a, width_d, CV_32FC1); + if(beta != 1.0) + { + status = fcvMultiplyScalarf32(src3p, width_d, height_a, src3_step, beta, (float32_t*)dst3.data, dst3.step); + if(status == FASTCV_SUCCESS) + fcvAddf32_v2(dstp, width_d, height_a, dst_stride, (float32_t*)dst3.data, dst3.step, dstp1, dst_stride); + } + else + fcvAddf32_v2(dstp, width_d, height_a, dst_stride, src3p, src3_step, dstp1, dst_stride); + } + + if(inplace) + { + cv::Mat dst_mat = cv::Mat(height_a, width_d, CV_32FC1, (void*)dst, dst_step); + dst_temp1.copyTo(dst_mat); + } + + CV_HAL_RETURN(status,hal_gemm32f); } \ No newline at end of file diff --git a/modules/core/test/test_operations.cpp b/modules/core/test/test_operations.cpp index 446f715d3e..9dfc030b2e 100644 --- a/modules/core/test/test_operations.cpp +++ b/modules/core/test/test_operations.cpp @@ -1214,7 +1214,7 @@ bool CV_OperationsTest::TestSVD() cvtest::norm(Vt*Vt.t(), I, CV_C) > FLT_EPSILON || W.at(2) < 0 || W.at(1) < W.at(2) || W.at(0) < W.at(1) || - cvtest::norm(U*Mat::diag(W)*Vt, Q, CV_C) > FLT_EPSILON ) + cvtest::norm(U*Mat::diag(W)*Vt, Q, CV_C) > FLT_EPSILON*2 ) throw test_excep(); } catch(const test_excep&) From 86a963cec9149fdf574ee25ea78eba7fe16cb6fa Mon Sep 17 00:00:00 2001 From: Kumataro Date: Fri, 25 Apr 2025 17:29:22 +0900 Subject: [PATCH 89/94] Merge pull request #27226 from Kumataro:fix27225 imgproc: cvtColor: remove to copy edge pixels for COLOR_Bayer*_VNGs. #27226 Close https://github.com/opencv/opencv/issues/27225 Close https://github.com/opencv/opencv/issues/5089 Related https://github.com/opencv/opencv_extra/pull/1249 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- modules/imgproc/src/demosaicing.cpp | 48 ++++++++++-------------- modules/imgproc/test/test_color.cpp | 58 +++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 29 deletions(-) diff --git a/modules/imgproc/src/demosaicing.cpp b/modules/imgproc/src/demosaicing.cpp index 65faf057c9..b268df753c 100644 --- a/modules/imgproc/src/demosaicing.cpp +++ b/modules/imgproc/src/demosaicing.cpp @@ -1041,9 +1041,11 @@ static void Bayer2RGB_( const Mat& srcmat, Mat& dstmat, int code ) int dst_step = (int)(dstmat.step/sizeof(T)); Size size = srcmat.size(); int blue = (code == COLOR_BayerBG2BGR || code == COLOR_BayerGB2BGR || - code == COLOR_BayerBG2BGRA || code == COLOR_BayerGB2BGRA ) ? -1 : 1; + code == COLOR_BayerBG2BGRA || code == COLOR_BayerGB2BGRA || + code == COLOR_BayerBG2BGR_VNG || code == COLOR_BayerGB2BGR_VNG) ? -1 : 1; int start_with_green = (code == COLOR_BayerGB2BGR || code == COLOR_BayerGR2BGR || - code == COLOR_BayerGB2BGRA || code == COLOR_BayerGR2BGRA); + code == COLOR_BayerGB2BGRA || code == COLOR_BayerGR2BGRA || + code == COLOR_BayerGB2BGR_VNG || code == COLOR_BayerGR2BGR_VNG); int dcn = dstmat.channels(); size.height -= 2; @@ -1073,8 +1075,20 @@ static void Bayer2RGB_( const Mat& srcmat, Mat& dstmat, int code ) /////////////////// Demosaicing using Variable Number of Gradients /////////////////////// -static void Bayer2RGB_VNG_8u( const Mat& srcmat, Mat& dstmat, int code ) +static void Bayer2RGB_VNG_8u( const Mat& _srcmat, Mat& dstmat, int code ) { + // for too small images use the simple interpolation algorithm + if( MIN(_srcmat.size().width, _srcmat.size().height) < 8 ) + { + Bayer2RGB_( _srcmat, dstmat, code ); + return; + } + + // VNG uses a 5x5 filter to calculate the gradient around the target pixel. + // To make it simple for edge pixels, 2 pixel paddings are added using reflection. + cv::Mat srcmat; + copyMakeBorder(_srcmat, srcmat, 2, 2, 2, 2, BORDER_REFLECT_101); + const uchar* bayer = srcmat.ptr(); int bstep = (int)srcmat.step; uchar* dst = dstmat.ptr(); @@ -1084,24 +1098,15 @@ static void Bayer2RGB_VNG_8u( const Mat& srcmat, Mat& dstmat, int code ) int blueIdx = code == COLOR_BayerBG2BGR_VNG || code == COLOR_BayerGB2BGR_VNG ? 0 : 2; bool greenCell0 = code != COLOR_BayerBG2BGR_VNG && code != COLOR_BayerRG2BGR_VNG; - // for too small images use the simple interpolation algorithm - if( MIN(size.width, size.height) < 8 ) - { - Bayer2RGB_( srcmat, dstmat, code ); - return; - } - const int brows = 3, bcn = 7; int N = size.width, N2 = N*2, N3 = N*3, N4 = N*4, N5 = N*5, N6 = N*6, N7 = N*7; int i, bufstep = N7*bcn; cv::AutoBuffer _buf(bufstep*brows); ushort* buf = _buf.data(); - bayer += bstep*2; - - for( int y = 2; y < size.height - 4; y++ ) + for( int y = 2; y < size.height - 2; y++ ) { - uchar* dstrow = dst + dststep*y + 6; + uchar* dstrow = dst + dststep*(y - 2); // srcmat has 2 pixel paddings, but dstmat has no padding. const uchar* srow; for( int dy = (y == 2 ? -1 : 1); dy <= 1; dy++ ) @@ -1583,24 +1588,9 @@ static void Bayer2RGB_VNG_8u( const Mat& srcmat, Mat& dstmat, int code ) } while( i < N - 2 ); - for( i = 0; i < 6; i++ ) - { - dst[dststep*y + 5 - i] = dst[dststep*y + 8 - i]; - dst[dststep*y + (N - 2)*3 + i] = dst[dststep*y + (N - 3)*3 + i]; - } - greenCell0 = !greenCell0; blueIdx ^= 2; } - - for( i = 0; i < size.width*3; i++ ) - { - dst[i] = dst[i + dststep] = dst[i + dststep*2]; - dst[i + dststep*(size.height-4)] = - dst[i + dststep*(size.height-3)] = - dst[i + dststep*(size.height-2)] = - dst[i + dststep*(size.height-1)] = dst[i + dststep*(size.height-5)]; - } } //////////////////////////////// Edge-Aware Demosaicing ////////////////////////////////// diff --git a/modules/imgproc/test/test_color.cpp b/modules/imgproc/test/test_color.cpp index d97c0ab706..b843448035 100644 --- a/modules/imgproc/test/test_color.cpp +++ b/modules/imgproc/test/test_color.cpp @@ -1905,6 +1905,64 @@ TEST(Imgproc_ColorBayerVNG, regression) } } +// See https://github.com/opencv/opencv/issues/5089 +// See https://github.com/opencv/opencv/issues/27225 +typedef tuple VNGandINT; +typedef testing::TestWithParam Imgproc_ColorBayerVNG_Codes; + +TEST_P(Imgproc_ColorBayerVNG_Codes, regression27225) +{ + const cv::ColorConversionCodes codeVNG = get<0>(GetParam()); + const int margin = (codeVNG == cv::COLOR_BayerGB2BGR_VNG || codeVNG == cv::COLOR_BayerGR2BGR_VNG)? 5 : 4; + + cv::Mat in = cv::Mat::eye(16, 16, CV_8UC1) * 255; + cv::resize(in, in, {}, 2, 2, cv::INTER_NEAREST); + + cv::Mat out; + EXPECT_NO_THROW(cv::cvtColor(in, out, codeVNG)); + + for(int iy=0; iy < out.size().height; iy++) { + for(int ix=0; ix < out.size().width; ix++) { + // Avoid to test around main diagonal pixels. + if(cv::abs(ix - iy) < margin) { + continue; + } + // Others should be completely black. + const Vec3b pixel = out.at(iy, ix); + EXPECT_EQ(pixel[0], 0) << cv::format(" - iy = %d, ix = %d", iy, ix); + EXPECT_EQ(pixel[1], 0) << cv::format(" - iy = %d, ix = %d", iy, ix); + EXPECT_EQ(pixel[2], 0) << cv::format(" - iy = %d, ix = %d", iy, ix); + } + } +} + +TEST_P(Imgproc_ColorBayerVNG_Codes, regression27225_small) +{ + // for too small images use the simple interpolation algorithm + const cv::ColorConversionCodes codeVNG = get<0>(GetParam()); + const cv::ColorConversionCodes codeINT = get<1>(GetParam()); + cv::Mat in = cv::Mat::eye(7, 7, CV_8UC1) * 255; + + cv::Mat outVNG; + EXPECT_NO_THROW(cv::cvtColor(in, outVNG, codeVNG)); + cv::Mat outINT; + EXPECT_NO_THROW(cv::cvtColor(in, outINT, codeINT)); + + Mat diff; + absdiff(outVNG, outINT, diff); + + imwrite("outVNG.png", outVNG); + imwrite("outINT.png", outINT); + EXPECT_EQ(0, countNonZero(diff.reshape(1) > 1)); +} + +INSTANTIATE_TEST_CASE_P(/**/, Imgproc_ColorBayerVNG_Codes, + testing::Values( + make_tuple(cv::COLOR_BayerBG2BGR_VNG, cv::COLOR_BayerBG2BGR), + make_tuple(cv::COLOR_BayerGB2BGR_VNG, cv::COLOR_BayerGB2BGR), + make_tuple(cv::COLOR_BayerRG2BGR_VNG, cv::COLOR_BayerRG2BGR), + make_tuple(cv::COLOR_BayerGR2BGR_VNG, cv::COLOR_BayerGR2BGR))); + // creating Bayer pattern template static void calculateBayerPattern(const Mat& src, Mat& bayer, const char* pattern) From 19c4d9763828239ca92618b03407eb86cf203f25 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov <2536374+asmorkalov@users.noreply.github.com> Date: Fri, 25 Apr 2025 14:56:42 +0300 Subject: [PATCH 90/94] Merge pull request #27252 from asmorkalov:as/extract_hal Extract all HALs from 3rdparty to dedicated folder. #27252 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [ ] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- CMakeLists.txt | 14 +++--- cmake/OpenCVFindLibsPerf.cmake | 2 +- {3rdparty => hal}/carotene/.gitignore | 0 {3rdparty => hal}/carotene/CMakeLists.txt | 0 {3rdparty => hal}/carotene/README.md | 0 {3rdparty => hal}/carotene/hal/CMakeLists.txt | 0 {3rdparty => hal}/carotene/hal/dummy.cpp | 0 {3rdparty => hal}/carotene/hal/tegra_hal.hpp | 0 .../carotene/include/carotene/definitions.hpp | 0 .../carotene/include/carotene/functions.hpp | 4 +- .../carotene/include/carotene/types.hpp | 0 {3rdparty => hal}/carotene/src/absdiff.cpp | 0 {3rdparty => hal}/carotene/src/accumulate.cpp | 0 {3rdparty => hal}/carotene/src/add.cpp | 0 .../carotene/src/add_weighted.cpp | 0 {3rdparty => hal}/carotene/src/bitwise.cpp | 0 {3rdparty => hal}/carotene/src/blur.cpp | 0 {3rdparty => hal}/carotene/src/canny.cpp | 0 .../carotene/src/channel_extract.cpp | 2 +- .../carotene/src/channels_combine.cpp | 0 {3rdparty => hal}/carotene/src/cmp.cpp | 0 .../carotene/src/colorconvert.cpp | 0 {3rdparty => hal}/carotene/src/common.cpp | 0 {3rdparty => hal}/carotene/src/common.hpp | 0 {3rdparty => hal}/carotene/src/convert.cpp | 0 .../carotene/src/convert_depth.cpp | 0 .../carotene/src/convert_scale.cpp | 0 .../carotene/src/convolution.cpp | 0 .../carotene/src/count_nonzero.cpp | 0 {3rdparty => hal}/carotene/src/div.cpp | 0 .../carotene/src/dot_product.cpp | 0 {3rdparty => hal}/carotene/src/dummy.cpp | 0 {3rdparty => hal}/carotene/src/fast.cpp | 0 .../carotene/src/fill_minmaxloc.cpp | 0 {3rdparty => hal}/carotene/src/flip.cpp | 0 .../carotene/src/gaussian_blur.cpp | 0 {3rdparty => hal}/carotene/src/in_range.cpp | 0 {3rdparty => hal}/carotene/src/integral.cpp | 0 {3rdparty => hal}/carotene/src/intrinsics.hpp | 0 {3rdparty => hal}/carotene/src/laplacian.cpp | 0 {3rdparty => hal}/carotene/src/magnitude.cpp | 0 {3rdparty => hal}/carotene/src/meanstddev.cpp | 0 .../carotene/src/median_filter.cpp | 0 {3rdparty => hal}/carotene/src/min_max.cpp | 0 {3rdparty => hal}/carotene/src/minmaxloc.cpp | 0 {3rdparty => hal}/carotene/src/morph.cpp | 0 {3rdparty => hal}/carotene/src/mul.cpp | 0 {3rdparty => hal}/carotene/src/norm.cpp | 0 .../carotene/src/opticalflow.cpp | 0 {3rdparty => hal}/carotene/src/phase.cpp | 0 {3rdparty => hal}/carotene/src/pyramid.cpp | 0 {3rdparty => hal}/carotene/src/reduce.cpp | 0 {3rdparty => hal}/carotene/src/remap.cpp | 0 {3rdparty => hal}/carotene/src/remap.hpp | 0 {3rdparty => hal}/carotene/src/resize.cpp | 0 .../carotene/src/saturate_cast.hpp | 0 {3rdparty => hal}/carotene/src/scharr.cpp | 0 .../carotene/src/separable_filter.cpp | 0 .../carotene/src/separable_filter.hpp | 0 {3rdparty => hal}/carotene/src/sobel.cpp | 0 {3rdparty => hal}/carotene/src/sub.cpp | 0 {3rdparty => hal}/carotene/src/sum.cpp | 0 .../carotene/src/template_matching.cpp | 0 {3rdparty => hal}/carotene/src/threshold.cpp | 0 .../carotene/src/vround_helper.hpp | 0 {3rdparty => hal}/carotene/src/vtransform.hpp | 0 .../carotene/src/warp_affine.cpp | 0 .../carotene/src/warp_perspective.cpp | 0 {3rdparty => hal}/fastcv/CMakeLists.txt | 0 .../fastcv/include/fastcv_hal_core.hpp | 0 .../fastcv/include/fastcv_hal_imgproc.hpp | 0 .../fastcv/include/fastcv_hal_utils.hpp | 0 .../fastcv/src/fastcv_hal_core.cpp | 3 +- .../fastcv/src/fastcv_hal_imgproc.cpp | 4 +- .../fastcv/src/fastcv_hal_utils.cpp | 0 {3rdparty/ipphal => hal/ipp}/CMakeLists.txt | 0 .../ipp}/include/ipp_hal_core.hpp | 0 .../ipphal => hal/ipp}/include/ipp_utils.hpp | 0 .../ipphal => hal/ipp}/src/cart_polar_ipp.cpp | 0 {3rdparty/ipphal => hal/ipp}/src/mean_ipp.cpp | 0 .../ipphal => hal/ipp}/src/minmax_ipp.cpp | 0 {3rdparty/ipphal => hal/ipp}/src/norm_ipp.cpp | 0 .../ipphal => hal/ipp}/src/transforms_ipp.cpp | 0 {3rdparty => hal}/kleidicv/CMakeLists.txt | 0 {3rdparty => hal}/kleidicv/kleidicv.cmake | 0 {3rdparty => hal}/ndsrvp/CMakeLists.txt | 0 {3rdparty => hal}/ndsrvp/include/core.hpp | 0 .../ndsrvp/include/features2d.hpp | 2 +- {3rdparty => hal}/ndsrvp/include/imgproc.hpp | 0 {3rdparty => hal}/ndsrvp/ndsrvp_hal.hpp | 0 .../ndsrvp/src/bilateralFilter.cpp | 0 {3rdparty => hal}/ndsrvp/src/cvutils.cpp | 2 +- {3rdparty => hal}/ndsrvp/src/cvutils.hpp | 2 +- {3rdparty => hal}/ndsrvp/src/filter.cpp | 0 {3rdparty => hal}/ndsrvp/src/integral.cpp | 4 +- {3rdparty => hal}/ndsrvp/src/medianBlur.cpp | 2 +- {3rdparty => hal}/ndsrvp/src/remap.cpp | 0 {3rdparty => hal}/ndsrvp/src/threshold.cpp | 6 +-- {3rdparty => hal}/ndsrvp/src/warpAffine.cpp | 2 +- .../ndsrvp/src/warpPerspective.cpp | 2 +- {3rdparty => hal}/openvx/CMakeLists.txt | 0 {3rdparty => hal}/openvx/README.md | 46 +++++++++---------- {3rdparty => hal}/openvx/hal/CMakeLists.txt | 0 {3rdparty => hal}/openvx/hal/README.md | 0 {3rdparty => hal}/openvx/hal/openvx_hal.cpp | 0 {3rdparty => hal}/openvx/hal/openvx_hal.hpp | 0 {3rdparty => hal}/openvx/include/ivx.hpp | 0 .../openvx/include/ivx_lib_debug.hpp | 0 .../hal_rvv => hal/riscv-rvv}/CMakeLists.txt | 0 .../hal_rvv => hal/riscv-rvv}/hal_rvv.hpp | 0 .../riscv-rvv}/hal_rvv_1p0/atan.hpp | 0 .../riscv-rvv}/hal_rvv_1p0/cart_to_polar.hpp | 0 .../riscv-rvv}/hal_rvv_1p0/cholesky.hpp | 0 .../riscv-rvv}/hal_rvv_1p0/color.hpp | 0 .../riscv-rvv}/hal_rvv_1p0/common.hpp | 0 .../riscv-rvv}/hal_rvv_1p0/compare.hpp | 0 .../riscv-rvv}/hal_rvv_1p0/convert_scale.hpp | 0 .../riscv-rvv}/hal_rvv_1p0/copy_mask.hpp | 0 .../riscv-rvv}/hal_rvv_1p0/div.hpp | 0 .../riscv-rvv}/hal_rvv_1p0/dotprod.hpp | 1 - .../riscv-rvv}/hal_rvv_1p0/dxt.hpp | 0 .../riscv-rvv}/hal_rvv_1p0/exp.hpp | 0 .../riscv-rvv}/hal_rvv_1p0/filter.hpp | 0 .../riscv-rvv}/hal_rvv_1p0/flip.hpp | 0 .../riscv-rvv}/hal_rvv_1p0/histogram.hpp | 0 .../riscv-rvv}/hal_rvv_1p0/integral.hpp | 0 .../riscv-rvv}/hal_rvv_1p0/log.hpp | 0 .../riscv-rvv}/hal_rvv_1p0/lu.hpp | 0 .../riscv-rvv}/hal_rvv_1p0/lut.hpp | 0 .../riscv-rvv}/hal_rvv_1p0/magnitude.hpp | 0 .../riscv-rvv}/hal_rvv_1p0/mean.hpp | 0 .../riscv-rvv}/hal_rvv_1p0/merge.hpp | 0 .../riscv-rvv}/hal_rvv_1p0/minmax.hpp | 0 .../riscv-rvv}/hal_rvv_1p0/moments.hpp | 0 .../riscv-rvv}/hal_rvv_1p0/norm.hpp | 0 .../riscv-rvv}/hal_rvv_1p0/norm_diff.hpp | 0 .../riscv-rvv}/hal_rvv_1p0/norm_hamming.hpp | 0 .../riscv-rvv}/hal_rvv_1p0/polar_to_cart.hpp | 0 .../riscv-rvv}/hal_rvv_1p0/pyramids.hpp | 0 .../riscv-rvv}/hal_rvv_1p0/qr.hpp | 0 .../riscv-rvv}/hal_rvv_1p0/resize.hpp | 0 .../riscv-rvv}/hal_rvv_1p0/sincos.hpp | 0 .../riscv-rvv}/hal_rvv_1p0/split.hpp | 0 .../riscv-rvv}/hal_rvv_1p0/sqrt.hpp | 0 .../riscv-rvv}/hal_rvv_1p0/svd.hpp | 2 +- .../riscv-rvv}/hal_rvv_1p0/thresh.hpp | 8 ++-- .../riscv-rvv}/hal_rvv_1p0/transpose.hpp | 0 .../riscv-rvv}/hal_rvv_1p0/types.hpp | 0 .../riscv-rvv}/hal_rvv_1p0/warp.hpp | 2 +- .../riscv-rvv}/version/hal_rvv_071.hpp | 0 150 files changed, 54 insertions(+), 56 deletions(-) rename {3rdparty => hal}/carotene/.gitignore (100%) rename {3rdparty => hal}/carotene/CMakeLists.txt (100%) rename {3rdparty => hal}/carotene/README.md (100%) rename {3rdparty => hal}/carotene/hal/CMakeLists.txt (100%) rename {3rdparty => hal}/carotene/hal/dummy.cpp (100%) rename {3rdparty => hal}/carotene/hal/tegra_hal.hpp (100%) rename {3rdparty => hal}/carotene/include/carotene/definitions.hpp (100%) rename {3rdparty => hal}/carotene/include/carotene/functions.hpp (99%) rename {3rdparty => hal}/carotene/include/carotene/types.hpp (100%) rename {3rdparty => hal}/carotene/src/absdiff.cpp (100%) rename {3rdparty => hal}/carotene/src/accumulate.cpp (100%) rename {3rdparty => hal}/carotene/src/add.cpp (100%) rename {3rdparty => hal}/carotene/src/add_weighted.cpp (100%) rename {3rdparty => hal}/carotene/src/bitwise.cpp (100%) rename {3rdparty => hal}/carotene/src/blur.cpp (100%) rename {3rdparty => hal}/carotene/src/canny.cpp (100%) rename {3rdparty => hal}/carotene/src/channel_extract.cpp (99%) rename {3rdparty => hal}/carotene/src/channels_combine.cpp (100%) rename {3rdparty => hal}/carotene/src/cmp.cpp (100%) rename {3rdparty => hal}/carotene/src/colorconvert.cpp (100%) rename {3rdparty => hal}/carotene/src/common.cpp (100%) rename {3rdparty => hal}/carotene/src/common.hpp (100%) rename {3rdparty => hal}/carotene/src/convert.cpp (100%) rename {3rdparty => hal}/carotene/src/convert_depth.cpp (100%) rename {3rdparty => hal}/carotene/src/convert_scale.cpp (100%) rename {3rdparty => hal}/carotene/src/convolution.cpp (100%) rename {3rdparty => hal}/carotene/src/count_nonzero.cpp (100%) rename {3rdparty => hal}/carotene/src/div.cpp (100%) rename {3rdparty => hal}/carotene/src/dot_product.cpp (100%) rename {3rdparty => hal}/carotene/src/dummy.cpp (100%) rename {3rdparty => hal}/carotene/src/fast.cpp (100%) rename {3rdparty => hal}/carotene/src/fill_minmaxloc.cpp (100%) rename {3rdparty => hal}/carotene/src/flip.cpp (100%) rename {3rdparty => hal}/carotene/src/gaussian_blur.cpp (100%) rename {3rdparty => hal}/carotene/src/in_range.cpp (100%) rename {3rdparty => hal}/carotene/src/integral.cpp (100%) rename {3rdparty => hal}/carotene/src/intrinsics.hpp (100%) rename {3rdparty => hal}/carotene/src/laplacian.cpp (100%) rename {3rdparty => hal}/carotene/src/magnitude.cpp (100%) rename {3rdparty => hal}/carotene/src/meanstddev.cpp (100%) rename {3rdparty => hal}/carotene/src/median_filter.cpp (100%) rename {3rdparty => hal}/carotene/src/min_max.cpp (100%) rename {3rdparty => hal}/carotene/src/minmaxloc.cpp (100%) rename {3rdparty => hal}/carotene/src/morph.cpp (100%) rename {3rdparty => hal}/carotene/src/mul.cpp (100%) rename {3rdparty => hal}/carotene/src/norm.cpp (100%) rename {3rdparty => hal}/carotene/src/opticalflow.cpp (100%) rename {3rdparty => hal}/carotene/src/phase.cpp (100%) rename {3rdparty => hal}/carotene/src/pyramid.cpp (100%) rename {3rdparty => hal}/carotene/src/reduce.cpp (100%) rename {3rdparty => hal}/carotene/src/remap.cpp (100%) rename {3rdparty => hal}/carotene/src/remap.hpp (100%) rename {3rdparty => hal}/carotene/src/resize.cpp (100%) rename {3rdparty => hal}/carotene/src/saturate_cast.hpp (100%) rename {3rdparty => hal}/carotene/src/scharr.cpp (100%) rename {3rdparty => hal}/carotene/src/separable_filter.cpp (100%) rename {3rdparty => hal}/carotene/src/separable_filter.hpp (100%) rename {3rdparty => hal}/carotene/src/sobel.cpp (100%) rename {3rdparty => hal}/carotene/src/sub.cpp (100%) rename {3rdparty => hal}/carotene/src/sum.cpp (100%) rename {3rdparty => hal}/carotene/src/template_matching.cpp (100%) rename {3rdparty => hal}/carotene/src/threshold.cpp (100%) rename {3rdparty => hal}/carotene/src/vround_helper.hpp (100%) rename {3rdparty => hal}/carotene/src/vtransform.hpp (100%) rename {3rdparty => hal}/carotene/src/warp_affine.cpp (100%) rename {3rdparty => hal}/carotene/src/warp_perspective.cpp (100%) rename {3rdparty => hal}/fastcv/CMakeLists.txt (100%) rename {3rdparty => hal}/fastcv/include/fastcv_hal_core.hpp (100%) rename {3rdparty => hal}/fastcv/include/fastcv_hal_imgproc.hpp (100%) rename {3rdparty => hal}/fastcv/include/fastcv_hal_utils.hpp (100%) rename {3rdparty => hal}/fastcv/src/fastcv_hal_core.cpp (99%) rename {3rdparty => hal}/fastcv/src/fastcv_hal_imgproc.cpp (99%) rename {3rdparty => hal}/fastcv/src/fastcv_hal_utils.cpp (100%) rename {3rdparty/ipphal => hal/ipp}/CMakeLists.txt (100%) rename {3rdparty/ipphal => hal/ipp}/include/ipp_hal_core.hpp (100%) rename {3rdparty/ipphal => hal/ipp}/include/ipp_utils.hpp (100%) rename {3rdparty/ipphal => hal/ipp}/src/cart_polar_ipp.cpp (100%) rename {3rdparty/ipphal => hal/ipp}/src/mean_ipp.cpp (100%) rename {3rdparty/ipphal => hal/ipp}/src/minmax_ipp.cpp (100%) rename {3rdparty/ipphal => hal/ipp}/src/norm_ipp.cpp (100%) rename {3rdparty/ipphal => hal/ipp}/src/transforms_ipp.cpp (100%) rename {3rdparty => hal}/kleidicv/CMakeLists.txt (100%) rename {3rdparty => hal}/kleidicv/kleidicv.cmake (100%) rename {3rdparty => hal}/ndsrvp/CMakeLists.txt (100%) rename {3rdparty => hal}/ndsrvp/include/core.hpp (100%) rename {3rdparty => hal}/ndsrvp/include/features2d.hpp (76%) rename {3rdparty => hal}/ndsrvp/include/imgproc.hpp (100%) rename {3rdparty => hal}/ndsrvp/ndsrvp_hal.hpp (100%) rename {3rdparty => hal}/ndsrvp/src/bilateralFilter.cpp (100%) rename {3rdparty => hal}/ndsrvp/src/cvutils.cpp (98%) rename {3rdparty => hal}/ndsrvp/src/cvutils.hpp (99%) rename {3rdparty => hal}/ndsrvp/src/filter.cpp (100%) rename {3rdparty => hal}/ndsrvp/src/integral.cpp (99%) rename {3rdparty => hal}/ndsrvp/src/medianBlur.cpp (99%) rename {3rdparty => hal}/ndsrvp/src/remap.cpp (100%) rename {3rdparty => hal}/ndsrvp/src/threshold.cpp (96%) rename {3rdparty => hal}/ndsrvp/src/warpAffine.cpp (97%) rename {3rdparty => hal}/ndsrvp/src/warpPerspective.cpp (98%) rename {3rdparty => hal}/openvx/CMakeLists.txt (100%) rename {3rdparty => hal}/openvx/README.md (77%) rename {3rdparty => hal}/openvx/hal/CMakeLists.txt (100%) rename {3rdparty => hal}/openvx/hal/README.md (100%) rename {3rdparty => hal}/openvx/hal/openvx_hal.cpp (100%) rename {3rdparty => hal}/openvx/hal/openvx_hal.hpp (100%) rename {3rdparty => hal}/openvx/include/ivx.hpp (100%) rename {3rdparty => hal}/openvx/include/ivx_lib_debug.hpp (100%) rename {3rdparty/hal_rvv => hal/riscv-rvv}/CMakeLists.txt (100%) rename {3rdparty/hal_rvv => hal/riscv-rvv}/hal_rvv.hpp (100%) rename {3rdparty/hal_rvv => hal/riscv-rvv}/hal_rvv_1p0/atan.hpp (100%) rename {3rdparty/hal_rvv => hal/riscv-rvv}/hal_rvv_1p0/cart_to_polar.hpp (100%) rename {3rdparty/hal_rvv => hal/riscv-rvv}/hal_rvv_1p0/cholesky.hpp (100%) rename {3rdparty/hal_rvv => hal/riscv-rvv}/hal_rvv_1p0/color.hpp (100%) rename {3rdparty/hal_rvv => hal/riscv-rvv}/hal_rvv_1p0/common.hpp (100%) rename {3rdparty/hal_rvv => hal/riscv-rvv}/hal_rvv_1p0/compare.hpp (100%) rename {3rdparty/hal_rvv => hal/riscv-rvv}/hal_rvv_1p0/convert_scale.hpp (100%) rename {3rdparty/hal_rvv => hal/riscv-rvv}/hal_rvv_1p0/copy_mask.hpp (100%) rename {3rdparty/hal_rvv => hal/riscv-rvv}/hal_rvv_1p0/div.hpp (100%) rename {3rdparty/hal_rvv => hal/riscv-rvv}/hal_rvv_1p0/dotprod.hpp (99%) rename {3rdparty/hal_rvv => hal/riscv-rvv}/hal_rvv_1p0/dxt.hpp (100%) rename {3rdparty/hal_rvv => hal/riscv-rvv}/hal_rvv_1p0/exp.hpp (100%) rename {3rdparty/hal_rvv => hal/riscv-rvv}/hal_rvv_1p0/filter.hpp (100%) rename {3rdparty/hal_rvv => hal/riscv-rvv}/hal_rvv_1p0/flip.hpp (100%) rename {3rdparty/hal_rvv => hal/riscv-rvv}/hal_rvv_1p0/histogram.hpp (100%) rename {3rdparty/hal_rvv => hal/riscv-rvv}/hal_rvv_1p0/integral.hpp (100%) rename {3rdparty/hal_rvv => hal/riscv-rvv}/hal_rvv_1p0/log.hpp (100%) rename {3rdparty/hal_rvv => hal/riscv-rvv}/hal_rvv_1p0/lu.hpp (100%) rename {3rdparty/hal_rvv => hal/riscv-rvv}/hal_rvv_1p0/lut.hpp (100%) rename {3rdparty/hal_rvv => hal/riscv-rvv}/hal_rvv_1p0/magnitude.hpp (100%) rename {3rdparty/hal_rvv => hal/riscv-rvv}/hal_rvv_1p0/mean.hpp (100%) rename {3rdparty/hal_rvv => hal/riscv-rvv}/hal_rvv_1p0/merge.hpp (100%) rename {3rdparty/hal_rvv => hal/riscv-rvv}/hal_rvv_1p0/minmax.hpp (100%) rename {3rdparty/hal_rvv => hal/riscv-rvv}/hal_rvv_1p0/moments.hpp (100%) rename {3rdparty/hal_rvv => hal/riscv-rvv}/hal_rvv_1p0/norm.hpp (100%) rename {3rdparty/hal_rvv => hal/riscv-rvv}/hal_rvv_1p0/norm_diff.hpp (100%) rename {3rdparty/hal_rvv => hal/riscv-rvv}/hal_rvv_1p0/norm_hamming.hpp (100%) rename {3rdparty/hal_rvv => hal/riscv-rvv}/hal_rvv_1p0/polar_to_cart.hpp (100%) rename {3rdparty/hal_rvv => hal/riscv-rvv}/hal_rvv_1p0/pyramids.hpp (100%) rename {3rdparty/hal_rvv => hal/riscv-rvv}/hal_rvv_1p0/qr.hpp (100%) rename {3rdparty/hal_rvv => hal/riscv-rvv}/hal_rvv_1p0/resize.hpp (100%) rename {3rdparty/hal_rvv => hal/riscv-rvv}/hal_rvv_1p0/sincos.hpp (100%) rename {3rdparty/hal_rvv => hal/riscv-rvv}/hal_rvv_1p0/split.hpp (100%) rename {3rdparty/hal_rvv => hal/riscv-rvv}/hal_rvv_1p0/sqrt.hpp (100%) rename {3rdparty/hal_rvv => hal/riscv-rvv}/hal_rvv_1p0/svd.hpp (99%) rename {3rdparty/hal_rvv => hal/riscv-rvv}/hal_rvv_1p0/thresh.hpp (99%) rename {3rdparty/hal_rvv => hal/riscv-rvv}/hal_rvv_1p0/transpose.hpp (100%) rename {3rdparty/hal_rvv => hal/riscv-rvv}/hal_rvv_1p0/types.hpp (100%) rename {3rdparty/hal_rvv => hal/riscv-rvv}/hal_rvv_1p0/warp.hpp (99%) rename {3rdparty/hal_rvv => hal/riscv-rvv}/version/hal_rvv_071.hpp (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index ca402b65c9..870bfdd723 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1005,7 +1005,7 @@ endif() foreach(hal ${OpenCV_HAL}) if(hal STREQUAL "carotene") if(";${CPU_BASELINE_FINAL};" MATCHES ";NEON;") - add_subdirectory(3rdparty/carotene/hal) + add_subdirectory(hal/carotene/hal) ocv_hal_register(CAROTENE_HAL_LIBRARIES CAROTENE_HAL_HEADERS CAROTENE_HAL_INCLUDE_DIRS) list(APPEND OpenCV_USED_HAL "carotene (ver ${CAROTENE_HAL_VERSION})") else() @@ -1013,19 +1013,19 @@ foreach(hal ${OpenCV_HAL}) endif() elseif(hal STREQUAL "fastcv") if((ARM OR AARCH64) AND (ANDROID OR (UNIX AND NOT APPLE AND NOT IOS AND NOT XROS))) - add_subdirectory(3rdparty/fastcv) + add_subdirectory(hal/fastcv) ocv_hal_register(FASTCV_HAL_LIBRARIES FASTCV_HAL_HEADERS FASTCV_HAL_INCLUDE_DIRS) list(APPEND OpenCV_USED_HAL "fastcv (ver ${FASTCV_HAL_VERSION})") else() message(STATUS "FastCV: fastcv is not available, disabling fastcv...") endif() elseif(hal STREQUAL "kleidicv") - add_subdirectory(3rdparty/kleidicv) + add_subdirectory(hal/kleidicv) ocv_hal_register(KLEIDICV_HAL_LIBRARIES KLEIDICV_HAL_HEADERS KLEIDICV_HAL_INCLUDE_DIRS) list(APPEND OpenCV_USED_HAL "KleidiCV (ver ${KLEIDICV_HAL_VERSION})") elseif(hal STREQUAL "ndsrvp") if(CMAKE_C_FLAGS MATCHES "-mext-dsp" AND CMAKE_CXX_FLAGS MATCHES "-mext-dsp" AND NOT ";${CPU_BASELINE_FINAL};" MATCHES ";RVV;") - add_subdirectory(3rdparty/ndsrvp) + add_subdirectory(hal/ndsrvp) ocv_hal_register(NDSRVP_HAL_LIBRARIES NDSRVP_HAL_HEADERS NDSRVP_HAL_INCLUDE_DIRS) list(APPEND OpenCV_USED_HAL "ndsrvp (ver ${NDSRVP_HAL_VERSION})") else() @@ -1033,18 +1033,18 @@ foreach(hal ${OpenCV_HAL}) endif() elseif(hal STREQUAL "halrvv") if(";${CPU_BASELINE_FINAL};" MATCHES ";RVV;") - add_subdirectory(3rdparty/hal_rvv/) + add_subdirectory(hal/riscv-rvv) ocv_hal_register(RVV_HAL_LIBRARIES RVV_HAL_HEADERS RVV_HAL_INCLUDE_DIRS) list(APPEND OpenCV_USED_HAL "HAL RVV (ver ${RVV_HAL_VERSION})") else() message(STATUS "HAL RVV: RVV is not available, disabling halrvv...") endif() elseif(hal STREQUAL "ipp") - add_subdirectory(3rdparty/ipphal) + add_subdirectory(hal/ipp) ocv_hal_register(IPP_HAL_LIBRARIES IPP_HAL_HEADERS IPP_HAL_INCLUDE_DIRS) list(APPEND OpenCV_USED_HAL "ipp (ver ${IPP_HAL_VERSION})") elseif(hal STREQUAL "openvx") - add_subdirectory(3rdparty/openvx) + add_subdirectory(hal/openvx) ocv_hal_register(OPENVX_HAL_LIBRARIES OPENVX_HAL_HEADERS OPENVX_HAL_INCLUDE_DIRS) list(APPEND OpenCV_USED_HAL "openvx (ver ${OPENVX_HAL_VERSION})") else() diff --git a/cmake/OpenCVFindLibsPerf.cmake b/cmake/OpenCVFindLibsPerf.cmake index 012b80a182..6d1d786517 100644 --- a/cmake/OpenCVFindLibsPerf.cmake +++ b/cmake/OpenCVFindLibsPerf.cmake @@ -169,7 +169,7 @@ if(WITH_KLEIDICV) set(HAVE_KLEIDICV ON) endif() if(NOT HAVE_KLEIDICV) - include("${OpenCV_SOURCE_DIR}/3rdparty/kleidicv/kleidicv.cmake") + include("${OpenCV_SOURCE_DIR}/hal/kleidicv/kleidicv.cmake") download_kleidicv(KLEIDICV_SOURCE_PATH) if(KLEIDICV_SOURCE_PATH) set(HAVE_KLEIDICV ON) diff --git a/3rdparty/carotene/.gitignore b/hal/carotene/.gitignore similarity index 100% rename from 3rdparty/carotene/.gitignore rename to hal/carotene/.gitignore diff --git a/3rdparty/carotene/CMakeLists.txt b/hal/carotene/CMakeLists.txt similarity index 100% rename from 3rdparty/carotene/CMakeLists.txt rename to hal/carotene/CMakeLists.txt diff --git a/3rdparty/carotene/README.md b/hal/carotene/README.md similarity index 100% rename from 3rdparty/carotene/README.md rename to hal/carotene/README.md diff --git a/3rdparty/carotene/hal/CMakeLists.txt b/hal/carotene/hal/CMakeLists.txt similarity index 100% rename from 3rdparty/carotene/hal/CMakeLists.txt rename to hal/carotene/hal/CMakeLists.txt diff --git a/3rdparty/carotene/hal/dummy.cpp b/hal/carotene/hal/dummy.cpp similarity index 100% rename from 3rdparty/carotene/hal/dummy.cpp rename to hal/carotene/hal/dummy.cpp diff --git a/3rdparty/carotene/hal/tegra_hal.hpp b/hal/carotene/hal/tegra_hal.hpp similarity index 100% rename from 3rdparty/carotene/hal/tegra_hal.hpp rename to hal/carotene/hal/tegra_hal.hpp diff --git a/3rdparty/carotene/include/carotene/definitions.hpp b/hal/carotene/include/carotene/definitions.hpp similarity index 100% rename from 3rdparty/carotene/include/carotene/definitions.hpp rename to hal/carotene/include/carotene/definitions.hpp diff --git a/3rdparty/carotene/include/carotene/functions.hpp b/hal/carotene/include/carotene/functions.hpp similarity index 99% rename from 3rdparty/carotene/include/carotene/functions.hpp rename to hal/carotene/include/carotene/functions.hpp index 8a4fa3efdd..06f1adf3b3 100644 --- a/3rdparty/carotene/include/carotene/functions.hpp +++ b/hal/carotene/include/carotene/functions.hpp @@ -359,7 +359,7 @@ namespace CAROTENE_NS { /* For each point `p` within `size`, do: - dst[p] = src0[p] * scale / src1[p] + dst[p] = src0[p] * scale / src1[p] NOTE: ROUND_TO_ZERO convert policy is used */ @@ -420,7 +420,7 @@ namespace CAROTENE_NS { /* For each point `p` within `size`, do: - dst[p] = scale / src[p] + dst[p] = scale / src[p] NOTE: ROUND_TO_ZERO convert policy is used */ diff --git a/3rdparty/carotene/include/carotene/types.hpp b/hal/carotene/include/carotene/types.hpp similarity index 100% rename from 3rdparty/carotene/include/carotene/types.hpp rename to hal/carotene/include/carotene/types.hpp diff --git a/3rdparty/carotene/src/absdiff.cpp b/hal/carotene/src/absdiff.cpp similarity index 100% rename from 3rdparty/carotene/src/absdiff.cpp rename to hal/carotene/src/absdiff.cpp diff --git a/3rdparty/carotene/src/accumulate.cpp b/hal/carotene/src/accumulate.cpp similarity index 100% rename from 3rdparty/carotene/src/accumulate.cpp rename to hal/carotene/src/accumulate.cpp diff --git a/3rdparty/carotene/src/add.cpp b/hal/carotene/src/add.cpp similarity index 100% rename from 3rdparty/carotene/src/add.cpp rename to hal/carotene/src/add.cpp diff --git a/3rdparty/carotene/src/add_weighted.cpp b/hal/carotene/src/add_weighted.cpp similarity index 100% rename from 3rdparty/carotene/src/add_weighted.cpp rename to hal/carotene/src/add_weighted.cpp diff --git a/3rdparty/carotene/src/bitwise.cpp b/hal/carotene/src/bitwise.cpp similarity index 100% rename from 3rdparty/carotene/src/bitwise.cpp rename to hal/carotene/src/bitwise.cpp diff --git a/3rdparty/carotene/src/blur.cpp b/hal/carotene/src/blur.cpp similarity index 100% rename from 3rdparty/carotene/src/blur.cpp rename to hal/carotene/src/blur.cpp diff --git a/3rdparty/carotene/src/canny.cpp b/hal/carotene/src/canny.cpp similarity index 100% rename from 3rdparty/carotene/src/canny.cpp rename to hal/carotene/src/canny.cpp diff --git a/3rdparty/carotene/src/channel_extract.cpp b/hal/carotene/src/channel_extract.cpp similarity index 99% rename from 3rdparty/carotene/src/channel_extract.cpp rename to hal/carotene/src/channel_extract.cpp index ff4fb3770c..904a047b7e 100644 --- a/3rdparty/carotene/src/channel_extract.cpp +++ b/hal/carotene/src/channel_extract.cpp @@ -378,7 +378,7 @@ void extract4(const Size2D &size, vst1q_##sgn##bits(dst1 + d1j, vals.v4.val[3]); \ } -#endif +#endif #define SPLIT4ALPHA(sgn,bits) void split4(const Size2D &_size, \ const sgn##bits * srcBase, ptrdiff_t srcStride, \ diff --git a/3rdparty/carotene/src/channels_combine.cpp b/hal/carotene/src/channels_combine.cpp similarity index 100% rename from 3rdparty/carotene/src/channels_combine.cpp rename to hal/carotene/src/channels_combine.cpp diff --git a/3rdparty/carotene/src/cmp.cpp b/hal/carotene/src/cmp.cpp similarity index 100% rename from 3rdparty/carotene/src/cmp.cpp rename to hal/carotene/src/cmp.cpp diff --git a/3rdparty/carotene/src/colorconvert.cpp b/hal/carotene/src/colorconvert.cpp similarity index 100% rename from 3rdparty/carotene/src/colorconvert.cpp rename to hal/carotene/src/colorconvert.cpp diff --git a/3rdparty/carotene/src/common.cpp b/hal/carotene/src/common.cpp similarity index 100% rename from 3rdparty/carotene/src/common.cpp rename to hal/carotene/src/common.cpp diff --git a/3rdparty/carotene/src/common.hpp b/hal/carotene/src/common.hpp similarity index 100% rename from 3rdparty/carotene/src/common.hpp rename to hal/carotene/src/common.hpp diff --git a/3rdparty/carotene/src/convert.cpp b/hal/carotene/src/convert.cpp similarity index 100% rename from 3rdparty/carotene/src/convert.cpp rename to hal/carotene/src/convert.cpp diff --git a/3rdparty/carotene/src/convert_depth.cpp b/hal/carotene/src/convert_depth.cpp similarity index 100% rename from 3rdparty/carotene/src/convert_depth.cpp rename to hal/carotene/src/convert_depth.cpp diff --git a/3rdparty/carotene/src/convert_scale.cpp b/hal/carotene/src/convert_scale.cpp similarity index 100% rename from 3rdparty/carotene/src/convert_scale.cpp rename to hal/carotene/src/convert_scale.cpp diff --git a/3rdparty/carotene/src/convolution.cpp b/hal/carotene/src/convolution.cpp similarity index 100% rename from 3rdparty/carotene/src/convolution.cpp rename to hal/carotene/src/convolution.cpp diff --git a/3rdparty/carotene/src/count_nonzero.cpp b/hal/carotene/src/count_nonzero.cpp similarity index 100% rename from 3rdparty/carotene/src/count_nonzero.cpp rename to hal/carotene/src/count_nonzero.cpp diff --git a/3rdparty/carotene/src/div.cpp b/hal/carotene/src/div.cpp similarity index 100% rename from 3rdparty/carotene/src/div.cpp rename to hal/carotene/src/div.cpp diff --git a/3rdparty/carotene/src/dot_product.cpp b/hal/carotene/src/dot_product.cpp similarity index 100% rename from 3rdparty/carotene/src/dot_product.cpp rename to hal/carotene/src/dot_product.cpp diff --git a/3rdparty/carotene/src/dummy.cpp b/hal/carotene/src/dummy.cpp similarity index 100% rename from 3rdparty/carotene/src/dummy.cpp rename to hal/carotene/src/dummy.cpp diff --git a/3rdparty/carotene/src/fast.cpp b/hal/carotene/src/fast.cpp similarity index 100% rename from 3rdparty/carotene/src/fast.cpp rename to hal/carotene/src/fast.cpp diff --git a/3rdparty/carotene/src/fill_minmaxloc.cpp b/hal/carotene/src/fill_minmaxloc.cpp similarity index 100% rename from 3rdparty/carotene/src/fill_minmaxloc.cpp rename to hal/carotene/src/fill_minmaxloc.cpp diff --git a/3rdparty/carotene/src/flip.cpp b/hal/carotene/src/flip.cpp similarity index 100% rename from 3rdparty/carotene/src/flip.cpp rename to hal/carotene/src/flip.cpp diff --git a/3rdparty/carotene/src/gaussian_blur.cpp b/hal/carotene/src/gaussian_blur.cpp similarity index 100% rename from 3rdparty/carotene/src/gaussian_blur.cpp rename to hal/carotene/src/gaussian_blur.cpp diff --git a/3rdparty/carotene/src/in_range.cpp b/hal/carotene/src/in_range.cpp similarity index 100% rename from 3rdparty/carotene/src/in_range.cpp rename to hal/carotene/src/in_range.cpp diff --git a/3rdparty/carotene/src/integral.cpp b/hal/carotene/src/integral.cpp similarity index 100% rename from 3rdparty/carotene/src/integral.cpp rename to hal/carotene/src/integral.cpp diff --git a/3rdparty/carotene/src/intrinsics.hpp b/hal/carotene/src/intrinsics.hpp similarity index 100% rename from 3rdparty/carotene/src/intrinsics.hpp rename to hal/carotene/src/intrinsics.hpp diff --git a/3rdparty/carotene/src/laplacian.cpp b/hal/carotene/src/laplacian.cpp similarity index 100% rename from 3rdparty/carotene/src/laplacian.cpp rename to hal/carotene/src/laplacian.cpp diff --git a/3rdparty/carotene/src/magnitude.cpp b/hal/carotene/src/magnitude.cpp similarity index 100% rename from 3rdparty/carotene/src/magnitude.cpp rename to hal/carotene/src/magnitude.cpp diff --git a/3rdparty/carotene/src/meanstddev.cpp b/hal/carotene/src/meanstddev.cpp similarity index 100% rename from 3rdparty/carotene/src/meanstddev.cpp rename to hal/carotene/src/meanstddev.cpp diff --git a/3rdparty/carotene/src/median_filter.cpp b/hal/carotene/src/median_filter.cpp similarity index 100% rename from 3rdparty/carotene/src/median_filter.cpp rename to hal/carotene/src/median_filter.cpp diff --git a/3rdparty/carotene/src/min_max.cpp b/hal/carotene/src/min_max.cpp similarity index 100% rename from 3rdparty/carotene/src/min_max.cpp rename to hal/carotene/src/min_max.cpp diff --git a/3rdparty/carotene/src/minmaxloc.cpp b/hal/carotene/src/minmaxloc.cpp similarity index 100% rename from 3rdparty/carotene/src/minmaxloc.cpp rename to hal/carotene/src/minmaxloc.cpp diff --git a/3rdparty/carotene/src/morph.cpp b/hal/carotene/src/morph.cpp similarity index 100% rename from 3rdparty/carotene/src/morph.cpp rename to hal/carotene/src/morph.cpp diff --git a/3rdparty/carotene/src/mul.cpp b/hal/carotene/src/mul.cpp similarity index 100% rename from 3rdparty/carotene/src/mul.cpp rename to hal/carotene/src/mul.cpp diff --git a/3rdparty/carotene/src/norm.cpp b/hal/carotene/src/norm.cpp similarity index 100% rename from 3rdparty/carotene/src/norm.cpp rename to hal/carotene/src/norm.cpp diff --git a/3rdparty/carotene/src/opticalflow.cpp b/hal/carotene/src/opticalflow.cpp similarity index 100% rename from 3rdparty/carotene/src/opticalflow.cpp rename to hal/carotene/src/opticalflow.cpp diff --git a/3rdparty/carotene/src/phase.cpp b/hal/carotene/src/phase.cpp similarity index 100% rename from 3rdparty/carotene/src/phase.cpp rename to hal/carotene/src/phase.cpp diff --git a/3rdparty/carotene/src/pyramid.cpp b/hal/carotene/src/pyramid.cpp similarity index 100% rename from 3rdparty/carotene/src/pyramid.cpp rename to hal/carotene/src/pyramid.cpp diff --git a/3rdparty/carotene/src/reduce.cpp b/hal/carotene/src/reduce.cpp similarity index 100% rename from 3rdparty/carotene/src/reduce.cpp rename to hal/carotene/src/reduce.cpp diff --git a/3rdparty/carotene/src/remap.cpp b/hal/carotene/src/remap.cpp similarity index 100% rename from 3rdparty/carotene/src/remap.cpp rename to hal/carotene/src/remap.cpp diff --git a/3rdparty/carotene/src/remap.hpp b/hal/carotene/src/remap.hpp similarity index 100% rename from 3rdparty/carotene/src/remap.hpp rename to hal/carotene/src/remap.hpp diff --git a/3rdparty/carotene/src/resize.cpp b/hal/carotene/src/resize.cpp similarity index 100% rename from 3rdparty/carotene/src/resize.cpp rename to hal/carotene/src/resize.cpp diff --git a/3rdparty/carotene/src/saturate_cast.hpp b/hal/carotene/src/saturate_cast.hpp similarity index 100% rename from 3rdparty/carotene/src/saturate_cast.hpp rename to hal/carotene/src/saturate_cast.hpp diff --git a/3rdparty/carotene/src/scharr.cpp b/hal/carotene/src/scharr.cpp similarity index 100% rename from 3rdparty/carotene/src/scharr.cpp rename to hal/carotene/src/scharr.cpp diff --git a/3rdparty/carotene/src/separable_filter.cpp b/hal/carotene/src/separable_filter.cpp similarity index 100% rename from 3rdparty/carotene/src/separable_filter.cpp rename to hal/carotene/src/separable_filter.cpp diff --git a/3rdparty/carotene/src/separable_filter.hpp b/hal/carotene/src/separable_filter.hpp similarity index 100% rename from 3rdparty/carotene/src/separable_filter.hpp rename to hal/carotene/src/separable_filter.hpp diff --git a/3rdparty/carotene/src/sobel.cpp b/hal/carotene/src/sobel.cpp similarity index 100% rename from 3rdparty/carotene/src/sobel.cpp rename to hal/carotene/src/sobel.cpp diff --git a/3rdparty/carotene/src/sub.cpp b/hal/carotene/src/sub.cpp similarity index 100% rename from 3rdparty/carotene/src/sub.cpp rename to hal/carotene/src/sub.cpp diff --git a/3rdparty/carotene/src/sum.cpp b/hal/carotene/src/sum.cpp similarity index 100% rename from 3rdparty/carotene/src/sum.cpp rename to hal/carotene/src/sum.cpp diff --git a/3rdparty/carotene/src/template_matching.cpp b/hal/carotene/src/template_matching.cpp similarity index 100% rename from 3rdparty/carotene/src/template_matching.cpp rename to hal/carotene/src/template_matching.cpp diff --git a/3rdparty/carotene/src/threshold.cpp b/hal/carotene/src/threshold.cpp similarity index 100% rename from 3rdparty/carotene/src/threshold.cpp rename to hal/carotene/src/threshold.cpp diff --git a/3rdparty/carotene/src/vround_helper.hpp b/hal/carotene/src/vround_helper.hpp similarity index 100% rename from 3rdparty/carotene/src/vround_helper.hpp rename to hal/carotene/src/vround_helper.hpp diff --git a/3rdparty/carotene/src/vtransform.hpp b/hal/carotene/src/vtransform.hpp similarity index 100% rename from 3rdparty/carotene/src/vtransform.hpp rename to hal/carotene/src/vtransform.hpp diff --git a/3rdparty/carotene/src/warp_affine.cpp b/hal/carotene/src/warp_affine.cpp similarity index 100% rename from 3rdparty/carotene/src/warp_affine.cpp rename to hal/carotene/src/warp_affine.cpp diff --git a/3rdparty/carotene/src/warp_perspective.cpp b/hal/carotene/src/warp_perspective.cpp similarity index 100% rename from 3rdparty/carotene/src/warp_perspective.cpp rename to hal/carotene/src/warp_perspective.cpp diff --git a/3rdparty/fastcv/CMakeLists.txt b/hal/fastcv/CMakeLists.txt similarity index 100% rename from 3rdparty/fastcv/CMakeLists.txt rename to hal/fastcv/CMakeLists.txt diff --git a/3rdparty/fastcv/include/fastcv_hal_core.hpp b/hal/fastcv/include/fastcv_hal_core.hpp similarity index 100% rename from 3rdparty/fastcv/include/fastcv_hal_core.hpp rename to hal/fastcv/include/fastcv_hal_core.hpp diff --git a/3rdparty/fastcv/include/fastcv_hal_imgproc.hpp b/hal/fastcv/include/fastcv_hal_imgproc.hpp similarity index 100% rename from 3rdparty/fastcv/include/fastcv_hal_imgproc.hpp rename to hal/fastcv/include/fastcv_hal_imgproc.hpp diff --git a/3rdparty/fastcv/include/fastcv_hal_utils.hpp b/hal/fastcv/include/fastcv_hal_utils.hpp similarity index 100% rename from 3rdparty/fastcv/include/fastcv_hal_utils.hpp rename to hal/fastcv/include/fastcv_hal_utils.hpp diff --git a/3rdparty/fastcv/src/fastcv_hal_core.cpp b/hal/fastcv/src/fastcv_hal_core.cpp similarity index 99% rename from 3rdparty/fastcv/src/fastcv_hal_core.cpp rename to hal/fastcv/src/fastcv_hal_core.cpp index bc7459bcf4..5bb35817f3 100644 --- a/3rdparty/fastcv/src/fastcv_hal_core.cpp +++ b/hal/fastcv/src/fastcv_hal_core.cpp @@ -704,7 +704,6 @@ int fastcv_hal_gemm32f( fcvMatrixMultiplyf32_v2(src2p, m, k, src2_step, src1p, n, src1_step, dst_temp2.ptr(), dst_temp2.step[0]); fcvTransposef32_v2(dst_temp2.ptr(), n, k, dst_temp2.step[0], dstp, dst_stride); - } else { @@ -738,4 +737,4 @@ int fastcv_hal_gemm32f( } CV_HAL_RETURN(status,hal_gemm32f); -} \ No newline at end of file +} diff --git a/3rdparty/fastcv/src/fastcv_hal_imgproc.cpp b/hal/fastcv/src/fastcv_hal_imgproc.cpp similarity index 99% rename from 3rdparty/fastcv/src/fastcv_hal_imgproc.cpp rename to hal/fastcv/src/fastcv_hal_imgproc.cpp index c04998cc44..34d1e8f41c 100644 --- a/3rdparty/fastcv/src/fastcv_hal_imgproc.cpp +++ b/hal/fastcv/src/fastcv_hal_imgproc.cpp @@ -452,7 +452,7 @@ int fastcv_hal_boxFilter( nStripes = nThreads; stripeHeight = src.rows/nThreads; } - + cv::parallel_for_(cv::Range(0, nStripes), FcvBoxLoop_Invoker(src, width, height, dst_temp, border_type, ksize_width, normalize, stripeHeight, nStripes, src_depth), nStripes); @@ -1139,4 +1139,4 @@ int fastcv_hal_canny( CV_HAL_RETURN_NOT_IMPLEMENTED(cv::format("Ksize:%d is not supported", ksize)); } CV_HAL_RETURN(status, hal_canny); -} \ No newline at end of file +} diff --git a/3rdparty/fastcv/src/fastcv_hal_utils.cpp b/hal/fastcv/src/fastcv_hal_utils.cpp similarity index 100% rename from 3rdparty/fastcv/src/fastcv_hal_utils.cpp rename to hal/fastcv/src/fastcv_hal_utils.cpp diff --git a/3rdparty/ipphal/CMakeLists.txt b/hal/ipp/CMakeLists.txt similarity index 100% rename from 3rdparty/ipphal/CMakeLists.txt rename to hal/ipp/CMakeLists.txt diff --git a/3rdparty/ipphal/include/ipp_hal_core.hpp b/hal/ipp/include/ipp_hal_core.hpp similarity index 100% rename from 3rdparty/ipphal/include/ipp_hal_core.hpp rename to hal/ipp/include/ipp_hal_core.hpp diff --git a/3rdparty/ipphal/include/ipp_utils.hpp b/hal/ipp/include/ipp_utils.hpp similarity index 100% rename from 3rdparty/ipphal/include/ipp_utils.hpp rename to hal/ipp/include/ipp_utils.hpp diff --git a/3rdparty/ipphal/src/cart_polar_ipp.cpp b/hal/ipp/src/cart_polar_ipp.cpp similarity index 100% rename from 3rdparty/ipphal/src/cart_polar_ipp.cpp rename to hal/ipp/src/cart_polar_ipp.cpp diff --git a/3rdparty/ipphal/src/mean_ipp.cpp b/hal/ipp/src/mean_ipp.cpp similarity index 100% rename from 3rdparty/ipphal/src/mean_ipp.cpp rename to hal/ipp/src/mean_ipp.cpp diff --git a/3rdparty/ipphal/src/minmax_ipp.cpp b/hal/ipp/src/minmax_ipp.cpp similarity index 100% rename from 3rdparty/ipphal/src/minmax_ipp.cpp rename to hal/ipp/src/minmax_ipp.cpp diff --git a/3rdparty/ipphal/src/norm_ipp.cpp b/hal/ipp/src/norm_ipp.cpp similarity index 100% rename from 3rdparty/ipphal/src/norm_ipp.cpp rename to hal/ipp/src/norm_ipp.cpp diff --git a/3rdparty/ipphal/src/transforms_ipp.cpp b/hal/ipp/src/transforms_ipp.cpp similarity index 100% rename from 3rdparty/ipphal/src/transforms_ipp.cpp rename to hal/ipp/src/transforms_ipp.cpp diff --git a/3rdparty/kleidicv/CMakeLists.txt b/hal/kleidicv/CMakeLists.txt similarity index 100% rename from 3rdparty/kleidicv/CMakeLists.txt rename to hal/kleidicv/CMakeLists.txt diff --git a/3rdparty/kleidicv/kleidicv.cmake b/hal/kleidicv/kleidicv.cmake similarity index 100% rename from 3rdparty/kleidicv/kleidicv.cmake rename to hal/kleidicv/kleidicv.cmake diff --git a/3rdparty/ndsrvp/CMakeLists.txt b/hal/ndsrvp/CMakeLists.txt similarity index 100% rename from 3rdparty/ndsrvp/CMakeLists.txt rename to hal/ndsrvp/CMakeLists.txt diff --git a/3rdparty/ndsrvp/include/core.hpp b/hal/ndsrvp/include/core.hpp similarity index 100% rename from 3rdparty/ndsrvp/include/core.hpp rename to hal/ndsrvp/include/core.hpp diff --git a/3rdparty/ndsrvp/include/features2d.hpp b/hal/ndsrvp/include/features2d.hpp similarity index 76% rename from 3rdparty/ndsrvp/include/features2d.hpp rename to hal/ndsrvp/include/features2d.hpp index 1f6180a795..d0f9ddd31e 100644 --- a/3rdparty/ndsrvp/include/features2d.hpp +++ b/hal/ndsrvp/include/features2d.hpp @@ -1,6 +1,6 @@ // This file is part of OpenCV project. // It is subject to the license terms in the LICENSE file found in the top-level directory -// of this distribution and at http://opencv.org/license.html. +// of this distribution and at http://opencv.org/license.html. #ifndef OPENCV_NDSRVP_FEATURES2D_HPP #define OPENCV_NDSRVP_FEATURES2D_HPP diff --git a/3rdparty/ndsrvp/include/imgproc.hpp b/hal/ndsrvp/include/imgproc.hpp similarity index 100% rename from 3rdparty/ndsrvp/include/imgproc.hpp rename to hal/ndsrvp/include/imgproc.hpp diff --git a/3rdparty/ndsrvp/ndsrvp_hal.hpp b/hal/ndsrvp/ndsrvp_hal.hpp similarity index 100% rename from 3rdparty/ndsrvp/ndsrvp_hal.hpp rename to hal/ndsrvp/ndsrvp_hal.hpp diff --git a/3rdparty/ndsrvp/src/bilateralFilter.cpp b/hal/ndsrvp/src/bilateralFilter.cpp similarity index 100% rename from 3rdparty/ndsrvp/src/bilateralFilter.cpp rename to hal/ndsrvp/src/bilateralFilter.cpp diff --git a/3rdparty/ndsrvp/src/cvutils.cpp b/hal/ndsrvp/src/cvutils.cpp similarity index 98% rename from 3rdparty/ndsrvp/src/cvutils.cpp rename to hal/ndsrvp/src/cvutils.cpp index 6afac5136d..ba1e4daaaa 100644 --- a/3rdparty/ndsrvp/src/cvutils.cpp +++ b/hal/ndsrvp/src/cvutils.cpp @@ -1,6 +1,6 @@ // This file is part of OpenCV project. // It is subject to the license terms in the LICENSE file found in the top-level directory -// of this distribution and at http://opencv.org/license.html. +// of this distribution and at http://opencv.org/license.html. #include "cvutils.hpp" diff --git a/3rdparty/ndsrvp/src/cvutils.hpp b/hal/ndsrvp/src/cvutils.hpp similarity index 99% rename from 3rdparty/ndsrvp/src/cvutils.hpp rename to hal/ndsrvp/src/cvutils.hpp index 78bb11d95f..d1b38b4bd6 100644 --- a/3rdparty/ndsrvp/src/cvutils.hpp +++ b/hal/ndsrvp/src/cvutils.hpp @@ -1,6 +1,6 @@ // This file is part of OpenCV project. // It is subject to the license terms in the LICENSE file found in the top-level directory -// of this distribution and at http://opencv.org/license.html. +// of this distribution and at http://opencv.org/license.html. #ifndef OPENCV_NDSRVP_CVUTILS_HPP #define OPENCV_NDSRVP_CVUTILS_HPP diff --git a/3rdparty/ndsrvp/src/filter.cpp b/hal/ndsrvp/src/filter.cpp similarity index 100% rename from 3rdparty/ndsrvp/src/filter.cpp rename to hal/ndsrvp/src/filter.cpp diff --git a/3rdparty/ndsrvp/src/integral.cpp b/hal/ndsrvp/src/integral.cpp similarity index 99% rename from 3rdparty/ndsrvp/src/integral.cpp rename to hal/ndsrvp/src/integral.cpp index e1dd993a90..8fc7f367a7 100644 --- a/3rdparty/ndsrvp/src/integral.cpp +++ b/hal/ndsrvp/src/integral.cpp @@ -1,6 +1,6 @@ // This file is part of OpenCV project. // It is subject to the license terms in the LICENSE file found in the top-level directory -// of this distribution and at http://opencv.org/license.html. +// of this distribution and at http://opencv.org/license.html. #include "ndsrvp_hal.hpp" #include "opencv2/imgproc/hal/interface.h" @@ -49,7 +49,7 @@ int integral(int depth, int sdepth, int sqdepth, int j = 0; for (; j + 8 <= width; j += 8) { - unsigned long vs8x8 = *(unsigned long*)(src_row + j); + unsigned long vs8x8 = *(unsigned long*)(src_row + j); unsigned long vs810 = __nds__zunpkd810(vs8x8); unsigned long vs832 = __nds__zunpkd832(vs8x8); diff --git a/3rdparty/ndsrvp/src/medianBlur.cpp b/hal/ndsrvp/src/medianBlur.cpp similarity index 99% rename from 3rdparty/ndsrvp/src/medianBlur.cpp rename to hal/ndsrvp/src/medianBlur.cpp index c511367f31..286a30dc82 100644 --- a/3rdparty/ndsrvp/src/medianBlur.cpp +++ b/hal/ndsrvp/src/medianBlur.cpp @@ -287,7 +287,7 @@ int medianBlur(const uchar* src_data, size_t src_step, medianBlur_SortNet( src_data_rep, src_step, dst_data, dst_step, width, height, cn, ksize ); else if( depth == CV_16S ) medianBlur_SortNet( src_data_rep, src_step, dst_data, dst_step, width, height, cn, ksize ); - else + else return CV_HAL_ERROR_NOT_IMPLEMENTED; return CV_HAL_ERROR_OK; diff --git a/3rdparty/ndsrvp/src/remap.cpp b/hal/ndsrvp/src/remap.cpp similarity index 100% rename from 3rdparty/ndsrvp/src/remap.cpp rename to hal/ndsrvp/src/remap.cpp diff --git a/3rdparty/ndsrvp/src/threshold.cpp b/hal/ndsrvp/src/threshold.cpp similarity index 96% rename from 3rdparty/ndsrvp/src/threshold.cpp rename to hal/ndsrvp/src/threshold.cpp index 0812100311..43c65114f7 100644 --- a/3rdparty/ndsrvp/src/threshold.cpp +++ b/hal/ndsrvp/src/threshold.cpp @@ -1,6 +1,6 @@ // This file is part of OpenCV project. // It is subject to the license terms in the LICENSE file found in the top-level directory -// of this distribution and at http://opencv.org/license.html. +// of this distribution and at http://opencv.org/license.html. #include "ndsrvp_hal.hpp" #include "opencv2/imgproc/hal/interface.h" @@ -129,13 +129,13 @@ int threshold(const uchar* src_data, size_t src_step, { threshold_op, threshold_op, - threshold_op, + threshold_op, threshold_op, threshold_op }, { threshold_op, threshold_op, - threshold_op, + threshold_op, threshold_op, threshold_op }, { diff --git a/3rdparty/ndsrvp/src/warpAffine.cpp b/hal/ndsrvp/src/warpAffine.cpp similarity index 97% rename from 3rdparty/ndsrvp/src/warpAffine.cpp rename to hal/ndsrvp/src/warpAffine.cpp index 4257361d1d..dbb7f4e238 100644 --- a/3rdparty/ndsrvp/src/warpAffine.cpp +++ b/hal/ndsrvp/src/warpAffine.cpp @@ -1,6 +1,6 @@ // This file is part of OpenCV project. // It is subject to the license terms in the LICENSE file found in the top-level directory -// of this distribution and at http://opencv.org/license.html. +// of this distribution and at http://opencv.org/license.html. #include "ndsrvp_hal.hpp" #include "opencv2/imgproc/hal/interface.h" diff --git a/3rdparty/ndsrvp/src/warpPerspective.cpp b/hal/ndsrvp/src/warpPerspective.cpp similarity index 98% rename from 3rdparty/ndsrvp/src/warpPerspective.cpp rename to hal/ndsrvp/src/warpPerspective.cpp index 40e44729d9..70f3ede134 100644 --- a/3rdparty/ndsrvp/src/warpPerspective.cpp +++ b/hal/ndsrvp/src/warpPerspective.cpp @@ -1,6 +1,6 @@ // This file is part of OpenCV project. // It is subject to the license terms in the LICENSE file found in the top-level directory -// of this distribution and at http://opencv.org/license.html. +// of this distribution and at http://opencv.org/license.html. #include "ndsrvp_hal.hpp" #include "opencv2/imgproc/hal/interface.h" diff --git a/3rdparty/openvx/CMakeLists.txt b/hal/openvx/CMakeLists.txt similarity index 100% rename from 3rdparty/openvx/CMakeLists.txt rename to hal/openvx/CMakeLists.txt diff --git a/3rdparty/openvx/README.md b/hal/openvx/README.md similarity index 77% rename from 3rdparty/openvx/README.md rename to hal/openvx/README.md index 3c89ba4bee..bcb7943547 100644 --- a/3rdparty/openvx/README.md +++ b/hal/openvx/README.md @@ -19,33 +19,33 @@ The following short sample gives basic knowledge on the wrappers usage: int main() { - vx_uint32 width = 640, height = 480; - try - { - ivx::Context context = ivx::Context::create(); - ivx::Graph graph = ivx::Graph::create(context); - ivx::Image - gray = ivx::Image::create(context, width, height, VX_DF_IMAGE_U8), - gb = ivx::Image::createVirtual(graph), - res = ivx::Image::create(context, width, height, VX_DF_IMAGE_U8); + vx_uint32 width = 640, height = 480; + try + { + ivx::Context context = ivx::Context::create(); + ivx::Graph graph = ivx::Graph::create(context); + ivx::Image + gray = ivx::Image::create(context, width, height, VX_DF_IMAGE_U8), + gb = ivx::Image::createVirtual(graph), + res = ivx::Image::create(context, width, height, VX_DF_IMAGE_U8); - context.loadKernels("openvx-debug"); // ivx::debug::* + context.loadKernels("openvx-debug"); // ivx::debug::* - ivx::debug::fReadImage(context, inputPath, gray); + ivx::debug::fReadImage(context, inputPath, gray); - ivx::Node::create(graph, VX_KERNEL_GAUSSIAN_3x3, gray, gb); - ivx::Node::create( - graph, - VX_KERNEL_THRESHOLD, - gb, - ivx::Threshold::createBinary(context, VX_TYPE_UINT8, 50), - res - ); + ivx::Node::create(graph, VX_KERNEL_GAUSSIAN_3x3, gray, gb); + ivx::Node::create( + graph, + VX_KERNEL_THRESHOLD, + gb, + ivx::Threshold::createBinary(context, VX_TYPE_UINT8, 50), + res + ); - graph.verify(); - graph.process(); + graph.verify(); + graph.process(); - ivx::debug::fWriteImage(context, res, "ovx-res-cpp.pgm"); + ivx::debug::fWriteImage(context, res, "ovx-res-cpp.pgm"); } catch (const ivx::RuntimeError& e) { @@ -65,7 +65,7 @@ int main() return 0; } - + ``` ## C++ API overview diff --git a/3rdparty/openvx/hal/CMakeLists.txt b/hal/openvx/hal/CMakeLists.txt similarity index 100% rename from 3rdparty/openvx/hal/CMakeLists.txt rename to hal/openvx/hal/CMakeLists.txt diff --git a/3rdparty/openvx/hal/README.md b/hal/openvx/hal/README.md similarity index 100% rename from 3rdparty/openvx/hal/README.md rename to hal/openvx/hal/README.md diff --git a/3rdparty/openvx/hal/openvx_hal.cpp b/hal/openvx/hal/openvx_hal.cpp similarity index 100% rename from 3rdparty/openvx/hal/openvx_hal.cpp rename to hal/openvx/hal/openvx_hal.cpp diff --git a/3rdparty/openvx/hal/openvx_hal.hpp b/hal/openvx/hal/openvx_hal.hpp similarity index 100% rename from 3rdparty/openvx/hal/openvx_hal.hpp rename to hal/openvx/hal/openvx_hal.hpp diff --git a/3rdparty/openvx/include/ivx.hpp b/hal/openvx/include/ivx.hpp similarity index 100% rename from 3rdparty/openvx/include/ivx.hpp rename to hal/openvx/include/ivx.hpp diff --git a/3rdparty/openvx/include/ivx_lib_debug.hpp b/hal/openvx/include/ivx_lib_debug.hpp similarity index 100% rename from 3rdparty/openvx/include/ivx_lib_debug.hpp rename to hal/openvx/include/ivx_lib_debug.hpp diff --git a/3rdparty/hal_rvv/CMakeLists.txt b/hal/riscv-rvv/CMakeLists.txt similarity index 100% rename from 3rdparty/hal_rvv/CMakeLists.txt rename to hal/riscv-rvv/CMakeLists.txt diff --git a/3rdparty/hal_rvv/hal_rvv.hpp b/hal/riscv-rvv/hal_rvv.hpp similarity index 100% rename from 3rdparty/hal_rvv/hal_rvv.hpp rename to hal/riscv-rvv/hal_rvv.hpp diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/atan.hpp b/hal/riscv-rvv/hal_rvv_1p0/atan.hpp similarity index 100% rename from 3rdparty/hal_rvv/hal_rvv_1p0/atan.hpp rename to hal/riscv-rvv/hal_rvv_1p0/atan.hpp diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/cart_to_polar.hpp b/hal/riscv-rvv/hal_rvv_1p0/cart_to_polar.hpp similarity index 100% rename from 3rdparty/hal_rvv/hal_rvv_1p0/cart_to_polar.hpp rename to hal/riscv-rvv/hal_rvv_1p0/cart_to_polar.hpp diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/cholesky.hpp b/hal/riscv-rvv/hal_rvv_1p0/cholesky.hpp similarity index 100% rename from 3rdparty/hal_rvv/hal_rvv_1p0/cholesky.hpp rename to hal/riscv-rvv/hal_rvv_1p0/cholesky.hpp diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/color.hpp b/hal/riscv-rvv/hal_rvv_1p0/color.hpp similarity index 100% rename from 3rdparty/hal_rvv/hal_rvv_1p0/color.hpp rename to hal/riscv-rvv/hal_rvv_1p0/color.hpp diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/common.hpp b/hal/riscv-rvv/hal_rvv_1p0/common.hpp similarity index 100% rename from 3rdparty/hal_rvv/hal_rvv_1p0/common.hpp rename to hal/riscv-rvv/hal_rvv_1p0/common.hpp diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/compare.hpp b/hal/riscv-rvv/hal_rvv_1p0/compare.hpp similarity index 100% rename from 3rdparty/hal_rvv/hal_rvv_1p0/compare.hpp rename to hal/riscv-rvv/hal_rvv_1p0/compare.hpp diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/convert_scale.hpp b/hal/riscv-rvv/hal_rvv_1p0/convert_scale.hpp similarity index 100% rename from 3rdparty/hal_rvv/hal_rvv_1p0/convert_scale.hpp rename to hal/riscv-rvv/hal_rvv_1p0/convert_scale.hpp diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/copy_mask.hpp b/hal/riscv-rvv/hal_rvv_1p0/copy_mask.hpp similarity index 100% rename from 3rdparty/hal_rvv/hal_rvv_1p0/copy_mask.hpp rename to hal/riscv-rvv/hal_rvv_1p0/copy_mask.hpp diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/div.hpp b/hal/riscv-rvv/hal_rvv_1p0/div.hpp similarity index 100% rename from 3rdparty/hal_rvv/hal_rvv_1p0/div.hpp rename to hal/riscv-rvv/hal_rvv_1p0/div.hpp diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/dotprod.hpp b/hal/riscv-rvv/hal_rvv_1p0/dotprod.hpp similarity index 99% rename from 3rdparty/hal_rvv/hal_rvv_1p0/dotprod.hpp rename to hal/riscv-rvv/hal_rvv_1p0/dotprod.hpp index 1f53aa56b1..e16a97cf6a 100644 --- a/3rdparty/hal_rvv/hal_rvv_1p0/dotprod.hpp +++ b/hal/riscv-rvv/hal_rvv_1p0/dotprod.hpp @@ -231,4 +231,3 @@ inline int dotprod(const uchar *a_data, size_t a_step, const uchar *b_data, size }}} // cv::cv_hal_rvv::dotprod #endif // OPENCV_HAL_RVV_DOTPROD_HPP_INCLUDED - diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/dxt.hpp b/hal/riscv-rvv/hal_rvv_1p0/dxt.hpp similarity index 100% rename from 3rdparty/hal_rvv/hal_rvv_1p0/dxt.hpp rename to hal/riscv-rvv/hal_rvv_1p0/dxt.hpp diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/exp.hpp b/hal/riscv-rvv/hal_rvv_1p0/exp.hpp similarity index 100% rename from 3rdparty/hal_rvv/hal_rvv_1p0/exp.hpp rename to hal/riscv-rvv/hal_rvv_1p0/exp.hpp diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/filter.hpp b/hal/riscv-rvv/hal_rvv_1p0/filter.hpp similarity index 100% rename from 3rdparty/hal_rvv/hal_rvv_1p0/filter.hpp rename to hal/riscv-rvv/hal_rvv_1p0/filter.hpp diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/flip.hpp b/hal/riscv-rvv/hal_rvv_1p0/flip.hpp similarity index 100% rename from 3rdparty/hal_rvv/hal_rvv_1p0/flip.hpp rename to hal/riscv-rvv/hal_rvv_1p0/flip.hpp diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/histogram.hpp b/hal/riscv-rvv/hal_rvv_1p0/histogram.hpp similarity index 100% rename from 3rdparty/hal_rvv/hal_rvv_1p0/histogram.hpp rename to hal/riscv-rvv/hal_rvv_1p0/histogram.hpp diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/integral.hpp b/hal/riscv-rvv/hal_rvv_1p0/integral.hpp similarity index 100% rename from 3rdparty/hal_rvv/hal_rvv_1p0/integral.hpp rename to hal/riscv-rvv/hal_rvv_1p0/integral.hpp diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/log.hpp b/hal/riscv-rvv/hal_rvv_1p0/log.hpp similarity index 100% rename from 3rdparty/hal_rvv/hal_rvv_1p0/log.hpp rename to hal/riscv-rvv/hal_rvv_1p0/log.hpp diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/lu.hpp b/hal/riscv-rvv/hal_rvv_1p0/lu.hpp similarity index 100% rename from 3rdparty/hal_rvv/hal_rvv_1p0/lu.hpp rename to hal/riscv-rvv/hal_rvv_1p0/lu.hpp diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/lut.hpp b/hal/riscv-rvv/hal_rvv_1p0/lut.hpp similarity index 100% rename from 3rdparty/hal_rvv/hal_rvv_1p0/lut.hpp rename to hal/riscv-rvv/hal_rvv_1p0/lut.hpp diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/magnitude.hpp b/hal/riscv-rvv/hal_rvv_1p0/magnitude.hpp similarity index 100% rename from 3rdparty/hal_rvv/hal_rvv_1p0/magnitude.hpp rename to hal/riscv-rvv/hal_rvv_1p0/magnitude.hpp diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/mean.hpp b/hal/riscv-rvv/hal_rvv_1p0/mean.hpp similarity index 100% rename from 3rdparty/hal_rvv/hal_rvv_1p0/mean.hpp rename to hal/riscv-rvv/hal_rvv_1p0/mean.hpp diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/merge.hpp b/hal/riscv-rvv/hal_rvv_1p0/merge.hpp similarity index 100% rename from 3rdparty/hal_rvv/hal_rvv_1p0/merge.hpp rename to hal/riscv-rvv/hal_rvv_1p0/merge.hpp diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/minmax.hpp b/hal/riscv-rvv/hal_rvv_1p0/minmax.hpp similarity index 100% rename from 3rdparty/hal_rvv/hal_rvv_1p0/minmax.hpp rename to hal/riscv-rvv/hal_rvv_1p0/minmax.hpp diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/moments.hpp b/hal/riscv-rvv/hal_rvv_1p0/moments.hpp similarity index 100% rename from 3rdparty/hal_rvv/hal_rvv_1p0/moments.hpp rename to hal/riscv-rvv/hal_rvv_1p0/moments.hpp diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/norm.hpp b/hal/riscv-rvv/hal_rvv_1p0/norm.hpp similarity index 100% rename from 3rdparty/hal_rvv/hal_rvv_1p0/norm.hpp rename to hal/riscv-rvv/hal_rvv_1p0/norm.hpp diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/norm_diff.hpp b/hal/riscv-rvv/hal_rvv_1p0/norm_diff.hpp similarity index 100% rename from 3rdparty/hal_rvv/hal_rvv_1p0/norm_diff.hpp rename to hal/riscv-rvv/hal_rvv_1p0/norm_diff.hpp diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/norm_hamming.hpp b/hal/riscv-rvv/hal_rvv_1p0/norm_hamming.hpp similarity index 100% rename from 3rdparty/hal_rvv/hal_rvv_1p0/norm_hamming.hpp rename to hal/riscv-rvv/hal_rvv_1p0/norm_hamming.hpp diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/polar_to_cart.hpp b/hal/riscv-rvv/hal_rvv_1p0/polar_to_cart.hpp similarity index 100% rename from 3rdparty/hal_rvv/hal_rvv_1p0/polar_to_cart.hpp rename to hal/riscv-rvv/hal_rvv_1p0/polar_to_cart.hpp diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/pyramids.hpp b/hal/riscv-rvv/hal_rvv_1p0/pyramids.hpp similarity index 100% rename from 3rdparty/hal_rvv/hal_rvv_1p0/pyramids.hpp rename to hal/riscv-rvv/hal_rvv_1p0/pyramids.hpp diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/qr.hpp b/hal/riscv-rvv/hal_rvv_1p0/qr.hpp similarity index 100% rename from 3rdparty/hal_rvv/hal_rvv_1p0/qr.hpp rename to hal/riscv-rvv/hal_rvv_1p0/qr.hpp diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/resize.hpp b/hal/riscv-rvv/hal_rvv_1p0/resize.hpp similarity index 100% rename from 3rdparty/hal_rvv/hal_rvv_1p0/resize.hpp rename to hal/riscv-rvv/hal_rvv_1p0/resize.hpp diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/sincos.hpp b/hal/riscv-rvv/hal_rvv_1p0/sincos.hpp similarity index 100% rename from 3rdparty/hal_rvv/hal_rvv_1p0/sincos.hpp rename to hal/riscv-rvv/hal_rvv_1p0/sincos.hpp diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/split.hpp b/hal/riscv-rvv/hal_rvv_1p0/split.hpp similarity index 100% rename from 3rdparty/hal_rvv/hal_rvv_1p0/split.hpp rename to hal/riscv-rvv/hal_rvv_1p0/split.hpp diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/sqrt.hpp b/hal/riscv-rvv/hal_rvv_1p0/sqrt.hpp similarity index 100% rename from 3rdparty/hal_rvv/hal_rvv_1p0/sqrt.hpp rename to hal/riscv-rvv/hal_rvv_1p0/sqrt.hpp diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/svd.hpp b/hal/riscv-rvv/hal_rvv_1p0/svd.hpp similarity index 99% rename from 3rdparty/hal_rvv/hal_rvv_1p0/svd.hpp rename to hal/riscv-rvv/hal_rvv_1p0/svd.hpp index dfe644ad06..2ecad0671e 100644 --- a/3rdparty/hal_rvv/hal_rvv_1p0/svd.hpp +++ b/hal/riscv-rvv/hal_rvv_1p0/svd.hpp @@ -243,7 +243,7 @@ inline int SVD(T* src, size_t src_step, T* w, T*, size_t, T* vt, size_t vt_step, } } } - + auto vec_sum = RVV_T::vmv(0, vlmax); for( k = 0; k < m; k += vl ) { diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/thresh.hpp b/hal/riscv-rvv/hal_rvv_1p0/thresh.hpp similarity index 99% rename from 3rdparty/hal_rvv/hal_rvv_1p0/thresh.hpp rename to hal/riscv-rvv/hal_rvv_1p0/thresh.hpp index 5842540c35..738e3d5012 100644 --- a/3rdparty/hal_rvv/hal_rvv_1p0/thresh.hpp +++ b/hal/riscv-rvv/hal_rvv_1p0/thresh.hpp @@ -298,9 +298,9 @@ static inline int adaptiveThreshold(int start, int end, const uchar* src_data, s { if (i >= 0 && i < height) { - for (int j = 0; j < left; j++) + for (int j = 0; j < left; j++) process(i, j); - for (int j = right; j < width; j++) + for (int j = right; j < width; j++) process(i, j); int vl; @@ -378,9 +378,9 @@ static inline int adaptiveThreshold(int start, int end, const uchar* src_data, s { if (i >= 0 && i < height) { - for (int j = 0; j < left; j++) + for (int j = 0; j < left; j++) process(i, j); - for (int j = right; j < width; j++) + for (int j = right; j < width; j++) process(i, j); int vl; diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/transpose.hpp b/hal/riscv-rvv/hal_rvv_1p0/transpose.hpp similarity index 100% rename from 3rdparty/hal_rvv/hal_rvv_1p0/transpose.hpp rename to hal/riscv-rvv/hal_rvv_1p0/transpose.hpp diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/types.hpp b/hal/riscv-rvv/hal_rvv_1p0/types.hpp similarity index 100% rename from 3rdparty/hal_rvv/hal_rvv_1p0/types.hpp rename to hal/riscv-rvv/hal_rvv_1p0/types.hpp diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/warp.hpp b/hal/riscv-rvv/hal_rvv_1p0/warp.hpp similarity index 99% rename from 3rdparty/hal_rvv/hal_rvv_1p0/warp.hpp rename to hal/riscv-rvv/hal_rvv_1p0/warp.hpp index d9fcf9c109..f207c7cb95 100644 --- a/3rdparty/hal_rvv/hal_rvv_1p0/warp.hpp +++ b/hal/riscv-rvv/hal_rvv_1p0/warp.hpp @@ -834,7 +834,7 @@ inline int remap32f(int src_type, const uchar *src_data, size_t src_step, int sr case CV_HAL_INTER_NEAREST*100 + CV_32FC1: case CV_HAL_INTER_LINEAR*100 + CV_32FC1: return invoke(dst_width, dst_height, {remap32fC1}, s16, src_data, src_step, src_width, src_height, dst_data, dst_step, dst_width, mapx, mapx_step, mapy, mapy_step, interpolation, border_type, border_value); - + case CV_HAL_INTER_CUBIC*100 + CV_8UC1: return invoke(dst_width, dst_height, {remap32fCubic}, s16, src_data, src_step, src_width, src_height, dst_data, dst_step, dst_width, mapx, mapx_step, mapy, mapy_step, interpolation, border_type, border_value); case CV_HAL_INTER_CUBIC*100 + CV_16UC1: diff --git a/3rdparty/hal_rvv/version/hal_rvv_071.hpp b/hal/riscv-rvv/version/hal_rvv_071.hpp similarity index 100% rename from 3rdparty/hal_rvv/version/hal_rvv_071.hpp rename to hal/riscv-rvv/version/hal_rvv_071.hpp From 55a063f025e47c5c89c91bb188f88e729aeed46a Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Fri, 25 Apr 2025 15:03:22 +0300 Subject: [PATCH 91/94] Enable GIF support by default. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 870bfdd723..93a7ad2799 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -435,7 +435,7 @@ OCV_OPTION(WITH_ITT "Include Intel ITT support" ON OCV_OPTION(WITH_PROTOBUF "Enable libprotobuf" ON VISIBLE_IF TRUE VERIFY HAVE_PROTOBUF) -OCV_OPTION(WITH_IMGCODEC_GIF "Include GIF support" OFF +OCV_OPTION(WITH_IMGCODEC_GIF "Include GIF support" ON VISIBLE_IF TRUE VERIFY HAVE_IMGCODEC_GIF) OCV_OPTION(WITH_IMGCODEC_HDR "Include HDR support" ON From c774dd41cf40c902d48c53903da9209c3a53a226 Mon Sep 17 00:00:00 2001 From: utibenkei Date: Sat, 26 Apr 2025 01:31:43 +0900 Subject: [PATCH 92/94] Add CV_WRAP to registerOutput for language bindings support --- modules/dnn/include/opencv2/dnn/dnn.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/dnn/include/opencv2/dnn/dnn.hpp b/modules/dnn/include/opencv2/dnn/dnn.hpp index 779318c33f..e153738215 100644 --- a/modules/dnn/include/opencv2/dnn/dnn.hpp +++ b/modules/dnn/include/opencv2/dnn/dnn.hpp @@ -606,7 +606,7 @@ CV__DNN_INLINE_NS_BEGIN * * @returns index of bound layer (the same as layerId or newly created) */ - int registerOutput(const std::string& outputName, int layerId, int outputPort); + CV_WRAP int registerOutput(const std::string& outputName, int layerId, int outputPort); /** @brief Sets outputs names of the network input pseudo layer. * From 2fb786532a343b2d16134360d1c01245c87229ba Mon Sep 17 00:00:00 2001 From: Yuantao Feng Date: Sat, 26 Apr 2025 16:08:29 +0800 Subject: [PATCH 93/94] Merge pull request #27257 from fengyuentau:4x/hal_rvv/flip_opt hal_rvv: further optimized flip #27257 Checklist: - [x] flipX - [x] flipY - [x] flipXY ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- hal/riscv-rvv/hal_rvv_1p0/flip.hpp | 146 ++++++++++++++++++++--------- modules/core/perf/perf_flip.cpp | 2 +- 2 files changed, 103 insertions(+), 45 deletions(-) diff --git a/hal/riscv-rvv/hal_rvv_1p0/flip.hpp b/hal/riscv-rvv/hal_rvv_1p0/flip.hpp index 8d58532832..d5b6994465 100644 --- a/hal/riscv-rvv/hal_rvv_1p0/flip.hpp +++ b/hal/riscv-rvv/hal_rvv_1p0/flip.hpp @@ -3,6 +3,7 @@ // of this distribution and at http://opencv.org/license.html. // Copyright (C) 2025, Institute of Software, Chinese Academy of Sciences. +// Copyright (C) 2025, SpaceMIT Inc., all rights reserved. #ifndef OPENCV_HAL_RVV_FLIP_HPP_INCLUDED #define OPENCV_HAL_RVV_FLIP_HPP_INCLUDED @@ -17,6 +18,75 @@ namespace cv { namespace cv_hal_rvv { #undef cv_hal_flip #define cv_hal_flip cv::cv_hal_rvv::flip +namespace { + +#define CV_HAL_RVV_FLIPY_C1(name, _Tps, RVV) \ +inline void flip_##name(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, int src_width, int src_height, int flip_mode) { \ + for (int h = 0; h < src_height; h++) { \ + const _Tps* src_row = (const _Tps*)(src_data + src_step * h); \ + _Tps* dst_row = (_Tps*)(dst_data + dst_step * (flip_mode < 0 ? (src_height - h) : (h + 1))); \ + int vl; \ + for (int w = 0; w < src_width; w += vl) { \ + vl = RVV::setvl(src_width - w); \ + RVV::VecType indices = __riscv_vrsub(RVV::vid(vl), vl - 1, vl); \ + auto v = RVV::vload(src_row + w, vl); \ + RVV::vstore(dst_row - w - vl, __riscv_vrgather(v, indices, vl), vl); \ + } \ + } \ +} +CV_HAL_RVV_FLIPY_C1(8UC1, uchar, RVV_U8M8) +CV_HAL_RVV_FLIPY_C1(16UC1, ushort, RVV_U16M8) +CV_HAL_RVV_FLIPY_C1(32UC1, unsigned, RVV_U32M8) +CV_HAL_RVV_FLIPY_C1(64UC1, uint64_t, RVV_U64M8) + +#if defined (__clang__) && __clang_major__ < 18 +#define OPENCV_HAL_IMPL_RVV_VCREATE_x3(suffix, width, v0, v1, v2) \ + __riscv_vset_v_##suffix##m##width##_##suffix##m##width##x3(v, 0, v0); \ + v = __riscv_vset(v, 1, v1); \ + v = __riscv_vset(v, 2, v2); +#define __riscv_vcreate_v_u8m2x3(v0, v1, v2) OPENCV_HAL_IMPL_RVV_VCREATE_x3(u8, 2, v0, v1, v2) +#define __riscv_vcreate_v_u16m2x3(v0, v1, v2) OPENCV_HAL_IMPL_RVV_VCREATE_x3(u16, 2, v0, v1, v2) +#define __riscv_vcreate_v_u32m2x3(v0, v1, v2) OPENCV_HAL_IMPL_RVV_VCREATE_x3(u32, 2, v0, v1, v2) +#define __riscv_vcreate_v_u64m2x3(v0, v1, v2) OPENCV_HAL_IMPL_RVV_VCREATE_x3(u64, 2, v0, v1, v2) +#endif + +#define CV_HAL_RVV_FLIPY_C3_TYPES(width) \ +struct RVV_C3_U##width##M2 : RVV_U##width##M2 { \ + static inline vuint##width##m2x3_t vload3(const uint##width##_t *base, size_t vl) { return __riscv_vlseg3e##width##_v_u##width##m2x3(base, vl); } \ + static inline vuint##width##m2x3_t vflip3(const vuint##width##m2x3_t &v_tuple, const vuint##width##m2_t &indices, size_t vl) { \ + auto v0 = __riscv_vrgather(__riscv_vget_u##width##m2(v_tuple, 0), indices, vl); \ + auto v1 = __riscv_vrgather(__riscv_vget_u##width##m2(v_tuple, 1), indices, vl); \ + auto v2 = __riscv_vrgather(__riscv_vget_u##width##m2(v_tuple, 2), indices, vl); \ + vuint##width##m2x3_t v = __riscv_vcreate_v_u##width##m2x3(v0, v1, v2); \ + return v; \ + } \ + static inline void vstore3(uint##width##_t *base, const vuint##width##m2x3_t &v_tuple, size_t vl) { __riscv_vsseg3e##width(base, v_tuple, vl); } \ +}; +CV_HAL_RVV_FLIPY_C3_TYPES(8) +CV_HAL_RVV_FLIPY_C3_TYPES(16) +CV_HAL_RVV_FLIPY_C3_TYPES(32) +CV_HAL_RVV_FLIPY_C3_TYPES(64) + +#define CV_HAL_RVV_FLIPY_C3(name, _Tps, RVV) \ +inline void flip_##name(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, int src_width, int src_height, int flip_mode) { \ + for (int h = 0; h < src_height; h++) { \ + const _Tps* src_row = (const _Tps*)(src_data + src_step * h); \ + _Tps* dst_row = (_Tps*)(dst_data + dst_step * (flip_mode < 0 ? (src_height - h) : (h + 1))); \ + int vl; \ + for (int w = 0; w < src_width; w += vl) { \ + vl = RVV::setvl(src_width - w); \ + RVV::VecType indices = __riscv_vrsub(RVV::vid(vl), vl - 1, vl); \ + auto v = RVV::vload3(src_row + 3 * w, vl); \ + auto flipped = RVV::vflip3(v, indices, vl); \ + RVV::vstore3(dst_row - 3 * (w + vl), flipped, vl); \ + } \ + } \ +} +CV_HAL_RVV_FLIPY_C3(8UC3, uchar, RVV_C3_U8M2) +CV_HAL_RVV_FLIPY_C3(16UC3, ushort, RVV_C3_U16M2) +CV_HAL_RVV_FLIPY_C3(32UC3, unsigned, RVV_C3_U32M2) +CV_HAL_RVV_FLIPY_C3(64UC3, uint64_t, RVV_C3_U64M2) + struct FlipVlen256 { using SrcType = RVV_U8M8; @@ -51,38 +121,6 @@ inline void flipFillBuffer(T* buf, size_t len, int esz) buf[j] = (T)(i + j); } -inline void flipX(int esz, - const uchar* src_data, - size_t src_step, - int src_width, - int src_height, - uchar* dst_data, - size_t dst_step) -{ - size_t w = (size_t)src_width * esz; - auto src0 = src_data, src1 = src_data + src_step * (src_height - 1); - auto dst0 = dst_data, dst1 = dst_data + dst_step * (src_height - 1); - size_t vl; - for (src_height -= 2; src_height >= 0; - src_height -= 2, src0 += src_step, dst0 += dst_step, src1 -= src_step, dst1 -= dst_step) - { - for (size_t i = 0; i < w; i += vl) - { - vl = __riscv_vsetvl_e8m8(w - i); - __riscv_vse8(dst1 + i, __riscv_vle8_v_u8m8(src0 + i, vl), vl); - __riscv_vse8(dst0 + i, __riscv_vle8_v_u8m8(src1 + i, vl), vl); - } - } - if (src_height == -1) - { - for (size_t i = 0; i < w; i += (int)vl) - { - vl = __riscv_vsetvl_e8m8(w - i); - __riscv_vse8(dst0 + i, __riscv_vle8_v_u8m8(src1 + i, vl), vl); - } - } -} - template @@ -192,24 +230,44 @@ inline void flipXY(int esz, } } -inline int flip(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 flip_mode) +} // namespace anonymous + +inline int flip(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 flip_mode) { - if (src_width < 0 || src_height < 0 || src_data == dst_data) + int esz = CV_ELEM_SIZE(src_type); + if (src_width < 0 || src_height < 0 || src_data == dst_data || esz > 32) return CV_HAL_ERROR_NOT_IMPLEMENTED; - int esz = CV_ELEM_SIZE(src_type); if (flip_mode == 0) { - flipX(esz, src_data, src_step, src_width, src_height, dst_data, dst_step); + for (int h = 0; h < src_height; h++) { + const uchar* src_row = src_data + src_step * h; + uchar* dst_row = dst_data + dst_step * (src_height - h - 1); + std::memcpy(dst_row, src_row, esz * src_width); + } + return CV_HAL_ERROR_OK; } - else if (flip_mode > 0) + + using FlipFunc = void (*)(const uchar*, size_t, uchar*, size_t, int, int, int); + static FlipFunc flip_func_tab[] = { + 0, flip_8UC1, flip_16UC1, flip_8UC3, + flip_32UC1, 0, flip_16UC3, 0, + flip_64UC1, 0, 0, 0, + flip_32UC3, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + flip_64UC3, 0, 0, 0, + 0, 0, 0, 0, + 0 + }; + FlipFunc func = flip_func_tab[esz]; + if (func) { + func(src_data, src_step, dst_data, dst_step, src_width, src_height, flip_mode); + return CV_HAL_ERROR_OK; + } + + if (flip_mode > 0) { if (__riscv_vlenb() * 8 <= 256) flipY(esz, src_data, src_step, src_width, src_height, dst_data, dst_step); diff --git a/modules/core/perf/perf_flip.cpp b/modules/core/perf/perf_flip.cpp index 2a24d394c0..6e89cd6e9a 100644 --- a/modules/core/perf/perf_flip.cpp +++ b/modules/core/perf/perf_flip.cpp @@ -15,7 +15,7 @@ enum }; #define FLIP_SIZES szQVGA, szVGA, sz1080p -#define FLIP_TYPES CV_8UC1, CV_8UC3, CV_8UC4 +#define FLIP_TYPES CV_8UC1, CV_8UC2, CV_8UC3, CV_8UC4, CV_8SC1, CV_16SC1, CV_16SC2, CV_16SC3, CV_16SC4, CV_32SC1, CV_32FC1 #define FLIP_CODES FLIP_X, FLIP_Y, FLIP_XY CV_FLAGS(FlipCode, FLIP_X, FLIP_Y, FLIP_XY); From ab5a65b5a26e5ecbebffac419241a2d895e6b9cc Mon Sep 17 00:00:00 2001 From: fengyuentau Date: Mon, 28 Apr 2025 14:18:48 +0800 Subject: [PATCH 94/94] perf: implemented flip_inplace in hal_rvv to boost cv::rotate on RISC-V platforms --- hal/riscv-rvv/hal_rvv_1p0/flip.hpp | 135 +++++++++++++++++++++++------ 1 file changed, 109 insertions(+), 26 deletions(-) diff --git a/hal/riscv-rvv/hal_rvv_1p0/flip.hpp b/hal/riscv-rvv/hal_rvv_1p0/flip.hpp index d5b6994465..02abeb6e93 100644 --- a/hal/riscv-rvv/hal_rvv_1p0/flip.hpp +++ b/hal/riscv-rvv/hal_rvv_1p0/flip.hpp @@ -13,6 +13,17 @@ #include #include "hal_rvv_1p0/types.hpp" +#if defined (__clang__) && __clang_major__ < 18 +#define OPENCV_HAL_IMPL_RVV_VCREATE_x3(suffix, width, v0, v1, v2) \ + __riscv_vset_v_##suffix##m##width##_##suffix##m##width##x3(v, 0, v0); \ + v = __riscv_vset(v, 1, v1); \ + v = __riscv_vset(v, 2, v2); +#define __riscv_vcreate_v_u8m2x3(v0, v1, v2) OPENCV_HAL_IMPL_RVV_VCREATE_x3(u8, 2, v0, v1, v2) +#define __riscv_vcreate_v_u16m2x3(v0, v1, v2) OPENCV_HAL_IMPL_RVV_VCREATE_x3(u16, 2, v0, v1, v2) +#define __riscv_vcreate_v_u32m2x3(v0, v1, v2) OPENCV_HAL_IMPL_RVV_VCREATE_x3(u32, 2, v0, v1, v2) +#define __riscv_vcreate_v_u64m2x3(v0, v1, v2) OPENCV_HAL_IMPL_RVV_VCREATE_x3(u64, 2, v0, v1, v2) +#endif + namespace cv { namespace cv_hal_rvv { #undef cv_hal_flip @@ -20,7 +31,7 @@ namespace cv { namespace cv_hal_rvv { namespace { -#define CV_HAL_RVV_FLIPY_C1(name, _Tps, RVV) \ +#define CV_HAL_RVV_FLIP_C1(name, _Tps, RVV) \ inline void flip_##name(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, int src_width, int src_height, int flip_mode) { \ for (int h = 0; h < src_height; h++) { \ const _Tps* src_row = (const _Tps*)(src_data + src_step * h); \ @@ -34,23 +45,35 @@ inline void flip_##name(const uchar* src_data, size_t src_step, uchar* dst_data, } \ } \ } -CV_HAL_RVV_FLIPY_C1(8UC1, uchar, RVV_U8M8) -CV_HAL_RVV_FLIPY_C1(16UC1, ushort, RVV_U16M8) -CV_HAL_RVV_FLIPY_C1(32UC1, unsigned, RVV_U32M8) -CV_HAL_RVV_FLIPY_C1(64UC1, uint64_t, RVV_U64M8) +CV_HAL_RVV_FLIP_C1(8UC1, uchar, RVV_U8M8) +CV_HAL_RVV_FLIP_C1(16UC1, ushort, RVV_U16M8) +CV_HAL_RVV_FLIP_C1(32UC1, unsigned, RVV_U32M8) +CV_HAL_RVV_FLIP_C1(64UC1, uint64_t, RVV_U64M8) -#if defined (__clang__) && __clang_major__ < 18 -#define OPENCV_HAL_IMPL_RVV_VCREATE_x3(suffix, width, v0, v1, v2) \ - __riscv_vset_v_##suffix##m##width##_##suffix##m##width##x3(v, 0, v0); \ - v = __riscv_vset(v, 1, v1); \ - v = __riscv_vset(v, 2, v2); -#define __riscv_vcreate_v_u8m2x3(v0, v1, v2) OPENCV_HAL_IMPL_RVV_VCREATE_x3(u8, 2, v0, v1, v2) -#define __riscv_vcreate_v_u16m2x3(v0, v1, v2) OPENCV_HAL_IMPL_RVV_VCREATE_x3(u16, 2, v0, v1, v2) -#define __riscv_vcreate_v_u32m2x3(v0, v1, v2) OPENCV_HAL_IMPL_RVV_VCREATE_x3(u32, 2, v0, v1, v2) -#define __riscv_vcreate_v_u64m2x3(v0, v1, v2) OPENCV_HAL_IMPL_RVV_VCREATE_x3(u64, 2, v0, v1, v2) -#endif +#define CV_HAL_RVV_FLIP_INPLACE_C1(name, _Tps, RVV) \ +inline void flip_inplace_##name(uchar* data, size_t step, int width, int height, int flip_mode) { \ + auto new_height = (flip_mode < 0 ? height / 2 : height); \ + auto new_width = width / 2; \ + for (int h = 0; h < new_height; h++) { \ + _Tps* row_begin = (_Tps*)(data + step * h); \ + _Tps* row_end = (_Tps*)(data + step * (flip_mode < 0 ? (new_height - h) : (h + 1))); \ + int vl; \ + for (int w = 0; w < new_width; w += vl) { \ + vl = RVV::setvl(new_width - w); \ + RVV::VecType indices = __riscv_vrsub(RVV::vid(vl), vl - 1, vl); \ + auto v_left = RVV::vload(row_begin + w, vl); \ + auto v_right = RVV::vload(row_end - w - vl, vl); \ + RVV::vstore(row_begin + w, __riscv_vrgather(v_right, indices, vl), vl); \ + RVV::vstore(row_end - w - vl, __riscv_vrgather(v_left, indices, vl), vl); \ + } \ + } \ +} +CV_HAL_RVV_FLIP_INPLACE_C1(8UC1, uchar, RVV_U8M8) +CV_HAL_RVV_FLIP_INPLACE_C1(16UC1, ushort, RVV_U16M8) +CV_HAL_RVV_FLIP_INPLACE_C1(32UC1, unsigned, RVV_U32M8) +CV_HAL_RVV_FLIP_INPLACE_C1(64UC1, uint64_t, RVV_U64M8) -#define CV_HAL_RVV_FLIPY_C3_TYPES(width) \ +#define CV_HAL_RVV_FLIP_C3_TYPES(width) \ struct RVV_C3_U##width##M2 : RVV_U##width##M2 { \ static inline vuint##width##m2x3_t vload3(const uint##width##_t *base, size_t vl) { return __riscv_vlseg3e##width##_v_u##width##m2x3(base, vl); } \ static inline vuint##width##m2x3_t vflip3(const vuint##width##m2x3_t &v_tuple, const vuint##width##m2_t &indices, size_t vl) { \ @@ -62,12 +85,12 @@ struct RVV_C3_U##width##M2 : RVV_U##width##M2 { \ } \ static inline void vstore3(uint##width##_t *base, const vuint##width##m2x3_t &v_tuple, size_t vl) { __riscv_vsseg3e##width(base, v_tuple, vl); } \ }; -CV_HAL_RVV_FLIPY_C3_TYPES(8) -CV_HAL_RVV_FLIPY_C3_TYPES(16) -CV_HAL_RVV_FLIPY_C3_TYPES(32) -CV_HAL_RVV_FLIPY_C3_TYPES(64) +CV_HAL_RVV_FLIP_C3_TYPES(8) +CV_HAL_RVV_FLIP_C3_TYPES(16) +CV_HAL_RVV_FLIP_C3_TYPES(32) +CV_HAL_RVV_FLIP_C3_TYPES(64) -#define CV_HAL_RVV_FLIPY_C3(name, _Tps, RVV) \ +#define CV_HAL_RVV_FLIP_C3(name, _Tps, RVV) \ inline void flip_##name(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, int src_width, int src_height, int flip_mode) { \ for (int h = 0; h < src_height; h++) { \ const _Tps* src_row = (const _Tps*)(src_data + src_step * h); \ @@ -82,10 +105,35 @@ inline void flip_##name(const uchar* src_data, size_t src_step, uchar* dst_data, } \ } \ } -CV_HAL_RVV_FLIPY_C3(8UC3, uchar, RVV_C3_U8M2) -CV_HAL_RVV_FLIPY_C3(16UC3, ushort, RVV_C3_U16M2) -CV_HAL_RVV_FLIPY_C3(32UC3, unsigned, RVV_C3_U32M2) -CV_HAL_RVV_FLIPY_C3(64UC3, uint64_t, RVV_C3_U64M2) +CV_HAL_RVV_FLIP_C3(8UC3, uchar, RVV_C3_U8M2) +CV_HAL_RVV_FLIP_C3(16UC3, ushort, RVV_C3_U16M2) +CV_HAL_RVV_FLIP_C3(32UC3, unsigned, RVV_C3_U32M2) +CV_HAL_RVV_FLIP_C3(64UC3, uint64_t, RVV_C3_U64M2) + +#define CV_HAL_RVV_FLIP_INPLACE_C3(name, _Tps, RVV) \ +inline void flip_inplace_##name(uchar* data, size_t step, int width, int height, int flip_mode) { \ + auto new_height = (flip_mode < 0 ? height / 2 : height); \ + auto new_width = width / 2; \ + for (int h = 0; h < new_height; h++) { \ + _Tps* row_begin = (_Tps*)(data + step * h); \ + _Tps* row_end = (_Tps*)(data + step * (flip_mode < 0 ? (new_height - h) : (h + 1))); \ + int vl; \ + for (int w = 0; w < new_width; w += vl) { \ + vl = RVV::setvl(new_width - w); \ + RVV::VecType indices = __riscv_vrsub(RVV::vid(vl), vl - 1, vl); \ + auto v_left = RVV::vload3(row_begin + 3 * w, vl); \ + auto flipped_left = RVV::vflip3(v_left, indices, vl); \ + auto v_right = RVV::vload3(row_end - 3 * (w + vl), vl); \ + auto flipped_right = RVV::vflip3(v_right, indices, vl); \ + RVV::vstore3(row_begin + 3 * w, flipped_right, vl); \ + RVV::vstore3(row_end - 3 * (w + vl), flipped_left, vl); \ + } \ + } \ +} +CV_HAL_RVV_FLIP_INPLACE_C3(8UC3, uchar, RVV_C3_U8M2) +CV_HAL_RVV_FLIP_INPLACE_C3(16UC3, ushort, RVV_C3_U16M2) +CV_HAL_RVV_FLIP_INPLACE_C3(32UC3, unsigned, RVV_C3_U32M2) +CV_HAL_RVV_FLIP_INPLACE_C3(64UC3, uint64_t, RVV_C3_U64M2) struct FlipVlen256 { @@ -232,13 +280,48 @@ inline void flipXY(int esz, } // namespace anonymous +inline int flip_inplace(int esz, uchar* data, size_t step, int width, int height, int flip_mode) { + if (flip_mode == 0) { + for (int h = 0; h < (height / 2); h++) { + uchar* top_row = data + step * h; + uchar* bottom_row = data + step * (height - h - 1); + std::swap_ranges(top_row, top_row + esz * width, bottom_row); + } + return CV_HAL_ERROR_OK; + } + + using FlipInplaceFunc = void (*)(uchar*, size_t, int, int, int); + static FlipInplaceFunc flip_inplace_func_tab[] = { + 0, flip_inplace_8UC1, flip_inplace_16UC1, flip_inplace_8UC3, + flip_inplace_32UC1, 0, flip_inplace_16UC3, 0, + flip_inplace_64UC1, 0, 0, 0, + flip_inplace_32UC3, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + flip_inplace_64UC3, 0, 0, 0, + 0, 0, 0, 0, + 0 + }; + FlipInplaceFunc func = flip_inplace_func_tab[esz]; + if (!func) { + return CV_HAL_ERROR_NOT_IMPLEMENTED; + } + func(data, step, width, height, flip_mode); + + return CV_HAL_ERROR_OK; +} + inline int flip(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 flip_mode) { int esz = CV_ELEM_SIZE(src_type); - if (src_width < 0 || src_height < 0 || src_data == dst_data || esz > 32) + if (src_width < 0 || src_height < 0 || esz > 32) return CV_HAL_ERROR_NOT_IMPLEMENTED; + if (src_data == dst_data) { + return flip_inplace(esz, dst_data, dst_step, src_width, src_height, flip_mode); + } + if (flip_mode == 0) { for (int h = 0; h < src_height; h++) {