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

Compare commits

...

177 Commits

Author SHA1 Message Date
Yuantao Feng 0e09f1a238 Merge pull request #29557 from fengyuentau:4x/hal_rvv/split_opt
hal_rvv core: Cover more data types for split with native RVV intrinsics #29557

Covering 16-bit, 32-bit and 64-bit split.

### Pull Request Readiness Checklist

See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request

- [x] I agree to contribute to the project under Apache 2 License.
- [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
- [x] The PR is proposed to the proper branch
- [x] There is a reference to the original bug report and related work
- [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
      Patch to opencv_extra has the same branch name.
- [x] The feature is well documented and sample code can be built with the project CMake
2026-07-21 10:16:38 +03:00
Alexander Smorkalov ba53d1d6b7 Merge pull request #29566 from fengyuentau:4x/hal_rvv/copy_mask_opt
hal_rvv core: Optimize RVV HAL masked copy
2026-07-21 10:15:07 +03:00
FENG Yuantao 1f36f15a49 Optimize RVV HAL masked copy 2026-07-20 23:43:41 +08:00
Alexander Smorkalov 2b6ea5f344 Merge pull request #29556 from fengyuentau:4x/hal_rvv/merge_opt
hal_rvv core: Optimize 32-bit and 64-bit merge
2026-07-20 12:26:12 +03:00
FENG Yuantao 898f1e9691 Optimize RVV HAL 32-bit and 64-bit merge 2026-07-20 00:22:38 +08:00
Alexander Smorkalov 6f402272e7 Merge branch 'as/release_4.14.0' into 4.x 2026-07-18 09:55:21 +03:00
Alexander Smorkalov 0654a42e19 Release: OpenCV 4.14.0 2026-07-17 15:55:59 +03:00
Alexander Smorkalov f7dd7df170 Merge pull request #29531 from asmorkalov:as/openvino_ci_fail
Disable test test that sporadically fails with OpenVINO on CI #29531

### Pull Request Readiness Checklist

See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request

- [x] I agree to contribute to the project under Apache 2 License.
- [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
- [ ] The PR is proposed to the proper branch
- [ ] There is a reference to the original bug report and related work
- [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
      Patch to opencv_extra has the same branch name.
- [ ] The feature is well documented and sample code can be built with the project CMake
2026-07-17 13:32:48 +03:00
Alexander Smorkalov 7aa163b83f Merge pull request #29539 from uwezkhan:aruco-readdict-marker-bound
bound marker string length in aruco readDictionary
2026-07-17 11:42:51 +03:00
Alexander Smorkalov c47541acbd Merge pull request #29525 from asmorkalov:as/ffmpeg_update_4.14
FFmpeg wrapper update for 4.14.0 release.
2026-07-17 10:37:20 +03:00
Uwez Khan b5fe8dfef3 bound marker string length in aruco readDictionary 2026-07-16 19:44:15 +05:30
Alexander Smorkalov 4d1e206f5e FFmpeg wrapper update for 4.14.0 release. 2026-07-16 15:12:50 +03:00
Yuantao Feng 2e778c52c1 Merge pull request #27510 from fengyuentau:4x/core/reduce_simd
core: vectorize cv::reduce #27510

- [ ] ~reduceR_~ Dropped due to performance
- [x] reduceC_

### 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-07-15 22:36:29 +03:00
Madan mohan Manokar 6b640b424c Merge pull request #28650 from amd:fast_pyrDown
imgproc: Optimized OpticalFlowPyrLK (PyrDownH) #28650

- Optimized Uchar PyrDown Horizontal processing intermediate storage.
- Improved horizontal processing with AVX512 vbmi ISA.
- Todo: further refinement for other archs.

### 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
2026-07-15 12:31:28 +03:00
Alexander Smorkalov 06574736b3 Merge pull request #29503 from uwezkhan:flann-kdtree-load-bounds
bound kd-tree node indices when loading a saved flann index
2026-07-14 15:36:16 +03:00
Madan mohan Manokar 4fe51e51e0 Merge pull request #29507 from amd:fast_remap_ext
imgproc: Optimized remap interpolation #29507

- Add a SIMD dispatch file for remap and vectorize the single-channel (C1) in-bounds paths of bilinear, bicubic and lanczos4 interpolation (32F / 16U / 16S) using width-agnostic gather
- Dispatch the bilinear C1 path and drop the per-pixel weight-table gathers
- Widen the fixed-point coordinate map conversion
- 32F lanczos4 is kept on the scalar path: its vectorized 64-tap accumulation deviates beyond the set float accuracy tolerance
- Vectorize the bilinear inlier/outlier run detection so any-channel linear remap skips constant-status runs with SIMD instead of a per-pixel bounds test

### 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
2026-07-14 15:17:22 +03:00
Alexander Smorkalov 335abd236f Merge pull request #29523 from asmorkalov:as/relax_gapi_gstreamer_test
Relaxed G-API tests for GStreamer to support Ubuntu 26.04.
2026-07-14 12:09:48 +03:00
Alexander Smorkalov 46d1b6c99d Relaxed G-API tests for GStreamer to support Ubuntu 26.04. 2026-07-14 08:47:33 +03:00
Alexander Smorkalov e6b1ef272e Merge pull request #29511 from Akansha-977:histogram_IPP_4.x
Extracted IPP to HAL for calcHist function in 4.x
2026-07-14 08:42:06 +03:00
Alexander Smorkalov 1bea199e00 Merge pull request #29437 from intel-staging:dev/tizmajlo/ippicv_20260630
Update IPP ICV integration to IPP 2026.0.0 (20260630)
2026-07-14 08:25:34 +03:00
Alexander Smorkalov 42cd88d737 Merge pull request #29509 from asmorkalov:as/ubuntu_26.04_warn_fix
Samples build warning fix on Ubuntu 26.04
2026-07-14 08:19:57 +03:00
Alexander Smorkalov 4c3895e96c Samples build warning fix on Ubuntu 26.04 2026-07-13 15:22:08 +03:00
Akansha Mallick 2c14cc1897 Extracted IPP to HAL for calcHist function 2026-07-13 14:12:52 +05:30
Uwez Khan 13fb140932 bound kd-tree node indices when loading a saved flann index 2026-07-12 21:36:42 +05:30
Alexander Smorkalov 7f9f6acb83 Merge pull request #29502 from asmorkalov:as/msmf_audio
videoio(MSMF): fix audio sample-rate validation and dropped last frame
2026-07-12 17:11:55 +03:00
Varun Jaiswal 4533beab85 videoio(MSMF): fix audio sample-rate validation and dropped last frame (#27438) 2026-07-12 12:14:59 +03:00
Akansha-977 87af90b847 Merge pull request #29463 from Akansha-977:distransform_IPP_migration_4.x
Distransform function IPP migration to HAL in 4.x #29463

### Pull Request Readiness Checklist

See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request

- [x] I agree to contribute to the project under Apache 2 License.
- [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
- [x] The PR is proposed to the proper branch
- [x] There is a reference to the original bug report and related work
- [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
      Patch to opencv_extra has the same branch name.
- [x] The feature is well documented and sample code can be built with the project CMake
2026-07-10 18:28:10 +03:00
FAN YUCHEN d7e6e58652 Merge pull request #29487 from Functionhx:fix/docs-combined
Fix calibration tutorial docs, decomposeProjectionMatrix, and convertMaps performance claims #29487

Fixes #25655, #26791, #27277.

Three doc fixes:
1. Calibration tutorial: rows/cols swapped, fixed np.mgrid consistency
2. decomposeProjectionMatrix: clarified transVect is camera center in homogeneous coordinates
3. convertMaps: replaced overstated 2x speed claim

### Pull Request Readiness Checklist
- [x] I agree to contribute under Apache 2 License
- [x] Not based on GPL/incompatible license
- [x] PR proposed to proper branch (4.x)
- [x] Reference to original bug report and related work
- [ ] Accuracy test, performance test, test data: N/A (doc-only)
- [x] Feature well documented and sample code buildable
2026-07-10 15:59:29 +03:00
LiuChenjiayi b21eae2b8b Merge pull request #29149 from EuropaRanger:gapi_doc_update
Gapi doc update #29149

This PR improves G-API documentation by clarifying the introduction and kernel API pages
- fixing some spelling errors
- operator|() can now link to the overloaded version that is actually called.
- supplemented and revised the doc based on the author's annotations.

The change is documentation-only and does not affect runtime behavior. I tested the doc reconstruction of this module. 

### 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-07-10 15:44:14 +03:00
Alexander Smorkalov b41dd0fdff Merge pull request #29491 from Akansha-977:threshold_IPP_4.x
Extracted IPP to HAL for threshold function in 4.x
2026-07-10 15:06:40 +03:00
Alexander Smorkalov be4ef205b1 Merge pull request #29490 from asmorkalov:as/disable_ipp_warpPerspective_F32C4_nearest
Disabled IPP implementation for warpPerspective F32C4 as it triggers exception on Windows
2026-07-10 14:19:41 +03:00
Akansha Mallick 062a00bab3 Extracted IPP to HAL for threshold function 2026-07-10 14:00:33 +03:00
Prasad Ayush Kumar b022a9b321 Merge pull request #29434 from Prasadayus:canny_ipp_extract
Extracting IPP integaration as HAL for Canny #29434

Backport of : https://github.com/opencv/opencv/pull/29433

**Performance Numbers on Intel(R) Core(TM) i9-11900K:** https://docs.google.com/spreadsheets/d/1RMvUZP1tSQtdjuNQY9JasYmlx1L5iN9XWGhXtG5u1uI/edit?usp=sharing 

### Pull Request Readiness Checklist

See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request

- [x] I agree to contribute to the project under Apache 2 License.
- [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
- [x] The PR is proposed to the proper branch
- [x] There is a reference to the original bug report and related work
- [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
      Patch to opencv_extra has the same branch name.
- [x] The feature is well documented and sample code can be built with the project CMake
2026-07-10 13:59:24 +03:00
Akansha-977 7230c1ce08 Merge pull request #29464 from Akansha-977:templatematch_IPP_4.x
Extracting IPP to HAL for matchTemplate function in 4.x #29464

### Pull Request Readiness Checklist

See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request

- [x] I agree to contribute to the project under Apache 2 License.
- [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
- [x] The PR is proposed to the proper branch
- [x] There is a reference to the original bug report and related work
- [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
      Patch to opencv_extra has the same branch name.
- [x] The feature is well documented and sample code can be built with the project CMake
2026-07-10 12:16:45 +03:00
Alexander Smorkalov 818145f41d Disabled IPP implementation for warpPerspective F32C4 as it does not pass tests. 2026-07-10 09:13:56 +03:00
Andrei Fedorov 93cd614472 Merge pull request #29477 from intel-staging:ipp/sort_enabling
Enable ipp calls for sort, sortIdx#29477

Follow up on https://github.com/opencv/opencv/pull/29184
Also introduced parallelization to sort similar to #29192
Performance is up to ~17x faster per our measurements
+ ~127KB to binary size.

### 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-07-10 08:39:21 +03:00
Alexander Smorkalov 7507accf2e Merge pull request #29486 from asmorkalov:as/drop_yuv_gray_hal
Drop HAL for cv_hal_cvtColorYUV2Gray as it's just copy.
2026-07-09 18:06:28 +03:00
Alexander Smorkalov 8eb2c8998c Drop HAL for cv_hal_cvtColorYUV2Gray as it's just copy. 2026-07-09 16:51:20 +03:00
Alexander Smorkalov 738739d7c2 Merge pull request #29483 from asmorkalov:update_version_4.14.0-pre
pre: OpenCV 4.14.0 (version++)
2026-07-09 15:35:03 +03:00
Alexander Smorkalov e978c193cf pre: OpenCV 4.14.0 (version++) 2026-07-09 12:35:42 +03:00
yyyisyyy11 b887e2fd3e Merge pull request #29018 from yyyisyyy11:project4_Lunhan_Yan
dnn: add DynamicQuantizeLinear ONNX layer support #29018

### 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 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 target platform(s)
- [x] There is an accuracy test
- [ ] There is a performance test

### Description

Implements the ONNX `DynamicQuantizeLinear` operator (opset 11) for the OpenCV DNN module.

**What it does:**

- Adds `QuantizeDynamicLayer` and `DequantizeDynamicLayer` layer classes
- Registers layers and adds ONNX importer dispatch for `DynamicQuantizeLinear`
- Computes scale and zero-point at runtime from activation min/max
- Quantizes FP32 input to int8 (stored as uint8 - 128, matching OpenCV convention)

**Framework limitation & workaround:**
Due to the single-dtype-per-layer constraint in `LayerData::dtype` (see #29017), the float32 scale output cannot be passed through the CV_8S blob pipeline directly. As a workaround, the scale is encoded as 4 raw bytes in a CV_8S `{1,4}` blob using `memcpy`, and decoded by the downstream `DequantizeDynamic` layer.

**Testing:**

- Custom accuracy tests reproduce all 3 ONNX conformance test cases: `test_dynamicquantizelinear`, `test_dynamicquantizelinear_max_adjusted`, `test_dynamicquantizelinear_min_adjusted`
- Each test verifies: quantized values, scale, zero point, and round-trip dequantize accuracy
- All existing quantization regression tests pass (42/42)

**Conformance tests:**
The 6 conformance tests for `DynamicQuantizeLinear` remain in the parser denylist (they were already denylisted before this PR) because the framework cannot produce mixed-type outputs. The custom tests provide equivalent coverage.

### Files changed

- `modules/dnn/include/opencv2/dnn/all_layers.hpp` — layer class declarations
- `modules/dnn/src/init.cpp` — layer registration
- `modules/dnn/src/onnx/onnx_importer.cpp` — ONNX import dispatch
- `modules/dnn/src/int8layers/quantization_utils.cpp` — layer implementations
- `modules/dnn/test/test_onnx_importer.cpp` — custom accuracy tests
2026-07-09 11:07:12 +03:00
Alexander Smorkalov 0eb3778a1b Merge pull request #29473 from velonica0:28870
imgproc: make RVV HAL resize bit-exact for 8U INTER_LINEAR (fixes #28870)
2026-07-08 19:19:35 +03:00
velonica0 9121ebe28b imgproc: make RVV HAL resize bit-exact for 8U INTER_LINEAR 2026-07-08 05:24:31 -07:00
Anushka d48bf69f65 Merge pull request #29455 from anushkagupta200615-jpg:fix-issue-29452
Fix #29452: Remove <complex.h> to prevent _Complex macro conflicts #29455

### Pull Request Readiness Checklist

See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request

- [x] I agree to contribute to the project under Apache 2 License.
- [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
- [x] The PR is proposed to the proper branch
- [x] There is a reference to the original bug report and related work
- [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
      Patch to opencv_extra has the same branch name.
- [ ] The feature is well documented and sample code can be built with the project CMake

---

**Description:**
Resolves https://github.com/opencv/opencv/issues/29452

**Reason for the issue:**
The C99 `<complex.h>` header defines the macro `complex` on some platforms (like NetBSD with GCC 14). Because it was included before C++ `<complex>`, this caused conflicts where `std::complex<T>` was being expanded into `std::_Complex<T>`, resulting in the reported syntax errors.

**Changes made:**
- Removed the unnecessary C-style `#include <complex.h>`.
- Added a safety guard to `#undef complex` in case any transitive lapack headers attempt to define it, ensuring `std::complex` works cleanly without C-preprocessor interference.
2026-07-07 15:20:09 +03:00
Alexander Smorkalov aa70c82ebe Merge pull request #29418 from uwezkhan:einsum-subscript-bound
bound einsum subscript index before reading input shape
2026-07-07 14:42:49 +03:00
Alexander Smorkalov c85397c3bb Merge pull request #29462 from intel-staging:use_ipp_check_derive
Added missing IPP check in IPP HAL
2026-07-07 13:20:45 +03:00
Andrei Fedorov 0488f6e942 Update deriv_ipp.cpp 2026-07-07 11:39:39 +02:00
Alexander Smorkalov 51495995d2 Merge pull request #29431 from asmorkalov:as/no_exact_ipp
Enable non-exact IPP optimizations if build-time algorithm hint allows it
2026-07-07 11:45:59 +03:00
Alexander Smorkalov c344b7dcef Merge pull request #29461 from rmsalinas:update-truco-ref
doc: update TRUCO publication status
2026-07-07 10:21:31 +03:00
rmsalinas 0e6433327a doc: update TRUCO publication status 2026-07-07 08:08:43 +02:00
Alexander Smorkalov f6be96989e Merge pull request #29442 from orbisai0security:fix-filter-buffer-size-overflow
fix: in filter in filter.cpp
2026-07-06 17:19:05 +03:00
MAAZIZ Adel Ayoub e9289fc7c4 Merge pull request #29447 from Adel-Ayoub:fix/matexpr-mul-scalar-lifetime
core: fix use-after-scope when Mat::mul() is given a scalar #29447

### Summary

`cv::Mat::mul()` called with a scalar returns a `MatExpr` that reads a dead stack slot when it is evaluated. In a normal (non-instrumented) build this produces silently wrong values as soon as the slot is reused:

```cpp
static cv::MatExpr makeExpr(const cv::Mat& m)
{
    return m.mul(7);               // 7.0 is a temporary double in THIS frame
}

cv::Mat matrix(2, 3, CV_32FC1, cv::Scalar(3.0f));
cv::MatExpr expr = makeExpr(matrix);
// ... any further calls reuse the dead frame ...
cv::Mat result = expr;             // observed: all 0, expected: all 21
```

Under AddressSanitizer this is the `stack-use-after-scope` reported in #23577, with the same stack trace (`cvt64s` -> `convertAndUnrollScalar` -> `arithm_op` -> `multiply` -> `MatOp_Bin::assign`).

Storing the expression is the documented lazy-evaluation usage of `MatExpr`; the argument is ordinary supported API usage (`mat.hpp` itself shows `Mat C = A.mul(5/B);`).

### Root cause

A scalar argument binds to `_InputArray(const double& val)`, which records the **address** of the temporary with kind `MATX`:

```cpp
inline _InputArray::_InputArray(const double& val)
{ init(FIXED_TYPE + FIXED_SIZE + MATX + CV_64F + ACCESS_READ, &val, Size(1,1)); }
```

`Mat::mul()` then parks `m.getMat()` inside the returned `MatExpr`. For `MATX` kind, `getMat_()` returns a non-owning, non-refcounted header over that stack memory (`return Mat(sz, flags, obj);`). The temporary dies at the end of the full expression, but the `MatExpr` keeps the header, and `MatOp_Bin::assign()` later feeds it to `cv::multiply()`. `Matx`/`Vec` arguments take the same path.

`Mat::mul()` is the only `MatExpr` factory in `matrix_expressions.cpp` that takes an `InputArray`; every other scalar operand there is stored by value in the `Scalar` member (`e.s`), so no other expression path can capture a stack pointer this way.

### Fix

Snapshot the operand with `clone()` unless it is a `Mat`/`UMat`, which keep the current zero-copy behaviour: their headers are refcounted and already safe to defer. Any other `InputArray` kind (a scalar, `Matx`, `Vec`, `std::vector`, an evaluated expression) is a potentially non-owning view, so it is copied once at expression construction, off any hot path.

### Test

Adds `Core_MatExpr.mul_scalar_use_after_scope_23577` to `modules/core/test/test_operations.cpp`. It builds the expression in a helper frame and overwrites the stack before evaluating; the helpers are called through volatile function pointers so they cannot be inlined, which makes the stale read deterministic. The test fails before the fix (result is all 0 instead of all 21) and passes after. It is self-contained: no opencv_extra data is needed.

Verified locally on macOS/AArch64 (Apple clang 17, Release): full `opencv_test_core` passes, and the AddressSanitizer reproducer from the issue is clean after the fix.

Fixes #23577.

### Pull Request Readiness Checklist

See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request

- [x] I agree to contribute to the project under Apache 2 License.
- [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
- [x] The PR is proposed to the proper branch
- [x] There is a reference to the original bug report and related work
- [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
      Self-contained accuracy regression test in `modules/core/test/test_operations.cpp`; no opencv_extra data required. No performance test: no existing perf test covers `Mat::mul` expression construction, and the copy happens once at expression construction, only for non-`Mat`/`UMat` operands.
- [ ] The feature is well documented and sample code can be built with the project CMake
      N/A - bug fix, no new API.
2026-07-06 17:11:51 +03:00
Alexander Smorkalov 50beb24c6b Enable non-exact IPP optimizations if build-time algorithm hint allows it. 2026-07-06 11:50:53 +03:00
Alexander Smorkalov fcc3418cd7 Merge pull request #29446 from anushkagupta200615-jpg:fix-apple-conversions-cpp26
macOS: Fix CGBitmapInfo C++26 enum compilation error
2026-07-06 10:15:38 +03:00
Akansha-977 80fda1da8c Merge pull request #29427 from Akansha-977:filter2D_IPP_migration_4.x
Filter2D IPP migration to HAL for 4.x #29427

### Pull Request Readiness Checklist

See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request

- [x] I agree to contribute to the project under Apache 2 License.
- [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
- [x] The PR is proposed to the proper branch
- [x] There is a reference to the original bug report and related work
- [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
      Patch to opencv_extra has the same branch name.
- [x] The feature is well documented and sample code can be built with the project CMake
2026-07-06 09:52:20 +03:00
anushkagupta200615-jpg cddfd62a18 macOS: Fix CGBitmapInfo C++26 enum compilation error 2026-07-06 02:58:34 +05:30
orbisai0security a9573e5ac0 fix: V-002 security vulnerability
Automated security fix generated by OrbisAI Security
2026-07-05 12:15:53 +00:00
Timur Izmajlov 9d66a589b4 Update IPP ICV integration to IPP 2026.0.0 (20260630) 2026-07-03 17:25:24 +02:00
Alexander Smorkalov 24030ff3a2 Merge pull request #29390 from amd:fast_countNonZero
core: Optimized countNonZero with AVX-512 signmask path
2026-07-03 15:44:43 +03:00
Prasad Ayush Kumar c77286749a Merge pull request #29420 from Prasadayus:box_filter_ipp_extract
Extract IPP integration as HAL function for box_filter #29420

Backport of https://github.com/opencv/opencv/pull/29414

**Performance Numbers on Intel(R) Core(TM) i9-11900K:** https://docs.google.com/spreadsheets/d/1kMKiZWh--pH30hqsQo6j1suNw1lfQiKzSankMCMa2FI/edit?usp=sharing

### Pull Request Readiness Checklist

See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request

- [x] I agree to contribute to the project under Apache 2 License.
- [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
- [x] The PR is proposed to the proper branch
- [x] There is a reference to the original bug report and related work
- [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
      Patch to opencv_extra has the same branch name.
- [x] The feature is well documented and sample code can be built with the project CMake
2026-07-02 13:10:52 +03:00
Madan mohan Manokar e6d0c0340b Merge pull request #29413 from amd:fast_basic_op
core: Fix mul32f and addWeighted32f to use native f32 SIMD paths #29413

OpenCV Extra: https://github.com/opencv/opencv_extra/pull/1388

- add 32FC1 coverage to addWeighted benchmark
- avoid intermediate double for f32 variants of scaled multiply and addWeighted.
- Relax AddWeighted 32F test tolerance to match f32 FMA semantics.

### 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
2026-07-02 12:58:39 +03:00
Alexander Smorkalov 9460e30793 Merge pull request #29400 from amd:fast_sum
core: enable AVX-512 dispatch for sum
2026-07-02 12:56:27 +03:00
Uwez Khan b4164a4bc4 bound einsum subscript index before reading input shape 2026-07-01 18:19:00 +05:30
Madan mohan Manokar 579a505734 Merge pull request #29335 from amd:fast_norm_simd
core: SIMD optimizations for norm, distance and Hamming APIs. (Improves ORB & BRISK) #29335

Generic universal-intrinsic kernels (benefit all SIMD backends: NEON, AVX2, AVX-512, etc.), plus enabling wider dispatch for the norm module.

- hal::normHamming: cached-pointer dispatch (resolve once, no per-call dispatch chain or trace region) + vector popcount path. cv::norm(NORM_HAMMING) and binary-descriptor matching (BFMatcher ORB/BRISK/FREAK via cv::batchDistance).
- hal::normL2Sqr_ / normL1_: direct inlinable kernels with single-vector tail (cv::batchDistance / BFMatcher float L2/L1, cv::kmeans).
- cv::norm masked NORM_INF: deinterleave SIMD for multichannel + back-step tail.
- cv::norm(src1, src2, type, mask): SIMD masked norm-diff; INF is one templated kernel for all element types, plus uchar L1/L2 and int L1 kernels.
- Enable AVX512_SKX/AVX512_ICL dispatch for the norm module.
- features2d: add BFMatcher knnMatch perf tests (float L2/L1, binary Hamming).
- ORB and BRISK performance improved
### 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
2026-07-01 15:48:47 +03:00
Alexander Smorkalov 83ee55fcda Merge pull request #29417 from intel-staging:deriv_hal
Moved IPP-based Sobel and Scharr to HAL
2026-07-01 14:20:57 +03:00
Madan mohan Manokar 28a1d0dddb Merge pull request #29242 from amd:fast_gemm_simd
Optimized gemm implementation with Universal SIMD #29242

- vectorized GEMMSingleMul and GEMMBlockMul

### 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
2026-07-01 13:47:43 +03:00
Fedorov, Andrey 2f779c5c36 moved ipp deriv to hal 2026-07-01 02:13:48 -07:00
Alexander Smorkalov 5b83b0c440 Merge pull request #29405 from Teddy-Yangjiale:rvv-dnn-conv-4x
dnn: add RVV LMUL=1 kernel for convBlock_F32 (ConvolutionLayer, rv64gcv)
2026-06-29 18:08:55 +03:00
ZC a16c4a9fe2 Merge pull request #29359 from zcinclude:fix/avfoundation-videowriter-destructor-sigsegv
videoio(avfoundation): fix SIGSEGV crash when releasing VideoWriter on iOS #29359

Replace deprecated synchronous finishWriting with
finishWritingWithCompletionHandler + dispatch_semaphore
to properly wait for the async completion handler.

The synchronous finishWriting method is deprecated since iOS 6
and, despite blocking until writing status completes, does NOT
wait for internal NSOperation KVO observer blocks that are
dispatched asynchronously to GCD worker threads. When the
destructor returns and drains the autorelease pool, these
blocks may access already-released objects, causing SIGSEGV.

This fix uses a semaphore to block until the
finishWritingWithCompletionHandler callback has fully completed,
eliminating the race condition between the async block and
autorelease pool drain.

Fixes #28165
2026-06-29 15:22:16 +03:00
Anshu 2a47e2c1a5 Merge pull request #29396 from 0AnshuAditya0:fix-macos-framework-rpath-29028
cmake: fix #29028 macOS dynamic framework RPATH for relocatable install #29396

### Problem
When building OpenCV as a dynamic framework on macOS (`-DAPPLE_FRAMEWORK=ON -DBUILD_SHARED_LIBS=ON`), installed binaries contain an absolute `LC_RPATH` pointing to the build tree, making the install non-relocatable. Moving the install directory or deploying to another Mac causes binaries to crash on launch.

### Root cause
`cmake/OpenCVInstallLayout.cmake` unconditionally sets `CMAKE_INSTALL_RPATH` to `${CMAKE_INSTALL_PREFIX}/${OPENCV_LIB_INSTALL_PATH}` for all platforms. Additionally, `cmake/platforms/OpenCV-Darwin.cmake` was empty and missing the `@executable_path`/`@loader_path` linker flags that the iOS toolchain already sets for the same `APPLE_FRAMEWORK AND BUILD_SHARED_LIBS` case.

### Fix
- Guard `CMAKE_INSTALL_RPATH` in `OpenCVInstallLayout.cmake` to use relocatable `@executable_path` tokens for Apple framework builds
- Add equivalent linker flags to `OpenCV-Darwin.cmake`, mirroring `platforms/ios/cmake/Modules/Platform/iOS.cmake`

Fixes #29028

### 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-29 14:27:21 +03:00
Jonas Perolini 37f3c7539b Merge pull request #29252 from JonasPerolini:pr-aruco-bit-threshold-in-refine
Use validBitIdThreshold for Aruco refineDetectedMarkers #29252

The goal of this PR is to solve the issue raised by @vrabaud in https://github.com/opencv/opencv/pull/28289 (comment: https://github.com/opencv/opencv/pull/28289#discussion_r3355812646). 

**Issue:** 

`refineDetectedMarkers()` converted the extracted cell ratios with `convertTo(CV_8UC1)` (an implicit 0.5 threshold) before computing the code distance, ignoring `detectorParams.validBitIdThreshold`. 

**Solution:** 

Make the refine path consistent with the main detection path `Dictionary::identify`.

**Changes:**

- Add a `Dictionary::getDistanceToId()` overload that takes the float cell pixel ratio matrix and `validBitIdThreshold` (similar to how it's done for the `identify()` overload.
- Move the per cell distance computation into a private `getDistanceToIdImpl` helper used by both `identify()` and the new overload of `getDistanceToId()` to avoid repetitions.
- `refineDetectedMarkers()` now calls the new overload.

**Tests:**

- `CV_ArucoRefine.validBitIdThreshold`: a marker with one degraded cell is recovered at threshold 0.7 but not at 0.49.
- `CV_ArucoDictionary.getDistanceToIdCellPixelRatio`: unit-tests both `getDistanceToId` overloads.

All passed
2026-06-29 13:37:43 +03:00
Alexander Smorkalov 3c10324630 Merge pull request #29404 from tonuonu:perf-svm-kernels
ml: SIMD for SVM kernel reductions 🧑‍💻🤖
2026-06-29 11:40:14 +03:00
Teddy-Yangjiale c831290769 dnn: add RVV LMUL=1 kernel for convBlock_F32 (ConvolutionLayer, rv64gcv) 2026-06-29 00:47:44 +08:00
Tonu Samuel a015333f04 ml: SIMD for SVM kernel reductions
SVM::predict is dominated by the per-feature kernel reduction over the
support vectors. Vectorize the four reduction kernels in svm.cpp with
universal intrinsics (two independent accumulators + scalar tail):
calc_non_rbf_base (dot product), calc_rbf (squared distance),
calc_intersec (min-sum) and calc_chi2. Same approach as the KNN
findNearest reduction in #29380. Adds modules/ml/perf/perf_svm.cpp
covering the RBF/POLY/INTER/CHI2 kernels.
2026-06-28 18:09:05 +03:00
Mulham Fetna 5dbfbcdcac Merge pull request #28935 from molhamfetnah:fix/21960-unicode-temp-path
core: fix Unicode temp path handling on Windows (fix #21960) #28935

## Summary

Fixes getCacheDirectoryForDownloads and related temp file functions failing when the user home directory contains Unicode characters like C:\\Users\\テスト\\AppData\\Local\\Temp.

## Root Cause

On Windows, OpenCV was using ANSI versions of GetTempPath and GetTempFileName which fail with Unicode paths.

## Fix

Replace with wide-character GetTempPathW plus UTF-8 conversion:

- modules/core/src/utils/filesystem.cpp - getCacheDirectory for cache paths
- modules/core/src/system.cpp - temporary file creation  
- modules/ts/src/ts_gtest.cpp - test stream capture

## Testing

The fix has been verified compiles. Manual testing on Windows with Unicode username required.

## Risk

Low: Uses same output format, just different encoding path. Existing ASCII functionality preserved.

---

Fixes opencv/opencv#21960
2026-06-28 14:20:45 +03:00
Alexander Smorkalov edd89cfca5 Merge pull request #29371 from uwezkhan:tflite-pad-resize-bounds
check parameter tensor length in tflite parsePadding and parseResize
2026-06-28 14:19:17 +03:00
uwezkhan 15b5d28325 Merge pull request #29378 from uwezkhan:qr-alpha-map-bound
bound alphanumeric values in qr decodeAlpha before map lookup #29378

decodeAlpha in the no-quirc QR backend reads alphanumeric symbols from the post-ECC bitstream and indexes a fixed 45-entry map[] with the raw values. The 11-bit pair from next(11) goes up to 2047, so tuple/45 lands on 45 once the pair passes 2024, and the 6-bit trailing char from next(6) goes up to 63, so map[value] runs out to map[63]. A QR whose data codewords carry an alphanumeric segment with one of those out-of-range values reads past the static array, and the stray byte lands in the string returned by detectAndDecode. WITH_QUIRC defaults off, so this is the decoder a stock build runs.

Before, the only guard was on the encode side, which never emits those values, so the decoder trusted the stream and indexed map[] directly. New version checks value range and return empty string for malformed qr codes.

### 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 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
- [ ] The feature is well documented and sample code can be built with the project CMake
2026-06-28 14:18:20 +03:00
Alexander Smorkalov dd541965e9 Merge pull request #29397 from Prasadayus:cvtcolor_ipp_extract
Extract IPP integration as HAL function for cvtcolor
2026-06-27 08:32:00 +03:00
Alexander Smorkalov da4a417b2a Merge pull request #29346 from Ijtihed:fix/flann-heap-pool-tls
flann: use per-thread storage for heap pool to remove lock contention
2026-06-26 16:35:27 +03:00
Alexander Smorkalov 416e730a61 Merge pull request #29395 from asmorkalov:as/restore_ipp_check
Restore missing IPP check in IPP HAL.
2026-06-26 16:26:17 +03:00
uwezkhan 69d2303531 Merge pull request #29345 from uwezkhan:onnx-tile-bounds
bound tile axis and repeats length in onnx parseTile #29345

OpenCV Extra: https://github.com/opencv/opencv_extra/pull/1385

parseTile sizes repeats_vec from the input-0 rank, then fills it from fields of the model that are never checked against that size. In the tile-1 path the axis taken from the third input indexes repeats_vec directly, and in the tile>1 path the loop writes one entry per element of the repeats tensor. A crafted ONNX with an out-of-range axis, or a repeats tensor longer than the input rank, writes past repeats_vec while loading the model through readNetFromONNX.

The fix runs axis through normalize_axis, the same helper the squeeze and concat paths in this file already use, so a negative or oversized axis is rejected before the write, and it checks the repeats length equals the input rank before the loop. Keeping both bounds in the parser puts the check next to the write instead of trusting the model to be well formed. Before, a repeats tensor shorter than the rank was silently accepted; after, it is rejected, which matches the ONNX rule that repeats carries one entry per input dimension.

- [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
- [ ] The feature is well documented and sample code can be built with the project CMake
2026-06-26 12:39:04 +03:00
Tõnu Samuel b64a215e71 Merge pull request #29377 from tonuonu:perf-dnn-activation-simd
dnn: SIMD for transcendental activation layers (15 functors) 🧑‍💻🤖 #2937

# dnn: SIMD for transcendental activation layers (15 functors) 🧑‍💻🤖

### Summary

Many `BaseDefaultFunctor`-based activation functors fall back to the scalar `BaseDefaultFunctor::apply`
— one `libm` call per element. This PR adds vectorized `apply()` overrides (universal intrinsics,
matching the existing `MishFunctor`/`GeluFunctor`/`SigmoidFunctor` pattern, each with a scalar tail) to
the **15 transcendental activations** that have a clean SIMD path:

| functor | uses | | functor | uses |
|---|---|---|---|---|
| TanH | `v_exp` (1−2/(e²ˣ+1)) | | Asinh | `v_log`,`v_sqrt` (sign·log(\|x\|+√(x²+1))) |
| Log | `v_log` | | Acosh | `v_log`,`v_sqrt` |
| Erf | `v_erf` | | Atanh | `v_log` |
| Exp | `v_exp` | | Softplus | `v_exp`,`v_log` |
| Sin | `v_sin` | | BNLL | `v_exp`,`v_log` (max(x,0)+log1p(e^−\|x\|)) |
| Cos | `v_cos` | | GeluApproximation | `v_exp` (tanh) |
| Tan | `v_sin`/`v_cos` | | Sinh / Cosh | `v_exp` |

Functors compile at the baseline SIMD width (SSE on x86, NEON on ARM — same as the existing SIMD
functors). `Sqrt`/`Floor`/`Ceil`/`Round`/`Abs`/etc. are intentionally **not** included — those are
single cheap ops the compiler already auto-vectorizes (measured `Sqrt` at 0.95×). `Asin`/`Acos`/`Atan`
are omitted (no `v_atan`/`v_asin`/`v_acos` intrinsic).

### Performance

Real `opencv_perf_dnn` `Layer_Activation` (added here), 8×256×128×100 = 26.2 M-elem CV_32F blob, A/B
vs the scalar fallback, median speedup:

| functor | M4 clang/NEON | A76 gcc/NEON | Threadripper gcc | Xeon W-2235 gcc | functor | M4 | A76 | TR | Xeon |
|---|---|---|---|---|---|---|---|---|---|
| TanH | 9.1× | 2.3× | 7.3× | 7.3× | Sinh | 3.3× | 3.2× | 5.2× | 5.0× |
| Cos | 8.8× | 3.0× | 3.3× | 3.0× | Acosh | 3.2× | 2.9× | 2.7× | 2.6× |
| Log | 7.8× | 3.0× | 2.5× | 2.4× | Cosh | 2.8× | 3.2× | 2.6× | 2.5× |
| Sin | 7.8× | 3.0× | 3.4× | 2.9× | Exp | 2.4× | 2.5× | 1.4× | 1.5× |
| GeluApprox | 7.6× | 2.4× | 6.6× | 5.8× | BNLL | 4.2× | 2.0× | 1.6× | 1.4× |
| Atanh | 7.0× | 3.6× | 6.0× | 5.2× | Softplus | 2.0× | 2.0× | 3.3× | 2.7× |
| Tan | 6.8× | 3.6× | 6.6× | 6.0× | Erf | 4.2× | 2.4× | 3.9× | 3.5× |
| Asinh | 3.9× | 2.4× | 5.8× | 5.7× | | | | | |

Every functor is a win on every tested out-of-order core (worst case 1.4×). On the **in-order
Cortex-A55** (a55-tuned build) all 15 measure 0.985–0.99× — neutral, within noise: the SIMD
polynomials are dependency-chain-bound, which an in-order pipeline can neither accelerate nor (here)
slow down. So: meaningful wins on out-of-order ARM + x86, no regression on in-order ARM.

### Accuracy

Uses the `v_exp`/`v_log`/`v_erf`/`v_sin`/`v_cos` polynomials OpenCV already ships and relies on (Gelu
via `v_erf`, Mish/Sigmoid via `v_exp`). Verified vs scalar `libm` on 320 K random elements per functor
with domain-correct inputs (Acosh x≥1, Atanh |x|<1, …): **max abs error ≤ 3e-5** (most ≤ 2e-6), zero
elements exceeding rel>1e-3 & abs>1e-4. ONNX conformance and the layer accuracy tests exercise these ops.

### 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 a code under GPL or another license that is incompatible with OpenCV
- [x] The PR is proposed to the proper branch (4.x)
- [ ] There is a reference to the original bug report and related work — N/A (perf improvement)
- [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable — perf test added (`Layer_Activation`, `SANITY_CHECK_NOTHING`); accuracy covered by existing ONNX conformance/layer tests
- [ ] The feature is well documented and sample code can be built with the project CMake — N/A (internal optimization, no new API)
2026-06-26 11:06:53 +03:00
정상원 61bd5e7b55 Merge pull request #29387 from CodeHotel:fix-js-ximgproc-edge-drawing
Fix js ximgproc edge drawing #29387

## Summary

This PR fixes OpenCV.js binding generation for factory functions that return `cv::Ptr<T>` where `T` is a namespaced class exported with a module-prefixed JavaScript binding name.

The failure is reproduced with OpenCV.js plus `opencv_contrib/modules/ximgproc`. The generated binding for `cv::ximgproc::EdgeDrawing` currently contains:

```cpp
.constructor(select_overload<Ptr<EdgeDrawing>()>(&cv::ximgproc::createEdgeDrawing))
```

but `EdgeDrawing` is not available in the generated C++ scope as an unqualified type. The generated constructor should use:

```cpp
.constructor(select_overload<Ptr<cv::ximgproc::EdgeDrawing>()>(&cv::ximgproc::createEdgeDrawing))
```

## Related issues

Fixes opencv/opencv_contrib#4161.

Related to #27963, #28130, and #28143.

#28143 added namespace qualification for factory `Ptr<...>` return types when the inner `Ptr` type matches the generator class key. The remaining `ximgproc::EdgeDrawing` case is different:

```text
inner Ptr type:      EdgeDrawing
generator class key: ximgproc_EdgeDrawing
C++ class name:      cv::ximgproc::EdgeDrawing
```

Because `EdgeDrawing` does not match `ximgproc_EdgeDrawing`, the previous condition does not handle this case.

## Fix approach

The JS generator now centralizes factory `Ptr<...>` return-type qualification in a helper used by both generator paths:

* `gen_function_binding_with_wrapper`
* `gen_function_binding`

The helper keeps the existing behavior for already qualified types and for the existing class-key match. It additionally handles the case where the inner `Ptr` type matches the basename of the C++ class name:

```text
cv::ximgproc::EdgeDrawing -> EdgeDrawing
```

This allows the generator to emit `Ptr<cv::ximgproc::EdgeDrawing>` for `createEdgeDrawing()` without changing generated files directly.

## Necessity as an opencv code change

The failing API is exposed by `opencv_contrib/modules/ximgproc`, but the invalid C++ line is emitted by OpenCV core's JavaScript binding generator in `modules/js/generator/embindgen.py`.

A contrib-only workaround would have to change the `ximgproc` public declaration or special-case the JS export list for `createEdgeDrawing`. That would only work around one symbol and would not fix the generator's handling of namespaced factory `Ptr` return types. The generator already has the class metadata needed to produce the correct fully qualified C++ type, so the fix belongs in OpenCV core.

## Verification

Tested with:

* OpenCV core `4.x`
* opencv_contrib `4.x`
* Emscripten `6.0.1`
* CMake generator: Ninja
* Build list: `core,imgproc,imgcodecs,video,calib3d,ximgproc,js`

Generator target:

```bash
emcmake cmake \
  -S /path/to/opencv \
  -B /path/to/build \
  -G Ninja \
  -DCMAKE_BUILD_TYPE=Release \
  -DCMAKE_CXX_STANDARD=17 \
  -DOPENCV_EXTRA_MODULES_PATH=/path/to/opencv_contrib/modules \
  -DBUILD_LIST=core,imgproc,imgcodecs,video,calib3d,ximgproc,js \
  -DBUILD_SHARED_LIBS=OFF \
  -DBUILD_opencv_js=ON \
  -DBUILD_TESTS=OFF \
  -DBUILD_PERF_TESTS=OFF \
  -DBUILD_EXAMPLES=OFF \
  -DBUILD_DOCS=OFF

ninja -C /path/to/build gen_opencv_js_source
```

This generated the expected binding:

```cpp
.constructor(select_overload<Ptr<cv::ximgproc::EdgeDrawing>()>(&cv::ximgproc::createEdgeDrawing))
.smart_ptr<Ptr<cv::ximgproc::EdgeDrawing>>("Ptr<ximgproc_EdgeDrawing>")
```

The same patch was also verified with the full OpenCV.js target:

```bash
ninja -C /path/to/build opencv.js
```
2026-06-26 10:57:37 +03:00
Alexander Smorkalov fe849ec08d Restore missing IPP check in IPP HAL. 2026-06-26 10:45:46 +03:00
Prasadayus b864ee7335 Extract IPP integration as HAL function 2026-06-26 11:37:44 +05:30
Madan mohan Manokar d4005e3cb1 core: enable AVX-512 dispatch for sum
- Add AVX512_SKX/AVX512_ICL to sum dispatch
2026-06-26 02:21:02 +00:00
Ijtihed Kilani fbd4dc6ca0 flann: use per-thread storage for heap pool to remove lock contention 2026-06-25 21:18:59 +03:00
Alexander Smorkalov c0c9e5e76c Merge pull request #29380 from tonuonu:perf-knn-findnearest-simd
ml: SIMD for KNN brute-force findNearest 🧑‍💻🤖
2026-06-25 17:18:29 +03:00
Madan mohan Manokar 8dcaac1ac4 Merge pull request #29379 from amd:fix_warning_minmaxloc
fix MSVC warning for minMaxIdx_simd #29379

### 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
2026-06-25 15:51:33 +03:00
Madan mohan Manokar d6a187480f core: speed up countNonZero with AVX-512 signmask path
Use signmask+popcount only in AVX-512 dispatch units; keep the legacy
batched SIMD kernels for AVX2, NEON, and LASX to avoid regressions on
narrow SIMD widths.
2026-06-25 08:43:27 +00:00
Madan mohan Manokar 74a25df87e Merge pull request #28706 from amd:perf_gemm
Perf test for gemm #28706

OpenCV Extra: https://github.com/opencv/opencv_extra/pull/1336

- Added perf test to verify gemm performance.
- small sizes, square & rectangular matrix shapes are added.
- special case of n=1 and m=1 are added.

### 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-25 09:46:33 +03:00
Alexander Smorkalov 1aea68260a Merge pull request #28474 from WalkingDevFlag:fix/cuda-version-patch-26965
cmake: relax CUDA version check to major.minor in OpenCVConfig (#26965)
2026-06-24 14:04:12 +03:00
WalkingDevFlag 13eba3d842 cmake: relax CUDA version check to major.minor in OpenCVConfig (#26965)
The first-class-language OpenCV CMake config (OpenCVConfig-CUDALanguage.cmake.in)
pinned the consumer's CUDA Toolkit to the exact patch version OpenCV was built
with (e.g. 12.3.107), via find_package(CUDAToolkit ... EXACT) and a full
VERSION_EQUAL check. Patch versions change frequently while the CUDA runtime API
is stable within a minor release, so this forced needless rebuilds and could
FATAL_ERROR even for a matching toolkit.

Per the core team's direction on #26966, the change lives in the installed
config template rather than the build scripts, and is controlled by a
consumer-set variable:

- Default: match major.minor only (fixes the reported patch-pinning bug).
- OPENCV_STRONG_CUDA_VERSION_CHECK: restore the exact full-version match.

The build-time detection (OpenCVDetectCUDALanguage.cmake) is left untouched, so
OpenCV_CUDA_VERSION still records the full version for diagnostics.

Fixes #26965
2026-06-24 14:24:28 +05:30
Tonu Samuel 84d900cfda ml: SIMD for KNN brute-force findNearest distance
Vectorize the per-sample squared-Euclidean distance in
BruteForceImpl::findNearestCore with universal intrinsics (two
independent v_fma accumulators + scalar tail). The scalar reduction
accumulates in float in strict order, which the compiler cannot
auto-vectorize without -ffast-math; independent SIMD accumulators
break that serial-accumulation dependency.

Accumulates in float exactly as before (only summation order changes,
~1e-7, below stored float precision); selected neighbors and distances
matched scalar on all test data, ML_KNearest tests pass. Real
findNearest A/B: 3.76x M4, 3.47x Threadripper, 3.73x Xeon, 4.10x A76,
2.82x A55 (in-order).

Adds modules/ml/perf with a findNearest perf test.
2026-06-24 11:31:38 +03:00
kjg0724 2e80d30df4 imgproc: migrate MomentsInTile_SIMD<ushort> to universal:scalable intrinsics (#28881)
Migrate the ushort specialization from CV_SIMD128 to (CV_SIMD ||
CV_SIMD_SCALABLE), enabling AVX2/AVX512/RVV widths instead of 128-bit only.
uchar specialization left at CV_SIMD128 — initial migration regressed on
RVV (Muse Pi v3.0, 0.77~0.85x) when vlanes16 == TILE_SIZE collapses the
SIMD loop to one iteration and per-tile setup overhead dominates.

- replace v_int32x4/v_uint32x4/v_uint64x2 with scalable v_int32/v_uint32/v_uint64
- iota vector in static const sized by VTraits::max_nlanes (initialized once at program load)
- drop buf64; use v_reduce_sum(v_uint64) directly (defined on all backends)
- vx_cleanup() at operator() tail for RVV vsetvl hygiene
2026-06-24 09:33:49 +03:00
胡晨宇 5137676ea7 Merge pull request #29172 from hcy11123323:op
Fast-path transposeND for identity and 2D transpose orders #29172

### Pull Request Readiness Checklist

See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request

- [x] I agree to contribute to the project under Apache 2 License.
- [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
- [x] The PR is proposed to the proper branch
- [ ] There is a reference to the original bug report and related work
- [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
      Patch to opencv_extra has the same branch name.
- [x] The feature is well documented and sample code can be built with the project CMake

<!-- Note!!! If you are an automated agent, we have a special process for you: add 🤖🤖🤖 to the end of the PR title. -->

This PR adds fast paths to cv::transposeND() for two common cases:
- identity permutation: dispatch to copyTo()
- 2D permutation {1, 0}: dispatch to transpose()
All other permutations continue to use the existing generic ND implementation.

Why:
transposeND() currently falls back to the generic memcpy/index-update loop even for the common 2D transpose case, while OpenCV already has an optimized transpose() path. Reusing that path avoids unnecessary index arithmetic and improves performance for 2D inputs passed through transposeND().

Performance:
[BinaryOpTest.transposeND/21 (1920x1080, 8UC3): 28.21 ms -> 0.66 ms (-97.7%)]
[BinaryOpTest.transposeND/22 (1920x1080, 8UC4): 37.61 ms -> 0.88 ms (-97.7%)]
[BinaryOpTest.transposeND/29 (1920x1080, 32FC1): 10.26 ms -> 0.75 ms (-92.7%)]
Full transposeND perf subset: 6760 ms -> 592 ms (-91.2%)
2026-06-24 09:05:14 +03:00
Alexander Smorkalov 15367f7c52 Merge pull request #29374 from Akansha-977:Resize_IPP_4.x
IPP extraction to HAL for resize in imgproc module
2026-06-24 08:59:47 +03:00
Alexander Smorkalov 577f395bee Merge pull request #29373 from asmorkalov:as/tune_meanStdDev_hals
Disable several HAL cases for cv_hal_meanStdDev
2026-06-24 08:58:38 +03:00
Alexander Smorkalov 639fd5d07a Merge pull request #29340 from amd:fast_minmax_simd
core: Optimize minMaxLoc
2026-06-23 17:16:54 +03:00
Akansha Mallick bc184b560e Resize_IPP_Extraction 2026-06-23 18:18:55 +05:30
Alexander Smorkalov af1e2232cd Merge pull request #29368 from tonuonu:fix-filestorage-read-bigint-29363
core: FileStorage reads integers above INT_MAX into float/double without truncation 🧑‍💻🤖
2026-06-23 15:38:59 +03:00
Alexander Smorkalov d8f609c09f Disable several HAL cases for cv_hal_meanStdDev 2026-06-23 15:09:00 +03:00
Uwez Khan 6678e48c12 check parameter tensor length in tflite parsePadding and parseResize
guard the parsePadding NHWC swap on paddings.total()==8 and require the parseResize size tensor to hold height and width, matching the existing parseStridedSlice check
2026-06-23 16:46:41 +05:30
Alexander Smorkalov 2adeab8504 Merge pull request #29314 from uwezkhan:onnx-tensor-payload-size
validate onnx tensor payload size in getMatFromTensor
2026-06-23 13:18:24 +03:00
Madan mohan Manokar 4eb3f8ecb6 Merge pull request #29339 from amd:fast_mean_simd
core: Vectorize meanStdDev #29339

- Add SIMD SumSqr_SIMD reductions (previously scalar) plus AVX-512 dispatch for the mean TU. 
- Uses universal intrinsics, so all SIMD backends benefit.

### 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
2026-06-23 13:13:15 +03:00
Tonu Samuel dceead809e core: FileStorage reads integers above INT_MAX into float/double without truncation
FileNode::operator double() and operator float() read an INT node via readInt()
(32-bit), truncating values above INT_MAX -- e.g. an integer 6662329666 from an
externally-produced json/yaml/xml is read back as -1927604926. The node stores
the value as int64 (operator int64_t() already reads it correctly via readLong),
so use readLong() for the floating-point conversions too. Values that fit in
int32 are unchanged (sign-extended); larger ones are now correct.

Reader side of #29363 (the writer side was #29364).
2026-06-23 11:15:11 +03:00
Pratham Kumar df0c9d7950 Merge pull request #29316 from pratham-mcw:dnn-arm64-neon-blobfromimages
dnn: optimize blobFromImages using NEON intrinsics on ARM64 #29316

- This PR optimizes the HWC-to-NCHW conversion in blobFromImages() by adding NEON implementation.
- On x64, the MSVC compiler automatically vectorizes the existing scalar implementation, resulting in significantly better performance.
- On Windows ARM64, MSVC does not auto-vectorize this stride-3 access pattern, leading to a noticeable performance gap compared to x64.
- Added CV_NEON macro guard to ensure SIMD optimization is enabled on ARM64 architecture, leaving behavior on other platforms completely unchanged.

**Performance Improvements**

- This optimization significantly improves the performance of HWC-to-NCHW conversion on Windows ARM64.

<img width="823" height="254" alt="image" src="https://github.com/user-attachments/assets/84b22deb-22da-4607-8009-3405c2529b80" />

- [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-23 09:25:30 +03:00
Alexander Smorkalov c3aecdd531 Merge pull request #29328 from intel-staging:reenable_ipp_deriv
Enable ipp calls for Sobel, Scharr
2026-06-22 13:18:02 +03:00
Alexander Smorkalov 10f4847bf6 Merge pull request #29321 from uwezkhan:tflite-kernel-scale-count
check tflite kernel scale count against output channels
2026-06-22 11:29:08 +03:00
Alexander Smorkalov 5814ff8f9c Merge pull request #29348 from uwezkhan:filestorage-fmt-pairs-buffer
fix fmt_pairs stack overflow in calcElemSize and decodeSimpleFormat
2026-06-22 09:43:13 +03:00
uwezkhan fc746f35b9 fix fmt_pairs stack overflow in calcElemSize and decodeSimpleFormat 2026-06-20 15:31:34 +05:30
Alexander Smorkalov 4f17d30997 Merge pull request #29192 from hcy11123323:op4
parallelize sort_ using parallel_for_
2026-06-19 14:10:38 +03:00
Alexander Smorkalov 60122e655c Merge pull request #29327 from uwezkhan:tf-slice-begin-size-length
check begin and size have equal length in tensorflow Slice import
2026-06-18 19:39:33 +03:00
Madan mohan Manokar 550251b3c2 core: Optimize minMaxIdx/minMaxLoc
- Replace the fixed-128-bit per-depth minMaxIdx kernels with a single templated universal-intrinsic core
- added simd dispatch AVX-512 dispatch
- A dual-accumulator inner loop added

Uses universal intrinsics, so all SIMD backends benefit.
2026-06-18 16:33:50 +00:00
arshsmith de90d1aec4 Merge pull request #29322 from arshsmith:fix-exif-raw-profile-alloc
Follow-up to the recent "reject under-length raw exif profile" change.

The declared length in a PNG raw exif profile (tEXt/zTXt "Raw profile type
exif" chunk) is fully attacker controlled. The < 6 guard stops the underflow,
but a large positive value still passes it and reaches HexStringToBytes(),
which does raw_data.resize(expected_length) before reading anything. So a few
bytes in a text chunk can force a ~2GB allocation -> easy memory-amplification
DoS while decoding an otherwise small PNG.

The payload is hex encoded (two characters per byte), so a valid length can
never exceed the profile text carrying it. Reject any declared length greater
than profile_len. This bounds the allocation to the input size and doesn't
reject any valid profile; the valid decode path is unchanged.
2026-06-18 19:26:14 +03:00
Madan mohan Manokar 70a2c50b43 Merge pull request #29250 from amd:fast_lut8u_simd
imgproc: optimized LUT and equalizeHist with SIMD #29250

- optimized lut with SIMD
- support equalizeHist with v_lut

### 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
2026-06-18 17:29:18 +03:00
Madan mohan Manokar d0925fa360 Merge pull request #28992 from amd:fast_convert
Improved color convert and AVX512 dispatch added #28992

### 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
2026-06-18 15:20:11 +03:00
Alexander Smorkalov bc18ec2ed3 Merge pull request #29032 from asmorkalov:as/musl_c_tuning_4.x
Musl-C CI and related fixes for 4.x.
2026-06-18 11:06:19 +03:00
Jonas Perolini 853ee9b961 Merge pull request #29329 from JonasPerolini:get-border-errors-once
Optimization ArUco: get border errors in one pass for both normal and inverted markers #29329

This PR is a follow up to optimization in https://github.com/opencv/opencv/pull/29267.

## Context:

When detecting inverted markers, we call `_getBorderErrors` twice:
- `int borderErrors = _getBorderErrors(cellPixelRatio, ...);`
- `Mat invCellPixelRatio = 1.f - cellPixelRatio;`
- `int invBError = _getBorderErrors(invCellPixelRatio, ...);`

meaning:

1. Scan border cells once.
2. Allocate a new Mat.
3. Invert the entire `cellPixelRatio` matrix, not just the border.
4. Scan border cells again on the inverted matrix.
5. If inverted marker is better, copy the full inverted matrix back

## Solution

only call `_getBorderErrors` once and compute both 
- borderErrors: `cellPixelRatio > validBitIdThreshold`
- invBorderErrors: `1 - cellPixelRatio > validBitIdThreshold`

This saves: 
1. full invert into temporary Mat: (`Mat invCellPixelRatio = 1.f - cellPixelRatio;`)
2. scan temporary border: the second `_getBorderErrors`
3. copy temporary Mat back into `cellPixelRatio`: `invCellPixelRatio.copyTo(cellPixelRatio);`

Note that the solution does not implement: 

```
 if(params.detectInvertedMarker) {
    _getBorderErrorsBoth(...);
} else {
    borderErrors = _getBorderErrors(...);
}
```

because the extra cost to computing invBorderErrors: `if(1.f - ratio > validBitIdThreshold) invBorderErrors++;` is negligible. this also avoids having to maintain two functions: `_getBorderErrorsBoth` and `_getBorderErrors`

## Tests:

No visible results when testing the full marker detection pipeline because this step is not expensive. There is a roughly 8% noise when re-running the marker detections making it hard to spot small speed diffs. 

When testing only this specific step with a float matrix of size: `(markerSize + 2*border)^2` on a AMD Ryzen 7 (8 cores / 16 threads):
 
-  The new code is 30-45× faster at this stage depending on the marker size and whether the candidate was actually an inverted one, saving ~530-580 ns per candidate, mainly because we removed `Mat invCellPixelRatio = 1.f - cellPixelRatio`
- The new code is equivalent when detecting non-inverted markers +/- 3 ns

---

### Pull Request Readiness Checklist

See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request

- [x] I agree to contribute to the project under Apache 2 License.
- [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
- [ ] The PR is proposed to the proper branch
- [ ] There is a reference to the original bug report and related work
- [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
      Patch to opencv_extra has the same branch name.
- [ ] The feature is well documented and sample code can be built with the project CMake
2026-06-18 09:40:20 +03:00
Alexander Smorkalov c796023029 Musl-C CI and related fixes for 4.x. 2026-06-18 09:14:36 +03:00
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
uwezkhan 4e56cf9ac8 check begin and size lengths match in tensorflow parseSlice 2026-06-17 14:05:39 +05:30
uwezkhan 414a0379d8 check tflite kernel scale count against output channels 2026-06-16 22:19:58 +05:30
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
uwezkhan 57081ac946 validate onnx tensor payload size in getMatFromTensor
Reject initializers whose declared shape claims more elements than the tensor payload (raw_data or a typed *_data field) actually holds, before the Mat copy/convert reads them.
2026-06-16 15:55:31 +05:30
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
Andrei Fedorov 8e157165e8 enable ipp calls 2026-06-15 12:28:12 +02: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
胡晨宇 d79d9d7a5b parallelize sort_ using parallel_for_ 2026-05-31 11:07:12 +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
236 changed files with 15904 additions and 4780 deletions
+3
View File
@@ -15,6 +15,9 @@ jobs:
Linux-no-HAL:
uses: opencv/ci-gha-workflow/.github/workflows/OCV-PR-Linux-NoHAL.yaml@main
Linux-Apline:
uses: opencv/ci-gha-workflow/.github/workflows/OCV-PR-Linux-Alpine.yaml@main
Windows:
uses: opencv/ci-gha-workflow/.github/workflows/OCV-PR-Windows.yaml@main
with:
+6 -6
View File
@@ -1,9 +1,9 @@
# Binaries branch name: ffmpeg/4.x_20251226
# Binaries were created for OpenCV: cff7581175d2abfc6aef2e4f04f482e258b5c864
ocv_update(FFMPEG_BINARIES_COMMIT "d82ad9a54a7b42a1648a9cae8fed5c2f20ea396c")
ocv_update(FFMPEG_FILE_HASH_BIN32 "47730de2286110b0d1250ff9cf50ce56")
ocv_update(FFMPEG_FILE_HASH_BIN64 "3248b4663ffef770cdb54ec8b9d16a28")
ocv_update(FFMPEG_FILE_HASH_CMAKE "8862c87496e2e8c375965e1277dee1c7")
# Binaries branch name: ffmpeg/4.x_20260715
# Binaries were created for OpenCV: 6b640b424c516d27217700392a7ed6a362e040a5
ocv_update(FFMPEG_BINARIES_COMMIT "bd9418020a5c342be979c56d6e6434261959d3af")
ocv_update(FFMPEG_FILE_HASH_BIN32 "31968b434799d3dd56969b15aac3efa8")
ocv_update(FFMPEG_FILE_HASH_BIN64 "84757ed0f16ddedce99227529d3165e9")
ocv_update(FFMPEG_FILE_HASH_CMAKE "e09efc33312d1173be8a9446f3b088fe")
function(download_win_ffmpeg script_var)
set(${script_var} "" PARENT_SCOPE)
+5 -5
View File
@@ -2,7 +2,7 @@ function(download_ippicv root_var)
set(${root_var} "" PARENT_SCOPE)
# Commit SHA in the opencv_3rdparty repo
set(IPPICV_COMMIT "406d398c436d0465c8e53dd432d9ecd9301d5f4a")
set(IPPICV_COMMIT "8338862a733cb3980d8b51d8e14917fe0e695f71")
# Define actual ICV versions
if(APPLE)
set(IPPICV_COMMIT "0cc4aa06bf2bef4b05d237c69a5a96b9cd0cb85a")
@@ -14,8 +14,8 @@ function(download_ippicv root_var)
set(OPENCV_ICV_PLATFORM "linux")
set(OPENCV_ICV_PACKAGE_SUBDIR "ippicv_lnx")
if(X86_64)
set(OPENCV_ICV_NAME "ippicv_2026.0.0_lnx_intel64_20260327_general.tgz")
set(OPENCV_ICV_HASH "9a3ee0c5c3c02102faa422d60bfd1f4a")
set(OPENCV_ICV_NAME "ippicv_2026.0.0_lnx_intel64_20260630_general.tgz")
set(OPENCV_ICV_HASH "a77e60db544e07a126ae98f5ffe83be1")
else()
if(ANDROID)
set(IPPICV_COMMIT "c7c6d527dde5fee7cb914ee9e4e20f7436aab3a1")
@@ -31,8 +31,8 @@ function(download_ippicv root_var)
set(OPENCV_ICV_PLATFORM "windows")
set(OPENCV_ICV_PACKAGE_SUBDIR "ippicv_win")
if(X86_64)
set(OPENCV_ICV_NAME "ippicv_2026.0.0_win_intel64_20260327_general.zip")
set(OPENCV_ICV_HASH "73bc67cd5e4c8da706fa88fe84630231")
set(OPENCV_ICV_NAME "ippicv_2026.0.0_win_intel64_20260630_general.zip")
set(OPENCV_ICV_HASH "d81c8b7d40da2867df82f0077a40afa1")
else()
set(IPPICV_COMMIT "7f55c0c26be418d494615afca15218566775c725")
set(OPENCV_ICV_NAME "ippicv_2021.12.0_win_ia32_20240425_general.zip")
-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")
+5 -1
View File
@@ -96,7 +96,11 @@ else() # UNIX
endif()
ocv_update(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${OPENCV_LIB_INSTALL_PATH}")
if(APPLE_FRAMEWORK AND BUILD_SHARED_LIBS)
ocv_update(CMAKE_INSTALL_RPATH "@executable_path/Frameworks;@loader_path/Frameworks;@executable_path/../${OPENCV_3P_LIB_INSTALL_PATH}")
else()
ocv_update(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${OPENCV_LIB_INSTALL_PATH}")
endif()
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
if(INSTALL_TO_MANGLED_PATHS)
+8 -1
View File
@@ -1 +1,8 @@
# empty
# Additional flags for dynamic framework (macOS)
if(APPLE_FRAMEWORK AND BUILD_SHARED_LIBS)
string(APPEND CMAKE_MODULE_LINKER_FLAGS " -rpath @executable_path/Frameworks -rpath @loader_path/Frameworks")
string(APPEND CMAKE_SHARED_LINKER_FLAGS " -rpath @executable_path/Frameworks -rpath @loader_path/Frameworks")
string(APPEND CMAKE_EXE_LINKER_FLAGS " -rpath @executable_path/Frameworks -rpath @loader_path/Frameworks")
set(CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG 1)
set(CMAKE_INSTALL_NAME_DIR "@rpath")
endif()
@@ -10,6 +10,14 @@ set(OpenCV_CUDNN_VERSION "@CUDNN_VERSION@")
set(OpenCV_USE_CUDNN "@HAVE_CUDNN@")
set(ENABLE_CUDA_FIRST_CLASS_LANGUAGE ON)
# By default OpenCV only requires the consumer's CUDA Toolkit to match the
# major.minor version it was built with. The CUDA runtime API is stable within
# a minor release and patch versions change frequently, so pinning the patch
# component forces needless rebuilds (issue #26965). Set
# OPENCV_STRONG_CUDA_VERSION_CHECK before find_package(OpenCV) to require an
# exact match including the patch component.
string(REGEX MATCH "^[0-9]+\\.[0-9]+" OpenCV_CUDA_VERSION_MAJOR_MINOR "${OpenCV_CUDA_VERSION}")
if(NOT CUDAToolkit_FOUND)
if(NOT CMAKE_VERSION VERSION_LESS 3.18)
if(UNIX AND NOT CMAKE_CUDA_COMPILER AND NOT CUDAToolkit_ROOT)
@@ -17,15 +25,28 @@ if(NOT CUDAToolkit_FOUND)
set(CUDA_PATH "/usr/local/cuda" CACHE INTERNAL "")
set(ENV{CUDA_PATH} ${CUDA_PATH})
endif()
find_package(CUDAToolkit ${OpenCV_CUDA_VERSION} EXACT REQUIRED)
if(OPENCV_STRONG_CUDA_VERSION_CHECK)
find_package(CUDAToolkit ${OpenCV_CUDA_VERSION} EXACT REQUIRED)
else()
find_package(CUDAToolkit ${OpenCV_CUDA_VERSION_MAJOR_MINOR} REQUIRED)
endif()
else()
message(FATAL_ERROR "Using OpenCV compiled with CUDA as first class language requires CMake \>= 3.18.")
endif()
else()
if(CUDAToolkit_FOUND)
set(CUDA_VERSION_STRING ${CUDAToolkit_VERSION})
endif()
if(NOT CUDA_VERSION_STRING VERSION_EQUAL OpenCV_CUDA_VERSION)
message(FATAL_ERROR "OpenCV library was compiled with CUDA ${OpenCV_CUDA_VERSION} support. Please, use the same version or rebuild OpenCV with CUDA ${CUDA_VERSION_STRING}")
endif()
endif()
if(CUDAToolkit_FOUND)
set(CUDA_VERSION_STRING ${CUDAToolkit_VERSION})
endif()
string(REGEX MATCH "^[0-9]+\\.[0-9]+" CUDA_VERSION_STRING_MAJOR_MINOR "${CUDA_VERSION_STRING}")
if(OPENCV_STRONG_CUDA_VERSION_CHECK)
set(__ocv_cuda_found "${CUDA_VERSION_STRING}")
set(__ocv_cuda_built "${OpenCV_CUDA_VERSION}")
else()
set(__ocv_cuda_found "${CUDA_VERSION_STRING_MAJOR_MINOR}")
set(__ocv_cuda_built "${OpenCV_CUDA_VERSION_MAJOR_MINOR}")
endif()
if(NOT __ocv_cuda_found VERSION_EQUAL __ocv_cuda_built)
message(FATAL_ERROR "OpenCV library was compiled with CUDA ${OpenCV_CUDA_VERSION} support. Please, use the same version or rebuild OpenCV with CUDA ${CUDA_VERSION_STRING}")
endif()
@@ -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.
+1 -1
View File
@@ -1350,7 +1350,7 @@
@article{TRUCO2026,
title={TRUCO: A Scalable Lock-Free Algorithm for Parallel Contour Extraction},
author={Mu\~noz-Salinas, Rafael and Romero-Ramírez, Francisco J. and Marín-Jiménez, Manuel J.},
journal={Pattern Recognition under review},
journal={To be published},
year={2026}
}
@@ -81,7 +81,7 @@ pass in terms of square size).
So to find pattern in chess board, we can use the function, **cv.findChessboardCorners()**. We also
need to pass what kind of pattern we are looking for, like 8x8 grid, 5x5 grid etc. In this example, we
use 7x6 grid. (Normally a chess board has 8x8 squares and 7x7 internal corners). It returns the
use 6x7 grid. (Normally a chess board has 8x8 squares and 7x7 internal corners). It returns the
corner points and retval which will be True if pattern is obtained. These corners will be placed in
an order (from left-to-right, top-to-bottom)
@@ -107,9 +107,11 @@ import glob
# termination criteria
criteria = (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 30, 0.001)
# prepare object points, like (0,0,0), (1,0,0), (2,0,0) ....,(6,5,0)
objp = np.zeros((6*7,3), np.float32)
objp[:,:2] = np.mgrid[0:7,0:6].T.reshape(-1,2)
# prepare object points, like (0,0,0), (1,0,0), (2,0,0) ....,(5,6,0)
cols = 6
rows = 7
objp = np.zeros((cols*rows,3), np.float32)
objp[:,:2] = np.mgrid[0:cols,0:rows].T.reshape(-1,2)
# Arrays to store object points and image points from all the images.
objpoints = [] # 3d point in real world space
@@ -122,7 +124,7 @@ for fname in images:
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# Find the chess board corners
ret, corners = cv.findChessboardCorners(gray, (7,6), None)
ret, corners = cv.findChessboardCorners(gray, (cols, rows), None)
# If found, add object points, image points (after refining them)
if ret == True:
@@ -132,7 +134,7 @@ for fname in images:
imgpoints.append(corners2)
# Draw and display the corners
cv.drawChessboardCorners(img, (7,6), corners2, ret)
cv.drawChessboardCorners(img, (cols, rows), corners2, ret)
cv.imshow('img', img)
cv.waitKey(500)
@@ -50,12 +50,14 @@ our X axis is drawn from (0,0,0) to (3,0,0), so for Y axis. For Z axis, it is dr
(0,0,-3). Negative denotes it is drawn towards the camera.
@code{.py}
criteria = (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 30, 0.001)
objp = np.zeros((6*7,3), np.float32)
objp[:,:2] = np.mgrid[0:7,0:6].T.reshape(-1,2)
cols = 6
rows = 7
objp = np.zeros((cols*rows,3), np.float32)
objp[:,:2] = np.mgrid[0:cols,0:rows].T.reshape(-1,2)
axis = np.float32([[3,0,0], [0,3,0], [0,0,-3]]).reshape(-1,3)
@endcode
Now, as usual, we load each image. Search for 7x6 grid. If found, we refine it with subcorner
Now, as usual, we load each image. Search for 6x7 grid. If found, we refine it with subcorner
pixels. Then to calculate the rotation and translation, we use the function,
**cv.solvePnPRansac()**. Once we those transformation matrices, we use them to project our **axis
points** to the image plane. In simple words, we find the points on image plane corresponding to
@@ -65,7 +67,7 @@ to each of these points using our generateImage() function. Done !!!
for fname in glob.glob('left*.jpg'):
img = cv.imread(fname)
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
ret, corners = cv.findChessboardCorners(gray, (7,6),None)
ret, corners = cv.findChessboardCorners(gray, (cols, rows), None)
if ret == True:
corners2 = cv.cornerSubPix(gray,corners,(11,11),(-1,-1),criteria)
@@ -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
@@ -46,14 +46,14 @@ Open your Doxyfile using your favorite text editor and search for the key
`TAGFILES`. Change it as follows:
@code
TAGFILES = ./docs/doxygen-tags/opencv.tag=http://docs.opencv.org/4.13.0
TAGFILES = ./docs/doxygen-tags/opencv.tag=http://docs.opencv.org/4.14.0
@endcode
If you had other definitions already, you can append the line using a `\`:
@code
TAGFILES = ./docs/doxygen-tags/libstdc++.tag=https://gcc.gnu.org/onlinedocs/libstdc++/latest-doxygen \
./docs/doxygen-tags/opencv.tag=http://docs.opencv.org/4.13.0
./docs/doxygen-tags/opencv.tag=http://docs.opencv.org/4.14.0
@endcode
Doxygen can now use the information from the tag file to link to the OpenCV
@@ -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)
{
+11
View File
@@ -9,13 +9,24 @@ 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"
"${CMAKE_CURRENT_SOURCE_DIR}/src/cart_polar_ipp.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/transforms_ipp.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/warp_ipp.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/deriv_ipp.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/resize_ipp.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/box_filter_ipp.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/sum_ipp.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/color_ipp.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/filter_ipp.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/matchtemplate_ipp.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/canny_ipp.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/threshold_ipp.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/distancetransform_ipp.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/histogram_ipp.cpp"
)
#TODO: HAVE_IPP_ICV and HAVE_IPP_IW added as private macro till OpenCV itself is
+31 -2
View File
@@ -12,8 +12,10 @@
int ipp_hal_meanStdDev(const uchar* src_data, size_t src_step, int width, int height, int src_type,
double* mean_val, double* stddev_val, uchar* mask, size_t mask_step);
#undef cv_hal_meanStdDev
#define cv_hal_meanStdDev ipp_hal_meanStdDev
// IPP version is less efficient than implementation with universtal intrinsics
// See https://github.com/opencv/opencv/pull/29339
//#undef cv_hal_meanStdDev
//#define cv_hal_meanStdDev ipp_hal_meanStdDev
int ipp_hal_minMaxIdxMaskStep(const uchar* src_data, size_t src_step, int width, int height, int depth,
double* _minVal, double* _maxVal, int* _minIdx, int* _maxIdx, uchar* mask, size_t mask_step);
@@ -74,6 +76,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
+143 -1
View File
@@ -8,13 +8,35 @@
#include <opencv2/core/base.hpp>
#include "ipp_utils.hpp"
#if IPP_VERSION_X100 >= 810
// Disabled in https://github.com/opencv/opencv/pull/13085 due large binary size
#define DISABLE_IPP_BOX_FILTER 1
// IPP filter2D integration is disabled in main OpenCV; kept behind a macro like box filter
#define DISABLE_IPP_FILTER2D 1
// Too big difference compared to OpenCV FFT-based convolution, different results on masks > 7x7
#define IPP_DISABLE_FILTER2D_BIG_MASK 1
#if IPP_VERSION_X100 >= 810
#if defined(HAVE_IPP_IW)
int ipp_hal_warpAffine(int src_type, const uchar *src_data, size_t src_step, int src_width, int src_height, uchar *dst_data, size_t dst_step, int dst_width,
int dst_height, const double M[6], int interpolation, int borderType, const double borderValue[4]);
#undef cv_hal_warpAffine
#define cv_hal_warpAffine ipp_hal_warpAffine
int ipp_hal_sobel(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step,
int width, int height, int src_depth, int dst_depth, int cn,
int margin_left, int margin_top, int margin_right, int margin_bottom,
int dx, int dy, int ksize, double scale, double delta, int border_type);
#undef cv_hal_sobel
#define cv_hal_sobel ipp_hal_sobel
int ipp_hal_scharr(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step,
int width, int height, int src_depth, int dst_depth, int cn,
int margin_left, int margin_top, int margin_right, int margin_bottom,
int dx, int dy, double scale, double delta, int border_type);
#undef cv_hal_scharr
#define cv_hal_scharr ipp_hal_scharr
#endif
#if IPP_VERSION_X100 >= 202600
@@ -26,6 +48,24 @@ int ipp_hal_warpPerspective(int src_type, const uchar *src_data, size_t src_step
#endif // IPP_VERSION_X100 >= 202600
#if defined(HAVE_IPP_IW)
int ipp_hal_resize(int src_type, const uchar *src_data, size_t src_step, int src_width, int src_height,
uchar *dst_data, size_t dst_step, int dst_width, int dst_height,
double inv_scale_x, double inv_scale_y, int interpolation);
#undef cv_hal_resize
#define cv_hal_resize ipp_hal_resize
#endif // HAVE_IPP_IW
#if defined(HAVE_IPP_IW) && !DISABLE_IPP_BOX_FILTER
int ipp_hal_boxFilter(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step,
int width, int height, int src_depth, int dst_depth, int cn,
int margin_left, int margin_top, int margin_right, int margin_bottom,
size_t ksize_width, size_t ksize_height, int anchor_x, int anchor_y,
bool normalize, int border_type);
#undef cv_hal_boxFilter
#define cv_hal_boxFilter ipp_hal_boxFilter
#endif // defined(HAVE_IPP_IW) && !DISABLE_IPP_BOX_FILTER
int ipp_hal_remap32f(int src_type, const uchar *src_data, size_t src_step, int src_width, int src_height,
uchar *dst_data, size_t dst_step, int dst_width, int dst_height,
float* mapx, size_t mapx_step, float* mapy, size_t mapy_step,
@@ -33,6 +73,108 @@ int ipp_hal_remap32f(int src_type, const uchar *src_data, size_t src_step, int s
#undef cv_hal_remap32f
#define cv_hal_remap32f ipp_hal_remap32f
#if defined(HAVE_IPP_IW) && !DISABLE_IPP_FILTER2D
int ipp_hal_filter2D(const uchar * src_data, size_t src_step, int src_type,
uchar * dst_data, size_t dst_step, int dst_type,
int width, int height, int full_width, int full_height,
int offset_x, int offset_y,
const uchar * kernel_data, size_t kernel_step, int kernel_type,
int kernel_width, int kernel_height,
int anchor_x, int anchor_y, double delta, int borderType,
bool isSubmatrix, bool allowInplace);
#undef cv_hal_filter_stateless
#define cv_hal_filter_stateless ipp_hal_filter2D
#endif // defined(HAVE_IPP_IW) && !DISABLE_IPP_FILTER2D
#endif //IPP_VERSION_X100 >= 810
#if IPP_VERSION_X100 >= 700
int ipp_hal_cvtBGRtoGray(const uchar * src_data, size_t src_step, uchar * dst_data, size_t dst_step, int width, int height, int depth, int scn, bool swapBlue);
#undef cv_hal_cvtBGRtoGray
#define cv_hal_cvtBGRtoGray ipp_hal_cvtBGRtoGray
int ipp_hal_cvtBGRtoBGR(const uchar * src_data, size_t src_step, uchar * dst_data, size_t dst_step, int width, int height, int depth, int scn, int dcn, bool swapBlue);
#undef cv_hal_cvtBGRtoBGR
#define cv_hal_cvtBGRtoBGR ipp_hal_cvtBGRtoBGR
int ipp_hal_cvtGraytoBGR(const uchar * src_data, size_t src_step, uchar * dst_data, size_t dst_step, int width, int height, int depth, int dcn);
#undef cv_hal_cvtGraytoBGR
#define cv_hal_cvtGraytoBGR ipp_hal_cvtGraytoBGR
int ipp_hal_cvtBGRtoHSV(const uchar * src_data, size_t src_step, uchar * dst_data, size_t dst_step, int width, int height, int depth, int scn, bool swapBlue, bool isFullRange, bool isHSV);
#undef cv_hal_cvtBGRtoHSV
#define cv_hal_cvtBGRtoHSV ipp_hal_cvtBGRtoHSV
int ipp_hal_cvtHSVtoBGR(const uchar * src_data, size_t src_step, uchar * dst_data, size_t dst_step, int width, int height, int depth, int dcn, bool swapBlue, bool isFullRange, bool isHSV);
#undef cv_hal_cvtHSVtoBGR
#define cv_hal_cvtHSVtoBGR ipp_hal_cvtHSVtoBGR
int ipp_hal_cvtBGRtoYUV(const uchar * src_data, size_t src_step, uchar * dst_data, size_t dst_step, int width, int height, int depth, int scn, bool swapBlue, bool isCbCr);
#undef cv_hal_cvtBGRtoYUV
#define cv_hal_cvtBGRtoYUV ipp_hal_cvtBGRtoYUV
int ipp_hal_cvtYUVtoBGR(const uchar * src_data, size_t src_step, uchar * dst_data, size_t dst_step, int width, int height, int depth, int dcn, bool swapBlue, bool isCbCr);
#undef cv_hal_cvtYUVtoBGR
#define cv_hal_cvtYUVtoBGR ipp_hal_cvtYUVtoBGR
int ipp_hal_cvtBGRtoXYZ(const uchar * src_data, size_t src_step, uchar * dst_data, size_t dst_step, int width, int height, int depth, int scn, bool swapBlue);
#undef cv_hal_cvtBGRtoXYZ
#define cv_hal_cvtBGRtoXYZ ipp_hal_cvtBGRtoXYZ
int ipp_hal_cvtXYZtoBGR(const uchar * src_data, size_t src_step, uchar * dst_data, size_t dst_step, int width, int height, int depth, int dcn, bool swapBlue);
#undef cv_hal_cvtXYZtoBGR
#define cv_hal_cvtXYZtoBGR ipp_hal_cvtXYZtoBGR
int ipp_hal_cvtBGRtoLab(const uchar * src_data, size_t src_step, uchar * dst_data, size_t dst_step, int width, int height, int depth, int scn, bool swapBlue, bool isLab, bool srgb);
#undef cv_hal_cvtBGRtoLab
#define cv_hal_cvtBGRtoLab ipp_hal_cvtBGRtoLab
int ipp_hal_cvtLabtoBGR(const uchar * src_data, size_t src_step, uchar * dst_data, size_t dst_step, int width, int height, int depth, int dcn, bool swapBlue, bool isLab, bool srgb);
#undef cv_hal_cvtLabtoBGR
#define cv_hal_cvtLabtoBGR ipp_hal_cvtLabtoBGR
int ipp_hal_cvtRGBAtoMultipliedRGBA(const uchar * src_data, size_t src_step, uchar * dst_data, size_t dst_step, int width, int height);
#undef cv_hal_cvtRGBAtoMultipliedRGBA
#define cv_hal_cvtRGBAtoMultipliedRGBA ipp_hal_cvtRGBAtoMultipliedRGBA
int ipp_hal_matchTemplate(const uchar* src_data, size_t src_step, int src_width, int src_height,
const uchar* templ_data, size_t templ_step, int templ_width, int templ_height,
float* result_data, size_t result_step, int depth, int cn, int method);
#undef cv_hal_matchTemplate
#define cv_hal_matchTemplate ipp_hal_matchTemplate
int ipp_hal_threshold(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step,
int width, int height, int depth, int cn, double thresh, double maxValue, int thresholdType);
#undef cv_hal_threshold
#define cv_hal_threshold ipp_hal_threshold
int ipp_hal_distanceTransform(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step,
int width, int height, int dst_type, int dist_type, int mask_size);
#undef cv_hal_distanceTransform
#define cv_hal_distanceTransform ipp_hal_distanceTransform
int ipp_hal_calcHist(const uchar* src_data, size_t src_step, int src_type, int src_width, int src_height,
float* hist_data, int hist_size, const float** ranges, bool uniform, bool accumulate);
#undef cv_hal_calcHist
#define cv_hal_calcHist ipp_hal_calcHist
#endif // IPP_VERSION_X100 >= 700
#define IPP_DISABLE_PERF_CANNY_MT 1 // cv::Canny OpenCV MT performance is better
#if defined(HAVE_IPP_IW)
int ipp_hal_canny(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step,
int width, int height, int cn,
double lowThreshold, double highThreshold, int ksize, bool L2gradient);
#undef cv_hal_canny
#define cv_hal_canny ipp_hal_canny
int ipp_hal_canny_deriv(const short* dx_data, size_t dx_step, const short* dy_data, size_t dy_step,
uchar* dst_data, size_t dst_step, int width, int height, int cn,
double lowThreshold, double highThreshold, bool L2gradient);
#undef cv_hal_canny_deriv
#define cv_hal_canny_deriv ipp_hal_canny_deriv
#endif
#endif //__IPP_HAL_IMGPROC_HPP__
+144
View File
@@ -0,0 +1,144 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html
// Copyright (C) 2026, BigVision LLC, all rights reserved.
// Third party copyrights are property of their respective owners.
#include "ipp_hal_imgproc.hpp"
#include <opencv2/core.hpp>
#include "precomp_ipp.hpp"
#if defined(HAVE_IPP_IW) && !DISABLE_IPP_BOX_FILTER
namespace cv { namespace ipp { unsigned long long getIppTopFeatures(); } }
// Copied from core/private.hpp (gated by HAVE_IPP, which the plugin lacks).
// boxGetIppBorderType: distinct name, adds the REFLECT_101 precomp's version drops.
static inline IppiBorderType boxGetIppBorderType(int borderTypeNI)
{
return borderTypeNI == cv::BORDER_CONSTANT ? ippBorderConst :
borderTypeNI == cv::BORDER_TRANSPARENT ? ippBorderTransp :
borderTypeNI == cv::BORDER_REPLICATE ? ippBorderRepl :
borderTypeNI == cv::BORDER_REFLECT_101 ? ippBorderMirror :
(IppiBorderType)-1;
}
static inline bool ippiCheckAnchor(int x, int y, int kernelWidth, int kernelHeight)
{
return (x == (kernelWidth - 1)/2 && y == (kernelHeight - 1)/2);
}
static inline IppiBorderType ippiGetBorder(::ipp::IwiImage &image, int ocvBorderType, ::ipp::IwiBorderSize &borderSize)
{
int inMemFlags = 0;
IppiBorderType border = boxGetIppBorderType(ocvBorderType & ~cv::BORDER_ISOLATED);
if((int)border == -1)
return (IppiBorderType)0;
if(!(ocvBorderType & cv::BORDER_ISOLATED))
{
if(image.m_inMemSize.left)
{
if(image.m_inMemSize.left >= borderSize.left)
inMemFlags |= ippBorderInMemLeft;
else
return (IppiBorderType)0;
}
else
borderSize.left = 0;
if(image.m_inMemSize.top)
{
if(image.m_inMemSize.top >= borderSize.top)
inMemFlags |= ippBorderInMemTop;
else
return (IppiBorderType)0;
}
else
borderSize.top = 0;
if(image.m_inMemSize.right)
{
if(image.m_inMemSize.right >= borderSize.right)
inMemFlags |= ippBorderInMemRight;
else
return (IppiBorderType)0;
}
else
borderSize.right = 0;
if(image.m_inMemSize.bottom)
{
if(image.m_inMemSize.bottom >= borderSize.bottom)
inMemFlags |= ippBorderInMemBottom;
else
return (IppiBorderType)0;
}
else
borderSize.bottom = 0;
}
else
borderSize.left = borderSize.right = borderSize.top = borderSize.bottom = 0;
return (IppiBorderType)(border | inMemFlags);
}
int ipp_hal_boxFilter(const uchar* src_data, size_t src_step,
uchar* dst_data, size_t dst_step,
int width, int height, int src_depth, int dst_depth, int cn,
int margin_left, int margin_top, int margin_right, int margin_bottom,
size_t ksize_width, size_t ksize_height,
int anchor_x, int anchor_y,
bool normalize, int border_type)
{
CV_HAL_CHECK_USE_IPP();
#if IPP_VERSION_X100 < 201801
// Problem with SSE42 optimization for 16s and some 8u modes
if(cv::ipp::getIppTopFeatures() == ippCPUID_SSE42 &&
(((src_depth == CV_16S || src_depth == CV_16U) && (cn == 3 || cn == 4)) ||
(src_depth == CV_8U && cn == 3 && (ksize_width > 5 || ksize_height > 5))))
return CV_HAL_ERROR_NOT_IMPLEMENTED;
// Other optimizations has some degradations too
if(((src_depth == CV_16S || src_depth == CV_16U) && cn == 4) ||
(src_depth == CV_8U && cn == 1 && (ksize_width > 5 || ksize_height > 5)))
return CV_HAL_ERROR_NOT_IMPLEMENTED;
#endif
if(!normalize)
return CV_HAL_ERROR_NOT_IMPLEMENTED;
if(!ippiCheckAnchor(anchor_x, anchor_y, (int)ksize_width, (int)ksize_height))
return CV_HAL_ERROR_NOT_IMPLEMENTED;
try
{
// raw-pointer equivalent of ippiGetImage: margins are the in-memory border
::ipp::IwiBorderSize inMemBorder;
inMemBorder.left = (IwSize)margin_left;
inMemBorder.top = (IwSize)margin_top;
inMemBorder.right = (IwSize)margin_right;
inMemBorder.bottom = (IwSize)margin_bottom;
::ipp::IwiImage iwSrc, iwDst;
iwSrc.Init(IwiSize{width, height}, ippiGetDataType(src_depth), cn,
inMemBorder, (void*)src_data, IwSize(src_step));
iwDst.Init(IwiSize{width, height}, ippiGetDataType(dst_depth), cn,
::ipp::IwiBorderSize(), dst_data, IwSize(dst_step));
::ipp::IwiSize iwKSize{(int)ksize_width, (int)ksize_height};
::ipp::IwiBorderSize borderSize(iwKSize);
::ipp::IwiBorderType ippBorder(ippiGetBorder(iwSrc, border_type, borderSize));
if(!ippBorder)
return CV_HAL_ERROR_NOT_IMPLEMENTED;
CV_INSTRUMENT_FUN_IPP(::ipp::iwiFilterBox, iwSrc, iwDst, iwKSize, ::ipp::IwDefault(), ippBorder);
}
catch (const ::ipp::IwException &)
{
return CV_HAL_ERROR_NOT_IMPLEMENTED;
}
return CV_HAL_ERROR_OK;
}
#endif // defined(HAVE_IPP_IW) && !DISABLE_IPP_BOX_FILTER
+95
View File
@@ -0,0 +1,95 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html
// Copyright (C) 2026, BigVision LLC, all rights reserved.
// Third party copyrights are property of their respective owners.
#include "ipp_hal_imgproc.hpp"
#ifdef HAVE_IPP_IW
#include <opencv2/core.hpp>
#include "precomp_ipp.hpp"
int ipp_hal_canny(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step,
int width, int height, int cn,
double lowThreshold, double highThreshold, int ksize, bool L2gradient)
{
CV_HAL_CHECK_USE_IPP();
#if IPP_DISABLE_PERF_CANNY_MT
if(cv::getNumThreads() > 1)
return CV_HAL_ERROR_NOT_IMPLEMENTED;
#endif
if(width <= 3 || height <= 3)
return CV_HAL_ERROR_NOT_IMPLEMENTED;
if(cn != 1)
return CV_HAL_ERROR_NOT_IMPLEMENTED;
IppiMaskSize kernel;
if(ksize == 3)
kernel = ippMskSize3x3;
else if(ksize == 5)
kernel = ippMskSize5x5;
else
return CV_HAL_ERROR_NOT_IMPLEMENTED;
IppNormType norm = L2gradient ? ippNormL2 : ippNormL1;
try
{
::ipp::IwiImage iwSrc, iwDst;
iwSrc.Init(IwiSize{width, height}, ipp8u, cn, ::ipp::IwiBorderSize(), (void*)src_data, IwSize(src_step));
iwDst.Init(IwiSize{width, height}, ipp8u, cn, ::ipp::IwiBorderSize(), dst_data, IwSize(dst_step));
CV_INSTRUMENT_FUN_IPP(::ipp::iwiFilterCanny, iwSrc, iwDst, (float)lowThreshold, (float)highThreshold,
::ipp::IwiFilterCannyParams(ippFilterSobel, kernel, norm), ippBorderRepl);
}
catch (const ::ipp::IwException &)
{
return CV_HAL_ERROR_NOT_IMPLEMENTED;
}
return CV_HAL_ERROR_OK;
}
int ipp_hal_canny_deriv(const short* dx_data, size_t dx_step, const short* dy_data, size_t dy_step,
uchar* dst_data, size_t dst_step, int width, int height, int cn,
double lowThreshold, double highThreshold, bool L2gradient)
{
CV_HAL_CHECK_USE_IPP();
#if IPP_DISABLE_PERF_CANNY_MT
if(cv::getNumThreads() > 1)
return CV_HAL_ERROR_NOT_IMPLEMENTED;
#endif
if(width <= 3 || height <= 3)
return CV_HAL_ERROR_NOT_IMPLEMENTED;
if(cn != 1)
return CV_HAL_ERROR_NOT_IMPLEMENTED;
IppNormType norm = L2gradient ? ippNormL2 : ippNormL1;
try
{
::ipp::IwiImage iwSrcDx, iwSrcDy, iwDst;
iwSrcDx.Init(IwiSize{width, height}, ipp16s, cn, ::ipp::IwiBorderSize(), (void*)dx_data, IwSize(dx_step));
iwSrcDy.Init(IwiSize{width, height}, ipp16s, cn, ::ipp::IwiBorderSize(), (void*)dy_data, IwSize(dy_step));
iwDst.Init(IwiSize{width, height}, ipp8u, cn, ::ipp::IwiBorderSize(), dst_data, IwSize(dst_step));
CV_INSTRUMENT_FUN_IPP(::ipp::iwiFilterCannyDeriv, iwSrcDx, iwSrcDy, iwDst, (float)lowThreshold, (float)highThreshold,
::ipp::IwiFilterCannyDerivParams(norm));
}
catch (const ::ipp::IwException &)
{
return CV_HAL_ERROR_NOT_IMPLEMENTED;
}
return CV_HAL_ERROR_OK;
}
#endif
+958
View File
@@ -0,0 +1,958 @@
// 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_imgproc.hpp"
#include <opencv2/core.hpp>
#include "precomp_ipp.hpp"
#include <limits>
#if IPP_VERSION_X100 >= 700
#define MAX_IPP8u 255
#define MAX_IPP16u 65535
#define MAX_IPP32f 1.0
// moved from color_rgb.dispatch.cpp
#define IPP_DISABLE_CVTCOLOR_GRAY2BGR_8UC3 1
// replicated from core/private.hpp
#define IPP_DISABLE_RGB_HSV 1
#define IPP_DISABLE_RGB_YUV 1
#define IPP_DISABLE_YUV_RGB 1
#define IPP_DISABLE_RGB_XYZ 1
#define IPP_DISABLE_XYZ_RGB 1
#define IPP_DISABLE_RGB_LAB 1
#define IPP_DISABLE_LAB_RGB 1
namespace {
typedef IppStatus (CV_STDCALL* ippiGeneralFunc)(const void *, int, void *, int, IppiSize);
typedef IppStatus (CV_STDCALL* ippiColor2GrayFunc)(const void *, int, void *, int, IppiSize, const Ipp32f *);
typedef IppStatus (CV_STDCALL* ippiReorderFunc)(const void *, int, void *, int, IppiSize, const int *);
// BT.601 BGR->Y weights
static const float B2YF = 0.114f;
static const float G2YF = 0.587f;
static const float R2YF = 0.299f;
template<typename _Tp> struct ColorChannel
{
static inline _Tp max() { return std::numeric_limits<_Tp>::max(); }
};
template<> struct ColorChannel<float>
{
static inline float max() { return 1.f; }
};
template <typename Cvt>
class CvtColorIPPLoop_Invoker : public cv::ParallelLoopBody
{
public:
CvtColorIPPLoop_Invoker(const uchar * src_data_, size_t src_step_, uchar * dst_data_, size_t dst_step_,
int width_, const Cvt& _cvt, bool *_ok) :
ParallelLoopBody(), src_data(src_data_), src_step(src_step_), dst_data(dst_data_), dst_step(dst_step_),
width(width_), cvt(_cvt), ok(_ok)
{
*ok = true;
}
virtual void operator()(const cv::Range& range) const CV_OVERRIDE
{
const void *yS = src_data + src_step * range.start;
void *yD = dst_data + dst_step * range.start;
if( !cvt(yS, static_cast<int>(src_step), yD, static_cast<int>(dst_step), width, range.end - range.start) )
*ok = false;
else
{
CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT);
}
}
private:
const uchar * src_data;
const size_t src_step;
uchar * dst_data;
const size_t dst_step;
const int width;
const Cvt& cvt;
bool *ok;
const CvtColorIPPLoop_Invoker& operator= (const CvtColorIPPLoop_Invoker&);
};
template <typename Cvt>
bool CvtColorIPPLoop(const uchar * src_data, size_t src_step, uchar * dst_data, size_t dst_step,
int width, int height, const Cvt& cvt)
{
bool ok;
cv::parallel_for_(cv::Range(0, height),
CvtColorIPPLoop_Invoker<Cvt>(src_data, src_step, dst_data, dst_step, width, cvt, &ok),
(width * height)/(double)(1<<16) );
return ok;
}
template <typename Cvt>
bool CvtColorIPPLoopCopy(const uchar * src_data, size_t src_step, int src_type, uchar * dst_data, size_t dst_step,
int width, int height, const Cvt& cvt)
{
cv::Mat temp;
cv::Mat src(cv::Size(width, height), src_type, const_cast<uchar*>(src_data), src_step);
cv::Mat source = src;
if( src_data == dst_data )
{
src.copyTo(temp);
source = temp;
}
bool ok;
cv::parallel_for_(cv::Range(0, source.rows),
CvtColorIPPLoop_Invoker<Cvt>(source.data, source.step, dst_data, dst_step,
source.cols, cvt, &ok),
source.total()/(double)(1<<16) );
return ok;
}
struct IPPGeneralFunctor
{
IPPGeneralFunctor(ippiGeneralFunc _func) : ippiColorConvertGeneral(_func){}
bool operator()(const void *src, int srcStep, void *dst, int dstStep, int cols, int rows) const
{
return ippiColorConvertGeneral ? CV_INSTRUMENT_FUN_IPP(ippiColorConvertGeneral, src, srcStep, dst, dstStep, ippiSize(cols, rows)) >= 0 : false;
}
private:
ippiGeneralFunc ippiColorConvertGeneral;
};
struct IPPReorderFunctor
{
IPPReorderFunctor(ippiReorderFunc _func, int _order0, int _order1, int _order2) : ippiColorConvertReorder(_func)
{
order[0] = _order0;
order[1] = _order1;
order[2] = _order2;
order[3] = 3;
}
bool operator()(const void *src, int srcStep, void *dst, int dstStep, int cols, int rows) const
{
return ippiColorConvertReorder ? CV_INSTRUMENT_FUN_IPP(ippiColorConvertReorder, src, srcStep, dst, dstStep, ippiSize(cols, rows), order) >= 0 : false;
}
private:
ippiReorderFunc ippiColorConvertReorder;
int order[4];
};
struct IPPReorderGeneralFunctor
{
IPPReorderGeneralFunctor(ippiReorderFunc _func1, ippiGeneralFunc _func2, int _order0, int _order1, int _order2, int _depth) :
ippiColorConvertReorder(_func1), ippiColorConvertGeneral(_func2), depth(_depth)
{
order[0] = _order0;
order[1] = _order1;
order[2] = _order2;
order[3] = 3;
}
bool operator()(const void *src, int srcStep, void *dst, int dstStep, int cols, int rows) const
{
if (ippiColorConvertReorder == 0 || ippiColorConvertGeneral == 0)
return false;
cv::Mat temp;
temp.create(rows, cols, CV_MAKETYPE(depth, 3));
if(CV_INSTRUMENT_FUN_IPP(ippiColorConvertReorder, src, srcStep, temp.ptr(), (int)temp.step[0], ippiSize(cols, rows), order) < 0)
return false;
return CV_INSTRUMENT_FUN_IPP(ippiColorConvertGeneral, temp.ptr(), (int)temp.step[0], dst, dstStep, ippiSize(cols, rows)) >= 0;
}
private:
ippiReorderFunc ippiColorConvertReorder;
ippiGeneralFunc ippiColorConvertGeneral;
int order[4];
int depth;
};
struct IPPGeneralReorderFunctor
{
IPPGeneralReorderFunctor(ippiGeneralFunc _func1, ippiReorderFunc _func2, int _order0, int _order1, int _order2, int _depth) :
ippiColorConvertGeneral(_func1), ippiColorConvertReorder(_func2), depth(_depth)
{
order[0] = _order0;
order[1] = _order1;
order[2] = _order2;
order[3] = 3;
}
bool operator()(const void *src, int srcStep, void *dst, int dstStep, int cols, int rows) const
{
if (ippiColorConvertGeneral == 0 || ippiColorConvertReorder == 0)
return false;
cv::Mat temp;
temp.create(rows, cols, CV_MAKETYPE(depth, 3));
if(CV_INSTRUMENT_FUN_IPP(ippiColorConvertGeneral, src, srcStep, temp.ptr(), (int)temp.step[0], ippiSize(cols, rows)) < 0)
return false;
return CV_INSTRUMENT_FUN_IPP(ippiColorConvertReorder, temp.ptr(), (int)temp.step[0], dst, dstStep, ippiSize(cols, rows), order) >= 0;
}
private:
ippiGeneralFunc ippiColorConvertGeneral;
ippiReorderFunc ippiColorConvertReorder;
int order[4];
int depth;
};
struct IPPColor2GrayFunctor
{
IPPColor2GrayFunctor(ippiColor2GrayFunc _func) : ippiColorToGray(_func)
{
coeffs[0] = B2YF;
coeffs[1] = G2YF;
coeffs[2] = R2YF;
}
bool operator()(const void *src, int srcStep, void *dst, int dstStep, int cols, int rows) const
{
return ippiColorToGray ? CV_INSTRUMENT_FUN_IPP(ippiColorToGray, src, srcStep, dst, dstStep, ippiSize(cols, rows), coeffs) >= 0 : false;
}
private:
ippiColor2GrayFunc ippiColorToGray;
Ipp32f coeffs[3];
};
#if !IPP_DISABLE_CVTCOLOR_GRAY2BGR_8UC3
static IppStatus ippiGrayToRGB_C1C3R(const Ipp8u* pSrc, int srcStep, Ipp8u* pDst, int dstStep, IppiSize roiSize)
{
return CV_INSTRUMENT_FUN_IPP(ippiGrayToRGB_8u_C1C3R, pSrc, srcStep, pDst, dstStep, roiSize);
}
#endif
static IppStatus ippiGrayToRGB_C1C3R(const Ipp16u* pSrc, int srcStep, Ipp16u* pDst, int dstStep, IppiSize roiSize)
{
return CV_INSTRUMENT_FUN_IPP(ippiGrayToRGB_16u_C1C3R, pSrc, srcStep, pDst, dstStep, roiSize);
}
static IppStatus ippiGrayToRGB_C1C3R(const Ipp32f* pSrc, int srcStep, Ipp32f* pDst, int dstStep, IppiSize roiSize)
{
return CV_INSTRUMENT_FUN_IPP(ippiGrayToRGB_32f_C1C3R, pSrc, srcStep, pDst, dstStep, roiSize);
}
static IppStatus ippiGrayToRGB_C1C4R(const Ipp8u* pSrc, int srcStep, Ipp8u* pDst, int dstStep, IppiSize roiSize, Ipp8u aval)
{
return CV_INSTRUMENT_FUN_IPP(ippiGrayToRGB_8u_C1C4R, pSrc, srcStep, pDst, dstStep, roiSize, aval);
}
static IppStatus ippiGrayToRGB_C1C4R(const Ipp16u* pSrc, int srcStep, Ipp16u* pDst, int dstStep, IppiSize roiSize, Ipp16u aval)
{
return CV_INSTRUMENT_FUN_IPP(ippiGrayToRGB_16u_C1C4R, pSrc, srcStep, pDst, dstStep, roiSize, aval);
}
static IppStatus ippiGrayToRGB_C1C4R(const Ipp32f* pSrc, int srcStep, Ipp32f* pDst, int dstStep, IppiSize roiSize, Ipp32f aval)
{
return CV_INSTRUMENT_FUN_IPP(ippiGrayToRGB_32f_C1C4R, pSrc, srcStep, pDst, dstStep, roiSize, aval);
}
template <typename T>
struct IPPGray2BGRFunctor
{
IPPGray2BGRFunctor(){}
bool operator()(const void *src, int srcStep, void *dst, int dstStep, int cols, int rows) const
{
return ippiGrayToRGB_C1C3R((T*)src, srcStep, (T*)dst, dstStep, ippiSize(cols, rows)) >= 0;
}
};
template <typename T>
struct IPPGray2BGRAFunctor
{
IPPGray2BGRAFunctor()
{
alpha = ColorChannel<T>::max();
}
bool operator()(const void *src, int srcStep, void *dst, int dstStep, int cols, int rows) const
{
return ippiGrayToRGB_C1C4R((T*)src, srcStep, (T*)dst, dstStep, ippiSize(cols, rows), alpha) >= 0;
}
T alpha;
};
static IppStatus CV_STDCALL ippiSwapChannels_8u_C3C4Rf(const Ipp8u* pSrc, int srcStep, Ipp8u* pDst, int dstStep,
IppiSize roiSize, const int *dstOrder)
{
return CV_INSTRUMENT_FUN_IPP(ippiSwapChannels_8u_C3C4R, pSrc, srcStep, pDst, dstStep, roiSize, dstOrder, MAX_IPP8u);
}
static IppStatus CV_STDCALL ippiSwapChannels_16u_C3C4Rf(const Ipp16u* pSrc, int srcStep, Ipp16u* pDst, int dstStep,
IppiSize roiSize, const int *dstOrder)
{
return CV_INSTRUMENT_FUN_IPP(ippiSwapChannels_16u_C3C4R, pSrc, srcStep, pDst, dstStep, roiSize, dstOrder, MAX_IPP16u);
}
static IppStatus CV_STDCALL ippiSwapChannels_32f_C3C4Rf(const Ipp32f* pSrc, int srcStep, Ipp32f* pDst, int dstStep,
IppiSize roiSize, const int *dstOrder)
{
return CV_INSTRUMENT_FUN_IPP(ippiSwapChannels_32f_C3C4R, pSrc, srcStep, pDst, dstStep, roiSize, dstOrder, MAX_IPP32f);
}
static const ippiColor2GrayFunc ippiColor2GrayC3Tab[CV_DEPTH_MAX] =
{
(ippiColor2GrayFunc)ippiColorToGray_8u_C3C1R, 0, (ippiColor2GrayFunc)ippiColorToGray_16u_C3C1R, 0,
0, (ippiColor2GrayFunc)ippiColorToGray_32f_C3C1R, 0, 0
};
static const ippiColor2GrayFunc ippiColor2GrayC4Tab[CV_DEPTH_MAX] =
{
(ippiColor2GrayFunc)ippiColorToGray_8u_AC4C1R, 0, (ippiColor2GrayFunc)ippiColorToGray_16u_AC4C1R, 0,
0, (ippiColor2GrayFunc)ippiColorToGray_32f_AC4C1R, 0, 0
};
static const ippiGeneralFunc ippiRGB2GrayC3Tab[CV_DEPTH_MAX] =
{
(ippiGeneralFunc)ippiRGBToGray_8u_C3C1R, 0, (ippiGeneralFunc)ippiRGBToGray_16u_C3C1R, 0,
0, (ippiGeneralFunc)ippiRGBToGray_32f_C3C1R, 0, 0
};
static const ippiGeneralFunc ippiRGB2GrayC4Tab[CV_DEPTH_MAX] =
{
(ippiGeneralFunc)ippiRGBToGray_8u_AC4C1R, 0, (ippiGeneralFunc)ippiRGBToGray_16u_AC4C1R, 0,
0, (ippiGeneralFunc)ippiRGBToGray_32f_AC4C1R, 0, 0
};
static const ippiReorderFunc ippiSwapChannelsC3C4RTab[CV_DEPTH_MAX] =
{
(ippiReorderFunc)ippiSwapChannels_8u_C3C4Rf, 0, (ippiReorderFunc)ippiSwapChannels_16u_C3C4Rf, 0,
0, (ippiReorderFunc)ippiSwapChannels_32f_C3C4Rf, 0, 0
};
static const ippiGeneralFunc ippiCopyAC4C3RTab[CV_DEPTH_MAX] =
{
(ippiGeneralFunc)ippiCopy_8u_AC4C3R, 0, (ippiGeneralFunc)ippiCopy_16u_AC4C3R, 0,
0, (ippiGeneralFunc)ippiCopy_32f_AC4C3R, 0, 0
};
static const ippiReorderFunc ippiSwapChannelsC4C3RTab[CV_DEPTH_MAX] =
{
(ippiReorderFunc)ippiSwapChannels_8u_C4C3R, 0, (ippiReorderFunc)ippiSwapChannels_16u_C4C3R, 0,
0, (ippiReorderFunc)ippiSwapChannels_32f_C4C3R, 0, 0
};
static const ippiReorderFunc ippiSwapChannelsC3RTab[CV_DEPTH_MAX] =
{
(ippiReorderFunc)ippiSwapChannels_8u_C3R, 0, (ippiReorderFunc)ippiSwapChannels_16u_C3R, 0,
0, (ippiReorderFunc)ippiSwapChannels_32f_C3R, 0, 0
};
#if IPP_VERSION_X100 >= 810
static const ippiReorderFunc ippiSwapChannelsC4RTab[CV_DEPTH_MAX] =
{
(ippiReorderFunc)ippiSwapChannels_8u_C4R, 0, (ippiReorderFunc)ippiSwapChannels_16u_C4R, 0,
0, (ippiReorderFunc)ippiSwapChannels_32f_C4R, 0, 0
};
#endif
#if !IPP_DISABLE_RGB_HSV
static const ippiGeneralFunc ippiRGB2HSVTab[CV_DEPTH_MAX] =
{
(ippiGeneralFunc)ippiRGBToHSV_8u_C3R, 0, (ippiGeneralFunc)ippiRGBToHSV_16u_C3R, 0,
0, 0, 0, 0
};
#endif
static const ippiGeneralFunc ippiHSV2RGBTab[CV_DEPTH_MAX] =
{
(ippiGeneralFunc)ippiHSVToRGB_8u_C3R, 0, (ippiGeneralFunc)ippiHSVToRGB_16u_C3R, 0,
0, 0, 0, 0
};
static const ippiGeneralFunc ippiRGB2HLSTab[CV_DEPTH_MAX] =
{
(ippiGeneralFunc)ippiRGBToHLS_8u_C3R, 0, (ippiGeneralFunc)ippiRGBToHLS_16u_C3R, 0,
0, (ippiGeneralFunc)ippiRGBToHLS_32f_C3R, 0, 0
};
static const ippiGeneralFunc ippiHLS2RGBTab[CV_DEPTH_MAX] =
{
(ippiGeneralFunc)ippiHLSToRGB_8u_C3R, 0, (ippiGeneralFunc)ippiHLSToRGB_16u_C3R, 0,
0, (ippiGeneralFunc)ippiHLSToRGB_32f_C3R, 0, 0
};
#if !IPP_DISABLE_RGB_XYZ
static const ippiGeneralFunc ippiRGB2XYZTab[CV_DEPTH_MAX] =
{
(ippiGeneralFunc)ippiRGBToXYZ_8u_C3R, 0, (ippiGeneralFunc)ippiRGBToXYZ_16u_C3R, 0,
0, (ippiGeneralFunc)ippiRGBToXYZ_32f_C3R, 0, 0
};
#endif
#if !IPP_DISABLE_XYZ_RGB
static const ippiGeneralFunc ippiXYZ2RGBTab[CV_DEPTH_MAX] =
{
(ippiGeneralFunc)ippiXYZToRGB_8u_C3R, 0, (ippiGeneralFunc)ippiXYZToRGB_16u_C3R, 0,
0, (ippiGeneralFunc)ippiXYZToRGB_32f_C3R, 0, 0
};
#endif
#if !IPP_DISABLE_RGB_LAB
static const ippiGeneralFunc ippiRGBToLUVTab[CV_DEPTH_MAX] =
{
(ippiGeneralFunc)ippiRGBToLUV_8u_C3R, 0, (ippiGeneralFunc)ippiRGBToLUV_16u_C3R, 0,
0, (ippiGeneralFunc)ippiRGBToLUV_32f_C3R, 0, 0
};
#endif
#if !IPP_DISABLE_LAB_RGB
static const ippiGeneralFunc ippiLUVToRGBTab[CV_DEPTH_MAX] =
{
(ippiGeneralFunc)ippiLUVToRGB_8u_C3R, 0, (ippiGeneralFunc)ippiLUVToRGB_16u_C3R, 0,
0, (ippiGeneralFunc)ippiLUVToRGB_32f_C3R, 0, 0
};
#endif
} // namespace
int ipp_hal_cvtBGRtoBGR(const uchar * src_data, size_t src_step, uchar * dst_data, size_t dst_step,
int width, int height, int depth, int scn, int dcn, bool swapBlue)
{
CV_HAL_CHECK_USE_IPP();
if(scn == 3 && dcn == 4 && !swapBlue)
{
if( CvtColorIPPLoop(src_data, src_step, dst_data, dst_step, width, height,
IPPReorderFunctor(ippiSwapChannelsC3C4RTab[depth], 0, 1, 2)) )
return CV_HAL_ERROR_OK;
}
else if(scn == 4 && dcn == 3 && !swapBlue)
{
if( CvtColorIPPLoop(src_data, src_step, dst_data, dst_step, width, height,
IPPGeneralFunctor(ippiCopyAC4C3RTab[depth])) )
return CV_HAL_ERROR_OK;
}
else if(scn == 3 && dcn == 4 && swapBlue)
{
if( CvtColorIPPLoop(src_data, src_step, dst_data, dst_step, width, height,
IPPReorderFunctor(ippiSwapChannelsC3C4RTab[depth], 2, 1, 0)) )
return CV_HAL_ERROR_OK;
}
else if(scn == 4 && dcn == 3 && swapBlue)
{
if( CvtColorIPPLoop(src_data, src_step, dst_data, dst_step, width, height,
IPPReorderFunctor(ippiSwapChannelsC4C3RTab[depth], 2, 1, 0)) )
return CV_HAL_ERROR_OK;
}
else if(scn == 3 && dcn == 3 && swapBlue)
{
if( CvtColorIPPLoopCopy(src_data, src_step, CV_MAKETYPE(depth, scn), dst_data, dst_step, width, height,
IPPReorderFunctor(ippiSwapChannelsC3RTab[depth], 2, 1, 0)) )
return CV_HAL_ERROR_OK;
}
#if IPP_VERSION_X100 >= 810
else if(scn == 4 && dcn == 4 && swapBlue)
{
if( CvtColorIPPLoopCopy(src_data, src_step, CV_MAKETYPE(depth, scn), dst_data, dst_step, width, height,
IPPReorderFunctor(ippiSwapChannelsC4RTab[depth], 2, 1, 0)) )
return CV_HAL_ERROR_OK;
}
#endif
return CV_HAL_ERROR_NOT_IMPLEMENTED;
}
int ipp_hal_cvtGraytoBGR(const uchar * src_data, size_t src_step, uchar * dst_data, size_t dst_step,
int width, int height, int depth, int dcn)
{
CV_HAL_CHECK_USE_IPP();
bool ippres = false;
if(dcn == 3)
{
if( depth == CV_8U )
{
#if !IPP_DISABLE_CVTCOLOR_GRAY2BGR_8UC3
ippres = CvtColorIPPLoop(src_data, src_step, dst_data, dst_step, width, height, IPPGray2BGRFunctor<Ipp8u>());
#endif
}
else if( depth == CV_16U )
ippres = CvtColorIPPLoop(src_data, src_step, dst_data, dst_step, width, height, IPPGray2BGRFunctor<Ipp16u>());
else
ippres = CvtColorIPPLoop(src_data, src_step, dst_data, dst_step, width, height, IPPGray2BGRFunctor<Ipp32f>());
}
else if(dcn == 4)
{
if( depth == CV_8U )
ippres = CvtColorIPPLoop(src_data, src_step, dst_data, dst_step, width, height, IPPGray2BGRAFunctor<Ipp8u>());
else if( depth == CV_16U )
ippres = CvtColorIPPLoop(src_data, src_step, dst_data, dst_step, width, height, IPPGray2BGRAFunctor<Ipp16u>());
else
ippres = CvtColorIPPLoop(src_data, src_step, dst_data, dst_step, width, height, IPPGray2BGRAFunctor<Ipp32f>());
}
return ippres ? CV_HAL_ERROR_OK : CV_HAL_ERROR_NOT_IMPLEMENTED;
}
int ipp_hal_cvtBGRtoGray(const uchar * src_data, size_t src_step, uchar * dst_data, size_t dst_step,
int width, int height, int depth, int scn, bool swapBlue)
{
CV_HAL_CHECK_USE_IPP();
// preserves original cvtBGRtoGray routing: only 32f was sent to IPP, other depths use the dispatch path
if (depth != CV_32F)
return CV_HAL_ERROR_NOT_IMPLEMENTED;
if(scn == 3 && !swapBlue)
{
if( CvtColorIPPLoop(src_data, src_step, dst_data, dst_step, width, height,
IPPColor2GrayFunctor(ippiColor2GrayC3Tab[depth])) )
return CV_HAL_ERROR_OK;
}
else if(scn == 3 && swapBlue)
{
if( CvtColorIPPLoop(src_data, src_step, dst_data, dst_step, width, height,
IPPGeneralFunctor(ippiRGB2GrayC3Tab[depth])) )
return CV_HAL_ERROR_OK;
}
else if(scn == 4 && !swapBlue)
{
if( CvtColorIPPLoop(src_data, src_step, dst_data, dst_step, width, height,
IPPColor2GrayFunctor(ippiColor2GrayC4Tab[depth])) )
return CV_HAL_ERROR_OK;
}
else if(scn == 4 && swapBlue)
{
if( CvtColorIPPLoop(src_data, src_step, dst_data, dst_step, width, height,
IPPGeneralFunctor(ippiRGB2GrayC4Tab[depth])) )
return CV_HAL_ERROR_OK;
}
return CV_HAL_ERROR_NOT_IMPLEMENTED;
}
int ipp_hal_cvtBGRtoHSV(const uchar * src_data, size_t src_step, uchar * dst_data, size_t dst_step,
int width, int height, int depth, int scn, bool swapBlue, bool isFullRange, bool isHSV)
{
CV_HAL_CHECK_USE_IPP();
if(depth == CV_8U && isFullRange)
{
if (isHSV)
{
#if !IPP_DISABLE_RGB_HSV
if(scn == 3 && !swapBlue)
{
if( CvtColorIPPLoopCopy(src_data, src_step, CV_MAKE_TYPE(depth, scn), dst_data, dst_step, width, height,
IPPReorderGeneralFunctor(ippiSwapChannelsC3RTab[depth], ippiRGB2HSVTab[depth], 2, 1, 0, depth)) )
return CV_HAL_ERROR_OK;
}
else if(scn == 4 && !swapBlue)
{
if( CvtColorIPPLoop(src_data, src_step, dst_data, dst_step, width, height,
IPPReorderGeneralFunctor(ippiSwapChannelsC4C3RTab[depth], ippiRGB2HSVTab[depth], 2, 1, 0, depth)) )
return CV_HAL_ERROR_OK;
}
else if(scn == 4 && swapBlue)
{
if( CvtColorIPPLoop(src_data, src_step, dst_data, dst_step, width, height,
IPPReorderGeneralFunctor(ippiSwapChannelsC4C3RTab[depth], ippiRGB2HSVTab[depth], 0, 1, 2, depth)) )
return CV_HAL_ERROR_OK;
}
#endif
}
else
{
if(scn == 3 && !swapBlue)
{
if( CvtColorIPPLoopCopy(src_data, src_step, CV_MAKE_TYPE(depth, scn), dst_data, dst_step, width, height,
IPPReorderGeneralFunctor(ippiSwapChannelsC3RTab[depth], ippiRGB2HLSTab[depth], 2, 1, 0, depth)) )
return CV_HAL_ERROR_OK;
}
else if(scn == 4 && !swapBlue)
{
if( CvtColorIPPLoop(src_data, src_step, dst_data, dst_step, width, height,
IPPReorderGeneralFunctor(ippiSwapChannelsC4C3RTab[depth], ippiRGB2HLSTab[depth], 2, 1, 0, depth)) )
return CV_HAL_ERROR_OK;
}
else if(scn == 3 && swapBlue)
{
if( CvtColorIPPLoopCopy(src_data, src_step, CV_MAKE_TYPE(depth, scn), dst_data, dst_step, width, height,
IPPGeneralFunctor(ippiRGB2HLSTab[depth])) )
return CV_HAL_ERROR_OK;
}
else if(scn == 4 && swapBlue)
{
if( CvtColorIPPLoop(src_data, src_step, dst_data, dst_step, width, height,
IPPReorderGeneralFunctor(ippiSwapChannelsC4C3RTab[depth], ippiRGB2HLSTab[depth], 0, 1, 2, depth)) )
return CV_HAL_ERROR_OK;
}
}
}
return CV_HAL_ERROR_NOT_IMPLEMENTED;
}
int ipp_hal_cvtHSVtoBGR(const uchar * src_data, size_t src_step, uchar * dst_data, size_t dst_step,
int width, int height, int depth, int dcn, bool swapBlue, bool isFullRange, bool isHSV)
{
CV_HAL_CHECK_USE_IPP();
if (depth == CV_8U && isFullRange)
{
if (isHSV)
{
if(dcn == 3 && !swapBlue)
{
if( CvtColorIPPLoopCopy(src_data, src_step, CV_MAKETYPE(depth, 3), dst_data, dst_step, width, height,
IPPGeneralReorderFunctor(ippiHSV2RGBTab[depth], ippiSwapChannelsC3RTab[depth], 2, 1, 0, depth)) )
return CV_HAL_ERROR_OK;
}
else if(dcn == 4 && !swapBlue)
{
if( CvtColorIPPLoop(src_data, src_step, dst_data, dst_step, width, height,
IPPGeneralReorderFunctor(ippiHSV2RGBTab[depth], ippiSwapChannelsC3C4RTab[depth], 2, 1, 0, depth)) )
return CV_HAL_ERROR_OK;
}
else if(dcn == 3 && swapBlue)
{
if( CvtColorIPPLoopCopy(src_data, src_step, CV_MAKETYPE(depth, 3), dst_data, dst_step, width, height,
IPPGeneralFunctor(ippiHSV2RGBTab[depth])) )
return CV_HAL_ERROR_OK;
}
else if(dcn == 4 && swapBlue)
{
if( CvtColorIPPLoop(src_data, src_step, dst_data, dst_step, width, height,
IPPGeneralReorderFunctor(ippiHSV2RGBTab[depth], ippiSwapChannelsC3C4RTab[depth], 0, 1, 2, depth)) )
return CV_HAL_ERROR_OK;
}
}
else
{
if(dcn == 3 && !swapBlue)
{
if( CvtColorIPPLoopCopy(src_data, src_step, CV_MAKETYPE(depth, 3), dst_data, dst_step, width, height,
IPPGeneralReorderFunctor(ippiHLS2RGBTab[depth], ippiSwapChannelsC3RTab[depth], 2, 1, 0, depth)) )
return CV_HAL_ERROR_OK;
}
else if(dcn == 4 && !swapBlue)
{
if( CvtColorIPPLoop(src_data, src_step, dst_data, dst_step, width, height,
IPPGeneralReorderFunctor(ippiHLS2RGBTab[depth], ippiSwapChannelsC3C4RTab[depth], 2, 1, 0, depth)) )
return CV_HAL_ERROR_OK;
}
else if(dcn == 3 && swapBlue)
{
if( CvtColorIPPLoopCopy(src_data, src_step, CV_MAKETYPE(depth, 3), dst_data, dst_step, width, height,
IPPGeneralFunctor(ippiHLS2RGBTab[depth])) )
return CV_HAL_ERROR_OK;
}
else if(dcn == 4 && swapBlue)
{
if( CvtColorIPPLoop(src_data, src_step, dst_data, dst_step, width, height,
IPPGeneralReorderFunctor(ippiHLS2RGBTab[depth], ippiSwapChannelsC3C4RTab[depth], 0, 1, 2, depth)) )
return CV_HAL_ERROR_OK;
}
}
}
return CV_HAL_ERROR_NOT_IMPLEMENTED;
}
int ipp_hal_cvtRGBAtoMultipliedRGBA(const uchar * src_data, size_t src_step, uchar * dst_data, size_t dst_step,
int width, int height)
{
CV_HAL_CHECK_USE_IPP();
if( CvtColorIPPLoop(src_data, src_step, dst_data, dst_step, width, height,
IPPGeneralFunctor((ippiGeneralFunc)ippiAlphaPremul_8u_AC4R)) )
return CV_HAL_ERROR_OK;
return CV_HAL_ERROR_NOT_IMPLEMENTED;
}
int ipp_hal_cvtBGRtoYUV(const uchar * src_data, size_t src_step, uchar * dst_data, size_t dst_step,
int width, int height, int depth, int scn, bool swapBlue, bool isCbCr)
{
CV_UNUSED(src_data); CV_UNUSED(src_step); CV_UNUSED(dst_data); CV_UNUSED(dst_step); CV_UNUSED(width); CV_UNUSED(height); CV_UNUSED(depth); CV_UNUSED(scn); CV_UNUSED(swapBlue); CV_UNUSED(isCbCr);
CV_HAL_CHECK_USE_IPP();
#if !IPP_DISABLE_RGB_YUV
if (scn == 3 && depth == CV_8U && swapBlue && !isCbCr)
{
if (CvtColorIPPLoop(src_data, src_step, dst_data, dst_step, width, height,
IPPGeneralFunctor((ippiGeneralFunc)ippiRGBToYUV_8u_C3R)))
return CV_HAL_ERROR_OK;
}
else if (scn == 3 && depth == CV_8U && !swapBlue && !isCbCr)
{
if (CvtColorIPPLoop(src_data, src_step, dst_data, dst_step, width, height,
IPPReorderGeneralFunctor(ippiSwapChannelsC3RTab[depth],
(ippiGeneralFunc)ippiRGBToYUV_8u_C3R, 2, 1, 0, depth)))
return CV_HAL_ERROR_OK;
}
else if (scn == 4 && depth == CV_8U && swapBlue && !isCbCr)
{
if (CvtColorIPPLoop(src_data, src_step, dst_data, dst_step, width, height,
IPPReorderGeneralFunctor(ippiSwapChannelsC4C3RTab[depth],
(ippiGeneralFunc)ippiRGBToYUV_8u_C3R, 0, 1, 2, depth)))
return CV_HAL_ERROR_OK;
}
else if (scn == 4 && depth == CV_8U && !swapBlue && !isCbCr)
{
if (CvtColorIPPLoop(src_data, src_step, dst_data, dst_step, width, height,
IPPReorderGeneralFunctor(ippiSwapChannelsC4C3RTab[depth],
(ippiGeneralFunc)ippiRGBToYUV_8u_C3R, 2, 1, 0, depth)))
return CV_HAL_ERROR_OK;
}
#endif
return CV_HAL_ERROR_NOT_IMPLEMENTED;
}
int ipp_hal_cvtYUVtoBGR(const uchar * src_data, size_t src_step, uchar * dst_data, size_t dst_step,
int width, int height, int depth, int dcn, bool swapBlue, bool isCbCr)
{
CV_UNUSED(src_data); CV_UNUSED(src_step); CV_UNUSED(dst_data); CV_UNUSED(dst_step); CV_UNUSED(width); CV_UNUSED(height); CV_UNUSED(depth); CV_UNUSED(dcn); CV_UNUSED(swapBlue); CV_UNUSED(isCbCr);
CV_HAL_CHECK_USE_IPP();
#if !IPP_DISABLE_YUV_RGB
if (dcn == 3 && depth == CV_8U && swapBlue && !isCbCr)
{
if (CvtColorIPPLoop(src_data, src_step, dst_data, dst_step, width, height,
IPPGeneralFunctor((ippiGeneralFunc)ippiYUVToRGB_8u_C3R)))
return CV_HAL_ERROR_OK;
}
else if (dcn == 3 && depth == CV_8U && !swapBlue && !isCbCr)
{
if (CvtColorIPPLoop(src_data, src_step, dst_data, dst_step, width, height,
IPPGeneralReorderFunctor((ippiGeneralFunc)ippiYUVToRGB_8u_C3R,
ippiSwapChannelsC3RTab[depth], 2, 1, 0, depth)))
return CV_HAL_ERROR_OK;
}
else if (dcn == 4 && depth == CV_8U && swapBlue && !isCbCr)
{
if (CvtColorIPPLoop(src_data, src_step, dst_data, dst_step, width, height,
IPPGeneralReorderFunctor((ippiGeneralFunc)ippiYUVToRGB_8u_C3R,
ippiSwapChannelsC3C4RTab[depth], 0, 1, 2, depth)))
return CV_HAL_ERROR_OK;
}
else if (dcn == 4 && depth == CV_8U && !swapBlue && !isCbCr)
{
if (CvtColorIPPLoop(src_data, src_step, dst_data, dst_step, width, height,
IPPGeneralReorderFunctor((ippiGeneralFunc)ippiYUVToRGB_8u_C3R,
ippiSwapChannelsC3C4RTab[depth], 2, 1, 0, depth)))
return CV_HAL_ERROR_OK;
}
#endif
return CV_HAL_ERROR_NOT_IMPLEMENTED;
}
int ipp_hal_cvtBGRtoXYZ(const uchar * src_data, size_t src_step, uchar * dst_data, size_t dst_step,
int width, int height, int depth, int scn, bool swapBlue)
{
CV_UNUSED(src_data); CV_UNUSED(src_step); CV_UNUSED(dst_data); CV_UNUSED(dst_step); CV_UNUSED(width); CV_UNUSED(height); CV_UNUSED(depth); CV_UNUSED(scn); CV_UNUSED(swapBlue);
CV_HAL_CHECK_USE_IPP();
#if !IPP_DISABLE_RGB_XYZ
if(scn == 3 && depth != CV_32F && !swapBlue)
{
if( CvtColorIPPLoopCopy(src_data, src_step, CV_MAKETYPE(depth, scn), dst_data, dst_step, width, height,
IPPReorderGeneralFunctor(ippiSwapChannelsC3RTab[depth], ippiRGB2XYZTab[depth], 2, 1, 0, depth)) )
return CV_HAL_ERROR_OK;
}
else if(scn == 4 && depth != CV_32F && !swapBlue)
{
if( CvtColorIPPLoop(src_data, src_step, dst_data, dst_step, width, height,
IPPReorderGeneralFunctor(ippiSwapChannelsC4C3RTab[depth], ippiRGB2XYZTab[depth], 2, 1, 0, depth)) )
return CV_HAL_ERROR_OK;
}
else if(scn == 3 && depth != CV_32F && swapBlue)
{
if( CvtColorIPPLoopCopy(src_data, src_step, CV_MAKETYPE(depth, scn), dst_data, dst_step, width, height,
IPPGeneralFunctor(ippiRGB2XYZTab[depth])) )
return CV_HAL_ERROR_OK;
}
else if(scn == 4 && depth != CV_32F && swapBlue)
{
if( CvtColorIPPLoop(src_data, src_step, dst_data, dst_step, width, height,
IPPReorderGeneralFunctor(ippiSwapChannelsC4C3RTab[depth], ippiRGB2XYZTab[depth], 0, 1, 2, depth)) )
return CV_HAL_ERROR_OK;
}
#endif
return CV_HAL_ERROR_NOT_IMPLEMENTED;
}
int ipp_hal_cvtXYZtoBGR(const uchar * src_data, size_t src_step, uchar * dst_data, size_t dst_step,
int width, int height, int depth, int dcn, bool swapBlue)
{
CV_UNUSED(src_data); CV_UNUSED(src_step); CV_UNUSED(dst_data); CV_UNUSED(dst_step); CV_UNUSED(width); CV_UNUSED(height); CV_UNUSED(depth); CV_UNUSED(dcn); CV_UNUSED(swapBlue);
CV_HAL_CHECK_USE_IPP();
#if !IPP_DISABLE_XYZ_RGB
if(dcn == 3 && depth != CV_32F && !swapBlue)
{
if( CvtColorIPPLoopCopy(src_data, src_step, CV_MAKETYPE(depth, 3), dst_data, dst_step, width, height,
IPPGeneralReorderFunctor(ippiXYZ2RGBTab[depth], ippiSwapChannelsC3RTab[depth], 2, 1, 0, depth)) )
return CV_HAL_ERROR_OK;
}
else if(dcn == 4 && depth != CV_32F && !swapBlue)
{
if( CvtColorIPPLoop(src_data, src_step, dst_data, dst_step, width, height,
IPPGeneralReorderFunctor(ippiXYZ2RGBTab[depth], ippiSwapChannelsC3C4RTab[depth], 2, 1, 0, depth)) )
return CV_HAL_ERROR_OK;
}
if(dcn == 3 && depth != CV_32F && swapBlue)
{
if( CvtColorIPPLoopCopy(src_data, src_step, CV_MAKETYPE(depth, 3), dst_data, dst_step, width, height,
IPPGeneralFunctor(ippiXYZ2RGBTab[depth])) )
return CV_HAL_ERROR_OK;
}
else if(dcn == 4 && depth != CV_32F && swapBlue)
{
if( CvtColorIPPLoop(src_data, src_step, dst_data, dst_step, width, height,
IPPGeneralReorderFunctor(ippiXYZ2RGBTab[depth], ippiSwapChannelsC3C4RTab[depth], 0, 1, 2, depth)) )
return CV_HAL_ERROR_OK;
}
#endif
return CV_HAL_ERROR_NOT_IMPLEMENTED;
}
int ipp_hal_cvtBGRtoLab(const uchar * src_data, size_t src_step, uchar * dst_data, size_t dst_step,
int width, int height, int depth, int scn, bool swapBlue, bool isLab, bool srgb)
{
CV_UNUSED(src_data); CV_UNUSED(src_step); CV_UNUSED(dst_data); CV_UNUSED(dst_step); CV_UNUSED(width); CV_UNUSED(height); CV_UNUSED(depth); CV_UNUSED(scn); CV_UNUSED(swapBlue); CV_UNUSED(isLab); CV_UNUSED(srgb);
CV_HAL_CHECK_USE_IPP();
#if !IPP_DISABLE_RGB_LAB
if (!srgb)
{
if (isLab)
{
if (scn == 3 && depth == CV_8U && !swapBlue)
{
if (CvtColorIPPLoop(src_data, src_step, dst_data, dst_step, width, height,
IPPGeneralFunctor((ippiGeneralFunc)ippiBGRToLab_8u_C3R)))
return CV_HAL_ERROR_OK;
}
else if (scn == 4 && depth == CV_8U && !swapBlue)
{
if (CvtColorIPPLoop(src_data, src_step, dst_data, dst_step, width, height,
IPPReorderGeneralFunctor(ippiSwapChannelsC4C3RTab[depth],
(ippiGeneralFunc)ippiBGRToLab_8u_C3R, 0, 1, 2, depth)))
return CV_HAL_ERROR_OK;
}
else if (scn == 3 && depth == CV_8U && swapBlue) // slower than OpenCV
{
if (CvtColorIPPLoop(src_data, src_step, dst_data, dst_step, width, height,
IPPReorderGeneralFunctor(ippiSwapChannelsC3RTab[depth],
(ippiGeneralFunc)ippiBGRToLab_8u_C3R, 2, 1, 0, depth)))
return CV_HAL_ERROR_OK;
}
else if (scn == 4 && depth == CV_8U && swapBlue) // slower than OpenCV
{
if (CvtColorIPPLoop(src_data, src_step, dst_data, dst_step, width, height,
IPPReorderGeneralFunctor(ippiSwapChannelsC4C3RTab[depth],
(ippiGeneralFunc)ippiBGRToLab_8u_C3R, 2, 1, 0, depth)))
return CV_HAL_ERROR_OK;
}
}
else
{
if (scn == 3 && swapBlue)
{
if (CvtColorIPPLoop(src_data, src_step, dst_data, dst_step, width, height,
IPPGeneralFunctor(ippiRGBToLUVTab[depth])))
return CV_HAL_ERROR_OK;
}
else if (scn == 4 && swapBlue)
{
if (CvtColorIPPLoop(src_data, src_step, dst_data, dst_step, width, height,
IPPReorderGeneralFunctor(ippiSwapChannelsC4C3RTab[depth],
ippiRGBToLUVTab[depth], 0, 1, 2, depth)))
return CV_HAL_ERROR_OK;
}
else if (scn == 3 && !swapBlue)
{
if (CvtColorIPPLoop(src_data, src_step, dst_data, dst_step, width, height,
IPPReorderGeneralFunctor(ippiSwapChannelsC3RTab[depth],
ippiRGBToLUVTab[depth], 2, 1, 0, depth)))
return CV_HAL_ERROR_OK;
}
else if (scn == 4 && !swapBlue)
{
if (CvtColorIPPLoop(src_data, src_step, dst_data, dst_step, width, height,
IPPReorderGeneralFunctor(ippiSwapChannelsC4C3RTab[depth],
ippiRGBToLUVTab[depth], 2, 1, 0, depth)))
return CV_HAL_ERROR_OK;
}
}
}
#endif
return CV_HAL_ERROR_NOT_IMPLEMENTED;
}
int ipp_hal_cvtLabtoBGR(const uchar * src_data, size_t src_step, uchar * dst_data, size_t dst_step,
int width, int height, int depth, int dcn, bool swapBlue, bool isLab, bool srgb)
{
CV_UNUSED(src_data); CV_UNUSED(src_step); CV_UNUSED(dst_data); CV_UNUSED(dst_step); CV_UNUSED(width); CV_UNUSED(height); CV_UNUSED(depth); CV_UNUSED(dcn); CV_UNUSED(swapBlue); CV_UNUSED(isLab); CV_UNUSED(srgb);
CV_HAL_CHECK_USE_IPP();
#if !IPP_DISABLE_LAB_RGB
if (!srgb)
{
if (isLab)
{
if( dcn == 3 && depth == CV_8U && !swapBlue)
{
if( CvtColorIPPLoop(src_data, src_step, dst_data, dst_step, width, height,
IPPGeneralFunctor((ippiGeneralFunc)ippiLabToBGR_8u_C3R)) )
return CV_HAL_ERROR_OK;
}
else if( dcn == 4 && depth == CV_8U && !swapBlue)
{
if( CvtColorIPPLoop(src_data, src_step, dst_data, dst_step, width, height,
IPPGeneralReorderFunctor((ippiGeneralFunc)ippiLabToBGR_8u_C3R,
ippiSwapChannelsC3C4RTab[depth], 0, 1, 2, depth)) )
return CV_HAL_ERROR_OK;
}
if( dcn == 3 && depth == CV_8U && swapBlue)
{
if( CvtColorIPPLoop(src_data, src_step, dst_data, dst_step, width, height,
IPPGeneralReorderFunctor((ippiGeneralFunc)ippiLabToBGR_8u_C3R,
ippiSwapChannelsC3RTab[depth], 2, 1, 0, depth)) )
return CV_HAL_ERROR_OK;
}
else if( dcn == 4 && depth == CV_8U && swapBlue)
{
if( CvtColorIPPLoop(src_data, src_step, dst_data, dst_step, width, height,
IPPGeneralReorderFunctor((ippiGeneralFunc)ippiLabToBGR_8u_C3R,
ippiSwapChannelsC3C4RTab[depth], 2, 1, 0, depth)) )
return CV_HAL_ERROR_OK;
}
}
else
{
if( dcn == 3 && swapBlue)
{
if( CvtColorIPPLoop(src_data, src_step, dst_data, dst_step, width, height,
IPPGeneralFunctor(ippiLUVToRGBTab[depth])) )
return CV_HAL_ERROR_OK;
}
else if( dcn == 4 && swapBlue)
{
if( CvtColorIPPLoop(src_data, src_step, dst_data, dst_step, width, height,
IPPGeneralReorderFunctor(ippiLUVToRGBTab[depth],
ippiSwapChannelsC3C4RTab[depth], 0, 1, 2, depth)) )
return CV_HAL_ERROR_OK;
}
if( dcn == 3 && !swapBlue)
{
if( CvtColorIPPLoop(src_data, src_step, dst_data, dst_step, width, height,
IPPGeneralReorderFunctor(ippiLUVToRGBTab[depth],
ippiSwapChannelsC3RTab[depth], 2, 1, 0, depth)) )
return CV_HAL_ERROR_OK;
}
else if( dcn == 4 && !swapBlue)
{
if( CvtColorIPPLoop(src_data, src_step, dst_data, dst_step, width, height,
IPPGeneralReorderFunctor(ippiLUVToRGBTab[depth],
ippiSwapChannelsC3C4RTab[depth], 2, 1, 0, depth)) )
return CV_HAL_ERROR_OK;
}
}
}
#endif
return CV_HAL_ERROR_NOT_IMPLEMENTED;
}
#endif // IPP_VERSION_X100 >= 700
+247
View File
@@ -0,0 +1,247 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html
// Copyright (C) 2026, Intel Corporation, all rights reserved.
#include "ipp_hal_imgproc.hpp"
#include "precomp_ipp.hpp"
#include <opencv2/core.hpp>
#include <opencv2/core/base.hpp>
#if IPP_VERSION_X100 >= 810
#ifdef HAVE_IPP_IW
#include "iw++/iw.hpp"
static inline IppiMaskSize ippiGetMaskSize(int kx, int ky)
{
return (kx == 1 && ky == 3) ? ippMskSize1x3 :
(kx == 1 && ky == 5) ? ippMskSize1x5 :
(kx == 3 && ky == 1) ? ippMskSize3x1 :
(kx == 3 && ky == 3) ? ippMskSize3x3 :
(kx == 5 && ky == 1) ? ippMskSize5x1 :
(kx == 5 && ky == 5) ? ippMskSize5x5 :
(IppiMaskSize)-1;
}
static inline IwiDerivativeType ippiGetDerivType(int dx, int dy, bool nvert)
{
return (dx == 1 && dy == 0) ? ((nvert)?iwiDerivNVerFirst:iwiDerivVerFirst) :
(dx == 0 && dy == 1) ? iwiDerivHorFirst :
(dx == 2 && dy == 0) ? iwiDerivVerSecond :
(dx == 0 && dy == 2) ? iwiDerivHorSecond :
(IwiDerivativeType)-1;
}
// Build an IwiImage from a raw buffer, encoding the in-memory border (the OpenCV
// margins around the ROI) so that BORDER_*-with-physical-pixels works as in core.
// TODO: promote to precomp_ipp.hpp and unify with the raw-pointer ippiGetImage in
// transforms_ipp.cpp once more filter HALs share it
static inline ::ipp::IwiImage ippiGetImage(int depth, int channels, const uchar* data, size_t step,
int width, int height,
int margin_left, int margin_top, int margin_right, int margin_bottom)
{
::ipp::IwiImage image;
::ipp::IwiBorderSize inMemBorder((IwSize)margin_left, (IwSize)margin_top, (IwSize)margin_right, (IwSize)margin_bottom);
image.Init({width, height}, ippiGetDataType(depth), channels, inMemBorder, (void*)data, step);
return image;
}
// Translate the OpenCV border type into an IPP border, accounting for in-memory pixels.
// Returns (IppiBorderType)0 on unsupported configuration.
// TODO: promote to precomp_ipp.hpp once shared by other filter HALs (box/gaussian/bilateral/
// sepFilter etc.). Note the shared ippiGetBorderType there does not map BORDER_REFLECT_101;
// widening it would also affect warp_ipp.cpp, so keep this filter-specific mapping separate
// (or add a dedicated filter border helper) when consolidating.
static inline IppiBorderType ippiGetBorder(::ipp::IwiImage &image, int ocvBorderType, ::ipp::IwiBorderSize &borderSize)
{
int inMemFlags = 0;
int borderTypeNI = ocvBorderType & ~cv::BORDER_ISOLATED;
IppiBorderType border = borderTypeNI == cv::BORDER_CONSTANT ? ippBorderConst :
borderTypeNI == cv::BORDER_TRANSPARENT ? ippBorderTransp :
borderTypeNI == cv::BORDER_REPLICATE ? ippBorderRepl :
borderTypeNI == cv::BORDER_REFLECT_101 ? ippBorderMirror :
(IppiBorderType)-1;
if((int)border == -1)
return (IppiBorderType)0;
if(!(ocvBorderType & cv::BORDER_ISOLATED))
{
if(image.m_inMemSize.left)
{
if(image.m_inMemSize.left >= borderSize.left)
inMemFlags |= ippBorderInMemLeft;
else
return (IppiBorderType)0;
}
else
borderSize.left = 0;
if(image.m_inMemSize.top)
{
if(image.m_inMemSize.top >= borderSize.top)
inMemFlags |= ippBorderInMemTop;
else
return (IppiBorderType)0;
}
else
borderSize.top = 0;
if(image.m_inMemSize.right)
{
if(image.m_inMemSize.right >= borderSize.right)
inMemFlags |= ippBorderInMemRight;
else
return (IppiBorderType)0;
}
else
borderSize.right = 0;
if(image.m_inMemSize.bottom)
{
if(image.m_inMemSize.bottom >= borderSize.bottom)
inMemFlags |= ippBorderInMemBottom;
else
return (IppiBorderType)0;
}
else
borderSize.bottom = 0;
}
else
borderSize.left = borderSize.right = borderSize.top = borderSize.bottom = 0;
return (IppiBorderType)(border|inMemFlags);
}
// Shared worker for Sobel (useScharr == false) and Scharr (useScharr == true).
static int ipp_Deriv(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step,
int width, int height, int src_depth, int dst_depth, int cn,
int margin_left, int margin_top, int margin_right, int margin_bottom,
int dx, int dy, int ksize, double scale, double delta, int borderType,
bool useScharr)
{
IppDataType srcType = ippiGetDataType(src_depth);
IppDataType dstType = ippiGetDataType(dst_depth);
bool useScale = false;
if(cn < 1 || cn > 4)
return CV_HAL_ERROR_NOT_IMPLEMENTED;
if(src_depth < 0 || src_depth >= CV_DEPTH_MAX || dst_depth < 0 || dst_depth >= CV_DEPTH_MAX)
return CV_HAL_ERROR_NOT_IMPLEMENTED;
// Supported (source depth -> destination depth) x channels combinations.
// Index order: [src_depth][dst_depth][channel-1]; depths are 8U,8S,16U,16S,32S,32F,64F.
// IPP iwiFilterSobel/iwiFilterScharr provide only single-channel kernels for
// 8u->16s, 16s->16s and 32f->32f; 8u->8u is realized as an 8u->16s filter plus a 16s->8u scale.
// 8u->32f and 16s->32f are intentionally left disabled: IPP would need an extra full-image
// conversion pass there and is slower than OpenCV's fused sepFilter2D.
/* dst: 8U 8S 16U 16S 32S 32F 64F */
#if defined(IPP_CALLS_ENFORCED)
const char impl[CV_DEPTH_MAX][CV_DEPTH_MAX][4] = {
/* src 8U */ {{1,0,0,0},{0,0,0,0},{0,0,0,0},{1,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}},
/* src 8S */ {{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}},
/* src 16U */ {{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}},
/* src 16S */ {{0,0,0,0},{0,0,0,0},{0,0,0,0},{1,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}},
/* src 32S */ {{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}},
/* src 32F */ {{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{1,0,0,0},{0,0,0,0}},
/* src 64F */ {{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}}};
#else
const char impl[CV_DEPTH_MAX][CV_DEPTH_MAX][4] = {
/* src 8U */ {{1,0,0,0},{0,0,0,0},{0,0,0,0},{1,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}},
/* src 8S */ {{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}},
/* src 16U */ {{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}},
/* src 16S */ {{0,0,0,0},{0,0,0,0},{0,0,0,0},{1,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}},
/* src 32S */ {{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}},
/* src 32F */ {{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{1,0,0,0},{0,0,0,0}},
/* src 64F */ {{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}}};
#endif // IPP_CALLS_ENFORCED
if(impl[src_depth][dst_depth][cn-1] == 0)
return CV_HAL_ERROR_NOT_IMPLEMENTED;
if(fabs(delta) > FLT_EPSILON || fabs(scale-1) > FLT_EPSILON)
useScale = true;
// cv::Sobel accepts ksize == FILTER_SCHARR (-1, i.e. ksize <= 0) which selects the 3x3
// Scharr derivative, so the Sobel entry point may legitimately request a Scharr filter.
if(ksize <= 0)
{
ksize = 3;
useScharr = true;
}
IppiMaskSize maskSize = ippiGetMaskSize(ksize, ksize);
if((int)maskSize < 0)
return CV_HAL_ERROR_NOT_IMPLEMENTED;
#if IPP_VERSION_X100 <= 201703
// Bug with mirror wrap
if(borderType == cv::BORDER_REFLECT_101 && (ksize/2+1 > width || ksize/2+1 > height))
return CV_HAL_ERROR_NOT_IMPLEMENTED;
#endif
IwiDerivativeType derivType = ippiGetDerivType(dx, dy, (useScharr)?false:true);
if((int)derivType < 0)
return CV_HAL_ERROR_NOT_IMPLEMENTED;
try
{
::ipp::IwiImage iwSrc = ippiGetImage(src_depth, cn, src_data, src_step, width, height,
margin_left, margin_top, margin_right, margin_bottom);
::ipp::IwiImage iwDst = ippiGetImage(dst_depth, cn, dst_data, dst_step, width, height,
0, 0, 0, 0);
::ipp::IwiImage iwSrcProc = iwSrc;
::ipp::IwiImage iwDstProc = iwDst;
::ipp::IwiBorderSize borderSize(maskSize);
::ipp::IwiBorderType ippBorder(ippiGetBorder(iwSrc, borderType, borderSize));
if(!ippBorder)
return CV_HAL_ERROR_NOT_IMPLEMENTED;
// IPP needs an extra iwiScale pass for 32f output with scale/delta, slower than OpenCV's fused approach
if(useScale && dstType == ipp32f)
return CV_HAL_ERROR_NOT_IMPLEMENTED;
if(srcType == ipp8u && dstType == ipp8u)
{
iwDstProc.Alloc(iwDst.m_size, ipp16s, cn);
useScale = true;
}
if(useScharr)
CV_INSTRUMENT_FUN_IPP(::ipp::iwiFilterScharr, iwSrcProc, iwDstProc, derivType, maskSize, ::ipp::IwDefault(), ippBorder);
else
CV_INSTRUMENT_FUN_IPP(::ipp::iwiFilterSobel, iwSrcProc, iwDstProc, derivType, maskSize, ::ipp::IwDefault(), ippBorder);
if(useScale)
CV_INSTRUMENT_FUN_IPP(::ipp::iwiScale, iwDstProc, iwDst, scale, delta, ::ipp::IwiScaleParams(ippAlgHintFast));
}
catch (const ::ipp::IwException &)
{
return CV_HAL_ERROR_NOT_IMPLEMENTED;
}
return CV_HAL_ERROR_OK;
}
int ipp_hal_sobel(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step,
int width, int height, int src_depth, int dst_depth, int cn,
int margin_left, int margin_top, int margin_right, int margin_bottom,
int dx, int dy, int ksize, double scale, double delta, int border_type)
{
CV_HAL_CHECK_USE_IPP();
return ipp_Deriv(src_data, src_step, dst_data, dst_step, width, height, src_depth, dst_depth, cn,
margin_left, margin_top, margin_right, margin_bottom,
dx, dy, ksize, scale, delta, border_type, false);
}
int ipp_hal_scharr(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step,
int width, int height, int src_depth, int dst_depth, int cn,
int margin_left, int margin_top, int margin_right, int margin_bottom,
int dx, int dy, double scale, double delta, int border_type)
{
CV_HAL_CHECK_USE_IPP();
return ipp_Deriv(src_data, src_step, dst_data, dst_step, width, height, src_depth, dst_depth, cn,
margin_left, margin_top, margin_right, margin_bottom,
dx, dy, 0, scale, delta, border_type, true);
}
#endif // HAVE_IPP_IW
#endif // IPP_VERSION_X100 >= 810
+148
View File
@@ -0,0 +1,148 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html
// Copyright (C) 2026, BigVision LLC, all rights reserved.
// Third party copyrights are property of their respective owners.
#include "ipp_hal_imgproc.hpp"
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <climits>
#include <cmath>
#if IPP_VERSION_X100 >= 700
// Mirrors CV_IPP_MALLOC from modules/core/include/opencv2/core/private.hpp: ippicv (IPP >= 2017)
// exposes only the 64-bit ippMalloc_L.
#if IPP_VERSION_X100 >= 201700
#define IPP_HAL_MALLOC(SIZE) ippMalloc_L(SIZE)
#else
#define IPP_HAL_MALLOC(SIZE) ippMalloc((int)(SIZE))
#endif
#ifndef IPP_DISABLE_PERF_TRUE_DIST_MT
#define IPP_DISABLE_PERF_TRUE_DIST_MT 1
#endif
// Fixed distance-transform mask weights, mirroring getDistanceTransformMask() in
// modules/imgproc/src/distransform.cpp. maskType == distTypeIndex + mask_size*10.
static bool ippGetDistanceTransformMask(int dist_type, int mask_size, float *metrics)
{
int maskType = (dist_type == cv::DIST_C ? 0 : dist_type == cv::DIST_L1 ? 1 : 2) + mask_size * 10;
switch (maskType)
{
case 30: metrics[0] = 1.0f; metrics[1] = 1.0f; break;
case 31: metrics[0] = 1.0f; metrics[1] = 2.0f; break;
case 32: metrics[0] = 0.955f; metrics[1] = 1.3693f; break;
case 50: metrics[0] = 1.0f; metrics[1] = 1.0f; metrics[2] = 2.0f; break;
case 51: metrics[0] = 1.0f; metrics[1] = 2.0f; metrics[2] = 3.0f; break;
case 52: metrics[0] = 1.0f; metrics[1] = 1.4f; metrics[2] = 2.1969f; break;
default:
return false;
}
return true;
}
int ipp_hal_distanceTransform(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step,
int width, int height, int dst_type, int dist_type, int mask_size)
{
CV_HAL_CHECK_USE_IPP();
IppiSize roi = { width, height };
// DIST_L1 with 8-bit output: fixed L1 / 3x3 metrics.
if (dst_type == CV_8U)
{
Ipp32s pMetrics[2] = { 1, 2 }; // L1, 3x3 mask
if (CV_INSTRUMENT_FUN_IPP(ippiDistanceTransform_3x3_8u_C1R, src_data, (int)src_step,
dst_data, (int)dst_step, roi, pMetrics) >= 0)
{
return CV_HAL_ERROR_OK;
}
return CV_HAL_ERROR_NOT_IMPLEMENTED;
}
// All remaining paths produce a 32-bit float distance map.
if (dst_type != CV_32F)
return CV_HAL_ERROR_NOT_IMPLEMENTED;
if (mask_size == cv::DIST_MASK_PRECISE)
{
// 4097 can't square into a float.
#if IPP_DISABLE_PERF_TRUE_DIST_MT
if (!((cv::getNumThreads() <= 1 || ((size_t)width * height < (size_t)(1 << 14))) &&
height < 4097 && width < 4097))
{
return CV_HAL_ERROR_NOT_IMPLEMENTED;
}
#endif
int bufSize = 0;
if (ippiTrueDistanceTransformGetBufferSize_8u32f_C1R(roi, &bufSize) < 0)
return CV_HAL_ERROR_NOT_IMPLEMENTED;
Ipp8u *pBuffer = (Ipp8u *)IPP_HAL_MALLOC(bufSize);
if (!pBuffer)
return CV_HAL_ERROR_NOT_IMPLEMENTED;
IppStatus status = CV_INSTRUMENT_FUN_IPP(ippiTrueDistanceTransform_8u32f_C1R, src_data, (int)src_step,
(Ipp32f *)dst_data, (int)dst_step, roi, pBuffer);
ippFree(pBuffer);
if (status < 0)
return CV_HAL_ERROR_NOT_IMPLEMENTED;
// https://github.com/opencv/opencv/issues/24082
// There is probably a rounding issue that leads to non-deterministic behavior
// between runs on positions closer to zeros by x-axis in straight direction.
// As a workaround, we detect the distances that expected to be exact
// number of pixels and round manually.
static const float correctionDiff = 1.0f / (1 << 11);
for (int i = 0; i < height; ++i)
{
float* row = (float*)(dst_data + (size_t)i * dst_step);
for (int j = 0; j < width; ++j)
{
float rounded = static_cast<float>(cvRound(row[j]));
if (std::fabs(row[j] - rounded) <= correctionDiff)
row[j] = rounded;
}
}
return CV_HAL_ERROR_OK;
}
// DIST_MASK_3 / DIST_MASK_5 chamfer distances.
if (mask_size != cv::DIST_MASK_3 && mask_size != cv::DIST_MASK_5)
return CV_HAL_ERROR_NOT_IMPLEMENTED;
// IPP uses 32-bit signed intermediates; bail out when the image is too large.
bool has_int_overflow = (int64)width * height >= INT_MAX;
if (has_int_overflow)
return CV_HAL_ERROR_NOT_IMPLEMENTED;
float metrics[5] = {0};
if (!ippGetDistanceTransformMask(dist_type, mask_size, metrics))
return CV_HAL_ERROR_NOT_IMPLEMENTED;
if (mask_size == cv::DIST_MASK_3)
{
if (CV_INSTRUMENT_FUN_IPP(ippiDistanceTransform_3x3_8u32f_C1R, src_data, (int)src_step,
(Ipp32f *)dst_data, (int)dst_step, roi, metrics) >= 0)
{
return CV_HAL_ERROR_OK;
}
}
else // DIST_MASK_5
{
if (CV_INSTRUMENT_FUN_IPP(ippiDistanceTransform_5x5_8u32f_C1R, src_data, (int)src_step,
(Ipp32f *)dst_data, (int)dst_step, roi, metrics) >= 0)
{
return CV_HAL_ERROR_OK;
}
}
return CV_HAL_ERROR_NOT_IMPLEMENTED;
}
#endif // IPP_VERSION_X100 >= 700
+157
View File
@@ -0,0 +1,157 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html
// Copyright (C) 2026, BigVision LLC, all rights reserved.
// Third party copyrights are property of their respective owners.
#include "ipp_hal_imgproc.hpp"
#include <opencv2/core.hpp>
#include "precomp_ipp.hpp"
#include <cmath>
#include <cfloat>
#if defined(HAVE_IPP_IW) && !DISABLE_IPP_FILTER2D
// Copied from core/private.hpp (gated by HAVE_IPP, which the plugin lacks).
// filterGetIppBorderType: distinct name; adds the BORDER_REFLECT_101 -> ippBorderMirror
// case that precomp_ipp.hpp's ippiGetBorderType omits.
static inline IppiBorderType filterGetIppBorderType(int borderTypeNI)
{
return borderTypeNI == cv::BORDER_CONSTANT ? ippBorderConst :
borderTypeNI == cv::BORDER_TRANSPARENT ? ippBorderTransp :
borderTypeNI == cv::BORDER_REPLICATE ? ippBorderRepl :
borderTypeNI == cv::BORDER_REFLECT_101 ? ippBorderMirror :
(IppiBorderType)-1;
}
static inline bool ippiCheckAnchor(int x, int y, int kernelWidth, int kernelHeight)
{
return (x == (kernelWidth - 1)/2 && y == (kernelHeight - 1)/2);
}
static inline IppiBorderType ippiGetBorder(::ipp::IwiImage &image, int ocvBorderType, ::ipp::IwiBorderSize &borderSize)
{
int inMemFlags = 0;
IppiBorderType border = filterGetIppBorderType(ocvBorderType & ~cv::BORDER_ISOLATED);
if((int)border == -1)
return (IppiBorderType)0;
if(!(ocvBorderType & cv::BORDER_ISOLATED))
{
if(image.m_inMemSize.left)
{
if(image.m_inMemSize.left >= borderSize.left)
inMemFlags |= ippBorderInMemLeft;
else
return (IppiBorderType)0;
}
else
borderSize.left = 0;
if(image.m_inMemSize.top)
{
if(image.m_inMemSize.top >= borderSize.top)
inMemFlags |= ippBorderInMemTop;
else
return (IppiBorderType)0;
}
else
borderSize.top = 0;
if(image.m_inMemSize.right)
{
if(image.m_inMemSize.right >= borderSize.right)
inMemFlags |= ippBorderInMemRight;
else
return (IppiBorderType)0;
}
else
borderSize.right = 0;
if(image.m_inMemSize.bottom)
{
if(image.m_inMemSize.bottom >= borderSize.bottom)
inMemFlags |= ippBorderInMemBottom;
else
return (IppiBorderType)0;
}
else
borderSize.bottom = 0;
}
else
borderSize.left = borderSize.right = borderSize.top = borderSize.bottom = 0;
return (IppiBorderType)(border | inMemFlags);
}
int ipp_hal_filter2D(const uchar * src_data, size_t src_step, int src_type,
uchar * dst_data, size_t dst_step, int dst_type,
int width, int height, int full_width, int full_height,
int offset_x, int offset_y,
const uchar * kernel_data, size_t kernel_step, int kernel_type,
int kernel_width, int kernel_height,
int anchor_x, int anchor_y, double delta, int borderType,
bool isSubmatrix, bool allowInplace)
{
CV_HAL_CHECK_USE_IPP();
IppDataType type = ippiGetDataType(CV_MAT_DEPTH(src_type));
int channels = CV_MAT_CN(src_type);
CV_UNUSED(isSubmatrix);
CV_UNUSED(allowInplace);
#if IPP_VERSION_X100 >= 201700 && IPP_VERSION_X100 <= 201702 // IPP bug with 1x1 kernel
if(kernel_width == 1 && kernel_height == 1)
return CV_HAL_ERROR_NOT_IMPLEMENTED;
#endif
#if IPP_DISABLE_FILTER2D_BIG_MASK
// Too big difference compared to OpenCV FFT-based convolution
if(kernel_type == CV_32FC1 && (type == ipp16s || type == ipp16u) && (kernel_width > 7 || kernel_height > 7))
return CV_HAL_ERROR_NOT_IMPLEMENTED;
// Poor optimization for big kernels
if(kernel_width > 7 || kernel_height > 7)
return CV_HAL_ERROR_NOT_IMPLEMENTED;
#endif
if(src_data == dst_data)
return CV_HAL_ERROR_NOT_IMPLEMENTED;
if(src_type != dst_type)
return CV_HAL_ERROR_NOT_IMPLEMENTED;
if(kernel_type != CV_16SC1 && kernel_type != CV_32FC1)
return CV_HAL_ERROR_NOT_IMPLEMENTED;
// TODO: Implement offset for 8u, 16u
if(std::fabs(delta) >= DBL_EPSILON)
return CV_HAL_ERROR_NOT_IMPLEMENTED;
if(!ippiCheckAnchor(anchor_x, anchor_y, kernel_width, kernel_height))
return CV_HAL_ERROR_NOT_IMPLEMENTED;
try
{
::ipp::IwiSize iwSize(width, height);
::ipp::IwiSize kernelSize(kernel_width, kernel_height);
::ipp::IwiImage iwKernel(ippiSize(kernel_width, kernel_height), ippiGetDataType(CV_MAT_DEPTH(kernel_type)), CV_MAT_CN(kernel_type), 0, (void*)kernel_data, kernel_step);
::ipp::IwiImage iwSrc(iwSize, type, channels, ::ipp::IwiBorderSize(offset_x, offset_y, full_width-offset_x-width, full_height-offset_y-height), (void*)src_data, src_step);
::ipp::IwiImage iwDst(iwSize, type, channels, ::ipp::IwiBorderSize(offset_x, offset_y, full_width-offset_x-width, full_height-offset_y-height), (void*)dst_data, dst_step);
::ipp::IwiBorderSize iwBorderSize = ::ipp::iwiSizeToBorderSize(kernelSize);
::ipp::IwiBorderType iwBorderType = ippiGetBorder(iwSrc, borderType, iwBorderSize);
if(!iwBorderType)
return CV_HAL_ERROR_NOT_IMPLEMENTED;
CV_INSTRUMENT_FUN_IPP(::ipp::iwiFilter, iwSrc, iwDst, iwKernel, ::ipp::IwiFilterParams(1, 0, ippAlgHintNone, ippRndFinancial), iwBorderType);
}
catch (const ::ipp::IwException &)
{
return CV_HAL_ERROR_NOT_IMPLEMENTED;
}
return CV_HAL_ERROR_OK;
}
#endif // defined(HAVE_IPP_IW) && !DISABLE_IPP_FILTER2D
+223
View File
@@ -0,0 +1,223 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
// Copyright (C) 2026, BigVision LLC, all rights reserved.
// Third party copyrights are property of their respective owners.
#include "ipp_hal_imgproc.hpp"
#include <opencv2/core.hpp>
#include <opencv2/core/utils/tls.hpp>
#include "precomp_ipp.hpp"
#if IPP_VERSION_X100 >= 700
#define IPP_HISTOGRAM_PARALLEL 1
namespace cv { namespace ipp { unsigned long long getIppTopFeatures(); } }
using namespace cv;
namespace {
typedef IppStatus(CV_STDCALL * IppiHistogram_C1)(const void* pSrc, int srcStep,
IppiSize roiSize, Ipp32u* pHist, const IppiHistogramSpec* pSpec, Ipp8u* pBuffer);
static IppiHistogram_C1 getIppiHistogramFunction_C1(int type)
{
IppiHistogram_C1 ippFunction =
(type == CV_8UC1) ? (IppiHistogram_C1)ippiHistogram_8u_C1R :
(type == CV_16UC1) ? (IppiHistogram_C1)ippiHistogram_16u_C1R :
(type == CV_32FC1) ? (IppiHistogram_C1)ippiHistogram_32f_C1R :
NULL;
return ippFunction;
}
class ipp_calcHistParallelTLS
{
public:
ipp_calcHistParallelTLS() {}
IppAutoBuffer<IppiHistogramSpec> spec;
IppAutoBuffer<Ipp8u> buffer;
IppAutoBuffer<Ipp32u> thist;
};
class ipp_calcHistParallel: public ParallelLoopBody
{
public:
ipp_calcHistParallel(const Mat &src, Mat &hist, Ipp32s histSize, const float *ranges, bool uniform, bool &ok):
ParallelLoopBody(), m_src(src), m_hist(hist), m_ok(ok)
{
ok = true;
m_uniform = uniform;
m_ranges = ranges;
m_histSize = histSize;
m_type = ippiGetDataType(src.type());
m_levelsNum = histSize+1;
ippiHistogram_C1 = getIppiHistogramFunction_C1(src.type());
m_fullRoi = ippiSize(src.size());
m_bufferSize = 0;
m_specSize = 0;
if(!ippiHistogram_C1)
{
ok = false;
return;
}
if(ippiHistogramGetBufferSize(m_type, m_fullRoi, &m_levelsNum, 1, 1, &m_specSize, &m_bufferSize) < 0)
{
ok = false;
return;
}
hist.setTo(0);
}
virtual void operator() (const Range & range) const CV_OVERRIDE
{
if(!m_ok)
return;
ipp_calcHistParallelTLS *pTls = m_tls.get();
IppiSize roi = {m_src.cols, range.end - range.start };
bool mtLoop = false;
if(m_fullRoi.height != roi.height)
mtLoop = true;
if(!pTls->spec)
{
pTls->spec.allocate(m_specSize);
if(!pTls->spec.get())
{
m_ok = false;
return;
}
pTls->buffer.allocate(m_bufferSize);
if(!pTls->buffer.get() && m_bufferSize)
{
m_ok = false;
return;
}
if(m_uniform)
{
if(ippiHistogramUniformInit(m_type, (Ipp32f*)&m_ranges[0], (Ipp32f*)&m_ranges[1], (Ipp32s*)&m_levelsNum, 1, pTls->spec) < 0)
{
m_ok = false;
return;
}
}
else
{
if(ippiHistogramInit(m_type, (const Ipp32f**)&m_ranges, (Ipp32s*)&m_levelsNum, 1, pTls->spec) < 0)
{
m_ok = false;
return;
}
}
pTls->thist.allocate(m_histSize*sizeof(Ipp32u));
}
if(CV_INSTRUMENT_FUN_IPP(ippiHistogram_C1, m_src.ptr(range.start), (int)m_src.step, roi, pTls->thist, pTls->spec, pTls->buffer) < 0)
{
m_ok = false;
return;
}
if(mtLoop)
{
for(int i = 0; i < m_histSize; i++)
CV_XADD((int*)(m_hist.ptr(i)), *(int*)((Ipp32u*)pTls->thist + i));
}
else
ippiCopy_32s_C1R((Ipp32s*)pTls->thist.get(), sizeof(Ipp32u), (Ipp32s*)m_hist.ptr(), (int)m_hist.step, ippiSize(1, m_histSize));
}
private:
const Mat &m_src;
Mat &m_hist;
Ipp32s m_histSize;
const float *m_ranges;
bool m_uniform;
IppiHistogram_C1 ippiHistogram_C1;
IppiSize m_fullRoi;
IppDataType m_type;
Ipp32s m_levelsNum;
int m_bufferSize;
int m_specSize;
mutable Mutex m_syncMutex;
TLSData<ipp_calcHistParallelTLS> m_tls;
volatile bool &m_ok;
const ipp_calcHistParallel & operator = (const ipp_calcHistParallel & );
};
static bool ipp_calchist(const Mat &image, Mat &hist, int histSize, const float** ranges, bool uniform, bool accumulate)
{
#if IPP_VERSION_X100 < 201801
// No SSE42 optimization for uniform 32f
if(uniform && image.depth() == CV_32F && cv::ipp::getIppTopFeatures() == ippCPUID_SSE42)
return false;
#endif
// IPP_DISABLE_HISTOGRAM - https://github.com/opencv/opencv/issues/11544
// and https://github.com/opencv/opencv/issues/21595
if ((uniform && (ranges[0][1] - ranges[0][0]) != histSize) || abs(ranges[0][0]) != cvFloor(ranges[0][0]))
return false;
Mat ihist = hist;
if(accumulate)
ihist.create(1, &histSize, CV_32S);
bool ok = true;
int threads = ippiSuggestThreadsNum(image.cols, image.rows, image.elemSize(), (1+((double)ihist.total()/image.total()))*2);
Range range(0, image.rows);
ipp_calcHistParallel invoker(image, ihist, histSize, ranges[0], uniform, ok);
if(!ok)
return false;
if(IPP_HISTOGRAM_PARALLEL && threads > 1)
parallel_for_(range, invoker, threads*2);
else
invoker(range);
if(ok)
{
if(accumulate)
{
IppiSize histRoi = ippiSize(1, histSize);
IppAutoBuffer<Ipp32f> fhist(histSize*sizeof(Ipp32f));
CV_INSTRUMENT_FUN_IPP(ippiConvert_32s32f_C1R, (Ipp32s*)ihist.ptr(), (int)ihist.step, (Ipp32f*)fhist, sizeof(Ipp32f), histRoi);
CV_INSTRUMENT_FUN_IPP(ippiAdd_32f_C1IR, (Ipp32f*)fhist, sizeof(Ipp32f), (Ipp32f*)hist.ptr(), (int)hist.step, histRoi);
}
else
CV_INSTRUMENT_FUN_IPP(ippiConvert_32s32f_C1R, (Ipp32s*)ihist.ptr(), (int)ihist.step, (Ipp32f*)hist.ptr(), (int)hist.step, ippiSize(1, histSize));
}
return ok;
}
} // namespace
int ipp_hal_calcHist(const uchar* src_data, size_t src_step, int src_type, int src_width, int src_height,
float* hist_data, int hist_size, const float** ranges, bool uniform, bool accumulate)
{
CV_HAL_CHECK_USE_IPP();
Mat image(src_height, src_width, src_type, (void*)src_data, src_step);
Mat hist(1, &hist_size, CV_32F, (void*)hist_data);
if(ipp_calchist(image, hist, hist_size, ranges, uniform, accumulate))
return CV_HAL_ERROR_OK;
return CV_HAL_ERROR_NOT_IMPLEMENTED;
}
#endif
+144
View File
@@ -0,0 +1,144 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html
// Copyright (C) 2026, BigVision LLC, all rights reserved.
// Third party copyrights are property of their respective owners.
#include "ipp_hal_imgproc.hpp"
#include <opencv2/core.hpp>
#include "precomp_ipp.hpp"
#if IPP_VERSION_X100 >= 700
using namespace cv;
#if IPP_VERSION_X100 >= 201700
#define IPP_HAL_MALLOC(SIZE) ippMalloc_L(SIZE)
#else
#define IPP_HAL_MALLOC(SIZE) ippMalloc((int)(SIZE))
#endif
typedef IppStatus (CV_STDCALL * ippimatchTemplate)(const void*, int, IppiSize, const void*, int, IppiSize, Ipp32f* , int , IppEnum , Ipp8u*);
static bool ipp_crossCorr(const Mat& src, const Mat& tpl, Mat& dst, bool normed)
{
IppStatus status;
IppiSize srcRoiSize = {src.cols,src.rows};
IppiSize tplRoiSize = {tpl.cols,tpl.rows};
int bufSize=0;
int depth = src.depth();
ippimatchTemplate ippiCrossCorrNorm =
depth==CV_8U ? (ippimatchTemplate)ippiCrossCorrNorm_8u32f_C1R:
depth==CV_32F? (ippimatchTemplate)ippiCrossCorrNorm_32f_C1R: 0;
if (ippiCrossCorrNorm==0)
return false;
IppEnum funCfg = (IppEnum)(+ippAlgAuto | ippiROIValid);
if(normed)
funCfg |= ippiNorm;
else
funCfg |= ippiNormNone;
status = ippiCrossCorrNormGetBufferSize(srcRoiSize, tplRoiSize, funCfg, &bufSize);
if ( status < 0 )
return false;
Ipp8u* buffer = bufSize > 0 ? (Ipp8u*)IPP_HAL_MALLOC(bufSize) : 0;
if (bufSize > 0 && buffer == 0)
return false;
status = CV_INSTRUMENT_FUN_IPP(ippiCrossCorrNorm, src.ptr(), (int)src.step, srcRoiSize, tpl.ptr(), (int)tpl.step, tplRoiSize, dst.ptr<Ipp32f>(), (int)dst.step, funCfg, buffer);
if (buffer)
ippFree(buffer);
return status >= 0;
}
static bool ipp_sqrDistance(const Mat& src, const Mat& tpl, Mat& dst)
{
IppStatus status;
IppiSize srcRoiSize = {src.cols,src.rows};
IppiSize tplRoiSize = {tpl.cols,tpl.rows};
int bufSize=0;
int depth = src.depth();
ippimatchTemplate ippiSqrDistanceNorm =
depth==CV_8U ? (ippimatchTemplate)ippiSqrDistanceNorm_8u32f_C1R:
depth==CV_32F? (ippimatchTemplate)ippiSqrDistanceNorm_32f_C1R: 0;
if (ippiSqrDistanceNorm==0)
return false;
IppEnum funCfg = (IppEnum)(+ippAlgAuto | ippiROIValid | ippiNormNone);
status = ippiSqrDistanceNormGetBufferSize(srcRoiSize, tplRoiSize, funCfg, &bufSize);
if ( status < 0 )
return false;
Ipp8u* buffer = bufSize > 0 ? (Ipp8u*)IPP_HAL_MALLOC(bufSize) : 0;
if (bufSize > 0 && buffer == 0)
return false;
status = CV_INSTRUMENT_FUN_IPP(ippiSqrDistanceNorm, src.ptr(), (int)src.step, srcRoiSize, tpl.ptr(), (int)tpl.step, tplRoiSize, dst.ptr<Ipp32f>(), (int)dst.step, funCfg, buffer);
if (buffer)
ippFree(buffer);
dst = cv::max(dst, 0); // handle edge case from rounding in variance computation which can result in negative values
return status >= 0;
}
int ipp_hal_matchTemplate(const uchar* src_data, size_t src_step, int src_width, int src_height,
const uchar* templ_data, size_t templ_step, int templ_width, int templ_height,
float* result_data, size_t result_step, int depth, int cn, int method)
{
CV_HAL_CHECK_USE_IPP();
if(cn != 1)
return CV_HAL_ERROR_NOT_IMPLEMENTED;
if(depth != CV_8U && depth != CV_32F)
return CV_HAL_ERROR_NOT_IMPLEMENTED;
Mat img(src_height, src_width, CV_MAKETYPE(depth, cn), (void*)src_data, src_step);
Mat templ(templ_height, templ_width, CV_MAKETYPE(depth, cn), (void*)templ_data, templ_step);
Mat result(src_height - templ_height + 1, src_width - templ_width + 1, CV_32FC1, (void*)result_data, result_step);
// These functions are not efficient if template size is comparable with image size
if(templ.size().area()*4 > img.size().area())
return CV_HAL_ERROR_NOT_IMPLEMENTED;
// CV_8U SQDIFF/SQDIFF_NORMED suffer from float32 catastrophic cancellation
// in IPP's internal accumulators; fall through to the double-precision path instead.
if(depth == CV_8U && (method == cv::TM_SQDIFF || method == cv::TM_SQDIFF_NORMED))
return CV_HAL_ERROR_NOT_IMPLEMENTED;
if(method == cv::TM_SQDIFF)
{
if(ipp_sqrDistance(img, templ, result))
return CV_HAL_ERROR_OK;
}
else if(method == cv::TM_CCORR_NORMED)
{
if(ipp_crossCorr(img, templ, result, true))
return CV_HAL_ERROR_OK;
}
else if(method == cv::TM_SQDIFF_NORMED || method == cv::TM_CCORR ||
method == cv::TM_CCOEFF || method == cv::TM_CCOEFF_NORMED)
{
// Raw cross-correlation; caller finishes SQDIFF_NORMED/CCOEFF/CCOEFF_NORMED via
// common_matchTemplate (TM_CCORR is already final).
if(ipp_crossCorr(img, templ, result, false))
return CV_HAL_ERROR_OK;
}
return CV_HAL_ERROR_NOT_IMPLEMENTED;
}
#endif // IPP_VERSION_X100 >= 700
+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;
}
+40
View File
@@ -23,6 +23,20 @@ static inline IppiSize ippiSize(const cv::Size & _size)
return size;
}
#if IPP_VERSION_X100 >= 201700
static inline IppiSizeL ippiSizeL(size_t width, size_t height)
{
IppiSizeL size = { (IppSizeL)width, (IppSizeL)height };
return size;
}
static inline IppiSizeL ippiSizeL(const cv::Size & _size)
{
IppiSizeL size = { _size.width, _size.height };
return size;
}
#endif
static inline IppDataType ippiGetDataType(int depth)
{
depth = CV_MAT_DEPTH(depth);
@@ -106,4 +120,30 @@ static inline int ippiSuggestRowThreadsNum(const ::ipp::IwiImage &image, size_t
}
#endif
#if IPP_VERSION_X100 >= 201700
#define CV_IPP_MALLOC(SIZE) ippMalloc_L(SIZE)
#else
#define CV_IPP_MALLOC(SIZE) ippMalloc((int)SIZE)
#endif
template<typename T>
class IppAutoBuffer
{
public:
IppAutoBuffer() { m_size = 0; m_pBuffer = NULL; }
explicit IppAutoBuffer(size_t size) { m_size = 0; m_pBuffer = NULL; allocate(size); }
~IppAutoBuffer() { deallocate(); }
T* allocate(size_t size) { if(m_size < size) { deallocate(); m_pBuffer = (T*)CV_IPP_MALLOC(size); m_size = size; } return m_pBuffer; }
void deallocate() { if(m_pBuffer) { ippFree(m_pBuffer); m_pBuffer = NULL; } m_size = 0; }
inline T* get() { return (T*)m_pBuffer;}
inline operator T* () { return (T*)m_pBuffer;}
inline operator const T* () const { return (const T*)m_pBuffer;}
private:
IppAutoBuffer(IppAutoBuffer &) {}
IppAutoBuffer& operator =(const IppAutoBuffer &) {return *this;}
size_t m_size;
T* m_pBuffer;
};
#endif //__PRECOMP_IPP_HPP__
+196
View File
@@ -0,0 +1,196 @@
// 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_imgproc.hpp"
#ifdef HAVE_IPP_IW
#include <opencv2/core.hpp>
#include "precomp_ipp.hpp"
#include "iw++/iw.hpp"
#include <cfloat>
#define IPP_RESIZE_PARALLEL 1
class ipp_resizeParallel: public cv::ParallelLoopBody
{
public:
ipp_resizeParallel(::ipp::IwiImage &src, ::ipp::IwiImage &dst, bool &ok):
m_src(src), m_dst(dst), m_ok(ok) {}
~ipp_resizeParallel()
{
}
void Init(IppiInterpolationType inter)
{
iwiResize.InitAlloc(m_src.m_size, m_dst.m_size, m_src.m_dataType, m_src.m_channels, inter, ::ipp::IwiResizeParams(0, 0, 0.75, 4), ippBorderRepl);
m_ok = true;
}
virtual void operator() (const cv::Range& range) const CV_OVERRIDE
{
if(!m_ok)
return;
try
{
::ipp::IwiTile tile = ::ipp::IwiRoi(0, range.start, m_dst.m_size.width, range.end - range.start);
CV_INSTRUMENT_FUN_IPP(iwiResize, m_src, m_dst, ippBorderRepl, tile);
}
catch(const ::ipp::IwException &)
{
m_ok = false;
return;
}
}
private:
::ipp::IwiImage &m_src;
::ipp::IwiImage &m_dst;
mutable ::ipp::IwiResize iwiResize;
volatile bool &m_ok;
const ipp_resizeParallel& operator= (const ipp_resizeParallel&);
};
class ipp_resizeAffineParallel: public cv::ParallelLoopBody
{
public:
ipp_resizeAffineParallel(::ipp::IwiImage &src, ::ipp::IwiImage &dst, bool &ok):
m_src(src), m_dst(dst), m_ok(ok) {}
~ipp_resizeAffineParallel()
{
}
void Init(IppiInterpolationType inter, double scaleX, double scaleY)
{
double shift = (inter == ippNearest)?-1e-10:-0.5;
double coeffs[2][3] = {
{scaleX, 0, shift+0.5*scaleX},
{0, scaleY, shift+0.5*scaleY}
};
iwiWarpAffine.InitAlloc(m_src.m_size, m_dst.m_size, m_src.m_dataType, m_src.m_channels, coeffs, iwTransForward, inter, ::ipp::IwiWarpAffineParams(0, 0, 0.75), ippBorderRepl);
m_ok = true;
}
virtual void operator() (const cv::Range& range) const CV_OVERRIDE
{
if(!m_ok)
return;
try
{
::ipp::IwiTile tile = ::ipp::IwiRoi(0, range.start, m_dst.m_size.width, range.end - range.start);
CV_INSTRUMENT_FUN_IPP(iwiWarpAffine, m_src, m_dst, tile);
}
catch(const ::ipp::IwException &)
{
m_ok = false;
return;
}
}
private:
::ipp::IwiImage &m_src;
::ipp::IwiImage &m_dst;
mutable ::ipp::IwiWarpAffine iwiWarpAffine;
volatile bool &m_ok;
const ipp_resizeAffineParallel& operator= (const ipp_resizeAffineParallel&);
};
int ipp_hal_resize(int src_type, const uchar *src_data, size_t src_step, int src_width, int src_height,
uchar *dst_data, size_t dst_step, int dst_width, int dst_height,
double inv_scale_x, double inv_scale_y, int interpolation)
{
CV_HAL_CHECK_USE_IPP();
int depth = CV_MAT_DEPTH(src_type), channels = CV_MAT_CN(src_type);
IppDataType ippDataType = ippiGetDataType(depth);
IppiInterpolationType ippInter = ippiGetInterpolation(interpolation);
if((int)ippInter < 0)
return CV_HAL_ERROR_NOT_IMPLEMENTED;
// Resize which doesn't match OpenCV exactly
if (!cv::ipp::useIPP_NotExact())
{
if (ippInter == ippNearest || ippInter == ippSuper || (ippDataType == ipp8u && ippInter == ippLinear))
return CV_HAL_ERROR_NOT_IMPLEMENTED;
}
if(ippInter != ippLinear && ippDataType == ipp64f)
return CV_HAL_ERROR_NOT_IMPLEMENTED;
#if IPP_VERSION_X100 < 201801
// Degradations on int^2 linear downscale
if (ippDataType != ipp64f && ippInter == ippLinear && inv_scale_x < 1 && inv_scale_y < 1) // if downscale
{
int scale_x = (int)(1 / inv_scale_x);
int scale_y = (int)(1 / inv_scale_y);
if (1 / inv_scale_x - scale_x < DBL_EPSILON && 1 / inv_scale_y - scale_y < DBL_EPSILON) // if integer
{
if (!(scale_x&(scale_x - 1)) && !(scale_y&(scale_y - 1))) // if power of 2
return CV_HAL_ERROR_NOT_IMPLEMENTED;
}
}
#endif
bool affine = false;
const double IPP_RESIZE_EPS = (depth == CV_64F)?0:1e-10;
double ex = fabs((double)dst_width / src_width - inv_scale_x) / inv_scale_x;
double ey = fabs((double)dst_height / src_height - inv_scale_y) / inv_scale_y;
// Use affine transform resize to allow sub-pixel accuracy
if(ex > IPP_RESIZE_EPS || ey > IPP_RESIZE_EPS)
affine = true;
// Affine doesn't support Lanczos and Super interpolations
if(affine && (ippInter == ippLanczos || ippInter == ippSuper))
return CV_HAL_ERROR_NOT_IMPLEMENTED;
try
{
::ipp::IwiImage iwSrc(::ipp::IwiSize(src_width, src_height), ippDataType, channels, 0, (void*)src_data, src_step);
::ipp::IwiImage iwDst(::ipp::IwiSize(dst_width, dst_height), ippDataType, channels, 0, (void*)dst_data, dst_step);
bool ok;
int threads = ippiSuggestThreadsNum(iwDst, 1+((double)(src_width*src_height)/(dst_width*dst_height)));
cv::Range range(0, dst_height);
ipp_resizeParallel invokerGeneral(iwSrc, iwDst, ok);
ipp_resizeAffineParallel invokerAffine(iwSrc, iwDst, ok);
cv::ParallelLoopBody *pInvoker = NULL;
if(affine)
{
pInvoker = &invokerAffine;
invokerAffine.Init(ippInter, inv_scale_x, inv_scale_y);
}
else
{
pInvoker = &invokerGeneral;
invokerGeneral.Init(ippInter);
}
if(IPP_RESIZE_PARALLEL && threads > 1)
cv::parallel_for_(range, *pInvoker, threads*4);
else
pInvoker->operator()(range);
if(!ok)
return CV_HAL_ERROR_NOT_IMPLEMENTED;
}
catch(const ::ipp::IwException &)
{
return CV_HAL_ERROR_NOT_IMPLEMENTED;
}
return CV_HAL_ERROR_OK;
}
#endif // HAVE_IPP_IW
+128
View File
@@ -0,0 +1,128 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html
// Copyright (C) 2026, BigVision LLC, all rights reserved.
// Third party copyrights are property of their respective owners.
#include "ipp_hal_imgproc.hpp"
#include <opencv2/core.hpp>
#include "precomp_ipp.hpp"
#include <cmath>
#include <limits>
#if IPP_VERSION_X100 >= 700
int ipp_hal_threshold(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step,
int width, int height, int depth, int cn, double thresh, double maxValue,
int thresholdType)
{
CV_HAL_CHECK_USE_IPP();
CV_UNUSED(maxValue);
// IPP only implements these three types and depths; everything else falls back to OpenCV.
if (thresholdType != cv::THRESH_TRUNC && thresholdType != cv::THRESH_TOZERO &&
thresholdType != cv::THRESH_TOZERO_INV)
return CV_HAL_ERROR_NOT_IMPLEMENTED;
if (depth != CV_8U && depth != CV_16S && depth != CV_32F)
return CV_HAL_ERROR_NOT_IMPLEMENTED;
int roi_width = width * cn; // threshold is elementwise, so channels fold into the width
int roi_height = height;
int elemSize1 = (depth == CV_8U) ? 1 : (depth == CV_16S) ? 2 : 4;
if (src_step == (size_t)roi_width * elemSize1 && dst_step == (size_t)roi_width * elemSize1)
{
roi_width *= roi_height;
roi_height = 1;
src_step = dst_step = (size_t)roi_width * elemSize1;
}
IppiSize sz = { roi_width, roi_height };
const bool inplace = (src_data == dst_data);
IppStatus status = ippStsErr;
CV_SUPPRESS_DEPRECATED_START
switch (depth)
{
case CV_8U:
{
Ipp8u t = (Ipp8u)thresh;
switch (thresholdType)
{
case cv::THRESH_TRUNC:
if (inplace)
status = CV_INSTRUMENT_FUN_IPP(ippiThreshold_GT_8u_C1IR, dst_data, (int)dst_step, sz, t);
if (!inplace || status < 0)
status = CV_INSTRUMENT_FUN_IPP(ippiThreshold_GT_8u_C1R, src_data, (int)src_step, dst_data, (int)dst_step, sz, t);
break;
case cv::THRESH_TOZERO:
if (inplace)
status = CV_INSTRUMENT_FUN_IPP(ippiThreshold_LTVal_8u_C1IR, dst_data, (int)dst_step, sz, t + 1, 0);
if (!inplace || status < 0)
status = CV_INSTRUMENT_FUN_IPP(ippiThreshold_LTVal_8u_C1R, src_data, (int)src_step, dst_data, (int)dst_step, sz, t + 1, 0);
break;
case cv::THRESH_TOZERO_INV:
if (inplace)
status = CV_INSTRUMENT_FUN_IPP(ippiThreshold_GTVal_8u_C1IR, dst_data, (int)dst_step, sz, t, 0);
if (!inplace || status < 0)
status = CV_INSTRUMENT_FUN_IPP(ippiThreshold_GTVal_8u_C1R, src_data, (int)src_step, dst_data, (int)dst_step, sz, t, 0);
break;
}
break;
}
case CV_16S:
{
const Ipp16s* src = (const Ipp16s*)src_data;
Ipp16s* dst = (Ipp16s*)dst_data;
Ipp16s t = (Ipp16s)thresh;
switch (thresholdType)
{
case cv::THRESH_TRUNC:
if (inplace)
status = CV_INSTRUMENT_FUN_IPP(ippiThreshold_GT_16s_C1IR, dst, (int)dst_step, sz, t);
if (!inplace || status < 0)
status = CV_INSTRUMENT_FUN_IPP(ippiThreshold_GT_16s_C1R, src, (int)src_step, dst, (int)dst_step, sz, t);
break;
case cv::THRESH_TOZERO:
if (inplace)
status = CV_INSTRUMENT_FUN_IPP(ippiThreshold_LTVal_16s_C1IR, dst, (int)dst_step, sz, t + 1, 0);
if (!inplace || status < 0)
status = CV_INSTRUMENT_FUN_IPP(ippiThreshold_LTVal_16s_C1R, src, (int)src_step, dst, (int)dst_step, sz, t + 1, 0);
break;
case cv::THRESH_TOZERO_INV:
if (inplace)
status = CV_INSTRUMENT_FUN_IPP(ippiThreshold_GTVal_16s_C1IR, dst, (int)dst_step, sz, t, 0);
if (!inplace || status < 0)
status = CV_INSTRUMENT_FUN_IPP(ippiThreshold_GTVal_16s_C1R, src, (int)src_step, dst, (int)dst_step, sz, t, 0);
break;
}
break;
}
case CV_32F:
{
const Ipp32f* src = (const Ipp32f*)src_data;
Ipp32f* dst = (Ipp32f*)dst_data;
Ipp32f t = (Ipp32f)thresh;
switch (thresholdType)
{
case cv::THRESH_TRUNC:
status = CV_INSTRUMENT_FUN_IPP(ippiThreshold_GT_32f_C1R, src, (int)src_step, dst, (int)dst_step, sz, t);
break;
case cv::THRESH_TOZERO:
status = CV_INSTRUMENT_FUN_IPP(ippiThreshold_LTVal_32f_C1R, src, (int)src_step, dst, (int)dst_step, sz, nextafterf(t, std::numeric_limits<float>::infinity()), 0);
break;
case cv::THRESH_TOZERO_INV:
status = CV_INSTRUMENT_FUN_IPP(ippiThreshold_GTVal_32f_C1R, src, (int)src_step, dst, (int)dst_step, sz, t, 0);
break;
}
break;
}
}
CV_SUPPRESS_DEPRECATED_END
return status >= 0 ? CV_HAL_ERROR_OK : CV_HAL_ERROR_NOT_IMPLEMENTED;
}
#endif // IPP_VERSION_X100 >= 700
+7 -1
View File
@@ -81,6 +81,8 @@ static bool IPPSet(const double value[4], void *dataPointer, int step, IppiSize
int ipp_hal_warpAffine(int src_type, const uchar *src_data, size_t src_step, int src_width, int src_height, uchar *dst_data, size_t dst_step,
int dst_width, int dst_height, const double M[6], int interpolation, int borderType, const double borderValue[4])
{
CV_HAL_CHECK_USE_IPP();
//CV_INSTRUMENT_REGION_IPP();
IppiInterpolationType ippInter = ippiGetInterpolation(interpolation);
@@ -188,6 +190,8 @@ int ipp_hal_warpAffine(int src_type, const uchar *src_data, size_t src_step, int
int ipp_hal_warpPerspective(int src_type, const uchar *src_data, size_t src_step, int src_width, int src_height, uchar * dst_data, size_t dst_step,
int dst_width, int dst_height, const double M[9], int interpolation, int borderType, const double borderValue[4])
{
CV_HAL_CHECK_USE_IPP();
//CV_INSTRUMENT_REGION_IPP();
IppiInterpolationType ippInter = ippiGetInterpolation(interpolation);
@@ -231,7 +235,7 @@ int ipp_hal_warpPerspective(int src_type, const uchar *src_data, size_t src_step
{{0, 0}, {0, 0}, {0, 0}, {0, 1}}, //16U
{{1, 1}, {0, 0}, {1, 0}, {1, 1}}, //16S
{{1, 1}, {0, 0}, {1, 0}, {1, 1}}, //32S
{{0, 0}, {0, 0}, {0, 0}, {1, 0}}, //32F
{{0, 0}, {0, 0}, {0, 0}, {0, 0}}, //32F
{{0, 0}, {0, 0}, {0, 0}, {0, 0}}}; //64F
#endif
@@ -384,6 +388,8 @@ int ipp_hal_remap32f(int src_type, const uchar *src_data, size_t src_step, int s
float *mapx, size_t mapx_step, float *mapy, size_t mapy_step,
int interpolation, int border_type, const double border_value[4])
{
CV_HAL_CHECK_USE_IPP();
if (!((interpolation == cv::INTER_LINEAR || interpolation == cv::INTER_CUBIC || interpolation == cv::INTER_NEAREST) &&
(border_type == cv::BORDER_CONSTANT || border_type == cv::BORDER_TRANSPARENT)))
{
+1 -1
View File
@@ -141,7 +141,7 @@ int filter(cvhalFilter2D *context,
int cal_y = offset_y - ctx->anchor_y; // negative if top border exceeded
// calculate source border
ctx->padding.resize(cal_width * cal_height * cnes);
ctx->padding.resize((size_t)cal_width * cal_height * cnes);
uchar* pad_data = &ctx->padding[0];
int pad_step = cal_width * cnes;
+9
View File
@@ -99,9 +99,18 @@ int fast_atan_64(const double* y, const double* x, double* dst, size_t n, bool a
/* ############ split ############ */
int split8u(const uchar* src, uchar** dst, int len, int cn);
int split16u(const ushort* src, ushort** dst, int len, int cn);
int split32s(const int* src, int** dst, int len, int cn);
int split64s(const int64* src, int64** dst, int len, int cn);
#undef cv_hal_split8u
#define cv_hal_split8u cv::rvv_hal::core::split8u
#undef cv_hal_split16u
#define cv_hal_split16u cv::rvv_hal::core::split16u
#undef cv_hal_split32s
#define cv_hal_split32s cv::rvv_hal::core::split32s
#undef cv_hal_split64s
#define cv_hal_split64s cv::rvv_hal::core::split64s
/* ############ sqrt ############ */
+21 -2
View File
@@ -30,8 +30,27 @@ static int copyToMasked_e##X##c1(const uchar *src_data, size_t src_step, const u
return CV_HAL_ERROR_OK; \
}
CV_HAL_RVV_COPY_MASK_eXc1(8, 8)
CV_HAL_RVV_COPY_MASK_eXc1(16, 4)
#define CV_HAL_RVV_COPY_MASK_MERGE_eXc1(X, data_lmul, mask_lmul) \
static int copyToMasked_e##X##c1(const uchar *src_data, size_t src_step, const uchar *mask_data, size_t mask_step, \
uchar *dst_data, size_t dst_step, int width, int height) { \
for (; height--; mask_data += mask_step, src_data += src_step, dst_data += dst_step) { \
const uint##X##_t *src = (const uint##X##_t*)src_data; \
uint##X##_t *dst = (uint##X##_t*)dst_data; \
int vl; \
for (int i = 0; i < width; i += vl) { \
vl = __riscv_vsetvl_e##X##m##data_lmul(width - i); \
auto m = __riscv_vmsne(__riscv_vle8_v_u8m##mask_lmul(mask_data + i, vl), 0, vl); \
auto src_v = __riscv_vle##X##_v_u##X##m##data_lmul(src + i, vl); \
auto dst_v = __riscv_vle##X##_v_u##X##m##data_lmul(dst + i, vl); \
__riscv_vse##X##_v_u##X##m##data_lmul(dst + i, __riscv_vmerge(dst_v, src_v, m, vl), vl); \
} \
} \
return CV_HAL_ERROR_OK; \
}
CV_HAL_RVV_COPY_MASK_MERGE_eXc1(8, 8, 8)
CV_HAL_RVV_COPY_MASK_MERGE_eXc1(16, 8, 4)
CV_HAL_RVV_COPY_MASK_eXc1(32, 2)
CV_HAL_RVV_COPY_MASK_eXc1(64, 1)
+8
View File
@@ -19,6 +19,14 @@ inline int meanStdDev_32FC1(const uchar* src_data, size_t src_step, int width, i
int meanStdDev(const uchar* src_data, size_t src_step, int width, int height, int src_type,
double* mean_val, double* stddev_val, uchar* mask, size_t mask_step) {
// NOTE: The OpenCV imlementation with universal intrinscs is more efficient
// See https://github.com/opencv/opencv/pull/29339#issuecomment-4778104352
if (!mask && !stddev_val)
{
return CV_HAL_ERROR_NOT_IMPLEMENTED;
}
switch (src_type)
{
case CV_8UC1:
+135 -98
View File
@@ -28,6 +28,12 @@
#define __riscv_vcreate_v_u16m4x2(v0, v1) OPENCV_HAL_IMPL_RVV_VCREATE_x2(u16, 4, v0, v1)
#define __riscv_vcreate_v_u16m2x3(v0, v1, v2) OPENCV_HAL_IMPL_RVV_VCREATE_x3(u16, 2, v0, v1, v2)
#define __riscv_vcreate_v_u16m2x4(v0, v1, v2, v3) OPENCV_HAL_IMPL_RVV_VCREATE_x4(u16, 2, v0, v1, v2, v3)
#define __riscv_vcreate_v_i32m4x2(v0, v1) OPENCV_HAL_IMPL_RVV_VCREATE_x2(i32, 4, v0, v1)
#define __riscv_vcreate_v_i32m2x3(v0, v1, v2) OPENCV_HAL_IMPL_RVV_VCREATE_x3(i32, 2, v0, v1, v2)
#define __riscv_vcreate_v_i32m2x4(v0, v1, v2, v3) OPENCV_HAL_IMPL_RVV_VCREATE_x4(i32, 2, v0, v1, v2, v3)
#define __riscv_vcreate_v_i64m4x2(v0, v1) OPENCV_HAL_IMPL_RVV_VCREATE_x2(i64, 4, v0, v1)
#define __riscv_vcreate_v_i64m2x3(v0, v1, v2) OPENCV_HAL_IMPL_RVV_VCREATE_x3(i64, 2, v0, v1, v2)
#define __riscv_vcreate_v_i64m2x4(v0, v1, v2, v3) OPENCV_HAL_IMPL_RVV_VCREATE_x4(i64, 2, v0, v1, v2, v3)
#endif // clang < 18
namespace cv { namespace rvv_hal { namespace core {
@@ -204,146 +210,177 @@ int merge16u(const ushort** src, ushort* dst, int len, int cn ) {
return CV_HAL_ERROR_OK;
}
#if defined __GNUC__ && !defined(__clang__)
__attribute__((optimize("no-tree-vectorize")))
#endif
int merge32s(const int** src, int* dst, int len, int cn ) {
int k = cn % 4 ? cn % 4 : 4;
int i, j;
if( k == 1 )
int vl = 0;
if (cn == 1)
{
const int* src0 = src[0];
#if defined(__clang__)
#pragma clang loop vectorize(disable)
#endif
for( i = j = 0; i < len; i++, j += cn )
dst[j] = src0[i];
}
else if( k == 2 )
{
const int *src0 = src[0], *src1 = src[1];
i = j = 0;
#if defined(__clang__)
#pragma clang loop vectorize(disable)
#endif
for( ; i < len; i++, j += cn )
for (int i = 0; i < len; i += vl)
{
dst[j] = src0[i];
dst[j+1] = src1[i];
vl = __riscv_vsetvl_e32m8(len - i);
__riscv_vse32_v_i32m8(dst + i, __riscv_vle32_v_i32m8(src0 + i, vl), vl);
}
}
else if( k == 3 )
else if (cn == 2)
{
const int *src0 = src[0], *src1 = src[1];
for (int i = 0; i < len; i += vl)
{
vl = __riscv_vsetvl_e32m4(len - i);
vint32m4x2_t seg = __riscv_vcreate_v_i32m4x2(
__riscv_vle32_v_i32m4(src0 + i, vl),
__riscv_vle32_v_i32m4(src1 + i, vl)
);
__riscv_vsseg2e32_v_i32m4x2(dst + i * cn, seg, vl);
}
}
else if (cn == 3)
{
const int *src0 = src[0], *src1 = src[1], *src2 = src[2];
i = j = 0;
#if defined(__clang__)
#pragma clang loop vectorize(disable)
#endif
for( ; i < len; i++, j += cn )
for (int i = 0; i < len; i += vl)
{
dst[j] = src0[i];
dst[j+1] = src1[i];
dst[j+2] = src2[i];
vl = __riscv_vsetvl_e32m2(len - i);
vint32m2x3_t seg = __riscv_vcreate_v_i32m2x3(
__riscv_vle32_v_i32m2(src0 + i, vl),
__riscv_vle32_v_i32m2(src1 + i, vl),
__riscv_vle32_v_i32m2(src2 + i, vl)
);
__riscv_vsseg3e32_v_i32m2x3(dst + i * cn, seg, vl);
}
}
else if (cn == 4)
{
const int *src0 = src[0], *src1 = src[1], *src2 = src[2], *src3 = src[3];
for (int i = 0; i < len; i += vl)
{
vl = __riscv_vsetvl_e32m2(len - i);
vint32m2x4_t seg = __riscv_vcreate_v_i32m2x4(
__riscv_vle32_v_i32m2(src0 + i, vl),
__riscv_vle32_v_i32m2(src1 + i, vl),
__riscv_vle32_v_i32m2(src2 + i, vl),
__riscv_vle32_v_i32m2(src3 + i, vl)
);
__riscv_vsseg4e32_v_i32m2x4(dst + i * cn, seg, vl);
}
}
else
{
const int *src0 = src[0], *src1 = src[1], *src2 = src[2], *src3 = src[3];
i = j = 0;
#if defined(__clang__)
#pragma clang loop vectorize(disable)
#endif
for( ; i < len; i++, j += cn )
int k = 0;
for (; k <= cn - 4; k += 4)
{
dst[j] = src0[i]; dst[j+1] = src1[i];
dst[j+2] = src2[i]; dst[j+3] = src3[i];
const int *src0 = src[k], *src1 = src[k + 1], *src2 = src[k + 2], *src3 = src[k + 3];
for (int i = 0; i < len; i += vl)
{
vl = __riscv_vsetvl_e32m2(len - i);
vint32m2x4_t seg = __riscv_vcreate_v_i32m2x4(
__riscv_vle32_v_i32m2(src0 + i, vl),
__riscv_vle32_v_i32m2(src1 + i, vl),
__riscv_vle32_v_i32m2(src2 + i, vl),
__riscv_vle32_v_i32m2(src3 + i, vl)
);
__riscv_vssseg4e32_v_i32m2x4(dst + k + i * cn, cn * sizeof(int), seg, vl);
}
}
}
#if defined(__clang__)
#pragma clang loop vectorize(disable)
#endif
for( ; k < cn; k += 4 )
{
const int *src0 = src[k], *src1 = src[k+1], *src2 = src[k+2], *src3 = src[k+3];
for( i = 0, j = k; i < len; i++, j += cn )
for (; k < cn; ++k)
{
dst[j] = src0[i]; dst[j+1] = src1[i];
dst[j+2] = src2[i]; dst[j+3] = src3[i];
const int* srcK = src[k];
for (int i = 0; i < len; i += vl)
{
vl = __riscv_vsetvl_e32m2(len - i);
vint32m2_t seg = __riscv_vle32_v_i32m2(srcK + i, vl);
__riscv_vsse32_v_i32m2(dst + k + i * cn, cn * sizeof(int), seg, vl);
}
}
}
return CV_HAL_ERROR_OK;
}
#if defined __GNUC__ && !defined(__clang__)
__attribute__((optimize("no-tree-vectorize")))
#endif
int merge64s(const int64** src, int64* dst, int len, int cn ) {
int k = cn % 4 ? cn % 4 : 4;
int i, j;
if( k == 1 )
int vl = 0;
if (cn == 1)
{
const int64* src0 = src[0];
#if defined(__clang__)
#pragma clang loop vectorize(disable)
#endif
for( i = j = 0; i < len; i++, j += cn )
dst[j] = src0[i];
}
else if( k == 2 )
{
const int64 *src0 = src[0], *src1 = src[1];
i = j = 0;
#if defined(__clang__)
#pragma clang loop vectorize(disable)
#endif
for( ; i < len; i++, j += cn )
for (int i = 0; i < len; i += vl)
{
dst[j] = src0[i];
dst[j+1] = src1[i];
vl = __riscv_vsetvl_e64m8(len - i);
__riscv_vse64_v_i64m8(dst + i, __riscv_vle64_v_i64m8(src0 + i, vl), vl);
}
}
else if( k == 3 )
else if (cn == 2)
{
const int64 *src0 = src[0], *src1 = src[1];
for (int i = 0; i < len; i += vl)
{
vl = __riscv_vsetvl_e64m4(len - i);
vint64m4x2_t seg = __riscv_vcreate_v_i64m4x2(
__riscv_vle64_v_i64m4(src0 + i, vl),
__riscv_vle64_v_i64m4(src1 + i, vl)
);
__riscv_vsseg2e64_v_i64m4x2(dst + i * cn, seg, vl);
}
}
else if (cn == 3)
{
const int64 *src0 = src[0], *src1 = src[1], *src2 = src[2];
i = j = 0;
#if defined(__clang__)
#pragma clang loop vectorize(disable)
#endif
for( ; i < len; i++, j += cn )
for (int i = 0; i < len; i += vl)
{
dst[j] = src0[i];
dst[j+1] = src1[i];
dst[j+2] = src2[i];
vl = __riscv_vsetvl_e64m2(len - i);
vint64m2x3_t seg = __riscv_vcreate_v_i64m2x3(
__riscv_vle64_v_i64m2(src0 + i, vl),
__riscv_vle64_v_i64m2(src1 + i, vl),
__riscv_vle64_v_i64m2(src2 + i, vl)
);
__riscv_vsseg3e64_v_i64m2x3(dst + i * cn, seg, vl);
}
}
else if (cn == 4)
{
const int64 *src0 = src[0], *src1 = src[1], *src2 = src[2], *src3 = src[3];
for (int i = 0; i < len; i += vl)
{
vl = __riscv_vsetvl_e64m2(len - i);
vint64m2x4_t seg = __riscv_vcreate_v_i64m2x4(
__riscv_vle64_v_i64m2(src0 + i, vl),
__riscv_vle64_v_i64m2(src1 + i, vl),
__riscv_vle64_v_i64m2(src2 + i, vl),
__riscv_vle64_v_i64m2(src3 + i, vl)
);
__riscv_vsseg4e64_v_i64m2x4(dst + i * cn, seg, vl);
}
}
else
{
const int64 *src0 = src[0], *src1 = src[1], *src2 = src[2], *src3 = src[3];
i = j = 0;
#if defined(__clang__)
#pragma clang loop vectorize(disable)
#endif
for( ; i < len; i++, j += cn )
int k = 0;
for (; k <= cn - 4; k += 4)
{
dst[j] = src0[i]; dst[j+1] = src1[i];
dst[j+2] = src2[i]; dst[j+3] = src3[i];
const int64 *src0 = src[k], *src1 = src[k + 1], *src2 = src[k + 2], *src3 = src[k + 3];
for (int i = 0; i < len; i += vl)
{
vl = __riscv_vsetvl_e64m2(len - i);
vint64m2x4_t seg = __riscv_vcreate_v_i64m2x4(
__riscv_vle64_v_i64m2(src0 + i, vl),
__riscv_vle64_v_i64m2(src1 + i, vl),
__riscv_vle64_v_i64m2(src2 + i, vl),
__riscv_vle64_v_i64m2(src3 + i, vl)
);
__riscv_vssseg4e64_v_i64m2x4(dst + k + i * cn, cn * sizeof(int64), seg, vl);
}
}
}
#if defined(__clang__)
#pragma clang loop vectorize(disable)
#endif
for( ; k < cn; k += 4 )
{
const int64 *src0 = src[k], *src1 = src[k+1], *src2 = src[k+2], *src3 = src[k+3];
for( i = 0, j = k; i < len; i++, j += cn )
for (; k < cn; ++k)
{
dst[j] = src0[i]; dst[j+1] = src1[i];
dst[j+2] = src2[i]; dst[j+3] = src3[i];
const int64* srcK = src[k];
for (int i = 0; i < len; i += vl)
{
vl = __riscv_vsetvl_e64m2(len - i);
vint64m2_t seg = __riscv_vle64_v_i64m2(srcK + i, vl);
__riscv_vsse64_v_i64m2(dst + k + i * cn, cn * sizeof(int64), seg, vl);
}
}
}
return CV_HAL_ERROR_OK;
}
#endif // CV_HAL_RVV_1P0_ENABLED
}}} // cv::rvv_hal::core
+84 -76
View File
@@ -8,84 +8,92 @@ namespace cv { namespace rvv_hal { namespace core {
#if CV_HAL_RVV_1P0_ENABLED
int split8u(const uchar* src, uchar** dst, int len, int cn)
{
int vl = 0;
if (cn == 1)
{
uchar* dst0 = dst[0];
for (int i = 0; i < len; i += vl)
{
vl = __riscv_vsetvl_e8m8(len - i);
__riscv_vse8_v_u8m8(dst0 + i, __riscv_vle8_v_u8m8(src + i, vl), vl);
}
}
else if (cn == 2)
{
uchar *dst0 = dst[0], *dst1 = dst[1];
for (int i = 0; i < len; i += vl)
{
vl = __riscv_vsetvl_e8m4(len - i);
vuint8m4x2_t seg = __riscv_vlseg2e8_v_u8m4x2(src + i * cn, vl);
__riscv_vse8_v_u8m4(dst0 + i, __riscv_vget_v_u8m4x2_u8m4(seg, 0), vl);
__riscv_vse8_v_u8m4(dst1 + i, __riscv_vget_v_u8m4x2_u8m4(seg, 1), vl);
}
}
else if (cn == 3)
{
uchar *dst0 = dst[0], *dst1 = dst[1], *dst2 = dst[2];
for (int i = 0; i < len; i += vl)
{
vl = __riscv_vsetvl_e8m2(len - i);
vuint8m2x3_t seg = __riscv_vlseg3e8_v_u8m2x3(src + i * cn, vl);
__riscv_vse8_v_u8m2(dst0 + i, __riscv_vget_v_u8m2x3_u8m2(seg, 0), vl);
__riscv_vse8_v_u8m2(dst1 + i, __riscv_vget_v_u8m2x3_u8m2(seg, 1), vl);
__riscv_vse8_v_u8m2(dst2 + i, __riscv_vget_v_u8m2x3_u8m2(seg, 2), vl);
}
}
else if (cn == 4)
{
uchar *dst0 = dst[0], *dst1 = dst[1], *dst2 = dst[2], *dst3 = dst[3];
for (int i = 0; i < len; i += vl)
{
vl = __riscv_vsetvl_e8m2(len - i);
vuint8m2x4_t seg = __riscv_vlseg4e8_v_u8m2x4(src + i * cn, vl);
__riscv_vse8_v_u8m2(dst0 + i, __riscv_vget_v_u8m2x4_u8m2(seg, 0), vl);
__riscv_vse8_v_u8m2(dst1 + i, __riscv_vget_v_u8m2x4_u8m2(seg, 1), vl);
__riscv_vse8_v_u8m2(dst2 + i, __riscv_vget_v_u8m2x4_u8m2(seg, 2), vl);
__riscv_vse8_v_u8m2(dst3 + i, __riscv_vget_v_u8m2x4_u8m2(seg, 3), vl);
}
}
else
{
int k = 0;
for (; k <= cn - 4; k += 4)
{
uchar *dst0 = dst[k], *dst1 = dst[k + 1], *dst2 = dst[k + 2], *dst3 = dst[k + 3];
for (int i = 0; i < len; i += vl)
{
vl = __riscv_vsetvl_e8m2(len - i);
vuint8m2x4_t seg = __riscv_vlsseg4e8_v_u8m2x4(src + k + i * cn, cn, vl);
__riscv_vse8_v_u8m2(dst0 + i, __riscv_vget_v_u8m2x4_u8m2(seg, 0), vl);
__riscv_vse8_v_u8m2(dst1 + i, __riscv_vget_v_u8m2x4_u8m2(seg, 1), vl);
__riscv_vse8_v_u8m2(dst2 + i, __riscv_vget_v_u8m2x4_u8m2(seg, 2), vl);
__riscv_vse8_v_u8m2(dst3 + i, __riscv_vget_v_u8m2x4_u8m2(seg, 3), vl);
}
}
for (; k < cn; ++k)
{
uchar* dstK = dst[k];
for (int i = 0; i < len; i += vl)
{
vl = __riscv_vsetvl_e8m2(len - i);
vuint8m2_t seg = __riscv_vlse8_v_u8m2(src + k + i * cn, cn, vl);
__riscv_vse8_v_u8m2(dstK + i, seg, vl);
}
}
}
return CV_HAL_ERROR_OK;
#define OPENCV_HAL_IMPL_RVV_SPLIT(func, T, vtype, suffix, width) \
int func(const T* src, T** dst, int len, int cn) \
{ \
int vl = 0; \
if (cn == 1) \
{ \
T* dst0 = dst[0]; \
for (int i = 0; i < len; i += vl) \
{ \
vl = __riscv_vsetvl_e##width##m8(len - i); \
__riscv_vse##width##_v_##suffix##m8(dst0 + i, __riscv_vle##width##_v_##suffix##m8(src + i, vl), vl); \
} \
} \
else if (cn == 2) \
{ \
T *dst0 = dst[0], *dst1 = dst[1]; \
for (int i = 0; i < len; i += vl) \
{ \
vl = __riscv_vsetvl_e##width##m4(len - i); \
v##vtype##m4x2_t seg = __riscv_vlseg2e##width##_v_##suffix##m4x2(src + i * cn, vl); \
__riscv_vse##width##_v_##suffix##m4(dst0 + i, __riscv_vget_v_##suffix##m4x2_##suffix##m4(seg, 0), vl); \
__riscv_vse##width##_v_##suffix##m4(dst1 + i, __riscv_vget_v_##suffix##m4x2_##suffix##m4(seg, 1), vl); \
} \
} \
else if (cn == 3) \
{ \
T *dst0 = dst[0], *dst1 = dst[1], *dst2 = dst[2]; \
for (int i = 0; i < len; i += vl) \
{ \
vl = __riscv_vsetvl_e##width##m2(len - i); \
v##vtype##m2x3_t seg = __riscv_vlseg3e##width##_v_##suffix##m2x3(src + i * cn, vl); \
__riscv_vse##width##_v_##suffix##m2(dst0 + i, __riscv_vget_v_##suffix##m2x3_##suffix##m2(seg, 0), vl); \
__riscv_vse##width##_v_##suffix##m2(dst1 + i, __riscv_vget_v_##suffix##m2x3_##suffix##m2(seg, 1), vl); \
__riscv_vse##width##_v_##suffix##m2(dst2 + i, __riscv_vget_v_##suffix##m2x3_##suffix##m2(seg, 2), vl); \
} \
} \
else if (cn == 4) \
{ \
T *dst0 = dst[0], *dst1 = dst[1], *dst2 = dst[2], *dst3 = dst[3]; \
for (int i = 0; i < len; i += vl) \
{ \
vl = __riscv_vsetvl_e##width##m2(len - i); \
v##vtype##m2x4_t seg = __riscv_vlseg4e##width##_v_##suffix##m2x4(src + i * cn, vl); \
__riscv_vse##width##_v_##suffix##m2(dst0 + i, __riscv_vget_v_##suffix##m2x4_##suffix##m2(seg, 0), vl); \
__riscv_vse##width##_v_##suffix##m2(dst1 + i, __riscv_vget_v_##suffix##m2x4_##suffix##m2(seg, 1), vl); \
__riscv_vse##width##_v_##suffix##m2(dst2 + i, __riscv_vget_v_##suffix##m2x4_##suffix##m2(seg, 2), vl); \
__riscv_vse##width##_v_##suffix##m2(dst3 + i, __riscv_vget_v_##suffix##m2x4_##suffix##m2(seg, 3), vl); \
} \
} \
else \
{ \
int k = 0; \
for (; k <= cn - 4; k += 4) \
{ \
T *dst0 = dst[k], *dst1 = dst[k + 1], *dst2 = dst[k + 2], *dst3 = dst[k + 3]; \
for (int i = 0; i < len; i += vl) \
{ \
vl = __riscv_vsetvl_e##width##m2(len - i); \
v##vtype##m2x4_t seg = __riscv_vlsseg4e##width##_v_##suffix##m2x4(src + k + i * cn, cn * sizeof(T), vl); \
__riscv_vse##width##_v_##suffix##m2(dst0 + i, __riscv_vget_v_##suffix##m2x4_##suffix##m2(seg, 0), vl); \
__riscv_vse##width##_v_##suffix##m2(dst1 + i, __riscv_vget_v_##suffix##m2x4_##suffix##m2(seg, 1), vl); \
__riscv_vse##width##_v_##suffix##m2(dst2 + i, __riscv_vget_v_##suffix##m2x4_##suffix##m2(seg, 2), vl); \
__riscv_vse##width##_v_##suffix##m2(dst3 + i, __riscv_vget_v_##suffix##m2x4_##suffix##m2(seg, 3), vl); \
} \
} \
for (; k < cn; ++k) \
{ \
T* dstK = dst[k]; \
for (int i = 0; i < len; i += vl) \
{ \
vl = __riscv_vsetvl_e##width##m2(len - i); \
v##vtype##m2_t seg = __riscv_vlse##width##_v_##suffix##m2(src + k + i * cn, cn * sizeof(T), vl); \
__riscv_vse##width##_v_##suffix##m2(dstK + i, seg, vl); \
} \
} \
} \
return CV_HAL_ERROR_OK; \
}
OPENCV_HAL_IMPL_RVV_SPLIT(split8u, uchar, uint8, u8, 8)
OPENCV_HAL_IMPL_RVV_SPLIT(split16u, ushort, uint16, u16, 16)
OPENCV_HAL_IMPL_RVV_SPLIT(split32s, int, int32, i32, 32)
OPENCV_HAL_IMPL_RVV_SPLIT(split64s, int64, int64, i64, 64)
#undef OPENCV_HAL_IMPL_RVV_SPLIT
#endif // CV_HAL_RVV_1P0_ENABLED
}}} // cv::rvv_hal::core
+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));
+179
View File
@@ -102,6 +102,8 @@ template<> struct rvv<RVV_U8M1>
{
static inline vfloat32m4_t vcvt0(vuint8m1_t a, size_t b) { return __riscv_vfcvt_f(__riscv_vzext_vf4(a, b), b); }
static inline vuint8m1_t vcvt1(vfloat32m4_t a, size_t b) { return __riscv_vnclipu(__riscv_vfncvt_xu(a, b), 0, __RISCV_VXRM_RNU, b); }
static inline vint32m4_t vwiden(vuint8m1_t a, size_t b) { return __riscv_vreinterpret_v_u32m4_i32m4(__riscv_vzext_vf4(a, b)); }
static inline vuint8m1_t vnarrow(vint32m4_t a, size_t b) { return __riscv_vncvt_x(__riscv_vncvt_x(__riscv_vreinterpret_v_i32m4_u32m4(a), b), b); }
static inline vuint8m1_t vloxei(const uchar* a, vuint16m2_t b, size_t c) { return __riscv_vloxei16_v_u8m1(a, b, c); }
static inline void vloxseg2ei(const uchar* a, vuint16m2_t b, size_t c, vuint8m1_t& x, vuint8m1_t& y) { auto src = __riscv_vloxseg2ei16_v_u8m1x2(a, b, c); x = __riscv_vget_v_u8m1x2_u8m1(src, 0); y = __riscv_vget_v_u8m1x2_u8m1(src, 1); }
static inline void vloxseg3ei(const uchar* a, vuint16m2_t b, size_t c, vuint8m1_t& x, vuint8m1_t& y, vuint8m1_t& z) { auto src = __riscv_vloxseg3ei16_v_u8m1x3(a, b, c); x = __riscv_vget_v_u8m1x3_u8m1(src, 0); y = __riscv_vget_v_u8m1x3_u8m1(src, 1); z = __riscv_vget_v_u8m1x3_u8m1(src, 2); }
@@ -148,6 +150,8 @@ template<> struct rvv<RVV_U8MF2>
{
static inline vfloat32m2_t vcvt0(vuint8mf2_t a, size_t b) { return __riscv_vfcvt_f(__riscv_vzext_vf4(a, b), b); }
static inline vuint8mf2_t vcvt1(vfloat32m2_t a, size_t b) { return __riscv_vnclipu(__riscv_vfncvt_xu(a, b), 0, __RISCV_VXRM_RNU, b); }
static inline vint32m2_t vwiden(vuint8mf2_t a, size_t b) { return __riscv_vreinterpret_v_u32m2_i32m2(__riscv_vzext_vf4(a, b)); }
static inline vuint8mf2_t vnarrow(vint32m2_t a, size_t b) { return __riscv_vncvt_x(__riscv_vncvt_x(__riscv_vreinterpret_v_i32m2_u32m2(a), b), b); }
static inline vuint8mf2_t vloxei(const uchar* a, vuint16m1_t b, size_t c) { return __riscv_vloxei16_v_u8mf2(a, b, c); }
static inline void vloxseg2ei(const uchar* a, vuint16m1_t b, size_t c, vuint8mf2_t& x, vuint8mf2_t& y) { auto src = __riscv_vloxseg2ei16_v_u8mf2x2(a, b, c); x = __riscv_vget_v_u8mf2x2_u8mf2(src, 0); y = __riscv_vget_v_u8mf2x2_u8mf2(src, 1); }
static inline void vloxseg3ei(const uchar* a, vuint16m1_t b, size_t c, vuint8mf2_t& x, vuint8mf2_t& y, vuint8mf2_t& z) { auto src = __riscv_vloxseg3ei16_v_u8mf2x3(a, b, c); x = __riscv_vget_v_u8mf2x3_u8mf2(src, 0); y = __riscv_vget_v_u8mf2x3_u8mf2(src, 1); z = __riscv_vget_v_u8mf2x3_u8mf2(src, 2); }
@@ -315,6 +319,150 @@ static inline int resizeLinear(int start, int end, const uchar *src_data, size_t
return CV_HAL_ERROR_OK;
}
static const int INTER_RESIZE_COEF_BITS = 11;
static const int INTER_RESIZE_COEF_SCALE = 1 << INTER_RESIZE_COEF_BITS;
// Bit-exact fixed-point vertical+horizontal linear combine for one channel, matching the reference
// HResizeLinear<uchar,int,short,INTER_RESIZE_COEF_SCALE> + VResizeLinear<uchar,int,short,
// FixedPtCast<int,uchar,INTER_RESIZE_COEF_BITS*2>> in modules/imgproc/src/resize.cpp.
// Sxy are the four widened (int32) source corners; a0/a1 the horizontal weights (per column),
// b0/b1 the vertical weights (per row). The staged >>4 / >>16 / >>2 shifts (total 22) reproduce the
// reference exactly and keep the intermediates within int32.
template<typename VI>
static inline VI resizeLinearU8Combine(VI S00, VI S01, VI S10, VI S11, VI a0, VI a1, int b0, int b1, size_t vl)
{
VI h0 = __riscv_vmacc(__riscv_vmul(S00, a0, vl), S01, a1, vl); // S[y0][x0]*a0 + S[y0][x1]*a1
VI h1 = __riscv_vmacc(__riscv_vmul(S10, a0, vl), S11, a1, vl); // S[y1][x0]*a0 + S[y1][x1]*a1
h0 = __riscv_vsra(__riscv_vmul(__riscv_vsra(h0, 4, vl), b0, vl), 16, vl); // (b0*(h0>>4))>>16
h1 = __riscv_vsra(__riscv_vmul(__riscv_vsra(h1, 4, vl), b1, vl), 16, vl); // (b1*(h1>>4))>>16
return __riscv_vsra(__riscv_vadd(__riscv_vadd(h0, h1, vl), 2, vl), 2, vl); // (t0 + t1 + 2) >> 2
}
// the algorithm is copied from imgproc/src/resize.cpp, HResizeLinear + VResizeLinear for uchar.
// The generic resizeLinear<> above computes in float, which is bit-exact only for 16U/32F; 8U
// INTER_LINEAR must use this fixed-point path to match the reference (float drift breaks tight
// consumers such as DNN blobFromImage preprocessing, issue #28870).
template<typename helper, int cn>
static inline int resizeLinearU8(int start, int end, const uchar *src_data, size_t src_step, int src_height, uchar *dst_data, size_t dst_step, int dst_width, double scale_y, const ushort* x_ofs0, const ushort* x_ofs1, const int* ialpha0, const int* ialpha1)
{
for (int i = start; i < end; i++)
{
float my = (i + 0.5) * scale_y - 0.5;
int y_ofs = static_cast<int>(std::floor(my));
my -= y_ofs;
int y_ofs0 = std::min(std::max(y_ofs , 0), src_height - 1);
int y_ofs1 = std::min(std::max(y_ofs + 1, 0), src_height - 1);
int b0 = saturate_cast<short>((1.f - my) * INTER_RESIZE_COEF_SCALE);
int b1 = saturate_cast<short>(my * INTER_RESIZE_COEF_SCALE);
const uchar* S0 = src_data + y_ofs0 * src_step;
const uchar* S1 = src_data + y_ofs1 * src_step;
int vl;
switch (cn)
{
case 1:
for (int j = 0; j < dst_width; j += vl)
{
vl = helper::setvl(dst_width - j);
auto ptr0 = RVV_SameLen<ushort, helper>::vload(x_ofs0 + j, vl);
auto ptr1 = RVV_SameLen<ushort, helper>::vload(x_ofs1 + j, vl);
auto a0 = RVV_SameLen<int, helper>::vload(ialpha0 + j, vl);
auto a1 = RVV_SameLen<int, helper>::vload(ialpha1 + j, vl);
auto v00 = rvv<helper>::vwiden(rvv<helper>::vloxei(S0, ptr0, vl), vl);
auto v01 = rvv<helper>::vwiden(rvv<helper>::vloxei(S0, ptr1, vl), vl);
auto v02 = rvv<helper>::vwiden(rvv<helper>::vloxei(S1, ptr0, vl), vl);
auto v03 = rvv<helper>::vwiden(rvv<helper>::vloxei(S1, ptr1, vl), vl);
auto d0 = resizeLinearU8Combine(v00, v01, v02, v03, a0, a1, b0, b1, vl);
helper::vstore(reinterpret_cast<typename helper::ElemType*>(dst_data + i * dst_step) + j, rvv<helper>::vnarrow(d0, vl), vl);
}
break;
case 2:
for (int j = 0; j < dst_width; j += vl)
{
vl = helper::setvl(dst_width - j);
auto ptr0 = RVV_SameLen<ushort, helper>::vload(x_ofs0 + j, vl);
auto ptr1 = RVV_SameLen<ushort, helper>::vload(x_ofs1 + j, vl);
auto a0 = RVV_SameLen<int, helper>::vload(ialpha0 + j, vl);
auto a1 = RVV_SameLen<int, helper>::vload(ialpha1 + j, vl);
typename helper::VecType s0, s1;
rvv<helper>::vloxseg2ei(S0, ptr0, vl, s0, s1);
auto v00 = rvv<helper>::vwiden(s0, vl), v10 = rvv<helper>::vwiden(s1, vl);
rvv<helper>::vloxseg2ei(S0, ptr1, vl, s0, s1);
auto v01 = rvv<helper>::vwiden(s0, vl), v11 = rvv<helper>::vwiden(s1, vl);
rvv<helper>::vloxseg2ei(S1, ptr0, vl, s0, s1);
auto v02 = rvv<helper>::vwiden(s0, vl), v12 = rvv<helper>::vwiden(s1, vl);
rvv<helper>::vloxseg2ei(S1, ptr1, vl, s0, s1);
auto v03 = rvv<helper>::vwiden(s0, vl), v13 = rvv<helper>::vwiden(s1, vl);
auto d0 = resizeLinearU8Combine(v00, v01, v02, v03, a0, a1, b0, b1, vl);
auto d1 = resizeLinearU8Combine(v10, v11, v12, v13, a0, a1, b0, b1, vl);
rvv<helper>::vsseg2e(reinterpret_cast<typename helper::ElemType*>(dst_data + i * dst_step) + j * 2, vl, rvv<helper>::vnarrow(d0, vl), rvv<helper>::vnarrow(d1, vl));
}
break;
case 3:
for (int j = 0; j < dst_width; j += vl)
{
vl = helper::setvl(dst_width - j);
auto ptr0 = RVV_SameLen<ushort, helper>::vload(x_ofs0 + j, vl);
auto ptr1 = RVV_SameLen<ushort, helper>::vload(x_ofs1 + j, vl);
auto a0 = RVV_SameLen<int, helper>::vload(ialpha0 + j, vl);
auto a1 = RVV_SameLen<int, helper>::vload(ialpha1 + j, vl);
typename helper::VecType s0, s1, s2;
rvv<helper>::vloxseg3ei(S0, ptr0, vl, s0, s1, s2);
auto v00 = rvv<helper>::vwiden(s0, vl), v10 = rvv<helper>::vwiden(s1, vl), v20 = rvv<helper>::vwiden(s2, vl);
rvv<helper>::vloxseg3ei(S0, ptr1, vl, s0, s1, s2);
auto v01 = rvv<helper>::vwiden(s0, vl), v11 = rvv<helper>::vwiden(s1, vl), v21 = rvv<helper>::vwiden(s2, vl);
rvv<helper>::vloxseg3ei(S1, ptr0, vl, s0, s1, s2);
auto v02 = rvv<helper>::vwiden(s0, vl), v12 = rvv<helper>::vwiden(s1, vl), v22 = rvv<helper>::vwiden(s2, vl);
rvv<helper>::vloxseg3ei(S1, ptr1, vl, s0, s1, s2);
auto v03 = rvv<helper>::vwiden(s0, vl), v13 = rvv<helper>::vwiden(s1, vl), v23 = rvv<helper>::vwiden(s2, vl);
auto d0 = resizeLinearU8Combine(v00, v01, v02, v03, a0, a1, b0, b1, vl);
auto d1 = resizeLinearU8Combine(v10, v11, v12, v13, a0, a1, b0, b1, vl);
auto d2 = resizeLinearU8Combine(v20, v21, v22, v23, a0, a1, b0, b1, vl);
rvv<helper>::vsseg3e(reinterpret_cast<typename helper::ElemType*>(dst_data + i * dst_step) + j * 3, vl, rvv<helper>::vnarrow(d0, vl), rvv<helper>::vnarrow(d1, vl), rvv<helper>::vnarrow(d2, vl));
}
break;
case 4:
for (int j = 0; j < dst_width; j += vl)
{
vl = helper::setvl(dst_width - j);
auto ptr0 = RVV_SameLen<ushort, helper>::vload(x_ofs0 + j, vl);
auto ptr1 = RVV_SameLen<ushort, helper>::vload(x_ofs1 + j, vl);
auto a0 = RVV_SameLen<int, helper>::vload(ialpha0 + j, vl);
auto a1 = RVV_SameLen<int, helper>::vload(ialpha1 + j, vl);
typename helper::VecType s0, s1, s2, s3;
rvv<helper>::vloxseg4ei(S0, ptr0, vl, s0, s1, s2, s3);
auto v00 = rvv<helper>::vwiden(s0, vl), v10 = rvv<helper>::vwiden(s1, vl), v20 = rvv<helper>::vwiden(s2, vl), v30 = rvv<helper>::vwiden(s3, vl);
rvv<helper>::vloxseg4ei(S0, ptr1, vl, s0, s1, s2, s3);
auto v01 = rvv<helper>::vwiden(s0, vl), v11 = rvv<helper>::vwiden(s1, vl), v21 = rvv<helper>::vwiden(s2, vl), v31 = rvv<helper>::vwiden(s3, vl);
rvv<helper>::vloxseg4ei(S1, ptr0, vl, s0, s1, s2, s3);
auto v02 = rvv<helper>::vwiden(s0, vl), v12 = rvv<helper>::vwiden(s1, vl), v22 = rvv<helper>::vwiden(s2, vl), v32 = rvv<helper>::vwiden(s3, vl);
rvv<helper>::vloxseg4ei(S1, ptr1, vl, s0, s1, s2, s3);
auto v03 = rvv<helper>::vwiden(s0, vl), v13 = rvv<helper>::vwiden(s1, vl), v23 = rvv<helper>::vwiden(s2, vl), v33 = rvv<helper>::vwiden(s3, vl);
auto d0 = resizeLinearU8Combine(v00, v01, v02, v03, a0, a1, b0, b1, vl);
auto d1 = resizeLinearU8Combine(v10, v11, v12, v13, a0, a1, b0, b1, vl);
auto d2 = resizeLinearU8Combine(v20, v21, v22, v23, a0, a1, b0, b1, vl);
auto d3 = resizeLinearU8Combine(v30, v31, v32, v33, a0, a1, b0, b1, vl);
rvv<helper>::vsseg4e(reinterpret_cast<typename helper::ElemType*>(dst_data + i * dst_step) + j * 4, vl, rvv<helper>::vnarrow(d0, vl), rvv<helper>::vnarrow(d1, vl), rvv<helper>::vnarrow(d2, vl), rvv<helper>::vnarrow(d3, vl));
}
break;
default:
return CV_HAL_ERROR_NOT_IMPLEMENTED;
}
}
return CV_HAL_ERROR_OK;
}
template<int cn>
static inline int resizeLinearExact(int start, int end, const uchar *src_data, size_t src_step, int src_height, uchar *dst_data, size_t dst_step, int dst_width, double scale_y, const ushort* x_ofs0, const ushort* x_ofs1, const ushort* x_val)
{
@@ -801,6 +949,37 @@ static inline int resizeLinear(int src_type, const uchar *src_data, size_t src_s
return invoke(dst_height, {resizeLinearExact<4>}, src_data, src_step, src_height, dst_data, dst_step, dst_width, scale_y, x_ofs0.data(), x_ofs1.data(), x_val.data());
}
}
else if (CV_MAT_DEPTH(src_type) == CV_8U)
{
// 8U INTER_LINEAR must be bit-exact fixed-point to match the reference implementation;
// computing it in float drifts and breaks tight consumers (e.g. DNN preprocessing, #28870).
std::vector<int> ialpha0(dst_width), ialpha1(dst_width);
for (int i = 0; i < dst_width; i++)
{
float fx = (float)((i + 0.5) * scale_x - 0.5);
int x_ofs = static_cast<int>(std::floor(fx));
fx -= x_ofs;
if (x_ofs < 0) { fx = 0; x_ofs = 0; }
if (x_ofs >= src_width - 1) { fx = 0; x_ofs = src_width - 1; }
x_ofs0[i] = static_cast<ushort>(x_ofs) * cn;
x_ofs1[i] = static_cast<ushort>(std::min(x_ofs + 1, src_width - 1)) * cn;
ialpha0[i] = saturate_cast<short>((1.f - fx) * INTER_RESIZE_COEF_SCALE);
ialpha1[i] = saturate_cast<short>(fx * INTER_RESIZE_COEF_SCALE);
}
switch (src_type)
{
case CV_8UC1:
return invoke(dst_height, {resizeLinearU8<RVV_U8M1, 1>}, src_data, src_step, src_height, dst_data, dst_step, dst_width, scale_y, x_ofs0.data(), x_ofs1.data(), ialpha0.data(), ialpha1.data());
case CV_8UC2:
return invoke(dst_height, {resizeLinearU8<RVV_U8M1, 2>}, src_data, src_step, src_height, dst_data, dst_step, dst_width, scale_y, x_ofs0.data(), x_ofs1.data(), ialpha0.data(), ialpha1.data());
case CV_8UC3:
return invoke(dst_height, {resizeLinearU8<RVV_U8MF2, 3>}, src_data, src_step, src_height, dst_data, dst_step, dst_width, scale_y, x_ofs0.data(), x_ofs1.data(), ialpha0.data(), ialpha1.data());
case CV_8UC4:
return invoke(dst_height, {resizeLinearU8<RVV_U8MF2, 4>}, src_data, src_step, src_height, dst_data, dst_step, dst_width, scale_y, x_ofs0.data(), x_ofs1.data(), ialpha0.data(), ialpha1.data());
}
}
else
{
std::vector<float> x_val(dst_width);
+2 -1
View File
@@ -881,7 +881,8 @@ CV_EXPORTS_W Vec3d RQDecomp3x3( InputArray src, OutputArray mtxR, OutputArray mt
@param projMatrix 3x4 input projection matrix P.
@param cameraMatrix Output 3x3 camera intrinsic matrix \f$\cameramatrix{A}\f$.
@param rotMatrix Output 3x3 external rotation matrix R.
@param transVect Output 4x1 translation vector T.
@param transVect Output 4x1 vector representing the camera position in homogeneous coordinates.
To obtain the translation vector, use t = -rotMatrix * transVect[:3].
@param rotMatrixX Optional 3x3 rotation matrix around x-axis.
@param rotMatrixY Optional 3x3 rotation matrix around y-axis.
@param rotMatrixZ Optional 3x3 rotation matrix around z-axis.
+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");
+7 -5
View File
@@ -1,19 +1,21 @@
set(the_description "The Core Functionality")
ocv_add_dispatched_file(mathfuncs_core SSE2 AVX AVX2 LASX)
ocv_add_dispatched_file(stat SSE4_2 AVX2 LASX)
ocv_add_dispatched_file(stat SSE4_2 AVX2 AVX512_SKX AVX512_ICL LASX)
ocv_add_dispatched_file(arithm SSE2 SSE4_1 AVX2 VSX3 LASX)
ocv_add_dispatched_file(convert SSE2 AVX2 VSX3 LASX)
ocv_add_dispatched_file(convert_scale SSE2 AVX2 LASX)
ocv_add_dispatched_file(count_non_zero SSE2 AVX2 LASX)
ocv_add_dispatched_file(count_non_zero SSE2 AVX2 AVX512_SKX AVX512_ICL LASX)
ocv_add_dispatched_file(has_non_zero SSE2 AVX2 LASX )
ocv_add_dispatched_file(matmul SSE2 SSE4_1 AVX2 AVX512_SKX NEON_DOTPROD LASX)
ocv_add_dispatched_file(mean SSE2 AVX2 LASX)
ocv_add_dispatched_file(mean SSE2 AVX2 AVX512_SKX AVX512_ICL LASX)
ocv_add_dispatched_file(merge SSE2 AVX2 LASX)
ocv_add_dispatched_file(minmax SSE2 SSE4_1 AVX2 AVX512_SKX AVX512_ICL LASX)
ocv_add_dispatched_file(split SSE2 AVX2 LASX)
ocv_add_dispatched_file(sum SSE2 AVX2 LASX)
ocv_add_dispatched_file(sum SSE2 AVX2 AVX512_SKX AVX512_ICL LASX)
ocv_add_dispatched_file(reduce SSE2 SSSE3 AVX2 NEON_DOTPROD)
ocv_add_dispatched_file(norm SSE2 SSE4_1 AVX AVX2 NEON_DOTPROD LASX)
ocv_add_dispatched_file(norm SSE2 SSE4_1 AVX AVX2 AVX512_SKX AVX512_ICL NEON_DOTPROD LASX)
ocv_add_dispatched_file(lut AVX512_ICL)
# dispatching for accuracy tests
ocv_add_dispatched_file_force_all(test_intrin128 TEST SSE2 SSE3 SSSE3 SSE4_1 SSE4_2 AVX FP16 AVX2 AVX512_SKX)
@@ -1598,6 +1598,20 @@ inline v_uint8x32 v256_lut(const uchar* tab, const int* idx) { return v_reinterp
inline v_uint8x32 v256_lut_pairs(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v256_lut_pairs((const schar *)tab, idx)); }
inline v_uint8x32 v256_lut_quads(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v256_lut_quads((const schar *)tab, idx)); }
inline v_uint8x32 v256_lut(const uchar* tab, const v_uint8x32& idx)
{
uchar CV_DECL_ALIGNED(32) indices[32], result[32];
_mm256_store_si256((__m256i*)indices, idx.val);
for (int i = 0; i < 32; i++) result[i] = tab[indices[i]];
return v_uint8x32(_mm256_load_si256((const __m256i*)result));
}
inline v_int8x32 v256_lut(const schar* tab, const v_uint8x32& idx)
{ return v_reinterpret_as_s8(v256_lut((const uchar *)tab, idx)); }
inline v_uint8x32 v_lut(const uchar* tab, const v_uint8x32& idx) { return v256_lut(tab, idx); }
inline v_int8x32 v_lut(const schar* tab, const v_uint8x32& idx) { return v256_lut(tab, idx); }
inline v_int16x16 v256_lut(const short* tab, const int* idx)
{
return v_int16x16(_mm256_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]],
@@ -1634,6 +1634,122 @@ inline v_uint8x64 v512_lut(const uchar* tab, const int* idx) { return v_reinterp
inline v_uint8x64 v512_lut_pairs(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v512_lut_pairs((const schar *)tab, idx)); }
inline v_uint8x64 v512_lut_quads(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v512_lut_quads((const schar *)tab, idx)); }
// Byte-indexed LUT: takes v_uint8x64 byte indices instead of int* indices
// Table must have at least 256 entries (byte indices cover 0-255)
// VBMI path uses vpermb (register-to-register), fallback uses gather
inline v_uint8x64 v512_lut(const uchar* tab, const v_uint8x64& idx)
{
#if CV_AVX_512VBMI
const __m512i lut0 = _mm512_loadu_si512(tab);
const __m512i lut1 = _mm512_loadu_si512(tab + 64);
const __m512i lut2 = _mm512_loadu_si512(tab + 128);
const __m512i lut3 = _mm512_loadu_si512(tab + 192);
__m512i idx6 = _mm512_and_si512(idx.val, _mm512_set1_epi8(0x3F));
__m512i r0 = _mm512_permutexvar_epi8(idx6, lut0);
__m512i r1 = _mm512_permutexvar_epi8(idx6, lut1);
__m512i r2 = _mm512_permutexvar_epi8(idx6, lut2);
__m512i r3 = _mm512_permutexvar_epi8(idx6, lut3);
__mmask64 k6 = _mm512_test_epi8_mask(idx.val, _mm512_set1_epi8(0x40));
__mmask64 k7 = _mm512_test_epi8_mask(idx.val, _mm512_set1_epi8((char)0x80));
__m512i low = _mm512_mask_blend_epi8(k6, r0, r1);
__m512i high = _mm512_mask_blend_epi8(k6, r2, r3);
return v_uint8x64(_mm512_mask_blend_epi8(k7, low, high));
#else
uchar CV_DECL_ALIGNED(64) indices[64], result[64];
_mm512_store_si512((__m512i*)indices, idx.val);
for (int i = 0; i < 64; i++) result[i] = tab[indices[i]];
return v_uint8x64(_mm512_load_si512((const __m512i*)result));
#endif
}
inline v_int8x64 v512_lut(const schar* tab, const v_uint8x64& idx)
{ return v_reinterpret_as_s8(v512_lut((const uchar *)tab, idx)); }
// Universal v_lut overloads for vector byte indices (aliases to v512_lut)
inline v_uint8x64 v_lut(const uchar* tab, const v_uint8x64& idx)
{ return v512_lut(tab, idx); }
inline v_int8x64 v_lut(const schar* tab, const v_uint8x64& idx)
{ return v512_lut(tab, idx); }
// Byte-indexed pair LUT: 32 byte indices (lower half of idx) → 32 pairs = 64 bytes
inline v_uint8x64 v512_lut_pairs(const uchar* tab, const v_uint8x64& idx)
{
#if CV_AVX_512VBMI
const __m512i lut0 = _mm512_loadu_si512(tab);
const __m512i lut1 = _mm512_loadu_si512(tab + 64);
const __m512i lut2 = _mm512_loadu_si512(tab + 128);
const __m512i lut3 = _mm512_loadu_si512(tab + 192);
// Expand 32 byte indices to interleaved pairs: (i0, i0+1, i1, i1+1, ...)
__m512i idx16 = _mm512_cvtepu8_epi16(_mm512_castsi512_si256(idx.val));
__m512i idx_dup = _mm512_or_si512(idx16, _mm512_slli_epi16(idx16, 8));
__m512i idx_pairs = _mm512_add_epi8(idx_dup, _mm512_set1_epi16(0x0100));
__m512i idx6 = _mm512_and_si512(idx_pairs, _mm512_set1_epi8(0x3F));
__m512i r0 = _mm512_permutexvar_epi8(idx6, lut0);
__m512i r1 = _mm512_permutexvar_epi8(idx6, lut1);
__m512i r2 = _mm512_permutexvar_epi8(idx6, lut2);
__m512i r3 = _mm512_permutexvar_epi8(idx6, lut3);
__mmask64 k6 = _mm512_test_epi8_mask(idx_pairs, _mm512_set1_epi8(0x40));
__mmask64 k7 = _mm512_test_epi8_mask(idx_pairs, _mm512_set1_epi8((char)0x80));
__m512i low = _mm512_mask_blend_epi8(k6, r0, r1);
__m512i high = _mm512_mask_blend_epi8(k6, r2, r3);
return v_uint8x64(_mm512_mask_blend_epi8(k7, low, high));
#else
uchar CV_DECL_ALIGNED(64) indices[64], result[64];
_mm512_store_si512((__m512i*)indices, idx.val);
for (int i = 0; i < 64; i++) result[i] = tab[indices[i]];
return v_uint8x64(_mm512_load_si512((const __m512i*)result));
#endif
}
inline v_int8x64 v512_lut_pairs(const schar* tab, const v_uint8x64& idx)
{ return v_reinterpret_as_s8(v512_lut_pairs((const uchar *)tab, idx)); }
// Byte-indexed quad LUT: 16 byte indices (lower 16 of idx) → 16 quads = 64 bytes
inline v_uint8x64 v512_lut_quads(const uchar* tab, const v_uint8x64& idx)
{
#if CV_AVX_512VBMI
const __m512i lut0 = _mm512_loadu_si512(tab);
const __m512i lut1 = _mm512_loadu_si512(tab + 64);
const __m512i lut2 = _mm512_loadu_si512(tab + 128);
const __m512i lut3 = _mm512_loadu_si512(tab + 192);
// Expand 16 byte indices to quad offsets: (i0, i0+1, i0+2, i0+3, i1, ...)
__m512i idx32 = _mm512_cvtepu8_epi32(_mm512_castsi512_si128(idx.val));
__m512i t1 = _mm512_or_si512(idx32, _mm512_slli_epi32(idx32, 8));
__m512i idx_bcast = _mm512_or_si512(t1, _mm512_slli_epi32(t1, 16));
__m512i idx_quads = _mm512_add_epi8(idx_bcast, _mm512_set1_epi32(0x03020100));
__m512i idx6 = _mm512_and_si512(idx_quads, _mm512_set1_epi8(0x3F));
__m512i r0 = _mm512_permutexvar_epi8(idx6, lut0);
__m512i r1 = _mm512_permutexvar_epi8(idx6, lut1);
__m512i r2 = _mm512_permutexvar_epi8(idx6, lut2);
__m512i r3 = _mm512_permutexvar_epi8(idx6, lut3);
__mmask64 k6 = _mm512_test_epi8_mask(idx_quads, _mm512_set1_epi8(0x40));
__mmask64 k7 = _mm512_test_epi8_mask(idx_quads, _mm512_set1_epi8((char)0x80));
__m512i low = _mm512_mask_blend_epi8(k6, r0, r1);
__m512i high = _mm512_mask_blend_epi8(k6, r2, r3);
return v_uint8x64(_mm512_mask_blend_epi8(k7, low, high));
#else
uchar CV_DECL_ALIGNED(64) indices[64], result[64];
_mm512_store_si512((__m512i*)indices, idx.val);
for (int i = 0; i < 64; i++) result[i] = tab[indices[i]];
return v_uint8x64(_mm512_load_si512((const __m512i*)result));
#endif
}
inline v_int8x64 v512_lut_quads(const schar* tab, const v_uint8x64& idx)
{ return v_reinterpret_as_s8(v512_lut_quads((const uchar *)tab, idx)); }
inline v_int16x32 v512_lut(const short* tab, const int* idx)
{
__m256i p0 = _mm512_cvtepi32_epi16(_mm512_i32gather_epi32(_mm512_loadu_si512((const __m512i*)idx ), (const int *)tab, 2));
@@ -14,6 +14,7 @@
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
// Copyright (C) 2015, Itseez Inc., all rights reserved.
// Copyright (C) 2026, Advanced Micro Devices, Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
@@ -2714,6 +2715,16 @@ template<typename _Tp> inline v_reg<_Tp, simd128_width / sizeof(_Tp)> v_lut_quad
return c;
}
template<int n> inline v_reg<uchar, n> v_lut(const uchar* tab, const v_reg<uchar, n>& idx)
{
v_reg<uchar, n> c;
for( int i = 0; i < n; i++ )
c.s[i] = tab[idx.s[i]];
return c;
}
template<int n> inline v_reg<schar, n> v_lut(const schar* tab, const v_reg<uchar, n>& idx)
{ return v_reinterpret_as_s8(v_lut((const uchar*)tab, idx)); }
template<int n> inline v_reg<int, n> v_lut(const int* tab, const v_reg<int, n>& idx)
{
v_reg<int, n> c;
@@ -1673,6 +1673,21 @@ inline v_uint8x32 v256_lut(const uchar* tab, const int* idx) { return v_reinterp
inline v_uint8x32 v256_lut_pairs(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v256_lut_pairs((const schar *)tab, idx)); }
inline v_uint8x32 v256_lut_quads(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v256_lut_quads((const schar *)tab, idx)); }
// Byte-indexed LUT: 32 byte indices in a vector -> 32 looked-up bytes
inline v_uint8x32 v256_lut(const uchar* tab, const v_uint8x32& idx)
{
uchar CV_DECL_ALIGNED(32) indices[32], result[32];
__lasx_xvst(idx.val, indices, 0);
for (int i = 0; i < 32; i++) result[i] = tab[indices[i]];
return v_uint8x32(__lasx_xvld(result, 0));
}
inline v_int8x32 v256_lut(const schar* tab, const v_uint8x32& idx)
{ return v_reinterpret_as_s8(v256_lut((const uchar*)tab, idx)); }
// Universal v_lut overloads for vector byte indices (aliases to v256_lut)
inline v_uint8x32 v_lut(const uchar* tab, const v_uint8x32& idx) { return v256_lut(tab, idx); }
inline v_int8x32 v_lut(const schar* tab, const v_uint8x32& idx) { return v256_lut(tab, idx); }
inline v_int16x16 v256_lut(const short* tab, const int* idx)
{
return v_int16x16(_v256_setr_h(tab[idx[ 0]], tab[idx[ 1]], tab[idx[ 2]], tab[idx[ 3]], tab[idx[ 4]],
@@ -1443,6 +1443,16 @@ inline v_uint8x16 v_lut_pairs(const uchar* tab, const int* idx)
inline v_uint8x16 v_lut_quads(const uchar* tab, const int* idx)
{ return v_reinterpret_as_u8(v_lut_quads((const schar*)tab, idx)); }
inline v_uint8x16 v_lut(const uchar* tab, const v_uint8x16& idx)
{
uchar CV_DECL_ALIGNED(16) indices[16], result[16];
__lsx_vst(idx.val, indices, 0);
for (int i = 0; i < 16; i++) result[i] = tab[indices[i]];
return v_uint8x16(__lsx_vld(result, 0));
}
inline v_int8x16 v_lut(const schar* tab, const v_uint8x16& idx)
{ return v_reinterpret_as_s8(v_lut((const uchar*)tab, idx)); }
inline v_int16x8 v_lut(const short* tab, const int* idx)
{
return v_int16x8(_v128_setr_h(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]],
@@ -1566,6 +1566,15 @@ inline v_uint8x16 v_lut(const uchar* tab, const int* idx) { return v_reinterpret
inline v_uint8x16 v_lut_pairs(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_pairs((schar*)tab, idx)); }
inline v_uint8x16 v_lut_quads(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_quads((schar*)tab, idx)); }
inline v_uint8x16 v_lut(const uchar* tab, const v_uint8x16& idx)
{
uchar CV_DECL_ALIGNED(16) indices[16], result[16];
msa_st1q_u8(indices, idx.val);
for (int i = 0; i < 16; i++) result[i] = tab[indices[i]];
return v_uint8x16(msa_ld1q_u8(result));
}
inline v_int8x16 v_lut(const schar* tab, const v_uint8x16& idx)
{ return v_reinterpret_as_s8(v_lut((const uchar*)tab, idx)); }
inline v_int16x8 v_lut(const short* tab, const int* idx)
{
@@ -14,6 +14,7 @@
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
// Copyright (C) 2015, Itseez Inc., all rights reserved.
// Copyright (C) 2026, Advanced Micro Devices, Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
@@ -2370,6 +2371,48 @@ inline v_uint8x16 v_lut(const uchar* tab, const int* idx) { return v_reinterpret
inline v_uint8x16 v_lut_pairs(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_pairs((schar*)tab, idx)); }
inline v_uint8x16 v_lut_quads(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_quads((schar*)tab, idx)); }
inline v_uint8x16 v_lut(const uchar* tab, const v_uint8x16& idx)
{
#if CV_NEON_AARCH64
uint8x16_t index = idx.val;
uint8x16_t idx6 = vandq_u8(index, vdupq_n_u8(0x3F));
uint8x16x4_t t0, t1, t2, t3;
t0.val[0] = vld1q_u8(tab); t0.val[1] = vld1q_u8(tab + 16);
t0.val[2] = vld1q_u8(tab + 32); t0.val[3] = vld1q_u8(tab + 48);
uint8x16_t r0 = vqtbl4q_u8(t0, idx6);
t1.val[0] = vld1q_u8(tab + 64); t1.val[1] = vld1q_u8(tab + 80);
t1.val[2] = vld1q_u8(tab + 96); t1.val[3] = vld1q_u8(tab + 112);
uint8x16_t r1 = vqtbl4q_u8(t1, idx6);
t2.val[0] = vld1q_u8(tab + 128); t2.val[1] = vld1q_u8(tab + 144);
t2.val[2] = vld1q_u8(tab + 160); t2.val[3] = vld1q_u8(tab + 176);
uint8x16_t r2 = vqtbl4q_u8(t2, idx6);
t3.val[0] = vld1q_u8(tab + 192); t3.val[1] = vld1q_u8(tab + 208);
t3.val[2] = vld1q_u8(tab + 224); t3.val[3] = vld1q_u8(tab + 240);
uint8x16_t r3 = vqtbl4q_u8(t3, idx6);
uint8x16_t bit6 = vtstq_u8(index, vdupq_n_u8(0x40));
uint8x16_t low = vbslq_u8(bit6, r1, r0);
uint8x16_t high = vbslq_u8(bit6, r3, r2);
uint8x16_t bit7 = vtstq_u8(index, vdupq_n_u8(0x80));
return v_uint8x16(vbslq_u8(bit7, high, low));
#else
uchar CV_DECL_ALIGNED(16) indices[16], result[16];
vst1q_u8(indices, idx.val);
for (int i = 0; i < 16; i++) result[i] = tab[indices[i]];
return v_uint8x16(vld1q_u8(result));
#endif
}
inline v_int8x16 v_lut(const schar* tab, const v_uint8x16& idx)
{
return v_reinterpret_as_s8(v_lut((const uchar*)tab, idx));
}
inline v_int16x8 v_lut(const short* tab, const int* idx)
{
short CV_DECL_ALIGNED(32) elems[8] =
@@ -3,6 +3,7 @@
// of this distribution and at http://opencv.org/license.html
// Copyright (C) 2015, PingTouGe Semiconductor Co., Ltd., all rights reserved.
// Copyright (C) 2026, Advanced Micro Devices, Inc., all rights reserved.
#ifndef OPENCV_HAL_INTRIN_RISCVV_HPP
#define OPENCV_HAL_INTRIN_RISCVV_HPP
@@ -1625,6 +1626,16 @@ inline v_uint8x16 v_lut(const uchar* tab, const int* idx) { return v_reinterpret
inline v_uint8x16 v_lut_pairs(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_pairs((schar*)tab, idx)); }
inline v_uint8x16 v_lut_quads(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_quads((schar*)tab, idx)); }
inline v_uint8x16 v_lut(const uchar* tab, const v_uint8x16& idx)
{
uchar CV_DECL_ALIGNED(16) indices[16], result[16];
vse8_v_u8m1(indices, idx.val, 16);
for (int i = 0; i < 16; i++) result[i] = tab[indices[i]];
return v_uint8x16(vle8_v_u8m1(result, 16));
}
inline v_int8x16 v_lut(const schar* tab, const v_uint8x16& idx)
{ return v_reinterpret_as_s8(v_lut((const uchar*)tab, idx)); }
inline v_int16x8 v_lut(const short* tab, const int* idx)
{
#if 0
@@ -4,6 +4,7 @@
// The original implementation is contributed by HAN Liutong.
// Copyright (C) 2022, Institute of Software, Chinese Academy of Sciences.
// Copyright (C) 2026, Advanced Micro Devices, Inc., all rights reserved.
#ifndef OPENCV_HAL_INTRIN_RVV_SCALABLE_HPP
#define OPENCV_HAL_INTRIN_RVV_SCALABLE_HPP
@@ -534,6 +535,13 @@ inline void v_lut_deinterleave(const double* tab, const v_int32& vidx, v_float64
inline v_uint8 v_lut(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut((schar*)tab, idx)); }
inline v_uint8 v_lut_pairs(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_pairs((schar*)tab, idx)); }
inline v_uint8 v_lut_quads(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_quads((schar*)tab, idx)); }
// Byte-indexed LUT: vector byte indices -> looked-up bytes (uses RVV indexed load)
inline v_uint8 v_lut(const uchar* tab, const v_uint8& idx)
{ return __riscv_vluxei8_v_u8m2(tab, idx, VTraits<v_uint8>::vlanes()); }
inline v_int8 v_lut(const schar* tab, const v_uint8& idx)
{ return v_reinterpret_as_s8(v_lut((const uchar*)tab, idx)); }
inline v_uint16 v_lut(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v_lut((short*)tab, idx)); }
inline v_uint16 v_lut_pairs(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v_lut_pairs((short*)tab, idx)); }
inline v_uint16 v_lut_quads(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v_lut_quads((short*)tab, idx)); }
@@ -931,7 +939,7 @@ inline scalartype v_reduce_sum(const _Tpvec& a) \
}
OPENCV_HAL_IMPL_RVV_REDUCE_SUM_FP(v_float32, v_float32, vfloat32m1_t, float, f32, VTraits<v_float32>::vlanes())
#if CV_SIMD_SCALABLE_64F
OPENCV_HAL_IMPL_RVV_REDUCE_SUM_FP(v_float64, v_float64, vfloat64m1_t, float, f64, VTraits<v_float64>::vlanes())
OPENCV_HAL_IMPL_RVV_REDUCE_SUM_FP(v_float64, v_float64, vfloat64m1_t, double, f64, VTraits<v_float64>::vlanes())
#endif
#define OPENCV_HAL_IMPL_RVV_REDUCE(_Tpvec, _nTpvec, func, scalartype, suffix, vl, red) \
@@ -14,6 +14,7 @@
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
// Copyright (C) 2015, Itseez Inc., all rights reserved.
// Copyright (C) 2026, Advanced Micro Devices, Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
@@ -3060,71 +3061,55 @@ 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)); }
inline v_uint8x16 v_lut_quads(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_quads((const schar *)tab, idx)); }
inline v_uint8x16 v_lut(const uchar* tab, const v_uint8x16& idx)
{
uchar CV_DECL_ALIGNED(16) indices[16], result[16];
_mm_store_si128((__m128i*)indices, idx.val);
for (int i = 0; i < 16; i++) result[i] = tab[indices[i]];
return v_uint8x16(_mm_load_si128((const __m128i*)result));
}
inline v_int8x16 v_lut(const schar* tab, const v_uint8x16& idx)
{ return v_reinterpret_as_s8(v_lut((const uchar*)tab, idx)); }
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 +3117,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)
{
@@ -1175,6 +1175,16 @@ inline v_uint8x16 v_lut(const uchar* tab, const int* idx) { return v_reinterpret
inline v_uint8x16 v_lut_pairs(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_pairs((const schar*)tab, idx)); }
inline v_uint8x16 v_lut_quads(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_quads((const schar*)tab, idx)); }
inline v_uint8x16 v_lut(const uchar* tab, const v_uint8x16& idx)
{
uchar CV_DECL_ALIGNED(16) indices[16], result[16];
vsx_st(idx.val, 0, indices);
for (int i = 0; i < 16; i++) result[i] = tab[indices[i]];
return v_uint8x16(vsx_ld(0, result));
}
inline v_int8x16 v_lut(const schar* tab, const v_uint8x16& idx)
{ return v_reinterpret_as_s8(v_lut((const uchar*)tab, idx)); }
inline v_int16x8 v_lut(const short* tab, const int* idx)
{
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]]);
@@ -2584,6 +2584,16 @@ inline v_uint8x16 v_lut(const uchar* tab, const int* idx) { return v_reinterpret
inline v_uint8x16 v_lut_pairs(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_pairs((const schar *)tab, idx)); }
inline v_uint8x16 v_lut_quads(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_quads((const schar *)tab, idx)); }
inline v_uint8x16 v_lut(const uchar* tab, const v_uint8x16& idx)
{
uchar CV_DECL_ALIGNED(16) indices[16], result[16];
wasm_v128_store(indices, idx.val);
for (int i = 0; i < 16; i++) result[i] = tab[indices[i]];
return v_uint8x16(wasm_v128_load(result));
}
inline v_int8x16 v_lut(const schar* tab, const v_uint8x16& idx)
{ return v_reinterpret_as_s8(v_lut((const uchar*)tab, idx)); }
inline v_int16x8 v_lut(const short* tab, const int* idx)
{
return v_int16x8(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]],
@@ -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_
{
@@ -211,7 +211,6 @@ T* allocSingletonNew() { return new(allocSingletonNewBuffer(sizeof(T))) T(); }
// Temporary disabled named IPP region. Performance
#define IPP_DISABLE_PERF_COPYMAKE 1 // performance variations
#define IPP_DISABLE_PERF_TRUE_DIST_MT 1 // cv::distanceTransform OpenCV MT performance is better
#define IPP_DISABLE_PERF_CANNY_MT 1 // cv::Canny OpenCV MT performance is better
#ifdef HAVE_IPP
#include "ippversion.h"
@@ -6,9 +6,9 @@
#define OPENCV_VERSION_HPP
#define CV_VERSION_MAJOR 4
#define CV_VERSION_MINOR 14
#define CV_VERSION_MINOR 15
#define CV_VERSION_REVISION 0
#define CV_VERSION_STATUS "-pre"
#define CV_VERSION_STATUS "-dev"
#define CVAUX_STR_EXP(__A) #__A
#define CVAUX_STR(__A) CVAUX_STR_EXP(__A)
+3
View File
@@ -1202,6 +1202,9 @@ typedef TestBaseWithParam<ReduceMinMaxParams> ReduceMinMaxFixture;
OCL_PERF_TEST_P(ReduceMinMaxFixture, Reduce,
::testing::Combine(OCL_TEST_SIZES,
OCL_PERF_ENUM(std::make_pair<MatType, MatType>(CV_8UC1, CV_8UC1),
std::make_pair<MatType, MatType>(CV_8UC4, CV_8UC4),
std::make_pair<MatType, MatType>(CV_32FC1, CV_32FC1),
std::make_pair<MatType, MatType>(CV_32FC3, CV_32FC3),
std::make_pair<MatType, MatType>(CV_32FC4, CV_32FC4)),
OCL_PERF_ENUM(0, 1),
ReduceMinMaxOp::all()))
+5 -2
View File
@@ -4,7 +4,7 @@ namespace opencv_test
{
using namespace perf;
#define TYPICAL_MAT_TYPES_ADWEIGHTED CV_8UC1, CV_8UC4, CV_8SC1, CV_16UC1, CV_16SC1, CV_32SC1
#define TYPICAL_MAT_TYPES_ADWEIGHTED CV_8UC1, CV_8UC4, CV_8SC1, CV_16UC1, CV_16SC1, CV_32SC1, CV_32FC1
#define TYPICAL_MATS_ADWEIGHTED testing::Combine(testing::Values(szVGA, sz720p, sz1080p), testing::Values(TYPICAL_MAT_TYPES_ADWEIGHTED))
PERF_TEST_P(Size_MatType, addWeighted, TYPICAL_MATS_ADWEIGHTED)
@@ -31,7 +31,10 @@ PERF_TEST_P(Size_MatType, addWeighted, TYPICAL_MATS_ADWEIGHTED)
TEST_CYCLE() cv::addWeighted( src1, alpha, src2, beta, gamma, dst, dst.type() );
SANITY_CHECK(dst, depth == CV_32S ? 4 : 1);
if (depth == CV_32F)
SANITY_CHECK(dst, 1e-4, ERROR_RELATIVE);
else
SANITY_CHECK(dst, depth == CV_32S ? 4 : 1);
}
} // namespace
+72 -7
View File
@@ -436,7 +436,22 @@ PERF_TEST_P_(BinaryOpTest, transpose2d)
SANITY_CHECK_NOTHING();
}
PERF_TEST_P_(BinaryOpTest, transposeND)
static cv::Mat makeTransposeNDOutput(const cv::Mat& src, const std::vector<int>& order)
{
std::vector<int> new_sz(order.size());
for (size_t i = 0; i < order.size(); ++i)
new_sz[i] = src.size[order[i]];
return Mat(static_cast<int>(new_sz.size()), new_sz.data(), src.type());
}
static cv::Mat makeTransposeNDInput3D(Size sz, int type)
{
const int channels = CV_MAT_CN(type);
int dims[] = { sz.height, sz.width, channels };
return Mat(3, dims, CV_MAKETYPE(CV_MAT_DEPTH(type), 1));
}
PERF_TEST_P_(BinaryOpTest, transposeND_identity_order)
{
Size sz = get<0>(GetParam());
int type = get<1>(GetParam());
@@ -444,14 +459,64 @@ PERF_TEST_P_(BinaryOpTest, transposeND)
std::vector<int> order(a.dims);
std::iota(order.begin(), order.end(), 0);
std::reverse(order.begin(), order.end());
std::vector<int> new_sz(a.dims);
std::copy(a.size.p, a.size.p + a.dims, new_sz.begin());
std::reverse(new_sz.begin(), new_sz.end());
cv::Mat b = Mat(new_sz, type);
cv::Mat b = makeTransposeNDOutput(a, order);
declare.in(a,WARMUP_RNG).out(b);
declare.in(a, WARMUP_RNG).out(b);
TEST_CYCLE() cv::transposeND(a, order, b);
SANITY_CHECK_NOTHING();
}
PERF_TEST_P_(BinaryOpTest, transposeND_2d_swap_order)
{
Size sz = get<0>(GetParam());
int type = get<1>(GetParam());
cv::Mat a = Mat(sz, type).reshape(1);
std::vector<int> order{1, 0};
cv::Mat b = makeTransposeNDOutput(a, order);
declare.in(a, WARMUP_RNG).out(b);
TEST_CYCLE() cv::transposeND(a, order, b);
SANITY_CHECK_NOTHING();
}
PERF_TEST_P_(BinaryOpTest, transposeND_generic_keep_tail_order)
{
Size sz = get<0>(GetParam());
int type = get<1>(GetParam());
cv::Mat a = makeTransposeNDInput3D(sz, type);
std::vector<int> order{1, 0, 2};
cv::Mat b = makeTransposeNDOutput(a, order);
randu(a, 0, 255);
declare.in(a).out(b);
TEST_CYCLE() cv::transposeND(a, order, b);
SANITY_CHECK_NOTHING();
}
PERF_TEST_P_(BinaryOpTest, transposeND_generic_move_tail_order)
{
Size sz = get<0>(GetParam());
int type = get<1>(GetParam());
cv::Mat a = makeTransposeNDInput3D(sz, type);
std::vector<int> order{2, 0, 1};
cv::Mat b = makeTransposeNDOutput(a, order);
randu(a, 0, 255);
declare.in(a).out(b);
TEST_CYCLE() cv::transposeND(a, order, b);
+117
View File
@@ -0,0 +1,117 @@
#include "perf_precomp.hpp"
namespace opencv_test
{
using namespace perf;
CV_FLAGS(GemmFlag, 0, GEMM_1_T, GEMM_2_T, GEMM_3_T)
typedef tuple<tuple<int, int, int>, MatType, GemmFlag> GemmTestParams_t;
class GemmTest : public perf::TestBaseWithParam<GemmTestParams_t>
{
public:
void runGemmTest(const GemmTestParams_t& params)
{
int M = get<0>(get<0>(params));
int N = get<1>(get<0>(params));
int K = get<2>(get<0>(params));
int type = get<1>(params);
int flags = get<2>(params);
Size aSize = (flags & GEMM_1_T) ? Size(M, K) : Size(K, M);
Size bSize = (flags & GEMM_2_T) ? Size(K, N) : Size(N, K);
Mat src1(aSize, type), src2(bSize, type), src3(M, N, type), dst(M, N, type);
declare.in(src1, src2, src3, WARMUP_RNG).out(dst);
TEST_CYCLE() cv::gemm(src1, src2, 1.0, src3, 1.0, dst, flags);
if (dst.total() * dst.channels() < 26)
SANITY_CHECK_NOTHING();
else
SANITY_CHECK(dst, (CV_MAT_DEPTH(type) == CV_32F) ? 1e-4 : 1e-6, ERROR_RELATIVE);
};
};
// Sparse coverage: exercise tiny/small/rectangular shapes and m=1/n=1 edge cases.
// Large square sizes (640+) are covered by opencl/perf_gemm.cpp on the CPU perf tool.
PERF_TEST_P(GemmTest, gemmTiny,
testing::Combine(
testing::Values(
make_tuple(2, 2, 2),
make_tuple(3, 3, 3)
),
testing::Values(CV_32FC1, CV_64FC1),
testing::Values(0, (int)GEMM_1_T, (int)GEMM_2_T,
(int)(GEMM_1_T | GEMM_2_T))
))
{
GemmTest::runGemmTest(GetParam());
}
PERF_TEST_P(GemmTest, gemmSmall,
testing::Combine(
testing::Values(
make_tuple(8, 8, 8),
make_tuple(32, 32, 32),
make_tuple(64, 64, 64),
make_tuple(8, 16, 32)
),
testing::Values(CV_32FC1),
testing::Values(0, (int)GEMM_2_T)
))
{
GemmTest::runGemmTest(GetParam());
}
PERF_TEST_P(GemmTest, gemmSquare,
testing::Combine(
testing::Values(
make_tuple(256, 256, 256),
make_tuple(512, 512, 512)
),
testing::Values(CV_32FC1),
testing::Values(0)
))
{
GemmTest::runGemmTest(GetParam());
}
PERF_TEST_P(GemmTest, gemmRect,
testing::Combine(
testing::Values(
make_tuple(1024, 64, 256),
make_tuple(256, 1024, 512)
),
testing::Values(CV_32FC1),
testing::Values(0)
))
{
GemmTest::runGemmTest(GetParam());
}
PERF_TEST_P(GemmTest, gemmM1,
testing::Combine(
testing::Values(
make_tuple(1, 64, 2500)
),
testing::Values(CV_32FC1),
testing::Values(0, (int)GEMM_1_T)
))
{
GemmTest::runGemmTest(GetParam());
}
PERF_TEST_P(GemmTest, gemmN1,
testing::Combine(
testing::Values(
make_tuple(256, 1, 256)
),
testing::Values(CV_32FC1),
testing::Values(0)
))
{
GemmTest::runGemmTest(GetParam());
}
} // namespace
+2 -2
View File
@@ -1566,7 +1566,7 @@ void mul_loop_d<double, v_float64>(const double* src1, size_t step1, const doubl
DEFINE_SIMD_FUN(fun, _T1, v_float64, _OP)
DEFINE_SIMD_SAT(mul, mul_loop)
DEFINE_SIMD_F32(mul, mul_loop_d)
DEFINE_SIMD_F32(mul, mul_loop)
DEFINE_SIMD_S32(mul, mul_loop_d)
DEFINE_SIMD_F64(mul, mul_loop_d)
@@ -1834,7 +1834,7 @@ void add_weighted_loop_d<double, v_float64>(const double* src1, size_t step1, co
DEFINE_SIMD_SAT(addWeighted, add_weighted_loop)
DEFINE_SIMD_S32(addWeighted, add_weighted_loop_d)
DEFINE_SIMD_F32(addWeighted, add_weighted_loop_d)
DEFINE_SIMD_F32(addWeighted, add_weighted_loop)
DEFINE_SIMD_F64(addWeighted, add_weighted_loop_d)
//=======================================
+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;
+251 -127
View File
@@ -29,16 +29,138 @@ static int countNonZero_(const T* src, int len )
return nz;
}
static int countNonZero8u( const void* src_ptr, int len )
{
const uchar* src = static_cast<const uchar*>(src_ptr);
int i=0, nz = 0;
#if (CV_SIMD || CV_SIMD_SCALABLE)
int len0 = len & -VTraits<v_uint8>::vlanes();
#if defined(CV_CPU_COMPILE_AVX512_SKX) || defined(CV_CPU_COMPILE_AVX512_ICL)
#define CV_COUNTNONZERO_AVX512 1
#else
#define CV_COUNTNONZERO_AVX512 0
#endif
#if CV_COUNTNONZERO_AVX512
static inline int cnz_popcount32(unsigned x)
{
#if defined(__GNUC__) || defined(__clang__)
return __builtin_popcount(x);
#else
int c = 0;
for (; x; x &= x - 1)
++c;
return c;
#endif
}
static inline int cnz_popcount64(uint64 x)
{
#if defined(__GNUC__) || defined(__clang__)
return (int)__builtin_popcountll((unsigned long long)x);
#else
int c = 0;
for (; x; x &= x - 1)
++c;
return c;
#endif
}
static inline int cnz_lane_pop(int m) { return cnz_popcount32((unsigned)m); }
static inline int cnz_lane_pop(int64 m) { return cnz_popcount64((uint64)m); }
template<typename VT> static inline int cnz_pop_ne(const VT& v, const VT& z);
template<> inline int cnz_pop_ne<v_uint8>(const v_uint8& v, const v_uint8& z)
{ return cnz_lane_pop(v_signmask(v_reinterpret_as_s8(v_ne(v, z)))); }
template<> inline int cnz_pop_ne<v_uint16>(const v_uint16& v, const v_uint16& z)
{ return cnz_lane_pop(v_signmask(v_reinterpret_as_s16(v_ne(v, z)))); }
template<> inline int cnz_pop_ne<v_int32>(const v_int32& v, const v_int32& z)
{ return cnz_lane_pop(v_signmask(v_ne(v, z))); }
template<> inline int cnz_pop_ne<v_float32>(const v_float32& v, const v_float32& z)
{ return cnz_lane_pop(v_signmask(v_ne(v, z))); }
#if (CV_SIMD_64F || CV_SIMD_SCALABLE_64F)
template<> inline int cnz_pop_ne<v_float64>(const v_float64& v, const v_float64& z)
{ return cnz_lane_pop(v_signmask(v_ne(v, z))); }
#endif
#endif // CV_COUNTNONZERO_AVX512
struct cnz_pack16u
{
static inline v_int8 eq(const ushort* src, int k, v_uint16 z)
{
const int n = VTraits<v_uint16>::vlanes();
return v_pack(v_reinterpret_as_s16(v_eq(vx_load(src + k), z)),
v_reinterpret_as_s16(v_eq(vx_load(src + k + n), z)));
}
};
struct cnz_pack32s
{
static inline v_int8 eq(const int* src, int k, v_int32 z)
{
const int n = VTraits<v_int32>::vlanes();
return v_pack(v_pack(v_eq(vx_load(src + k), z), v_eq(vx_load(src + k + n), z)),
v_pack(v_eq(vx_load(src + k + 2 * n), z), v_eq(vx_load(src + k + 3 * n), z)));
}
};
struct cnz_pack32f
{
static inline v_int8 eq(const float* src, int k, v_float32 z)
{
const int n = VTraits<v_float32>::vlanes();
return v_pack(v_pack(v_reinterpret_as_s32(v_eq(vx_load(src + k), z)),
v_reinterpret_as_s32(v_eq(vx_load(src + k + n), z))),
v_pack(v_reinterpret_as_s32(v_eq(vx_load(src + k + 2 * n), z)),
v_reinterpret_as_s32(v_eq(vx_load(src + k + 3 * n), z))));
}
};
template<typename PackOp, typename ST, typename ZVT>
static int countNonZeroVT_batched(const ST* src, int len, const ZVT& z)
{
int i = 0, nz = 0;
const int vl8 = VTraits<v_int8>::vlanes();
const int len0 = len & -vl8;
v_int8 v_one = vx_setall_s8(1);
v_int32 v_sum32 = vx_setzero_s32();
while (i < len0)
{
v_int16 v_sum16 = vx_setzero_s16();
int j = i;
while (j < std::min(len0, i + 32766 * VTraits<v_int16>::vlanes()))
{
v_int8 v_sum8 = vx_setzero_s8();
int k = j;
for (; k < std::min(len0, j + 127 * vl8); k += vl8)
v_sum8 = v_add(v_sum8, v_and(v_one, PackOp::eq(src, k, z)));
v_int16 part1, part2;
v_expand(v_sum8, part1, part2);
v_sum16 = v_add(v_sum16, v_add(part1, part2));
j = k;
}
v_int32 part1, part2;
v_expand(v_sum16, part1, part2);
v_sum32 = v_add(v_sum32, v_add(part1, part2));
i = j;
}
nz = i - v_reduce_sum(v_sum32);
v_cleanup();
return nz + countNonZero_(src + i, len - i);
}
static int countNonZeroVT_u8(const uchar* src, int len)
{
int i = 0, nz = 0;
const int len0 = len & -VTraits<v_uint8>::vlanes();
v_uint8 v_zero = vx_setzero_u8();
v_uint8 v_one = vx_setall_u8(1);
v_uint32 v_sum32 = vx_setzero_u32();
while (i < len0)
{
v_uint16 v_sum16 = vx_setzero_u16();
@@ -61,143 +183,145 @@ static int countNonZero8u( const void* src_ptr, int len )
}
nz = i - v_reduce_sum(v_sum32);
v_cleanup();
#endif
for( ; i < len; i++ )
for (; i < len; i++)
nz += src[i] != 0;
return nz;
}
static int countNonZero16u( const void* src_ptr, int len )
{
const ushort* src = static_cast<const ushort*>(src_ptr);
int i = 0, nz = 0;
#if (CV_SIMD || CV_SIMD_SCALABLE)
int len0 = len & -VTraits<v_int8>::vlanes();
v_uint16 v_zero = vx_setzero_u16();
v_int8 v_one = vx_setall_s8(1);
v_int32 v_sum32 = vx_setzero_s32();
while (i < len0)
{
v_int16 v_sum16 = vx_setzero_s16();
int j = i;
while (j < std::min(len0, i + 32766 * VTraits<v_int16>::vlanes()))
{
v_int8 v_sum8 = vx_setzero_s8();
int k = j;
for (; k < std::min(len0, j + 127 * VTraits<v_int8>::vlanes()); k += VTraits<v_int8>::vlanes())
v_sum8 = v_add(v_sum8, v_and(v_one, v_pack(v_reinterpret_as_s16(v_eq(vx_load(src + k), v_zero)), v_reinterpret_as_s16(v_eq(vx_load(src + k + VTraits<v_uint16>::vlanes()), v_zero)))));
v_int16 part1, part2;
v_expand(v_sum8, part1, part2);
v_sum16 = v_add(v_sum16, v_add(part1, part2));
j = k;
}
v_int32 part1, part2;
v_expand(v_sum16, part1, part2);
v_sum32 = v_add(v_sum32, v_add(part1, part2));
i = j;
}
nz = i - v_reduce_sum(v_sum32);
v_cleanup();
#endif
return nz + countNonZero_(src + i, len - i);
}
static int countNonZero32s( const void* src_ptr, int len )
{
const int* src = static_cast<const int*>(src_ptr);
int i = 0, nz = 0;
#if (CV_SIMD || CV_SIMD_SCALABLE)
int len0 = len & -VTraits<v_int8>::vlanes();
v_int32 v_zero = vx_setzero_s32();
v_int8 v_one = vx_setall_s8(1);
v_int32 v_sum32 = vx_setzero_s32();
while (i < len0)
{
v_int16 v_sum16 = vx_setzero_s16();
int j = i;
while (j < std::min(len0, i + 32766 * VTraits<v_int16>::vlanes()))
{
v_int8 v_sum8 = vx_setzero_s8();
int k = j;
for (; k < std::min(len0, j + 127 * VTraits<v_int8>::vlanes()); k += VTraits<v_int8>::vlanes())
v_sum8 = v_add(v_sum8, v_and(v_one, v_pack(v_pack(v_eq(vx_load(src + k), v_zero), v_eq(vx_load(src + k + VTraits<v_int32>::vlanes()), v_zero)), v_pack(v_eq(vx_load(src + k + 2 * VTraits<v_int32>::vlanes()), v_zero), v_eq(vx_load(src + k + 3 * VTraits<v_int32>::vlanes()), v_zero)))));
v_int16 part1, part2;
v_expand(v_sum8, part1, part2);
v_sum16 = v_add(v_sum16, v_add(part1, part2));
j = k;
}
v_int32 part1, part2;
v_expand(v_sum16, part1, part2);
v_sum32 = v_add(v_sum32, v_add(part1, part2));
i = j;
}
nz = i - v_reduce_sum(v_sum32);
v_cleanup();
#endif
return nz + countNonZero_(src + i, len - i);
}
static int countNonZero32f( const void* src_ptr, int len )
{
const float* src = static_cast<const float*>(src_ptr);
int i = 0, nz = 0;
#if (CV_SIMD || CV_SIMD_SCALABLE)
int len0 = len & -VTraits<v_int8>::vlanes();
v_float32 v_zero = vx_setzero_f32();
v_int8 v_one = vx_setall_s8(1);
v_int32 v_sum32 = vx_setzero_s32();
while (i < len0)
{
v_int16 v_sum16 = vx_setzero_s16();
int j = i;
while (j < std::min(len0, i + 32766 * VTraits<v_int16>::vlanes()))
{
v_int8 v_sum8 = vx_setzero_s8();
int k = j;
for (; k < std::min(len0, j + 127 * VTraits<v_int8>::vlanes()); k += VTraits<v_int8>::vlanes())
v_sum8 = v_add(v_sum8, v_and(v_one, v_pack(v_pack(v_reinterpret_as_s32(v_eq(vx_load(src + k), v_zero)), v_reinterpret_as_s32(v_eq(vx_load(src + k + VTraits<v_float32>::vlanes()), v_zero))), v_pack(v_reinterpret_as_s32(v_eq(vx_load(src + k + 2 * VTraits<v_float32>::vlanes()), v_zero)), v_reinterpret_as_s32(v_eq(vx_load(src + k + 3 * VTraits<v_float32>::vlanes()), v_zero))))));
v_int16 part1, part2;
v_expand(v_sum8, part1, part2);
v_sum16 = v_add(v_sum16, v_add(part1, part2));
j = k;
}
v_int32 part1, part2;
v_expand(v_sum16, part1, part2);
v_sum32 = v_add(v_sum32, v_add(part1, part2));
i = j;
}
nz = i - v_reduce_sum(v_sum32);
v_cleanup();
#endif
return nz + countNonZero_(src + i, len - i);
}
static int countNonZero64f( const void* src_ptr, int len )
{
const double* src = static_cast<const double*>(src_ptr);
int nz = 0, i = 0;
#if (CV_SIMD_64F || CV_SIMD_SCALABLE_64F)
static int countNonZeroVT_f64(const double* src, int len)
{
int nz = 0, i = 0;
v_int64 sum1 = vx_setzero_s64();
v_int64 sum2 = vx_setzero_s64();
v_float64 zero = vx_setzero_f64();
int step = VTraits<v_float64>::vlanes() * 2;
int len0 = len & -step;
const int step = VTraits<v_float64>::vlanes() * 2;
const int len0 = len & -step;
for(i = 0; i < len0; i += step )
{
for (i = 0; i < len0; i += step)
{
sum1 = v_add(sum1, v_reinterpret_as_s64(v_eq(vx_load(&src[i]), zero)));
sum2 = v_add(sum2, v_reinterpret_as_s64(v_eq(vx_load(&src[i + step / 2]), zero)));
}
}
// N.B the value is incremented by -1 (0xF...F) for each value
nz = i + (int)v_reduce_sum(v_add(sum1, sum2));
v_cleanup();
#endif
return nz + countNonZero_(src + i, len - i);
}
#endif
template<typename VT> struct cnz_vt_traits;
template<>
struct cnz_vt_traits<v_uint8>
{
typedef uchar ST;
static inline v_uint8 zero() { return vx_setzero_u8(); }
static int legacy(const uchar* src, int len, const v_uint8&) { return countNonZeroVT_u8(src, len); }
};
template<>
struct cnz_vt_traits<v_uint16>
{
typedef ushort ST;
static inline v_uint16 zero() { return vx_setzero_u16(); }
static int legacy(const ushort* src, int len, const v_uint16& z)
{ return countNonZeroVT_batched<cnz_pack16u, ushort, v_uint16>(src, len, z); }
};
template<>
struct cnz_vt_traits<v_int32>
{
typedef int ST;
static inline v_int32 zero() { return vx_setzero_s32(); }
static int legacy(const int* src, int len, const v_int32& z)
{ return countNonZeroVT_batched<cnz_pack32s, int, v_int32>(src, len, z); }
};
template<>
struct cnz_vt_traits<v_float32>
{
typedef float ST;
static inline v_float32 zero() { return vx_setzero_f32(); }
static int legacy(const float* src, int len, const v_float32& z)
{ return countNonZeroVT_batched<cnz_pack32f, float, v_float32>(src, len, z); }
};
#if (CV_SIMD_64F || CV_SIMD_SCALABLE_64F)
template<>
struct cnz_vt_traits<v_float64>
{
typedef double ST;
static inline v_float64 zero() { return vx_setzero_f64(); }
static int legacy(const double* src, int len, const v_float64&)
{ return countNonZeroVT_f64(src, len); }
};
#endif
template<typename VT, typename ST>
static int countNonZeroVT(const ST* src, int len, const VT& z)
{
#if CV_COUNTNONZERO_AVX512
const int nlanes = VTraits<VT>::vlanes();
const int step = nlanes * 2;
const int len0 = len & -step;
int i = 0, nz = 0;
for (; i < len0; i += step)
{
nz += cnz_pop_ne<VT>(vx_load(src + i), z);
nz += cnz_pop_ne<VT>(vx_load(src + i + nlanes), z);
}
for (; i <= len - nlanes; i += nlanes)
nz += cnz_pop_ne<VT>(vx_load(src + i), z);
v_cleanup();
return nz + countNonZero_(src + i, len - i);
#else
return cnz_vt_traits<VT>::legacy(src, len, z);
#endif
}
template<typename VT>
static int countNonZeroSimd(const void* src_ptr, int len)
{
typedef cnz_vt_traits<VT> traits;
const typename traits::ST* src = static_cast<const typename traits::ST*>(src_ptr);
return countNonZeroVT<VT>(src, len, traits::zero());
}
#endif // CV_SIMD
template<typename VT, typename ST>
static int countNonZeroDepth(const void* src_ptr, int len)
{
#if (CV_SIMD || CV_SIMD_SCALABLE)
return countNonZeroSimd<VT>(src_ptr, len);
#else
return countNonZero_(static_cast<const ST*>(src_ptr), len);
#endif
}
static int countNonZero8u( const void* src_ptr, int len )
{ return countNonZeroDepth<v_uint8, uchar>(src_ptr, len); }
static int countNonZero16u( const void* src_ptr, int len )
{ return countNonZeroDepth<v_uint16, ushort>(src_ptr, len); }
static int countNonZero32s( const void* src_ptr, int len )
{ return countNonZeroDepth<v_int32, int>(src_ptr, len); }
static int countNonZero32f( const void* src_ptr, int len )
{ return countNonZeroDepth<v_float32, float>(src_ptr, len); }
static int countNonZero64f( const void* src_ptr, int len )
{
#if (CV_SIMD || CV_SIMD_SCALABLE) && (CV_SIMD_64F || CV_SIMD_SCALABLE_64F)
return countNonZeroSimd<v_float64>(src_ptr, len);
#else
return countNonZero_(static_cast<const double*>(src_ptr), len);
#endif
}
CountNonZeroFunc getCountNonZeroTab(int depth)
{
-1
View File
@@ -47,7 +47,6 @@
#ifdef HAVE_LAPACK
#include <complex.h>
#include "opencv_lapack.h"
#include <cmath>
+8 -2
View File
@@ -1,6 +1,7 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html
// Copyright (C) 2026, Advanced Micro Devices, Inc., all rights reserved.
#include "precomp.hpp"
@@ -8,6 +9,11 @@
#include "convert.hpp"
#include <sys/types.h>
namespace cv {
void LUT8u_dispatch( const uchar* src, const uchar* lut, uchar* dst, int len, int cn, int lutcn );
void LUT16u_dispatch( const uchar* src, const ushort* lut, ushort* dst, int len, int cn, int lutcn );
} // namespace cv
/****************************************************************************************\
* LUT Transform *
\****************************************************************************************/
@@ -40,9 +46,9 @@ static LUTFunc getLUTFunc(const int srcDepth, const int dstDepth)
{
switch(dstDepth)
{
case CV_8U: ret = (LUTFunc)LUT_<uint8_t, uint8_t>; break;
case CV_8U: ret = (LUTFunc)LUT8u_dispatch; break;
case CV_8S: ret = (LUTFunc)LUT_<uint8_t, int8_t>; break;
case CV_16U: ret = (LUTFunc)LUT_<uint8_t, uint16_t>; break;
case CV_16U: ret = (LUTFunc)LUT16u_dispatch; break;
case CV_16S: ret = (LUTFunc)LUT_<uint8_t, int16_t>; break;
case CV_32S: ret = (LUTFunc)LUT_<uint8_t, int32_t>; break;
case CV_32F: ret = (LUTFunc)LUT_<uint8_t, int32_t>; break; // float
+56
View File
@@ -0,0 +1,56 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html
// Copyright (C) 2026, Advanced Micro Devices, Inc., all rights reserved.
#include "precomp.hpp"
#include "lut.simd.hpp"
#include "lut.simd_declarations.hpp" // defines CV_CPU_DISPATCH_MODES_ALL=AVX512_ICL,...,BASELINE based on CMakeLists.txt content
namespace cv {
namespace {
typedef void (*LUT8uFunc)( const uchar*, const uchar*, uchar*, int, int, int );
typedef void (*LUT16uFunc)( const uchar*, const ushort*, ushort*, int, int, int );
static LUT8uFunc resolveLUT8uFunc()
{
#if CV_TRY_AVX512_ICL
if (cv::checkHardwareSupport(CV_CPU_AVX512_ICL))
return opt_AVX512_ICL::LUT8u_;
#endif
return cpu_baseline::LUT8u_;
}
static LUT16uFunc resolveLUT16uFunc()
{
#if CV_TRY_AVX512_ICL
if (cv::checkHardwareSupport(CV_CPU_AVX512_ICL))
return opt_AVX512_ICL::LUT16u_;
#endif
return cpu_baseline::LUT16u_;
}
} // namespace
void LUT8u_dispatch( const uchar* src, const uchar* lut, uchar* dst, int len, int cn, int lutcn );
void LUT8u_dispatch( const uchar* src, const uchar* lut, uchar* dst, int len, int cn, int lutcn )
{
CV_INSTRUMENT_REGION();
static LUT8uFunc fn = resolveLUT8uFunc();
fn(src, lut, dst, len, cn, lutcn);
}
void LUT16u_dispatch( const uchar* src, const ushort* lut, ushort* dst, int len, int cn, int lutcn );
void LUT16u_dispatch( const uchar* src, const ushort* lut, ushort* dst, int len, int cn, int lutcn )
{
CV_INSTRUMENT_REGION();
static LUT16uFunc fn = resolveLUT16uFunc();
fn(src, lut, dst, len, cn, lutcn);
}
} // namespace cv
+146
View File
@@ -0,0 +1,146 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html
// Copyright (C) 2026, Advanced Micro Devices, Inc., all rights reserved.
#include "precomp.hpp"
// VBMI vpermb is the only x86 SIMD path faster than scalar for byte LUT.
// x86 gather (AVX2/SSE) is slower than scalar due to high gather latency.
// Non-x86 (NEON, etc.) benefits from v_lut implementation.
#if CV_AVX_512VBMI
#define CV_LUT8U_SIMD 1
#elif (defined(CV_CPU_COMPILE_SSE) || defined(CV_CPU_COMPILE_SSE2) || defined(CV_CPU_COMPILE_SSE3) || \
defined(CV_CPU_COMPILE_SSSE3) || defined(CV_CPU_COMPILE_SSE4_1) || defined(CV_CPU_COMPILE_SSE4_2) || \
defined(CV_CPU_COMPILE_AVX) || defined(CV_CPU_COMPILE_AVX2) || defined(CV_CPU_COMPILE_AVX_512F) || \
defined(CV_CPU_COMPILE_AVX512_COMMON) || defined(CV_CPU_COMPILE_AVX512_SKX) || \
defined(CV_CPU_COMPILE_AVX512_CNL) || defined(CV_CPU_COMPILE_AVX512_CLX))
#define CV_LUT8U_SIMD 0
#elif CV_SIMD || CV_SIMD_SCALABLE
#define CV_LUT8U_SIMD 1
#else
#define CV_LUT8U_SIMD 0
#endif
namespace cv {
CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN
void LUT8u_( const uchar* src, const uchar* lut, uchar* dst, int len, int cn, int lutcn );
void LUT16u_( const uchar* src, const ushort* lut, ushort* dst, int len, int cn, int lutcn );
#ifndef CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY
void LUT8u_( const uchar* src, const uchar* lut, uchar* dst, int len, int cn, int lutcn )
{
const int total = len * cn;
if( lutcn == 1 )
{
int i = 0;
#if CV_LUT8U_SIMD
const int nlanes = VTraits<v_uint8>::vlanes();
for( ; i <= total - nlanes; i += nlanes )
{
v_uint8 idx = vx_load(src + i);
v_uint8 result = v_lut(lut, idx);
v_store(dst + i, result);
}
#endif
for( ; i < total; i++ )
dst[i] = lut[src[i]];
}
#if CV_LUT8U_SIMD
else if( cn == 3 )
{
// Deinterleave the 3-channel LUT into per-channel tables
uchar CV_DECL_ALIGNED(64) lut0[256], lut1[256], lut2[256];
const int nlanes = VTraits<v_uint8>::vlanes();
{
unsigned j = 0;
for( ; j + (unsigned)nlanes <= 256; j += (unsigned)nlanes )
{
v_uint8 a, b, c;
v_load_deinterleave(lut + j * 3, a, b, c);
v_store(lut0 + j, a);
v_store(lut1 + j, b);
v_store(lut2 + j, c);
}
for( ; j < 256; j++ )
{
const unsigned idx = j * 3;
lut0[j] = lut[idx];
lut1[j] = lut[idx + 1];
lut2[j] = lut[idx + 2];
}
}
int i = 0;
for( ; i <= total - nlanes*3; i += nlanes*3 )
{
v_uint8 r, g, b;
v_load_deinterleave(src + i, r, g, b);
r = v_lut(lut0, r);
g = v_lut(lut1, g);
b = v_lut(lut2, b);
v_store_interleave(dst + i, r, g, b);
}
for( ; i < total; i += 3 )
{
dst[i] = lut0[src[i]];
dst[i+1] = lut1[src[i+1]];
dst[i+2] = lut2[src[i+2]];
}
}
#endif
else
{
for( int i = 0; i < total; i += cn )
for( int k = 0; k < cn; k++ )
dst[i+k] = lut[src[i+k]*cn + k];
}
}
void LUT16u_( const uchar* src, const ushort* lut, ushort* dst, int len, int cn, int lutcn )
{
const int total = len * cn;
if( lutcn == 1 )
{
int i = 0;
#if CV_LUT8U_SIMD
// Split ushort LUT into low-byte and high-byte tables,
// then use two byte v_lut (vpermb on VBMI) + interleave.
uchar CV_DECL_ALIGNED(64) lut_lo[256], lut_hi[256];
for( int j = 0; j < 256; j++ )
{
lut_lo[j] = (uchar)(lut[j]);
lut_hi[j] = (uchar)(lut[j] >> 8);
}
const int nlanes8 = VTraits<v_uint8>::vlanes();
const int nlanes16 = VTraits<v_uint16>::vlanes();
for( ; i <= total - nlanes8; i += nlanes8 )
{
v_uint8 idx = vx_load(src + i);
v_uint8 lo = v_lut(lut_lo, idx);
v_uint8 hi = v_lut(lut_hi, idx);
// Zip low and high bytes: [l0,h0,l1,h1,...] → ushort values on little-endian
v_uint8 res0, res1;
v_zip(lo, hi, res0, res1);
v_store(dst + i, v_reinterpret_as_u16(res0));
v_store(dst + i + nlanes16, v_reinterpret_as_u16(res1));
}
#endif
for( ; i < total; i++ )
dst[i] = lut[src[i]];
}
else
{
for( int i = 0; i < total; i += cn )
for( int k = 0; k < cn; k++ )
dst[i+k] = lut[src[i+k]*cn + k];
}
}
#endif // CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY
CV_CPU_OPTIMIZATION_NAMESPACE_END
} // namespace cv
@@ -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();
+574 -45
View File
@@ -13,6 +13,7 @@
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
// Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved.
// Copyright (C) 2014-2015, Itseez Inc., all rights reserved.
// Copyright (C) 2026, Advanced Micro Devices, Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
@@ -175,6 +176,337 @@ GEMM_TransposeBlock( const uchar* src, size_t src_step,
}
}
#if CV_SIMD
template<typename ST> struct VecTraits;
template<> struct VecTraits<float> {
typedef v_float32 VT;
static inline VT zero() { return vx_setzero_f32(); }
static inline VT load(const float* p) { return vx_load(p); }
static inline VT setall(float v) { return vx_setall_f32(v); }
};
#endif
#if CV_SIMD_64F
template<> struct VecTraits<double> {
typedef v_float64 VT;
static inline VT zero() { return vx_setzero_f64(); }
static inline VT load(const double* p) { return vx_load(p); }
static inline VT setall(double v) { return vx_setall_f64(v); }
};
#endif
#if CV_SIMD
template<typename ST>
static inline ST simdDotProduct(const ST* a, const ST* b, int n, int& k)
{
typedef typename VecTraits<ST>::VT VecT;
const int vlanes = VTraits<VecT>::vlanes();
const int stride4 = vlanes * 4;
VecT vs0 = VecTraits<ST>::zero();
VecT vs1 = VecTraits<ST>::zero();
VecT vs2 = VecTraits<ST>::zero();
VecT vs3 = VecTraits<ST>::zero();
for( ; k <= n - stride4; k += stride4 )
{
vs0 = v_muladd(VecTraits<ST>::load(a + k), VecTraits<ST>::load(b + k), vs0);
vs1 = v_muladd(VecTraits<ST>::load(a + k + vlanes), VecTraits<ST>::load(b + k + vlanes), vs1);
vs2 = v_muladd(VecTraits<ST>::load(a + k + vlanes * 2), VecTraits<ST>::load(b + k + vlanes * 2), vs2);
vs3 = v_muladd(VecTraits<ST>::load(a + k + vlanes * 3), VecTraits<ST>::load(b + k + vlanes * 3), vs3);
}
vs0 = v_add(v_add(vs0, vs1), v_add(vs2, vs3));
for( ; k <= n - vlanes; k += vlanes )
vs0 = v_muladd(VecTraits<ST>::load(a + k), VecTraits<ST>::load(b + k), vs0);
return v_reduce_sum(vs0);
}
#endif
#if CV_SIMD_64F
// Float input, double accumulation dot product (for 32FC1 where WT=double)
static inline double simdDotProduct_f32f64(const float* a, const float* b, int n, int& k)
{
const int vlanes_f32 = VTraits<v_float32>::vlanes();
const int stride2 = vlanes_f32 * 2;
v_float64 vs0 = vx_setzero_f64();
v_float64 vs1 = vx_setzero_f64();
v_float64 vs2 = vx_setzero_f64();
v_float64 vs3 = vx_setzero_f64();
for( ; k <= n - stride2; k += stride2 )
{
v_float32 va0 = vx_load(a + k);
v_float32 vb0 = vx_load(b + k);
vs0 = v_muladd(v_cvt_f64(va0), v_cvt_f64(vb0), vs0);
vs1 = v_muladd(v_cvt_f64_high(va0), v_cvt_f64_high(vb0), vs1);
v_float32 va1 = vx_load(a + k + vlanes_f32);
v_float32 vb1 = vx_load(b + k + vlanes_f32);
vs2 = v_muladd(v_cvt_f64(va1), v_cvt_f64(vb1), vs2);
vs3 = v_muladd(v_cvt_f64_high(va1), v_cvt_f64_high(vb1), vs3);
}
vs0 = v_add(v_add(vs0, vs1), v_add(vs2, vs3));
for( ; k <= n - vlanes_f32; k += vlanes_f32 )
{
v_float32 va = vx_load(a + k);
v_float32 vb = vx_load(b + k);
vs0 = v_muladd(v_cvt_f64(va), v_cvt_f64(vb), vs0);
vs0 = v_muladd(v_cvt_f64_high(va), v_cvt_f64_high(vb), vs0);
}
return v_reduce_sum(vs0);
}
// Float input, double accumulation GEMM k-outer/j-inner (for 32FC1 where WT=double)
static inline void simdGEMM_kj_f32f64(
const float* a_data, const float* _b_data, size_t b_step,
const float* _c_data, size_t c_step1,
float* d_data,
int n, int m,
double alpha, double beta,
int& j)
{
const int vlanes_f32 = VTraits<v_float32>::vlanes();
const int vlanes_f64 = VTraits<v_float64>::vlanes();
if( m >= 2 * vlanes_f32 && n >= 64 )
{
// k-outer / j-inner with double accumulation
const int max_m = 1600 / (int)sizeof(float);
cv::AutoBuffer<double> _s_buf(max_m);
double* s_buf = _s_buf.data();
{
v_float64 vz = vx_setzero_f64();
for( j = 0; j <= m - vlanes_f64; j += vlanes_f64 )
v_store(s_buf + j, vz);
for( ; j < m; j++ )
s_buf[j] = 0.0;
}
{
const float* b_ptr = _b_data;
for( int k = 0; k < n; k++, b_ptr += b_step )
{
v_float64 va = vx_setall_f64((double)a_data[k]);
j = 0;
for( ; j <= m - vlanes_f32; j += vlanes_f32 )
{
v_float32 vb = vx_load(b_ptr + j);
v_float64 vb_lo = v_cvt_f64(vb);
v_float64 vb_hi = v_cvt_f64_high(vb);
v_float64 vd_lo = vx_load(s_buf + j);
v_float64 vd_hi = vx_load(s_buf + j + vlanes_f64);
vd_lo = v_muladd(va, vb_lo, vd_lo);
vd_hi = v_muladd(va, vb_hi, vd_hi);
v_store(s_buf + j, vd_lo);
v_store(s_buf + j + vlanes_f64, vd_hi);
}
double a_val = (double)a_data[k];
for( ; j < m; j++ )
s_buf[j] += a_val * (double)b_ptr[j];
}
}
{
v_float64 valpha = vx_setall_f64(alpha);
const float* c_data = _c_data;
j = 0;
if( !_c_data )
{
for( ; j <= m - vlanes_f32; j += vlanes_f32 )
{
v_float64 vs_lo = v_mul(vx_load(s_buf + j), valpha);
v_float64 vs_hi = v_mul(vx_load(s_buf + j + vlanes_f64), valpha);
v_store(d_data + j, v_cvt_f32(vs_lo, vs_hi));
}
for( ; j < m; j++ )
d_data[j] = (float)(s_buf[j] * alpha);
}
else if( c_step1 == 1 )
{
v_float64 vbeta = vx_setall_f64(beta);
for( ; j <= m - vlanes_f32; j += vlanes_f32 )
{
v_float64 vs_lo = v_mul(vx_load(s_buf + j), valpha);
v_float64 vs_hi = v_mul(vx_load(s_buf + j + vlanes_f64), valpha);
v_float32 vc = vx_load(c_data + j);
vs_lo = v_muladd(v_cvt_f64(vc), vbeta, vs_lo);
vs_hi = v_muladd(v_cvt_f64_high(vc), vbeta, vs_hi);
v_store(d_data + j, v_cvt_f32(vs_lo, vs_hi));
}
for( ; j < m; j++ )
d_data[j] = (float)(s_buf[j] * alpha + (double)c_data[j] * beta);
}
else
{
for( j = 0; j < m; j++, c_data += c_step1 )
d_data[j] = (float)(s_buf[j] * alpha + (double)c_data[0] * beta);
}
}
j = m;
}
else if( vlanes_f32 * (int)sizeof(float) >= 32 )
{
// j-outer with SIMD: double accumulation per vlanes_f64 group
const float* c_data = _c_data;
for( ; j <= m - vlanes_f32; j += vlanes_f32, c_data += vlanes_f32 * c_step1 )
{
const float* b = _b_data + j;
v_float64 vs_lo = vx_setzero_f64();
v_float64 vs_hi = vx_setzero_f64();
for( int k = 0; k < n; k++, b += b_step )
{
v_float64 va = vx_setall_f64((double)a_data[k]);
v_float32 vb = vx_load(b);
vs_lo = v_muladd(va, v_cvt_f64(vb), vs_lo);
vs_hi = v_muladd(va, v_cvt_f64_high(vb), vs_hi);
}
v_float64 valpha = vx_setall_f64(alpha);
vs_lo = v_mul(vs_lo, valpha);
vs_hi = v_mul(vs_hi, valpha);
if( !_c_data )
{
v_store(d_data + j, v_cvt_f32(vs_lo, vs_hi));
}
else if( c_step1 == 1 )
{
v_float64 vbeta = vx_setall_f64(beta);
v_float32 vc = vx_load(c_data);
vs_lo = v_muladd(v_cvt_f64(vc), vbeta, vs_lo);
vs_hi = v_muladd(v_cvt_f64_high(vc), vbeta, vs_hi);
v_store(d_data + j, v_cvt_f32(vs_lo, vs_hi));
}
else
{
CV_DECL_ALIGNED(CV_SIMD_WIDTH) float buf[VTraits<v_float32>::max_nlanes];
v_store_aligned(buf, v_cvt_f32(vs_lo, vs_hi));
for( int jj = 0; jj < vlanes_f32; jj++ )
d_data[j + jj] = buf[jj] + c_data[jj * c_step1] * (float)beta;
}
}
}
}
#endif
#if CV_SIMD
template<typename ST>
static inline void simdGEMM_kj(
const ST* a_data, const ST* _b_data, size_t b_step,
const ST* _c_data, size_t c_step1,
ST* d_data,
int n, int m,
double alpha, double beta,
int& j)
{
typedef typename VecTraits<ST>::VT VecT;
const int vlanes = VTraits<VecT>::vlanes();
if( m >= 2 * vlanes && n >= 64 )
{
// k-outer / j-inner SIMD: sequential B-row access, good for large n
const int max_m = 1600 / (int)sizeof(ST);
CV_DECL_ALIGNED(CV_SIMD_WIDTH) ST s_buf[max_m];
{
VecT vz = VecTraits<ST>::zero();
for( j = 0; j <= m - vlanes; j += vlanes )
v_store_aligned(s_buf + j, vz);
for( ; j < m; j++ )
s_buf[j] = (ST)0;
}
{
const ST* b_ptr = _b_data;
for( int k = 0; k < n; k++, b_ptr += b_step )
{
VecT va = VecTraits<ST>::setall(a_data[k]);
j = 0;
for( ; j <= m - vlanes; j += vlanes )
{
VecT vd = vx_load_aligned(s_buf + j);
VecT vb = VecTraits<ST>::load(b_ptr + j);
vd = v_muladd(va, vb, vd);
v_store_aligned(s_buf + j, vd);
}
ST a_val = a_data[k];
for( ; j < m; j++ )
s_buf[j] += a_val * b_ptr[j];
}
}
{
VecT valpha = VecTraits<ST>::setall((ST)alpha);
const ST* c_data = _c_data;
j = 0;
if( !_c_data )
{
for( ; j <= m - vlanes; j += vlanes )
{
VecT vs = v_mul(vx_load_aligned(s_buf + j), valpha);
v_store(d_data + j, vs);
}
for( ; j < m; j++ )
d_data[j] = s_buf[j] * (ST)alpha;
}
else if( c_step1 == 1 )
{
VecT vbeta = VecTraits<ST>::setall((ST)beta);
for( ; j <= m - vlanes; j += vlanes )
{
VecT vs = v_mul(vx_load_aligned(s_buf + j), valpha);
VecT vc = VecTraits<ST>::load(c_data + j);
vs = v_muladd(vc, vbeta, vs);
v_store(d_data + j, vs);
}
for( ; j < m; j++ )
d_data[j] = s_buf[j] * (ST)alpha + c_data[j] * (ST)beta;
}
else
{
for( j = 0; j < m; j++, c_data += c_step1 )
d_data[j] = s_buf[j] * (ST)alpha + c_data[0] * (ST)beta;
}
}
j = m; // all columns handled
}
else if( vlanes * (int)sizeof(ST) >= 32 )
{
// j-outer with SIMD: process vlanes columns per group
// (skip on narrow SIMD where 4-column scalar is faster)
VecT valpha = VecTraits<ST>::setall((ST)alpha);
const ST* c_data = _c_data;
for( ; j <= m - vlanes; j += vlanes, c_data += vlanes * c_step1 )
{
const ST* b = _b_data + j;
VecT vs = VecTraits<ST>::zero();
for( int k = 0; k < n; k++, b += b_step )
{
VecT va = VecTraits<ST>::setall(a_data[k]);
VecT vb = VecTraits<ST>::load(b);
vs = v_muladd(va, vb, vs);
}
vs = v_mul(vs, valpha);
if( !_c_data )
{
v_store(d_data + j, vs);
}
else if( c_step1 == 1 )
{
VecT vc = VecTraits<ST>::load(c_data);
vs = v_muladd(vc, VecTraits<ST>::setall((ST)beta), vs);
v_store(d_data + j, vs);
}
else
{
CV_DECL_ALIGNED(CV_SIMD_WIDTH) ST buf[VTraits<VecT>::max_nlanes];
v_store_aligned(buf, vs);
for( int jj = 0; jj < vlanes; jj++ )
d_data[j + jj] = buf[jj] + c_data[jj * c_step1] * (ST)beta;
}
}
// Remaining columns handled by 4-column scalar in caller
}
}
#endif
template<typename T, typename WT> static void
GEMMSingleMul( const T* a_data, size_t a_step,
@@ -286,20 +618,38 @@ GEMMSingleMul( const T* a_data, size_t a_step,
for( j = 0; j < d_size.width; j++, b_data += b_step,
c_data += c_step1 )
{
WT s0(0), s1(0), s2(0), s3(0);
WT s0(0);
k = 0;
#if CV_ENABLE_UNROLLED
for( ; k <= n - 4; k += 4 )
#if CV_SIMD
if( sizeof(WT) == sizeof(double) )
{
s0 += WT(a_data[k])*WT(b_data[k]);
s1 += WT(a_data[k+1])*WT(b_data[k+1]);
s2 += WT(a_data[k+2])*WT(b_data[k+2]);
s3 += WT(a_data[k+3])*WT(b_data[k+3]);
#if CV_SIMD_64F
if( sizeof(T) == sizeof(double) )
s0 = (WT)simdDotProduct((const double*)a_data, (const double*)b_data, n, k);
else if( sizeof(T) == sizeof(float) )
s0 = (WT)simdDotProduct_f32f64((const float*)a_data, (const float*)b_data, n, k);
else
#endif
s0 = (WT)simdDotProduct((const float*)a_data, (const float*)b_data, n, k);
}
else
#endif
{
WT s1(0), s2(0), s3(0);
#if CV_ENABLE_UNROLLED
for( ; k <= n - 4; k += 4 )
{
s0 += WT(a_data[k])*WT(b_data[k]);
s1 += WT(a_data[k+1])*WT(b_data[k+1]);
s2 += WT(a_data[k+2])*WT(b_data[k+2]);
s3 += WT(a_data[k+3])*WT(b_data[k+3]);
}
#endif
s0 += s1 + s2 + s3;
}
#endif
for( ; k < n; k++ )
s0 += WT(a_data[k])*WT(b_data[k]);
s0 = (s0+s1+s2+s3)*alpha;
s0 = s0*alpha;
if( !c_data )
d_data[j] = T(s0);
@@ -323,7 +673,29 @@ GEMMSingleMul( const T* a_data, size_t a_step,
a_data = a_buf;
}
for( j = 0; j <= m - 4; j += 4, c_data += 4*c_step1 )
j = 0;
#if CV_SIMD
if( sizeof(WT) == sizeof(double) )
{
#if CV_SIMD_64F
if( sizeof(T) == sizeof(double) )
simdGEMM_kj((const double*)a_data, (const double*)_b_data, b_step,
(const double*)_c_data, c_step1,
(double*)d_data, n, m, alpha, beta, j);
else if( sizeof(T) == sizeof(float) )
simdGEMM_kj_f32f64((const float*)a_data, (const float*)_b_data, b_step,
(const float*)_c_data, c_step1,
(float*)d_data, n, m, alpha, beta, j);
else
#endif
simdGEMM_kj((const float*)a_data, (const float*)_b_data, b_step,
(const float*)_c_data, c_step1,
(float*)d_data, n, m, alpha, beta, j);
}
#endif
c_data = _c_data + j * c_step1;
// 4-column j-outer with register accumulators for remaining columns
for( ; j <= m - 4; j += 4, c_data += 4*c_step1 )
{
const T* b = _b_data + j;
WT s0(0), s1(0), s2(0), s3(0);
@@ -394,6 +766,41 @@ GEMMSingleMul( const T* a_data, size_t a_step,
{
WT al(a_data[k]);
j=0;
#if CV_SIMD_64F
if( sizeof(WT) == sizeof(double) )
{
if( sizeof(T) == sizeof(double) )
{
const int vlanes = VTraits<v_float64>::vlanes();
v_float64 val = vx_setall_f64(*((const double*)&al));
for( ; j <= m - vlanes; j += vlanes )
{
v_float64 vd = vx_load((const double*)(d_buf + j));
v_float64 vb = vx_load((const double*)(b_data + j));
vd = v_muladd(vb, val, vd);
v_store((double*)(d_buf + j), vd);
}
}
else
{
const int vlanes_f32 = VTraits<v_float32>::vlanes();
const int vlanes_f64 = VTraits<v_float64>::vlanes();
v_float64 val = vx_setall_f64(*((const double*)&al));
for( ; j <= m - vlanes_f32; j += vlanes_f32 )
{
v_float32 vb = vx_load((const float*)(b_data + j));
v_float64 vb_lo = v_cvt_f64(vb);
v_float64 vb_hi = v_cvt_f64_high(vb);
v_float64 vd_lo = vx_load((const double*)(d_buf + j));
v_float64 vd_hi = vx_load((const double*)(d_buf + j + vlanes_f64));
vd_lo = v_muladd(vb_lo, val, vd_lo);
vd_hi = v_muladd(vb_hi, val, vd_hi);
v_store((double*)(d_buf + j), vd_lo);
v_store((double*)(d_buf + j + vlanes_f64), vd_hi);
}
}
}
#endif
for( ; j < m; j++ )
d_buf[j] += WT(b_data[j])*al;
}
@@ -410,7 +817,96 @@ GEMMSingleMul( const T* a_data, size_t a_step,
}
}
}
#if CV_SIMD_64F
template<typename WT>
static inline void setDoubleDataZero(WT* d_data, int m, int vlanes)
{
v_float64 vz = vx_setzero_f64();
int j = 0;
for( ; j <= m - vlanes; j += vlanes )
v_store((double*)(d_data + j), vz);
for( ; j < m; j++ )
d_data[j] = WT(0);
}
template<typename ST>
static inline void scalarTail_kj(const ST* a_k, const ST* b_j,
double* d_j, int j, int m)
{
double al = (double)(*a_k);
for( ; j < m; j++ )
d_j[j] += al * (double)(b_j[j]);
}
template<typename ST>
static inline void simdMulAdd(const ST* b, double* d, int m, double a_val);
template<>
inline void simdMulAdd<float>(
const float* b, double* d, int m, double a_val)
{
const int vlanes_f32 = VTraits<v_float32>::vlanes();
const int vlanes_f64 = VTraits<v_float64>::vlanes();
v_float64 va = vx_setall_f64(a_val);
int j = 0;
for( ; j <= m - vlanes_f32; j += vlanes_f32 )
{
v_float32 vb = vx_load(b + j);
v_float64 vb_lo = v_cvt_f64(vb);
v_float64 vb_hi = v_cvt_f64_high(vb);
v_float64 vd_lo = vx_load(d + j);
v_float64 vd_hi = vx_load(d + j + vlanes_f64);
vd_lo = v_muladd(va, vb_lo, vd_lo);
vd_hi = v_muladd(va, vb_hi, vd_hi);
v_store(d + j, vd_lo);
v_store(d + j + vlanes_f64, vd_hi);
}
for( ; j < m; j++ )
d[j] += a_val * (double)b[j];
}
template<>
inline void simdMulAdd<double>(
const double* b, double* d, int m, double a_val)
{
const int vlanes = VTraits<v_float64>::vlanes();
const int stride2 = vlanes * 2;
v_float64 va = vx_setall_f64(a_val);
int j = 0;
for( ; j <= m - stride2; j += stride2 )
{
v_float64 vd0 = vx_load(d + j);
v_float64 vb0 = vx_load(b + j);
v_float64 vd1 = vx_load(d + j + vlanes);
v_float64 vb1 = vx_load(b + j + vlanes);
vd0 = v_muladd(va, vb0, vd0);
vd1 = v_muladd(va, vb1, vd1);
v_store(d + j, vd0);
v_store(d + j + vlanes, vd1);
}
for( ; j <= m - vlanes; j += vlanes )
{
v_float64 vd = vx_load(d + j);
v_float64 vb = vx_load(b + j);
vd = v_muladd(va, vb, vd);
v_store(d + j, vd);
}
for( ; j < m; j++ )
d[j] += a_val * b[j];
}
template<typename ST>
static inline void simdBlockMul_kj(
const ST* a_data, const ST* b_data, size_t b_step,
double* d_data, int n, int m, bool do_acc)
{
const int vlanes = VTraits<v_float64>::vlanes();
if( !do_acc )
setDoubleDataZero(d_data, m, vlanes);
for( int k = 0; k < n; k++, b_data += b_step )
simdMulAdd(b_data, d_data, m, (double)a_data[k]);
}
#endif // CV_SIMD_64F
template<typename T, typename WT> static void
GEMMBlockMul( const T* a_data, size_t a_step,
@@ -456,17 +952,35 @@ GEMMBlockMul( const T* a_data, size_t a_step,
for( j = 0; j < d_size.width; j++, b_data += b_step )
{
WT s0 = do_acc ? d_data[j]:WT(0), s1(0);
for( k = 0; k <= n - 2; k += 2 )
WT s0 = do_acc ? d_data[j] : WT(0);
k = 0;
#if CV_SIMD
if( sizeof(WT) == sizeof(double) )
{
s0 += WT(a_data[k])*WT(b_data[k]);
s1 += WT(a_data[k+1])*WT(b_data[k+1]);
#if CV_SIMD_64F
if( sizeof(T) == sizeof(double) )
s0 += (WT)simdDotProduct((const double*)a_data, (const double*)b_data, n, k);
else if( sizeof(T) == sizeof(float) )
s0 += (WT)simdDotProduct_f32f64((const float*)a_data, (const float*)b_data, n, k);
else
#endif
s0 += (WT)simdDotProduct((const float*)a_data, (const float*)b_data, n, k);
}
else
#endif
{
WT s1(0);
for( ; k <= n - 2; k += 2 )
{
s0 += WT(a_data[k])*WT(b_data[k]);
s1 += WT(a_data[k+1])*WT(b_data[k+1]);
}
s0 += s1;
}
for( ; k < n; k++ )
s0 += WT(a_data[k])*WT(b_data[k]);
d_data[j] = s0 + s1;
d_data[j] = s0;
}
}
}
@@ -482,40 +996,55 @@ GEMMBlockMul( const T* a_data, size_t a_step,
a_buf[k] = a_data[a_step1*k];
a_data = a_buf;
}
for( j = 0; j <= m - 4; j += 4 )
#if CV_SIMD_64F
if( sizeof(WT) == sizeof(double) )
{
WT s0, s1, s2, s3;
const T* b = b_data + j;
if( do_acc )
{
s0 = d_data[j]; s1 = d_data[j+1];
s2 = d_data[j+2]; s3 = d_data[j+3];
}
if( sizeof(T) == sizeof(double) )
simdBlockMul_kj(
(const double*)a_data, (const double*)b_data, b_step,
(double*)d_data, n, m, do_acc != 0);
else
s0 = s1 = s2 = s3 = WT(0);
for( k = 0; k < n; k++, b += b_step )
simdBlockMul_kj(
(const float*)a_data, (const float*)b_data, b_step,
(double*)d_data, n, m, do_acc != 0);
}
else
#endif
{
for( j = 0; j <= m - 4; j += 4 )
{
WT a(a_data[k]);
s0 += a * WT(b[0]); s1 += a * WT(b[1]);
s2 += a * WT(b[2]); s3 += a * WT(b[3]);
WT s0, s1, s2, s3;
const T* b = b_data + j;
if( do_acc )
{
s0 = d_data[j]; s1 = d_data[j+1];
s2 = d_data[j+2]; s3 = d_data[j+3];
}
else
s0 = s1 = s2 = s3 = WT(0);
for( k = 0; k < n; k++, b += b_step )
{
WT a(a_data[k]);
s0 += a * WT(b[0]); s1 += a * WT(b[1]);
s2 += a * WT(b[2]); s3 += a * WT(b[3]);
}
d_data[j] = s0; d_data[j+1] = s1;
d_data[j+2] = s2; d_data[j+3] = s3;
}
d_data[j] = s0; d_data[j+1] = s1;
d_data[j+2] = s2; d_data[j+3] = s3;
}
for( ; j < m; j++ )
{
const T* b = b_data + j;
WT s0 = do_acc ? d_data[j] : WT(0);
for( ; j < m; j++ )
{
const T* b = b_data + j;
WT s0 = do_acc ? d_data[j] : WT(0);
for( k = 0; k < n; k++, b += b_step )
s0 += WT(a_data[k]) * WT(b[0]);
for( k = 0; k < n; k++, b += b_step )
s0 += WT(a_data[k]) * WT(b[0]);
d_data[j] = s0;
d_data[j] = s0;
}
}
}
}
@@ -680,7 +1209,7 @@ static void GEMMBlockMul_32fc( const Complexf* a_data, size_t a_step,
Complexd* d_data, size_t d_step,
Size a_size, Size d_size, int flags )
{
GEMMBlockMul(a_data, a_step, b_data, b_step, d_data, d_step, a_size, d_size, flags);
GEMMBlockMul<Complexf,Complexd>(a_data, a_step, b_data, b_step, d_data, d_step, a_size, d_size, flags);
}
@@ -689,7 +1218,7 @@ static void GEMMBlockMul_64fc( const Complexd* a_data, size_t a_step,
Complexd* d_data, size_t d_step,
Size a_size, Size d_size, int flags )
{
GEMMBlockMul(a_data, a_step, b_data, b_step, d_data, d_step, a_size, d_size, flags);
GEMMBlockMul<Complexd,Complexd>(a_data, a_step, b_data, b_step, d_data, d_step, a_size, d_size, flags);
}
+9 -1
View File
@@ -1738,8 +1738,16 @@ MatExpr Mat::mul(InputArray m, double scale) const
{
CV_INSTRUMENT_REGION();
Mat b = m.getMat();
// Unless the argument is a refcounted Mat/UMat, the header returned by getMat() may be
// a non-owning view of caller memory (e.g. a scalar bound to _InputArray(const double&),
// a Matx or a Vec) that the returned MatExpr can outlive, so snapshot it.
// See https://github.com/opencv/opencv/issues/23577
if( !m.isMat() && !m.isUMat() )
b = b.clone();
MatExpr e;
MatOp_Bin::makeExpr(e, '*', *this, m.getMat(), scale);
MatOp_Bin::makeExpr(e, '*', *this, b, scale);
return e;
}
+124 -99
View File
@@ -6,11 +6,8 @@
#include "opencv2/core/mat.hpp"
#include "opencl_kernels_core.hpp"
#undef HAVE_IPP
#undef CV_IPP_RUN_FAST
#define CV_IPP_RUN_FAST(f, ...)
#undef CV_IPP_RUN
#define CV_IPP_RUN(c, f, ...)
#define IPP_DISABLE_REDUCE 1
#define IPP_DISABLE_SORT 0
/*************************************************************************************************\
Matrix Operations
@@ -341,9 +338,13 @@ cv::Mat cv::Mat::cross(InputArray _m) const
namespace cv
{
typedef void (*ReduceSumFunc)(const Mat& src, Mat& dst);
ReduceSumFunc getReduceCSumFunc(int sdepth, int ddepth);
ReduceSumFunc getReduceRSumFunc(int sdepth, int ddepth);
typedef void (*ReduceFunc)( const Mat& src, Mat& dst );
ReduceFunc getReduceCSumFunc(int sdepth, int ddepth);
ReduceFunc getReduceCAvgFunc(int sdepth, int ddepth);
ReduceFunc getReduceCMaxFunc(int sdepth, int ddepth);
ReduceFunc getReduceCMinFunc(int sdepth, int ddepth);
ReduceFunc getReduceCSum2Func(int sdepth, int ddepth);
ReduceFunc getReduceRSumFunc(int sdepth, int ddepth);
template <typename T, typename WT, typename Op>
struct ReduceR_SIMD
@@ -354,7 +355,6 @@ struct ReduceR_SIMD
}
};
template<typename T, typename ST, typename WT, class Op, class OpInit>
class ReduceR_Invoker : public ParallelLoopBody
{
@@ -474,8 +474,6 @@ reduceC_( const Mat& srcmat, Mat& dstmat)
parallel_for_(Range(0, srcmat.size().height), body);
}
typedef void (*ReduceFunc)( const Mat& src, Mat& dst );
}
#define reduceSumR8u32s reduceR_<uchar, int, OpAdd<int>, OpNop<int> >
@@ -512,7 +510,7 @@ typedef void (*ReduceFunc)( const Mat& src, Mat& dst );
#define reduceMinR32f reduceR_<float, float, OpMin<float> >
#define reduceMinR64f reduceR_<double,double,OpMin<double> >
#ifdef HAVE_IPP
#if defined(HAVE_IPP) && !IPP_DISABLE_REDUCE
static inline bool ipp_reduceSumC_8u16u16s32f_64f(const cv::Mat& srcmat, cv::Mat& dstmat)
{
int sstep = (int)srcmat.step, stype = srcmat.type(),
@@ -601,7 +599,7 @@ static inline void reduceSumC_8u16u16s32f_64f(const cv::Mat& srcmat, cv::Mat& ds
#define reduceSum2C32f32f reduceC_<float, float, OpAddSqr<float>, OpSqr<float> >
#define reduceSum2C64f64f reduceC_<double,double,OpAddSqr<double>,OpSqr<double> >
#ifdef HAVE_IPP
#if defined(HAVE_IPP) && !IPP_DISABLE_REDUCE
#define reduceSumC8u64f reduceSumC_8u16u16s32f_64f
#define reduceSumC16u64f reduceSumC_8u16u16s32f_64f
#define reduceSumC16s64f reduceSumC_8u16u16s32f_64f
@@ -611,14 +609,14 @@ static inline void reduceSumC_8u16u16s32f_64f(const cv::Mat& srcmat, cv::Mat& ds
#define reduceSumC16u64f reduceC_<ushort,double,OpAdd<double> >
#define reduceSumC16s64f reduceC_<short, double,OpAdd<double> >
#define reduceSumC32f64f reduceC_<float, double,OpAdd<double> >
#endif
#define reduceSum2C8u64f reduceC_<uchar, double,OpAddSqr<int>, OpSqr<int> >
#define reduceSum2C16u64f reduceC_<ushort,double,OpAddSqr<double>,OpSqr<double> >
#define reduceSum2C16s64f reduceC_<short, double,OpAddSqr<double>,OpSqr<double> >
#define reduceSum2C32f64f reduceC_<float, double,OpAddSqr<double>,OpSqr<double> >
#endif
#ifdef HAVE_IPP
#if defined(HAVE_IPP) && !IPP_DISABLE_REDUCE
#define REDUCE_OP(favor, optype, type1, type2) \
static inline bool ipp_reduce##optype##C##favor(const cv::Mat& srcmat, cv::Mat& dstmat) \
{ \
@@ -643,7 +641,7 @@ static inline void reduce##optype##C##favor(const cv::Mat& srcmat, cv::Mat& dstm
}
#endif
#ifdef HAVE_IPP
#if defined(HAVE_IPP) && !IPP_DISABLE_REDUCE
REDUCE_OP(8u, Max, uchar, uchar)
REDUCE_OP(16u, Max, ushort, ushort)
REDUCE_OP(16s, Max, short, short)
@@ -656,7 +654,7 @@ REDUCE_OP(32f, Max, float, float)
#endif
#define reduceMaxC64f reduceC_<double,double,OpMax<double> >
#ifdef HAVE_IPP
#if defined(HAVE_IPP) && !IPP_DISABLE_REDUCE
REDUCE_OP(8u, Min, uchar, uchar)
REDUCE_OP(16u, Min, ushort, ushort)
REDUCE_OP(16s, Min, short, short)
@@ -821,9 +819,9 @@ void cv::reduce(InputArray _src, OutputArray _dst, int dim, int op, int dtype)
{
if( op == REDUCE_SUM )
{
ReduceSumFunc simd_func = getReduceRSumFunc(sdepth, ddepth);
ReduceFunc simd_func = getReduceRSumFunc(sdepth, ddepth);
if(simd_func)
func = (ReduceFunc)simd_func;
func = simd_func;
else if(sdepth == CV_8U && ddepth == CV_32S)
func = reduceSumR8u32s;
else if(sdepth == CV_8U && ddepth == CV_32F)
@@ -899,9 +897,11 @@ void cv::reduce(InputArray _src, OutputArray _dst, int dim, int op, int dtype)
{
if(op == REDUCE_SUM)
{
ReduceSumFunc simd_func = getReduceCSumFunc(sdepth, ddepth);
ReduceFunc simd_func = op0 == REDUCE_AVG
? getReduceCAvgFunc(sdepth, ddepth)
: getReduceCSumFunc(sdepth, ddepth);
if(simd_func)
func = (ReduceFunc)simd_func;
func = simd_func;
else if(sdepth == CV_8U && ddepth == CV_32S)
func = reduceSumC8u32s;
else if(sdepth == CV_8U && ddepth == CV_32F)
@@ -925,7 +925,10 @@ void cv::reduce(InputArray _src, OutputArray _dst, int dim, int op, int dtype)
}
else if(op == REDUCE_MAX)
{
if(sdepth == CV_8U && ddepth == CV_8U)
ReduceFunc simd_func = getReduceCMaxFunc(sdepth, ddepth);
if(simd_func)
func = simd_func;
else if(sdepth == CV_8U && ddepth == CV_8U)
func = reduceMaxC8u;
else if(sdepth == CV_16U && ddepth == CV_16U)
func = reduceMaxC16u;
@@ -938,7 +941,10 @@ void cv::reduce(InputArray _src, OutputArray _dst, int dim, int op, int dtype)
}
else if(op == REDUCE_MIN)
{
if(sdepth == CV_8U && ddepth == CV_8U)
ReduceFunc simd_func = getReduceCMinFunc(sdepth, ddepth);
if(simd_func)
func = simd_func;
else if(sdepth == CV_8U && ddepth == CV_8U)
func = reduceMinC8u;
else if(sdepth == CV_16U && ddepth == CV_16U)
func = reduceMinC16u;
@@ -951,7 +957,10 @@ void cv::reduce(InputArray _src, OutputArray _dst, int dim, int op, int dtype)
}
else if(op == REDUCE_SUM2)
{
if(sdepth == CV_8U && ddepth == CV_32S)
ReduceFunc simd_func = getReduceCSum2Func(sdepth, ddepth);
if(simd_func)
func = simd_func;
else if(sdepth == CV_8U && ddepth == CV_32S)
func = reduceSum2C8u32s;
else if(sdepth == CV_8U && ddepth == CV_32F)
func = reduceSum2C8u32f;
@@ -992,7 +1001,6 @@ namespace cv
template<typename T> static void sort_( const Mat& src, Mat& dst, int flags )
{
AutoBuffer<T> buf;
int n, len;
bool sortRows = (flags & 1) == SORT_EVERY_ROW;
bool inplace = src.data == dst.data;
@@ -1001,45 +1009,48 @@ template<typename T> static void sort_( const Mat& src, Mat& dst, int flags )
if( sortRows )
n = src.rows, len = src.cols;
else
{
n = src.cols, len = src.rows;
buf.allocate(len);
}
T* bptr = buf.data();
for( int i = 0; i < n; i++ )
parallel_for_(Range(0, n), [&](const Range& range)
{
T* ptr = bptr;
if( sortRows )
{
T* dptr = dst.ptr<T>(i);
if( !inplace )
{
const T* sptr = src.ptr<T>(i);
memcpy(dptr, sptr, sizeof(T) * len);
}
ptr = dptr;
}
else
{
for( int j = 0; j < len; j++ )
ptr[j] = src.ptr<T>(j)[i];
}
std::sort( ptr, ptr + len );
if( sortDescending )
{
for( int j = 0; j < len/2; j++ )
std::swap(ptr[j], ptr[len-1-j]);
}
AutoBuffer<T> buf;
if( !sortRows )
for( int j = 0; j < len; j++ )
dst.ptr<T>(j)[i] = ptr[j];
}
buf.allocate(len);
T* bptr = buf.data();
for( int i = range.start; i < range.end; i++ )
{
T* ptr = bptr;
if( sortRows )
{
T* dptr = dst.ptr<T>(i);
if( !inplace )
{
const T* sptr = src.ptr<T>(i);
memcpy(dptr, sptr, sizeof(T) * len);
}
ptr = dptr;
}
else
{
for( int j = 0; j < len; j++ )
ptr[j] = src.ptr<T>(j)[i];
}
std::sort( ptr, ptr + len );
if( sortDescending )
{
for( int j = 0; j < len/2; j++ )
std::swap(ptr[j], ptr[len-1-j]);
}
if( !sortRows )
for( int j = 0; j < len; j++ )
dst.ptr<T>(j)[i] = ptr[j];
}
});
}
#ifdef HAVE_IPP
#if defined(HAVE_IPP) && !IPP_DISABLE_SORT
typedef IppStatus (CV_STDCALL *IppSortFunc)(void *pSrcDst, int len, Ipp8u *pBuffer);
static IppSortFunc getSortFunc(int depth, bool sortDescending)
@@ -1076,54 +1087,64 @@ static bool ipp_sort(const Mat& src, Mat& dst, int flags)
if(!ippsSortRadix_I)
return false;
if(sortRows)
{
AutoBuffer<Ipp8u> buffer;
int bufferSize;
if(ippsSortRadixGetBufferSize(src.cols, type, &bufferSize) < 0)
return false;
buffer.allocate(bufferSize);
if(!inplace)
src.copyTo(dst);
for(int i = 0; i < dst.rows; i++)
{
if(CV_INSTRUMENT_FUN_IPP(ippsSortRadix_I, (void*)dst.ptr(i), dst.cols, buffer.data()) < 0)
return false;
}
}
int n, len;
if( sortRows )
n = src.rows, len = src.cols;
else
n = src.cols, len = src.rows;
int bufferSize;
if(ippsSortRadixGetBufferSize(len, type, &bufferSize) < 0)
return false;
// Each row/column sorts independently, so parallelize over n like sort_ does.
// IPP scratch (buffer) and per-column temporaries are thread-local; a shared flag
// records IPP failures since the lambda cannot return from ipp_sort.
volatile bool ok = true;
parallel_for_(Range(0, n), [&](const Range& range)
{
AutoBuffer<Ipp8u> buffer;
int bufferSize;
if(ippsSortRadixGetBufferSize(src.rows, type, &bufferSize) < 0)
return false;
buffer.allocate(bufferSize);
Mat row(1, src.rows, src.type());
Mat srcSub;
Mat dstSub;
AutoBuffer<Ipp8u> buffer(bufferSize);
Mat row;
Rect subRect(0,0,1,src.rows);
if( !sortRows )
row.create(1, src.rows, src.type());
for(int i = 0; i < src.cols; i++)
for( int i = range.start; i < range.end; i++ )
{
subRect.x = i;
srcSub = Mat(src, subRect);
dstSub = Mat(dst, subRect);
srcSub.copyTo(row);
if( !ok )
break;
void* ptr;
if( sortRows )
{
uchar* dptr = dst.ptr(i);
if( !inplace )
memcpy(dptr, src.ptr(i), src.cols * src.elemSize());
ptr = dptr;
}
else
{
subRect.x = i;
Mat(src, subRect).copyTo(row);
ptr = row.ptr();
}
if(CV_INSTRUMENT_FUN_IPP(ippsSortRadix_I, (void*)row.ptr(), dst.rows, buffer.data()) < 0)
return false;
if( CV_INSTRUMENT_FUN_IPP(ippsSortRadix_I, ptr, len, buffer.data()) < 0 )
{
ok = false;
break;
}
row = row.reshape(1, dstSub.rows);
row.copyTo(dstSub);
if( !sortRows )
{
Mat dstSub(dst, subRect);
row = row.reshape(1, dstSub.rows);
row.copyTo(dstSub);
row = row.reshape(1, 1);
}
}
}
});
return true;
return ok;
}
#endif
@@ -1187,7 +1208,7 @@ template<typename T> static void sortIdx_( const Mat& src, Mat& dst, int flags )
}
}
#ifdef HAVE_IPP
#if defined(HAVE_IPP) && !IPP_DISABLE_SORT
typedef IppStatus (CV_STDCALL *IppSortIndexFunc)(const void* pSrc, Ipp32s srcStrideBytes, Ipp32s *pDstIndx, int len, Ipp8u *pBuffer);
static IppSortIndexFunc getSortIndexFunc(int depth, bool sortDescending)
@@ -1278,7 +1299,9 @@ void cv::sort( InputArray _src, OutputArray _dst, int flags )
CV_Assert( src.dims <= 2 && src.channels() == 1 );
_dst.create( src.size(), src.type() );
Mat dst = _dst.getMat();
#if !IPP_DISABLE_SORT
CV_IPP_RUN_FAST(ipp_sort(src, dst, flags));
#endif
static SortFunc tab[CV_DEPTH_MAX] =
{
@@ -1303,7 +1326,9 @@ void cv::sortIdx( InputArray _src, OutputArray _dst, int flags )
_dst.create( src.size(), CV_32S );
dst = _dst.getMat();
#if !IPP_DISABLE_SORT
CV_IPP_RUN_FAST(ipp_sortIdx(src, dst, flags));
#endif
static SortFunc tab[CV_DEPTH_MAX] =
{
+31 -4
View File
@@ -551,6 +551,33 @@ void transposeND(InputArray src_, const std::vector<int>& order, OutputArray dst
CV_CheckEQ(inp.channels(), 1, "Input array should be single-channel");
CV_CheckEQ(order.size(), static_cast<size_t>(inp.dims), "Number of dimensions shouldn't change");
bool isIdentityOrder = true;
for (size_t i = 0; i < order.size(); ++i)
{
if (order[i] != static_cast<int>(i))
{
isIdentityOrder = false;
break;
}
}
if (isIdentityOrder)
{
dst_.create(inp.dims, inp.size.p, inp.type());
Mat out = dst_.getMat();
CV_Assert(out.isContinuous());
if (inp.data != out.data)
inp.copyTo(out);
return;
}
const bool is2DSwap = inp.dims == 2 && order.size() == 2 && order[0] == 1 && order[1] == 0;
if (is2DSwap)
{
transpose(inp, dst_);
return;
}
auto order_ = order;
std::sort(order_.begin(), order_.end());
for (size_t i = 0; i < order_.size(); ++i)
@@ -558,7 +585,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 +609,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 +1256,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 +1355,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]);
+294 -2
View File
@@ -2,6 +2,7 @@
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html
// Copyright (C) 2026, Advanced Micro Devices, Inc., all rights reserved.
#include "precomp.hpp"
#include "stat.hpp"
@@ -31,7 +32,35 @@ struct SumSqr_SIMD<uchar, int, int>
{
int operator () (const uchar * src0, const uchar * mask, int * sum, int * sqsum, int len, int cn) const
{
if (mask || (cn != 1 && cn != 2 && cn != 4))
if (mask)
return 0;
// cn==3: deinterleave so each lane belongs to one channel, then reduce per
// channel. sum/sqsum stay exact in s32 within the dispatcher's 1<<15 block.
if (cn == 3)
{
const int vl8 = VTraits<v_uint8>::vlanes();
v_int32 vs0 = vx_setzero_s32(), vs1 = vx_setzero_s32(), vs2 = vx_setzero_s32();
v_int32 vq0 = vx_setzero_s32(), vq1 = vx_setzero_s32(), vq2 = vx_setzero_s32();
auto acc = [](const v_uint8& ch, v_int32& vs, v_int32& vq)
{
v_uint16 lo, hi; v_expand(ch, lo, hi);
v_uint32 s0, s1, s2, s3; v_expand(lo, s0, s1); v_expand(hi, s2, s3);
vs = v_add(vs, v_reinterpret_as_s32(v_add(v_add(s0, s1), v_add(s2, s3))));
v_int16 d0 = v_reinterpret_as_s16(lo), d1 = v_reinterpret_as_s16(hi);
vq = v_add(vq, v_add(v_dotprod(d0, d0), v_dotprod(d1, d1)));
};
int e = 0;
for (; e <= len - vl8; e += vl8)
{
v_uint8 a, b, c; v_load_deinterleave(src0 + e * 3, a, b, c);
acc(a, vs0, vq0); acc(b, vs1, vq1); acc(c, vs2, vq2);
}
sum[0] += v_reduce_sum(vs0); sum[1] += v_reduce_sum(vs1); sum[2] += v_reduce_sum(vs2);
sqsum[0] += v_reduce_sum(vq0); sqsum[1] += v_reduce_sum(vq1); sqsum[2] += v_reduce_sum(vq2);
v_cleanup();
return e;
}
if (cn != 1 && cn != 2 && cn != 4)
return 0;
len *= cn;
@@ -98,7 +127,32 @@ struct SumSqr_SIMD<schar, int, int>
{
int operator () (const schar * src0, const uchar * mask, int * sum, int * sqsum, int len, int cn) const
{
if (mask || (cn != 1 && cn != 2 && cn != 4))
if (mask)
return 0;
if (cn == 3)
{
const int vl8 = VTraits<v_int8>::vlanes();
v_int32 vs0 = vx_setzero_s32(), vs1 = vx_setzero_s32(), vs2 = vx_setzero_s32();
v_int32 vq0 = vx_setzero_s32(), vq1 = vx_setzero_s32(), vq2 = vx_setzero_s32();
auto acc = [](const v_int8& ch, v_int32& vs, v_int32& vq)
{
v_int16 lo, hi; v_expand(ch, lo, hi);
v_int32 s0, s1, s2, s3; v_expand(lo, s0, s1); v_expand(hi, s2, s3);
vs = v_add(vs, v_add(v_add(s0, s1), v_add(s2, s3)));
vq = v_add(vq, v_add(v_dotprod(lo, lo), v_dotprod(hi, hi)));
};
int e = 0;
for (; e <= len - vl8; e += vl8)
{
v_int8 a, b, c; v_load_deinterleave(src0 + e * 3, a, b, c);
acc(a, vs0, vq0); acc(b, vs1, vq1); acc(c, vs2, vq2);
}
sum[0] += v_reduce_sum(vs0); sum[1] += v_reduce_sum(vs1); sum[2] += v_reduce_sum(vs2);
sqsum[0] += v_reduce_sum(vq0); sqsum[1] += v_reduce_sum(vq1); sqsum[2] += v_reduce_sum(vq2);
v_cleanup();
return e;
}
if (cn != 1 && cn != 2 && cn != 4)
return 0;
len *= cn;
@@ -160,6 +214,212 @@ struct SumSqr_SIMD<schar, int, int>
}
};
#if (CV_SIMD_64F || CV_SIMD_SCALABLE_64F)
// Expand a 16-bit vector into two s32 halves. The unsigned overload reinterprets
// after a widening expand (values 0..65535 stay non-negative in s32, so the
// subsequent v_cvt_f64 is exact); the signed overload expands directly. This is
// the only real difference between the ushort and short reductions below.
static inline void sumSqrExpandS32(const v_uint16& v, v_int32& lo, v_int32& hi)
{
v_uint32 a, b; v_expand(v, a, b);
lo = v_reinterpret_as_s32(a); hi = v_reinterpret_as_s32(b);
}
static inline void sumSqrExpandS32(const v_int16& v, v_int32& lo, v_int32& hi)
{
v_expand(v, lo, hi);
}
// Shared 16-bit -> {s32 sum, f64 sqsum} reduction for ushort/short. The register
// type is deduced from the pointer, so the body is identical for both depths.
template <typename T>
static inline int sumSqr16ToF64(const T* src0, int* sum, double* sqsum, int len, int cn)
{
typedef decltype(vx_load(src0)) VT; // v_uint16 or v_int16
if (cn == 3)
{
const int vl = VTraits<VT>::vlanes();
v_int32 vs0 = vx_setzero_s32(), vs1 = vx_setzero_s32(), vs2 = vx_setzero_s32();
v_float64 vq0 = vx_setzero_f64(), vq1 = vx_setzero_f64(), vq2 = vx_setzero_f64();
auto acc = [](const VT& ch, v_int32& vs, v_float64& vq)
{
v_int32 lo, hi; sumSqrExpandS32(ch, lo, hi);
vs = v_add(vs, v_add(lo, hi));
v_float64 f0 = v_cvt_f64(lo), f1 = v_cvt_f64_high(lo);
v_float64 f2 = v_cvt_f64(hi), f3 = v_cvt_f64_high(hi);
vq = v_fma(f0, f0, v_fma(f1, f1, v_fma(f2, f2, v_fma(f3, f3, vq))));
};
int e = 0;
for (; e <= len - vl; e += vl)
{
VT a, b, c; v_load_deinterleave(src0 + e * 3, a, b, c);
acc(a, vs0, vq0); acc(b, vs1, vq1); acc(c, vs2, vq2);
}
sum[0] += v_reduce_sum(vs0); sum[1] += v_reduce_sum(vs1); sum[2] += v_reduce_sum(vs2);
sqsum[0] += v_reduce_sum(vq0); sqsum[1] += v_reduce_sum(vq1); sqsum[2] += v_reduce_sum(vq2);
v_cleanup();
return e;
}
if (cn != 1 && cn != 2 && cn != 4)
return 0;
len *= cn;
const int vl16 = VTraits<VT>::vlanes();
const int vl32 = VTraits<v_int32>::vlanes();
const int vl64 = VTraits<v_float64>::vlanes();
// The lane->channel scatter below (i % cn) and the returned pixel count
// (x / cn) require the lane counts to be a multiple of cn. This always holds
// for fixed-width SIMD (vlanes is a power of two >= cn for cn in {1,2,4}),
// but scalable backends (e.g. RVV) may report a non-multiple; fall back to
// scalar there. vl16 == 2*vl32, so checking vl32 covers both scatter loops.
if (vl32 % cn != 0)
return 0;
v_int32 v_sum = vx_setzero_s32();
v_float64 v_sq0 = vx_setzero_f64(), v_sq1 = vx_setzero_f64();
v_float64 v_sq2 = vx_setzero_f64(), v_sq3 = vx_setzero_f64();
int x = 0;
for (; x <= len - vl16; x += vl16)
{
VT v_src = vx_load(src0 + x);
v_int32 lo, hi; sumSqrExpandS32(v_src, lo, hi);
v_sum = v_add(v_sum, v_add(lo, hi));
v_float64 f0 = v_cvt_f64(lo);
v_float64 f1 = v_cvt_f64_high(lo);
v_float64 f2 = v_cvt_f64(hi);
v_float64 f3 = v_cvt_f64_high(hi);
v_sq0 = v_fma(f0, f0, v_sq0);
v_sq1 = v_fma(f1, f1, v_sq1);
v_sq2 = v_fma(f2, f2, v_sq2);
v_sq3 = v_fma(f3, f3, v_sq3);
}
int CV_DECL_ALIGNED(CV_SIMD_WIDTH) sbuf[VTraits<v_int32>::max_nlanes];
double CV_DECL_ALIGNED(CV_SIMD_WIDTH) qbuf[VTraits<v_float64>::max_nlanes * 4];
v_store_aligned(sbuf, v_sum);
v_store_aligned(qbuf + vl64 * 0, v_sq0);
v_store_aligned(qbuf + vl64 * 1, v_sq1);
v_store_aligned(qbuf + vl64 * 2, v_sq2);
v_store_aligned(qbuf + vl64 * 3, v_sq3);
// sbuf lane j (j<vl32) holds samples j and j+vl32 -> channel j%cn (vl32%cn==0).
// qbuf index i (i<vl16) holds sample lane i -> channel i%cn (vl16%cn==0).
for (int i = 0; i < vl32; ++i)
sum[i % cn] += sbuf[i];
for (int i = 0; i < vl16; ++i)
sqsum[i % cn] += qbuf[i];
v_cleanup();
return x / cn;
}
// Shared 32-bit -> f64 reduction for int/float. Both sum and sqsum accumulate in
// f64 to match the scalar reference's double accumulation; values are widened to
// f64 before squaring so the square is computed in double (same as (double)v*v).
// The register type is deduced from the pointer, so int and float share one body.
template <typename T>
static inline int sumSqr32ToF64(const T* src0, double* sum, double* sqsum, int len, int cn)
{
typedef decltype(vx_load(src0)) VT; // v_int32 or v_float32
if (cn == 3)
{
const int vl = VTraits<VT>::vlanes();
v_float64 vs0 = vx_setzero_f64(), vs1 = vx_setzero_f64(), vs2 = vx_setzero_f64();
v_float64 vq0 = vx_setzero_f64(), vq1 = vx_setzero_f64(), vq2 = vx_setzero_f64();
auto acc = [](const VT& ch, v_float64& vs, v_float64& vq)
{
v_float64 f0 = v_cvt_f64(ch), f1 = v_cvt_f64_high(ch);
vs = v_add(vs, v_add(f0, f1));
vq = v_fma(f0, f0, v_fma(f1, f1, vq));
};
int e = 0;
for (; e <= len - vl; e += vl)
{
VT a, b, c; v_load_deinterleave(src0 + e * 3, a, b, c);
acc(a, vs0, vq0); acc(b, vs1, vq1); acc(c, vs2, vq2);
}
sum[0] += v_reduce_sum(vs0); sum[1] += v_reduce_sum(vs1); sum[2] += v_reduce_sum(vs2);
sqsum[0] += v_reduce_sum(vq0); sqsum[1] += v_reduce_sum(vq1); sqsum[2] += v_reduce_sum(vq2);
v_cleanup();
return e;
}
if (cn != 1 && cn != 2 && cn != 4)
return 0;
len *= cn;
const int vl32 = VTraits<VT>::vlanes();
const int vl64 = VTraits<v_float64>::vlanes();
// See note in sumSqr16ToF64: guard scalable backends whose lane count may
// not divide cn (the i % cn scatter / x / cn rely on it).
if (vl32 % cn != 0)
return 0;
v_float64 vs0 = vx_setzero_f64(), vs1 = vx_setzero_f64();
v_float64 vq0 = vx_setzero_f64(), vq1 = vx_setzero_f64();
int x = 0;
for (; x <= len - vl32; x += vl32)
{
VT v_src = vx_load(src0 + x);
v_float64 f0 = v_cvt_f64(v_src);
v_float64 f1 = v_cvt_f64_high(v_src);
vs0 = v_add(vs0, f0);
vs1 = v_add(vs1, f1);
vq0 = v_fma(f0, f0, vq0);
vq1 = v_fma(f1, f1, vq1);
}
double CV_DECL_ALIGNED(CV_SIMD_WIDTH) sbuf[VTraits<v_float64>::max_nlanes * 2];
double CV_DECL_ALIGNED(CV_SIMD_WIDTH) qbuf[VTraits<v_float64>::max_nlanes * 2];
v_store_aligned(sbuf, vs0); v_store_aligned(sbuf + vl64, vs1);
v_store_aligned(qbuf, vq0); v_store_aligned(qbuf + vl64, vq1);
for (int i = 0; i < vl32; ++i)
{
sum[i % cn] += sbuf[i];
sqsum[i % cn] += qbuf[i];
}
v_cleanup();
return x / cn;
}
template <>
struct SumSqr_SIMD<ushort, int, double>
{
int operator () (const ushort* src0, const uchar* mask, int* sum, double* sqsum, int len, int cn) const
{ return mask ? 0 : sumSqr16ToF64(src0, sum, sqsum, len, cn); }
};
template <>
struct SumSqr_SIMD<short, int, double>
{
int operator () (const short* src0, const uchar* mask, int* sum, double* sqsum, int len, int cn) const
{ return mask ? 0 : sumSqr16ToF64(src0, sum, sqsum, len, cn); }
};
template <>
struct SumSqr_SIMD<int, double, double>
{
int operator () (const int* src0, const uchar* mask, double* sum, double* sqsum, int len, int cn) const
{ return mask ? 0 : sumSqr32ToF64(src0, sum, sqsum, len, cn); }
};
template <>
struct SumSqr_SIMD<float, double, double>
{
int operator () (const float* src0, const uchar* mask, double* sum, double* sqsum, int len, int cn) const
{ return mask ? 0 : sumSqr32ToF64(src0, sum, sqsum, len, cn); }
};
#endif // CV_SIMD_64F
#endif
template<typename T, typename ST, typename SQT>
@@ -253,6 +513,21 @@ static int sumsqr_(const T* src0, const uchar* mask, ST* sum, SQT* sqsum, int le
sum[0] = s0;
sqsum[0] = sq0;
}
else if( cn == 2 )
{
ST s0 = sum[0], s1 = sum[1];
SQT sq0 = sqsum[0], sq1 = sqsum[1];
for( i = 0; i < len; i++, src += 2 )
if( mask[i] )
{
T v0 = src[0], v1 = src[1];
s0 += v0; sq0 += (SQT)v0*v0;
s1 += v1; sq1 += (SQT)v1*v1;
nzm++;
}
sum[0] = s0; sum[1] = s1;
sqsum[0] = sq0; sqsum[1] = sq1;
}
else if( cn == 3 )
{
ST s0 = sum[0], s1 = sum[1], s2 = sum[2];
@@ -269,6 +544,23 @@ static int sumsqr_(const T* src0, const uchar* mask, ST* sum, SQT* sqsum, int le
sum[0] = s0; sum[1] = s1; sum[2] = s2;
sqsum[0] = sq0; sqsum[1] = sq1; sqsum[2] = sq2;
}
else if( cn == 4 )
{
ST s0 = sum[0], s1 = sum[1], s2 = sum[2], s3 = sum[3];
SQT sq0 = sqsum[0], sq1 = sqsum[1], sq2 = sqsum[2], sq3 = sqsum[3];
for( i = 0; i < len; i++, src += 4 )
if( mask[i] )
{
T v0 = src[0], v1 = src[1], v2 = src[2], v3 = src[3];
s0 += v0; sq0 += (SQT)v0*v0;
s1 += v1; sq1 += (SQT)v1*v1;
s2 += v2; sq2 += (SQT)v2*v2;
s3 += v3; sq3 += (SQT)v3*v3;
nzm++;
}
sum[0] = s0; sum[1] = s1; sum[2] = s2; sum[3] = s3;
sqsum[0] = sq0; sqsum[1] = sq1; sqsum[2] = sq2; sqsum[3] = sq3;
}
else
{
for( i = 0; i < len; i++, src += cn )
File diff suppressed because it is too large Load Diff
+540
View File
@@ -0,0 +1,540 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html
// Copyright (C) 2026, Advanced Micro Devices, Inc., all rights reserved.
#include "precomp.hpp"
#include "opencl_kernels_core.hpp"
#include "stat.hpp"
#include "opencv2/core/detail/dispatch_helper.impl.hpp"
#include <algorithm>
#include "minmax.simd.hpp"
#include "minmax.simd_declarations.hpp" // defines CV_CPU_DISPATCH_MODES_ALL=AVX2,...,BASELINE based on CMakeLists.txt content
/****************************************************************************************\
* minMaxLoc *
\****************************************************************************************/
namespace cv
{
static MinMaxIdxFunc getMinmaxTab(int depth)
{
CV_INSTRUMENT_REGION();
CV_CPU_DISPATCH(getMinmaxTab, (depth),
CV_CPU_DISPATCH_MODES_ALL);
}
// The function expects 1-based indexing for ofs
// Zero is treated as invalid offset (not found)
static void ofs2idx(const Mat& a, size_t ofs, int* idx)
{
int i, d = a.dims;
if( ofs > 0 )
{
ofs--;
for( i = d-1; i >= 0; i-- )
{
int sz = a.size[i];
idx[i] = (int)(ofs % sz);
ofs /= sz;
}
}
else
{
for( i = d-1; i >= 0; i-- )
idx[i] = -1;
}
}
#ifdef HAVE_OPENCL
#define MINMAX_STRUCT_ALIGNMENT 8 // sizeof double
template <typename T>
void getMinMaxRes(const Mat & db, double * minVal, double * maxVal,
int* minLoc, int* maxLoc,
int groupnum, int cols, double * maxVal2)
{
uint index_max = std::numeric_limits<uint>::max();
T minval = std::numeric_limits<T>::max();
T maxval = std::numeric_limits<T>::min() > 0 ? -std::numeric_limits<T>::max() : std::numeric_limits<T>::min(), maxval2 = maxval;
uint minloc = index_max, maxloc = index_max;
size_t index = 0;
const T * minptr = NULL, * maxptr = NULL, * maxptr2 = NULL;
const uint * minlocptr = NULL, * maxlocptr = NULL;
if (minVal || minLoc)
{
minptr = db.ptr<T>();
index += sizeof(T) * groupnum;
index = alignSize(index, MINMAX_STRUCT_ALIGNMENT);
}
if (maxVal || maxLoc)
{
maxptr = (const T *)(db.ptr() + index);
index += sizeof(T) * groupnum;
index = alignSize(index, MINMAX_STRUCT_ALIGNMENT);
}
if (minLoc)
{
minlocptr = (const uint *)(db.ptr() + index);
index += sizeof(uint) * groupnum;
index = alignSize(index, MINMAX_STRUCT_ALIGNMENT);
}
if (maxLoc)
{
maxlocptr = (const uint *)(db.ptr() + index);
index += sizeof(uint) * groupnum;
index = alignSize(index, MINMAX_STRUCT_ALIGNMENT);
}
if (maxVal2)
maxptr2 = (const T *)(db.ptr() + index);
for (int i = 0; i < groupnum; i++)
{
if (minptr && minptr[i] <= minval)
{
if (minptr[i] == minval)
{
if (minlocptr)
minloc = std::min(minlocptr[i], minloc);
}
else
{
if (minlocptr)
minloc = minlocptr[i];
minval = minptr[i];
}
}
if (maxptr && maxptr[i] >= maxval)
{
if (maxptr[i] == maxval)
{
if (maxlocptr)
maxloc = std::min(maxlocptr[i], maxloc);
}
else
{
if (maxlocptr)
maxloc = maxlocptr[i];
maxval = maxptr[i];
}
}
if (maxptr2 && maxptr2[i] > maxval2)
maxval2 = maxptr2[i];
}
bool zero_mask = (minLoc && minloc == index_max) ||
(maxLoc && maxloc == index_max);
if (minVal)
*minVal = zero_mask ? 0 : (double)minval;
if (maxVal)
*maxVal = zero_mask ? 0 : (double)maxval;
if (maxVal2)
*maxVal2 = zero_mask ? 0 : (double)maxval2;
if (minLoc)
{
minLoc[0] = zero_mask ? -1 : minloc / cols;
minLoc[1] = zero_mask ? -1 : minloc % cols;
}
if (maxLoc)
{
maxLoc[0] = zero_mask ? -1 : maxloc / cols;
maxLoc[1] = zero_mask ? -1 : maxloc % cols;
}
}
typedef void (*getMinMaxResFunc)(const Mat & db, double * minVal, double * maxVal,
int * minLoc, int *maxLoc, int gropunum, int cols, double * maxVal2);
bool ocl_minMaxIdx( InputArray _src, double* minVal, double* maxVal, int* minLoc, int* maxLoc, InputArray _mask,
int ddepth, bool absValues, InputArray _src2, double * maxVal2)
{
const ocl::Device & dev = ocl::Device::getDefault();
#ifdef __ANDROID__
if (dev.isNVidia())
return false;
#endif
if (dev.deviceVersionMajor() == 1 && dev.deviceVersionMinor() < 2)
{
// 'static' storage class specifier used by "minmaxloc" is available from OpenCL 1.2+ only
return false;
}
bool doubleSupport = dev.doubleFPConfig() > 0, haveMask = !_mask.empty(),
haveSrc2 = _src2.kind() != _InputArray::NONE;
int type = _src.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type),
kercn = haveMask ? cn : std::min(4, ocl::predictOptimalVectorWidth(_src, _src2));
if (depth >= CV_16F)
return false;
// disabled following modes since it occasionally fails on AMD devices (e.g. A10-6800K, sep. 2014)
if ((haveMask || type == CV_32FC1) && dev.isAMD())
return false;
CV_Assert( (cn == 1 && (!haveMask || _mask.type() == CV_8U)) ||
(cn >= 1 && !minLoc && !maxLoc) );
if (ddepth < 0)
ddepth = depth;
CV_Assert(!haveSrc2 || _src2.type() == type);
if (depth == CV_32S)
return false;
if ((depth == CV_64F || ddepth == CV_64F) && !doubleSupport)
return false;
int groupnum = dev.maxComputeUnits();
size_t wgs = dev.maxWorkGroupSize();
int wgs2_aligned = 1;
while (wgs2_aligned < (int)wgs)
wgs2_aligned <<= 1;
wgs2_aligned >>= 1;
bool needMinVal = minVal || minLoc, needMinLoc = minLoc != NULL,
needMaxVal = maxVal || maxLoc, needMaxLoc = maxLoc != NULL;
// in case of mask we must know whether mask is filled with zeros or not
// so let's calculate min or max location, if it's undefined, so mask is zeros
if (!(needMaxLoc || needMinLoc) && haveMask)
{
if (needMinVal)
needMinLoc = true;
else
needMaxLoc = true;
}
char cvt[2][50];
String opts = format("-D DEPTH_%d -D srcT1=%s%s -D WGS=%d -D srcT=%s"
" -D WGS2_ALIGNED=%d%s%s%s -D kercn=%d%s%s%s%s"
" -D dstT1=%s -D dstT=%s -D convertToDT=%s%s%s%s%s -D wdepth=%d -D convertFromU=%s"
" -D MINMAX_STRUCT_ALIGNMENT=%d",
depth, ocl::typeToStr(depth), haveMask ? " -D HAVE_MASK" : "", (int)wgs,
ocl::typeToStr(CV_MAKE_TYPE(depth, kercn)), wgs2_aligned,
doubleSupport ? " -D DOUBLE_SUPPORT" : "",
_src.isContinuous() ? " -D HAVE_SRC_CONT" : "",
_mask.isContinuous() ? " -D HAVE_MASK_CONT" : "", kercn,
needMinVal ? " -D NEED_MINVAL" : "", needMaxVal ? " -D NEED_MAXVAL" : "",
needMinLoc ? " -D NEED_MINLOC" : "", needMaxLoc ? " -D NEED_MAXLOC" : "",
ocl::typeToStr(ddepth), ocl::typeToStr(CV_MAKE_TYPE(ddepth, kercn)),
ocl::convertTypeStr(depth, ddepth, kercn, cvt[0], sizeof(cvt[0])),
absValues ? " -D OP_ABS" : "",
haveSrc2 ? " -D HAVE_SRC2" : "", maxVal2 ? " -D OP_CALC2" : "",
haveSrc2 && _src2.isContinuous() ? " -D HAVE_SRC2_CONT" : "", ddepth,
depth <= CV_32S && ddepth == CV_32S ? ocl::convertTypeStr(CV_8U, ddepth, kercn, cvt[1], sizeof(cvt[1])) : "noconvert",
MINMAX_STRUCT_ALIGNMENT);
ocl::Kernel k("minmaxloc", ocl::core::minmaxloc_oclsrc, opts);
if (k.empty())
return false;
int esz = CV_ELEM_SIZE(ddepth), esz32s = CV_ELEM_SIZE1(CV_32S),
dbsize = groupnum * ((needMinVal ? esz : 0) + (needMaxVal ? esz : 0) +
(needMinLoc ? esz32s : 0) + (needMaxLoc ? esz32s : 0) +
(maxVal2 ? esz : 0))
+ 5 * MINMAX_STRUCT_ALIGNMENT;
UMat src = _src.getUMat(), src2 = _src2.getUMat(), db(1, dbsize, CV_8UC1), mask = _mask.getUMat();
if (cn > 1 && !haveMask)
{
src = src.reshape(1);
src2 = src2.reshape(1);
}
if (haveSrc2)
{
if (!haveMask)
k.args(ocl::KernelArg::ReadOnlyNoSize(src), src.cols, (int)src.total(),
groupnum, ocl::KernelArg::PtrWriteOnly(db), ocl::KernelArg::ReadOnlyNoSize(src2));
else
k.args(ocl::KernelArg::ReadOnlyNoSize(src), src.cols, (int)src.total(),
groupnum, ocl::KernelArg::PtrWriteOnly(db), ocl::KernelArg::ReadOnlyNoSize(mask),
ocl::KernelArg::ReadOnlyNoSize(src2));
}
else
{
if (!haveMask)
k.args(ocl::KernelArg::ReadOnlyNoSize(src), src.cols, (int)src.total(),
groupnum, ocl::KernelArg::PtrWriteOnly(db));
else
k.args(ocl::KernelArg::ReadOnlyNoSize(src), src.cols, (int)src.total(),
groupnum, ocl::KernelArg::PtrWriteOnly(db), ocl::KernelArg::ReadOnlyNoSize(mask));
}
size_t globalsize = groupnum * wgs;
if (!k.run(1, &globalsize, &wgs, true))
return false;
static const getMinMaxResFunc functab[7] =
{
getMinMaxRes<uchar>,
getMinMaxRes<char>,
getMinMaxRes<ushort>,
getMinMaxRes<short>,
getMinMaxRes<int>,
getMinMaxRes<float>,
getMinMaxRes<double>
};
CV_Assert(ddepth <= CV_64F);
getMinMaxResFunc func = functab[ddepth];
int locTemp[2];
func(db.getMat(ACCESS_READ), minVal, maxVal,
needMinLoc ? minLoc ? minLoc : locTemp : minLoc,
needMaxLoc ? maxLoc ? maxLoc : locTemp : maxLoc,
groupnum, src.cols, maxVal2);
return true;
}
#endif
}
void cv::minMaxIdx(InputArray _src, double* minVal,
double* maxVal, int* minIdx, int* maxIdx,
InputArray _mask)
{
CV_INSTRUMENT_REGION();
int type = _src.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type);
CV_Assert( (cn == 1 && (_mask.empty() || _mask.type() == CV_8U)) ||
(cn > 1 && _mask.empty() && !minIdx && !maxIdx) );
CV_OCL_RUN(OCL_PERFORMANCE_CHECK(_src.isUMat()) && _src.dims() <= 2 && (_mask.empty() || _src.size() == _mask.size()),
ocl_minMaxIdx(_src, minVal, maxVal, minIdx, maxIdx, _mask))
Mat src = _src.getMat(), mask = _mask.getMat();
if (src.dims <= 2)
{
if ((size_t)src.step == (size_t)mask.step || mask.empty())
{
CALL_HAL(minMaxIdx, cv_hal_minMaxIdx, src.data, src.step, src.cols*cn, src.rows,
src.depth(), minVal, maxVal, minIdx, maxIdx, mask.data);
}
CALL_HAL(minMaxIdxMaskStep, cv_hal_minMaxIdxMaskStep, src.data, src.step, src.cols*cn, src.rows,
src.depth(), minVal, maxVal, minIdx, maxIdx, mask.data, mask.step);
}
else if (src.isContinuous() && (mask.isContinuous() || mask.empty()))
{
int res = cv_hal_minMaxIdx(src.data, 0, (int)src.total()*cn, 1, src.depth(),
minVal, maxVal, minIdx, maxIdx, mask.data);
if (res == CV_HAL_ERROR_OK)
{
// minIdx[0] and minIdx[0] are always 0 for "flatten" version
if (minIdx)
ofs2idx(src, minIdx[1]+1, minIdx);
if (maxIdx)
ofs2idx(src, maxIdx[1]+1, maxIdx);
return;
}
else if (res != CV_HAL_ERROR_NOT_IMPLEMENTED)
{
CV_Error_(cv::Error::StsInternal,
("HAL implementation minMaxIdx ==> " CVAUX_STR(cv_hal_minMaxIdx) " returned %d (0x%08x)", res, res));
}
}
MinMaxIdxFunc func = getMinmaxTab(depth);
CV_Assert( func != 0 );
const Mat* arrays[] = {&src, &mask, 0};
uchar* ptrs[2] = {};
NAryMatIterator it(arrays, ptrs);
size_t minidx = 0, maxidx = 0;
int iminval = INT_MAX, imaxval = INT_MIN;
float fminval = std::numeric_limits<float>::infinity(), fmaxval = -fminval;
double dminval = std::numeric_limits<double>::infinity(), dmaxval = -dminval;
size_t startidx = 1;
void *minval = &iminval, *maxval = &imaxval;
int planeSize = (int)it.size*cn;
if( depth == CV_32F )
minval = &fminval, maxval = &fmaxval;
else if( depth == CV_64F )
minval = &dminval, maxval = &dmaxval;
for( size_t i = 0; i < it.nplanes; i++, ++it, startidx += planeSize )
func( ptrs[0], ptrs[1], minval, maxval, &minidx, &maxidx, planeSize, startidx );
if (!src.empty() && mask.empty())
{
if( minidx == 0 )
minidx = 1;
if( maxidx == 0 )
maxidx = 1;
}
if( minidx == 0 )
dminval = dmaxval = 0;
else if( depth == CV_32F )
dminval = fminval, dmaxval = fmaxval;
else if( depth <= CV_32S )
dminval = iminval, dmaxval = imaxval;
if( minVal )
*minVal = dminval;
if( maxVal )
*maxVal = dmaxval;
if( minIdx )
ofs2idx(src, minidx, minIdx);
if( maxIdx )
ofs2idx(src, maxidx, maxIdx);
}
void cv::minMaxLoc( InputArray _img, double* minVal, double* maxVal,
Point* minLoc, Point* maxLoc, InputArray mask )
{
CV_INSTRUMENT_REGION();
int dims = _img.dims();
CV_CheckLE(dims, 2, "");
minMaxIdx(_img, minVal, maxVal, (int*)minLoc, (int*)maxLoc, mask);
if( minLoc )
{
if (dims == 2)
std::swap(minLoc->x, minLoc->y);
else
minLoc->y = 0;
}
if( maxLoc )
{
if (dims == 2)
std::swap(maxLoc->x, maxLoc->y);
else
maxLoc->y = 0;
}
}
enum class ReduceMode
{
FIRST_MIN = 0, //!< get index of first min occurrence
LAST_MIN = 1, //!< get index of last min occurrence
FIRST_MAX = 2, //!< get index of first max occurrence
LAST_MAX = 3, //!< get index of last max occurrence
};
template <typename T>
struct reduceMinMaxImpl
{
void operator()(const cv::Mat& src, cv::Mat& dst, ReduceMode mode, const int axis) const
{
switch(mode)
{
case ReduceMode::FIRST_MIN:
reduceMinMaxApply<std::less>(src, dst, axis);
break;
case ReduceMode::LAST_MIN:
reduceMinMaxApply<std::less_equal>(src, dst, axis);
break;
case ReduceMode::FIRST_MAX:
reduceMinMaxApply<std::greater>(src, dst, axis);
break;
case ReduceMode::LAST_MAX:
reduceMinMaxApply<std::greater_equal>(src, dst, axis);
break;
}
}
template <template<class> class Cmp>
static void reduceMinMaxApply(const cv::Mat& src, cv::Mat& dst, const int axis)
{
Cmp<T> cmp;
const auto *src_ptr = src.ptr<T>();
auto *dst_ptr = dst.ptr<int32_t>();
const size_t outer_size = src.total(0, axis);
const auto mid_size = static_cast<size_t>(src.size[axis]);
const size_t outer_step = src.total(axis);
const size_t dst_step = dst.total(axis);
const size_t mid_step = src.total(axis + 1);
for (size_t outer = 0; outer < outer_size; ++outer)
{
const size_t outer_offset = outer * outer_step;
const size_t dst_offset = outer * dst_step;
for (size_t mid = 0; mid != mid_size; ++mid)
{
const size_t src_offset = outer_offset + mid * mid_step;
for (size_t inner = 0; inner < mid_step; inner++)
{
int32_t& index = dst_ptr[dst_offset + inner];
const size_t prev = outer_offset + index * mid_step + inner;
const size_t curr = src_offset + inner;
if (cmp(src_ptr[curr], src_ptr[prev]))
{
index = static_cast<int32_t>(mid);
}
}
}
}
}
};
static void reduceMinMax(cv::InputArray src, cv::OutputArray dst, ReduceMode mode, int axis)
{
CV_INSTRUMENT_REGION();
cv::Mat srcMat = src.getMat();
axis = (axis + srcMat.dims) % srcMat.dims;
CV_Assert(srcMat.channels() == 1 && axis >= 0 && axis < srcMat.dims);
cv::AutoBuffer<int> sizes(srcMat.dims);
std::copy(srcMat.size.p, srcMat.size.p + srcMat.dims, sizes.begin());
sizes[axis] = 1;
dst.create(srcMat.dims, sizes.data(), CV_32SC1); // indices
cv::Mat dstMat = dst.getMat();
dstMat.setTo(cv::Scalar::all(0));
if (!srcMat.isContinuous())
{
srcMat = srcMat.clone();
}
bool needs_copy = !dstMat.isContinuous();
if (needs_copy)
{
dstMat = dstMat.clone();
}
cv::detail::depthDispatch<reduceMinMaxImpl>(srcMat.depth(), srcMat, dstMat, mode, axis);
if (needs_copy)
{
dstMat.copyTo(dst);
}
}
void cv::reduceArgMin(InputArray src, OutputArray dst, int axis, bool lastIndex)
{
reduceMinMax(src, dst, lastIndex ? ReduceMode::LAST_MIN : ReduceMode::FIRST_MIN, axis);
}
void cv::reduceArgMax(InputArray src, OutputArray dst, int axis, bool lastIndex)
{
reduceMinMax(src, dst, lastIndex ? ReduceMode::LAST_MAX : ReduceMode::FIRST_MAX, axis);
}
+471
View File
@@ -0,0 +1,471 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html
// Copyright (C) 2026, Advanced Micro Devices, Inc., all rights reserved.
#include "precomp.hpp"
#include "stat.hpp"
#include <algorithm>
namespace cv {
typedef void (*MinMaxIdxFunc)(const void*, const uchar*, void*, void*, size_t*, size_t*, int, size_t);
CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN
MinMaxIdxFunc getMinmaxTab(int depth);
#ifndef CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY
template<typename T, typename WT> static void
minMaxIdx_( const T* src, const uchar* mask, WT* _minVal, WT* _maxVal,
size_t* _minIdx, size_t* _maxIdx, int len, size_t startIdx )
{
WT minVal = *_minVal, maxVal = *_maxVal;
size_t minIdx = *_minIdx, maxIdx = *_maxIdx;
if( !mask )
{
for( int i = 0; i < len; i++ )
{
T val = src[i];
if( val < minVal )
{
minVal = val;
minIdx = startIdx + i;
}
if( val > maxVal )
{
maxVal = val;
maxIdx = startIdx + i;
}
}
}
else
{
for( int i = 0; i < len; i++ )
{
T val = src[i];
if( mask[i] && val < minVal )
{
minVal = val;
minIdx = startIdx + i;
}
if( mask[i] && val > maxVal )
{
maxVal = val;
maxIdx = startIdx + i;
}
}
}
*_minIdx = minIdx;
*_maxIdx = maxIdx;
*_minVal = minVal;
*_maxVal = maxVal;
}
#if (CV_SIMD || CV_SIMD_SCALABLE)
template<typename T, typename WT> CV_ALWAYS_INLINE void
minMaxIdx_init( const T* src, const uchar* mask, WT* minval, WT* maxval,
size_t* minidx, size_t* maxidx, WT &minVal, WT &maxVal,
size_t &minIdx, size_t &maxIdx, const WT minInit, const WT maxInit,
const int nlanes, int len, size_t startidx, int &j, int &len0 )
{
len0 = len & -nlanes;
j = 0;
minVal = *minval, maxVal = *maxval;
minIdx = *minidx, maxIdx = *maxidx;
// To handle start values out of range
if ( minVal < minInit || maxVal < minInit || minVal > maxInit || maxVal > maxInit )
{
uchar done = 0x00;
for ( ; (j < len) && (done != 0x03); j++ )
{
if ( !mask || mask[j] ) {
T val = src[j];
if ( val < minVal )
{
minVal = val;
minIdx = startidx + j;
done |= 0x01;
}
if ( val > maxVal )
{
maxVal = val;
maxIdx = startidx + j;
done |= 0x02;
}
}
}
len0 = j + ((len - j) & -nlanes);
}
}
template<typename T, typename WT> CV_ALWAYS_INLINE void
minMaxIdx_finish( const T* src, const uchar* mask, WT* minval, WT* maxval,
size_t* minidx, size_t* maxidx, WT minVal, WT maxVal,
size_t minIdx, size_t maxIdx, int len, size_t startidx,
int j )
{
for ( ; j < len ; j++ )
{
if ( !mask || mask[j] )
{
T val = src[j];
if ( val < minVal )
{
minVal = val;
minIdx = startidx + j;
}
if ( val > maxVal )
{
maxVal = val;
maxIdx = startidx + j;
}
}
}
*minidx = minIdx;
*maxidx = maxIdx;
*minval = minVal;
*maxval = maxVal;
}
//============================================================================
// Templated SIMD core (universal intrinsics) shared by every depth. The
// per-depth wrappers below only select the value/index vector + result types.
//============================================================================
// Fast single-instruction broadcast into any universal vector (value or index).
template<typename V> static inline V mm_set(typename VTraits<V>::lane_type v);
template<> inline v_uint8 mm_set<v_uint8 >(uchar v) { return vx_setall_u8(v); }
template<> inline v_int8 mm_set<v_int8 >(schar v) { return vx_setall_s8(v); }
template<> inline v_uint16 mm_set<v_uint16 >(ushort v) { return vx_setall_u16(v); }
template<> inline v_int16 mm_set<v_int16 >(short v) { return vx_setall_s16(v); }
template<> inline v_uint32 mm_set<v_uint32 >(uint v) { return vx_setall_u32(v); }
template<> inline v_int32 mm_set<v_int32 >(int v) { return vx_setall_s32(v); }
template<> inline v_float32 mm_set<v_float32>(float v) { return vx_setall_f32(v); }
#if (CV_SIMD_64F || CV_SIMD_SCALABLE_64F)
template<> inline v_uint64 mm_set<v_uint64 >(uint64_t v) { return vx_setall_u64(v); }
template<> inline v_float64 mm_set<v_float64>(double v) { return vx_setall_f64(v); }
#endif
// Reinterpret a value-domain comparison mask to the matching unsigned index vector.
static inline v_uint8 mm_idxmask(const v_uint8& m) { return m; }
static inline v_uint8 mm_idxmask(const v_int8& m) { return v_reinterpret_as_u8(m); }
static inline v_uint16 mm_idxmask(const v_uint16& m) { return m; }
static inline v_uint16 mm_idxmask(const v_int16& m) { return v_reinterpret_as_u16(m); }
static inline v_uint32 mm_idxmask(const v_int32& m) { return v_reinterpret_as_u32(m); }
static inline v_uint32 mm_idxmask(const v_float32& m) { return v_reinterpret_as_u32(m); }
#if (CV_SIMD_64F || CV_SIMD_SCALABLE_64F)
static inline v_uint64 mm_idxmask(const v_float64& m) { return v_reinterpret_as_u64(m); }
#endif
// Blend for index vectors (universal API has no v_select for u64).
template<typename IT> static inline IT mm_sel(const IT& mask, const IT& a, const IT& b)
{ return v_xor(b, v_and(v_xor(a, b), mask)); }
// Value reduce-min / reduce-max (f64 has no universal v_reduce_*).
static inline int mm_vmin(const v_uint8& v){ return v_reduce_min(v); }
static inline int mm_vmin(const v_int8& v){ return v_reduce_min(v); }
static inline int mm_vmin(const v_uint16& v){ return v_reduce_min(v); }
static inline int mm_vmin(const v_int16& v){ return v_reduce_min(v); }
static inline int mm_vmin(const v_int32& v){ return v_reduce_min(v); }
static inline float mm_vmin(const v_float32&v){ return v_reduce_min(v); }
static inline int mm_vmax(const v_uint8& v){ return v_reduce_max(v); }
static inline int mm_vmax(const v_int8& v){ return v_reduce_max(v); }
static inline int mm_vmax(const v_uint16& v){ return v_reduce_max(v); }
static inline int mm_vmax(const v_int16& v){ return v_reduce_max(v); }
static inline int mm_vmax(const v_int32& v){ return v_reduce_max(v); }
static inline float mm_vmax(const v_float32&v){ return v_reduce_max(v); }
#if (CV_SIMD_64F || CV_SIMD_SCALABLE_64F)
static inline double mm_vmin(const v_float64& v)
{ double b[VTraits<v_float64>::max_nlanes]; v_store(b, v); double r=b[0]; const int n=VTraits<v_float64>::vlanes(); for(int i=1;i<n;i++) if(b[i]<r) r=b[i]; return r; }
static inline double mm_vmax(const v_float64& v)
{ double b[VTraits<v_float64>::max_nlanes]; v_store(b, v); double r=b[0]; const int n=VTraits<v_float64>::vlanes(); for(int i=1;i<n;i++) if(b[i]>r) r=b[i]; return r; }
#endif
// Index reduce-min (u64 has no universal v_reduce_min).
static inline unsigned mm_imin(const v_uint8& v){ return v_reduce_min(v); }
static inline unsigned mm_imin(const v_uint16& v){ return v_reduce_min(v); }
static inline unsigned mm_imin(const v_uint32& v){ return v_reduce_min(v); }
#if (CV_SIMD_64F || CV_SIMD_SCALABLE_64F)
static inline uint64_t mm_imin(const v_uint64& v)
{ uint64_t b[VTraits<v_uint64>::max_nlanes]; v_store(b, v); uint64_t r=b[0]; const int n=VTraits<v_uint64>::vlanes(); for(int i=1;i<n;i++) if(b[i]<r) r=b[i]; return r; }
#endif
// Active-lane mask in the *value* vector domain, from a uchar* mask.
template<typename VT> static inline VT mm_active(const uchar* mask, int k, int nlanes);
template<> inline v_uint8 mm_active<v_uint8 >(const uchar* mask, int k, int)
{ return v_ne(vx_load(mask + k), vx_setzero_u8()); }
template<> inline v_int8 mm_active<v_int8 >(const uchar* mask, int k, int)
{ return v_reinterpret_as_s8(v_ne(vx_load(mask + k), vx_setzero_u8())); }
template<> inline v_uint16 mm_active<v_uint16>(const uchar* mask, int k, int)
{ return v_ne(vx_load_expand(mask + k), vx_setzero_u16()); }
template<> inline v_int16 mm_active<v_int16 >(const uchar* mask, int k, int)
{ return v_reinterpret_as_s16(v_ne(vx_load_expand(mask + k), vx_setzero_u16())); }
template<> inline v_int32 mm_active<v_int32 >(const uchar* mask, int k, int nlanes)
{ uint32_t b[VTraits<v_uint32>::max_nlanes]; for(int t=0;t<nlanes;t++) b[t]=mask[k+t]?~0u:0u; return v_reinterpret_as_s32(vx_load(b)); }
template<> inline v_float32 mm_active<v_float32>(const uchar* mask, int k, int nlanes)
{ uint32_t b[VTraits<v_uint32>::max_nlanes]; for(int t=0;t<nlanes;t++) b[t]=mask[k+t]?~0u:0u; return v_reinterpret_as_f32(vx_load(b)); }
#if (CV_SIMD_64F || CV_SIMD_SCALABLE_64F)
template<> inline v_float64 mm_active<v_float64>(const uchar* mask, int k, int nlanes)
{ uint64_t b[VTraits<v_uint64>::max_nlanes]; for(int t=0;t<nlanes;t++) b[t]=mask[k+t]?~0ull:0ull; return v_reinterpret_as_f64(vx_load(b)); }
#endif
// Fold one accumulator stream's (value,index) extremum into the running result.
// Scalar tie-break (smaller index wins) lets several independent streams combine
// correctly without any vector index comparison (works for u64 indices too).
template<typename VT, typename IT, typename WT>
static inline void mm_fold_min(const VT& valMin, const IT& idxMin, const IT& none,
size_t delta, WT& minVal, size_t& minIdx)
{
if ( v_check_any(v_ne(idxMin, none)) )
{
WT cv = (WT)mm_vmin(valMin);
IT sel = mm_sel(mm_idxmask(v_eq(mm_set<VT>((typename VTraits<VT>::lane_type)cv), valMin)), idxMin, none);
size_t ci = (size_t)mm_imin(sel) + delta;
if ( cv < minVal || (cv == minVal && ci < minIdx) ) { minVal = cv; minIdx = ci; }
}
}
template<typename VT, typename IT, typename WT>
static inline void mm_fold_max(const VT& valMax, const IT& idxMax, const IT& none,
size_t delta, WT& maxVal, size_t& maxIdx)
{
if ( v_check_any(v_ne(idxMax, none)) )
{
WT cv = (WT)mm_vmax(valMax);
IT sel = mm_sel(mm_idxmask(v_eq(mm_set<VT>((typename VTraits<VT>::lane_type)cv), valMax)), idxMax, none);
size_t ci = (size_t)mm_imin(sel) + delta;
if ( cv > maxVal || (cv == maxVal && ci < maxIdx) ) { maxVal = cv; maxIdx = ci; }
}
}
// IST = unsigned index lane type (uchar / ushort / uint / uint64_t).
template<typename T, typename VT, typename IT, typename IST, typename WT>
static void minMaxIdx_simd_(const T* src, const uchar* mask, WT* minval, WT* maxval,
size_t* minidx, size_t* maxidx, int len, size_t startidx,
WT minInit, WT maxInit)
{
const int nlanes = VTraits<VT>::vlanes();
if ( len >= nlanes )
{
int j, len0;
WT minVal, maxVal;
size_t minIdx, maxIdx;
minMaxIdx_init( src, mask, minval, maxval, minidx, maxidx, minVal, maxVal, minIdx, maxIdx,
minInit, maxInit, nlanes, len, startidx, j, len0 );
if ( j <= len0 - nlanes )
{
IST idxbuf[VTraits<IT>::max_nlanes];
for ( int t = 0; t < nlanes; t++ ) idxbuf[t] = (IST)t;
const IT idxStart = vx_load(idxbuf);
const IT inc = mm_set<IT>((IST)nlanes);
const IT none = mm_set<IT>((IST)~(IST)0);
// Reduce before the per-lane index could reach the 'none' sentinel.
// For >= 32-bit indices (len <= INT_MAX) one block covers everything.
const int64_t idxcap = (sizeof(IST) <= 2) ? (int64_t)std::numeric_limits<IST>::max() : (int64_t)INT_MAX;
const int blockStep = (int)((idxcap / nlanes) * nlanes);
const IT inc2 = mm_set<IT>((IST)(nlanes * 2));
do
{
// Two independent accumulator streams break the serial
// min/max + index-select dependency chain so the CPU can
// overlap iterations (latency-bound loop). Stream 0 covers the
// even vector slots, stream 1 the odd ones; results are merged
// by mm_fold_*. (2 streams is the sweet spot: 4 spills the 16
// YMM regs on AVX2 and gives no gain on AVX-512.) The masked
// path keeps a single stream (stream 0).
VT vMin0 = mm_set<VT>((T)minVal), vMin1 = vMin0;
VT vMax0 = mm_set<VT>((T)maxVal), vMax1 = vMax0;
IT idx0 = idxStart, idx1 = v_add(idxStart, inc);
IT iMin0 = none, iMin1 = none, iMax0 = none, iMax1 = none;
int k = j;
size_t delta = startidx + j;
// 64-bit math: blockStep can be ~INT_MAX (32/64-bit indices), so
// j + blockStep would overflow int once j advances past the start.
const int limit = (int)std::min<int64_t>((int64_t)len0, (int64_t)j + (int64_t)blockStep);
if ( !mask )
{
// Dual stream only for >= 16-bit indices: 8-bit forces tiny
// reduce-blocks (u8 index < 256), where extra per-block folds
// outweigh the dependency-break benefit.
for ( ; (sizeof(IST) > 1) && k <= limit - 2 * nlanes; k += 2 * nlanes )
{
VT d0 = vx_load(src + k), d1 = vx_load(src + k + nlanes);
iMin0 = mm_sel(mm_idxmask(v_lt(d0, vMin0)), idx0, iMin0);
iMin1 = mm_sel(mm_idxmask(v_lt(d1, vMin1)), idx1, iMin1);
iMax0 = mm_sel(mm_idxmask(v_gt(d0, vMax0)), idx0, iMax0);
iMax1 = mm_sel(mm_idxmask(v_gt(d1, vMax1)), idx1, iMax1);
vMin0 = v_min(d0, vMin0); vMin1 = v_min(d1, vMin1);
vMax0 = v_max(d0, vMax0); vMax1 = v_max(d1, vMax1);
idx0 = v_add(idx0, inc2); idx1 = v_add(idx1, inc2);
}
for ( ; k < limit; k += nlanes ) // odd trailing vector
{
VT d0 = vx_load(src + k);
iMin0 = mm_sel(mm_idxmask(v_lt(d0, vMin0)), idx0, iMin0);
iMax0 = mm_sel(mm_idxmask(v_gt(d0, vMax0)), idx0, iMax0);
vMin0 = v_min(d0, vMin0); vMax0 = v_max(d0, vMax0);
idx0 = v_add(idx0, inc);
}
}
else
{
for ( ; k < limit; k += nlanes )
{
VT data = vx_load(src + k);
VT active = mm_active<VT>(mask, k, nlanes);
VT cmpMin = v_and(v_lt(data, vMin0), active);
VT cmpMax = v_and(v_gt(data, vMax0), active);
iMin0 = mm_sel(mm_idxmask(cmpMin), idx0, iMin0);
iMax0 = mm_sel(mm_idxmask(cmpMax), idx0, iMax0);
vMin0 = v_select(cmpMin, data, vMin0);
vMax0 = v_select(cmpMax, data, vMax0);
idx0 = v_add(idx0, inc);
}
}
j = k;
mm_fold_min(vMin0, iMin0, none, delta, minVal, minIdx);
mm_fold_min(vMin1, iMin1, none, delta, minVal, minIdx);
mm_fold_max(vMax0, iMax0, none, delta, maxVal, maxIdx);
mm_fold_max(vMax1, iMax1, none, delta, maxVal, maxIdx);
}
while ( j < len0 );
}
minMaxIdx_finish( src, mask, minval, maxval, minidx, maxidx, minVal, maxVal,
minIdx, maxIdx, len, startidx, j );
vx_cleanup();
}
else
{
minMaxIdx_(src, mask, minval, maxval, minidx, maxidx, len, startidx);
}
}
#endif
static void minMaxIdx_8u(const void* src_, const uchar* mask, void* minval_, void* maxval_,
size_t* minidx, size_t* maxidx, int len, size_t startidx )
{
#if (CV_SIMD || CV_SIMD_SCALABLE)
minMaxIdx_simd_<uchar, v_uint8, v_uint8, uchar, int>(
static_cast<const uchar*>(src_), mask, static_cast<int*>(minval_), static_cast<int*>(maxval_),
minidx, maxidx, len, startidx, (int)0, (int)UCHAR_MAX);
#else
minMaxIdx_(static_cast<const uchar*>(src_), mask, static_cast<int*>(minval_),
static_cast<int*>(maxval_), minidx, maxidx, len, startidx);
#endif
}
static void minMaxIdx_8s(const void* src_, const uchar* mask, void* minval_, void* maxval_,
size_t* minidx, size_t* maxidx, int len, size_t startidx )
{
#if (CV_SIMD || CV_SIMD_SCALABLE)
minMaxIdx_simd_<schar, v_int8, v_uint8, uchar, int>(
static_cast<const schar*>(src_), mask, static_cast<int*>(minval_), static_cast<int*>(maxval_),
minidx, maxidx, len, startidx, (int)SCHAR_MIN, (int)SCHAR_MAX);
#else
minMaxIdx_(static_cast<const schar*>(src_), mask, static_cast<int*>(minval_),
static_cast<int*>(maxval_), minidx, maxidx, len, startidx);
#endif
}
static void minMaxIdx_16u(const void* src_, const uchar* mask, void* minval_, void* maxval_,
size_t* minidx, size_t* maxidx, int len, size_t startidx )
{
#if (CV_SIMD || CV_SIMD_SCALABLE)
minMaxIdx_simd_<ushort, v_uint16, v_uint16, ushort, int>(
static_cast<const ushort*>(src_), mask, static_cast<int*>(minval_), static_cast<int*>(maxval_),
minidx, maxidx, len, startidx, (int)0, (int)USHRT_MAX);
#else
minMaxIdx_(static_cast<const ushort*>(src_), mask, static_cast<int*>(minval_),
static_cast<int*>(maxval_), minidx, maxidx, len, startidx);
#endif
}
static void minMaxIdx_16s(const void* src_, const uchar* mask, void* minval_, void* maxval_,
size_t* minidx, size_t* maxidx, int len, size_t startidx )
{
#if (CV_SIMD || CV_SIMD_SCALABLE)
minMaxIdx_simd_<short, v_int16, v_uint16, ushort, int>(
static_cast<const short*>(src_), mask, static_cast<int*>(minval_), static_cast<int*>(maxval_),
minidx, maxidx, len, startidx, (int)SHRT_MIN, (int)SHRT_MAX);
#else
minMaxIdx_(static_cast<const short*>(src_), mask, static_cast<int*>(minval_),
static_cast<int*>(maxval_), minidx, maxidx, len, startidx);
#endif
}
static void minMaxIdx_32s(const void* src_, const uchar* mask, void* minval_, void* maxval_,
size_t* minidx, size_t* maxidx, int len, size_t startidx )
{
#if (CV_SIMD || CV_SIMD_SCALABLE)
minMaxIdx_simd_<int, v_int32, v_uint32, uint, int>(
static_cast<const int*>(src_), mask, static_cast<int*>(minval_), static_cast<int*>(maxval_),
minidx, maxidx, len, startidx, INT_MIN, INT_MAX);
#else
minMaxIdx_(static_cast<const int*>(src_), mask, static_cast<int*>(minval_),
static_cast<int*>(maxval_), minidx, maxidx, len, startidx);
#endif
}
static void minMaxIdx_32f(const void* src_, const uchar* mask, void* minval_, void* maxval_,
size_t* minidx, size_t* maxidx, int len, size_t startidx )
{
#if (CV_SIMD || CV_SIMD_SCALABLE)
minMaxIdx_simd_<float, v_float32, v_uint32, uint, float>(
static_cast<const float*>(src_), mask, static_cast<float*>(minval_), static_cast<float*>(maxval_),
minidx, maxidx, len, startidx, FLT_MIN, FLT_MAX);
#else
minMaxIdx_(static_cast<const float*>(src_), mask, static_cast<float*>(minval_),
static_cast<float*>(maxval_), minidx, maxidx, len, startidx);
#endif
}
static void minMaxIdx_64f(const void* src_, const uchar* mask, void* minval_, void* maxval_,
size_t* minidx, size_t* maxidx, int len, size_t startidx )
{
#if (CV_SIMD_64F || CV_SIMD_SCALABLE_64F)
minMaxIdx_simd_<double, v_float64, v_uint64, uint64_t, double>(
static_cast<const double*>(src_), mask, static_cast<double*>(minval_), static_cast<double*>(maxval_),
minidx, maxidx, len, startidx, DBL_MIN, DBL_MAX);
#else
minMaxIdx_(static_cast<const double*>(src_), mask, static_cast<double*>(minval_),
static_cast<double*>(maxval_), minidx, maxidx, len, startidx);
#endif
}
MinMaxIdxFunc getMinmaxTab(int depth)
{
static MinMaxIdxFunc minmaxTab[CV_DEPTH_MAX] =
{
GET_OPTIMIZED(minMaxIdx_8u), GET_OPTIMIZED(minMaxIdx_8s),
GET_OPTIMIZED(minMaxIdx_16u), GET_OPTIMIZED(minMaxIdx_16s),
GET_OPTIMIZED(minMaxIdx_32s),
GET_OPTIMIZED(minMaxIdx_32f), GET_OPTIMIZED(minMaxIdx_64f),
0
};
return minmaxTab[depth];
}
#endif // CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY
CV_CPU_OPTIMIZATION_NAMESPACE_END
} // namespace cv
+30 -14
View File
@@ -1,6 +1,8 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html
//
// Copyright (C) 2026, Advanced Micro Devices, Inc., all rights reserved.
#include "precomp.hpp"
@@ -155,20 +157,27 @@ float normL2Sqr_(const float* a, const float* b, int n)
{
int j = 0; float d = 0.f;
#if (CV_SIMD || CV_SIMD_SCALABLE)
const int vl = VTraits<v_float32>::vlanes();
v_float32 v_d0 = vx_setzero_f32(), v_d1 = vx_setzero_f32();
v_float32 v_d2 = vx_setzero_f32(), v_d3 = vx_setzero_f32();
for (; j <= n - 4 * VTraits<v_float32>::vlanes(); j += 4 * VTraits<v_float32>::vlanes())
for (; j <= n - 4 * vl; j += 4 * vl)
{
v_float32 t0 = v_sub(vx_load(a + j), vx_load(b + j));
v_float32 t1 = v_sub(vx_load(a + j + VTraits<v_float32>::vlanes()), vx_load(b + j + VTraits<v_float32>::vlanes()));
v_float32 t1 = v_sub(vx_load(a + j + vl), vx_load(b + j + vl));
v_d0 = v_muladd(t0, t0, v_d0);
v_float32 t2 = v_sub(vx_load(a + j + 2 * VTraits<v_float32>::vlanes()), vx_load(b + j + 2 * VTraits<v_float32>::vlanes()));
v_float32 t2 = v_sub(vx_load(a + j + 2 * vl), vx_load(b + j + 2 * vl));
v_d1 = v_muladd(t1, t1, v_d1);
v_float32 t3 = v_sub(vx_load(a + j + 3 * VTraits<v_float32>::vlanes()), vx_load(b + j + 3 * VTraits<v_float32>::vlanes()));
v_float32 t3 = v_sub(vx_load(a + j + 3 * vl), vx_load(b + j + 3 * vl));
v_d2 = v_muladd(t2, t2, v_d2);
v_d3 = v_muladd(t3, t3, v_d3);
}
d = v_reduce_sum(v_add(v_add(v_add(v_d0, v_d1), v_d2), v_d3));
v_d0 = v_add(v_add(v_d0, v_d1), v_add(v_d2, v_d3));
for (; j <= n - vl; j += vl)
{
v_float32 t0 = v_sub(vx_load(a + j), vx_load(b + j));
v_d0 = v_muladd(t0, t0, v_d0);
}
d = v_reduce_sum(v_d0);
#endif
for( ; j < n; j++ )
{
@@ -183,16 +192,20 @@ float normL1_(const float* a, const float* b, int n)
{
int j = 0; float d = 0.f;
#if (CV_SIMD || CV_SIMD_SCALABLE)
const int vl = VTraits<v_float32>::vlanes();
v_float32 v_d0 = vx_setzero_f32(), v_d1 = vx_setzero_f32();
v_float32 v_d2 = vx_setzero_f32(), v_d3 = vx_setzero_f32();
for (; j <= n - 4 * VTraits<v_float32>::vlanes(); j += 4 * VTraits<v_float32>::vlanes())
for (; j <= n - 4 * vl; j += 4 * vl)
{
v_d0 = v_add(v_d0, v_absdiff(vx_load(a + j), vx_load(b + j)));
v_d1 = v_add(v_d1, v_absdiff(vx_load(a + j + VTraits<v_float32>::vlanes()), vx_load(b + j + VTraits<v_float32>::vlanes())));
v_d2 = v_add(v_d2, v_absdiff(vx_load(a + j + 2 * VTraits<v_float32>::vlanes()), vx_load(b + j + 2 * VTraits<v_float32>::vlanes())));
v_d3 = v_add(v_d3, v_absdiff(vx_load(a + j + 3 * VTraits<v_float32>::vlanes()), vx_load(b + j + 3 * VTraits<v_float32>::vlanes())));
v_d1 = v_add(v_d1, v_absdiff(vx_load(a + j + vl), vx_load(b + j + vl)));
v_d2 = v_add(v_d2, v_absdiff(vx_load(a + j + 2 * vl), vx_load(b + j + 2 * vl)));
v_d3 = v_add(v_d3, v_absdiff(vx_load(a + j + 3 * vl), vx_load(b + j + 3 * vl)));
}
d = v_reduce_sum(v_add(v_add(v_add(v_d0, v_d1), v_d2), v_d3));
v_d0 = v_add(v_add(v_d0, v_d1), v_add(v_d2, v_d3));
for (; j <= n - vl; j += vl)
v_d0 = v_add(v_d0, v_absdiff(vx_load(a + j), vx_load(b + j)));
d = v_reduce_sum(v_d0);
#endif
for( ; j < n; j++ )
d += std::abs(a[j] - b[j]);
@@ -203,11 +216,14 @@ int normL1_(const uchar* a, const uchar* b, int n)
{
int j = 0, d = 0;
#if (CV_SIMD || CV_SIMD_SCALABLE)
for (; j <= n - 4 * VTraits<v_uint8>::vlanes(); j += 4 * VTraits<v_uint8>::vlanes())
const int vl = VTraits<v_uint8>::vlanes();
for (; j <= n - 4 * vl; j += 4 * vl)
d += v_reduce_sad(vx_load(a + j), vx_load(b + j)) +
v_reduce_sad(vx_load(a + j + VTraits<v_uint8>::vlanes()), vx_load(b + j + VTraits<v_uint8>::vlanes())) +
v_reduce_sad(vx_load(a + j + 2 * VTraits<v_uint8>::vlanes()), vx_load(b + j + 2 * VTraits<v_uint8>::vlanes())) +
v_reduce_sad(vx_load(a + j + 3 * VTraits<v_uint8>::vlanes()), vx_load(b + j + 3 * VTraits<v_uint8>::vlanes()));
v_reduce_sad(vx_load(a + j + vl), vx_load(b + j + vl)) +
v_reduce_sad(vx_load(a + j + 2 * vl), vx_load(b + j + 2 * vl)) +
v_reduce_sad(vx_load(a + j + 3 * vl), vx_load(b + j + 3 * vl));
for (; j <= n - vl; j += vl)
d += v_reduce_sad(vx_load(a + j), vx_load(b + j));
#endif
for( ; j < n; j++ )
d += std::abs(a[j] - b[j]);
+481 -48
View File
@@ -3,6 +3,7 @@
// of this distribution and at http://opencv.org/license.html
//
// Copyright (C) 2025, SpaceMIT Inc., all rights reserved.
// Copyright (C) 2026, Advanced Micro Devices, Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
#include "precomp.hpp"
@@ -1462,21 +1463,39 @@ struct MaskedNormInf_SIMD<uchar, int> {
result = std::max(result, (int)src[i]);
}
}
else if (cn == 4 && len >= VTraits<v_uint8>::vlanes()) {
const int vstep = VTraits<v_uint8>::vlanes();
v_uint8 acc = vx_setzero_u8();
int i = 0;
for (;;) {
if (i > len - vstep)
i = len - vstep;
v_uint8 c0, c1, c2, c3;
v_load_deinterleave(src + i * 4, c0, c1, c2, c3);
v_uint8 pmax = v_max(v_max(c0, c1), v_max(c2, c3));
v_uint8 m = v_gt(vx_load(mask + i), vx_setzero_u8());
acc = v_max(acc, v_and(pmax, m));
if (i >= len - vstep)
break;
i += vstep;
}
result = (int)v_reduce_max(acc);
}
else if (cn == 4) {
// len < one vector: pure scalar
for (int i = 0; i < len; i++) {
if (mask[i]) {
const uchar* elem = src + i * 4;
result = std::max(result, (int)std::max(std::max(elem[0], elem[1]),
std::max(elem[2], elem[3])));
}
}
}
else {
for (int i = 0; i < len; i++) {
if (mask[i]) {
const uchar* elem = src + i * cn;
int k = 0;
const int vstep = VTraits<v_uint8>::vlanes();
v_uint8 acc = vx_setzero_u8();
for (; k <= cn - vstep; k += vstep) {
acc = v_max(acc, vx_load(elem + k));
}
result = std::max(result, (int)v_reduce_max(acc));
for (; k < cn; k++)
for (int k = 0; k < cn; k++)
result = std::max(result, (int)elem[k]);
}
}
@@ -1508,22 +1527,30 @@ struct MaskedNormL1_SIMD<uchar, int> {
}
}
else {
for (int i = 0; i < len; i++) {
if (mask[i]) {
const uchar* elem = src + i * cn;
int k = 0;
const int vstep = VTraits<v_uint8>::vlanes() / 4;
v_uint32 acc = vx_setzero_u32();
for (; k <= cn - vstep; k += vstep) {
v_uint32 s = vx_load_expand_q(elem + k);
acc = v_add(acc, s);
const int vstep = VTraits<v_uint8>::vlanes() / 4;
if (cn >= vstep) {
for (int i = 0; i < len; i++) {
if (mask[i]) {
const uchar* elem = src + i * cn;
int k = 0;
v_uint32 acc = vx_setzero_u32();
for (; k <= cn - vstep; k += vstep) {
v_uint32 s = vx_load_expand_q(elem + k);
acc = v_add(acc, s);
}
result += (int)v_reduce_sum(acc);
for (; k < cn; k++)
result += elem[k];
}
}
}
else {
for (int i = 0; i < len; i++) {
if (mask[i]) {
const uchar* elem = src + i * cn;
for (int k = 0; k < cn; k++)
result += elem[k];
}
result += (int)v_reduce_sum(acc);
for (; k < cn; k++)
result += elem[k];
}
}
}
@@ -1738,6 +1765,428 @@ normL2_(const T* src, const uchar* mask, ST* _result, int len, int cn) {
return 0;
}
#if (CV_SIMD || CV_SIMD_SCALABLE)
inline v_uint8 v_diffmask(const v_uint8& d, const uchar* m) { return v_and(d, v_gt(vx_load(m), vx_setzero_u8())); }
inline v_uint16 v_diffmask(const v_uint16& d, const uchar* m) { return v_and(d, v_gt(vx_load_expand(m), vx_setzero_u16())); }
inline v_uint32 v_diffmask(const v_uint32& d, const uchar* m) { return v_and(d, v_gt(vx_load_expand_q(m), vx_setzero_u32())); }
inline v_float32 v_diffmask(const v_float32& d, const uchar* m) {
v_uint32 cm = v_gt(vx_load_expand_q(m), vx_setzero_u32());
return v_reinterpret_as_f32(v_and(v_reinterpret_as_u32(d), cm));
}
#endif
template <typename T, typename ST>
struct MaskedNormDiffInf_SIMD {
inline ST operator()(const T* s1, const T* s2, const uchar* mask, int len, int cn) const {
ST result = 0;
#if (CV_SIMD || CV_SIMD_SCALABLE)
const int vstep = VTraits<decltype(vx_load(s1))>::vlanes();
if (cn == 1 && len >= vstep) {
auto acc = v_diffmask(v_absdiff(vx_load(s1), vx_load(s2)), mask);
int i = vstep;
for (; i <= len - vstep; i += vstep)
acc = v_max(acc, v_diffmask(v_absdiff(vx_load(s1 + i), vx_load(s2 + i)), mask + i));
result = (ST)v_reduce_max(acc);
for (; i < len; i++) if (mask[i]) result = std::max(result, (ST)std::abs((double)s1[i] - (double)s2[i]));
return result;
}
#endif
for (int i = 0; i < len; i++) if (mask[i]) {
const T* e1 = s1 + i*cn; const T* e2 = s2 + i*cn;
for (int k = 0; k < cn; k++) result = std::max(result, (ST)std::abs((double)e1[k] - (double)e2[k]));
}
return result;
}
};
template <typename T, typename ST>
struct MaskedNormDiffL1_SIMD {
inline ST operator()(const T* s1, const T* s2, const uchar* mask, int len, int cn) const {
ST s = 0;
for (int i = 0; i < len; i++) if (mask[i]) {
const T* e1 = s1 + i*cn; const T* e2 = s2 + i*cn;
for (int k = 0; k < cn; k++) s += std::abs(e1[k] - e2[k]);
}
return s;
}
};
template <typename T, typename ST>
struct MaskedNormDiffL2_SIMD {
inline ST operator()(const T* s1, const T* s2, const uchar* mask, int len, int cn) const {
ST s = 0;
for (int i = 0; i < len; i++) if (mask[i]) {
const T* e1 = s1 + i*cn; const T* e2 = s2 + i*cn;
for (int k = 0; k < cn; k++) { ST v = (ST)e1[k] - (ST)e2[k]; s += v*v; }
}
return s;
}
};
#if (CV_SIMD || CV_SIMD_SCALABLE)
// Shared scalar cn>1 fallback for masked norm-diff (RT = accumulator type).
// Used by every SIMD specialization's multi-channel branch.
template<typename T, typename RT>
static inline RT maskedNormDiffL1Tail(const T* s1, const T* s2, const uchar* mask, int len, int cn, RT result) {
for (int i = 0; i < len; i++) if (mask[i]) {
const T* e1 = s1 + i*cn; const T* e2 = s2 + i*cn;
for (int k = 0; k < cn; k++) result += (RT)std::abs((RT)e1[k] - (RT)e2[k]);
}
return result;
}
template<typename T, typename RT>
static inline RT maskedNormDiffL2Tail(const T* s1, const T* s2, const uchar* mask, int len, int cn, RT result) {
for (int i = 0; i < len; i++) if (mask[i]) {
const T* e1 = s1 + i*cn; const T* e2 = s2 + i*cn;
for (int k = 0; k < cn; k++) { RT v = (RT)e1[k] - (RT)e2[k]; result += v*v; }
}
return result;
}
// Shared 8-bit cn==1 masked kernels (uchar/schar): v_absdiff() yields v_uint8
// for both, so a single typename-T body serves both depths.
template<typename T>
static inline int maskedNormDiffL1_8(const T* s1, const T* s2, const uchar* mask, int len) {
int i = 0; const int vstep = VTraits<v_uint8>::vlanes();
const v_uint8 one = vx_setall_u8(1);
v_uint32 acc = vx_setzero_u32();
for (; i <= len - vstep; i += vstep) {
v_uint8 m = v_gt(vx_load(mask + i), vx_setzero_u8());
v_uint8 ad = v_and(v_absdiff(vx_load(s1 + i), vx_load(s2 + i)), m);
acc = v_dotprod_expand_fast(ad, one, acc); // sum of masked |diff|
}
int result = (int)v_reduce_sum(acc);
for (; i < len; i++) if (mask[i]) result += std::abs((int)s1[i] - (int)s2[i]);
return result;
}
template<typename T>
static inline int maskedNormDiffL2_8(const T* s1, const T* s2, const uchar* mask, int len) {
int i = 0; const int vstep = VTraits<v_uint8>::vlanes();
v_uint32 acc = vx_setzero_u32();
for (; i <= len - vstep; i += vstep) {
v_uint8 m = v_gt(vx_load(mask + i), vx_setzero_u8());
v_uint8 ad = v_and(v_absdiff(vx_load(s1 + i), vx_load(s2 + i)), m);
acc = v_dotprod_expand_fast(ad, ad, acc); // sum of masked diff^2
}
int result = (int)v_reduce_sum(acc);
for (; i < len; i++) if (mask[i]) { int v = (int)s1[i] - (int)s2[i]; result += v*v; }
return result;
}
template<>
struct MaskedNormDiffInf_SIMD<uchar, int> {
int operator()(const uchar* s1, const uchar* s2, const uchar* mask, int len, int cn) const {
int result = 0;
const int vstep = VTraits<v_uint8>::vlanes();
if (cn == 1) {
int i = 0;
v_uint8 acc = vx_setzero_u8();
for (; i <= len - vstep; i += vstep) {
v_uint8 ad = v_absdiff(vx_load(s1 + i), vx_load(s2 + i));
v_uint8 m = v_gt(vx_load(mask + i), vx_setzero_u8());
acc = v_max(acc, v_and(ad, m));
}
result = (int)v_reduce_max(acc);
for (; i < len; i++) if (mask[i]) result = std::max(result, std::abs((int)s1[i] - (int)s2[i]));
}
else if (cn == 4 && len >= vstep) {
v_uint8 acc = vx_setzero_u8();
int i = 0;
for (;;) {
if (i > len - vstep) i = len - vstep; // back-step (max is idempotent)
v_uint8 a0,a1,a2,a3,b0,b1,b2,b3;
v_load_deinterleave(s1 + i*4, a0,a1,a2,a3);
v_load_deinterleave(s2 + i*4, b0,b1,b2,b3);
v_uint8 ad = v_max(v_max(v_absdiff(a0,b0), v_absdiff(a1,b1)),
v_max(v_absdiff(a2,b2), v_absdiff(a3,b3)));
v_uint8 m = v_gt(vx_load(mask + i), vx_setzero_u8());
acc = v_max(acc, v_and(ad, m));
if (i >= len - vstep) break;
i += vstep;
}
result = (int)v_reduce_max(acc);
}
else {
for (int i = 0; i < len; i++) if (mask[i]) {
const uchar* e1 = s1 + i*cn; const uchar* e2 = s2 + i*cn;
for (int k = 0; k < cn; k++) result = std::max(result, std::abs((int)e1[k] - (int)e2[k]));
}
}
return result;
}
};
template<>
struct MaskedNormDiffInf_SIMD<int, int> {
int operator()(const int* s1, const int* s2, const uchar* mask, int len, int cn) const {
int result = 0;
const int vstep = VTraits<v_int32>::vlanes();
if (cn == 1 && len >= vstep) {
// Use wrapping int subtraction (v_abs(v_sub)) to match the non-masked
// NormDiffInf_SIMD<int,int> kernel. v_absdiff would compute the true
// unsigned |a-b| which can exceed INT_MAX and overflow on the cast to int.
v_uint32 acc = vx_setzero_u32();
int i = 0;
for (; i <= len - vstep; i += vstep) {
v_uint32 ad = v_abs(v_sub(vx_load(s1 + i), vx_load(s2 + i)));
v_uint32 m = v_gt(vx_load_expand_q(mask + i), vx_setzero_u32());
acc = v_max(acc, v_and(ad, m));
}
result = (int)v_reduce_max(acc);
for (; i < len; i++) if (mask[i]) result = std::max(result, (int)std::abs(s1[i] - s2[i]));
return result;
}
for (int i = 0; i < len; i++) if (mask[i]) {
const int* e1 = s1 + i*cn; const int* e2 = s2 + i*cn;
for (int k = 0; k < cn; k++) result = std::max(result, (int)std::abs(e1[k] - e2[k]));
}
return result;
}
};
template<>
struct MaskedNormDiffL1_SIMD<uchar, int> {
int operator()(const uchar* s1, const uchar* s2, const uchar* mask, int len, int cn) const {
if (cn == 1) return maskedNormDiffL1_8(s1, s2, mask, len);
if (cn == 4) {
int result = 0;
int i = 0;
const int vstep = VTraits<v_uint8>::vlanes();
const v_uint8 one = vx_setall_u8(1);
v_uint32 acc = vx_setzero_u32();
for (; i <= len - vstep; i += vstep) {
v_uint8 a0,a1,a2,a3,b0,b1,b2,b3;
v_load_deinterleave(s1 + i*4, a0,a1,a2,a3);
v_load_deinterleave(s2 + i*4, b0,b1,b2,b3);
v_uint8 m = v_gt(vx_load(mask + i), vx_setzero_u8());
acc = v_dotprod_expand_fast(v_and(v_absdiff(a0,b0), m), one, acc);
acc = v_dotprod_expand_fast(v_and(v_absdiff(a1,b1), m), one, acc);
acc = v_dotprod_expand_fast(v_and(v_absdiff(a2,b2), m), one, acc);
acc = v_dotprod_expand_fast(v_and(v_absdiff(a3,b3), m), one, acc);
}
result = (int)v_reduce_sum(acc);
for (; i < len; i++) if (mask[i]) {
const uchar* e1 = s1 + i*4; const uchar* e2 = s2 + i*4;
for (int k = 0; k < 4; k++) result += std::abs((int)e1[k] - (int)e2[k]);
}
return result;
}
return maskedNormDiffL1Tail<uchar, int>(s1, s2, mask, len, cn, 0);
}
};
template<>
struct MaskedNormDiffL2_SIMD<uchar, int> {
int operator()(const uchar* s1, const uchar* s2, const uchar* mask, int len, int cn) const {
if (cn == 1) return maskedNormDiffL2_8(s1, s2, mask, len);
if (cn == 4) {
int result = 0;
int i = 0;
const int vstep = VTraits<v_uint8>::vlanes();
v_uint32 acc = vx_setzero_u32();
for (; i <= len - vstep; i += vstep) {
v_uint8 a0,a1,a2,a3,b0,b1,b2,b3;
v_load_deinterleave(s1 + i*4, a0,a1,a2,a3);
v_load_deinterleave(s2 + i*4, b0,b1,b2,b3);
v_uint8 m = v_gt(vx_load(mask + i), vx_setzero_u8());
v_uint8 d0 = v_and(v_absdiff(a0,b0), m), d1 = v_and(v_absdiff(a1,b1), m);
v_uint8 d2 = v_and(v_absdiff(a2,b2), m), d3 = v_and(v_absdiff(a3,b3), m);
acc = v_dotprod_expand_fast(d0, d0, acc);
acc = v_dotprod_expand_fast(d1, d1, acc);
acc = v_dotprod_expand_fast(d2, d2, acc);
acc = v_dotprod_expand_fast(d3, d3, acc);
}
result = (int)v_reduce_sum(acc);
for (; i < len; i++) if (mask[i]) {
const uchar* e1 = s1 + i*4; const uchar* e2 = s2 + i*4;
for (int k = 0; k < 4; k++) { int v = (int)e1[k] - (int)e2[k]; result += v*v; }
}
return result;
}
return maskedNormDiffL2Tail<uchar, int>(s1, s2, mask, len, cn, 0);
}
};
template<>
struct MaskedNormDiffInf_SIMD<double, double> {
inline double operator()(const double* s1, const double* s2, const uchar* mask, int len, int cn) const {
double result = 0.0;
for (int i = 0; i < len; i++) if (mask[i]) {
const double* e1 = s1 + i*cn; const double* e2 = s2 + i*cn;
for (int k = 0; k < cn; k++) result = std::max(result, std::abs(e1[k] - e2[k]));
}
return result;
}
};
#if CV_SIMD_64F || CV_SIMD_SCALABLE_64F
template<>
struct MaskedNormDiffL1_SIMD<int, double> {
double operator()(const int* s1, const int* s2, const uchar* mask, int len, int cn) const {
double result = 0.0;
if (cn == 1) {
int i = 0;
const int vstep = VTraits<v_int32>::vlanes();
v_float64 acc = vx_setzero_f64();
for (; i <= len - vstep; i += vstep) {
v_uint32 m = v_gt(vx_load_expand_q(mask + i), vx_setzero_u32());
v_uint32 ad = v_and(v_abs(v_sub(vx_load(s1 + i), vx_load(s2 + i))), m);
v_int32 adi = v_reinterpret_as_s32(ad);
acc = v_add(acc, v_cvt_f64(adi));
acc = v_add(acc, v_cvt_f64_high(adi));
}
result = v_reduce_sum(acc);
// Use wrapping int subtraction to match the SIMD body (v_abs(v_sub))
// and the non-masked NormDiffL1_SIMD<int,double> kernel.
for (; i < len; i++) if (mask[i]) result += std::abs(s1[i] - s2[i]);
}
else {
result = maskedNormDiffL1Tail<int, double>(s1, s2, mask, len, cn, 0.0);
}
return result;
}
};
// Masked L1/L2 SIMD for the remaining depths (cn==1). Without these the masked
// norm-diff for 8s/16u/16s/32s/32f fell back to the scalar base template, which
// the compiler autovectorizes poorly once AVX-512 dispatch is enabled.
template<> struct MaskedNormDiffL1_SIMD<schar, int> {
int operator()(const schar* s1, const schar* s2, const uchar* mask, int len, int cn) const {
if (cn == 1) return maskedNormDiffL1_8(s1, s2, mask, len);
return maskedNormDiffL1Tail<schar, int>(s1, s2, mask, len, cn, 0);
}
};
// Shared 16-bit masked L1 kernel (ushort/short): v_absdiff() yields v_uint16
// for both, so a single typename-T body serves both depths.
template<typename T>
static inline int maskedNormDiffL1_16(const T* s1, const T* s2, const uchar* mask, int len, int cn) {
int result = 0;
if (cn == 1) {
int i = 0; const int vstep = VTraits<v_uint16>::vlanes();
v_uint32 acc = vx_setzero_u32();
for (; i <= len - vstep; i += vstep) {
v_uint16 m = v_gt(vx_load_expand(mask + i), vx_setzero_u16());
v_uint16 ad = v_and(v_absdiff(vx_load(s1 + i), vx_load(s2 + i)), m);
v_uint32 lo, hi; v_expand(ad, lo, hi);
acc = v_add(acc, v_add(lo, hi));
}
result = (int)v_reduce_sum(acc);
for (; i < len; i++) if (mask[i]) result += std::abs((int)s1[i] - (int)s2[i]);
} else {
result = maskedNormDiffL1Tail<T, int>(s1, s2, mask, len, cn, 0);
}
return result;
}
template<> struct MaskedNormDiffL1_SIMD<ushort, int> {
int operator()(const ushort* s1, const ushort* s2, const uchar* mask, int len, int cn) const
{ return maskedNormDiffL1_16(s1, s2, mask, len, cn); } };
template<> struct MaskedNormDiffL1_SIMD<short, int> {
int operator()(const short* s1, const short* s2, const uchar* mask, int len, int cn) const
{ return maskedNormDiffL1_16(s1, s2, mask, len, cn); } };
template<>
struct MaskedNormDiffL1_SIMD<float, double> {
double operator()(const float* s1, const float* s2, const uchar* mask, int len, int cn) const {
double result = 0.0;
if (cn == 1) {
int i = 0; const int vstep = VTraits<v_float32>::vlanes();
v_float64 acc0 = vx_setzero_f64(), acc1 = vx_setzero_f64();
for (; i <= len - vstep; i += vstep) {
v_float32 m = v_reinterpret_as_f32(v_gt(vx_load_expand_q(mask + i), vx_setzero_u32()));
v_float32 ad = v_and(v_absdiff(vx_load(s1 + i), vx_load(s2 + i)), m);
acc0 = v_add(acc0, v_cvt_f64(ad));
acc1 = v_add(acc1, v_cvt_f64_high(ad));
}
result = v_reduce_sum(v_add(acc0, acc1));
for (; i < len; i++) if (mask[i]) result += std::abs((double)s1[i] - (double)s2[i]);
} else {
result = maskedNormDiffL1Tail<float, double>(s1, s2, mask, len, cn, 0.0);
}
return result;
}
};
template<> struct MaskedNormDiffL2_SIMD<schar, int> {
int operator()(const schar* s1, const schar* s2, const uchar* mask, int len, int cn) const {
if (cn == 1) return maskedNormDiffL2_8(s1, s2, mask, len);
return maskedNormDiffL2Tail<schar, int>(s1, s2, mask, len, cn, 0);
}
};
// Shared 16-bit masked L2 kernel (ushort/short): v_absdiff() yields v_uint16
// for both, so a single typename-T body serves both depths.
template<typename T>
static inline double maskedNormDiffL2_16(const T* s1, const T* s2, const uchar* mask, int len, int cn) {
double result = 0.0;
if (cn == 1) {
int i = 0; const int vstep = VTraits<v_uint16>::vlanes();
v_float64 acc = vx_setzero_f64();
for (; i <= len - vstep; i += vstep) {
v_uint16 m = v_gt(vx_load_expand(mask + i), vx_setzero_u16());
v_uint16 ad = v_and(v_absdiff(vx_load(s1 + i), vx_load(s2 + i)), m);
v_uint64 u = v_dotprod_expand_fast(ad, ad);
acc = v_add(acc, v_cvt_f64(v_reinterpret_as_s64(u)));
}
result = v_reduce_sum(acc);
for (; i < len; i++) if (mask[i]) { double v = (double)s1[i] - (double)s2[i]; result += v*v; }
} else {
result = maskedNormDiffL2Tail<T, double>(s1, s2, mask, len, cn, 0.0);
}
return result;
}
template<> struct MaskedNormDiffL2_SIMD<ushort, double> {
double operator()(const ushort* s1, const ushort* s2, const uchar* mask, int len, int cn) const
{ return maskedNormDiffL2_16(s1, s2, mask, len, cn); } };
template<> struct MaskedNormDiffL2_SIMD<short, double> {
double operator()(const short* s1, const short* s2, const uchar* mask, int len, int cn) const
{ return maskedNormDiffL2_16(s1, s2, mask, len, cn); } };
template<>
struct MaskedNormDiffL2_SIMD<int, double> {
double operator()(const int* s1, const int* s2, const uchar* mask, int len, int cn) const {
double result = 0.0;
if (cn == 1) {
int i = 0; const int vstep = VTraits<v_int32>::vlanes();
v_float64 r0 = vx_setzero_f64(), r1 = vx_setzero_f64();
for (; i <= len - vstep; i += vstep) {
v_uint32 m = v_gt(vx_load_expand_q(mask + i), vx_setzero_u32());
v_uint32 ad = v_and(v_absdiff(vx_load(s1 + i), vx_load(s2 + i)), m);
v_uint64 e0, e1; v_expand(ad, e0, e1);
v_float64 f0 = v_cvt_f64(v_reinterpret_as_s64(e0)), f1 = v_cvt_f64(v_reinterpret_as_s64(e1));
r0 = v_fma(f0, f0, r0); r1 = v_fma(f1, f1, r1);
}
result = v_reduce_sum(v_add(r0, r1));
for (; i < len; i++) if (mask[i]) { double v = (double)s1[i] - (double)s2[i]; result += v*v; }
} else {
result = maskedNormDiffL2Tail<int, double>(s1, s2, mask, len, cn, 0.0);
}
return result;
}
};
template<>
struct MaskedNormDiffL2_SIMD<float, double> {
double operator()(const float* s1, const float* s2, const uchar* mask, int len, int cn) const {
double result = 0.0;
if (cn == 1) {
int i = 0; const int vstep = VTraits<v_float32>::vlanes();
v_float64 r0 = vx_setzero_f64(), r1 = vx_setzero_f64();
for (; i <= len - vstep; i += vstep) {
v_float32 m = v_reinterpret_as_f32(v_gt(vx_load_expand_q(mask + i), vx_setzero_u32()));
v_float32 ad = v_and(v_absdiff(vx_load(s1 + i), vx_load(s2 + i)), m);
v_float64 f0 = v_cvt_f64(ad), f1 = v_cvt_f64_high(ad);
r0 = v_fma(f0, f0, r0); r1 = v_fma(f1, f1, r1);
}
result = v_reduce_sum(v_add(r0, r1));
for (; i < len; i++) if (mask[i]) { double v = (double)s1[i] - (double)s2[i]; result += v*v; }
} else {
result = maskedNormDiffL2Tail<float, double>(s1, s2, mask, len, cn, 0.0);
}
return result;
}
};
#endif
#endif
template<typename T, typename ST> int
normDiffInf_(const T* src1, const T* src2, const uchar* mask, ST* _result, int len, int cn) {
ST result = *_result;
@@ -1745,13 +2194,8 @@ normDiffInf_(const T* src1, const T* src2, const uchar* mask, ST* _result, int l
NormDiffInf_SIMD<T, ST> op;
result = std::max(result, op(src1, src2, len*cn));
} else {
for( int i = 0; i < len; i++, src1 += cn, src2 += cn ) {
if( mask[i] ) {
for( int k = 0; k < cn; k++ ) {
result = std::max(result, (ST)std::abs(src1[k] - src2[k]));
}
}
}
MaskedNormDiffInf_SIMD<T, ST> op;
result = std::max(result, op(src1, src2, mask, len, cn));
}
*_result = result;
return 0;
@@ -1765,13 +2209,8 @@ normDiffL1_(const T* src1, const T* src2, const uchar* mask, ST* _result, int le
result += op(src1, src2, len*cn);
}
else {
for( int i = 0; i < len; i++, src1 += cn, src2 += cn ) {
if( mask[i] ) {
for( int k = 0; k < cn; k++ ) {
result += std::abs(src1[k] - src2[k]);
}
}
}
MaskedNormDiffL1_SIMD<T, ST> op;
result += op(src1, src2, mask, len, cn);
}
*_result = result;
return 0;
@@ -1784,14 +2223,8 @@ normDiffL2_(const T* src1, const T* src2, const uchar* mask, ST* _result, int le
NormDiffL2_SIMD<T, ST> op;
result += op(src1, src2, len*cn);
} else {
for( int i = 0; i < len; i++, src1 += cn, src2 += cn ) {
if( mask[i] ) {
for( int k = 0; k < cn; k++ ) {
ST v = (ST)src1[k] - (ST)src2[k];
result += v*v;
}
}
}
MaskedNormDiffL2_SIMD<T, ST> op;
result += op(src1, src2, mask, len, cn);
}
*_result = result;
return 0;
+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
+4 -4
View File
@@ -266,7 +266,7 @@ int decodeFormat( const char* dt, int* fmt_pairs, int max_len )
int calcElemSize( const char* dt, int initial_size )
{
int size = 0;
int fmt_pairs[CV_FS_MAX_FMT_PAIRS], i, fmt_pair_count;
int fmt_pairs[CV_FS_MAX_FMT_PAIRS*2], i, fmt_pair_count;
int comp_size;
fmt_pair_count = decodeFormat( dt, fmt_pairs, CV_FS_MAX_FMT_PAIRS );
@@ -316,7 +316,7 @@ int calcStructSize( const char* dt, int initial_size )
int decodeSimpleFormat( const char* dt )
{
int elem_type = -1;
int fmt_pairs[CV_FS_MAX_FMT_PAIRS], fmt_pair_count;
int fmt_pairs[CV_FS_MAX_FMT_PAIRS*2], fmt_pair_count;
fmt_pair_count = decodeFormat( dt, fmt_pairs, CV_FS_MAX_FMT_PAIRS );
if( fmt_pair_count != 1 || fmt_pairs[0] >= CV_CN_MAX)
@@ -2376,7 +2376,7 @@ FileNode::operator float() const
if( type == INT )
{
return (float)readInt(p);
return (float)readLong(p);
}
else if( type == REAL )
{
@@ -2397,7 +2397,7 @@ FileNode::operator double() const
if( type == INT )
{
return (double)readInt(p);
return (double)readLong(p);
}
else if( type == REAL )
{
@@ -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);
+37 -5
View File
@@ -9,18 +9,50 @@
namespace cv {
typedef void (*ReduceSumFunc)(const Mat& src, Mat& dst);
ReduceSumFunc getReduceCSumFunc(int sdepth, int ddepth);
ReduceSumFunc getReduceRSumFunc(int sdepth, int ddepth);
typedef void (*ReduceFunc)( const Mat& src, Mat& dst );
ReduceFunc getReduceCSumFunc(int sdepth, int ddepth);
ReduceFunc getReduceCAvgFunc(int sdepth, int ddepth);
ReduceFunc getReduceCMaxFunc(int sdepth, int ddepth);
ReduceFunc getReduceCMinFunc(int sdepth, int ddepth);
ReduceFunc getReduceCSum2Func(int sdepth, int ddepth);
ReduceFunc getReduceRSumFunc(int sdepth, int ddepth);
ReduceSumFunc getReduceCSumFunc(int sdepth, int ddepth)
ReduceFunc getReduceCSumFunc(int sdepth, int ddepth)
{
CV_INSTRUMENT_REGION();
CV_CPU_DISPATCH(getReduceCSumFunc, (sdepth, ddepth),
CV_CPU_DISPATCH_MODES_ALL);
}
ReduceSumFunc getReduceRSumFunc(int sdepth, int ddepth)
ReduceFunc getReduceCAvgFunc(int sdepth, int ddepth)
{
CV_INSTRUMENT_REGION();
CV_CPU_DISPATCH(getReduceCAvgFunc, (sdepth, ddepth),
CV_CPU_DISPATCH_MODES_ALL);
}
ReduceFunc getReduceCMaxFunc(int sdepth, int ddepth)
{
CV_INSTRUMENT_REGION();
CV_CPU_DISPATCH(getReduceCMaxFunc, (sdepth, ddepth),
CV_CPU_DISPATCH_MODES_ALL);
}
ReduceFunc getReduceCMinFunc(int sdepth, int ddepth)
{
CV_INSTRUMENT_REGION();
CV_CPU_DISPATCH(getReduceCMinFunc, (sdepth, ddepth),
CV_CPU_DISPATCH_MODES_ALL);
}
ReduceFunc getReduceCSum2Func(int sdepth, int ddepth)
{
CV_INSTRUMENT_REGION();
CV_CPU_DISPATCH(getReduceCSum2Func, (sdepth, ddepth),
CV_CPU_DISPATCH_MODES_ALL);
}
ReduceFunc getReduceRSumFunc(int sdepth, int ddepth)
{
CV_INSTRUMENT_REGION();
CV_CPU_DISPATCH(getReduceRSumFunc, (sdepth, ddepth),
+74 -5
View File
@@ -7,12 +7,20 @@
namespace cv {
CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN
typedef void (*ReduceSumFunc)(const Mat& src, Mat& dst);
ReduceSumFunc getReduceCSumFunc(int sdepth, int ddepth);
ReduceSumFunc getReduceRSumFunc(int sdepth, int ddepth);
typedef void (*ReduceFunc)( const Mat& src, Mat& dst );
ReduceFunc getReduceCSumFunc(int sdepth, int ddepth);
ReduceFunc getReduceCAvgFunc(int sdepth, int ddepth);
ReduceFunc getReduceCMaxFunc(int sdepth, int ddepth);
ReduceFunc getReduceCMinFunc(int sdepth, int ddepth);
ReduceFunc getReduceCSum2Func(int sdepth, int ddepth);
ReduceFunc getReduceRSumFunc(int sdepth, int ddepth);
#ifndef CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY
#if (CV_SIMD || CV_SIMD_SCALABLE)
#include "reduce_c.simd.hpp"
#endif
// =====================================================================
// Col reduce SUM (dim=1): sum each row into cn output values
// =====================================================================
@@ -1089,7 +1097,7 @@ static void reduceRowSum_64f64f(const Mat& srcmat, Mat& dstmat)
// Dispatchers
// =====================================================================
ReduceSumFunc getReduceCSumFunc(int sdepth, int ddepth)
ReduceFunc getReduceCSumFunc(int sdepth, int ddepth)
{
#if (CV_SIMD || CV_SIMD_SCALABLE)
if (sdepth == CV_8U && ddepth == CV_32S) return reduceColSum_8u32s;
@@ -1108,7 +1116,68 @@ ReduceSumFunc getReduceCSumFunc(int sdepth, int ddepth)
return nullptr;
}
ReduceSumFunc getReduceRSumFunc(int sdepth, int ddepth)
ReduceFunc getReduceCAvgFunc(int sdepth, int ddepth)
{
return getReduceCSumFunc(sdepth, ddepth);
}
ReduceFunc getReduceCMaxFunc(int sdepth, int ddepth)
{
#if (CV_SIMD || CV_SIMD_SCALABLE)
if (sdepth == CV_8U && ddepth == CV_8U) return reduceColMax_8u;
if (sdepth == CV_16U && ddepth == CV_16U) return reduceColMax_16u;
if (sdepth == CV_16S && ddepth == CV_16S) return reduceColMax_16s;
if (sdepth == CV_32F && ddepth == CV_32F) return reduceColMax_32f;
#if (CV_SIMD_64F || CV_SIMD_SCALABLE_64F)
if (sdepth == CV_64F && ddepth == CV_64F) return reduceColMax_64f;
#endif
#else
CV_UNUSED(sdepth);
CV_UNUSED(ddepth);
#endif
return nullptr;
}
ReduceFunc getReduceCMinFunc(int sdepth, int ddepth)
{
#if (CV_SIMD || CV_SIMD_SCALABLE)
if (sdepth == CV_8U && ddepth == CV_8U) return reduceColMin_8u;
if (sdepth == CV_16U && ddepth == CV_16U) return reduceColMin_16u;
if (sdepth == CV_16S && ddepth == CV_16S) return reduceColMin_16s;
if (sdepth == CV_32F && ddepth == CV_32F) return reduceColMin_32f;
#if (CV_SIMD_64F || CV_SIMD_SCALABLE_64F)
if (sdepth == CV_64F && ddepth == CV_64F) return reduceColMin_64f;
#endif
#else
CV_UNUSED(sdepth);
CV_UNUSED(ddepth);
#endif
return nullptr;
}
ReduceFunc getReduceCSum2Func(int sdepth, int ddepth)
{
#if (CV_SIMD || CV_SIMD_SCALABLE)
if (sdepth == CV_8U && ddepth == CV_32S) return reduceColSum2_8u32s;
if (sdepth == CV_8U && ddepth == CV_32F) return reduceColSum2_8u32f;
if (sdepth == CV_8U && ddepth == CV_64F) return reduceColSum2_8u64f;
if (sdepth == CV_16U && ddepth == CV_32F) return reduceColSum2_16u32f;
if (sdepth == CV_16S && ddepth == CV_32F) return reduceColSum2_16s32f;
if (sdepth == CV_32F && ddepth == CV_32F) return reduceColSum2_32f32f;
#if (CV_SIMD_64F || CV_SIMD_SCALABLE_64F)
if (sdepth == CV_16U && ddepth == CV_64F) return reduceColSum2_16u64f;
if (sdepth == CV_16S && ddepth == CV_64F) return reduceColSum2_16s64f;
if (sdepth == CV_32F && ddepth == CV_64F) return reduceColSum2_32f64f;
if (sdepth == CV_64F && ddepth == CV_64F) return reduceColSum2_64f64f;
#endif
#else
CV_UNUSED(sdepth);
CV_UNUSED(ddepth);
#endif
return nullptr;
}
ReduceFunc getReduceRSumFunc(int sdepth, int ddepth)
{
#if (CV_SIMD || CV_SIMD_SCALABLE)
if (sdepth == CV_8U && ddepth == CV_32S) return reduceRowSum_8u32s;

Some files were not shown because too many files have changed in this diff Show More