From 84ca221184bf88665782d4bb277cb665aa254746 Mon Sep 17 00:00:00 2001 From: Sam Carlberg Date: Fri, 25 Jul 2025 00:28:29 -0400 Subject: [PATCH 01/14] Set Automatic-Module-Name manifest attribute This is a useful property for modularized libraries and applications to be able to use OpenCV A full module-info.java file for true compatibility with the Java module system would require compiling with Java 9+ and either use a multi-release JAR (which can be tricky to maintain) or compile the entire library with Java 9+ (which would break Android consumers on older SDK versions). In the interest of causing the least disruption, only an automatic module name is set so that modular Java 9+ consumers can use OpenCV but not break anybody else. --- modules/java/jar/MANIFEST.MF.in | 1 + modules/java/jar/build.xml.in | 1 + 2 files changed, 2 insertions(+) diff --git a/modules/java/jar/MANIFEST.MF.in b/modules/java/jar/MANIFEST.MF.in index 07d2fda79b..b26719ce22 100644 --- a/modules/java/jar/MANIFEST.MF.in +++ b/modules/java/jar/MANIFEST.MF.in @@ -3,3 +3,4 @@ Specification-Version: @OPENCV_VERSION@ Implementation-Title: OpenCV Implementation-Version: @OPENCV_VCSVERSION@ Implementation-Date: @OPENCV_TIMESTAMP@ +Automatic-Module-Name: org.opencv diff --git a/modules/java/jar/build.xml.in b/modules/java/jar/build.xml.in index 41ef1d55ba..58bd27727b 100644 --- a/modules/java/jar/build.xml.in +++ b/modules/java/jar/build.xml.in @@ -24,6 +24,7 @@ + From fb37e14cbfe2c1c9ff79dec78fe27073d2fa35ab Mon Sep 17 00:00:00 2001 From: rowdha fakier Date: Wed, 30 Jul 2025 20:37:03 +0200 Subject: [PATCH 02/14] Update py_intro.markdown Changed line 69 from "Remember, we together" to "Remember, together we..." I saw that this page encouraged us to contribute no matter how small the contribution is so I decided to "shoot my shot" as a beginner developer and fix a small grammatical error. Would be nice if I could be allowed to be recognized as a contributor with my contribution albeit pretty small, hopefully in the future, more meaningful contributions will be made! :) --- doc/py_tutorials/py_setup/py_intro/py_intro.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/py_tutorials/py_setup/py_intro/py_intro.markdown b/doc/py_tutorials/py_setup/py_intro/py_intro.markdown index ca05a8643d..21af3b7a8e 100644 --- a/doc/py_tutorials/py_setup/py_intro/py_intro.markdown +++ b/doc/py_tutorials/py_setup/py_intro/py_intro.markdown @@ -66,7 +66,7 @@ As new modules are added to OpenCV-Python, this tutorial will have to be expande familiar with a particular algorithm and can write up a tutorial including basic theory of the algorithm and code showing example usage, please do so. -Remember, we **together** can make this project a great success !!! +Remember,**together** we can make this project a great success !!! Contributors ------------ From 3dec85df7101926504c39a254c6348c680a2627d Mon Sep 17 00:00:00 2001 From: Jeremy Kruid Date: Thu, 31 Jul 2025 15:40:59 -0500 Subject: [PATCH 03/14] Update helpers.js to include .dlete --- modules/js/src/helpers.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/modules/js/src/helpers.js b/modules/js/src/helpers.js index d89414209a..ab88d18eec 100644 --- a/modules/js/src/helpers.js +++ b/modules/js/src/helpers.js @@ -397,3 +397,16 @@ Module['matFromImageData'] = function(imageData) { mat.data.set(imageData.data); return mat; }; +// Add Symbol.dispose support for using declaration in TypeScript 5.2+ and future JS +if ( + typeof Symbol !== "undefined" && + Symbol.dispose && + typeof cv !== "undefined" && + cv.Mat && + typeof cv.Mat.prototype.delete === "function" +) { + cv.Mat.prototype[Symbol.dispose] = cv.Mat.prototype.delete; + // Optionally repeat for other types that require manual cleanup: + if (cv.UMat) cv.UMat.prototype[Symbol.dispose] = cv.UMat.prototype.delete; + // Add more as OpenCV gains new manual-cleanup classes +} From 96fdbc58e12748a2b47c024b6b4ba7a3edc5e55e Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Tue, 5 Aug 2025 10:09:12 +0300 Subject: [PATCH 04/14] Convert grayscale frames into color in nteractive calibration preview --- apps/interactive-calibration/frameProcessor.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/interactive-calibration/frameProcessor.cpp b/apps/interactive-calibration/frameProcessor.cpp index aaeabe6fbb..6da0f1fabe 100644 --- a/apps/interactive-calibration/frameProcessor.cpp +++ b/apps/interactive-calibration/frameProcessor.cpp @@ -308,7 +308,10 @@ cv::Mat CalibProcessor::processFrame(const cv::Mat &frame) { cv::Mat frameCopy; cv::Mat frameCopyToSave; - frame.copyTo(frameCopy); + if (frame.channels() == 1) + cv::cvtColor(frame, frameCopy, cv::COLOR_GRAY2BGR); + else + frame.copyTo(frameCopy); bool isTemplateFound = false; mCurrentImagePoints.clear(); From 8d1ec466f07f991a7e9bb31b9e6cda27e770339c Mon Sep 17 00:00:00 2001 From: Suleyman TURKMEN Date: Thu, 7 Aug 2025 17:04:12 +0300 Subject: [PATCH 05/14] Merge pull request #27605 from sturkmen72:perf_imgcodecs Performance tests for writing and reading animations #27605 ### Pull Request Readiness Checklist related : https://github.com/opencv/opencv/pull/27496 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/perf/perf_decode_encode.cpp | 154 +++++++++++++++++- 1 file changed, 152 insertions(+), 2 deletions(-) diff --git a/modules/imgcodecs/perf/perf_decode_encode.cpp b/modules/imgcodecs/perf/perf_decode_encode.cpp index 5f675f7a44..bdef7882ee 100644 --- a/modules/imgcodecs/perf/perf_decode_encode.cpp +++ b/modules/imgcodecs/perf/perf_decode_encode.cpp @@ -7,10 +7,82 @@ namespace opencv_test { -#ifdef HAVE_PNG - using namespace perf; +static Animation makeCirclesAnimation(Size size = Size(320, 240), int type = CV_8UC4, int nbits = 8, int frameCount = 40) +{ + struct AnimatedCircle { + cv::Point2f pos; + cv::Point2f velocity; + float radius; + float radius_speed; + cv::Scalar color; + cv::Scalar border_color; + }; + + const int numCircles = 80; + const int maxval = (1 << nbits) - 1; + + cv::RNG rng = theRNG(); + std::vector circles; + Animation animation; + + // Initialize animated circles + for (int i = 0; i < numCircles; ++i) { + AnimatedCircle c; + c.pos = cv::Point2f(rng.uniform(0.f, (float)size.width), + rng.uniform(0.f, (float)size.height)); + c.velocity = cv::Point2f(rng.uniform(-2.f, 2.f), + rng.uniform(-2.f, 2.f)); + c.radius = rng.uniform(10.f, 40.f); + c.radius_speed = rng.uniform(-0.5f, 0.5f); + c.color = cv::Scalar(rng.uniform(0, maxval), + rng.uniform(0, maxval), + rng.uniform(0, maxval), + rng.uniform(230, maxval)); + c.border_color = c.color; + circles.push_back(c); + } + + // Generate frames + for (int frame = 0; frame < frameCount; ++frame) { + cv::Mat img(size, type, cv::Scalar(20, 0, 10, 128)); + + for (size_t i = 0; i < circles.size(); ++i) { + AnimatedCircle& c = circles[i]; + + // Update position + c.pos += c.velocity; + + // Bounce on edges + if (c.pos.x < 0 || c.pos.x > size.width) c.velocity.x *= -1; + if (c.pos.y < 0 || c.pos.y > size.height) c.velocity.y *= -1; + + // Update radius + c.radius += c.radius_speed; + if (c.radius < 10.f || c.radius > 80.f) { + c.radius_speed *= -1; + c.radius = std::max(10.f, std::min(c.radius, 80.f)); + } + + c.color = c.color - Scalar(c.velocity.x, 0, c.velocity.y, rng.uniform(1, 4)); + + // Draw + cv::circle(img, c.pos, (int)c.radius, c.color, cv::FILLED, cv::LINE_AA); + cv::circle(img, c.pos, (int)c.radius, c.border_color, 1, cv::LINE_AA); + } + + animation.frames.push_back(img); + animation.durations.push_back(20); // milliseconds + } + + for (int i = (int)animation.frames.size() - 1; i >= 0; --i) { + animation.frames.push_back(animation.frames[i].clone()); + animation.durations.push_back(15); + } + return animation; +} + typedef perf::TestBaseWithParam Decode; typedef perf::TestBaseWithParam Encode; @@ -32,7 +104,9 @@ const string exts[] = { #ifdef HAVE_JPEGXL ".jxl", #endif +#ifdef HAVE_PNG ".png", +#endif #ifdef HAVE_IMGCODEC_PXM ".ppm", #endif @@ -54,7 +128,9 @@ const string exts_multi[] = { #ifdef HAVE_IMGCODEC_GIF ".gif", #endif +#ifdef HAVE_PNG ".png", +#endif #ifdef HAVE_TIFF ".tiff", #endif @@ -63,6 +139,23 @@ const string exts_multi[] = { #endif }; +const string exts_anim[] = { +#ifdef HAVE_AVIF + ".avif", +#endif +#ifdef HAVE_IMGCODEC_GIF + ".gif", +#endif +#ifdef HAVE_PNG + ".png", +#endif +#ifdef HAVE_WEBP + ".webp", +#endif +}; + +#ifdef HAVE_PNG + PERF_TEST_P(Decode, bgr, testing::ValuesIn(exts)) { String filename = getDataPath("perf/1920x1080.png"); @@ -126,6 +219,63 @@ PERF_TEST_P(Encode, multi, testing::ValuesIn(exts_multi)) SANITY_CHECK_NOTHING(); } + #endif // HAVE_PNG +PERF_TEST_P(Encode, animation, testing::ValuesIn(exts_anim)) +{ + Animation animation = makeCirclesAnimation(); + + TEST_CYCLE() + { + vector buf; + EXPECT_TRUE(imencodeanimation(GetParam().c_str(), animation, buf)); + } + + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P(Encode, multi_page, testing::ValuesIn(exts_multi)) +{ + Animation animation = makeCirclesAnimation(); + + TEST_CYCLE() + { + vector buf; + EXPECT_TRUE(imencodemulti(GetParam().c_str(), animation.frames, buf)); + } + + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P(Decode, animation, testing::ValuesIn(exts_anim)) +{ + Animation animation = makeCirclesAnimation(); + vector buf; + ASSERT_TRUE(imencodeanimation(GetParam().c_str(), animation, buf)); + + TEST_CYCLE() + { + Animation tmp_animation; + EXPECT_TRUE(imdecodeanimation(buf, tmp_animation)); + } + + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P(Decode, multi_page, testing::ValuesIn(exts_multi)) +{ + Animation animation = makeCirclesAnimation(); + vector buf; + ASSERT_TRUE(imencodemulti(GetParam().c_str(), animation.frames, buf)); + + TEST_CYCLE() + { + vector tmp_frames; + EXPECT_TRUE(imdecodemulti(buf, IMREAD_UNCHANGED, tmp_frames)); + } + + SANITY_CHECK_NOTHING(); +} + } // namespace From e31ff001042ea2321c3d67266a8d240dc836ecd2 Mon Sep 17 00:00:00 2001 From: Pierre Chatelier Date: Thu, 7 Aug 2025 16:19:55 +0200 Subject: [PATCH 06/14] Merge pull request #27599 from chacha21:drawing_stack_alloc optimize some drawing with stack allocation #27599 Some drawings can try a stack allocation instead of a std::vector ### 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/imgproc/src/drawing.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/modules/imgproc/src/drawing.cpp b/modules/imgproc/src/drawing.cpp index d0368cfa95..e3158750c5 100644 --- a/modules/imgproc/src/drawing.cpp +++ b/modules/imgproc/src/drawing.cpp @@ -2034,9 +2034,12 @@ void fillPoly( InputOutputArray _img, const Point** pts, const int* npts, int nc edges.reserve( total + 1 ); for (i = 0; i < ncontours; i++) { - if (npts[i] > 0 && pts[i]) + const Point* currentContour = pts[i]; + const int currentContourLength = npts[i]; + if ( (currentContourLength > 0) && currentContour ) { - std::vector _pts(pts[i], pts[i] + npts[i]); + AutoBuffer _pts(currentContourLength); + std::copy(currentContour, currentContour+currentContourLength, _pts.data()); CollectPolyEdges(img, _pts.data(), npts[i], edges, buf, line_type, shift, offset); } } @@ -2063,8 +2066,11 @@ void polylines( InputOutputArray _img, const Point* const* pts, const int* npts, for( int i = 0; i < ncontours; i++ ) { - std::vector _pts(pts[i], pts[i]+npts[i]); - PolyLine( img, _pts.data(), npts[i], isClosed, buf, thickness, line_type, shift ); + const Point* currentContour = pts[i]; + const int currentContourLength = npts[i]; + AutoBuffer _pts(currentContourLength); + std::copy(currentContour, currentContour+currentContourLength, _pts.data()); + PolyLine( img, _pts.data(), currentContourLength, isClosed, buf, thickness, line_type, shift ); } } From 12efb8499de6093835c0987130ef8ac815702dfd Mon Sep 17 00:00:00 2001 From: Kumataro Date: Sat, 9 Aug 2025 13:10:08 +0900 Subject: [PATCH 07/14] test: gif: to test GIF codec at write_gif_flags --- modules/imgcodecs/test/test_gif.cpp | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/modules/imgcodecs/test/test_gif.cpp b/modules/imgcodecs/test/test_gif.cpp index 1ceef24637..fcf5954aa3 100644 --- a/modules/imgcodecs/test/test_gif.cpp +++ b/modules/imgcodecs/test/test_gif.cpp @@ -257,20 +257,16 @@ TEST(Imgcodecs_Gif, read_gif_special){ } TEST(Imgcodecs_Gif,write_gif_flags){ - const string root = cvtest::TS::ptr()->get_data_path(); - const string png_filename = root + "gifsuite/special1.png"; vector buff; const int expected_rows=611; const int expected_cols=293; - Mat img_gt = Mat::ones(expected_rows, expected_cols, CV_8UC1); - vector param; - param.push_back(IMWRITE_GIF_QUALITY); - param.push_back(7); - param.push_back(IMWRITE_GIF_DITHER); - param.push_back(2); - EXPECT_NO_THROW(imencode(".png", img_gt, buff, param)); + Mat img_gt = Mat::ones(expected_rows, expected_cols, CV_8UC3); + const vector param = { IMWRITE_GIF_QUALITY, IMWRITE_GIF_FAST_NO_DITHER, IMWRITE_GIF_DITHER, 3}; + bool ret = false; + EXPECT_NO_THROW(ret = imencode(".gif", img_gt, buff, param)); + EXPECT_TRUE(ret); Mat img; - EXPECT_NO_THROW(img = imdecode(buff, IMREAD_ANYDEPTH)); // hang + EXPECT_NO_THROW(img = imdecode(buff, IMREAD_COLOR)); EXPECT_FALSE(img.empty()); EXPECT_EQ(img.cols, expected_cols); EXPECT_EQ(img.rows, expected_rows); @@ -280,7 +276,7 @@ TEST(Imgcodecs_Gif,write_gif_flags){ TEST(Imgcodecs_Gif, write_gif_big) { const string root = cvtest::TS::ptr()->get_data_path(); const string png_filename = root + "gifsuite/gif_big.png"; - const string gif_filename = cv::tempfile(".png"); + const string gif_filename = cv::tempfile(".gif"); cv::Mat img; ASSERT_NO_THROW(img = cv::imread(png_filename, IMREAD_UNCHANGED)); ASSERT_FALSE(img.empty()); From f0888a10e8266b2202d930c6974433a421e6f9a7 Mon Sep 17 00:00:00 2001 From: jmackay2 <1.732mackay@gmail.com> Date: Mon, 11 Aug 2025 05:15:24 -0400 Subject: [PATCH 08/14] Merge pull request #27636 from jmackay2:cuda_13 Cuda 13.0 compatibility #27636 ### 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 ### Issue CUDA 13 deprecated some fields, resulting in build failures with CUDA 13. This updates to use the replacement API. The reference to the deprecated features is here: https://docs.nvidia.com/cuda/cuda-toolkit-release-notes/index.html#id6 ### Testing This was testing by building on the following configurations: OS: Ubuntu 24.04 CUDA: 12.9, 13.0 --- modules/core/src/cuda_info.cpp | 40 ++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/modules/core/src/cuda_info.cpp b/modules/core/src/cuda_info.cpp index 2558ec8ea5..d97fadd4ec 100644 --- a/modules/core/src/cuda_info.cpp +++ b/modules/core/src/cuda_info.cpp @@ -424,7 +424,9 @@ int cv::cuda::DeviceInfo::clockRate() const #ifndef HAVE_CUDA throw_no_cuda(); #else - return deviceProps().get(device_id_)->clockRate; + int clockRate; + cudaSafeCall(cudaDeviceGetAttribute(&clockRate, cudaDevAttrClockRate, device_id_)); + return clockRate; #endif } @@ -487,7 +489,9 @@ bool cv::cuda::DeviceInfo::kernelExecTimeoutEnabled() const #ifndef HAVE_CUDA throw_no_cuda(); #else - return deviceProps().get(device_id_)->kernelExecTimeoutEnabled != 0; + int kernelExecTimeoutEnabled; + cudaSafeCall(cudaDeviceGetAttribute(&kernelExecTimeoutEnabled, cudaDevAttrKernelExecTimeout, device_id_)); + return kernelExecTimeoutEnabled != 0; #endif } @@ -522,7 +526,9 @@ DeviceInfo::ComputeMode cv::cuda::DeviceInfo::computeMode() const ComputeModeExclusiveProcess }; - return tbl[deviceProps().get(device_id_)->computeMode]; + int computeMode; + cudaSafeCall(cudaDeviceGetAttribute(&computeMode, cudaDevAttrComputeMode, device_id_)); + return tbl[computeMode]; #endif } @@ -554,7 +560,14 @@ int cv::cuda::DeviceInfo::maxTexture1DLinear() const #ifndef HAVE_CUDA throw_no_cuda(); #else - return deviceProps().get(device_id_)->maxTexture1DLinear; + #if CUDA_VERSION >= 13000 + size_t maxWidthInElements; + cudaChannelFormatDesc fmtDesc = cudaCreateChannelDesc(); + cudaSafeCall(cudaDeviceGetTexture1DLinearMaxWidth(&maxWidthInElements, &fmtDesc, device_id_)); + return maxWidthInElements; + #else + return deviceProps().get(device_id_)->maxTexture1DLinear; + #endif #endif } @@ -793,7 +806,9 @@ int cv::cuda::DeviceInfo::memoryClockRate() const #ifndef HAVE_CUDA throw_no_cuda(); #else - return deviceProps().get(device_id_)->memoryClockRate; + int memoryClockRate; + cudaSafeCall(cudaDeviceGetAttribute(&memoryClockRate, cudaDevAttrMemoryClockRate, device_id_)); + return memoryClockRate; #endif } @@ -933,7 +948,9 @@ void cv::cuda::printCudaDeviceInfo(int device) if (cores > 0) printf(" (%2d) Multiprocessors x (%2d) CUDA Cores/MP: %d CUDA Cores\n", prop.multiProcessorCount, cores, cores * prop.multiProcessorCount); - printf(" GPU Clock Speed: %.2f GHz\n", prop.clockRate * 1e-6f); + int clockRate; + cudaSafeCall(cudaDeviceGetAttribute(&clockRate, cudaDevAttrClockRate, dev)); + printf(" GPU Clock Speed: %.2f GHz\n", clockRate * 1e-6f); printf(" Max Texture Dimension Size (x,y,z) 1D=(%d), 2D=(%d,%d), 3D=(%d,%d,%d)\n", prop.maxTexture1D, prop.maxTexture2D[0], prop.maxTexture2D[1], @@ -952,8 +969,10 @@ void cv::cuda::printCudaDeviceInfo(int device) printf(" Maximum memory pitch: %u bytes\n", (int)prop.memPitch); printf(" Texture alignment: %u bytes\n", (int)prop.textureAlignment); - printf(" Concurrent copy and execution: %s with %d copy engine(s)\n", (prop.deviceOverlap ? "Yes" : "No"), prop.asyncEngineCount); - printf(" Run time limit on kernels: %s\n", prop.kernelExecTimeoutEnabled ? "Yes" : "No"); + printf(" Concurrent copy and execution: %s with %d copy engine(s)\n", (prop.asyncEngineCount ? "Yes" : "No"), prop.asyncEngineCount); + int kernelExecTimeoutEnabled; + cudaSafeCall(cudaDeviceGetAttribute(&kernelExecTimeoutEnabled, cudaDevAttrKernelExecTimeout, dev)); + printf(" Run time limit on kernels: %s\n", kernelExecTimeoutEnabled ? "Yes" : "No"); printf(" Integrated GPU sharing Host Memory: %s\n", prop.integrated ? "Yes" : "No"); printf(" Support host page-locked memory mapping: %s\n", prop.canMapHostMemory ? "Yes" : "No"); @@ -963,8 +982,11 @@ void cv::cuda::printCudaDeviceInfo(int device) printf(" Device is using TCC driver mode: %s\n", prop.tccDriver ? "Yes" : "No"); printf(" Device supports Unified Addressing (UVA): %s\n", prop.unifiedAddressing ? "Yes" : "No"); printf(" Device PCI Bus ID / PCI location ID: %d / %d\n", prop.pciBusID, prop.pciDeviceID ); + + int propComputeMode; + cudaSafeCall(cudaDeviceGetAttribute(&propComputeMode, cudaDevAttrComputeMode, dev)); printf(" Compute Mode:\n"); - printf(" %s \n", computeMode[prop.computeMode]); + printf(" %s \n", computeMode[propComputeMode]); } printf("\n"); From 75d9ac39643f8e08edf3299e47ffd1468e0c5196 Mon Sep 17 00:00:00 2001 From: Haosonn <90189584+Haosonn@users.noreply.github.com> Date: Mon, 11 Aug 2025 18:03:34 +0800 Subject: [PATCH 09/14] Merge pull request #27391 from Haosonn:pr-rvv-hal-fast hal/riscv-rvv: implement FAST keypoint detection #27391 An implementation of FAST keypoint detection with NMS/noNMS version. A new perf test is written, and the perf test is evaluated in two platforms: K1/K230. Accelaration is achieved when threshold is high, however, weird stat shows that the acceleration doesn't work when threshold is low (the number of keypoint candidates is high). K1: ``` # GCC Name of Test scalar rvv rvv vs scalar (x-factor) detect::Fast_Params::(20, 2, false, "cv/cameracalibration/chess9.png") 22.113 23.721 0.93 detect::Fast_Params::(20, 2, false, "cv/inpaint/orig.png") 4.605 7.168 0.64 detect::Fast_Params::(20, 2, true, "cv/cameracalibration/chess9.png") 26.228 24.689 1.06 detect::Fast_Params::(20, 2, true, "cv/inpaint/orig.png") 7.134 7.561 0.94 detect::Fast_Params::(30, 2, false, "cv/cameracalibration/chess9.png") 19.488 21.407 0.91 detect::Fast_Params::(30, 2, false, "cv/inpaint/orig.png") 3.481 5.404 0.64 detect::Fast_Params::(30, 2, true, "cv/cameracalibration/chess9.png") 22.309 22.145 1.01 detect::Fast_Params::(30, 2, true, "cv/inpaint/orig.png") 4.826 5.654 0.85 detect::Fast_Params::(100, 2, false, "cv/cameracalibration/chess9.png") 14.108 8.205 1.72 detect::Fast_Params::(100, 2, false, "cv/inpaint/orig.png") 2.520 1.072 2.35 detect::Fast_Params::(100, 2, true, "cv/cameracalibration/chess9.png") 14.133 8.410 1.68 detect::Fast_Params::(100, 2, true, "cv/inpaint/orig.png") 2.556 1.097 2.33 # Clang Name of Test scalar rvv rvv vs scalar (x-factor) detect::Fast_Params::(20, 2, false, "cv/cameracalibration/chess9.png") 25.130 23.695 1.06 detect::Fast_Params::(20, 2, false, "cv/inpaint/orig.png") 4.987 7.168 0.70 detect::Fast_Params::(20, 2, true, "cv/cameracalibration/chess9.png") 28.035 24.467 1.15 detect::Fast_Params::(20, 2, true, "cv/inpaint/orig.png") 6.760 7.503 0.90 detect::Fast_Params::(30, 2, false, "cv/cameracalibration/chess9.png") 22.954 21.373 1.07 detect::Fast_Params::(30, 2, false, "cv/inpaint/orig.png") 3.838 5.330 0.72 detect::Fast_Params::(30, 2, true, "cv/cameracalibration/chess9.png") 24.523 21.998 1.11 detect::Fast_Params::(30, 2, true, "cv/inpaint/orig.png") 4.795 5.543 0.87 detect::Fast_Params::(100, 2, false, "cv/cameracalibration/chess9.png") 16.799 8.102 2.07 detect::Fast_Params::(100, 2, false, "cv/inpaint/orig.png") 2.874 1.024 2.81 detect::Fast_Params::(100, 2, true, "cv/cameracalibration/chess9.png") 16.950 8.073 2.10 detect::Fast_Params::(100, 2, true, "cv/inpaint/orig.png") 2.899 1.027 2.82 ``` K230 ``` # GCC Name of Test scalar rvv rvv vs scalar (x-factor) detect::Fast_Params::(20, 2, false, "cv/cameracalibration/chess9.png") 21.082 32.090 0.66 detect::Fast_Params::(20, 2, false, "cv/inpaint/orig.png") 4.837 9.157 0.53 detect::Fast_Params::(20, 2, true, "cv/cameracalibration/chess9.png") 25.479 33.576 0.76 detect::Fast_Params::(20, 2, true, "cv/inpaint/orig.png") 7.549 9.716 0.78 detect::Fast_Params::(30, 2, false, "cv/cameracalibration/chess9.png") 18.463 30.087 0.61 detect::Fast_Params::(30, 2, false, "cv/inpaint/orig.png") 3.716 6.544 0.57 detect::Fast_Params::(30, 2, true, "cv/cameracalibration/chess9.png") 21.548 31.374 0.69 detect::Fast_Params::(30, 2, true, "cv/inpaint/orig.png") 5.107 6.928 0.74 detect::Fast_Params::(100, 2, false, "cv/cameracalibration/chess9.png") 13.763 8.712 1.58 detect::Fast_Params::(100, 2, false, "cv/inpaint/orig.png") 2.578 1.284 2.01 detect::Fast_Params::(100, 2, true, "cv/cameracalibration/chess9.png") 13.804 8.831 1.56 detect::Fast_Params::(100, 2, true, "cv/inpaint/orig.png") 2.615 1.289 2.03 # Clang Name of Test scalar rvv rvv vs scalar (x-factor) detect::Fast_Params::(20, 2, false, "cv/cameracalibration/chess9.png") 23.424 35.072 0.67 detect::Fast_Params::(20, 2, false, "cv/inpaint/orig.png") 5.284 10.107 0.52 detect::Fast_Params::(20, 2, true, "cv/cameracalibration/chess9.png") 26.487 35.978 0.74 detect::Fast_Params::(20, 2, true, "cv/inpaint/orig.png") 7.146 10.612 0.67 detect::Fast_Params::(30, 2, false, "cv/cameracalibration/chess9.png") 21.155 32.858 0.64 detect::Fast_Params::(30, 2, false, "cv/inpaint/orig.png") 4.101 7.153 0.57 detect::Fast_Params::(30, 2, true, "cv/cameracalibration/chess9.png") 23.321 33.505 0.70 detect::Fast_Params::(30, 2, true, "cv/inpaint/orig.png") 5.106 7.415 0.69 detect::Fast_Params::(100, 2, false, "cv/cameracalibration/chess9.png") 15.597 8.792 1.77 detect::Fast_Params::(100, 2, false, "cv/inpaint/orig.png") 2.922 1.228 2.38 detect::Fast_Params::(100, 2, true, "cv/cameracalibration/chess9.png") 15.626 8.817 1.77 detect::Fast_Params::(100, 2, true, "cv/inpaint/orig.png") 2.963 1.240 2.39 ``` ### 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 - [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 --- hal/riscv-rvv/CMakeLists.txt | 3 +- hal/riscv-rvv/include/features2d.hpp | 26 +++ hal/riscv-rvv/rvv_hal.hpp | 1 + hal/riscv-rvv/src/features2d/common.hpp | 23 ++ hal/riscv-rvv/src/features2d/fast.cpp | 256 +++++++++++++++++++++ modules/features2d/perf/perf_fast.cpp | 43 ++++ modules/features2d/src/fast.cpp | 20 +- modules/features2d/src/hal_replacement.hpp | 16 ++ 8 files changed, 383 insertions(+), 5 deletions(-) create mode 100644 hal/riscv-rvv/include/features2d.hpp create mode 100644 hal/riscv-rvv/src/features2d/common.hpp create mode 100644 hal/riscv-rvv/src/features2d/fast.cpp create mode 100644 modules/features2d/perf/perf_fast.cpp diff --git a/hal/riscv-rvv/CMakeLists.txt b/hal/riscv-rvv/CMakeLists.txt index a0c9e628b3..522b71434e 100644 --- a/hal/riscv-rvv/CMakeLists.txt +++ b/hal/riscv-rvv/CMakeLists.txt @@ -17,7 +17,8 @@ endif() target_include_directories(${HAL_LIB_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/modules/core/include - ${CMAKE_SOURCE_DIR}/modules/imgproc/include) # ${CMAKE_SOURCE_DIR}/modules/features2d/include + ${CMAKE_SOURCE_DIR}/modules/imgproc/include + ${CMAKE_SOURCE_DIR}/modules/features2d/include) set(RVV_HAL_FOUND TRUE CACHE INTERNAL "") set(RVV_HAL_VERSION "0.0.1" CACHE INTERNAL "") diff --git a/hal/riscv-rvv/include/features2d.hpp b/hal/riscv-rvv/include/features2d.hpp new file mode 100644 index 0000000000..6e16b618a4 --- /dev/null +++ b/hal/riscv-rvv/include/features2d.hpp @@ -0,0 +1,26 @@ +// 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_RVV_HAL_FEATURES2D_HPP +#define OPENCV_RVV_HAL_FEATURES2D_HPP + +struct cvhalFilter2D; + +namespace cv { namespace rvv_hal { namespace features2d { + +#if CV_HAL_RVV_1P0_ENABLED + +int FAST(const uchar* src_data, size_t src_step, int width, int height, + void** keypoints_data, size_t* keypoints_count, + int threshold, bool nonmax_suppression, int detector_type, void* (*realloc_func)(void*, size_t)); + +#undef cv_hal_FASTv2 +#define cv_hal_FASTv2 cv::rvv_hal::features2d::FAST + +#endif // CV_HAL_RVV_1P0_ENABLED + + +}}} // cv::rvv_hal::features2d + +#endif // OPENCV_RVV_HAL_IMGPROC_HPP diff --git a/hal/riscv-rvv/rvv_hal.hpp b/hal/riscv-rvv/rvv_hal.hpp index 88989aaeb8..327f0af1e4 100644 --- a/hal/riscv-rvv/rvv_hal.hpp +++ b/hal/riscv-rvv/rvv_hal.hpp @@ -27,5 +27,6 @@ #include "include/types.hpp" #include "include/core.hpp" #include "include/imgproc.hpp" +#include "include/features2d.hpp" #endif // OPENCV_HAL_RVV_HPP_INCLUDED diff --git a/hal/riscv-rvv/src/features2d/common.hpp b/hal/riscv-rvv/src/features2d/common.hpp new file mode 100644 index 0000000000..5ae9314062 --- /dev/null +++ b/hal/riscv-rvv/src/features2d/common.hpp @@ -0,0 +1,23 @@ +// 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. +// Copyright (C) 2025, Institute of Software, Chinese Academy of Sciences. +// Third party copyrights are property of their respective owners. + +#ifndef OPENCV_HAL_RVV_FEATURES2D_COMMON_HPP_INCLUDED +#define OPENCV_HAL_RVV_FEATURES2D_COMMON_HPP_INCLUDED + +#include +#include "opencv2/features2d/hal/interface.h" + +namespace cv { namespace rvv_hal { namespace features2d { namespace common { + +#if CV_HAL_RVV_1P0_ENABLED + +#endif // CV_HAL_RVV_1P0_ENABLED + +}}}} // cv::rvv_hal::core::common + +#endif // OPENCV_HAL_RVV_CORE_COMMON_HPP_INCLUDED diff --git a/hal/riscv-rvv/src/features2d/fast.cpp b/hal/riscv-rvv/src/features2d/fast.cpp new file mode 100644 index 0000000000..fbe5813c59 --- /dev/null +++ b/hal/riscv-rvv/src/features2d/fast.cpp @@ -0,0 +1,256 @@ +#include "rvv_hal.hpp" +#include "common.hpp" +#include + +namespace cv { namespace rvv_hal { namespace features2d { + +static inline uint8_t cornerScore(const uint8_t* ptr, const int* pixel) +{ + constexpr int K = 8, N = 16 + K + 1; + int v = ptr[0]; + int16_t d[32] = {0}; + for (int k = 0; k < N; k++) + d[k] = (int16_t)(v - ptr[pixel[k]]); + auto vlenb = __riscv_vlenb(); + switch (vlenb) { + #define CV_RVV_HAL_FAST_CORNERSOCRE16_CASE(lmul) \ + size_t vl = __riscv_vsetvl_e16m##lmul(N); \ + vint16m##lmul##_t vd = __riscv_vle16_v_i16m##lmul(d, vl); \ + vint16m##lmul##_t q0 = __riscv_vmv_v_x_i16m##lmul((int16_t)(-1000), vl); \ + vint16m##lmul##_t q1 = __riscv_vmv_v_x_i16m##lmul((int16_t)(1000), vl); \ + vint16m##lmul##_t vds = vd, ak0 = vd, bk0 = vd; \ + for (int i = 0; i < 8; i++) { \ + vds = __riscv_vslide1down(vds, 0, vl); \ + ak0 = __riscv_vmin(ak0, vds, vl); \ + bk0 = __riscv_vmax(bk0, vds, vl); \ + } \ + q0 = __riscv_vmax(q0, __riscv_vmin(ak0, vd, vl), vl); \ + q1 = __riscv_vmin(q1, __riscv_vmax(bk0, vd, vl), vl); \ + vds = __riscv_vslide1down(vds, 0, vl); \ + q0 = __riscv_vmax(q0, __riscv_vmin(ak0, vds, vl), vl); \ + q1 = __riscv_vmin(q1, __riscv_vmax(bk0, vds, vl), vl); \ + q0 = __riscv_vmax(q0, __riscv_vrsub(q1, 0, vl), vl); \ + return (uint8_t)(__riscv_vmv_x(__riscv_vredmax(q0, __riscv_vmv_s_x_i16m1(0, vl), vl)) - 1); + case 16: { // 128-bit + CV_RVV_HAL_FAST_CORNERSOCRE16_CASE(4) + } break; + case 32: { // 256-bit + CV_RVV_HAL_FAST_CORNERSOCRE16_CASE(2) + } break; + default: { // >=512-bit + CV_RVV_HAL_FAST_CORNERSOCRE16_CASE(1) + } + } +} + + +inline int fast_16(const uchar* src_data, size_t src_step, + int width, int height, + std::vector &keypoints, + int threshold, bool nonmax_suppression) +{ + + constexpr int patternSize = 16; + constexpr int K = patternSize/2, N = patternSize + K + 1; + constexpr int quarterPatternSize = patternSize/4; + + int i, j, k; + int pixel[N] = {0}; + pixel[0] = 0 + (int)src_step * 3; + pixel[1] = 1 + (int)src_step * 3; + pixel[2] = 2 + (int)src_step * 2; + pixel[3] = 3 + (int)src_step * 1; + pixel[4] = 3 + (int)src_step * 0; + pixel[5] = 3 + (int)src_step * -1; + pixel[6] = 2 + (int)src_step * -2; + pixel[7] = 1 + (int)src_step * -3; + pixel[8] = 0 + (int)src_step * -3; + pixel[9] = -1 + (int)src_step * -3; + pixel[10] = -2 + (int)src_step * -2; + pixel[11] = -3 + (int)src_step * -1; + pixel[12] = -3 + (int)src_step * 0; + pixel[13] = -3 + (int)src_step * 1; + pixel[14] = -2 + (int)src_step * 2; + pixel[15] = -1 + (int)src_step * 3; + for (k = 16; k < N; k++) + { + pixel[k] = pixel[k - 16]; + } + + std::vector _buf((width+16)*3*(sizeof(ptrdiff_t) + sizeof(uchar)) + 128); + uchar* buf[3]; + buf[0] = &_buf[0]; buf[1] = buf[0] + width; buf[2] = buf[1] + width; + ptrdiff_t* cpbuf[3]; + cpbuf[0] = (ptrdiff_t*)alignPtr(buf[2] + width, sizeof(ptrdiff_t)) + 1; + cpbuf[1] = cpbuf[0] + width + 1; + cpbuf[2] = cpbuf[1] + width + 1; + memset(buf[0], 0, width*3); + + int vlmax = __riscv_vsetvlmax_e8m4(); + vuint8m4_t v_c_delta = __riscv_vmv_v_x_u8m4(0x80, vlmax); + vuint8m4_t v_c_threshold = __riscv_vmv_v_x_u8m4((char) threshold, vlmax); + vuint8m4_t v_c_k = __riscv_vmv_v_x_u8m4((uint8_t)K, vlmax); + vuint8m4_t v_c_zero = __riscv_vmv_v_x_u8m4(0, vlmax); + + for( i = 3; i < height - 2; i++) + { + + const uchar* ptr = src_data + i * src_step + 3; + uchar* curr = buf[(i - 3)%3]; + ptrdiff_t* cornerpos = cpbuf[(i - 3)%3]; + memset(curr, 0, width); + ptrdiff_t ncorners = 0; + + if( i < height - 3 ) + { + j = 3; + { + int margin = width - 3; + int vl = __riscv_vsetvl_e8m4(margin - j); + for (; j < margin; j += vl, ptr += vl) + { + vl = __riscv_vsetvl_e8m4(margin - j); + vuint8m4_t v_pixels = __riscv_vle8_v_u8m4(ptr, vl); + // pixels add threshold + vuint8m4_t v_pat = __riscv_vsaddu(v_pixels, v_c_threshold, vl); + // pixels sub threshold + vuint8m4_t v_pst = __riscv_vssubu(v_pixels, v_c_threshold, vl); + vint8m4_t v0 = __riscv_vreinterpret_i8m4(__riscv_vxor(v_pat, v_c_delta, vl)); + vint8m4_t v1 = __riscv_vreinterpret_i8m4(__riscv_vxor(v_pst, v_c_delta, vl)); + + v_pixels = __riscv_vle8_v_u8m4(ptr + pixel[0], vl); + vint8m4_t x0 = __riscv_vreinterpret_i8m4(__riscv_vsub(v_pixels, v_c_delta, vl)); + v_pixels = __riscv_vle8_v_u8m4(ptr + pixel[quarterPatternSize], vl); + vint8m4_t x1 = __riscv_vreinterpret_i8m4(__riscv_vsub(v_pixels, v_c_delta, vl)); + v_pixels = __riscv_vle8_v_u8m4(ptr + pixel[2*quarterPatternSize], vl); + vint8m4_t x2 = __riscv_vreinterpret_i8m4(__riscv_vsub(v_pixels, v_c_delta, vl)); + v_pixels = __riscv_vle8_v_u8m4(ptr + pixel[3*quarterPatternSize], vl); + vint8m4_t x3 = __riscv_vreinterpret_i8m4(__riscv_vsub(v_pixels, v_c_delta, vl)); + + vbool2_t m0, m1; + m0 = __riscv_vmand(__riscv_vmslt(v0, x0, vl), __riscv_vmslt(v0, x1, vl), vl); + m1 = __riscv_vmand(__riscv_vmslt(x0, v1, vl), __riscv_vmslt(x1, v1, vl), vl); + m0 = __riscv_vmor(m0, __riscv_vmand(__riscv_vmslt(v0, x1, vl), __riscv_vmslt(v0, x2, vl), vl), vl); + m1 = __riscv_vmor(m1, __riscv_vmand(__riscv_vmslt(x1, v1, vl), __riscv_vmslt(x2, v1, vl), vl), vl); + m0 = __riscv_vmor(m0, __riscv_vmand(__riscv_vmslt(v0, x2, vl), __riscv_vmslt(v0, x3, vl), vl), vl); + m1 = __riscv_vmor(m1, __riscv_vmand(__riscv_vmslt(x2, v1, vl), __riscv_vmslt(x3, v1, vl), vl), vl); + m0 = __riscv_vmor(m0, __riscv_vmand(__riscv_vmslt(v0, x3, vl), __riscv_vmslt(v0, x0, vl), vl), vl); + m1 = __riscv_vmor(m1, __riscv_vmand(__riscv_vmslt(x3, v1, vl), __riscv_vmslt(x0, v1, vl), vl), vl); + m0 = __riscv_vmor(m0, m1, vl); + + unsigned long mask_cnt = __riscv_vcpop(m0, vl); + if(!mask_cnt) + continue; + + // TODO: Test if skipping to the first possible key point pixel if faster + // Memory access maybe expensive since the data is not aligned + // long first_set = __riscv_vfirst(m0, vl); + // if( first_set == -1 ) { + // j -= first_set; + // ptr -= first_set; + // } + + vuint8m4_t c0 = __riscv_vmv_v_x_u8m4(0, vl); + vuint8m4_t c1 = __riscv_vmv_v_x_u8m4(0, vl); + vuint8m4_t max0 = __riscv_vmv_v_x_u8m4(0, vl); + vuint8m4_t max1 = __riscv_vmv_v_x_u8m4(0, vl); + + for( k = 0; k < N; k++ ) + { + vint8m4_t x = __riscv_vreinterpret_i8m4(__riscv_vxor(__riscv_vle8_v_u8m4(ptr + pixel[k], vl), v_c_delta, vl)); + + m0 = __riscv_vmslt(v0, x, vl); + m1 = __riscv_vmslt(x, v1, vl); + + c0 = __riscv_vadd_mu(m0, c0, c0, (uint8_t)1, vl); + c1 = __riscv_vadd_mu(m1, c1, c1, (uint8_t)1, vl); + c0 = __riscv_vmerge(v_c_zero, c0, m0, vl); + c1 = __riscv_vmerge(v_c_zero, c1, m1, vl); + + max0 = __riscv_vmaxu(max0, c0, vl); + max1 = __riscv_vmaxu(max1, c1, vl); + } + + vbool2_t v_comparek = __riscv_vmsltu(v_c_k, __riscv_vmaxu(max0, max1, vl), vl); + uint8_t m[64]; + __riscv_vse8(m, __riscv_vreinterpret_u8m1(v_comparek), vl); + + for( k = 0; k < vl; k++ ) + { + if( (m[k / 8] >> (k % 8)) & 1 ) + { + cornerpos[ncorners++] = j + k; + if(nonmax_suppression) { + curr[j + k] = (uchar)cornerScore(ptr + k, pixel); + } + } + } + } + } + } + + cornerpos[-1] = ncorners; + + if( i == 3 ) continue; + + const uchar* prev = buf[(i - 4 + 3)%3]; + const uchar* pprev = buf[(i - 5 + 3)%3]; + cornerpos = cpbuf[(i - 4 + 3)%3]; // cornerpos[-1] is used to store a value + ncorners = cornerpos[-1]; + for( k = 0; k < ncorners; k++ ) + { + j = cornerpos[k]; + int score = prev[j]; + if(!nonmax_suppression || + (score > prev[j+1] && score > prev[j-1] && + score > pprev[j-1] && score > pprev[j] && score > pprev[j+1] && + score > curr[j-1] && score > curr[j] && score > curr[j+1]) ) + { + cvhalKeyPoint kp; + kp.x = (float)j; + kp.y = (float)(i-1); + kp.size = 7.f; + kp.angle = -1.f; + kp.response = (float)score; + kp.octave = 0; // Not used in FAST + kp.class_id = -1; // Not used in FAST + keypoints.push_back(kp); + } + } + } + return CV_HAL_ERROR_OK; +} + +int FAST(const uchar* src_data, size_t src_step, + int width, int height, void** keypoints_data, + size_t* keypoints_count, int threshold, + bool nonmax_suppression, int detector_type, void* (*realloc_func)(void*, size_t)) +{ + int res = CV_HAL_ERROR_UNKNOWN; + switch(detector_type) { + case CV_HAL_TYPE_5_8: + return CV_HAL_ERROR_NOT_IMPLEMENTED; + case CV_HAL_TYPE_7_12: + return CV_HAL_ERROR_NOT_IMPLEMENTED; + case CV_HAL_TYPE_9_16: { + std::vector keypoints; + res = fast_16(src_data, src_step, width, height, keypoints, threshold, nonmax_suppression); + if (res == CV_HAL_ERROR_OK) { + if (keypoints.size() > *keypoints_count) { + *keypoints_count = keypoints.size(); + uchar *tmp = (uchar*)realloc_func(*keypoints_data, sizeof(cvhalKeyPoint)*(*keypoints_count)); + memcpy(tmp, (uchar*)keypoints.data(), sizeof(cvhalKeyPoint)*(*keypoints_count)); + *keypoints_data = tmp; + } else { + *keypoints_count = keypoints.size(); + memcpy(*keypoints_data, (uchar*)keypoints.data(), sizeof(cvhalKeyPoint)*(*keypoints_count)); + } + } + return res; + } + default: + return res; + } +} + +}}} // namespace cv::rvv_hal::features2d diff --git a/modules/features2d/perf/perf_fast.cpp b/modules/features2d/perf/perf_fast.cpp new file mode 100644 index 0000000000..31da48b8f1 --- /dev/null +++ b/modules/features2d/perf/perf_fast.cpp @@ -0,0 +1,43 @@ +#include "perf_precomp.hpp" +#include "perf_feature2d.hpp" + +namespace opencv_test +{ +using namespace perf; + +typedef tuple Fast_Params_t; +typedef perf::TestBaseWithParam Fast_Params; + +PERF_TEST_P(Fast_Params, detect, + testing::Combine( + testing::Values(20,30,100), // threshold + testing::Values( + // (int)FastFeatureDetector::TYPE_5_8, + // (int)FastFeatureDetector::TYPE_7_12, + (int)FastFeatureDetector::TYPE_9_16 // detector_type + ), + testing::Bool(), // nonmaxSuppression + testing::Values("cv/inpaint/orig.png", + "cv/cameracalibration/chess9.png") + )) +{ + int threshold_p = get<0>(GetParam()); + int type_p = get<1>(GetParam()); + bool nonmaxSuppression_p = get<2>(GetParam()); + string filename = getDataPath(get<3>(GetParam())); + + Mat img = imread(filename, IMREAD_GRAYSCALE); + ASSERT_FALSE(img.empty()) << "Failed to load image: " << filename; + + vector keypoints; + + declare.in(img); + TEST_CYCLE() + { + FAST(img, keypoints, threshold_p, nonmaxSuppression_p, (FastFeatureDetector::DetectorType)type_p); + } + + SANITY_CHECK_NOTHING(); +} + +} // namespace opencv_test diff --git a/modules/features2d/src/fast.cpp b/modules/features2d/src/fast.cpp index a9615da5bd..23c4bed6bd 100644 --- a/modules/features2d/src/fast.cpp +++ b/modules/features2d/src/fast.cpp @@ -435,11 +435,23 @@ void FAST(InputArray _img, std::vector& keypoints, int threshold, bool cv::Mat img = _img.getMat(); CALL_HAL(fast_dense, hal_FAST, img, keypoints, threshold, nonmax_suppression, type); - size_t keypoints_count = 10000; + size_t keypoints_count = 1; keypoints.clear(); - keypoints.resize(keypoints_count); - CALL_HAL(fast, cv_hal_FAST, img.data, img.step, img.cols, img.rows, - (uchar*)(keypoints.data()), &keypoints_count, threshold, nonmax_suppression, type); + KeyPoint* kps = (KeyPoint*)malloc(sizeof(KeyPoint) * keypoints_count); + int hal_ret = cv_hal_FASTv2(img.data, img.step, img.cols, img.rows, (void**)&kps, + &keypoints_count, threshold, nonmax_suppression, type, realloc); + if (hal_ret == CV_HAL_ERROR_OK) { + keypoints.assign(kps, kps + keypoints_count); + free(kps); + return; + } else { + free(kps); + keypoints_count = 10000; + keypoints.clear(); + keypoints.resize(keypoints_count); + CALL_HAL(fast, cv_hal_FAST, img.data, img.step, img.cols, img.rows, + (uchar*)(keypoints.data()), &keypoints_count, threshold, nonmax_suppression, type); + } switch(type) { case FastFeatureDetector::TYPE_5_8: diff --git a/modules/features2d/src/hal_replacement.hpp b/modules/features2d/src/hal_replacement.hpp index 6476d21651..81cc4656b6 100644 --- a/modules/features2d/src/hal_replacement.hpp +++ b/modules/features2d/src/hal_replacement.hpp @@ -104,8 +104,24 @@ inline int hal_ni_FAST_NMS(const uchar* src_data, size_t src_step, uchar* dst_da */ inline int hal_ni_FAST(const uchar* src_data, size_t src_step, int width, int height, uchar* keypoints_data, size_t* keypoints_count, int threshold, bool nonmax_suppression, int /*cv::FastFeatureDetector::DetectorType*/ type) { return CV_HAL_ERROR_NOT_IMPLEMENTED; } +/** + @brief Detects corners using the FAST algorithm. + @param src_data Source image data + @param src_step Source image step + @param width Source image width + @param height Source image height + @param keypoints_data Pointer to keypoints + @param keypoints_count Count of keypoints + @param threshold Threshold for keypoint + @param nonmax_suppression Indicates if make nonmaxima suppression or not. + @param type FAST type + @param realloc_func function for reallocation +*/ +inline int hal_ni_FASTv2(const uchar* src_data, size_t src_step, int width, int height, void** keypoints_data, size_t* keypoints_count, int threshold, bool nonmax_suppression, int /*cv::FastFeatureDetector::DetectorType*/ type, void* (*realloc_func)(void*, size_t)) { return CV_HAL_ERROR_NOT_IMPLEMENTED; } + //! @cond IGNORED #define cv_hal_FAST hal_ni_FAST +#define cv_hal_FASTv2 hal_ni_FASTv2 //! @endcond //! @} From 84d391aac42ecdce4092abcd424ca878b014e555 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Mon, 11 Aug 2025 13:26:34 +0300 Subject: [PATCH 10/14] Added heuristic to allocate buffer for FAST features depending on input image resolution. --- modules/features2d/src/fast.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/features2d/src/fast.cpp b/modules/features2d/src/fast.cpp index 23c4bed6bd..bd39898b32 100644 --- a/modules/features2d/src/fast.cpp +++ b/modules/features2d/src/fast.cpp @@ -429,8 +429,10 @@ void FAST(InputArray _img, std::vector& keypoints, int threshold, bool { CV_INSTRUMENT_REGION(); + const size_t max_fast_features = std::max(_img.total()/100, size_t(1000)); // Simple heuristic that depends on resolution. + CV_OCL_RUN(_img.isUMat() && type == FastFeatureDetector::TYPE_9_16, - ocl_FAST(_img, keypoints, threshold, nonmax_suppression, 10000)); + ocl_FAST(_img, keypoints, threshold, nonmax_suppression, (int)max_fast_features)); cv::Mat img = _img.getMat(); CALL_HAL(fast_dense, hal_FAST, img, keypoints, threshold, nonmax_suppression, type); @@ -446,7 +448,7 @@ void FAST(InputArray _img, std::vector& keypoints, int threshold, bool return; } else { free(kps); - keypoints_count = 10000; + keypoints_count = max_fast_features; keypoints.clear(); keypoints.resize(keypoints_count); CALL_HAL(fast, cv_hal_FAST, img.data, img.step, img.cols, img.rows, From 252403bbf2fc560007c2c9057db5a9a151e99dd7 Mon Sep 17 00:00:00 2001 From: Andrei Tamas Date: Tue, 12 Aug 2025 14:34:42 +0300 Subject: [PATCH 11/14] Merge pull request #27600 from atamas19:ov_output_clamping G-API: Implement cfgClampOutputs option to OpenVINO Params #27600 Added the option `cfgClampOutputs` to control where output clamping is performed for OpenVINO models. When enabled, output values are clamped in the PrePostProcessor stage instead of by the device or plugin. This provides a consistent and standardized clamping method across devices, helping to maintain accuracy regardless of device-specific clamping behavior. ### 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. - [x] The feature is well documented and sample code can be built with the project CMake --- .../gapi/include/opencv2/gapi/infer/ov.hpp | 28 ++++++++++++++++ modules/gapi/src/backends/ov/govbackend.cpp | 33 +++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/modules/gapi/include/opencv2/gapi/infer/ov.hpp b/modules/gapi/include/opencv2/gapi/infer/ov.hpp index 9515744cac..3673ff53a2 100644 --- a/modules/gapi/include/opencv2/gapi/infer/ov.hpp +++ b/modules/gapi/include/opencv2/gapi/infer/ov.hpp @@ -66,6 +66,8 @@ struct ParamDesc { LayerVariantAttr> scale_values; LayerVariantAttr interpolation; + + bool clamp_outputs = false; }; struct CompiledModel { @@ -356,6 +358,24 @@ public: return *this; } + /** @brief Enables or disables clamping of model outputs in the PrePostProcessor. + + By default, output values are clamped to the valid range for the output precision + by the device or plugin. Enabling this option moves clamping to the PrePostProcessor stage. + + @note This feature is only available with OpenVINO 2025.2 and newer. + + @param flag If true, clamping is performed in the PrePostProcessor; + otherwise, it is handled by the device or plugin. + @return reference to this parameter structure. + */ + Params& + cfgClampOutputs(bool flag = true) { + detail::getModelToSetAttrOrThrow(m_desc.kind, "clamp outputs") + .clamp_outputs = std::move(flag); + return *this; + } + /** @brief Specifies the new shape for input layers. The function is used to set new shape for input layers. @@ -625,6 +645,14 @@ public: return *this; } + /** @see ov::Params::cfgClampOutputs. */ + Params& + cfgClampOutputs(bool flag = true) { + detail::getModelToSetAttrOrThrow(m_desc.kind, "clamp outputs") + .clamp_outputs = std::move(flag); + return *this; + } + /** @see ov::Params::cfgReshape. */ Params& cfgReshape(std::vector new_shape) { detail::getModelToSetAttrOrThrow(m_desc.kind, "reshape") diff --git a/modules/gapi/src/backends/ov/govbackend.cpp b/modules/gapi/src/backends/ov/govbackend.cpp index dbaba382db..4ea1c1cc0f 100644 --- a/modules/gapi/src/backends/ov/govbackend.cpp +++ b/modules/gapi/src/backends/ov/govbackend.cpp @@ -147,6 +147,25 @@ static int toCV(const ov::element::Type &type) { return -1; } +static inline std::pair get_CV_type_range(int cv_type) { + switch (cv_type) { + case CV_8U: + return { static_cast(std::numeric_limits::min()), + static_cast(std::numeric_limits::max()) }; + case CV_32S: + return { static_cast(std::numeric_limits::min()), + static_cast(std::numeric_limits::max()) }; + case CV_32F: + return { static_cast(std::numeric_limits::lowest()), + static_cast(std::numeric_limits::max()) }; + case CV_16F: + return { -65504.0, 65504.0 }; + default: + GAPI_Error("OV Backend: Unsupported data type"); + } + return {0.0, 0.0}; +} + static void copyFromOV(const ov::Tensor &tensor, cv::Mat &mat) { const auto total = mat.total() * mat.channels(); if (toCV(tensor.get_element_type()) != mat.depth() || @@ -1052,6 +1071,20 @@ public: if (explicit_out_tensor_prec) { m_ppp.output(output_name).tensor() .set_element_type(toOV(*explicit_out_tensor_prec)); + + if (m_model_info.clamp_outputs) { + #if INF_ENGINE_RELEASE >= 2025020000 + auto clamp_range = get_CV_type_range(*explicit_out_tensor_prec); + m_ppp.output(output_name).postprocess() + .clamp(clamp_range.first, clamp_range.second); + #else + static bool warned = false; + if (!warned) { + GAPI_LOG_WARNING(NULL, "cfgClampOutputs is enabled, but not supported in this OpenVINO version. Clamping will be ignored."); + warned = true; + } + #endif // INF_ENGINE_RELEASE >= 2025020000 + } } } } From c5bb6a014a496ef1a842cb4308d665b6d66fab41 Mon Sep 17 00:00:00 2001 From: Kumataro Date: Tue, 12 Aug 2025 22:04:45 +0900 Subject: [PATCH 12/14] Merge pull request #27621 from Kumataro:trial27557 Add strict validation for encoding parameters #27621 Close https://github.com/opencv/opencv/issues/27557 ### 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 | 11 ++- modules/imgcodecs/src/grfmt_avif.cpp | 19 +++- modules/imgcodecs/src/grfmt_base.cpp | 8 ++ modules/imgcodecs/src/grfmt_base.hpp | 8 ++ modules/imgcodecs/src/grfmt_exr.cpp | 20 +++- modules/imgcodecs/src/grfmt_gif.cpp | 25 ++++- modules/imgcodecs/src/grfmt_hdr.cpp | 15 ++- modules/imgcodecs/src/grfmt_jpeg.cpp | 44 ++++++--- modules/imgcodecs/src/grfmt_jpeg2000.cpp | 10 +- .../imgcodecs/src/grfmt_jpeg2000_openjpeg.cpp | 13 ++- modules/imgcodecs/src/grfmt_jpegxl.cpp | 14 ++- modules/imgcodecs/src/grfmt_pam.cpp | 23 ++++- modules/imgcodecs/src/grfmt_png.cpp | 57 +++++++++-- modules/imgcodecs/src/grfmt_pxm.cpp | 9 +- modules/imgcodecs/src/grfmt_spng.cpp | 44 +++++++-- modules/imgcodecs/src/grfmt_tiff.cpp | 95 +++++++++++++++++-- modules/imgcodecs/src/grfmt_webp.cpp | 9 +- modules/imgcodecs/src/loadsave.cpp | 55 +++++++++++ modules/imgcodecs/test/test_avif.cpp | 11 --- modules/imgcodecs/test/test_grfmt.cpp | 34 +++++++ modules/imgcodecs/test/test_webp.cpp | 2 +- modules/videoio/src/cap_images.cpp | 6 +- 22 files changed, 448 insertions(+), 84 deletions(-) diff --git a/modules/imgcodecs/include/opencv2/imgcodecs.hpp b/modules/imgcodecs/include/opencv2/imgcodecs.hpp index 841d5b32cf..2aeac8d60a 100644 --- a/modules/imgcodecs/include/opencv2/imgcodecs.hpp +++ b/modules/imgcodecs/include/opencv2/imgcodecs.hpp @@ -105,16 +105,16 @@ enum ImwriteFlags { IMWRITE_WEBP_QUALITY = 64, //!< For WEBP, it can be a quality from 1 to 100 (the higher is the better). By default (without any parameter) and for quality above 100 the lossless compression is used. IMWRITE_HDR_COMPRESSION = (5 << 4) + 0 /* 80 */, //!< specify HDR compression IMWRITE_PAM_TUPLETYPE = 128,//!< For PAM, sets the TUPLETYPE field to the corresponding string value that is defined for the format - IMWRITE_TIFF_RESUNIT = 256,//!< For TIFF, use to specify which DPI resolution unit to set; see libtiff documentation for valid values + IMWRITE_TIFF_RESUNIT = 256,//!< For TIFF, use to specify which DPI resolution unit to set. See ImwriteTiffResolutionUnitFlags. Default is IMWRITE_TIFF_RESOLUTION_UNIT_INCH. IMWRITE_TIFF_XDPI = 257,//!< For TIFF, use to specify the X direction DPI IMWRITE_TIFF_YDPI = 258,//!< For TIFF, use to specify the Y direction DPI IMWRITE_TIFF_COMPRESSION = 259,//!< For TIFF, use to specify the image compression scheme. See cv::ImwriteTiffCompressionFlags. Note, for images whose depth is CV_32F, only libtiff's SGILOG compression scheme is used. For other supported depths, the compression scheme can be specified by this flag; LZW compression is the default. IMWRITE_TIFF_ROWSPERSTRIP = 278,//!< For TIFF, use to specify the number of rows per strip. - IMWRITE_TIFF_PREDICTOR = 317,//!< For TIFF, use to specify predictor. See cv::ImwriteTiffPredictorFlags. + IMWRITE_TIFF_PREDICTOR = 317,//!< For TIFF, use to specify predictor. See cv::ImwriteTiffPredictorFlags. Default is IMWRITE_TIFF_PREDICTOR_HORIZONTAL . IMWRITE_JPEG2000_COMPRESSION_X1000 = 272,//!< For JPEG2000, use to specify the target compression rate (multiplied by 1000). The value can be from 0 to 1000. Default is 1000. IMWRITE_AVIF_QUALITY = 512,//!< For AVIF, it can be a quality between 0 and 100 (the higher the better). Default is 95. IMWRITE_AVIF_DEPTH = 513,//!< For AVIF, it can be 8, 10 or 12. If >8, it is stored/read as CV_32F. Default is 8. - IMWRITE_AVIF_SPEED = 514,//!< For AVIF, it is between 0 (slowest) and (fastest). Default is 9. + IMWRITE_AVIF_SPEED = 514,//!< For AVIF, it is between 0 (slowest) and 10(fastest). Default is 9. IMWRITE_JPEGXL_QUALITY = 640,//!< For JPEG XL, it can be a quality from 0 to 100 (the higher is the better). Default value is 95. If set, distance parameter is re-calicurated from quality level automatically. This parameter request libjxl v0.10 or later. 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. @@ -175,7 +175,12 @@ enum ImwriteTiffPredictorFlags { IMWRITE_TIFF_PREDICTOR_NONE = 1, //!< no prediction scheme used IMWRITE_TIFF_PREDICTOR_HORIZONTAL = 2, //!< horizontal differencing IMWRITE_TIFF_PREDICTOR_FLOATINGPOINT = 3 //!< floating point predictor +}; +enum ImwriteTiffResolutionUnitFlags { + IMWRITE_TIFF_RESOLUTION_UNIT_NONE = 1, //!< no absolute unit of measurement. + IMWRITE_TIFF_RESOLUTION_UNIT_INCH = 2, //!< inch + IMWRITE_TIFF_RESOLUTION_UNIT_CENTIMETER = 3, //!< centimeter }; enum ImwriteEXRTypeFlags { diff --git a/modules/imgcodecs/src/grfmt_avif.cpp b/modules/imgcodecs/src/grfmt_avif.cpp index c1b86362e0..35b4411b3b 100644 --- a/modules/imgcodecs/src/grfmt_avif.cpp +++ b/modules/imgcodecs/src/grfmt_avif.cpp @@ -319,6 +319,7 @@ AvifEncoder::AvifEncoder() { m_support_metadata[(size_t)IMAGE_METADATA_XMP] = true; m_support_metadata[(size_t)IMAGE_METADATA_ICCP] = true; encoder_ = avifEncoderCreate(); + m_supported_encode_key = { IMWRITE_AVIF_QUALITY, IMWRITE_AVIF_DEPTH, IMWRITE_AVIF_SPEED }; } AvifEncoder::~AvifEncoder() { @@ -334,9 +335,13 @@ bool AvifEncoder::writeanimation(const Animation& animation, int bit_depth = 8; int speed = AVIF_SPEED_FASTEST; for (size_t i = 0; i < params.size(); i += 2) { + const int value = params[i + 1]; if (params[i] == IMWRITE_AVIF_QUALITY) { - const int quality = std::min(std::max(params[i + 1], AVIF_QUALITY_WORST), + const int quality = std::min(std::max(value, AVIF_QUALITY_WORST), AVIF_QUALITY_BEST); + if (value != quality) { + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_AVIF_QUALITY must be between 0 to 100. It is fallbacked to %d", value, quality)); + } #if CV_AVIF_USE_QUALITY encoder_->quality = quality; #else @@ -346,9 +351,17 @@ bool AvifEncoder::writeanimation(const Animation& animation, AVIF_QUANTIZER_WORST_QUALITY; #endif } else if (params[i] == IMWRITE_AVIF_DEPTH) { - bit_depth = params[i + 1]; + bit_depth = value; + if ((bit_depth != 8) && (bit_depth !=10) && (bit_depth !=12)) + { + bit_depth = 8; + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_AVIF_DEPTH must be 8, 10 or 12. It is fallbacked to %d", value, bit_depth)); + } } else if (params[i] == IMWRITE_AVIF_SPEED) { - speed = params[i + 1]; + speed = std::min(std::max(value,0),10); + if (value != speed) { + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_AVIF_SPEED must be between 0 to 10. It is fallbacked to %d", value, speed)); + } } } diff --git a/modules/imgcodecs/src/grfmt_base.cpp b/modules/imgcodecs/src/grfmt_base.cpp index 9c9dead740..0cd372ec28 100644 --- a/modules/imgcodecs/src/grfmt_base.cpp +++ b/modules/imgcodecs/src/grfmt_base.cpp @@ -155,6 +155,14 @@ bool BaseImageEncoder::isFormatSupported( int depth ) const return depth == CV_8U; } +bool BaseImageEncoder::isValidEncodeKey(const int key) const +{ + auto first = m_supported_encode_key.begin(); + auto last = m_supported_encode_key.end(); + + return (std::find(first, last, key) != last); +} + String BaseImageEncoder::getDescription() const { return m_description; diff --git a/modules/imgcodecs/src/grfmt_base.hpp b/modules/imgcodecs/src/grfmt_base.hpp index 889fdd0a1d..3fec6055c2 100644 --- a/modules/imgcodecs/src/grfmt_base.hpp +++ b/modules/imgcodecs/src/grfmt_base.hpp @@ -224,6 +224,13 @@ public: */ virtual bool isFormatSupported(int depth) const; + /** + * @brief Validates if the encoder supports a specific key. + * @param key The key of the encode parameter. + * @return true if key is supported for this encoder, false otherwise. + */ + virtual bool isValidEncodeKey(const int key) const; + /** * @brief Set the destination for encoding as a file. * @param filename The name of the file to which the image will be written. @@ -290,6 +297,7 @@ protected: std::vector* m_buf; ///< Pointer to the buffer for encoded data if using memory-based destination. bool m_buf_supported; ///< Flag indicating whether buffer-based encoding is supported. String m_last_error; ///< Stores the last error message encountered during encoding. + std::vector m_supported_encode_key; ///< Supported encode key list }; } diff --git a/modules/imgcodecs/src/grfmt_exr.cpp b/modules/imgcodecs/src/grfmt_exr.cpp index 44a0934517..f1d4126e16 100644 --- a/modules/imgcodecs/src/grfmt_exr.cpp +++ b/modules/imgcodecs/src/grfmt_exr.cpp @@ -741,6 +741,7 @@ ImageDecoder ExrDecoder::newDecoder() const ExrEncoder::ExrEncoder() { m_description = "OpenEXR Image files (*.exr)"; + m_supported_encode_key = {IMWRITE_EXR_TYPE, IMWRITE_EXR_COMPRESSION, IMWRITE_EXR_DWA_COMPRESSION_LEVEL}; } @@ -767,9 +768,10 @@ bool ExrEncoder::write( const Mat& img, const std::vector& params ) for( size_t i = 0; i < params.size(); i += 2 ) { + const int value = params[i+1]; if( params[i] == IMWRITE_EXR_TYPE ) { - switch( params[i+1] ) + switch( value ) { case IMWRITE_EXR_TYPE_HALF: type = HALF; @@ -778,12 +780,14 @@ bool ExrEncoder::write( const Mat& img, const std::vector& params ) type = FLOAT; break; default: - CV_Error(Error::StsBadArg, "IMWRITE_EXR_TYPE is invalid or not supported"); + type = FLOAT; + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_EXR_TYPE must be one of ImwriteEXRTypeFlags. It is fallbacked to IMWRITE_EXR_TYPE_FLOAT", value)); + break; } } if ( params[i] == IMWRITE_EXR_COMPRESSION ) { - switch ( params[i + 1] ) + switch ( value ) { case IMWRITE_EXR_COMPRESSION_NO: header.compression() = NO_COMPRESSION; @@ -821,7 +825,9 @@ bool ExrEncoder::write( const Mat& img, const std::vector& params ) break; #endif default: - CV_Error(Error::StsBadArg, "IMWRITE_EXR_COMPRESSION is invalid or not supported"); + header.compression() = ZIP_COMPRESSION; + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_EXR_COMPRESSION must be one of ImwriteEXRCompressionFlags. It is fallbacked to IMWRITE_EXR_COMPRESSION_ZIP", value)); + break; } } if (params[i] == IMWRITE_EXR_DWA_COMPRESSION_LEVEL) @@ -831,7 +837,11 @@ bool ExrEncoder::write( const Mat& img, const std::vector& params ) #elif OPENEXR_VERSION_MAJOR < 3 CV_LOG_ONCE_WARNING(NULL, "Setting `IMWRITE_EXR_DWA_COMPRESSION_LEVEL` not supported in OpenEXR version " + std::to_string(OPENEXR_VERSION_MAJOR) + " (version 3 is required)"); #else - header.dwaCompressionLevel() = params[i + 1]; + // See https://github.com/AcademySoftwareFoundation/openexr/blob/v3.3.5/src/lib/OpenEXR/ImfDwaCompressor.cpp#L85 + header.dwaCompressionLevel() = std::max(params[i + 1], 0); + if(value < 0) { + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_EXR_DWA_COMPRESSION_LEVEL must be 0 or more. It is fallbacked to 0", value)); + } #endif } } diff --git a/modules/imgcodecs/src/grfmt_gif.cpp b/modules/imgcodecs/src/grfmt_gif.cpp index 1954e2d501..f5307ca39b 100644 --- a/modules/imgcodecs/src/grfmt_gif.cpp +++ b/modules/imgcodecs/src/grfmt_gif.cpp @@ -554,6 +554,8 @@ GifEncoder::GifEncoder() { colorNum = 256; // the number of colors in the color table, default 256 dithering = 0; // the level dithering, default 0 globalColorTableSize = 256, localColorTableSize = 0; + + m_supported_encode_key = {IMWRITE_GIF_QUALITY, IMWRITE_GIF_DITHER, IMWRITE_GIF_TRANSPARENCY, IMWRITE_GIF_COLORTABLE}; } GifEncoder::~GifEncoder() { @@ -576,6 +578,7 @@ bool GifEncoder::writeanimation(const Animation& animation, const std::vector& params ) int compression = IMWRITE_HDR_COMPRESSION_RLE; for (size_t i = 0; i + 1 < params.size(); i += 2) { + const int value = params[i+1]; switch (params[i]) { case IMWRITE_HDR_COMPRESSION: - compression = params[i + 1]; + switch(value) + { + case IMWRITE_HDR_COMPRESSION_NONE: + case IMWRITE_HDR_COMPRESSION_RLE: + compression = value; + break; + default: + compression = IMWRITE_HDR_COMPRESSION_RLE; + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_HDR_COMPRESSION must be one of ImwriteHDRCompressionFlags. It is fallbacked to IMWRITE_HDR_COMPRESSION_RLE", value)); + break; + } break; default: break; diff --git a/modules/imgcodecs/src/grfmt_jpeg.cpp b/modules/imgcodecs/src/grfmt_jpeg.cpp index 810ae1d63b..499036cedf 100644 --- a/modules/imgcodecs/src/grfmt_jpeg.cpp +++ b/modules/imgcodecs/src/grfmt_jpeg.cpp @@ -616,6 +616,7 @@ JpegEncoder::JpegEncoder() m_support_metadata[(size_t)IMAGE_METADATA_EXIF] = true; m_support_metadata[(size_t)IMAGE_METADATA_XMP] = true; m_support_metadata[(size_t)IMAGE_METADATA_ICCP] = true; + m_supported_encode_key = {IMWRITE_JPEG_QUALITY, IMWRITE_JPEG_PROGRESSIVE, IMWRITE_JPEG_OPTIMIZE, IMWRITE_JPEG_RST_INTERVAL, IMWRITE_JPEG_LUMA_QUALITY, IMWRITE_JPEG_CHROMA_QUALITY, IMWRITE_JPEG_SAMPLING_FACTOR}; } @@ -724,27 +725,39 @@ bool JpegEncoder::write( const Mat& img, const std::vector& params ) for( size_t i = 0; i < params.size(); i += 2 ) { + const int value = params[i+1]; if( params[i] == IMWRITE_JPEG_QUALITY ) { - quality = params[i+1]; - quality = MIN(MAX(quality, 0), 100); + quality = MIN(MAX(value, 0), 100); + if(value != quality) { + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_JPEG_QUALITY must be between 0 to 100. It is fallbacked to %d", value, quality)); + } } if( params[i] == IMWRITE_JPEG_PROGRESSIVE ) { - progressive = params[i+1]; + progressive = MIN(MAX(value,0), 1); + if(value != progressive) { + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_JPEG_PROGRESSIVE must be 0 or 1. It is fallbacked to %d", value, progressive)); + } } if( params[i] == IMWRITE_JPEG_OPTIMIZE ) { - optimize = params[i+1]; + optimize = MIN(MAX(value,0), 1); + if(value != optimize) { + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_JPEG_OPTIMIZE must be 0 or 1. It is fallbacked to %d", value, optimize)); + } } if( params[i] == IMWRITE_JPEG_LUMA_QUALITY ) { - if (params[i+1] >= 0) + if (value >= 0) { - luma_quality = MIN(MAX(params[i+1], 0), 100); + luma_quality = MIN(MAX(value, 0), 100); + if(value != luma_quality) { + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_JPEG_LUMA_QUALITY must be between 0 to 100. It is fallbacked to %d.", value, luma_quality)); + } quality = luma_quality; @@ -752,6 +765,8 @@ bool JpegEncoder::write( const Mat& img, const std::vector& params ) { chroma_quality = luma_quality; } + } else { + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_JPEG_LUMA_QUALITY must be between 0 to 100. It is ignored.", value)); } } @@ -759,19 +774,26 @@ bool JpegEncoder::write( const Mat& img, const std::vector& params ) { if (params[i+1] >= 0) { - chroma_quality = MIN(MAX(params[i+1], 0), 100); + chroma_quality = MIN(MAX(value, 0), 100); + if(value != chroma_quality) { + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_JPEG_CHROMA_QUALITY must be between 0 to 100. It is fallbacked to %d.", value, chroma_quality)); + } + } else { + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_JPEG_CHROMA_QUALITY must be between 0 to 100. It is ignored.", value)); } } if( params[i] == IMWRITE_JPEG_RST_INTERVAL ) { - rst_interval = params[i+1]; - rst_interval = MIN(MAX(rst_interval, 0), 65535L); + rst_interval = MIN(MAX(value, 0), 65535L); + if(value != rst_interval) { + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_JPEG_RST_INTERVAL must be between 0 to 65535. It is fallbacked to %d.", value, rst_interval)); + } } if( params[i] == IMWRITE_JPEG_SAMPLING_FACTOR ) { - sampling_factor = static_cast(params[i+1]); + sampling_factor = static_cast(value); switch ( sampling_factor ) { @@ -784,7 +806,7 @@ bool JpegEncoder::write( const Mat& img, const std::vector& params ) break; default: - CV_LOG_WARNING(NULL, cv::format("Unknown value for IMWRITE_JPEG_SAMPLING_FACTOR: 0x%06x", sampling_factor ) ); + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_JPEG_SAMPLING_FACTOR must be one of ImwriteJPEGSamplingFactorParams. It is fallbacked to IMWRITE_JPEG_SAMPLING_FACTOR_420.", value)); sampling_factor = 0; break; } diff --git a/modules/imgcodecs/src/grfmt_jpeg2000.cpp b/modules/imgcodecs/src/grfmt_jpeg2000.cpp index 9b8680ac1f..9611012c32 100644 --- a/modules/imgcodecs/src/grfmt_jpeg2000.cpp +++ b/modules/imgcodecs/src/grfmt_jpeg2000.cpp @@ -493,6 +493,7 @@ bool Jpeg2KDecoder::readComponent16u( unsigned short *data, void *_buffer, Jpeg2KEncoder::Jpeg2KEncoder() { m_description = "JPEG-2000 files (*.jp2)"; + m_supported_encode_key = {IMWRITE_JPEG2000_COMPRESSION_X1000}; } @@ -526,10 +527,17 @@ bool Jpeg2KEncoder::write( const Mat& _img, const std::vector& params ) double target_compression_rate = 1.0; for( size_t i = 0; i < params.size(); i += 2 ) { + const int value = params[i+1]; switch(params[i]) { case cv::IMWRITE_JPEG2000_COMPRESSION_X1000: - target_compression_rate = std::min(std::max(params[i+1], 0), 1000) / 1000.0; + { + const int compression = std::min(std::max(value, 0), 1000); + target_compression_rate = static_cast(compression) / 1000.0; + if(value != compression){ + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_JPEG2000_COMPRESSION_X1000 must be between 0 to 1000. It is fallbacked to %d", value, compression)); + } + } break; } } diff --git a/modules/imgcodecs/src/grfmt_jpeg2000_openjpeg.cpp b/modules/imgcodecs/src/grfmt_jpeg2000_openjpeg.cpp index 70832277ef..047a522eca 100644 --- a/modules/imgcodecs/src/grfmt_jpeg2000_openjpeg.cpp +++ b/modules/imgcodecs/src/grfmt_jpeg2000_openjpeg.cpp @@ -330,14 +330,20 @@ opj_cparameters setupEncoderParameters(const std::vector& params) bool rate_is_specified = false; for (size_t i = 0; i < params.size(); i += 2) { + const int value = params[i + 1]; switch (params[i]) { case cv::IMWRITE_JPEG2000_COMPRESSION_X1000: - parameters.tcp_rates[0] = 1000.f / std::min(std::max(params[i + 1], 1), 1000); - rate_is_specified = true; + { + const int compression = std::min(std::max(value, 1), 1000); + parameters.tcp_rates[0] = 1000.f / static_cast(compression); + if(value != compression) { + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_JPEG2000_COMPRESSION_X1000 must be between 1 to 1000. It is fallbacked to 1", value)); + } + rate_is_specified = true; + } break; default: - CV_LOG_WARNING(NULL, "OpenJPEG2000(encoder): skip unsupported parameter: " << params[i]); break; } } @@ -685,6 +691,7 @@ ImageDecoder Jpeg2KJ2KOpjDecoder::newDecoder() const Jpeg2KOpjEncoder::Jpeg2KOpjEncoder() { m_description = "JPEG-2000 files (*.jp2)"; + m_supported_encode_key = {IMWRITE_JPEG2000_COMPRESSION_X1000}; } ImageEncoder Jpeg2KOpjEncoder::newEncoder() const diff --git a/modules/imgcodecs/src/grfmt_jpegxl.cpp b/modules/imgcodecs/src/grfmt_jpegxl.cpp index 72118184f6..0d8feb878a 100644 --- a/modules/imgcodecs/src/grfmt_jpegxl.cpp +++ b/modules/imgcodecs/src/grfmt_jpegxl.cpp @@ -473,6 +473,7 @@ JpegXLEncoder::JpegXLEncoder() { m_description = "JPEG XL files (*.jxl)"; m_buf_supported = true; + m_supported_encode_key = {IMWRITE_JPEGXL_QUALITY, IMWRITE_JPEGXL_EFFORT, IMWRITE_JPEGXL_DISTANCE, IMWRITE_JPEGXL_DECODING_SPEED}; } JpegXLEncoder::~JpegXLEncoder() @@ -522,11 +523,14 @@ bool JpegXLEncoder::write(const Mat& img, const std::vector& params) float distance = -1.0; // Negative means not set for( size_t i = 0; i < params.size(); i += 2 ) { + const int value = params[i+1]; if( params[i] == IMWRITE_JPEGXL_QUALITY ) { #if JPEGXL_MAJOR_VERSION > 0 || JPEGXL_MINOR_VERSION >= 10 - int quality = params[i+1]; - quality = MIN(MAX(quality, 0), 100); + const int quality = MIN(MAX(value, 0), 100); + if(value != quality) { + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_JPEGXL_QUALITY must be between 0 to 100, It is fallbacked to %d", value, quality)); + } distance = JxlEncoderDistanceFromQuality(static_cast(quality)); #else CV_LOG_ONCE_WARNING(NULL, "Quality parameter is supported with libjxl v0.10.0 or later"); @@ -534,8 +538,10 @@ bool JpegXLEncoder::write(const Mat& img, const std::vector& params) } if( params[i] == IMWRITE_JPEGXL_DISTANCE ) { - int distanceInt = params[i+1]; - distanceInt = MIN(MAX(distanceInt, 0), 25); + const int distanceInt = MIN(MAX(value, 0), 25); + if(value != distanceInt) { + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_JPEGXL_DISTANCE must be between 0 to 25, It is fallbacked to %d", value, distanceInt)); + } distance = static_cast(distanceInt); } } diff --git a/modules/imgcodecs/src/grfmt_pam.cpp b/modules/imgcodecs/src/grfmt_pam.cpp index 2c15ab244c..12371e1f5d 100644 --- a/modules/imgcodecs/src/grfmt_pam.cpp +++ b/modules/imgcodecs/src/grfmt_pam.cpp @@ -55,6 +55,7 @@ #include "utils.hpp" #include "grfmt_pam.hpp" +#include "opencv2/core/utils/logger.hpp" namespace cv { @@ -657,6 +658,7 @@ PAMEncoder::PAMEncoder() { m_description = "Portable arbitrary format (*.pam)"; m_buf_supported = true; + m_supported_encode_key = {IMWRITE_PAM_TUPLETYPE}; } @@ -689,12 +691,25 @@ bool PAMEncoder::write( const Mat& img, const std::vector& params ) int x, y, tmp, bufsize = 256; /* parse save file type */ - for( size_t i = 0; i < params.size(); i += 2 ) + for( size_t i = 0; i < params.size(); i += 2 ) { + const int value = params[i+1]; if( params[i] == IMWRITE_PAM_TUPLETYPE ) { - if ( params[i+1] > IMWRITE_PAM_FORMAT_NULL && - params[i+1] < (int) PAM_FORMATS_NO) - fmt = &formats[params[i+1]]; + switch(value) { + case IMWRITE_PAM_FORMAT_NULL: + case IMWRITE_PAM_FORMAT_BLACKANDWHITE: + case IMWRITE_PAM_FORMAT_GRAYSCALE: + case IMWRITE_PAM_FORMAT_GRAYSCALE_ALPHA: + case IMWRITE_PAM_FORMAT_RGB: + case IMWRITE_PAM_FORMAT_RGB_ALPHA: + fmt = &formats[value]; + break; + default: + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_PAM_TUPLETYPE must be one of ImwritePAMFlags. It is ignored", value)); + fmt = nullptr; + break; + } } + } if( m_buf ) { diff --git a/modules/imgcodecs/src/grfmt_png.cpp b/modules/imgcodecs/src/grfmt_png.cpp index 4d3d8f9d8d..9a93f68b70 100644 --- a/modules/imgcodecs/src/grfmt_png.cpp +++ b/modules/imgcodecs/src/grfmt_png.cpp @@ -913,6 +913,7 @@ PngEncoder::PngEncoder() memset(palette, 0, sizeof(palette)); memset(trns, 0, sizeof(trns)); memset(op, 0, sizeof(op)); + m_supported_encode_key = {IMWRITE_PNG_COMPRESSION, IMWRITE_PNG_STRATEGY, IMWRITE_PNG_BILEVEL, IMWRITE_PNG_FILTER, IMWRITE_PNG_ZLIBBUFFER_SIZE}; } PngEncoder::~PngEncoder() @@ -983,26 +984,61 @@ bool PngEncoder::write( const Mat& img, const std::vector& params ) for( size_t i = 0; i < params.size(); i += 2 ) { + const int value = params[i+1]; switch (params[i]) { case IMWRITE_PNG_COMPRESSION: m_compression_strategy = IMWRITE_PNG_STRATEGY_DEFAULT; // Default strategy - m_compression_level = params[i+1]; - m_compression_level = MIN(MAX(m_compression_level, 0), Z_BEST_COMPRESSION); + m_compression_level = MIN(MAX(value, 0), Z_BEST_COMPRESSION); + if(value != m_compression_level) { + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_PNG_COMPRESSION must be between 0 to 9. It is fallbacked to %d", value, m_compression_level)); + } set_compression_level = true; break; case IMWRITE_PNG_STRATEGY: - m_compression_strategy = params[i+1]; - m_compression_strategy = MIN(MAX(m_compression_strategy, 0), Z_FIXED); + { + switch(value) { + case IMWRITE_PNG_STRATEGY_DEFAULT: + case IMWRITE_PNG_STRATEGY_FILTERED: + case IMWRITE_PNG_STRATEGY_HUFFMAN_ONLY: + case IMWRITE_PNG_STRATEGY_RLE: + case IMWRITE_PNG_STRATEGY_FIXED: + m_compression_strategy = value; + break; + default: + m_compression_strategy = IMWRITE_PNG_STRATEGY_RLE; + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_PNG_STRATEGY must be one of ImwritePNGFlags. It is fallbacked to IMWRITE_PNG_STRATEGY_RLE", value)); + break; + } + } break; case IMWRITE_PNG_BILEVEL: - m_isBilevel = params[i+1] != 0; + m_isBilevel = value != 0; + if((value != 0) && (value != 1)) { + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_PNG_BILEVEL must be 0 or 1. It is fallbacked to 1", value )); + } break; case IMWRITE_PNG_FILTER: - m_filter = params[i+1]; + { + switch(value) { + case IMWRITE_PNG_FILTER_NONE: + case IMWRITE_PNG_FILTER_SUB: + case IMWRITE_PNG_FILTER_UP: + case IMWRITE_PNG_FILTER_AVG: + case IMWRITE_PNG_FILTER_PAETH: + case IMWRITE_PNG_FAST_FILTERS: + case IMWRITE_PNG_ALL_FILTERS: + m_filter = value; + break; + default: + m_filter = IMWRITE_PNG_FILTER_SUB; + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_PNG_FILTER must be one of ImwritePNGFilterFlags. It is fallbacked to IMWRITE_PNG_FILTER_SUB", value )); + break; + } + } set_filter = true; break; @@ -1011,11 +1047,16 @@ bool PngEncoder::write( const Mat& img, const std::vector& params ) // The minimum limit is 6, which is from from https://github.com/opencv/opencv/blob/4.12.0/3rdparty/libpng/pngset.c#L1600 . // The maximum limit is 1 MiB, which has been provisionally set. libpng limitation is 2 GiB(INT32_MAX), but it is too large. // For normal use, 128 or 256 KiB may be sufficient. See https://zlib.net/zlib_how.html . - png_set_compression_buffer_size(png_ptr, MIN(MAX(params[i+1],6), 1024*1024)); + { + const int zlen = MIN(MAX(value, 6), 1024*1024); + if(value != zlen) { + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_PNG_ZLIBBUFFER_SIZE must be between 6 to 1024*1024. It is fallbacked to %d", value , zlen)); + } + png_set_compression_buffer_size(png_ptr, zlen); + } break; default: - CV_LOG_WARNING(NULL, "An unknown or unsupported ImwriteFlags value was specified and has been ignored."); break; } } diff --git a/modules/imgcodecs/src/grfmt_pxm.cpp b/modules/imgcodecs/src/grfmt_pxm.cpp index 20c815e833..3ddab22b56 100644 --- a/modules/imgcodecs/src/grfmt_pxm.cpp +++ b/modules/imgcodecs/src/grfmt_pxm.cpp @@ -389,6 +389,7 @@ PxMEncoder::PxMEncoder(PxMMode mode) : CV_Error(Error::StsInternal, ""); } m_buf_supported = true; + m_supported_encode_key = {IMWRITE_PXM_BINARY}; } PxMEncoder::~PxMEncoder() @@ -414,8 +415,14 @@ bool PxMEncoder::write(const Mat& img, const std::vector& params) for( size_t i = 0; i < params.size(); i += 2 ) { + const int value = params[i+1]; if( params[i] == IMWRITE_PXM_BINARY ) - isBinary = params[i+1] != 0; + { + isBinary = value != 0; + if((value != 0) && (value != 1)) { + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_PXM_BINARY must be 0 or 1. It is fallbacked to 1", value)); + } + } } int mode = mode_; diff --git a/modules/imgcodecs/src/grfmt_spng.cpp b/modules/imgcodecs/src/grfmt_spng.cpp index e2f6f02bb9..1311757b44 100644 --- a/modules/imgcodecs/src/grfmt_spng.cpp +++ b/modules/imgcodecs/src/grfmt_spng.cpp @@ -482,6 +482,7 @@ SPngEncoder::SPngEncoder() m_support_metadata[IMAGE_METADATA_EXIF] = true; m_support_metadata[IMAGE_METADATA_XMP] = true; m_support_metadata[IMAGE_METADATA_ICCP] = true; + m_supported_encode_key = {IMWRITE_PNG_COMPRESSION, IMWRITE_PNG_STRATEGY, IMWRITE_PNG_BILEVEL, IMWRITE_PNG_FILTER, IMWRITE_PNG_ZLIBBUFFER_SIZE}; } SPngEncoder::~SPngEncoder() @@ -534,25 +535,56 @@ bool SPngEncoder::write(const Mat &img, const std::vector ¶ms) for (size_t i = 0; i < params.size(); i += 2) { + const int value = params[i+1]; if (params[i] == IMWRITE_PNG_COMPRESSION) { compression_strategy = IMWRITE_PNG_STRATEGY_DEFAULT; // Default strategy - compression_level = params[i + 1]; - compression_level = MIN(MAX(compression_level, 0), Z_BEST_COMPRESSION); + compression_level = MIN(MAX(value, 0), Z_BEST_COMPRESSION); + if(value != m_compression_level) { + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_PNG_COMPRESSION must be between 0 to 9. It is fallbacked to %d", value, m_compression_level)); + } set_compression_level = true; } if (params[i] == IMWRITE_PNG_STRATEGY) { - compression_strategy = params[i + 1]; - compression_strategy = MIN(MAX(compression_strategy, 0), Z_FIXED); + switch(value) { + case IMWRITE_PNG_STRATEGY_DEFAULT: + case IMWRITE_PNG_STRATEGY_FILTERED: + case IMWRITE_PNG_STRATEGY_HUFFMAN_ONLY: + case IMWRITE_PNG_STRATEGY_RLE: + case IMWRITE_PNG_STRATEGY_FIXED: + compression_strategy = value; + break; + default: + compression_strategy = IMWRITE_PNG_STRATEGY_RLE: + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_PNG_STRATEGY must be one of ImwritePNGFlags. It is fallbacked to IMWRITE_PNG_STRATEGY_RLE", value)); + break; + } } if (params[i] == IMWRITE_PNG_BILEVEL) { - isBilevel = params[i + 1] != 0; + isBilevel = value != 0; + if((value != 0) && (value != 1)) { + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_PNG_BILEVEL must be 0 or 1. It is fallbacked to 1", value )); + } } if( params[i] == IMWRITE_PNG_FILTER ) { - filter = params[i+1]; + switch(value) { + case IMWRITE_PNG_FILTER_NONE: + case IMWRITE_PNG_FILTER_SUB: + case IMWRITE_PNG_FILTER_UP: + case IMWRITE_PNG_FILTER_AVG: + case IMWRITE_PNG_FILTER_PAETH: + case IMWRITE_PNG_FAST_FILTERS: + case IMWRITE_PNG_ALL_FILTERS: + filter = value; + break; + default: + filter = IMWRITE_PNG_FILTER_SUB; + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_PNG_FILTER must be one of ImwritePNGFilterFlags. It is fallbacked to IMWRITE_PNG_FILTER_SUB", value )); + break; + } set_filter = true; } if( params[i] == IMWRITE_PNG_ZLIBBUFFER_SIZE ) diff --git a/modules/imgcodecs/src/grfmt_tiff.cpp b/modules/imgcodecs/src/grfmt_tiff.cpp index ccc6579a01..142ad559a0 100644 --- a/modules/imgcodecs/src/grfmt_tiff.cpp +++ b/modules/imgcodecs/src/grfmt_tiff.cpp @@ -1083,6 +1083,7 @@ TiffEncoder::TiffEncoder() { m_description = "TIFF Files (*.tiff;*.tif)"; m_buf_supported = true; + m_supported_encode_key = {IMWRITE_TIFF_RESUNIT, IMWRITE_TIFF_XDPI, IMWRITE_TIFF_YDPI, IMWRITE_TIFF_COMPRESSION, IMWRITE_TIFF_ROWSPERSTRIP, IMWRITE_TIFF_PREDICTOR}; } TiffEncoder::~TiffEncoder() @@ -1218,15 +1219,92 @@ bool TiffEncoder::writeLibTiff( const std::vector& img_vec, const std::vect cv::Ptr tif_cleanup(tif, cv_tiffCloseHandle); //Settings that matter to all images - int compression = COMPRESSION_LZW; - int predictor = PREDICTOR_HORIZONTAL; + int compression = IMWRITE_TIFF_COMPRESSION_LZW; + int predictor = IMWRITE_TIFF_PREDICTOR_HORIZONTAL; int resUnit = -1, dpiX = -1, dpiY = -1; - readParam(params, IMWRITE_TIFF_COMPRESSION, compression); - readParam(params, IMWRITE_TIFF_PREDICTOR, predictor); - readParam(params, IMWRITE_TIFF_RESUNIT, resUnit); - readParam(params, IMWRITE_TIFF_XDPI, dpiX); - readParam(params, IMWRITE_TIFF_YDPI, dpiY); + if(readParam(params, IMWRITE_TIFF_COMPRESSION, compression)) + { + switch(compression) { + case IMWRITE_TIFF_COMPRESSION_NONE: + case IMWRITE_TIFF_COMPRESSION_CCITTRLE: + case IMWRITE_TIFF_COMPRESSION_CCITTFAX3: // IMWRITE_TIFF_COMPRESSION_CCITT_T4: + case IMWRITE_TIFF_COMPRESSION_CCITTFAX4: // IMWRITE_TIFF_COMPRESSION_CCITT_T6: + case IMWRITE_TIFF_COMPRESSION_LZW: + case IMWRITE_TIFF_COMPRESSION_OJPEG: + case IMWRITE_TIFF_COMPRESSION_JPEG: + case IMWRITE_TIFF_COMPRESSION_T85: + case IMWRITE_TIFF_COMPRESSION_T43: + case IMWRITE_TIFF_COMPRESSION_NEXT: + case IMWRITE_TIFF_COMPRESSION_CCITTRLEW: + case IMWRITE_TIFF_COMPRESSION_PACKBITS: + case IMWRITE_TIFF_COMPRESSION_THUNDERSCAN: + case IMWRITE_TIFF_COMPRESSION_IT8CTPAD: + case IMWRITE_TIFF_COMPRESSION_IT8LW: + case IMWRITE_TIFF_COMPRESSION_IT8MP: + case IMWRITE_TIFF_COMPRESSION_IT8BL: + case IMWRITE_TIFF_COMPRESSION_PIXARFILM: + case IMWRITE_TIFF_COMPRESSION_PIXARLOG: + case IMWRITE_TIFF_COMPRESSION_DEFLATE: + case IMWRITE_TIFF_COMPRESSION_ADOBE_DEFLATE: + case IMWRITE_TIFF_COMPRESSION_DCS: + case IMWRITE_TIFF_COMPRESSION_JBIG: + case IMWRITE_TIFF_COMPRESSION_SGILOG: + case IMWRITE_TIFF_COMPRESSION_SGILOG24: + case IMWRITE_TIFF_COMPRESSION_JP2000: + case IMWRITE_TIFF_COMPRESSION_LERC: + case IMWRITE_TIFF_COMPRESSION_LZMA: + case IMWRITE_TIFF_COMPRESSION_ZSTD: + case IMWRITE_TIFF_COMPRESSION_WEBP: + case IMWRITE_TIFF_COMPRESSION_JXL: + break; + default: + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_TIFF_COMPRESSION must be one of ImwriteTiffCompressionFlags. It is fallbacked to IMWRITE_TIFF_COMPRESSION_LZW", compression)); + compression = IMWRITE_TIFF_COMPRESSION_LZW; + break; + } + } + if(readParam(params, IMWRITE_TIFF_PREDICTOR, predictor)) + { + switch(predictor) { + case IMWRITE_TIFF_PREDICTOR_NONE: + case IMWRITE_TIFF_PREDICTOR_HORIZONTAL: + case IMWRITE_TIFF_PREDICTOR_FLOATINGPOINT: + break; + default: + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_TIFF_PREDICTOR must be one of ImwriteTiffPredictorFlags. It is fallbacked to IMWRITE_TIFF_PREDICTOR_HORIZONTAL", predictor)); + predictor = IMWRITE_TIFF_PREDICTOR_HORIZONTAL; + break; + } + } + if(readParam(params, IMWRITE_TIFF_RESUNIT, resUnit)) + { + switch(resUnit) { + case IMWRITE_TIFF_RESOLUTION_UNIT_NONE: + case IMWRITE_TIFF_RESOLUTION_UNIT_INCH: + case IMWRITE_TIFF_RESOLUTION_UNIT_CENTIMETER: + break; + default: + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_TIFF_RESUNIT must be one of ImwriteTiffResolutionUnitFlags. It is fallbacked to IMWRITE_TIFF_RESOLUTION_UNIT_INCH", resUnit)); + resUnit = IMWRITE_TIFF_RESOLUTION_UNIT_INCH; + break; + } + } + + if(readParam(params, IMWRITE_TIFF_XDPI, dpiX)) + { + if(dpiX <= 0) { + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_TIFF_XDPI must be positive. It is ignored.", dpiX)); + dpiX = -1; + } + } + if(readParam(params, IMWRITE_TIFF_YDPI, dpiY)) + { + if(dpiY <= 0) { + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_TIFF_YDPI must be positive. It is ignored.", dpiX)); + dpiY = -1; + } + } //Iterate through each image in the vector and write them out as Tiff directories for (size_t page = 0; page < img_vec.size(); page++) @@ -1249,8 +1327,7 @@ bool TiffEncoder::writeLibTiff( const std::vector& img_vec, const std::vect CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_PAGENUMBER, page, img_vec.size())); } - int compression_param = -1; // OPENCV_FUTURE - if (type == CV_32FC3 && (!readParam(params, IMWRITE_TIFF_COMPRESSION, compression_param) || compression_param == COMPRESSION_SGILOG)) + if (type == CV_32FC3 && compression == COMPRESSION_SGILOG) { if (!write_32FC3_SGILOG(img, tif)) return false; diff --git a/modules/imgcodecs/src/grfmt_webp.cpp b/modules/imgcodecs/src/grfmt_webp.cpp index c82750020a..ae11c7ce2c 100644 --- a/modules/imgcodecs/src/grfmt_webp.cpp +++ b/modules/imgcodecs/src/grfmt_webp.cpp @@ -338,6 +338,7 @@ WebPEncoder::WebPEncoder() m_support_metadata[IMAGE_METADATA_EXIF] = true; m_support_metadata[IMAGE_METADATA_XMP] = true; m_support_metadata[IMAGE_METADATA_ICCP] = true; + m_supported_encode_key = {IMWRITE_WEBP_QUALITY}; } WebPEncoder::~WebPEncoder() { } @@ -356,15 +357,17 @@ bool WebPEncoder::write(const Mat& img, const std::vector& params) bool comp_lossless = true; float quality = 100.0f; - if (params.size() > 1) + for(size_t i = 0; i < params.size(); i += 2) { - if (params[0] == IMWRITE_WEBP_QUALITY) + const int value = params[i+1]; + if (params[i] == IMWRITE_WEBP_QUALITY) { comp_lossless = false; - quality = static_cast(params[1]); + quality = static_cast(value); if (quality < 1.0f) { quality = 1.0f; + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_WEBP_QUALITY must be between 1 to 100(lossy) or more(lossless). It is fallbacked to 1", value)); } if (quality > 100.0f) { diff --git a/modules/imgcodecs/src/loadsave.cpp b/modules/imgcodecs/src/loadsave.cpp index 8e354c4dc9..31e7e4c709 100644 --- a/modules/imgcodecs/src/loadsave.cpp +++ b/modules/imgcodecs/src/loadsave.cpp @@ -364,6 +364,20 @@ static ImageEncoder findEncoder( const String& _ext ) return ImageEncoder(); } +static bool isValidEncodeKeyAtAll(const int key) +{ + bool ret = false; + ImageCodecInitializer& codecs = getCodecs(); + for( size_t i = 0; i < codecs.encoders.size(); i++ ) + { + if( codecs.encoders[i]->isValidEncodeKey(key) == true ) + { + ret = true; + break; + } + } + return ret; +} static void ExifTransform(int orientation, OutputArray img) { @@ -1110,6 +1124,27 @@ static bool imwrite_( const String& filename, const std::vector& img_vec, CV_Check(params.size(), (params.size() & 1) == 0, "Encoding 'params' must be key-value pairs"); CV_CheckLE(params.size(), (size_t)(CV_IO_MAX_IMAGE_PARAMS*2), ""); + + for(size_t v = 0; v < params.size(); v+= 2) + { + const int key = params[v]; + if(encoder->isValidEncodeKey(key)) + { + // Current encoder supports specified key. + // Do nothing. + } + else if(isValidEncodeKeyAtAll(key)) + { + // Current encoder does not support specified key, but some encoder supports it. + CV_LOG_WARNING(nullptr, cv::format("An unsupported key(%d) was specified and has been ignored.", key)); + } + else + { + // No encoder supports specified key. + CV_LOG_WARNING(nullptr, cv::format("An unknown key(%d) was specified and has been ignored.", key)); + } + } + bool code = false; try { @@ -1663,6 +1698,26 @@ bool imencodeWithMetadata( const String& ext, InputArray _img, CV_Check(params.size(), (params.size() & 1) == 0, "Encoding 'params' must be key-value pairs"); CV_CheckLE(params.size(), (size_t)(CV_IO_MAX_IMAGE_PARAMS*2), ""); + for(size_t v = 0; v < params.size(); v+= 2) + { + const int key = params[v]; + if(encoder->isValidEncodeKey(key)) + { + // Current encoder supports specified key. + // Do nothing. + } + else if(isValidEncodeKeyAtAll(key)) + { + // Current encoder does not support specified key, but some encoder supports it. + CV_LOG_WARNING(nullptr, cv::format("An unsupported key(%d) was specified and has been ignored.", key)); + } + else + { + // No encoder supports specified key. + CV_LOG_WARNING(nullptr, cv::format("An unknown key(%d) was specified and has been ignored.", key)); + } + } + bool code = false; String filename; if( !encoder->setDestination(buf) ) diff --git a/modules/imgcodecs/test/test_avif.cpp b/modules/imgcodecs/test/test_avif.cpp index 5b82452049..81cbfe9c66 100644 --- a/modules/imgcodecs/test/test_avif.cpp +++ b/modules/imgcodecs/test/test_avif.cpp @@ -128,12 +128,6 @@ TEST_P(Imgcodecs_Avif_Image_WriteReadSuite, imwrite_imread) { // Encode. const string output = cv::tempfile(".avif"); - if (!IsBitDepthValid()) { - EXPECT_NO_FATAL_FAILURE( - cv::imwrite(output, img_original, encoding_params_)); - EXPECT_NE(0, remove(output.c_str())); - return; - } EXPECT_NO_THROW(cv::imwrite(output, img_original, encoding_params_)); // Read from file. @@ -164,11 +158,6 @@ TEST_P(Imgcodecs_Avif_Image_EncodeDecodeSuite, imencode_imdecode) { bool result = true; EXPECT_NO_THROW( result = cv::imencode(".avif", img_original, buf, encoding_params_);); - - if (!IsBitDepthValid()) { - EXPECT_FALSE(result); - return; - } EXPECT_TRUE(result); // Read back. diff --git a/modules/imgcodecs/test/test_grfmt.cpp b/modules/imgcodecs/test/test_grfmt.cpp index f44e64ec7a..3ef0c9d6fb 100644 --- a/modules/imgcodecs/test/test_grfmt.cpp +++ b/modules/imgcodecs/test/test_grfmt.cpp @@ -166,6 +166,8 @@ TEST_P(Imgcodecs_ExtSize, write_imageseq) continue; if (cn == 1 && ext == ".gif") continue; + if (cn == 1 && ext == ".webp") + continue; string filename = cv::tempfile(format("%d%s", cn, ext.c_str()).c_str()); Mat img_gt(size, CV_MAKETYPE(CV_8U, cn), Scalar::all(0)); @@ -257,6 +259,9 @@ const string all_exts[] = #ifdef HAVE_IMGCODEC_GIF ".gif", #endif +#ifdef HAVE_WEBP + ".webp", +#endif }; vector all_sizes() @@ -307,6 +312,35 @@ TEST_P(Imgcodecs_pbm, write_read) INSTANTIATE_TEST_CASE_P(All, Imgcodecs_pbm, testing::Bool()); #endif +// See https://github.com/opencv/opencv/issues/27557 +typedef testing::TestWithParam Imgcodecs_invalid_key; + +TEST_P(Imgcodecs_invalid_key, encode_regression27557) +{ + const string ext = GetParam(); + const int matType = ((ext == ".pbm") || (ext == ".pgm"))? CV_8UC1 : CV_8UC3; + Mat src(100, 100, matType, Scalar(0, 255, 0)); + std::vector buf; + bool status = false; + EXPECT_NO_THROW(status = imencode(ext, src, buf, { -1, -1 })); + EXPECT_TRUE(status); +} + +TEST_P(Imgcodecs_invalid_key, write_regression27557) +{ + const string ext = GetParam(); + string fname = tempfile(ext.c_str()); + + const int matType = ((ext == ".pbm") || (ext == ".pgm"))? CV_8UC1 : CV_8UC3; + Mat src(100, 100, matType, Scalar(0, 255, 0)); + std::vector buf; + bool status = false; + EXPECT_NO_THROW(status = imwrite(fname, src, { -1, -1 })); + EXPECT_TRUE(status); + remove(fname.c_str()); +} + +INSTANTIATE_TEST_CASE_P(All, Imgcodecs_invalid_key, testing::ValuesIn(all_exts)); //================================================================================================== diff --git a/modules/imgcodecs/test/test_webp.cpp b/modules/imgcodecs/test/test_webp.cpp index 6414e60469..b3fe9b2caf 100644 --- a/modules/imgcodecs/test/test_webp.cpp +++ b/modules/imgcodecs/test/test_webp.cpp @@ -72,7 +72,7 @@ TEST(Imgcodecs_WebP, encode_decode_lossy_webp) { std::vector params; params.push_back(IMWRITE_WEBP_QUALITY); - params.push_back(q); + params.push_back(MAX(q,1)); string output = cv::tempfile(".webp"); EXPECT_NO_THROW(cv::imwrite(output, img, params)); diff --git a/modules/videoio/src/cap_images.cpp b/modules/videoio/src/cap_images.cpp index f472ff711b..1d7190003e 100644 --- a/modules/videoio/src/cap_images.cpp +++ b/modules/videoio/src/cap_images.cpp @@ -389,12 +389,8 @@ void CvVideoWriter_Images::write(InputArray image) cv::String filename = cv::format(filename_pattern.c_str(), (int)currentframe); CV_Assert(!filename.empty()); - std::vector image_params = params; - image_params.push_back(0); // append parameters 'stop' mark - image_params.push_back(0); - cv::Mat img = image.getMat(); - cv::imwrite(filename, img, image_params); + cv::imwrite(filename, img); currentframe++; } From 2762ffe7cca1f2aec81c65b14dad9c087808dda7 Mon Sep 17 00:00:00 2001 From: Madan mohan Manokar Date: Tue, 12 Aug 2025 20:20:44 +0530 Subject: [PATCH 13/14] Merge pull request #27433 from amd:fast_bilateral_simd imgproc: Bilateral filter performance improvement #27433 ### 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 --- .../imgproc/src/bilateral_filter.dispatch.cpp | 25 +- modules/imgproc/src/bilateral_filter.simd.hpp | 1230 +++++++---------- 2 files changed, 502 insertions(+), 753 deletions(-) diff --git a/modules/imgproc/src/bilateral_filter.dispatch.cpp b/modules/imgproc/src/bilateral_filter.dispatch.cpp index 7b992303b6..9ac499f685 100644 --- a/modules/imgproc/src/bilateral_filter.dispatch.cpp +++ b/modules/imgproc/src/bilateral_filter.dispatch.cpp @@ -13,6 +13,7 @@ // Copyright (C) 2000-2008, 2018, Intel Corporation, all rights reserved. // Copyright (C) 2009, Willow Garage Inc., all rights reserved. // Copyright (C) 2014-2015, Itseez Inc., all rights reserved. +// Copyright (C) 2025, Advanced Micro Devices, all rights reserved. // Third party copyrights are property of their respective owners. // // Redistribution and use in source and binary forms, with or without modification, @@ -174,8 +175,8 @@ bilateralFilter_8u( const Mat& src, Mat& dst, int d, return; } - double gauss_color_coeff = -0.5/(sigma_color*sigma_color); - double gauss_space_coeff = -0.5/(sigma_space*sigma_space); + float gauss_color_coeff = (float)(-0.5/(sigma_color*sigma_color)); + float gauss_space_coeff = (float)(-0.5/(sigma_space*sigma_space)); if( d <= 0 ) radius = cvRound(sigma_space*1.5); @@ -195,15 +196,31 @@ bilateralFilter_8u( const Mat& src, Mat& dst, int d, int* space_ofs = &_space_ofs[0]; // initialize color-related bilateral filter coefficients + i = 0; +#if (CV_SIMD || CV_SIMD_SCALABLE) + int nlanes = VTraits::vlanes(); + v_float32 v_gauss_color_coeff = vx_setall_f32(gauss_color_coeff); + float counter[16] = {0.0, 1., 2., 3., 4., 5., 6., 7., + 8., 9., 10., 11., 12., 13., 14., 15.}; + v_float32 v_i = vx_load(counter); + v_float32 v_inc = vx_setall_f32(float(nlanes)); - for( i = 0; i < 256*cn; i++ ) + for( ; i < (256*cn) - nlanes; i += nlanes ) + { + v_float32 v_color_weight = v_mul(v_mul(v_i,v_i), v_gauss_color_coeff); + v_store(color_weight + i, v_exp(v_color_weight)); + v_i = v_add(v_i, v_inc); + } +#endif + for(; i < 256*cn; i++ ) + { color_weight[i] = (float)std::exp(i*i*gauss_color_coeff); + } // initialize space-related bilateral filter coefficients for( i = -radius, maxk = 0; i <= radius; i++ ) { j = -radius; - for( ; j <= radius; j++ ) { double r = std::sqrt((double)i*i + (double)j*j); diff --git a/modules/imgproc/src/bilateral_filter.simd.hpp b/modules/imgproc/src/bilateral_filter.simd.hpp index 77e0328678..ab718eac91 100644 --- a/modules/imgproc/src/bilateral_filter.simd.hpp +++ b/modules/imgproc/src/bilateral_filter.simd.hpp @@ -13,6 +13,7 @@ // Copyright (C) 2000-2008, 2018, Intel Corporation, all rights reserved. // Copyright (C) 2009, Willow Garage Inc., all rights reserved. // Copyright (C) 2014-2015, Itseez Inc., all rights reserved. +// Copyright (C) 2025, Advanced Micro Devices, all rights reserved. // Third party copyrights are property of their respective owners. // // Redistribution and use in source and binary forms, with or without modification, @@ -73,13 +74,71 @@ public: { } +#if (CV_SIMD || CV_SIMD_SCALABLE) + static void expand(const v_uint8& v_input, v_uint32& v_out0, v_uint32& v_out1, v_uint32& v_out2, v_uint32& v_out3) + { + v_uint16 d0, d1; + v_expand(v_input, d0, d1); + v_expand(d0, v_out0, v_out1); + v_expand(d1, v_out2, v_out3); + } + + static void computeBilateral(const v_uint8& v_val_u8,const v_uint8& v_abs_diff, v_float32& kweight, const float* color_weight, + v_float32& v_wsum0, v_float32& v_sum0, v_float32& v_wsum1, v_float32& v_sum1, v_float32& v_wsum2, v_float32& v_sum2, v_float32& v_wsum3, v_float32& v_sum3) + { + v_uint32 d0, d1, d2, d3; + v_float32 w0, w1, w2, w3; + v_uint32 val0, val1, val2, val3; + expand(v_abs_diff, d0, d1, d2, d3); + expand(v_val_u8, val0, val1, val2, val3); + w0 = v_mul(kweight, v_lut(color_weight, v_reinterpret_as_s32(d0))); + w1 = v_mul(kweight, v_lut(color_weight, v_reinterpret_as_s32(d1))); + w2 = v_mul(kweight, v_lut(color_weight, v_reinterpret_as_s32(d2))); + w3 = v_mul(kweight, v_lut(color_weight, v_reinterpret_as_s32(d3))); + v_wsum0 = v_add(v_wsum0, w0); + v_sum0 = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val0)), w0, v_sum0); + v_wsum1 = v_add(v_wsum1, w1); + v_sum1 = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val1)), w1, v_sum1); + v_wsum2 = v_add(v_wsum2, w2); + v_sum2 = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val2)), w2, v_sum2); + v_wsum3 = v_add(v_wsum3, w3); + v_sum3 = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val3)), w3, v_sum3); + } + + static void computeBilateral(const v_uint8& v_val_u8,const v_uint8& v_abs_diff, const float* color_weight, + v_float32& v_wsum0, v_float32& v_sum0, v_float32& v_wsum1, v_float32& v_sum1, v_float32& v_wsum2, v_float32& v_sum2, v_float32& v_wsum3, v_float32& v_sum3) + { + v_uint32 d0, d1, d2, d3; + v_float32 w0, w1, w2, w3; + v_uint32 val0, val1, val2, val3; + expand(v_abs_diff, d0, d1, d2, d3); + expand(v_val_u8, val0, val1, val2, val3); + w0 = v_lut(color_weight, v_reinterpret_as_s32(d0)); + w1 = v_lut(color_weight, v_reinterpret_as_s32(d1)); + w2 = v_lut(color_weight, v_reinterpret_as_s32(d2)); + w3 = v_lut(color_weight, v_reinterpret_as_s32(d3)); + v_wsum0 = v_add(v_wsum0, w0); + v_sum0 = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val0)), w0, v_sum0); + v_wsum1 = v_add(v_wsum1, w1); + v_sum1 = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val1)), w1, v_sum1); + v_wsum2 = v_add(v_wsum2, w2); + v_sum2 = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val2)), w2, v_sum2); + v_wsum3 = v_add(v_wsum3, w3); + v_sum3 = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val3)), w3, v_sum3); + } +#endif + virtual void operator() (const Range& range) const CV_OVERRIDE { CV_INSTRUMENT_REGION(); int i, j, cn = dest->channels(), k; Size size = dest->size(); - +#if (CV_SIMD || CV_SIMD_SCALABLE) + int nlanes = VTraits::vlanes(); + int nlanes_2 = 2*nlanes; + int nlanes_4 = 4*nlanes; +#endif for( i = range.start; i < range.end; i++ ) { const uchar* sptr = temp->ptr(i+radius) + radius*cn; @@ -87,365 +146,233 @@ public: if( cn == 1 ) { - AutoBuffer buf(alignSize(size.width, CV_SIMD_WIDTH) + size.width + CV_SIMD_WIDTH - 1); - memset(buf.data(), 0, buf.size() * sizeof(float)); - float *sum = alignPtr(buf.data(), CV_SIMD_WIDTH); - float *wsum = sum + alignSize(size.width, CV_SIMD_WIDTH); - k = 0; - for(; k <= maxk-4; k+=4) + k = 0; j=0; + +#if (CV_SIMD || CV_SIMD_SCALABLE) + for ( ;j <= size.width - nlanes_4; j += nlanes_4) { - const uchar* ksptr0 = sptr + space_ofs[k]; - const uchar* ksptr1 = sptr + space_ofs[k+1]; - const uchar* ksptr2 = sptr + space_ofs[k+2]; - const uchar* ksptr3 = sptr + space_ofs[k+3]; - j = 0; -#if (CV_SIMD || CV_SIMD_SCALABLE) - v_float32 kweight0 = vx_setall_f32(space_weight[k]); - v_float32 kweight1 = vx_setall_f32(space_weight[k+1]); - v_float32 kweight2 = vx_setall_f32(space_weight[k+2]); - v_float32 kweight3 = vx_setall_f32(space_weight[k+3]); - for (; j <= size.width - VTraits::vlanes(); j += VTraits::vlanes()) + const uchar* sptr_j = sptr + j; + v_float32 v_wsum0 = vx_setzero_f32(); + v_float32 v_wsum1 = vx_setzero_f32(); + v_float32 v_wsum2 = vx_setzero_f32(); + v_float32 v_wsum3 = vx_setzero_f32(); + v_float32 v_sum0 = vx_setzero_f32(); + v_float32 v_sum1 = vx_setzero_f32(); + v_float32 v_sum2 = vx_setzero_f32(); + v_float32 v_sum3 = vx_setzero_f32(); + v_uint8 v_sptr8 = vx_load(sptr_j); + + k=0; + if(maxk==5) { - v_uint32 rval = vx_load_expand_q(sptr + j); + const uchar* ksptrline1 = sptr_j + space_ofs[0]; + const uchar* ksptrline2 = sptr_j + space_ofs[1]; + const uchar* ksptrline3 = sptr_j + space_ofs[4]; - v_uint32 val = vx_load_expand_q(ksptr0 + j); - v_float32 w = v_mul(kweight0, v_lut(color_weight, v_reinterpret_as_s32(v_absdiff(val, rval)))); - v_float32 v_wsum = v_add(vx_load_aligned(wsum + j), w); - v_float32 v_sum = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val)), w, vx_load_aligned(sum + j)); + v_float32 kweight = vx_setall_f32(space_weight[0]);//same weight for all, expect centre one - val = vx_load_expand_q(ksptr1 + j); - w = v_mul(kweight1, v_lut(color_weight, v_reinterpret_as_s32(v_absdiff(val, rval)))); - v_wsum = v_add(v_wsum, w); - v_sum = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val)), w, v_sum); + v_uint8 v_val_u8_line1 = vx_load(ksptrline1); + v_uint8 v_val_u8_line2_0 = vx_load(ksptrline2); + v_uint8 v_val_u8_line2_1 = vx_load(ksptrline2 + 1); + v_uint8 v_val_u8_line2_2 = vx_load(ksptrline2 + 2); + v_uint8 v_val_u8_line3 = vx_load(ksptrline3); - val = vx_load_expand_q(ksptr2 + j); - w = v_mul(kweight2, v_lut(color_weight, v_reinterpret_as_s32(v_absdiff(val, rval)))); - v_wsum = v_add(v_wsum, w); - v_sum = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val)), w, v_sum); + //compute abs diff + v_uint8 v_abs_diff_line1 = v_absdiff(v_val_u8_line1, v_sptr8); + v_uint8 v_abs_diff_line2_0 = v_absdiff(v_val_u8_line2_0, v_sptr8); + v_uint8 v_abs_diff_line2_1 = v_absdiff(v_val_u8_line2_1, v_sptr8); + v_uint8 v_abs_diff_line2_2 = v_absdiff(v_val_u8_line2_2, v_sptr8); + v_uint8 v_abs_diff_line3 = v_absdiff(v_val_u8_line3, v_sptr8); - val = vx_load_expand_q(ksptr3 + j); - w = v_mul(kweight3, v_lut(color_weight, v_reinterpret_as_s32(v_absdiff(val, rval)))); - v_wsum = v_add(v_wsum, w); - v_sum = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val)), w, v_sum); + computeBilateral( v_val_u8_line1, v_abs_diff_line1, kweight, color_weight, + v_wsum0, v_sum0, v_wsum1, v_sum1, v_wsum2, v_sum2, v_wsum3, v_sum3); + computeBilateral( v_val_u8_line2_0, v_abs_diff_line2_0, kweight, color_weight, + v_wsum0, v_sum0, v_wsum1, v_sum1, v_wsum2, v_sum2, v_wsum3, v_sum3); + computeBilateral( v_val_u8_line2_1, v_abs_diff_line2_1, color_weight, + v_wsum0, v_sum0, v_wsum1, v_sum1, v_wsum2, v_sum2, v_wsum3, v_sum3); + computeBilateral( v_val_u8_line2_2, v_abs_diff_line2_2, kweight, color_weight, + v_wsum0, v_sum0, v_wsum1, v_sum1, v_wsum2, v_sum2, v_wsum3, v_sum3); + computeBilateral( v_val_u8_line3, v_abs_diff_line3, kweight, color_weight, + v_wsum0, v_sum0, v_wsum1, v_sum1, v_wsum2, v_sum2, v_wsum3, v_sum3); - v_store_aligned(wsum + j, v_wsum); - v_store_aligned(sum + j, v_sum); + k = maxk; } -#endif -#if CV_SIMD128 - v_float32x4 kweight4 = v_load(space_weight + k); -#endif - for (; j < size.width; j++) + else if(maxk==13) { -#if CV_SIMD128 - v_uint32x4 rval = v_setall_u32(sptr[j]); - v_uint32x4 val(ksptr0[j], ksptr1[j], ksptr2[j], ksptr3[j]); - v_float32x4 w = v_mul(kweight4, v_lut(this->color_weight, v_reinterpret_as_s32(v_absdiff(val, rval)))); - wsum[j] += v_reduce_sum(w); - sum[j] += v_reduce_sum(v_mul(v_cvt_f32(v_reinterpret_as_s32(val)), w)); -#else - int rval = sptr[j]; + const uchar* ksptrline1 = sptr_j + space_ofs[0]; + const uchar* ksptrline5 = sptr_j + space_ofs[12];//last element + const uchar* ksptrline2 = sptr_j + space_ofs[1]; + const uchar* ksptrline3 = sptr_j + space_ofs[4]; + const uchar* ksptrline4 = sptr_j + space_ofs[9]; - int val = ksptr0[j]; - float w = space_weight[k] * color_weight[std::abs(val - rval)]; - wsum[j] += w; - sum[j] += val * w; + v_float32 kweight = vx_setall_f32(space_weight[0]); - val = ksptr1[j]; - w = space_weight[k+1] * color_weight[std::abs(val - rval)]; - wsum[j] += w; - sum[j] += val * w; + //compute line 1 and 5 + v_uint8 v_val_u8_line1 = vx_load(ksptrline1); + v_uint8 v_val_u8_line5 = vx_load(ksptrline5); + v_uint8 v_abs_diff_line1 = v_absdiff(v_val_u8_line1, v_sptr8); + v_uint8 v_abs_diff_line5 = v_absdiff(v_val_u8_line5, v_sptr8); + computeBilateral( v_val_u8_line1, v_abs_diff_line1, kweight, color_weight, + v_wsum0, v_sum0, v_wsum1, v_sum1, v_wsum2, v_sum2, v_wsum3, v_sum3); + computeBilateral( v_val_u8_line5, v_abs_diff_line5, kweight, color_weight, + v_wsum0, v_sum0, v_wsum1, v_sum1, v_wsum2, v_sum2, v_wsum3, v_sum3); - val = ksptr2[j]; - w = space_weight[k+2] * color_weight[std::abs(val - rval)]; - wsum[j] += w; - sum[j] += val * w; + //compute line 2 and 4 + v_uint8 v_val_u8_line2_0 = vx_load(ksptrline2); + v_uint8 v_val_u8_line2_1 = vx_load(ksptrline2 + 1); + v_uint8 v_val_u8_line2_2 = vx_load(ksptrline2 + 2); - val = ksptr3[j]; - w = space_weight[k+3] * color_weight[std::abs(val - rval)]; - wsum[j] += w; - sum[j] += val * w; -#endif + v_uint8 v_val_u8_line4_0 = vx_load(ksptrline4); + v_uint8 v_val_u8_line4_1 = vx_load(ksptrline4 + 1); + v_uint8 v_val_u8_line4_2 = vx_load(ksptrline4 + 2); + + v_uint8 v_abs_diff_line2_0 = v_absdiff(v_val_u8_line2_0, v_sptr8); + v_uint8 v_abs_diff_line2_1 = v_absdiff(v_val_u8_line2_1, v_sptr8); + v_uint8 v_abs_diff_line2_2 = v_absdiff(v_val_u8_line2_2, v_sptr8); + v_uint8 v_abs_diff_line4_0 = v_absdiff(v_val_u8_line4_0, v_sptr8); + v_uint8 v_abs_diff_line4_1 = v_absdiff(v_val_u8_line4_1, v_sptr8); + v_uint8 v_abs_diff_line4_2 = v_absdiff(v_val_u8_line4_2, v_sptr8); + v_float32 kweight_1 = vx_setall_f32(space_weight[1]); + v_float32 kweight_2 = vx_setall_f32(space_weight[2]); + computeBilateral( v_val_u8_line2_0, v_abs_diff_line2_0, kweight_1, color_weight, + v_wsum0, v_sum0, v_wsum1, v_sum1, v_wsum2, v_sum2, v_wsum3, v_sum3); + computeBilateral( v_val_u8_line2_1, v_abs_diff_line2_1, kweight_2, color_weight, + v_wsum0, v_sum0, v_wsum1, v_sum1, v_wsum2, v_sum2, v_wsum3, v_sum3); + computeBilateral( v_val_u8_line2_2, v_abs_diff_line2_2, kweight_1, color_weight, + v_wsum0, v_sum0, v_wsum1, v_sum1, v_wsum2, v_sum2, v_wsum3, v_sum3); + + computeBilateral( v_val_u8_line4_0, v_abs_diff_line4_0, kweight_1, color_weight, + v_wsum0, v_sum0, v_wsum1, v_sum1, v_wsum2, v_sum2, v_wsum3, v_sum3); + computeBilateral( v_val_u8_line4_1, v_abs_diff_line4_1, kweight_2, color_weight, + v_wsum0, v_sum0, v_wsum1, v_sum1, v_wsum2, v_sum2, v_wsum3, v_sum3); + computeBilateral( v_val_u8_line4_2, v_abs_diff_line4_2, kweight_1, color_weight, + v_wsum0, v_sum0, v_wsum1, v_sum1, v_wsum2, v_sum2, v_wsum3, v_sum3); + + //compute line 3 + v_uint8 v_val_u8_line3_0 = vx_load(ksptrline3); + v_uint8 v_val_u8_line3_1 = vx_load(ksptrline3 + 1); + v_uint8 v_val_u8_line3_2 = vx_load(ksptrline3 + 2); + v_uint8 v_val_u8_line3_3 = vx_load(ksptrline3 + 3); + v_uint8 v_val_u8_line3_4 = vx_load(ksptrline3 + 4); + + v_uint8 v_abs_diff_line3_0 = v_absdiff(v_val_u8_line3_0, v_sptr8); + v_uint8 v_abs_diff_line3_1 = v_absdiff(v_val_u8_line3_1, v_sptr8); + v_uint8 v_abs_diff_line3_2 = v_absdiff(v_val_u8_line3_2, v_sptr8); + v_uint8 v_abs_diff_line3_3 = v_absdiff(v_val_u8_line3_3, v_sptr8); + v_uint8 v_abs_diff_line3_4 = v_absdiff(v_val_u8_line3_4, v_sptr8); + + computeBilateral( v_val_u8_line3_0, v_abs_diff_line3_0, kweight, color_weight, + v_wsum0, v_sum0, v_wsum1, v_sum1, v_wsum2, v_sum2, v_wsum3, v_sum3); + computeBilateral( v_val_u8_line3_1, v_abs_diff_line3_1, kweight_2, color_weight, + v_wsum0, v_sum0, v_wsum1, v_sum1, v_wsum2, v_sum2, v_wsum3, v_sum3); + computeBilateral( v_val_u8_line3_2, v_abs_diff_line3_2, color_weight, + v_wsum0, v_sum0, v_wsum1, v_sum1, v_wsum2, v_sum2, v_wsum3, v_sum3); + computeBilateral( v_val_u8_line3_3, v_abs_diff_line3_3, kweight_2, color_weight, + v_wsum0, v_sum0, v_wsum1, v_sum1, v_wsum2, v_sum2, v_wsum3, v_sum3); + computeBilateral( v_val_u8_line3_4, v_abs_diff_line3_4, kweight, color_weight, + v_wsum0, v_sum0, v_wsum1, v_sum1, v_wsum2, v_sum2, v_wsum3, v_sum3); + + k = maxk; } + + for(; k < maxk; k++) + { + const uchar* ksptr = sptr_j + space_ofs[k]; + v_float32 kweight = vx_setall_f32(space_weight[k]); + + v_uint8 v_val_u8 = vx_load(ksptr); + v_uint8 v_abs_diff = v_absdiff(v_val_u8, v_sptr8); + + v_uint16 d0, d1; + v_uint32 diff0, diff1, diff2, diff3; + v_expand(v_abs_diff, d0, d1); + v_expand(d0, diff0, diff1); + v_expand(d1, diff2, diff3); + + v_uint16 v0, v1; + v_uint32 val0, val1, val2, val3; + v_expand(v_val_u8, v0, v1); + v_expand(v0, val0, val1); + v_expand(v1, val2, val3); + + v_float32 w0 = v_mul(kweight, v_lut(color_weight, v_reinterpret_as_s32(diff0))); + v_float32 w1 = v_mul(kweight, v_lut(color_weight, v_reinterpret_as_s32(diff1))); + v_float32 w2 = v_mul(kweight, v_lut(color_weight, v_reinterpret_as_s32(diff2))); + v_float32 w3 = v_mul(kweight, v_lut(color_weight, v_reinterpret_as_s32(diff3))); + + v_wsum0 = v_add(v_wsum0, w0); + v_sum0 = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val0)), w0, v_sum0); + v_wsum1 = v_add(v_wsum1, w1); + v_sum1 = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val1)), w1, v_sum1); + v_wsum2 = v_add(v_wsum2, w2); + v_sum2 = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val2)), w2, v_sum2); + v_wsum3 = v_add(v_wsum3, w3); + v_sum3 = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val3)), w3, v_sum3); + } + v_pack_u_store(dptr + j, v_pack(v_round(v_div(v_sum0, v_wsum0)), + v_round(v_div(v_sum1, v_wsum1)))); + + v_pack_u_store(dptr + j + nlanes_2, v_pack(v_round(v_div(v_sum2, v_wsum2)), + v_round(v_div(v_sum3, v_wsum3)))); } - for(; k < maxk; k++) - { - const uchar* ksptr = sptr + space_ofs[k]; - j = 0; -#if (CV_SIMD || CV_SIMD_SCALABLE) - v_float32 kweight = vx_setall_f32(space_weight[k]); - for (; j <= size.width - VTraits::vlanes(); j += VTraits::vlanes()) - { - v_uint32 val = vx_load_expand_q(ksptr + j); - v_float32 w = v_mul(kweight, v_lut(color_weight, v_reinterpret_as_s32(v_absdiff(val, vx_load_expand_q(sptr + j))))); - v_store_aligned(wsum + j, v_add(vx_load_aligned(wsum + j), w)); - v_store_aligned(sum + j, v_muladd(v_cvt_f32(v_reinterpret_as_s32(val)), w, vx_load_aligned(sum + j))); - } -#endif - for (; j < size.width; j++) - { - int val = ksptr[j]; - float w = space_weight[k] * color_weight[std::abs(val - sptr[j])]; - wsum[j] += w; - sum[j] += val * w; - } - } - j = 0; -#if (CV_SIMD || CV_SIMD_SCALABLE) - for (; j <= size.width - 2*VTraits::vlanes(); j += 2*VTraits::vlanes()) - v_pack_u_store(dptr + j, v_pack(v_round(v_div(vx_load_aligned(sum + j), vx_load_aligned(wsum + j))), - v_round(v_div(vx_load_aligned(sum + j + VTraits::vlanes()), vx_load_aligned(wsum + j + VTraits::vlanes()))))); #endif for (; j < size.width; j++) { + uchar val0 = sptr[j]; + float wsumT = 0; + float sumT = 0; + for(k=0; k < maxk; k++) + { + const uchar* ksptr = sptr + space_ofs[k]; + uchar val = ksptr[j]; + float w = space_weight[k] * color_weight[std::abs(val - val0)]; + wsumT += w; + sumT += val * w; + } + // overflow is not possible here => there is no need to use cv::saturate_cast - CV_DbgAssert(fabs(wsum[j]) > 0); - dptr[j] = (uchar)cvRound(sum[j]/wsum[j]); + CV_DbgAssert(fabs(wsumT) > 0); + dptr[j] = (uchar)cvRound(sumT/wsumT); } + } else { CV_Assert( cn == 3 ); - AutoBuffer buf(alignSize(size.width, CV_SIMD_WIDTH)*3 + size.width + CV_SIMD_WIDTH - 1); - memset(buf.data(), 0, buf.size() * sizeof(float)); - float *sum_b = alignPtr(buf.data(), CV_SIMD_WIDTH); - float *sum_g = sum_b + alignSize(size.width, CV_SIMD_WIDTH); - float *sum_r = sum_g + alignSize(size.width, CV_SIMD_WIDTH); - float *wsum = sum_r + alignSize(size.width, CV_SIMD_WIDTH); - k = 0; - for(; k <= maxk-4; k+=4) - { - const uchar* ksptr0 = sptr + space_ofs[k]; - const uchar* ksptr1 = sptr + space_ofs[k+1]; - const uchar* ksptr2 = sptr + space_ofs[k+2]; - const uchar* ksptr3 = sptr + space_ofs[k+3]; - const uchar* rsptr = sptr; - j = 0; + j = 0; + const uchar* sptr_j = sptr; #if (CV_SIMD || CV_SIMD_SCALABLE) - v_float32 kweight0 = vx_setall_f32(space_weight[k]); - v_float32 kweight1 = vx_setall_f32(space_weight[k+1]); - v_float32 kweight2 = vx_setall_f32(space_weight[k+2]); - v_float32 kweight3 = vx_setall_f32(space_weight[k+3]); - for (; j <= size.width - VTraits::vlanes(); j += VTraits::vlanes(), rsptr += 3*VTraits::vlanes(), - ksptr0 += 3*VTraits::vlanes(), ksptr1 += 3*VTraits::vlanes(), ksptr2 += 3*VTraits::vlanes(), ksptr3 += 3*VTraits::vlanes()) - { - v_uint8 kb, kg, kr, rb, rg, rr; - v_load_deinterleave(rsptr, rb, rg, rr); - - v_load_deinterleave(ksptr0, kb, kg, kr); - v_uint16 val0, val1, val2, val3, val4; - v_expand(v_absdiff(kb, rb), val0, val1); - v_expand(v_absdiff(kg, rg), val2, val3); - val0 = v_add(val0, val2); val1 = v_add(val1, val3); - v_expand(v_absdiff(kr, rr), val2, val3); - val0 = v_add(val0, val2); val1 = v_add(val1, val3); - - v_uint32 vall, valh; - v_expand(val0, vall, valh); - v_float32 w0 = v_mul(kweight0, v_lut(color_weight, v_reinterpret_as_s32(vall))); - v_float32 w1 = v_mul(kweight0, v_lut(color_weight, v_reinterpret_as_s32(valh))); - v_store_aligned(wsum + j, v_add(w0, vx_load_aligned(wsum + j))); - v_store_aligned(wsum + j + VTraits::vlanes(), v_add(w1, vx_load_aligned(wsum + j + VTraits::vlanes()))); - v_expand(kb, val0, val2); - v_expand(val0, vall, valh); - v_store_aligned(sum_b + j , v_muladd(v_cvt_f32(v_reinterpret_as_s32(vall)), w0, vx_load_aligned(sum_b + j))); - v_store_aligned(sum_b + j + VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(valh)), w1, vx_load_aligned(sum_b + j + VTraits::vlanes()))); - v_expand(kg, val0, val3); - v_expand(val0, vall, valh); - v_store_aligned(sum_g + j , v_muladd(v_cvt_f32(v_reinterpret_as_s32(vall)), w0, vx_load_aligned(sum_g + j))); - v_store_aligned(sum_g + j + VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(valh)), w1, vx_load_aligned(sum_g + j + VTraits::vlanes()))); - v_expand(kr, val0, val4); - v_expand(val0, vall, valh); - v_store_aligned(sum_r + j , v_muladd(v_cvt_f32(v_reinterpret_as_s32(vall)), w0, vx_load_aligned(sum_r + j))); - v_store_aligned(sum_r + j + VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(valh)), w1, vx_load_aligned(sum_r + j + VTraits::vlanes()))); - - v_expand(val1, vall, valh); - w0 = v_mul(kweight0, v_lut(color_weight, v_reinterpret_as_s32(vall))); - w1 = v_mul(kweight0, v_lut(color_weight, v_reinterpret_as_s32(valh))); - v_store_aligned(wsum + j + 2 * VTraits::vlanes(), v_add(w0, vx_load_aligned(wsum + j + 2 * VTraits::vlanes()))); - v_store_aligned(wsum + j + 3 * VTraits::vlanes(), v_add(w1, vx_load_aligned(wsum + j + 3 * VTraits::vlanes()))); - v_expand(val2, vall, valh); - v_store_aligned(sum_b + j + 2 * VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(vall)), w0, vx_load_aligned(sum_b + j + 2 * VTraits::vlanes()))); - v_store_aligned(sum_b + j + 3 * VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(valh)), w1, vx_load_aligned(sum_b + j + 3 * VTraits::vlanes()))); - v_expand(val3, vall, valh); - v_store_aligned(sum_g + j + 2 * VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(vall)), w0, vx_load_aligned(sum_g + j + 2 * VTraits::vlanes()))); - v_store_aligned(sum_g + j + 3 * VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(valh)), w1, vx_load_aligned(sum_g + j + 3 * VTraits::vlanes()))); - v_expand(val4, vall, valh); - v_store_aligned(sum_r + j + 2*VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(vall)), w0, vx_load_aligned(sum_r + j + 2*VTraits::vlanes()))); - v_store_aligned(sum_r + j + 3*VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(valh)), w1, vx_load_aligned(sum_r + j + 3*VTraits::vlanes()))); - - v_load_deinterleave(ksptr1, kb, kg, kr); - v_expand(v_absdiff(kb, rb), val0, val1); - v_expand(v_absdiff(kg, rg), val2, val3); - val0 = v_add(val0, val2); val1 = v_add(val1, val3); - v_expand(v_absdiff(kr, rr), val2, val3); - val0 = v_add(val0, val2); val1 = v_add(val1, val3); - - v_expand(val0, vall, valh); - w0 = v_mul(kweight1, v_lut(color_weight, v_reinterpret_as_s32(vall))); - w1 = v_mul(kweight1, v_lut(color_weight, v_reinterpret_as_s32(valh))); - v_store_aligned(wsum + j, v_add(w0, vx_load_aligned(wsum + j))); - v_store_aligned(wsum + j + VTraits::vlanes(), v_add(w1, vx_load_aligned(wsum + j + VTraits::vlanes()))); - v_expand(kb, val0, val2); - v_expand(val0, vall, valh); - v_store_aligned(sum_b + j, v_muladd(v_cvt_f32(v_reinterpret_as_s32(vall)), w0, vx_load_aligned(sum_b + j))); - v_store_aligned(sum_b + j + VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(valh)), w1, vx_load_aligned(sum_b + j + VTraits::vlanes()))); - v_expand(kg, val0, val3); - v_expand(val0, vall, valh); - v_store_aligned(sum_g + j, v_muladd(v_cvt_f32(v_reinterpret_as_s32(vall)), w0, vx_load_aligned(sum_g + j))); - v_store_aligned(sum_g + j + VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(valh)), w1, vx_load_aligned(sum_g + j + VTraits::vlanes()))); - v_expand(kr, val0, val4); - v_expand(val0, vall, valh); - v_store_aligned(sum_r + j, v_muladd(v_cvt_f32(v_reinterpret_as_s32(vall)), w0, vx_load_aligned(sum_r + j))); - v_store_aligned(sum_r + j + VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(valh)), w1, vx_load_aligned(sum_r + j + VTraits::vlanes()))); - - v_expand(val1, vall, valh); - w0 = v_mul(kweight1, v_lut(color_weight, v_reinterpret_as_s32(vall))); - w1 = v_mul(kweight1, v_lut(color_weight, v_reinterpret_as_s32(valh))); - v_store_aligned(wsum + j + 2 * VTraits::vlanes(), v_add(w0, vx_load_aligned(wsum + j + 2 * VTraits::vlanes()))); - v_store_aligned(wsum + j + 3 * VTraits::vlanes(), v_add(w1, vx_load_aligned(wsum + j + 3 * VTraits::vlanes()))); - v_expand(val2, vall, valh); - v_store_aligned(sum_b + j + 2 * VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(vall)), w0, vx_load_aligned(sum_b + j + 2 * VTraits::vlanes()))); - v_store_aligned(sum_b + j + 3 * VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(valh)), w1, vx_load_aligned(sum_b + j + 3 * VTraits::vlanes()))); - v_expand(val3, vall, valh); - v_store_aligned(sum_g + j + 2 * VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(vall)), w0, vx_load_aligned(sum_g + j + 2 * VTraits::vlanes()))); - v_store_aligned(sum_g + j + 3 * VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(valh)), w1, vx_load_aligned(sum_g + j + 3 * VTraits::vlanes()))); - v_expand(val4, vall, valh); - v_store_aligned(sum_r + j + 2 * VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(vall)), w0, vx_load_aligned(sum_r + j + 2 * VTraits::vlanes()))); - v_store_aligned(sum_r + j + 3 * VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(valh)), w1, vx_load_aligned(sum_r + j + 3 * VTraits::vlanes()))); - - v_load_deinterleave(ksptr2, kb, kg, kr); - v_expand(v_absdiff(kb, rb), val0, val1); - v_expand(v_absdiff(kg, rg), val2, val3); - val0 = v_add(val0, val2); val1 = v_add(val1, val3); - v_expand(v_absdiff(kr, rr), val2, val3); - val0 = v_add(val0, val2); val1 = v_add(val1, val3); - - v_expand(val0, vall, valh); - w0 = v_mul(kweight2, v_lut(color_weight, v_reinterpret_as_s32(vall))); - w1 = v_mul(kweight2, v_lut(color_weight, v_reinterpret_as_s32(valh))); - v_store_aligned(wsum + j, v_add(w0, vx_load_aligned(wsum + j))); - v_store_aligned(wsum + j + VTraits::vlanes(), v_add(w1, vx_load_aligned(wsum + j + VTraits::vlanes()))); - v_expand(kb, val0, val2); - v_expand(val0, vall, valh); - v_store_aligned(sum_b + j, v_muladd(v_cvt_f32(v_reinterpret_as_s32(vall)), w0, vx_load_aligned(sum_b + j))); - v_store_aligned(sum_b + j + VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(valh)), w1, vx_load_aligned(sum_b + j + VTraits::vlanes()))); - v_expand(kg, val0, val3); - v_expand(val0, vall, valh); - v_store_aligned(sum_g + j, v_muladd(v_cvt_f32(v_reinterpret_as_s32(vall)), w0, vx_load_aligned(sum_g + j))); - v_store_aligned(sum_g + j + VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(valh)), w1, vx_load_aligned(sum_g + j + VTraits::vlanes()))); - v_expand(kr, val0, val4); - v_expand(val0, vall, valh); - v_store_aligned(sum_r + j, v_muladd(v_cvt_f32(v_reinterpret_as_s32(vall)), w0, vx_load_aligned(sum_r + j))); - v_store_aligned(sum_r + j + VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(valh)), w1, vx_load_aligned(sum_r + j + VTraits::vlanes()))); - - v_expand(val1, vall, valh); - w0 = v_mul(kweight2, v_lut(color_weight, v_reinterpret_as_s32(vall))); - w1 = v_mul(kweight2, v_lut(color_weight, v_reinterpret_as_s32(valh))); - v_store_aligned(wsum + j + 2 * VTraits::vlanes(), v_add(w0, vx_load_aligned(wsum + j + 2 * VTraits::vlanes()))); - v_store_aligned(wsum + j + 3 * VTraits::vlanes(), v_add(w1, vx_load_aligned(wsum + j + 3 * VTraits::vlanes()))); - v_expand(val2, vall, valh); - v_store_aligned(sum_b + j + 2 * VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(vall)), w0, vx_load_aligned(sum_b + j + 2 * VTraits::vlanes()))); - v_store_aligned(sum_b + j + 3 * VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(valh)), w1, vx_load_aligned(sum_b + j + 3 * VTraits::vlanes()))); - v_expand(val3, vall, valh); - v_store_aligned(sum_g + j + 2 * VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(vall)), w0, vx_load_aligned(sum_g + j + 2 * VTraits::vlanes()))); - v_store_aligned(sum_g + j + 3 * VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(valh)), w1, vx_load_aligned(sum_g + j + 3 * VTraits::vlanes()))); - v_expand(val4, vall, valh); - v_store_aligned(sum_r + j + 2 * VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(vall)), w0, vx_load_aligned(sum_r + j + 2 * VTraits::vlanes()))); - v_store_aligned(sum_r + j + 3 * VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(valh)), w1, vx_load_aligned(sum_r + j + 3 * VTraits::vlanes()))); - - v_load_deinterleave(ksptr3, kb, kg, kr); - v_expand(v_absdiff(kb, rb), val0, val1); - v_expand(v_absdiff(kg, rg), val2, val3); - val0 = v_add(val0, val2); val1 = v_add(val1, val3); - v_expand(v_absdiff(kr, rr), val2, val3); - val0 = v_add(val0, val2); val1 = v_add(val1, val3); - - v_expand(val0, vall, valh); - w0 = v_mul(kweight3, v_lut(color_weight, v_reinterpret_as_s32(vall))); - w1 = v_mul(kweight3, v_lut(color_weight, v_reinterpret_as_s32(valh))); - v_store_aligned(wsum + j, v_add(w0, vx_load_aligned(wsum + j))); - v_store_aligned(wsum + j + VTraits::vlanes(), v_add(w1, vx_load_aligned(wsum + j + VTraits::vlanes()))); - v_expand(kb, val0, val2); - v_expand(val0, vall, valh); - v_store_aligned(sum_b + j, v_muladd(v_cvt_f32(v_reinterpret_as_s32(vall)), w0, vx_load_aligned(sum_b + j))); - v_store_aligned(sum_b + j + VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(valh)), w1, vx_load_aligned(sum_b + j + VTraits::vlanes()))); - v_expand(kg, val0, val3); - v_expand(val0, vall, valh); - v_store_aligned(sum_g + j, v_muladd(v_cvt_f32(v_reinterpret_as_s32(vall)), w0, vx_load_aligned(sum_g + j))); - v_store_aligned(sum_g + j + VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(valh)), w1, vx_load_aligned(sum_g + j + VTraits::vlanes()))); - v_expand(kr, val0, val4); - v_expand(val0, vall, valh); - v_store_aligned(sum_r + j, v_muladd(v_cvt_f32(v_reinterpret_as_s32(vall)), w0, vx_load_aligned(sum_r + j))); - v_store_aligned(sum_r + j + VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(valh)), w1, vx_load_aligned(sum_r + j + VTraits::vlanes()))); - - v_expand(val1, vall, valh); - w0 = v_mul(kweight3, v_lut(color_weight, v_reinterpret_as_s32(vall))); - w1 = v_mul(kweight3, v_lut(color_weight, v_reinterpret_as_s32(valh))); - v_store_aligned(wsum + j + 2 * VTraits::vlanes(), v_add(w0, vx_load_aligned(wsum + j + 2 * VTraits::vlanes()))); - v_store_aligned(wsum + j + 3 * VTraits::vlanes(), v_add(w1, vx_load_aligned(wsum + j + 3 * VTraits::vlanes()))); - v_expand(val2, vall, valh); - v_store_aligned(sum_b + j + 2 * VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(vall)), w0, vx_load_aligned(sum_b + j + 2 * VTraits::vlanes()))); - v_store_aligned(sum_b + j + 3 * VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(valh)), w1, vx_load_aligned(sum_b + j + 3 * VTraits::vlanes()))); - v_expand(val3, vall, valh); - v_store_aligned(sum_g + j + 2 * VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(vall)), w0, vx_load_aligned(sum_g + j + 2 * VTraits::vlanes()))); - v_store_aligned(sum_g + j + 3 * VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(valh)), w1, vx_load_aligned(sum_g + j + 3 * VTraits::vlanes()))); - v_expand(val4, vall, valh); - v_store_aligned(sum_r + j + 2 * VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(vall)), w0, vx_load_aligned(sum_r + j + 2 * VTraits::vlanes()))); - v_store_aligned(sum_r + j + 3 * VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(valh)), w1, vx_load_aligned(sum_r + j + 3 * VTraits::vlanes()))); - } -#endif -#if CV_SIMD128 - v_float32x4 kweight4 = v_load(space_weight + k); -#endif - for(; j < size.width; j++, rsptr += 3, ksptr0 += 3, ksptr1 += 3, ksptr2 += 3, ksptr3 += 3) - { -#if CV_SIMD128 - v_uint32x4 rb = v_setall_u32(rsptr[0]); - v_uint32x4 rg = v_setall_u32(rsptr[1]); - v_uint32x4 rr = v_setall_u32(rsptr[2]); - v_uint32x4 b(ksptr0[0], ksptr1[0], ksptr2[0], ksptr3[0]); - v_uint32x4 g(ksptr0[1], ksptr1[1], ksptr2[1], ksptr3[1]); - v_uint32x4 r(ksptr0[2], ksptr1[2], ksptr2[2], ksptr3[2]); - v_float32x4 w = v_mul(kweight4, v_lut(this->color_weight, v_reinterpret_as_s32(v_add(v_add(v_absdiff(b, rb), v_absdiff(g, rg)), v_absdiff(r, rr))))); - wsum[j] += v_reduce_sum(w); - sum_b[j] += v_reduce_sum(v_mul(v_cvt_f32(v_reinterpret_as_s32(b)), w)); - sum_g[j] += v_reduce_sum(v_mul(v_cvt_f32(v_reinterpret_as_s32(g)), w)); - sum_r[j] += v_reduce_sum(v_mul(v_cvt_f32(v_reinterpret_as_s32(r)), w)); -#else - int rb = rsptr[0], rg = rsptr[1], rr = rsptr[2]; - - int b = ksptr0[0], g = ksptr0[1], r = ksptr0[2]; - float w = space_weight[k]*color_weight[std::abs(b - rb) + std::abs(g - rg) + std::abs(r - rr)]; - wsum[j] += w; - sum_b[j] += b*w; sum_g[j] += g*w; sum_r[j] += r*w; - - b = ksptr1[0]; g = ksptr1[1]; r = ksptr1[2]; - w = space_weight[k+1] * color_weight[std::abs(b - rb) + std::abs(g - rg) + std::abs(r - rr)]; - wsum[j] += w; - sum_b[j] += b*w; sum_g[j] += g*w; sum_r[j] += r*w; - - b = ksptr2[0]; g = ksptr2[1]; r = ksptr2[2]; - w = space_weight[k+2] * color_weight[std::abs(b - rb) + std::abs(g - rg) + std::abs(r - rr)]; - wsum[j] += w; - sum_b[j] += b*w; sum_g[j] += g*w; sum_r[j] += r*w; - - b = ksptr3[0]; g = ksptr3[1]; r = ksptr3[2]; - w = space_weight[k+3] * color_weight[std::abs(b - rb) + std::abs(g - rg) + std::abs(r - rr)]; - wsum[j] += w; - sum_b[j] += b*w; sum_g[j] += g*w; sum_r[j] += r*w; -#endif - } - } - for(; k < maxk; k++) + int n_8_lanes = VTraits::vlanes(); + for (; j <= size.width - n_8_lanes; j += n_8_lanes, sptr_j += 3*n_8_lanes, dptr += 3*n_8_lanes) { - const uchar* ksptr = sptr + space_ofs[k]; - const uchar* rsptr = sptr; - j = 0; -#if (CV_SIMD || CV_SIMD_SCALABLE) - v_float32 kweight = vx_setall_f32(space_weight[k]); - for (; j <= size.width - VTraits::vlanes(); j += VTraits::vlanes(), ksptr += 3*VTraits::vlanes(), rsptr += 3*VTraits::vlanes()) + const uchar* rsptr = sptr_j; + v_float32 v_wsum_0 = vx_setzero_f32(); + v_float32 v_wsum_1 = vx_setzero_f32(); + v_float32 v_wsum_2 = vx_setzero_f32(); + v_float32 v_wsum_3 = vx_setzero_f32(); + + v_float32 v_sum_b_0 = vx_setzero_f32(); + v_float32 v_sum_b_1 = vx_setzero_f32(); + v_float32 v_sum_b_2 = vx_setzero_f32(); + v_float32 v_sum_b_3 = vx_setzero_f32(); + + v_float32 v_sum_g_0 = vx_setzero_f32(); + v_float32 v_sum_g_1 = vx_setzero_f32(); + v_float32 v_sum_g_2 = vx_setzero_f32(); + v_float32 v_sum_g_3 = vx_setzero_f32(); + + v_float32 v_sum_r_0 = vx_setzero_f32(); + v_float32 v_sum_r_1 = vx_setzero_f32(); + v_float32 v_sum_r_2 = vx_setzero_f32(); + v_float32 v_sum_r_3 = vx_setzero_f32(); + + v_float32 v_one = vx_setall_f32(1.f); + + for(k=0; k < maxk; k++) { + const uchar* ksptr = sptr_j + space_ofs[k]; + v_float32 kweight = vx_setall_f32(space_weight[k]); + v_uint8 kb, kg, kr, rb, rg, rr; v_load_deinterleave(ksptr, kb, kg, kr); v_load_deinterleave(rsptr, rb, rg, rr); @@ -467,69 +394,71 @@ public: v_float32 w1 = v_mul(kweight, v_lut(color_weight, v_reinterpret_as_s32(val1))); v_float32 w2 = v_mul(kweight, v_lut(color_weight, v_reinterpret_as_s32(val2))); v_float32 w3 = v_mul(kweight, v_lut(color_weight, v_reinterpret_as_s32(val3))); - v_store_aligned(wsum + j , v_add(w0, vx_load_aligned(wsum + j))); - v_store_aligned(wsum + j + VTraits::vlanes(), v_add(w1, vx_load_aligned(wsum + j + VTraits::vlanes()))); - v_store_aligned(wsum + j + 2*VTraits::vlanes(), v_add(w2, vx_load_aligned(wsum + j + 2 * VTraits::vlanes()))); - v_store_aligned(wsum + j + 3*VTraits::vlanes(), v_add(w3, vx_load_aligned(wsum + j + 3 * VTraits::vlanes()))); + v_wsum_0 = v_add(w0, v_wsum_0); + v_wsum_1 = v_add(w1, v_wsum_1); + v_wsum_2 = v_add(w2, v_wsum_2); + v_wsum_3 = v_add(w3, v_wsum_3); + v_expand(b_l, val0, val1); v_expand(b_h, val2, val3); - v_store_aligned(sum_b + j , v_muladd(v_cvt_f32(v_reinterpret_as_s32(val0)), w0, vx_load_aligned(sum_b + j))); - v_store_aligned(sum_b + j + VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(val1)), w1, vx_load_aligned(sum_b + j + VTraits::vlanes()))); - v_store_aligned(sum_b + j + 2*VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(val2)), w2, vx_load_aligned(sum_b + j + 2*VTraits::vlanes()))); - v_store_aligned(sum_b + j + 3*VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(val3)), w3, vx_load_aligned(sum_b + j + 3*VTraits::vlanes()))); + v_sum_b_0 = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val0)), w0, v_sum_b_0); + v_sum_b_1 = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val1)), w1, v_sum_b_1); + v_sum_b_2 = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val2)), w2, v_sum_b_2); + v_sum_b_3 = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val3)), w3, v_sum_b_3); + v_expand(g_l, val0, val1); v_expand(g_h, val2, val3); - v_store_aligned(sum_g + j , v_muladd(v_cvt_f32(v_reinterpret_as_s32(val0)), w0, vx_load_aligned(sum_g + j))); - v_store_aligned(sum_g + j + VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(val1)), w1, vx_load_aligned(sum_g + j + VTraits::vlanes()))); - v_store_aligned(sum_g + j + 2*VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(val2)), w2, vx_load_aligned(sum_g + j + 2*VTraits::vlanes()))); - v_store_aligned(sum_g + j + 3*VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(val3)), w3, vx_load_aligned(sum_g + j + 3*VTraits::vlanes()))); + v_sum_g_0 = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val0)), w0, v_sum_g_0); + v_sum_g_1 = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val1)), w1, v_sum_g_1); + v_sum_g_2 = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val2)), w2, v_sum_g_2); + v_sum_g_3 = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val3)), w3, v_sum_g_3); + v_expand(r_l, val0, val1); v_expand(r_h, val2, val3); - v_store_aligned(sum_r + j , v_muladd(v_cvt_f32(v_reinterpret_as_s32(val0)), w0, vx_load_aligned(sum_r + j))); - v_store_aligned(sum_r + j + VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(val1)), w1, vx_load_aligned(sum_r + j + VTraits::vlanes()))); - v_store_aligned(sum_r + j + 2*VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(val2)), w2, vx_load_aligned(sum_r + j + 2*VTraits::vlanes()))); - v_store_aligned(sum_r + j + 3*VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(val3)), w3, vx_load_aligned(sum_r + j + 3*VTraits::vlanes()))); + v_sum_r_0 = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val0)), w0, v_sum_r_0); + v_sum_r_1 = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val1)), w1, v_sum_r_1); + v_sum_r_2 = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val2)), w2, v_sum_r_2); + v_sum_r_3 = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val3)), w3, v_sum_r_3); } + v_float32 w0 = v_div(v_one, v_wsum_0); + v_float32 w1 = v_div(v_one, v_wsum_1); + v_float32 w2 = v_div(v_one, v_wsum_2); + v_float32 w3 = v_div(v_one, v_wsum_3); + + v_store_interleave(dptr, v_pack_u(v_pack(v_round(v_mul(w0, v_sum_b_0)), + v_round(v_mul(w1, v_sum_b_1))), + v_pack(v_round(v_mul(w2, v_sum_b_2)), + v_round(v_mul(w3, v_sum_b_3)))), + v_pack_u(v_pack(v_round(v_mul(w0, v_sum_g_0)), + v_round(v_mul(w1, v_sum_g_1))), + v_pack(v_round(v_mul(w2, v_sum_g_2)), + v_round(v_mul(w3, v_sum_g_3)))), + v_pack_u(v_pack(v_round(v_mul(w0, v_sum_r_0)), + v_round(v_mul(w1, v_sum_r_1))), + v_pack(v_round(v_mul(w2, v_sum_r_2)), + v_round(v_mul(w3, v_sum_r_3))))); + } #endif - for(; j < size.width; j++, ksptr += 3, rsptr += 3) + for(; j < size.width; j++, sptr_j += 3) + { + const uchar* rsptr = sptr_j; + float wsum = 0.f; + float sum_b = 0.f, sum_g = 0.f, sum_r = 0.f; + for(k=0; k < maxk; k++) { + const uchar* ksptr = sptr_j + space_ofs[k]; + int b = ksptr[0], g = ksptr[1], r = ksptr[2]; float w = space_weight[k]*color_weight[std::abs(b - rsptr[0]) + std::abs(g - rsptr[1]) + std::abs(r - rsptr[2])]; - wsum[j] += w; - sum_b[j] += b*w; sum_g[j] += g*w; sum_r[j] += r*w; + wsum += w; + sum_b += b*w; sum_g += g*w; sum_r += r*w; } - } - j = 0; -#if (CV_SIMD || CV_SIMD_SCALABLE) - v_float32 v_one = vx_setall_f32(1.f); - for(; j <= size.width - VTraits::vlanes(); j += VTraits::vlanes(), dptr += 3*VTraits::vlanes()) - { - v_float32 w0 = v_div(v_one, vx_load_aligned(wsum + j)); - v_float32 w1 = v_div(v_one, vx_load_aligned(wsum + j + VTraits::vlanes())); - v_float32 w2 = v_div(v_one, vx_load_aligned(wsum + j + 2 * VTraits::vlanes())); - v_float32 w3 = v_div(v_one, vx_load_aligned(wsum + j + 3 * VTraits::vlanes())); - v_store_interleave(dptr, v_pack_u(v_pack(v_round(v_mul(w0, vx_load_aligned(sum_b + j))), - v_round(v_mul(w1, vx_load_aligned(sum_b + j + VTraits::vlanes())))), - v_pack(v_round(v_mul(w2, vx_load_aligned(sum_b + j + 2 * VTraits::vlanes()))), - v_round(v_mul(w3, vx_load_aligned(sum_b + j + 3 * VTraits::vlanes()))))), - v_pack_u(v_pack(v_round(v_mul(w0, vx_load_aligned(sum_g + j))), - v_round(v_mul(w1, vx_load_aligned(sum_g + j + VTraits::vlanes())))), - v_pack(v_round(v_mul(w2, vx_load_aligned(sum_g + j + 2 * VTraits::vlanes()))), - v_round(v_mul(w3, vx_load_aligned(sum_g + j + 3 * VTraits::vlanes()))))), - v_pack_u(v_pack(v_round(v_mul(w0, vx_load_aligned(sum_r + j))), - v_round(v_mul(w1, vx_load_aligned(sum_r + j + VTraits::vlanes())))), - v_pack(v_round(v_mul(w2, vx_load_aligned(sum_r + j + 2 * VTraits::vlanes()))), - v_round(v_mul(w3, vx_load_aligned(sum_r + j + 3 * VTraits::vlanes())))))); - } -#endif - for(; j < size.width; j++) - { - CV_DbgAssert(fabs(wsum[j]) > 0); - wsum[j] = 1.f/wsum[j]; - *(dptr++) = (uchar)cvRound(sum_b[j]*wsum[j]); - *(dptr++) = (uchar)cvRound(sum_g[j]*wsum[j]); - *(dptr++) = (uchar)cvRound(sum_r[j]*wsum[j]); + CV_DbgAssert(fabs(wsum) > 0); + wsum = 1.f/wsum; + *(dptr++) = (uchar)cvRound(sum_b*wsum); + *(dptr++) = (uchar)cvRound(sum_g*wsum); + *(dptr++) = (uchar)cvRound(sum_r*wsum); } } } @@ -552,6 +481,7 @@ void bilateralFilterInvoker_8u( int* space_ofs, float *space_weight, float *color_weight) { CV_INSTRUMENT_REGION(); + BilateralFilter_8u_Invoker body(dst, temp, radius, maxk, space_ofs, space_weight, color_weight); parallel_for_(Range(0, dst.rows), body, dst.total()/(double)(1<<16)); } @@ -577,7 +507,12 @@ public: int i, j, k; Size size = dest->size(); - +#if (CV_SIMD || CV_SIMD_SCALABLE) + int nlanes = VTraits::vlanes(); + int nlanes_2 = 2 * nlanes; + int nlanes_3 = 3 * nlanes; + int nlanes_4 = 4 * nlanes; +#endif for( i = range.start; i < range.end; i++ ) { const float* sptr = temp->ptr(i+radius) + radius*cn; @@ -585,363 +520,161 @@ public: if( cn == 1 ) { - AutoBuffer buf(alignSize(size.width, CV_SIMD_WIDTH) + size.width + CV_SIMD_WIDTH - 1); - memset(buf.data(), 0, buf.size() * sizeof(float)); - float *sum = alignPtr(buf.data(), CV_SIMD_WIDTH); - float *wsum = sum + alignSize(size.width, CV_SIMD_WIDTH); + j = 0; + const float* sptr_j = sptr; #if (CV_SIMD || CV_SIMD_SCALABLE) v_float32 v_one = vx_setall_f32(1.f); v_float32 sindex = vx_setall_f32(scale_index); -#endif - k = 0; - for(; k <= maxk - 4; k+=4) + + for(; j <= size.width - nlanes_4; j += nlanes_4, sptr_j += nlanes_4, dptr += nlanes_4) { - const float* ksptr0 = sptr + space_ofs[k]; - const float* ksptr1 = sptr + space_ofs[k + 1]; - const float* ksptr2 = sptr + space_ofs[k + 2]; - const float* ksptr3 = sptr + space_ofs[k + 3]; - j = 0; -#if (CV_SIMD || CV_SIMD_SCALABLE) - v_float32 kweight0 = vx_setall_f32(space_weight[k]); - v_float32 kweight1 = vx_setall_f32(space_weight[k+1]); - v_float32 kweight2 = vx_setall_f32(space_weight[k+2]); - v_float32 kweight3 = vx_setall_f32(space_weight[k+3]); - for (; j <= size.width - VTraits::vlanes(); j += VTraits::vlanes()) + v_float32 v_wsum0 = vx_setzero_f32(); + v_float32 v_wsum1 = vx_setzero_f32(); + v_float32 v_wsum2 = vx_setzero_f32(); + v_float32 v_wsum3 = vx_setzero_f32(); + v_float32 v_sum0 = vx_setzero_f32(); + v_float32 v_sum1 = vx_setzero_f32(); + v_float32 v_sum2 = vx_setzero_f32(); + v_float32 v_sum3 = vx_setzero_f32(); + + v_float32 rval0 = vx_load(sptr_j); + v_float32 rval1 = vx_load(sptr_j + nlanes); + v_float32 rval2 = vx_load(sptr_j + nlanes_2); + v_float32 rval3 = vx_load(sptr_j + nlanes_3); + for(k = 0; k < maxk; k++) { - v_float32 rval = vx_load(sptr + j); + const float* ksptr = sptr_j + space_ofs[k]; + v_float32 kweight = vx_setall_f32(space_weight[k]); - v_float32 val = vx_load(ksptr0 + j); - v_float32 knan = v_not_nan(val); - v_float32 alpha = v_and(v_and(v_mul(v_absdiff(val, rval), sindex), v_not_nan(rval)), knan); - v_int32 idx = v_trunc(alpha); - alpha = v_sub(alpha, v_cvt_f32(idx)); - v_float32 w = v_and(v_mul(kweight0, v_muladd(v_lut(this->expLUT + 1, idx), alpha, v_mul(v_lut(this->expLUT, idx), v_sub(v_one, alpha)))), knan); - v_float32 v_wsum = v_add(vx_load_aligned(wsum + j), w); - v_float32 v_sum = v_muladd(v_and(val, knan), w, vx_load_aligned(sum + j)); + //0th + v_float32 val0 = vx_load(ksptr); + v_float32 knan0 = v_not_nan(val0); + v_float32 alpha0 = v_and(v_and(v_mul(v_absdiff(val0, rval0), sindex), v_not_nan(rval0)), knan0); + v_int32 idx0 = v_trunc(alpha0); + alpha0 = v_sub(alpha0, v_cvt_f32(idx0)); + v_float32 w0 = v_and(v_mul(kweight, v_muladd(v_lut(this->expLUT + 1, idx0), alpha0, v_mul(v_lut(this->expLUT, idx0), v_sub(v_one, alpha0)))), knan0); + v_wsum0 = v_add(v_wsum0, w0); + v_sum0 = v_muladd(v_and(val0, knan0), w0, v_sum0); - val = vx_load(ksptr1 + j); - knan = v_not_nan(val); - alpha = v_and(v_and(v_mul(v_absdiff(val, rval), sindex), v_not_nan(rval)), knan); - idx = v_trunc(alpha); - alpha = v_sub(alpha, v_cvt_f32(idx)); - w = v_and(v_mul(kweight1, v_muladd(v_lut(this->expLUT + 1, idx), alpha, v_mul(v_lut(this->expLUT, idx), v_sub(v_one, alpha)))), knan); - v_wsum = v_add(v_wsum, w); - v_sum = v_muladd(v_and(val, knan), w, v_sum); + //1st + v_float32 val1 = vx_load(ksptr + nlanes); + v_float32 knan1 = v_not_nan(val1); + v_float32 alpha1 = v_and(v_and(v_mul(v_absdiff(val1, rval1), sindex), v_not_nan(rval1)), knan1); + v_int32 idx1 = v_trunc(alpha1); + alpha1 = v_sub(alpha1, v_cvt_f32(idx1)); + v_float32 w1 = v_and(v_mul(kweight, v_muladd(v_lut(this->expLUT + 1, idx1), alpha1, v_mul(v_lut(this->expLUT, idx1), v_sub(v_one, alpha1)))), knan1); + v_wsum1 = v_add(v_wsum1, w1); + v_sum1 = v_muladd(v_and(val1, knan1), w1, v_sum1); - val = vx_load(ksptr2 + j); - knan = v_not_nan(val); - alpha = v_and(v_and(v_mul(v_absdiff(val, rval), sindex), v_not_nan(rval)), knan); - idx = v_trunc(alpha); - alpha = v_sub(alpha, v_cvt_f32(idx)); - w = v_and(v_mul(kweight2, v_muladd(v_lut(this->expLUT + 1, idx), alpha, v_mul(v_lut(this->expLUT, idx), v_sub(v_one, alpha)))), knan); - v_wsum = v_add(v_wsum, w); - v_sum = v_muladd(v_and(val, knan), w, v_sum); + //2nd + v_float32 val2 = vx_load(ksptr + nlanes_2); + v_float32 knan2 = v_not_nan(val2); + v_float32 alpha2 = v_and(v_and(v_mul(v_absdiff(val2, rval2), sindex), v_not_nan(rval2)), knan2); + v_int32 idx2 = v_trunc(alpha2); + alpha2 = v_sub(alpha2, v_cvt_f32(idx2)); + v_float32 w2 = v_and(v_mul(kweight, v_muladd(v_lut(this->expLUT + 1, idx2), alpha2, v_mul(v_lut(this->expLUT, idx2), v_sub(v_one, alpha2)))), knan2); + v_wsum2 = v_add(v_wsum2, w2); + v_sum2 = v_muladd(v_and(val2, knan2), w2, v_sum2); - val = vx_load(ksptr3 + j); - knan = v_not_nan(val); - alpha = v_and(v_and(v_mul(v_absdiff(val, rval), sindex), v_not_nan(rval)), knan); - idx = v_trunc(alpha); - alpha = v_sub(alpha, v_cvt_f32(idx)); - w = v_and(v_mul(kweight3, v_muladd(v_lut(this->expLUT + 1, idx), alpha, v_mul(v_lut(this->expLUT, idx), v_sub(v_one, alpha)))), knan); - v_wsum = v_add(v_wsum, w); - v_sum = v_muladd(v_and(val, knan), w, v_sum); - - v_store_aligned(wsum + j, v_wsum); - v_store_aligned(sum + j, v_sum); + //3rd + v_float32 val3 = vx_load(ksptr + nlanes_3); + v_float32 knan3 = v_not_nan(val3); + v_float32 alpha3 = v_and(v_and(v_mul(v_absdiff(val3, rval3), sindex), v_not_nan(rval3)), knan3); + v_int32 idx3 = v_trunc(alpha3); + alpha3 = v_sub(alpha3, v_cvt_f32(idx3)); + v_float32 w3 = v_and(v_mul(kweight, v_muladd(v_lut(this->expLUT + 1, idx3), alpha3, v_mul(v_lut(this->expLUT, idx3), v_sub(v_one, alpha3)))), knan3); + v_wsum3 = v_add(v_wsum3, w3); + v_sum3 = v_muladd(v_and(val3, knan3), w3, v_sum3); } -#endif -#if CV_SIMD128 - v_float32x4 v_one4 = v_setall_f32(1.f); - v_float32x4 sindex4 = v_setall_f32(scale_index); - v_float32x4 kweight4 = v_load(space_weight + k); -#endif - for (; j < size.width; j++) - { -#if CV_SIMD128 - v_float32x4 rval = v_setall_f32(sptr[j]); - v_float32x4 val(ksptr0[j], ksptr1[j], ksptr2[j], ksptr3[j]); - v_float32x4 knan = v_not_nan(val); - v_float32x4 alpha = v_and(v_and(v_mul(v_absdiff(val, rval), sindex4), v_not_nan(rval)), knan); - v_int32x4 idx = v_trunc(alpha); - alpha = v_sub(alpha, v_cvt_f32(idx)); - v_float32x4 w = v_and(v_mul(kweight4, v_muladd(v_lut(this->expLUT + 1, idx), alpha, v_mul(v_lut(this->expLUT, idx), v_sub(v_one4, alpha)))), knan); - wsum[j] += v_reduce_sum(w); - sum[j] += v_reduce_sum(v_mul(v_and(val, knan), w)); -#else - float rval = sptr[j]; + v_store(dptr , v_div(v_add(v_sum0, v_and(rval0, v_not_nan(rval0))), v_add(v_wsum0, v_and(v_one, v_not_nan(rval0))))); + v_store(dptr + nlanes, v_div(v_add(v_sum1, v_and(rval1, v_not_nan(rval1))), v_add(v_wsum1, v_and(v_one, v_not_nan(rval1))))); + v_store(dptr + nlanes_2, v_div(v_add(v_sum2, v_and(rval2, v_not_nan(rval2))), v_add(v_wsum2, v_and(v_one, v_not_nan(rval2))))); + v_store(dptr + nlanes_3, v_div(v_add(v_sum3, v_and(rval3, v_not_nan(rval3))), v_add(v_wsum3, v_and(v_one, v_not_nan(rval3))))); + } + for (; j <= size.width - nlanes_2; j += nlanes_2, sptr_j += nlanes_2, dptr += nlanes_2) + { + v_float32 v_wsum0 = vx_setzero_f32(); + v_float32 v_wsum1 = vx_setzero_f32(); + v_float32 v_sum0 = vx_setzero_f32(); + v_float32 v_sum1 = vx_setzero_f32(); + v_float32 rval0 = vx_load(sptr_j); + v_float32 rval1 = vx_load(sptr_j + nlanes); + v_float32 rval0_not_nan = v_not_nan(rval0); + v_float32 rval1_not_nan = v_not_nan(rval1); - float val = ksptr0[j]; + for (k = 0; k < maxk; k++) + { + v_float32 kweight = vx_setall_f32(space_weight[k]); + const float* ksptr = sptr_j + space_ofs[k]; + + //0th + v_float32 val0 = vx_load(ksptr); + v_float32 knan0 = v_not_nan(val0); + v_float32 alpha0 = v_and(v_and(v_mul(v_absdiff(val0, rval0), sindex), rval0_not_nan), knan0); + v_int32 idx0 = v_trunc(alpha0); + alpha0 = v_sub(alpha0, v_cvt_f32(idx0)); + v_float32 w0 = v_and(v_mul(kweight, v_muladd(v_lut(this->expLUT + 1, idx0), alpha0, v_mul(v_lut(this->expLUT, idx0), v_sub(v_one, alpha0)))), knan0); + v_wsum0 = v_add(v_wsum0, w0); + v_sum0 = v_muladd(v_and(val0, knan0), w0, v_sum0); + + //1st + v_float32 val1 = vx_load(ksptr + nlanes); + v_float32 knan1 = v_not_nan(val1); + v_float32 alpha1 = v_and(v_and(v_mul(v_absdiff(val1, rval1), sindex), rval1_not_nan), knan1); + v_int32 idx1 = v_trunc(alpha1); + alpha1 = v_sub(alpha1, v_cvt_f32(idx1)); + v_float32 w1 = v_and(v_mul(kweight, v_muladd(v_lut(this->expLUT + 1, idx1), alpha1, v_mul(v_lut(this->expLUT, idx1), v_sub(v_one, alpha1)))), knan1); + v_wsum1 = v_add(v_wsum1, w1); + v_sum1 = v_muladd(v_and(val1, knan1), w1, v_sum1); + } + v_store(dptr, v_div(v_add(v_sum0, v_and(rval0, rval0_not_nan)), v_add(v_wsum0, v_and(v_one, rval0_not_nan)))); + v_store(dptr + nlanes, v_div(v_add(v_sum1, v_and(rval1, rval1_not_nan)), v_add(v_wsum1, v_and(v_one, rval1_not_nan)))); + } +#endif + for (; j < size.width; j++, sptr_j++, dptr++) + { + float rval = *sptr_j; + float wsum = 0.f; + float sum = 0.f; + for (k = 0; k < maxk; k++) + { + const float* ksptr = sptr_j + space_ofs[k]; + float val = *ksptr; float alpha = std::abs(val - rval) * scale_index; int idx = cvFloor(alpha); alpha -= idx; if (!cvIsNaN(val)) { - float w = space_weight[k] * (cvIsNaN(rval) ? 1.f : (expLUT[idx] + alpha*(expLUT[idx + 1] - expLUT[idx]))); - wsum[j] += w; - sum[j] += val * w; - } - - val = ksptr1[j]; - alpha = std::abs(val - rval) * scale_index; - idx = cvFloor(alpha); - alpha -= idx; - if (!cvIsNaN(val)) - { - float w = space_weight[k+1] * (cvIsNaN(rval) ? 1.f : (expLUT[idx] + alpha*(expLUT[idx + 1] - expLUT[idx]))); - wsum[j] += w; - sum[j] += val * w; - } - - val = ksptr2[j]; - alpha = std::abs(val - rval) * scale_index; - idx = cvFloor(alpha); - alpha -= idx; - if (!cvIsNaN(val)) - { - float w = space_weight[k+2] * (cvIsNaN(rval) ? 1.f : (expLUT[idx] + alpha*(expLUT[idx + 1] - expLUT[idx]))); - wsum[j] += w; - sum[j] += val * w; - } - - val = ksptr3[j]; - alpha = std::abs(val - rval) * scale_index; - idx = cvFloor(alpha); - alpha -= idx; - if (!cvIsNaN(val)) - { - float w = space_weight[k+3] * (cvIsNaN(rval) ? 1.f : (expLUT[idx] + alpha*(expLUT[idx + 1] - expLUT[idx]))); - wsum[j] += w; - sum[j] += val * w; - } -#endif - } - } - for(; k < maxk; k++) - { - const float* ksptr = sptr + space_ofs[k]; - j = 0; -#if (CV_SIMD || CV_SIMD_SCALABLE) - v_float32 kweight = vx_setall_f32(space_weight[k]); - for (; j <= size.width - VTraits::vlanes(); j += VTraits::vlanes()) - { - v_float32 val = vx_load(ksptr + j); - v_float32 rval = vx_load(sptr + j); - v_float32 knan = v_not_nan(val); - v_float32 alpha = v_and(v_and(v_mul(v_absdiff(val, rval), sindex), v_not_nan(rval)), knan); - v_int32 idx = v_trunc(alpha); - alpha = v_sub(alpha, v_cvt_f32(idx)); - - v_float32 w = v_and(v_mul(kweight, v_muladd(v_lut(this->expLUT + 1, idx), alpha, v_mul(v_lut(this->expLUT, idx), v_sub(v_one, alpha)))), knan); - v_store_aligned(wsum + j, v_add(vx_load_aligned(wsum + j), w)); - v_store_aligned(sum + j, v_muladd(v_and(val, knan), w, vx_load_aligned(sum + j))); - } -#endif - for (; j < size.width; j++) - { - float val = ksptr[j]; - float rval = sptr[j]; - float alpha = std::abs(val - rval) * scale_index; - int idx = cvFloor(alpha); - alpha -= idx; - if (!cvIsNaN(val)) - { - float w = space_weight[k] * (cvIsNaN(rval) ? 1.f : (expLUT[idx] + alpha*(expLUT[idx + 1] - expLUT[idx]))); - wsum[j] += w; - sum[j] += val * w; + float w = space_weight[k] * (cvIsNaN(rval) ? 1.f : (expLUT[idx] + alpha * (expLUT[idx + 1] - expLUT[idx]))); + wsum += w; + sum += val * w; } } - } - j = 0; -#if (CV_SIMD || CV_SIMD_SCALABLE) - for (; j <= size.width - VTraits::vlanes(); j += VTraits::vlanes()) - { - v_float32 v_val = vx_load(sptr + j); - v_store(dptr + j, v_div(v_add(vx_load_aligned(sum + j), v_and(v_val, v_not_nan(v_val))), v_add(vx_load_aligned(wsum + j), v_and(v_one, v_not_nan(v_val))))); - } -#endif - for (; j < size.width; j++) - { - CV_DbgAssert(fabs(wsum[j]) >= 0); - dptr[j] = cvIsNaN(sptr[j]) ? sum[j] / wsum[j] : (sum[j] + sptr[j]) / (wsum[j] + 1.f); + CV_DbgAssert(fabs(wsum) >= 0); + *dptr = cvIsNaN(rval) ? sum / wsum : (sum + rval) / (wsum + 1.f); } } else { - CV_Assert( cn == 3 ); - AutoBuffer buf(alignSize(size.width, CV_SIMD_WIDTH)*3 + size.width + CV_SIMD_WIDTH - 1); - memset(buf.data(), 0, buf.size() * sizeof(float)); - float *sum_b = alignPtr(buf.data(), CV_SIMD_WIDTH); - float *sum_g = sum_b + alignSize(size.width, CV_SIMD_WIDTH); - float *sum_r = sum_g + alignSize(size.width, CV_SIMD_WIDTH); - float *wsum = sum_r + alignSize(size.width, CV_SIMD_WIDTH); + CV_Assert(cn == 3); + j = 0; + const float* sptr_j = sptr; #if (CV_SIMD || CV_SIMD_SCALABLE) v_float32 v_one = vx_setall_f32(1.f); v_float32 sindex = vx_setall_f32(scale_index); -#endif - k = 0; - for (; k <= maxk-4; k+=4) + + for (; j <= size.width - nlanes; j += nlanes, sptr_j += nlanes_3, dptr += nlanes_3) { - const float* ksptr0 = sptr + space_ofs[k]; - const float* ksptr1 = sptr + space_ofs[k+1]; - const float* ksptr2 = sptr + space_ofs[k+2]; - const float* ksptr3 = sptr + space_ofs[k+3]; - const float* rsptr = sptr; - j = 0; -#if (CV_SIMD || CV_SIMD_SCALABLE) - v_float32 kweight0 = vx_setall_f32(space_weight[k]); - v_float32 kweight1 = vx_setall_f32(space_weight[k+1]); - v_float32 kweight2 = vx_setall_f32(space_weight[k+2]); - v_float32 kweight3 = vx_setall_f32(space_weight[k+3]); - for (; j <= size.width - VTraits::vlanes(); j += VTraits::vlanes(), rsptr += 3 * VTraits::vlanes(), - ksptr0 += 3 * VTraits::vlanes(), ksptr1 += 3 * VTraits::vlanes(), ksptr2 += 3 * VTraits::vlanes(), ksptr3 += 3 * VTraits::vlanes()) + const float* rsptr = sptr_j; + v_float32 v_wsum = vx_setzero_f32(); + v_float32 v_sum_b = vx_setzero_f32(); + v_float32 v_sum_g = vx_setzero_f32(); + v_float32 v_sum_r = vx_setzero_f32(); + for (k = 0; k < maxk; k++) { - v_float32 kb, kg, kr, rb, rg, rr; - v_load_deinterleave(rsptr, rb, rg, rr); + const float* ksptr = sptr_j + space_ofs[k]; + v_float32 kweight = vx_setall_f32(space_weight[k]); - v_load_deinterleave(ksptr0, kb, kg, kr); - v_float32 knan = v_and(v_and(v_not_nan(kb), v_not_nan(kg)), v_not_nan(kr)); - v_float32 alpha = v_and(v_and(v_and(v_and(v_mul(v_add(v_add(v_absdiff(kb, rb), v_absdiff(kg, rg)), v_absdiff(kr, rr)), sindex), v_not_nan(rb)), v_not_nan(rg)), v_not_nan(rr)), knan); - v_int32 idx = v_trunc(alpha); - alpha = v_sub(alpha, v_cvt_f32(idx)); - v_float32 w = v_and(v_mul(kweight0, v_muladd(v_lut(this->expLUT + 1, idx), alpha, v_mul(v_lut(this->expLUT, idx), v_sub(v_one, alpha)))), knan); - v_float32 v_wsum = v_add(vx_load_aligned(wsum + j), w); - v_float32 v_sum_b = v_muladd(v_and(kb, knan), w, vx_load_aligned(sum_b + j)); - v_float32 v_sum_g = v_muladd(v_and(kg, knan), w, vx_load_aligned(sum_g + j)); - v_float32 v_sum_r = v_muladd(v_and(kr, knan), w, vx_load_aligned(sum_r + j)); - - v_load_deinterleave(ksptr1, kb, kg, kr); - knan = v_and(v_and(v_not_nan(kb), v_not_nan(kg)), v_not_nan(kr)); - alpha = v_and(v_and(v_and(v_and(v_mul(v_add(v_add(v_absdiff(kb, rb), v_absdiff(kg, rg)), v_absdiff(kr, rr)), sindex), v_not_nan(rb)), v_not_nan(rg)), v_not_nan(rr)), knan); - idx = v_trunc(alpha); - alpha = v_sub(alpha, v_cvt_f32(idx)); - w = v_and(v_mul(kweight1, v_muladd(v_lut(this->expLUT + 1, idx), alpha, v_mul(v_lut(this->expLUT, idx), v_sub(v_one, alpha)))), knan); - v_wsum = v_add(v_wsum, w); - v_sum_b = v_muladd(v_and(kb, knan), w, v_sum_b); - v_sum_g = v_muladd(v_and(kg, knan), w, v_sum_g); - v_sum_r = v_muladd(v_and(kr, knan), w, v_sum_r); - - v_load_deinterleave(ksptr2, kb, kg, kr); - knan = v_and(v_and(v_not_nan(kb), v_not_nan(kg)), v_not_nan(kr)); - alpha = v_and(v_and(v_and(v_and(v_mul(v_add(v_add(v_absdiff(kb, rb), v_absdiff(kg, rg)), v_absdiff(kr, rr)), sindex), v_not_nan(rb)), v_not_nan(rg)), v_not_nan(rr)), knan); - idx = v_trunc(alpha); - alpha = v_sub(alpha, v_cvt_f32(idx)); - w = v_and(v_mul(kweight2, v_muladd(v_lut(this->expLUT + 1, idx), alpha, v_mul(v_lut(this->expLUT, idx), v_sub(v_one, alpha)))), knan); - v_wsum = v_add(v_wsum, w); - v_sum_b = v_muladd(v_and(kb, knan), w, v_sum_b); - v_sum_g = v_muladd(v_and(kg, knan), w, v_sum_g); - v_sum_r = v_muladd(v_and(kr, knan), w, v_sum_r); - - v_load_deinterleave(ksptr3, kb, kg, kr); - knan = v_and(v_and(v_not_nan(kb), v_not_nan(kg)), v_not_nan(kr)); - alpha = v_and(v_and(v_and(v_and(v_mul(v_add(v_add(v_absdiff(kb, rb), v_absdiff(kg, rg)), v_absdiff(kr, rr)), sindex), v_not_nan(rb)), v_not_nan(rg)), v_not_nan(rr)), knan); - idx = v_trunc(alpha); - alpha = v_sub(alpha, v_cvt_f32(idx)); - w = v_and(v_mul(kweight3, v_muladd(v_lut(this->expLUT + 1, idx), alpha, v_mul(v_lut(this->expLUT, idx), v_sub(v_one, alpha)))), knan); - v_wsum = v_add(v_wsum, w); - v_sum_b = v_muladd(v_and(kb, knan), w, v_sum_b); - v_sum_g = v_muladd(v_and(kg, knan), w, v_sum_g); - v_sum_r = v_muladd(v_and(kr, knan), w, v_sum_r); - - v_store_aligned(wsum + j, v_wsum); - v_store_aligned(sum_b + j, v_sum_b); - v_store_aligned(sum_g + j, v_sum_g); - v_store_aligned(sum_r + j, v_sum_r); - } -#endif -#if CV_SIMD128 - v_float32x4 v_one4 = v_setall_f32(1.f); - v_float32x4 sindex4 = v_setall_f32(scale_index); - v_float32x4 kweight4 = v_load(space_weight + k); -#endif - for (; j < size.width; j++, rsptr += 3, ksptr0 += 3, ksptr1 += 3, ksptr2 += 3, ksptr3 += 3) - { -#if CV_SIMD128 - v_float32x4 rb = v_setall_f32(rsptr[0]); - v_float32x4 rg = v_setall_f32(rsptr[1]); - v_float32x4 rr = v_setall_f32(rsptr[2]); - v_float32x4 kb(ksptr0[0], ksptr1[0], ksptr2[0], ksptr3[0]); - v_float32x4 kg(ksptr0[1], ksptr1[1], ksptr2[1], ksptr3[1]); - v_float32x4 kr(ksptr0[2], ksptr1[2], ksptr2[2], ksptr3[2]); - v_float32x4 knan = v_and(v_and(v_not_nan(kb), v_not_nan(kg)), v_not_nan(kr)); - v_float32x4 alpha = v_and(v_and(v_and(v_and(v_mul(v_add(v_add(v_absdiff(kb, rb), v_absdiff(kg, rg)), v_absdiff(kr, rr)), sindex4), v_not_nan(rb)), v_not_nan(rg)), v_not_nan(rr)), knan); - v_int32x4 idx = v_trunc(alpha); - alpha = v_sub(alpha, v_cvt_f32(idx)); - v_float32x4 w = v_and(v_mul(kweight4, v_muladd(v_lut(this->expLUT + 1, idx), alpha, v_mul(v_lut(this->expLUT, idx), v_sub(v_one4, alpha)))), knan); - wsum[j] += v_reduce_sum(w); - sum_b[j] += v_reduce_sum(v_mul(v_and(kb, knan), w)); - sum_g[j] += v_reduce_sum(v_mul(v_and(kg, knan), w)); - sum_r[j] += v_reduce_sum(v_mul(v_and(kr, knan), w)); -#else - float rb = rsptr[0], rg = rsptr[1], rr = rsptr[2]; - bool r_NAN = cvIsNaN(rb) || cvIsNaN(rg) || cvIsNaN(rr); - - float b = ksptr0[0], g = ksptr0[1], r = ksptr0[2]; - bool v_NAN = cvIsNaN(b) || cvIsNaN(g) || cvIsNaN(r); - float alpha = (std::abs(b - rb) + std::abs(g - rg) + std::abs(r - rr)) * scale_index; - int idx = cvFloor(alpha); - alpha -= idx; - if (!v_NAN) - { - float w = space_weight[k] * (r_NAN ? 1.f : (expLUT[idx] + alpha*(expLUT[idx + 1] - expLUT[idx]))); - wsum[j] += w; - sum_b[j] += b*w; - sum_g[j] += g*w; - sum_r[j] += r*w; - } - - b = ksptr1[0]; g = ksptr1[1]; r = ksptr1[2]; - v_NAN = cvIsNaN(b) || cvIsNaN(g) || cvIsNaN(r); - alpha = (std::abs(b - rb) + std::abs(g - rg) + std::abs(r - rr)) * scale_index; - idx = cvFloor(alpha); - alpha -= idx; - if (!v_NAN) - { - float w = space_weight[k+1] * (r_NAN ? 1.f : (expLUT[idx] + alpha*(expLUT[idx + 1] - expLUT[idx]))); - wsum[j] += w; - sum_b[j] += b*w; - sum_g[j] += g*w; - sum_r[j] += r*w; - } - - b = ksptr2[0]; g = ksptr2[1]; r = ksptr2[2]; - v_NAN = cvIsNaN(b) || cvIsNaN(g) || cvIsNaN(r); - alpha = (std::abs(b - rb) + std::abs(g - rg) + std::abs(r - rr)) * scale_index; - idx = cvFloor(alpha); - alpha -= idx; - if (!v_NAN) - { - float w = space_weight[k+2] * (r_NAN ? 1.f : (expLUT[idx] + alpha*(expLUT[idx + 1] - expLUT[idx]))); - wsum[j] += w; - sum_b[j] += b*w; - sum_g[j] += g*w; - sum_r[j] += r*w; - } - - b = ksptr3[0]; g = ksptr3[1]; r = ksptr3[2]; - v_NAN = cvIsNaN(b) || cvIsNaN(g) || cvIsNaN(r); - alpha = (std::abs(b - rb) + std::abs(g - rg) + std::abs(r - rr)) * scale_index; - idx = cvFloor(alpha); - alpha -= idx; - if (!v_NAN) - { - float w = space_weight[k+3] * (r_NAN ? 1.f : (expLUT[idx] + alpha*(expLUT[idx + 1] - expLUT[idx]))); - wsum[j] += w; - sum_b[j] += b*w; - sum_g[j] += g*w; - sum_r[j] += r*w; - } -#endif - } - } - for (; k < maxk; k++) - { - const float* ksptr = sptr + space_ofs[k]; - const float* rsptr = sptr; - j = 0; -#if (CV_SIMD || CV_SIMD_SCALABLE) - v_float32 kweight = vx_setall_f32(space_weight[k]); - for (; j <= size.width - VTraits::vlanes(); j += VTraits::vlanes(), ksptr += 3*VTraits::vlanes(), rsptr += 3*VTraits::vlanes()) - { v_float32 kb, kg, kr, rb, rg, rr; v_load_deinterleave(ksptr, kb, kg, kr); v_load_deinterleave(rsptr, rb, rg, rr); @@ -952,14 +685,26 @@ public: alpha = v_sub(alpha, v_cvt_f32(idx)); v_float32 w = v_and(v_mul(kweight, v_muladd(v_lut(this->expLUT + 1, idx), alpha, v_mul(v_lut(this->expLUT, idx), v_sub(v_one, alpha)))), knan); - v_store_aligned(wsum + j, v_add(vx_load_aligned(wsum + j), w)); - v_store_aligned(sum_b + j, v_muladd(v_and(kb, knan), w, vx_load_aligned(sum_b + j))); - v_store_aligned(sum_g + j, v_muladd(v_and(kg, knan), w, vx_load_aligned(sum_g + j))); - v_store_aligned(sum_r + j, v_muladd(v_and(kr, knan), w, vx_load_aligned(sum_r + j))); + v_wsum = v_add(v_wsum, w); + v_sum_b = v_muladd(v_and(kb, knan), w, v_sum_b); + v_sum_g = v_muladd(v_and(kg, knan), w, v_sum_g); + v_sum_r = v_muladd(v_and(kr, knan), w, v_sum_r); } + + v_float32 b, g, r; + v_load_deinterleave(sptr_j, b, g, r); + v_float32 mask = v_and(v_and(v_not_nan(b), v_not_nan(g)), v_not_nan(r)); + v_float32 w = v_div(v_one, v_add(v_wsum, v_and(v_one, mask))); + v_store_interleave(dptr, v_mul(v_add(v_sum_b, v_and(b, mask)), w), v_mul(v_add(v_sum_g, v_and(g, mask)), w), v_mul(v_add(v_sum_r, v_and(r, mask)), w)); + } #endif - for (; j < size.width; j++, ksptr += 3, rsptr += 3) + for (; j < size.width; j++, sptr_j += 3) + { + const float* rsptr = sptr_j; + float wsum = 0.f, sum_b = 0.f, sum_g = 0.f, sum_r = 0.f; + for (k = 0; k < maxk; k++) { + const float* ksptr = sptr_j + space_ofs[k]; float b = ksptr[0], g = ksptr[1], r = ksptr[2]; bool v_NAN = cvIsNaN(b) || cvIsNaN(g) || cvIsNaN(r); float rb = rsptr[0], rg = rsptr[1], rr = rsptr[2]; @@ -969,44 +714,31 @@ public: alpha -= idx; if (!v_NAN) { - float w = space_weight[k] * (r_NAN ? 1.f : (expLUT[idx] + alpha*(expLUT[idx + 1] - expLUT[idx]))); - wsum[j] += w; - sum_b[j] += b*w; - sum_g[j] += g*w; - sum_r[j] += r*w; + float w = space_weight[k] * (r_NAN ? 1.f : (expLUT[idx] + alpha * (expLUT[idx + 1] - expLUT[idx]))); + wsum += w; + sum_b += b * w; + sum_g += g * w; + sum_r += r * w; } } - } - j = 0; -#if (CV_SIMD || CV_SIMD_SCALABLE) - for (; j <= size.width - VTraits::vlanes(); j += VTraits::vlanes(), sptr += 3*VTraits::vlanes(), dptr += 3*VTraits::vlanes()) - { - v_float32 b, g, r; - v_load_deinterleave(sptr, b, g, r); - v_float32 mask = v_and(v_and(v_not_nan(b), v_not_nan(g)), v_not_nan(r)); - v_float32 w = v_div(v_one, v_add(vx_load_aligned(wsum + j), v_and(v_one, mask))); - v_store_interleave(dptr, v_mul(v_add(vx_load_aligned(sum_b + j), v_and(b, mask)), w), v_mul(v_add(vx_load_aligned(sum_g + j), v_and(g, mask)), w), v_mul(v_add(vx_load_aligned(sum_r + j), v_and(r, mask)), w)); - } -#endif - for (; j < size.width; j++) - { - CV_DbgAssert(fabs(wsum[j]) >= 0); - float b = *(sptr++); - float g = *(sptr++); - float r = *(sptr++); + + CV_DbgAssert(fabs(wsum) >= 0); + float b = *(sptr_j); + float g = *(sptr_j+1); + float r = *(sptr_j+2); if (cvIsNaN(b) || cvIsNaN(g) || cvIsNaN(r)) { - wsum[j] = 1.f / wsum[j]; - *(dptr++) = sum_b[j] * wsum[j]; - *(dptr++) = sum_g[j] * wsum[j]; - *(dptr++) = sum_r[j] * wsum[j]; + wsum = 1.f / wsum; + *(dptr++) = sum_b * wsum; + *(dptr++) = sum_g * wsum; + *(dptr++) = sum_r * wsum; } else { - wsum[j] = 1.f / (wsum[j] + 1.f); - *(dptr++) = (sum_b[j] + b) * wsum[j]; - *(dptr++) = (sum_g[j] + g) * wsum[j]; - *(dptr++) = (sum_r[j] + r) * wsum[j]; + wsum = 1.f / (wsum + 1.f); + *(dptr++) = (sum_b + b) * wsum; + *(dptr++) = (sum_g + g) * wsum; + *(dptr++) = (sum_r + r) * wsum; } } } From 88fb0bad69796132c649c7c3ec71b24dc70c4e62 Mon Sep 17 00:00:00 2001 From: Ansh Swaroop <122546794+ansh1406@users.noreply.github.com> Date: Wed, 13 Aug 2025 02:28:44 +0530 Subject: [PATCH 14/14] videoio: add missing CV_OVERRIDE to VideoCapture_DShow::isOpened() This aligns with other virtual method declarations in cap_dshow.hpp and silences compiler warnings (-Wsuggest-override) while improving compile-time safety. --- modules/videoio/src/cap_dshow.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/videoio/src/cap_dshow.hpp b/modules/videoio/src/cap_dshow.hpp index 732ba81d8d..21af1ea399 100644 --- a/modules/videoio/src/cap_dshow.hpp +++ b/modules/videoio/src/cap_dshow.hpp @@ -30,7 +30,7 @@ public: virtual bool grabFrame() CV_OVERRIDE; virtual bool retrieveFrame(int outputType, OutputArray frame) CV_OVERRIDE; virtual int getCaptureDomain() CV_OVERRIDE; - virtual bool isOpened() const; + virtual bool isOpened() const CV_OVERRIDE; protected: void open(int index); void close();