From c4763279eb1e4a7918566643ebf24bcc73e4450c Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Wed, 1 Oct 2025 10:18:11 +0300 Subject: [PATCH 01/10] Added inRange HAL entry point. --- modules/core/src/arithm.cpp | 12 +++++++++ modules/core/src/hal_replacement.hpp | 38 ++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/modules/core/src/arithm.cpp b/modules/core/src/arithm.cpp index 01a1a02900..5c1033be81 100644 --- a/modules/core/src/arithm.cpp +++ b/modules/core/src/arithm.cpp @@ -2113,6 +2113,18 @@ void cv::inRange(InputArray _src, InputArray _lowerb, convertAndUnrollScalar( lb, src.type(), lbuf, blocksize ); convertAndUnrollScalar( ub, src.type(), ubuf, blocksize ); + + if (depth == CV_8U && src.dims <= 2) { + uint8_t lb_scalar = lbuf[0]; + uint8_t ub_scalar = ubuf[0]; + CALL_HAL(inRange_u8, cv_hal_inRange8u, src.data, src.step, dst.data, dst.step, dst.depth(), src.cols, src.rows, src.channels(), + lb_scalar, ub_scalar); + } else if (depth == CV_32F && src.dims <= 2) { + double lb_scalar = lb.ptr(0)[0]; + double ub_scalar = ub.ptr(0)[0]; + CALL_HAL(inRange_f32, cv_hal_inRange32f, src.data, src.step, dst.data, dst.step, dst.depth(), src.cols, src.rows, src.channels(), + lb_scalar, ub_scalar); + } } for( size_t i = 0; i < it.nplanes; i++, ++it ) diff --git a/modules/core/src/hal_replacement.hpp b/modules/core/src/hal_replacement.hpp index c48f1163bb..f809351550 100644 --- a/modules/core/src/hal_replacement.hpp +++ b/modules/core/src/hal_replacement.hpp @@ -1138,6 +1138,44 @@ inline int hal_ni_sum(const uchar *src_data, size_t src_step, int src_type, int #define cv_hal_sum hal_ni_sum //! @endcond +/** + @brief inRange (lower_bound <= src_value) && (src_value <= upper_bound) ? 255 : 0 + @param src_data Source image data + @param src_step Source image step + @param dst_data Destination image data + @param dst_step Destination image step + @param dst_depth Destination image depth + @param width Image width + @param height Image height + @param cn number of channels + @param lower_bound Range lower bound + @param upper_bound Range upper bound +*/ +inline int hal_ni_inRange8u(const uchar *src_data, size_t src_step, + uchar *dst_data, size_t dst_step, int dst_depth, int width, + int height, int cn, uchar lower_bound, uchar upper_bound) { return CV_HAL_ERROR_NOT_IMPLEMENTED; } +/** + @brief inRange (lower_bound <= src_value) && (src_value <= upper_bound) ? 255 : 0 + @param src_data Source image data + @param src_step Source image step + @param dst_data Destination image data + @param dst_step Destination image step + @param dst_depth Destination image depth + @param width Image width + @param height Image height + @param cn number of channels + @param lower_bound Range lower bound + @param upper_bound Range upper bound +*/ +inline int hal_ni_inRange32f(const uchar *src_data, size_t src_step, + uchar *dst_data, size_t dst_step, int dst_depth, int width, + int height, int cn, double lower_bound, double upper_bound) { return CV_HAL_ERROR_NOT_IMPLEMENTED; } + +//! @cond IGNORED +#define cv_hal_inRange8u hal_ni_inRange8u +#define cv_hal_inRange32f hal_ni_inRange32f +//! @endcond + //! @} #if defined(__clang__) From acc76304d5fb237ea0ff7b6736dd1c3e565f41b8 Mon Sep 17 00:00:00 2001 From: D00E Date: Thu, 2 Oct 2025 00:03:28 +0100 Subject: [PATCH 02/10] Updated Tutorial PSNR for identical images --- .../gpu-basics-similarity.cpp | 45 +++++++++---------- .../video-input-psnr-ssim.cpp | 4 +- .../videoio/video-input-psnr-ssim.py | 2 +- 3 files changed, 24 insertions(+), 27 deletions(-) diff --git a/samples/cpp/tutorial_code/gpu/gpu-basics-similarity/gpu-basics-similarity.cpp b/samples/cpp/tutorial_code/gpu/gpu-basics-similarity/gpu-basics-similarity.cpp index c80799828c..879fda86fc 100644 --- a/samples/cpp/tutorial_code/gpu/gpu-basics-similarity/gpu-basics-similarity.cpp +++ b/samples/cpp/tutorial_code/gpu/gpu-basics-similarity/gpu-basics-similarity.cpp @@ -181,14 +181,13 @@ double getPSNR(const Mat& I1, const Mat& I2) double sse = s.val[0] + s.val[1] + s.val[2]; // sum channels - if( sse <= 1e-10) // for small values return zero - return 0; - else - { - double mse =sse /(double)(I1.channels() * I1.total()); - double psnr = 10.0*log10((255*255)/mse); - return psnr; - } + double mse = sse /(double)(I1.channels() * I1.total()); + + // For very small SSE, add epsilon to approximate infinite PSNR (~361 dB) + if( sse <= 1e-10) mse+= DBL_EPSILON; + + double psnr = 10.0*log10((255*255)/mse); + return psnr; } //! [getpsnr] @@ -206,14 +205,13 @@ double getPSNR_CUDA_optimized(const Mat& I1, const Mat& I2, BufferPSNR& b) double sse = cuda::sum(b.gs, b.buf)[0]; - if( sse <= 1e-10) // for small values return zero - return 0; - else - { - double mse = sse /(double)(I1.channels() * I1.total()); - double psnr = 10.0*log10((255*255)/mse); - return psnr; - } + double mse = sse /(double)(I1.channels() * I1.total()); + + // For very small SSE, add epsilon to approximate infinite PSNR (~361 dB) + if (sse <= 1e-10) mse += DBL_EPSILON; + + double psnr = 10.0*log10((255*255)/mse); + return psnr; } //! [getpsnropt] @@ -234,14 +232,13 @@ double getPSNR_CUDA(const Mat& I1, const Mat& I2) Scalar s = cuda::sum(gs); double sse = s.val[0] + s.val[1] + s.val[2]; - if( sse <= 1e-10) // for small values return zero - return 0; - else - { - double mse =sse /(double)(gI1.channels() * I1.total()); - double psnr = 10.0*log10((255*255)/mse); - return psnr; - } + double mse = sse /(double)(gI1.channels() * I1.total()); + + // For very small SSE, add epsilon to approximate infinite PSNR (~361 dB) + if( sse <= 1e-10) mse+= DBL_EPSILON; + + double psnr = 10.0*log10((255*255)/mse); + return psnr; } //! [getpsnrcuda] diff --git a/samples/cpp/tutorial_code/videoio/video-input-psnr-ssim/video-input-psnr-ssim.cpp b/samples/cpp/tutorial_code/videoio/video-input-psnr-ssim/video-input-psnr-ssim.cpp index 8d567b2f5e..4d9082be65 100644 --- a/samples/cpp/tutorial_code/videoio/video-input-psnr-ssim/video-input-psnr-ssim.cpp +++ b/samples/cpp/tutorial_code/videoio/video-input-psnr-ssim/video-input-psnr-ssim.cpp @@ -144,8 +144,8 @@ double getPSNR(const Mat& I1, const Mat& I2) double sse = s.val[0] + s.val[1] + s.val[2]; // sum channels - if( sse <= 1e-10) // for small values return zero - return 0; + if( sse <= 1e-10) // For very small values, return 360 to cap PSNR (theoretical value is infinity) + return 360.0; else { double mse = sse / (double)(I1.channels() * I1.total()); diff --git a/samples/python/tutorial_code/videoio/video-input-psnr-ssim.py b/samples/python/tutorial_code/videoio/video-input-psnr-ssim.py index 973854feb6..17037b2433 100644 --- a/samples/python/tutorial_code/videoio/video-input-psnr-ssim.py +++ b/samples/python/tutorial_code/videoio/video-input-psnr-ssim.py @@ -16,7 +16,7 @@ def getPSNR(I1, I2): s1 = s1 * s1 # |I1 - I2|^2 sse = s1.sum() # sum elements per channel if sse <= 1e-10: # sum channels - return 0 # for small values return zero + return 360 # For very small SSE, return 360 to cap PSNR (theoretical value is infinity) else: shape = I1.shape mse = 1.0 * sse / (shape[0] * shape[1] * shape[2]) From d5cdad7629d9190baa08f28cc8a766182720fbb3 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Thu, 2 Oct 2025 09:00:22 +0300 Subject: [PATCH 03/10] Restored PYTHON_DEBUG_LIBRARIES in python bindings. --- modules/python/common.cmake | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/modules/python/common.cmake b/modules/python/common.cmake index 3c0c4febd7..e9375af2c0 100644 --- a/modules/python/common.cmake +++ b/modules/python/common.cmake @@ -59,7 +59,12 @@ endif() if(APPLE) set_target_properties(${the_module} PROPERTIES LINK_FLAGS "-undefined dynamic_lookup") elseif(WIN32 OR OPENCV_FORCE_PYTHON_LIBS) - ocv_target_link_libraries(${the_module} PRIVATE ${${PYTHON}_LIBRARIES}) + if(${PYTHON}_DEBUG_LIBRARIES AND NOT ${PYTHON}_LIBRARIES MATCHES "optimized.*debug") + ocv_target_link_libraries(${the_module} PRIVATE ${${PYTHON}_LIBRARIES}) + ocv_target_link_libraries(${the_module} PRIVATE debug ${${PYTHON}_DEBUG_LIBRARIES} optimized ${${PYTHON}_LIBRARIES}) + else() + ocv_target_link_libraries(${the_module} PRIVATE ${${PYTHON}_LIBRARIES}) + endif() endif() if(TARGET gen_opencv_python_source) From 80fef356ecc82e99a59cf367ad45076feb2e827a Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Thu, 2 Oct 2025 09:12:05 +0300 Subject: [PATCH 04/10] Warning fixes in DLpack integration on Win32. --- modules/core/misc/python/pyopencv_cuda.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/core/misc/python/pyopencv_cuda.hpp b/modules/core/misc/python/pyopencv_cuda.hpp index 5fd21ca630..2d6b284d95 100644 --- a/modules/core/misc/python/pyopencv_cuda.hpp +++ b/modules/core/misc/python/pyopencv_cuda.hpp @@ -112,7 +112,7 @@ bool parseDLPackTensor(DLManagedTensor* tensor, cv::cuda::GpuMat& obj, bool copy static_cast(tensor->dl_tensor.shape[1]), type, tensor->dl_tensor.data, - tensor->dl_tensor.strides[0] * tensor->dl_tensor.dtype.bits / 8 + static_cast(tensor->dl_tensor.strides[0] * tensor->dl_tensor.dtype.bits / 8) ); if (copy) obj = obj.clone(); @@ -140,7 +140,7 @@ bool parseDLPackTensor(DLManagedTensor* tensor, cv::cuda::GpuMatND& obj, bool co std::vector sizes(tensor->dl_tensor.ndim); for (int i = 0; i < tensor->dl_tensor.ndim - 1; ++i) { - steps[i] = tensor->dl_tensor.strides[i] * tensor->dl_tensor.dtype.bits / 8; + steps[i] = static_cast(tensor->dl_tensor.strides[i] * tensor->dl_tensor.dtype.bits / 8); sizes[i] = static_cast(tensor->dl_tensor.shape[i]); } sizes.back() = static_cast(tensor->dl_tensor.shape[tensor->dl_tensor.ndim - 1]); From 8f3976ae9703701a21e1e566ec5a6fd0bcfdcb41 Mon Sep 17 00:00:00 2001 From: pratham-mcw Date: Fri, 3 Oct 2025 13:20:50 +0530 Subject: [PATCH 05/10] Merge pull request #27785 from pratham-mcw:dnn-lstm-neon MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dnn: added neon intrinsics implementation of fastGEMM1T function #27785 ### 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 - This PR improves the performance of the LSTM function on ARM64 targets. - Added a NEON intrinsics implementation of the fastGEMM1T function and enabled its use in fully connected and recurrent layers file. - As a result, ARM64 now benefits from vectorized matrix–vector multiplications, leading to measurable performance improvements in the LSTM layer. - This change is limited to ARM64 and does not affect other architectures. **Performance impact:** - The optimization significantly improves the performance of lstm functions on ARM64 targets. image --- modules/dnn/CMakeLists.txt | 4 +- modules/dnn/src/layers/layers_common.simd.hpp | 89 +++++++++++++++++++ modules/dnn/src/layers/recurrent_layers.cpp | 48 ++++++++++ 3 files changed, 139 insertions(+), 2 deletions(-) diff --git a/modules/dnn/CMakeLists.txt b/modules/dnn/CMakeLists.txt index 382379cb1c..cd83684f77 100644 --- a/modules/dnn/CMakeLists.txt +++ b/modules/dnn/CMakeLists.txt @@ -4,8 +4,8 @@ endif() set(the_description "Deep neural network module. It allows to load models from different frameworks and to make forward pass") -ocv_add_dispatched_file_force_all("layers/layers_common" AVX AVX2 AVX512_SKX RVV LASX) -ocv_add_dispatched_file_force_all("int8layers/layers_common" AVX2 AVX512_SKX RVV LASX) +ocv_add_dispatched_file_force_all("layers/layers_common" AVX AVX2 AVX512_SKX RVV LASX NEON) +ocv_add_dispatched_file_force_all("int8layers/layers_common" AVX2 AVX512_SKX RVV LASX NEON) ocv_add_dispatched_file_force_all("layers/cpu_kernels/conv_block" AVX AVX2 NEON NEON_FP16) ocv_add_dispatched_file_force_all("layers/cpu_kernels/conv_depthwise" AVX AVX2 RVV LASX) ocv_add_dispatched_file("layers/cpu_kernels/conv_winograd_f63" AVX AVX2 NEON NEON_FP16) diff --git a/modules/dnn/src/layers/layers_common.simd.hpp b/modules/dnn/src/layers/layers_common.simd.hpp index 8882168ce8..0da90156b6 100644 --- a/modules/dnn/src/layers/layers_common.simd.hpp +++ b/modules/dnn/src/layers/layers_common.simd.hpp @@ -53,6 +53,95 @@ void fastGEMM( const float* aptr, size_t astep, const float* bptr, size_t bstep, float* cptr, size_t cstep, int ma, int na, int nb ); +#if !defined(CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY) && CV_NEON + +static const uint32_t tailMaskArray[7] = { + 0u, 0u, 0u, 0u, + 0xffffffffu, 0xffffffffu, 0xffffffffu +}; + +void fastGEMM1T( const float* vec, const float* weights, + size_t wstep, const float* bias, + float* dst, int nvecs, int vecsize ) +{ + int i = 0; + CV_Assert(vecsize >= 4 || vecsize == 0); + const uint32_t* tailMaskPtr = tailMaskArray + (vecsize % 4); + const uint32x4_t tailMaskU = vld1q_u32(tailMaskPtr); + const float32x4_t tailMask = vreinterpretq_f32_u32(tailMaskU); + + for ( ; i <= nvecs - 4; i += 4 ) + { + const float* wptr = weights + i * wstep; + float32x4_t vs0 = vdupq_n_f32(0.0f); + float32x4_t vs1 = vdupq_n_f32(0.0f); + float32x4_t vs2 = vdupq_n_f32(0.0f); + float32x4_t vs3 = vdupq_n_f32(0.0f); + int k = 0; + for ( ; k <= vecsize - 4; k += 4, wptr += 4 ) + { + float32x4_t v = vld1q_f32(vec + k); + + vs0 = vmlaq_f32(vs0, vld1q_f32(wptr), v); + vs1 = vmlaq_f32(vs1, vld1q_f32(wptr + wstep), v); + vs2 = vmlaq_f32(vs2, vld1q_f32(wptr + wstep * 2), v); + vs3 = vmlaq_f32(vs3, vld1q_f32(wptr + wstep * 3), v); + } + if (k != vecsize) + { + k = vecsize - 4; + wptr = weights + i * wstep + k; + float32x4_t v = vld1q_f32(vec + k); + v = vreinterpretq_f32_u32( vandq_u32(vreinterpretq_u32_f32(v), vreinterpretq_u32_f32(tailMask)) ); + float32x4_t w0 = vreinterpretq_f32_u32( vandq_u32(vreinterpretq_u32_f32(vld1q_f32(wptr)), vreinterpretq_u32_f32(tailMask)) ); + float32x4_t w1 = vreinterpretq_f32_u32( vandq_u32(vreinterpretq_u32_f32(vld1q_f32(wptr + wstep)), vreinterpretq_u32_f32(tailMask)) ); + float32x4_t w2 = vreinterpretq_f32_u32( vandq_u32(vreinterpretq_u32_f32(vld1q_f32(wptr + wstep * 2)), vreinterpretq_u32_f32(tailMask)) ); + float32x4_t w3 = vreinterpretq_f32_u32( vandq_u32(vreinterpretq_u32_f32(vld1q_f32(wptr + wstep * 3)), vreinterpretq_u32_f32(tailMask)) ); + vs0 = vmlaq_f32(vs0, w0, v); + vs1 = vmlaq_f32(vs1, w1, v); + vs2 = vmlaq_f32(vs2, w2, v); + vs3 = vmlaq_f32(vs3, w3, v); + } + + auto hsumq_f32 = [](float32x4_t x) -> float { + float32x2_t s2 = vadd_f32(vget_low_f32(x), vget_high_f32(x)); + s2 = vpadd_f32(s2, s2); + return vget_lane_f32(s2, 0); + }; + + dst[i + 0] = hsumq_f32(vs0) + bias[i + 0]; + dst[i + 1] = hsumq_f32(vs1) + bias[i + 1]; + dst[i + 2] = hsumq_f32(vs2) + bias[i + 2]; + dst[i + 3] = hsumq_f32(vs3) + bias[i + 3]; + } + + for ( ; i < nvecs; i++ ) + { + const float* wptr = weights + i * wstep; + float32x4_t vs0 = vdupq_n_f32(0.0f); + int k = 0; + for ( ; k <= vecsize - 4; k += 4, wptr += 4 ) + { + float32x4_t v = vld1q_f32(vec + k); + vs0 = vmlaq_f32(vs0, vld1q_f32(wptr), v); + } + if (k != vecsize) + { + k = vecsize - 4; + wptr = weights + i * wstep + k; + float32x4_t v = vld1q_f32(vec + k); + v = vreinterpretq_f32_u32( vandq_u32(vreinterpretq_u32_f32(v), vreinterpretq_u32_f32(tailMask)) ); + float32x4_t w0 = vreinterpretq_f32_u32( vandq_u32(vreinterpretq_u32_f32(vld1q_f32(wptr)), vreinterpretq_u32_f32(tailMask)) ); + vs0 = vmlaq_f32(vs0, w0, v); + } + float32x2_t s2 = vadd_f32(vget_low_f32(vs0), vget_high_f32(vs0)); + s2 = vpadd_f32(s2, s2); + dst[i] = vget_lane_f32(s2, 0) + bias[i]; + } +} + +#endif //CV_NEON + #if !defined(CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY) && CV_AVX #if !CV_FMA3 // AVX workaround diff --git a/modules/dnn/src/layers/recurrent_layers.cpp b/modules/dnn/src/layers/recurrent_layers.cpp index ad45a8a2a9..202933b4ca 100644 --- a/modules/dnn/src/layers/recurrent_layers.cpp +++ b/modules/dnn/src/layers/recurrent_layers.cpp @@ -138,6 +138,9 @@ class LSTMLayerImpl CV_FINAL : public LSTMLayer #if CV_TRY_AVX2 bool useAVX2; #endif +#if CV_TRY_NEON + bool useNEON; +#endif // CUDA needs input blobs to be rearranged in a specific way, but some transformations // in ONNXImporter are destructive, so we keep a copy. @@ -152,6 +155,9 @@ public: #endif #if CV_TRY_AVX2 , useAVX2(checkHardwareSupport(CPU_AVX2)) +#endif +#if CV_TRY_NEON + , useNEON(checkHardwareSupport(CPU_NEON)) #endif { setParamsFrom(params); @@ -489,6 +495,14 @@ public: && Wh.depth() == CV_32F && hInternal.depth() == CV_32F && gates.depth() == CV_32F && Wh.cols >= 8; #endif +#if CV_TRY_NEON + bool canUseNeon = gates.isContinuous() && bias.isContinuous() + && Wx.depth() == CV_32F && gates.depth() == CV_32F + && bias.depth() == CV_32F && Wx.cols >= 4; + bool canUseNeon_hInternal = hInternal.isContinuous() && gates.isContinuous() && bias.isContinuous() + && Wh.depth() == CV_32F && hInternal.depth() == CV_32F && gates.depth() == CV_32F + && Wh.cols >= 4; +#endif int tsStart, tsEnd, tsInc; if (reverse || i == 1) { @@ -539,6 +553,23 @@ public: } } else +#endif +#if CV_TRY_NEON + if (useNEON && canUseNeon && xCurr.isContinuous()) + { + for (int n = 0; n < xCurr.rows; n++) { + opt_NEON::fastGEMM1T( + xCurr.ptr(n), + Wx.ptr(), + Wx.step1(), + bias.ptr(), + gates.ptr(n), + Wx.rows, + Wx.cols + ); + } + } + else #endif { gemm(xCurr, Wx, 1, gates, 0, gates, GEMM_2_T); // Wx * x_t @@ -578,6 +609,23 @@ public: } } else +#endif +#if CV_TRY_NEON + if (useNEON && canUseNeon_hInternal) + { + for (int n = 0; n < hInternal.rows; n++) { + opt_NEON::fastGEMM1T( + hInternal.ptr(n), + Wh.ptr(), + Wh.step1(), + gates.ptr(n), + gates.ptr(n), + Wh.rows, + Wh.cols + ); + } + } + else #endif { gemm(hInternal, Wh, 1, gates, 1, gates, GEMM_2_T); //+Wh * h_{t-1} From 4e94116e7ac54e4444e46534bc2ee4ce1860c57c Mon Sep 17 00:00:00 2001 From: Peter Rekdal Khan-Sunde Date: Sat, 4 Oct 2025 12:24:28 +0200 Subject: [PATCH 06/10] Merge pull request #27864 from peters:patch-2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add OPENCV_FFMPEG_SKIP_LOG_CALLBACK to preserve custom FFmpeg logging #27864 OpenCV’s InternalFFMpegRegister overwrites av_log_set_callback, blocking custom FFmpeg log handlers in statically linked apps. Add `OPENCV_FFMPEG_SKIP_LOG_CALLBACK` to let applications keep their own logging. ### 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 --- .../env_reference/env_reference.markdown | 1 + modules/videoio/src/cap_ffmpeg_impl.hpp | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/doc/tutorials/introduction/env_reference/env_reference.markdown b/doc/tutorials/introduction/env_reference/env_reference.markdown index 88b4008c8f..d4e2881f42 100644 --- a/doc/tutorials/introduction/env_reference/env_reference.markdown +++ b/doc/tutorials/introduction/env_reference/env_reference.markdown @@ -273,6 +273,7 @@ Some external dependencies can be detached into a dynamic library, which will be | OPENCV_FFMPEG_THREADS | num | | set FFmpeg thread count | | OPENCV_FFMPEG_DEBUG | bool | false | enable logging messages from FFmpeg | | OPENCV_FFMPEG_LOGLEVEL | num | | set FFmpeg logging level | +| OPENCV_FFMPEG_SKIP_LOG_CALLBACK | bool | false | do not install OpenCV's FFmpeg log callback (preserve default/user callback) | | OPENCV_FFMPEG_DLL_DIR | dir path | | directory with FFmpeg plugin (legacy) | | OPENCV_FFMPEG_IS_THREAD_SAFE | bool | false | enabling this option will turn off thread safety locks in the FFmpeg backend (use only if you are sure FFmpeg is built with threading support, tested on Linux) | | OPENCV_FFMPEG_READ_ATTEMPTS | num | 4096 | number of failed `av_read_frame` attempts before failing read procedure | diff --git a/modules/videoio/src/cap_ffmpeg_impl.hpp b/modules/videoio/src/cap_ffmpeg_impl.hpp index 7ce18fdfbf..6d7749d21c 100644 --- a/modules/videoio/src/cap_ffmpeg_impl.hpp +++ b/modules/videoio/src/cap_ffmpeg_impl.hpp @@ -937,6 +937,8 @@ static void ffmpeg_log_callback(void *ptr, int level, const char *fmt, va_list v class InternalFFMpegRegister { + static bool skip_log_callback; + public: static void init(const bool threadSafe) { @@ -949,6 +951,7 @@ public: { const bool debug_option = utils::getConfigurationParameterBool("OPENCV_FFMPEG_DEBUG"); std::string level_option = utils::getConfigurationParameterString("OPENCV_FFMPEG_LOGLEVEL"); + skip_log_callback = utils::getConfigurationParameterBool("OPENCV_FFMPEG_SKIP_LOG_CALLBACK"); int level = AV_LOG_VERBOSE; if (!level_option.empty()) { @@ -957,7 +960,10 @@ public: if ( debug_option || (!level_option.empty()) ) { av_log_set_level(level); - av_log_set_callback(ffmpeg_log_callback); + if (!skip_log_callback) + { + av_log_set_callback(ffmpeg_log_callback); + } } else { @@ -986,10 +992,15 @@ public: #ifdef CV_FFMPEG_LOCKMGR av_lockmgr_register(NULL); #endif - av_log_set_callback(NULL); + if (!skip_log_callback) + { + av_log_set_callback(NULL); + } } }; +bool InternalFFMpegRegister::skip_log_callback = false; + inline void fill_codec_context(AVCodecContext * enc, AVDictionary * dict) { if (!enc->thread_count) From 387afc4eb485affc818a94c79cc9d31afc275490 Mon Sep 17 00:00:00 2001 From: Dmitry Kurtaev Date: Sun, 5 Oct 2025 21:51:58 +0300 Subject: [PATCH 07/10] Rename fields in camera calibration results --- apps/interactive-calibration/calibController.cpp | 8 ++++---- .../interactive_calibration.markdown | 16 ++++++++-------- .../aruco_board_detection.markdown | 2 +- .../charuco_detection/charuco_detection.markdown | 2 +- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/apps/interactive-calibration/calibController.cpp b/apps/interactive-calibration/calibController.cpp index b2e5b87106..7cb9857a4c 100644 --- a/apps/interactive-calibration/calibController.cpp +++ b/apps/interactive-calibration/calibController.cpp @@ -306,10 +306,10 @@ bool calib::calibDataController::saveCurrentCameraParameters() const parametersWriter << "calibrationDate" << buf; parametersWriter << "framesCount" << std::max((int)mCalibData->objectPoints.size(), (int)mCalibData->allCharucoCorners.size()); parametersWriter << "cameraResolution" << mCalibData->imageSize; - parametersWriter << "cameraMatrix" << mCalibData->cameraMatrix; - parametersWriter << "cameraMatrix_std_dev" << mCalibData->stdDeviations.rowRange(cv::Range(0, 4)); - parametersWriter << "dist_coeffs" << mCalibData->distCoeffs; - parametersWriter << "dist_coeffs_std_dev" << mCalibData->stdDeviations.rowRange(cv::Range(4, 9)); + parametersWriter << "camera_matrix" << mCalibData->cameraMatrix; + parametersWriter << "camera_matrix_std_dev" << mCalibData->stdDeviations.rowRange(cv::Range(0, 4)); + parametersWriter << "distortion_coefficients" << mCalibData->distCoeffs; + parametersWriter << "distortion_coefficients_std_dev" << mCalibData->stdDeviations.rowRange(cv::Range(4, 9)); parametersWriter << "avg_reprojection_error" << mCalibData->totalAvgErr; parametersWriter.release(); diff --git a/doc/tutorials/calib3d/interactive_calibration/interactive_calibration.markdown b/doc/tutorials/calib3d/interactive_calibration/interactive_calibration.markdown index 14db876933..10c121e3fc 100644 --- a/doc/tutorials/calib3d/interactive_calibration/interactive_calibration.markdown +++ b/doc/tutorials/calib3d/interactive_calibration/interactive_calibration.markdown @@ -180,34 +180,34 @@ Example of output XML file: 21 1280 720 - + 3 3
d
1.2519588293098975e+03 0. 6.6684948780852471e+02 0. - 1.2519588293098975e+03 3.6298123112613683e+02 0. 0. 1.
- + 1.2519588293098975e+03 3.6298123112613683e+02 0. 0. 1. + 4 1
d
0. 1.2887048808572649e+01 2.8536856683866230e+00 - 2.8341737483430314e+00
- + 2.8341737483430314e+00 + 1 5
d
1.3569117181595716e-01 -8.2513063822554633e-01 0. 0. - 1.6412101575010554e+00
- + 1.6412101575010554e+00 + 5 1
d
1.5570675523402111e-02 8.7229075437543435e-02 0. 0. - 1.8382427901856876e-01
+ 1.8382427901856876e-01 4.2691743074130178e-01 @endcode diff --git a/doc/tutorials/objdetect/aruco_board_detection/aruco_board_detection.markdown b/doc/tutorials/objdetect/aruco_board_detection/aruco_board_detection.markdown index 400891dfaa..352762a2b8 100644 --- a/doc/tutorials/objdetect/aruco_board_detection/aruco_board_detection.markdown +++ b/doc/tutorials/objdetect/aruco_board_detection/aruco_board_detection.markdown @@ -127,7 +127,7 @@ in ascending order starting on 0, so they will be 0, 1, 2, ..., 34. After creating a grid board, we probably want to print it and use it. There are two ways to do this: -1. By using the script `apps/pattern_tools/generate_pattern.py `, see @subpage tutorial_camera_calibration_pattern. +1. By using the script `apps/pattern-tools/generate_pattern.py`, see @subpage tutorial_camera_calibration_pattern. 2. By using the function `cv::aruco::GridBoard::generateImage()`. The function `cv::aruco::GridBoard::generateImage()` is provided in cv::aruco::GridBoard class and diff --git a/doc/tutorials/objdetect/charuco_detection/charuco_detection.markdown b/doc/tutorials/objdetect/charuco_detection/charuco_detection.markdown index 40fc331000..7ea0f39d23 100644 --- a/doc/tutorials/objdetect/charuco_detection/charuco_detection.markdown +++ b/doc/tutorials/objdetect/charuco_detection/charuco_detection.markdown @@ -77,7 +77,7 @@ through `board.ids`, like in the `cv::aruco::Board` parent class. Once we have our `cv::aruco::CharucoBoard` object, we can create an image to print it. There are two ways to do this: -1. By using the script `apps/pattern_tools/generate_pattern.py `, see @subpage tutorial_camera_calibration_pattern. +1. By using the script `apps/pattern-tools/generate_pattern.py`, see @subpage tutorial_camera_calibration_pattern. 2. By using the function `cv::aruco::CharucoBoard::generateImage()`. The function `cv::aruco::CharucoBoard::generateImage()` is provided in cv::aruco::CharucoBoard class From 19c41c2936ce7cd7deb9a8738c2224ac47eccf16 Mon Sep 17 00:00:00 2001 From: ashish-sriram <135826843+ashish-sriram@users.noreply.github.com> Date: Mon, 6 Oct 2025 13:30:30 +0530 Subject: [PATCH 08/10] Merge pull request #27822 from amd:fast_blur_simd Improved blur #27822 * Perform row and column filter operations in a single pass. * Temporary storage of intermediate results are avoided. * Impacts 32F and 64F inputs for ksize <=5. ### 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/CMakeLists.txt | 2 +- modules/imgproc/src/box_filter.dispatch.cpp | 8 + modules/imgproc/src/box_filter.simd.hpp | 418 ++++++++++++++++++++ 3 files changed, 427 insertions(+), 1 deletion(-) diff --git a/modules/imgproc/CMakeLists.txt b/modules/imgproc/CMakeLists.txt index f8ceddf55a..dd5ba447f2 100644 --- a/modules/imgproc/CMakeLists.txt +++ b/modules/imgproc/CMakeLists.txt @@ -1,7 +1,7 @@ set(the_description "Image Processing") ocv_add_dispatched_file(accum SSE4_1 AVX AVX2) ocv_add_dispatched_file(bilateral_filter SSE2 AVX2) -ocv_add_dispatched_file(box_filter SSE2 SSE4_1 AVX2) +ocv_add_dispatched_file(box_filter SSE2 SSE4_1 AVX2 AVX512_SKX) ocv_add_dispatched_file(filter SSE2 SSE4_1 AVX2) ocv_add_dispatched_file(color_hsv SSE2 SSE4_1 AVX2) ocv_add_dispatched_file(color_rgb SSE2 SSE4_1 AVX2) diff --git a/modules/imgproc/src/box_filter.dispatch.cpp b/modules/imgproc/src/box_filter.dispatch.cpp index ba90adee1d..347cdc421e 100644 --- a/modules/imgproc/src/box_filter.dispatch.cpp +++ b/modules/imgproc/src/box_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, @@ -403,6 +404,13 @@ void boxFilter(InputArray _src, OutputArray _dst, int ddepth, borderType = (borderType&~BORDER_ISOLATED); + if(sdepth >= CV_32F && src.type() == dst.type() && (ksize.height <= 5 && ksize.width <= 5)) + { + CV_CPU_DISPATCH(blockSum, (src, dst, ksize, anchor, wsz, ofs, normalize, borderType), + CV_CPU_DISPATCH_MODES_ALL); + return; + } + Ptr f = createBoxFilter( src.type(), dst.type(), ksize, anchor, normalize, borderType ); diff --git a/modules/imgproc/src/box_filter.simd.hpp b/modules/imgproc/src/box_filter.simd.hpp index ac0b0f1b56..b7d2a8b214 100644 --- a/modules/imgproc/src/box_filter.simd.hpp +++ b/modules/imgproc/src/box_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, @@ -51,6 +52,8 @@ Ptr getRowSumFilter(int srcType, int sumType, int ksize, int anch Ptr getColumnSumFilter(int sumType, int dstType, int ksize, int anchor, double scale); Ptr createBoxFilter(int srcType, int dstType, Size ksize, Point anchor, bool normalize, int borderType); +void blockSum(const Mat& src, Mat& dst, Size ksize, Point anchor, + const Size &wsz, const Point &ofs, bool normalize, int borderType); Ptr getSqrRowSumFilter(int srcType, int sumType, int ksize, int anchor); @@ -1166,6 +1169,392 @@ struct ColumnSum : std::vector sum; }; +#define VEC_ALIGN CV_MALLOC_ALIGN +template +inline int BlockSumBorder(const T* ref, const T* S, T* R, ST* SUM, T* D, ST** buf_ptr, + const int *btab, int width, int i, int cn, int dx1, int dx2, int kheight, int kwidth, + bool leftBorder, int borderLen, int borderType, double scale) +{ + int j=0, k; + const T* Si = S+i; + if( leftBorder ) + { + for( k=0; k < dx1*cn; k++, j++ ) + { + if( borderType != BORDER_CONSTANT ) + R[j] = ref[btab[k]]; + else + R[j] = 0; + } + if( width >= dx1+dx2 ) + { + for( k=0; k < (kwidth-1)*cn; k++, j++ ) + { + R[j] = Si[k]; + } + } + else + { + int diff = dx1+dx2-width; + for( k=0; k < (kwidth-1-diff)*cn; k++, j++ ) + { + R[j] = Si[k]; + } + int rem = min(diff, dx2); + for( k=0; k < rem*cn; k++, j++ ) + { + if( borderType != BORDER_CONSTANT ) + R[j] = ref[btab[dx1*cn+k]]; + else + R[j] = 0; + } + } + } + else + { + for( k=0; k < borderLen+(kwidth-1-dx2)*cn; k++, j++ ) + { + R[j] = Si[k]; + } + for( k=0; k < dx2*cn; k++, j++ ) + { + if( borderType != BORDER_CONSTANT ) + R[j] = ref[btab[dx1*cn+k]]; + else + { + R[j] = 0; + } + } + } + + for( j=0; j < borderLen; j++, i++ ) + { + ST Sp = 0; + for(k=0; k < kwidth; k++) + Sp += (ST)R[j+k*cn]; + buf_ptr[kheight-1][i] = Sp; + ST s0 = SUM[i] + Sp; + D[i] = saturate_cast(s0*scale); + SUM[i] = s0 - buf_ptr[0][i]; + } + + return i; +} + +template +inline void BlockSumBorderInplace(const T* ref, const T* S, T* R, + const int *btab, int width, int cn, int dx1, int dx2, int borderType) +{ + int j=0, k; + for( k=0; k < dx1*cn; k++, j++ ) + { + if( borderType != BORDER_CONSTANT ) + R[j] = ref[btab[k]]; + else + R[j] = 0; + } + for( k=0; k < width*cn; k++, j++ ) + { + R[j] = S[k]; + } + for( k=0; k < dx2*cn; k++, j++ ) + { + if( borderType != BORDER_CONSTANT ) + R[j] = ref[btab[dx1*cn+k]]; + else + { + R[j] = 0; + } + } +} + +template +inline int BlockSumCoreRow(const T* S, ST* SUM, T* D, ST** buf_ptr, double scale, + int widthcn, int i, int cn, int kheight, int kwidth) +{ + bool haveScale = scale != 1; + if( haveScale ) + { + for( ; i < widthcn; i++ ) + { + ST Ss = 0; + for( int k=0; k < kwidth; k++ ) + Ss += (ST)S[i+k*cn]; + buf_ptr[kheight-1][i] = Ss; + ST s0 = SUM[i] + Ss; + D[i] = saturate_cast(s0*scale); + SUM[i] = s0 - buf_ptr[0][i]; + } + } + else + { + for( ; i < widthcn; i++ ) + { + ST Ss = 0; + for( int k=0; k < kwidth; k++ ) + Ss += (ST)S[i+k*cn]; + buf_ptr[kheight-1][i] = Ss; + ST s0 = SUM[i] + Ss; + D[i] = saturate_cast(s0); + SUM[i] = s0 - buf_ptr[0][i]; + } + } + return i; +} + +template +inline int BlockSumCore(const T* S, ST* SUM, T* D, ST** buf_ptr, double scale, + int widthcn, int i, int cn, int kheight, int kwidth) +{ + bool haveScale = scale != 1; + switch(kwidth) + { + case 3: + if( haveScale ) + { + for( ; i < widthcn; i++ ) + { + ST Sp = (ST)S[i] + (ST)S[i+1*cn] + (ST)S[i+2*cn]; + buf_ptr[kheight-1][i] = Sp; + ST s0 = SUM[i] + Sp; + D[i] = saturate_cast(s0*scale); + SUM[i] = s0 - buf_ptr[0][i]; + } + } + else + { + for( ; i < widthcn; i++ ) + { + ST Sp = (ST)S[i] + (ST)S[i+1*cn] + (ST)S[i+2*cn]; + buf_ptr[kheight-1][i] = Sp; + ST s0 = SUM[i] + Sp; + D[i] = saturate_cast(s0); + SUM[i] = s0 - buf_ptr[0][i]; + } + } + break; + case 5: + if( haveScale ) + { + for( ; i < widthcn; i++ ) + { + ST Sp = (ST)S[i] + (ST)S[i+1*cn] + (ST)S[i+2*cn] + (ST)S[i+3*cn] + (ST)S[i+4*cn]; + buf_ptr[kheight-1][i] = Sp; + ST s0 = SUM[i] + Sp; + D[i] = saturate_cast(s0*scale); + SUM[i] = s0 - buf_ptr[0][i]; + } + } + else + { + for( ; i < widthcn; i++ ) + { + ST Sp = (ST)S[i] + (ST)S[i+1*cn] + (ST)S[i+2*cn] + (ST)S[i+3*cn] + (ST)S[i+4*cn]; + buf_ptr[kheight-1][i] = Sp; + ST s0 = SUM[i] + Sp; + D[i] = saturate_cast(s0); + SUM[i] = s0 - buf_ptr[0][i]; + } + } + break; + case 1: + i = BlockSumCoreRow(S, SUM, D, buf_ptr, scale, widthcn, i, cn, kheight, 1); + break; + case 2: + i = BlockSumCoreRow(S, SUM, D, buf_ptr, scale, widthcn, i, cn, kheight, 2); + break; + case 4: + i = BlockSumCoreRow(S, SUM, D, buf_ptr, scale, widthcn, i, cn, kheight, 4); + break; + default: + CV_Error_( cv::Error::StsNotImplemented, + ("Unsupported kernel width (=%d)", kwidth)); + break; + } + return i; +} + +uchar* alignCore(uchar* ptr, int borderBytes, bool inplace) // align core region to VEC_ALIGN +{ + if( inplace ) // borders handled in core + return alignPtr((uchar*)ptr, VEC_ALIGN); + else // borders handled separately + { + ptr += borderBytes; + uchar* aligned = alignPtr((uchar*)ptr, VEC_ALIGN); + return aligned - borderBytes; + } +} + +template +void BlockSum(const Mat& _src, Mat& _dst, Size ksize, Point anchor, const Size &wsz, + const Point &ofs, double scale, int borderType, int sumType, bool inplace) +{ + int i, j; + cv::utils::BufferArea area, area2; + int* borderTab = 0; + uchar* buf = 0, *constBorder = 0; + uchar* border = 0, *inplaceSrc = 0, *inplaceDst = 0; + std::vector bufPtr; + const uchar* src = _src.ptr(); + uchar* dst = _dst.ptr(); + Size wholeSize = wsz; + Rect roi = Rect(ofs, _src.size()); + CV_Assert( roi.x >= 0 && roi.y >= 0 && roi.width >= 0 && roi.height >= 0 && + roi.x + roi.width <= wholeSize.width && + roi.y + roi.height <= wholeSize.height ); + + size_t srcStep = _src.step; + size_t dstStep = _dst.step; + int srcType = _src.type(); + int cn = CV_MAT_CN(srcType); + int sesz = (int)getElemSize(srcType); + int besz = (int)getElemSize(sumType); + int kwidth = ksize.width; + int kheight = ksize.height; + int borderLength = std::max(kwidth - 1, 1); + int width = roi.width; + int height = roi.height; + int width1 = width + kwidth - 1; + int height1 = height + kheight - 1; + int xofs1 = std::min(roi.x, anchor.x); + int dx1 = std::max(anchor.x - roi.x, 0); + int dx2 = std::max(kwidth - anchor.x - 1 + roi.x + roi.width - wholeSize.width, 0); + int dy2 = std::max(kheight - anchor.y - 1 + roi.y + roi.height - wholeSize.height, 0); + int borderLeft = min(dx1, width)*cn; + int bufWidth = (width1+borderLeft+VEC_ALIGN); + int bufStep = bufWidth*besz; + + src -= xofs1*sesz; + area.allocate(borderTab, borderLength*cn); + area.allocate(buf, bufWidth*(kheight+1)*besz); + area.allocate(constBorder, bufWidth*sesz); + area.commit(); + area.zeroFill(); + + // compute border tables + if( dx1 > 0 || dx2 > 0 ) + { + if( borderType != BORDER_CONSTANT ) + { + int xofs1w = std::min(roi.x, anchor.x) - roi.x; + int wholeWidth = wholeSize.width; + int* btabx = borderTab; + + for( i = 0; i < dx1; i++ ) + { + int p0 = (borderInterpolate(i-dx1, wholeWidth, borderType) + xofs1w)*cn; + for( j = 0; j < cn; j++ ) + btabx[i*cn + j] = p0 + j; + } + + for( i = 0; i < dx2; i++ ) + { + int p0 = (borderInterpolate(wholeWidth+i, wholeWidth, borderType) + xofs1w)*cn; + for( j = 0; j < cn; j++ ) + btabx[(i + dx1)*cn + j] = p0 + j; + } + } + } + + if( inplace ) + { + area2.allocate(border, bufWidth*sesz); + area2.allocate(inplaceDst, bufWidth*sesz); + if( dy2 ) + { + area2.allocate(inplaceSrc, dy2*bufWidth*sesz); + area2.commit(); + if( borderType == BORDER_CONSTANT ) + memset(inplaceSrc, 0, dy2*bufWidth*sesz); + else + { + for( int idx=0; idx < dy2; ++idx ) + { + uchar* out = (uchar*)alignCore(&inplaceSrc[bufWidth*sesz*idx], + borderLeft*sizeof(T), inplace); + int rc = height1-(dy2-idx); + int srcY = borderInterpolate(rc + roi.y - anchor.y, wholeSize.height, borderType); + const uchar* inp = (uchar*)(src+(srcY-roi.y)*srcStep); + memcpy(out, inp, width*sesz); + } + } + } + else + area2.commit(); + } + else + { + area2.allocate(border, (kwidth-1+max(dx1, dx2)+VEC_ALIGN)*sesz); + area2.commit(); + } + bufPtr.resize(kheight); + + const T *S; + const T* ref; + T* D; + T* R; + ST** buf_ptr = &bufPtr[0]; + const int *btab = borderTab; + T* C = (T*)alignCore(constBorder, borderLeft*sizeof(T), inplace); + ST* SUM = (ST*)alignCore(&buf[bufStep*kheight], borderLeft*sizeof(ST), inplace); + for( int k=0;k= height1-dy2 ) + { + int idx = rc - (height1-dy2); + ref = (const T*)alignCore(&inplaceSrc[bufWidth*sesz*idx], + borderLeft*sizeof(T), inplace); + } + else + ref = (const T*)(src+(srcY-roi.y)*srcStep); + + S = ref; + R = (T*)alignPtr(border, VEC_ALIGN); + if ( inplace && (rc < (kheight-1)) ) + D = (T*)alignCore(inplaceDst, borderLeft*sizeof(T), inplace); + else + D = (T*)dst; + for( int k=0; k < kheight; k++ ) + buf_ptr[k] = (ST*)alignCore(&buf[((bbi+k)%kheight)*bufStep], + borderLeft*sizeof(ST), inplace); + + int widthcn = width*cn; + i=0; + if( inplace ) + { + BlockSumBorderInplace(ref, S, R, btab, width, cn, dx1, dx2, borderType); + BlockSumCore(R, SUM, D, buf_ptr, scale, widthcn, i, cn, kheight, kwidth); + } + else + { + if( dx1>0 ) + { + i = BlockSumBorder(ref, S, R, SUM, D, buf_ptr, btab, width, i, cn, + dx1, dx2, kheight, kwidth, true, borderLeft, borderType, scale); + S-=(dx1*cn); + } + i = BlockSumCore(S, SUM, D, buf_ptr, scale, widthcn-(dx2*cn), i, cn, kheight, kwidth); + if( i0 ) + { + int border_len_right = widthcn-i; + i = BlockSumBorder(ref, S, R, SUM, D, buf_ptr, btab, width, i, cn, + dx1, dx2, kheight, kwidth, false, border_len_right, borderType, scale); + } + } + + bbi++; bbi %= kheight; + if( rc >= (kheight-1) ) + dst += dstStep; + } +} + } // namespace anon @@ -1271,6 +1660,35 @@ Ptr createBoxFilter(int srcType, int dstType, Size ksize, srcType, dstType, sumType, borderType ); } +void blockSum(const Mat& src, Mat& dst, Size ksize, Point anchor, + const Size &wsz, const Point &ofs, bool normalize, int borderType) +{ + CV_INSTRUMENT_REGION(); + + int cn = CV_MAT_CN(src.type()), sumType = CV_64F; + sumType = CV_MAKETYPE( sumType, cn ); + + CV_Assert( CV_MAT_CN(src.type()) == CV_MAT_CN(dst.type()) ); + int sdepth = CV_MAT_DEPTH(sumType), ddepth = CV_MAT_DEPTH(dst.type()); + + if( anchor.x < 0 ) + anchor.x = ksize.width/2; + + if( anchor.y < 0 ) + anchor.y = ksize.height/2; + + double scale = normalize ? 1./(ksize.width*ksize.height) : 1; + bool inplace = src.data == dst.data; + + if( ddepth == CV_32F && sdepth == CV_64F ) + BlockSum (src, dst, ksize, anchor, wsz, ofs, scale, borderType, sumType, inplace); + else if( ddepth == CV_64F && sdepth == CV_64F ) + BlockSum (src, dst, ksize, anchor, wsz, ofs, scale, borderType, sumType, inplace); + else + CV_Error_( cv::Error::StsNotImplemented, + ("Unsupported combination of sum format (=%d), and destination format (=%d)", + sumType, dst.type())); +} /****************************************************************************************\ Squared Box Filter From b6447542269e5c7788f372affc68a594b7863f6b Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Mon, 6 Oct 2025 15:29:44 +0300 Subject: [PATCH 09/10] Merge pull request #27865 from MaximSmolskiy:fix_invalid_memory_access_in_usac Fix invalid memory access in USAC #27865 ### Pull Request Readiness Checklist Fix #27863 See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- modules/calib3d/src/usac/fundamental_solver.cpp | 4 ++-- modules/calib3d/src/usac/homography_solver.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/calib3d/src/usac/fundamental_solver.cpp b/modules/calib3d/src/usac/fundamental_solver.cpp index d2fb6d3d10..b3ef5b0fc0 100644 --- a/modules/calib3d/src/usac/fundamental_solver.cpp +++ b/modules/calib3d/src/usac/fundamental_solver.cpp @@ -385,7 +385,7 @@ public: Matx AtA_(AtA), U, Vt; Vec W; SVD::compute(AtA_, W, U, Vt, SVD::FULL_UV + SVD::MODIFY_A); - models = std::vector { Mat_(3, 3, Vt.val + 72 /*=8*9*/) }; + models = std::vector { Mat_(3, 3, Vt.val + 72 /*=8*9*/).clone() }; #endif } @@ -503,7 +503,7 @@ public: Matx AtA_(covariance), U, Vt; Vec W; SVD::compute(AtA_, W, U, Vt, SVD::FULL_UV + SVD::MODIFY_A); - models = std::vector { Mat_(3, 3, Vt.val + 72 /*=8*9*/) }; + models = std::vector { Mat_(3, 3, Vt.val + 72 /*=8*9*/).clone() }; #endif if (enforce_rank) FundamentalDegeneracy::recoverRank(models[0], is_fundamental); diff --git a/modules/calib3d/src/usac/homography_solver.cpp b/modules/calib3d/src/usac/homography_solver.cpp index 3be5f2dc20..fe528f59a6 100644 --- a/modules/calib3d/src/usac/homography_solver.cpp +++ b/modules/calib3d/src/usac/homography_solver.cpp @@ -253,7 +253,7 @@ public: Matx Vt; Vec D; if (! eigen(Matx(AtA), D, Vt)) return 0; - H = Mat_(3, 3, Vt.val + 72/*=8*9*/); + H = Mat_(3, 3, Vt.val + 72/*=8*9*/).clone(); #endif } const auto * const h = (double *) H.data; From 75598e5377af6edb6c080da6c2a4b437cc8f0675 Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Wed, 8 Oct 2025 08:50:54 +0300 Subject: [PATCH 10/10] Merge pull request #27877 from MaximSmolskiy:fix_QRCodeDetector_detectAndDecode_crash Fix QRCodeDetector::detectAndDecode crash #27877 ### Pull Request Readiness Checklist Fix #27807 The problem is that when we find closest points from hull, we can get same closest point for several different points See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- modules/objdetect/src/qrcode.cpp | 19 ++++++++++--------- modules/objdetect/test/test_qrcode.cpp | 2 +- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/modules/objdetect/src/qrcode.cpp b/modules/objdetect/src/qrcode.cpp index 8391aabf62..f79829eb7c 100644 --- a/modules/objdetect/src/qrcode.cpp +++ b/modules/objdetect/src/qrcode.cpp @@ -772,21 +772,27 @@ vector QRDetect::getQuadrilateral(vector angle_list) const double experimental_area = fabs(contourArea(hull)); vector result_hull_point(angle_size); + vector used_hull_point(hull_size, false); double min_norm; for (size_t i = 0; i < angle_size; i++) { min_norm = std::numeric_limits::max(); - Point closest_pnt; + int closest_pnt_idx = -1; for (int j = 0; j < hull_size; j++) { + if (used_hull_point[j]) + { + continue; + } double temp_norm = norm(hull[j] - angle_list[i]); if (min_norm > temp_norm) { min_norm = temp_norm; - closest_pnt = hull[j]; + closest_pnt_idx = j; } } - result_hull_point[i] = closest_pnt; + result_hull_point[i] = hull[closest_pnt_idx]; + used_hull_point[closest_pnt_idx] = true; } int start_line[2] = { 0, 0 }, finish_line[2] = { 0, 0 }, unstable_pnt = 0; @@ -2958,12 +2964,7 @@ std::string ImplContour::decode(InputArray in, InputArray points, OutputArray st vector src_points; points.copyTo(src_points); CV_Assert(src_points.size() == 4); - if (contourArea(src_points) <= 0.0) - { - if (straight_qrcode.needed()) - straight_qrcode.release(); - return std::string(); - } + CV_CheckGT(contourArea(src_points), 0.0, "Invalid QR code source points"); QRDecode qrdec(useAlignmentMarkers); qrdec.init(inarr, src_points); diff --git a/modules/objdetect/test/test_qrcode.cpp b/modules/objdetect/test/test_qrcode.cpp index 2912d39028..c49480d38f 100644 --- a/modules/objdetect/test/test_qrcode.cpp +++ b/modules/objdetect/test/test_qrcode.cpp @@ -470,7 +470,7 @@ TEST(Objdetect_QRCode_basic, not_found_qrcode) QRCodeDetector qrcode; EXPECT_FALSE(qrcode.detect(zero_image, corners)); corners = std::vector(4); - EXPECT_NO_THROW(qrcode.decode(zero_image, corners, straight_barcode)); + EXPECT_ANY_THROW(qrcode.decode(zero_image, corners, straight_barcode)); } TEST(Objdetect_QRCode_detect, detect_regression_21287)