mirror of
https://github.com/opencv/opencv.git
synced 2026-07-21 19:33:03 +04:00
Merge pull request #29335 from amd:fast_norm_simd
core: SIMD optimizations for norm, distance and Hamming APIs. (Improves ORB & BRISK) #29335 Generic universal-intrinsic kernels (benefit all SIMD backends: NEON, AVX2, AVX-512, etc.), plus enabling wider dispatch for the norm module. - hal::normHamming: cached-pointer dispatch (resolve once, no per-call dispatch chain or trace region) + vector popcount path. cv::norm(NORM_HAMMING) and binary-descriptor matching (BFMatcher ORB/BRISK/FREAK via cv::batchDistance). - hal::normL2Sqr_ / normL1_: direct inlinable kernels with single-vector tail (cv::batchDistance / BFMatcher float L2/L1, cv::kmeans). - cv::norm masked NORM_INF: deinterleave SIMD for multichannel + back-step tail. - cv::norm(src1, src2, type, mask): SIMD masked norm-diff; INF is one templated kernel for all element types, plus uchar L1/L2 and int L1 kernels. - Enable AVX512_SKX/AVX512_ICL dispatch for the norm module. - features2d: add BFMatcher knnMatch perf tests (float L2/L1, binary Hamming). - ORB and BRISK performance improved ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [x] The feature is well documented and sample code can be built with the project CMake
This commit is contained in:
committed by
GitHub
parent
83ee55fcda
commit
579a505734
@@ -1,7 +1,7 @@
|
||||
set(the_description "The Core Functionality")
|
||||
|
||||
ocv_add_dispatched_file(mathfuncs_core SSE2 AVX AVX2 LASX)
|
||||
ocv_add_dispatched_file(stat SSE4_2 AVX2 LASX)
|
||||
ocv_add_dispatched_file(stat SSE4_2 AVX2 AVX512_SKX AVX512_ICL LASX)
|
||||
ocv_add_dispatched_file(arithm SSE2 SSE4_1 AVX2 VSX3 LASX)
|
||||
ocv_add_dispatched_file(convert SSE2 AVX2 VSX3 LASX)
|
||||
ocv_add_dispatched_file(convert_scale SSE2 AVX2 LASX)
|
||||
@@ -14,7 +14,7 @@ ocv_add_dispatched_file(minmax SSE2 SSE4_1 AVX2 AVX512_SKX AVX512_ICL LASX)
|
||||
ocv_add_dispatched_file(split SSE2 AVX2 LASX)
|
||||
ocv_add_dispatched_file(sum SSE2 AVX2 LASX)
|
||||
ocv_add_dispatched_file(reduce SSE2 SSSE3 AVX2 NEON_DOTPROD)
|
||||
ocv_add_dispatched_file(norm SSE2 SSE4_1 AVX AVX2 NEON_DOTPROD LASX)
|
||||
ocv_add_dispatched_file(norm SSE2 SSE4_1 AVX AVX2 AVX512_SKX AVX512_ICL NEON_DOTPROD LASX)
|
||||
ocv_add_dispatched_file(lut AVX512_ICL)
|
||||
|
||||
# dispatching for accuracy tests
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html
|
||||
//
|
||||
// Copyright (C) 2026, Advanced Micro Devices, Inc., all rights reserved.
|
||||
|
||||
|
||||
#include "precomp.hpp"
|
||||
@@ -155,20 +157,27 @@ float normL2Sqr_(const float* a, const float* b, int n)
|
||||
{
|
||||
int j = 0; float d = 0.f;
|
||||
#if (CV_SIMD || CV_SIMD_SCALABLE)
|
||||
const int vl = VTraits<v_float32>::vlanes();
|
||||
v_float32 v_d0 = vx_setzero_f32(), v_d1 = vx_setzero_f32();
|
||||
v_float32 v_d2 = vx_setzero_f32(), v_d3 = vx_setzero_f32();
|
||||
for (; j <= n - 4 * VTraits<v_float32>::vlanes(); j += 4 * VTraits<v_float32>::vlanes())
|
||||
for (; j <= n - 4 * vl; j += 4 * vl)
|
||||
{
|
||||
v_float32 t0 = v_sub(vx_load(a + j), vx_load(b + j));
|
||||
v_float32 t1 = v_sub(vx_load(a + j + VTraits<v_float32>::vlanes()), vx_load(b + j + VTraits<v_float32>::vlanes()));
|
||||
v_float32 t1 = v_sub(vx_load(a + j + vl), vx_load(b + j + vl));
|
||||
v_d0 = v_muladd(t0, t0, v_d0);
|
||||
v_float32 t2 = v_sub(vx_load(a + j + 2 * VTraits<v_float32>::vlanes()), vx_load(b + j + 2 * VTraits<v_float32>::vlanes()));
|
||||
v_float32 t2 = v_sub(vx_load(a + j + 2 * vl), vx_load(b + j + 2 * vl));
|
||||
v_d1 = v_muladd(t1, t1, v_d1);
|
||||
v_float32 t3 = v_sub(vx_load(a + j + 3 * VTraits<v_float32>::vlanes()), vx_load(b + j + 3 * VTraits<v_float32>::vlanes()));
|
||||
v_float32 t3 = v_sub(vx_load(a + j + 3 * vl), vx_load(b + j + 3 * vl));
|
||||
v_d2 = v_muladd(t2, t2, v_d2);
|
||||
v_d3 = v_muladd(t3, t3, v_d3);
|
||||
}
|
||||
d = v_reduce_sum(v_add(v_add(v_add(v_d0, v_d1), v_d2), v_d3));
|
||||
v_d0 = v_add(v_add(v_d0, v_d1), v_add(v_d2, v_d3));
|
||||
for (; j <= n - vl; j += vl)
|
||||
{
|
||||
v_float32 t0 = v_sub(vx_load(a + j), vx_load(b + j));
|
||||
v_d0 = v_muladd(t0, t0, v_d0);
|
||||
}
|
||||
d = v_reduce_sum(v_d0);
|
||||
#endif
|
||||
for( ; j < n; j++ )
|
||||
{
|
||||
@@ -183,16 +192,20 @@ float normL1_(const float* a, const float* b, int n)
|
||||
{
|
||||
int j = 0; float d = 0.f;
|
||||
#if (CV_SIMD || CV_SIMD_SCALABLE)
|
||||
const int vl = VTraits<v_float32>::vlanes();
|
||||
v_float32 v_d0 = vx_setzero_f32(), v_d1 = vx_setzero_f32();
|
||||
v_float32 v_d2 = vx_setzero_f32(), v_d3 = vx_setzero_f32();
|
||||
for (; j <= n - 4 * VTraits<v_float32>::vlanes(); j += 4 * VTraits<v_float32>::vlanes())
|
||||
for (; j <= n - 4 * vl; j += 4 * vl)
|
||||
{
|
||||
v_d0 = v_add(v_d0, v_absdiff(vx_load(a + j), vx_load(b + j)));
|
||||
v_d1 = v_add(v_d1, v_absdiff(vx_load(a + j + VTraits<v_float32>::vlanes()), vx_load(b + j + VTraits<v_float32>::vlanes())));
|
||||
v_d2 = v_add(v_d2, v_absdiff(vx_load(a + j + 2 * VTraits<v_float32>::vlanes()), vx_load(b + j + 2 * VTraits<v_float32>::vlanes())));
|
||||
v_d3 = v_add(v_d3, v_absdiff(vx_load(a + j + 3 * VTraits<v_float32>::vlanes()), vx_load(b + j + 3 * VTraits<v_float32>::vlanes())));
|
||||
v_d1 = v_add(v_d1, v_absdiff(vx_load(a + j + vl), vx_load(b + j + vl)));
|
||||
v_d2 = v_add(v_d2, v_absdiff(vx_load(a + j + 2 * vl), vx_load(b + j + 2 * vl)));
|
||||
v_d3 = v_add(v_d3, v_absdiff(vx_load(a + j + 3 * vl), vx_load(b + j + 3 * vl)));
|
||||
}
|
||||
d = v_reduce_sum(v_add(v_add(v_add(v_d0, v_d1), v_d2), v_d3));
|
||||
v_d0 = v_add(v_add(v_d0, v_d1), v_add(v_d2, v_d3));
|
||||
for (; j <= n - vl; j += vl)
|
||||
v_d0 = v_add(v_d0, v_absdiff(vx_load(a + j), vx_load(b + j)));
|
||||
d = v_reduce_sum(v_d0);
|
||||
#endif
|
||||
for( ; j < n; j++ )
|
||||
d += std::abs(a[j] - b[j]);
|
||||
@@ -203,11 +216,14 @@ int normL1_(const uchar* a, const uchar* b, int n)
|
||||
{
|
||||
int j = 0, d = 0;
|
||||
#if (CV_SIMD || CV_SIMD_SCALABLE)
|
||||
for (; j <= n - 4 * VTraits<v_uint8>::vlanes(); j += 4 * VTraits<v_uint8>::vlanes())
|
||||
const int vl = VTraits<v_uint8>::vlanes();
|
||||
for (; j <= n - 4 * vl; j += 4 * vl)
|
||||
d += v_reduce_sad(vx_load(a + j), vx_load(b + j)) +
|
||||
v_reduce_sad(vx_load(a + j + VTraits<v_uint8>::vlanes()), vx_load(b + j + VTraits<v_uint8>::vlanes())) +
|
||||
v_reduce_sad(vx_load(a + j + 2 * VTraits<v_uint8>::vlanes()), vx_load(b + j + 2 * VTraits<v_uint8>::vlanes())) +
|
||||
v_reduce_sad(vx_load(a + j + 3 * VTraits<v_uint8>::vlanes()), vx_load(b + j + 3 * VTraits<v_uint8>::vlanes()));
|
||||
v_reduce_sad(vx_load(a + j + vl), vx_load(b + j + vl)) +
|
||||
v_reduce_sad(vx_load(a + j + 2 * vl), vx_load(b + j + 2 * vl)) +
|
||||
v_reduce_sad(vx_load(a + j + 3 * vl), vx_load(b + j + 3 * vl));
|
||||
for (; j <= n - vl; j += vl)
|
||||
d += v_reduce_sad(vx_load(a + j), vx_load(b + j));
|
||||
#endif
|
||||
for( ; j < n; j++ )
|
||||
d += std::abs(a[j] - b[j]);
|
||||
|
||||
+481
-48
@@ -3,6 +3,7 @@
|
||||
// of this distribution and at http://opencv.org/license.html
|
||||
//
|
||||
// Copyright (C) 2025, SpaceMIT Inc., all rights reserved.
|
||||
// Copyright (C) 2026, Advanced Micro Devices, Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
|
||||
#include "precomp.hpp"
|
||||
@@ -1462,21 +1463,39 @@ struct MaskedNormInf_SIMD<uchar, int> {
|
||||
result = std::max(result, (int)src[i]);
|
||||
}
|
||||
}
|
||||
else if (cn == 4 && len >= VTraits<v_uint8>::vlanes()) {
|
||||
const int vstep = VTraits<v_uint8>::vlanes();
|
||||
v_uint8 acc = vx_setzero_u8();
|
||||
int i = 0;
|
||||
for (;;) {
|
||||
if (i > len - vstep)
|
||||
i = len - vstep;
|
||||
v_uint8 c0, c1, c2, c3;
|
||||
v_load_deinterleave(src + i * 4, c0, c1, c2, c3);
|
||||
v_uint8 pmax = v_max(v_max(c0, c1), v_max(c2, c3));
|
||||
v_uint8 m = v_gt(vx_load(mask + i), vx_setzero_u8());
|
||||
acc = v_max(acc, v_and(pmax, m));
|
||||
if (i >= len - vstep)
|
||||
break;
|
||||
i += vstep;
|
||||
}
|
||||
result = (int)v_reduce_max(acc);
|
||||
}
|
||||
else if (cn == 4) {
|
||||
// len < one vector: pure scalar
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (mask[i]) {
|
||||
const uchar* elem = src + i * 4;
|
||||
result = std::max(result, (int)std::max(std::max(elem[0], elem[1]),
|
||||
std::max(elem[2], elem[3])));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (mask[i]) {
|
||||
const uchar* elem = src + i * cn;
|
||||
int k = 0;
|
||||
const int vstep = VTraits<v_uint8>::vlanes();
|
||||
v_uint8 acc = vx_setzero_u8();
|
||||
|
||||
for (; k <= cn - vstep; k += vstep) {
|
||||
acc = v_max(acc, vx_load(elem + k));
|
||||
}
|
||||
|
||||
result = std::max(result, (int)v_reduce_max(acc));
|
||||
|
||||
for (; k < cn; k++)
|
||||
for (int k = 0; k < cn; k++)
|
||||
result = std::max(result, (int)elem[k]);
|
||||
}
|
||||
}
|
||||
@@ -1508,22 +1527,30 @@ struct MaskedNormL1_SIMD<uchar, int> {
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (mask[i]) {
|
||||
const uchar* elem = src + i * cn;
|
||||
int k = 0;
|
||||
const int vstep = VTraits<v_uint8>::vlanes() / 4;
|
||||
v_uint32 acc = vx_setzero_u32();
|
||||
|
||||
for (; k <= cn - vstep; k += vstep) {
|
||||
v_uint32 s = vx_load_expand_q(elem + k);
|
||||
acc = v_add(acc, s);
|
||||
const int vstep = VTraits<v_uint8>::vlanes() / 4;
|
||||
if (cn >= vstep) {
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (mask[i]) {
|
||||
const uchar* elem = src + i * cn;
|
||||
int k = 0;
|
||||
v_uint32 acc = vx_setzero_u32();
|
||||
for (; k <= cn - vstep; k += vstep) {
|
||||
v_uint32 s = vx_load_expand_q(elem + k);
|
||||
acc = v_add(acc, s);
|
||||
}
|
||||
result += (int)v_reduce_sum(acc);
|
||||
for (; k < cn; k++)
|
||||
result += elem[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (mask[i]) {
|
||||
const uchar* elem = src + i * cn;
|
||||
for (int k = 0; k < cn; k++)
|
||||
result += elem[k];
|
||||
}
|
||||
|
||||
result += (int)v_reduce_sum(acc);
|
||||
|
||||
for (; k < cn; k++)
|
||||
result += elem[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1738,6 +1765,428 @@ normL2_(const T* src, const uchar* mask, ST* _result, int len, int cn) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if (CV_SIMD || CV_SIMD_SCALABLE)
|
||||
inline v_uint8 v_diffmask(const v_uint8& d, const uchar* m) { return v_and(d, v_gt(vx_load(m), vx_setzero_u8())); }
|
||||
inline v_uint16 v_diffmask(const v_uint16& d, const uchar* m) { return v_and(d, v_gt(vx_load_expand(m), vx_setzero_u16())); }
|
||||
inline v_uint32 v_diffmask(const v_uint32& d, const uchar* m) { return v_and(d, v_gt(vx_load_expand_q(m), vx_setzero_u32())); }
|
||||
inline v_float32 v_diffmask(const v_float32& d, const uchar* m) {
|
||||
v_uint32 cm = v_gt(vx_load_expand_q(m), vx_setzero_u32());
|
||||
return v_reinterpret_as_f32(v_and(v_reinterpret_as_u32(d), cm));
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename T, typename ST>
|
||||
struct MaskedNormDiffInf_SIMD {
|
||||
inline ST operator()(const T* s1, const T* s2, const uchar* mask, int len, int cn) const {
|
||||
ST result = 0;
|
||||
#if (CV_SIMD || CV_SIMD_SCALABLE)
|
||||
const int vstep = VTraits<decltype(vx_load(s1))>::vlanes();
|
||||
if (cn == 1 && len >= vstep) {
|
||||
auto acc = v_diffmask(v_absdiff(vx_load(s1), vx_load(s2)), mask);
|
||||
int i = vstep;
|
||||
for (; i <= len - vstep; i += vstep)
|
||||
acc = v_max(acc, v_diffmask(v_absdiff(vx_load(s1 + i), vx_load(s2 + i)), mask + i));
|
||||
result = (ST)v_reduce_max(acc);
|
||||
for (; i < len; i++) if (mask[i]) result = std::max(result, (ST)std::abs((double)s1[i] - (double)s2[i]));
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
for (int i = 0; i < len; i++) if (mask[i]) {
|
||||
const T* e1 = s1 + i*cn; const T* e2 = s2 + i*cn;
|
||||
for (int k = 0; k < cn; k++) result = std::max(result, (ST)std::abs((double)e1[k] - (double)e2[k]));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
template <typename T, typename ST>
|
||||
struct MaskedNormDiffL1_SIMD {
|
||||
inline ST operator()(const T* s1, const T* s2, const uchar* mask, int len, int cn) const {
|
||||
ST s = 0;
|
||||
for (int i = 0; i < len; i++) if (mask[i]) {
|
||||
const T* e1 = s1 + i*cn; const T* e2 = s2 + i*cn;
|
||||
for (int k = 0; k < cn; k++) s += std::abs(e1[k] - e2[k]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
};
|
||||
template <typename T, typename ST>
|
||||
struct MaskedNormDiffL2_SIMD {
|
||||
inline ST operator()(const T* s1, const T* s2, const uchar* mask, int len, int cn) const {
|
||||
ST s = 0;
|
||||
for (int i = 0; i < len; i++) if (mask[i]) {
|
||||
const T* e1 = s1 + i*cn; const T* e2 = s2 + i*cn;
|
||||
for (int k = 0; k < cn; k++) { ST v = (ST)e1[k] - (ST)e2[k]; s += v*v; }
|
||||
}
|
||||
return s;
|
||||
}
|
||||
};
|
||||
|
||||
#if (CV_SIMD || CV_SIMD_SCALABLE)
|
||||
// Shared scalar cn>1 fallback for masked norm-diff (RT = accumulator type).
|
||||
// Used by every SIMD specialization's multi-channel branch.
|
||||
template<typename T, typename RT>
|
||||
static inline RT maskedNormDiffL1Tail(const T* s1, const T* s2, const uchar* mask, int len, int cn, RT result) {
|
||||
for (int i = 0; i < len; i++) if (mask[i]) {
|
||||
const T* e1 = s1 + i*cn; const T* e2 = s2 + i*cn;
|
||||
for (int k = 0; k < cn; k++) result += (RT)std::abs((RT)e1[k] - (RT)e2[k]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
template<typename T, typename RT>
|
||||
static inline RT maskedNormDiffL2Tail(const T* s1, const T* s2, const uchar* mask, int len, int cn, RT result) {
|
||||
for (int i = 0; i < len; i++) if (mask[i]) {
|
||||
const T* e1 = s1 + i*cn; const T* e2 = s2 + i*cn;
|
||||
for (int k = 0; k < cn; k++) { RT v = (RT)e1[k] - (RT)e2[k]; result += v*v; }
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Shared 8-bit cn==1 masked kernels (uchar/schar): v_absdiff() yields v_uint8
|
||||
// for both, so a single typename-T body serves both depths.
|
||||
template<typename T>
|
||||
static inline int maskedNormDiffL1_8(const T* s1, const T* s2, const uchar* mask, int len) {
|
||||
int i = 0; const int vstep = VTraits<v_uint8>::vlanes();
|
||||
const v_uint8 one = vx_setall_u8(1);
|
||||
v_uint32 acc = vx_setzero_u32();
|
||||
for (; i <= len - vstep; i += vstep) {
|
||||
v_uint8 m = v_gt(vx_load(mask + i), vx_setzero_u8());
|
||||
v_uint8 ad = v_and(v_absdiff(vx_load(s1 + i), vx_load(s2 + i)), m);
|
||||
acc = v_dotprod_expand_fast(ad, one, acc); // sum of masked |diff|
|
||||
}
|
||||
int result = (int)v_reduce_sum(acc);
|
||||
for (; i < len; i++) if (mask[i]) result += std::abs((int)s1[i] - (int)s2[i]);
|
||||
return result;
|
||||
}
|
||||
template<typename T>
|
||||
static inline int maskedNormDiffL2_8(const T* s1, const T* s2, const uchar* mask, int len) {
|
||||
int i = 0; const int vstep = VTraits<v_uint8>::vlanes();
|
||||
v_uint32 acc = vx_setzero_u32();
|
||||
for (; i <= len - vstep; i += vstep) {
|
||||
v_uint8 m = v_gt(vx_load(mask + i), vx_setzero_u8());
|
||||
v_uint8 ad = v_and(v_absdiff(vx_load(s1 + i), vx_load(s2 + i)), m);
|
||||
acc = v_dotprod_expand_fast(ad, ad, acc); // sum of masked diff^2
|
||||
}
|
||||
int result = (int)v_reduce_sum(acc);
|
||||
for (; i < len; i++) if (mask[i]) { int v = (int)s1[i] - (int)s2[i]; result += v*v; }
|
||||
return result;
|
||||
}
|
||||
|
||||
template<>
|
||||
struct MaskedNormDiffInf_SIMD<uchar, int> {
|
||||
int operator()(const uchar* s1, const uchar* s2, const uchar* mask, int len, int cn) const {
|
||||
int result = 0;
|
||||
const int vstep = VTraits<v_uint8>::vlanes();
|
||||
if (cn == 1) {
|
||||
int i = 0;
|
||||
v_uint8 acc = vx_setzero_u8();
|
||||
for (; i <= len - vstep; i += vstep) {
|
||||
v_uint8 ad = v_absdiff(vx_load(s1 + i), vx_load(s2 + i));
|
||||
v_uint8 m = v_gt(vx_load(mask + i), vx_setzero_u8());
|
||||
acc = v_max(acc, v_and(ad, m));
|
||||
}
|
||||
result = (int)v_reduce_max(acc);
|
||||
for (; i < len; i++) if (mask[i]) result = std::max(result, std::abs((int)s1[i] - (int)s2[i]));
|
||||
}
|
||||
else if (cn == 4 && len >= vstep) {
|
||||
v_uint8 acc = vx_setzero_u8();
|
||||
int i = 0;
|
||||
for (;;) {
|
||||
if (i > len - vstep) i = len - vstep; // back-step (max is idempotent)
|
||||
v_uint8 a0,a1,a2,a3,b0,b1,b2,b3;
|
||||
v_load_deinterleave(s1 + i*4, a0,a1,a2,a3);
|
||||
v_load_deinterleave(s2 + i*4, b0,b1,b2,b3);
|
||||
v_uint8 ad = v_max(v_max(v_absdiff(a0,b0), v_absdiff(a1,b1)),
|
||||
v_max(v_absdiff(a2,b2), v_absdiff(a3,b3)));
|
||||
v_uint8 m = v_gt(vx_load(mask + i), vx_setzero_u8());
|
||||
acc = v_max(acc, v_and(ad, m));
|
||||
if (i >= len - vstep) break;
|
||||
i += vstep;
|
||||
}
|
||||
result = (int)v_reduce_max(acc);
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < len; i++) if (mask[i]) {
|
||||
const uchar* e1 = s1 + i*cn; const uchar* e2 = s2 + i*cn;
|
||||
for (int k = 0; k < cn; k++) result = std::max(result, std::abs((int)e1[k] - (int)e2[k]));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct MaskedNormDiffInf_SIMD<int, int> {
|
||||
int operator()(const int* s1, const int* s2, const uchar* mask, int len, int cn) const {
|
||||
int result = 0;
|
||||
const int vstep = VTraits<v_int32>::vlanes();
|
||||
if (cn == 1 && len >= vstep) {
|
||||
// Use wrapping int subtraction (v_abs(v_sub)) to match the non-masked
|
||||
// NormDiffInf_SIMD<int,int> kernel. v_absdiff would compute the true
|
||||
// unsigned |a-b| which can exceed INT_MAX and overflow on the cast to int.
|
||||
v_uint32 acc = vx_setzero_u32();
|
||||
int i = 0;
|
||||
for (; i <= len - vstep; i += vstep) {
|
||||
v_uint32 ad = v_abs(v_sub(vx_load(s1 + i), vx_load(s2 + i)));
|
||||
v_uint32 m = v_gt(vx_load_expand_q(mask + i), vx_setzero_u32());
|
||||
acc = v_max(acc, v_and(ad, m));
|
||||
}
|
||||
result = (int)v_reduce_max(acc);
|
||||
for (; i < len; i++) if (mask[i]) result = std::max(result, (int)std::abs(s1[i] - s2[i]));
|
||||
return result;
|
||||
}
|
||||
for (int i = 0; i < len; i++) if (mask[i]) {
|
||||
const int* e1 = s1 + i*cn; const int* e2 = s2 + i*cn;
|
||||
for (int k = 0; k < cn; k++) result = std::max(result, (int)std::abs(e1[k] - e2[k]));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct MaskedNormDiffL1_SIMD<uchar, int> {
|
||||
int operator()(const uchar* s1, const uchar* s2, const uchar* mask, int len, int cn) const {
|
||||
if (cn == 1) return maskedNormDiffL1_8(s1, s2, mask, len);
|
||||
if (cn == 4) {
|
||||
int result = 0;
|
||||
int i = 0;
|
||||
const int vstep = VTraits<v_uint8>::vlanes();
|
||||
const v_uint8 one = vx_setall_u8(1);
|
||||
v_uint32 acc = vx_setzero_u32();
|
||||
for (; i <= len - vstep; i += vstep) {
|
||||
v_uint8 a0,a1,a2,a3,b0,b1,b2,b3;
|
||||
v_load_deinterleave(s1 + i*4, a0,a1,a2,a3);
|
||||
v_load_deinterleave(s2 + i*4, b0,b1,b2,b3);
|
||||
v_uint8 m = v_gt(vx_load(mask + i), vx_setzero_u8());
|
||||
acc = v_dotprod_expand_fast(v_and(v_absdiff(a0,b0), m), one, acc);
|
||||
acc = v_dotprod_expand_fast(v_and(v_absdiff(a1,b1), m), one, acc);
|
||||
acc = v_dotprod_expand_fast(v_and(v_absdiff(a2,b2), m), one, acc);
|
||||
acc = v_dotprod_expand_fast(v_and(v_absdiff(a3,b3), m), one, acc);
|
||||
}
|
||||
result = (int)v_reduce_sum(acc);
|
||||
for (; i < len; i++) if (mask[i]) {
|
||||
const uchar* e1 = s1 + i*4; const uchar* e2 = s2 + i*4;
|
||||
for (int k = 0; k < 4; k++) result += std::abs((int)e1[k] - (int)e2[k]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return maskedNormDiffL1Tail<uchar, int>(s1, s2, mask, len, cn, 0);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct MaskedNormDiffL2_SIMD<uchar, int> {
|
||||
int operator()(const uchar* s1, const uchar* s2, const uchar* mask, int len, int cn) const {
|
||||
if (cn == 1) return maskedNormDiffL2_8(s1, s2, mask, len);
|
||||
if (cn == 4) {
|
||||
int result = 0;
|
||||
int i = 0;
|
||||
const int vstep = VTraits<v_uint8>::vlanes();
|
||||
v_uint32 acc = vx_setzero_u32();
|
||||
for (; i <= len - vstep; i += vstep) {
|
||||
v_uint8 a0,a1,a2,a3,b0,b1,b2,b3;
|
||||
v_load_deinterleave(s1 + i*4, a0,a1,a2,a3);
|
||||
v_load_deinterleave(s2 + i*4, b0,b1,b2,b3);
|
||||
v_uint8 m = v_gt(vx_load(mask + i), vx_setzero_u8());
|
||||
v_uint8 d0 = v_and(v_absdiff(a0,b0), m), d1 = v_and(v_absdiff(a1,b1), m);
|
||||
v_uint8 d2 = v_and(v_absdiff(a2,b2), m), d3 = v_and(v_absdiff(a3,b3), m);
|
||||
acc = v_dotprod_expand_fast(d0, d0, acc);
|
||||
acc = v_dotprod_expand_fast(d1, d1, acc);
|
||||
acc = v_dotprod_expand_fast(d2, d2, acc);
|
||||
acc = v_dotprod_expand_fast(d3, d3, acc);
|
||||
}
|
||||
result = (int)v_reduce_sum(acc);
|
||||
for (; i < len; i++) if (mask[i]) {
|
||||
const uchar* e1 = s1 + i*4; const uchar* e2 = s2 + i*4;
|
||||
for (int k = 0; k < 4; k++) { int v = (int)e1[k] - (int)e2[k]; result += v*v; }
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return maskedNormDiffL2Tail<uchar, int>(s1, s2, mask, len, cn, 0);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct MaskedNormDiffInf_SIMD<double, double> {
|
||||
inline double operator()(const double* s1, const double* s2, const uchar* mask, int len, int cn) const {
|
||||
double result = 0.0;
|
||||
for (int i = 0; i < len; i++) if (mask[i]) {
|
||||
const double* e1 = s1 + i*cn; const double* e2 = s2 + i*cn;
|
||||
for (int k = 0; k < cn; k++) result = std::max(result, std::abs(e1[k] - e2[k]));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
#if CV_SIMD_64F || CV_SIMD_SCALABLE_64F
|
||||
template<>
|
||||
struct MaskedNormDiffL1_SIMD<int, double> {
|
||||
double operator()(const int* s1, const int* s2, const uchar* mask, int len, int cn) const {
|
||||
double result = 0.0;
|
||||
if (cn == 1) {
|
||||
int i = 0;
|
||||
const int vstep = VTraits<v_int32>::vlanes();
|
||||
v_float64 acc = vx_setzero_f64();
|
||||
for (; i <= len - vstep; i += vstep) {
|
||||
v_uint32 m = v_gt(vx_load_expand_q(mask + i), vx_setzero_u32());
|
||||
v_uint32 ad = v_and(v_abs(v_sub(vx_load(s1 + i), vx_load(s2 + i))), m);
|
||||
v_int32 adi = v_reinterpret_as_s32(ad);
|
||||
acc = v_add(acc, v_cvt_f64(adi));
|
||||
acc = v_add(acc, v_cvt_f64_high(adi));
|
||||
}
|
||||
result = v_reduce_sum(acc);
|
||||
// Use wrapping int subtraction to match the SIMD body (v_abs(v_sub))
|
||||
// and the non-masked NormDiffL1_SIMD<int,double> kernel.
|
||||
for (; i < len; i++) if (mask[i]) result += std::abs(s1[i] - s2[i]);
|
||||
}
|
||||
else {
|
||||
result = maskedNormDiffL1Tail<int, double>(s1, s2, mask, len, cn, 0.0);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
// Masked L1/L2 SIMD for the remaining depths (cn==1). Without these the masked
|
||||
// norm-diff for 8s/16u/16s/32s/32f fell back to the scalar base template, which
|
||||
// the compiler autovectorizes poorly once AVX-512 dispatch is enabled.
|
||||
template<> struct MaskedNormDiffL1_SIMD<schar, int> {
|
||||
int operator()(const schar* s1, const schar* s2, const uchar* mask, int len, int cn) const {
|
||||
if (cn == 1) return maskedNormDiffL1_8(s1, s2, mask, len);
|
||||
return maskedNormDiffL1Tail<schar, int>(s1, s2, mask, len, cn, 0);
|
||||
}
|
||||
};
|
||||
|
||||
// Shared 16-bit masked L1 kernel (ushort/short): v_absdiff() yields v_uint16
|
||||
// for both, so a single typename-T body serves both depths.
|
||||
template<typename T>
|
||||
static inline int maskedNormDiffL1_16(const T* s1, const T* s2, const uchar* mask, int len, int cn) {
|
||||
int result = 0;
|
||||
if (cn == 1) {
|
||||
int i = 0; const int vstep = VTraits<v_uint16>::vlanes();
|
||||
v_uint32 acc = vx_setzero_u32();
|
||||
for (; i <= len - vstep; i += vstep) {
|
||||
v_uint16 m = v_gt(vx_load_expand(mask + i), vx_setzero_u16());
|
||||
v_uint16 ad = v_and(v_absdiff(vx_load(s1 + i), vx_load(s2 + i)), m);
|
||||
v_uint32 lo, hi; v_expand(ad, lo, hi);
|
||||
acc = v_add(acc, v_add(lo, hi));
|
||||
}
|
||||
result = (int)v_reduce_sum(acc);
|
||||
for (; i < len; i++) if (mask[i]) result += std::abs((int)s1[i] - (int)s2[i]);
|
||||
} else {
|
||||
result = maskedNormDiffL1Tail<T, int>(s1, s2, mask, len, cn, 0);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
template<> struct MaskedNormDiffL1_SIMD<ushort, int> {
|
||||
int operator()(const ushort* s1, const ushort* s2, const uchar* mask, int len, int cn) const
|
||||
{ return maskedNormDiffL1_16(s1, s2, mask, len, cn); } };
|
||||
template<> struct MaskedNormDiffL1_SIMD<short, int> {
|
||||
int operator()(const short* s1, const short* s2, const uchar* mask, int len, int cn) const
|
||||
{ return maskedNormDiffL1_16(s1, s2, mask, len, cn); } };
|
||||
|
||||
template<>
|
||||
struct MaskedNormDiffL1_SIMD<float, double> {
|
||||
double operator()(const float* s1, const float* s2, const uchar* mask, int len, int cn) const {
|
||||
double result = 0.0;
|
||||
if (cn == 1) {
|
||||
int i = 0; const int vstep = VTraits<v_float32>::vlanes();
|
||||
v_float64 acc0 = vx_setzero_f64(), acc1 = vx_setzero_f64();
|
||||
for (; i <= len - vstep; i += vstep) {
|
||||
v_float32 m = v_reinterpret_as_f32(v_gt(vx_load_expand_q(mask + i), vx_setzero_u32()));
|
||||
v_float32 ad = v_and(v_absdiff(vx_load(s1 + i), vx_load(s2 + i)), m);
|
||||
acc0 = v_add(acc0, v_cvt_f64(ad));
|
||||
acc1 = v_add(acc1, v_cvt_f64_high(ad));
|
||||
}
|
||||
result = v_reduce_sum(v_add(acc0, acc1));
|
||||
for (; i < len; i++) if (mask[i]) result += std::abs((double)s1[i] - (double)s2[i]);
|
||||
} else {
|
||||
result = maskedNormDiffL1Tail<float, double>(s1, s2, mask, len, cn, 0.0);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct MaskedNormDiffL2_SIMD<schar, int> {
|
||||
int operator()(const schar* s1, const schar* s2, const uchar* mask, int len, int cn) const {
|
||||
if (cn == 1) return maskedNormDiffL2_8(s1, s2, mask, len);
|
||||
return maskedNormDiffL2Tail<schar, int>(s1, s2, mask, len, cn, 0);
|
||||
}
|
||||
};
|
||||
|
||||
// Shared 16-bit masked L2 kernel (ushort/short): v_absdiff() yields v_uint16
|
||||
// for both, so a single typename-T body serves both depths.
|
||||
template<typename T>
|
||||
static inline double maskedNormDiffL2_16(const T* s1, const T* s2, const uchar* mask, int len, int cn) {
|
||||
double result = 0.0;
|
||||
if (cn == 1) {
|
||||
int i = 0; const int vstep = VTraits<v_uint16>::vlanes();
|
||||
v_float64 acc = vx_setzero_f64();
|
||||
for (; i <= len - vstep; i += vstep) {
|
||||
v_uint16 m = v_gt(vx_load_expand(mask + i), vx_setzero_u16());
|
||||
v_uint16 ad = v_and(v_absdiff(vx_load(s1 + i), vx_load(s2 + i)), m);
|
||||
v_uint64 u = v_dotprod_expand_fast(ad, ad);
|
||||
acc = v_add(acc, v_cvt_f64(v_reinterpret_as_s64(u)));
|
||||
}
|
||||
result = v_reduce_sum(acc);
|
||||
for (; i < len; i++) if (mask[i]) { double v = (double)s1[i] - (double)s2[i]; result += v*v; }
|
||||
} else {
|
||||
result = maskedNormDiffL2Tail<T, double>(s1, s2, mask, len, cn, 0.0);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
template<> struct MaskedNormDiffL2_SIMD<ushort, double> {
|
||||
double operator()(const ushort* s1, const ushort* s2, const uchar* mask, int len, int cn) const
|
||||
{ return maskedNormDiffL2_16(s1, s2, mask, len, cn); } };
|
||||
template<> struct MaskedNormDiffL2_SIMD<short, double> {
|
||||
double operator()(const short* s1, const short* s2, const uchar* mask, int len, int cn) const
|
||||
{ return maskedNormDiffL2_16(s1, s2, mask, len, cn); } };
|
||||
|
||||
template<>
|
||||
struct MaskedNormDiffL2_SIMD<int, double> {
|
||||
double operator()(const int* s1, const int* s2, const uchar* mask, int len, int cn) const {
|
||||
double result = 0.0;
|
||||
if (cn == 1) {
|
||||
int i = 0; const int vstep = VTraits<v_int32>::vlanes();
|
||||
v_float64 r0 = vx_setzero_f64(), r1 = vx_setzero_f64();
|
||||
for (; i <= len - vstep; i += vstep) {
|
||||
v_uint32 m = v_gt(vx_load_expand_q(mask + i), vx_setzero_u32());
|
||||
v_uint32 ad = v_and(v_absdiff(vx_load(s1 + i), vx_load(s2 + i)), m);
|
||||
v_uint64 e0, e1; v_expand(ad, e0, e1);
|
||||
v_float64 f0 = v_cvt_f64(v_reinterpret_as_s64(e0)), f1 = v_cvt_f64(v_reinterpret_as_s64(e1));
|
||||
r0 = v_fma(f0, f0, r0); r1 = v_fma(f1, f1, r1);
|
||||
}
|
||||
result = v_reduce_sum(v_add(r0, r1));
|
||||
for (; i < len; i++) if (mask[i]) { double v = (double)s1[i] - (double)s2[i]; result += v*v; }
|
||||
} else {
|
||||
result = maskedNormDiffL2Tail<int, double>(s1, s2, mask, len, cn, 0.0);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct MaskedNormDiffL2_SIMD<float, double> {
|
||||
double operator()(const float* s1, const float* s2, const uchar* mask, int len, int cn) const {
|
||||
double result = 0.0;
|
||||
if (cn == 1) {
|
||||
int i = 0; const int vstep = VTraits<v_float32>::vlanes();
|
||||
v_float64 r0 = vx_setzero_f64(), r1 = vx_setzero_f64();
|
||||
for (; i <= len - vstep; i += vstep) {
|
||||
v_float32 m = v_reinterpret_as_f32(v_gt(vx_load_expand_q(mask + i), vx_setzero_u32()));
|
||||
v_float32 ad = v_and(v_absdiff(vx_load(s1 + i), vx_load(s2 + i)), m);
|
||||
v_float64 f0 = v_cvt_f64(ad), f1 = v_cvt_f64_high(ad);
|
||||
r0 = v_fma(f0, f0, r0); r1 = v_fma(f1, f1, r1);
|
||||
}
|
||||
result = v_reduce_sum(v_add(r0, r1));
|
||||
for (; i < len; i++) if (mask[i]) { double v = (double)s1[i] - (double)s2[i]; result += v*v; }
|
||||
} else {
|
||||
result = maskedNormDiffL2Tail<float, double>(s1, s2, mask, len, cn, 0.0);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
#endif
|
||||
|
||||
template<typename T, typename ST> int
|
||||
normDiffInf_(const T* src1, const T* src2, const uchar* mask, ST* _result, int len, int cn) {
|
||||
ST result = *_result;
|
||||
@@ -1745,13 +2194,8 @@ normDiffInf_(const T* src1, const T* src2, const uchar* mask, ST* _result, int l
|
||||
NormDiffInf_SIMD<T, ST> op;
|
||||
result = std::max(result, op(src1, src2, len*cn));
|
||||
} else {
|
||||
for( int i = 0; i < len; i++, src1 += cn, src2 += cn ) {
|
||||
if( mask[i] ) {
|
||||
for( int k = 0; k < cn; k++ ) {
|
||||
result = std::max(result, (ST)std::abs(src1[k] - src2[k]));
|
||||
}
|
||||
}
|
||||
}
|
||||
MaskedNormDiffInf_SIMD<T, ST> op;
|
||||
result = std::max(result, op(src1, src2, mask, len, cn));
|
||||
}
|
||||
*_result = result;
|
||||
return 0;
|
||||
@@ -1765,13 +2209,8 @@ normDiffL1_(const T* src1, const T* src2, const uchar* mask, ST* _result, int le
|
||||
result += op(src1, src2, len*cn);
|
||||
}
|
||||
else {
|
||||
for( int i = 0; i < len; i++, src1 += cn, src2 += cn ) {
|
||||
if( mask[i] ) {
|
||||
for( int k = 0; k < cn; k++ ) {
|
||||
result += std::abs(src1[k] - src2[k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
MaskedNormDiffL1_SIMD<T, ST> op;
|
||||
result += op(src1, src2, mask, len, cn);
|
||||
}
|
||||
*_result = result;
|
||||
return 0;
|
||||
@@ -1784,14 +2223,8 @@ normDiffL2_(const T* src1, const T* src2, const uchar* mask, ST* _result, int le
|
||||
NormDiffL2_SIMD<T, ST> op;
|
||||
result += op(src1, src2, len*cn);
|
||||
} else {
|
||||
for( int i = 0; i < len; i++, src1 += cn, src2 += cn ) {
|
||||
if( mask[i] ) {
|
||||
for( int k = 0; k < cn; k++ ) {
|
||||
ST v = (ST)src1[k] - (ST)src2[k];
|
||||
result += v*v;
|
||||
}
|
||||
}
|
||||
}
|
||||
MaskedNormDiffL2_SIMD<T, ST> op;
|
||||
result += op(src1, src2, mask, len, cn);
|
||||
}
|
||||
*_result = result;
|
||||
return 0;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Copyright (C) 2026, Advanced Micro Devices, Inc., all rights reserved.
|
||||
|
||||
#include "precomp.hpp"
|
||||
|
||||
@@ -9,20 +11,26 @@
|
||||
|
||||
namespace cv { namespace hal {
|
||||
|
||||
// Resolve the SIMD implementation ONCE via a cached function pointer. normHamming
|
||||
// is called per-descriptor in hot matcher loops on short (32/64-byte) descriptors,
|
||||
// where a per-call dispatch chain + CV_INSTRUMENT_REGION dominate the cost.
|
||||
static NormHammingFunc getNormHammingFunc() {
|
||||
CV_CPU_DISPATCH(getNormHammingFunc, (), CV_CPU_DISPATCH_MODES_ALL);
|
||||
}
|
||||
static NormHammingDiffFunc getNormHammingDiffFunc() {
|
||||
CV_CPU_DISPATCH(getNormHammingDiffFunc, (), CV_CPU_DISPATCH_MODES_ALL);
|
||||
}
|
||||
|
||||
int normHamming(const uchar* a, int n)
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
CV_CPU_DISPATCH(normHamming, (a, n),
|
||||
CV_CPU_DISPATCH_MODES_ALL);
|
||||
static const NormHammingFunc fn = getNormHammingFunc();
|
||||
return fn(a, n);
|
||||
}
|
||||
|
||||
int normHamming(const uchar* a, const uchar* b, int n)
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
CV_CPU_DISPATCH(normHamming, (a, b, n),
|
||||
CV_CPU_DISPATCH_MODES_ALL);
|
||||
static const NormHammingDiffFunc fn = getNormHammingDiffFunc();
|
||||
return fn(a, b, n);
|
||||
}
|
||||
|
||||
}} //cv::hal
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Copyright (C) 2026, Advanced Micro Devices, Inc., all rights reserved.
|
||||
|
||||
#include "opencv2/core/hal/intrin.hpp"
|
||||
|
||||
@@ -8,12 +10,18 @@ namespace cv { namespace hal {
|
||||
|
||||
extern const uchar popCountTable[256];
|
||||
|
||||
typedef int (*NormHammingFunc)(const uchar*, int);
|
||||
typedef int (*NormHammingDiffFunc)(const uchar*, const uchar*, int);
|
||||
|
||||
CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN
|
||||
|
||||
// forward declarations
|
||||
int normHamming(const uchar* a, int n);
|
||||
int normHamming(const uchar* a, const uchar* b, int n);
|
||||
|
||||
NormHammingFunc getNormHammingFunc();
|
||||
NormHammingDiffFunc getNormHammingDiffFunc();
|
||||
|
||||
#ifndef CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY
|
||||
|
||||
#if CV_AVX2
|
||||
@@ -125,6 +133,17 @@ int normHamming(const uchar* a, const uchar* b, int n)
|
||||
return result;
|
||||
}
|
||||
|
||||
NormHammingFunc getNormHammingFunc()
|
||||
{
|
||||
NormHammingFunc f = &normHamming; // disambiguate the (a,n) overload
|
||||
return f;
|
||||
}
|
||||
NormHammingDiffFunc getNormHammingDiffFunc()
|
||||
{
|
||||
NormHammingDiffFunc f = &normHamming; // disambiguate the (a,b,n) overload
|
||||
return f;
|
||||
}
|
||||
|
||||
#endif // CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY
|
||||
|
||||
CV_CPU_OPTIMIZATION_NAMESPACE_END
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Copyright (C) 2026, Advanced Micro Devices, Inc., all rights reserved.
|
||||
|
||||
// Brute-force descriptor matching perf tests. These exercise the core distance
|
||||
// kernels (hal::normL2Sqr_ / normL1_ / normHamming via cv::batchDistance) that
|
||||
// back BFMatcher, which is how float descriptors (SIFT 128-d, SURF 64-d) and
|
||||
// binary descriptors (ORB 32-byte) are matched.
|
||||
|
||||
#include "perf_precomp.hpp"
|
||||
|
||||
namespace opencv_test
|
||||
{
|
||||
using namespace perf;
|
||||
|
||||
// (descriptor dimension, query/train descriptor count)
|
||||
typedef tuple<int, int> Dim_Count_t;
|
||||
typedef TestBaseWithParam<Dim_Count_t> DescriptorMatcherFixture;
|
||||
|
||||
// Float descriptors matched with L2 (SIFT=128, SURF=64) — uses hal::normL2Sqr_.
|
||||
PERF_TEST_P(DescriptorMatcherFixture, bfmatch_knn_L2_float,
|
||||
testing::Combine(
|
||||
testing::Values(64, 128), // SURF, SIFT descriptor sizes
|
||||
testing::Values(512, 1000)
|
||||
))
|
||||
{
|
||||
const int dim = get<0>(GetParam());
|
||||
const int count = get<1>(GetParam());
|
||||
|
||||
Mat query(count, dim, CV_32F);
|
||||
Mat train(count, dim, CV_32F);
|
||||
declare.in(query, train, WARMUP_RNG);
|
||||
declare.time(60);
|
||||
|
||||
BFMatcher matcher(NORM_L2, false);
|
||||
std::vector<std::vector<DMatch> > matches;
|
||||
|
||||
TEST_CYCLE() matcher.knnMatch(query, train, matches, 2);
|
||||
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
// Float descriptors matched with L1 — uses hal::normL1_.
|
||||
PERF_TEST_P(DescriptorMatcherFixture, bfmatch_knn_L1_float,
|
||||
testing::Combine(
|
||||
testing::Values(64, 128),
|
||||
testing::Values(512, 1000)
|
||||
))
|
||||
{
|
||||
const int dim = get<0>(GetParam());
|
||||
const int count = get<1>(GetParam());
|
||||
|
||||
Mat query(count, dim, CV_32F);
|
||||
Mat train(count, dim, CV_32F);
|
||||
declare.in(query, train, WARMUP_RNG);
|
||||
declare.time(60);
|
||||
|
||||
BFMatcher matcher(NORM_L1, false);
|
||||
std::vector<std::vector<DMatch> > matches;
|
||||
|
||||
TEST_CYCLE() matcher.knnMatch(query, train, matches, 2);
|
||||
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
// Binary descriptors matched with Hamming (ORB/BRISK=32 bytes) — uses hal::normHamming.
|
||||
PERF_TEST_P(DescriptorMatcherFixture, bfmatch_knn_Hamming_binary,
|
||||
testing::Combine(
|
||||
testing::Values(32, 64), // ORB (32), BRISK/FREAK (64) byte sizes
|
||||
testing::Values(512, 1000)
|
||||
))
|
||||
{
|
||||
const int bytes = get<0>(GetParam());
|
||||
const int count = get<1>(GetParam());
|
||||
|
||||
Mat query(count, bytes, CV_8U);
|
||||
Mat train(count, bytes, CV_8U);
|
||||
declare.in(query, train, WARMUP_RNG);
|
||||
declare.time(60);
|
||||
|
||||
BFMatcher matcher(NORM_HAMMING, false);
|
||||
std::vector<std::vector<DMatch> > matches;
|
||||
|
||||
TEST_CYCLE() matcher.knnMatch(query, train, matches, 2);
|
||||
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
Reference in New Issue
Block a user