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

Compare commits

...

248 Commits

Author SHA1 Message Date
Alexander Smorkalov 8d69623051 Merge pull request #29529 from ajayraj30002:ajayraj30002-patch-3
cmake: set policy CMP0218 to NEW to suppress deprecation warning
2026-07-21 12:39:10 +03:00
Arman Rostami 279bc4a279 Merge pull request #27823 from armanrasta:5.x
Add ColorHashTSDFVolume implementation #27823

# Add ColorHashTSDFVolume implementation  [[#25155](https://github.com/opencv/opencv/issues/25155)]
## Description
Added a new ColorHashTSDFVolume implementation that combines the benefits of HashTSDFVolume's efficient spatial hashing with color support. This provides memory-efficient RGB-D fusion with better performance compared to regular ColorTSDFVolume.

### Key Features
- Hash-based spatial data structure for efficient storage
- Color integration during volume updates
- Raycast with color interpolation
- Compatible with existing TSDF interfaces
- CPU implementation with parallel processing support

### Implementation Details
- Added new ColorHashTSDFVolume class with create() factory method
- ColorVoxel structure combining TSDF and RGB data
- Spatial hashing for efficient voxel lookup
- Weighted running average for color updates
- Trilinear interpolation during raycasting
- Unit tests for basic operations and edge cases

### Files Modified/Added
- modules/3d/src/rgbd/color_hash_volume.hpp - New header defining ColorHashTSDFVolume interface
- modules/3d/src/rgbd/color_hash_volume.cpp - Implementation of ColorHashTSDFVolume
- modules/3d/test/test_color_hash_volume.cpp - Unit tests

### Performance
The implementation uses spatial hashing to only store voxels near surfaces, significantly reducing memory usage compared to regular ColorTSDFVolume while maintaining similar processing speed.

### Testing
Added unit tests that verify:
- Basic integration and raycasting operations
- Empty volume handling
- Memory usage patterns

### Future Work
- GPU/OpenCL implementation
- Additional color interpolation methods
- Extended comparison tests with other volume types
2026-07-20 10:40:19 +03:00
Vadim Pisarevsky f968fb969f Broadcasting element-wise engine for cv::Mat (+ cv::texpr) (#29426)
* experimental new arithmetics; work-in-progress

* continue working on new-gen arithmetic expressions

* * improved performance of the new add on small arrays
* added sub
* extended tests

* fixed potential bug when adding multi-channel array and a single-channel scalar

* improved const handling

* * added copyMask
* added mul/dev (without scale so far)

* * accelerated mul
* addedd scale to mul and div

* * done substantial refactoring; however a few more rounds of refactoring are ahead.
* added min, max, absdiff, addweighted.

* improved performance of the new arithmetic functions, but some of them are still slow, e.g. operations with mask have some bugs (that affect speed, not accuracy).

* * further (significantly) accelerated several functions, especially on small arrays: mul, binary ops with mask

* further polished the new arithmetic engine

* started integration of the new element-wise arithmetic engine into core

* big step forward. We now use the new engine inside cv::add, subtract, multiply, divide, absdiff, min and max.

* big progress:
* added bitwise operations
* fixed and accelerated compare
* ported regression tests to test new broadcasting behaviour of arithmetic functions

* lot's of improvements in compare, divide, addWeighted!

* lot's of small and big performance improvements in the new arithmetics

* * some more optimizations; parsing texpr-expressions is now faster as well

* port new_arithm to Linux/x86: dispatch guards, scalar-Mat compat fallback, dnn shape-contract fixes

Core:
- arithm.simd.hpp: CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY guards (the file is included
  once per dispatched mode on x86), vx_load_expand instead of the 128-bit v_load_expand,
  VTraits::vlanes() instead of ::nlanes
- arithm.cpp/precomp.hpp: compat fallback for scalar-like Mat operands (1x1, 1xcn/cnx1,
  4x1 CV_64F - java/python tuples, operator-(Mat, Matx)): treated as a per-channel scalar
  ONLY when the shapes are not broadcast-compatible, so every valid numpy-style broadcast
  keeps its meaning and calls that would otherwise throw get the 4.x semantics

DNN (fallout of the stricter shape semantics, found by the new engine):
- dict.hpp: DictValue relied on fresh AutoBuffer having size()==fixed_size; allocate explicitly
- batch_norm: weights_/bias_ are 1-D [n] now; 0/1-D forward runs on exact-shape 1-D views
- net_impl2: extend the post-forward sanity check to non-temp outputs - a layer that
  reallocates its preallocated output tensor now fails loudly instead of silently
  detaching the result from the graph
- LSTM/LSTM2 batchwise (layout=1): getMemoryShapes now matches what forward() writes
  (ONNX: Y=(batch,seq,dirs,hid), Yh/Yc=(batch,dirs,hid)); forward assembles seq-major
  results in a local buffer and transposes INTO the preallocated outputs in place

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* u8/s8 multiply: exact integer SIMD path on non-FP16 builds (3-10x vs 5.x)

The unit-scale branch of vecBinaryKernel already supported a separate work-vector
type Wvec1 (used by the ARM f16 build and by u16/s16 everywhere), but on x86 the
u8/s8 same-type multiply still went through the f32 hub. Route it through
v_uint16/v_int16: products of 8-bit values fit exactly (255^2 < 2^16), the
saturating pack on store gives bit-exact results at half the vector traffic.

Also fix a latent kernel bug this exposed: the unit-scale branch stepped by
Wvec's lane count while loading/storing Wvec1 vectors. All previous Wvec1
instantiations had equal lane counts, but u8's v_uint16 has 2x the lanes of
v_float32 - the pairs overlapped (50% redundant work) and the tail backoff
could write VECSZ bytes past the row end. The branch now derives its step,
offsets and tail condition from Wvec1 itself.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* vecBinaryKernel: constexpr Op::useScalar instead of a runtime-only scale check

Every binary op functor now declares whether it consumes the scale scalar
(params[0]): true only for mul and the two div variants. Ops that ignore it
(add/sub/min/max/absdiff) take the fast 2-arg branch unconditionally - the
'scalar == 1' check used to fail for them (their params[0] is 0), sending them
through the preproc branch, and the condition now folds at compile time.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* restore cv::hal::mul8u as a wrapper over the element-wise engine

The symbol is still declared in core/hal/hal.hpp and called directly by external
code (the G-API fluid backend in opencv_contrib), but its implementation went
away with the old arithm kernels. Forward it to getMulFunc(CV_8U, CV_8U) - with
scale==1 it lands on the new exact integer SIMD path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* silence every new-arithm warning reported by CI (ARM64/Mac) and gcc 15

- arithm.cpp: bitwise_op_ocl and the actualScalarDepth/coerceTypes helpers are
  consumed only by the OpenCL paths - guard them with HAVE_OPENCL; haveScalar in
  cv::compare is read only inside CV_OCL_RUN - CV_UNUSED for OpenCL-less builds
- arithm_expr.hpp: declare getBitwiseFunc/getNotFunc/getAddWeightedFunc next to
  the other per-op entry points (-Wmissing-prototypes in arithm.dispatch.cpp)
- arithm.simd.hpp: define CV_SIMD_16F to 0 when FP16 SIMD is absent (-Wundef);
  {}-init the expandScalar staging buffers (-Wmaybe-uninitialized: they are
  fully written before use, but the compiler cannot prove it with runtime
  vector widths); rename the compare kernel's lambda parameter (-Wshadow)
- arithm_expr.cpp: rename the exec tile-lambda's hot-field locals that shadowed
  TExpr members and outer locals (-Wshadow)
- test_new_arithm_extensive.cpp: rename the name-generator lambdas' parameter
  shadowing the INSTANTIATE macro's own (-Wshadow), drop an unused variable

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* restore 4.x scalar semantics for the bindings' 4x1 CV_64F Scalar columns

The python/java bindings materialize numbers and tuples as a 2-D 4x1 CV_64F
Mat - or UMat, when the call carries UMat arguments. Three CI-reported python
failures came from those pseudo-scalars reaching the engine as arrays:

- absdiff(int_arr, 0): the (4,1) column is broadcast-COMPATIBLE with a 1-D
  array, so numpy semantics silently won - an outer-product f64 result instead
  of the int per-channel-scalar one;
- subtract(u8 4x8x4, (40,)): same, by the rows==4 coincidence;
- multiply(UMat, 2., dst=UMat): the scalar arrives as a UMAT, which the
  scalar detection did not recognize at all.

isScalarArg now treats the exact bindings shape - 2-D 4x1 CV_64F single-channel
Mat/UMat against a <=4-channel array - as a scalar UNCONDITIONALLY (a 1-D [4]
array has dims==1 and still broadcasts). One exception, decided in arithm_op:
when the partner is itself a tiny scalar-shaped array, both are honest data and
ride the broadcast (compare(Mat 4x1, Mat 1x1) - issue #8999 - stays elementwise).

Small-array discipline, this all runs per engine call: the probes read Mat/UMat
fields directly (rows == 4 alone rejects almost everything, no _InputArray
getter dispatch), and a UMAT scalar's 32 bytes are copied into a caller-stack
buffer - no heap, no getMat mapping. Measured: no latency change on 4x4/16x16
element-wise calls.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* cv::texpr: std::string_view -> const std::string& in the public API

string_view in an exported signature breaks some CUDA toolchain builds, and for
short expression strings the difference is immaterial (SSO, parsed once). The
parser internals keep string_view - the argument converts implicitly. Also drop
the now-unused <string_view> include from cvstd.hpp, so the header does not
reach every nvcc TU.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* compare boundary-rewrite: fixed 4-slot kind/bound arrays instead of AutoBuffers

A CONST operand is capped at 4 channels (addConst), so the per-channel
kind/bound staging needs no dynamic buffers - plain int[4]/double[4], with a
CV_Assert on the contract. This is also what gcc's -Wmaybe-uninitialized was
flagging (it could not see the AutoBuffer's inline storage get filled).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* element-wise engine: unary math kernels (sqrt/exp/log/sin/cos/tanh/erf/relu) + select

New dispatched pair math.simd.hpp / math.dispatch.cpp - the unary/ternary sibling
of arithm.simd.hpp:

- vecUnaryKernel: T -> T over f16/bf16/f32/f64 on top of the intrin_math
  primitives (v_exp/v_log/v_sin/v_cos/v_sqrt/v_erf/v_max). f32/f64 compute
  natively, f16/bf16 ride the f32 hub inside the kernel (vx_load_pair_as /
  v_store_pair_as) - no materialized casts. Continuity collapse + the halide
  right-edge backoff, suppressed in-place (it would re-apply Op to
  already-written values). tanh = (e^2x-1)/(e^2x+1) with the input clamped to
  +/-10 (f32) / +/-20 (f64) - unclamped saturation hits inf/inf = NaN. erf has
  no f64 SIMD primitive: std::erf per lane.
- selectKernel(mask, x, y): 1-byte mask expanded to lane width and tested
  against zero in the INTEGER domain (immune to DAZ/FTZ), branches of any
  depth by element size, broadcast branches supported.
- emitUnary: math over a float input is T -> T now (f16 in -> f16 out, native
  kernel when input and result depths match); integer inputs still compute in
  the float domain and land in f32.
- emitTernary/select: literal branches are typed via typedConstFrom (an
  OP_CAST of a depth-less flex const crashed); a non-1-byte mask is normalized
  by an explicit "mask != 0" compare, never a value cast.

texpr already parsed the function names - they now execute. Tests: per-depth
accuracy of all 8 ops against the double std:: reference, integer input,
in-place, select over 4 depths / const branch / float mask.

Perf vs the classic kernels (1920x1080 f32, 16 threads, AVX2): exp 3.7x,
log 2.6x, sqrt 5.7x faster; polarToCart expressed as (r*cos(a), r*sin(a)) 4.0x.
Accuracy improves too (max rel err vs f64 reference): exp 8.1e-8 vs 2.1e-7,
log 8.0e-8 vs 1.5e-7.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* engine: OP_COPY_MASK folded into OP_SELECT; selectKernel moved to arithm.simd.hpp

copyMask(dst, mask, src) is select(mask, src, dst) - one masking primitive
instead of two. The compiler emits the masked-op tail as
addInsn(OP_SELECT, mask, r, out, out): the output slot rides as both arg2 and
the result, so unmasked elements are preserved by reading them back through
the b-branch. OP_COPY_MASK, copyMaskKernel and getCopyMaskFunc are gone.

selectKernel (moved from math.simd.hpp to arithm.simd.hpp) inherits every
copyMaskKernel optimization:
- the interleaved multichannel fast path (2..4 channels under a per-pixel
  mask: expand the mask once per VECSZ rows, v_store_interleave across lanes);
- the per-row scalar path with the row-skip when the selected source row IS
  dst (the "leave the output untouched" half of copyMask);
- plus the select-specific ones: branch broadcasts (stepx == 0) and the
  right-edge tail backoff under dst-aliases-a-branch - safe because re-running
  select over already-blended elements is idempotent; only dst == mask keeps
  the backoff off (the store would rewrite mask bytes before the re-read).

Masked-add perf is on par with the old copyMask (1280x720, 1 thread: 8UC3
204 -> 198 us, 32FC3 1395 -> 1344, 8UC1/32FC1 within noise). Full core suite
24117 green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* engine: dedicated vectorized pow kernel (moved from arithm to math.simd.hpp)

pow was the last scalar-only binary op (scalarBinaryKernel + std::pow, f32/f64
only). The new powKernel keeps exact std::pow semantics and is T x T -> T over
all four float depths (f16/bf16 via the f32 hub):

- scalar exponent (the dominant call shape - texpr literals ride as 0-dim
  broadcast consts) is dispatched PER ROW to the special cases:
  y==2 -> x*x, y==3 -> x*x*x, y==0.5 -> v_sqrt, y==1 -> copy, y==0 -> fill 1;
- everything else - including a per-element exponent array - runs the general
  vectorized exp(y * log(x)) path, valid for x > 0; a vector pair containing
  any x <= 0 lane falls back to scalar std::pow for that pair (v_check_any),
  which preserves every std::pow subtlety: signed results for integer y on
  negative bases, NaN for fractional y, the x == 0 family;
- no right-edge tail backoff: pow is not idempotent, in-place calls finish
  rows in the scalar tail.

Perf vs the classic cv::pow (1920x1080 f32, 16 threads, AVX2): p=2 1.4x
(classic special-cases it too), p=3 6.1x, p=0.5 6.3x, fractional p 4.5x with
slightly better accuracy (6.1e-7 vs 7.4e-7 max rel err). Tests: exponent
sweep 2/3/0.5/1/0/2.5/-1.5 vs the double std::pow reference on f32/f64,
negative bases (exact signed cubes, NaN for fractional), array exponent.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* powKernel: halide right-edge tail backoff in every SIMD loop

Same shape as vecBinaryKernel: the final partial vector re-processes
[width - VECSZ*2, width) instead of finishing scalar, suppressed when dst
aliases an input (pow is not idempotent - the overlap region must be
recomputed from an untouched source, which the no-alias case guarantees).

Modest measured win (~2% on ROI rows for the special-cased exponents; the
general path tail was already cheap - modern libm powf is fast), no
regressions; mainly aligns the kernel with the house style, where every
SIMD loop ends vector-wide.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix MSVC 2019 C2975: function-local constexpr as a template argument inside a lambda

MSVC 2019 loses the constexpr-ness of function-local constants (LOCAL_OPS,
MAX_DIMS, ...) when they are used as template arguments inside a lambda body
(AutoBuffer<Slice, LOCAL_OPS> / std::array<int, MAX_DIMS> in the parallel
bodies of BroadcastOp::run and TExpr::exec). Hoist them to namespace scope -
no behavior change.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* ocl_arithm_op: route 16U multiply to the CPU engine on Apple OpenCL

The Apple OpenCL driver miscompiles the 16U multiply kernel: products near
the top of the u16 range come back wrapped instead of saturated (CPU vs GPU
NORM_INF up to 65535 in OCL_Arithm/Mul.Mat CV_16U cases). The same arithm.cl
kernel is correct on Intel NEO and NVIDIA drivers - verified not to reproduce
on Linux/Intel iGPU - so gate the decline to __APPLE__ only; the CPU engine
computes 16u multiply exactly (integer SIMD path).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* engine: neg/abs as compositions, clamp kernel, ** and ?: operators, abs(a-b) peephole

- OP_NEG and OP_ABS need no kernels: neg = sub(0, a), abs = absdiff(a, 0) -
  including the engine absdiff auto-type rule (signed |a| lands in the
  UNSIGNED type of the same width: |SHRT_MIN| fits u16 exactly instead of
  saturating; NB the public cv::absdiff auto depth keeps the source type for
  4.x compatibility - values agree, the depth rule is the engine own).
- peephole: abs(x - y) rewrites to absdiff(x, y) ALWAYS. On integers the
  literal semantics differ (the subtract saturates first: u8 gives
  max(x-y, 0)), but whoever writes abs(a - b) means absdiff - we deliberately
  hand out the useful semantics instead of the saturation artifact. The just-
  emitted OP_SUB is retired via the moveToOutput manoeuvre, so the program
  shrinks to the single absdiff instruction. abs(x), abs(x - 0) and
  absdiff(x, 0) all give one result.
- OP_CLAMP kernel (arithm.simd.hpp): v_min(v_max(x, lo), hi) over
  u8/s8/u16/s16/u32/s32/f32 (+f64 with 64-bit SIMD), scalar f16/bf16/64-bit
  ints; lo/hi may broadcast (the common clamp(img, a, b) shape) or be full
  arrays; the tail backoff stays on under dst-aliases-x (clamp is idempotent).
  emitTernary types literal bounds via typedConstFrom (same flex-const crash
  select had) and keeps the auto result type pinned to x.
- parser: "a ** b" == pow(a, b), precedence above * /, RIGHT-associative
  (a ** 2 ** 3 == a ** 8); "cond ? a : b" == select(cond, a, b), precedence
  below everything, right-associative chains (f1 ? a : f2 ? b : c) work
  without parentheses. parseTernary() is the expression entry point now.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* cv::exp/log/sqrt on the engine via math_op; hal functions wrapped as engine kernels

math_op is the master function of the unary math family (the arithm_op
analogue): same-shape same-type output over f16/bf16/f32/f64 (classic
exp/log accepted f32/f64 only - the half floats are new), two tiers:
- small (<= 100000 elements) and continuous: call the kernel DIRECTLY over
  the flattened data - no TExpr, no broadcastOp, no parallel_for setup;
- everything else: the usual single-instruction program via compile()/exec()
  (parallelism for large arrays, real steps for ROIs).

getMathFunc routes OP_EXP/OP_LOG at f32/f64 through the full cv::hal stack -
an external vendor HAL (CALL_HAL), IPP, or the built-in table kernels,
whichever is installed - by wrapping hal::exp32f/exp64f/log32f/log64f as
engine kernels with the function pointer in TKernel::userdata, the same
mechanism castKernel uses for core BinaryFuncs. The engine adds tiling and
parallelism on top, so every tier gets the best available scalar-span
implementation. v_exp/v_log remain for f16/bf16 (the f32 hub) and the ops
hal has no entry points for.

v_log_default_32f: the degree-8 polynomial is evaluated by Estrin pairing
(4 dependent levels) instead of an 8-FMA Horner chain (~5% on the f16 hub
path). An exp64 Taylor-without-division rewrite was tried and benched SLOWER
than the Cephes Pade scheme (the evaluation is FMA-throughput-bound, and
vdivpd pipelines well enough) - reverted; a table-based reduction is the only
way further there.

cv::exp f32 (16 threads, AVX2+IPP build), old -> new: 640 elements
0.16 -> 0.15 us, 16k 2.79 -> 2.49, 640x480 47 -> 20, 1920x1080 452 -> 60 us
(the old CPU loops were single-threaded); f64 exp 1080p 1295 -> 161 us.
No size regresses; small arrays now run at installed-HAL speed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* getMathFunc: IPP tier + raw-HAL probing; the exp/log table kernels are deleted

The raw cv_hal_* entry points return int for a reason: without an installed
HAL they are stubs returning CV_HAL_ERROR_NOT_IMPLEMENTED. getMathFunc now
selects the exp/log implementation in three tiers:
 1. HAVE_IPP && ipp::useIPP(): ippsExp/ippsLn through thin int adapters (IPP
    is not routed through the cv_hal_ hooks, so it needs its own tier);
 2. the raw cv_hal_exp32f/... hook, PROBED once with a 1-element call on the
    safe input 1.0 (cached in magic statics): implemented -> wrapped as an
    engine kernel with the function pointer in TKernel::userdata;
 3. the engine own v_exp/v_log kernels.
Whichever wins, the engine adds tiling and parallelism on top.

The EXPTAB/LOGTAB table kernels and their tables (~790 lines in
mathfuncs_core.simd.hpp + mathfuncs.cpp) are DELETED: they benched within
~15% of v_exp/v_log, not worth a second implementation. The public
cv::hal::exp32f/exp64f/log32f/log64f keep their contract - CALL_HAL, then
IPP, then the built-in implementation - but the built-in is now the engine
vector kernel via ew::mathSpanEngine (one contiguous span, exported from
math.dispatch.cpp).

All unary math kernels (vec/scalar/hal wrappers, pow) also handle the
vertical-broadcast tile (s0y == 0, a row expanded into a matrix): the first
row is computed, the rest are memcpy of it - transcendentals cost far more
than a row copy.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* texpr: hypot(x, y) binary op (alias: mag)

hypot = sqrt(x^2 + y^2), NAIVE like cv::magnitude (not the overflow-safe
std::hypot), computed in the float work type; kernels for the four float
depths only (T x T -> T; integer inputs ride the usual f32-compute + cast).
A 10-line EwHypot functor on top of vecBinaryKernel in arithm.simd.hpp -
broadcast branches, continuity collapse and the tail backoff come for free.
Registered in the parser as both "hypot" (the C/numpy name) and "mag" (the
cv::magnitude-flavored alias). A building block for the future cartToPolar.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* texpr: atan2(y, x) binary op - radians, standard C range

v_atan2 (arithm.simd.hpp, generic over the universal-intrinsic float vector):
the fastAtan2 minimax polynomial from mathfuncs_core v_atan_f32 reworked to
plain radians - the 180/pi factor dropped from the coefficients and the C
quadrant logic instead of the [0, 360) wrap, so the result matches std::atan2
over (-pi, pi]. Measured absolute accuracy ~1.6e-4 rad. (v_atan_f32 itself is
untouched - cv::phase/fastAtan2 keep their degree semantics.)

EwAtan2 rides vecBinaryKernel: f16/bf16/f32 through v_atan2 (the f32 hub),
f64 through exact scalar std::atan2. arg0 = y, arg1 = x, like std::atan2;
float depths only, same emitBinary policy as pow/hypot. Parser name "atan2".
Together with hypot this completes the cartToPolar building blocks.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix the RISC-V RVV build and two ARM64 warnings

The new f64 kernel registrations (hypot, pow, the unary math family) gate on
CV_SIMD_64F || CV_SIMD_SCALABLE_64F, but the vx_setall_as(const double*,
v_float64&) helper family in arithm.simd.hpp was still CV_SIMD_64F-only -
scalable platforms (RVV) have v_float64 with CV_SIMD_64F == 0, so
vecBinaryKernel<double, ...> failed to instantiate there. Widen the helper
gate to match (verified with a riscv64 rv64gcv cross-build of opencv_core -
the engine f64 paths now vectorize on RVV instead of not compiling).

cv::exp/cv::log: the depth local is consumed by CV_OCL_RUN only - CV_UNUSED
for OpenCL-less builds (ARM64 -Wunused-variable).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* silence the remaining ARM64 gcc warnings

- compare boundary-rewrite: {}-init the fixed kind/bound arrays (filled for
  every channel used below, but gcc cannot prove it across the cn <= 4 loop);
- cv::exp/log: [[maybe_unused]] on the depth local (consumed by CV_OCL_RUN
  only), instead of the CV_UNUSED idiom;
- AutoBuffer::reserve: a targeted -Wmaybe-uninitialized suppression around
  the live-element copy loop - only [0, sz) is read, all written before, but
  gcc inlining a grow-from-inline-storage chain cannot see that. An
  annotation for the analyzer, no behavior change.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* saturating 32-bit add/sub kernels; cv::texpr python binding; two CI warnings

- v_add_sat/v_sub_sat for v_int32/v_uint32, local to arithm.simd.hpp for now
  (the plan is to grow them into proper universal intrinsics later): NEON
  single-instruction vqadd/vqsub, elsewhere the Hacker Delight bit tricks
  over universal intrinsics (u32 add is 2 ops: or with the wrapped-compare
  mask). EwAdd/EwSub overload vec() for the 32-bit lanes and getAddSubFunc
  routes 32S/32U T->T through vecBinaryKernel instead of the former pure
  scalar kernel. Semantics unchanged - the scalar int64 tail already
  saturated; directed boundary tests added (both rails, 0 - INT_MIN, u32
  cases, a full-range random block vs an exact int64 reference).
  640x480 32S add: 0.48x of 5.x -> parity (memory-bound); 1080p: 6-8x.

- cv::texpr becomes CV_EXPORTS_W: python gets cv.texpr(expr, [inputs]) ->
  tuple of ndarrays, so `res, = cv.texpr(...)` and `mag, ang = cv.texpr(...)`
  unpacking both work. modules/python/test/test_expr.py covers arithmetic,
  the fused abs(a-b), casts, broadcasting, ?: and ** operators, math
  functions vs numpy, clamp, named temporaries, tuple outputs, the one-line
  cartToPolar and the int32 saturation cases.

- warnings: {}-init the parser args array (gcc -Wmaybe-uninitialized on
  Ubuntu 20/22); the compare short-row block gates sizeof(T) <= 4 as
  constexpr so the f64 instantiation does not leave set-but-unused locals
  (gcc 9 -Wunused-but-set-variable).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* arithm_op: direct-kernel fast path for small continuous arrays

Building and compiling the 1-instruction program plus the BroadcastOp setup
costs ~40-250ns per call - negligible on big images, dominant at 127x61-class
sizes where the classic 5.x functions were 1.3-2x faster. Mirror math_op two
tiers in arithm_op: two same-type same-shape continuous arrays, no mask, no
scalar, result depth == input depth, <= 100k elements -> call the T x T -> T
kernel directly over the flattened elements (checks ordered cheapest-first).
Applies to add/subtract/min/max/absdiff/multiply/addWeighted/and/or/xor;
compare and divide lower to more than a single kernel (boundary rewrites, int
guards) and keep the ordinary path. addWeighted falls through automatically
for the 32/64-bit int types whose lowering is wide-compute + cast
(getElemwiseFunc returns no direct kernel there).

127x61 vs 5.x, was -> now: add/subtract 8UC1 0.76x -> 1.3x, min/max u8
0.6x -> ~1x, addWeighted 1.0x -> 1.1-1.4x (32SC1 stays 4.5x); the one
remaining laggard is add/sub 32SC1 (0.74-0.82x) - the price of the new
SATURATING semantics (7-instruction AVX2 emulation vs the wrapping single
add of 5.x; single-instruction on NEON).

The 127x61 size is ADDED PERMANENTLY to the arithmetic/addWeighted/compare
perf grids: per-call overhead regressions in these base functions must be
caught by CI, not discovered by users.

dst creation goes through createSameSize (whole-shape transfer including
layout and future metadata, not piecemeal dims+sizes) here and in math_op.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* cv::pow rebuilt on the engine; integer-exponent and 1/sqrt(x) kernel branches

Routing: p = 0/1/2 keep their early special cases (fill/copy/multiply); an
INTEGER array with an INTEGER power keeps the classic iPow multiply chain -
bit-exact compatibility, including its wrap-around quirks (iPow squares in
int, so e.g. pow(255,4) on u8 wraps negative and saturates to 0 - somebody
may rely on that). Everything else goes through the engine with the math_op
two-tier scheme: small continuous arrays call powKernel directly (the
exponent rides as a broadcast T scalar), the rest run the tiled parallel
program. Integer arrays with fractional powers compute in the float domain
and saturate back; the 32U/64-bit depths (classic iPow asserted on them) and
f16/bf16 (the classic float path misread them) now just work.

powKernel gets two new per-row exponent branches:
- p == -0.5: 1/v_sqrt(x) (the classic path used IPP ippsInvSqrt_A21, a
  21-bit approximation; ours is exact - slightly slower on small arrays,
  4.5x faster at 1080p via parallelism);
- any other INTEGER p (|p| <= 65536): LSB-first binary exponentiation, the
  same multiply chain and order as iPow, fully vectorized - a few ulp
  accurate vs ~2e-7 of exp(p*log x), and exact on non-positive bases (the
  sign falls out of the multiplies, 0^negative divides to inf) - no scalar
  patching.

cv::pow f32 vs 5.x: p=0.5 365 -> 61 us at 1080p (6x), p=3 7.7x, p=5 7x AND
faster at every size (the old scalar chain: 0.81 -> 0.47 us at 127x61),
p=2.5 5.3x. The s16^5 iPow path is untouched (118.7 == 118.5 us).
pow_exponents accuracy tests extended to 11 exponents x f32/f64 against the
double std::pow reference.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* perf: SANITY_CHECK_NOTHING for the tests whose grids got the 127x61 size

The 127x61 entry added to guard per-call overhead has no regression data in
opencv_extra, so the legacy SANITY_CHECK in addWeighted/compare failed on CI
(locally it passes silently without the test-data path). Accuracy of both
functions is covered by the accuracy suite; the perf tests should measure
time. PatchNaNs/finiteMask keep their SANITY_CHECK - their grids are
untouched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix a temp-buffer double release in the liveness pass ("d*d" crash)

When the same temp is passed as SEVERAL arguments of its last-use
instruction (e.g. the named-intermediate expression "d = {0} - {1}; d*d",
where the MUL consumes slot d twice), the buffer-reuse scan pushed the
temp's physical buffer onto the free list once per argument. That
overflows the ntemps-sized freeBufs array (caught by the AutoBuffer range
check in Debug: python test_expr.py::test_named_temporary) and, in larger
programs, would hand the same physical buffer to two live temps.

Release the buffer once by retiring lastUse[t] after the first hit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* executor: recalibrate opCost to vectorized cycles + clamp the stripe count

The per-element op costs fed into the parallel_for_ stripe hint were
scalar-era estimates (~20x above the vectorized reality: the atan2/exp
polynomials run at ~1.5 cycles/element, not 30). The hint therefore split
transcendental/divide work into hundreds of ~1 us jobs, which the
macOS/GCD backend dispatches poorly under sustained load, on top of the
P/E-core equal-share straggler effect. Measured on M4 Max (12P+4E),
sustained medians @1920x1080 f32: texpr atan2 320 -> 133 us, cv::exp
280 -> 141 us, cv::log 301 -> ~200 us, cv::pow(x,2.5) 388 -> 376 us;
the PR tables' math rows improved ~1.5-2x across the board.

- opCost is now in units of ~1/4 cycle/element of the SIMD kernels:
  cheap ops 1 (unchanged), div/sqrt/hypot/convert_scale 10 -> 3,
  transcendentals 30 -> 6.
- the stripe hint is clamped by min(4*nthreads, max(32, 3*nthreads)):
  ~4 stripes/thread is plenty of granularity for element-wise work, and
  the ceiling is 32 pieces except on machines with many (heterogeneous)
  cores, where anything coarser than ~3 pieces/worker turns the slow
  cores into equal-share bottlenecks (measured: 32 stripes on 16 threads
  is the worst point of the curve - 193 us vs 137 us at 48 for atan2).
  getNumThreads() is clamped from below (WINRT/plugin backends may
  report 0).

Not addressed here (needs cross-machine data, M2/M3 Ultra): streaming
memory-bound ops saturate the M4 Max fabric at ~8 fat stripes and E-core
participation only adds contention - a cost model cannot express that;
candidate follow-up is a bytes-aware clamp or a GCD-backend-level fix.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* multiply: v_mul_sat integer kernels (full product clamped to the type)

v_mul_sat(V, V) -> V for u8/s8/u16/s16/u32/s32 - the full-precision
product clamped to the lane type, which is exactly cv::multiply's integer
semantics at scale == 1. Local to arithm.simd.hpp for now, next to
v_add_sat/v_sub_sat, to be promoted into proper universal intrinsics
later. NEON: widening vmull + saturating narrow (vqmovn); other backends:
the portable v_mul_expand + saturating v_pack composition for 8/16-bit
lanes. 32-bit lanes have no universal widening multiply (no v_mul_expand
for s32), so the 32-bit integer fast path is NEON-only for now and the
other backends keep the previous f64 work-vector kernels (which measure
well on x86 with IPP-free AVX2).

EwMul::vec() now routes the integer lane types through v_mul_sat, and
getMulFunc_ passes the NATIVE lane vector as the scale==1 fast-path type:
whole-register loads/stores, the widening happens inside the multiply.
Replaces both the half-register widening loads (u8/s8/u16/s16) and the
scalar-equivalent f64 path for 32S/32U on NEON.

M4 Max, 640x480 (the sizes where the old kernels lost to carotene):
8U 22.0 -> 13.6 us, 8S 15.0 -> 11.1, 16S 21.2 -> 17.4, 32S 84.8 -> 30.5
(parity with the classic path everywhere, 32S was 0.38x). 1920x1080:
8S 1.30x -> 1.78x, 16S 2.57x -> 2.98x, 32S 2.20x -> 4.02x vs 5.x.
Correctness: exhaustive 8-bit (all 65536 pairs per sign), directed
saturation corners for 16/32-bit (46341^2, INT_MIN*-1, 65536*65536, all
sign combinations) against an exact int64 reference.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* NEON: make v_cvt_f64(v_int32) exact (was via f32, losing bits > 2^24)

The NEON implementations of v_cvt_f64/v_cvt_f64_high for v_int32 did
s32 -> f32 -> f64 (vcvt_f32_s32 + vcvt_f64_f32), silently rounding any
|x| > 2^24. Every vectorized f64 work path with int32 inputs on AArch64
was affected: the engine's addWeighted/divide 32S kernels, convertTo
32S -> 64F, etc. Found via addWeighted 32SC1 on values ~1e9: max error
was 32 vs the exact double reference (the classic carotene path is worse
still - it computes in f32 end-to-end with f32-truncated weights, max
error 96 on the same data).

The exact sequence sxtl + scvtf (vmovl_s32 + vcvtq_f64_s64) is the same
2 instructions, so there is no cost. addWeighted 32SC1 on the engine now
matches the exact-double reference bit-for-bit and stays at parity/1.4x
vs the classic path (640x480/1920x1080).

Pre-existing upstream bug (same code in 4.x) - worth a standalone
backport with directed large-value tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* math_op: drop the temporary direct-IPP tier for exp/log

Upstream moved the IPP math wrappers into the hal/ipp HAL module (cv_hal_exp32f/
log32f & co now resolve to ipp_hal_* which honor cv::ipp::useIPP via
CV_HAL_CHECK_USE_IPP). The engine's single probeHalUnary(cv_hal_*) probe already
picks that up uniformly, so the stopgap #ifdef HAVE_IPP ippExp/ippLog tier and its
ipp::useIPP() branch in getMathFunc are now redundant - removed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* arithm: restore cv::hal::and8u/or8u/xor8u/not8u as engine wrappers

These public CV_EXPORTS entry points (core/hal/hal.hpp) lost their definitions when
the bitwise ops moved to the element-wise engine, but they are still declared and called
by other modules (opencv_objdetect's aruco) and external code - the link broke with
undefined references to cv::hal::and8u/xor8u. Restore them as thin forwarders over the
engine's byte-wise bitwise kernels (getBitwiseFunc / getNotFunc), mirroring the existing
mul8u wrapper. CV_Assert guards the kernel lookup.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* blobdetector: guard empty contour when computing blob radius

The new AutoBuffer leaves its tail uninitialized for trivial types, which surfaced a
-Wmaybe-uninitialized in findBlobs where the median of per-point distances is read. Use a
std::vector, default the radius to 0, and compute the median only for a non-empty contour -
no unproven size invariant.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 01:26:36 +03:00
velonica0 5aa9c9562a Merge pull request #29541 from velonica0:rvv-fix-cvtabs-gcc-build
core: fix RISC-V GCC build break in cvtabs_32f (#29369 follow-up) #29541

### Summary

`modules/core/src/convert_scale.simd.hpp` does not compile on RISC-V with GCC on current 5.x:

```
convert_scale.simd.hpp:44:13: error: 'useSIMD' was not declared in this scope
convert_scale.simd.hpp:45:37: error: 'VECSZ' was not declared in this scope
convert_scale.simd.hpp:55:28: error: 'va' was not declared in this scope
convert_scale.simd.hpp:55:32: error: 'vb' was not declared in this scope
```

This breaks every RISC-V + GCC build of the core module.

### Root cause

In #29369 `cvtabs_32f` ended up with a declaration guard and a usage guard that disagree:

```c
#if (CV_SIMD || (CV_SIMD_SCALABLE && !(defined(__GNUC__) && !defined(__clang__))))  // decl
    v_float32 va = ..., vb = ...;  const int VECSZ = ...;  const bool useSIMD = ...;
#endif
        ...
#if (CV_SIMD || CV_SIMD_SCALABLE)                                                   // usage
        if( useSIMD ) for( ; j < size.width; j += VECSZ ) { ... v_fma(v0, va, vb) ... }
```

On RISC-V `CV_SIMD == 0` and `CV_SIMD_SCALABLE == 1`, so under GCC the first is false and the second is true: the declarations are dropped while the loop using them is still compiled.

### Second commit (independent, droppable)

`useSIMD` asks "is this a 128-bit vector machine?" but tests `vlanes() != 8`. `vlanes()` is `VLEN/32 * LMUL`, so the literal `8` encodes both "VLEN == 128" **and** "LMUL == 2" — correct only while `v_float32` stays at LMUL=2. Under #29493 (LMUL=1) the same expression silently becomes "VLEN != 256": disabling SIMD on 256-bit machines and enabling it at VLEN=128, the exact case the workaround exists to avoid. CI cannot catch that regression, since the block is GCC-only.

VLEN is not recoverable from the universal intrinsics (every type scales with LMUL, so the factor never cancels), so this asks the hardware: `__riscv_vlenb()*8 != 128`. Behaviour is unchanged today — with LMUL=2, `vlanes() == 8` iff `VLEN == 128`.


### Testing

SpaceMIT K3, GCC 15.2, **unmodified LMUL=2 backend** (no #29493 changes involved):

| configuration | result |
| --- | --- |
| 5.x as-is | `opencv_core` fails to build (errors above) |
| + commit 1 | builds clean, 0 errors |
| + commits 1&2 | builds clean, 0 errors |
| `Core_ConvertScale*`, X100 @ VLEN=256 | 4/4 pass (incl. `Core_ConvertScaleAbs/ElemWiseTest.accuracy`) |
| `Core_ConvertScale*`, A100 @ VLEN=1024 | 4/4 pass |
| `useSIMD == false` path (temporary probe forcing it at VLEN=256) | passes |
2026-07-17 11:47:03 +03:00
Alexander Smorkalov 47d9544d90 Merge pull request #29534 from vrabaud:persistence2
Do not include headers in cv namespace
2026-07-17 11:32:24 +03:00
Vincent Rabaud c2bf59ca0c Do not include headers in cv namespace
That creates some conflict on some windows platform with blaze.
2026-07-16 14:14:37 +02:00
Alexander Smorkalov 5aff519a06 Merge pull request #29530 from opencv:revert-29492-threshold_IPP_5.x
Revert "Refactoring and extracting IPP to HAL for threshold function in 5.x"
2026-07-16 11:45:45 +03:00
Alexander Smorkalov 430909e2a3 Revert "Merge pull request #29492 from Akansha-977:threshold_IPP_5.x"
This reverts commit c64d89e75c.
2026-07-16 10:26:56 +03:00
Alexander Smorkalov f07e06fd47 Merge pull request #29526 from asmorkalov:as/model_name_update
Renamed model to prevent name conflict.
2026-07-16 08:56:18 +03:00
Ajay Raj 3701817083 cmake: set policy CMP0218 to NEW to suppress deprecation warning
Added policy CMP0218 to suppress deprecation warnings.
2026-07-16 03:53:15 +05:30
Akansha-977 c64d89e75c Merge pull request #29492 from Akansha-977:threshold_IPP_5.x
Refactoring and extracting IPP to HAL for threshold function in 5.x #29492

### 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-15 17:08:19 +03:00
Alexander Smorkalov 507c824cd8 Renamed model to prevent name conflict. 2026-07-15 15:58:50 +03:00
Abhishek Gola 34074075b2 Merge pull request #29369 from abhishek-gola:fp8_support
FP8 support in core module #29369

Core part of https://github.com/opencv/opencv/issues/29313

### 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-15 15:45:01 +03:00
Akansha-977 18f9bffb0d Merge pull request #29467 from Akansha-977:distransform_IPP_migration_5.x
### 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-15 12:34:47 +03:00
Akansha-977 8ee67ccb65 Merge pull request #29466 from Akansha-977:templatematch_IPP_5.x
Extracting IPP to HAL for matchTemplate function in 5.x #29466

### 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-15 12:34:01 +03:00
Anand Mahesh 60c705e93b Merge pull request #29450 from manand881:feat/ocl-sift-descriptor
Feat: add OpenCL SIFT detector and descriptor #29450

Add OpenCL implementation of SIFT detector and
descriptor extractor, triggered via T-API when
the caller passes a UMat. Targets the features
module (5.x directory layout).

Implementation:
- 4 OpenCL kernels in sift.cl: gaussian_blur_h, gaussian_blur_v, detect_and_orient, and compute_descriptor.
- Host-side dispatch in sift.dispatch.cpp using CV_OCL_RUN_ macro, pure additions to the existing CPU path.
- T-API entry: detectAndCompute, detect, compute.

Gating and fallback:
- 64-bit only. 32-bit falls back to CPU via sizeof(void*) > 4 guard.
- OpenCL 1.1 or later required. Devices below 1.1 fall back to CPU.
- No-OpenCL builds fall back to CPU. Verified with WITH_OPENCL=OFF: 79 tests pass, same 3 pre-existing failures (DISK/AFFINE_FEATURE missing .npy data files).

Correctness:
- nOctaveLayers > 3 OOB fix: gk_coeffs and gk_radius resized to nOctaveLayers+2 via std::vector (were fixed size 5).
- Regression test for nOctaveLayers > 3.
- 10 OCL SIFT tests pass, 60 OCL tests pass, 139 features tests pass (3 pre-existing failures unrelated to SIFT).

Performance optimizations:
- native_exp, native_sqrt, native_recip for hardware approximations (OpenCL 1.1 builtins).
- Eliminate intermediate rawDst[128] buffer; normalize in-place from hist[].
- Non-blocking kernel launches with single ocl::finish() before host reads keypoint count.
- Custom separable Gaussian blur for init image bypassing T-API GaussianBlur dispatch overhead.
- exp2() replaces pow(2.0f, x); scl_octv reused.
- Hoist UMat tmp allocation out of pyramid loop.
- Interleaved keypoint output buffer (6 arrays to 1) for coalesced writes and fewer copies.
- Consolidated descriptor keypoint copies (2N to 2 host-to-device transfers).
- std::map replaced with flat vector indexed by pyramid level (O(1) vs O(log n)).
- DoG computed on-the-fly in detect kernel via READ_DOG macro, eliminating 45 subtract() calls and dog_pack allocation.
- Shared gauss_packs between detect and descriptor, eliminating duplicate copyTo.
- Cross-level packing per octave: one descriptor kernel launch per octave instead of per level. Keypoint buffer expanded to 5 floats (added layer index). Levels packed into single UMat. Reduces kernel launch overhead and improves GPU utilization for octaves with few keypoints.
- __local memory tiling for Gaussian blur kernels with 16x16 workgroups and cooperative halo loading. Reduces global memory traffic.
- Direct uchar descriptor output for CV_8U: kernel writes convert_uchar_sat_rte directly instead of float buffer + host convertTo pass. Eliminates temp allocation and extra kernel launch.

Performance result (stitching/s2.jpg resized):
- DetectAndCompute: OCL wins at >=960x540 (1.32x at 960x540, 1.80x at 1280x720, 1.87x at 1920x1080).
- Compute-only: OCL wins at >=1280x720 (1.60x at 1280x720, 1.78x at 1920x1080).

Tests:
- modules/features/test/ocl/test_feature2d.cpp with OCL SIFT correctness tests on real images (leuven img1.png, a3.png, s2.jpg).
- DescriptorType, Regression_26139, and Batch tests mirror CPU coverage (CV_8U descriptor type, single-keypoint edge case, 6-image detect+compute).
- modules/features/perf/opencl/perf_sift.cpp with real image and 5-size sweep.
- CPU SIFT_Scaled fixture added to perf_feature2d.cpp for aligned OCL vs CPU comparison.

### 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:29:43 +03:00
Alexander Smorkalov 5c7b438295 Merge pull request #29518 from Teddy-Yangjiale:rvv-lk
video: add RVV intrinsics path for pyramidal LK optical flow iteration loop
2026-07-15 10:35:45 +03:00
Alexander Smorkalov 647b32ac7c Merge pull request #29517 from Teddy-Yangjiale:rvv-k1-05-conv-mr1
dnn: add RVV fp32 microkernel for MR=1 (depthwise-remain) convolution
2026-07-14 16:06:10 +03:00
Alexander Smorkalov 2d2611d5bd Merge pull request #29519 from alessio-antochi:fix-missing-geometry-include
Add missing geometry module include to opencv.hpp
2026-07-14 15:18:08 +03:00
Teddy-Yangjiale 3803eb828e Merge pull request #29440 from Teddy-Yangjiale:rvv-k1-06-filter-cov
imgproc: extend boxFilter / gaussianBlurBinomial coverage in RISC-V RVV HAL #29440

### Summary

This PR extends RVV HAL coverage of the two most frequently used smoothing filters. Previously, `cv_hal_boxFilter` was only implemented for single-channel types (plus 32FC3) at 3×3/5×5, and `cv_hal_gaussianBlurBinomial` for 8UC1/16UC1/8UC4. The most common cases in practice — interleaved **8UC3** images and **7×7** box kernels — fell back to the core `FilterEngine` path, which runs single-threaded, costing 6–19× on multi-core RISC-V hardware.

New coverage:

- `boxFilter`: **8UC3 / 8UC4** (3×3, 5×5, 7×7); **7×7** for 8UC1, 16SC1, 32FC1, 32FC3 and the 8U→16U variant
- `gaussianBlurBinomial`: **8UC3** (3×3, 5×5)

Combinations already covered upstream keep their existing kernels byte-for-byte; net diff is +274/−162 across two files.

### Implementation

1. **Channel-blind "flat" kernels** (`boxFilterFlat8U<ksize, cn>`, `gaussianBlurFlat8U<ksize, cn>`). An interleaved row is processed as `width*cn` flat u8 elements whose horizontal taps sit at strides of `cn` elements: element *i* sums exactly its own channel's taps at `i, i+cn, …, i+(ksize−1)·cn`. One template therefore serves all channel counts, and:
   - the horizontal pass becomes `ksize` independent unaligned `vle8` loads combined with widening adds — no `vlseg`/`vsseg` and no serial `vslide1down` chains, which are microcoded/serialized on current RVV hardware;
   - the vertical pass is a single contiguous u16 stream processed at LMUL=8;
   - the output is a plain `vse8` after saturating narrowing.
2. **Incremental vertical sliding window** for box with `ksize > 3`: the column-sum ring buffer keeps `ksize+1` rows so the window slides in O(1) per output row (add the entering row, subtract the leaving row) instead of re-summing `ksize` rows. u16 modular arithmetic stays exact because window sums never exceed 49·255 = 12495.
3. **Division-free normalization**: normalized box output uses multiply-high + shift (`m = ceil(2^18 / k²)`, Granlund–Montgomery; exact for divisors 9/25/49 over the full value range, verified per divisor) instead of long-latency vector integer division. Rounding is bit-identical to the previous `(sum + k²/2) / k²`.
4. **Existing kernels are extended, not rewritten**: `boxFilterC1` and the float `boxFilterC3` gain a 7×7 step in their existing unrolled style, so the 3×3/5×5 instantiations compile to the same code as before. The previous per-channel gaussian 8UC4 segment kernel is replaced by the flat kernel at measured parity, which is why the diff removes more gaussian lines than it adds.
5. Constraints respected: no hard-coded VLEN, RVV 1.0 intrinsics only, no scalable vector types in arrays.

### Performance

SpacemiT K1 (X60, rv64gcv, VLEN=256, 8×1.6 GHz), 1280×720, against a clean build of current 5.x HEAD.

**Measurement protocol:** each case is timed for **30 iterations** (after 2 warm-up runs) and the minimum is taken; the whole benchmark is executed in **2 interleaved rounds per library** (baseline/candidate alternated to cancel frequency/thermal drift, measured at up to 2× per run on this board), reporting the per-case minimum across rounds.

| Case | Generic | HAL | Speedup |
|------|--------:|----:|--------:|
| boxFilter 8UC1 7×7 | 6.539 ms | 0.330 ms | 19.8× |
| boxFilter 8UC3 3×3 | 7.325 ms | 0.994 ms | 7.4× |
| boxFilter 8UC3 5×5 | 8.118 ms | 1.142 ms | 7.1× |
| boxFilter 8UC3 7×7 | 15.930 ms | 1.328 ms | 12.0× |
| boxFilter 8UC4 3×3 | 9.552 ms | 1.466 ms | 6.5× |
| boxFilter 8UC4 5×5 | 10.775 ms | 1.841 ms | 5.9× |
| boxFilter 8UC4 7×7 | 14.268 ms | 2.112 ms | 6.8× |
| gaussianBlur 8UC3 3×3 | 2.382 ms | 1.023 ms | 2.3× |
| gaussianBlur 8UC3 5×5 | 2.746 ms | 1.684 ms | 1.6× |

**Geometric mean: ~6.1×** across the newly covered cases. Most of the box gain is the HAL's row-parallel execution (core `FilterEngine` has no internal parallelism) multiplied by the flat-layout kernels; the incremental window gives 7×7 a further ~1.9× over the plain flat version.

Regression check: the full `opencv_perf_imgproc` sweep (5610 timed cases, `--perf_min_samples=10`, one round per library) shows a geomean of 1.015× with no regressions beyond run-to-run noise — every case initially below 0.90× was re-verified with 2×2 interleaved rounds and per-case minima, and none persisted. The blur fixtures independently confirm 4.7–8.6× on 8UC4 across sizes and border types.

### Accuracy

- **Bit-exact against the core fallback**: for every newly covered combination (all 15 type/ksize pairs exercised), the HAL output was checksummed against the generic implementation on identical random inputs — all checksums match exactly. This includes the multiply-high normalization (bit-identical rounding to the previous division) and the incremental vertical window (u16 modular add/subtract cancels exactly).
- **Full accuracy suites**: `opencv_test_imgproc` filter/blur/smooth tests (`*BoxFilter*:*GaussianBlur*:*Blur*:*blur*:*Smooth*:*smooth*`) were run on K1 against a clean upstream HEAD build: **819 tests pass on both libraries, and the failure sets (pre-existing upstream failures, unrelated to this PR) are line-for-line identical**. These suites cover random anchors, all border modes, ROI offsets, and in-place operation.
- Combinations already covered upstream are unchanged by construction (same kernels, same codegen), so their accuracy behavior is inherited.

Note for reviewers: the large baseline gap also reflects that core `FilterEngine` runs single-threaded; that affects all non-HAL targets and may deserve a separate issue.


### 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-07-14 12:16:46 +03:00
Alexander Smorkalov a88eac3223 Merge pull request #29515 from Prasadayus:yolov3_replace
Replace Qualcomm yolov3.onnx with darknet-converted yolov3 for better results
2026-07-14 12:12:26 +03:00
Alexander Smorkalov 4c936e6c18 Merge pull request #29468 from aoguntayo:fix-mlas-s390x-missing-headers
mlas: add missing s390x ZVECTOR kernel headers to fix build
2026-07-14 10:45:51 +03:00
Abhishek Gola 42f41f749a Merge pull request #29516 from abhishek-gola:add_missing_mlas_support
3rdparty(mlas): add missing power (ppc64le) kernel headers #29516

Closes: https://github.com/opencv/opencv/issues/29465

### 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-14 10:45:06 +03:00
Alexander Smorkalov 526e42f355 Merge branch 4.x 2026-07-14 09:19:26 +03:00
Alexander Smorkalov d94b6d0df3 Merge pull request #29506 from manand881:fix/bfmatcher-crosscheck-mask
Fix: BFMatcher isMaskSupported false on crossCheck
2026-07-14 08:46:06 +03:00
Alessio Antochi bb90bb0d64 core: add missing geometry module include to opencv.hpp 2026-07-13 18:51:59 +03:00
Teddy-Yangjiale eec2c6fd3d video: add RVV intrinsics path for pyramidal LK optical flow iteration loop 2026-07-13 23:51:22 +08:00
Teddy-Yangjiale 60825757e4 dnn: add RVV fp32 microkernel for MR=1 (depthwise-remain) convolution 2026-07-13 22:47:39 +08:00
Prasadayus 56ae1e603b replace Qualcomm yolov3.onnx with darknet-converted yolov3 2026-07-13 16:20:24 +05:30
Anand Mahesh 0fb620f0f0 Fix: BFMatcher isMaskSupported false on crossCheck
isMaskSupported now returns false when the matcher is
created with crossCheck enabled, because the mask
path is unsupported in that mode. knnMatchImpl also
drops the mask so it cannot reach batchDistance and
trigger its mask.empty() assertion.

https://github.com/opencv/opencv/issues/22093
2026-07-13 08:56:07 +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
Alexander Smorkalov 8fdf15e26e Merge pull request #29499 from varun-jaiswal17:msmf-audio
videoio MSMF: audio sample-rate validation and last frame
2026-07-12 12:12:33 +03:00
Madan mohan Manokar 498e4692d6 Merge pull request #29479 from amd:5_fast_norm_simd
core: SIMD optimizations for norm, distance and Hamming APIs (5.x) #29479

- Cache normHamming SIMD impl via a function pointer to cut per-call dispatch cost (improves ORB & BRISK)
- Add masked norm and norm-diff SIMD kernels (incl. cn==4 paths) and AVX-512 dispatch for norm/stat
- Add brute-force matcher perf test
- Alternative port of PR #29335 adapted to the 5.x norm.simd.hpp structure

### 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-10 18:31:01 +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
Varun Jaiswal 97c2c31d9a videoio(MSMF): fix audio sample-rate validation and dropped last frame (#27438) 2026-07-10 19:29:04 +05:30
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 bc647fe3ec Merge pull request #29484 from velonica0:dnn-rvv-winograd-vlen-fix
dnn: fix out-of-bounds access in RVV Winograd kernels when VLEN > 256
2026-07-10 11:02:30 +03:00
Vincent Rabaud cbd227741f Merge pull request #29453 from vrabaud:persistence
Replace "static inline" by "inline" in headers #29453

This fixes #29436

I also replaced CV_INLINE which can be removed in a later PR.

I can make a similar PR for 4.x if you want.

### 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-07-10 10:52:42 +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 7559555599 Merge pull request #29480 from amd:5_fast_countNonZero
core: speed up countNonZero with AVX-512 signmask path (5.x)
2026-07-09 18:40:40 +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 4b38c5562b Merge pull request #29430 from Teddy-Yangjiale:rvv-k1-04-resize-nn
imgproc: re-enable RVV HAL resize NEAREST/NEAREST_EXACT
2026-07-09 18:05:56 +03:00
Abhishek Gola aa1b8a2a21 Merge pull request #29288 from abhishek-gola:doc_v3
Documentation fixes, Added How to use pre-built opencv doc #29288

closes: https://github.com/opencv/opencv/issues/29263

co-authored by: @kirtijindal14 @Akansha-977 
### 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
2026-07-09 17:31:43 +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 112959651a Merge pull request #29478 from vrabaud:persistence2
Fix case for intrin.h
2026-07-09 16:24:36 +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
velonica0 c86d642d19 dnn: fix out-of-bounds access in RVV Winograd kernels when VLEN > 256 2026-07-09 02:37:34 -07:00
Alexander Smorkalov e978c193cf pre: OpenCV 4.14.0 (version++) 2026-07-09 12:35:42 +03:00
Alexander Smorkalov abb0115648 Merge branch 4.x 2026-07-09 12:17:24 +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
Vincent Rabaud 71d900dc36 Fix case for intrin.h
This is similar to: https://github.com/opencv/opencv/pull/28754
2026-07-09 01:17:15 +02: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
Madan mohan Manokar 9ce06a5675 core: speed up countNonZero with AVX-512 signmask path (5.x)
- Count zero lanes via v_signmask()+popcount in AVX-512 dispatch units
    - Alternative port of PR #29390 adapted to the 5.x macro-based kernels, including a SIMD path for 64F.
2026-07-08 12:53:12 +00:00
velonica0 9121ebe28b imgproc: make RVV HAL resize bit-exact for 8U INTER_LINEAR 2026-07-08 05:24:31 -07:00
Anu Oguntayo eafce2f0ce mlas: add missing s390x ZVECTOR kernel headers to fix build
The previous commit bdf348c13a introduced
MLAS support including the s390x backend, but accidentally omitted the
required internal header files. This causes a compilation failure on
s390x:

  fatal error: SgemmKernelZVECTOR.h: No such file or directory

This PR restores the build by adding the following missing headers from
upstream (microsoft/onnxruntime):

- FgemmKernelZVECTOR.h
- SgemmKernelZVECTOR.h

This is the same class of bug as #29422 (loongarch64), which was fixed
by PR #29423 for that architecture only.
2026-07-07 14:02:27 +01: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
Alexander Smorkalov 4ce2389a62 Merge pull request #29425 from fkenmar:dnn_remove_caffe_leftovers
dnn: remove Caffe protobuf leftovers, fix builds with external protobuf
2026-07-07 12:52:37 +03:00
Andrei Fedorov 0488f6e942 Update deriv_ipp.cpp 2026-07-07 11:39:39 +02:00
Akansha-977 35bf0b4ac6 Merge pull request #29428 from Akansha-977:filter2D_IPP_migration
Filter2D IPP extraction to HAL for 5.x #29428

### 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-07 12:36:03 +03: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
Scott d1264cc6ec Merge pull request #29457 from Scott-Nx:fix/objdetect-new-dnn-target
dnn: silence false-positive CPU target warning for New graph engine #29457

### 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 (`5.x`)
* [ ] There is a reference to the original bug report and related work
  No existing OpenCV issue or pull request directly covers this warning. This PR is the original report and fix.
* [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
  Not applicable. This patch changes only diagnostic behavior for an existing CPU no-op path.
* [ ] The feature is well documented and sample code can be built with the project CMake
  Not applicable. This patch adds no feature or public API.

## Summary

Suppress a misleading warning emitted when OpenCV 5 New DNN graph engine receives its default CPU target request.

`FaceDetectorYN` and `FaceRecognizerSF` call:

```cpp
net.setPreferableTarget(DNN_TARGET_CPU);
```

after loading their networks.

When New graph engine is active, target switching is not supported. OpenCV therefore currently prints:

```text
Targets are not supported by the new graph engine for now
```

However, New graph engine already executes on CPU. The CPU request does not require a target change, is ignored, and inference continues through New graph engine.

The warning therefore suggests a failed configuration or fallback to Classic DNN even though neither occurs.

## Change

Treat `DNN_TARGET_CPU` as a silent no-op when generic New graph engine is active.

```text
New graph engine + CPU target
→ no target change needed
→ return unchanged
→ no warning

New graph engine + non-CPU target
→ target remains unsupported
→ preserve existing warning
→ return unchanged
```

## Behavior before

```text
New graph engine active
→ caller requests CPU
→ request is ignored
→ warning emitted
→ inference continues on New graph engine
```

## Behavior after

```text
New graph engine active
→ caller requests CPU
→ request is ignored
→ no warning
→ inference continues on New graph engine
```

## Unchanged behavior

* New graph engine selection is unchanged.
* Inference execution is unchanged.
* CPU remains the effective target for generic New graph engine.
* Classic DNN behavior is unchanged.
* ONNX Runtime handling is unchanged.
* Non-CPU targets continue to emit the existing warning.

## Motivation

The current diagnostic is a false positive for the default CPU request. It reports unsupported target selection even though CPU is already the active execution target and inference succeeds through New graph engine.

## Testing

* Built OpenCV locally.
* Ran relevant DNN tests.
* Verified New graph engine still loads and executes affected models.
* Verified CPU target requests no longer emit the misleading warning.
* Verified non-CPU target requests retain the existing unsupported-target warning.
2026-07-07 11:32:25 +03:00
Alexander Smorkalov 0b5ff7b3a2 Merge pull request #29449 from asmorkalov:as/skip_dnn_tests_32bit
Skip some BERT tests on 32-bit platforms as they do not fit into 2gb ram
2026-07-07 11:29:52 +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
Kenmar Francisco f7ad23157f dnn: remove Caffe protobuf leftovers, fix external protobuf builds
The Caffe importer removal (#28678) left behind the protoc-generated
opencv-caffe.pb.{cc,h} and caffe_io.{cpp,hpp}. The generated files can
only be compiled with an old libprotobuf and can no longer be
regenerated since opencv-caffe.proto was deleted, so any build with
PROTOBUF_UPDATE_FILES=ON or an external protobuf failed (#29291, #29419).

- Move the generic ReadProto* helpers, the only remaining consumers of
  caffe_io (used by the TensorFlow importer), to src/protobuf_io.{hpp,cpp};
  delete src/caffe and misc/caffe, and move glog_emulator.hpp to src/.
- Guard the protobuf version check so it also works with newer protobuf
  where GOOGLE_PROTOBUF_VERSION is not defined.
- Drop the unconditional opencv-onnx.pb.h include from cast2_layer.cpp,
  using the spec-fixed ONNX data type values instead, and update stale
  readNetFromONNX/readNetFromTensorflow stubs to the current signatures
  so WITH_PROTOBUF=OFF builds link again.
2026-07-06 14:38:46 -04:00
Alexander Smorkalov 77dff6dc75 Merge pull request #29448 from asmorkalov:as/skip_deep_features_tests_32bit
Skip DNN features test on 32-bit platforms as they time-to-time do not fit 2GB RAM
2026-07-06 17:46:26 +03: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
Alexander Smorkalov 3d0ae71ac1 Merge pull request #29429 from vrabaud:persistence
Fix CALIB_DISABLE_SCHUR_COMPLEMENT and CALIB_TILTED_MODEL collision
2026-07-06 10:11:57 +03:00
Alexander Smorkalov 80da12e55a Merge pull request #29432 from asmorkalov:as/python_testing
Replace np.testing.assert_allclose due to Numpy bug
2026-07-06 10:10:23 +03:00
Alexander Smorkalov bb96382942 Skip some BERT tests on 32-bit platforms as they do not fit into 2gb ram. 2026-07-06 10:08:21 +03:00
Alexander Smorkalov 854cf4225d Skip DNN features test on 32-bit platforms as they time-to-time do not feet 2GB RAM. 2026-07-06 10:03:24 +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
Alexander Smorkalov 100496b371 Replace np.testing.assert_allclose due to Numpy bug. 2026-07-03 16:25:51 +03: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
Vincent Rabaud e770c269b0 Fix CALIB_DISABLE_SCHUR_COMPLEMENT and CALIB_TILTED_MODEL collision 2026-07-03 13:19:11 +02:00
Prasad Ayush Kumar bbfe2eb0de Merge pull request #29414 from Prasadayus:box_filter_refactor
Merge pull request #29414 from Prasadayus:box_filter_refactor

Moving IPP functions to HAL for box_filter in Imgproc #29414

**Performance Numbers on Intel(R) Core(TM) i9-11900K:** https://docs.google.com/spreadsheets/d/1puWmOSTtAFwWjPu8J1SpuccPQvxUJU8NlFQs7mAZwL8/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-03 13:45:20 +03:00
Prasad Ayush Kumar ae93a81ec0 Merge pull request #29389 from Prasadayus:cvtcolor_refactor
Refactoring and optimizing cvtcolor in Imgproc #29389

**Key Changes:**

1. Unified three divergent SIMD swap implementations into a single shared `v_swap` helper.
2. Consolidated the repeated `CV_8U/CV_16U/CV_32F` depth-dispatch chains into a single `CvtColorLoopDepth` template.
3. Extracted the duplicated coefficient-selection loops in the YCrCb/YUV constructors into `selectYuvCoeffs`.
4. Collapsed the shared YUV420 store logic duplicated across both decode invokers into `storeYUV420block`.
5. Generalized the inline blue-channel coefficient swaps in `color_lab.cpp` into `swapBlueCoeffsCols`/`swapBlueCoeffsRows`.
6. Added a `v_dotprod`-based SIMD path (`v_RGB2Y`/`v_RGB2UV`) for the previously scalar `RGB8toYUV422Invoker`.
7. Migrated all inline `CV_IPP_CHECK` cvtColor paths out of the dispatch files and into the IPP HAL plugin (`hal/ipp/src/color_ipp.cpp`), replacing each call site with `CALL_HAL`.

**Performance Numbers on Intel(R) Core(TM) i9-11900K**: https://docs.google.com/spreadsheets/d/1pz0aHlTeG4Ao8RT7LipgdNSjhCJz92QfZG3GmNa63hU/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-03 10:53:13 +03:00
Alexander Smorkalov db37d38eb4 Merge pull request #29424 from Teddy-Yangjiale:rvv-k1-03-conv2-int8-kernel
dnn: add RVV kernel for new-engine int8 convolution
2026-07-03 10:48:59 +03:00
Teddy-Yangjiale 3aadaa8385 imgproc: re-enable RVV HAL resize NEAREST/NEAREST_EXACT 2026-07-03 14:29:07 +08: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
Alexander Smorkalov d82fbe3530 Merge pull request #29421 from Teddy-Yangjiale:rvv-k1-01-02-fastgemm-packb
dnn: add RISC-V RVV FP32 fastGemm micro-kernel and Pack-B support
2026-07-02 13:06:31 +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
Alexander Smorkalov 1439136ef8 Merge pull request #29423 from wojiushixiaobai:fix_loongarch64_5.x
mlas: add missing LoongArch64 kernel headers to fix build
2026-07-02 11:28:51 +03:00
吴小白 ee9c019556 mlas: add missing LoongArch64 kernel headers to fix build
Signed-off-by: 吴小白 <296015668@qq.com>
2026-07-02 11:50:07 +08:00
Teddy-Yangjiale e39c6c6bcc dnn: add RVV kernel for new-engine int8 convolution 2026-07-02 07:16:59 +08:00
Teddy-Yangjiale dcae1f1dc1 dnn: add RISC-V RVV FP32 fastGemm micro-kernel and Pack-B support 2026-07-02 05:46:43 +08: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
Abhishek Gola c7b8fb28b6 Merge pull request #29333 from abhishek-gola:attention_layer_extension
Extended Attention layer support #29333

Implemented present/past KV support.

Merge with: https://github.com/opencv/opencv_extra/pull/1381

Co-authored by: @Akansha-977 

### 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-01 15:41:44 +03:00
Teddy-Yangjiale 5866bbd9ff Merge pull request #29411 from Teddy-Yangjiale:rvv-dnn-int8-conv
dnn: add Winograd F(6,3) RVV implementation for RISC-V (VLEN≥256) #29411

### Summary

Adds a RISC-V Vector (RVV) backend for the Winograd F(6,3) convolution path
in the DNN module, targeting VLEN≥256 processors (tested on SpacemiT K1,
rv64gcv).

### Background

The Winograd F(6,3) path was already gated on `useSIMD128 || useAVX ||
useAVX2 || useNEON` at runtime, and protected by the same set of compile-time
macros. RVV was simply missing from both guards, so it always fell through to
generic convolution even when the hardware supports it.

A second issue: `conv_winograd_f63` was registered with
`ocv_add_dispatched_file`, which skips ISA variants not present in the host
toolchain during cross-compilation. Changing to `ocv_add_dispatched_file_force_all`
forces the `.rvv.cpp` translation unit to be generated unconditionally, matching
how every other RVV-enabled kernel in the DNN module is registered.

### Implementation notes

**Atom width.** `vsetvlmax_e32m1()` returns 8 on VLEN=256, so `winoAtomF32=8`
is chosen, matching the AVX2 atom width. The `impl_accum_F32` and transform
functions are structured identically to the AVX2 path (4 output channels ×
6 input tiles per atom).

**Input/output transform.** AVX2 uses `_mm256_unpacklo/hi_ps` + `permute2f128`
for an in-register 8×8 transpose. RVV has no equivalent cross-lane shuffle at
this width without `vrgather`, which adds index-vector overhead for a
non-bottleneck step. Instead the 8×8 intermediate matrix is transposed
scalar-in-memory between the two `wino_bt8x8_rvv` / `wino_at8x6_rvv` passes.
This keeps the transform code simple and correct; the GEMM in `impl_accum_F32`
dominates runtime.

**VLEN guard.** `getWinofunc_F32` checks `vsetvlmax_e32m1() >= 8` at runtime
and returns an empty functor on narrower implementations, so the code is safe
on VLEN=128 targets without a separate code path.

### Testing

**Correctness:** `ConvolutionWinograd.Accuracy` passes on the K1 board (9 ms).

**Performance:** `opencv_perf_dnn`, SpacemiT K1 (rv64gcv, VLEN=256),
GCC 13, `-O2 -march=rv64gcv`. Reported times are per-iteration medians from
`[ PERFSTAT ]` output (`--perf_min_samples=5`). "Generic" is the same build
with Winograd disabled via `OPENCV_DNN_DISABLE_WINOGRAD=1`.

| Input → Output C         | Winograd (ms) | Generic (ms) | Speedup |
|--------------------------|--------------|-------------|---------|
| {1,128,52,52} → 256     | 35.97        | 62.86       | 1.75×   |
| {1,512,13,13} → 1024    | 65.18        | 112.50      | 1.73×   |
| {1,256,75,75} → 256     | 159.75       | 271.65      | 1.70×   |
| {1,64,300,300} → 64     | 174.21       | 295.32      | 1.69×   |
| {1,1152,16,16} → 1152   | 176.12       | 245.33      | 1.39×   |

### Pull Request Readiness Checklist

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

- [x] I agree to contribute to the project under Apache 2 License.
- [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
- [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-07-01 14:33:46 +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
Alexander Smorkalov 80f48cc952 Merge pull request #29416 from vrabaud:persistence
Remove useless logger include from the public API
2026-07-01 13:41:50 +03:00
Saravana Balaji Mohan Balaji e2f0876777 Merge pull request #29217 from MBSaravanaBalaji:feat/onnx-lppool
dnn: implement LpPool ONNX operator #29217

## Summary

Implements the `LpPool` ONNX operator (opset 1–18), which was previously unregistered and caused a parse failure. `LpPool` computes the Lp-norm pooling: `(sum(|x|^p))^(1/p)` over a sliding window.

## Changes

- New `LpPoolLayer` in `modules/dnn/src/layers/lppool_layer.cpp`
  - Supports `kernel_shape`, `strides`, `dilations`, `pads`, `auto_pad` (NOTSET/SAME_UPPER), `ceil_mode`, and `p` (default 2)
  - SIMD fast paths for p=1 (abs + accumulate) and p=2 (square + accumulate + sqrt); scalar fallback for other values of p
- Registered `LpPool` dispatch entry in both `onnx_importer.cpp` (classic engine) and `onnx_importer2.cpp` (new graph engine)
- Added `LpPoolLayer` declaration to `modules/dnn/include/opencv2/dnn/all_layers.hpp`
- Registered layer class in `modules/dnn/src/init.cpp`
- Re-enabled 8 lppool conformance tests in `test_onnx_conformance.cpp` (previously in parser denylist)
- `test_lppool_2d_same_lower` added to the global conformance denylist — same known SAME_LOWER padding bug that affects `averagepool` and `maxpool`

## Testing

All applicable ONNX conformance tests pass:

| Test | Result |
|------|--------|
| test_lppool_1d_default | PASSED |
| test_lppool_2d_default | PASSED |
| test_lppool_2d_dilations | PASSED |
| test_lppool_2d_pads | PASSED |
| test_lppool_2d_same_lower | SKIPPED (known SAME_LOWER padding bug, consistent with avgpool/maxpool) |
| test_lppool_2d_same_upper | PASSED |
| test_lppool_2d_strides | PASSED |
| test_lppool_3d_default | PASSED |

Tested on: macOS (x86_64/SSE4, Rosetta 2) and Linux x86_64 (AVX2/AVX-512, GCC 13.3.0), Release build
OpenCV version: 5.0.0-pre

## Related Issues

None

---

### 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-07-01 13:27:55 +03:00
Fedorov, Andrey 2f779c5c36 moved ipp deriv to hal 2026-07-01 02:13:48 -07:00
Vincent Rabaud 68fb8aeb30 Remove useless logger include from the public API
This creates a conflict with some internal Google lib defining
the same symbols.
2026-07-01 11:09:57 +02:00
Alexander Smorkalov d36133b325 Merge pull request #29403 from Teddy-Yangjiale:rvv-dnn-conv-rvv
dnn: add RISC-V RVV fp32 microkernel for classic ConvolutionLayer
2026-06-30 14:19:32 +03: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
Alexander Smorkalov 97f62825a2 Merge pull request #29402 from tonuonu:perf-dnn-act-5x
dnn: SIMD for 13 transcendental activations in the 5.x engine
2026-06-29 14:31:21 +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
Teddy-Yangjiale cb0b3d820b dnn: add RISC-V RVV fp32 microkernel for classic ConvolutionLayer 2026-06-28 05:44:31 +08:00
Tonu Samuel 4d839a16f0 dnn: SIMD for 13 transcendental activations in the 5.x engine
Port of #29377 to 5.x. Wires Log, Erf, Exp, Sin, Cos, Sinh, Cosh, Tan,
Softplus, BNLL, Asinh, Acosh, Atanh into the dispatched activation_kernels
registry, plus a Layer_Activation perf test. 1.4-12.3x across
M4/A76/Threadripper/Xeon, correct to <=5e-7 vs scalar.
2026-06-27 09:11:09 +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
uwezkhan 8b06f28e1b Merge pull request #29370 from uwezkhan:onnx-tensor-payload-size-5.x
Validate onnx tensor payload size in getMatFromTensor (5.x) #29370

5.x port of #29314. The DNN ONNX importer diverged here, so the change is ported manually.

`getMatFromTensor()` sizes the output blob from `TensorProto.dims` and then reads that many elements out of the tensor payload, but the payload is sized independently in the model and never checked against the shape. In 5.x the payload reaches the read through three sources: a typed `*_data` field, `raw_data`, or external-file data routed through `getTensorRAWData()`. An initializer whose dims claim more elements than the payload holds makes the `Mat` copy/convert read past the buffer.

### Before
A FLOAT tensor declaring `[1000000]` backed by a 4-byte `raw_data` reads about 4 MB out of bounds (ASan flags a heap over-read in `cv::Mat::copyTo`). The same mismatch is present in every datatype branch and for both the typed-field and `rawdata` sources, reachable from `readNetFromONNX`/`readNetFromONNXBuffer` for every initializer.

### After
Compute the element count the shape implies once, then check each payload source holds at least that many elements before the read. `getTensorRAWData()` now reports the byte count it returns (covering both `raw_data` and external-file data), and `getMatFromTensor()` validates the typed fields and the raw byte count per datatype. A short payload throws a clear `cv::Exception` instead of over-reading.

### Tradeoffs
The check lives in `getMatFromTensor` because that is the one place the shape and the payload meet, so every initializer and attribute tensor is covered without each caller repeating it. The added cost is one multiply-accumulate over the dims plus a comparison per tensor at load time; well-formed models, where the payload already matches the shape, are unaffected. The element-count accumulation saturates on overflow so an oversized shape can't wrap to a small total and slip past the check.

### 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 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-25 16:05:22 +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
Savya Sanchi Sharma 5d121b768f Merge pull request #29386 from SavyaSanchi-Sharma:test_debug
fixed Dynamic quantized linear layer error #29386

### 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-06-25 15:44:02 +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
Alexander Smorkalov ae3542afa4 Merge pull request #29383 from asmorkalov:as/drop_timvx_5.x
Drop timvx builds in CI for 5.x as non-relevant any more.
2026-06-24 13:43:42 +03:00
Alexander Smorkalov bb645944e2 Merge pull request #29382 from asmorkalov:as/riscv-rvv-warn-fix
Fixed build warning in RISC-V RVV configuration.
2026-06-24 13:42:49 +03:00
Alexander Smorkalov 01efb9f92b Drop timvx builds in CI for 5.x as non-relevant any more. 2026-06-24 12:09:50 +03:00
Alexander Smorkalov c833519366 Fixed build warning in RISC-V RVV configuration. 2026-06-24 12:03:02 +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
Alexander Smorkalov 4ceb58f9f5 Merge pull request #29367 from vrabaud:persistence
Remove leftover fcvtns assembly
2026-06-23 12:38:32 +03:00
velonica0 c02b096a77 Merge pull request #29357 from velonica0:dnn_conv_rvv
dnn: vectorize fp32 convolution on RISC-V RVV #29357

### Summary

Vectorizes the new DNN engine's fp32 convolution on RISC-V **RVV**. Convolution was the last major block-layout operator still running scalar on RVV; this brings it to parity with the already-vectorized depthwise/pool/batchnorm path.

Single file, **purely additive** (`#elif CV_SIMD_SCALABLE` branches only) — x86/AVX2, ARM/NEON and AArch64 codegen are unchanged.

### Background

- **#28585** introduced block-layout conv with fp32 kernels specialized for AVX2 (`C0=8`) and NEON, scalar `#else` for everything else, and explicitly noted *"we could add the respective kernels later."*
- On RVV that `#else` meant conv ran **scalar at every VLEN** — the C0=8 fixed width approach doesn't transfer to a variable-VLEN ISA.
- **#29304** made the block size track the hardware width (`C0 = vlanes()`), fixing the VLEN≥256 assertion but leaving conv scalar.
- **This PR** supplies the deferred RVV conv vectorization, on top of that `C0=vlanes()` foundation.

### Approach

- Re-enable the `SPAT_BLOCK_SIZE=10` **blocked path** on RVV. Because `K0 == vlanes()`, one `v_float32` accumulator covers a full output-channel block, so 10 accumulators process 10 output positions together. A single K0-wide weight `vx_load` is reused across all 10 — amortizing the per-output-point load that bottlenecks the scalar path (this is what enables multi-core scaling; the scalar/per-FMA-load path is bandwidth-bound).
- Vectorize the **scalar tail** (the `<10`-position remainder) the same way.
- **Vector-length-agnostic:** `C0` is runtime, the kernel is written in terms of `v_float32`/`vlanes()`, so the same binary runs at full width on VLEN 128/256/512/1024 with no recompile. No per-VLEN kernels.
- The six `C0=8` specialized kernels stay `#if !CV_SIMD_SCALABLE`-gated (they can't run at `C0=vlanes()`); on RVV all shapes use the generic kernel.

### Performance

Convolution throughput, blocked (this PR) vs scalar, same machine/engine (VLEN=256, 8× rv64 @ 2.4 GHz), GFLOP/s:

| layer | scalar 1T | this 1T | scalar 8T | this 8T | 8T speedup |
|---|---|---|---|---|---|
| 3×3, 256→256, 64² | 1.13 | 13.23 | 8.28 | 75.62 | **9.1×** |
| 1×1, 256→256, 64² | 1.08 | 11.73 | 8.02 | 71.70 | 8.9× |
| 3×3, 128→128, 128² | 1.10 | 13.02 | 8.45 | 89.14 | **10.6×** |

≈ **12× single-thread, 9–11× at 8 threads**. Convolution dominates CNN inference time, so this is a large end-to-end win on RVV hardware.

### Validation

K3-class RVV board, VLEN=256: `Test_ONNX_layers` **264/264**, `Test_ONNX_conformance` **1641/1641**, **0 assertions**. Output is bit-identical to the scalar path (same accumulation order, just vectorized over `K0`).
Vector-length-agnosticism confirmed by running the VLEN=256 binary unchanged at **VLEN=1024**.


### 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-06-23 12:37:27 +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
Alexander Smorkalov bb3c1558e0 Merge pull request #29366 from Gold856:clean-up-cmake-version-checks
Clean up CMake version checks
2026-06-23 10:41:26 +03:00
Vincent Rabaud b8e82fa2c2 Remove leftover fcvtns assembly
Nothing in the history justifies this. It can trigger some memory
sanitizer that do not support raw assembly.
2026-06-23 09:20:19 +02: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
Abhishek Gola 81621ced10 Merge pull request #29323 from abhishek-gola:split_layer_extension
Extended support for Resize and Split layers #29323

Merge with: https://github.com/opencv/opencv_extra/pull/1379

Key changes: 

Resize layer: 
_antialias_ (linear & cubic) :- PIL-style separable resampling with stretched filter support and edge-clamped, renormalized weights.
_axes_ (incl. reversed [3,2]) :- getOutShape, the scale override, and runtime _tf_crop_and_resize_ ROI parsing now map 2-element sizes/scales/roi by the axes order instead of assuming [2,3].
_keep_aspect_ratio_policy_ (not_larger/not_smaller) :- output size from min/max per-axis scale.
_half_pixel_symmetric_ :- new coordinate-transform mode + importer mapping.
_align_corners_ downsampling :- coordinate scale uses the unfloored scaled length (in−1)/(in·x_scale−1).

Split Layer:
_convertTo empty 1-D Mat:_ the empty-Mat branch collapsed a 1-D [0] to 2-D [1,0] via cv::Size(); now uses allowTransposed like the non-empty path.
### 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-06-23 09:23:19 +03:00
Alexander Smorkalov 5049343874 Merge pull request #29362 from varun-jaiswal17:fix/mlas-hgemm-static-link
Define MlasGemmSupported under MLAS_GEMM_ONLY
2026-06-23 09:18:52 +03:00
Gold856 6fd1b687a1 Clean up CMake version checks 2026-06-22 20:38:16 -04:00
vrooomy 48a063158a define MlasGemmSupported under MLAS_GEMM_ONLY 2026-06-22 18:35:06 +05:30
Alexander Smorkalov 47eefa5abf Merge pull request #29356 from JArmandoAnaya:fix-ocl-predictvectorwidth-newtypes
core(ocl): fix out-of-bounds read and SIGFPE in predictOptimalVectorWidth for new depths
2026-06-22 13:29:43 +03:00
Alexander Smorkalov d939383524 Merge pull request #29352 from ShivaPriyanShanmuga:fix-mlas-mingw-posix-memalign
3rdparty(mlas): fix MinGW build by guarding aligned scratch buffer on _WIN32
2026-06-22 13:28:22 +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
Jesus Armando Anaya 7334957476 core(ocl): fix out-of-bounds read and SIGFPE in predictOptimalVectorWidth for new depths
predictOptimalVectorWidth() built its vectorWidths table with 8 entries
(depths CV_8U..CV_16F), but checkOptimalVectorWidth() indexes it by depth.
The 5.x depths CV_16BF, CV_Bool, CV_64U, CV_64S and CV_32U therefore read
past the end of the array; the garbage value can slip past the ckercn <= 0
guard, and the divider normalization loop then shifts the divider to zero,
so "offsets[i] % dividers[i]" raises SIGFPE. The failure is allocation
dependent and shows up as a sequence-dependent crash, e.g. in the OpenCL
Norm tests for CV_32U (cv::norm on a UMat reaches this via ocl_sum, which
calls predictOptimalVectorWidth before its own depth guard bails out).

Size the table to CV_DEPTH_MAX so every depth is in bounds and map the new
fixed-size integer depths to their natural OpenCL vector widths; CV_16BF
has no OpenCL vector type and stays scalar. Add a regression test covering
all depths.
2026-06-21 16:39:40 -07:00
ShivaPriyanShanmuga f831c94309 3rdparty(mlas): fix MinGW build of aligned scratch buffer
MlasThreadedBufAlloc() and its ThreadedBufHolder guarded the
_aligned_malloc / _aligned_free path on _MSC_VER. Non-MSVC Windows
toolchains (MinGW, clang) therefore fell through to posix_memalign(),
which the Windows CRT does not provide, so the build failed with
"'posix_memalign' was not declared in this scope".

Guard the aligned-allocation path on _WIN32 instead, in both the holder
declaration (mlasi.h) and definition (platform.cpp) so the unique_ptr
deleter type stays consistent across translation units, and include
<malloc.h> on Windows since MinGW only declares the _aligned_* functions
there. The non-Windows posix_memalign / aligned_alloc fallback is
unchanged.

Fixes #29350
2026-06-21 11:54:38 -04:00
uwezkhan fc746f35b9 fix fmt_pairs stack overflow in calcElemSize and decodeSimpleFormat 2026-06-20 15:31:34 +05:30
Alexander Smorkalov 6d2cd14bb2 Merge pull request #29343 from abhishek-gola:disable_windows_mlas_assembly
Disable MLAS on windows
2026-06-19 18:55:43 +03:00
Alexander Smorkalov 4f17d30997 Merge pull request #29192 from hcy11123323:op4
parallelize sort_ using parallel_for_
2026-06-19 14:10:38 +03:00
Abhishek Gola bc11496dcb disable mlas assembly on windows 2026-06-19 15:32:54 +05:30
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 72d21d7241 Merge pull request #29031 from asmorkalov:as/musl_c_tuning
Musl-C related fixes 5.x
2026-06-18 12:01:02 +03:00
Alexander Smorkalov 20d3018169 Musl-C related fixes and C for 5.x 2026-06-18 11:11:58 +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
385 changed files with 28716 additions and 88435 deletions
+3 -3
View File
@@ -14,6 +14,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:
@@ -52,9 +55,6 @@ jobs:
Android:
uses: opencv/ci-gha-workflow/.github/workflows/OCV-PR-5.x-Android.yaml@main
TIM-VX:
uses: opencv/ci-gha-workflow/.github/workflows/OCV-timvx-backend-tests-4.x.yml@main
docs:
uses: opencv/ci-gha-workflow/.github/workflows/OCV-PR-5.x-docs.yaml@main
-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)
+4
View File
@@ -71,6 +71,10 @@ set(mlas_platform_srcs)
set(OPENCV_DNN_MLAS_ENABLED 0 CACHE INTERNAL "" FORCE)
set(OPENCV_DNN_MLAS_SKIP_REASON "" CACHE INTERNAL "" FORCE)
if(WIN32)
return()
endif()
# Probe ASM language once. check_language(ASM) is a no-op when
# CMAKE_ASM_COMPILER is already set in cache — and the Android NDK toolchain
# pre-sets it for every ABI. So on Android the guard falls through to
+100
View File
@@ -0,0 +1,100 @@
/*++
Copyright (C) 2023 Loongson Technology Corporation Limited. All rights reserved.
Licensed under the MIT License.
Module Name:
FgemmKernelCommon.h
Abstract:
This module contains common kernel macros and structures for the floating
point matrix/matrix multiply operation (SGEMM and DGEMM).
--*/
//
// Define the typed instruction template.
//
#define FGEMM_TYPED_INSTRUCTION(Untyped, Typed) \
.macro Untyped Operand:vararg; Typed \Operand\(); .endm;
/*++
Macro Description:
This macro generates code to execute the block compute macro multiple
times and advancing the matrix A and matrix B data pointers.
Arguments:
ComputeBlock - Supplies the macro to compute a single block.
RowCount - Supplies the number of rows to process.
AdvanceMatrixAPlusRows - Supplies a non-zero value if the data pointer
in rbx should also be advanced as part of the loop.
Implicit Arguments:
a0 - Supplies the address into the matrix A data.
t7 - Supplies the address into the matrix A data plus 3 rows.
a1 - Supplies the address into the matrix B data.
a3 - Supplies the number of columns from matrix A and the number of rows
from matrix B to iterate over.
vr4-vr15 - Supplies the block accumulators.
--*/
.macro ComputeBlockLoop ComputeBlock, RowCount, AdvanceMatrixAPlusRows
move $t8, $a3 # reload CountK
li.d $s0, 4
blt $t8, $s0, .LProcessRemainingBlocks\@
.LComputeBlockBy4Loop\@:
\ComputeBlock\() \RowCount\(), 0, LFgemmElementSize*0, 64*4
\ComputeBlock\() \RowCount\(), 2*32, LFgemmElementSize*1, 64*4
addi.d $a1, $a1, 2*2*32 # advance matrix B by 128 bytes
\ComputeBlock\() \RowCount\(), 0, LFgemmElementSize*2, 64*4
\ComputeBlock\() \RowCount\(), 2*32, LFgemmElementSize*3, 64*4
addi.d $a1, $a1, 2*2*32 # advance matrix B by 128 bytes
addi.d $a0, $a0, 4*LFgemmElementSize # advance matrix A by 4 elements
.if \RowCount\() > 3
addi.d $t7, $t7, 4*LFgemmElementSize # advance matrix A plus rows by 4 elements
.if \RowCount\() == 12
addi.d $t3, $t3, 4*LFgemmElementSize
addi.d $t4,, $t4, 4*LFgemmElementSize
.endif
.endif
addi.d $t8, $t8, -4
li.d $s0, 4
bge $t8, $s0, .LComputeBlockBy4Loop\@
.LProcessRemainingBlocks\@:
beqz $t8, .LOutputBlock\@
.LComputeBlockBy1Loop\@:
\ComputeBlock\() \RowCount\(), 0, 0
addi.d $a1, $a1, 2*32 # advance matrix B by 64 bytes
addi.d $a0, $a0, LFgemmElementSize # advance matrix A by 1 element
.if \RowCount\() > 3
addi.d $t7, $t7, LFgemmElementSize # advance matrix A plus rows by 1 element
.if \RowCount\() == 12
addi.d $t3, $t3, LFgemmElementSize
addi.d $t4, $t4, LFgemmElementSize
.endif
.endif
addi.d $t8, $t8, -1
bnez $t8, .LComputeBlockBy1Loop\@
.LOutputBlock\@:
.endm
+546
View File
@@ -0,0 +1,546 @@
/*++
Copyright (C) 2023 Loongson Technology Corporation Limited. All rights reserved.
Licensed under the MIT License.
Module Name:
FgemmKernelLasxCommon.h
Abstract:
This module implements the kernels for the floating point matrix/matrix
multiply operation (SGEMM and DGEMM).
This implementation uses LASX instructions.
--*/
/*++
Macro Description:
This macro multiplies and accumulates for 2 YMMWORDs by N rows of the output
matrix.
Arguments:
RowCount - Supplies the number of rows to process.
VectorOffset - Supplies the byte offset from matrix B to fetch elements.
BroadcastOffset - Supplies the byte offset from matrix A to fetch elements.
PrefetchOffset - Optionally supplies the byte offset from matrix B to
prefetch elements.
Implicit Arguments:
a0 - Supplies the address into the matrix A data.
t7 - Supplies the address into the matrix A data plus 2 rows.
a1 - Supplies the address into the matrix B data.
t0 - Supplies the length in bytes of a row from matrix A.
xr8-xr15 - Supplies the block accumulators.
--*/
.macro ComputeBlockLasxBy16 RowCount, VectorOffset, BroadcastOffset, PrefetchOffset
.if \RowCount\() == 1
xvldrepl.w $xr3, $a0, \BroadcastOffset\()
xvld $xr4, $a1, \VectorOffset\()
xvfmadd $xr8, $xr4, $xr3, $xr8
xvld $xr5, $a1, \VectorOffset\()+32
xvfmadd $xr9, $xr5, $xr3, $xr9
.else
xvld $xr0, $a1, \VectorOffset\()
xvld $xr1, $a1, \VectorOffset\()+32
EmitIfCountGE \RowCount\(), 1, "xvldrepl $xr3,$a0, \BroadcastOffset\()"
EmitIfCountGE \RowCount\(), 1, "xvfmadd $xr8, $xr3, $xr0, $xr8"
EmitIfCountGE \RowCount\(), 1, "xvfmadd $xr9, $xr3, $xr1, $xr9"
EmitIfCountGE \RowCount\(), 2, "add.d $s0,$a0, $t0"
EmitIfCountGE \RowCount\(), 2, "xvldrepl $xr3,$s0, \BroadcastOffset\()"
EmitIfCountGE \RowCount\(), 2, "xvfmadd $xr10, $xr3, $xr0, $xr10"
EmitIfCountGE \RowCount\(), 2, "xvfmadd $xr11, $xr3, $xr1, $xr11"
EmitIfCountGE \RowCount\(), 3, "xvldrepl $xr3,$t7, \BroadcastOffset\()"
EmitIfCountGE \RowCount\(), 3, "xvfmadd $xr12, $xr3, $xr0, $xr12"
EmitIfCountGE \RowCount\(), 3, "xvfmadd $xr13, $xr3, $xr1, $xr13"
EmitIfCountGE \RowCount\(), 4, "add.d $s0,$t7, $t0"
EmitIfCountGE \RowCount\(), 4, "xvldrepl $xr3,$s0, \BroadcastOffset\()"
EmitIfCountGE \RowCount\(), 4, "xvfmadd $xr14, $xr3, $xr0, $xr14"
EmitIfCountGE \RowCount\(), 4, "xvfmadd $xr15, $xr3, $xr1, $xr15"
.endif
.endm
/*++
Macro Description:
This macro multiplies and accumulates for 1 YMMWORD by N rows of the output
matrix.
Arguments:
RowCount - Supplies the number of rows to process.
VectorOffset - Supplies the byte offset from matrix B to fetch elements.
BroadcastOffset - Supplies the byte offset from matrix A to fetch elements.
PrefetchOffset - Optionally supplies the byte offset from matrix B to
prefetch elements.
Implicit Arguments:
a0 - Supplies the address into the matrix A data.
t7 - Supplies the address into the matrix A data plus 2 rows.
a1 - Supplies the address into the matrix B data.
t0 - Supplies the length in bytes of a row from matrix A.
xr8-xr15 - Supplies the block accumulators.
--*/
.macro ComputeBlockLasxBy8 RowCount, VectorOffset, BroadcastOffset, PrefetchOffset
.if \RowCount\() == 1
xvldrepl.w $xr3, $a0, \BroadcastOffset\()
xvld $xr5, $a1, \VectorOffset\()
xvfmadd.s $xr9, $xr5, $xr3, $xr9
.else
xvld $xr0, $a1, \VectorOffset\()
EmitIfCountGE \RowCount\(), 1, "xvldrepl $xr3, $a0, \BroadcastOffset\()"
EmitIfCountGE \RowCount\(), 1, "xvfmadd $xr9, $xr3, $xr0, $xr9"
EmitIfCountGE \RowCount\(), 2, "add.d $s0, $a0, $t0"
EmitIfCountGE \RowCount\(), 2, "xvldrepl $xr3, $s0, \BroadcastOffset\()"
EmitIfCountGE \RowCount\(), 2, "xvfmadd $xr11, $xr3, $xr0, $xr11"
EmitIfCountGE \RowCount\(), 3, "xvldrepl $xr3, $t7, \BroadcastOffset\()"
EmitIfCountGE \RowCount\(), 3, "xvfmadd $xr13, $xr3, $xr0, $xr13"
EmitIfCountGE \RowCount\(), 4, "add.d $s0, $t7, $t0"
EmitIfCountGE \RowCount\(), 4, "xvldrepl $xr3, $s0, \BroadcastOffset\()"
EmitIfCountGE \RowCount\(), 4, "xvfmadd $xr15, $xr3, $xr0, $xr15"
.endif
.endm
/*++
Macro Description:
This macro generates code to execute the block compute macro multiple
times and advancing the matrix A and matrix B data pointers.
Arguments:
ComputeBlock - Supplies the macro to compute a single block.
RowCount - Supplies the number of rows to process.
Implicit Arguments:
a0 - Supplies the address into the matrix A data.
a1 - Supplies the address into the matrix B data.
a3 - Supplies the number of columns from matrix A and the number of rows
from matrix B to iterate over.
t0 - Supplies the length in bytes of a row from matrix A.
vr4-vr15 - Supplies the block accumulators.
--*/
.macro ComputeBlockLasxLoop ComputeBlock, RowCount
.if \RowCount\() > 2
# compute matrix A plus 2 rows
slli.d $s0, $t0, 1
add.d $t7, $a0, $s0
.endif
ComputeBlockLoop \ComputeBlock\(), \RowCount\(), \RowCount\() > 2
.if \RowCount\() > 2
# compute matrix C plus 2 rows
slli.d $s0, $t6, 1
add.d $t7, $a2, $s0
.endif
.endm
.macro store_n src, num, dst
move $s2, \num\()
beqz $s2, .Lstore_exit\@
xvstelm.w \src\(), \dst\(), 0, 0
addi.d $s2, $s2, -1
beqz $s2, .Lstore_exit\@
xvstelm.w \src\(), \dst\(), 4, 1
addi.d $s2, $s2, -1
beqz $s2, .Lstore_exit\@
xvstelm.w \src\(), \dst\(), 8, 2
addi.d $s2, $s2, -1
beqz $s2, .Lstore_exit\@
xvstelm.w \src\(), \dst\(), 12, 3
addi.d $s2, $s2, -1
beqz $s2, .Lstore_exit\@
xvstelm.w \src\(), \dst\(), 16, 4
addi.d $s2, $s2, -1
beqz $s2, .Lstore_exit\@
xvstelm.w \src\(), \dst\(), 20, 5
addi.d $s2, $s2, -1
beqz $s2, .Lstore_exit\@
xvstelm.w \src\(), \dst\(), 24, 6
addi.d $s2, $s2, -1
beqz $s2, .Lstore_exit\@
.Lstore_exit\@:
.endm
/*++
Macro Description:
This macro generates code to compute matrix multiplication for a fixed set
of rows.
Arguments:
RowCount - Supplies the number of rows to process.
Fallthrough - Supplies a non-blank value if the macro may fall through to
the ExitKernel label.
Implicit Arguments:
a0 - Supplies the address of matrix A.
a1 - Supplies the address of matrix B.
t1 - Supplies the address of matrix A.
a5 - Supplies the number of columns from matrix B and matrix C to iterate
over.
a2 - Supplies the address of matrix C.
a3 - Supplies the number of columns from matrix A and the number of rows
from matrix B to iterate over.
t0 - Supplies the length in bytes of a row from matrix A.
t6 - Supplies the length in bytes of a row from matrix C.
t5 - Stores the ZeroMode argument from the stack frame.
--*/
.macro ProcessCountM RowCount, Fallthrough
ori $s1, $r0, LFgemmYmmElementCount
bgeu $s1, $a5, .LProcessRemainingCountN\@
.LProcessNextColumnLoop2xN\@:
EmitIfCountGE \RowCount\(), 1, "xvxor.v $xr8, $xr8, $xr8"
EmitIfCountGE \RowCount\(), 1, "xvxor.v $xr9, $xr9, $xr9"
EmitIfCountGE \RowCount\(), 2, "xvxor.v $xr10, $xr10, $xr10"
EmitIfCountGE \RowCount\(), 2, "xvxor.v $xr11, $xr11, $xr11"
EmitIfCountGE \RowCount\(), 3, "xvxor.v $xr12, $xr12, $xr12"
EmitIfCountGE \RowCount\(), 3, "xvxor.v $xr13, $xr13, $xr13"
EmitIfCountGE \RowCount\(), 4, "xvxor.v $xr14, $xr14, $xr14"
EmitIfCountGE \RowCount\(), 4, "xvxor.v $xr15, $xr15, $xr15"
ComputeBlockLasxLoop ComputeBlockLasxBy16, \RowCount\()
EmitIfCountGE \RowCount\(), 1, "xvfmul $xr8, $xr8, $xr2"
EmitIfCountGE \RowCount\(), 1, "xvfmul $xr9, $xr9, $xr2"
EmitIfCountGE \RowCount\(), 2, "xvfmul $xr10, $xr10, $xr2"
EmitIfCountGE \RowCount\(), 2, "xvfmul $xr11, $xr11, $xr2"
EmitIfCountGE \RowCount\(), 3, "xvfmul $xr12, $xr12, $xr2"
EmitIfCountGE \RowCount\(), 3, "xvfmul $xr13, $xr13, $xr2"
EmitIfCountGE \RowCount\(), 4, "xvfmul $xr14, $xr14, $xr2"
EmitIfCountGE \RowCount\(), 4, "xvfmul $xr15, $xr15, $xr2"
sub.d $a5, $a5, $s1
sub.d $a5, $a5, $s1
blt $a5, $zero, .LOutputMasked2xNBlock\@
andi $s0, $t5, 0xff # ZeroMode?
bnez $s0, .LStore2xNBlock\@
EmitIfCountGE \RowCount\(), 1, "xvld $xr16, $a2, 0"
EmitIfCountGE \RowCount\(), 1, "xvfadd $xr8, $xr8, $xr16"
EmitIfCountGE \RowCount\(), 1, "xvld $xr16, $a2, 0x20"
EmitIfCountGE \RowCount\(), 1, "xvfadd $xr9, $xr9, $xr16"
EmitIfCountGE \RowCount\(), 2, "xvldx $xr16, $a2, $t6"
EmitIfCountGE \RowCount\(), 2, "xvfadd $xr10, $xr10, $xr16"
EmitIfCountGE \RowCount\(), 2, "add.d $s0, $a2, $t6"
EmitIfCountGE \RowCount\(), 2, "xvld $xr16, $s0, 0x20"
EmitIfCountGE \RowCount\(), 2, "xvfadd $xr11, $xr11, $xr16"
EmitIfCountGE \RowCount\(), 3, "xvld $xr16, $t7, 0"
EmitIfCountGE \RowCount\(), 3, "xvfadd $xr12, $xr12, $xr16"
EmitIfCountGE \RowCount\(), 3, "xvld $xr16, $t7, 0x20"
EmitIfCountGE \RowCount\(), 3, "xvfadd $xr13, $xr13, $xr16"
EmitIfCountGE \RowCount\(), 4, "xvldx $xr16, $t7, $t6"
EmitIfCountGE \RowCount\(), 4, "xvfadd $xr14, $xr14, $xr16"
EmitIfCountGE \RowCount\(), 4, "add.d $s0, $t7, $t6"
EmitIfCountGE \RowCount\(), 4, "xvld $xr16, $s0, 0x20"
EmitIfCountGE \RowCount\(), 4, "xvfadd $xr15, $xr15, $xr16"
.LStore2xNBlock\@:
EmitIfCountGE \RowCount\(), 1, "xvst $xr8, $a2, 0"
EmitIfCountGE \RowCount\(), 1, "xvst $xr9, $a2, 0x20"
EmitIfCountGE \RowCount\(), 2, "xvstx $xr10, $a2, $t6"
EmitIfCountGE \RowCount\(), 2, "add.d $s0, $a2, $t6"
EmitIfCountGE \RowCount\(), 2, "xvst $xr11, $s0, 0x20"
EmitIfCountGE \RowCount\(), 3, "xvst $xr12, $t7, 0"
EmitIfCountGE \RowCount\(), 3, "xvst $xr13, $t7, 0x20"
EmitIfCountGE \RowCount\(), 4, "xvstx $xr14, $t7, $t6"
EmitIfCountGE \RowCount\(), 4, "add.d $s0, $t7, $t6"
EmitIfCountGE \RowCount\(), 4, "xvst $xr15, $s0, 0x20"
addi.d $a2, $a2, 0x40 # advance matrix C by 2 XRWORDs
move $a0, $t1 # reload matrix A
bltu $s1, $a5, .LProcessNextColumnLoop2xN\@
beqz $a5, .LExitKernel
.LProcessRemainingCountN\@:
EmitIfCountGE \RowCount\(), 1, "xvxor.v $xr9, $xr9, $xr9"
EmitIfCountGE \RowCount\(), 2, "xvxor.v $xr11, $xr11, $xr11"
EmitIfCountGE \RowCount\(), 3, "xvxor.v $xr13, $xr13, $xr13"
EmitIfCountGE \RowCount\(), 4, "xvxor.v $xr15, $xr15, $xr15"
ComputeBlockLasxLoop ComputeBlockLasxBy8, \RowCount\()
EmitIfCountGE \RowCount\(), 1, "xvfmul $xr9, $xr9, $xr2"
EmitIfCountGE \RowCount\(), 2, "xvfmul $xr11, $xr11, $xr2"
EmitIfCountGE \RowCount\(), 3, "xvfmul $xr13, $xr13, $xr2"
EmitIfCountGE \RowCount\(), 4, "xvfmul $xr15, $xr15, $xr2"
bltu $a5, $s1, .LOutputMasked1xNBlock\@
andi $s0, $t5, 0xff # ZeroMode?
bnez $s0, .LStore1xNBlock\@
EmitIfCountGE \RowCount\(), 1, "xvld $xr16, $a2, 0"
EmitIfCountGE \RowCount\(), 1, "xvfadd $xr9, $xr9, $xr16"
EmitIfCountGE \RowCount\(), 2, "xvldx $xr16, $a2, $t6"
EmitIfCountGE \RowCount\(), 2, "xvfadd $xr11, $xr11, $xr16"
EmitIfCountGE \RowCount\(), 3, "xvld $xr16, $t7, 0"
EmitIfCountGE \RowCount\(), 3, "xvfadd $xr13, $xr13, $xr16"
EmitIfCountGE \RowCount\(), 4, "xvldx $xr16, $t7, $t6"
EmitIfCountGE \RowCount\(), 4, "xvfadd $xr15, $xr15, $xr16"
.LStore1xNBlock\@:
EmitIfCountGE \RowCount\(), 1, "xvst $xr9, $a2, 0"
EmitIfCountGE \RowCount\(), 2, "xvstx $xr11, $a2, $t6"
EmitIfCountGE \RowCount\(), 3, "xvst $xr13, $t7, 0"
EmitIfCountGE \RowCount\(), 4, "xvstx $xr15, $t7, $t6"
b .LExitKernel
.LOutputMasked2xNBlock\@:
andi $s0, $t5, 0xff # ZeroMode?
bnez $s0, .LStoreMasked2xNBlock\@
EmitIfCountGE \RowCount\(), 1, "xvld $xr16, $a2, 0"
EmitIfCountGE \RowCount\(), 1, "xvfadd $xr8, $xr8, $xr16"
EmitIfCountGE \RowCount\(), 2, "xvldx $xr16, $a2, $t6"
EmitIfCountGE \RowCount\(), 2, "xvfadd $xr10, $xr10, $xr16"
EmitIfCountGE \RowCount\(), 3, "xvld $xr16, $t7, 0"
EmitIfCountGE \RowCount\(), 3, "xvfadd $xr12, $xr12, $xr16"
EmitIfCountGE \RowCount\(), 4, "xvldx $xr16, $t7, $t6"
EmitIfCountGE \RowCount\(), 4, "xvfadd $xr14, $xr14, $xr16"
.LStoreMasked2xNBlock\@:
EmitIfCountGE \RowCount\(), 1, "xvst $xr8, $a2, 0"
EmitIfCountGE \RowCount\(), 2, "xvstx $xr10, $a2, $t6"
EmitIfCountGE \RowCount\(), 3, "xvst $xr12, $t7, 0"
EmitIfCountGE \RowCount\(), 4, "xvstx $xr14, $t7, $t6"
addi.d $a2, $a2, 0x20 # advance matrix C by YMMWORD
.if \RowCount\() > 2
addi.d $t7, $t7, 0x20 # advance matrix C plus 2 rows by YMMWORD
.endif
addi.d $a5, $a5, LFgemmYmmElementCount # correct for over-subtract above
.LOutputMasked1xNBlock\@:
.if \RowCount\() > 2
slli.d $s0, $t0, 1
add.d $t7, $a0, $s0
.endif
.if \RowCount\() == 1
.else
.endif
.if \RowCount\() > 2
slli.d $s0, $t6, 1
add.d $t7, $a2, $s0
.endif
sub.d $a5, $zero, $a5
la.global $a0, MlasMaskMoveTableLasx
ori $s0, $r0, LFgemmElementSize
mul.d $s0, $a5, $s0
addi.d $s0, $s0, 8*4
xvldx $xr0, $a0, $s0
andi $s0, $t5, 0xff
sub.d $a5, $zero, $a5
bnez $s0, .LStoreMasked1xNBlock\@
EmitIfCountGE \RowCount\(), 1, "xvld $xr16, $a2, 0"
EmitIfCountGE \RowCount\(), 1, "xvand.v $xr8, $xr16, $xr0"
EmitIfCountGE \RowCount\(), 2, "xvldx $xr16, $a2, $t6"
EmitIfCountGE \RowCount\(), 2, "xvand.v $xr10, $xr16, $xr0"
EmitIfCountGE \RowCount\(), 3, "xvld $xr16, $t7, 0"
EmitIfCountGE \RowCount\(), 3, "xvand.v $xr12, $xr16, $xr0"
EmitIfCountGE \RowCount\(), 4, "xvldx $xr16, $t7, $t6"
EmitIfCountGE \RowCount\(), 4, "xvand.v $xr14, $xr16, $xr0"
EmitIfCountGE \RowCount\(), 1, "xvfadd $xr9, $xr9, $xr8"
EmitIfCountGE \RowCount\(), 2, "xvfadd $xr11, $xr11, $xr10"
EmitIfCountGE \RowCount\(), 3, "xvfadd $xr13, $xr13, $xr12"
EmitIfCountGE \RowCount\(), 4, "xvfadd $xr15, $xr15, $xr14"
.LStoreMasked1xNBlock\@:
EmitIfCountGE \RowCount\(), 1, "store_n $xr9, $a5, $a2"
add.d $s3, $a2, $t6
EmitIfCountGE \RowCount\(), 2, "store_n $xr11, $a5, $s3"
EmitIfCountGE \RowCount\(), 3, "store_n $xr13, $a5, $t7"
add.d $s3, $t7, $t6
EmitIfCountGE \RowCount\(), 4, "store_n $xr15, $a5, $s3"
sub.d $a5, $zero, $a5
.ifb \Fallthrough\()
b .LExitKernel
.endif
.endm
/*++
Macro Description:
This macro generates the inner kernel to compute matrix multiplication.
Arguments:
FunctionName - Supplies the name for the generated function.
--*/
.macro FgemmKernelLasxFunction FunctionName
/*++
Routine Description:
This routine is an inner kernel to compute matrix multiplication for a
set of rows.
Arguments:
A a0 - Supplies the address of matrix A.
B a1 - Supplies the address of matrix B. The matrix data has been packed
using MlasSgemmCopyPackB or MlasSgemmTransposePackB.
C a2 - Supplies the address of matrix C.
CountK a3 - Supplies the number of columns from matrix A and the number
of rows from matrix B to iterate over.
CountM a4 - Supplies the maximum number of rows that can be processed for
matrix A and matrix C. The actual number of rows handled for this
invocation depends on the kernel implementation.
CountN a5 - Supplies the number of columns from matrix B and matrix C to
iterate over.
lda a6 - Supplies the first dimension of matrix A.
ldc a7 - Supplies the first dimension of matrix C.
Alpha f0 - Supplies the scalar alpha multiplier (see GEMM definition).
ZeroMode (sp + 0)- Supplies true if the output matrix must be zero initialized,
else false if the output matrix is accumulated into.
Return Value:
Returns the number of rows handled.
--*/
FUNCTION_ENTRY \FunctionName\()
addi.d $sp, $sp, -64
st.d $ra, $sp, 56
st.d $s0, $sp, 0*8
st.d $s1, $sp, 1*8
fst.s $f0, $sp, 2*8
fst.d $f16, $sp,3*8
st.d $s2, $sp, 4*8
st.d $s3, $sp, 5*8
move $t1, $a0
slli.d $t0, $a6, 2 # convert lda to bytes
slli.d $t6, $a7, 2 # convert ldc to bytes
ld.d $t5, $sp, 64 # get zeromode
fst.s $f0, $sp, 2*8
xvldrepl.w $xr2, $sp, 0x10
//
// Process 4 rows of the matrices.
//
ori $s0, $zero, 4
bltu $a4, $s0, .LProcessCountMLessThan4
li.d $a4, 4 # return 4 rows handled
ProcessCountM 4, Fallthrough
//
// Restore non-volatile registers and return.
//
.LExitKernel:
bstrpick.d $a0, $a4, 31, 0
ld.d $s0, $sp, 0
ld.d $s1, $sp, 8
fld.d $f16, $sp,3*8
ld.d $s2, $sp, 4*8
ld.d $s3, $sp, 5*8
ld.d $ra, $sp, 7*8
addi.d $sp, $sp, 64
jr $ra
//
// Process 2 rows of the matrices.
//
.LProcessCountMLessThan4:
ori $s0, $r0, 2
bltu $a4, $s0, .LProcessCountMLessThan2
li.d $a4, 2 # return 2 rows handled
ProcessCountM 2
//
// Process 1 row of the matrices.
//
.LProcessCountMLessThan2:
ProcessCountM 1
.endm
+170
View File
@@ -0,0 +1,170 @@
/*++
Copyright (C) 2023 Loongson Technology Corporation Limited. All rights reserved.
Licensed under the MIT License.
Module Name:
FgemmKernelLsxCommon.h
Abstract:
This module implements the kernels for the floating point matrix/matrix
multiply operation (SGEMM and DGEMM).
This implementation uses Lsx instructions.
--*/
#include "FgemmKernelCommon.h"
/*++
Macro Description:
This stores the block accumulators to the output matrix with an optional
accumulation of the existing contents of the output matrix.
Arguments:
RowCount - Supplies the number of rows to process.
VectorCount - Supplies the number of vector columns to process.
Implicit Arguments:
t5 - Supplies the length in bytes of a row from matrix C.
a2 - Supplies the address of matrix C.
s3 - Stores the ZeroMode argument from the stack frame.
vr8-vr15 - Supplies the block accumulators.
--*/
.macro AccumulateAndStoreBlock RowCount, VectorCount
and $s0, $t5,$t5 # ZeroMode?
bnez $s0 , .LSkipAccumulateOutput\@
EmitIfCount2GE \RowCount\(), 1, \VectorCount\(), 1, "vld $vr0, $a2, 0"
EmitIfCount2GE \RowCount\(), 1, \VectorCount\(), 2, "vld $vr1, $a2, 16"
EmitIfCount2GE \RowCount\(), 1, \VectorCount\(), 3, "vld $vr2, $a2, 32"
EmitIfCount2GE \RowCount\(), 1, \VectorCount\(), 4, "vld $vr3, $a2, 48"
EmitIfCount2GE \RowCount\(), 2, \VectorCount\(), 1, "vldx $vr4, $a2, $t6"
EmitIfCount2GE \RowCount\(), 2, \VectorCount\(), 2, "addi.d $s0, $t6, 16"
EmitIfCount2GE \RowCount\(), 2, \VectorCount\(), 2, "vldx $vr5, $a2, $s0"
EmitIfCount2GE \RowCount\(), 2, \VectorCount\(), 3, "addi.d $s0, $t6, 32"
EmitIfCount2GE \RowCount\(), 2, \VectorCount\(), 3, "vldx $vr6, $a2, $s0"
EmitIfCount2GE \RowCount\(), 2, \VectorCount\(), 4, "addi.d $s0, $t6, 48"
EmitIfCount2GE \RowCount\(), 2, \VectorCount\(), 4, "vldx $vr7, $a2, $s0"
EmitIfCount2GE \RowCount\(), 1, \VectorCount\(), 1, "vfadd $vr8, $vr8, $vr0"
EmitIfCount2GE \RowCount\(), 1, \VectorCount\(), 2, "vfadd $vr9, $vr9, $vr1"
EmitIfCount2GE \RowCount\(), 1, \VectorCount\(), 3, "vfadd $vr10,$vr10,$vr2"
EmitIfCount2GE \RowCount\(), 1, \VectorCount\(), 4, "vfadd $vr11,$vr11,$vr3"
EmitIfCount2GE \RowCount\(), 2, \VectorCount\(), 1, "vfadd $vr12,$vr12,$vr4"
EmitIfCount2GE \RowCount\(), 2, \VectorCount\(), 2, "vfadd $vr13,$vr13,$vr5"
EmitIfCount2GE \RowCount\(), 2, \VectorCount\(), 3, "vfadd $vr14,$vr14,$vr6"
EmitIfCount2GE \RowCount\(), 2, \VectorCount\(), 4, "vfadd $vr15,$vr15,$vr7"
.LSkipAccumulateOutput\@:
EmitIfCount2GE \RowCount\(), 1, \VectorCount\(), 1, "vst $vr8, $a2, 0"
EmitIfCount2GE \RowCount\(), 1, \VectorCount\(), 2, "vst $vr9, $a2, 16"
EmitIfCount2GE \RowCount\(), 1, \VectorCount\(), 3, "vst $vr10, $a2, 32"
EmitIfCount2GE \RowCount\(), 1, \VectorCount\(), 4, "vst $vr11, $a2, 48"
EmitIfCount2GE \RowCount\(), 2, \VectorCount\(), 1, "vstx $vr12, $a2, $t6"
EmitIfCount2GE \RowCount\(), 2, \VectorCount\(), 2, "addi.d $s0, $t6, 16"
EmitIfCount2GE \RowCount\(), 2, \VectorCount\(), 2, "vstx $vr13, $a2, $s0"
EmitIfCount2GE \RowCount\(), 2, \VectorCount\(), 3, "addi.d $s0, $t6, 32"
EmitIfCount2GE \RowCount\(), 2, \VectorCount\(), 3, "vstx $vr14, $a2, $s0"
EmitIfCount2GE \RowCount\(), 2, \VectorCount\(), 4, "addi.d $s0, $t6, 48"
EmitIfCount2GE \RowCount\(), 2, \VectorCount\(), 4, "vstx $vr15, $a2, $s0"
.endm
/*++
Macro Description:
This macro generates the inner kernel to compute matrix multiplication.
Arguments:
FunctionName - Supplies the name for the generated function.
--*/
.macro FgemmKernelLsxFunction FunctionName
/*++
Routine Description:
This routine is an inner kernel to compute matrix multiplication for a
set of rows.
Arguments:
A (a0) - Supplies the address of matrix A.
B (a1) - Supplies the address of matrix B. The matrix data has been packed
using MlasSgemmCopyPackB or MlasSgemmTransposePackB.
C (a2) - Supplies the address of matrix C.
CountK (a3) - Supplies the number of columns from matrix A and the number
of rows from matrix B to iterate over.
CountM (a4) - Supplies the maximum number of rows that can be processed for
matrix A and matrix C. The actual number of rows handled for this
invocation depends on the kernel implementation.
CountN (a5) - Supplies the number of columns from matrix B and matrix C to
iterate over.
lda (a6) Supplies the first dimension of matrix A.
ldc (a7) Supplies the first dimension of matrix C.
Alpha (f0) - Supplies the scalar alpha multiplier (see GEMM definition).
ZeroMode (sp 0) - Supplies true if the output matrix must be zero initialized,
else false if the output matrix is accumulated into.
Return Value:
Returns the number of rows handled.
--*/
FUNCTION_ENTRY \FunctionName\()
addi.d $sp, $sp, -64
st.d $t5, $sp, 0
st.d $s0, $sp, 1*8
st.d $s1, $sp, 2*8
st.d $s2, $sp, 3*8
st.d $s3, $sp, 4*8
move $t1, $a0
slli.d $t0, $a6, 2 //convert lda to bytes
slli.d $t6, $a7, 2 //convert ldc to bytes
ld.d $t5, $sp, 64
fmov.s $f24, $f0 //f0 destroyed by lsx
li.d $s0, 2
blt $a4, $s0, .LProcessCountM1
li.d $a4, 2
ProcessCountM 2, Fallthrough
.LExitKernel:
ld.d $t5, $sp, 0
ld.d $s0, $sp, 1*8
ld.d $s1, $sp, 2*8
ld.d $s2, $sp, 3*8
ld.d $s3, $sp, 4*8
addi.d $sp, $sp, 64
move $a0, $a4
jr $ra
.LProcessCountM1:
ProcessCountM 1
.endm
+35
View File
@@ -0,0 +1,35 @@
/*++
Copyright (C) 2023 Loongson Technology Corporation Limited. All rights reserved.
Licensed under the MIT License.
Module Name:
SgemmKernelCommon.h
Abstract:
This module contains common kernel macros and structures for the single
precision matrix/matrix multiply operation (SGEMM).
--*/
//
// Define the single precision parameters.
//
#define LFgemmElementShift 2
#define LFgemmElementSize (1 << LFgemmElementShift)
#define LFgemmYmmElementCount (32/LFgemmElementSize)
#include "FgemmKernelCommon.h"
//
// Define the typed instructions for single precision.
//
FGEMM_TYPED_INSTRUCTION(xvfadd, xvfadd.s)
FGEMM_TYPED_INSTRUCTION(xvfmadd, xvfmadd.s)
FGEMM_TYPED_INSTRUCTION(xvldrepl, xvldrepl.w)
FGEMM_TYPED_INSTRUCTION(xvfmul, xvfmul.s)
+3 -2
View File
@@ -48,6 +48,7 @@ Abstract:
#endif
#include <windows.h>
#include <intrin.h>
#include <malloc.h>
#else
#if defined(__arm__) || defined(__aarch64__)
#include <arm_neon.h>
@@ -3014,7 +3015,7 @@ MlasReadTimeStampCounter(void)
constexpr size_t ThreadedBufAlignment = 64;
extern thread_local size_t ThreadedBufSize;
#ifdef _MSC_VER
#ifdef _WIN32
extern thread_local std::unique_ptr<uint8_t, decltype(&_aligned_free)> ThreadedBufHolder;
#else
extern thread_local std::unique_ptr<uint8_t, decltype(&free)> ThreadedBufHolder;
@@ -3034,7 +3035,7 @@ void
MlasThreadedBufAlloc(size_t size)
{
if (size > ThreadedBufSize) {
#ifdef _MSC_VER
#ifdef _WIN32
ThreadedBufHolder.reset(
reinterpret_cast<uint8_t*>(_aligned_malloc(size, ThreadedBufAlignment)));
#elif (__STDC_VERSION__ >= 201112L) && !defined(__APPLE__)
+13 -2
View File
@@ -420,6 +420,17 @@ MLAS_PLATFORM::MLAS_PLATFORM(void)
// calls MlasSgemmKernelZero / MlasSgemmKernelAdd directly without going
// through GetMlasPlatform().GemmFloatKernel.
}
// FP16 HGemm kernels aren't vendored (MLAS_GEMM_ONLY) but compute.cpp references this probe; define it so static links resolve. See opencv/opencv#29342.
bool
MLASCALL
MlasHGemmSupported(
CBLAS_TRANSPOSE /*TransA*/,
CBLAS_TRANSPOSE /*TransB*/
)
{
return false;
}
#else // !MLAS_GEMM_ONLY
MLAS_PLATFORM::MLAS_PLATFORM(
void
@@ -881,7 +892,7 @@ Return Value:
}
else{
this->ErfFP16KernelRoutine = MlasNeonErfFP16Kernel;
this->GeluFP16KernelRoutine = MlasNeonGeluFP16Kernel;
this->GeluFP16KernelRoutine = MlasNeonGeluFP16Kernel;
}
#else
this->ErfFP16KernelRoutine = MlasNeonErfFP16Kernel;
@@ -1088,7 +1099,7 @@ MlasPlatformU8S8Overflow(
#endif
thread_local size_t ThreadedBufSize = 0;
#ifdef _MSC_VER
#ifdef _WIN32
thread_local std::unique_ptr<uint8_t, decltype(&_aligned_free)> ThreadedBufHolder(nullptr, &_aligned_free);
#else
thread_local std::unique_ptr<uint8_t, decltype(&free)> ThreadedBufHolder(nullptr, &free);
+333
View File
@@ -0,0 +1,333 @@
/*++
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the MIT License.
Module Name:
FgemmKernelPower.h
Abstract:
This module implements the kernels for the single/double precision matrix/matrix
multiply operation (DGEMM/SGEMM).
--*/
#include "mlasi.h"
#if defined(SINGLE)
#define MLAS_FLOATTYPE MLAS_FLOAT32X4
#define MLAS_GEMMTYPE float
#define MLAS_LOAD_FLOAT MlasLoadFloat32x4
#define MLAS_ZERO_FLOAT MlasZeroFloat32x4
#define MLAS_STORE_FLOAT MlasStoreFloat32x4
#define MLAS_EXTRACT_FLOAT MlasExtractLaneFloat32x4
#define MLAS_MUL_FLOAT MlasMultiplyFloat32x4
#define MLAS_MULADD_FLOAT MlasMultiplyAddFloat32x4
#define MLAS_BROADCAST_FLOAT MlasBroadcastFloat32x4
#else
#define MLAS_FLOATTYPE MLAS_FLOAT64X2
#define MLAS_GEMMTYPE double
#define MLAS_LOAD_FLOAT MlasLoadFloat64x2
#define MLAS_ZERO_FLOAT MlasZeroFloat64x2
#define MLAS_STORE_FLOAT MlasStoreFloat64x2
#define MLAS_EXTRACT_FLOAT MlasExtractLaneFloat64x2
#define MLAS_MUL_FLOAT MlasMultiplyFloat64x2
#define MLAS_MULADD_FLOAT MlasMultiplyAddFloat64x2
#define MLAS_BROADCAST_FLOAT MlasBroadcastFloat64x2
#endif
//
// Templates to ensure that a loop is unrolled.
//
template<size_t Count, size_t Index>
struct MlasLoopUnrollStep
{
template<typename IterationType, typename... IterationArgs>
MLAS_FORCEINLINE
static
void
Step(
IterationArgs&&... Arguments
)
{
IterationType::template Iteration<Count, Index>(Arguments...);
MlasLoopUnrollStep<Count, Index + 1>::template Step<IterationType>(Arguments...);
}
};
template<size_t Count>
struct MlasLoopUnrollStep<Count, Count>
{
template<typename IterationType, typename... IterationArgs>
MLAS_FORCEINLINE
static
void
Step(
IterationArgs&&...
)
{
// Terminate the loop.
}
};
template<size_t Count, typename IteratorType>
struct MlasLoopUnroll
{
template<typename... IterationArgs>
MLAS_FORCEINLINE
void
operator()(
IterationArgs&&... Arguments
)
{
MlasLoopUnrollStep<Count, 0>::template Step<IteratorType>(Arguments...);
}
};
//
// Templates used with loop unrolling to perform an action on one row of the
// output.
//
struct MlasFgemmZeroAccumulators
{
template<size_t RowCount, size_t Row>
MLAS_FORCEINLINE
static
void
Iteration(
MLAS_FLOATTYPE Accumulators[RowCount][4]
)
{
Accumulators[Row][0] = MLAS_ZERO_FLOAT();
Accumulators[Row][1] = MLAS_ZERO_FLOAT();
Accumulators[Row][2] = MLAS_ZERO_FLOAT();
Accumulators[Row][3] = MLAS_ZERO_FLOAT();
}
};
struct MlasFgemmLoadAElements
{
template<size_t RowCount, size_t Row>
MLAS_FORCEINLINE
static
void
Iteration(
MLAS_FLOATTYPE AElements[RowCount],
const MLAS_GEMMTYPE* A,
size_t lda
)
{
AElements[Row] = MLAS_LOAD_FLOAT(A + Row * lda);
}
};
struct MlasFgemmBroadcastAElements
{
template<size_t RowCount, size_t Row>
MLAS_FORCEINLINE
static
void
Iteration(
MLAS_FLOATTYPE ABroadcast[RowCount],
const MLAS_GEMMTYPE* A,
size_t lda
)
{
ABroadcast[Row] = MLAS_BROADCAST_FLOAT(A + Row * lda);
}
};
template<unsigned Lane>
struct MlasFgemmSplatAElements
{
template<size_t RowCount, size_t Row>
MLAS_FORCEINLINE
static
void
Iteration(
MLAS_FLOATTYPE AElements[RowCount],
MLAS_FLOATTYPE ABroadcast[RowCount]
)
{
ABroadcast[Row] = vec_splat(AElements[Row], Lane);
}
};
struct MlasFgemmMultiplyAddRow
{
template<size_t RowCount, size_t Row>
MLAS_FORCEINLINE
static
void
Iteration(
MLAS_FLOATTYPE Accumulators[RowCount][4],
MLAS_FLOATTYPE ABroadcast[RowCount],
MLAS_FLOATTYPE BElements[4]
)
{
Accumulators[Row][0] = MLAS_MULADD_FLOAT(ABroadcast[Row], BElements[0], Accumulators[Row][0]);
Accumulators[Row][1] = MLAS_MULADD_FLOAT(ABroadcast[Row], BElements[1], Accumulators[Row][1]);
Accumulators[Row][2] = MLAS_MULADD_FLOAT(ABroadcast[Row], BElements[2], Accumulators[Row][2]);
Accumulators[Row][3] = MLAS_MULADD_FLOAT(ABroadcast[Row], BElements[3], Accumulators[Row][3]);
}
};
template<size_t RowCount>
MLAS_FORCEINLINE
void
MlasFgemmComputeBlock(
MLAS_FLOATTYPE Accumulators[RowCount][4],
MLAS_FLOATTYPE ABroadcast[RowCount],
const MLAS_GEMMTYPE* B
)
{
MLAS_FLOATTYPE BElements[4];
#if defined(SINGLE)
BElements[0] = MLAS_LOAD_FLOAT(B);
BElements[1] = MLAS_LOAD_FLOAT(B + 4);
BElements[2] = MLAS_LOAD_FLOAT(B + 8);
BElements[3] = MLAS_LOAD_FLOAT(B + 12);
#else
BElements[0] = MLAS_LOAD_FLOAT(B);
BElements[1] = MLAS_LOAD_FLOAT(B + 2);
BElements[2] = MLAS_LOAD_FLOAT(B + 4);
BElements[3] = MLAS_LOAD_FLOAT(B + 6);
#endif
MlasLoopUnroll<RowCount, MlasFgemmMultiplyAddRow>()(Accumulators, ABroadcast, BElements);
}
struct MlasFgemmMultiplyAlphaRow
{
template<size_t Count, size_t Index>
MLAS_FORCEINLINE
static
void
Iteration(
MLAS_FLOATTYPE Accumulators[4],
MLAS_FLOATTYPE AlphaBroadcast
)
{
Accumulators[Index] = MLAS_MUL_FLOAT(Accumulators[Index], AlphaBroadcast);
}
};
struct MlasFgemmMultiplyAlphaAddRow
{
template<size_t Count, size_t Index>
MLAS_FORCEINLINE
static
void
Iteration(
MLAS_FLOATTYPE Accumulators[4],
MLAS_FLOATTYPE AlphaBroadcast,
const MLAS_GEMMTYPE* C
)
{
#if defined(SINGLE)
Accumulators[Index] = MLAS_MULADD_FLOAT(Accumulators[Index],
AlphaBroadcast, MLAS_LOAD_FLOAT(C + Index * 4));
#else
Accumulators[Index] = MLAS_MULADD_FLOAT(Accumulators[Index],
AlphaBroadcast, MLAS_LOAD_FLOAT(C + Index * 2));
#endif
}
};
struct MlasFgemmStoreRow
{
template<size_t Count, size_t Index>
MLAS_FORCEINLINE
static
void
Iteration(
MLAS_FLOATTYPE Accumulators[4],
MLAS_GEMMTYPE* C
)
{
#if defined(SINGLE)
MLAS_STORE_FLOAT(C + Index * 4, Accumulators[Index]);
#else
MLAS_STORE_FLOAT(C + Index * 2, Accumulators[Index]);
#endif
}
};
template<size_t VectorCount>
struct MlasFgemmStoreVector
{
template<size_t RowCount, size_t Row>
MLAS_FORCEINLINE
static
void
Iteration(
MLAS_FLOATTYPE Accumulators[RowCount][4],
MLAS_GEMMTYPE* C,
size_t ldc,
MLAS_FLOATTYPE AlphaBroadcast,
bool ZeroMode
)
{
MLAS_GEMMTYPE* c = C + Row * ldc;
if (ZeroMode) {
MlasLoopUnroll<VectorCount, MlasFgemmMultiplyAlphaRow>()(Accumulators[Row], AlphaBroadcast);
} else {
MlasLoopUnroll<VectorCount, MlasFgemmMultiplyAlphaAddRow>()(Accumulators[Row], AlphaBroadcast, c);
}
MlasLoopUnroll<VectorCount, MlasFgemmStoreRow>()(Accumulators[Row], c);
//
// Shift down any unaligned elements to the bottom for further processing.
//
if (VectorCount < 4) {
Accumulators[Row][0] = Accumulators[Row][VectorCount];
}
}
};
struct MlasFgemmMultiplyAlphaTrailing
{
template<size_t RowCount, size_t Row>
MLAS_FORCEINLINE
static
void
Iteration(
MLAS_FLOATTYPE Accumulators[RowCount][4],
MLAS_FLOATTYPE AlphaBroadcast
)
{
Accumulators[Row][0] = MLAS_MUL_FLOAT(Accumulators[Row][0], AlphaBroadcast);
}
};
template<unsigned Lane>
struct MlasFgemmStoreScalar
{
template<size_t RowCount, size_t Row>
MLAS_FORCEINLINE
static
void
Iteration(
MLAS_FLOATTYPE Accumulators[RowCount][4],
MLAS_GEMMTYPE* C,
size_t ldc,
bool ZeroMode
)
{
MLAS_GEMMTYPE* c = C + Row * ldc + Lane;
MLAS_GEMMTYPE Value = MLAS_EXTRACT_FLOAT<Lane>(Accumulators[Row][0]);
if (!ZeroMode) {
Value += *c;
}
*c = Value;
}
};
+1
View File
@@ -19,6 +19,7 @@ Abstract:
asm volatile("dcbt 0, %0" ::"r"(addr) : "memory");
#include "SgemmKernelpower.h"
#include <cstring> // memset; OpenCV's vendored mlasi.h subset does not pull in <cstring>
extern "C" void
PackAKernelPOWER10(__vector float* D, const float* A, size_t lda, size_t k, size_t RowCount);
struct MlasSgemmBroadcastAElementsMMA
+139
View File
@@ -0,0 +1,139 @@
/*++
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the MIT License.
Module Name:
SgemmKernelpower.h
Abstract:
This module implements the kernels for the single precision matrix/matrix
multiply operation (SGEMM).
--*/
#include "FgemmKernelpower.h"
template<size_t RowCount>
MLAS_FORCEINLINE
size_t
MlasSgemmProcessCount(
const float* A,
const float* B,
float* C,
size_t CountK,
size_t CountN,
size_t lda,
size_t ldc,
MLAS_FLOAT32X4 AlphaBroadcast,
bool ZeroMode
)
{
do {
const float* a = A;
size_t k = CountK;
MLAS_FLOAT32X4 Accumulators[RowCount][4];
MLAS_FLOAT32X4 AElements[RowCount];
MLAS_FLOAT32X4 ABroadcast[RowCount];
//
// Clear the block accumulators.
//
MlasLoopUnroll<RowCount, MlasFgemmZeroAccumulators>()(Accumulators);
//
// Compute the output block.
//
while (k >= 4) {
MlasLoopUnroll<RowCount, MlasFgemmLoadAElements>()(AElements, a, lda);
MlasLoopUnroll<RowCount, MlasFgemmSplatAElements<0>>()(AElements, ABroadcast);
MlasFgemmComputeBlock<RowCount>(Accumulators, ABroadcast, B);
MlasLoopUnroll<RowCount, MlasFgemmSplatAElements<1>>()(AElements, ABroadcast);
MlasFgemmComputeBlock<RowCount>(Accumulators, ABroadcast, B + 16);
MlasLoopUnroll<RowCount, MlasFgemmSplatAElements<2>>()(AElements, ABroadcast);
MlasFgemmComputeBlock<RowCount>(Accumulators, ABroadcast, B + 32);
MlasLoopUnroll<RowCount, MlasFgemmSplatAElements<3>>()(AElements, ABroadcast);
MlasFgemmComputeBlock<RowCount>(Accumulators, ABroadcast, B + 48);
a += 4;
B += 16 * 4;
k -= 4;
}
while (k > 0) {
MlasLoopUnroll<RowCount, MlasFgemmBroadcastAElements>()(ABroadcast, a, lda);
MlasFgemmComputeBlock<RowCount>(Accumulators, ABroadcast, B);
a += 1;
B += 16;
k -= 1;
}
if (CountN >= 16) {
//
// Store the entire output block.
//
MlasLoopUnroll<RowCount, MlasFgemmStoreVector<4>>()(Accumulators, C, ldc, AlphaBroadcast, ZeroMode);
} else {
//
// Store the partial output block.
//
if (CountN >= 12) {
MlasLoopUnroll<RowCount, MlasFgemmStoreVector<3>>()(Accumulators, C, ldc, AlphaBroadcast, ZeroMode);
} else if (CountN >= 8) {
MlasLoopUnroll<RowCount, MlasFgemmStoreVector<2>>()(Accumulators, C, ldc, AlphaBroadcast, ZeroMode);
} else if (CountN >= 4) {
MlasLoopUnroll<RowCount, MlasFgemmStoreVector<1>>()(Accumulators, C, ldc, AlphaBroadcast, ZeroMode);
}
//
// Store the remaining unaligned columns.
//
C += (CountN & ~3);
CountN &= 3;
if (CountN > 0) {
MlasLoopUnroll<RowCount, MlasFgemmMultiplyAlphaTrailing>()(Accumulators, AlphaBroadcast);
MlasLoopUnroll<RowCount, MlasFgemmStoreScalar<0>>()(Accumulators, C, ldc, ZeroMode);
if (CountN >= 2) {
MlasLoopUnroll<RowCount, MlasFgemmStoreScalar<1>>()(Accumulators, C, ldc, ZeroMode);
}
if (CountN >= 3) {
MlasLoopUnroll<RowCount, MlasFgemmStoreScalar<2>>()(Accumulators, C, ldc, ZeroMode);
}
}
break;
}
C += 16;
CountN -= 16;
} while (CountN > 0);
return RowCount;
}
+47
View File
@@ -0,0 +1,47 @@
/*++
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the MIT License.
Module Name:
asmmacro.h
Abstract:
This module implements common macros for the assembly modules.
--*/
#if defined(__APPLE__)
#define C_UNDERSCORE(symbol) _##symbol
#else
#define C_UNDERSCORE(symbol) symbol
#endif
/*++
Macro Description:
This macro emits the assembler directives to annotate a new function.
Arguments:
FunctionName - Supplies the name of the function.
--*/
.macro FUNCTION_ENTRY FunctionName
.p2align 4
#if defined(__APPLE__)
.globl _\FunctionName\()
_\FunctionName\():
#else
.globl \FunctionName\()
.type \FunctionName\(),@function
\FunctionName\():
#endif
.endm
+333
View File
@@ -0,0 +1,333 @@
/*++
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the MIT License.
Module Name:
FgemmKernelZVECTOR.h
Abstract:
This module implements the kernels for the single/double precision matrix/matrix
multiply operation (DGEMM/SGEMM).
--*/
#include "mlasi.h"
#if defined(SINGLE)
#define MLAS_FLOATTYPE MLAS_FLOAT32X4
#define MLAS_GEMMTYPE float
#define MLAS_LOAD_FLOAT MlasLoadFloat32x4
#define MLAS_ZERO_FLOAT MlasZeroFloat32x4
#define MLAS_STORE_FLOAT MlasStoreFloat32x4
#define MLAS_EXTRACT_FLOAT MlasExtractLaneFloat32x4
#define MLAS_MUL_FLOAT MlasMultiplyFloat32x4
#define MLAS_MULADD_FLOAT MlasMultiplyAddFloat32x4
#define MLAS_BROADCAST_FLOAT MlasBroadcastFloat32x4
#else
#define MLAS_FLOATTYPE MLAS_FLOAT64X2
#define MLAS_GEMMTYPE double
#define MLAS_LOAD_FLOAT MlasLoadFloat64x2
#define MLAS_ZERO_FLOAT MlasZeroFloat64x2
#define MLAS_STORE_FLOAT MlasStoreFloat64x2
#define MLAS_EXTRACT_FLOAT MlasExtractLaneFloat64x2
#define MLAS_MUL_FLOAT MlasMultiplyFloat64x2
#define MLAS_MULADD_FLOAT MlasMultiplyAddFloat64x2
#define MLAS_BROADCAST_FLOAT MlasBroadcastFloat64x2
#endif
//
// Templates to ensure that a loop is unrolled.
//
template<size_t Count, size_t Index>
struct MlasLoopUnrollStep
{
template<typename IterationType, typename... IterationArgs>
MLAS_FORCEINLINE
static
void
Step(
IterationArgs&&... Arguments
)
{
IterationType::template Iteration<Count, Index>(Arguments...);
MlasLoopUnrollStep<Count, Index + 1>::template Step<IterationType>(Arguments...);
}
};
template<size_t Count>
struct MlasLoopUnrollStep<Count, Count>
{
template<typename IterationType, typename... IterationArgs>
MLAS_FORCEINLINE
static
void
Step(
IterationArgs&&...
)
{
// Terminate the loop.
}
};
template<size_t Count, typename IteratorType>
struct MlasLoopUnroll
{
template<typename... IterationArgs>
MLAS_FORCEINLINE
void
operator()(
IterationArgs&&... Arguments
)
{
MlasLoopUnrollStep<Count, 0>::template Step<IteratorType>(Arguments...);
}
};
//
// Templates used with loop unrolling to perform an action on one row of the
// output.
//
struct MlasFgemmZeroAccumulators
{
template<size_t RowCount, size_t Row>
MLAS_FORCEINLINE
static
void
Iteration(
MLAS_FLOATTYPE Accumulators[RowCount][4]
)
{
Accumulators[Row][0] = MLAS_ZERO_FLOAT();
Accumulators[Row][1] = MLAS_ZERO_FLOAT();
Accumulators[Row][2] = MLAS_ZERO_FLOAT();
Accumulators[Row][3] = MLAS_ZERO_FLOAT();
}
};
struct MlasFgemmLoadAElements
{
template<size_t RowCount, size_t Row>
MLAS_FORCEINLINE
static
void
Iteration(
MLAS_FLOATTYPE AElements[RowCount],
const MLAS_GEMMTYPE* A,
size_t lda
)
{
AElements[Row] = MLAS_LOAD_FLOAT(A + Row * lda);
}
};
struct MlasFgemmBroadcastAElements
{
template<size_t RowCount, size_t Row>
MLAS_FORCEINLINE
static
void
Iteration(
MLAS_FLOATTYPE ABroadcast[RowCount],
const MLAS_GEMMTYPE* A,
size_t lda
)
{
ABroadcast[Row] = MLAS_BROADCAST_FLOAT(A + Row * lda);
}
};
template<unsigned Lane>
struct MlasFgemmSplatAElements
{
template<size_t RowCount, size_t Row>
MLAS_FORCEINLINE
static
void
Iteration(
MLAS_FLOATTYPE AElements[RowCount],
MLAS_FLOATTYPE ABroadcast[RowCount]
)
{
ABroadcast[Row] = vec_splat(AElements[Row], Lane);
}
};
struct MlasFgemmMultiplyAddRow
{
template<size_t RowCount, size_t Row>
MLAS_FORCEINLINE
static
void
Iteration(
MLAS_FLOATTYPE Accumulators[RowCount][4],
MLAS_FLOATTYPE ABroadcast[RowCount],
MLAS_FLOATTYPE BElements[4]
)
{
Accumulators[Row][0] = MLAS_MULADD_FLOAT(ABroadcast[Row], BElements[0], Accumulators[Row][0]);
Accumulators[Row][1] = MLAS_MULADD_FLOAT(ABroadcast[Row], BElements[1], Accumulators[Row][1]);
Accumulators[Row][2] = MLAS_MULADD_FLOAT(ABroadcast[Row], BElements[2], Accumulators[Row][2]);
Accumulators[Row][3] = MLAS_MULADD_FLOAT(ABroadcast[Row], BElements[3], Accumulators[Row][3]);
}
};
template<size_t RowCount>
MLAS_FORCEINLINE
void
MlasFgemmComputeBlock(
MLAS_FLOATTYPE Accumulators[RowCount][4],
MLAS_FLOATTYPE ABroadcast[RowCount],
const MLAS_GEMMTYPE* B
)
{
MLAS_FLOATTYPE BElements[4];
#if defined(SINGLE)
BElements[0] = MLAS_LOAD_FLOAT(B);
BElements[1] = MLAS_LOAD_FLOAT(B + 4);
BElements[2] = MLAS_LOAD_FLOAT(B + 8);
BElements[3] = MLAS_LOAD_FLOAT(B + 12);
#else
BElements[0] = MLAS_LOAD_FLOAT(B);
BElements[1] = MLAS_LOAD_FLOAT(B + 2);
BElements[2] = MLAS_LOAD_FLOAT(B + 4);
BElements[3] = MLAS_LOAD_FLOAT(B + 6);
#endif
MlasLoopUnroll<RowCount, MlasFgemmMultiplyAddRow>()(Accumulators, ABroadcast, BElements);
}
struct MlasFgemmMultiplyAlphaRow
{
template<size_t Count, size_t Index>
MLAS_FORCEINLINE
static
void
Iteration(
MLAS_FLOATTYPE Accumulators[4],
MLAS_FLOATTYPE AlphaBroadcast
)
{
Accumulators[Index] = MLAS_MUL_FLOAT(Accumulators[Index], AlphaBroadcast);
}
};
struct MlasFgemmMultiplyAlphaAddRow
{
template<size_t Count, size_t Index>
MLAS_FORCEINLINE
static
void
Iteration(
MLAS_FLOATTYPE Accumulators[4],
MLAS_FLOATTYPE AlphaBroadcast,
const MLAS_GEMMTYPE* C
)
{
#if defined(SINGLE)
Accumulators[Index] = MLAS_MULADD_FLOAT(Accumulators[Index],
AlphaBroadcast, MLAS_LOAD_FLOAT(C + Index * 4));
#else
Accumulators[Index] = MLAS_MULADD_FLOAT(Accumulators[Index],
AlphaBroadcast, MLAS_LOAD_FLOAT(C + Index * 2));
#endif
}
};
struct MlasFgemmStoreRow
{
template<size_t Count, size_t Index>
MLAS_FORCEINLINE
static
void
Iteration(
MLAS_FLOATTYPE Accumulators[4],
MLAS_GEMMTYPE* C
)
{
#if defined(SINGLE)
MLAS_STORE_FLOAT(C + Index * 4, Accumulators[Index]);
#else
MLAS_STORE_FLOAT(C + Index * 2, Accumulators[Index]);
#endif
}
};
template<size_t VectorCount>
struct MlasFgemmStoreVector
{
template<size_t RowCount, size_t Row>
MLAS_FORCEINLINE
static
void
Iteration(
MLAS_FLOATTYPE Accumulators[RowCount][4],
MLAS_GEMMTYPE* C,
size_t ldc,
MLAS_FLOATTYPE AlphaBroadcast,
bool ZeroMode
)
{
MLAS_GEMMTYPE* c = C + Row * ldc;
if (ZeroMode) {
MlasLoopUnroll<VectorCount, MlasFgemmMultiplyAlphaRow>()(Accumulators[Row], AlphaBroadcast);
} else {
MlasLoopUnroll<VectorCount, MlasFgemmMultiplyAlphaAddRow>()(Accumulators[Row], AlphaBroadcast, c);
}
MlasLoopUnroll<VectorCount, MlasFgemmStoreRow>()(Accumulators[Row], c);
//
// Shift down any unaligned elements to the bottom for further processing.
//
if (VectorCount < 4) {
Accumulators[Row][0] = Accumulators[Row][VectorCount];
}
}
};
struct MlasFgemmMultiplyAlphaTrailing
{
template<size_t RowCount, size_t Row>
MLAS_FORCEINLINE
static
void
Iteration(
MLAS_FLOATTYPE Accumulators[RowCount][4],
MLAS_FLOATTYPE AlphaBroadcast
)
{
Accumulators[Row][0] = MLAS_MUL_FLOAT(Accumulators[Row][0], AlphaBroadcast);
}
};
template<unsigned Lane>
struct MlasFgemmStoreScalar
{
template<size_t RowCount, size_t Row>
MLAS_FORCEINLINE
static
void
Iteration(
MLAS_FLOATTYPE Accumulators[RowCount][4],
MLAS_GEMMTYPE* C,
size_t ldc,
bool ZeroMode
)
{
MLAS_GEMMTYPE* c = C + Row * ldc + Lane;
MLAS_GEMMTYPE Value = MLAS_EXTRACT_FLOAT<Lane>(Accumulators[Row][0]);
if (!ZeroMode) {
Value += *c;
}
*c = Value;
}
};
+139
View File
@@ -0,0 +1,139 @@
/*++
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the MIT License.
Module Name:
SgemmKernelZVECTOR.h
Abstract:
This module implements the kernels for the single precision matrix/matrix
multiply operation (SGEMM).
--*/
#include "FgemmKernelZVECTOR.h"
template<size_t RowCount>
MLAS_FORCEINLINE
size_t
MlasSgemmProcessCount(
const float* A,
const float* B,
float* C,
size_t CountK,
size_t CountN,
size_t lda,
size_t ldc,
MLAS_FLOAT32X4 AlphaBroadcast,
bool ZeroMode
)
{
do {
const float* a = A;
size_t k = CountK;
MLAS_FLOAT32X4 Accumulators[RowCount][4];
MLAS_FLOAT32X4 AElements[RowCount];
MLAS_FLOAT32X4 ABroadcast[RowCount];
//
// Clear the block accumulators.
//
MlasLoopUnroll<RowCount, MlasFgemmZeroAccumulators>()(Accumulators);
//
// Compute the output block.
//
while (k >= 4) {
MlasLoopUnroll<RowCount, MlasFgemmLoadAElements>()(AElements, a, lda);
MlasLoopUnroll<RowCount, MlasFgemmSplatAElements<0>>()(AElements, ABroadcast);
MlasFgemmComputeBlock<RowCount>(Accumulators, ABroadcast, B);
MlasLoopUnroll<RowCount, MlasFgemmSplatAElements<1>>()(AElements, ABroadcast);
MlasFgemmComputeBlock<RowCount>(Accumulators, ABroadcast, B + 16);
MlasLoopUnroll<RowCount, MlasFgemmSplatAElements<2>>()(AElements, ABroadcast);
MlasFgemmComputeBlock<RowCount>(Accumulators, ABroadcast, B + 32);
MlasLoopUnroll<RowCount, MlasFgemmSplatAElements<3>>()(AElements, ABroadcast);
MlasFgemmComputeBlock<RowCount>(Accumulators, ABroadcast, B + 48);
a += 4;
B += 16 * 4;
k -= 4;
}
while (k > 0) {
MlasLoopUnroll<RowCount, MlasFgemmBroadcastAElements>()(ABroadcast, a, lda);
MlasFgemmComputeBlock<RowCount>(Accumulators, ABroadcast, B);
a += 1;
B += 16;
k -= 1;
}
if (CountN >= 16) {
//
// Store the entire output block.
//
MlasLoopUnroll<RowCount, MlasFgemmStoreVector<4>>()(Accumulators, C, ldc, AlphaBroadcast, ZeroMode);
} else {
//
// Store the partial output block.
//
if (CountN >= 12) {
MlasLoopUnroll<RowCount, MlasFgemmStoreVector<3>>()(Accumulators, C, ldc, AlphaBroadcast, ZeroMode);
} else if (CountN >= 8) {
MlasLoopUnroll<RowCount, MlasFgemmStoreVector<2>>()(Accumulators, C, ldc, AlphaBroadcast, ZeroMode);
} else if (CountN >= 4) {
MlasLoopUnroll<RowCount, MlasFgemmStoreVector<1>>()(Accumulators, C, ldc, AlphaBroadcast, ZeroMode);
}
//
// Store the remaining unaligned columns.
//
C += (CountN & ~3);
CountN &= 3;
if (CountN > 0) {
MlasLoopUnroll<RowCount, MlasFgemmMultiplyAlphaTrailing>()(Accumulators, AlphaBroadcast);
MlasLoopUnroll<RowCount, MlasFgemmStoreScalar<0>>()(Accumulators, C, ldc, ZeroMode);
if (CountN >= 2) {
MlasLoopUnroll<RowCount, MlasFgemmStoreScalar<1>>()(Accumulators, C, ldc, ZeroMode);
}
if (CountN >= 3) {
MlasLoopUnroll<RowCount, MlasFgemmStoreScalar<2>>()(Accumulators, C, ldc, ZeroMode);
}
}
break;
}
C += 16;
CountN -= 16;
} while (CountN > 0);
return RowCount;
}
+4
View File
@@ -32,6 +32,10 @@ if(POLICY CMP0148)
cmake_policy(SET CMP0148 OLD) # CMake 3.27+: use CMake FindPythonInterp and FindPythonLib if available.
endif()
if(POLICY CMP0218)
cmake_policy(SET CMP0218 NEW) # Suppress warning for CMAKE_WARN_DEPRECATED in CMake 4.4.0+
endif()
#
# Configure OpenCV CMake hooks
#
+2 -2
View File
@@ -377,7 +377,7 @@ if(NOT OPENCV_SKIP_LINK_AS_NEEDED)
if(UNIX)
set(_option "-Wl,--as-needed")
set(_saved_CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${_option}") # requires CMake 3.2+ and CMP0056
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${_option}")
ocv_check_compiler_flag(CXX "" HAVE_LINK_AS_NEEDED)
set(CMAKE_EXE_LINKER_FLAGS "${_saved_CMAKE_EXE_LINKER_FLAGS}")
if(HAVE_LINK_AS_NEEDED)
@@ -393,7 +393,7 @@ if(NOT OPENCV_SKIP_LINK_NO_UNDEFINED)
if(UNIX AND (NOT CMAKE_SYSTEM_NAME MATCHES "OpenBSD"))
set(_option "-Wl,--no-undefined")
set(_saved_CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${_option}") # requires CMake 3.2+ and CMP0056
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${_option}")
ocv_check_compiler_flag(CXX "" HAVE_LINK_NO_UNDEFINED)
set(CMAKE_EXE_LINKER_FLAGS "${_saved_CMAKE_EXE_LINKER_FLAGS}")
if(HAVE_LINK_NO_UNDEFINED)
+15
View File
@@ -95,8 +95,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)
-3
View File
@@ -1944,9 +1944,6 @@ macro(ocv_get_smart_file_name output_var fpath)
unset(__subir)
endmacro()
# Needed by install(DIRECTORY ...)
set(compatible_MESSAGE_NEVER MESSAGE_NEVER)
macro(ocv_git_describe var_name path)
if(GIT_FOUND)
execute_process(COMMAND "${GIT_EXECUTABLE}" describe --tags --exact-match --dirty
+1 -1
View File
@@ -2,7 +2,7 @@
#if defined _WIN32 && (defined(_M_ARM) || defined(_M_ARM64) || defined(_M_ARM64EC))
# define _ARM64_DISTINCT_NEON_TYPES
# include <Intrin.h>
# include <intrin.h>
# include <arm_neon.h>
# define CV_NEON 1
#elif defined(__ARM_NEON)
+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()
+26 -46
View File
@@ -54,8 +54,7 @@ SET(OpenCV_VERSION_STATUS "@OPENCV_VERSION_STATUS@")
include(FindPackageHandleStandardArgs)
if(NOT CMAKE_VERSION VERSION_LESS 2.8.8
AND OpenCV_FIND_COMPONENTS # prevent excessive output
if(OpenCV_FIND_COMPONENTS # prevent excessive output
)
# HANDLE_COMPONENTS was introduced in CMake 2.8.8
list(APPEND _OpenCV_FPHSA_ARGS HANDLE_COMPONENTS)
@@ -67,13 +66,6 @@ else()
set(_OpenCV_HANDLE_COMPONENTS_MANUALLY TRUE)
endif()
# Extract directory name from full path of the file currently being processed.
# Note that CMake 2.8.3 introduced CMAKE_CURRENT_LIST_DIR. We reimplement it
# for older versions of CMake to support these as well.
if(CMAKE_VERSION VERSION_LESS "2.8.3")
get_filename_component(CMAKE_CURRENT_LIST_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
endif()
# Extract the directory where *this* file has been installed (determined at cmake run-time)
# Get the absolute path with no ../.. relative marks, to eliminate implicit linker warnings
get_filename_component(OpenCV_CONFIG_PATH "${CMAKE_CURRENT_LIST_DIR}" REALPATH)
@@ -128,19 +120,15 @@ if(NOT TARGET opencv_core)
include(${CMAKE_CURRENT_LIST_DIR}/OpenCVModules${OpenCV_MODULES_SUFFIX}.cmake)
endif()
if(NOT CMAKE_VERSION VERSION_LESS "2.8.11")
# Target property INTERFACE_INCLUDE_DIRECTORIES available since 2.8.11:
# * http://www.cmake.org/cmake/help/v2.8.11/cmake.html#prop_tgt:INTERFACE_INCLUDE_DIRECTORIES
foreach(__component ${OpenCV_LIB_COMPONENTS})
if(TARGET ${__component})
set_target_properties(
${__component}
PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${OpenCV_INCLUDE_DIRS}"
)
endif()
endforeach()
endif()
foreach(__component ${OpenCV_LIB_COMPONENTS})
if(TARGET ${__component})
set_target_properties(
${__component}
PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${OpenCV_INCLUDE_DIRS}"
)
endif()
endforeach()
if(NOT DEFINED OPENCV_MAP_IMPORTED_CONFIG)
@@ -275,31 +263,23 @@ endif()
# ==============================================================
set(OpenCV_LIBRARIES ${OpenCV_LIBS})
# Require C++11 features for OpenCV modules
if(CMAKE_VERSION VERSION_LESS "3.1")
if(NOT OpenCV_FIND_QUIETLY AND NOT OPENCV_HIDE_WARNING_COMPILE_FEATURES)
message(STATUS "OpenCV: CMake version is low (${CMAKE_VERSION}, required 3.1+). Can't enable C++11 features: https://github.com/opencv/opencv/issues/13000")
endif()
else()
set(__target opencv_core)
if(TARGET opencv_world)
set(__target opencv_world)
endif()
set(__compile_features cxx_std_11) # CMake 3.8+
if(DEFINED OPENCV_COMPILE_FEATURES)
set(__compile_features ${OPENCV_COMPILE_FEATURES}) # custom override
elseif(CMAKE_VERSION VERSION_LESS "3.8")
set(__compile_features cxx_auto_type cxx_rvalue_references cxx_lambdas)
endif()
if(__compile_features)
# Simulate exported result of target_compile_features(opencv_core PUBLIC ...)
set_target_properties(${__target} PROPERTIES
INTERFACE_COMPILE_FEATURES "${__compile_features}"
)
endif()
unset(__target)
unset(__compile_features)
# Require C++17 features for OpenCV modules
set(__target opencv_core)
if(TARGET opencv_world)
set(__target opencv_world)
endif()
set(__compile_features cxx_std_17)
if(DEFINED OPENCV_COMPILE_FEATURES)
set(__compile_features ${OPENCV_COMPILE_FEATURES}) # custom override
endif()
if(__compile_features)
# Simulate exported result of target_compile_features(opencv_core PUBLIC ...)
set_target_properties(${__target} PROPERTIES
INTERFACE_COMPILE_FEATURES "${__compile_features}"
)
endif()
unset(__target)
unset(__compile_features)
#
# Some macros for samples
@@ -28,13 +28,6 @@
#
# ===================================================================================
# Extract directory name from full path of the file currently being processed.
# Note that CMake 2.8.3 introduced CMAKE_CURRENT_LIST_DIR. We reimplement it
# for older versions of CMake to support these as well.
if(CMAKE_VERSION VERSION_LESS "2.8.3")
get_filename_component(CMAKE_CURRENT_LIST_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
endif()
if(NOT DEFINED OpenCV_CONFIG_SUBDIR)
set(OpenCV_CONFIG_SUBDIR "/abi-${ANDROID_NDK_ABI_NAME}")
endif()
@@ -34,11 +34,9 @@
# - OpenCV_STATIC
# - OpenCV_CUDA
if(CMAKE_VERSION VERSION_GREATER 2.6)
get_property(OpenCV_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES)
if(NOT ";${OpenCV_LANGUAGES};" MATCHES ";CXX;")
enable_language(CXX)
endif()
get_property(OpenCV_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES)
if(NOT ";${OpenCV_LANGUAGES};" MATCHES ";CXX;")
enable_language(CXX)
endif()
if(NOT DEFINED OpenCV_STATIC)
+1 -1
View File
@@ -309,6 +309,6 @@ if(DOXYGEN_FOUND)
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/doxygen/html
DESTINATION "${OPENCV_DOC_INSTALL_PATH}"
COMPONENT "docs" OPTIONAL
${compatible_MESSAGE_NEVER}
MESSAGE_NEVER
)
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.
@@ -174,4 +174,4 @@ Try it
<iframe src="../../js_morphological_ops_getStructuringElement.html" width="100%"
onload="this.style.height=this.contentDocument.body.scrollHeight +'px';">
</iframe>
\endhtmlonly
\endhtmlonly
+1 -1
View File
@@ -1265,7 +1265,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.
@@ -62,7 +62,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++) {
@@ -83,7 +83,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) {
@@ -96,7 +96,7 @@ you may access it. For sequences you need to go through them to query a specific
@snippet cpp/tutorial_code/core/file_input_output/file_input_output.cpp readNum
@end_toggle
@add_toggle_python
@snippet cpp/tutorial_code/core/file_input_output/file_input_output.cpp readNum
@snippet python/tutorial_code/core/file_input_output/file_input_output.py readNum
@end_toggle
-# **Input/Output of OpenCV Data structures.** Well these behave exactly just as the basic C++
and Python types:
@@ -44,6 +44,9 @@ Supported SIMD/VLA technologies are detailed in @ref core_hal_intrin .
* Load and store
* Mathematical Operations
* Reduce and Mask
* 16-bit float arithmetic (FP16 and BF16)
* Math function intrinsics
* Multi-channel operations
### Register Structures
@@ -76,7 +79,10 @@ There are **two types** of registers:
|-:|:-|
|uint| 8, 16, 32, 64|
|int | 8, 16, 32, 64|
|float | 32, 64|
|float | 16, 32, 64|
|bfloat | 16|
@note The 16-bit floating-point types (`v_float16`, `v_bfloat16`) require their SIMD guards to be enabled - see the [FP16 and BF16 Arithmetic](#fp16-and-bf16-arithmetic) section below.
* **Constant sized registers**: These structures have a fixed bit size and hold a constant number of values. We need to know what SIMD instruction set is supported by the system and select compatible registers. Use these only if exact bit length is necessary.
<br>
@@ -116,7 +122,6 @@ Now that we know how registers work, let us look at the functions used for filli
// Or we can explicitly write down the values.
v_float32x4(1, 2, 3, 4);
<br>
* *Load Function* - We can use the load method and provide the memory address of the data:
float ptr[32] = {1, 2, 3, ..., 32};
@@ -238,6 +243,187 @@ See also: https://github.com/opencv/opencv/issues/27267
It is common to set all values of b to 0. Thus, v_select will give values of "a" or 0 based on the mask.
*/
FP16 and BF16 Arithmetic {#fp16-and-bf16-arithmetic}
------------------------
OpenCV 5.0 introduces universal intrinsic support for 16-bit floating-point types: FP16 (`cv::hfloat`, `CV_16F`) and BF16 (`cv::bfloat`, `CV_16BF`). These are guarded by two preprocessor flags that mirror the existing `CV_SIMD_64F` / `CV_SIMD_SCALABLE_64F` guards for double-precision support.
| Flag | Description |
|---|---|
| `CV_SIMD_16F` | Fixed-width SIMD targets where native FP16 arithmetic is available (e.g. ARMv8.2+ NEON, RISC-V RVV). |
| `CV_SIMD_SCALABLE_16F` | VLA targets (SVE, RVV) where the register width is determined at runtime. |
Code written against these guards is forward-compatible. As x86 and other architectures add native FP16/BF16 arithmetic instructions in future hardware generations, the same source will automatically benefit without modification.
### Basic Usage
To operate on 16-bit floating-point types, utilize the `vx_load_f16`, `v_add`, and `vx_store_f16` APIs.
@code{.cpp}
#if CV_SIMD_16F || CV_SIMD_SCALABLE_16F
// Load FP16 values from memory into a v_float16 register
v_float16 a = vx_load_f16(src1_ptr);
v_float16 b = vx_load_f16(src2_ptr);
// Element-wise addition and fused multiply-add
v_float16 c = v_add(a, b);
v_float16 d = v_fma(a, b, c); // a*b + c
// Store result back to FP16 memory
vx_store_f16(dst_ptr, d);
#endif
@endcode
### Expanding and Packing
To perform intermediate computation in higher precision (FP32), the universal intrinsics provide `v_expand` and `v_pack`. Expanding converts a single 16-bit register into two 32-bit registers, and packing reverses the operation.
@code{.cpp}
#if CV_SIMD_16F || CV_SIMD_SCALABLE_16F
v_float16 val_16 = vx_load_f16(src_ptr);
// Widen to FP32 for mixed-precision computation
v_float32 lo, hi;
v_expand(val_16, lo, hi);
// Intermediate computation on FP32 registers
v_float32 res_lo = v_mul(lo, vx_setall_f32(3.14159f));
v_float32 res_hi = v_mul(hi, vx_setall_f32(3.14159f));
// Pack back to FP16 for storage
v_float16 res_16 = v_pack(res_lo, res_hi);
vx_store_f16(dst_ptr, res_16);
#endif
@endcode
### BF16 Load/Store and Mixed Precision
BF16 shares its exponent range with FP32, making it critical for deep learning inference. Conversion between BF16 and FP32 via expand/pack is highly efficient and executes primarily as a bit-shift. To handle BF16 data, use the specific `v_bfloat16` type alongside `vx_load_bf16` and `vx_store_bf16`.
@code{.cpp}
#if CV_SIMD_16BF || CV_SIMD_SCALABLE_16BF
// Load BF16 values from memory
v_bfloat16 b_val = vx_load_bf16(bf16_ptr);
// Widen to FP32 for high-precision deep learning accumulation
v_float32 b_lo, b_hi;
v_expand(b_val, b_lo, b_hi);
// Computation in FP32
v_float32 res_lo = v_add(b_lo, vx_setall_f32(1.0f));
v_float32 res_hi = v_add(b_hi, vx_setall_f32(1.0f));
// Pack back to BF16 for output
v_bfloat16 b_res = v_pack_b(res_lo, res_hi);
vx_store_bf16(dst_bf16_ptr, b_res);
#endif
@endcode
### Integer Dot Products (Quantization)
To support the optimized DNN module in OpenCV 5.0, universal intrinsics now include fast 8-bit integer dot products. These are essential for writing custom layers for quantized neural networks. Architectures like ARMv8.4+ and AVX-VNNI have dedicated hardware instructions to multiply 8-bit integers and accumulate them into 32-bit integers in a single pass.
@code{.cpp}
v_int8 vec_a = vx_load(int8_src1);
v_int8 vec_b = vx_load(int8_src2);
v_int32 acc = vx_setzero_s32();
// Multiply 8-bit integers and accumulate into 32-bit registers
acc = v_dotprod_int8(vec_a, vec_b, acc);
@endcode
### Build Requirements
FP16 SIMD arithmetic and scalable vectors are not available by default. The required targets must be enabled at build time. OpenCV's runtime dispatcher will select the appropriate kernel based on the actual CPU capabilities at launch.
@code{.sh}
# ARM - enable ARMv8.2+ FP16 extensions
cmake -DCPU_BASELINE=NEON -DCPU_DISPATCH=FP16 ..
# RISC-V - enable RVV vector extension
cmake -DRISCV_RVV_SCALABLE=ON ..
# WebAssembly (WASM) - enable 128-bit SIMD for browser execution
cmake -DCMAKE_TOOLCHAIN_FILE=../platforms/js/build_wasm.sh -DENABLE_WASM_SIMD=ON ..
@endcode
@note On platforms where `CV_SIMD_16F` is not defined, FP16 intrinsics are not compiled in. Any code that calls them must be wrapped in the corresponding `#if` guard.
### Dynamic Dispatch & Lane Counting
When compiling for Vector Length Agnostic (VLA) architectures (guarded by `CV_SIMD_SCALABLE_*`), the size of the SIMD register is unknown at compile time. It is determined dynamically by the hardware at runtime.
Because of this, you cannot hardcode loop increments (e.g., `i += 4` or `i += 8`). You must use OpenCV's lane-counting macros to advance memory pointers safely.
@code{.cpp}
// Correct approach for scalable loops
int step = VTraits<v_float32>::vlanes(); // Dynamic step size
for (int i = 0; i <= length - step; i += step) {
v_float32 v = vx_load(src + i);
// ...
}
@endcode
Math Function Intrinsics
------------------------
Vectorized implementations of common mathematical functions were added in OpenCV 5.0 (and backported to 4.x). They operate element-wise on `v_float32` and `v_float64` registers. Generic implementations cover all platforms, with hardware-specific fast paths mapped where supported by the architecture.
### Supported Operations
| Function | Operation | Notes |
|---|---|---|
| `v_exp(x)` | \f$e^x\f$ | |
| `v_log(x)` | \f$\ln(x)\f$ | Requires \f$x > 0\f$ |
| `v_erf(x)` | Gauss error function | Utilized in GELU activation kernels |
| `v_sincos(x, s, c)` | Sine and cosine | Simultaneous calculation, more efficient than separate calls |
| `v_pow(x, y)` | \f$x^y\f$ | |
| `v_tanh(x)` | Hyperbolic tangent | Utilized in activation kernels |
| `v_atan2(y, x)` | Arctangent of \f$y/x\f$ | Result in radians |
### Basic Usage
@code{.cpp}
v_float32 x = vx_load(src_ptr);
v_float32 e = v_exp(x); // e^x for each lane
v_float32 l = v_log(x); // ln(x) for each lane, x > 0
v_float32 err = v_erf(x); // Gauss error function
v_float32 s, c;
v_sincos(x, s, c); // sine and cosine in one pass
// Sigmoid: 1 / (1 + e^{-x})
v_float32 ones = vx_setall_f32(1.f);
v_float32 neg_x = v_mul(x, vx_setall_f32(-1.f));
v_float32 sig = v_div(ones, v_add(ones, v_exp(neg_x)));
@endcode
@note Math intrinsics do not require guard macros. Generic scalar-loop fallback implementations ensure code compilation across all target platforms.
Multi-Channel Data Operations
-----------------------------
When vectorizing operations on multi-channel data types (e.g., interleaved 3-channel BGR images), standard load functions such as `vx_load` will capture adjacent channel values into the same register. Element-wise arithmetic operations on such a register will yield incorrect scalar results across the respective channels.
To process multi-channel arrays, use the `v_load_deinterleave` and `v_store_interleave` APIs to distribute channel values into independent registers.
### Basic Usage
@code{.cpp}
// 3-channel deinterleave
v_float32 b, g, r;
v_load_deinterleave(src_ptr, b, g, r);
// Apply independent scalar multipliers per channel
v_float32 y = v_mul(b, vx_setall_f32(0.114f));
y = v_fma(g, vx_setall_f32(0.587f), y);
y = v_fma(r, vx_setall_f32(0.299f), y);
// Interleave registers back to target address
v_store_interleave(dst_ptr, y, y, y);
@endcode
## Demonstration
In the following section, we will vectorize a simple convolution function for single channel and compare the results to a scalar implementation.
@note Not all algorithms are improved by manual vectorization. In fact, in certain cases, the compiler may *autovectorize* the code, thus producing faster results for scalar implementations.
@@ -65,25 +65,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).
@@ -97,25 +97,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.
@@ -134,11 +134,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
@@ -128,7 +128,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
@@ -0,0 +1,98 @@
Convex Hull {#tutorial_geometry_convex_hull}
===========
@tableofcontents
| | |
| -: | :- |
| Compatibility | OpenCV >= 5.0 |
Goal
----
In this tutorial you will learn how to:
- Use the OpenCV function @ref cv::convexHull (part of the **geometry** module in OpenCV 5.0)
to find the convex hull of a 2D point set or contour.
- Link the new `opencv_geometry` module in your CMake project.
Theory
------
The convex hull of a set of points is the smallest convex polygon that contains all the
points. To visualize this, imagine a rubber band stretched open to encompass all the given
points; when released, it snaps around the outermost points, taking the shape of the convex
hull.
### The algorithm
OpenCV computes the hull using Sklansky's algorithm, which is highly efficient for 2D points:
- If the points are unsorted, the time complexity is \f$O(N \log N)\f$.
- If the points are already sorted (for example, ordered contour points from
@ref cv::findContours), the complexity drops to \f$O(N)\f$.
@note In OpenCV 5.0 the computational-geometry algorithms were reorganized: functions such as
@ref cv::convexHull moved from `imgproc` to the new **geometry** module. C++ code must now
include `<opencv2/geometry.hpp>`.
Project configuration (CMake)
-----------------------------
Because OpenCV 5.0 moved these functions into a dedicated module, link `opencv_geometry` in
your `CMakeLists.txt` alongside the standard modules:
@code{.cmake}
cmake_minimum_required(VERSION 3.1)
project(ConvexHullDemo)
find_package(OpenCV REQUIRED)
add_executable(ConvexHullDemo convex_hull_demo.cpp)
target_link_libraries(ConvexHullDemo ${OpenCV_LIBS} opencv_geometry)
@endcode
Code
----
@add_toggle_cpp
This tutorial code's is shown lines below. You can also download it from
[here](https://github.com/opencv/opencv/tree/5.x/samples/cpp/tutorial_code/ShapeDescriptors/hull_demo.cpp)
@include samples/cpp/tutorial_code/ShapeDescriptors/hull_demo.cpp
@end_toggle
@add_toggle_java
This tutorial code's is shown lines below. You can also download it from
[here](https://github.com/opencv/opencv/tree/5.x/samples/java/tutorial_code/ShapeDescriptors/hull/HullDemo.java)
@include samples/java/tutorial_code/ShapeDescriptors/hull/HullDemo.java
@end_toggle
@add_toggle_python
This tutorial code's is shown lines below. You can also download it from
[here](https://github.com/opencv/opencv/tree/5.x/samples/python/tutorial_code/ShapeDescriptors/hull/hull_demo.py)
@include samples/python/tutorial_code/ShapeDescriptors/hull/hull_demo.py
@end_toggle
Explanation
-----------
- **Include the headers.** The demo includes `imgproc.hpp` for edge and contour detection
and the new `geometry.hpp` for the convex-hull algorithm.
- **Edge detection and contour extraction.** Inside the trackbar callback the Canny edge
detector finds the raw edges, and @ref cv::findContours retrieves the boundary shapes.
- **Compute the convex hull.** For each contour, @ref cv::convexHull computes its convex
polygon. By default it takes the input point set and outputs the hull coordinates.
- **Draw the results.** Both the original contours and their bounding hulls are drawn with
@ref cv::drawContours on a blank image, each contour/hull pair sharing a random color.
@note `cv::convexHull(points, hull, clockwise, returnPoints)` accepts two useful flags.
`clockwise` orders the output points clockwise when `true` (default `false`). `returnPoints`
returns hull coordinates when `true` (default); set it to `false` to get the **indices** of
the original points instead — required if you later call @ref cv::convexityDefects.
Result
------
After compiling and running with an input image, a window with a trackbar appears. As you
adjust the Canny threshold, the contours are recomputed dynamically and drawn alongside their
convex hulls, each matching pair sharing the same randomized color.
@@ -1,3 +1,5 @@
Computational geometry module {#tutorial_table_of_content_geometry}
==========================================================
- @subpage tutorial_geometry_convex_hull
@@ -14,4 +14,5 @@ Changes overview {#tutorial_transition_overview}
================
This document is intended to software developers who want to migrate their code to OpenCV 5.0.
**TODO**
For the full list of breaking changes and porting notes, see
[Learn more](https://github.com/opencv/opencv/wiki/OpenCV-4-to-5-migration).
@@ -0,0 +1,147 @@
Using OpenCV pre-built binaries in your own projects {#tutorial_using_prebuilt_binaries}
====================================================
| | |
| -: | :- |
| Original authors | Abhishek Gola & Kirti Jindal |
| Compatibility | OpenCV >= 5.0, C++17, Python >= 3.6 |
@tableofcontents
Goal
----
The objective of this tutorial is to show how to configure a local build environment to use
pre-built OpenCV 5.0 binaries that are already present on your device.
By the end of this guide you will know how to reference, link, and use an existing OpenCV
installation from your own C++ or Python application, without rebuilding the library from
source.
Detailed Description
--------------------
When OpenCV is installed via an installer, a system package manager (apt, Homebrew, vcpkg),
or built into a local workspace directory, it exports configuration scripts that let build
tools locate and link it automatically.
Because OpenCV 5.0 modernizes its build requirements, keep two things in mind:
- **C++17 is required.** The library headers use modern language features, so your project
must be compiled with the C++17 standard or higher.
- **Modern CMake targets.** The legacy 1.x C API has been removed. Link via the
`${OpenCV_LIBS}` variable from `find_package(OpenCV)`, or by naming the specific
libraries on the compiler command line.
C++ Project Configuration
-------------------------
You can link against your pre-built binaries with CMake (recommended for cross-platform
stability) or by invoking g++ directly.
### Method 1: Using CMake (recommended)
#### 1. File layout
@code{.unparsed}
my_opencv_project/
├── CMakeLists.txt
└── main.cpp
@endcode
#### 2. CMakeLists.txt
@code{.cmake}
cmake_minimum_required(VERSION 3.22)
project(OpenCV5_Local_Project)
# OpenCV 5.0 requires C++17
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Locate the pre-built OpenCV installation. If CMake cannot find it
# automatically, pass the path explicitly:
# cmake -DOpenCV_DIR=/absolute/path/to/opencv/build/ ..
find_package(OpenCV 5.0 REQUIRED)
message(STATUS "Found OpenCV version: ${OpenCV_VERSION}")
message(STATUS "OpenCV include directories: ${OpenCV_INCLUDE_DIRS}")
add_executable(opencv_test_app main.cpp)
target_link_libraries(opencv_test_app PRIVATE ${OpenCV_LIBS})
@endcode
#### 3. Build and run
@code{.bash}
cd path/to/my_opencv_project
mkdir build && cd build
cmake ..
cmake --build .
./opencv_test_app
@endcode
### Method 2: Direct compilation with g++
On Linux or macOS you can compile with a single command, without generating build files.
#### 1. Locate your paths
- Standard global location: `/usr/local/include/opencv5` and `/usr/local/lib`
- Custom build location: `/path/to/opencv/build/include` and `/path/to/opencv/build/lib`
#### 2. Using pkg-config
@code{.bash}
g++ -std=c++17 main.cpp -o opencv_test_app $(pkg-config --cflags --libs opencv5)
@endcode
#### 3. Explicit paths (custom or local builds)
@code{.bash}
g++ -std=c++17 main.cpp -o opencv_test_app \
-I/usr/local/include/opencv5 \
-L/usr/local/lib \
-lopencv_core -lopencv_imgcodecs -lopencv_highgui -lopencv_imgproc
@endcode
@note In OpenCV 5.0 the legacy `calib3d` module was split into four modules: `geometry`,
`calib`, `stereo`, and `ptcloud`. Link the specific one(s) you need, e.g. `-lopencv_geometry`,
`-lopencv_calib`, `-lopencv_stereo`, or `-lopencv_ptcloud`, instead of `-lopencv_calib3d`.
Python Project Configuration
----------------------------
If your device already has the pre-compiled Python bindings (`cv2`), you can use them directly.
#### 1. Verify the binding
If you are using a local or custom build, ensure your system can locate the generated `cv2`
binary by setting the `PYTHONPATH` environment variable:
@code{.bash}
export PYTHONPATH=/path/to/opencv/build/lib:$PYTHONPATH
@endcode
@note Replace `/path/to/opencv/build/lib` with the actual path to the directory containing your
compiled `cv2` module (usually inside your local `build/lib` or `build/lib/python3` folder).
Once the path is set, run the following command to verify that the module imports cleanly and
prints the correct version:
@code{.bash}
python3 -c "import cv2; print('OpenCV version:', cv2.__version__)"
@endcode
#### 2. Test script
Write a short `app.py` that imports `cv2`, loads an image, and displays it to confirm the bindings work.
Troubleshooting
---------------
- **CMake: "Could not find a package configuration file ..."** &mdash; CMake cannot locate
the install. Pass the path explicitly: `cmake -DOpenCV_DIR=/path/to/opencv/build/ ..`
- **Compiler: "OpenCV 5.0 requires C++17"** &mdash; the toolchain fell back to an older
standard. Add `-std=c++17` to the g++ command, or `set(CMAKE_CXX_STANDARD 17)` before
`find_package` in CMake.
@@ -168,7 +168,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
@@ -199,7 +199,7 @@ set(_SPHINX_WARNINGS "${CMAKE_CURRENT_BINARY_DIR}/sphinx-warnings.log")
set(_OPENCV_JS "${CMAKE_CURRENT_BINARY_DIR}/opencv.js")
if(NOT EXISTS "${_OPENCV_JS}")
message(STATUS "docs_sphinx: downloading opencv.js (one-time)")
file(DOWNLOAD "https://docs.opencv.org/5.x/opencv.js" "${_OPENCV_JS}"
file(DOWNLOAD "https://docs.opencv.org/5.x/js_tutorials/opencv.js" "${_OPENCV_JS}"
SHOW_PROGRESS STATUS _DL_STATUS)
list(GET _DL_STATUS 0 _DL_OK)
if(NOT _DL_OK EQUAL 0)
+87 -1
View File
@@ -67,6 +67,15 @@ html[data-theme="dark"] .bd-article-container li > a:hover {
}
.bd-content a > code, .bd-content code > a { color: inherit; }
/* Re-assert link colour for xref links inside code the `color: inherit`
above would otherwise render them in the plain code colour. */
.bd-content code > a.reference,
.bd-content pre a.reference {
color: var(--pst-color-link, #0969da) !important;
}
.bd-content code > a.reference span,
.bd-content pre a.reference span { color: inherit !important; }
.bd-content a.opencv-enum-link,
.bd-content code.opencv-enum-sig > a,
.bd-content .opencv-enum-clickable a,
@@ -320,6 +329,45 @@ html[data-theme="dark"] table.opencv-meta-table td:last-child { border-right: no
.bd-header button.theme-switch-button:hover,
.bd-header .navbar-icon-links a.nav-link:hover { color: var(--opencv-accent); }
/* Override the theme's col-lg-3 (25%) width on __start, which would wrap the
version switcher below the logo; size to content and keep the row unwrapped. */
.bd-header .navbar-header-items__start { width: auto; }
.bd-header .navbar-header-items__start,
.bd-header .navbar-header-items__end,
.bd-header .navbar-header-items__center,
.bd-header .navbar-nav { flex-wrap: nowrap; }
/* Below 1200px: collapse only the nav links into the drawer; keep search,
theme toggle and GitHub icon in the header (search shrinks to its icon). */
@media (max-width: 1199.98px) {
.bd-header .navbar-header-items { display: flex !important; flex-grow: 1; }
.bd-header .navbar-header-items__center { display: none !important; }
.bd-header .navbar-header-items__end { margin-left: auto; }
.bd-header button.primary-toggle { display: flex !important; }
.bd-sidebar-primary .sidebar-header-items { display: flex !important; }
.bd-sidebar-primary .sidebar-header-items__end { display: none !important; }
.search-button-field > :not(svg) { display: none !important; }
/* Hide the desktop "Collapse Sidebar" toggle; also keeps it out of the
mobile drawer, which reuses this content. */
.bd-sidebar-primary .pst-sidebar-collapse { display: none !important; }
}
/* 960-1200px: header has folded into the hamburger, so extend the theme's own
<960px off-canvas drawer up to the header breakpoint instead of showing a
persistent column (which would mix header links + nav + collapse toggle). */
@media (min-width: 960px) and (max-width: 1199.98px) {
.bd-sidebar-primary {
position: fixed; top: 0; left: 0; z-index: 1055;
height: 100vh; max-height: 100vh; width: 75%; max-width: 350px;
margin-left: -75%; visibility: hidden; border: 0; flex-grow: 0.75;
}
}
/* On small screens drop the wordmark + subtitle, keep just the logo mark. */
@media (max-width: 768px) {
.bd-header .navbar-brand.logo .logo__textwrap { display: none; }
}
/* --- Version switcher dropdown (navbar) -------------------------------- *
* Sits in the `navbar_start` slot right after `navbar-logo`. The custom
* template `_templates/opencv-version-switcher.html` renders a plain
@@ -384,9 +432,14 @@ html[data-theme="dark"] .opencv-version-switcher #opencv-version-select option {
the content and the (often wide) diagrams get more room. Desktop only; the
mobile off-canvas drawer keeps the theme's own sizing. */
@media (min-width: 960px) {
.bd-sidebar-primary { width: 16rem; flex: 0 0 16rem; }
/* flex:0 0 auto (not a rigid 16rem basis) so the theme's collapse rule
(.pst-squeeze{width:4rem}) can still shrink it when collapsed. */
.bd-sidebar-primary { width: 16rem; flex: 0 0 auto; }
}
/* Collapsed: drop the divider so no leftover vertical border remains. */
.bd-sidebar-primary.pst-squeeze { border-right: none; }
.bd-sidebar-primary nav.bd-links { margin-right: 0 !important; }
.bd-sidebar-primary nav.bd-docs-nav p.bd-links__title {
font-size: 0.9rem !important;
@@ -422,6 +475,10 @@ html[data-theme="dark"] .opencv-version-switcher #opencv-version-select option {
}
html[data-theme="dark"] .bd-sidebar-primary nav.bd-links .current > a { color: #539bf5 !important; }
/* Hide the site footer (Sphinx/PyData credit); the prev/next nav
(.prev-next-footer) is a separate element and stays. */
.bd-footer { display: none !important; }
/* --- Code blocks ------------------------------------------------------- */
div.highlight {
position: relative;
@@ -662,6 +719,17 @@ html[data-theme="dark"] section[id$="-documentation"] > section:hover {
border-color: #58a6ff;
box-shadow: none;
}
/* Landing page id "opencv-documentation" matches the module card selectors
above; undo the boxing so the landing sections stay flat. */
#opencv-documentation > section,
html[data-theme="dark"] #opencv-documentation > section {
border: none;
background: none;
box-shadow: none;
margin: 0;
}
#opencv-documentation > section > h2 { margin-bottom: 0.25rem; }
#opencv-documentation > section > ul { margin-top: 0; }
section[id$="-documentation"] > section > h3 {
display: flex;
@@ -1781,3 +1849,21 @@ html[data-theme="dark"] section[id$="-documentation"] code.opencv-param-name {
border: 1px solid #444c56 !important;
color: #adbac7 !important;
}
/* Breathe (namespace pages) renders param names as plain <strong>; box them
to match the code.opencv-param-name chips used on other function pages. */
dl.field-list dd ul.simple > li > p > strong:first-child {
font-family: var(--pst-font-family-monospace, monospace);
font-weight: 400;
background-color: #eff1f3;
border: 1px solid #d0d7de;
border-radius: 4px;
padding: 0.1em 0.4em;
font-size: 0.85em;
color: #24292f;
white-space: nowrap;
}
html[data-theme="dark"] dl.field-list dd ul.simple > li > p > strong:first-child {
background-color: #2d333b;
border: 1px solid #444c56;
color: #adbac7;
}
+80 -8
View File
@@ -12,7 +12,12 @@
// SearchBox lives in search.js; guard so a load failure can't abort this
// script (which would leave the modal trigger unwired).
var searchBox;
try { searchBox = new SearchBox("searchBox", "{{ _search }}", '.html'); } catch (e) {}
try {
searchBox = new SearchBox("searchBox", "{{ _search }}", '.html');
// Short debounce: the top-N renderer below makes each search cheap enough
// for live-feeling results.
if (searchBox) searchBox.keyTimeoutLength = 120;
} catch (e) {}
// Open/close the centered modal. Defined early so the Ctrl+K handler can use
// them, and kept independent of the Doxygen backend so opening always works.
@@ -40,6 +45,22 @@
}, true);
document.addEventListener("DOMContentLoaded", function () {
document.querySelectorAll(".bd-links__title").forEach(function (el) {
if (/section navigation/i.test(el.textContent)) el.textContent = "Navigation Bar";
});
// The theme's hamburger only opens the drawer dialog; add toggle-to-close
// and backdrop-click-to-close.
(function () {
var dlg = document.getElementById("pst-primary-sidebar-modal");
var tog = document.querySelector(".primary-toggle");
if (!dlg || !tog) return;
tog.addEventListener("click", function (e) {
if (dlg.open) { e.preventDefault(); e.stopImmediatePropagation(); dlg.close(); }
}, true); // capture: pre-empt the theme's open-only handler when already open
dlg.addEventListener("click", function (e) { if (e.target === dlg) dlg.close(); });
})();
// The theme renders navbar_end twice (desktop header + mobile drawer), so
// the search component appears twice → duplicate #opencvSearchTrigger /
// #opencvSearchOverlay IDs. Keep ONE overlay; wire EVERY trigger to it
@@ -81,6 +102,54 @@
init_search();
searchBox.OnSelectItem(0); // default to "All" on every page load
// Fast results: render only the top matches from the in-memory index,
// instead of Doxygen building/toggling all ~4000 rows every keystroke.
try { createResults = function () {}; } catch (e) {}
if (typeof searchResults !== "undefined" && searchResults) {
searchResults.Search = function (q) {
q = (q || "").replace(/^ +| +$/g, "").toLowerCase();
var box = document.getElementById("SRResults");
if (!box) return true;
box.innerHTML = "";
var data = (typeof searchData !== "undefined") ? searchData : [];
var rp = searchBox.resultsPath, shown = 0, MAX = 40;
function cls(el, c) { el.setAttribute("class", c); el.setAttribute("className", c); }
for (var i = 0; i < data.length && shown < MAX; i++) {
var el = data[i];
if ((el[1][0] || "").replace(/<[^>]*>/g, "").toLowerCase().indexOf(q) !== 0) continue;
var r = document.createElement("div");
r.id = "SR_" + el[0]; cls(r, "SRResult"); r.style.display = "block";
var e = document.createElement("div"); cls(e, "SREntry");
var a = document.createElement("a"); a.id = "Item" + shown; cls(a, "SRSymbol");
a.innerHTML = el[1][0]; e.appendChild(a);
if (el[1].length === 2) {
a.setAttribute("href", rp + el[1][1][0]);
a.setAttribute("onclick", "searchBox.CloseResultsWindow()");
a.setAttribute("target", el[1][1][1] ? "_parent" : "_blank");
var sp = document.createElement("span"); cls(sp, "SRScope");
sp.innerHTML = el[1][1][2] || ""; e.appendChild(sp);
} else {
a.setAttribute("href", 'javascript:searchResults.Toggle("SR_' + el[0] + '")');
var chn = document.createElement("div"); cls(chn, "SRChildren");
for (var c = 0; c < el[1].length - 1; c++) {
var ca = document.createElement("a"); cls(ca, "SRScope");
ca.setAttribute("href", rp + el[1][c + 1][0]);
ca.setAttribute("onclick", "searchBox.CloseResultsWindow()");
ca.setAttribute("target", el[1][c + 1][1] ? "_parent" : "_blank");
ca.innerHTML = el[1][c + 1][2] || ""; chn.appendChild(ca);
}
e.appendChild(chn);
}
r.appendChild(e); box.appendChild(r); shown++;
}
var nm = document.getElementById("NoMatches"); if (nm) nm.style.display = shown ? "none" : "block";
var ld = document.getElementById("Loading"); if (ld) ld.style.display = "none";
var sg = document.getElementById("Searching"); if (sg) sg.style.display = "none";
searchResults.lastMatchCount = shown;
return true;
};
}
var sphinxRoot = "{{ '../' * (pagename or '').count('/') }}";
// Doxygen result href -> Sphinx page, or "" if that page is not in our build.
function resolveSphinx(href) {
@@ -106,13 +175,16 @@
if (sphinxPath) {
e.preventDefault();
e.stopPropagation();
// Doxygen #autotoc_md anchors don't exist in Sphinx; rebuild from heading text.
var frag = "";
if (/#autotoc_md/.test(href)) {
var slug = (a.textContent || "").trim().toLowerCase()
.replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
if (slug) frag = "#" + slug;
}
// Doxygen anchors (#ga…, #autotoc_md) don't exist in Sphinx, so
// rebuild the fragment slug from the result text: tutorials use the
// full heading; symbols use the name with scope + "(args)" stripped
// -> e.g. "cv::Canny(...)" -> #canny.
var t = (a.textContent || "").trim();
var slug = /#autotoc_md/.test(href)
? t.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "")
: t.replace(/.*::/, "").replace(/\s*\(.*$/, "").toLowerCase()
.replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
var frag = slug ? "#" + slug : "";
window.location.href = sphinxRoot + sphinxPath + frag;
} else if (href.indexOf("doc/doxygen/html/") >= 0) {
e.preventDefault();
+14
View File
@@ -0,0 +1,14 @@
{# Right-sidebar "Resources" box (landing page). Mirrors page-toc.html classes
so it matches "On this page"; `reference external` gets the theme's arrow. #}
<div class="page-toc tocsection onthispage">
<i class="fa-solid fa-link"></i> {{ _('Resources') }}
</div>
<nav class="page-toc" aria-label="{{ _('Resources') }}">
<ul class="visible nav section-nav flex-column">
<li class="toc-h2 nav-item toc-entry"><a class="reference external nav-link" href="https://github.com/opencv/opencv/wiki">OpenCV Wiki</a></li>
<li class="toc-h2 nav-item toc-entry"><a class="reference external nav-link" href="https://github.com/opencv/opencv/wiki/OpenCV-5">What's new in 5.0</a></li>
<li class="toc-h2 nav-item toc-entry"><a class="reference external nav-link" href="https://forum.opencv.org">Forum</a></li>
<li class="toc-h2 nav-item toc-entry"><a class="reference external nav-link" href="https://stackoverflow.com/questions/tagged/opencv">Stack Overflow</a></li>
<li class="toc-h2 nav-item toc-entry"><a class="reference external nav-link" href="https://github.com/opencv/opencv/issues">Issue tracker</a></li>
</ul>
</nav>
+6 -2
View File
@@ -169,9 +169,12 @@ html_theme_options = {
"show_toc_level": 2,
"navigation_with_keys": True,
"show_prev_next": True,
"show_nav_level": 2,
# 1, not 2: the theme force-opens every <details> up to this level and
# modules render at toctree-l1, so 2 would expand all of them; 1 opens only
# the current module's branch. Render depth is unaffected (navigation_depth).
"show_nav_level": 1,
"navigation_depth": 4,
"secondary_sidebar_items": {"**": ["page-toc"], "index": []},
"secondary_sidebar_items": {"**": ["page-toc"], "index": ["page-toc", "resources"]},
"back_to_top_button": True,
"show_version_warning_banner": False,
"icon_links": [{"name": "GitHub",
@@ -200,4 +203,5 @@ if not _in_source_tree(SPHINX_INPUT_ROOT):
def setup(app):
app.connect("source-read", _source_read)
app.connect("build-finished", _inline_coll_graphs_on_finish)
conf_helpers.patches.register_global_sidebar(app)
return {"parallel_read_safe": True, "parallel_write_safe": True}
+117 -12
View File
@@ -109,6 +109,98 @@ for _m in CONTRIB_MODULES:
_IMAGE_INDEX.setdefault(_img.name,
f"contrib_modules/{_rel}")
# Tutorials can reference images that live only under samples/ (+ apps/), which
# Doxygen's IMAGE_PATH covered but the tutorial-only index misses (renders
# broken). Index+stage ONLY sample images a tutorial actually references and
# that a tutorial `images/` dir doesn't already provide, to avoid copying the
# whole samples image set. Staged under sample_pics/ like api_pics above.
_referenced_images: set[str] = set()
for _md in (DOC_ROOT / "tutorials").rglob("*.markdown"):
try:
_txt = _md.read_text(encoding="utf-8", errors="replace")
except OSError:
continue
for _m in re.finditer(r'!\[[^\]]*\]\((?:[^)\s]*?/)?images/([^)\s]+)\)', _txt):
_referenced_images.add(pathlib.Path(_m.group(1)).name)
_missing_images = {n for n in _referenced_images if n not in _IMAGE_INDEX}
if _missing_images:
_sample_pics = SPHINX_INPUT_ROOT / "sample_pics"
_stage_samples = SPHINX_INPUT_ROOT != DOC_ROOT
for _base in (OPENCV_ROOT / "samples", OPENCV_ROOT / "apps"):
if not _base.is_dir() or not _missing_images:
continue
for _img in _base.rglob("*"):
if (_img.name in _missing_images and _img.is_file()
and _img.suffix.lower() in _IMAGE_EXTS):
_IMAGE_INDEX[_img.name] = f"sample_pics/{_img.name}"
_missing_images.discard(_img.name) # first match wins; stop looking
if _stage_samples:
_sample_pics.mkdir(parents=True, exist_ok=True)
_link = _sample_pics / _img.name
if not _link.exists():
try:
_os.symlink(_img, _link)
except (OSError, NotImplementedError):
try:
_shutil.copy2(_img, _link)
except OSError:
pass
if not _missing_images:
break
# Hand-authored dnn topic; content lives in the dnn module's own doc dir.
_DNN_ENGINE_SELECTION_MD = (
OPENCV_ROOT / "modules" / "dnn" / "doc" / "dnn_engine.markdown").read_text(
encoding="utf-8")
def _add_dnn_engine_selection_topic(out_dir: pathlib.Path) -> None:
"""Add the 'DNN Engine Selection' page as a dnn-module topic.
Must run after stub generation (its stale-file sweep) and before the anchor
scan, so the new {#anchor} + @subpage get picked up."""
dnn = out_dir / "dnn.md"
if not dnn.is_file():
return
(out_dir / "dnn_engine_selection.md").write_text(
_DNN_ENGINE_SELECTION_MD, encoding="utf-8")
text = dnn.read_text(encoding="utf-8")
if "api_dnn_engine_selection" in text:
return
new = re.sub(
r"(## Topics\n\n(?:- @subpage [^\n]*\n)+)",
lambda m: m.group(1) + "- @subpage api_dnn_engine_selection\n",
text, count=1)
if new != text:
dnn.write_text(new, encoding="utf-8")
# Hand-authored standalone HAL page; content lives in core's own doc dir.
_HAL_MD = (
OPENCV_ROOT / "modules" / "core" / "doc" / "hal.markdown").read_text(
encoding="utf-8")
def _add_hal_page(out_dir: pathlib.Path) -> None:
"""Write the HAL page and add a 'Learn about HAL' link on the api_root page.
Call after stub generation and before the anchor scan."""
api_root = out_dir / "api_root.markdown"
if not api_root.is_file():
return
(out_dir / "hal.md").write_text(_HAL_MD, encoding="utf-8")
text = api_root.read_text(encoding="utf-8")
if "](hal.md)" in text:
return
text = re.sub(r"(```\{toctree\}\n.*?\n)(```\n)", r"\1hal\n\2",
text, count=1, flags=re.S)
text = text.rstrip() + (
"\n\n## Learn about HAL\n\n"
"OpenCV ships a Hardware Acceleration Layer that lets hardware vendors "
"inject tuned, silicon-specific implementations behind a stable C "
"interface. See [OpenCV Hardware Acceleration Layer (HAL)](hal.md).\n")
api_root.write_text(text, encoding="utf-8")
if API_MODULES:
_api_pics = SPHINX_INPUT_ROOT / "api_pics"
_stage_pics = SPHINX_INPUT_ROOT != DOC_ROOT
@@ -146,6 +238,8 @@ if API_MODULES:
_generate_api_stubs(_main_api, _API_XML_DIR, SPHINX_INPUT_ROOT / "main_modules",
root_anchor="api_root", root_title="Main modules",
extra_groups=_main_orphans)
_add_dnn_engine_selection_topic(SPHINX_INPUT_ROOT / "main_modules")
_add_hal_page(SPHINX_INPUT_ROOT / "main_modules")
_scan_internal(SPHINX_INPUT_ROOT / "main_modules")
if _extra_api or _extra_orphans:
_generate_api_stubs(_extra_api, _API_XML_DIR, SPHINX_INPUT_ROOT / "extra_modules",
@@ -176,6 +270,11 @@ def _write_root_index() -> None:
entries.append((heading, link_text, docname))
add("Introduction", "Introduction", "intro", "intro" in _ANCHOR_TO_DOC)
# Its own landing section rather than a bullet in the intro's Usage list.
_prebuilt = _ANCHOR_TO_DOC.get("tutorial_using_prebuilt_binaries")
add("How to use pre-built OpenCV binaries",
"Using OpenCV pre-built binaries in your own projects",
_prebuilt or "", bool(_prebuilt))
add("OpenCV Tutorials", "OpenCV tutorials", "tutorials/tutorials")
add("Python Tutorials", "OpenCV-Python tutorials",
"py_tutorials/py_tutorials", bool(PY_DOC_MODULES))
@@ -194,27 +293,33 @@ def _write_root_index() -> None:
toctree = "\n".join(
f"{heading} <{docname}>" for heading, _link, docname in entries)
# Body: raw HTML so links resolve correctly relative to index.html.
html_lines = ['<div class="ocv-landing">']
# Markdown H2 headings so each shows in the "On this page" TOC; links stay
# raw HTML to resolve relative to index.html (headings can't sit in a <div>).
body_lines: list[str] = []
for heading, link_text, docname in entries:
if link_text is None:
html_lines.append(
f'<h2><a href="{docname}.html">{heading}</a></h2>')
else:
html_lines.append(f'<h2>{heading}</h2>')
html_lines.append(f'<p><a href="{docname}.html">{link_text}</a></p>')
html_lines.append("</div>")
body = "\n".join(html_lines)
body_lines.append(f"## {heading}\n")
body_lines.append(
f'<ul><li><a href="{docname}.html">{link_text or heading}</a></li></ul>\n')
body = "\n".join(body_lines).rstrip()
# Landing-page intro prose lives in a hand-editable Markdown file next to
# conf.py (docs_sphinx/index_intro.md), not inline here.
_intro_md = pathlib.Path(__file__).resolve().parent.parent / "index_intro.md"
try:
intro = _intro_md.read_text(encoding="utf-8").strip()
except OSError:
intro = ""
text = (
"OpenCV modules\n"
"==============\n\n"
"OpenCV documentation\n"
"====================\n\n"
"```{toctree}\n"
":hidden:\n"
":maxdepth: 1\n"
":titlesonly:\n\n"
f"{toctree}\n"
"```\n\n"
f"{intro}\n\n"
f"{body}\n"
)
try:
+122
View File
@@ -7,6 +7,8 @@
"""Runtime patches for Sphinx C++ domain and breathe; applied at import."""
from __future__ import annotations
import re
def _patch_cpp_xref_resolver():
"""Work around Sphinx 8.1.x parentSymbol assert in _resolve_xref_inner."""
try:
@@ -169,3 +171,123 @@ def _silence_orphan_toctree_warning():
_silence_orphan_toctree_warning()
def _patch_sidebar_section_root():
"""Root the left sidebar at a page's own top-level section.
A page in two toctrees (e.g. a `cuda*` extra module also grouped under main
`cuda`) gets a last-wins parent from `_get_toctree_ancestors`, rooting its
sidebar at the foreign section. Re-pick the parent sharing the longest path
prefix (same section) so the full sibling list shows."""
try:
import pydata_sphinx_theme.toctree as _pt
from sphinx.environment.adapters.toctree import TocTree
except ImportError:
return
def _section_aware_ancestor(app, pagename, startdepth):
ti = app.env.toctree_includes
cand: dict[str, list[str]] = {}
for _p, _children in ti.items():
for _c in _children:
cand.setdefault(_c, []).append(_p)
def _shared(parent: str, child: str) -> int:
a, b, i = parent.split("/"), child.split("/"), 0
while i < len(a) and i < len(b) and a[i] == b[i]:
i += 1
return i
ancestors: list[str] = []
d = pagename
while d not in ancestors:
ps = cand.get(d)
if not ps:
break
ancestors.append(d)
d = max(ps, key=lambda p: _shared(p, d))
try:
out = ancestors[-startdepth]
except IndexError:
out = None
# Childless root => empty sidebar. Fall back to the dead-end ancestor `d`
# when it's a same-section page with children, else the section api_root.
if out is None or not ti.get(out):
_sec = pagename.split("/", 1)[0]
_base = pagename.rsplit("/", 1)[-1]
# Doxygen file/dir-reference pages are orphan utilities, not module
# content: leave None to suppress the sidebar rather than root at api_root.
if re.search(r"_8\w+$", _base) or _base.startswith("dir_"):
out = None
elif d != pagename and ti.get(d) and d.split("/", 1)[0] == _sec:
out = d
elif ti.get(_sec + "/api_root"):
out = _sec + "/api_root"
return out, TocTree(app.env)
_pt._get_ancestor_pagename = _section_aware_ancestor
_patch_sidebar_section_root()
def _patch_sphinx_toctree_ancestors():
"""Fix which branch the collapsed startdepth=0 sidebar auto-expands.
Sphinx picks it via `_get_toctree_ancestors`, whose last-wins parent map
mis-picks for a page in two toctrees (e.g. a `cuda*` extra module also under
main `cuda`), so its section won't expand. Prefer the parent sharing the
longest path prefix (same section)."""
try:
import sphinx.environment.adapters.toctree as _st
except ImportError:
return
def _section_aware(toctree_includes, docname):
cand: dict[str, list[str]] = {}
for _p, _children in toctree_includes.items():
for _c in _children:
cand.setdefault(_c, []).append(_p)
def _shared(parent: str, child: str) -> int:
a, b, i = parent.split("/"), child.split("/"), 0
while i < len(a) and i < len(b) and a[i] == b[i]:
i += 1
return i
ancestors: list[str] = []
d = docname
while d not in ancestors:
ps = cand.get(d)
if not ps:
break
ancestors.append(d)
d = max(ps, key=lambda p: _shared(p, d))
return dict.fromkeys(ancestors).keys()
_st._get_toctree_ancestors = _section_aware
_patch_sphinx_toctree_ancestors()
def register_global_sidebar(app):
"""Make the sidebar list ALL top-level sections (startdepth=0), current one
auto-expanded, instead of only the active section's subtree.
Wrap the theme's `generate_toctree_html` (its sole sidebar nav generator) to
force startdepth=0, connecting after the theme's html-page-context handler so
it keeps its own template and we flip only this arg. This makes
`_patch_sidebar_section_root` a no-op (its lookup only runs when startdepth != 0)."""
def _globalize(app, pagename, templatename, context, doctree):
gen = context.get("generate_toctree_html")
if not callable(gen):
return
def wrapped(kind, startdepth=0, show_nav_level=0, **kwargs):
# collapse=True: expand only the current branch. Without it,
# startdepth=0 renders the whole tree on every page (slow + bloated).
kwargs["collapse"] = True
return gen(kind, startdepth=0, show_nav_level=0, **kwargs)
context["generate_toctree_html"] = wrapped
app.connect("html-page-context", _globalize, priority=900)
+339 -9
View File
@@ -9,7 +9,8 @@ from __future__ import annotations
import pathlib, re
from .state import (_doxy_page_to_local, _DOXY_ANCHOR_TO_MEMBER, DOXYGEN_BASE_URL,
_LOCAL_CLASS_URL, _LOCAL_TYPEDEF_URL, _FILE_URL, _API_XML_DIR, DOC_ROOT)
_LOCAL_CLASS_URL, _LOCAL_TYPEDEF_URL, _FILE_URL, _API_XML_DIR, DOC_ROOT,
_CV_SYMBOL_URL)
def _doxy_parent_page(page: str, api_dir: pathlib.Path) -> str:
@@ -171,12 +172,14 @@ def _copy_js_tryit_files(out_dir: pathlib.Path) -> None:
dst = dest / src.name
if not dst.exists():
shutil.copy2(src, dst)
# opencv.js from CMake (OPENCV_JS_PATH); bundle it alongside the Try-it pages.
# opencv.js from CMake (OPENCV_JS_PATH). Needed both alongside the Try-it
# pages (relative `src="opencv.js"`) AND at the doc-site root, where
# external/js_usage docs link https://docs.opencv.org/<ver>/opencv.js.
opencv_js = os.environ.get("OPENCV_JS_PATH", "")
if opencv_js and pathlib.Path(opencv_js).is_file():
dst = dest / "opencv.js"
if not dst.exists():
shutil.copy2(opencv_js, dst)
for dst in (dest / "opencv.js", dest.parent / "opencv.js"):
if not dst.exists():
shutil.copy2(opencv_js, dst)
# Extra assets referenced by Try-it pages but not in js_assets/.
_opencv_root = DOC_ROOT.parent
for _name, _src in {
@@ -328,6 +331,28 @@ _PYG_CPF_SPAN_RE = re.compile(
)
# C++ free-function linkifier. The `(` lookahead means only call sites match,
# never a like-named local (`log`, `min`, …); it also makes the pass idempotent
# and class-safe (a wrapped/constructor name is followed by `</a>`, not `(`).
_PYG_CPP_PRE_RE = re.compile(
r'(?P<open><div class="highlight-cpp[^"]*"><div class="highlight"><pre>)'
r'(?P<body>.*?)(?P<close></pre>)', re.DOTALL)
_PYG_CALL_SPAN_RE = re.compile(
r'(?P<span><span class="n">)(?P<name>[A-Za-z_][A-Za-z0-9_]*)(?P<end></span>)'
r'(?=<span class="p">\()'
)
# Python free-function calls. Requires a `cv.`/`cv2.` prefix (OpenCV's Python
# API is always module-qualified) so bare builtins / other modules never link.
_PYG_PY_PRE_RE = re.compile(
r'(?P<open><div class="highlight-(?:python|py|pycon|ipython3?|default)[^"]*">'
r'<div class="highlight"><pre>)(?P<body>.*?)(?P<close></pre>)', re.DOTALL)
_PYG_PY_CALL_RE = re.compile(
r'(?P<pre><span class="n">cv2?</span><span class="o">\.</span>)'
r'(?P<span><span class="n">)(?P<name>[A-Za-z_][A-Za-z0-9_]*)(?P<end></span>)'
r'(?=<span class="p">\()'
)
def _linkify_code_blocks(html_dir: pathlib.Path) -> None:
"""Walk every `.html` under `html_dir` and turn known identifier
tokens inside Pygments-rendered `<pre>` blocks into clickable
@@ -335,15 +360,73 @@ def _linkify_code_blocks(html_dir: pathlib.Path) -> None:
don't accidentally repaint inline `<code class="n">` chips in
prose; the rule above already targets only Pygments span classes
that Pygments uses inside its `<pre>` output."""
if not (_LOCAL_CLASS_URL or _LOCAL_TYPEDEF_URL or _FILE_URL):
if not (_LOCAL_CLASS_URL or _LOCAL_TYPEDEF_URL or _FILE_URL or _CV_SYMBOL_URL):
return
if not html_dir.is_dir():
return
import os
# basename -> path relative to the html root. Stored URLs are relative to
# main_modules/, so without re-pointing they 404 when linked from a page at
# a different depth (e.g. a tutorial referencing `core_basic.html#…`).
_skip_dirs = {"_static", "_sources", "_images", "_sphinx_design_static"}
_page_paths: dict[str, str] = {}
for _f in html_dir.rglob("*.html"):
_rel = _f.relative_to(html_dir)
if _rel.parts and _rel.parts[0] in _skip_dirs:
continue
_page_paths.setdefault(_f.name, _rel.as_posix())
def _rel_local(url: str, current_html: pathlib.Path) -> "str | None":
# Relativize a bare local Sphinx page URL to the current page. Returns
# None when the target page wasn't built (e.g. a struct documented
# inline, with no standalone page) so the caller drops the link instead
# of emitting a 404. Non-local URLs (http/already-relative) pass through.
page, sep, frag = url.partition("#")
if page.startswith(("http://", "https://", "../", "/")):
return url
tgt = _page_paths.get(page)
if not tgt:
return None
rel = os.path.relpath(html_dir / tgt, start=current_html.parent)
return rel + (sep + frag if sep else "")
def _resolve(name: str) -> str | None:
return _LOCAL_CLASS_URL.get(name) or _LOCAL_TYPEDEF_URL.get(name)
# Free functions only (classes/typedefs already get a local link via
# `_resolve`). Emits the docs.opencv.org URL; `_localize_doxygen_links`
# (run right after) rewrites it to the local Sphinx page when one exists.
def _resolve_fn(name: str) -> str | None:
if name in _LOCAL_CLASS_URL or name in _LOCAL_TYPEDEF_URL:
return None
return _CV_SYMBOL_URL.get(name)
def _wrap_call(m: "re.Match") -> str:
url = _resolve_fn(m.group("name"))
if not url:
return m.group(0)
return (f'<a class="reference external" href="{url}">'
f'{m.group("span")}{m.group("name")}{m.group("end")}</a>')
def _rewrite_cpp_calls(m: "re.Match") -> str:
return (m.group("open")
+ _PYG_CALL_SPAN_RE.sub(_wrap_call, m.group("body"))
+ m.group("close"))
def _wrap_py_call(m: "re.Match") -> str:
url = _resolve_fn(m.group("name"))
if not url:
return m.group(0)
return (m.group("pre")
+ f'<a class="reference external" href="{url}">'
+ f'{m.group("span")}{m.group("name")}{m.group("end")}</a>')
def _rewrite_py_calls(m: "re.Match") -> str:
return (m.group("open")
+ _PYG_PY_CALL_RE.sub(_wrap_py_call, m.group("body"))
+ m.group("close"))
# `<pre>…</pre>` blocks only — keeps the substitution from touching
# inline `<span class="n">` runs that may appear in other contexts.
_PRE_BLOCK_RE = re.compile(r"<pre>(.*?)</pre>", re.DOTALL)
@@ -361,11 +444,14 @@ def _linkify_code_blocks(html_dir: pathlib.Path) -> None:
except ValueError:
return f"../../../doc/doxygen/html/{file_url}"
def _wrap_span(m: re.Match) -> str:
def _wrap_span(m: re.Match, current_html: pathlib.Path) -> str:
name = m.group("name")
url = _resolve(name)
if not url:
return m.group(0)
url = _rel_local(url, current_html)
if url is None: # target page not built — don't emit a 404 link
return m.group(0)
return (f'<a class="reference internal" href="{url}">'
f'{m.group("prefix")}{name}{m.group("suffix")}</a>')
@@ -408,13 +494,13 @@ def _linkify_code_blocks(html_dir: pathlib.Path) -> None:
k = inner.find("<a ", i)
if k < 0:
seg = inner[i:]
seg = _PYG_IDENT_SPAN_RE.sub(_wrap_span, seg)
seg = _PYG_IDENT_SPAN_RE.sub(lambda mm: _wrap_span(mm, current_html), seg)
seg = _PYG_CPF_SPAN_RE.sub(
lambda mm: _wrap_cpf(mm, current_html), seg)
out.append(seg)
break
seg = inner[i:k]
seg = _PYG_IDENT_SPAN_RE.sub(_wrap_span, seg)
seg = _PYG_IDENT_SPAN_RE.sub(lambda mm: _wrap_span(mm, current_html), seg)
seg = _PYG_CPF_SPAN_RE.sub(
lambda mm: _wrap_cpf(mm, current_html), seg)
out.append(seg)
@@ -430,6 +516,10 @@ def _linkify_code_blocks(html_dir: pathlib.Path) -> None:
continue
new_text = _PRE_BLOCK_RE.sub(
lambda m: _rewrite_pre(m, html), text)
# Function-call passes run after the class/typedef pass so constructors
# are already wrapped and skipped by the `(` lookahead.
new_text = _PYG_CPP_PRE_RE.sub(_rewrite_cpp_calls, new_text)
new_text = _PYG_PY_PRE_RE.sub(_rewrite_py_calls, new_text)
if new_text != text:
try:
html.write_text(new_text, encoding="utf-8")
@@ -501,6 +591,240 @@ def _linkify_inline_code(html_dir: pathlib.Path) -> None:
pass
# Center the active nav entry in the scrollable left sidebar on load so a
# module far down the list isn't below the fold. The theme has no such hook.
_AUTOSCROLL_SNIPPET = (
'<script id="opencv-sidebar-autoscroll">'
"document.addEventListener('DOMContentLoaded',function(){"
"var s=document.querySelector('.bd-sidebar-primary');if(!s)return;"
"var a=s.querySelectorAll('a.current'),t=a[a.length-1];if(!t)return;" # deepest = current page
"var sr=s.getBoundingClientRect(),tr=t.getBoundingClientRect();"
"s.scrollTop+=(tr.top-sr.top)-(s.clientHeight-tr.height)/2;});"
"</script>"
)
def _inject_sidebar_autoscroll(out_dir: pathlib.Path) -> None:
"""Inject the sidebar auto-scroll script before </body>; idempotent."""
for html in out_dir.rglob("*.html"):
text = html.read_text(encoding="utf-8")
if "opencv-sidebar-autoscroll" in text or "</body>" not in text:
continue
html.write_text(
text.replace("</body>", _AUTOSCROLL_SNIPPET + "</body>", 1),
encoding="utf-8")
_TOC_NAV_RE = re.compile(r'id="pst-page-toc-nav".*?</nav>', re.S)
_TOC_HREF_RE = re.compile(r'href="#([^"]+)"')
_ANY_ID_RE = re.compile(r'id="([^"]+)"')
_DETAIL_SEC_RE = re.compile(r'(<section id="detailed-description"[^>]*>)')
def _repair_dangling_toc_anchors(out_dir: pathlib.Path) -> None:
"""Stub a missing #anchor for any secondary-TOC link with no target element.
A dangling anchor makes the theme scroll-spy throw, which aborts init and
kills the collapse-sidebar button on that page. Idempotent."""
for html in out_dir.rglob("*.html"):
text = html.read_text(encoding="utf-8")
nav = _TOC_NAV_RE.search(text)
if not nav:
continue
ids = set(_ANY_ID_RE.findall(text))
missing = [a for a in _TOC_HREF_RE.findall(nav.group(0)) if a not in ids]
if not missing:
continue
stubs = "".join(f'<span id="{a}"></span>' for a in missing)
new = _DETAIL_SEC_RE.sub(lambda m: m.group(1) + stubs, text, count=1)
if new != text:
html.write_text(new, encoding="utf-8")
def _redirect_orphan_duplicates(app, out_dir: pathlib.Path) -> None:
"""An orphaned main_modules/* page that duplicates an in-nav extra_modules
page redirects to that twin, which has proper section nav. Idempotent."""
ti = app.env.toctree_includes
seen, stack = set(), [app.env.config.root_doc]
while stack:
d = stack.pop()
if d in seen:
continue
seen.add(d)
stack.extend(ti.get(d, []))
for doc in set(app.env.all_docs):
if not doc.startswith("main_modules/") or doc in seen:
continue
base = doc.split("/", 1)[1]
if "extra_modules/" + base not in seen:
continue
html = out_dir / (doc + ".html")
if not html.is_file():
continue
text = html.read_text(encoding="utf-8")
if "opencv-dup-redirect" in text:
continue
target = f"../extra_modules/{base}.html"
snippet = (
f'<link rel="canonical" href="{target}">'
f'<script id="opencv-dup-redirect">location.replace("{target}"+location.hash)</script>'
f'<noscript><meta http-equiv="refresh" content="0;url={target}"></noscript>'
)
new = text.replace("<head>", "<head>" + snippet, 1)
if new != text:
html.write_text(new, encoding="utf-8")
# DNN engine-selection topic: symbols → their API anchor (same dir as the page).
_DNN_ENGINE_LINKS = {
"readNet": "dnn.html#readnet",
"readNetFromONNX": "dnn.html#readnetfromonnx",
"ENGINE_NEW": "dnn.html#enginetype",
"ENGINE_CLASSIC": "dnn.html#enginetype",
"ENGINE_AUTO": "dnn.html#enginetype",
"ENGINE_ORT": "dnn.html#enginetype",
"EngineType": "dnn.html#enginetype",
"DNN_BACKEND_CUDA": "dnn.html#backend",
"DNN_BACKEND_OPENVINO": "dnn.html#backend",
"DNN_TARGET_CUDA": "dnn.html#target",
"setPreferableBackend": "classcv_1_1dnn_1_1Net.html#setpreferablebackend",
"setPreferableTarget": "classcv_1_1dnn_1_1Net.html#setpreferabletarget",
}
# Whole inline-code spans (qualified forms) → anchor; bare names reuse the above.
_DNN_ENGINE_INLINE = {
"cv::dnn::readNet()": "dnn.html#readnet",
"net.forward()": "classcv_1_1dnn_1_1Net.html#forward",
"readNet*()": "dnn.html#readnet",
**_DNN_ENGINE_LINKS,
}
_INLINE_CODE_SPAN_RE = re.compile(
r'<code class="docutils literal notranslate">'
r'(?:<span class="pre">)?([^<]+)(?:</span>)?</code>')
_PYG_TOKEN_SPAN_RE = re.compile(r'<span class="(n|nc|nf|nb|nv|na)">(\w+)</span>')
def _linkify_dnn_engine_selection(out_dir: pathlib.Path) -> None:
"""Make DNN engine/backend symbols clickable on the engine-selection topic,
in both inline code and highlighted code blocks. Idempotent."""
page = out_dir / "main_modules" / "dnn_engine_selection.html"
if not page.is_file():
return
text = page.read_text(encoding="utf-8")
if 'href="dnn.html#enginetype"' in text: # already linkified
return
def _inline(m: "re.Match") -> str:
href = _DNN_ENGINE_INLINE.get(m.group(1).strip())
return (f'<code class="docutils literal notranslate">'
f'<a class="reference internal" href="{href}">'
f'<span class="pre">{m.group(1)}</span></a></code>'
) if href else m.group(0)
def _token(m: "re.Match") -> str:
href = _DNN_ENGINE_LINKS.get(m.group(2))
return (f'<a class="reference internal" href="{href}">'
f'<span class="{m.group(1)}">{m.group(2)}</span></a>'
) if href else m.group(0)
text = _PYG_TOKEN_SPAN_RE.sub(_token, _INLINE_CODE_SPAN_RE.sub(_inline, text))
page.write_text(text, encoding="utf-8")
# HAL page: cv::* API symbols -> their module-page anchors (same dir as the page).
_HAL_INLINE = {
"cv::resize": "imgproc_transform.html#resize",
"cv::cvtColor": "imgproc_color_conversions.html#cvtcolor",
"cv::gemm": "core_array.html#gemm",
}
def _linkify_hal_page(out_dir: pathlib.Path) -> None:
"""Make the cv::* API symbols on the HAL page clickable. Idempotent.
HAL-only tokens (cv_hal_*, NOT_IMPLEMENTED, WITH_*, ) have no API page and
are intentionally left plain."""
page = out_dir / "main_modules" / "hal.html"
if not page.is_file():
return
text = page.read_text(encoding="utf-8")
if 'href="imgproc_transform.html#resize"' in text: # already linkified
return
def _inline(m: "re.Match") -> str:
href = _HAL_INLINE.get(m.group(1).strip())
return (f'<code class="docutils literal notranslate">'
f'<a class="reference internal" href="{href}">'
f'<span class="pre">{m.group(1)}</span></a></code>'
) if href else m.group(0)
page.write_text(_INLINE_CODE_SPAN_RE.sub(_inline, text), encoding="utf-8")
# Universal Intrinsics tutorial: link the intrinsics/types in prose code, which
# Sphinx doesn't auto-link. Anchors are read off core_hal_intrin + the symbol
# maps so coverage tracks the docs (undocumented symbols stay plain).
_UI_MM = "../../../main_modules/" # page is 3 dirs deep
def _univ_intrin_link_map(out_dir: pathlib.Path) -> dict:
m = {"cv::hfloat": _UI_MM + "classcv_1_1hfloat.html",
"cv::bfloat": _UI_MM + "classcv_1_1bfloat.html",
"CV_16F": _UI_MM + "core_hal_interface.html#cv-16f",
"CV_16BF": _UI_MM + "core_hal_interface.html#cv-16bf"}
# Intrinsic functions, from the anchors on the rendered group page.
hp = out_dir / "main_modules" / "core_hal_intrin.html"
if hp.is_file():
html = hp.read_text(encoding="utf-8")
for a in set(re.findall(r'id="(cv-v-[a-z0-9-]+)"', html)):
m.setdefault("v_" + a[len("cv-v-"):].replace("-", "_"),
_UI_MM + "core_hal_intrin.html#" + a)
# Register typedefs (v_float32, …) and the FP16/BF16 classes, from the maps.
for sym, url in {**_LOCAL_TYPEDEF_URL, **_LOCAL_CLASS_URL}.items():
if sym.startswith("v_") or sym in ("hfloat", "bfloat"):
m.setdefault(sym, _UI_MM + url)
# Symbols with no local page but in the Doxygen tag (v_exp, v_log, …) link
# to the official docs; those absent here too have no target and stay plain.
for sym, url in _CV_SYMBOL_URL.items():
if sym.startswith(("v_", "vx_")) and sym not in m:
m[sym] = url
return m
def _linkify_univ_intrin(out_dir: pathlib.Path) -> None:
"""Link every documented intrinsic/type on the univ_intrin tutorial (inline
code and highlighted blocks). Idempotent; undocumented symbols stay plain."""
page = out_dir / "tutorials" / "core" / "univ_intrin" / "univ_intrin.html"
if not page.is_file():
return
text = page.read_text(encoding="utf-8")
if _UI_MM + "classcv_1_1hfloat.html" in text: # already linkified
return
links = _univ_intrin_link_map(out_dir)
def _inline(m: "re.Match") -> str:
txt = m.group(1).strip()
href = links.get(txt)
if not href: # e.g. "v_exp(x)" -> v_exp
lead = re.match(r"[A-Za-z_:][\w:]*", txt)
href = links.get(lead.group(0)) if lead else None
return (f'<code class="docutils literal notranslate">'
f'<a class="reference internal" href="{href}">'
f'<span class="pre">{m.group(1)}</span></a></code>'
) if href else m.group(0)
def _token(m: "re.Match") -> str:
href = links.get(m.group(2))
return (f'<a class="reference internal" href="{href}">'
f'<span class="{m.group(1)}">{m.group(2)}</span></a>'
) if href else m.group(0)
text = _INLINE_CODE_SPAN_RE.sub(_inline, text)
# Token-link only outside existing <a>…</a> spans (typedefs/classes are
# already linked by _linkify_code_blocks) so we never nest anchors.
parts = re.split(r"(<a\b[^>]*>.*?</a>)", text, flags=re.DOTALL)
text = "".join(p if i % 2 else _PYG_TOKEN_SPAN_RE.sub(_token, p)
for i, p in enumerate(parts))
page.write_text(text, encoding="utf-8")
def _inline_coll_graphs_on_finish(app, exception):
"""build-finished entry point."""
if exception is not None:
@@ -516,3 +840,9 @@ def _inline_coll_graphs_on_finish(app, exception):
_copy_js_tryit_files(out)
_fix_gapi_images(out)
_generate_search_map(out)
_repair_dangling_toc_anchors(out)
_redirect_orphan_duplicates(app, out)
_linkify_dnn_engine_selection(out)
_linkify_hal_page(out)
_linkify_univ_intrin(out)
_inject_sidebar_autoscroll(out)
+82 -1
View File
@@ -137,6 +137,64 @@ def _module_group_stem(m: str) -> str:
return _mm.group(1) if _mm else m
return m
# -- Header-free API-group doc overrides ------------------------------------
# When a module's headers use `@addtogroup` only (never `@defgroup`), Doxygen
# emits its subgroups un-nested and auto-titled from their id, leaving the module
# landing page empty. These overrides supply the module-group description and
# re-parent + retitle the subgroups at stub time, without editing the headers
# (cf. `_XPHOTO_DOCS` in stubs.py). Keyed by module group stem (== group id ==
# module folder, by convention).
_GROUP_DOC_OVERRIDES: dict = {
"geometry": {
"detailed": (
"Coordinate geometry grouped into one module: 2D shape analysis and "
"fitting, planar subdivision, multi-view 3D vision (camera pose, "
"triangulation, homography) and point-cloud sampling and "
"segmentation. In OpenCV 5 these were consolidated here from the "
"[imgproc](imgproc.md) and [calib](calib.md) modules.\n\n"
"**Migration (OpenCV 5):** these symbols are no longer re-exported "
"by [imgproc](imgproc.md); code that calls e.g. "
"[convexHull](geometry_shape.md) must include "
"[opencv2/geometry.hpp](geometry_8hpp.md) directly."
),
# Direct children to nest under the module page (each pulls in its own
# subgroups recursively). NB: "d_projection" is Doxygen's mangling of
# `@defgroup 3d_projection` (a group id can't start with a digit), and
# "_3d" is never `@defgroup`'d.
"subgroups": ["geometry_shape", "d_projection", "_3d"],
},
"ptcloud": {
"detailed": (
"Point-cloud and mesh processing: reading and writing point clouds "
"and meshes, triangle rasterization, spatial partitioning (octree), "
"and RGB-D / volumetric 3D reconstruction (odometry, TSDF volumes)."
),
# Symbols harvested from the cv namespace (see _GROUP_NS_HARVEST).
},
}
# Title fixes for groups Doxygen auto-titled from their id, applied by group id
# to every node in an overridden module's tree. Groups with a real title are
# left out (e.g. "d_projection" already renders as "3D vision functionality").
_GROUP_TITLE_OVERRIDES: dict = {
"geometry": "Computational Geometry Primitives Module",
"geometry_shape": "Shape analysis and fitting",
"geometry_subdiv2d": "Planar subdivision",
"_3d": "Point-cloud sampling and segmentation",
"ptcloud": "Point Cloud Processing", # header typo'd "Clound"
}
# Group ids re-parented by an override — skipped by orphan-group emission so each
# renders once, nested under its module, not also as a standalone page.
_GROUP_OVERRIDE_SUBGROUPS: set = {
_sub for _ov in _GROUP_DOC_OVERRIDES.values()
for _sub in _ov.get("subgroups", ())
}
# group stem -> include prefix: harvest cv-namespace symbols (orphaned when a
# header opens @addtogroup outside `namespace cv`) back into the module page.
_GROUP_NS_HARVEST: dict = {
"ptcloud": "opencv2/ptcloud",
"geometry": "opencv2/geometry/mst.hpp", # mst.hpp has no @addtogroup
}
# -- Python enum/constant signatures ----------------------------------------
# C++ enumerator FQN -> cv2.* name; env OPENCV_PYTHON_SIGNATURES_FILE
_PY_SIGNATURES: dict = {}
@@ -686,7 +744,7 @@ def _bib_fields(body: str) -> dict[str, str]:
return fields
# LaTeX accent + special-char cleanup
_LATEX_ACCENT_RE = re.compile(r"\\([\"'`^~.])\s*\{?\s*([A-Za-z])\s*\}?")
_LATEX_ACCENT_RE = re.compile(r"\\([\"'`^~.])\s*\{?\s*\\?([A-Za-z])\s*\}?")
_LATEX_ACCENT_MAP = {
('"', 'a'): 'ä', ('"', 'e'): 'ë', ('"', 'i'): 'ï', ('"', 'o'): 'ö',
('"', 'u'): 'ü', ('"', 'A'): 'Ä', ('"', 'O'): 'Ö', ('"', 'U'): 'Ü',
@@ -700,10 +758,26 @@ _LATEX_ACCENT_MAP = {
('~', 'a'): 'ã', ('~', 'n'): 'ñ', ('~', 'o'): 'õ',
('.', 'c'): 'ċ', ('.', 'e'): 'ė',
}
# Letter-command accents: \c c (cedilla), \v s (caron), \u g (breve), \H o
# (double acute), \k a (ogonek), \r u (ring). Form is "\c{c}" or "\c c".
_LATEX_CMD_ACCENT_RE = re.compile(r"\\([cvuHkr])(?:\s+|\{)\s*\\?([A-Za-z])\s*\}?")
_LATEX_CMD_ACCENT_MAP = {
('c', 'c'): 'ç', ('c', 'C'): 'Ç', ('c', 's'): 'ş', ('c', 'S'): 'Ş',
('c', 'g'): 'ģ', ('c', 'e'): 'ȩ',
('v', 'c'): 'č', ('v', 'C'): 'Č', ('v', 's'): 'š', ('v', 'S'): 'Š',
('v', 'z'): 'ž', ('v', 'Z'): 'Ž', ('v', 'r'): 'ř', ('v', 'n'): 'ň',
('v', 'e'): 'ě', ('v', 'd'): 'ď', ('v', 't'): 'ť', ('v', 'g'): 'ǧ',
('u', 'g'): 'ğ', ('u', 'a'): 'ă',
('H', 'o'): 'ő', ('H', 'u'): 'ű', ('H', 'O'): 'Ő', ('H', 'U'): 'Ű',
('k', 'a'): 'ą', ('k', 'e'): 'ę', ('k', 'A'): 'Ą', ('k', 'E'): 'Ę',
('r', 'u'): 'ů', ('r', 'a'): 'å', ('r', 'U'): 'Ů', ('r', 'A'): 'Å',
}
_LATEX_SPECIAL = {
r"\&": "&", r"\%": "%", r"\#": "#", r"\$": "$",
r"\_": "_", r"\{": "{", r"\}": "}",
r"\textendash": "", r"\textemdash": "",
r"\textregistered": "®", r"\texttrademark": "",
r"\textcopyright": "©", r"\copyright": "©",
r"\ldots": "", r"\dots": "",
r"\o": "ø", r"\O": "Ø", r"\ss": "ß",
r"\aa": "å", r"\AA": "Å", r"\ae": "æ", r"\AE": "Æ",
@@ -712,8 +786,13 @@ _LATEX_SPECIAL = {
def _bib_clean(s: str) -> str:
s = re.sub(r"\s+", " ", s or "").strip()
s = re.sub(r"\\url\s*\{([^}]*)\}", r"\1", s) # \url{X} -> X
s = re.sub(r"\\([lL])(?![A-Za-z])", # \l -> ł, \L -> Ł
lambda m: "ł" if m.group(1) == "l" else "Ł", s)
s = _LATEX_ACCENT_RE.sub(
lambda m: _LATEX_ACCENT_MAP.get((m.group(1), m.group(2)), m.group(2)), s)
s = _LATEX_CMD_ACCENT_RE.sub(
lambda m: _LATEX_CMD_ACCENT_MAP.get((m.group(1), m.group(2)), m.group(2)), s)
for k, v in _LATEX_SPECIAL.items():
s = s.replace(k, v)
return s.replace("{", "").replace("}", "").strip()
@@ -991,6 +1070,8 @@ __all__ = [
"DOC_MODULES", "JS_DOC_MODULES", "PY_DOC_MODULES",
"CONTRIB_MODULES", "CONTRIB_ROOT", "SPHINX_INPUT_ROOT", "API_MODULES",
"_API_XML_DIR", "_PATCHED_XML_DIR", "_module_group_stem",
"_GROUP_DOC_OVERRIDES", "_GROUP_OVERRIDE_SUBGROUPS", "_GROUP_TITLE_OVERRIDES",
"_GROUP_NS_HARVEST",
"_PY_SIGNATURES", "_python_enum_name",
"HAVE_SPHINX_DESIGN", "HAVE_BREATHE",
"DOXYGEN_BASE_URL", "_doxygen_url",
+220 -60
View File
@@ -114,6 +114,12 @@ def _enhance_xphoto_member(m: dict, class_name: str = "") -> dict:
# Drives write-if-changed and the stale-file sweep.
_stub_written: set[pathlib.Path] = set()
# "Shell" groups (<=2 classes, no own members) merged with their classes; see
# _write_api_stub. _MERGED_GROUPS -> [(out_path, intro_lines, classes)];
# _MERGED_CLASS_TO_GROUP -> {class_refid: group_docname} for cross-refs/redirect.
_MERGED_GROUPS: list = []
_MERGED_CLASS_TO_GROUP: dict[str, str] = {}
def _stub_write(path: pathlib.Path, content: str) -> None:
"""Write only if changed; mark path live for this run."""
@@ -752,44 +758,14 @@ def _write_namespace_stub(ns: dict, out_dir: pathlib.Path,
items = ns_sections.get(section_title, [])
if not items:
continue
# Drop the redundant summary when the detail loop covers every member
# (all but template specializations); keep Enumerations always.
if section_title != "Enumerations" and all(
"<" not in (m.get("name") or "") for m in items):
continue
lines.append(f"## {section_title}")
lines.append("")
if section_title == "Functions":
lines += ["{.api-reference-table .api-function-table}",
"| Return | Name |", "|---|---|"]
from html import escape as _esc_html_ns
def _ns_func_row(m: dict) -> str:
target = f"#{m['id']}"
qual = (m.get("qualified") or m["name"])
name_text = qual.replace("::", "&#58;&#58;")
name_html = (f'<a class="reference internal" '
f'href="{target}">{name_text}</a>')
params_sig = m.get("params_sig") or []
def _esc(s: str) -> str:
return _esc_html_ns(s).replace("|", "&#124;")
if not params_sig:
inner = f"{name_html}()"
elif len(params_sig) == 1:
t, nm, dv = params_sig[0]
decl = nm + (f" = {dv}" if dv else "")
inner = f"{name_html}({_esc(t)} {_esc(decl)})"
else:
last_i = len(params_sig) - 1
parts = [f"{name_html}("]
for i, (t, nm, dv) in enumerate(params_sig):
tail = " )" if i == last_i else ","
decl = nm + (f" = {dv}" if dv else "")
parts.append(f" {_esc(t)} {_esc(decl)}{tail}")
inner = "<br>".join(parts)
return f'<code class="docutils literal notranslate">{inner}</code>'
for m in items:
ret_md = _type_to_md(m.get("type_elem"))
if not ret_md:
ret_md = _md_escape_cell(m["type"]) or "\u00a0"
if m.get("static"):
ret_md = "static " + ret_md
lines.append(f"| {ret_md} | {_ns_func_row(m)} |")
elif section_title in ("Typedefs", "Variables"):
if section_title in ("Typedefs", "Variables"):
for m in items:
lines.append("```cpp")
if section_title == "Typedefs":
@@ -995,6 +971,23 @@ def _write_api_stub(node: dict, out_dir: pathlib.Path,
_stub_write(out_dir / f"{_cn}.md", _md + "\n")
return
# Classes-only shell group (<=2 classes, no own members/subgroups): defer the
# class content to the seeded pass (_generate_api_stubs), which hosts it here
# and redirects the standalone class pages.
if (1 <= len(node["innerclasses"]) <= 2 and not node["sections"]
and not node["children"]):
_intro = list(lines)
_txt = "\n\n".join(x for x in (
(node.get("brief") or "").strip(),
(node.get("detailed") or "").strip()) if x)
if _txt:
_intro += [_txt, ""]
for c in node["innerclasses"]:
classes_seen.setdefault(c["refid"], c)
_MERGED_CLASS_TO_GROUP[c["refid"]] = f"{out_dir.name}/{name}"
_MERGED_GROUPS.append((out, _intro, node["innerclasses"]))
return
# Brief + "View details" under the title (mirrors the Doxygen group page).
_brief = node.get("brief") or ""
if _brief:
@@ -1073,7 +1066,10 @@ def _write_api_stub(node: dict, out_dir: pathlib.Path,
parent_qualified = q.rsplit("::", 1)[0]
for c in classes_seen.values():
if c.get("qualified") == parent_qualified:
return f"{_class_page_name(c['refid'])}.md"
# Used in a raw-HTML <a href> (see _func_row_split_md); MyST
# only rewrites .md->.html for Markdown []() links, not raw
# HTML, so point at .html directly or the href 404s.
return f"{_class_page_name(c['refid'])}.html"
# Functions on core pages: target the `_func_slug`-based anchor
# that `_render_core_basic_func` actually emits.
if _is_core_page and m.get("kind") == "function" and m.get("name"):
@@ -1214,13 +1210,15 @@ def _write_api_stub(node: dict, out_dir: pathlib.Path,
def _safe(s: str) -> str:
return _html_mod.escape(s).replace("::", "&#58;&#58;")
for m in members:
# Named enums anchor on their `### Name` heading slug. Anonymous
# enums anchor on the detail block's MyST `({id})=` target, whose
# slug normalizes `_`-runs to `-`; a raw `#<id>` with underscores
# does NOT resolve in MyST, so match the normalized form here.
_enum_anchor = (m["name"].lower() if m.get("name")
else re.sub(r"_+", "-", m["id"]))
_more = ""
if _enum_more_link:
# Link to the enum detail block's heading-slug id
# (`### AccessFlag` → `#accessflag`). Same target
# the clickable synopsis tokens use, and a literal
# match on the actual element id on the page.
_more = f"[View details](#{m['name'].lower()})"
_more = f"[View details](#{_enum_anchor})"
if _clickable_synopsis:
_qual = m["qualified"] or m["name"]
_is_strong = bool(m.get("strong"))
@@ -1234,7 +1232,7 @@ def _write_api_stub(node: dict, out_dir: pathlib.Path,
_val_prefix = _qual.rsplit("::", 1)[0] + "::"
else:
_val_prefix = ""
_href = f"#{m['name'].lower()}" # enum detail block id
_href = f"#{_enum_anchor}"
out.append(
'<div class="highlight-cpp notranslate '
'opencv-enum-clickable"><div class="highlight"><pre>'
@@ -1288,7 +1286,15 @@ def _write_api_stub(node: dict, out_dir: pathlib.Path,
return out
_named_groups: list[tuple[str, str, list]] = [] # (header, section_title, members)
for _, section_title in _MEMBERDEF_SECTIONS:
def _grp_detail_covers(m, kind_key):
# Must mirror the detail loop's skips below: a summary member lacks a
# detail block only if it's a class member or a template specialization.
if kind_key in ("function", "variable") and _is_class_member(m):
return False
if _is_template_spec(m):
return False
return True
for kind_key, section_title in _MEMBERDEF_SECTIONS:
items = node["sections"].get(section_title, [])
if not items:
continue
@@ -1296,11 +1302,16 @@ def _write_api_stub(node: dict, out_dir: pathlib.Path,
for _hdr, _members in _group_by_section_header(
[m for m in items if (m.get("section_header") or "")]):
_named_groups.append((_hdr, section_title, _members))
# Drop the redundant summary only when the detail loop covers every
# ungrouped member; keep Enumerations always.
if ungrouped:
lines.append(f"## {section_title}")
lines.append("")
lines += _summary_block(section_title, ungrouped)
lines.append("")
_covered = section_title != "Enumerations" and all(
_grp_detail_covers(m, kind_key) for m in ungrouped)
if not _covered:
lines.append(f"## {section_title}")
lines.append("")
lines += _summary_block(section_title, ungrouped)
lines.append("")
for _hdr, section_title, _members in _named_groups:
lines.append(f"## {_hdr}")
lines.append("")
@@ -1363,7 +1374,14 @@ def _write_api_stub(node: dict, out_dir: pathlib.Path,
"",
]
else:
blk = [f'<h3 id="{m["id"]}">{_keyword}</h3>', ""]
# Anonymous enum has no heading slug; anchor it with a MyST
# `({id})=` target (what `_enum_anchor` links to). A raw
# `<h3 id=…>` is not a MyST reference target and dead-links.
blk = [
f"({m['id']})=",
f"### {_keyword}",
"",
]
if m.get("include_file"):
_einc = m["include_file"]
_ehref = _include_page_href(_einc)
@@ -1768,17 +1786,19 @@ def _render_core_basic_func(m: dict, idx: int, total: int,
def _write_class_stub(cls: dict, out_dir: pathlib.Path,
xml_dir: pathlib.Path) -> None:
xml_dir: pathlib.Path, inline: bool = False):
"""One .md per inner class, mirroring Doxygen's class-page layout.
Falls back to `{doxygenclass}`/`{doxygenstruct}` if class XML can't be read."""
Falls back to `{doxygenclass}`/`{doxygenstruct}` if class XML can't be read.
When `inline=True`, the body lines are returned (no title, not written) so a
single-class "shell" group page can host the class content directly."""
page = _class_page_name(cls["refid"])
out = out_dir / f"{page}.md"
qualified = cls["qualified"] or cls["name"]
kind_label = cls["kind"].title()
title = f"{kind_label} {qualified}"
# No `{#refid}` anchor; `_generate_api_stubs` seeds `_ANCHOR_TO_DOC` instead.
lines = [f"# {title}", ""]
lines = [] if inline else [f"# {title}", ""]
# Class-page header: brief + `View details` + `#include` line.
_header_data = _read_class_data(cls["refid"], xml_dir)
@@ -1786,10 +1806,12 @@ def _write_class_stub(cls: dict, out_dir: pathlib.Path,
import html as _html_pkg
_brief = (_header_data.get("brief") or "").strip()
if _brief:
# Link only when there's a detailed description to jump to.
# Link only when there's a detailed description to jump to; skip when
# inlined, where the #detailed-description anchor collides with a
# sibling class and the detail is right below anyway.
_more = (
' <a class="opencv-class-more" href="#detailed-description">View details</a>'
if _header_data.get("detailed") else ""
if _header_data.get("detailed") and not inline else ""
)
lines.append(
f'<p class="opencv-class-brief">'
@@ -1852,16 +1874,20 @@ def _write_class_stub(cls: dict, out_dir: pathlib.Path,
inline_inherited = []
_additional += [(summary_title, rid, qual, bi)
for rid, qual, bi in inherited]
if not items and not inline_inherited:
non_enum_items = [m for m in items if m["kind"] != "enum"]
enum_items = [m for m in items if m["kind"] == "enum"]
# Own typedef/function/variable get a detail block below, so drop their
# summary rows; keep kinds without one (e.g. friends) and enums.
kept_rows = [m for m in non_enum_items
if m["kind"] not in ("typedef", "function", "variable")]
if not kept_rows and not enum_items and not inline_inherited:
continue
lines.append(f"## {summary_title}")
lines.append("")
non_enum_items = [m for m in items if m["kind"] != "enum"]
enum_items = [m for m in items if m["kind"] == "enum"]
if non_enum_items:
if kept_rows:
lines += ["{.api-reference-table .api-function-table}",
"| Return | Name | Description |", "|---|---|---|"]
for m in non_enum_items:
for m in kept_rows:
ret = _md_escape_cell(m["type"])
if ret and m["static"]:
ret = "static " + ret
@@ -2012,6 +2038,8 @@ def _write_class_stub(cls: dict, out_dir: pathlib.Path,
"",
]
if inline:
return lines
_stub_write(out, "\n".join(lines))
@@ -2347,6 +2375,87 @@ def _fallback_module_tree(name: str):
}
def _apply_group_doc_override(tree: dict, xml_dir: pathlib.Path) -> None:
"""Repair module groups whose subgroups were `@addtogroup`'d but never nested
under a titled `@defgroup`, which Doxygen leaves as orphan top-level groups
with an empty landing page. Injects the group description and attaches the
real subgroups as children (recursively retitled). Driven by
`_GROUP_DOC_OVERRIDES` / `_GROUP_TITLE_OVERRIDES`; no-op without an override."""
ov = _GROUP_DOC_OVERRIDES.get(tree["name"])
if not ov:
return
if ov.get("detailed") and not tree["detailed"]:
tree["detailed"] = ov["detailed"]
have = {c["name"] for c in tree["children"]}
for sub in ov.get("subgroups", ()):
if sub in have:
continue
child = _build_api_hierarchy("group__" + sub.replace("_", "__"), xml_dir)
if child is not None:
tree["children"].append(child)
def _retitle(node: dict) -> None:
node["title"] = _GROUP_TITLE_OVERRIDES.get(node["name"], node["title"])
for c in node.get("children", ()):
_retitle(c)
_retitle(tree)
def _harvest_namespace_into_group(tree: dict, xml_dir: pathlib.Path) -> None:
# Pull funcs/classes under the module's include prefix (per _GROUP_NS_HARVEST)
# into the otherwise-empty group node, from the cv namespace and any group
# they were filed under (e.g. depth.hpp uses @addtogroup rgbd).
import xml.etree.ElementTree as _ET
prefix = _GROUP_NS_HARVEST.get(tree["name"])
if not prefix:
return
tree.setdefault("sections", {})
have = {c["refid"] for c in tree["innerclasses"]}
def _ingest(cd):
for title, members in _parse_member_sections(cd).items():
seen = {m["id"] for m in tree["sections"].get(title, [])}
kept = [m for m in members
if (m.get("include_file") or "").startswith(prefix)
and m["id"] not in seen]
if kept:
tree["sections"].setdefault(title, []).extend(kept)
for ic in cd.findall("innerclass"):
rid = ic.get("refid", "")
if ic.get("prot") != "public" or not rid or rid in have:
continue
cx = xml_dir / f"{rid}.xml"
if not cx.is_file():
continue
try:
ccd = _ET.parse(cx).getroot().find("compounddef")
except _ET.ParseError:
continue
loc = ccd.find("location") if ccd is not None else None
if loc is None or not (loc.get("file") or "").startswith(prefix):
continue
qualified = " ".join((ic.text or "").split())
tree["innerclasses"].append({
"refid": rid, "name": qualified, "qualified": qualified,
"kind": "struct" if rid.startswith("struct") else "class",
"brief": _read_class_brief(rid, xml_dir),
})
have.add(rid)
srcs = [xml_dir / "namespacecv.xml"]
srcs += [g for g in xml_dir.glob("group__*.xml")
if prefix in g.read_text(encoding="utf-8", errors="ignore")]
for s in srcs:
if not s.is_file():
continue
try:
cd = _ET.parse(s).getroot().find("compounddef")
except _ET.ParseError:
continue
if cd is not None:
_ingest(cd)
def _generate_api_stubs(modules, xml_dir, out_dir,
root_anchor="api_root", root_title="API Reference",
root_desc=None, extra_groups=()):
@@ -2394,8 +2503,10 @@ def _generate_api_stubs(modules, xml_dir, out_dir,
if out_dir.exists():
shutil.rmtree(out_dir)
out_dir.mkdir(parents=True, exist_ok=True)
global _stub_written
global _stub_written, _MERGED_GROUPS, _MERGED_CLASS_TO_GROUP
_stub_written = set()
_MERGED_GROUPS = []
_MERGED_CLASS_TO_GROUP = {}
_desc = root_desc or (
"Sphinx-rendered API reference. Each entry below is a module's "
"umbrella `@defgroup`; sub-pages mirror the Doxygen subgroup hierarchy.")
@@ -2414,6 +2525,10 @@ def _generate_api_stubs(modules, xml_dir, out_dir,
_gapi_tree = None # saved to write gapi.md wrapper after all stubs
for m in list(modules) + list(extra_groups):
is_extra = m in extra_groups
if m in _GROUP_OVERRIDE_SUBGROUPS:
# Re-parented under its module by _apply_group_doc_override; skip so
# it isn't also emitted as a standalone orphan page.
continue
stem = _module_group_stem(m)
tree = _build_api_hierarchy("group__" + stem.replace("_", "__"), xml_dir)
if tree is None:
@@ -2422,6 +2537,8 @@ def _generate_api_stubs(modules, xml_dir, out_dir,
tree = _fallback_module_tree(m)
if tree is None:
continue
_apply_group_doc_override(tree, xml_dir)
_harvest_namespace_into_group(tree, xml_dir)
trees.append(tree)
if is_extra:
pass
@@ -2493,8 +2610,51 @@ def _generate_api_stubs(modules, xml_dir, out_dir,
for _cls in classes_seen.values():
_ANCHOR_TO_DOC.setdefault(
_cls["refid"], f"{_doc_prefix}/{_class_page_name(_cls['refid'])}")
# Merged shell groups: a Classes box then each class inline. Done after
# seeding above so bases resolve for "inherited from" links.
for _mout, _mlines, _mclasses in _MERGED_GROUPS:
_lines = list(_mlines)
# Slug matching the per-class `## heading` its box entry links to.
def _cls_slug(_c):
_h = f"{_c['kind'].title()} {_c.get('qualified') or _c['name']}"
return re.sub(r"[^a-z0-9]+", "-", _h.lower()).strip("-")
_lines += ["## Classes", "", "{.api-reference-table}",
"| Name | Description |", "|---|---|"]
for _c in _mclasses:
# Encode "::" so the cv-linkifier doesn't nest an <a> in our link.
_nm = _c["name"].replace("::", "&#58;&#58;")
_lines.append(
f'| <a href="#{_cls_slug(_c)}"><code>{_c["kind"]} '
f'{_nm}</code></a> '
f"| {_md_escape_cell(_c.get('brief', ''))} |")
_lines.append("")
for _c in _mclasses:
_q = _c.get("qualified") or _c["name"]
_lines += [f"## {_c['kind'].title()} {_q}", ""]
_lines += _write_class_stub(_c, out_dir, xml_dir, inline=True) or []
_stub_write(_mout, "\n".join(_lines) + "\n")
# Per-class pages; seed `_ANCHOR_TO_DOC` refid→docname for `@ref`.
for cls in classes_seen.values():
_grp = _MERGED_CLASS_TO_GROUP.get(cls["refid"])
if _grp:
# Inlined into its group page: turn this page into a redirect and
# aim refid xrefs at the group instead.
_rel = _grp.split("/", 1)[-1]
_stub_write(
out_dir / f"{_class_page_name(cls['refid'])}.md",
f"# {cls.get('qualified') or cls['name']}\n\n"
f'<script>window.location.replace("{_rel}.html"'
f' + window.location.hash);</script>\n\n'
f'<p>This page has moved to '
f'<a href="{_rel}.html">{_rel}</a>.</p>\n')
_ANCHOR_TO_DOC[cls["refid"]] = _grp
_ALL_CLASSES[cls["refid"]] = {
"qualified": cls.get("qualified") or cls.get("name", ""),
"kind": cls.get("kind", "class"),
"brief": cls.get("brief", ""),
"docname": _grp,
}
continue
_write_class_stub(cls, out_dir, xml_dir)
_docname = f"{_doc_prefix}/{_class_page_name(cls['refid'])}"
_ANCHOR_TO_DOC[cls["refid"]] = _docname
+400 -37
View File
@@ -15,6 +15,50 @@ def _normalize_lang(lang: str) -> str:
return _LANG_ALIASES.get(lang, lang)
# Snippet-boundary marker lines (e.g. `// [name]`, `## [name]`), stripped
# from rendered code to mirror docs.opencv.org. Matches only a bare
# marker + `[name]` on its own line, so real code embedding a `[token]`
# is left alone.
_SNIPPET_MARKER_RE = re.compile(
r"^[ \t]*(?://!|//|##|#)[ \t]*\[[\w\- ]+\][ \t]*$"
r"|^[ \t]*<!--[ \t]*\[[\w\- ]+\][ \t]*-->[ \t]*$"
)
# Doxygen block comments (`/** … */`, `/*! … */`) are decorative chrome in
# OpenCV samples; strip them to mirror docs.opencv.org. Plain `/* … */`,
# `//`, and `///` comments are kept as visible commentary.
# Line-block pass first removes whole-line blocks (consuming the trailing
# newline so no hollow blank remains); inline pass then sweeps the rare
# mid-line block without eating surrounding code.
_DOXY_LINE_BLOCK_RE = re.compile(
r"^[ \t]*/\*[*!].*?\*/[ \t]*\n?",
re.DOTALL | re.MULTILINE,
)
_DOXY_INLINE_BLOCK_RE = re.compile(
r"/\*[*!].*?\*/",
re.DOTALL,
)
def _strip_snippet_markers(text: str) -> str:
"""Drop snippet-boundary marker lines from a code body (whole line
removed, no hollow blank left). Runs of 3+ resulting blank lines are
collapsed to 2, matching docs.opencv.org's rendering."""
out = [ln for ln in text.split("\n")
if not _SNIPPET_MARKER_RE.match(ln)]
joined = "\n".join(out)
return re.sub(r"\n{3,}", "\n\n", joined)
def _strip_doxygen_block_comments(text: str) -> str:
"""Drop every `/** … */` / `/*! … */` Doxygen block comment anywhere in
a snippet (with or without `@tag` directives). Plain `/* */`, `//`,
and `///` comments stay intact."""
text = _DOXY_LINE_BLOCK_RE.sub("", text)
text = _DOXY_INLINE_BLOCK_RE.sub("", text)
return text
def _read_snippet(rel_path: str, label: str | None) -> tuple[str, str]:
"""Return (code_text, language) for an @include / @snippet directive."""
rel_norm = rel_path.lstrip("/")
@@ -34,7 +78,11 @@ def _read_snippet(rel_path: str, label: str | None) -> tuple[str, str]:
".xml": "xml", ".html": "html",
".sh": "bash", ".bash": "bash"}.get(ext, "text")
if label is None:
return text, lang
# `@include`: whole file. Strip Doxygen block comments and snippet
# markers so the rendered block matches docs.opencv.org (starts at
# the first `#include`/`import`, no banners or doc-headers).
return _strip_snippet_markers(
_strip_doxygen_block_comments(text)), lang
# Match `[label]` after any comment marker.
pat = re.compile(r"^[^\[\n]*(?://!|//|##|#|<!--)[^\[\n]*\[" + re.escape(label)
+ r"\][^\n]*$", re.MULTILINE)
@@ -42,6 +90,10 @@ def _read_snippet(rel_path: str, label: str | None) -> tuple[str, str]:
if len(matches) < 2:
return f"// snippet not found: {rel_path} [{label}]\n", lang
body = text[matches[0].end():matches[1].start()].strip("\n")
# Strip nested markers and mid-snippet Doxygen blocks: samples often
# place a `/** @function bar */` doc-header inside a labelled range.
body = _strip_doxygen_block_comments(body)
body = _strip_snippet_markers(body)
lines = body.split("\n")
indents = [len(l) - len(l.lstrip(" ")) for l in lines if l.strip()]
if indents:
@@ -50,7 +102,172 @@ def _read_snippet(rel_path: str, label: str | None) -> tuple[str, str]:
return "\n".join(lines), lang
# Patterns used by `_dedent_dash_hash_indent` to skip code regions.
_DEDENT_AT_CODE_OPEN_RE = re.compile(r"^[ \t]*@code(?:\{[^}]*\})?\s*$")
_DEDENT_AT_CODE_CLOSE_RE = re.compile(r"^[ \t]*@endcode\s*$")
_DEDENT_FENCE_OPEN_RE = re.compile(r"^[ \t]*(`{3,}|~{3,})")
_DEDENT_FENCE_CLOSE_RE = re.compile(r"^[ \t]*([`~]{3,})[ \t]*$")
# Both ordered-list flavours OpenCV sources use — `-#` (Doxygen) and
# `\d+\.` (CommonMark) — indent bodies +4 spaces/level, so both trigger
# the same dedent.
_DEDENT_DASH_HASH_RE = re.compile(r"^[ \t]*(?:-#|\d+\.)[ \t]")
# Directives that steps 4/5 turn into fenced code; their stray-indent
# handling matches `@code` (see step 0a).
_SNIPPET_DIRECTIVE_RE = re.compile(r"^[ \t]*@(?:snippet|include(?:lineno)?)\b")
def _map_list_indent(n: int) -> int:
"""Map 4-space-per-level list indent to 3-space-per-level, preserving
the 0-3 space remainder within a level. Shared by
`_dedent_dash_hash_indent` and `_fenced` so both land on the same
dedented baseline."""
return (n // 4) * 3 + (n % 4)
def _dedent_keep_blanks(s: str) -> str:
"""Strip the common leading-whitespace prefix from `s`, leaving
whitespace-only lines untouched. Unlike `textwrap.dedent`, it does NOT
normalise interior blank lines to empty preserving them keeps snippet
bodies byte-stable through the re-indent/re-base round-trip."""
widths = [len(l) - len(l.lstrip(" \t")) for l in s.split("\n") if l.strip()]
n = min(widths) if widths else 0
return "\n".join(l[n:] if l.strip() else l for l in s.split("\n"))
def _fenced(base: str, lang: str, body: str) -> str:
"""Emit a fenced code block at `base` indentation.
Inside a list item the fence must sit at the item's content baseline
(relative-0): a column-0 fence terminates the list, and a relative +4
fence renders as an indented code block showing literal ``` backticks.
Only relative-0 keeps the fence recognised AND the list open.
`base=""` => column 0 (top-level, unchanged)."""
body = _dedent_keep_blanks(body).strip("\n")
if base:
body = "\n".join(base + ln if ln.strip() else ln
for ln in body.split("\n"))
return f"\n{base}```{lang}\n{body}\n{base}```\n"
_LIST_MARKER_RE = re.compile(
r"^(?P<ind> *)(?P<mk>-#|\d+\.|[-*+])(?P<gap> +)(?P<rest>.*)$")
def _dedent_dash_hash_indent(src: str) -> str:
"""Re-indent every list (ordered `-#`/`\\d+\\.` and bullet `-`/`*`/`+`)
to a canonical shape so deep nesting stays out of CommonMark's 4-space
indented-code trap.
OpenCV tutorials indent list bodies 4 spaces/level exactly where
CommonMark/MyST reads a line as an indented code block, wrecking fences,
continuation prose/nested bullets, and inline images inside list bodies.
The fix normalises the gap after each marker to one space and nests each
level under its parent's content column, then re-bases body lines to
that column (keeping hanging indent). Normalising marker *width* (not
just scaling indent) keeps fixed-width bullets like `- ` aligned with
the fence beneath them, which the old proportional 4->3 scaling broke.
`@code@endcode` and pre-existing fenced bodies pass through with native
indentation; only their marker lines and ordinary list content are
re-based. Lines outside any list are untouched."""
lines = src.split("\n")
out: list[str] = []
# Stack of open list ancestors: original marker/content columns (to test
# membership) plus the new content column each was re-based to.
stack: list[tuple[int, int, int]] = [] # (orig_col, orig_cont, new_cont)
in_at_code = False
in_fence = False
fence_char = ""
def rebase_content(line: str, code_marker: bool = False) -> str:
# Re-base a non-marker line to its governing marker's new content
# column, keeping hanging indent past it. Mutates `stack`.
ind = len(line) - len(line.lstrip(" "))
while stack and stack[-1][0] >= ind:
stack.pop()
if not stack:
# Outside any list: keep intended indented content as-is, but
# strip stray indent off a code directive/fence marker so a
# 4+-space `@code`/`@snippet` loosely attached to a top-level
# `@note` (uncaptured by the note regex past its blank line)
# doesn't become a root-level indented-code fence.
return line.lstrip(" ") if code_marker else line
_oc, ocont, ncont = stack[-1]
# Clamp code directives/fences to the item's content baseline
# (relative-0): extra source offset would land the fence at +N, and
# at +4 MyST shows literal backticks. Prose keeps its hanging indent.
new = ncont if code_marker else max(0, ncont + (ind - ocont))
return " " * new + line[ind:]
for line in lines:
# @code…@endcode body passes through unchanged; only the marker
# lines are re-based, so `_code_repl` later emits the fence at the
# list-content column (see `_fenced`).
if in_at_code:
if _DEDENT_AT_CODE_CLOSE_RE.match(line):
in_at_code = False
out.append(rebase_content(line, code_marker=True))
else:
out.append(line)
continue
if in_fence:
out.append(line)
cm = _DEDENT_FENCE_CLOSE_RE.match(line)
if cm and cm.group(1)[0] == fence_char:
in_fence = False
continue
if _DEDENT_AT_CODE_OPEN_RE.match(line):
in_at_code = True
out.append(rebase_content(line, code_marker=True))
continue
fm = _DEDENT_FENCE_OPEN_RE.match(line)
if fm:
fence_char = fm.group(1)[0]
in_fence = True
# Leave a pre-existing ``` fence as authored: only its opener is
# seen here (body/closer skipped via `in_fence`), so re-basing
# the opener alone would misalign the block. (`@code`/`@snippet`
# instead go through `_fenced`, re-indenting the whole block.)
out.append(line)
continue
if not line.strip():
out.append(line) # blank: keep, list stays open
continue
mm = _LIST_MARKER_RE.match(line)
if mm:
oc = len(mm.group("ind"))
mk = mm.group("mk")
ocont = oc + len(mk) + len(mm.group("gap"))
# Pop siblings / deeper levels this marker closes.
while stack and stack[-1][0] >= oc:
stack.pop()
# Nested marker sits at its parent's content column; a top-level
# marker is dedented 4->3-per-level so a merely-indented list
# (e.g. ` 1. step`) starts at 0-3 spaces and is recognised,
# not read as an indented code block at >=4 spaces.
new_mk = stack[-1][2] if stack else _map_list_indent(oc)
new_cont = new_mk + len(mk) + 1
out.append(" " * new_mk + mk + " " + mm.group("rest"))
stack.append((oc, ocont, new_cont))
continue
# `@snippet`/`@include` emit fenced code later, so treat them as code
# markers — a stray-indented one must not keep a 4+-space indent that
# would render as a root-level indented fence.
out.append(rebase_content(
line, code_marker=bool(_SNIPPET_DIRECTIVE_RE.match(line))))
return "\n".join(out)
def _emit_toggles(tabs: list[tuple[str, str]]) -> str:
# Re-base tab bodies to column 0: the `tab-item` container is emitted at
# column 0, so a fence arriving baseline-indented (from a snippet inside
# an indented `@add_toggle`) would sit at relative +N and render as
# literal backticks. No-op for the pre-existing column-0 snippet form.
tabs = [(lang, _dedent_keep_blanks(body)) for lang, body in tabs]
if HAVE_SPHINX_DESIGN:
out = ["", "``````{tab-set}"]
for lang, body in tabs:
@@ -115,6 +332,25 @@ def _translate(text: str, docname: str | None = None) -> str:
or docname.startswith("py_tutorials/py_objdetect/")):
text = "---\norphan: true\n---\n\n" + text
# 0a-dedent. Re-indent lists below CommonMark's 4-space indented-code
# threshold — see `_dedent_dash_hash_indent` for the rationale.
text = _dedent_dash_hash_indent(text)
# 0a2. Prose wedged between an `@endcode` and the next `@code` (no list,
# no blank line) is text, but its 4-space source indent makes CommonMark
# render it as a stray code block. Re-align it to the directives' column.
# Anchored on `@endcode … @code`, so inert on raw-indented-code pages.
def _dedent_interblock(m: re.Match) -> str:
base = m.group("ind")
mid = "\n".join((base + l.lstrip(" ")) if l.strip() else l
for l in m.group("mid").split("\n"))
return m.group("pre") + mid
text = re.sub(
r"(?m)^(?P<pre>(?P<ind>[ \t]*)@endcode[ \t]*\n)"
r"(?P<mid>(?:[ \t]+\S[^\n]*\n)+?)"
r"(?=(?P=ind)@code(?:\{[^}]*\})?[ \t]*$)",
_dedent_interblock, text)
# 0b. "-# foo" -> "1. foo".
text = re.sub(r"^(?P<indent>[ \t]*)-#[ \t]+",
lambda m: f"{m.group('indent')}1. ", text, flags=re.MULTILINE)
@@ -198,11 +434,36 @@ def _translate(text: str, docname: str | None = None) -> str:
def _admon_repl(m: re.Match) -> str:
kind = _ADMON_KIND[m.group("dir")]
raw = m.group("body")
# `head` is the matched `@note ` prefix before the body; it ends in a
# newline only in the next-line form, so a non-newline end means the
# body rode on the same line as the directive.
head = m.group(0)[: len(m.group(0)) - len(raw)]
same_line = not head.endswith("\n")
# Directive's leading indent (the list-content baseline after step
# 0a). Re-indent the whole admonition to it so an indented note stays
# inside its list item instead of breaking the list at column 0.
indent = head[: len(head) - len(head.lstrip(" \t"))]
lines = raw.split("\n")
min_ind = min(
(len(l) - len(l.lstrip()) for l in lines if l.strip()), default=0)
body = "\n".join(l[min_ind:] for l in lines).strip()
return f"\n:::{{{kind}}}\n{body}\n:::\n"
# Common body indent to strip. Exclude the same-line first line (which
# has zero leading indent) from the min, else min_ind collapses to 0
# and continuation lines keep their indent, rendering as a spurious
# code block inside the note box.
ind_src = lines[1:] if same_line else lines
cand = [len(l) - len(l.lstrip()) for l in ind_src if l.strip()]
min_ind = min(cand) if cand else 0
def _dedent(l: str) -> str:
# Strip up to min_ind leading-whitespace chars only — never slice
# into a less-indented line's text (e.g. the same-line first line).
i = 0
while i < min_ind and i < len(l) and l[i] in " \t":
i += 1
return l[i:]
body = "\n".join(_dedent(l) for l in lines).strip()
block = f":::{{{kind}}}\n{body}\n:::"
if indent:
block = "\n".join((indent + ln) if ln.strip() else ln
for ln in block.split("\n"))
return f"\n{block}\n"
_ac_stash: dict[str, str] = {}
def _ac_hide(m: re.Match) -> str:
k = f"\x00AC{len(_ac_stash)}\x00"; _ac_stash[k] = m.group(0); return k
@@ -252,11 +513,16 @@ def _translate(text: str, docname: str | None = None) -> str:
_split_adj_math, text, flags=re.MULTILINE)
def _fblock(m: re.Match) -> str:
ind = m.group("indent")
body = m.group("body").strip()
# Re-base the math body to fence baseline `ind` (relative-0 of the
# list item, matching `_fenced`); at the deeper source offset it lands
# at relative +4 and block recognition breaks.
body = _dedent_keep_blanks(m.group("body")).strip("\n").strip()
reindent = lambda b: "\n".join((ind + ln) if ln.strip() else ln
for ln in b.split("\n"))
if "\\\\" in body:
body = re.sub(r"\n\s*\n", "\n", body)
return f"\n{ind}```{{math}}\n{ind}{body}\n{ind}```\n"
return f"\n{ind}$$\n{body}\n{ind}$$\n"
return f"\n{ind}```{{math}}\n{reindent(body)}\n{ind}```\n"
return f"\n{ind}$$\n{reindent(body)}\n{ind}$$\n"
text = re.sub(r"^(?P<indent>[ \t]*)\\f\[(?P<body>.+?)\\f\]",
_fblock, text, flags=re.DOTALL | re.MULTILINE)
text = re.sub(r"\\f\[(.+?)\\f\]",
@@ -272,17 +538,12 @@ def _translate(text: str, docname: str | None = None) -> str:
+ r"\end{matrix}",
text)
# 3. @code{.lang} ... @endcode -> fenced block (indent preserved).
# 3. @code{.lang} ... @endcode -> fenced block at the list-content
# baseline (see `_fenced`). Step 0a already dedented the @code marker
# lines, so the captured `indent` is the right column.
def _code_repl(m: re.Match) -> str:
indent = m.group("indent") or ""
lang = _normalize_lang(m.group("lang") or "")
body = m.group("body")
if indent:
body = _textwrap.dedent(body).strip("\n")
body = "\n".join((indent + line) if line else line
for line in body.split("\n"))
return f"\n{indent}```{lang}\n{body}\n{indent}```\n"
return f"\n```{lang}\n{body.strip()}\n```\n"
return _fenced(m.group("indent") or "", lang, m.group("body"))
text = re.sub(
r"^(?P<indent>[ \t]*)@code(?:\{(?P<lang>[^}]*)\})?\s*\n(?P<body>.*?)\n[ \t]*@endcode",
_code_repl, text, flags=re.DOTALL | re.MULTILINE)
@@ -323,11 +584,12 @@ def _translate(text: str, docname: str | None = None) -> str:
lambda m: f"{m.group('fence')}{_normalize_lang(m.group('lang'))}",
text, flags=re.MULTILINE)
# Backtick fence with per-line indent (other fence forms break in tab-items).
# Backtick fence at the list-content baseline (see `_fenced`). Step 0a
# already dedented the `@include`/`@snippet` line, so `indent` is the
# baseline. A top-level snippet has indent "" (column 0); one inside an
# `@add_toggle` is re-based to column 0 by `_emit_toggles`.
def _emit_codeblock(indent: str, lang: str, body: str) -> str:
body_lines = body.rstrip().splitlines()
body_indented = "\n".join(indent + line for line in body_lines)
return f"\n{indent}```{lang}\n{body_indented}\n{indent}```\n"
return _fenced(indent, lang, body)
# 4. @include path / @includelineno path.
def _include_repl(m: re.Match) -> str:
@@ -395,6 +657,17 @@ def _translate(text: str, docname: str | None = None) -> str:
k = re.match(r"\s*", src[j:])
if not k or not re.match(r"@add_toggle_", src[j + k.end():]):
break
# A repeated language starts a NEW tab-set, not a duplicate
# tab: `cpp/python/cpp` must render as [C++|Python] then [C++].
# Distinct languages (cpp/java/python) still merge into one.
_nxt = re.match(r"@add_toggle_(\w+)", src[j + k.end():])
if _nxt and _nxt.group(1) in {t[0] for t in tabs}:
# Rewind to the toggle's line start so the outer loop's
# `^`-anchored opener re-matches it: the inner
# `@end_toggle\s*\n?` may have eaten the next line's
# leading indent, leaving the toggle stranded mid-line.
j = src.rfind("\n", 0, j + k.end()) + 1
break
j += k.end()
if not tabs:
out.append(src[m.start():m.start() + 1]); i = m.start() + 1; continue
@@ -722,7 +995,12 @@ def _translate(text: str, docname: str | None = None) -> str:
resolved.append((kind, "external", _doxygen_url(lookup),
disp or title, description, prefix, inline))
if not resolved:
return ""
# No item resolved — not really a subpage/toctree list (e.g. a
# bullet whose `@ref` is an inline cross-reference). Leave it
# untouched so its body (baseline-indented code/images) isn't
# swallowed as a bullet description and dropped; step 7 still
# links the bare `@ref`.
return m.group(1)
# toctree gets only @subpage (internal @ref would create cycles).
tt_lines = []
@@ -776,16 +1054,18 @@ def _translate(text: str, docname: str | None = None) -> str:
text = re.sub(r'@ref\s+(?P<name>[\w:-]+)(?:\s+"(?P<disp>[^"]+)")?',
_ref_repl, text)
# 7c. cv.Name -> Markdown link using _CV_SYMBOL_URL; skips code spans.
# 7c. cv.Name -> Markdown link using _CV_SYMBOL_URL. Routed through
# `_apply_outside_code` so fenced code (including nested in tab-sets) and
# inline spans are skipped — a prior brittle-regex approach mis-paired
# fence widths and leaked `[cv.foo](#anchor)` into tab-set code bodies.
if _CV_SYMBOL_URL:
_cv_dot_re = re.compile(
r'(?<!\[)(?<!\()cv\.([A-Za-z][A-Za-z0-9_]*)')
def _cvlink_repl(m: re.Match) -> str:
url = _CV_SYMBOL_URL.get(m.group(1))
return f'[cv.{m.group(1)}]({url})' if url else m.group(0)
_parts = re.split(r'(```.*?```|`[^`\n]+`)', text, flags=re.DOTALL)
text = ''.join(
p if i % 2 else re.sub(
r'(?<!\[)(?<!\()cv\.([A-Za-z][A-Za-z0-9_]*)', _cvlink_repl, p)
for i, p in enumerate(_parts))
text = _apply_outside_code(
text, lambda chunk: _cv_dot_re.sub(_cvlink_repl, chunk))
# 8. @cite KEY -> `[N]` HTML anchor to citelist (N from opencv.bib order).
def _cite_repl(m: re.Match) -> str:
@@ -798,13 +1078,19 @@ def _translate(text: str, docname: str | None = None) -> str:
else:
href = f"{DOXYGEN_BASE_URL}citelist.html#CITEREF_{key}"
return f'<a href="{href}">{label}</a>'
# Keys may contain ':'/'.' segments (Ma:2003:IVI, BT.709, Wulff:CVPR:2015);
# match those without swallowing a trailing sentence period.
text = _apply_outside_code(text, lambda chunk: re.sub(
r"@cite\s+(?P<key>[\w-]+)", _cite_repl, chunk))
r"@cite\s+(?P<key>[\w-]+(?:[.:][\w-]+)*)", _cite_repl, chunk))
if _CITE_NUMBER and docname != "citelist":
# Skip plain-lowercase keys (e.g. "pattern", "eigenfaces") in the bare pass:
# they collide with ordinary words and mis-cite prose. Mixed-case/digit keys
# (Zhang2000, BT2017, LBP) still link bare; explicit @cite always works.
_bare_keys = [k for k in _CITE_NUMBER if not re.fullmatch(r"[a-z]+", k)]
if _bare_keys and docname != "citelist":
_CITE_KEY_RE = re.compile(
r"(?<![\[\w])(?P<key>"
+ "|".join(re.escape(k) for k in _CITE_NUMBER)
+ "|".join(re.escape(k) for k in _bare_keys)
+ r")(?![\w\]])"
)
def _bare_cite_repl(m: re.Match) -> str:
@@ -1043,7 +1329,12 @@ def _translate(text: str, docname: str | None = None) -> str:
_BARE_URL_RE = re.compile(
r"(?<![<\[(\w\"'=])"
# Block a markdown-link URL via a 2-char lookbehind on `](`, instead of
# excluding a bare `(` — so a URL in plain parens, e.g.
# `PyPI (https://pypi.org/...)`, still gets autolinkified while
# markdown-link/autolink/HTML-attribute exclusions stay intact.
r"(?<!\]\()"
r"(?<![<\[\w\"'=])"
r"(?P<url>https?://[^\s<>()`\"']+[^\s<>()`\"'.,;:!?])"
)
# `cv.X`/`cv::X`; lookbehind blocks `frame.cv.X`-style false positives.
@@ -1059,10 +1350,82 @@ _FENCED_BLOCK_RE = re.compile(
_INLINE_CODE_RE = re.compile(r"`+[^`\n]*?`+")
# ATX heading line; exempted from the auto-linkifier.
_ATX_HEADING_RE = re.compile(r"^[ \t]{0,3}#{1,6}[ \t]")
# Per-line fence detector for `_code_regions`. Deliberately allows ANY
# leading whitespace (not CommonMark's ≤3): our pipeline emits fences at
# the 4+-space indent of their originating `@include`/`@snippet` inside an
# `@add_toggle`, and under the strict limit those slipped past, leaking
# `[cv.foo](#anchor)` into code blocks. Worst case of the relaxation is
# MORE text protected from the linkifiers, not less.
_FENCE_LINE_RE = re.compile(
r"^(?P<indent>[ \t]*)(?P<fence>`{3,}|~{3,})(?P<info>.*)$"
)
def _code_regions(src: str) -> list[tuple[int, int]]:
"""Return sorted `[(start, end), ...]` byte ranges spanning every fenced
code block (fence lines included) in `src`.
Handles MyST/Sphinx nested fences where the outer container is wider than
the inner code fence (e.g. `{tab-set}` (6) > `{tab-item}` (5) >
` ```python` (3)). Only *code*-block ranges are returned; a `{directive}`
info string marks a container, whose body the caller still walks so its
nested prose stays linkifiable. Inside a code fence, further fence-shaped
lines are inert per CommonMark §4.5."""
out: list[tuple[int, int]] = []
# Each stack entry: (fence_char, fence_width, is_code, opener_offset).
stack: list[tuple[str, int, bool, int]] = []
pos = 0
for line in src.splitlines(keepends=True):
line_start = pos
next_pos = pos + len(line)
ln = line.rstrip("\n").rstrip("\r")
m = _FENCE_LINE_RE.match(ln)
if m is None:
pos = next_pos
continue
fc = m.group("fence")[0]
fw = len(m.group("fence"))
info = m.group("info").strip()
# Inside a code fence nothing matters except the matching closer.
if stack and stack[-1][2]:
top_char, top_width, _, opener_start = stack[-1]
if fc == top_char and fw >= top_width and not info:
out.append((opener_start, next_pos))
stack.pop()
pos = next_pos
continue
# Outside code: an info-less fence of the same char & ≥ width closes
# the nearest open fence (a container, per the branch above); a fence
# with info opens a new block.
if stack and not info:
top_char, top_width, _, _ = stack[-1]
if fc == top_char and fw >= top_width:
stack.pop()
pos = next_pos
continue
# Opener: `{…}` info → container, anything else (language tag, plain
# text, or empty) → code block per CommonMark.
is_code = (not info) or (not info.startswith("{"))
stack.append((fc, fw, is_code, line_start))
# Shield a breathe directive's opener line: its argument (e.g.
# `{doxygenstruct} cv::MSTEdge`) must reach breathe unlinkified.
if info.startswith("{doxygen"):
out.append((line_start, next_pos))
pos = next_pos
# Unclosed code fences: extend protection to EOF so we don't mangle
# the tail of a pathologically-truncated document.
while stack:
char, width, is_code, opener_start = stack.pop()
if is_code:
out.append((opener_start, len(src)))
out.sort()
return out
def _apply_outside_code(src: str, transform) -> str:
"""Apply `transform` to regions outside fenced/inline code."""
"""Apply `transform` to regions outside fenced and inline code, using
`_code_regions` so nested container > code fences (tab-set > tab-item >
` ```python`) are excluded correctly."""
def _segment(text: str) -> str:
out, last = [], 0
for cm in _INLINE_CODE_RE.finditer(text):
@@ -1072,10 +1435,10 @@ def _apply_outside_code(src: str, transform) -> str:
out.append(transform(text[last:]))
return "".join(out)
out, last = [], 0
for fm in _FENCED_BLOCK_RE.finditer(src):
out.append(_segment(src[last:fm.start()]))
out.append(fm.group(0))
last = fm.end()
for s, e in _code_regions(src):
out.append(_segment(src[last:s]))
out.append(src[s:e])
last = e
out.append(_segment(src[last:]))
return "".join(out)
+3
View File
@@ -0,0 +1,3 @@
OpenCV (Open Source Computer Vision Library) is an open-source computer vision and machine learning software library. It has more than 2,500 optimised algorithms, a comprehensive mix of both classic and state-of-the-art computer vision and machine learning methods. These can be used to detect and recognise faces, identify objects, classify human actions in video, track camera and object motion, extract 3D models, stitch images together to produce high-resolution panoramas, and much more. The library has interfaces for C++, Python, Java, and JavaScript, runs on Windows, Linux, macOS, Android, and iOS, and accelerates work on CPU (SIMD), CUDA, OpenCL, and Vulkan.
OpenCV 5.0 is a major release built on OpenCV 4.x. C++17 is now the minimum required standard, Python 2 support has been dropped (Python 3.6+ is required), and the legacy C API has been fully removed. New data types (CV_16BF, CV_32U, CV_64U, CV_64S, CV_Bool) and proper 0D/1D array support extend the core, while the former calib3d module is split into the geometry, calib, stereo, and ptcloud modules. A next-generation DNN engine now covers over 80% of the ONNX specification (up from under 23%), with ONNX Runtime integration and models hosted on Hugging Face. Performance gains include Universal Intrinsics 2.0 (SSE/AVX/NEON/SVE/RISC-V), Vulkan compute support, image-warping speed-ups of 10% to over 300%, and USAC as the default framework for robust estimation.
+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)
{
+12
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/color_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"
)
#TODO: HAVE_IPP_ICV and HAVE_IPP_IW added as private macro till OpenCV itself is
@@ -42,6 +53,7 @@ target_include_directories(ipphal PRIVATE
"${CMAKE_CURRENT_SOURCE_DIR}/src"
${CMAKE_SOURCE_DIR}/modules/core/include
${CMAKE_SOURCE_DIR}/modules/imgproc/include
${CMAKE_SOURCE_DIR}/modules/geometry/include
${IPP_INCLUDE_DIRS}
)
+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
+173
View File
@@ -8,6 +8,13 @@
#include <opencv2/core/base.hpp>
#include "ipp_utils.hpp"
// 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)
@@ -17,6 +24,21 @@ int ipp_hal_warpAffine(int src_type, const uchar *src_data, size_t src_step, int
// Does not pass tests in 5.x branch
//#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
@@ -30,6 +52,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,
@@ -37,6 +77,139 @@ 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
#define IPP_DISABLE_YUV_RGB 1 // accuracy difference
#define IPP_DISABLE_RGB_YUV 1 // breaks OCL accuracy tests
#define IPP_DISABLE_RGB_HSV 1 // breaks OCL accuracy tests
#define IPP_DISABLE_RGB_LAB 1 // breaks OCL accuracy tests
#define IPP_DISABLE_LAB_RGB 1 // breaks OCL accuracy tests
#define IPP_DISABLE_RGB_XYZ 1 // big accuracy difference
#define IPP_DISABLE_XYZ_RGB 1 // big accuracy difference
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_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
#if !IPP_DISABLE_RGB_YUV
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
#endif // !IPP_DISABLE_RGB_YUV
#if !IPP_DISABLE_YUV_RGB
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
#endif // !IPP_DISABLE_YUV_RGB
#if !IPP_DISABLE_RGB_XYZ
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
#endif // !IPP_DISABLE_RGB_XYZ
#if !IPP_DISABLE_XYZ_RGB
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
#endif // !IPP_DISABLE_XYZ_RGB
#if !IPP_DISABLE_RGB_LAB
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
#endif // !IPP_DISABLE_RGB_LAB
#if !IPP_DISABLE_LAB_RGB
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
#endif // !IPP_DISABLE_LAB_RGB
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
#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
+942
View File
@@ -0,0 +1,942 @@
// 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
#define IPP_DISABLE_CVTCOLOR_GRAY2BGR_8UC3 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 // breaks OCL accuracy tests
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;
}
#if !IPP_DISABLE_RGB_YUV
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_HAL_CHECK_USE_IPP();
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;
}
return CV_HAL_ERROR_NOT_IMPLEMENTED;
}
#endif // !IPP_DISABLE_RGB_YUV
#if !IPP_DISABLE_YUV_RGB
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_HAL_CHECK_USE_IPP();
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;
}
return CV_HAL_ERROR_NOT_IMPLEMENTED;
}
#endif // !IPP_DISABLE_YUV_RGB
#if !IPP_DISABLE_RGB_XYZ
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_HAL_CHECK_USE_IPP();
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;
}
return CV_HAL_ERROR_NOT_IMPLEMENTED;
}
#endif // !IPP_DISABLE_RGB_XYZ
#if !IPP_DISABLE_XYZ_RGB
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_HAL_CHECK_USE_IPP();
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;
}
return CV_HAL_ERROR_NOT_IMPLEMENTED;
}
#endif // !IPP_DISABLE_XYZ_RGB
#if !IPP_DISABLE_RGB_LAB
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_HAL_CHECK_USE_IPP();
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;
}
}
}
return CV_HAL_ERROR_NOT_IMPLEMENTED;
}
#endif // !IPP_DISABLE_RGB_LAB
#if !IPP_DISABLE_LAB_RGB
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_HAL_CHECK_USE_IPP();
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;
}
}
}
return CV_HAL_ERROR_NOT_IMPLEMENTED;
}
#endif // !IPP_DISABLE_LAB_RGB
#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
+149
View File
@@ -0,0 +1,149 @@
// 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 <opencv2/geometry.hpp> // DistanceTypes (DIST_L1/L2/C) live in the geometry module on 5.x
#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
+162
View File
@@ -0,0 +1,162 @@
// 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"
#if IPP_VERSION_X100 >= 810
#include <opencv2/core.hpp>
#include "precomp_ipp.hpp"
#include <cmath>
#include <cfloat>
#if defined(HAVE_IPP_IW) && !DISABLE_IPP_FILTER2D
#include "iw++/iw.hpp"
static inline bool ippiCheckAnchor(int x, int y, int kernelWidth, int kernelHeight)
{
return (x == ((kernelWidth - 1) / 2) && y == ((kernelHeight - 1) / 2));
}
static inline IppiBorderType ippiFilterGetBorderType(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 IppiBorderType ippiGetBorder(::ipp::IwiImage &image, int ocvBorderType, ::ipp::IwiBorderSize &borderSize)
{
int inMemFlags = 0;
IppiBorderType border = ippiFilterGetBorderType(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();
::ipp::IwiSize iwSize(width, height);
::ipp::IwiSize kernelSize(kernel_width, kernel_height);
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::IwiBorderSize iwBorderSize;
::ipp::IwiBorderType iwBorderType;
::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);
iwBorderSize = ::ipp::iwiSizeToBorderSize(kernelSize);
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& ex)
{
CV_UNUSED(ex);
return CV_HAL_ERROR_NOT_IMPLEMENTED;
}
CV_IMPL_ADD(CV_IMPL_IPP);
return CV_HAL_ERROR_OK;
}
#endif // defined(HAVE_IPP_IW) && !DISABLE_IPP_FILTER2D
#endif // IPP_VERSION_X100 >= 810
+145
View File
@@ -0,0 +1,145 @@
// 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 only. TM_CCORR is already the final result; the caller
// (cv::matchTemplate) runs common_matchTemplate() to finish TM_SQDIFF_NORMED /
// TM_CCOEFF / TM_CCOEFF_NORMED.
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;
}
+14
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);
+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;
+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:
+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));
+215 -8
View File
@@ -13,12 +13,22 @@ namespace cv { namespace rvv_hal { namespace imgproc {
namespace {
// Exact (a + b/2) / b for b in {9, 25, 49} via multiply-high and shift
// (Granlund-Montgomery, m = ceil(2^18 / b)); exact while a + b/2 < 32768,
// the maximum here is 49*255 + 24 = 12519
template<typename VecType>
static inline VecType vdiv_box_u16(VecType a, ushort b, size_t vl)
{
const ushort m = b == 9 ? 29128 : b == 25 ? 10486 : 5350;
return __riscv_vsrl(__riscv_vmulhu(__riscv_vadd(a, b / 2, vl), m, vl), 2, vl);
}
template<typename T> struct rvv;
template<> struct rvv<uchar>
{
static inline vuint16m8_t vcvt0(vuint8m4_t a, size_t b) { return __riscv_vzext_vf2(a, b); }
static inline vuint8m4_t vcvt1(vuint16m8_t a, size_t b) { return __riscv_vnclipu(a, 0, __RISCV_VXRM_RNU, b); }
static inline vuint16m8_t vdiv(vuint16m8_t a, ushort b, size_t c) { return __riscv_vdivu(__riscv_vadd(a, b / 2, c), b, c); }
static inline vuint16m8_t vdiv(vuint16m8_t a, ushort b, size_t c) { return vdiv_box_u16(a, b, c); }
};
template<> struct rvv<short>
{
@@ -101,13 +111,20 @@ static inline int boxFilterC1(int start, int end, const uchar* src_data, size_t
sum = helperWT::vadd(sum, src, vl);
src = helperWT::vslide1down(src, extra[1], vl);
sum = helperWT::vadd(sum, src, vl);
if (ksize == 5)
if (ksize >= 5)
{
src = helperWT::vslide1down(src, extra[2], vl);
sum = helperWT::vadd(sum, src, vl);
src = helperWT::vslide1down(src, extra[3], vl);
sum = helperWT::vadd(sum, src, vl);
}
if (ksize == 7)
{
src = helperWT::vslide1down(src, extra[4], vl);
sum = helperWT::vadd(sum, src, vl);
src = helperWT::vslide1down(src, extra[5], vl);
sum = helperWT::vadd(sum, src, vl);
}
helperWT::vstore(res.data() + p2idx(i, j), sum, vl);
}
}
@@ -119,12 +136,17 @@ static inline int boxFilterC1(int start, int end, const uchar* src_data, size_t
const WT* row0 = accessX(cur ) == noval ? nullptr : res.data() + p2idx(accessX(cur ), 0);
const WT* row1 = accessX(cur + 1) == noval ? nullptr : res.data() + p2idx(accessX(cur + 1), 0);
const WT* row2 = accessX(cur + 2) == noval ? nullptr : res.data() + p2idx(accessX(cur + 2), 0);
const WT* row3 = nullptr, *row4 = nullptr;
if (ksize == 5)
const WT* row3 = nullptr, *row4 = nullptr, *row5 = nullptr, *row6 = nullptr;
if (ksize >= 5)
{
row3 = accessX(cur + 3) == noval ? nullptr : res.data() + p2idx(accessX(cur + 3), 0);
row4 = accessX(cur + 4) == noval ? nullptr : res.data() + p2idx(accessX(cur + 4), 0);
}
if (ksize == 7)
{
row5 = accessX(cur + 5) == noval ? nullptr : res.data() + p2idx(accessX(cur + 5), 0);
row6 = accessX(cur + 6) == noval ? nullptr : res.data() + p2idx(accessX(cur + 6), 0);
}
int vl;
for (int j = 0; j < width; j += vl)
@@ -135,6 +157,8 @@ static inline int boxFilterC1(int start, int end, const uchar* src_data, size_t
if (row2) sum = helperWT::vadd(sum, helperWT::vload(row2 + j, vl), vl);
if (row3) sum = helperWT::vadd(sum, helperWT::vload(row3 + j, vl), vl);
if (row4) sum = helperWT::vadd(sum, helperWT::vload(row4 + j, vl), vl);
if (row5) sum = helperWT::vadd(sum, helperWT::vload(row5 + j, vl), vl);
if (row6) sum = helperWT::vadd(sum, helperWT::vload(row6 + j, vl), vl);
if (normalize) sum = rvv<T>::vdiv(sum, ksize * ksize, vl);
if (cast)
@@ -226,7 +250,7 @@ static inline int boxFilterC3(int start, int end, const uchar* src_data, size_t
sum0 = __riscv_vfadd(sum0, src0, vl);
sum1 = __riscv_vfadd(sum1, src1, vl);
sum2 = __riscv_vfadd(sum2, src2, vl);
if (ksize == 5)
if (ksize >= 5)
{
src0 = __riscv_vfslide1down(src0, extra[6], vl);
src1 = __riscv_vfslide1down(src1, extra[7], vl);
@@ -241,6 +265,21 @@ static inline int boxFilterC3(int start, int end, const uchar* src_data, size_t
sum1 = __riscv_vfadd(sum1, src1, vl);
sum2 = __riscv_vfadd(sum2, src2, vl);
}
if (ksize == 7)
{
src0 = __riscv_vfslide1down(src0, extra[12], vl);
src1 = __riscv_vfslide1down(src1, extra[13], vl);
src2 = __riscv_vfslide1down(src2, extra[14], vl);
sum0 = __riscv_vfadd(sum0, src0, vl);
sum1 = __riscv_vfadd(sum1, src1, vl);
sum2 = __riscv_vfadd(sum2, src2, vl);
src0 = __riscv_vfslide1down(src0, extra[15], vl);
src1 = __riscv_vfslide1down(src1, extra[16], vl);
src2 = __riscv_vfslide1down(src2, extra[17], vl);
sum0 = __riscv_vfadd(sum0, src0, vl);
sum1 = __riscv_vfadd(sum1, src1, vl);
sum2 = __riscv_vfadd(sum2, src2, vl);
}
vfloat32m2x3_t dst{};
dst = __riscv_vset_v_f32m2_f32m2x3(dst, 0, sum0);
@@ -257,12 +296,17 @@ static inline int boxFilterC3(int start, int end, const uchar* src_data, size_t
const float* row0 = accessX(cur ) == noval ? nullptr : res.data() + p2idx(accessX(cur ), 0);
const float* row1 = accessX(cur + 1) == noval ? nullptr : res.data() + p2idx(accessX(cur + 1), 0);
const float* row2 = accessX(cur + 2) == noval ? nullptr : res.data() + p2idx(accessX(cur + 2), 0);
const float* row3 = nullptr, *row4 = nullptr;
if (ksize == 5)
const float* row3 = nullptr, *row4 = nullptr, *row5 = nullptr, *row6 = nullptr;
if (ksize >= 5)
{
row3 = accessX(cur + 3) == noval ? nullptr : res.data() + p2idx(accessX(cur + 3), 0);
row4 = accessX(cur + 4) == noval ? nullptr : res.data() + p2idx(accessX(cur + 4), 0);
}
if (ksize == 7)
{
row5 = accessX(cur + 5) == noval ? nullptr : res.data() + p2idx(accessX(cur + 5), 0);
row6 = accessX(cur + 6) == noval ? nullptr : res.data() + p2idx(accessX(cur + 6), 0);
}
int vl;
for (int j = 0; j < width; j += vl)
@@ -282,6 +326,8 @@ static inline int boxFilterC3(int start, int end, const uchar* src_data, size_t
loadres(row2);
loadres(row3);
loadres(row4);
loadres(row5);
loadres(row6);
if (normalize)
{
sum0 = __riscv_vfdiv(sum0, ksize * ksize, vl);
@@ -301,12 +347,139 @@ static inline int boxFilterC3(int start, int end, const uchar* src_data, size_t
return CV_HAL_ERROR_OK;
}
// Channel-blind kernel for interleaved 8U data: a row is treated as width*cn
// flat u8 elements, whose horizontal taps sit at strides of cn elements
// (flat[i] sums flat[i], flat[i+cn], ..., flat[i+(ksize-1)*cn], which are
// exactly element i's own-channel taps). This removes all segment loads and
// slide chains: the horizontal pass is ksize independent unaligned loads with
// widening adds, the vertical pass is a single contiguous u16 stream, and the
// output is a plain store. u16 column sums are exact for ksize <= 7
// (49*255 = 12495 < 65535).
template<int ksize, int cn>
static inline int boxFilterFlat8U(int start, int end, const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, int width, int full_width, int full_height, int offset_x, int offset_y, int anchor_x, int anchor_y, bool normalize, int border_type)
{
constexpr int noval = std::numeric_limits<int>::max();
auto accessX = [&](int x) {
int pi = common::borderInterpolate(offset_y + x - anchor_y, full_height, border_type);
return pi < 0 ? noval : pi - offset_y;
};
auto accessY = [&](int y) {
int pj = common::borderInterpolate(offset_x + y - anchor_x, full_width, border_type);
return pj < 0 ? noval : pj - offset_x;
};
const int W = width * cn;
// ring keeps one extra row so the row leaving the window is still present
// for the incremental vertical update (ksize > 3)
constexpr int ring = ksize > 3 ? ksize + 1 : ksize;
auto p2idx = [&](int x){ return (x + ring) % ring * W; };
std::vector<ushort> res(W * ring);
std::vector<ushort> vsum(ksize > 3 ? W : 0);
bool vsum_valid = false;
auto process = [&](int x, int y) {
ushort sum[cn];
for (int c = 0; c < cn; c++)
sum[c] = 0;
for (int i = 0; i < ksize; i++)
{
int p = accessY(y + i);
if (p != noval)
{
for (int c = 0; c < cn; c++)
sum[c] += (src_data + x * src_step)[p * cn + c];
}
}
for (int c = 0; c < cn; c++)
res[p2idx(x) + y * cn + c] = sum[c];
};
const int left = anchor_x, right = width - (ksize - 1 - anchor_x);
for (int i = start - anchor_y; i < end + (ksize - 1 - anchor_y); i++)
{
if (i + offset_y >= 0 && i + offset_y < full_height)
{
if (left >= right)
{
for (int j = 0; j < width; j++)
process(i, j);
}
else
{
for (int j = 0; j < left; j++)
process(i, j);
for (int j = right; j < width; j++)
process(i, j);
ushort* row = res.data() + p2idx(i);
int vl;
for (int fj = left * cn; fj < right * cn; fj += vl)
{
vl = __riscv_vsetvl_e8m4(right * cn - fj);
const uchar* p = src_data + i * src_step + fj - anchor_x * cn;
auto acc = __riscv_vwaddu_vv(__riscv_vle8_v_u8m4(p, vl), __riscv_vle8_v_u8m4(p + cn, vl), vl);
for (int t = 2; t < ksize; t++)
acc = __riscv_vwaddu_wv(acc, __riscv_vle8_v_u8m4(p + t * cn, vl), vl);
__riscv_vse16(row + fj, acc, vl);
}
}
}
int cur = i - (ksize - 1 - anchor_y);
if (cur >= start)
{
if (ksize > 3 && vsum_valid)
{
// slide the window down one row: add the entering row's column
// sums, subtract the leaving row's (u16 stays exact: the window
// sum and the intermediate sum+entering both fit)
int add_r = accessX(cur + ksize - 1);
int sub_r = accessX(cur - 1);
const ushort* addp = add_r == noval ? nullptr : res.data() + p2idx(add_r);
const ushort* subp = sub_r == noval ? nullptr : res.data() + p2idx(sub_r);
int vl;
for (int fj = 0; fj < W; fj += vl)
{
vl = __riscv_vsetvl_e16m8(W - fj);
auto sum = __riscv_vle16_v_u16m8(vsum.data() + fj, vl);
if (addp) sum = __riscv_vadd(sum, __riscv_vle16_v_u16m8(addp + fj, vl), vl);
if (subp) sum = __riscv_vsub(sum, __riscv_vle16_v_u16m8(subp + fj, vl), vl);
__riscv_vse16(vsum.data() + fj, sum, vl);
if (normalize) sum = vdiv_box_u16(sum, ksize * ksize, vl);
__riscv_vse8(dst_data + cur * dst_step + fj, __riscv_vnclipu(sum, 0, __RISCV_VXRM_RNU, vl), vl);
}
}
else
{
const ushort* rows[ksize];
for (int k = 0; k < ksize; k++)
rows[k] = accessX(cur + k) == noval ? nullptr : res.data() + p2idx(accessX(cur + k));
int vl;
for (int fj = 0; fj < W; fj += vl)
{
vl = __riscv_vsetvl_e16m8(W - fj);
auto sum = rows[0] ? __riscv_vle16_v_u16m8(rows[0] + fj, vl) : __riscv_vmv_v_x_u16m8(0, vl);
for (int k = 1; k < ksize; k++)
if (rows[k]) sum = __riscv_vadd(sum, __riscv_vle16_v_u16m8(rows[k] + fj, vl), vl);
if (ksize > 3) __riscv_vse16(vsum.data() + fj, sum, vl);
if (normalize) sum = vdiv_box_u16(sum, ksize * ksize, vl);
__riscv_vse8(dst_data + cur * dst_step + fj, __riscv_vnclipu(sum, 0, __RISCV_VXRM_RNU, vl), vl);
}
vsum_valid = true;
}
}
}
return CV_HAL_ERROR_OK;
}
} // anonymous
int 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)
{
const int src_type = CV_MAKETYPE(src_depth, cn), dst_type = CV_MAKETYPE(dst_depth, cn);
if (ksize_width != ksize_height || (ksize_width != 3 && ksize_width != 5))
if (ksize_width != ksize_height || (ksize_width != 3 && ksize_width != 5 && ksize_width != 7))
return CV_HAL_ERROR_NOT_IMPLEMENTED;
if (border_type & BORDER_ISOLATED || border_type == BORDER_WRAP)
return CV_HAL_ERROR_NOT_IMPLEMENTED;
@@ -337,6 +510,10 @@ int boxFilter(const uchar* src_data, size_t src_step, uchar* dst_data, size_t ds
{
res = common::invoke(height, {boxFilterC1<5, RVV_U8M4, RVV_U16M8, false>}, src_data, src_step, dst_data, dst_step, width, margin_left + width + margin_right, margin_top + height + margin_bottom, margin_left, margin_top, anchor_x, anchor_y, normalize, border_type);
}
if (ksize_width == 7)
{
res = common::invoke(height, {boxFilterC1<7, RVV_U8M4, RVV_U16M8, false>}, src_data, src_step, dst_data, dst_step, width, margin_left + width + margin_right, margin_top + height + margin_bottom, margin_left, margin_top, anchor_x, anchor_y, normalize, border_type);
}
}
}
else
@@ -349,12 +526,36 @@ int boxFilter(const uchar* src_data, size_t src_step, uchar* dst_data, size_t ds
case 500 + CV_8UC1:
res = common::invoke(height, {boxFilterC1<5, RVV_U8M4, RVV_U16M8, true>}, src_data, src_step, dst_data, dst_step, width, margin_left + width + margin_right, margin_top + height + margin_bottom, margin_left, margin_top, anchor_x, anchor_y, normalize, border_type);
break;
case 700 + CV_8UC1:
res = common::invoke(height, {boxFilterFlat8U<7, 1>}, src_data, src_step, dst_data, dst_step, width, margin_left + width + margin_right, margin_top + height + margin_bottom, margin_left, margin_top, anchor_x, anchor_y, normalize, border_type);
break;
case 300 + CV_8UC3:
res = common::invoke(height, {boxFilterFlat8U<3, 3>}, src_data, src_step, dst_data, dst_step, width, margin_left + width + margin_right, margin_top + height + margin_bottom, margin_left, margin_top, anchor_x, anchor_y, normalize, border_type);
break;
case 500 + CV_8UC3:
res = common::invoke(height, {boxFilterFlat8U<5, 3>}, src_data, src_step, dst_data, dst_step, width, margin_left + width + margin_right, margin_top + height + margin_bottom, margin_left, margin_top, anchor_x, anchor_y, normalize, border_type);
break;
case 700 + CV_8UC3:
res = common::invoke(height, {boxFilterFlat8U<7, 3>}, src_data, src_step, dst_data, dst_step, width, margin_left + width + margin_right, margin_top + height + margin_bottom, margin_left, margin_top, anchor_x, anchor_y, normalize, border_type);
break;
case 300 + CV_8UC4:
res = common::invoke(height, {boxFilterFlat8U<3, 4>}, src_data, src_step, dst_data, dst_step, width, margin_left + width + margin_right, margin_top + height + margin_bottom, margin_left, margin_top, anchor_x, anchor_y, normalize, border_type);
break;
case 500 + CV_8UC4:
res = common::invoke(height, {boxFilterFlat8U<5, 4>}, src_data, src_step, dst_data, dst_step, width, margin_left + width + margin_right, margin_top + height + margin_bottom, margin_left, margin_top, anchor_x, anchor_y, normalize, border_type);
break;
case 700 + CV_8UC4:
res = common::invoke(height, {boxFilterFlat8U<7, 4>}, src_data, src_step, dst_data, dst_step, width, margin_left + width + margin_right, margin_top + height + margin_bottom, margin_left, margin_top, anchor_x, anchor_y, normalize, border_type);
break;
case 300 + CV_16SC1:
res = common::invoke(height, {boxFilterC1<3, RVV_I16M4, RVV_I32M8, true>}, src_data, src_step, dst_data, dst_step, width, margin_left + width + margin_right, margin_top + height + margin_bottom, margin_left, margin_top, anchor_x, anchor_y, normalize, border_type);
break;
case 500 + CV_16SC1:
res = common::invoke(height, {boxFilterC1<5, RVV_I16M4, RVV_I32M8, true>}, src_data, src_step, dst_data, dst_step, width, margin_left + width + margin_right, margin_top + height + margin_bottom, margin_left, margin_top, anchor_x, anchor_y, normalize, border_type);
break;
case 700 + CV_16SC1:
res = common::invoke(height, {boxFilterC1<7, RVV_I16M4, RVV_I32M8, true>}, src_data, src_step, dst_data, dst_step, width, margin_left + width + margin_right, margin_top + height + margin_bottom, margin_left, margin_top, anchor_x, anchor_y, normalize, border_type);
break;
case 300 + CV_32SC1:
res = common::invoke(height, {boxFilterC1<3, RVV_I32M8, RVV_I32M8, true>}, src_data, src_step, dst_data, dst_step, width, margin_left + width + margin_right, margin_top + height + margin_bottom, margin_left, margin_top, anchor_x, anchor_y, normalize, border_type);
break;
@@ -367,12 +568,18 @@ int boxFilter(const uchar* src_data, size_t src_step, uchar* dst_data, size_t ds
case 500 + CV_32FC1:
res = common::invoke(height, {boxFilterC1<5, RVV_F32M8, RVV_F32M8, true>}, src_data, src_step, dst_data, dst_step, width, margin_left + width + margin_right, margin_top + height + margin_bottom, margin_left, margin_top, anchor_x, anchor_y, normalize, border_type);
break;
case 700 + CV_32FC1:
res = common::invoke(height, {boxFilterC1<7, RVV_F32M8, RVV_F32M8, true>}, src_data, src_step, dst_data, dst_step, width, margin_left + width + margin_right, margin_top + height + margin_bottom, margin_left, margin_top, anchor_x, anchor_y, normalize, border_type);
break;
case 300 + CV_32FC3:
res = common::invoke(height, {boxFilterC3<3>}, src_data, src_step, dst_data, dst_step, width, margin_left + width + margin_right, margin_top + height + margin_bottom, margin_left, margin_top, anchor_x, anchor_y, normalize, border_type);
break;
case 500 + CV_32FC3:
res = common::invoke(height, {boxFilterC3<5>}, src_data, src_step, dst_data, dst_step, width, margin_left + width + margin_right, margin_top + height + margin_bottom, margin_left, margin_top, anchor_x, anchor_y, normalize, border_type);
break;
case 700 + CV_32FC3:
res = common::invoke(height, {boxFilterC3<7>}, src_data, src_step, dst_data, dst_step, width, margin_left + width + margin_right, margin_top + height + margin_bottom, margin_left, margin_top, anchor_x, anchor_y, normalize, border_type);
break;
}
}
if (res == CV_HAL_ERROR_NOT_IMPLEMENTED)
+59 -154
View File
@@ -136,8 +136,13 @@ static inline int gaussianBlurC1(int start, int end, const uchar* src_data, size
return CV_HAL_ERROR_OK;
}
template<int ksize>
static inline int gaussianBlurC4(int start, int end, const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, int width, int full_width, int full_height, int offset_x, int offset_y, int border_type)
// Channel-blind kernel for interleaved 8U data: a row is treated as width*cn
// flat u8 elements whose horizontal taps sit at strides of cn elements, so no
// segment loads or slide chains are needed; the vertical pass is a single
// contiguous u16 stream and the output is a plain store. Weighted taps are
// combined with shifts; u16 is exact (two 16x passes: 16*16*255 = 65280).
template<int ksize, int cn>
static inline int gaussianBlurFlat8U(int start, int end, const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, int width, int full_width, int full_height, int offset_x, int offset_y, int border_type)
{
constexpr int noval = std::numeric_limits<int>::max();
auto accessX = [&](int x) {
@@ -148,28 +153,26 @@ static inline int gaussianBlurC4(int start, int end, const uchar* src_data, size
int pj = common::borderInterpolate(offset_x + y - ksize / 2, full_width, border_type);
return pj < 0 ? noval : pj - offset_x;
};
auto p2idx = [&](int x, int y){ return ((x + ksize) % ksize * width + y) * 4; };
const int W = width * cn;
auto p2idx = [&](int x){ return (x + ksize) % ksize * W; };
constexpr uint kernel[2][5] = {{1, 2, 1}, {1, 4, 6, 4, 1}};
std::vector<ushort> res(width * ksize * 4);
std::vector<ushort> res(W * ksize);
auto process = [&](int x, int y) {
ushort sum0, sum1, sum2, sum3;
sum0 = sum1 = sum2 = sum3 = 0;
ushort sum[cn];
for (int c = 0; c < cn; c++)
sum[c] = 0;
for (int i = 0; i < ksize; i++)
{
int p = accessY(y + i);
if (p != noval)
{
sum0 += kernel[ksize == 5][i] * static_cast<ushort>((src_data + x * src_step)[p * 4 ]);
sum1 += kernel[ksize == 5][i] * static_cast<ushort>((src_data + x * src_step)[p * 4 + 1]);
sum2 += kernel[ksize == 5][i] * static_cast<ushort>((src_data + x * src_step)[p * 4 + 2]);
sum3 += kernel[ksize == 5][i] * static_cast<ushort>((src_data + x * src_step)[p * 4 + 3]);
for (int c = 0; c < cn; c++)
sum[c] += kernel[ksize == 5][i] * static_cast<ushort>((src_data + x * src_step)[p * cn + c]);
}
}
res[p2idx(x, y) ] = sum0;
res[p2idx(x, y) + 1] = sum1;
res[p2idx(x, y) + 2] = sum2;
res[p2idx(x, y) + 3] = sum3;
for (int c = 0; c < cn; c++)
res[p2idx(x) + y * cn + c] = sum[c];
};
const int left = ksize / 2, right = width - ksize / 2;
@@ -189,80 +192,31 @@ static inline int gaussianBlurC4(int start, int end, const uchar* src_data, size
for (int j = right; j < width; j++)
process(i, j);
ushort* row = res.data() + p2idx(i);
int vl;
for (int j = left; j < right; j += vl)
for (int fj = left * cn; fj < right * cn; fj += vl)
{
vl = __riscv_vsetvl_e8m1(right - j);
const uchar* extra = src_data + i * src_step + (j - ksize / 2) * 4;
auto src = __riscv_vlseg4e8_v_u8m1x4(extra, vl);
auto src0 = __riscv_vzext_vf2(__riscv_vget_v_u8m1x4_u8m1(src, 0), vl);
auto src1 = __riscv_vzext_vf2(__riscv_vget_v_u8m1x4_u8m1(src, 1), vl);
auto src2 = __riscv_vzext_vf2(__riscv_vget_v_u8m1x4_u8m1(src, 2), vl);
auto src3 = __riscv_vzext_vf2(__riscv_vget_v_u8m1x4_u8m1(src, 3), vl);
extra += vl * 4;
auto sum0 = src0, sum1 = src1, sum2 = src2, sum3 = src3;
vl = __riscv_vsetvl_e8m4(right * cn - fj);
const uchar* p = src_data + i * src_step + fj - (ksize / 2) * cn;
vuint16m8_t acc;
if (ksize == 3)
{
src0 = __riscv_vslide1down(src0, extra[0], vl);
src1 = __riscv_vslide1down(src1, extra[1], vl);
src2 = __riscv_vslide1down(src2, extra[2], vl);
src3 = __riscv_vslide1down(src3, extra[3], vl);
sum0 = __riscv_vadd(sum0, __riscv_vsll(src0, 1, vl), vl);
sum1 = __riscv_vadd(sum1, __riscv_vsll(src1, 1, vl), vl);
sum2 = __riscv_vadd(sum2, __riscv_vsll(src2, 1, vl), vl);
sum3 = __riscv_vadd(sum3, __riscv_vsll(src3, 1, vl), vl);
src0 = __riscv_vslide1down(src0, extra[4], vl);
src1 = __riscv_vslide1down(src1, extra[5], vl);
src2 = __riscv_vslide1down(src2, extra[6], vl);
src3 = __riscv_vslide1down(src3, extra[7], vl);
sum0 = __riscv_vadd(sum0, src0, vl);
sum1 = __riscv_vadd(sum1, src1, vl);
sum2 = __riscv_vadd(sum2, src2, vl);
sum3 = __riscv_vadd(sum3, src3, vl);
// 1 2 1
acc = __riscv_vwaddu_vv(__riscv_vle8_v_u8m4(p, vl), __riscv_vle8_v_u8m4(p + 2 * cn, vl), vl);
auto mid = __riscv_vle8_v_u8m4(p + cn, vl);
acc = __riscv_vwaddu_wv(acc, mid, vl);
acc = __riscv_vwaddu_wv(acc, mid, vl);
}
else
{
src0 = __riscv_vslide1down(src0, extra[0], vl);
src1 = __riscv_vslide1down(src1, extra[1], vl);
src2 = __riscv_vslide1down(src2, extra[2], vl);
src3 = __riscv_vslide1down(src3, extra[3], vl);
sum0 = __riscv_vadd(sum0, __riscv_vsll(src0, 2, vl), vl);
sum1 = __riscv_vadd(sum1, __riscv_vsll(src1, 2, vl), vl);
sum2 = __riscv_vadd(sum2, __riscv_vsll(src2, 2, vl), vl);
sum3 = __riscv_vadd(sum3, __riscv_vsll(src3, 2, vl), vl);
src0 = __riscv_vslide1down(src0, extra[4], vl);
src1 = __riscv_vslide1down(src1, extra[5], vl);
src2 = __riscv_vslide1down(src2, extra[6], vl);
src3 = __riscv_vslide1down(src3, extra[7], vl);
sum0 = __riscv_vadd(sum0, __riscv_vadd(__riscv_vsll(src0, 1, vl), __riscv_vsll(src0, 2, vl), vl), vl);
sum1 = __riscv_vadd(sum1, __riscv_vadd(__riscv_vsll(src1, 1, vl), __riscv_vsll(src1, 2, vl), vl), vl);
sum2 = __riscv_vadd(sum2, __riscv_vadd(__riscv_vsll(src2, 1, vl), __riscv_vsll(src2, 2, vl), vl), vl);
sum3 = __riscv_vadd(sum3, __riscv_vadd(__riscv_vsll(src3, 1, vl), __riscv_vsll(src3, 2, vl), vl), vl);
src0 = __riscv_vslide1down(src0, extra[ 8], vl);
src1 = __riscv_vslide1down(src1, extra[ 9], vl);
src2 = __riscv_vslide1down(src2, extra[10], vl);
src3 = __riscv_vslide1down(src3, extra[11], vl);
sum0 = __riscv_vadd(sum0, __riscv_vsll(src0, 2, vl), vl);
sum1 = __riscv_vadd(sum1, __riscv_vsll(src1, 2, vl), vl);
sum2 = __riscv_vadd(sum2, __riscv_vsll(src2, 2, vl), vl);
sum3 = __riscv_vadd(sum3, __riscv_vsll(src3, 2, vl), vl);
src0 = __riscv_vslide1down(src0, extra[12], vl);
src1 = __riscv_vslide1down(src1, extra[13], vl);
src2 = __riscv_vslide1down(src2, extra[14], vl);
src3 = __riscv_vslide1down(src3, extra[15], vl);
sum0 = __riscv_vadd(sum0, src0, vl);
sum1 = __riscv_vadd(sum1, src1, vl);
sum2 = __riscv_vadd(sum2, src2, vl);
sum3 = __riscv_vadd(sum3, src3, vl);
// 1 4 6 4 1
acc = __riscv_vwaddu_vv(__riscv_vle8_v_u8m4(p, vl), __riscv_vle8_v_u8m4(p + 4 * cn, vl), vl);
auto t13 = __riscv_vwaddu_vv(__riscv_vle8_v_u8m4(p + cn, vl), __riscv_vle8_v_u8m4(p + 3 * cn, vl), vl);
acc = __riscv_vadd(acc, __riscv_vsll(t13, 2, vl), vl);
auto mid = __riscv_vzext_vf2(__riscv_vle8_v_u8m4(p + 2 * cn, vl), vl);
acc = __riscv_vadd(acc, __riscv_vadd(__riscv_vsll(mid, 2, vl), __riscv_vsll(mid, 1, vl), vl), vl);
}
vuint16m2x4_t dst{};
dst = __riscv_vset_v_u16m2_u16m2x4(dst, 0, sum0);
dst = __riscv_vset_v_u16m2_u16m2x4(dst, 1, sum1);
dst = __riscv_vset_v_u16m2_u16m2x4(dst, 2, sum2);
dst = __riscv_vset_v_u16m2_u16m2x4(dst, 3, sum3);
__riscv_vsseg4e16(res.data() + p2idx(i, j), dst, vl);
__riscv_vse16(row + fj, acc, vl);
}
}
}
@@ -270,84 +224,31 @@ static inline int gaussianBlurC4(int start, int end, const uchar* src_data, size
int cur = i - ksize / 2;
if (cur >= start)
{
const ushort* row0 = accessX(cur ) == noval ? nullptr : res.data() + p2idx(accessX(cur ), 0);
const ushort* row1 = accessX(cur + 1) == noval ? nullptr : res.data() + p2idx(accessX(cur + 1), 0);
const ushort* row2 = accessX(cur + 2) == noval ? nullptr : res.data() + p2idx(accessX(cur + 2), 0);
const ushort* row3 = nullptr, *row4 = nullptr;
if (ksize == 5)
{
row3 = accessX(cur + 3) == noval ? nullptr : res.data() + p2idx(accessX(cur + 3), 0);
row4 = accessX(cur + 4) == noval ? nullptr : res.data() + p2idx(accessX(cur + 4), 0);
}
const ushort* rows[ksize];
for (int k = 0; k < ksize; k++)
rows[k] = accessX(cur + k) == noval ? nullptr : res.data() + p2idx(accessX(cur + k));
int vl;
for (int j = 0; j < width; j += vl)
for (int fj = 0; fj < W; fj += vl)
{
vl = __riscv_vsetvl_e16m2(width - j);
vuint16m2_t sum0, sum1, sum2, sum3, src0{}, src1{}, src2{}, src3{};
sum0 = sum1 = sum2 = sum3 = __riscv_vmv_v_x_u16m2(0, vl);
auto loadres = [&](const ushort* row) {
auto src = __riscv_vlseg4e16_v_u16m2x4(row + j * 4, vl);
src0 = __riscv_vget_v_u16m2x4_u16m2(src, 0);
src1 = __riscv_vget_v_u16m2x4_u16m2(src, 1);
src2 = __riscv_vget_v_u16m2x4_u16m2(src, 2);
src3 = __riscv_vget_v_u16m2x4_u16m2(src, 3);
};
if (row0)
vl = __riscv_vsetvl_e16m8(W - fj);
auto vzero = __riscv_vmv_v_x_u16m8(0, vl);
auto load = [&](int k) { return rows[k] ? __riscv_vle16_v_u16m8(rows[k] + fj, vl) : vzero; };
vuint16m8_t sum;
if (ksize == 3)
{
loadres(row0);
sum0 = src0;
sum1 = src1;
sum2 = src2;
sum3 = src3;
// 1 2 1
sum = __riscv_vadd(__riscv_vadd(load(0), load(2), vl), __riscv_vsll(load(1), 1, vl), vl);
}
if (row1)
else
{
loadres(row1);
sum0 = __riscv_vadd(sum0, __riscv_vsll(src0, ksize == 5 ? 2 : 1, vl), vl);
sum1 = __riscv_vadd(sum1, __riscv_vsll(src1, ksize == 5 ? 2 : 1, vl), vl);
sum2 = __riscv_vadd(sum2, __riscv_vsll(src2, ksize == 5 ? 2 : 1, vl), vl);
sum3 = __riscv_vadd(sum3, __riscv_vsll(src3, ksize == 5 ? 2 : 1, vl), vl);
// 1 4 6 4 1
sum = __riscv_vadd(load(0), load(4), vl);
sum = __riscv_vadd(sum, __riscv_vsll(__riscv_vadd(load(1), load(3), vl), 2, vl), vl);
auto mid = load(2);
sum = __riscv_vadd(sum, __riscv_vadd(__riscv_vsll(mid, 2, vl), __riscv_vsll(mid, 1, vl), vl), vl);
}
if (row2)
{
loadres(row2);
if (ksize == 5)
{
src0 = __riscv_vadd(__riscv_vsll(src0, 1, vl), __riscv_vsll(src0, 2, vl), vl);
src1 = __riscv_vadd(__riscv_vsll(src1, 1, vl), __riscv_vsll(src1, 2, vl), vl);
src2 = __riscv_vadd(__riscv_vsll(src2, 1, vl), __riscv_vsll(src2, 2, vl), vl);
src3 = __riscv_vadd(__riscv_vsll(src3, 1, vl), __riscv_vsll(src3, 2, vl), vl);
}
sum0 = __riscv_vadd(sum0, src0, vl);
sum1 = __riscv_vadd(sum1, src1, vl);
sum2 = __riscv_vadd(sum2, src2, vl);
sum3 = __riscv_vadd(sum3, src3, vl);
}
if (row3)
{
loadres(row3);
sum0 = __riscv_vadd(sum0, __riscv_vsll(src0, 2, vl), vl);
sum1 = __riscv_vadd(sum1, __riscv_vsll(src1, 2, vl), vl);
sum2 = __riscv_vadd(sum2, __riscv_vsll(src2, 2, vl), vl);
sum3 = __riscv_vadd(sum3, __riscv_vsll(src3, 2, vl), vl);
}
if (row4)
{
loadres(row4);
sum0 = __riscv_vadd(sum0, src0, vl);
sum1 = __riscv_vadd(sum1, src1, vl);
sum2 = __riscv_vadd(sum2, src2, vl);
sum3 = __riscv_vadd(sum3, src3, vl);
}
vuint8m1x4_t dst{};
dst = __riscv_vset_v_u8m1_u8m1x4(dst, 0, __riscv_vnclipu(sum0, ksize == 5 ? 8 : 4, __RISCV_VXRM_RNU, vl));
dst = __riscv_vset_v_u8m1_u8m1x4(dst, 1, __riscv_vnclipu(sum1, ksize == 5 ? 8 : 4, __RISCV_VXRM_RNU, vl));
dst = __riscv_vset_v_u8m1_u8m1x4(dst, 2, __riscv_vnclipu(sum2, ksize == 5 ? 8 : 4, __RISCV_VXRM_RNU, vl));
dst = __riscv_vset_v_u8m1_u8m1x4(dst, 3, __riscv_vnclipu(sum3, ksize == 5 ? 8 : 4, __RISCV_VXRM_RNU, vl));
__riscv_vsseg4e8(dst_data + cur * dst_step + j * 4, dst, vl);
__riscv_vse8(dst_data + cur * dst_step + fj, __riscv_vnclipu(sum, ksize == 5 ? 8 : 4, __RISCV_VXRM_RNU, vl), vl);
}
}
}
@@ -360,7 +261,7 @@ static inline int gaussianBlurC4(int start, int end, const uchar* src_data, size
int gaussianBlurBinomial(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, int width, int height, int depth, int cn, size_t margin_left, size_t margin_top, size_t margin_right, size_t margin_bottom, size_t ksize, int border_type)
{
const int type = CV_MAKETYPE(depth, cn);
if ((type != CV_8UC1 && type != CV_8UC4 && type != CV_16UC1) || src_data == dst_data)
if ((type != CV_8UC1 && type != CV_8UC3 && type != CV_8UC4 && type != CV_16UC1) || src_data == dst_data)
return CV_HAL_ERROR_NOT_IMPLEMENTED;
if ((ksize != 3 && ksize != 5) || border_type & BORDER_ISOLATED || border_type == BORDER_WRAP)
return CV_HAL_ERROR_NOT_IMPLEMENTED;
@@ -375,10 +276,14 @@ int gaussianBlurBinomial(const uchar* src_data, size_t src_step, uchar* dst_data
return common::invoke(height, {gaussianBlurC1<3, RVV_U16M4, RVV_U32M8>}, src_data, src_step, dst_data, dst_step, width, margin_left + width + margin_right, margin_top + height + margin_bottom, margin_left, margin_top, border_type);
case 500 + CV_16UC1:
return common::invoke(height, {gaussianBlurC1<5, RVV_U16M4, RVV_U32M8>}, src_data, src_step, dst_data, dst_step, width, margin_left + width + margin_right, margin_top + height + margin_bottom, margin_left, margin_top, border_type);
case 300 + CV_8UC3:
return common::invoke(height, {gaussianBlurFlat8U<3, 3>}, src_data, src_step, dst_data, dst_step, width, margin_left + width + margin_right, margin_top + height + margin_bottom, margin_left, margin_top, border_type);
case 500 + CV_8UC3:
return common::invoke(height, {gaussianBlurFlat8U<5, 3>}, src_data, src_step, dst_data, dst_step, width, margin_left + width + margin_right, margin_top + height + margin_bottom, margin_left, margin_top, border_type);
case 300 + CV_8UC4:
return common::invoke(height, {gaussianBlurC4<3>}, src_data, src_step, dst_data, dst_step, width, margin_left + width + margin_right, margin_top + height + margin_bottom, margin_left, margin_top, border_type);
return common::invoke(height, {gaussianBlurFlat8U<3, 4>}, src_data, src_step, dst_data, dst_step, width, margin_left + width + margin_right, margin_top + height + margin_bottom, margin_left, margin_top, border_type);
case 500 + CV_8UC4:
return common::invoke(height, {gaussianBlurC4<5>}, src_data, src_step, dst_data, dst_step, width, margin_left + width + margin_right, margin_top + height + margin_bottom, margin_left, margin_top, border_type);
return common::invoke(height, {gaussianBlurFlat8U<5, 4>}, src_data, src_step, dst_data, dst_step, width, margin_left + width + margin_right, margin_top + height + margin_bottom, margin_left, margin_top, border_type);
}
return CV_HAL_ERROR_NOT_IMPLEMENTED;
+235 -24
View File
@@ -6,8 +6,27 @@
#include "rvv_hal.hpp"
#include "common.hpp"
#include "opencv2/core/softfloat.hpp"
#include <list>
// Coverage matrix (depth x channels x interpolation). Entries not listed fall back to the
// generic implementation via CV_HAL_ERROR_NOT_IMPLEMENTED.
//
// | 8UC1 8UC2 8UC3 8UC4 | 16UC1 16UC2 16UC3 16UC4 | 32FC1 32FC2 32FC3 32FC4
// NEAREST | Y Y Y Y | Y* Y* - - | Y* - - -
// NEAREST_EXACT | Y Y Y Y | Y* Y* - - | Y* - - -
// LINEAR | Y Y Y Y | Y Y Y Y | Y Y Y Y
// LINEAR_EXACT | Y Y Y Y | - - - - | n/a (core uses LINEAR)
// AREA (2x2 fast) | Y Y Y Y | Y Y Y Y | - - - -
// AREA (generic dn) | Y Y Y Y | - - - - | - - - -
// AREA (upscale) | - (falls back: core maps AREA upscale to LINEAR-like path)
// CUBIC / LANCZOS4 | - (not implemented)
//
// Y* : NEAREST dispatches on element size in bytes (1..4), so 16UC1/16UC2/32FC1/32SC1 etc.
// are covered by the same gather kernels as 8UC2/8UC4.
// Additional limits: width guards require cn*src_width (resp. cn*4*src_width for AREA
// generic) to fit in ushort, since gather indices are 16-bit byte offsets.
namespace cv { namespace rvv_hal { namespace imgproc {
#if CV_HAL_RVV_1P0_ENABLED
@@ -39,16 +58,14 @@ static inline int invoke(int height, std::function<int(int, int, Args...)> func,
return func(0, 1, std::forward<Args>(args)...);
}
// cn is the element size in bytes, not the channel count; x_ofs/y_ofs hold precomputed
// clamped source offsets (x in bytes) so NEAREST and NEAREST_EXACT share the same kernel
template<int cn>
static inline int resizeNN(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, int dst_height, double scale_y, int interpolation, const ushort* x_ofs)
static inline int resizeNN(int start, int end, const uchar *src_data, size_t src_step, uchar *dst_data, size_t dst_step, int dst_width, const int* y_ofs_tab, const ushort* x_ofs)
{
const int ify = ((src_height << 16) + dst_height / 2) / dst_height;
const int ify0 = ify / 2 - src_height % 2;
for (int i = start; i < end; i++)
{
int y_ofs = interpolation == CV_HAL_INTER_NEAREST ? static_cast<int>(std::floor(i * scale_y)) : (ify * i + ify0) >> 16;
y_ofs = std::min(y_ofs, src_height - 1);
int y_ofs = y_ofs_tab[i];
int vl;
switch (cn)
@@ -102,6 +119,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 +167,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 +336,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)
{
@@ -739,29 +904,44 @@ static inline int resizeArea(int start, int end, const uchar *src_data, size_t s
// in the function static void resizeNN and static void resizeNN_bitexact
static inline int resizeNN(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 scale_x, double scale_y, int interpolation)
{
const int cn = CV_ELEM_SIZE(src_type);
if (cn * src_width > std::numeric_limits<ushort>::max())
const int pix_size = CV_ELEM_SIZE(src_type);
if (pix_size * src_width > std::numeric_limits<ushort>::max())
return CV_HAL_ERROR_NOT_IMPLEMENTED;
std::vector<ushort> x_ofs(dst_width);
const int ifx = ((src_width << 16) + dst_width / 2) / dst_width;
const int ifx0 = ifx / 2 - src_width % 2;
for (int i = 0; i < dst_width; i++)
std::vector<int> y_ofs(dst_height);
if (interpolation == CV_HAL_INTER_NEAREST)
{
x_ofs[i] = interpolation == CV_HAL_INTER_NEAREST ? static_cast<ushort>(std::floor(i * scale_x)) : (ifx * i + ifx0) >> 16;
x_ofs[i] = std::min(x_ofs[i], static_cast<ushort>(src_width - 1)) * cn;
for (int i = 0; i < dst_width; i++)
x_ofs[i] = static_cast<ushort>(std::min(cvFloor(i * scale_x), src_width - 1) * pix_size);
for (int i = 0; i < dst_height; i++)
y_ofs[i] = std::min(cvFloor(i * scale_y), src_height - 1);
}
else
{
// NEAREST_EXACT must reproduce resizeNN_bitexact_tab: Pillow-compatible pixel-center
// mapping accumulated in softdouble, otherwise results diverge from the core path
const softdouble fx = softdouble(src_width) / softdouble(dst_width);
softdouble vx = fx * softdouble(0.5);
for (int i = 0; i < dst_width; i++, vx += fx)
x_ofs[i] = static_cast<ushort>(std::min(cvFloor(vx), src_width - 1) * pix_size);
const softdouble fy = softdouble(src_height) / softdouble(dst_height);
softdouble vy = fy * softdouble(0.5);
for (int i = 0; i < dst_height; i++, vy += fy)
y_ofs[i] = std::min(cvFloor(vy), src_height - 1);
}
switch (src_type)
// gather kernels only depend on the element size, so e.g. 16UC1 shares the 8UC2 path
switch (pix_size)
{
case CV_8UC1:
return invoke(dst_height, {resizeNN<1>}, src_data, src_step, src_height, dst_data, dst_step, dst_width, dst_height, scale_y, interpolation, x_ofs.data());
case CV_8UC2:
return invoke(dst_height, {resizeNN<2>}, src_data, src_step, src_height, dst_data, dst_step, dst_width, dst_height, scale_y, interpolation, x_ofs.data());
case CV_8UC3:
return invoke(dst_height, {resizeNN<3>}, src_data, src_step, src_height, dst_data, dst_step, dst_width, dst_height, scale_y, interpolation, x_ofs.data());
case CV_8UC4:
return invoke(dst_height, {resizeNN<4>}, src_data, src_step, src_height, dst_data, dst_step, dst_width, dst_height, scale_y, interpolation, x_ofs.data());
case 1:
return invoke(dst_height, {resizeNN<1>}, src_data, src_step, dst_data, dst_step, dst_width, y_ofs.data(), x_ofs.data());
case 2:
return invoke(dst_height, {resizeNN<2>}, src_data, src_step, dst_data, dst_step, dst_width, y_ofs.data(), x_ofs.data());
case 3:
return invoke(dst_height, {resizeNN<3>}, src_data, src_step, dst_data, dst_step, dst_width, y_ofs.data(), x_ofs.data());
case 4:
return invoke(dst_height, {resizeNN<4>}, src_data, src_step, dst_data, dst_step, dst_width, y_ofs.data(), x_ofs.data());
}
return CV_HAL_ERROR_NOT_IMPLEMENTED;
}
@@ -801,6 +981,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);
@@ -990,8 +1201,8 @@ int resize(int src_type, const uchar *src_data, size_t src_step, int src_width,
{
inv_scale_x = 1 / inv_scale_x;
inv_scale_y = 1 / inv_scale_y;
//if (interpolation == CV_HAL_INTER_NEAREST || interpolation == CV_HAL_INTER_NEAREST_EXACT)
// return resizeNN(src_type, src_data, src_step, src_width, src_height, dst_data, dst_step, dst_width, dst_height, inv_scale_x, inv_scale_y, interpolation);
if (interpolation == CV_HAL_INTER_NEAREST || interpolation == CV_HAL_INTER_NEAREST_EXACT)
return resizeNN(src_type, src_data, src_step, src_width, src_height, dst_data, dst_step, dst_width, dst_height, inv_scale_x, inv_scale_y, interpolation);
if (interpolation == CV_HAL_INTER_LINEAR || interpolation == CV_HAL_INTER_LINEAR_EXACT)
return resizeLinear(src_type, src_data, src_step, src_width, src_height, dst_data, dst_step, dst_width, dst_height, inv_scale_x, inv_scale_y, interpolation);
if (interpolation == CV_HAL_INTER_AREA)
+3
View File
@@ -67,6 +67,9 @@
#ifdef HAVE_OPENCV_FLANN
#include "opencv2/flann.hpp"
#endif
#ifdef HAVE_OPENCV_GEOMETRY
#include "opencv2/geometry.hpp"
#endif
#ifdef HAVE_OPENCV_HIGHGUI
#include "opencv2/highgui.hpp"
#endif
+22 -22
View File
@@ -493,31 +493,31 @@ enum CameraModel {
CALIB_MODEL_FISHEYE = 1, //!< Fisheye camera model
};
enum { CALIB_USE_INTRINSIC_GUESS = 0x00001, //!< Use user provided intrinsics as initial point for optimization.
CALIB_FIX_ASPECT_RATIO = 0x00002, //!< Use with CALIB_USE_INTRINSIC_GUESS. The ratio fx/fy stays the same as in the input cameraMatrix.
CALIB_FIX_PRINCIPAL_POINT = 0x00004, //!< The principal point (cx, cy) stays the same as in the input camera matrix. Image center is used as principal point, if CALIB_USE_INTRINSIC_GUESS is not set.
CALIB_ZERO_TANGENT_DIST = 0x00008, //!< For pinhole model only. Tangential distortion coefficients \f$(p_1, p_2)\f$ are set to zeros and stay zero.
CALIB_FIX_FOCAL_LENGTH = 0x00010, //!< Use with CALIB_USE_INTRINSIC_GUESS. The focal length (fx, fy) stays the same as in the input cameraMatrix.
CALIB_FIX_K1 = 0x00020, //!< The corresponding distortion coefficient is not changed during the optimization. 0 value is used, if CALIB_USE_INTRINSIC_GUESS is not set.
CALIB_FIX_K2 = 0x00040, //!< The corresponding distortion coefficient is not changed during the optimization. 0 value is used, if CALIB_USE_INTRINSIC_GUESS is not set.
CALIB_FIX_K3 = 0x00080, //!< The corresponding distortion coefficient is not changed during the optimization. 0 value is used, if CALIB_USE_INTRINSIC_GUESS is not set.
CALIB_FIX_K4 = 0x00800, //!< The corresponding distortion coefficient is not changed during the optimization. 0 value is used, if CALIB_USE_INTRINSIC_GUESS is not set.
CALIB_FIX_K5 = 0x01000, //!< For pinhole model only. The corresponding distortion coefficient is not changed during the optimization. 0 value is used, if CALIB_USE_INTRINSIC_GUESS is not set.
CALIB_FIX_K6 = 0x02000, //!< For pinhole model only. The corresponding distortion coefficient is not changed during the optimization. 0 value is used, if CALIB_USE_INTRINSIC_GUESS is not set.
CALIB_RATIONAL_MODEL = 0x04000, //!< For pinhole model only. Use rational distortion model with coefficients k4..k6.
CALIB_THIN_PRISM_MODEL = 0x08000, //!< For pinhole model only. Use thin prism distortion model with coefficients s1..s4.
CALIB_FIX_S1_S2_S3_S4 = 0x10000, //!< For pinhole model only. The thin prism distortion coefficients are not changed during the optimization. 0 value is used, if CALIB_USE_INTRINSIC_GUESS is not set.
CALIB_TILTED_MODEL = 0x40000, //!< For pinhole model only. Coefficients tauX and tauY are enabled in camera matrix.
CALIB_FIX_TAUX_TAUY = 0x80000, //!< For pinhole model only. The tauX and tauY coefficients are not changed during the optimization. 0 value is used, if CALIB_USE_INTRINSIC_GUESS is not set.
CALIB_USE_QR = 0x100000, //!< Use QR instead of SVD decomposition for solving. Faster but potentially less precise
CALIB_FIX_TANGENT_DIST = 0x200000, //!< For pinhole model only. Tangential distortion coefficients (p1,p2) are set to zeros and stay zero.
enum { CALIB_USE_INTRINSIC_GUESS = (1 << 0), //!< Use user provided intrinsics as initial point for optimization.
CALIB_FIX_ASPECT_RATIO = (1 << 1), //!< Use with CALIB_USE_INTRINSIC_GUESS. The ratio fx/fy stays the same as in the input cameraMatrix.
CALIB_FIX_PRINCIPAL_POINT = (1 << 2), //!< The principal point (cx, cy) stays the same as in the input camera matrix. Image center is used as principal point, if CALIB_USE_INTRINSIC_GUESS is not set.
CALIB_ZERO_TANGENT_DIST = (1 << 3), //!< For pinhole model only. Tangential distortion coefficients \f$(p_1, p_2)\f$ are set to zeros and stay zero.
CALIB_FIX_FOCAL_LENGTH = (1 << 4), //!< Use with CALIB_USE_INTRINSIC_GUESS. The focal length (fx, fy) stays the same as in the input cameraMatrix.
CALIB_FIX_K1 = (1 << 5), //!< The corresponding distortion coefficient is not changed during the optimization. 0 value is used, if CALIB_USE_INTRINSIC_GUESS is not set.
CALIB_FIX_K2 = (1 << 6), //!< The corresponding distortion coefficient is not changed during the optimization. 0 value is used, if CALIB_USE_INTRINSIC_GUESS is not set.
CALIB_FIX_K3 = (1 << 7), //!< The corresponding distortion coefficient is not changed during the optimization. 0 value is used, if CALIB_USE_INTRINSIC_GUESS is not set.
CALIB_FIX_K4 = (1 << 11), //!< The corresponding distortion coefficient is not changed during the optimization. 0 value is used, if CALIB_USE_INTRINSIC_GUESS is not set.
CALIB_FIX_K5 = (1 << 12), //!< For pinhole model only. The corresponding distortion coefficient is not changed during the optimization. 0 value is used, if CALIB_USE_INTRINSIC_GUESS is not set.
CALIB_FIX_K6 = (1 << 13), //!< For pinhole model only. The corresponding distortion coefficient is not changed during the optimization. 0 value is used, if CALIB_USE_INTRINSIC_GUESS is not set.
CALIB_RATIONAL_MODEL = (1 << 14), //!< For pinhole model only. Use rational distortion model with coefficients k4..k6.
CALIB_THIN_PRISM_MODEL = (1 << 15), //!< For pinhole model only. Use thin prism distortion model with coefficients s1..s4.
CALIB_FIX_S1_S2_S3_S4 = (1 << 16), //!< For pinhole model only. The thin prism distortion coefficients are not changed during the optimization. 0 value is used, if CALIB_USE_INTRINSIC_GUESS is not set.
CALIB_TILTED_MODEL = (1 << 18), //!< For pinhole model only. Coefficients tauX and tauY are enabled in camera matrix.
CALIB_FIX_TAUX_TAUY = (1 << 19), //!< For pinhole model only. The tauX and tauY coefficients are not changed during the optimization. 0 value is used, if CALIB_USE_INTRINSIC_GUESS is not set.
CALIB_USE_QR = (1 << 20), //!< Use QR instead of SVD decomposition for solving. Faster but potentially less precise
CALIB_FIX_TANGENT_DIST = (1 << 21), //!< For pinhole model only. Tangential distortion coefficients (p1,p2) are set to zeros and stay zero.
// only for stereo
CALIB_FIX_INTRINSIC = 0x00100, //!< For stereo and milti-camera calibration only. Do not optimize cameras intrinsics
CALIB_SAME_FOCAL_LENGTH = 0x00200, //!< For stereo calibration only. Use the same focal length for cameras in pair.
CALIB_FIX_INTRINSIC = (1 << 8), //!< For stereo and milti-camera calibration only. Do not optimize cameras intrinsics
CALIB_SAME_FOCAL_LENGTH = (1 << 9), //!< For stereo calibration only. Use the same focal length for cameras in pair.
// for stereo rectification
CALIB_ZERO_DISPARITY = 0x00400, //!< Deprecated synonim of @ref STEREO_ZERO_DISPARITY. See @ref stereoRectify.
CALIB_ZERO_DISPARITY = (1 << 10), //!< Deprecated synonim of @ref STEREO_ZERO_DISPARITY. See @ref stereoRectify.
CALIB_USE_LU = (1 << 17), //!< use LU instead of SVD decomposition for solving. much faster but potentially less precise
CALIB_DISABLE_SCHUR_COMPLEMENT = (1 << 18), //!< disable Schur complement (use Bouguet calibration engine)
CALIB_DISABLE_SCHUR_COMPLEMENT = (1 << 27), //!< disable Schur complement (use Bouguet calibration engine)
CALIB_USE_EXTRINSIC_GUESS = (1 << 22), //!< For stereo and multi-view calibration. Use user provided extrinsics (R, T) as initial point for optimization
// fisheye only flags
CALIB_RECOMPUTE_EXTRINSIC = (1 << 23), //!< For fisheye model only. Recompute board position on each calibration iteration
+1 -1
View File
@@ -45,7 +45,7 @@
#include "opencv2/core/utility.hpp"
#include "opencv2/core/private.hpp"
#include "opencv2/core/utils/logger.hpp"
#include "opencv2/calib.hpp"
#include "opencv2/geometry.hpp"
#include "opencv2/imgproc.hpp"
+9 -7
View File
@@ -1,21 +1,23 @@
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(arithm SSE2 SSE4_1 AVX2 VSX3 LASX)
ocv_add_dispatched_file(stat SSE4_2 AVX2 AVX512_SKX AVX512_ICL LASX)
ocv_add_dispatched_file(arithm SSE2 SSE4_1 AVX2 NEON_FP16 VSX3 LASX)
ocv_add_dispatched_file(math SSE2 SSE4_1 AVX2 NEON_FP16 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 VSX3 LASX)
ocv_add_dispatched_file(minmax SSE2 SSE4_1 AVX2 AVX512_SKX AVX512_ICL VSX3 LASX)
ocv_add_dispatched_file(nan_mask SSE2 AVX2 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)
ocv_add_dispatched_file(transpose AVX AVX2 NEON RVV LASX)
# dispatching for accuracy tests
+111
View File
@@ -0,0 +1,111 @@
# OpenCV Hardware Acceleration Layer (HAL) {#api_hal}
## Detailed Description
OpenCV ships a single source tree that must run efficiently on wildly diverse hardware architectures, including x86, Arm, and RISC-V. Because each architecture utilizes wildly different SIMD widths and instruction sets (such as SSE2, AVX-512, NEON, SVE, and RVV), writing separate hand-optimized kernels for every algorithm is an unmaintainable approach.
To solve this, the performance architecture relies on the **two pillars of OpenCV**:
- **Universal Intrinsics:** A framework allowing developers to write vectorized code (a vector loop) just once using portable wrappers. The compiler then emits the highly optimized native intrinsics for the target architecture. (For an in-depth guide on writing vectorized code, visit the @ref tutorial_univ_intrin "Universal Intrinsics Documentation").
- **The Hardware Acceleration Layer (HAL):** A replaceable, stable C-interface that allows hardware vendors to bypass generic implementations entirely and inject their own highly tuned, silicon-specific libraries into OpenCV's core operations.
This document focuses entirely on the second pillar: HAL.
## What is HAL?
The Hardware Acceleration Layer (HAL) acts as the thin, interceptable layer between OpenCV's high-level algorithms and the hardware metal. While Universal Intrinsics make generic code fast, hardware vendors often possess proprietary, hand-tuned libraries that utilize specific cache blocking strategies, custom ISAs, or hardware accelerators that outpace generic implementations.
HAL allows these vendors to reach OpenCV's enormous user base without needing to fork the repository or maintain complex patch sets.
## Architecture & The Call Chain
When an OpenCV application calls a standard algorithm, the execution follows a strict fallback chain to guarantee maximum performance and correctness:
1. **The OpenCV Algorithm** (e.g., `cv::resize`, `cv::cvtColor`, `cv::gemm`) is invoked.
2. **HAL Dispatcher:** OpenCV queries the `cv_hal_*` interface to check if a registered vendor backend has an optimized implementation for this specific operation and data type.
3. **Vendor Backend Execution:** If implemented, the vendor library (e.g., IPP, KleidiCV) executes the operation and returns the result.
4. **Graceful Fallback:** If the backend is absent, or returns a `NOT_IMPLEMENTED` status, OpenCV transparently falls back to its own dispatched Universal Intrinsics. If no SIMD path exists, it falls back to portable C++ scalar code.
## Scope of Interceptable Operations
HAL exposes approximately 280 interceptable operations across the library. A vendor does not need to implement all of them; covering just the ~20 hotspots that dominate real computer vision workloads (color conversions, resizes, warps, filters, and GEMM) captures most of the runtime.
| Module | `cv_hal_*` Entry Points | Examples |
|---|---|---|
| core | ~199 | add/sub/mul/div (all depths), gemm, SVD, LU, norm, split/merge, convertScale |
| imgproc | ~73 | resize, warpAffine/Perspective, remap, filter, sepFilter, morph, cvtColor, threshold, canny |
| geometry | ~4 | geometric transforms |
| features | ~4 | FAST corners, descriptor distance |
| video | ~3 | optical-flow primitives |
## Design Principles of the Replaceable Interface
The HAL interface is engineered to be as friction-less as possible for both OpenCV maintainers and hardware vendors:
- **Plain C API:** Avoids complex C++ ABI headaches, making compiled vendor libraries easy to ship and link.
- **Subset Implementation:** Vendors are encouraged to implement only the operations where their silicon has a distinct advantage. Partial coverage is a first-class feature.
- **Stable Signatures:** HAL function signatures are immutable. Backends built for older versions will survive and function across future OpenCV releases.
- **Low-Overhead Dispatch (New in OpenCV 5.0):** To reduce API bloat and eliminate calling overhead inside inner loops, modern OpenCV architecture uses a highly efficient function-pointer style dispatcher (`cv_hal_get_*_func()`) rather than rigid, type-specific C entry points. Through this new interface, rewritten geometric operations show performance gains ranging from 10% to over 300%.
## Plugging a Backend In
Vendor backends are integrated at build time via CMake. OpenCV provides built-in shortcut flags (like `WITH_IPP` or `WITH_KLEIDICV`) that automatically register known backends.
Alternatively, custom HALs can be pointed to directly:
```bash
cmake -DOpenCV_HAL="myvendor_hal;ipp"
```
**Runtime Coexistence:** Multiple HALs can coexist in a single build. At configure time, OpenCV registers every enabled HAL into an ordered list. At runtime, they are tried in registration order. This allows a user to combine a highly specialized custom HAL with a general-purpose HAL (like IPP) to achieve best-available acceleration automatically.
## Implementing a Custom HAL
If you are a hardware vendor or an advanced developer looking to build your own custom acceleration layer, OpenCV provides templates to get you started quickly.
### 1. Start with the Templates
Do not start from scratch. Navigate to the OpenCV source tree and look at the provided samples:
- `samples/hal/c_hal`: A barebones skeleton template demonstrating the required structure.
- `samples/hal/slow_hal`: A fully working example that demonstrates how to properly wire up the CMake configuration and intercept calls.
### 2. Implement the Interface
Write your optimized C code targeting the `cv_hal_<op>` signatures (or the new `cv_hal_get_<op>_func` signatures for OpenCV 5.0).
- If your library successfully computes the result, return `CV_HAL_OK` (or the equivalent success macro).
- If your library does not support the specific array size, data type, or operation, simply return `NOT_IMPLEMENTED`. OpenCV will gracefully handle the failure and compute the result using its own generic SIMD code.
### 3. CMake Configuration Requirements
When OpenCV searches for your custom HAL at build time, it runs `find_package(<your_hal_name>)`. Your CMake configuration must export the following variables so OpenCV can link against it:
- `<hal_name>_FOUND`
- `<hal_name>_LIBRARIES`
- `<hal_name>_HEADERS`
- `<hal_name>_INCLUDE_DIRS`
### 4. Build OpenCV with Your HAL
Once your custom library is compiled and your CMake configuration is ready, build OpenCV by explicitly pointing it to your build directory:
```bash
cmake -DOpenCV_HAL="myvendor_hal" -DOpenCV_HAL_DIR=<path-to-your-build> ..
```
Verify the integration by checking the CMake configure summary for the `OpenCV_USED_HAL` status line.
## The HAL Ecosystem
OpenCV supports multiple production-ready vendor backends natively, featuring deeply integrated support for modern architectures. These can be enabled at build time using the following CMake shortcuts:
| Backend | Vendor | Target | Scope | CMake Flag |
|---|---|---|---|---|
| IPP/IPPICV | Intel | x86/x86-64 | stats, transforms, warp | `WITH_IPP` |
| KleidiCV | Arm | AArch64 (NEON/SVE2/SME2) | color, filter, resize, warp | `WITH_KLEIDICV` |
| Carotene | NVIDIA | Arm NEON (v7/v8) | 80+ imgproc/arith ops | `WITH_CAROTENE` |
| FastCV | Qualcomm | Snapdragon (Android/Linux Arm) | core + imgproc | `WITH_FASTCV` |
| RVV HAL | OpenCV China, SpacemiT | RISC-V RVV 1.0 | ~119 core+imgproc ops | `WITH_HAL_RVV` |
| NDSRVP | Andes | RISC-V P-extension (DSP) | filter, morph, warp, remap | `WITH_NDSRVP` |
| ARMPL | Arm | AArch64 | BLAS/LAPACK linear algebra | `WITH_ARMPL` |
+16 -2
View File
@@ -1103,6 +1103,20 @@ CV_EXPORTS_W void broadcast(InputArray src, InputArray shape, OutputArray dst);
*/
CV_EXPORTS void broadcast(InputArray src, const MatShape& shape, OutputArray dst);
/** @brief Evaluate a broadcasting element-wise expression over the input arrays.
The expression is a small std::format-like string over placeholders `{0}`, `{1}`, ... (the entries of
@p inputs), C-style arithmetic / comparison / bitwise operators, type-cast and math function calls
(`uint8(...)`, `min`, `max`, `absdiff`, `pow`, ...), `;`-separated named temporaries and a
parenthesized tuple for multiple results. All operands broadcast against each other (numpy rules,
channels innermost) and the whole expression is fused into a single traversal of the data.
@param expr the expression string, e.g. `"{0} * 2.5 + {1}"` or `"({0} + {1}, {0} - {1})"`.
@param inputs the arrays bound to `{0}`, `{1}`, ...
@param outputs receives one array per top-level result (one entry, or several for a tuple).
*/
CV_EXPORTS_W void texpr(const String& expr, InputArrayOfArrays inputs, OutputArrayOfArrays outputs);
enum RotateFlags {
ROTATE_90_CLOCKWISE = 0, //!<Rotate 90 degrees clockwise
ROTATE_180 = 1, //!<Rotate 180 degrees clockwise
@@ -3112,7 +3126,7 @@ public:
};
static inline
inline
String& operator << (String& out, Ptr<Formatted> fmtd)
{
fmtd->reset();
@@ -3121,7 +3135,7 @@ String& operator << (String& out, Ptr<Formatted> fmtd)
return out;
}
static inline
inline
String& operator << (String& out, const Mat& mtx)
{
return out << Formatter::get()->format(mtx);
+2 -2
View File
@@ -615,7 +615,7 @@ V cv::operator*(const cv::Affine3<T>& affine, const V& v)
return r;
}
static inline
inline
cv::Vec3f cv::operator*(const cv::Affine3f& affine, const cv::Vec3f& v)
{
const cv::Matx44f& m = affine.matrix;
@@ -626,7 +626,7 @@ cv::Vec3f cv::operator*(const cv::Affine3f& affine, const cv::Vec3f& v)
return r;
}
static inline
inline
cv::Vec3d cv::operator*(const cv::Affine3d& affine, const cv::Vec3d& v)
{
const cv::Matx44d& m = affine.matrix;
+9 -9
View File
@@ -322,7 +322,7 @@ inline uint64 cv_absdiff(uint64 x, uint64 y) { return std::max(x, y) - std::min(
inline float cv_absdiff(hfloat x, hfloat y) { return std::abs((float)x - (float)y); }
inline float cv_absdiff(bfloat x, bfloat y) { return std::abs((float)x - (float)y); }
template<typename _Tp, typename _AccTp> static inline
template<typename _Tp, typename _AccTp> inline
_AccTp normL2Sqr(const _Tp* a, int n)
{
_AccTp s = 0;
@@ -334,7 +334,7 @@ _AccTp normL2Sqr(const _Tp* a, int n)
return s;
}
template<typename _Tp, typename _AccTp> static inline
template<typename _Tp, typename _AccTp> inline
_AccTp normL1(const _Tp* a, int n)
{
_AccTp s = 0;
@@ -343,7 +343,7 @@ _AccTp normL1(const _Tp* a, int n)
return s;
}
template<typename _Tp, typename _AccTp> static inline
template<typename _Tp, typename _AccTp> inline
_AccTp normInf(const _Tp* a, int n)
{
_AccTp s = 0;
@@ -352,7 +352,7 @@ _AccTp normInf(const _Tp* a, int n)
return s;
}
template<typename _Tp, typename _AccTp> static inline
template<typename _Tp, typename _AccTp> inline
_AccTp normL2Sqr(const _Tp* a, const _Tp* b, int n)
{
_AccTp s = 0;
@@ -364,7 +364,7 @@ _AccTp normL2Sqr(const _Tp* a, const _Tp* b, int n)
return s;
}
template<typename _Tp, typename _AccTp> static inline
template<typename _Tp, typename _AccTp> inline
_AccTp normL1(const _Tp* a, const _Tp* b, int n)
{
_AccTp s = 0;
@@ -373,7 +373,7 @@ _AccTp normL1(const _Tp* a, const _Tp* b, int n)
return s;
}
template<typename _Tp, typename _AccTp> static inline
template<typename _Tp, typename _AccTp> inline
_AccTp normInf(const _Tp* a, const _Tp* b, int n)
{
_AccTp s = 0;
@@ -395,7 +395,7 @@ CV_EXPORTS_W float cubeRoot(float val);
cubeRoot with argument of `double` type calls `std::cbrt(double)`
*/
static inline
inline
double cubeRoot(double val)
{
return std::cbrt(val);
@@ -440,8 +440,8 @@ CV_EXPORTS_W String getIppVersion();
CV_EXPORTS_W bool useIPP_NotExact();
CV_EXPORTS_W void setUseIPP_NotExact(bool flag);
#ifndef DISABLE_OPENCV_3_COMPATIBILITY
static inline bool useIPP_NE() { return useIPP_NotExact(); }
static inline void setUseIPP_NE(bool flag) { setUseIPP_NotExact(flag); }
inline bool useIPP_NE() { return useIPP_NotExact(); }
inline void setUseIPP_NE(bool flag) { setUseIPP_NotExact(flag); }
#endif
} // ipp
@@ -23,19 +23,19 @@ CV_EXPORTS_W String dumpInputOutputArray(InputOutputArray argument);
CV_EXPORTS_W String dumpInputOutputArrayOfArrays(InputOutputArrayOfArrays argument);
CV_WRAP static inline
CV_WRAP inline
String dumpBool(bool argument)
{
return (argument) ? String("Bool: True") : String("Bool: False");
}
CV_WRAP static inline
CV_WRAP inline
String dumpInt(int argument)
{
return cv::format("Int: %d", argument);
}
CV_WRAP static inline
CV_WRAP inline
String dumpInt64(int64 argument)
{
std::ostringstream oss("Int64: ", std::ios::ate);
@@ -43,7 +43,7 @@ String dumpInt64(int64 argument)
return oss.str();
}
CV_WRAP static inline
CV_WRAP inline
String dumpSizeT(size_t argument)
{
std::ostringstream oss("size_t: ", std::ios::ate);
@@ -51,45 +51,45 @@ String dumpSizeT(size_t argument)
return oss.str();
}
CV_WRAP static inline
CV_WRAP inline
String dumpFloat(float argument)
{
return cv::format("Float: %.2f", argument);
}
CV_WRAP static inline
CV_WRAP inline
String dumpDouble(double argument)
{
return cv::format("Double: %.2f", argument);
}
CV_WRAP static inline
CV_WRAP inline
String dumpCString(const char* argument)
{
return cv::format("String: %s", argument);
}
CV_WRAP static inline
CV_WRAP inline
String dumpString(const String& argument)
{
return cv::format("String: %s", argument.c_str());
}
CV_WRAP static inline
CV_WRAP inline
String dumpRect(const Rect& argument)
{
return format("rect: (x=%d, y=%d, w=%d, h=%d)", argument.x, argument.y,
argument.width, argument.height);
}
CV_WRAP static inline
CV_WRAP inline
String dumpTermCriteria(const TermCriteria& argument)
{
return format("term_criteria: (type=%d, max_count=%d, epsilon=%lf",
argument.type, argument.maxCount, argument.epsilon);
}
CV_WRAP static inline
CV_WRAP inline
String dumpRotatedRect(const RotatedRect& argument)
{
return format("rotated_rect: (c_x=%f, c_y=%f, w=%f, h=%f, a=%f)",
@@ -97,7 +97,7 @@ String dumpRotatedRect(const RotatedRect& argument)
argument.size.height, argument.angle);
}
CV_WRAP static inline
CV_WRAP inline
String dumpRange(const Range& argument)
{
if (argument == Range::all())
@@ -119,27 +119,27 @@ CV_EXPORTS_W String dumpVectorOfRect(const std::vector<Rect>& vec);
//! @cond IGNORED
CV_WRAP static inline
CV_WRAP inline
String testOverloadResolution(int value, const Point& point = Point(42, 24))
{
return format("overload (int=%d, point=(x=%d, y=%d))", value, point.x,
point.y);
}
CV_WRAP static inline
CV_WRAP inline
String testOverloadResolution(const Rect& rect)
{
return format("overload (rect=(x=%d, y=%d, w=%d, h=%d))", rect.x, rect.y,
rect.width, rect.height);
}
CV_WRAP static inline
CV_WRAP inline
RotatedRect testRotatedRect(float x, float y, float w, float h, float angle)
{
return RotatedRect(Point2f(x, y), Size2f(w, h), angle);
}
CV_WRAP static inline
CV_WRAP inline
std::vector<RotatedRect> testRotatedRectVector(float x, float y, float w, float h, float angle)
{
std::vector<RotatedRect> result;
@@ -148,19 +148,19 @@ std::vector<RotatedRect> testRotatedRectVector(float x, float y, float w, float
return result;
}
CV_WRAP static inline
CV_WRAP inline
int testOverwriteNativeMethod(int argument)
{
return argument;
}
CV_WRAP static inline
CV_WRAP inline
String testReservedKeywordConversion(int positional_argument, int lambda = 2, int from = 3)
{
return format("arg=%d, lambda=%d, from=%d", positional_argument, lambda, from);
}
CV_WRAP static inline
CV_WRAP inline
void generateVectorOfRect(size_t len, CV_OUT std::vector<Rect>& vec)
{
vec.resize(len);
@@ -173,7 +173,7 @@ void generateVectorOfRect(size_t len, CV_OUT std::vector<Rect>& vec)
}
}
CV_WRAP static inline
CV_WRAP inline
void generateVectorOfInt(size_t len, CV_OUT std::vector<int>& vec)
{
vec.resize(len);
@@ -186,7 +186,7 @@ void generateVectorOfInt(size_t len, CV_OUT std::vector<int>& vec)
}
}
CV_WRAP static inline
CV_WRAP inline
void generateVectorOfMat(size_t len, int rows, int cols, int dtype, CV_OUT std::vector<Mat>& vec)
{
vec.resize(len);
@@ -201,13 +201,13 @@ void generateVectorOfMat(size_t len, int rows, int cols, int dtype, CV_OUT std::
}
}
CV_WRAP static inline
CV_WRAP inline
void testRaiseGeneralException()
{
throw std::runtime_error("exception text");
}
CV_WRAP static inline
CV_WRAP inline
AsyncArray testAsyncArray(InputArray argument)
{
AsyncPromise p;
@@ -215,7 +215,7 @@ AsyncArray testAsyncArray(InputArray argument)
return p.getArrayResult();
}
CV_WRAP static inline
CV_WRAP inline
AsyncArray testAsyncException()
{
AsyncPromise p;
@@ -230,7 +230,7 @@ AsyncArray testAsyncException()
return p.getArrayResult();
}
CV_WRAP static inline
CV_WRAP inline
String dumpVec2i(const cv::Vec2i value = cv::Vec2i(42, 24)) {
return format("Vec2i(%d, %d)", value[0], value[1]);
}
@@ -264,7 +264,7 @@ struct CV_EXPORTS_W_PARAMS FunctionParams
}
};
CV_WRAP static inline String
CV_WRAP inline String
copyMatAndDumpNamedArguments(InputArray src, OutputArray dst,
const FunctionParams& params = FunctionParams())
{
@@ -274,7 +274,7 @@ copyMatAndDumpNamedArguments(InputArray src, OutputArray dst,
}
namespace nested {
CV_WRAP static inline bool testEchoBooleanFunction(bool flag) {
CV_WRAP inline bool testEchoBooleanFunction(bool flag) {
return flag;
}
@@ -373,7 +373,7 @@ void* GpuMat::cudaPtr() const
return data;
}
static inline
inline
GpuMat createContinuous(int rows, int cols, int type)
{
GpuMat m;
@@ -381,13 +381,13 @@ GpuMat createContinuous(int rows, int cols, int type)
return m;
}
static inline
inline
void createContinuous(Size size, int type, OutputArray arr)
{
createContinuous(size.height, size.width, type, arr);
}
static inline
inline
GpuMat createContinuous(Size size, int type)
{
GpuMat m;
@@ -395,13 +395,13 @@ GpuMat createContinuous(Size size, int type)
return m;
}
static inline
inline
void ensureSizeIsEnough(Size size, int type, OutputArray arr)
{
ensureSizeIsEnough(size.height, size.width, type, arr);
}
static inline
inline
void swap(GpuMat& a, GpuMat& b)
{
a.swap(b);
@@ -640,7 +640,7 @@ bool HostMem::empty() const
return data == 0;
}
static inline
inline
void swap(HostMem& a, HostMem& b)
{
a.swap(b);
@@ -63,7 +63,7 @@
#endif
namespace cv { namespace cuda {
static inline void checkCudaError(cudaError_t err, const char* file, const int line, const char* func)
inline void checkCudaError(cudaError_t err, const char* file, const int line, const char* func)
{
if (cudaSuccess != err) {
cudaGetLastError(); // reset the last stored error to cudaSuccess
@@ -78,12 +78,12 @@ namespace cv { namespace cuda {
namespace cv { namespace cuda
{
template <typename T> static inline bool isAligned(const T* ptr, size_t size)
template <typename T> inline bool isAligned(const T* ptr, size_t size)
{
return reinterpret_cast<size_t>(ptr) % size == 0;
}
static inline bool isAligned(size_t step, size_t size)
inline bool isAligned(size_t step, size_t size)
{
return step % size == 0;
}
@@ -56,14 +56,14 @@
namespace cv { namespace cuda { namespace device
{
template <typename T, typename D, typename UnOp, typename Mask>
static inline void transform(PtrStepSz<T> src, PtrStepSz<D> dst, UnOp op, const Mask& mask, cudaStream_t stream)
inline void transform(PtrStepSz<T> src, PtrStepSz<D> dst, UnOp op, const Mask& mask, cudaStream_t stream)
{
typedef TransformFunctorTraits<UnOp> ft;
transform_detail::TransformDispatcher<VecTraits<T>::cn == 1 && VecTraits<D>::cn == 1 && ft::smart_shift != 1>::call(src, dst, op, mask, stream);
}
template <typename T1, typename T2, typename D, typename BinOp, typename Mask>
static inline void transform(PtrStepSz<T1> src1, PtrStepSz<T2> src2, PtrStepSz<D> dst, BinOp op, const Mask& mask, cudaStream_t stream)
inline void transform(PtrStepSz<T1> src1, PtrStepSz<T2> src2, PtrStepSz<D> dst, BinOp op, const Mask& mask, cudaStream_t stream)
{
typedef TransformFunctorTraits<BinOp> ft;
transform_detail::TransformDispatcher<VecTraits<T1>::cn == 1 && VecTraits<T2>::cn == 1 && VecTraits<D>::cn == 1 && ft::smart_shift != 1>::call(src1, src2, dst, op, mask, stream);
+93 -13
View File
@@ -521,7 +521,10 @@ Cv64suf;
CV_16U - 2 bytes
...
*/
#define CV_ELEM_SIZE1(type) ((int)((0x4881228442211ULL >> (CV_MAT_DEPTH(type) * 4)) & 15))
#define CV_ELEM_SIZE1(type) \
((int)((CV_MAT_DEPTH(type) < 16 \
? (0x1114881228442211ULL >> (CV_MAT_DEPTH(type) * 4)) \
: (0x0000000000000001ULL >> ((CV_MAT_DEPTH(type) - 16) * 4))) & 15))
#define CV_ELEM_SIZE(type) (CV_MAT_CN(type)*CV_ELEM_SIZE1(type))
@@ -594,68 +597,68 @@ __CV_ENUM_CLASS_EXPOSE_1(EnumType, MEMBER_CONST);
__CV_EXPAND(__CV_ENUM_CLASS_EXPOSE_8(EnumType, __VA_ARGS__)); \
#define __CV_ENUM_FLAGS_LOGICAL_NOT(EnumType) \
static inline bool operator!(const EnumType& val) \
inline bool operator!(const EnumType& val) \
{ \
typedef std::underlying_type<EnumType>::type UnderlyingType; \
return !static_cast<UnderlyingType>(val); \
} \
#define __CV_ENUM_FLAGS_LOGICAL_NOT_EQ(Arg1Type, Arg2Type) \
static inline bool operator!=(const Arg1Type& a, const Arg2Type& b) \
inline bool operator!=(const Arg1Type& a, const Arg2Type& b) \
{ \
return static_cast<int>(a) != static_cast<int>(b); \
} \
#define __CV_ENUM_FLAGS_LOGICAL_EQ(Arg1Type, Arg2Type) \
static inline bool operator==(const Arg1Type& a, const Arg2Type& b) \
inline bool operator==(const Arg1Type& a, const Arg2Type& b) \
{ \
return static_cast<int>(a) == static_cast<int>(b); \
} \
#define __CV_ENUM_FLAGS_BITWISE_NOT(EnumType) \
static inline EnumType operator~(const EnumType& val) \
inline EnumType operator~(const EnumType& val) \
{ \
typedef std::underlying_type<EnumType>::type UnderlyingType; \
return static_cast<EnumType>(~static_cast<UnderlyingType>(val)); \
} \
#define __CV_ENUM_FLAGS_BITWISE_OR(EnumType, Arg1Type, Arg2Type) \
static inline EnumType operator|(const Arg1Type& a, const Arg2Type& b) \
inline EnumType operator|(const Arg1Type& a, const Arg2Type& b) \
{ \
typedef std::underlying_type<EnumType>::type UnderlyingType; \
return static_cast<EnumType>(static_cast<UnderlyingType>(a) | static_cast<UnderlyingType>(b)); \
} \
#define __CV_ENUM_FLAGS_BITWISE_AND(EnumType, Arg1Type, Arg2Type) \
static inline EnumType operator&(const Arg1Type& a, const Arg2Type& b) \
inline EnumType operator&(const Arg1Type& a, const Arg2Type& b) \
{ \
typedef std::underlying_type<EnumType>::type UnderlyingType; \
return static_cast<EnumType>(static_cast<UnderlyingType>(a) & static_cast<UnderlyingType>(b)); \
} \
#define __CV_ENUM_FLAGS_BITWISE_XOR(EnumType, Arg1Type, Arg2Type) \
static inline EnumType operator^(const Arg1Type& a, const Arg2Type& b) \
inline EnumType operator^(const Arg1Type& a, const Arg2Type& b) \
{ \
typedef std::underlying_type<EnumType>::type UnderlyingType; \
return static_cast<EnumType>(static_cast<UnderlyingType>(a) ^ static_cast<UnderlyingType>(b)); \
} \
#define __CV_ENUM_FLAGS_BITWISE_OR_EQ(EnumType, Arg1Type) \
static inline EnumType& operator|=(EnumType& _this, const Arg1Type& val) \
inline EnumType& operator|=(EnumType& _this, const Arg1Type& val) \
{ \
_this = static_cast<EnumType>(static_cast<int>(_this) | static_cast<int>(val)); \
return _this; \
} \
#define __CV_ENUM_FLAGS_BITWISE_AND_EQ(EnumType, Arg1Type) \
static inline EnumType& operator&=(EnumType& _this, const Arg1Type& val) \
inline EnumType& operator&=(EnumType& _this, const Arg1Type& val) \
{ \
_this = static_cast<EnumType>(static_cast<int>(_this) & static_cast<int>(val)); \
return _this; \
} \
#define __CV_ENUM_FLAGS_BITWISE_XOR_EQ(EnumType, Arg1Type) \
static inline EnumType& operator^=(EnumType& _this, const Arg1Type& val) \
inline EnumType& operator^=(EnumType& _this, const Arg1Type& val) \
{ \
_this = static_cast<EnumType>(static_cast<int>(_this) ^ static_cast<int>(val)); \
return _this; \
@@ -745,7 +748,7 @@ __CV_ENUM_FLAGS_BITWISE_XOR_EQ (EnumType, EnumType)
# define CV_XADD(addr, delta) (int)_InterlockedExchangeAdd((long volatile*)addr, delta)
#else
#ifdef OPENCV_FORCE_UNSAFE_XADD
CV_INLINE int CV_XADD(int* addr, int delta) { int tmp = *addr; *addr += delta; return tmp; }
inline int CV_XADD(int* addr, int delta) { int tmp = *addr; *addr += delta; return tmp; }
#else
#error "OpenCV: can't define safe CV_XADD macro for current platform (unsupported). Define CV_XADD macro through custom port header (see OPENCV_INCLUDE_PORT_FILE)"
#endif
@@ -979,13 +982,90 @@ protected:
ushort w;
};
namespace fp8_detail {
// round-half-up (ties away from zero) — deliberately NOT round-to-nearest-even / OCP-ONNX spec
inline unsigned roundHalfUp(unsigned full, int shift)
{
if (shift <= 0) return full << (-shift);
unsigned q = full >> shift, rem = full & ((1u << shift) - 1), half = 1u << (shift - 1);
if (rem >= half) q++;
return q;
}
inline float pow2(int n) { Cv32suf s; s.u = (unsigned)((n + 127) << 23); return s.f; }
// E4M3 encode; no inf, bias/fnuz select E4M3FN vs E4M3FNUZ
inline uchar encodeE4M3(float x, int bias, bool fnuz)
{
Cv32suf in; in.f = x;
unsigned u = in.u, sign = (u >> 31) & 1, e = (u >> 23) & 0xFF, m = u & 0x7FFFFF;
const unsigned sbit = sign << 7;
const unsigned nanc = fnuz ? 0x80u : (sbit | 0x7Fu);
if (e == 0xFF) return (uchar)nanc;
if (e == 0 && m == 0) return (uchar)(fnuz ? 0u : sbit);
int newexp = (int)e - 127 + bias;
const unsigned full = (1u << 23) | m;
if (newexp <= 0) // subnormal
{
const int shift = 20 + (1 - newexp);
unsigned mant = (shift >= 32) ? 0u : roundHalfUp(full, shift);
return (uchar)(mant == 0 ? (fnuz ? 0u : sbit) : (sbit | mant));
}
unsigned rounded = roundHalfUp(full, 20);
if (rounded & 16u) { rounded >>= 1; newexp++; } // carry into exponent
const unsigned mant = rounded & 7u;
const bool ov = fnuz ? ((unsigned)newexp > 15)
: ((unsigned)newexp > 15 || ((unsigned)newexp == 15 && mant == 7));
if (ov) return (uchar)nanc;
return (uchar)(sbit | ((unsigned)newexp << 3) | mant);
}
inline float decodeE4M3(uchar b, int bias, bool fnuz)
{
const unsigned sign = ((unsigned)b >> 7) & 1, exp = ((unsigned)b >> 3) & 15, man = (unsigned)b & 7;
const float s = sign ? -1.f : 1.f;
Cv32suf qn; qn.u = sign ? 0xFFC00000u : 0x7FC00000u;
if (fnuz) { if ((unsigned)b == 0x80u) return qn.f; }
else if (exp == 15) { if (man == 7) return qn.f; }
if (exp == 0) return s * (float)man * pow2(1 - bias - 3);
return s * (1.0f + (float)man / 8.0f) * pow2((int)exp - bias);
}
} // namespace fp8_detail
struct fp8_t // E4M3FN: bias 7, no inf, max 448
{
fp8_t() : b(0) {}
explicit fp8_t(float x) : b(fp8_detail::encodeE4M3(x, 7, false)) {}
operator float() const { return table()[b]; }
static const float* decodeLUT() { return table(); }
protected:
uchar b;
private:
static const float* table()
{ static const struct T { float v[256]; T() { for (int i = 0; i < 256; i++) v[i] = fp8_detail::decodeE4M3((uchar)i, 7, false); } } t; return t.v; }
};
struct fp8a_t // E4M3FNUZ: bias 8, no inf, single NaN, no -0, max 240
{
fp8a_t() : b(0) {}
explicit fp8a_t(float x) : b(fp8_detail::encodeE4M3(x, 8, true)) {}
operator float() const { return table()[b]; }
static const float* decodeLUT() { return table(); }
protected:
uchar b;
private:
static const float* table()
{ static const struct T { float v[256]; T() { for (int i = 0; i < 256; i++) v[i] = fp8_detail::decodeE4M3((uchar)i, 8, true); } } t; return t.v; }
};
}
#endif
/** @brief Constructs the 'fourcc' code, used in video codecs and many other places.
Simply call it with 4 chars like `CV_FOURCC('I', 'Y', 'U', 'V')`
*/
CV_INLINE int CV_FOURCC(char c1, char c2, char c3, char c4)
inline int CV_FOURCC(char c1, char c2, char c3, char c4)
{
return (c1 & 255) + ((c2 & 255) << 8) + ((c3 & 255) << 16) + ((c4 & 255) << 24);
}
+8 -8
View File
@@ -63,10 +63,10 @@
namespace cv
{
static inline uchar abs(uchar a) { return a; }
static inline ushort abs(ushort a) { return a; }
static inline unsigned abs(unsigned a) { return a; }
static inline uint64 abs(uint64 a) { return a; }
inline uchar abs(uchar a) { return a; }
inline ushort abs(ushort a) { return a; }
inline unsigned abs(unsigned a) { return a; }
inline uint64 abs(uint64 a) { return a; }
using std::min;
using std::max;
@@ -155,26 +155,26 @@ typedef std::string String;
//! @cond IGNORED
namespace details {
// std::tolower is int->int
static inline char char_tolower(char ch)
inline char char_tolower(char ch)
{
return (char)std::tolower((int)ch);
}
// std::toupper is int->int
static inline char char_toupper(char ch)
inline char char_toupper(char ch)
{
return (char)std::toupper((int)ch);
}
} // namespace details
//! @endcond
static inline std::string toLowerCase(const std::string& str)
inline std::string toLowerCase(const std::string& str)
{
std::string result(str);
std::transform(result.begin(), result.end(), result.begin(), details::char_tolower);
return result;
}
static inline std::string toUpperCase(const std::string& str)
inline std::string toUpperCase(const std::string& str)
{
std::string result(str);
std::transform(result.begin(), result.end(), result.begin(), details::char_toupper);
+14 -14
View File
@@ -74,7 +74,7 @@ public:
typedef Vec<channel_type, channels> vec_type;
};
static inline
inline
std::ostream& operator << (std::ostream& out, Ptr<Formatted> fmtd)
{
fmtd->reset();
@@ -83,59 +83,59 @@ std::ostream& operator << (std::ostream& out, Ptr<Formatted> fmtd)
return out;
}
static inline
inline
std::ostream& operator << (std::ostream& out, const Mat& mtx)
{
return out << Formatter::get()->format(mtx);
}
static inline
inline
std::ostream& operator << (std::ostream& out, const UMat& m)
{
return out << m.getMat(ACCESS_READ);
}
template<typename _Tp> static inline
template<typename _Tp> inline
std::ostream& operator << (std::ostream& out, const Complex<_Tp>& c)
{
return out << "(" << c.re << "," << c.im << ")";
}
template<typename _Tp> static inline
template<typename _Tp> inline
std::ostream& operator << (std::ostream& out, const std::vector<Point_<_Tp> >& vec)
{
return out << Formatter::get()->format(Mat(vec));
}
template<typename _Tp> static inline
template<typename _Tp> inline
std::ostream& operator << (std::ostream& out, const std::vector<Point3_<_Tp> >& vec)
{
return out << Formatter::get()->format(Mat(vec));
}
template<typename _Tp, int m, int n> static inline
template<typename _Tp, int m, int n> inline
std::ostream& operator << (std::ostream& out, const Matx<_Tp, m, n>& matx)
{
return out << Formatter::get()->format(Mat(matx));
}
template<typename _Tp> static inline
template<typename _Tp> inline
std::ostream& operator << (std::ostream& out, const Point_<_Tp>& p)
{
out << "[" << p.x << ", " << p.y << "]";
return out;
}
template<typename _Tp> static inline
template<typename _Tp> inline
std::ostream& operator << (std::ostream& out, const Point3_<_Tp>& p)
{
out << "[" << p.x << ", " << p.y << ", " << p.z << "]";
return out;
}
template<typename _Tp, int n> static inline
template<typename _Tp, int n> inline
std::ostream& operator << (std::ostream& out, const Vec<_Tp, n>& vec)
{
out << "[";
@@ -157,19 +157,19 @@ std::ostream& operator << (std::ostream& out, const Vec<_Tp, n>& vec)
return out;
}
template<typename _Tp> static inline
template<typename _Tp> inline
std::ostream& operator << (std::ostream& out, const Size_<_Tp>& size)
{
return out << "[" << size.width << " x " << size.height << "]";
}
template<typename _Tp> static inline
template<typename _Tp> inline
std::ostream& operator << (std::ostream& out, const Rect_<_Tp>& rect)
{
return out << "[" << rect.width << " x " << rect.height << " from (" << rect.x << ", " << rect.y << ")]";
}
static inline std::ostream& operator << (std::ostream& strm, const MatShape& shape)
inline std::ostream& operator << (std::ostream& strm, const MatShape& shape)
{
strm << '[';
if (shape.empty()) {
@@ -187,7 +187,7 @@ static inline std::ostream& operator << (std::ostream& strm, const MatShape& sha
return strm;
}
static inline std::ostream &operator<< (std::ostream &s, cv::Range &r)
inline std::ostream &operator<< (std::ostream &s, cv::Range &r)
{
return s << "[" << r.start << " : " << r.end << ")";
}
@@ -22,7 +22,7 @@ using std::nullptr_t;
template <typename _Tp> using Ptr = std::shared_ptr<_Tp>; // In ideal world it should look like this, but we need some compatibility workarounds below
template<typename _Tp, typename ... A1> static inline
template<typename _Tp, typename ... A1> inline
Ptr<_Tp> makePtr(const A1&... a1) { return std::make_shared<_Tp>(a1...); }
#else // cv::Ptr with compatibility workarounds
@@ -134,7 +134,7 @@ struct Ptr : public std::shared_ptr<T>
Ptr<Y> dynamicCast() const CV_NOEXCEPT { return std::dynamic_pointer_cast<Y>(*this); }
};
template<typename _Tp, typename ... A1> static inline
template<typename _Tp, typename ... A1> inline
Ptr<_Tp> makePtr(const A1&... a1)
{
static_assert( !has_custom_delete<_Tp>::value, "Can't use this makePtr with custom DefaultDeleter");
@@ -11,7 +11,7 @@ namespace cv {
namespace detail {
template<template<typename> class Functor, typename... Args>
static inline void depthDispatch(const int depth, Args&&... args)
inline void depthDispatch(const int depth, Args&&... args)
{
switch (depth)
{
+15 -15
View File
@@ -97,7 +97,7 @@ Mat a_mat;
eigen2cv(a_tensor, a_mat);
\endcode
*/
template <typename _Tp, int _layout> static inline
template <typename _Tp, int _layout> inline
void eigen2cv( const Eigen::Tensor<_Tp, 3, _layout> &src, OutputArray dst )
{
if( !(_layout & Eigen::RowMajorBit) )
@@ -129,7 +129,7 @@ Eigen::Tensor<float, 3, Eigen::RowMajor> a_tensor(...);
cv2eigen(a_mat, a_tensor);
\endcode
*/
template <typename _Tp, int _layout> static inline
template <typename _Tp, int _layout> inline
void cv2eigen( const Mat &src, Eigen::Tensor<_Tp, 3, _layout> &dst )
{
if( !(_layout & Eigen::RowMajorBit) )
@@ -173,7 +173,7 @@ Mat a_mat(2, 2, CV_32FC3, arr);
Eigen::TensorMap<Eigen::Tensor<float, 3, Eigen::RowMajor>> a_tensormap = cv2eigen_tensormap<float>(a_mat);
\endcode
*/
template <typename _Tp> static inline
template <typename _Tp> inline
Eigen::TensorMap<Eigen::Tensor<_Tp, 3, Eigen::RowMajor>> cv2eigen_tensormap(InputArray src)
{
Mat mat = src.getMat();
@@ -182,7 +182,7 @@ Eigen::TensorMap<Eigen::Tensor<_Tp, 3, Eigen::RowMajor>> cv2eigen_tensormap(Inpu
}
#endif // OPENCV_EIGEN_TENSOR_SUPPORT
template<typename _Tp, int _rows, int _cols, int _options, int _maxRows, int _maxCols> static inline
template<typename _Tp, int _rows, int _cols, int _options, int _maxRows, int _maxCols> inline
void eigen2cv( const Eigen::Matrix<_Tp, _rows, _cols, _options, _maxRows, _maxCols>& src, OutputArray dst )
{
if( !(src.Flags & Eigen::RowMajorBit) )
@@ -200,7 +200,7 @@ void eigen2cv( const Eigen::Matrix<_Tp, _rows, _cols, _options, _maxRows, _maxCo
}
// Matx case
template<typename _Tp, int _rows, int _cols, int _options, int _maxRows, int _maxCols> static inline
template<typename _Tp, int _rows, int _cols, int _options, int _maxRows, int _maxCols> inline
void eigen2cv( const Eigen::Matrix<_Tp, _rows, _cols, _options, _maxRows, _maxCols>& src,
Matx<_Tp, _rows, _cols>& dst )
{
@@ -214,7 +214,7 @@ void eigen2cv( const Eigen::Matrix<_Tp, _rows, _cols, _options, _maxRows, _maxCo
}
}
template<typename _Tp, int _rows, int _cols, int _options, int _maxRows, int _maxCols> static inline
template<typename _Tp, int _rows, int _cols, int _options, int _maxRows, int _maxCols> inline
void cv2eigen( const Mat& src,
Eigen::Matrix<_Tp, _rows, _cols, _options, _maxRows, _maxCols>& dst )
{
@@ -242,7 +242,7 @@ void cv2eigen( const Mat& src,
}
// Matx case
template<typename _Tp, int _rows, int _cols, int _options, int _maxRows, int _maxCols> static inline
template<typename _Tp, int _rows, int _cols, int _options, int _maxRows, int _maxCols> inline
void cv2eigen( const Matx<_Tp, _rows, _cols>& src,
Eigen::Matrix<_Tp, _rows, _cols, _options, _maxRows, _maxCols>& dst )
{
@@ -260,7 +260,7 @@ void cv2eigen( const Matx<_Tp, _rows, _cols>& src,
}
}
template<typename _Tp> static inline
template<typename _Tp> inline
void cv2eigen( const Mat& src,
Eigen::Matrix<_Tp, Eigen::Dynamic, Eigen::Dynamic>& dst )
{
@@ -287,7 +287,7 @@ void cv2eigen( const Mat& src,
}
}
template<typename _Tp> static inline
template<typename _Tp> inline
void cv2eigen( const Mat& src,
Eigen::Matrix<_Tp, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>& dst )
{
@@ -299,7 +299,7 @@ void cv2eigen( const Mat& src,
}
// Matx case
template<typename _Tp, int _rows, int _cols> static inline
template<typename _Tp, int _rows, int _cols> inline
void cv2eigen( const Matx<_Tp, _rows, _cols>& src,
Eigen::Matrix<_Tp, Eigen::Dynamic, Eigen::Dynamic>& dst )
{
@@ -318,7 +318,7 @@ void cv2eigen( const Matx<_Tp, _rows, _cols>& src,
}
}
template<typename _Tp, int _rows, int _cols> static inline
template<typename _Tp, int _rows, int _cols> inline
void cv2eigen( const Matx<_Tp, _rows, _cols>& src,
Eigen::Matrix<_Tp, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>& dst )
{
@@ -329,7 +329,7 @@ void cv2eigen( const Matx<_Tp, _rows, _cols>& src,
Mat(src).copyTo(_dst);
}
template<typename _Tp> static inline
template<typename _Tp> inline
void cv2eigen( const Mat& src,
Eigen::Matrix<_Tp, Eigen::Dynamic, 1>& dst )
{
@@ -354,7 +354,7 @@ void cv2eigen( const Mat& src,
}
// Matx case
template<typename _Tp, int _rows> static inline
template<typename _Tp, int _rows> inline
void cv2eigen( const Matx<_Tp, _rows, 1>& src,
Eigen::Matrix<_Tp, Eigen::Dynamic, 1>& dst )
{
@@ -375,7 +375,7 @@ void cv2eigen( const Matx<_Tp, _rows, 1>& src,
}
template<typename _Tp> static inline
template<typename _Tp> inline
void cv2eigen( const Mat& src,
Eigen::Matrix<_Tp, 1, Eigen::Dynamic>& dst )
{
@@ -399,7 +399,7 @@ void cv2eigen( const Mat& src,
}
//Matx
template<typename _Tp, int _cols> static inline
template<typename _Tp, int _cols> inline
void cv2eigen( const Matx<_Tp, 1, _cols>& src,
Eigen::Matrix<_Tp, 1, Eigen::Dynamic>& dst )
{

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