mirror of
https://github.com/opencv/opencv.git
synced 2026-07-21 19:33:03 +04:00
Compare commits
248 Commits
next
..
8d69623051
| Author | SHA1 | Date | |
|---|---|---|---|
| 8d69623051 | |||
| 279bc4a279 | |||
| f968fb969f | |||
| 5aa9c9562a | |||
| 47d9544d90 | |||
| c2bf59ca0c | |||
| 5aff519a06 | |||
| 430909e2a3 | |||
| f07e06fd47 | |||
| 3701817083 | |||
| c64d89e75c | |||
| 507c824cd8 | |||
| 34074075b2 | |||
| 18f9bffb0d | |||
| 8ee67ccb65 | |||
| 60c705e93b | |||
| 5c7b438295 | |||
| 647b32ac7c | |||
| 2d2611d5bd | |||
| 3803eb828e | |||
| a88eac3223 | |||
| 4c936e6c18 | |||
| 42f41f749a | |||
| 526e42f355 | |||
| d94b6d0df3 | |||
| bb90bb0d64 | |||
| eec2c6fd3d | |||
| 60825757e4 | |||
| 56ae1e603b | |||
| 0fb620f0f0 | |||
| 7f9f6acb83 | |||
| 4533beab85 | |||
| 8fdf15e26e | |||
| 498e4692d6 | |||
| 87af90b847 | |||
| 97c2c31d9a | |||
| d7e6e58652 | |||
| b21eae2b8b | |||
| b41dd0fdff | |||
| be4ef205b1 | |||
| 062a00bab3 | |||
| b022a9b321 | |||
| 7230c1ce08 | |||
| bc647fe3ec | |||
| cbd227741f | |||
| 818145f41d | |||
| 93cd614472 | |||
| 7559555599 | |||
| 7507accf2e | |||
| 4b38c5562b | |||
| aa1b8a2a21 | |||
| 8eb2c8998c | |||
| 112959651a | |||
| 738739d7c2 | |||
| c86d642d19 | |||
| e978c193cf | |||
| abb0115648 | |||
| b887e2fd3e | |||
| 71d900dc36 | |||
| 0eb3778a1b | |||
| 9ce06a5675 | |||
| 9121ebe28b | |||
| eafce2f0ce | |||
| d48bf69f65 | |||
| aa70c82ebe | |||
| c85397c3bb | |||
| 4ce2389a62 | |||
| 0488f6e942 | |||
| 35bf0b4ac6 | |||
| 51495995d2 | |||
| d1264cc6ec | |||
| 0b5ff7b3a2 | |||
| c344b7dcef | |||
| 0e6433327a | |||
| f7ad23157f | |||
| 77dff6dc75 | |||
| f6be96989e | |||
| e9289fc7c4 | |||
| 50beb24c6b | |||
| fcc3418cd7 | |||
| 3d0ae71ac1 | |||
| 80da12e55a | |||
| bb96382942 | |||
| 854cf4225d | |||
| 80fda1da8c | |||
| cddfd62a18 | |||
| a9573e5ac0 | |||
| 100496b371 | |||
| 24030ff3a2 | |||
| e770c269b0 | |||
| bbfe2eb0de | |||
| ae93a81ec0 | |||
| db37d38eb4 | |||
| 3aadaa8385 | |||
| c77286749a | |||
| d82fbe3530 | |||
| e6d0c0340b | |||
| 9460e30793 | |||
| 1439136ef8 | |||
| ee9c019556 | |||
| e39c6c6bcc | |||
| dcae1f1dc1 | |||
| b4164a4bc4 | |||
| 579a505734 | |||
| c7b8fb28b6 | |||
| 5866bbd9ff | |||
| 83ee55fcda | |||
| 28a1d0dddb | |||
| 80f48cc952 | |||
| e2f0876777 | |||
| 2f779c5c36 | |||
| 68fb8aeb30 | |||
| d36133b325 | |||
| 5b83b0c440 | |||
| a16c4a9fe2 | |||
| 97f62825a2 | |||
| 2a47e2c1a5 | |||
| 37f3c7539b | |||
| 3c10324630 | |||
| c831290769 | |||
| a015333f04 | |||
| 5dbfbcdcac | |||
| edd89cfca5 | |||
| 15b5d28325 | |||
| cb0b3d820b | |||
| 4d839a16f0 | |||
| dd541965e9 | |||
| da4a417b2a | |||
| 416e730a61 | |||
| 69d2303531 | |||
| b64a215e71 | |||
| 61bd5e7b55 | |||
| fe849ec08d | |||
| b864ee7335 | |||
| d4005e3cb1 | |||
| fbd4dc6ca0 | |||
| c0c9e5e76c | |||
| 8b06f28e1b | |||
| 8dcaac1ac4 | |||
| 5d121b768f | |||
| d6a187480f | |||
| 74a25df87e | |||
| 1aea68260a | |||
| ae3542afa4 | |||
| bb645944e2 | |||
| 01efb9f92b | |||
| c833519366 | |||
| 13eba3d842 | |||
| 84d900cfda | |||
| 2e80d30df4 | |||
| 5137676ea7 | |||
| 15367f7c52 | |||
| 577f395bee | |||
| 639fd5d07a | |||
| bc184b560e | |||
| af1e2232cd | |||
| d8f609c09f | |||
| 6678e48c12 | |||
| 2adeab8504 | |||
| 4eb3f8ecb6 | |||
| 4ceb58f9f5 | |||
| c02b096a77 | |||
| dceead809e | |||
| bb3c1558e0 | |||
| b8e82fa2c2 | |||
| df0c9d7950 | |||
| 81621ced10 | |||
| 5049343874 | |||
| 6fd1b687a1 | |||
| 48a063158a | |||
| 47eefa5abf | |||
| d939383524 | |||
| c3aecdd531 | |||
| 10f4847bf6 | |||
| 5814ff8f9c | |||
| 7334957476 | |||
| f831c94309 | |||
| fc746f35b9 | |||
| 6d2cd14bb2 | |||
| 4f17d30997 | |||
| bc11496dcb | |||
| 60122e655c | |||
| 550251b3c2 | |||
| de90d1aec4 | |||
| 70a2c50b43 | |||
| d0925fa360 | |||
| 72d21d7241 | |||
| 20d3018169 | |||
| bc18ec2ed3 | |||
| 853ee9b961 | |||
| c796023029 | |||
| 837f715eec | |||
| 4e56cf9ac8 | |||
| 414a0379d8 | |||
| 6eb0dc97f5 | |||
| 3def56d25a | |||
| 57081ac946 | |||
| 62bcd12f9c | |||
| 6a2d8d24da | |||
| d878b04c6b | |||
| 4208a89b3e | |||
| 41d48d6eeb | |||
| 0a4d6a849a | |||
| 017a5138d1 | |||
| afbfa275d8 | |||
| 6df9732c81 | |||
| 40d6727ea2 | |||
| 8e157165e8 | |||
| cfb3a8bbb7 | |||
| 194db6f5cb | |||
| 5aad95cb1d | |||
| 86433eac87 | |||
| 2906d4d73a | |||
| 96a71a8d83 | |||
| 19ec7ea28b | |||
| 12eaf9bd4c | |||
| cadcb7e4e0 | |||
| fa55ed2837 | |||
| 4dee742731 | |||
| a77474f3b1 | |||
| 590d8f6439 | |||
| aed41fdabe | |||
| f80d02e1c7 | |||
| ffa38e1b74 | |||
| 1780a86075 | |||
| 48b8099214 | |||
| 3d02d863f9 | |||
| 3d55d2fcef | |||
| d10138fa1c | |||
| 9780b3b329 | |||
| 63b59865d3 | |||
| b9a38df0e6 | |||
| fe61ae0e41 | |||
| 6f29af625b | |||
| 0397596e17 | |||
| 2a70226181 | |||
| 21356eed2b | |||
| 11b0bd15cf | |||
| c936dcaef7 | |||
| ab1aaa75aa | |||
| d9f89b028b | |||
| 517710fe56 | |||
| a99141acd7 | |||
| 1b047868dd | |||
| a7bb1d1e1e | |||
| aee03ffbb2 | |||
| d79d9d7a5b | |||
| b723ddd72b |
@@ -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
@@ -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)
|
||||
|
||||
Vendored
+4
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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)
|
||||
Vendored
+3
-2
@@ -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__)
|
||||
|
||||
Vendored
+13
-2
@@ -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
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
Vendored
+47
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
#
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
@@ -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
@@ -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.
|
||||
|
||||
+2
-2
@@ -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) {
|
||||
|
||||
+1
-1
@@ -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
|
||||
```
|
||||
|
||||
|
||||
@@ -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 ..."** — CMake cannot locate
|
||||
the install. Pass the path explicitly: `cmake -DOpenCV_DIR=/path/to/opencv/build/ ..`
|
||||
- **Compiler: "OpenCV 5.0 requires C++17"** — 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.
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
@@ -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}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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("::", "::")
|
||||
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("|", "|")
|
||||
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("::", "::")
|
||||
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("::", "::")
|
||||
_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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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}
|
||||
)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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__
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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)))
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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` |
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user