1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-21 19:33:03 +04:00

Compare commits

...

52 Commits

Author SHA1 Message Date
Pratham Kumar 837f715eec Merge pull request #29283 from pratham-mcw:exp-neon-opt
Restricting the condition with _M_IX86/_M_X64 so it only applies to x86/x64 MSVC builds. MSVC ARM64 now falls through to the existing `#else` branch, which already has a portable CV_SIMD-based exp32f/exp64f implementation

**Performance Benchmarks:**
<img width="976" height="486" alt="image" src="https://github.com/user-attachments/assets/62daf2c3-34ac-4fc7-92d0-268073f746f3" />

- [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
2026-06-17 11:50:58 +03:00
Pierre Chatelier 6eb0dc97f5 Merge pull request #28907 from chacha21:more_autobuffer
More use of AutoBuffer #28907

When possible, AutoBuffer should be faster than std::vector<>, and should not be worse if it requires a heap allocation rather than a stack allocation.

### 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
2026-06-16 18:51:37 +03:00
Alexander Smorkalov 3def56d25a Merge pull request #29096 from Rishiii57:fix/matchtemplate-sqdiff-precision-21786
imgproc: fix matchTemplate TM_SQDIFF precision loss for CV_8U images
2026-06-16 13:52:16 +03:00
Alexander Smorkalov 62bcd12f9c Merge pull request #29305 from uwezkhan:darknet-cfg-index-bound
bound darknet cfg layer and anchor indices before vector reads
2026-06-16 12:24:35 +03:00
熊阔豪 6a2d8d24da Merge pull request #29145 from MrBear999-ui:fix-remap-nearest-rounding
Fix remap nearest rounding #29145

Thanks, I checked the Windows x64 test logs. The imgproc failures are from the OpenCL `Remap_INTER_NEAREST` tests with `(16SC2, 16UC1)` maps. I missed the OpenCL fixed-point path in the first patch.

I updated `modules/imgproc/src/opencl/remap.cl` to use the same nearest rounding condition as the CPU path. This should address the OCL remap failures.

The `opencv_test_video` failure seems unrelated to this PR (`ECCfixtures/Video_ECC.accuracy/6` invalid memory access), since this PR only touches remap rounding in imgproc.

### 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
2026-06-16 12:22:44 +03:00
Alexander Smorkalov d878b04c6b Merge pull request #29307 from StephanTLavavej:angle-brackets
Consistently include `<math.h>` with angle brackets
2026-06-16 10:07:48 +03:00
Stephan T. Lavavej 4208a89b3e Consistently include <math.h> with angle brackets 2026-06-15 14:53:29 -07:00
Alexander Smorkalov 41d48d6eeb Merge pull request #29306 from asmorkalov:as/more_ipp_core
Moved IPP math functions from core module to HAL.
2026-06-16 00:15:30 +03:00
Alexander Smorkalov 0a4d6a849a Moved IPP math functions from core module to HAL. 2026-06-15 21:53:10 +03:00
Alexander Smorkalov 017a5138d1 Merge pull request #29303 from asmorkalov:as/win32_objdetect_warn_fix
Fixed new objdetect warnings on Windows.
2026-06-15 21:29:42 +03:00
uwezkhan afbfa275d8 bound darknet cfg layer and anchor indices before vector reads 2026-06-15 21:36:38 +05:30
arshsmith 6df9732c81 Merge pull request #29296 from arshsmith:fix-pam-grayalpha-overflow
imgcodecs(pam): fix out-of-bounds write when reading 2-channel PAM #29296

While poking at the PAM decoder I noticed basic_conversion() writes past the
end of the destination buffer when a GRAYSCALE_ALPHA (2 channel) image is read
with IMREAD_GRAYSCALE.

The 1-channel branch was written as if the destination had 3 channels:

    for( ; s < end; d += 3, s += src_sampe_size )
        d[0] = d[1] = d[2] = s[layout->graychan];

So for every source pixel it writes 3 bytes and advances d by 3, even though
the output row only has m_width bytes (1 channel). With m_channels == 2 that
ends up writing ~1.5 * m_width bytes per row, which runs off the row and, on
the last row, off the end of the Mat. m_width is taken straight from the file
header (up to 2^20), so the overflow size and contents are attacker controlled.
It's reachable with a plain imread(file, IMREAD_GRAYSCALE) on a crafted file.

While looking at it I also realised the loop bound was off for any multi
channel source: end was set to src + src_width, but the source has
src_width * channels samples, so it only ever processed m_width/channels pixels
instead of all of them. That's why GRAYSCALE_ALPHA / RGB_ALPHA come out only
partially filled.

Fix both at once:
- end now covers the whole row (src_width * src_sampe_size)
- the 1-channel branch writes one byte and advances d by 1

For the common grayscale->color case (channels == 1) the new end is identical
to the old one (m_width * 1 == m_width), so that path is byte for byte the same
and the existing PAM read_write test is unaffected. The only outputs that
change are the GRAYSCALE_ALPHA/RGB_ALPHA conversions, which were already broken.

Added a regression test (Imgcodecs_Pam.decode_graya_as_gray) that builds a small
2-channel PAM with an odd width, decodes it as grayscale and checks the result
matches the gray channel. It overflows/crashes on the old code and passes now.
2026-06-15 17:49:12 +03:00
Alexander Smorkalov 40d6727ea2 Merge pull request #29295 from uwezkhan:exif-rawprofile-len
reject under-length raw exif profile in processRawProfile
2026-06-15 13:51:48 +03:00
Alexander Smorkalov cfb3a8bbb7 Fixed new objdetect warnings on Windows. 2026-06-15 12:32:11 +03:00
Ayush Das 194db6f5cb Merge pull request #29299 from AyushDas4890:fix/doc-typos-tutorials
Fix typos in tutorial documentation #29299

This PR fixes spelling typos across several tutorial documentation files (dnn, calib3d, objdetect, app, js_tutorials), including `export=dowload` -> `export=download` in the DNN text-spotting tutorial's Google Drive URLs.

Documentation-only change; no functional code is affected.

Pull Request Readiness Checklist:

[x] I agree to contribute to the project under Apache 2 License.

[x] To the best of my knowledge, the proposed patch is not based on code under GPL or another license that is incompatible with OpenCV.

[x] The PR is proposed to the proper branch (4.x).

[x] Documentation-only change; no accuracy/performance tests or opencv_extra patch are applicable.
2026-06-15 12:28:35 +03:00
Alexander Smorkalov 5aad95cb1d Merge pull request #29297 from Kumataro:fix_doxygen_warning_at_mat
doc(core): fix doxygen warning for missing endcode
2026-06-15 10:21:33 +03:00
Alexander Smorkalov 86433eac87 Merge pull request #29301 from uwezkhan:persistence-nd-dim-bound
reject nd-matrix dim count above CV_MAX_DIM in FileStorage read
2026-06-15 10:16:16 +03:00
uwezkhan 2906d4d73a reject nd-matrix dim count above CV_MAX_DIM in FileStorage read 2026-06-14 02:51:20 +05:30
Kumataro 96a71a8d83 doc(core): fix doxygen warning for missing endcode 2026-06-13 20:45:30 +09:00
uwezkhan 19ec7ea28b reject under-length raw exif profile in processRawProfile 2026-06-13 15:04:24 +05:30
Rishiii57 12eaf9bd4c imgproc: fix matchTemplate TM_SQDIFF precision loss for CV_8U images
When computing TM_SQDIFF for 8-bit images, crossCorr stores intermediate
results in CV_32F which loses precision for large patch sums (e.g. 25x25
patch of value 255 gives sum=40603125, exceeding float32's ~7 significant
digits). This causes the final SQDIFF result to have incorrect non-zero
values even when image and template are identical.

Fix: use a CV_64F intermediate buffer for crossCorr and common_matchTemplate
when depth==CV_8U and method is TM_SQDIFF or TM_SQDIFF_NORMED, then convert
back to CV_32F for the final output.

Fixes #21786
2026-06-12 00:06:26 +05:30
ZNNAXLRQ cadcb7e4e0 Merge pull request #29059 from ZNNAXLRQ:Project4_ZHUWanqi
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.
2026-06-11 20:59:32 +03:00
Vincent Rabaud fa55ed2837 Merge pull request #29267 from vrabaud:aruco
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
2026-06-11 20:54:39 +03:00
Pranav Kaushik 4dee742731 Merge pull request #29281 from pranavkaushik1:improve-qrcode-colors
samples: add color-coded bounding boxes for multi QR code detection #29281

Improves the existing qrcode.py sample by adding color-coded bounding boxes when multiple QR codes are detected simultaneously.

Previously all QR codes were drawn with the same green color, making it hard to distinguish between them visually.

This change adds a QR_COLORS palette so each detected QR code gets a unique color, improving visual clarity for multi-QR detection.

Co-authored-by: Ayush Gupta <gupta.ayushg@gmail.com>
2026-06-11 18:43:27 +03:00
Alexander Smorkalov a77474f3b1 Merge pull request #29255 from ArneshBanerjee:samples-blob-contours-29254
samples: demonstrate SimpleBlobDetector contour collection
2026-06-11 18:30:34 +03:00
Yixuan Tang 590d8f6439 Merge pull request #29240 from plain-noodle-expert:fix/brisk-ubsan-negative-shift
fix(features2d): fix UB left-shift of negative value in BRISK subpixel2D #29240

## Summary

Fixes #29239

`BriskScaleSpace::subpixel2D` computes quadratic surface coefficients for sub-pixel keypoint refinement using AGAST scores from a 3×3 neighbourhood. Two of those coefficients were computed via left-shift:

```cpp
// Before (UB when operand is negative)
int coeff5 = (s_0_0 - s_0_2 - s_2_0 + s_2_2) << 2;
int coeff6 = -(s_0_0 + s_0_2 - ((s_1_0 + s_0_1 + s_1_2 + s_2_1) << 1) - 5 * s_1_1 + s_2_0 + s_2_2) << 1;
```

AGAST scores are non-negative (0–255), but their **differences can be negative**. Left-shifting a negative signed integer is **undefined behaviour** under C++11 [expr.shift]/2. UBSan reports:

```
brisk.cpp:2037:48: runtime error: left shift of negative value -3
```

## Fix

Replace the outer left-shifts with multiplications (`* 4` / `* 2`), which are always well-defined. Modern compilers (GCC, Clang, MSVC) emit identical code for the positive case.

```cpp
// After (safe for all inputs, identical semantics)
int coeff5 = (s_0_0 - s_0_2 - s_2_0 + s_2_2) * 4;
int coeff6 = -(s_0_0 + s_0_2 - ((s_1_0 + s_0_1 + s_1_2 + s_2_1) << 1) - 5 * s_1_1 + s_2_0 + s_2_2) * 2;
```

The inner `<< 1` inside the parenthesis of `coeff6` is applied to a sum of non-negative scores and remains safe.

## Tests

Four regression tests added to `test_brisk.cpp` (all verified to pass under `-fsanitize=undefined`):

| Test | Image | Purpose |
|---|---|---|
| `regression_ubsan_negative_shift_isolated_pixel` | 40×40, single pixel = 3 | Matches exact crash parameters (s_0_2=3, others=0) |
| `regression_ubsan_negative_shift_rect_corner` | 60×60 white rect on black | Strong AGAST responses at corners |
| `regression_ubsan_negative_shift_random_image` | 30×30 LCG noise (0–15) | Broad coverage of score combinations |
| `regression_ubsan_negative_shift_multi_octave` | 80×80 checkerboard, 3 octaves | Exercises cross-layer subpixel refinement |

## Crash Details

- **File**: `modules/features2d/src/brisk.cpp:2037`
- **Parameters at crash**: `s_0_0=0, s_0_1=0, s_0_2=3, s_1_0=0, s_1_1=0, s_1_2=0, s_2_0=0, s_2_1=0, s_2_2=0`
- **Expression**: `(0 - 3 - 0 + 0) << 2` = `-3 << 2` → **UB**
- **Detected by**: libFuzzer + UBSan (`-fsanitize=address,undefined`)
- **Affected path**: `detectAndCompute` → `computeKeypointsNoOrientation` → `getKeypoints` → `refine3D` → `getScoreMaxAbove` → `subpixel2D`
2026-06-11 13:08:05 +03:00
Srujan rai aed41fdabe Merge pull request #28981 from Srujan-rai:fix/opengl-extensions-memory-leak
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.
2026-06-10 20:26:55 +03:00
Alexander Smorkalov f80d02e1c7 Merge pull request #29277 from intel-staging:perf_blobFromImages
Added more perf tests for blobFromImage
2026-06-10 19:24:55 +03:00
Ding zhehao ffa38e1b74 Merge pull request #29194 from Enilrats:opt-emd-performance
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
2026-06-10 18:23:35 +03:00
Alexander Smorkalov 1780a86075 Merge pull request #29117 from SofianElmotiem:4.x
calib3d: use Delaunay triangulation in computeRNG for findCirclesGrid
2026-06-10 17:39:52 +03:00
Andrei Fedorov 48b8099214 added more perf tests for blobFromImages 2026-06-10 13:55:20 +02:00
Sofian Elmotiem 3d02d863f9 calib3d: use Delaunay triangulation in computeRNG for findCirclesGrid
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.
2026-06-10 13:17:26 +02:00
Alexander Smorkalov 3d55d2fcef Merge pull request #29201 from Primary-H:project4_David
features2d: improve detector parameter validation
2026-06-10 14:10:56 +03:00
胡晨宇 d10138fa1c Merge pull request #29132 from hcy11123323:4.x
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
2026-06-10 13:11:27 +03:00
Alexander Smorkalov 9780b3b329 Merge pull request #29274 from vrabaud:ub_lut
Fix undefined behavior with pointer alignment
2026-06-10 11:27:54 +03:00
Vincent Rabaud 63b59865d3 Fix undefined behavior with pointer alignment
While at it, replace MMX _mm_setr_epi64 to not do the MMX to XMM
move.
2026-06-09 15:43:34 +02:00
Alexander Smorkalov b9a38df0e6 Merge pull request #29271 from uwezkhan:torch-storage-size-cap
cap torch storage size to int to prevent heap overflow
2026-06-09 12:49:17 +03:00
uwezkhan fe61ae0e41 cap torch storage size to int to prevent heap overflow 2026-06-09 10:09:52 +05:30
Alexander Smorkalov 6f29af625b Merge pull request #29268 from asmorkalov:as/disable_ocl_imterop_sample_4.x
Disable OpenCV-OpenCL interop sample for now 4.x
2026-06-08 17:03:23 +03:00
Alexander Smorkalov 0397596e17 Merge pull request #29231 from ozhanghe:4.x
Spelling: Fix typos in HAL
2026-06-08 14:25:52 +03:00
WITHOUTNAMESES 2a70226181 Merge pull request #29191 from WITHOUTNAMESES:project4_czn
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.)*
2026-06-08 14:19:00 +03:00
Alexander Smorkalov 21356eed2b Merge pull request #29253 from ssam18:fix-29222-bgr2lab-first-call
Speed up first BGR2Lab and BGR2Luv call by building the color LUT faster
2026-06-08 13:40:02 +03:00
Arnesh Banerjee 11b0bd15cf samples: demonstrate SimpleBlobDetector contour collection
Enable collectContours in detect_blob.cpp and draw the per-blob
contours returned by getBlobContours(), so the sample exercises the
public contour-collection API added in #21942.

Refs #25904
2026-06-07 12:18:39 +05:30
Samaresh Kumar Singh c936dcaef7 Speed up first BGR2Lab and BGR2Luv call by building the color LUT faster
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.
2026-06-06 16:36:07 -05:00
Alexander Smorkalov ab1aaa75aa Merge pull request #29219 from Kumataro:show_jpeg-turbo-version
build: show libjpeg-turbo version for bundled and system-wide libraries
2026-06-06 13:44:26 +03:00
Alexander Smorkalov d9f89b028b Merge pull request #29187 from sparklejin:project4_sparklejin
imgproc: fix floodFill simple path never being used (small fix)
2026-06-06 13:40:25 +03:00
ozhanghe 517710fe56 Spelling: Fix typos in HAL 2026-06-04 20:26:54 -07:00
Kumataro a99141acd7 build: show libjpeg-turbo version for bundled and system-wide libraries 2026-06-03 08:31:27 +09:00
Alexander Smorkalov 1b047868dd Merge pull request #29197 from Shengyu-Wu:feat/fast-rvv-u8mf2-prescreen
feat(rvv-hal): optimize FAST pre-screen with deferred u8mf2 widening
2026-06-01 19:23:56 +03:00
Primary-H a7bb1d1e1e features2d: improve detector parameter validation 2026-05-31 23:40:50 +08:00
Shengyu Wu aee03ffbb2 feat(rvv-hal): optimize FAST pre-screen with deferred u8mf2 widening
Defer the u8→i16 widening (vzext) in the pre-screen phase until after
the quick-reject check passes. This avoids 5 unnecessary widen
operations per pixel-strip when the pre-screen rejects (which happens
~70-80% of the time at the default threshold=20).

The approach uses u8mf2 (same VL as i16m1) for the pre-screen
comparison with vssubu/vsaddu for bounds and vmsgtu for unsigned
comparison. When the pre-screen passes, the already-loaded u8mf2
variables are widened to i16m1 for the score computation, reusing
the same data without redundant memory loads.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-31 20:49:30 +08:00
sparklejin b723ddd72b imgproc: fix floodFill simple path never being used
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.
2026-05-30 15:53:54 +08:00
81 changed files with 1378 additions and 471 deletions
-3
View File
@@ -136,9 +136,6 @@ endif()
set(JPEG_LIB_VERSION 70)
# OpenCV
set(JPEG_LIB_VERSION "${VERSION}-${JPEG_LIB_VERSION}" PARENT_SCOPE)
set(THREAD_LOCAL "") # WITH_TURBOJPEG is not used
add_definitions(-DNO_GETENV -DNO_PUTENV)
+15
View File
@@ -102,8 +102,23 @@ if(WITH_JPEG)
macro(ocv_detect_jpeg_version header_file)
if(NOT DEFINED JPEG_LIB_VERSION AND EXISTS "${header_file}")
ocv_parse_header("${header_file}" JPEG_VERSION_LINES JPEG_LIB_VERSION)
if(DEFINED JPEG_LIB_VERSION)
# Extract libjpeg-turbo version from the header file if JPEG_LIB_VERSION is found.
file(STRINGS "${header_file}" JPEG_TURBO_VERSION_LINE REGEX "^#define[\t ]+LIBJPEG_TURBO_VERSION[\t ]")
if(JPEG_TURBO_VERSION_LINE)
# Support both raw values (e.g., 3.1.2) and quoted strings (e.g., "3.1.2").
string(REGEX REPLACE "^#define[\t ]+LIBJPEG_TURBO_VERSION[\t ]+\"?([^\"]+)\"?.*" "\\1" JPEG_TURBO_VERSION_STRING "${JPEG_TURBO_VERSION_LINE}")
if(JPEG_TURBO_VERSION_STRING)
string(STRIP "${JPEG_TURBO_VERSION_STRING}" JPEG_TURBO_VERSION_STRING)
set(JPEG_LIB_VERSION "${JPEG_TURBO_VERSION_STRING}-${JPEG_LIB_VERSION}")
endif()
endif()
endif()
endif()
endmacro()
ocv_detect_jpeg_version("${JPEG_INCLUDE_DIR}/jpeglib.h")
if(DEFINED CMAKE_CXX_LIBRARY_ARCHITECTURE)
ocv_detect_jpeg_version("${JPEG_INCLUDE_DIR}/${CMAKE_CXX_LIBRARY_ARCHITECTURE}/jconfig.h")
@@ -105,7 +105,7 @@ let mat = new cv.Mat();
let matVec = new cv.MatVector();
// Push a Mat back into MatVector
matVec.push_back(mat);
// Get a Mat fom MatVector
// Get a Mat from MatVector
let cnt = matVec.get(0);
mat.delete(); matVec.delete(); cnt.delete();
@endcode
@@ -27,7 +27,7 @@ foreground object (Always try to keep foreground in white). So what it does? The
through the image (as in 2D convolution). A pixel in the original image (either 1 or 0) will be
considered 1 only if all the pixels under the kernel is 1, otherwise it is eroded (made to zero).
So what happends is that, all the pixels near boundary will be discarded depending upon the size of
So what happens is that, all the pixels near boundary will be discarded depending upon the size of
kernel. So the thickness or size of the foreground object decreases or simply white region decreases
in the image. It is useful for removing small white noises (as we have seen in colorspace chapter),
detach two connected objects etc.
@@ -174,4 +174,4 @@ Try it
<iframe src="../../js_morphological_ops_getStructuringElement.html" width="100%"
onload="this.style.height=this.contentDocument.body.scrollHeight +'px';">
</iframe>
\endhtmlonly
\endhtmlonly
@@ -103,4 +103,4 @@ int main(void)
Limitation/Known problem
------------------------
- cv::moveWindow() is not implementated. ( See. https://github.com/opencv/opencv/issues/25478 )
- cv::moveWindow() is not implemented. ( See. https://github.com/opencv/opencv/issues/25478 )
@@ -72,8 +72,8 @@ In order to use the Astra camera's depth sensor with OpenCV you should do the fo
@note The last tried version `2.3.0.86_202210111154_4c8f5aa4_beta6` does not work correctly with
modern Linux, even after libusb rebuild as recommended by the instruction. The last know good
configuration is version 2.3.0.63 (tested with Ubuntu 18.04 amd64). It's not provided officialy
with the downloading page, but published by Orbbec technical suport on Orbbec community forum
configuration is version 2.3.0.63 (tested with Ubuntu 18.04 amd64). It's not provided officially
with the downloading page, but published by Orbbec technical support on Orbbec community forum
[here](https://3dclub.orbbec3d.com/t/universal-download-thread-for-astra-series-cameras/622).
-# Now you can configure OpenCV with OpenNI support enabled by setting the `WITH_OPENNI2` flag in CMake.
@@ -63,7 +63,7 @@ Example code to generate features coordinates for calibration with symmetric gri
}
}
```
Example code to generate features corrdinates for calibration with asymmetic grid (object points):
Example code to generate features coordinates for calibration with asymmetric grid (object points):
```
std::vector<cv::Point3f> objectPoints;
for (int i = 0; i < boardSize.height; i++) {
@@ -84,7 +84,7 @@ about ArUco pairs. In opposite to the previous pattern partially occluded board
corners are labeled. The board is rotation invariant, but set of ArUco markers and their order
should be known to detector apriori. It cannot detect ChAruco board with predefined size and random
set of markers.
Example code to generate features corrdinates for calibration (object points) for board size in units:
Example code to generate features coordinates for calibration (object points) for board size in units:
```
std::vector<cv::Point3f> objectPoints;
for (int i = 0; i < boardSize.height-1; ++i) {
@@ -64,25 +64,25 @@ We encourage you to add new algorithms to these APIs.
```
crnn.onnx:
url: https://drive.google.com/uc?export=dowload&id=1ooaLR-rkTl8jdpGy1DoQs0-X0lQsB6Fj
url: https://drive.google.com/uc?export=download&id=1ooaLR-rkTl8jdpGy1DoQs0-X0lQsB6Fj
sha: 270d92c9ccb670ada2459a25977e8deeaf8380d3,
alphabet_36.txt: https://drive.google.com/uc?export=dowload&id=1oPOYx5rQRp8L6XQciUwmwhMCfX0KyO4b
alphabet_36.txt: https://drive.google.com/uc?export=download&id=1oPOYx5rQRp8L6XQciUwmwhMCfX0KyO4b
parameter setting: -rgb=0;
description: The classification number of this model is 36 (0~9 + a~z).
The training dataset is MJSynth.
crnn_cs.onnx:
url: https://drive.google.com/uc?export=dowload&id=12diBsVJrS9ZEl6BNUiRp9s0xPALBS7kt
url: https://drive.google.com/uc?export=download&id=12diBsVJrS9ZEl6BNUiRp9s0xPALBS7kt
sha: a641e9c57a5147546f7a2dbea4fd322b47197cd5
alphabet_94.txt: https://drive.google.com/uc?export=dowload&id=1oKXxXKusquimp7XY1mFvj9nwLzldVgBR
alphabet_94.txt: https://drive.google.com/uc?export=download&id=1oKXxXKusquimp7XY1mFvj9nwLzldVgBR
parameter setting: -rgb=1;
description: The classification number of this model is 94 (0~9 + a~z + A~Z + punctuations).
The training datasets are MJsynth and SynthText.
crnn_cs_CN.onnx:
url: https://drive.google.com/uc?export=dowload&id=1is4eYEUKH7HR7Gl37Sw4WPXx6Ir8oQEG
url: https://drive.google.com/uc?export=download&id=1is4eYEUKH7HR7Gl37Sw4WPXx6Ir8oQEG
sha: 3940942b85761c7f240494cf662dcbf05dc00d14
alphabet_3944.txt: https://drive.google.com/uc?export=dowload&id=18IZUUdNzJ44heWTndDO6NNfIpJMmN-ul
alphabet_3944.txt: https://drive.google.com/uc?export=download&id=18IZUUdNzJ44heWTndDO6NNfIpJMmN-ul
parameter setting: -rgb=1;
description: The classification number of this model is 3944 (0~9 + a~z + A~Z + Chinese characters + special characters).
The training dataset is ReCTS (https://rrc.cvc.uab.es/?ch=12).
@@ -96,25 +96,25 @@ You can train more models by [CRNN](https://github.com/meijieru/crnn.pytorch), a
```
- DB_IC15_resnet50.onnx:
url: https://drive.google.com/uc?export=dowload&id=17_ABp79PlFt9yPCxSaarVc_DKTmrSGGf
url: https://drive.google.com/uc?export=download&id=17_ABp79PlFt9yPCxSaarVc_DKTmrSGGf
sha: bef233c28947ef6ec8c663d20a2b326302421fa3
recommended parameter setting: -inputHeight=736, -inputWidth=1280;
description: This model is trained on ICDAR2015, so it can only detect English text instances.
- DB_IC15_resnet18.onnx:
url: https://drive.google.com/uc?export=dowload&id=1vY_KsDZZZb_svd5RT6pjyI8BS1nPbBSX
url: https://drive.google.com/uc?export=download&id=1vY_KsDZZZb_svd5RT6pjyI8BS1nPbBSX
sha: 19543ce09b2efd35f49705c235cc46d0e22df30b
recommended parameter setting: -inputHeight=736, -inputWidth=1280;
description: This model is trained on ICDAR2015, so it can only detect English text instances.
- DB_TD500_resnet50.onnx:
url: https://drive.google.com/uc?export=dowload&id=19YWhArrNccaoSza0CfkXlA8im4-lAGsR
url: https://drive.google.com/uc?export=download&id=19YWhArrNccaoSza0CfkXlA8im4-lAGsR
sha: 1b4dd21a6baa5e3523156776970895bd3db6960a
recommended parameter setting: -inputHeight=736, -inputWidth=736;
description: This model is trained on MSRA-TD500, so it can detect both English and Chinese text instances.
- DB_TD500_resnet18.onnx:
url: https://drive.google.com/uc?export=dowload&id=1sZszH3pEt8hliyBlTmB-iulxHP1dCQWV
url: https://drive.google.com/uc?export=download&id=1sZszH3pEt8hliyBlTmB-iulxHP1dCQWV
sha: 8a3700bdc13e00336a815fc7afff5dcc1ce08546
recommended parameter setting: -inputHeight=736, -inputWidth=736;
description: This model is trained on MSRA-TD500, so it can detect both English and Chinese text instances.
@@ -133,11 +133,11 @@ This model is based on https://github.com/argman/EAST
```
Text Recognition:
url: https://drive.google.com/uc?export=dowload&id=1nMcEy68zDNpIlqAn6xCk_kYcUTIeSOtN
url: https://drive.google.com/uc?export=download&id=1nMcEy68zDNpIlqAn6xCk_kYcUTIeSOtN
sha: 89205612ce8dd2251effa16609342b69bff67ca3
Text Detection:
url: https://drive.google.com/uc?export=dowload&id=149tAhIcvfCYeyufRoZ9tmc2mZDKE_XrF
url: https://drive.google.com/uc?export=download&id=149tAhIcvfCYeyufRoZ9tmc2mZDKE_XrF
sha: ced3c03fb7f8d9608169a913acf7e7b93e07109b
```
+1 -1
View File
@@ -129,7 +129,7 @@ than YOLOX) in case it is needed. However, usually each YOLO repository has pred
#### Exporting YOLOv10 model
In oder to run YOLOv10 one needs to cut off postporcessing with dynamic shapes from torch and then convert it to ONNX. If someone is looking for on how to cut off the postprocessing, there is this [forked branch](https://github.com/Abdurrahheem/yolov10/tree/ash/opencv-export) from official YOLOv10. The forked branch cuts of the postprocessing by [returning output](https://github.com/Abdurrahheem/yolov10/blob/4fdaafd912c8891642bfbe85751ea66ec20f05ad/ultralytics/nn/modules/head.py#L522) of the model before postprocessing procedure itself. To convert torch model to ONNX follow this proceduce.
In order to run YOLOv10 one needs to cut off postprocessing with dynamic shapes from torch and then convert it to ONNX. If someone is looking for on how to cut off the postprocessing, there is this [forked branch](https://github.com/Abdurrahheem/yolov10/tree/ash/opencv-export) from official YOLOv10. The forked branch cuts off the postprocessing by [returning output](https://github.com/Abdurrahheem/yolov10/blob/4fdaafd912c8891642bfbe85751ea66ec20f05ad/ultralytics/nn/modules/head.py#L522) of the model before postprocessing procedure itself. To convert torch model to ONNX follow this procedure.
@code{.bash}
git clone git@github.com:Abdurrahheem/yolov10.git
@@ -167,7 +167,7 @@ for `cv::aruco::Dictionary`. The data member of board classes are public and can
To do so, you will need to use an external rendering engine library, such as OpenGL. The aruco module
only provides the functionality to obtain the camera pose, i.e. the rotation and translation vectors,
which is necessary to create the augmented reality effect. However, you will need to adapt the rotation
and traslation vectors from the OpenCV format to the format accepted by your 3d rendering library.
and translation vectors from the OpenCV format to the format accepted by your 3d rendering library.
The original ArUco library contains examples of how to do it for OpenGL and Ogre3D.
@@ -12,7 +12,7 @@ It is similar to a ChArUco board in appearance, however they are conceptually di
In both, ChArUco board and Diamond markers, their detection is based on the previous detected ArUco
markers. In the ChArUco case, the used markers are selected by directly looking their identifiers. This means
that if a marker (included in the board) is found on a image, it will be automatically assumed to belong to the board. Furthermore,
if a marker board is found more than once in the image, it will produce an ambiguity since the system wont
if a marker board is found more than once in the image, it will produce an ambiguity since the system won't
be able to know which one should be used for the Board.
On the other hand, the detection of Diamond marker is not based on the identifiers. Instead, their detection
+1 -1
View File
@@ -2285,7 +2285,7 @@ namespace CAROTENE_NS {
f64 alpha, f64 beta);
/*
Reduce matrix to a vector by calculatin given operation for each column
Reduce matrix to a vector by calculating given operation for each column
*/
void reduceColSum(const Size2D &size,
const u8 * srcBase, ptrdiff_t srcStride,
+1 -1
View File
@@ -231,7 +231,7 @@ void accumulateSquare(const Size2D &size,
internal::assertSupportedConfiguration();
#ifdef CAROTENE_NEON
// this ugly contruction is needed to avoid:
// this ugly construction is needed to avoid:
// /usr/lib/gcc/arm-linux-gnueabihf/4.8/include/arm_neon.h:3581:59: error: argument must be a constant
// return (int16x8_t)__builtin_neon_vshr_nv8hi (__a, __b, 1);
+1 -1
View File
@@ -524,7 +524,7 @@ inline void Canny3x3(const Size2D &size, s32 cn,
//i == 0
normEstimator.firstRow(size, cn, srcBase, srcStride, dxBase, dxStride, dyBase, dyStride, mag_buf);
// calculate magnitude and angle of gradient, perform non-maxima supression.
// calculate magnitude and angle of gradient, perform non-maxima suppression.
// fill the map with one of the following values:
// 0 - the pixel might belong to an edge
// 1 - the pixel can not belong to an edge
+1 -1
View File
@@ -66,7 +66,7 @@ inline float32x2_t vrecp_f32(float32x2_t val)
return reciprocal;
}
// caclulate sqrt value
// calculate sqrt value
inline float32x4_t vrsqrtq_f32(float32x4_t val)
{
+1
View File
@@ -9,6 +9,7 @@ set(IPP_HAL_HEADERS
CACHE INTERNAL "")
add_library(ipphal STATIC
"${CMAKE_CURRENT_SOURCE_DIR}/src/math_ipp.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/mean_ipp.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/minmax_ipp.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/norm_ipp.cpp"
+27
View File
@@ -74,6 +74,33 @@ int ipp_hal_transpose2d(const uchar* src_data, size_t src_step, uchar* dst_data,
#undef cv_hal_transpose2d
#define cv_hal_transpose2d ipp_hal_transpose2d
int ipp_hal_invSqrt32f(const float* src, float* dst, int len);
int ipp_hal_invSqrt64f(const double* src, double* dst, int len);
#undef cv_hal_invSqrt32f
#define cv_hal_invSqrt32f ipp_hal_invSqrt32f
#undef cv_hal_invSqrt64f
#define cv_hal_invSqrt64f ipp_hal_invSqrt64f
int ipp_hal_exp32f(const float* src, float* dst, int len);
int ipp_hal_exp64f(const double* src, double* dst, int len);
#undef cv_hal_exp32f
#define cv_hal_exp32f ipp_hal_exp32f
#undef cv_hal_exp64f
#define cv_hal_exp64f ipp_hal_exp64f
int ipp_hal_log32f(const float* src, float* dst, int len);
int ipp_hal_log64f(const double* src, double* dst, int len);
#undef cv_hal_log32f
#define cv_hal_log32f ipp_hal_log32f
#undef cv_hal_log64f
#define cv_hal_log64f ipp_hal_log64f
//! @endcond
#endif
+80
View File
@@ -0,0 +1,80 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html
#include "ipp_hal_core.hpp"
#include <opencv2/core/core.hpp>
#include <opencv2/core/base.hpp>
int ipp_hal_invSqrt32f(const float* src, float* dst, int len)
{
CV_HAL_CHECK_USE_IPP();
IppStatus status = CV_INSTRUMENT_FUN_IPP(ippsInvSqrt_32f_A21, src, dst, len);
if (status >= 0)
{
return CV_HAL_ERROR_OK;
}
return CV_HAL_ERROR_NOT_IMPLEMENTED;
}
int ipp_hal_invSqrt64f(const double* src, double* dst, int len)
{
CV_HAL_CHECK_USE_IPP();
IppStatus status = CV_INSTRUMENT_FUN_IPP(ippsInvSqrt_64f_A50, src, dst, len);
if (status >= 0)
{
return CV_HAL_ERROR_OK;
}
return CV_HAL_ERROR_NOT_IMPLEMENTED;
}
int ipp_hal_exp32f(const float* src, float* dst, int len)
{
CV_HAL_CHECK_USE_IPP();
IppStatus status = CV_INSTRUMENT_FUN_IPP(ippsExp_32f_A21, src, dst, len);
if (status >= 0)
{
return CV_HAL_ERROR_OK;
}
return CV_HAL_ERROR_NOT_IMPLEMENTED;
}
int ipp_hal_exp64f(const double* src, double* dst, int len)
{
CV_HAL_CHECK_USE_IPP();
IppStatus status = CV_INSTRUMENT_FUN_IPP(ippsExp_64f_A50, src, dst, len);
if (status >= 0)
{
return CV_HAL_ERROR_OK;
}
return CV_HAL_ERROR_NOT_IMPLEMENTED;
}
int ipp_hal_log32f(const float* src, float* dst, int len)
{
CV_HAL_CHECK_USE_IPP();
IppStatus status = CV_INSTRUMENT_FUN_IPP(ippsLn_32f_A21, src, dst, len);
if (status >= 0)
{
return CV_HAL_ERROR_OK;
}
return CV_HAL_ERROR_NOT_IMPLEMENTED;
}
int ipp_hal_log64f(const double* src, double* dst, int len)
{
CV_HAL_CHECK_USE_IPP();
IppStatus status = CV_INSTRUMENT_FUN_IPP(ippsLn_64f_A50, src, dst, len);
if (status >= 0)
{
return CV_HAL_ERROR_OK;
}
return CV_HAL_ERROR_NOT_IMPLEMENTED;
}
+24 -21
View File
@@ -56,32 +56,35 @@ inline int fast_16(const uchar* src_data, size_t src_step,
size_t vl;
for (; j < width - 3; j += vl, ptr += vl)
{
vl = __riscv_vsetvl_e16m1(width - 3 - j);
/* u8mf2 pre-screen: same VL as i16m1, defer vzext until needed */
vl = __riscv_vsetvl_e8mf2(width - 3 - j);
vuint8mf2_t vcen_8 = __riscv_vle8_v_u8mf2(ptr, vl);
vuint8mf2_t vlo_8 = __riscv_vssubu_vx_u8mf2(vcen_8, (uint8_t)threshold, vl);
vuint8mf2_t vhi_8 = __riscv_vsaddu_vx_u8mf2(vcen_8, (uint8_t)threshold, vl);
vuint8mf2_t vk0_8 = __riscv_vle8_v_u8mf2(ptr + pixel[0], vl);
vuint8mf2_t vk4_8 = __riscv_vle8_v_u8mf2(ptr + pixel[4], vl);
vuint8mf2_t vk8_8 = __riscv_vle8_v_u8mf2(ptr + pixel[8], vl);
vuint8mf2_t vk12_8 = __riscv_vle8_v_u8mf2(ptr + pixel[12], vl);
/* Load center pixel and widen to int16 */
vint16m1_t vcen = __riscv_vreinterpret_v_u16m1_i16m1(
__riscv_vzext_vf2(__riscv_vle8_v_u8mf2(ptr, vl), vl));
vint16m1_t vlo = __riscv_vsub_vx_i16m1(vcen, threshold, vl);
vint16m1_t vhi = __riscv_vadd_vx_i16m1(vcen, threshold, vl);
/* 4-direction quick reject */
vint16m1_t vk0 = __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2(__riscv_vle8_v_u8mf2(ptr + pixel[0], vl), vl));
vint16m1_t vk4 = __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2(__riscv_vle8_v_u8mf2(ptr + pixel[4], vl), vl));
vint16m1_t vk8 = __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2(__riscv_vle8_v_u8mf2(ptr + pixel[8], vl), vl));
vint16m1_t vk12 = __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2(__riscv_vle8_v_u8mf2(ptr + pixel[12], vl), vl));
vbool16_t bright = __riscv_vmand_mm_b16(__riscv_vmsgt_vv_i16m1_b16(vk0, vhi, vl), __riscv_vmsgt_vv_i16m1_b16(vk4, vhi, vl), vl);
vbool16_t dark = __riscv_vmand_mm_b16(__riscv_vmsgt_vv_i16m1_b16(vlo, vk0, vl), __riscv_vmsgt_vv_i16m1_b16(vlo, vk4, vl), vl);
bright = __riscv_vmor_mm_b16(bright, __riscv_vmand_mm_b16(__riscv_vmsgt_vv_i16m1_b16(vk4, vhi, vl), __riscv_vmsgt_vv_i16m1_b16(vk8, vhi, vl), vl), vl);
dark = __riscv_vmor_mm_b16(dark, __riscv_vmand_mm_b16(__riscv_vmsgt_vv_i16m1_b16(vlo, vk4, vl), __riscv_vmsgt_vv_i16m1_b16(vlo, vk8, vl), vl), vl);
bright = __riscv_vmor_mm_b16(bright, __riscv_vmand_mm_b16(__riscv_vmsgt_vv_i16m1_b16(vk8, vhi, vl), __riscv_vmsgt_vv_i16m1_b16(vk12, vhi, vl), vl), vl);
dark = __riscv_vmor_mm_b16(dark, __riscv_vmand_mm_b16(__riscv_vmsgt_vv_i16m1_b16(vlo, vk8, vl), __riscv_vmsgt_vv_i16m1_b16(vlo, vk12, vl), vl), vl);
bright = __riscv_vmor_mm_b16(bright, __riscv_vmand_mm_b16(__riscv_vmsgt_vv_i16m1_b16(vk12, vhi, vl), __riscv_vmsgt_vv_i16m1_b16(vk0, vhi, vl), vl), vl);
dark = __riscv_vmor_mm_b16(dark, __riscv_vmand_mm_b16(__riscv_vmsgt_vv_i16m1_b16(vlo, vk12, vl), __riscv_vmsgt_vv_i16m1_b16(vlo, vk0, vl), vl), vl);
vbool16_t bright = __riscv_vmand_mm_b16(__riscv_vmsgtu_vv_u8mf2_b16(vk0_8, vhi_8, vl), __riscv_vmsgtu_vv_u8mf2_b16(vk4_8, vhi_8, vl), vl);
vbool16_t dark = __riscv_vmand_mm_b16(__riscv_vmsgtu_vv_u8mf2_b16(vlo_8, vk0_8, vl), __riscv_vmsgtu_vv_u8mf2_b16(vlo_8, vk4_8, vl), vl);
bright = __riscv_vmor_mm_b16(bright, __riscv_vmand_mm_b16(__riscv_vmsgtu_vv_u8mf2_b16(vk4_8, vhi_8, vl), __riscv_vmsgtu_vv_u8mf2_b16(vk8_8, vhi_8, vl), vl), vl);
dark = __riscv_vmor_mm_b16(dark, __riscv_vmand_mm_b16(__riscv_vmsgtu_vv_u8mf2_b16(vlo_8, vk4_8, vl), __riscv_vmsgtu_vv_u8mf2_b16(vlo_8, vk8_8, vl), vl), vl);
bright = __riscv_vmor_mm_b16(bright, __riscv_vmand_mm_b16(__riscv_vmsgtu_vv_u8mf2_b16(vk8_8, vhi_8, vl), __riscv_vmsgtu_vv_u8mf2_b16(vk12_8, vhi_8, vl), vl), vl);
dark = __riscv_vmor_mm_b16(dark, __riscv_vmand_mm_b16(__riscv_vmsgtu_vv_u8mf2_b16(vlo_8, vk8_8, vl), __riscv_vmsgtu_vv_u8mf2_b16(vlo_8, vk12_8, vl), vl), vl);
bright = __riscv_vmor_mm_b16(bright, __riscv_vmand_mm_b16(__riscv_vmsgtu_vv_u8mf2_b16(vk12_8, vhi_8, vl), __riscv_vmsgtu_vv_u8mf2_b16(vk0_8, vhi_8, vl), vl), vl);
dark = __riscv_vmor_mm_b16(dark, __riscv_vmand_mm_b16(__riscv_vmsgtu_vv_u8mf2_b16(vlo_8, vk12_8, vl), __riscv_vmsgtu_vv_u8mf2_b16(vlo_8, vk0_8, vl), vl), vl);
if (__riscv_vfirst_m_b16(__riscv_vmor_mm_b16(bright, dark, vl), vl) < 0)
continue;
/* Widen pre-loaded u8mf2 to i16m1 for score computation */
vint16m1_t vcen = __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2(vcen_8, vl));
vint16m1_t vk0 = __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2(vk0_8, vl));
vint16m1_t vk4 = __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2(vk4_8, vl));
vint16m1_t vk8 = __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2(vk8_8, vl));
vint16m1_t vk12 = __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2(vk12_8, vl));
/* Load remaining 12 neighbors */
vint16m1_t vk1 = __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2(__riscv_vle8_v_u8mf2(ptr + pixel[1], vl), vl));
vint16m1_t vk2 = __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2(__riscv_vle8_v_u8mf2(ptr + pixel[2], vl), vl));
+31
View File
@@ -39,4 +39,35 @@ PERF_TEST_P(String_Size, asymm_circles_grid, testing::Values(
SANITY_CHECK(ptvec, 2);
}
// Perf test using synthetic keypoints (no image I/O). Exercises the RNG and
// findLongestPath code paths directly with a pre-detected point set.
typedef perf::TestBaseWithParam<cv::Size> CirclesGrid_RNG_Size;
PERF_TEST_P(CirclesGrid_RNG_Size, detect_keypoints_symmetric,
testing::Values(cv::Size(6, 5), cv::Size(8, 6), cv::Size(10, 8), cv::Size(15, 12), cv::Size(20, 15)))
{
const cv::Size patternSize = GetParam();
const float spacing = 30.f;
std::vector<cv::Point2f> pts;
pts.reserve(patternSize.area());
for (int r = 0; r < patternSize.height; r++)
for (int c = 0; c < patternSize.width; c++)
pts.push_back(cv::Point2f(c * spacing, r * spacing));
// Shuffle so the detector works from an unordered set, same as real use.
cv::RNG& rng = cv::theRNG();
for (int k = (int)pts.size() - 1; k > 0; k--)
std::swap(pts[k], pts[rng.uniform(0, k + 1)]);
std::vector<cv::Point2f> centers;
centers.resize(patternSize.area());
declare.in(cv::Mat(pts)).out(centers);
TEST_CYCLE() ASSERT_TRUE(findCirclesGrid(cv::Mat(pts), patternSize, centers,
CALIB_CB_SYMMETRIC_GRID, cv::Ptr<cv::FeatureDetector>()));
SANITY_CHECK_NOTHING();
}
} // namespace
+1 -1
View File
@@ -5,7 +5,7 @@
#include "precomp.hpp"
#include "opencv2/flann.hpp"
#include "chessboard.hpp"
#include "math.h"
#include <math.h>
//#define CV_DETECTORS_CHESSBOARD_DEBUG
#ifdef CV_DETECTORS_CHESSBOARD_DEBUG
+152 -94
View File
@@ -43,6 +43,7 @@
#include "precomp.hpp"
#include "circlesgrid.hpp"
#include <limits>
#include <queue>
// Requires CMake flag: DEBUG_opencv_calib3d=ON
//#define DEBUG_CIRCLES
@@ -569,8 +570,6 @@ CirclesGridFinder::Segment::Segment(cv::Point2f _s, cv::Point2f _e) :
{
}
void computeShortestPath(Mat &predecessorMatrix, int v1, int v2, std::vector<int> &path);
void computePredecessorMatrix(const Mat &dm, int verticesCount, Mat &predecessorMatrix);
CirclesGridFinderParameters::CirclesGridFinderParameters()
{
@@ -1204,79 +1203,91 @@ void CirclesGridFinder::computeRNG(Graph &rng, std::vector<cv::Point2f> &vectors
rng = Graph(keypoints.size());
vectors.clear();
//TODO: use more fast algorithm instead of naive N^3
for (size_t i = 0; i < keypoints.size(); i++)
{
for (size_t j = 0; j < keypoints.size(); j++)
{
if (i == j)
continue;
Point2f vec = keypoints[i] - keypoints[j];
double dist = norm(vec);
bool isNeighbors = true;
for (size_t k = 0; k < keypoints.size(); k++)
{
if (k == i || k == j)
continue;
double dist1 = norm(keypoints[i] - keypoints[k]);
double dist2 = norm(keypoints[j] - keypoints[k]);
if (dist1 < dist && dist2 < dist)
{
isNeighbors = false;
break;
}
}
if (isNeighbors)
{
rng.addEdge(i, j);
vectors.push_back(keypoints[i] - keypoints[j]);
if (drawImage != 0)
{
line(*drawImage, keypoints[i], keypoints[j], Scalar(255, 0, 0), 2);
circle(*drawImage, keypoints[i], 3, Scalar(0, 0, 255), -1);
circle(*drawImage, keypoints[j], 3, Scalar(0, 0, 255), -1);
}
}
}
}
}
void computePredecessorMatrix(const Mat &dm, int verticesCount, Mat &predecessorMatrix)
{
CV_Assert( dm.type() == CV_32SC1 );
predecessorMatrix.create(verticesCount, verticesCount, CV_32SC1);
predecessorMatrix = -1;
for (int i = 0; i < predecessorMatrix.rows; i++)
{
for (int j = 0; j < predecessorMatrix.cols; j++)
{
int dist = dm.at<int> (i, j);
for (int k = 0; k < verticesCount; k++)
{
if (dm.at<int> (i, k) == dist - 1 && dm.at<int> (k, j) == 1)
{
predecessorMatrix.at<int> (i, j) = k;
break;
}
}
}
}
}
static void computeShortestPath(Mat &predecessorMatrix, size_t v1, size_t v2, std::vector<size_t> &path)
{
if (predecessorMatrix.at<int> ((int)v1, (int)v2) < 0)
{
path.push_back(v1);
const size_t n = keypoints.size();
if (n < 2)
return;
// RNG is a subgraph of the Delaunay triangulation, so we only need to test
// Delaunay edges as candidates. This brings the complexity from O(N^3) down
// to O(N^2) in the worst case, and much better in practice for regular grids
// where Delaunay edges (~3N) are almost all RNG edges anyway.
float minX = keypoints[0].x, minY = keypoints[0].y;
float maxX = minX, maxY = minY;
for (size_t i = 1; i < n; i++)
{
minX = std::min(minX, keypoints[i].x);
minY = std::min(minY, keypoints[i].y);
maxX = std::max(maxX, keypoints[i].x);
maxY = std::max(maxY, keypoints[i].y);
}
computeShortestPath(predecessorMatrix, v1, predecessorMatrix.at<int> ((int)v1, (int)v2), path);
path.push_back(v2);
// Subdiv2D requires a rect that strictly contains all points.
const float margin = 1.f;
Rect2f rect(minX - margin, minY - margin,
(maxX - minX) + 2*margin,
(maxY - minY) + 2*margin);
Subdiv2D subdiv(rect);
subdiv.insert(std::vector<Point2f>(keypoints.begin(), keypoints.end()));
// Map coordinates back to keypoint indices. Subdiv2D stores and returns the
// exact float values we inserted, so direct comparison is safe here.
std::map<std::pair<float, float>, size_t> ptToIdx;
for (size_t i = 0; i < n; i++)
ptToIdx[{keypoints[i].x, keypoints[i].y}] = i;
std::vector<Vec4f> edgeList;
subdiv.getEdgeList(edgeList);
for (const Vec4f& e : edgeList)
{
auto it1 = ptToIdx.find({e[0], e[1]});
auto it2 = ptToIdx.find({e[2], e[3]});
// Edges involving the virtual bounding-rect vertices won't be in ptToIdx.
if (it1 == ptToIdx.end() || it2 == ptToIdx.end())
continue;
size_t i = it1->second;
size_t j = it2->second;
if (i == j)
continue;
if (i > j)
std::swap(i, j);
Point2f vec = keypoints[i] - keypoints[j];
double distSq = (double)vec.x*vec.x + (double)vec.y*vec.y;
bool isRNG = true;
for (size_t k = 0; k < n; k++)
{
if (k == i || k == j)
continue;
Point2f d1 = keypoints[i] - keypoints[k];
Point2f d2 = keypoints[j] - keypoints[k];
double d1Sq = (double)d1.x*d1.x + (double)d1.y*d1.y;
double d2Sq = (double)d2.x*d2.x + (double)d2.y*d2.y;
if (d1Sq < distSq && d2Sq < distSq)
{
isRNG = false;
break;
}
}
if (isRNG)
{
rng.addEdge(i, j);
// Push both directions; findBasis needs the full set to cluster into
// the 4 groups (two grid axes and their negatives) via k-means.
vectors.push_back(keypoints[i] - keypoints[j]);
vectors.push_back(keypoints[j] - keypoints[i]);
if (drawImage != 0)
{
line(*drawImage, keypoints[i], keypoints[j], Scalar(255, 0, 0), 2);
circle(*drawImage, keypoints[i], 3, Scalar(0, 0, 255), -1);
circle(*drawImage, keypoints[j], 3, Scalar(0, 0, 255), -1);
}
}
}
}
size_t CirclesGridFinder::findLongestPath(std::vector<Graph> &basisGraphs, Path &bestPath)
@@ -1285,45 +1296,93 @@ size_t CirclesGridFinder::findLongestPath(std::vector<Graph> &basisGraphs, Path
std::vector<int> confidences;
size_t bestGraphIdx = 0;
const int infinity = -1;
for (size_t graphIdx = 0; graphIdx < basisGraphs.size(); graphIdx++)
{
const Graph &g = basisGraphs[graphIdx];
Mat distanceMatrix;
g.floydWarshall(distanceMatrix, infinity);
Mat predecessorMatrix;
computePredecessorMatrix(distanceMatrix, (int)g.getVerticesCount(), predecessorMatrix);
const int n = (int)g.getVerticesCount();
double maxVal;
Point maxLoc;
minMaxLoc(distanceMatrix, 0, &maxVal, 0, &maxLoc);
// BFS from every vertex to find the diameter (longest shortest path).
// basisGraphs are sparse -- each vertex connects only to grid neighbors in
// one direction -- so this is O(N^2) vs Floyd-Warshall's O(N^3).
std::vector<int> dist(n);
std::queue<int> q;
if (maxVal > longestPaths[0].length)
int maxDist = 0;
int srcBest = 0, dstBest = 0;
for (int src = 0; src < n; src++)
{
std::fill(dist.begin(), dist.end(), -1);
dist[src] = 0;
q.push(src);
while (!q.empty())
{
int v = q.front(); q.pop();
for (size_t nb : g.getNeighbors((size_t)v))
{
int u = (int)nb;
if (dist[u] < 0)
{
dist[u] = dist[v] + 1;
q.push(u);
}
}
}
for (int dst = 0; dst < n; dst++)
{
if (dist[dst] > maxDist)
{
maxDist = dist[dst];
srcBest = src;
dstBest = dst;
}
}
}
if (maxDist > longestPaths[0].length)
{
longestPaths.clear();
confidences.clear();
bestGraphIdx = graphIdx;
}
if (longestPaths.empty() || (maxVal == longestPaths[0].length && graphIdx == bestGraphIdx))
if (longestPaths.empty() || (maxDist == longestPaths[0].length && graphIdx == bestGraphIdx))
{
Path path = Path(maxLoc.x, maxLoc.y, cvRound(maxVal));
CV_Assert(maxLoc.x >= 0 && maxLoc.y >= 0)
;
size_t id1 = static_cast<size_t> (maxLoc.x);
size_t id2 = static_cast<size_t> (maxLoc.y);
computeShortestPath(predecessorMatrix, id1, id2, path.vertices);
Path path = Path(srcBest, dstBest, maxDist);
// BFS again from srcBest to reconstruct the path to dstBest
std::vector<int> pred(n, -1);
std::fill(dist.begin(), dist.end(), -1);
dist[srcBest] = 0;
q.push(srcBest);
while (!q.empty())
{
int v = q.front(); q.pop();
for (size_t nb : g.getNeighbors((size_t)v))
{
int u = (int)nb;
if (dist[u] < 0)
{
dist[u] = dist[v] + 1;
pred[u] = v;
q.push(u);
}
}
}
std::vector<size_t> pathVertices;
for (int cur = dstBest; cur != srcBest; cur = pred[cur])
pathVertices.push_back((size_t)cur);
pathVertices.push_back((size_t)srcBest);
std::reverse(pathVertices.begin(), pathVertices.end());
path.vertices = pathVertices;
longestPaths.push_back(path);
int conf = 0;
for (int v2 = 0; v2 < (int)path.vertices.size(); v2++)
{
conf += (int)basisGraphs[1 - (int)graphIdx].getDegree(v2);
}
conf += (int)basisGraphs[1 - (int)graphIdx].getDegree(path.vertices[v2]);
confidences.push_back(conf);
}
}
//if( bestGraphIdx != 0 )
//CV_Error( 0, "" );
int maxConf = -1;
int bestPathIdx = -1;
@@ -1336,7 +1395,6 @@ size_t CirclesGridFinder::findLongestPath(std::vector<Graph> &basisGraphs, Path
}
}
//int bestPathIdx = rand() % longestPaths.size();
bestPath = longestPaths.at(bestPathIdx);
bool needReverse = (bestGraphIdx == 0 && keypoints[bestPath.lastVertex].x < keypoints[bestPath.firstVertex].x)
|| (bestGraphIdx == 1 && keypoints[bestPath.lastVertex].y < keypoints[bestPath.firstVertex].y);
+3 -2
View File
@@ -517,7 +517,7 @@ void filterHomographyDecompByVisibleRefpoints(InputArrayOfArrays _rotations,
CV_Assert(pointsMask.empty() || pointsMask.checkVector(1, CV_8U) == npoints);
const uchar* pointsMaskPtr = pointsMask.data;
std::vector<uchar> solutionMask(nsolutions, (uchar)1);
AutoBuffer<uchar> solutionMask(nsolutions, (uchar)1);
std::vector<Mat> normals(nsolutions);
std::vector<Mat> rotnorm(nsolutions);
Mat R;
@@ -559,7 +559,8 @@ void filterHomographyDecompByVisibleRefpoints(InputArrayOfArrays _rotations,
if( solutionMask[i] )
possibleSolutions.push_back(i);
Mat(possibleSolutions).copyTo(_possibleSolutions);
constexpr int cvType = traits::Type<decltype(possibleSolutions)::value_type>::value;
Mat(static_cast<int>(possibleSolutions.size()), 1, cvType, possibleSolutions.data()).copyTo(_possibleSolutions);
}
} //namespace cv
@@ -849,6 +849,97 @@ TEST(Calib3d_RotatedCirclesPatternDetector, issue_24964)
EXPECT_LE(error, precise_success_error_level);
}
// Generate a perfect W x H symmetric circle grid at the given spacing.
// Points are returned in shuffled order so the detector can't rely on input ordering.
static std::vector<Point2f> makeSyntheticSymmetricGrid(int cols, int rows, float spacing)
{
std::vector<Point2f> pts;
pts.reserve(cols * rows);
for (int r = 0; r < rows; r++)
for (int c = 0; c < cols; c++)
pts.push_back(Point2f(c * spacing, r * spacing));
cv::RNG& rng = cv::theRNG();
for (int k = (int)pts.size() - 1; k > 0; k--)
std::swap(pts[k], pts[rng.uniform(0, k + 1)]);
return pts;
}
// Generate an asymmetric circle grid. Even rows start at x=0, odd rows are offset by spacing/2.
static std::vector<Point2f> makeSyntheticAsymmetricGrid(int cols, int rows, float spacing)
{
std::vector<Point2f> pts;
pts.reserve(cols * rows);
for (int r = 0; r < rows; r++)
for (int c = 0; c < cols; c++)
pts.push_back(Point2f(c * spacing + (r % 2) * spacing * 0.5f, r * spacing * 0.5f));
cv::RNG& rng = cv::theRNG();
for (int k = (int)pts.size() - 1; k > 0; k--)
std::swap(pts[k], pts[rng.uniform(0, k + 1)]);
return pts;
}
typedef testing::TestWithParam<Size> Calib3d_CirclesGrid_RNG_Symmetric;
TEST_P(Calib3d_CirclesGrid_RNG_Symmetric, synthetic)
{
// Verify that findCirclesGrid correctly detects synthetic perfect symmetric grids of
// various sizes. This exercises the computeRNG path (Delaunay-based) end-to-end.
const float spacing = 30.f;
const Size gridSize = GetParam();
std::vector<Point2f> pts = makeSyntheticSymmetricGrid(gridSize.width, gridSize.height, spacing);
std::vector<Point2f> centers;
bool found = findCirclesGrid(Mat(pts), gridSize, centers,
CALIB_CB_SYMMETRIC_GRID, Ptr<FeatureDetector>());
ASSERT_TRUE(found) << "Symmetric grid " << gridSize.width << "x" << gridSize.height << " not detected";
ASSERT_EQ((int)centers.size(), gridSize.area());
for (const Point2f& c : centers)
{
bool matched = false;
for (const Point2f& p : pts)
if (cv::norm(c - p) < 1.f) { matched = true; break; }
EXPECT_TRUE(matched) << "Detected center " << c << " does not match any input point "
<< "for grid " << gridSize.width << "x" << gridSize.height;
}
}
INSTANTIATE_TEST_CASE_P(/**/, Calib3d_CirclesGrid_RNG_Symmetric,
testing::Values(Size(4, 4), Size(6, 5), Size(8, 6), Size(10, 8)));
typedef testing::TestWithParam<Size> Calib3d_CirclesGrid_RNG_Asymmetric;
TEST_P(Calib3d_CirclesGrid_RNG_Asymmetric, synthetic)
{
const float spacing = 30.f;
const Size gridSize = GetParam();
std::vector<Point2f> pts = makeSyntheticAsymmetricGrid(gridSize.width, gridSize.height, spacing);
std::vector<Point2f> centers;
bool found = findCirclesGrid(Mat(pts), gridSize, centers,
CALIB_CB_ASYMMETRIC_GRID, Ptr<FeatureDetector>());
ASSERT_TRUE(found) << "Asymmetric grid " << gridSize.width << "x" << gridSize.height << " not detected";
ASSERT_EQ((int)centers.size(), gridSize.area());
for (const Point2f& c : centers)
{
bool matched = false;
for (const Point2f& p : pts)
if (cv::norm(c - p) < 1.f) { matched = true; break; }
EXPECT_TRUE(matched) << "Detected center " << c << " does not match any input point "
<< "for grid " << gridSize.width << "x" << gridSize.height;
}
}
INSTANTIATE_TEST_CASE_P(/**/, Calib3d_CirclesGrid_RNG_Asymmetric,
testing::Values(Size(4, 6), Size(5, 8)));
TEST(Calib3d_CornerOrdering, issue_26830) {
const cv::String dataDir = string(TS::ptr()->get_data_path()) + "cv/cameracalibration/";
const cv::Mat image = cv::imread(dataDir + "checkerboard_marker_white.png");
@@ -3060,39 +3060,25 @@ inline v_float64x2 v_cvt_f64(const v_int64x2& v)
inline v_int8x16 v_lut(const schar* tab, const int* idx)
{
#if defined(_MSC_VER)
return v_int8x16(_mm_setr_epi8(tab[idx[0]], tab[idx[1]], tab[idx[ 2]], tab[idx[ 3]], tab[idx[ 4]], tab[idx[ 5]], tab[idx[ 6]], tab[idx[ 7]],
tab[idx[8]], tab[idx[9]], tab[idx[10]], tab[idx[11]], tab[idx[12]], tab[idx[13]], tab[idx[14]], tab[idx[15]]));
#else
return v_int8x16(_mm_setr_epi64(
_mm_setr_pi8(tab[idx[0]], tab[idx[1]], tab[idx[ 2]], tab[idx[ 3]], tab[idx[ 4]], tab[idx[ 5]], tab[idx[ 6]], tab[idx[ 7]]),
_mm_setr_pi8(tab[idx[8]], tab[idx[9]], tab[idx[10]], tab[idx[11]], tab[idx[12]], tab[idx[13]], tab[idx[14]], tab[idx[15]])
));
#endif
return v_int8x16(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]],
tab[idx[4]], tab[idx[5]], tab[idx[6]], tab[idx[7]],
tab[idx[8]], tab[idx[9]], tab[idx[10]], tab[idx[11]],
tab[idx[12]], tab[idx[13]], tab[idx[14]], tab[idx[15]]);
}
inline v_int8x16 v_lut_pairs(const schar* tab, const int* idx)
{
#if defined(_MSC_VER)
return v_int8x16(_mm_setr_epi16(*(const short*)(tab + idx[0]), *(const short*)(tab + idx[1]), *(const short*)(tab + idx[2]), *(const short*)(tab + idx[3]),
*(const short*)(tab + idx[4]), *(const short*)(tab + idx[5]), *(const short*)(tab + idx[6]), *(const short*)(tab + idx[7])));
#else
return v_int8x16(_mm_setr_epi64(
_mm_setr_pi16(*(const short*)(tab + idx[0]), *(const short*)(tab + idx[1]), *(const short*)(tab + idx[2]), *(const short*)(tab + idx[3])),
_mm_setr_pi16(*(const short*)(tab + idx[4]), *(const short*)(tab + idx[5]), *(const short*)(tab + idx[6]), *(const short*)(tab + idx[7]))
));
#endif
return v_int8x16(tab[idx[0]], tab[idx[0] + 1], tab[idx[1]], tab[idx[1] + 1],
tab[idx[2]], tab[idx[2] + 1], tab[idx[3]], tab[idx[3] + 1],
tab[idx[4]], tab[idx[4] + 1], tab[idx[5]], tab[idx[5] + 1],
tab[idx[6]], tab[idx[6] + 1], tab[idx[7]], tab[idx[7] + 1]);
}
inline v_int8x16 v_lut_quads(const schar* tab, const int* idx)
{
#if defined(_MSC_VER)
return v_int8x16(_mm_setr_epi32(*(const int*)(tab + idx[0]), *(const int*)(tab + idx[1]),
*(const int*)(tab + idx[2]), *(const int*)(tab + idx[3])));
#else
return v_int8x16(_mm_setr_epi64(
_mm_setr_pi32(*(const int*)(tab + idx[0]), *(const int*)(tab + idx[1])),
_mm_setr_pi32(*(const int*)(tab + idx[2]), *(const int*)(tab + idx[3]))
));
#endif
return v_int8x16(
tab[idx[0]], tab[idx[0] + 1], tab[idx[0] + 2], tab[idx[0] + 3],
tab[idx[1]], tab[idx[1] + 1], tab[idx[1] + 2], tab[idx[1] + 3],
tab[idx[2]], tab[idx[2] + 1], tab[idx[2] + 2], tab[idx[2] + 3],
tab[idx[3]], tab[idx[3] + 1], tab[idx[3] + 2], tab[idx[3] + 3]);
}
inline v_uint8x16 v_lut(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut((const schar *)tab, idx)); }
inline v_uint8x16 v_lut_pairs(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_pairs((const schar *)tab, idx)); }
@@ -3100,31 +3086,19 @@ inline v_uint8x16 v_lut_quads(const uchar* tab, const int* idx) { return v_reint
inline v_int16x8 v_lut(const short* tab, const int* idx)
{
#if defined(_MSC_VER)
return v_int16x8(_mm_setr_epi16(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]],
tab[idx[4]], tab[idx[5]], tab[idx[6]], tab[idx[7]]));
#else
return v_int16x8(_mm_setr_epi64(
_mm_setr_pi16(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]]),
_mm_setr_pi16(tab[idx[4]], tab[idx[5]], tab[idx[6]], tab[idx[7]])
));
#endif
return v_int16x8(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]],
tab[idx[4]], tab[idx[5]], tab[idx[6]], tab[idx[7]]);
}
inline v_int16x8 v_lut_pairs(const short* tab, const int* idx)
{
#if defined(_MSC_VER)
return v_int16x8(_mm_setr_epi32(*(const int*)(tab + idx[0]), *(const int*)(tab + idx[1]),
*(const int*)(tab + idx[2]), *(const int*)(tab + idx[3])));
#else
return v_int16x8(_mm_setr_epi64(
_mm_setr_pi32(*(const int*)(tab + idx[0]), *(const int*)(tab + idx[1])),
_mm_setr_pi32(*(const int*)(tab + idx[2]), *(const int*)(tab + idx[3]))
));
#endif
return v_int16x8(tab[idx[0]], tab[idx[0] + 1], tab[idx[1]], tab[idx[1] + 1],
tab[idx[2]], tab[idx[2] + 1], tab[idx[3]], tab[idx[3] + 1]);
}
inline v_int16x8 v_lut_quads(const short* tab, const int* idx)
{
return v_int16x8(_mm_set_epi64x(*(const int64_t*)(tab + idx[1]), *(const int64_t*)(tab + idx[0])));
return v_int16x8(tab[idx[0]], tab[idx[0] + 1], tab[idx[0] + 2],
tab[idx[0] + 3], tab[idx[1]], tab[idx[1] + 1],
tab[idx[1] + 2], tab[idx[1] + 3]);
}
inline v_uint16x8 v_lut(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v_lut((const short *)tab, idx)); }
inline v_uint16x8 v_lut_pairs(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v_lut_pairs((const short *)tab, idx)); }
@@ -3132,15 +3106,7 @@ inline v_uint16x8 v_lut_quads(const ushort* tab, const int* idx) { return v_rein
inline v_int32x4 v_lut(const int* tab, const int* idx)
{
#if defined(_MSC_VER)
return v_int32x4(_mm_setr_epi32(tab[idx[0]], tab[idx[1]],
tab[idx[2]], tab[idx[3]]));
#else
return v_int32x4(_mm_setr_epi64(
_mm_setr_pi32(tab[idx[0]], tab[idx[1]]),
_mm_setr_pi32(tab[idx[2]], tab[idx[3]])
));
#endif
return v_int32x4(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]]);
}
inline v_int32x4 v_lut_pairs(const int* tab, const int* idx)
{
@@ -555,6 +555,7 @@ public:
Mat_<uchar> m2({2, 3}, {1, 2, 3, 4, 5, 6}); // 2x3 Mat
Mat_<double> R({2, 2}, {a, -b, b, a}); // from example
\endcode
*/
template<typename _Tp> class MatCommaInitializer_
{
+1 -1
View File
@@ -2660,7 +2660,7 @@ cvReshapeMatND( const CvArr* arr,
mat = &stub;
}
if( CV_IS_MAT_CONT( mat->type ))
if( !CV_IS_MAT_CONT( mat->type ))
CV_Error( cv::Error::StsBadArg, "Non-continuous nD arrays are not supported" );
size1 = mat->dim[0].size;
@@ -107,7 +107,6 @@ void invSqrt32f(const float* src, float* dst, int len)
CV_INSTRUMENT_REGION();
CALL_HAL(invSqrt32f, cv_hal_invSqrt32f, src, dst, len);
CV_IPP_RUN_FAST(CV_INSTRUMENT_FUN_IPP(ippsInvSqrt_32f_A21, src, dst, len) >= 0);
CV_CPU_DISPATCH(invSqrt32f, (src, dst, len),
CV_CPU_DISPATCH_MODES_ALL);
@@ -119,7 +118,6 @@ void invSqrt64f(const double* src, double* dst, int len)
CV_INSTRUMENT_REGION();
CALL_HAL(invSqrt64f, cv_hal_invSqrt64f, src, dst, len);
CV_IPP_RUN_FAST(CV_INSTRUMENT_FUN_IPP(ippsInvSqrt_64f_A50, src, dst, len) >= 0);
CV_CPU_DISPATCH(invSqrt64f, (src, dst, len),
CV_CPU_DISPATCH_MODES_ALL);
@@ -152,7 +150,6 @@ void exp32f(const float *src, float *dst, int n)
CV_INSTRUMENT_REGION();
CALL_HAL(exp32f, cv_hal_exp32f, src, dst, n);
CV_IPP_RUN_FAST(CV_INSTRUMENT_FUN_IPP(ippsExp_32f_A21, src, dst, n) >= 0);
CV_CPU_DISPATCH(exp32f, (src, dst, n),
CV_CPU_DISPATCH_MODES_ALL);
@@ -163,7 +160,6 @@ void exp64f(const double *src, double *dst, int n)
CV_INSTRUMENT_REGION();
CALL_HAL(exp64f, cv_hal_exp64f, src, dst, n);
CV_IPP_RUN_FAST(CV_INSTRUMENT_FUN_IPP(ippsExp_64f_A50, src, dst, n) >= 0);
CV_CPU_DISPATCH(exp64f, (src, dst, n),
CV_CPU_DISPATCH_MODES_ALL);
@@ -174,7 +170,6 @@ void log32f(const float *src, float *dst, int n)
CV_INSTRUMENT_REGION();
CALL_HAL(log32f, cv_hal_log32f, src, dst, n);
CV_IPP_RUN_FAST(CV_INSTRUMENT_FUN_IPP(ippsLn_32f_A21, src, dst, n) >= 0);
CV_CPU_DISPATCH(log32f, (src, dst, n),
CV_CPU_DISPATCH_MODES_ALL);
@@ -185,7 +180,6 @@ void log64f(const double *src, double *dst, int n)
CV_INSTRUMENT_REGION();
CALL_HAL(log64f, cv_hal_log64f, src, dst, n);
CV_IPP_RUN_FAST(CV_INSTRUMENT_FUN_IPP(ippsLn_64f_A50, src, dst, n) >= 0);
CV_CPU_DISPATCH(log64f, (src, dst, n),
CV_CPU_DISPATCH_MODES_ALL);
+1 -1
View File
@@ -595,7 +595,7 @@ void sqrt64f(const double* src, double* dst, int len)
// Workaround for ICE in MSVS 2015 update 3 (issue #7795)
// CV_AVX is not used here, because generated code is faster in non-AVX mode.
// (tested with disabled IPP on i5-6300U)
#if (defined _MSC_VER && _MSC_VER >= 1900) || defined(__EMSCRIPTEN__)
#if (defined _MSC_VER && _MSC_VER >= 1900 && (defined(_M_IX86) || defined(_M_X64))) || defined(__EMSCRIPTEN__)
void exp32f(const float *src, float *dst, int n)
{
CV_INSTRUMENT_REGION();
+4 -4
View File
@@ -558,7 +558,7 @@ void transposeND(InputArray src_, const std::vector<int>& order, OutputArray dst
CV_CheckEQ(static_cast<size_t>(order_[i]), i, "New order should be a valid permutation of the old one");
}
std::vector<int> newShape(order.size());
AutoBuffer<int> newShape(order.size());
for (size_t i = 0; i < order.size(); ++i)
{
newShape[i] = inp.size[order[i]];
@@ -582,7 +582,7 @@ void transposeND(InputArray src_, const std::vector<int>& order, OutputArray dst
size_t continuous_size = continuous_idx == 0 ? out.total() : out.step1(continuous_idx - 1);
size_t outer_size = out.total() / continuous_size;
std::vector<size_t> steps(order.size());
AutoBuffer<size_t> steps(order.size());
for (int i = 0; i < static_cast<int>(steps.size()); ++i)
{
steps[i] = inp.step1(order[i]);
@@ -1229,7 +1229,7 @@ void broadcast(InputArray _src, InputArray _shape, OutputArray _dst) {
// impl
_dst.create(dims_shape, shape.ptr<int>(), src.type());
Mat dst = _dst.getMat();
std::vector<int> is_same_shape(dims_shape, 0);
AutoBuffer<int> is_same_shape(dims_shape, 0);
for (int i = 0; i < static_cast<int>(shape_src.size()); ++i) {
if (shape_src[i] == ptr_shape[i]) {
is_same_shape[i] = 1;
@@ -1328,7 +1328,7 @@ void broadcast(InputArray _src, InputArray _shape, OutputArray _dst) {
std::memcpy(p_dst + dst_offset, p_src + src_offset, dst.elemSize());
}
// broadcast copy (dst inplace)
std::vector<int> cumulative_shape(dims_shape, 1);
AutoBuffer<int> cumulative_shape(dims_shape, 1);
int total = static_cast<int>(dst.total());
for (int i = dims_shape - 1; i >= 0; --i) {
cumulative_shape[i] = static_cast<int>(total / ptr_shape[i]);
+1 -1
View File
@@ -1330,7 +1330,7 @@ static void reduceMinMax(cv::InputArray src, cv::OutputArray dst, ReduceMode mod
axis = (axis + srcMat.dims) % srcMat.dims;
CV_Assert(srcMat.channels() == 1 && axis >= 0 && axis < srcMat.dims);
std::vector<int> sizes(srcMat.dims);
cv::AutoBuffer<int> sizes(srcMat.dims);
std::copy(srcMat.size.p, srcMat.size.p + srcMat.dims, sizes.begin());
sizes[axis] = 1;
+6 -21
View File
@@ -1682,27 +1682,12 @@ Context& initializeContextFromGL()
if(extensionSize > 0)
{
char* extensions = nullptr;
try {
extensions = new char[extensionSize];
status = clGetDeviceInfo(devices[j], CL_DEVICE_EXTENSIONS, extensionSize, extensions, &extensionSize);
if (status != CL_SUCCESS)
continue;
} catch(...) {
CV_Error(cv::Error::OpenCLInitError, "OpenCL: Exception thrown during device extensions gathering");
}
std::string devString;
if(extensions != nullptr) {
devString = extensions;
delete[] extensions;
}
else {
CV_Error(cv::Error::OpenCLInitError, "OpenCL: Unexpected error during device extensions gathering");
}
std::string devString(extensionSize, '\0');
status = clGetDeviceInfo(devices[j], CL_DEVICE_EXTENSIONS, devString.size(), &devString[0], &extensionSize);
if (status != CL_SUCCESS)
continue;
if (extensionSize > 0 && devString.size() >= extensionSize)
devString.resize(extensionSize - 1);
size_t oldPos = 0;
size_t spacePos = devString.find(' ', oldPos); // extensions string is space delimited
@@ -65,15 +65,15 @@ public:
/*
* a convertor must provide :
* - `operator >> (uchar * & dst)` for writing current binary data to `dst` and moving to next data.
* - `operator bool` for checking if current loaction is valid and not the end.
* - `operator bool` for checking if current location is valid and not the end.
*/
template<typename _to_binary_convertor_t> inline
Base64ContextEmitter & write(_to_binary_convertor_t & convertor)
{
static const size_t BUFFER_MAX_LEN = 1024U;
constexpr size_t BUFFER_MAX_LEN = 1024U;
std::vector<uchar> buffer(BUFFER_MAX_LEN);
uchar * beg = buffer.data();
uchar buffer[BUFFER_MAX_LEN];
uchar * beg = buffer;
uchar * end = beg;
while (convertor) {
+3 -1
View File
@@ -75,7 +75,7 @@ void write( FileStorage& fs, const String& name, const SparseMat& m )
fs << "data" << "[:";
size_t i = 0, n = m.nzcount();
std::vector<const SparseMat::Node*> elems(n);
AutoBuffer<const SparseMat::Node*> elems(n);
SparseMatConstIterator it = m.begin(), it_end = m.end();
for( ; it != it_end; ++it )
@@ -142,6 +142,7 @@ void read(const FileNode& node, Mat& m, const Mat& default_mat)
CV_Assert( !sizes_node.empty() );
dims = (int)sizes_node.size();
CV_Assert( dims > 0 && dims <= CV_MAX_DIM );
sizes_node.readRaw("i", sizes, dims*sizeof(sizes[0]));
m.create(dims, sizes, elem_type);
@@ -180,6 +181,7 @@ void read( const FileNode& node, SparseMat& m, const SparseMat& default_mat )
CV_Assert( !sizes_node.empty() );
int dims = (int)sizes_node.size();
CV_Assert( dims > 0 && dims <= CV_MAX_DIM );
sizes_node.readRaw("i", sizes, dims*sizeof(sizes[0]));
m.create(dims, sizes, elem_type);
+29
View File
@@ -807,6 +807,35 @@ TEST(Core_InputOutput, filestorage_heap_overflow)
EXPECT_EQ(0, remove(name.c_str()));
}
TEST(Core_InputOutput, filestorage_nd_matrix_too_many_dims)
{
// A declared dimension count above CV_MAX_DIM used to write the sizes list
// past the fixed-size sizes[CV_MAX_DIM] stack buffer in cv::read().
std::string sizes;
for (int i = 0; i < CV_MAX_DIM + 8; i++)
sizes += "2, ";
sizes += "2";
const std::string content =
"%YAML:1.0\n---\n"
"m: !!opencv-nd-matrix\n"
" sizes: [ " + sizes + " ]\n"
" dt: f\n"
" data: [ 0., 0. ]\n"
"sm: !!opencv-sparse-matrix\n"
" sizes: [ " + sizes + " ]\n"
" dt: f\n"
" data: [ ]\n";
FileStorage fs(content, FileStorage::READ | FileStorage::MEMORY);
Mat m;
EXPECT_ANY_THROW(fs["m"] >> m);
SparseMat sm;
EXPECT_ANY_THROW(fs["sm"] >> sm);
}
TEST(Core_InputOutput, filestorage_base64_valid_call)
{
const ::testing::TestInfo* const test_info = ::testing::UnitTest::GetInstance()->current_test_info();
+18
View File
@@ -2626,6 +2626,24 @@ TEST(Mat1D, DISABLED_basic)
}
}
TEST(Mat, regression_cvReshapeMatND_continuous)
{
int sizes[] = {2, 3, 4};
Mat mat(3, sizes, CV_32SC1);
CvMatND src = cvMatND(mat);
CvMatND reshaped;
int new_sizes[] = {4, 3, 2};
CvArr* result = 0;
ASSERT_NO_THROW(result = cvReshapeMatND(&src, sizeof(reshaped), &reshaped, 0, 3, new_sizes));
ASSERT_NE((CvArr*)0, result);
EXPECT_EQ(3, reshaped.dims);
EXPECT_EQ(new_sizes[0], reshaped.dim[0].size);
EXPECT_EQ(new_sizes[1], reshaped.dim[1].size);
EXPECT_EQ(new_sizes[2], reshaped.dim[2].size);
EXPECT_EQ(src.data.ptr, reshaped.data.ptr);
}
TEST(Mat, ptrVecni_20044)
{
Mat_<int> m(3,4); m << 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12;
+339
View File
@@ -63,4 +63,343 @@ INSTANTIATE_TEST_CASE_P(/**/, Utils_blobFromImages,
std::vector<int>{16, 2048, 2048})
);
// NCHW, 8U->32F, C3, mean+scale+swapRB at 640x640
using Utils_blobFromImage_8U_NCHW = TestBaseWithParam<std::vector<int>>;
PERF_TEST_P_(Utils_blobFromImage_8U_NCHW, MeanScale_SwapRB) {
std::vector<int> input_shape = GetParam();
Mat input(input_shape, CV_8UC3);
randu(input, 0, 255);
TEST_CYCLE() {
Mat blob = blobFromImage(input, 1.0/255.0, Size(), Scalar(104, 117, 123), true, false, CV_32F);
}
SANITY_CHECK_NOTHING();
}
INSTANTIATE_TEST_CASE_P(/**/, Utils_blobFromImage_8U_NCHW,
Values(std::vector<int>{ 640, 640})
);
// NHWC, 8U->32F, C3
using Utils_blobFromImage_8U_NHWC = TestBaseWithParam<std::vector<int>>;
PERF_TEST_P_(Utils_blobFromImage_8U_NHWC, SwapRB) {
std::vector<int> input_shape = GetParam();
Mat input(input_shape, CV_8UC3);
randu(input, 0, 255);
Image2BlobParams params;
params.scalefactor = Scalar::all(1.0);
params.swapRB = true;
params.datalayout = DNN_LAYOUT_NHWC;
TEST_CYCLE() {
Mat blob = blobFromImageWithParams(input, params);
}
SANITY_CHECK_NOTHING();
}
PERF_TEST_P_(Utils_blobFromImage_8U_NHWC, MeanScale_SwapRB) {
std::vector<int> input_shape = GetParam();
Mat input(input_shape, CV_8UC3);
randu(input, 0, 255);
Image2BlobParams params;
params.scalefactor = Scalar::all(1.0/255.0);
params.mean = Scalar(104, 117, 123);
params.swapRB = true;
params.datalayout = DNN_LAYOUT_NHWC;
TEST_CYCLE() {
Mat blob = blobFromImageWithParams(input, params);
}
SANITY_CHECK_NOTHING();
}
INSTANTIATE_TEST_CASE_P(/**/, Utils_blobFromImage_8U_NHWC,
Values(std::vector<int>{ 224, 224},
std::vector<int>{ 640, 640})
);
// NHWC, 32F->32F, C3
using Utils_blobFromImage_32F_NHWC = TestBaseWithParam<std::vector<int>>;
PERF_TEST_P_(Utils_blobFromImage_32F_NHWC, SwapRB) {
std::vector<int> input_shape = GetParam();
Mat input(input_shape, CV_32FC3);
randu(input, 0.0f, 1.0f);
Image2BlobParams params;
params.scalefactor = Scalar::all(1.0);
params.swapRB = true;
params.datalayout = DNN_LAYOUT_NHWC;
TEST_CYCLE() {
Mat blob = blobFromImageWithParams(input, params);
}
SANITY_CHECK_NOTHING();
}
PERF_TEST_P_(Utils_blobFromImage_32F_NHWC, MeanScale_SwapRB) {
std::vector<int> input_shape = GetParam();
Mat input(input_shape, CV_32FC3);
randu(input, 0.0f, 1.0f);
Image2BlobParams params;
params.scalefactor = Scalar::all(1.0/0.226);
params.mean = Scalar(0.485, 0.456, 0.406);
params.swapRB = true;
params.datalayout = DNN_LAYOUT_NHWC;
TEST_CYCLE() {
Mat blob = blobFromImageWithParams(input, params);
}
SANITY_CHECK_NOTHING();
}
INSTANTIATE_TEST_CASE_P(/**/, Utils_blobFromImage_32F_NHWC,
Values(std::vector<int>{ 224, 224},
std::vector<int>{ 640, 640})
);
// Resize+crop, 8U->32F, C3, mean+scale+swapRB to 640x640
using Utils_blobFromImage_8U_Resize = TestBaseWithParam<std::vector<int>>;
PERF_TEST_P_(Utils_blobFromImage_8U_Resize, NHWC_Crop_MeanScale_SwapRB) {
std::vector<int> input_shape = GetParam();
Mat input(input_shape, CV_8UC3);
randu(input, 0, 255);
Image2BlobParams params;
params.scalefactor = Scalar::all(1.0/255.0);
params.size = Size(640, 640);
params.mean = Scalar(104, 117, 123);
params.swapRB = true;
params.datalayout = DNN_LAYOUT_NHWC;
params.paddingmode = DNN_PMODE_CROP_CENTER;
TEST_CYCLE() {
Mat blob = blobFromImageWithParams(input, params);
}
SANITY_CHECK_NOTHING();
}
PERF_TEST_P_(Utils_blobFromImage_8U_Resize, NCHW_Crop_MeanScale_SwapRB) {
std::vector<int> input_shape = GetParam();
Mat input(input_shape, CV_8UC3);
randu(input, 0, 255);
TEST_CYCLE() {
Mat blob = blobFromImage(input, 1.0/255.0, Size(640, 640), Scalar(104, 117, 123), true, true, CV_32F);
}
SANITY_CHECK_NOTHING();
}
INSTANTIATE_TEST_CASE_P(/**/, Utils_blobFromImage_8U_Resize,
Values(std::vector<int>{ 720, 1280},
std::vector<int>{ 1080, 1920},
std::vector<int>{ 2160, 3840})
);
// Resize+crop, NCHW, 32F->32F, C3, mean+scale+swapRB to 300x300
using Utils_blobFromImage_32F_NCHW_Resize = TestBaseWithParam<std::vector<int>>;
PERF_TEST_P_(Utils_blobFromImage_32F_NCHW_Resize, Crop_MeanScale_SwapRB_To300) {
std::vector<int> input_shape = GetParam();
Mat input(input_shape, CV_32FC3);
randu(input, 0.0f, 1.0f);
TEST_CYCLE() {
Mat blob = blobFromImage(input, 1.0/0.226, Size(300, 300), Scalar(0.485, 0.456, 0.406), true, true, CV_32F);
}
SANITY_CHECK_NOTHING();
}
INSTANTIATE_TEST_CASE_P(/**/, Utils_blobFromImage_32F_NCHW_Resize,
Values(std::vector<int>{ 720, 1280},
std::vector<int>{ 1080, 1920})
);
// Resize+crop, NCHW, 8U->8U, C3
using Utils_blobFromImage_8U_to_8U_Crop = TestBaseWithParam<std::vector<int>>;
PERF_TEST_P_(Utils_blobFromImage_8U_to_8U_Crop, NCHW_SwapRB) {
std::vector<int> input_shape = GetParam();
Mat input(input_shape, CV_8UC3);
randu(input, 0, 255);
TEST_CYCLE() {
Mat blob = blobFromImage(input, 1.0, Size(640, 640), Scalar(), true, true, CV_8U);
}
SANITY_CHECK_NOTHING();
}
PERF_TEST_P_(Utils_blobFromImage_8U_to_8U_Crop, NCHW) {
std::vector<int> input_shape = GetParam();
Mat input(input_shape, CV_8UC3);
randu(input, 0, 255);
TEST_CYCLE() {
Mat blob = blobFromImage(input, 1.0, Size(640, 640), Scalar(), false, true, CV_8U);
}
SANITY_CHECK_NOTHING();
}
INSTANTIATE_TEST_CASE_P(/**/, Utils_blobFromImage_8U_to_8U_Crop,
Values(std::vector<int>{ 1080, 1920},
std::vector<int>{ 2160, 3840})
);
// Resize, NCHW, 8U->8U, C3
using Utils_blobFromImage_8U_to_8U_Resize = TestBaseWithParam<std::vector<int>>;
PERF_TEST_P_(Utils_blobFromImage_8U_to_8U_Resize, NCHW_SwapRB) {
std::vector<int> input_shape = GetParam();
Mat input(input_shape, CV_8UC3);
randu(input, 0, 255);
TEST_CYCLE() {
Mat blob = blobFromImage(input, 1.0, Size(640, 640), Scalar(), true, false, CV_8U);
}
SANITY_CHECK_NOTHING();
}
INSTANTIATE_TEST_CASE_P(/**/, Utils_blobFromImage_8U_to_8U_Resize,
Values(std::vector<int>{ 1080, 1920},
std::vector<int>{ 2160, 3840})
);
// Resize+crop, NCHW, 32F->32F, C1, mean
using Utils_blobFromImage_32F_NCHW_C1 = TestBaseWithParam<std::vector<int>>;
PERF_TEST_P_(Utils_blobFromImage_32F_NCHW_C1, Crop_MeanScale_To224) {
std::vector<int> input_shape = GetParam();
Mat input(input_shape, CV_32FC1);
randu(input, 0.0f, 1.0f);
TEST_CYCLE() {
Mat blob = blobFromImage(input, 1.0/0.226, Size(224, 224), Scalar(0.5), false, true, CV_32F);
}
SANITY_CHECK_NOTHING();
}
PERF_TEST_P_(Utils_blobFromImage_32F_NCHW_C1, Crop_MeanScale_To640) {
std::vector<int> input_shape = GetParam();
Mat input(input_shape, CV_32FC1);
randu(input, 0.0f, 1.0f);
TEST_CYCLE() {
Mat blob = blobFromImage(input, 1.0/0.226, Size(640, 640), Scalar(0.5), false, true, CV_32F);
}
SANITY_CHECK_NOTHING();
}
INSTANTIATE_TEST_CASE_P(/**/, Utils_blobFromImage_32F_NCHW_C1,
Values(std::vector<int>{ 1080, 1920},
std::vector<int>{ 2160, 3840})
);
// Batch=8, NHWC, 8U->32F, C3, mean+scale+swapRB
using Utils_blobFromImages_NoResize = TestBaseWithParam<std::vector<int>>;
PERF_TEST_P_(Utils_blobFromImages_NoResize, NHWC_MeanScale_SwapRB) {
std::vector<int> input_shape = GetParam();
int batch = input_shape.front();
std::vector<int> input_shape_no_batch(input_shape.begin()+1, input_shape.end());
std::vector<Mat> inputs;
for (int i = 0; i < batch; i++) {
Mat input(input_shape_no_batch, CV_8UC3);
randu(input, 0, 255);
inputs.push_back(input);
}
Image2BlobParams params;
params.scalefactor = Scalar::all(1.0/255.0);
params.mean = Scalar(104, 117, 123);
params.swapRB = true;
params.datalayout = DNN_LAYOUT_NHWC;
TEST_CYCLE() {
Mat blob = blobFromImagesWithParams(inputs, params);
}
SANITY_CHECK_NOTHING();
}
INSTANTIATE_TEST_CASE_P(/**/, Utils_blobFromImages_NoResize,
Values(std::vector<int>{8, 640, 640})
);
// Batch=8, resize+crop to 640x640, 8U->32F, C3, mean+scale+swapRB
using Utils_blobFromImages_Resize = TestBaseWithParam<std::vector<int>>;
PERF_TEST_P_(Utils_blobFromImages_Resize, NHWC_Crop_MeanScale_SwapRB) {
std::vector<int> input_shape = GetParam();
int batch = input_shape.front();
std::vector<int> input_shape_no_batch(input_shape.begin()+1, input_shape.end());
std::vector<Mat> inputs;
for (int i = 0; i < batch; i++) {
Mat input(input_shape_no_batch, CV_8UC3);
randu(input, 0, 255);
inputs.push_back(input);
}
Image2BlobParams params;
params.scalefactor = Scalar::all(1.0/255.0);
params.size = Size(640, 640);
params.mean = Scalar(104, 117, 123);
params.swapRB = true;
params.datalayout = DNN_LAYOUT_NHWC;
params.paddingmode = DNN_PMODE_CROP_CENTER;
TEST_CYCLE() {
Mat blob = blobFromImagesWithParams(inputs, params);
}
SANITY_CHECK_NOTHING();
}
PERF_TEST_P_(Utils_blobFromImages_Resize, NCHW_Crop_MeanScale_SwapRB) {
std::vector<int> input_shape = GetParam();
int batch = input_shape.front();
std::vector<int> input_shape_no_batch(input_shape.begin()+1, input_shape.end());
std::vector<Mat> inputs;
for (int i = 0; i < batch; i++) {
Mat input(input_shape_no_batch, CV_8UC3);
randu(input, 0, 255);
inputs.push_back(input);
}
TEST_CYCLE() {
Mat blob = blobFromImages(inputs, 1.0/255.0, Size(640, 640), Scalar(104, 117, 123), true, true, CV_32F);
}
SANITY_CHECK_NOTHING();
}
INSTANTIATE_TEST_CASE_P(/**/, Utils_blobFromImages_Resize,
Values(std::vector<int>{8, 720, 1280},
std::vector<int>{8, 1080, 1920})
);
}
+5 -2
View File
@@ -534,8 +534,10 @@ namespace cv {
std::vector<float> usedAnchors(numAnchors * 2);
for (int i = 0; i < numAnchors; ++i)
{
usedAnchors[i * 2] = anchors[mask[i] * 2];
usedAnchors[i * 2 + 1] = anchors[mask[i] * 2 + 1];
const int m = mask[i];
CV_Assert(m >= 0 && static_cast<size_t>(m) * 2 + 1 < anchors.size());
usedAnchors[i * 2] = anchors[m * 2];
usedAnchors[i * 2 + 1] = anchors[m * 2 + 1];
}
cv::Mat biasData_mat = cv::Mat(1, numAnchors * 2, CV_32F, &usedAnchors[0]).clone();
@@ -835,6 +837,7 @@ namespace cv {
tensor_shape[0] = 0;
for (size_t k = 0; k < layers_vec.size(); ++k) {
layers_vec[k] = layers_vec[k] >= 0 ? layers_vec[k] : (layers_vec[k] + layers_counter);
CV_Assert(layers_vec[k] >= 0 && static_cast<size_t>(layers_vec[k]) < net->out_channels_vec.size());
tensor_shape[0] += net->out_channels_vec[layers_vec[k]];
}
+4
View File
@@ -255,6 +255,10 @@ struct TorchImporter
void readTorchStorage(int index, int type = -1)
{
long size = readLong();
// size is read as a 64-bit value but Mat::create() takes int columns, so a
// value above INT_MAX is truncated for the allocation while the THFile_read*Raw
// calls below still consume the full 64-bit count, overflowing the buffer.
CV_Assert(size >= 0 && size <= INT_MAX);
Mat storageMat;
switch (type)
+3
View File
@@ -8052,6 +8052,9 @@ void AGAST(InputArray _img, std::vector<KeyPoint>& keypoints, int threshold, boo
case AgastFeatureDetector::OAST_9_16:
OAST_9_16(_img, kpts, threshold);
break;
default:
CV_Error_(Error::StsBadArg,
("Unknown AgastFeatureDetector detector type: %d", static_cast<int>(type)));
}
cv::Mat img = _img.getMat();
+6 -4
View File
@@ -332,11 +332,13 @@ void SimpleBlobDetectorImpl::findBlobs(InputArray _image, InputArray _binaryImag
//compute blob radius
{
std::vector<double> dists;
for (size_t pointIdx = 0; pointIdx < contours[contourIdx].size(); pointIdx++)
const std::vector<cv::Point>& contour = contours[contourIdx];
const size_t contourSize = contour.size();
AutoBuffer<double> dists(contourSize);
for (size_t pointIdx = 0; pointIdx < contourSize; pointIdx++)
{
Point2d pt = contours[contourIdx][pointIdx];
dists.push_back(norm(center.location - pt));
const Point2d& pt = contour[pointIdx];
dists[pointIdx] = norm(center.location - pt);
}
std::sort(dists.begin(), dists.end());
center.radius = (dists[(dists.size() - 1) / 2] + dists[dists.size() / 2]) / 2.;
+4 -2
View File
@@ -104,6 +104,8 @@ public:
}
virtual void setPatternScale(float _patternScale) CV_OVERRIDE
{
CV_CheckGT(_patternScale, 0.f, "patternScale must be positive");
patternScale = _patternScale;
std::vector<float> rList;
std::vector<int> nList;
@@ -2034,8 +2036,8 @@ BriskScaleSpace::subpixel2D(const int s_0_0, const int s_0_1, const int s_0_2, c
int tmp4 = tmp3 - 2 * tmp2;
int coeff3 = -3 * (tmp3 + s_0_1 - s_2_1);
int coeff4 = -3 * (tmp4 + s_1_0 - s_1_2);
int coeff5 = (s_0_0 - s_0_2 - s_2_0 + s_2_2) << 2;
int coeff6 = -(s_0_0 + s_0_2 - ((s_1_0 + s_0_1 + s_1_2 + s_2_1) << 1) - 5 * s_1_1 + s_2_0 + s_2_2) << 1;
int coeff5 = (s_0_0 - s_0_2 - s_2_0 + s_2_2) * 4;
int coeff6 = -(s_0_0 + s_0_2 - ((s_1_0 + s_0_1 + s_1_2 + s_2_1) * 2) - 5 * s_1_1 + s_2_0 + s_2_2) * 2;
// 2nd derivative test:
int H_det = 4 * coeff1 * coeff2 - coeff5 * coeff5;
+10 -2
View File
@@ -441,6 +441,14 @@ void FAST(InputArray _img, std::vector<KeyPoint>& keypoints, int threshold, bool
{
CV_INSTRUMENT_REGION();
if (type != FastFeatureDetector::TYPE_5_8 &&
type != FastFeatureDetector::TYPE_7_12 &&
type != FastFeatureDetector::TYPE_9_16)
{
CV_Error_(Error::StsBadArg,
("Unknown FastFeatureDetector detector type: %d", static_cast<int>(type)));
}
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,
@@ -549,7 +557,7 @@ public:
else if(prop == FAST_N)
type = static_cast<FastFeatureDetector::DetectorType>(cvRound(value));
else
CV_Error(Error::StsBadArg, "");
CV_Error_(Error::StsBadArg, ("Unknown FastFeatureDetector property: %d", prop));
}
double get(int prop) const
@@ -560,7 +568,7 @@ public:
return nonmaxSuppression;
if(prop == FAST_N)
return static_cast<int>(type);
CV_Error(Error::StsBadArg, "");
CV_Error_(Error::StsBadArg, ("Unknown FastFeatureDetector property: %d", prop));
return 0;
}
+2 -2
View File
@@ -221,8 +221,8 @@ struct KeyPoint_LessThan
void KeyPointsFilter::removeDuplicated( std::vector<KeyPoint>& keypoints )
{
int i, j, n = (int)keypoints.size();
std::vector<int> kpidx(n);
std::vector<uchar> mask(n, (uchar)1);
AutoBuffer<int> kpidx(n);
AutoBuffer<uchar> mask(n, (uchar)1);
for( i = 0; i < n; i++ )
kpidx[i] = i;
+21 -3
View File
@@ -839,7 +839,7 @@ static void computeKeyPoints(const Mat& imagePyramid,
#endif
int i, nkeypoints, level, nlevels = (int)layerInfo.size();
std::vector<int> nfeaturesPerLevel(nlevels);
AutoBuffer<int> nfeaturesPerLevel(nlevels);
// fill the extractors and descriptors for the corresponding scales
float factor = (float)(1.0 / scaleFactor);
@@ -877,7 +877,7 @@ static void computeKeyPoints(const Mat& imagePyramid,
allKeypoints.clear();
std::vector<KeyPoint> keypoints;
std::vector<int> counters(nlevels);
AutoBuffer<int> counters(nlevels);
keypoints.reserve(nfeaturesPerLevel[0]*2);
for( level = 0; level < nlevels; level++ )
@@ -1266,7 +1266,25 @@ void ORB_Impl::detectAndCompute( InputArray _image, InputArray _mask,
Ptr<ORB> ORB::create(int nfeatures, float scaleFactor, int nlevels, int edgeThreshold,
int firstLevel, int wta_k, ORB::ScoreType scoreType, int patchSize, int fastThreshold)
{
CV_Assert(firstLevel >= 0);
CV_CheckGE(nfeatures, 0, "nfeatures must be non-negative");
CV_CheckGT(scaleFactor, 1.f, "scaleFactor must be greater than 1");
CV_CheckGT(nlevels, 0, "nlevels must be positive");
CV_CheckGE(edgeThreshold, 0, "edgeThreshold must be non-negative");
CV_CheckGE(firstLevel, 0, "firstLevel must be non-negative");
CV_CheckGE(patchSize, 2, "patchSize must be at least 2");
if (wta_k != 2 && wta_k != 3 && wta_k != 4)
{
CV_Error_(Error::StsBadArg,
("wta_k must be 2, 3, or 4, but got %d", wta_k));
}
if (scoreType != ORB::HARRIS_SCORE && scoreType != ORB::FAST_SCORE)
{
CV_Error_(Error::StsBadArg,
("Unknown ORB score type: %d", static_cast<int>(scoreType)));
}
return makePtr<ORB_Impl>(nfeatures, scaleFactor, nlevels, edgeThreshold,
firstLevel, wta_k, scoreType, patchSize, fastThreshold);
}
+1 -1
View File
@@ -225,7 +225,7 @@ void SIFT_Impl::buildGaussianPyramid( const Mat& base, std::vector<Mat>& pyr, in
{
CV_TRACE_FUNCTION();
std::vector<double> sig(nOctaveLayers + 3);
AutoBuffer<double> sig(nOctaveLayers + 3);
pyr.resize(nOctaves*(nOctaveLayers + 3));
// precompute Gaussian sigmas using the following formula:
+10
View File
@@ -135,4 +135,14 @@ void CV_AgastTest::run( int )
TEST(Features2d_AGAST, regression) { CV_AgastTest test; test.safe_run(); }
TEST(Features2d_AGAST, invalidDetectorType)
{
Mat img = Mat::zeros(16, 16, CV_8UC1);
vector<KeyPoint> keypoints;
EXPECT_THROW(AGAST(img, keypoints, 10, true,
static_cast<AgastFeatureDetector::DetectorType>(-1)),
cv::Exception);
}
}} // namespace
+6
View File
@@ -105,4 +105,10 @@ void CV_BRISKTest::run( int )
TEST(Features2d_BRISK, regression) { CV_BRISKTest test; test.safe_run(); }
TEST(Features2d_BRISK, invalidCreateParameters)
{
EXPECT_THROW(BRISK::create(30, 3, 0.0f), cv::Exception);
EXPECT_THROW(BRISK::create(30, 3, -1.0f), cv::Exception);
}
}} // namespace
+10
View File
@@ -165,4 +165,14 @@ TEST(Features2d_FAST, noNMS)
ASSERT_EQ( 0, cvtest::norm(gt_kps, kps, NORM_L2));
}
TEST(Features2d_FAST, invalidDetectorType)
{
Mat img = Mat::zeros(16, 16, CV_8UC1);
vector<KeyPoint> keypoints;
EXPECT_THROW(FAST(img, keypoints, 10, true,
static_cast<FastFeatureDetector::DetectorType>(-1)),
cv::Exception);
}
}} // namespace
+11
View File
@@ -195,4 +195,15 @@ TEST(Features2D_ORB, MaskValue)
ASSERT_EQ(countNonZero(diff), 0);
}
TEST(Features2D_ORB, invalidCreateParameters)
{
EXPECT_THROW(ORB::create(500, 1.0f), cv::Exception);
EXPECT_THROW(ORB::create(500, 1.2f, 0), cv::Exception);
EXPECT_THROW(ORB::create(500, 1.2f, 8, 31, 0, 5), cv::Exception);
EXPECT_THROW(ORB::create(500, 1.2f, 8, 31, 0, 2,
static_cast<ORB::ScoreType>(-1)), cv::Exception);
EXPECT_THROW(ORB::create(500, 1.2f, 8, 31, 0, 2,
ORB::HARRIS_SCORE, 1), cv::Exception);
}
}} // namespace
@@ -47,8 +47,8 @@ void find_nearest(const Matrix<typename Distance::ElementType>& dataset, typenam
typedef typename Distance::ResultType DistanceType;
int n = nn + skip;
std::vector<int> match(n);
std::vector<DistanceType> dists(n);
cv::AutoBuffer<int> match(n);
cv::AutoBuffer<DistanceType> dists(n);
dists[0] = distance(dataset[0], query, dataset.cols);
match[0] = 0;
@@ -686,8 +686,8 @@ private:
return;
}
std::vector<int> centers(branching);
std::vector<int> labels(indices_length);
cv::AutoBuffer<int> centers(branching);
cv::AutoBuffer<int> labels(indices_length);
int centers_length;
(this->*chooseCenters)(branching, dsindices, indices_length, &centers[0], centers_length);
@@ -99,8 +99,8 @@ float search_with_ground_truth(NNIndex<Distance>& index, const Matrix<typename D
KNNResultSet<DistanceType> resultSet(nn+skipMatches);
SearchParams searchParams(checks);
std::vector<int> indices(nn+skipMatches);
std::vector<DistanceType> dists(nn+skipMatches);
cv::AutoBuffer<int> indices(nn+skipMatches);
cv::AutoBuffer<DistanceType> dists(nn+skipMatches);
int* neighbors = &indices[skipMatches];
int correct = 0;
@@ -490,7 +490,7 @@ inline LshStats LshTable<unsigned char>::getStats() const
!= end; )
if (*iterator < bin_end) {
if (is_new_bin) {
stats.size_histogram_.push_back(std::vector<unsigned int>(3, 0));
stats.size_histogram_.emplace_back(3, 0);
stats.size_histogram_.back()[0] = bin_start;
stats.size_histogram_.back()[1] = bin_end - 1;
is_new_bin = false;
+6
View File
@@ -160,6 +160,12 @@ bool ExifReader::processRawProfile(const char* profile, size_t profile_len) {
}
++end;
// the payload starts with a 6-byte "Exif\0\0" header, so a shorter declared
// length underflows the size and pointer handed to parseExif() below
if (expected_length < 6) {
return false;
}
// 'end' now points to the profile payload.
std::string payload = HexStringToBytes(end, expected_length);
if (payload.size() == 0) return false;
+11 -11
View File
@@ -175,28 +175,28 @@ rgb_convert (void *src, void *target, int width, int target_channels, int target
*/
static void
basic_conversion (void *src, const struct channel_layout *layout, int src_sampe_size,
basic_conversion (void *src, const struct channel_layout *layout, int src_sample_size,
int src_width, void *target, int target_channels, int target_depth, bool use_rgb)
{
switch (target_depth) {
case CV_8U:
{
uchar *d = (uchar *)target, *s = (uchar *)src,
*end = ((uchar *)src) + src_width;
*end = ((uchar *)src) + src_width * src_sample_size;
switch (target_channels) {
case 1:
for( ; s < end; d += 3, s += src_sampe_size )
d[0] = d[1] = d[2] = s[layout->graychan];
for( ; s < end; d += 1, s += src_sample_size )
d[0] = s[layout->graychan];
break;
case 3:
if (use_rgb)
for( ; s < end; d += 3, s += src_sampe_size ) {
for( ; s < end; d += 3, s += src_sample_size ) {
d[0] = s[layout->rchan];
d[1] = s[layout->gchan];
d[2] = s[layout->bchan];
}
else
for( ; s < end; d += 3, s += src_sampe_size ) {
for( ; s < end; d += 3, s += src_sample_size ) {
d[0] = s[layout->bchan];
d[1] = s[layout->gchan];
d[2] = s[layout->rchan];
@@ -210,21 +210,21 @@ basic_conversion (void *src, const struct channel_layout *layout, int src_sampe_
case CV_16U:
{
ushort *d = (ushort *)target, *s = (ushort *)src,
*end = ((ushort *)src) + src_width;
*end = ((ushort *)src) + src_width * src_sample_size;
switch (target_channels) {
case 1:
for( ; s < end; d += 3, s += src_sampe_size )
d[0] = d[1] = d[2] = s[layout->graychan];
for( ; s < end; d += 1, s += src_sample_size )
d[0] = s[layout->graychan];
break;
case 3:
if (use_rgb)
for( ; s < end; d += 3, s += src_sampe_size ) {
for( ; s < end; d += 3, s += src_sample_size ) {
d[0] = s[layout->rchan];
d[1] = s[layout->gchan];
d[2] = s[layout->bchan];
}
else
for( ; s < end; d += 3, s += src_sampe_size ) {
for( ; s < end; d += 3, s += src_sample_size ) {
d[0] = s[layout->bchan];
d[1] = s[layout->gchan];
d[2] = s[layout->rchan];
+31
View File
@@ -564,6 +564,37 @@ TEST(Imgcodecs_Pam, read_write)
remove(writefile.c_str());
remove(writefile_no_param.c_str());
}
// Regression test: a 2-channel (GRAYSCALE_ALPHA) PAM decoded as single channel
// used to overflow the output row in basic_conversion() (3 bytes written per
// source pixel into a 1-channel row). Verify it decodes safely and correctly.
TEST(Imgcodecs_Pam, decode_graya_as_gray)
{
const int width = 9, height = 3; // odd width to expose off-by-row overflow
std::string header = cv::format(
"P7\nWIDTH %d\nHEIGHT %d\nDEPTH 2\nMAXVAL 255\n"
"TUPLTYPE GRAYSCALE_ALPHA\nENDHDR\n", width, height);
std::vector<uchar> buf(header.begin(), header.end());
Mat gray_ref(height, width, CV_8UC1);
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
{
uchar gray = (uchar)((y * width + x) * 7 + 1);
uchar alpha = (uchar)(255 - gray);
gray_ref.at<uchar>(y, x) = gray;
buf.push_back(gray); // channel 0: gray
buf.push_back(alpha); // channel 1: alpha (must be ignored)
}
Mat decoded;
ASSERT_NO_THROW(decoded = imdecode(buf, IMREAD_GRAYSCALE));
ASSERT_FALSE(decoded.empty());
EXPECT_EQ(width, decoded.cols);
EXPECT_EQ(height, decoded.rows);
EXPECT_EQ(1, decoded.channels());
EXPECT_EQ(0, cvtest::norm(gray_ref, decoded, NORM_INF));
}
#endif
#ifdef HAVE_IMGCODEC_PFM
+37
View File
@@ -0,0 +1,37 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html
#include "perf_precomp.hpp"
#include <cmath>
namespace opencv_test { namespace {
typedef tuple<int, int> EMD_Size_Dim_t;
typedef perf::TestBaseWithParam<EMD_Size_Dim_t> EMD_Fixture;
PERF_TEST_P(EMD_Fixture, L1_Distance, testing::Combine(
testing::Values(100, 500, 1000),
testing::Values(3, 64)
))
{
int size = get<0>(GetParam());
int dims = get<1>(GetParam());
Mat sign1(size, dims + 1, CV_32FC1);
Mat sign2(size, dims + 1, CV_32FC1);
theRNG().fill(sign1, RNG::UNIFORM, 0.1, 1.0);
theRNG().fill(sign2, RNG::UNIFORM, 0.1, 1.0);
declare.in(sign1, sign2);
TEST_CYCLE()
{
cv::EMD(sign1, sign2, cv::DIST_L1);
}
SANITY_CHECK_NOTHING();
}
}}
@@ -98,8 +98,8 @@ static bool ocl_bilateralFilter_8u(InputArray _src, OutputArray _dst, int d,
return false;
copyMakeBorder(src, temp, radius, radius, radius, radius, borderType);
std::vector<float> _space_weight(d * d);
std::vector<int> _space_ofs(d * d);
AutoBuffer<float> _space_weight(d * d);
AutoBuffer<int> _space_ofs(d * d);
float * const space_weight = &_space_weight[0];
int * const space_ofs = &_space_ofs[0];
@@ -188,9 +188,9 @@ bilateralFilter_8u( const Mat& src, Mat& dst, int d,
Mat temp;
copyMakeBorder( src, temp, radius, radius, radius, radius, borderType );
std::vector<float> _color_weight(cn*256);
std::vector<float> _space_weight(d*d);
std::vector<int> _space_ofs(d*d);
AutoBuffer<float> _color_weight(cn*256);
AutoBuffer<float> _space_weight(d*d);
AutoBuffer<int> _space_ofs(d*d);
float* color_weight = &_color_weight[0];
float* space_weight = &_space_weight[0];
int* space_ofs = &_space_ofs[0];
@@ -283,15 +283,15 @@ bilateralFilter_32f( const Mat& src, Mat& dst, int d,
copyMakeBorder( src, temp, radius, radius, radius, radius, borderType );
// allocate lookup tables
std::vector<float> _space_weight(d*d);
std::vector<int> _space_ofs(d*d);
AutoBuffer<float> _space_weight(d*d);
AutoBuffer<int> _space_ofs(d*d);
float* space_weight = &_space_weight[0];
int* space_ofs = &_space_ofs[0];
// assign a length which is slightly more than needed
len = (float)(maxValSrc - minValSrc) * cn;
kExpNumBins = kExpNumBinsPerChannel * cn;
std::vector<float> _expLUT(kExpNumBins+2);
AutoBuffer<float> _expLUT(kExpNumBins+2);
float* expLUT = &_expLUT[0];
scale_index = kExpNumBins/len;
+12 -8
View File
@@ -1160,20 +1160,23 @@ static LABLUVLUT_s16_t initLUTforLABLUVs16(const softfloat & un, const softfloat
AutoBuffer<int16_t> RGB2Labprev(LAB_LUT_DIM*LAB_LUT_DIM*LAB_LUT_DIM*3);
AutoBuffer<int16_t> RGB2Luvprev(LAB_LUT_DIM*LAB_LUT_DIM*LAB_LUT_DIM*3);
for(int p = 0; p < LAB_LUT_DIM; p++)
softfloat gammaTab[LAB_LUT_DIM];
for(int n = 0; n < LAB_LUT_DIM; n++)
gammaTab[n] = applyGamma(softfloat(n)/lld);
cv::parallel_for_(cv::Range(0, LAB_LUT_DIM), [&](const cv::Range& prange)
{
for(int p = prange.start; p < prange.end; p++)
{
for(int q = 0; q < LAB_LUT_DIM; q++)
{
for(int r = 0; r < LAB_LUT_DIM; r++)
{
int idx = p*3 + q*LAB_LUT_DIM*3 + r*LAB_LUT_DIM*LAB_LUT_DIM*3;
softfloat R = softfloat(p)/lld;
softfloat G = softfloat(q)/lld;
softfloat B = softfloat(r)/lld;
R = applyGamma(R);
G = applyGamma(G);
B = applyGamma(B);
softfloat R = gammaTab[p];
softfloat G = gammaTab[q];
softfloat B = gammaTab[r];
//RGB 2 Lab LUT building
{
@@ -1214,6 +1217,7 @@ static LABLUVLUT_s16_t initLUTforLABLUVs16(const softfloat & un, const softfloat
}
}
}
});
int16_t *RGB2LabLUT_s16 = cv::allocSingleton<int16_t>(LAB_LUT_DIM*LAB_LUT_DIM*LAB_LUT_DIM*3*8);
int16_t *RGB2LuvLUT_s16 = cv::allocSingleton<int16_t>(LAB_LUT_DIM*LAB_LUT_DIM*LAB_LUT_DIM*3*8);
+16 -16
View File
@@ -1153,10 +1153,10 @@ namespace cv{
//Array used to store info and labeled pixel by each thread.
//Different threads affect different memory location of chunksSizeAndLabels
const int chunksSizeAndLabelsSize = roundUp(h, 2);
std::vector<int> chunksSizeAndLabels(chunksSizeAndLabelsSize);
AutoBuffer<int> chunksSizeAndLabels(chunksSizeAndLabelsSize);
//Tree of labels
std::vector<LabelT> P(Plength, 0);
AutoBuffer<LabelT> P(Plength, 0);
//First label is for background
//P[0] = 0;
@@ -1176,7 +1176,7 @@ namespace cv{
}
//Array for statistics data
std::vector<StatsOp> sopArray(h);
AutoBuffer<StatsOp> sopArray(h);
sop.init(nLabels);
//Second scan
@@ -1218,7 +1218,7 @@ namespace cv{
// ............
const size_t Plength = size_t(((h + 1) / 2) * size_t((w + 1) / 2)) + 1;
std::vector<LabelT> P_(Plength, 0);
AutoBuffer<LabelT> P_(Plength, 0);
LabelT *P = P_.data();
//P[0] = 0;
LabelT lunique = 1;
@@ -1782,10 +1782,10 @@ namespace cv{
//Array used to store info and labeled pixel by each thread.
//Different threads affect different memory location of chunksSizeAndLabels
std::vector<int> chunksSizeAndLabels(roundUp(h, 2));
AutoBuffer<int> chunksSizeAndLabels(roundUp(h, 2));
//Tree of labels
std::vector<LabelT> P_(Plength, 0);
AutoBuffer<LabelT> P_(Plength, 0);
LabelT* P = P_.data();
//First label is for background
//P[0] = 0;
@@ -1806,7 +1806,7 @@ namespace cv{
}
//Array for statistics dataof threads
std::vector<StatsOp> sopArray(h);
AutoBuffer<StatsOp> sopArray(h);
sop.init(nLabels);
//Second scan
@@ -1842,7 +1842,7 @@ namespace cv{
// ............
const size_t Plength = size_t((size_t(h) * size_t(w) + 1) / 2) + 1;
std::vector<LabelT> P_(Plength, 0);
AutoBuffer<LabelT> P_(Plength, 0);
LabelT* P = P_.data();
P[0] = 0;
LabelT lunique = 1;
@@ -2315,10 +2315,10 @@ namespace cv{
//Array used to store info and labeled pixel by each thread.
//Different threads affect different memory location of chunksSizeAndLabels
std::vector<int> chunksSizeAndLabels(roundUp(h, 2));
AutoBuffer<int> chunksSizeAndLabels(roundUp(h, 2));
//Tree of labels
std::vector<LabelT> P_(Plength, 0);
AutoBuffer<LabelT> P_(Plength, 0);
LabelT *P = P_.data();
//First label is for background
//P[0] = 0;
@@ -2352,7 +2352,7 @@ namespace cv{
}
//Array for statistics dataof threads
std::vector<StatsOp> sopArray(h);
AutoBuffer<StatsOp> sopArray(h);
sop.init(nLabels);
//Second scan
@@ -2387,7 +2387,7 @@ namespace cv{
//Obviously, 4-way connectivity upper bound is also good for 8-way connectivity labeling
const size_t Plength = (size_t(h) * size_t(w) + 1) / 2 + 1;
//array P for equivalences resolution
std::vector<LabelT> P_(Plength, 0);
AutoBuffer<LabelT> P_(Plength, 0);
LabelT *P = P_.data();
//first label is for background pixels
//P[0] = 0;
@@ -4265,10 +4265,10 @@ namespace cv{
//Array used to store info and labeled pixel by each thread.
//Different threads affect different memory location of chunksSizeAndLabels
const int chunksSizeAndLabelsSize = roundUp(h, 2);
std::vector<int> chunksSizeAndLabels(chunksSizeAndLabelsSize);
AutoBuffer<int> chunksSizeAndLabels(chunksSizeAndLabelsSize);
//Tree of labels
std::vector<LabelT> P(Plength, 0);
AutoBuffer<LabelT> P(Plength, 0);
//First label is for background
//P[0] = 0;
@@ -4288,7 +4288,7 @@ namespace cv{
}
//Array for statistics data
std::vector<StatsOp> sopArray(h);
AutoBuffer<StatsOp> sopArray(h);
sop.init(nLabels);
//Second scan
@@ -4323,7 +4323,7 @@ namespace cv{
//............
const size_t Plength = size_t(((h + 1) / 2) * size_t((w + 1) / 2)) + 1;
std::vector<LabelT> P_(Plength, 0);
AutoBuffer<LabelT> P_(Plength, 0);
LabelT *P = P_.data();
//P[0] = 0;
LabelT lunique = 1;
+1 -1
View File
@@ -101,7 +101,7 @@ static void getSobelKernels( OutputArray _kx, OutputArray _ky,
if( _ksize % 2 == 0 || _ksize > 31 )
CV_Error( cv::Error::StsOutOfRange, "The kernel size must be odd and not larger than 31" );
std::vector<int> kerI(std::max(ksizeX, ksizeY) + 1);
AutoBuffer<int> kerI(std::max(ksizeX, ksizeY) + 1);
CV_Assert( dx >= 0 && dy >= 0 && dx+dy > 0 );
+1
View File
@@ -1844,6 +1844,7 @@ void line( InputOutputArray _img, Point pt1, Point pt2, const Scalar& color,
void arrowedLine(InputOutputArray img, Point pt1, Point pt2, const Scalar& color,
int thickness, int line_type, int shift, double tipLength)
{
CV_Assert( tipLength > 0.0 && tipLength <= 1.0 );
CV_INSTRUMENT_REGION();
const double tipSize = norm(pt1-pt2)*tipLength; // Factor to normalize the size of the tip depending on the length of the arrow
+51 -92
View File
@@ -442,119 +442,78 @@ double EMDSolver::calcFlow(Mat* flow_) const
int EMDSolver::findBasicVars() const
{
int i, j;
int u_cfound, v_cfound;
Node1D u0_head, u1_head, *cur_u, *prev_u;
Node1D v0_head, v1_head, *cur_v, *prev_v;
bool found;
CV_Assert(u != 0 && v != 0);
/* initialize the rows list (u) and the columns list (v) */
u0_head.next = u;
for (i = 0; i < ssize; i++)
{
u[i].next = u + i + 1;
}
u[ssize - 1].next = 0;
u1_head.next = 0;
// 1. Initialize status flags using contiguous memory to eliminate pointer chasing
AutoBuffer<char> computed_buf(ssize + dsize);
char* row_computed = computed_buf.data();
char* col_computed = computed_buf.data() + ssize;
memset(row_computed, 0, ssize + dsize);
v0_head.next = ssize > 1 ? v + 1 : 0;
for (i = 1; i < dsize; i++)
{
v[i].next = v + i + 1;
}
v[dsize - 1].next = 0;
v1_head.next = 0;
// 2. Create BFS queues
AutoBuffer<int> queue_buf(ssize + dsize);
int* row_queue = queue_buf.data();
int* col_queue = queue_buf.data() + ssize;
/* there are ssize+dsize variables but only ssize+dsize-1 independent equations,
so set v[0]=0 */
int row_head = 0, row_tail = 0;
int col_head = 0, col_tail = 0;
// Initial condition: enqueue column 0 as the root node and set its value to 0
v[0].val = 0;
v1_head.next = v;
v1_head.next->next = 0;
col_computed[0] = true;
col_queue[col_tail++] = 0;
/* loop until all variables are found */
u_cfound = v_cfound = 0;
while (u_cfound < ssize || v_cfound < dsize)
int u_cfound = 0;
int v_cfound = 1;
// 3. Dual-queue interactive BFS traversal over the spanning tree (Time Complexity: O(N + M))
while (row_head < row_tail || col_head < col_tail)
{
found = false;
if (v_cfound < dsize)
// Process currently marked columns to update their connected rows
while (col_head < col_tail)
{
/* loop over all marked columns */
prev_v = &v1_head;
cur_v = v1_head.next;
found = found || (cur_v != 0);
for (; cur_v != 0; cur_v = cur_v->next)
{
float cur_v_val = cur_v->val;
int j = col_queue[col_head++];
float cur_v_val = v[j].val;
j = (int)(cur_v - v);
/* find the variables in column j */
prev_u = &u0_head;
for (cur_u = u0_head.next; cur_u != 0;)
// Use adjacency list cols_x to directly access rows connected to column j, avoiding full scans
for (Node2D* xp = cols_x[j]; xp != 0; xp = xp->next[1])
{
int i = xp->i;
if (!row_computed[i])
{
i = (int)(cur_u - u);
if (getIsX(i, j))
{
/* compute u[i] */
cur_u->val = getCost(i, j) - cur_v_val;
/* ...and add it to the marked list */
prev_u->next = cur_u->next;
cur_u->next = u1_head.next;
u1_head.next = cur_u;
cur_u = prev_u->next;
}
else
{
prev_u = cur_u;
cur_u = cur_u->next;
}
u[i].val = getCost(i, j) - cur_v_val;
row_computed[i] = true;
row_queue[row_tail++] = i; // Enqueue the newly resolved row
u_cfound++;
}
prev_v->next = cur_v->next;
v_cfound++;
}
}
if (u_cfound < ssize)
// Process currently marked rows to update their connected columns
while (row_head < row_tail)
{
/* loop over all marked rows */
prev_u = &u1_head;
cur_u = u1_head.next;
found = found || (cur_u != 0);
for (; cur_u != 0; cur_u = cur_u->next)
int i = row_queue[row_head++];
float cur_u_val = u[i].val;
// Use adjacency list rows_x to directly access columns connected to row i
for (Node2D* xp = rows_x[i]; xp != 0; xp = xp->next[0])
{
float cur_u_val = cur_u->val;
i = (int)(cur_u - u);
/* find the variables in rows i */
prev_v = &v0_head;
for (cur_v = v0_head.next; cur_v != 0;)
int j = xp->j;
if (!col_computed[j])
{
j = (int)(cur_v - v);
if (getIsX(i, j))
{
/* compute v[j] */
cur_v->val = getCost(i, j) - cur_u_val;
/* ...and add it to the marked list */
prev_v->next = cur_v->next;
cur_v->next = v1_head.next;
v1_head.next = cur_v;
cur_v = prev_v->next;
}
else
{
prev_v = cur_v;
cur_v = cur_v->next;
}
v[j].val = getCost(i, j) - cur_u_val;
col_computed[j] = true;
col_queue[col_tail++] = j; // Enqueue the newly resolved column
v_cfound++;
}
prev_u->next = cur_u->next;
u_cfound++;
}
}
if (!found)
return -1;
}
// If the number of traversed nodes is insufficient, the graph is disconnected and the spanning tree is incomplete
if (u_cfound < ssize || v_cfound < dsize)
return -1;
return 0;
}
@@ -1008,4 +967,4 @@ float cv::wrapperEMD(InputArray _sign1,
OutputArray _flow)
{
return EMD(_sign1, _sign2, distType, _cost, lowerBound.get(), _flow);
}
}
+3 -2
View File
@@ -494,7 +494,8 @@ int cv::floodFill( InputOutputArray _image, InputOutputArray _mask,
if( connectivity != 0 && connectivity != 4 && connectivity != 8 )
CV_Error( cv::Error::StsBadFlag, "Connectivity must be 4, 0(=4) or 8" );
if( _mask.empty() )
bool noUserMask = _mask.empty();
if( noUserMask )
{
_mask.create( size.height + 2, size.width + 2, CV_8UC1 );
_mask.setTo(0);
@@ -508,7 +509,7 @@ int cv::floodFill( InputOutputArray _image, InputOutputArray _mask,
Mat mask_inner = mask( Rect(1, 1, mask.cols - 2, mask.rows - 2) );
copyMakeBorder( mask_inner, mask, 1, 1, 1, 1, BORDER_ISOLATED | BORDER_CONSTANT, Scalar(1) );
bool is_simple = mask.empty() && (flags & FLOODFILL_MASK_ONLY) == 0;
bool is_simple = noUserMask && (flags & FLOODFILL_MASK_ONLY) == 0;
for( i = 0; i < cn; i++ )
{
+81 -30
View File
@@ -184,31 +184,82 @@ HoughLinesStandard( InputArray src, OutputArray lines, int type,
irho, tabSin, tabCos);
// stage 1. fill accumulator
if (use_edgeval) {
for( i = 0; i < height; i++ )
for( j = 0; j < width; j++ )
{
if( image[i * step + j] != 0 )
for(int n = 0; n < numangle; n++ )
{
int r = cvRound( j * tabCos[n] + i * tabSin[n] );
r += (numrho - 1) / 2;
accum[(n + 1) * (numrho + 2) + r + 1] += image[i * step + j];
}
}
// Use the serial implementation for small numangle values to avoid parallel overhead.
constexpr int kParallelAngleThreshold = 100;
if (numangle < kParallelAngleThreshold) {
if (use_edgeval) {
for( i = 0; i < height; i++ )
for( j = 0; j < width; j++ )
{
if( image[i * step + j] != 0 )
for(int n = 0; n < numangle; n++ )
{
int r = cvRound( j * tabCos[n] + i * tabSin[n] );
r += (numrho - 1) / 2;
accum[(n + 1) * (numrho + 2) + r + 1] += image[i * step + j];
}
}
} else {
for( i = 0; i < height; i++ )
for( j = 0; j < width; j++ )
{
if( image[i * step + j] != 0 )
for(int n = 0; n < numangle; n++ )
{
int r = cvRound( j * tabCos[n] + i * tabSin[n] );
r += (numrho - 1) / 2;
accum[(n + 1) * (numrho + 2) + r + 1]++;
}
}
}
} else {
for( i = 0; i < height; i++ )
for( j = 0; j < width; j++ )
{
if( image[i * step + j] != 0 )
for(int n = 0; n < numangle; n++ )
{
int r = cvRound( j * tabCos[n] + i * tabSin[n] );
r += (numrho - 1) / 2;
accum[(n + 1) * (numrho + 2) + r + 1]++;
}
// Extract the coordinates of all edge points
std::vector<int> x_coords, y_coords, edge_vals;
size_t estimated_edges = (size_t)width * (size_t)height / 10;
x_coords.reserve(estimated_edges);
y_coords.reserve(estimated_edges);
if (use_edgeval) {
edge_vals.reserve(estimated_edges);
}
for (int y = 0; y < height; y++) {
const uchar* row_ptr = image + y * step;
for (int x = 0; x < width; x++) {
int val = row_ptr[x];
if (val != 0) {
x_coords.push_back(x);
y_coords.push_back(y);
if (use_edgeval) edge_vals.push_back(val);
}
}
}
}
int num_edges = (int)x_coords.size();
// Perform multi-threaded segmentation according to the numangle
// Since accum is divided into blocks according to angles, the accum areas written by different threads will not overlap
auto process_hough_by_angle = [&](const cv::Range& range) {
for (int n = range.start; n < range.end; n++) {
float cos_n = tabCos[n];
float sin_n = tabSin[n];
int* accum_n = accum + (n + 1) * (numrho + 2) + 1 + (numrho - 1) / 2;
if (use_edgeval) {
for (int k = 0; k < num_edges; k++) {
int r = cvRound(x_coords[k] * cos_n + y_coords[k] * sin_n);
accum_n[r] += edge_vals[k];
}
} else {
for (int k = 0; k < num_edges; k++) {
int r = cvRound(x_coords[k] * cos_n + y_coords[k] * sin_n);
accum_n[r]++;
}
}
}
};
cv::parallel_for_(cv::Range(0, numangle), process_hough_by_angle);
}
// stage 2. find local maximums
findLocalMaximums( numrho, numangle, threshold, accum, _sort_buf );
@@ -308,13 +359,13 @@ HoughLinesSDiv( InputArray image, OutputArray lines, int type,
lst.push_back(hough_index(threshold, -1.f, 0.f));
// Precalculate sin table
std::vector<float> _sinTable( 5 * tn * stn );
AutoBuffer<float> _sinTable( 5 * tn * stn );
float* sinTable = &_sinTable[0];
for( index = 0; index < 5 * tn * stn; index++ )
sinTable[index] = (float)cos( stheta * index * 0.2f );
std::vector<uchar> _caccum(rn * tn, (uchar)0);
AutoBuffer<uchar> _caccum(rn * tn, (uchar)0);
uchar* caccum = &_caccum[0];
// Counting all feature pixels
@@ -322,7 +373,7 @@ HoughLinesSDiv( InputArray image, OutputArray lines, int type,
for( col = 0; col < w; col++ )
fn += _POINT( row, col ) != 0;
std::vector<int> _x(fn), _y(fn);
AutoBuffer<int> _x(fn), _y(fn);
int* x = &_x[0], *y = &_y[0];
// Full Hough Transform (it's accumulator update part)
@@ -394,7 +445,7 @@ HoughLinesSDiv( InputArray image, OutputArray lines, int type,
return;
}
std::vector<uchar> _buffer(srn * stn + 2);
AutoBuffer<uchar> _buffer(srn * stn + 2);
uchar* buffer = &_buffer[0];
uchar* mcaccum = buffer + 1;
@@ -535,7 +586,7 @@ HoughLinesProbabilistic( Mat& image,
Mat accum = Mat::zeros( numangle, numrho, CV_32SC1 );
Mat mask( height, width, CV_8UC1 );
std::vector<float> trigtab(numangle*2);
AutoBuffer<float> trigtab(numangle*2);
for( int n = 0; n < numangle; n++ )
{
@@ -2331,7 +2382,7 @@ static void HoughCircles( InputArray _image, OutputArray _circles,
if( type == CV_32FC4 )
{
std::vector<Vec4f> cw(ncircles);
AutoBuffer<Vec4f> cw(ncircles);
for( i = 0; i < ncircles; i++ )
cw[i] = GetCircle4f(circles[i]);
if (ncircles > 0)
@@ -2339,7 +2390,7 @@ static void HoughCircles( InputArray _image, OutputArray _circles,
}
else if( type == CV_32FC3 )
{
std::vector<Vec3f> cwow(ncircles);
AutoBuffer<Vec3f> cwow(ncircles);
for( i = 0; i < ncircles; i++ )
cwow[i] = GetCircle(circles[i]);
if (ncircles > 0)
+2 -2
View File
@@ -173,8 +173,8 @@ static const void* initInterTab2D( int method, bool fixpt )
for( j = 0; j < INTER_TAB_SIZE; j++, tab += ksize*ksize, itab += ksize*ksize )
{
int isum = 0;
NNDeltaTab_i[i*INTER_TAB_SIZE+j][0] = j < INTER_TAB_SIZE/2;
NNDeltaTab_i[i*INTER_TAB_SIZE+j][1] = i < INTER_TAB_SIZE/2;
NNDeltaTab_i[i*INTER_TAB_SIZE+j][0] = j >= INTER_TAB_SIZE/2;
NNDeltaTab_i[i*INTER_TAB_SIZE+j][1] = i >= INTER_TAB_SIZE/2;
for( k1 = 0; k1 < ksize; k1++ )
{
+2 -2
View File
@@ -128,8 +128,8 @@ medianBlur_8u_O1( const Mat& _src, Mat& _dst, int ksize )
# define CV_ALIGNMENT 16
#endif
std::vector<HT> _h_coarse(1 * 16 * (STRIPE_SIZE + 2*r) * cn + CV_ALIGNMENT);
std::vector<HT> _h_fine(16 * 16 * (STRIPE_SIZE + 2*r) * cn + CV_ALIGNMENT);
AutoBuffer<HT> _h_coarse(1 * 16 * (STRIPE_SIZE + 2*r) * cn + CV_ALIGNMENT);
AutoBuffer<HT> _h_fine(16 * 16 * (STRIPE_SIZE + 2*r) * cn + CV_ALIGNMENT);
HT* h_coarse = alignPtr(&_h_coarse[0], CV_ALIGNMENT);
HT* h_fine = alignPtr(&_h_fine[0], CV_ALIGNMENT);
+1 -1
View File
@@ -887,7 +887,7 @@ static bool ocl_morphOp(InputArray _src, OutputArray _dst, InputArray _kernel,
if (actual_op < 0)
actual_op = op;
std::vector<ocl::Kernel> kernels(iterations);
AutoBuffer<ocl::Kernel> kernels(iterations);
for (int i = 0; i < iterations; i++)
{
int current_op = iterations == i + 1 ? actual_op : op;
+2 -2
View File
@@ -307,8 +307,8 @@ __kernel void remap_16SC2_16UC1(__global const uchar * srcptr, int src_step, int
__global T * dst = (__global T *)(dstptr + dst_index);
int map2Value = convert_int(map2[0]) & (INTER_TAB_SIZE2 - 1);
int dx = (map2Value & (INTER_TAB_SIZE - 1)) < (INTER_TAB_SIZE >> 1) ? 1 : 0;
int dy = (map2Value >> INTER_BITS) < (INTER_TAB_SIZE >> 1) ? 1 : 0;
int dx = (map2Value & (INTER_TAB_SIZE - 1)) >= (INTER_TAB_SIZE >> 1) ? 1 : 0;
int dy = (map2Value >> INTER_BITS) >= (INTER_TAB_SIZE >> 1) ? 1 : 0;
int2 gxy = convert_int2(map1[0]) + (int2)(dx, dy);
#if WARP_RELATIVE
gxy.x += x;
+2
View File
@@ -793,6 +793,7 @@ void Subdiv2D::getLeadingEdgeList(std::vector<int>& leadingEdgeList) const
{
leadingEdgeList.clear();
int i, total = (int)(qedges.size()*4);
//use a std::vector<bool> to benefit from the "bitset size/8" implementation
std::vector<bool> edgemask(total, false);
for( i = 4; i < total; i += 2 )
@@ -813,6 +814,7 @@ void Subdiv2D::getTriangleList(std::vector<Vec6f>& triangleList) const
{
triangleList.clear();
int i, total = (int)(qedges.size()*4);
//use a std::vector<bool> to benefit from the "bitset size/8" implementation
std::vector<bool> edgemask(total, false);
Rect2f rect(topLeft.x, topLeft.y, bottomRight.x - topLeft.x, bottomRight.y - topLeft.y);
+22 -7
View File
@@ -568,7 +568,6 @@ void crossCorr( const Mat& img, const Mat& _templ, Mat& corr,
{
const double blockScale = 4.5;
const int minBlockSize = 256;
std::vector<uchar> buf;
Mat templ = _templ;
int depth = img.depth(), cn = img.channels();
@@ -624,7 +623,7 @@ void crossCorr( const Mat& img, const Mat& _templ, Mat& corr,
if( (ccn > 1 || cn > 1) && cdepth != maxDepth )
bufSize = std::max( bufSize, blocksize.width*blocksize.height*CV_ELEM_SIZE(cdepth));
buf.resize(bufSize);
AutoBuffer<uchar> buf(bufSize);
Ptr<hal::DFT2D> c = hal::DFT2D::create(dftsize.width, dftsize.height, dftTempl.depth(), 1, 1, CV_HAL_DFT_IS_INPLACE, templ.rows);
@@ -975,13 +974,14 @@ static void common_matchTemplate( Mat& img, Mat& templ, Mat& result, int method,
for( i = 0; i < result.rows; i++ )
{
float* rrow = result.ptr<float>(i);
float* rrow = result.depth() == CV_32F ? result.ptr<float>(i) : nullptr;
double* drow = result.depth() == CV_64F ? result.ptr<double>(i) : nullptr;
int idx = i * sumstep;
int idx2 = i * sqstep;
for( j = 0; j < result.cols; j++, idx += cn, idx2 += cn )
{
double num = rrow[j], t;
double num = rrow ? (double)rrow[j] : drow[j], t;
double wndMean2 = 0, wndSum2 = 0;
if( numType == 1 )
@@ -1027,7 +1027,8 @@ static void common_matchTemplate( Mat& img, Mat& templ, Mat& result, int method,
num = method != cv::TM_SQDIFF_NORMED ? 0 : 1;
}
rrow[j] = (float)num;
if (rrow) rrow[j] = (float)num;
else drow[j] = num;
}
}
}
@@ -1120,6 +1121,11 @@ static bool ipp_matchTemplate( Mat& img, Mat& templ, Mat& result, int method)
if(templ.size().area()*4 > img.size().area())
return false;
// CV_8U SQDIFF/SQDIFF_NORMED suffer from float32 catastrophic cancellation
// in IPP's internal accumulators; fall through to the double-precision path instead.
if(img.depth() == CV_8U && (method == cv::TM_SQDIFF || method == cv::TM_SQDIFF_NORMED))
return false;
if(method == cv::TM_SQDIFF)
{
if(ipp_sqrDistance(img, templ, result))
@@ -1192,9 +1198,18 @@ void cv::matchTemplate( InputArray _img, InputArray _templ, OutputArray _result,
CV_IPP_RUN_FAST(ipp_matchTemplate(img, templ, result, method))
crossCorr( img, templ, result, Point(0,0), 0, 0);
bool use64f = (depth == CV_8U) && (method == cv::TM_SQDIFF || method == cv::TM_SQDIFF_NORMED);
Mat result64f;
Mat& workResult = use64f ? result64f : result;
if (use64f)
result64f.create(corrSize, CV_64F);
common_matchTemplate(img, templ, result, method, cn);
crossCorr( img, templ, workResult, Point(0,0), 0, 0);
common_matchTemplate(img, templ, workResult, method, cn);
if (use64f)
result64f.convertTo(result, CV_32F);
}
CV_IMPL void
+19
View File
@@ -1128,4 +1128,23 @@ TEST(Drawing, line_connectivity_regression_26413)
EXPECT_GT(count4, 15) << "LINE_4 diagonal should have significantly more pixels due to staircase";
}
//This test ensures that the tipLength geometric ratio is strictly bounded within the logical range (0.0, 1.0].
TEST(Imgproc_Drawing, arrowedLine_tipLength_validation)
{
// Create a simple miniature canvas for testing. Added cv:: prefix.
cv::Mat img = cv::Mat::zeros(100, 100, CV_8UC3);
cv::Point pt1(10, 10), pt2(90, 90);
// 1. Validate legal parameters: should not throw any exceptions (Normal cases)
EXPECT_NO_THROW(cv::arrowedLine(img, pt1, pt2, cv::Scalar(255, 255, 255), 1, 8, 0, 0.1));
EXPECT_NO_THROW(cv::arrowedLine(img, pt1, pt2, cv::Scalar(255, 255, 255), 1, 8, 0, 1.0));
// 2. Validate illegal parameters: expect cv::Exception to be thrown (Boundary violations)
// Negative ratio (tipLength <= 0.0)
EXPECT_THROW(cv::arrowedLine(img, pt1, pt2, cv::Scalar(255, 255, 255), 1, 8, 0, -0.5), cv::Exception);
// Overflow ratio (tipLength > 1.0)
EXPECT_THROW(cv::arrowedLine(img, pt1, pt2, cv::Scalar(255, 255, 255), 1, 8, 0, 1.5), cv::Exception);
}
}} // namespace
+15
View File
@@ -342,4 +342,19 @@ INSTANTIATE_TEST_CASE_P(/**/,
testing::Values(TM_SQDIFF, TM_SQDIFF_NORMED, TM_CCORR, TM_CCORR_NORMED, TM_CCOEFF, TM_CCOEFF_NORMED)));
TEST(Imgproc_MatchTemplate, bug_21786)
{
// CV_8U identical image/template with large patch sums triggers float32
// catastrophic cancellation in TM_SQDIFF. Result must be exactly zero.
Mat img(100, 100, CV_8U, Scalar(255));
Mat templ(25, 25, CV_8U, Scalar(255));
Mat result;
matchTemplate(img, templ, result, TM_SQDIFF);
EXPECT_NEAR(0.0, cv::norm(result, cv::NORM_INF), 1e-6);
matchTemplate(img, templ, result, TM_SQDIFF_NORMED);
EXPECT_NEAR(0.0, cv::norm(result, cv::NORM_INF), 1e-6);
}
}} // namespace
@@ -76,6 +76,36 @@ void Dictionary::writeDictionary(FileStorage& fs, const String &name)
bool Dictionary::identify(const Mat &onlyCellPixelRatio, CV_OUT int &idx, CV_OUT int &rotation, double maxCorrectionRate, float validBitIdThreshold) const {
CV_Assert(onlyCellPixelRatio.rows == markerSize && onlyCellPixelRatio.cols == markerSize);
// Fill bit masks of cells that are not black (not0) and not white (not1).
const int s = (markerSize * markerSize + 8 - 1) / 8;
AutoBuffer<uint8_t> temp(4 * s);
uint8_t* not0 = temp.data(), * not1 = not0 + s;
uint8_t not0Byte = 0, not1Byte = 0;
int currentByte = 0, currentBit = 0;
for(int j = 0; j < markerSize; j++) {
const float* cellPixelRatioRow = onlyCellPixelRatio.ptr<float>(j);
for(int i = 0; i < markerSize; i++) {
not0Byte <<= 1; not1Byte <<= 1;
if(cellPixelRatioRow[i] > validBitIdThreshold) not0Byte |= 1;
if(cellPixelRatioRow[i] < 1 - validBitIdThreshold) not1Byte |= 1;
++currentBit;
if(currentBit == 8) {
not0[currentByte] = not0Byte;
not1[currentByte] = not1Byte;
not0Byte = not1Byte = 0;
++currentByte;
currentBit = 0;
}
}
}
if (currentBit != 0) {
not0[currentByte] = not0Byte;
not1[currentByte] = not1Byte;
}
uint8_t* notXor = not1 + s, * temp0 = notXor + s;
// Computing: notXor = not0 ^ not1
hal::xor8u(not0, s, not1, s, notXor, s, s, 1, nullptr);
int maxCorrectionRecalculed = int(double(maxCorrectionBits) * maxCorrectionRate);
idx = -1; // by default, not found
@@ -84,25 +114,21 @@ bool Dictionary::identify(const Mat &onlyCellPixelRatio, CV_OUT int &idx, CV_OUT
for(int m = 0; m < bytesList.rows; m++) {
int currentMinDistance = markerSize * markerSize + 1;
int currentRotation = -1;
for(int r = 0; r < 4; r++) {
Mat bitsRot = getBitsFromByteList(bytesList.rowRange(m, m + 1), markerSize, r);
bitsRot.convertTo(bitsRot, CV_32F);
// Loop over all bits dictBitsList [m, markerSize * markerSize, 4]; onlyCellPixelRatio [markerSize, markerSize]
int currentHamming = 0;
for(int i = 0; i < markerSize; i++) {
for(int j = 0; j < markerSize; j++) {
// If detected bit is too far from the ground truth, consider it false.
if(fabs(onlyCellPixelRatio.at<float>(i, j) - static_cast<float>(bitsRot.at<float>(i, j))) > validBitIdThreshold){
currentHamming++;
}
}
}
const uchar* bytesRot = bytesList.ptr(m);
for(int r = 0; r < 4; r++, bytesRot += s) {
// Error if: (marker is 0 and input is not 0) or (marker is 1 and input is not 1)
// i.e. if: (!bytesRot && not0) || (bytesRot && not1)
// This is actually: not0 ^ ((not0 ^ not1) & bytesRot)
// Computing: temp0 = (not0 ^ not1) & bytesRot
hal::and8u(notXor, s, bytesRot, s, temp0, s, s, 1, nullptr);
// Computing the final result (xor is performed internally).
int currentHamming = cv::hal::normHamming(not0, temp0, s);
if(currentHamming < currentMinDistance) {
currentMinDistance = currentHamming;
currentRotation = r;
// Break for perfect distance.
if (currentMinDistance == 0) break;
}
}
+1 -1
View File
@@ -44,7 +44,7 @@
#include <iostream>
#include <stdlib.h>
#include <limits>
#include "math.h"
#include <math.h>
using namespace std;
+1 -1
View File
@@ -822,7 +822,7 @@ private:
float m_ig[4];
void setPolynomialExpansionConsts(int n, double sigma)
{
std::vector<float> buf(n*6 + 3);
AutoBuffer<float> buf(n*6 + 3);
float* g = &buf[0] + n;
float* xg = g + n*2 + 1;
float* xxg = xg + n*2 + 1;
+8 -2
View File
@@ -25,7 +25,10 @@ add_subdirectory(java/tutorial_code)
add_subdirectory(dnn)
add_subdirectory(gpu)
add_subdirectory(tapi)
add_subdirectory(opencl)
# HACK: CMake 4.x finds and links wrong OpenCL in 32-bit builds on Windows x64
if(NOT (WIN32 AND CMAKE_SIZEOF_VOID_P EQUAL 4))
add_subdirectory(opencl)
endif()
add_subdirectory(sycl)
if(WIN32 AND HAVE_DIRECTX)
add_subdirectory(directx)
@@ -128,7 +131,10 @@ if(WIN32)
endif()
add_subdirectory(dnn)
# add_subdirectory(gpu)
add_subdirectory(opencl)
# HACK: CMake 4.x finds and links wrong OpenCL in 32-bit builds on Windows x64
if(NOT (WIN32 AND CMAKE_SIZEOF_VOID_P EQUAL 4))
add_subdirectory(opencl)
endif()
add_subdirectory(sycl)
# add_subdirectory(opengl)
# add_subdirectory(openvx)
+7
View File
@@ -107,6 +107,8 @@ int main(int argc, char *argv[])
pDefaultBLOB.filterByConvexity = false;
pDefaultBLOB.minConvexity = 0.95f;
pDefaultBLOB.maxConvexity = (float)1e37;
// Enable contour collection so we can draw blob outlines (see getBlobContours() below).
pDefaultBLOB.collectContours = true;
// Descriptor array for BLOB
vector<String> typeDesc;
// Param array for BLOB
@@ -189,6 +191,11 @@ int main(int argc, char *argv[])
int i = 0;
for (vector<KeyPoint>::iterator k = keyImg.begin(); k != keyImg.end(); ++k, ++i)
circle(result, k->pt, (int)k->size, palette[i % 65536]);
// Retrieve the per-blob contours collected during detect() and outline each blob.
// Requires SimpleBlobDetector::Params::collectContours to be true.
const vector<vector<Point> >& blobContours = sbd->getBlobContours();
for (size_t c = 0; c < blobContours.size(); ++c)
drawContours(result, blobContours, (int)c, Scalar(0, 255, 0), 1);
}
namedWindow(*itDesc + label, WINDOW_AUTOSIZE);
imshow(*itDesc + label, result);
+1 -1
View File
@@ -10,7 +10,7 @@ using namespace cv;
static void help(char** argv)
{
printf("\nShow off image morphology: erosion, dialation, open and close\n"
printf("\nShow off image morphology: erosion, dilation, opening and closing\n"
"Call:\n %s [image]\n"
"This program also shows use of rect, ellipse, cross and diamond kernels\n\n", argv[0]);
printf( "Hot keys: \n"
+16 -4
View File
@@ -21,6 +21,18 @@ PY3 = sys.version_info[0] == 3
if PY3:
xrange = range
# Colors for distinguishing multiple QR codes visually
QR_COLORS = [
(0, 255, 0), # green
(255, 0, 0), # blue
(0, 0, 255), # red
(255, 255, 0), # cyan
(0, 255, 255), # yellow
(255, 0, 255), # magenta
(128, 255, 0), # lime
(255, 128, 0), # orange
]
class QrSample:
def __init__(self, args):
@@ -46,15 +58,14 @@ class QrSample:
cv.putText(result, message, (20, 20), 1,
cv.FONT_HERSHEY_DUPLEX, (0, 0, 255))
def drawQRCodeContours(self, image, cnt):
def drawQRCodeContours(self, image, cnt, color=(0, 255, 0)):
if cnt.size != 0:
rows, cols, _ = image.shape
show_radius = 2.813 * ((rows / cols) if rows > cols else (cols / rows))
contour_radius = show_radius * 0.4
cv.drawContours(image, [cnt], 0, (0, 255, 0), int(round(contour_radius)))
cv.drawContours(image, [cnt], 0, color, int(round(contour_radius)))
tpl = cnt.reshape((-1, 2))
for x in tuple(tpl.tolist()):
color = (255, 0, 0)
cv.circle(image, tuple(x), int(round(contour_radius)), color, -1)
def drawQRCodeResults(self, result, points, decode_info, fps):
@@ -64,7 +75,8 @@ class QrSample:
if n > 0:
for i in range(n):
cnt = np.array(points[i]).reshape((-1, 1, 2)).astype(np.int32)
self.drawQRCodeContours(result, cnt)
color = QR_COLORS[i % len(QR_COLORS)]
self.drawQRCodeContours(result, cnt, color)
msg = 'QR[{:d}]@{} : '.format(i, *(cnt.reshape(1, -1).tolist()))
print(msg, end="")
if len(decode_info) > i: