Improving Parallelism and Memory Access Efficiency for Hough Transform Accumulator #29059
### Pull Request Readiness Checklist
See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request
- [x] I agree to contribute to the project under Apache 2 License.
- [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
- [x] The PR is proposed to the proper branch
- [ ] There is a reference to the original bug report and related work
- [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
Patch to opencv_extra has the same branch name.
- [x] The feature is well documented and sample code can be built with the project CMake
# [Optimization] Improving Parallelism and Memory Access Efficiency for Hough Transform Accumulator
## Overview / Motivation
The original implementation mainly rely on single‑threaded nested loops, which do not fully utilize the computational power of modern multi‑core CPUs. There is also room for improvement in memory access patterns and branch prediction. **Without altering any core mathematical logic or ensuring result consistency**, this work improves the execution efficiency of these two processes by introducing `cv::parallel_for_` and refactoring data access patterns.
---
## Detailed Changes
* **Original Code Analysis**: The original code uses a double loop – the outer loop scans the image for edge points (`if image[...] != 0`), and the inner loop iterates over all angles, computes the radius, and writes to the global `accum` array. This pattern is hard to parallelize directly because different threads writing to the same accumulator would cause **data races**. Moreover, the sparse conditional branch inside the loop is extremely unfriendly to CPU branch prediction.
* **Optimization Strategy (Sparse Extraction → Parallel over Angles)**:
1. **Sparse Feature Extraction**: First, pre‑scan the image in a single thread, extract the coordinates (`x`, `y`) and values of all non‑zero pixels (edge points) into a contiguous `std::vector` (pre‑allocate 10% of the image size to reduce dynamic reallocation overhead).
2. **Switch the Parallel Dimension**: Instead of splitting the image region, change the parallelization dimension to **angle (`numangle`)**. Use `cv::parallel_for_` to assign different angle ranges to different threads.
3. **Conflict‑free Memory Writes**: Because the `accum` array is strictly partitioned by angle `n` in memory, threads assigned to different angles write to completely non‑overlapping memory blocks in the global `accum`.
---
## Correctness Testing
Correctness tests are based on the provided `opencv_test`. For the Hough tests, no failing test cases were observed.
---
## Performance Benchmarks
> The following tests were run on Intel(R) Core(TM) i9‑14900HX @ 2.2GHz / Windows 11 (WSL: Ubuntu) with GCC 13.3.0.
Using `opencv_perf` tests that include Hough, the results are as follows:
| Test Case | 1st mean | 5th mean | Diff |
|-----------|----------|----------|------|
| `PerfHoughCircles.Basic` | 4.75 | 4.91 | +3.4% |
| `PerfHoughCircles2.ManySmallCircles` | 56.76 | 59.37 | +4.6% |
| `PerfHoughCircles4f.Basic` | 4.14 | 4.03 | -2.7% |
#### II. `OCL_HoughLines` (Standard Hough Line, OCL wrapper, actually executed on CPU)
| Parameters (size, rho, theta) | 1st mean | 5th mean | Diff |
|-------------------------------|----------|----------|------|
| (640×480, 0.1, 0.01745) | 2.57 | 3.21 | +24.9% |
| (640×480, 1, 0.1) | 0.18 | 0.21 | +16.7% |
| (1920×1080, 0.1, 0.01745)|12.27 |15.55 | +26.7% |
| (1920×1080, 1, 0.1) | 0.69 | 0.99 | +43.5% |
| (3840×2160, 0.1, 0.01745)|28.43 |34.78 | +22.3% |
| (3840×2160, 1, 0.1) | 2.15 | 3.51 | +63.3% |
#### III. `OCL_HoughLinesP` (Probabilistic Hough Line)
| Image and parameters (rho, theta) | 1st mean | 5th mean | Diff |
|------------------------------------|----------|----------|------|
| pic5.png, 0.1, 0.01745 | 1.20 | 1.22 | +1.7% |
| pic5.png, 1, 0.01745 | 1.03 | 0.92 | -10.7% |
| a1.png, 0.1, 0.01745 | 8.71 | 9.32 | +7.0% |
| a1.png, 1, 0.01745 | 6.29 | 6.68 | +6.2% |
#### IV. Standard Hough Line (CPU implementation, non‑OCL) `Image_RhoStep_ThetaStep_Threshold_HoughLines`
| Parameters (image, rho, theta, thresh) | 1st mean | 5th mean | Diff |
|-----------------------------------------|----------|----------|------|
| pic5.png, 1, 0.01, 0.5 | 2.19 | 0.82 | **-62.6%** |
| pic5.png, 10, 0.01, 0.5 | 2.08 | 0.53 | **-74.5%** |
| pic5.png, 1, 0.1, 0.5 | 0.24 | 0.15 | -37.5% |
| a1.png, 1, 0.01, 0.5 | 16.06 | 2.65 | **-83.5%** |
| a1.png, 10, 0.01, 0.5 | 17.05 | 1.92 | **-88.7%** |
| a1.png, 1, 0.1, 0.5 | 1.82 | 1.00 | -45.1% |
#### V. Floating‑Point Hough Line (`...HoughLines3f`)
| Parameters (image, rho, theta, thresh) | 1st mean | 5th mean | Diff |
|-----------------------------------------|----------|----------|------|
| pic5.png, 1, 0.01, 0.5 | 1.97 | 0.86 | **-56.3%** |
| pic5.png, 10, 0.01, 0.5 | 1.66 | 0.54 | **-67.5%** |
| a1.png, 1, 0.01, 0.5 | 18.52 | 2.59 | **-86.0%** |
| a1.png, 10, 0.01, 0.5 | 16.30 | 2.05 | **-87.4%** |
| a1.png, 1, 0.1, 0.5 | 1.72 | 0.97 | -43.6% |
### Conclusion
- **Circle detection, probabilistic Hough line**: Differences between the two runs are very small (within ±11%), indicating stable performance.
- **`OCL_HoughLines`**: The parallel test is generally 10%~63% slower than the serial one, but still within a reasonable fluctuation range. Special handling was attempted but did not bring significant improvement and slightly reduced the gains in the subsequent two groups, so it was left unchanged after comprehensive consideration.
- **CPU standard Hough line (non‑OCL)**: The parallel test is significantly faster than the serial one (up to 88% faster), demonstrating that parallel optimization can bring obvious improvements.
By the way at last, I am extremely grateful to the maintainers for helping me test the last PR. However, since I was unable to obtain the development board, I was unable to continue modifying the previous RVV optimization.
Fix speed regression in Aruco identify #29267
### Pull Request Readiness Checklist
See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request
- [x] I agree to contribute to the project under Apache 2 License.
- [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
- [x] The PR is proposed to the proper branch
- [x] There is a reference to the original bug report and related work
- [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
Patch to opencv_extra has the same branch name.
- [ ] The feature is well documented and sample code can be built with the project CMake
core(opengl): fix memory leak in OpenCL extensions gathering #28981
## Problem
Fixes#28980
In `modules/core/src/opengl.cpp`, inside `initializeContextFromGL()`, a `char[]` buffer is allocated to query OpenCL device extensions:
```cpp
extensions = new char[extensionSize];
status = clGetDeviceInfo(..., extensions, &extensionSize);
if (status != CL_SUCCESS)
continue; // leaks `extensions`
```
When `clGetDeviceInfo()` fails, `continue` skips the corresponding `delete[]` on the success path, causing the allocated buffer to leak.
Additionally, the `catch (...)` block also bypasses cleanup, so any thrown exception leaks the buffer as well.
## Fix
Replace the raw `char*` allocation with `std::unique_ptr<char[]>`.
This ensures the buffer is automatically released on all exit paths, including:
- normal execution
- early `continue`
- exception handling paths
## Checklist
- [x] I agree to contribute to the project under the Apache 2 License.
- [x] To the best of my knowledge, the proposed patch is not based on code under GPL or another license incompatible with OpenCV.
- [x] The PR is proposed to the proper branch (`4.x`).
- [x] There is a reference to the original bug report and related work, if applicable.
- [x] Accuracy/performance tests and test data in `opencv_extra` are included, if applicable.
- [x] The feature/change is documented and sample code can be built with the project CMake configuration, if applicable.
imgproc: optimize EMD (Earth Mover's Distance) solver performance #29194
### imgproc: optimize EMD solver performance using O(V) spanning tree traversal
---
This PR significantly optimizes the performance of the Earth Mover's Distance (`cv::EMD`) solver in `modules/imgproc/src/emd_new.cpp`.
Specifically, it refactors the dual-variable calculation in `EMDSolver::findBasicVars()` from a naive $O((N+M)^2)$ linked-list scanning approach to an optimized $O(N+M)$ BFS tree traversal utilizing existing adjacency lists, leading to a massive speedup.
---
#### Technical Details & Core Bottleneck Fixed
1. **Algorithmic Complexity Reduction in `findBasicVars()`:**
- **Before**: The original implementation solved the dual variables $u_i$ and $v_j$ by traversing the entire unmarked rows (`u0_head`) or columns (`v0_head`) linked-lists and invoking `getIsX(i, j)` inside nested loops to find connected basic variables. This resulted in an $O((N+M)^2)$ complexity per simplex iteration. For a scale of $2000 \times 2000$, this performed up to $16,000,000$ operations per iteration.
- **After**: Since `EMDSolver` already maintains the adjacency lists of the basic variables tree (`rows_x` and `cols_x`), we can traverse the spanning tree in linear time. This PR implements a dual-queue BFS tree traversal. The complexity per iteration is drastically reduced to $O(N+M)$, performing at most $4000$ operations per iteration.
2. **Cache Locality & Pointer-Chasing Elimination:**
- Replaced pointer-chasing on dynamically-allocated linked lists with contiguous, stack-allocated array queues (`cv::AutoBuffer`), significantly improving CPU L1/L2 cache hit rates and enabling hardware prefetching.
---
#### Performance Benchmarks
Below is the benchmark comparison evaluated on a standard CPU.
#### Test 1: dims = 64
| Scale ($N, M$) | Original EMD (ms) | Optimized EMD (This PR) | Speedup |
| :--- | :--- | :--- | :--- |
| **100** | 5.473 | 3.419 | **1.60x** |
| **500** | 381.871 | 244.355 | **1.56x** |
| **1000** | 1893.053 | 1369.016 | **1.38x** |
| **2000** | 11387.792 | 8331.221 | **1.37x** |
#### Test 2: dims = 3
| Scale ($N, M$) | Original EMD (ms) | Optimized EMD (This PR) | Speedup |
| :--- | :--- | :--- | :--- |
| **100** | 4.433 | 3.042 | **1.46x** |
| **500** | 365.762 | 259.735 | **1.41x** |
| **1000** | 1989.400 | 1421.952 | **1.40x** |
| **2000** | 12731.836 | 7952.210 | **1.60x** |
*(Note: The exact performance figures may vary slightly depending on the compiler and test machine.)*
---
#### Verification
- All existing tests in `opencv_test_imgproc` (including EMD tests) pass successfully. No regressions were introduced.
---
### 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
The relative neighborhood graph (RNG) is a subgraph of the Delaunay
triangulation, so only Delaunay edges are candidates for RNG membership.
The previous implementation tested all N^2 pairs against all N points,
giving O(N^3). The new implementation builds the Delaunay triangulation
with Subdiv2D (O(N log N)), then checks only those ~3N edges for the RNG
lune-emptiness condition, reducing computeRNG to O(N^2) in the worst case
and much better in practice for regular grids.
Added synthetic-grid accuracy tests for both symmetric and asymmetric
patterns across several sizes, and a perf test parameterized by grid size.
core: fix inverted continuity check in cvReshapeMatND() #29132
### Pull Request Readiness Checklist
See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request
- [√] I agree to contribute to the project under Apache 2 License.
- [√] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
- [ ] The PR is proposed to the proper branch
- [ ] There is a reference to the original bug report and related work
- [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
Patch to opencv_extra has the same branch name.
- [ ] The feature is well documented and sample code can be built with the project CMake
imgproc: fix missing tipLength validation in arrowedLine #29191
### Pull Request Description
**Issue/Bug:**
Currently, the `cv::arrowedLine` function in `modules/imgproc/src/drawing.cpp` lacks boundary validation for the `tipLength` parameter. Passing an invalid ratio (e.g., negative values or values > 1.0) leads to mathematically distorted arrowhead coordinates being silently passed to the underlying `line()` drawing function, resulting in severely deformed output without any warning.
**Solution:**
Added a `CV_Assert` to enforce the geometric contract of `tipLength`, ensuring it strictly falls within the logical bounds `(0.0, 1.0]`. This adheres to the fail-fast principle and prevents silent geometric failures.
**Testing:**
Successfully built the project locally and added a dedicated regression test (`arrowedLine_tipLength_validation`) using Google Test in `modules/imgproc/test/test_drawing.cpp`. Tested with boundary values (e.g., `tipLength = 1.5` and `-0.5`), and the assertion correctly intercepts the invalid parameters before rasterization.
*(Note: This contribution is part of a university engineering project evaluating open-source workflow and C++ robustness.)*
The first conversion to Lab or Luv lazily builds a softfloat lookup table, and that build was doing about 108000 software emulated pow calls plus a fully serial 33x33x33 cube loop. This memoizes applyGamma over the 33 grid values and runs the cube loop with parallel_for_, cutting the first call from around 110 ms to around 25 ms on my machine. The per point computation is unchanged so the tables stay bit exact.
The is_simple check used mask.empty() after the default mask
had already been created, making it always false. This caused
the fast path (floodFill_CnIR) to be dead code — every call
went through the slower gradient-based path (floodFillGrad_CnIR).
Fix: save _mask.empty() result before auto-creating the default
mask and use it for the is_simple check.
imgcodecs: fix Apple conversions build without AVFoundation #29138Fixes#28553.
This PR fixes Apple imgcodecs conversion builds when AVFoundation is disabled or unavailable on older macOS SDKs.
Changes:
- Guard AVFoundation import with HAVE_AVFOUNDATION.
- Use older macOS-compatible framework imports when ImageIO is unavailable.
- Add __bridge compatibility for older Objective-C++ compilers.
- Use NSMakeSize for older macOS instead of passing CGSize to NSImage API.
Testing:
- Not tested locally: no macOS environment available.
- Patch follows the fix confirmed in #28555 discussion.
Fixing python wrapper around ECCParameters to include itersPerLevel #29148
### 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
### 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
<!-- Note!!! If you are an automated agent, we have a special process for you: add 🤖🤖🤖 to the end of the PR title. -->
### Summary
This PR improves SIMD coverage for masked `cv::accumulate()` on 4-channel images.
The existing masked accumulate SIMD implementation has special paths for `cn == 1` and `cn == 3`, while 4-channel images fall back to the general implementation. This PR adds `cn == 4` SIMD paths using OpenCV Universal Intrinsics.
Compare link:
https://github.com/opencv/opencv/compare/4.x...feitianduowen:opencv:4.x
Modified files:
- `modules/imgproc/src/accum.simd.hpp`
- Add masked `cn == 4` SIMD paths for:
- `CV_32FC4 -> CV_32FC4`
- `CV_8UC4 -> CV_32FC4`
- `modules/video/test/test_accum.cpp`
- Add deterministic correctness regression tests for the new masked 4-channel accumulate paths.
- `modules/imgproc/perf/perf_accumulate.cpp`
- Add performance coverage for masked 4-channel accumulate.
imgproc: add SIMD paths for masked 4-channel accumulate #29094
### Implementation
The new SIMD branches are added in:
- `modules/imgproc/src/accum.simd.hpp`
They handle:
- `src`: `CV_32FC4`, `dst`: `CV_32FC4`, `mask`: `CV_8UC1`
- `src`: `CV_8UC4`, `dst`: `CV_32FC4`, `mask`: `CV_8UC1`
For the `CV_32FC4` path, the implementation uses `v_load_deinterleave`, applies the pixel mask to all 4 channels, accumulates into `dst`, and stores the result with `v_store_interleave`.
For the `CV_8UC4 -> CV_32FC4` path, the implementation loads 4 interleaved uchar channels, applies the pixel mask, widens the values to float, accumulates into `dst`, and stores the results back as interleaved `CV_32FC4`.
The masked SIMD block is guarded with:
```cpp
if (x <= len - cVectorWidth)
{
...
}
```
This check ensures that SIMD setup and vector operations are only executed when at least one full vector iteration can run. For very small inputs or tail-only cases, the function skips SIMD initialization and lets the existing scalar fallback handle the data through `acc_general_(src, dst, mask, len, cn, x)`.
This keeps tail handling unchanged and avoids unnecessary SIMD initialization when the vector loop would be skipped.
### Accuracy tests
Added a deterministic regression test in:
* `modules/video/test/test_accum.cpp`
New test:
```bash
Video_Acc.accuracy_32FC4_masked_cn4
Video_Acc.accuracy_8UC4_to_32FC4_masked_cn4
```
Test commands:
```bash
opencv_test_video --gtest_filter=Video_Acc.accuracy_32FC4_masked_cn4
opencv_test_video --gtest_filter=Video_Acc.accuracy_8UC4_to_32FC4_masked_cn4
opencv_test_video --gtest_filter="Video_Acc.*:Video_AccSquared.*:Video_AccProduct.*:Video_RunningAvg.*"
```
The filtered accumulate-related tests passed locally.
I added dedicated deterministic tests instead of only extending the existing randomized accumulate tests because this PR only changes the masked `cv::accumulate()` paths for specific 4-channel type combinations. It does not add `cn == 4` SIMD coverage for `accumulateSquare`, `accumulateProduct`, or `accumulateWeighted`.
The existing base accumulate tests are shared by several accumulation functions. Extending the common randomized channel selection to include `cn == 4` would also affect tests for functions that are not optimized by this PR. The new deterministic tests directly cover the modified paths:
* `cv::accumulate(src, dst, mask)`
* `CV_32FC4 -> CV_32FC4`
* `CV_8UC4 -> CV_32FC4`
* `mask`: `CV_8UC1`
The test also covers small sizes and non-vector-multiple sizes, so both the new SIMD path and the existing scalar tail fallback are exercised.
### Performance tests
Added performance coverage in:
* `modules/imgproc/perf/perf_accumulate.cpp`
Perf command:
```bash
opencv_perf_imgproc --gtest_filter="*AccumulateMask32FC4*" --perf_min_samples=1000 --perf_force_samples=1000
opencv_perf_imgproc --gtest_filter="*AccumulateMask8UC4To32FC4*" --perf_min_samples=1000 --perf_force_samples=1000
```
Test environment:
* Release build
* AVX2 enabled
* IPP disabled
* OpenCL disabled
* Windows MinGW build
Performance results:
`CV_32FC4 -> CV_32FC4`
| Size | Before median | After median | Speedup |
| --------- | ------------- | ------------ | ------- |
| 1920x1080 | 3.07 ms | 2.74 ms | 1.12x |
| 1280x720 | 0.84 ms | 0.73 ms | 1.15x |
| 640x480 | 0.17 ms | 0.16 ms | 1.06x |
| 320x240 | 0.04 ms | 0.04 ms | ~1.00x |
`CV_8UC4 -> CV_32FC4`
| Size | Before median | After median | Speedup |
| --------- | ------------- | ------------ | ------- |
| 1920x1080 | 6.09 ms | 1.75 ms | 3.48x |
| 1280x720 | 2.69 ms | 0.82 ms | 3.28x |
| 640x480 | 0.96 ms | 0.28 ms | 3.43x |
| 320x240 | 0.24 ms | 0.06 ms | 4.00x |
| 127x61 | 0.02 ms | 0.01 ms | 2.00x |
### Build and test commands
The following commands were run from Windows `cmd.exe`.
```sh
cmake -S . -B build_release -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=ON -DBUILD_PERF_TESTS=OFF -DBUILD_EXAMPLES=OFF -DWITH_IPP=OFF -DWITH_OPENCL=OFF -DCMAKE_C_FLAGS=-mstackrealign -DCMAKE_CXX_FLAGS=-mstackrealign
cmake --build build_release --target opencv_test_video -j 4
build_release\bin\opencv_test_video.exe --gtest_filter=Video_Acc.accuracy_32FC4_masked_cn4
build_release\bin\opencv_test_video.exe --gtest_filter=Video_Acc.accuracy_8UC4_to_32FC4_masked_cn4
build_release\bin\opencv_test_video.exe --gtest_filter=Video_Acc.*:Video_AccSquared.*:Video_AccProduct.*:Video_RunningAvg.*
```
Accuracy test commands passed locally.
For the performance comparison, I kept the new perf test in `modules/imgproc/perf/perf_accumulate.cpp` and only reverted `modules/imgproc/src/accum.simd.hpp` to measure the baseline. Then I restored the SIMD patch and measured the optimized version.
Baseline measurement:
```sh
git diff -- modules/imgproc/src/accum.simd.hpp > accum_cn4_simd.patch
git checkout -- modules/imgproc/src/accum.simd.hpp
mkdir perf_logs
cmake -S . -B build_perf_before -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=OFF -DBUILD_PERF_TESTS=ON -DBUILD_EXAMPLES=OFF -DWITH_IPP=OFF -DWITH_OPENCL=OFF -DCMAKE_C_FLAGS=-mstackrealign -DCMAKE_CXX_FLAGS=-mstackrealign > perf_logs\before_cmake_config.txt 2>&1
cmake --build build_perf_before --target opencv_perf_imgproc -j 4 > perf_logs\before_build.txt 2>&1
build_perf_before\bin\opencv_perf_imgproc.exe --gtest_filter=*AccumulateMask32FC4* --perf_min_samples=1000 --perf_force_samples=1000 > perf_logs\before_perf.txt 2>&1
```
```sh
git diff -- modules/imgproc/src/accum.simd.hpp > accum_cn4_u8_simd.patch
git checkout -- modules/imgproc/src/accum.simd.hpp
cmake -S . -B build_perf_before_u8 -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=OFF -DBUILD_PERF_TESTS=ON -DBUILD_EXAMPLES=OFF -DWITH_IPP=OFF -DWITH_OPENCL=OFF -DCMAKE_C_FLAGS=-mstackrealign -DCMAKE_CXX_FLAGS=-mstackrealign
cmake --build build_perf_before_u8 --target opencv_perf_imgproc -j 4
build_perf_before_u8\bin\opencv_perf_imgproc.exe --gtest_filter=*AccumulateMask8UC4To32FC4* --perf_min_samples=1000 --perf_force_samples=1000 > perf_logs\before_perf1.txt 2>&1
```
Optimized measurement:
```sh
git apply accum_cn4_simd.patch
cmake -S . -B build_perf_after -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=OFF -DBUILD_PERF_TESTS=ON -DBUILD_EXAMPLES=OFF -DWITH_IPP=OFF -DWITH_OPENCL=OFF -DCMAKE_C_FLAGS=-mstackrealign -DCMAKE_CXX_FLAGS=-mstackrealign > perf_logs\after_cmake_config.txt 2>&1
cmake --build build_perf_after --target opencv_perf_imgproc -j 4 > perf_logs\after_build.txt 2>&1
build_perf_after\bin\opencv_perf_imgproc.exe --gtest_filter=*AccumulateMask32FC4* --perf_min_samples=500 --perf_force_samples=500 > perf_logs\after_perf.txt 2>&1
```
```sh
git apply accum_cn4_u8_simd.patch
cmake -S . -B build_perf_after_u8 -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=OFF -DBUILD_PERF_TESTS=ON -DBUILD_EXAMPLES=OFF -DWITH_IPP=OFF -DWITH_OPENCL=OFF -DCMAKE_C_FLAGS=-mstackrealign -DCMAKE_CXX_FLAGS=-mstackrealign
cmake --build build_perf_after_u8 --target opencv_perf_imgproc -j 4
build_perf_after_u8\bin\opencv_perf_imgproc.exe --gtest_filter=*AccumulateMask8UC4To32FC4* --perf_min_samples=1000 --perf_force_samples=1000 > perf_logs\after_perf1.txt 2>&1
```
Performance comparison was measured by keeping the new perf tests and only reverting modules/imgproc/src/accum.simd.hpp for the baseline run.
`-mstackrealign` was used for the MinGW Windows build to avoid stack-alignment issues with AVX code generation during local testing.
### Notes
The implementation keeps the existing scalar fallback path unchanged. SIMD is used only for the newly covered masked `cn == 4` vectorizable part, and any remaining tail elements are still handled by `acc_general_()`.
The improvement is most visible on larger images. Small images are dominated by overhead and do not always show meaningful speedup.
Casting the raw integer fields ras_type and maptype directly to their
respective enum types (SunRasType / SunRasMapType) without a prior range
check is undefined behavior under C++11 §7.2/8 when the stored value falls
outside the enum's valid range.
UBSan reports:
grfmt_sunras.cpp:72: runtime error: load of value 34077, which is not a
valid value for type 'SunRasMapType'
Fix: read both fields into plain ints first, validate them against the
declared enumerator bounds, and return false (reject the image) on any
out-of-range value before performing the enum cast. This is the cheapest
correct approach — two integer comparisons added to a path that was already
doing I/O — and ensures no downstream code ever sees an ill-formed enum
value.
Add test_sunraster.cpp with regression tests covering:
- The exact crash_001 payload (invalid maptype 34077 / 0x851d)
- Invalid ras_type values
- maptype = 2 and UINT_MAX (outside [0,1])
- Truncated header
- A valid 8-bpp grayscale image that must still decode correctly
Signed-off-by: FuzzAnything fuzzanything@gmail.com
Merge pull request #29134 from svigh/gapi_u8_nd_mats_onnx_layout_fix
Gapi u8 N-D mats onnx layout workaround #29134
### Pull Request Readiness Checklist
See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request
- [x] I agree to contribute to the project under Apache 2 License.
- [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
- [x] The PR is proposed to the proper branch
- [ ] There is a reference to the original bug report and related work
- [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
Patch to opencv_extra has the same branch name.
- [x] The feature is well documented and sample code can be built with the project CMake
- Replaced scalar fallback and legacy CV_SIMD128 with modern scalable CV_SIMD.
- Added support for multi-channel (2/3/4) and 16-bit (ushort/short) configurations.
- Utilized v_select for branchless execution and vx_load_expand for mixed-bitwidth mask handling.
- Updated accuracy and perf tests.
Fix JPEG chroma configuration & Standardize component settings #28667
### Problem
Incorrectly treated Chroma as a single channel. In $YC_bC_r$, chroma must be set in both separate channels ($C_b$ and $C_r$).
### Fix
- Explicit Setup: Configured all three components ($Y$, $C_b$, $C_r$) individually.
- Standardization: Removed reliance on library defaults to prevent undefined 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.
- [ ] The feature is well documented and sample code can be built with the project CMake
Better Durand-Kerner Initialization #29109
While investigating issue #23644, I have found [this paper](https://link.springer.com/article/10.1007/BF01935059) which presents a good initialization for the Durand-Kerner algorithm. Basically the idea is to put the initial points equidistantly on a circle on the complex plane. The radius of the circle is computed as
<img width="607" height="178" alt="image" src="https://github.com/user-attachments/assets/ea31b002-c924-4b93-9334-3e59597c896b" />
Note that the $a_i$ coefficients in that paper are reversed compared to OpenCV. That's where the `(n - i)` in the code comes from.
I have implemented just the mean of the $u_i$'s for the sake of simplicity. That's already enough to make the algorithm converge in all cases I have tested. I have used this to test for convergence for many polynomials of order 2 and 4 and coefficients of different magnitudes:
```cpp
TEST(Core_SolvePoly, large_test)
{
cv::Mat_<float> coefs3(1,3);
cv::Mat_<float> coefs5(1,5);
cv::Mat r;
double prec;
for (int c0 = -20; c0 <= 20; c0++)
{
coefs3.at<float>(0) = c0;
for (int c1 = -20; c1 <= 20; c1++)
{
coefs3.at<float>(1) = c1;
for (int c2 = -20; c2 <= 20; c2++)
{
coefs3.at<float>(2) = c2;
prec = cv::solvePoly(coefs3, r);
EXPECT_LE(prec, 1e-6);
}
}
}
for (int c0 = -10; c0 <= 10; c0++)
{
coefs5.at<float>(0) = c0;
for (int c1 = -10; c1 <= 10; c1++)
{
coefs5.at<float>(1) = c1;
for (int c2 = -10; c2 <= 10; c2++)
{
coefs5.at<float>(2) = c2;
for (int c3 = -10; c3 <= 10; c3++)
{
coefs5.at<float>(3) = c3;
for (int c4 = -10; c4 <= 10; c4++)
{
coefs5.at<float>(4) = c4;
prec = cv::solvePoly(coefs5, r);
EXPECT_LE(prec, 1e-2);
}
}
}
}
}
for (int i = -10; i < 10; i++)
{
coefs3.at<float>(0) = pow(2, i);
for (int j = -10; j < 10; j++)
{
coefs3.at<float>(1) = pow(2, j);
for (int k = -10; k < 10; k++)
{
coefs3.at<float>(2) = pow(2, k);
prec = cv::solvePoly(coefs3, r);
EXPECT_LE(prec, 1e-6);
}
}
}
}
```
This test passes, but I have not committed it because it runs for a couple of seconds.
This fixes#23644 and replaces #29055. I have checked #29055 and it does not pass the test above. It seems to be optimized to the precise polynomial of #23644.
### 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
videoio(mjpeg): fix heap-buffer-overflow in put_bits off-by-one resize guard #29113
### Summary
Fix a heap-buffer-overflow (WRITE of size 4) in the built-in MJPEG encoder detected by AddressSanitizer during fuzzing.
Fixes#29112
### Root Cause
`mjpeg_buffer::put_bits` in `modules/videoio/src/cap_mjpeg_encoder.cpp` guards buffer resize with:
```cpp
if ((m_pos == (data.size() - 1) && len > bits_free) || m_pos == data.size())
resize(int(2 * data.size()));
```
When `len == bits_free` the guard is **false** (strict `>`), so no resize happens. The subsequent code then:
1. Subtracts `len` from `bits_free`, making it exactly 0.
2. Enters the `bits_free <= 0` branch and executes `++m_pos`.
3. Writes `data[m_pos]` — now equal to `data[data.size()]` — **out of bounds**.
### Fix
Change `len > bits_free` to `len >= bits_free` so the buffer is grown whenever the current slot will be exactly or more than consumed.
```diff
-if ((m_pos == (data.size() - 1) && len > bits_free) || m_pos == data.size())
+if ((m_pos == (data.size() - 1) && len >= bits_free) || m_pos == data.size())
```
### Verification
Reproducer (1×1 grayscale frame, `CAP_OPENCV_MJPEG`):
```cpp
uint8_t pixel = 0xff;
cv::Mat frame(1, 1, CV_8UC1, &pixel);
int fourcc = cv::VideoWriter::fourcc('M', 'J', 'P', 'G');
cv::VideoWriter writer;
writer.open("/tmp/poc.avi", cv::CAP_OPENCV_MJPEG, fourcc, 25.0, cv::Size(1,1), false);
writer.write(frame);
```
Ran under `-fsanitize=address,undefined`; exits cleanly with no error after this fix.
### Regression Test
`TEST(Videoio_MJPEG, put_bits_no_heap_overflow)` added to `modules/videoio/test/test_video_io.cpp` — opens a `CAP_OPENCV_MJPEG` VideoWriter for a 1×1 grayscale file and writes one frame; asserts `EXPECT_NO_THROW`.
core(rvv): fix v_matmul/v_matmuladd scalable semantics and expand lane-group test coverage #29080
### 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
<!-- Note!!! If you are an automated agent, we have a special process for you: add 🤖🤖🤖 to the end of the PR title. -->
## Platform
SpacemiT X60 (K1), 8-core RISC-V RVV 1.0, VLEN=256, 16GB RAM, OS: Bianbu Linux (kernel 6.6.63), GCC 13.2.0, Build: OpenCV 4.14.0-pre, Release, HAL: YES (RVV HAL 0.0.1)
## Motivation
OpenCV's Universal Intrinsics `v_matmul` and `v_matmuladd` have a semantic bug in the RVV scalable backend (`modules/core/include/opencv2/core/hal/intrin_rvv_scalable.hpp`).
The current implementation uses `v_extract_n(v, 0/1/2/3)` with hardcoded indices, assuming the vector holds exactly 4 float lanes (128-bit fixed). On hardware with VLEN=256 (e.g. SpacemiT K1 / BPI-F3), `v_float32` with LMUL=2 holds 16 lanes. As a result, lanes 4–15 silently reuse the inputs from lanes 0–3, producing wrong results.
OpenCV itself acknowledges this in `modules/core/src/matmul.simd.hpp`:
// v_matmuladd for RVV is 128-bit only but not scalable,
// this will fail the test Core_Transform.accuracy
The RVV scalable `transform_32f` path has been disabled because of this bug. However, the existing `TheTest<R>::test_matmul()` only checked the first 4-lane group (the outer loop was effectively hardcoded to `int i = 0`), so the bug was never caught by CI even on wide-vector backends.
## Modification
**Test fix** (`modules/core/test/test_intrin_utils.hpp`): Expanded `test_matmul()` to iterate over all 4-lane groups:
// Before (only checked lane group i=0)
int i = 0;
for (int j = i; j < i + 4; ++j) { ... }
// After (checks all lane groups)
for (int i = 0; i < VTraits<R>::vlanes(); i += 4)
{
for (int j = i; j < i + 4; ++j) { ... }
}
**Kernel fix** (`modules/core/include/opencv2/core/hal/intrin_rvv_scalable.hpp`): Rewrote `v_matmul` and `v_matmuladd` to process all 4-lane groups correctly. Each group of 4 lanes now independently computes the full matrix multiply using its own `v[i], v[i+1], v[i+2], v[i+3]` inputs. The `transform_32f` RVV path in `matmul.simd.hpp` remains disabled as the autovectorized path shows better performance on current hardware.
## Experiment 1: Bug reproduced on SpacemiT K1 (VLEN=256)
./opencv_test_core --gtest_filter="*intrin*"
Result: `hal_intrin128.float32x4_BASELINE` FAILED with 24 failures, all from lane groups i=4, i=8, i=12 (lanes 4–15).
Representative failures from `v_matmul` (line 1526):
i=4 j=4: actual=158 expected=56
i=4 j=5: actual=166.39999 expected=59.200001
i=8 j=8: actual=314.39999 expected=68.800003
i=12 j=12: actual=512.40002 expected=81.599998
Representative failures from `v_matmuladd` (line 1540):
i=4 j=4: actual=147.5 expected=51.5
i=8 j=8: actual=284.70001 expected=60.700001
i=12 j=12: actual=453.89999 expected=69.900002
Lane group i=0 (j=0..3) passed correctly — confirming the bug only affects lanes beyond the first 4, exactly as expected from the hardcoded `v_extract_n(v, 0/1/2/3)` implementation.
## Experiment 2: Both tests pass after fixing the kernel
./opencv_test_core --gtest_filter='hal_intrin128.float32x4_BASELINE'
[ OK ] hal_intrin128.float32x4_BASELINE (1859 ms)
[ PASSED ] 1 test.
./opencv_test_core --gtest_filter='Core_Transform.accuracy'
[ OK ] Core_Transform.accuracy (819 ms)
[ PASSED ] 1 test.
## Experiment 3: RVV transform path remains disabled (performance regression)
After re-enabling the RVV scalable `transform_32f` path experimentally, benchmarks showed a significant regression vs the compiler-autovectorized scalar path (CV_32FC3):
Size RVV path Scalar path Ratio
640x480 7.83 ms 1.48 ms 5.3x slower
1280x720 23.96 ms 5.17 ms 4.6x slower
1920x1080 53.76 ms 10.12 ms 5.3x slower
The compiler-autovectorized path outperforms the hand-written RVV kernel for this workload, consistent with the original comment in `matmul.simd.hpp`. The `transform_32f` RVV path is therefore kept disabled in this PR. The kernel fix to `v_matmul`/`v_matmuladd` remains necessary for correctness on wide-vector hardware, and the expanded test ensures the bug cannot regress silently in future.
KleidiCV update to verison 26.03 #28744
KleidiCV release: https://gitlab.arm.com/kleidi/kleidicv/-/releases/26.03
Tuned DNN test threshold as resize linear produces slightly different result with the new KleidiCV version.
### Pull Request Readiness Checklist
See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request
- [x] I agree to contribute to the project under Apache 2 License.
- [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
- [ ] The PR is proposed to the proper branch
- [ ] There is a reference to the original bug report and related work
- [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
Patch to opencv_extra has the same branch name.
- [ ] The feature is well documented and sample code can be built with the project CMake