Merge pull request #29393 from amd:fast_moments

imgproc: Optimized Moments & perf test added - #29393

- Added dispatch for moments (CV_8U, CV_16U, CV_32F and CV_64F) 
- CV_16S is kept in scalar due to regresssions observed.
- Perf test added for meanShift, CamShift and matchShapes.
- Extend Moments1 perf coverage to CV_8U alongside 16/32/64-bit depths.

### 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:
Madan mohan Manokar
2026-09-03 19:08:40 +05:30
committed by GitHub
parent e39c9a2411
commit a15bcc7ea8
7 changed files with 609 additions and 172 deletions

View File

@@ -15,6 +15,7 @@ ocv_add_dispatched_file(sumpixels SSE2 AVX2 AVX512_SKX)
ocv_add_dispatched_file(equalize_hist AVX512_ICL)
ocv_add_dispatched_file(imgwarp SSE4_1 AVX2 AVX512_SKX AVX512_ICL)
ocv_add_dispatched_file(pyramids_avx512_vbmi AVX512_ICL)
ocv_add_dispatched_file(moments SSE2 SSE4_1 AVX AVX2 AVX512_SKX AVX512_ICL)
ocv_define_module(imgproc opencv_core WRAP java objc python js)
if(OPENCV_CORE_EXCLUDE_C_API)

View File

@@ -0,0 +1,52 @@
// 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 "perf_precomp.hpp"
namespace opencv_test {
CV_ENUM(MatchShapeMethod, CONTOURS_MATCH_I1, CONTOURS_MATCH_I2, CONTOURS_MATCH_I3)
typedef perf::TestBaseWithParam< tuple<int, MatchShapeMethod> > MatchShapesFixture;
static void generateContour(int npoints, Mat& contour)
{
RNG& rng = theRNG();
contour.create(npoints, 1, CV_32SC2);
Point* pts = contour.ptr<Point>();
const Point center(rng.uniform(200, 400), rng.uniform(200, 400));
const int radius = rng.uniform(50, 150);
for (int i = 0; i < npoints; ++i)
{
const double angle = 2 * CV_PI * i / npoints;
pts[i].x = center.x + cvRound(radius * cos(angle));
pts[i].y = center.y + cvRound(radius * sin(angle));
}
}
PERF_TEST_P(MatchShapesFixture, matchShapes,
testing::Combine(
testing::Values(64, 256, 1024),
MatchShapeMethod::all()))
{
const int npoints = get<0>(GetParam());
const int method = get<1>(GetParam());
Mat contour1, contour2;
generateContour(npoints, contour1);
generateContour(npoints, contour2);
declare.in(contour1).in(contour2);
double dist = 0;
TEST_CYCLE()
{
dist = cv::matchShapes(contour1, contour2, method, 0);
}
SANITY_CHECK(dist, 1e-3, ERROR_RELATIVE);
}
} // namespace opencv_test

View File

@@ -15,7 +15,7 @@ typedef perf::TestBaseWithParam<MomentsParams_t> MomentsFixture_val;
PERF_TEST_P(MomentsFixture_val, Moments1,
::testing::Combine(
testing::Values(TYPICAL_MAT_SIZES),
testing::Values(CV_16U, CV_16S, CV_32F, CV_64F),
testing::Values(CV_8U, CV_16U, CV_16S, CV_32F, CV_64F),
testing::Bool()))
{
const MomentsParams_t params = GetParam();

View File

@@ -39,13 +39,35 @@
//
//M*/
// Copyright (C) 2026, Advanced Micro Devices, Inc., all rights reserved.
#include "precomp.hpp"
#include "opencl_kernels_imgproc.hpp"
#include "opencv2/core/hal/intrin.hpp"
namespace cv
{
typedef void (*MomentsInTileFunc)(const Mat& img, double* moments);
MomentsInTileFunc getMomentsInTileFunc(int depth);
template<typename T, typename WT, typename MT>
#if defined(__GNUC__) && __GNUC__ >= 4 && (__GNUC__ > 4 || __GNUC_MINOR__ >= 5)
__attribute__((optimize("no-tree-vectorize")))
#endif
void momentsInTileAccumulateRow(const T* ptr, int x0, int len, WT& s0, WT& s1, WT& s2, MT& s3)
{
for (int x = x0; x < len; ++x)
{
WT p = ptr[x];
WT xp = x * p, xxp;
s0 += p;
s1 += xp;
xxp = xp * x;
s2 += xxp;
s3 += xxp * x;
}
}
// The function calculates center of gravity and the central second order moments
static void completeMomentState( Moments* moments )
{
@@ -200,172 +222,6 @@ static Moments contourMoments( const Mat& contour )
}
/****************************************************************************************\
* Spatial Raster Moments *
\****************************************************************************************/
template<typename T, typename WT, typename MT>
struct MomentsInTile_SIMD
{
int operator() (const T *, int, WT &, WT &, WT &, MT &)
{
return 0;
}
};
#if CV_SIMD128
template <>
struct MomentsInTile_SIMD<uchar, int, int>
{
MomentsInTile_SIMD()
{
// nothing
}
int operator() (const uchar * ptr, int len, int & x0, int & x1, int & x2, int & x3)
{
int x = 0;
{
v_int16x8 dx = v_setall_s16(8), qx = v_int16x8(0, 1, 2, 3, 4, 5, 6, 7);
v_uint32x4 z = v_setzero_u32(), qx0 = z, qx1 = z, qx2 = z, qx3 = z;
for( ; x <= len - 8; x += 8 )
{
v_int16x8 p = v_reinterpret_as_s16(v_load_expand(ptr + x));
v_int16x8 sx = v_mul_wrap(qx, qx);
qx0 = v_add(qx0, v_reinterpret_as_u32(p));
qx1 = v_reinterpret_as_u32(v_dotprod(p, qx, v_reinterpret_as_s32(qx1)));
qx2 = v_reinterpret_as_u32(v_dotprod(p, sx, v_reinterpret_as_s32(qx2)));
qx3 = v_reinterpret_as_u32(v_dotprod(v_mul_wrap(p, qx), sx, v_reinterpret_as_s32(qx3)));
qx = v_add(qx, dx);
}
x0 = v_reduce_sum(qx0);
x0 = (x0 & 0xffff) + (x0 >> 16);
x1 = v_reduce_sum(qx1);
x2 = v_reduce_sum(qx2);
x3 = v_reduce_sum(qx3);
}
return x;
}
};
#endif // CV_SIMD128
#if (CV_SIMD || CV_SIMD_SCALABLE)
namespace {
template <typename T, int N>
struct IotaInit {
T CV_DECL_ALIGNED(CV_SIMD_WIDTH) data[N];
IotaInit() { for (int i = 0; i < N; i++) data[i] = (T)i; }
};
static const IotaInit<int, VTraits<v_int32>::max_nlanes> g_ix0_init;
}
template <>
struct MomentsInTile_SIMD<ushort, int, int64>
{
MomentsInTile_SIMD() {}
int operator() (const ushort * ptr, int len, int & x0, int & x1, int & x2, int64 & x3)
{
int x = 0;
const int vlanes32 = VTraits<v_int32>::vlanes();
v_int32 v_delta = vx_setall_s32(vlanes32);
v_int32 v_ix0 = vx_load(g_ix0_init.data);
v_uint32 z = vx_setzero_u32();
v_uint32 v_x0 = z, v_x1 = z, v_x2 = z;
v_uint64 v_x3 = vx_setzero_u64();
for ( ; x <= len - vlanes32; x += vlanes32 )
{
v_int32 v_src = v_reinterpret_as_s32(vx_load_expand(ptr + x));
v_x0 = v_add(v_x0, v_reinterpret_as_u32(v_src));
v_x1 = v_add(v_x1, v_reinterpret_as_u32(v_mul(v_src, v_ix0)));
v_int32 v_ix1 = v_mul(v_ix0, v_ix0);
v_x2 = v_add(v_x2, v_reinterpret_as_u32(v_mul(v_src, v_ix1)));
v_ix1 = v_mul(v_ix0, v_ix1);
v_src = v_mul(v_src, v_ix1);
v_uint64 v_lo, v_hi;
v_expand(v_reinterpret_as_u32(v_src), v_lo, v_hi);
v_x3 = v_add(v_x3, v_add(v_lo, v_hi));
v_ix0 = v_add(v_ix0, v_delta);
}
x0 = v_reduce_sum(v_x0);
x1 = v_reduce_sum(v_x1);
x2 = v_reduce_sum(v_x2);
x3 = (int64)v_reduce_sum(v_x3);
vx_cleanup();
return x;
}
};
#endif // CV_SIMD || CV_SIMD_SCALABLE
template<typename T, typename WT, typename MT>
#if defined __GNUC__ && __GNUC__ == 4 && __GNUC_MINOR__ >= 5 && __GNUC_MINOR__ < 9
// Workaround for http://gcc.gnu.org/bugzilla/show_bug.cgi?id=60196
__attribute__((optimize("no-tree-vectorize")))
#endif
static void momentsInTile( const Mat& img, double* moments )
{
Size size = img.size();
int x, y;
MT mom[10] = {0,0,0,0,0,0,0,0,0,0};
MomentsInTile_SIMD<T, WT, MT> vop;
for( y = 0; y < size.height; y++ )
{
const T* ptr = img.ptr<T>(y);
WT x0 = 0, x1 = 0, x2 = 0;
MT x3 = 0;
x = vop(ptr, size.width, x0, x1, x2, x3);
for( ; x < size.width; x++ )
{
WT p = ptr[x];
WT xp = x * p, xxp;
x0 += p;
x1 += xp;
xxp = xp * x;
x2 += xxp;
x3 += xxp * x;
}
WT py = y * x0, sy = y*y;
mom[9] += ((MT)py) * sy; // m03
mom[8] += ((MT)x1) * sy; // m12
mom[7] += ((MT)x2) * y; // m21
mom[6] += x3; // m30
mom[5] += x0 * sy; // m02
mom[4] += x1 * y; // m11
mom[3] += x2; // m20
mom[2] += py; // m01
mom[1] += x1; // m10
mom[0] += x0; // m00
}
for( x = 0; x < 10; x++ )
moments[x] = (double)mom[x];
}
typedef void (*MomentsInTileFunc)(const Mat& img, double* moments);
Moments::Moments()
{
m00 = m10 = m01 = m20 = m11 = m02 = m30 = m21 = m12 = m03 =
@@ -568,6 +424,69 @@ static bool ipp_moments(Mat &src, Moments &m )
}
#endif
template void momentsInTileAccumulateRow<uchar, int, int>(const uchar*, int, int, int&, int&, int&, int&);
template void momentsInTileAccumulateRow<ushort, int, int64>(const ushort*, int, int, int&, int&, int&, int64&);
template void momentsInTileAccumulateRow<float, double, double>(const float*, int, int, double&, double&, double&, double&);
template void momentsInTileAccumulateRow<double, double, double>(const double*, int, int, double&, double&, double&, double&);
template<typename T, typename WT, typename MT>
struct MomentsInTile_SIMD
{
int operator() (const T *, int, WT &, WT &, WT &, MT &)
{
return 0;
}
};
template<typename T, typename WT, typename MT>
#if defined __GNUC__ && __GNUC__ == 4 && __GNUC_MINOR__ >= 5 && __GNUC_MINOR__ < 9
// Workaround for http://gcc.gnu.org/bugzilla/show_bug.cgi?id=60196
__attribute__((optimize("no-tree-vectorize")))
#endif
static void momentsInTile( const Mat& img, double* moments )
{
Size size = img.size();
int x, y;
MT mom[10] = {0,0,0,0,0,0,0,0,0,0};
MomentsInTile_SIMD<T, WT, MT> vop;
for( y = 0; y < size.height; y++ )
{
const T* ptr = img.ptr<T>(y);
WT x0 = 0, x1 = 0, x2 = 0;
MT x3 = 0;
x = vop(ptr, size.width, x0, x1, x2, x3);
for( ; x < size.width; x++ )
{
WT p = ptr[x];
WT xp = x * p, xxp;
x0 += p;
x1 += xp;
xxp = xp * x;
x2 += xxp;
x3 += xxp * x;
}
WT py = y * x0, sy = y*y;
mom[9] += ((MT)py) * sy; // m03
mom[8] += ((MT)x1) * sy; // m12
mom[7] += ((MT)x2) * y; // m21
mom[6] += x3; // m30
mom[5] += x0 * sy; // m02
mom[4] += x1 * y; // m11
mom[3] += x2; // m20
mom[2] += py; // m01
mom[1] += x1; // m10
mom[0] += x0; // m00
}
for( x = 0; x < 10; x++ )
moments[x] = (double)mom[x];
}
}
namespace cv { namespace hal {
@@ -633,15 +552,15 @@ cv::Moments cv::moments( InputArray _src, bool binary )
CV_IPP_RUN(!binary, ipp_moments(mat, m), m);
if( binary || depth == CV_8U )
func = momentsInTile<uchar, int, int>;
func = getMomentsInTileFunc(CV_8U);
else if( depth == CV_16U )
func = momentsInTile<ushort, int, int64>;
func = getMomentsInTileFunc(CV_16U);
else if( depth == CV_16S )
func = momentsInTile<short, int, int64>;
else if( depth == CV_32F )
func = momentsInTile<float, double, double>;
func = getMomentsInTileFunc(CV_32F);
else if( depth == CV_64F )
func = momentsInTile<double, double, double>;
func = getMomentsInTileFunc(CV_64F);
else
CV_Error( cv::Error::StsUnsupportedFormat, "" );

View File

@@ -0,0 +1,22 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html
// Copyright (C) 2026, Advanced Micro Devices, Inc., all rights reserved.
#include "precomp.hpp"
#include "moments.simd.hpp"
#include "moments.simd_declarations.hpp"
namespace cv {
MomentsInTileFunc getMomentsInTileFunc(int depth);
MomentsInTileFunc getMomentsInTileFunc(int depth)
{
CV_INSTRUMENT_REGION();
CV_CPU_DISPATCH(getMomentsInTileFunc, (depth),
CV_CPU_DISPATCH_MODES_ALL);
}
} // namespace cv

View File

@@ -0,0 +1,358 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html
// Copyright (C) 2026, Advanced Micro Devices, Inc., all rights reserved.
#include "precomp.hpp"
#include "opencv2/core/hal/intrin.hpp"
namespace cv {
typedef void (*MomentsInTileFunc)(const Mat& img, double* moments);
template<typename T, typename WT, typename MT>
void momentsInTileAccumulateRow(const T* ptr, int x0, int len, WT& s0, WT& s1, WT& s2, MT& s3);
CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN
MomentsInTileFunc getMomentsInTileFunc(int depth);
#ifndef CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY
template<typename T, typename WT, typename MT>
struct MomentsInTile_SIMD
{
int operator() (const T *, int, WT &, WT &, WT &, MT &) const
{
return 0;
}
};
#if CV_SIMD128
template <>
struct MomentsInTile_SIMD<uchar, int, int>
{
int operator() (const uchar * ptr, int len, int & x0, int & x1, int & x2, int & x3) const
{
int x = 0;
v_int16x8 dx = v_setall_s16(8), qx = v_int16x8(0, 1, 2, 3, 4, 5, 6, 7);
v_uint32x4 z = v_setzero_u32(), qx0 = z, qx1 = z, qx2 = z, qx3 = z;
for( ; x <= len - 8; x += 8 )
{
v_int16x8 p = v_reinterpret_as_s16(v_load_expand(ptr + x));
v_int16x8 sx = v_mul_wrap(qx, qx);
qx0 = v_add(qx0, v_reinterpret_as_u32(p));
qx1 = v_reinterpret_as_u32(v_dotprod(p, qx, v_reinterpret_as_s32(qx1)));
qx2 = v_reinterpret_as_u32(v_dotprod(p, sx, v_reinterpret_as_s32(qx2)));
qx3 = v_reinterpret_as_u32(v_dotprod(v_mul_wrap(p, qx), sx, v_reinterpret_as_s32(qx3)));
qx = v_add(qx, dx);
}
x0 = v_reduce_sum(qx0);
x0 = (x0 & 0xffff) + (x0 >> 16);
x1 = v_reduce_sum(qx1);
x2 = v_reduce_sum(qx2);
x3 = v_reduce_sum(qx3);
return x;
}
};
#endif // CV_SIMD128
#if (CV_SIMD || CV_SIMD_SCALABLE)
static inline v_int32 vx_load_iota_s32()
{
struct IotaInit
{
int CV_DECL_ALIGNED(CV_SIMD_WIDTH) data[VTraits<v_int32>::max_nlanes];
IotaInit() { for (int i = 0; i < VTraits<v_int32>::max_nlanes; ++i) data[i] = i; }
};
static const IotaInit init;
return vx_load(init.data);
}
#if !CV_SIMD128
template <>
struct MomentsInTile_SIMD<uchar, int, int>
{
int operator() (const uchar * ptr, int len, int & x0, int & x1, int & x2, int & x3) const
{
int x = 0;
const int vlanes32 = VTraits<v_int32>::vlanes();
v_int32 v_delta = vx_setall_s32(vlanes32);
v_int32 v_ix0 = vx_load_iota_s32();
v_int32 v_x0 = vx_setzero_s32(), v_x1 = vx_setzero_s32();
v_int32 v_x2 = vx_setzero_s32(), v_x3 = vx_setzero_s32();
for ( ; x <= len - vlanes32; x += vlanes32 )
{
v_int32 v_src = v_reinterpret_as_s32(vx_load_expand_q(ptr + x));
v_x0 = v_add(v_x0, v_src);
v_x1 = v_add(v_x1, v_mul(v_src, v_ix0));
v_int32 v_ix1 = v_mul(v_ix0, v_ix0);
v_x2 = v_add(v_x2, v_mul(v_src, v_ix1));
v_ix1 = v_mul(v_ix0, v_ix1);
v_x3 = v_add(v_x3, v_mul(v_src, v_ix1));
v_ix0 = v_add(v_ix0, v_delta);
}
x0 = v_reduce_sum(v_x0);
x1 = v_reduce_sum(v_x1);
x2 = v_reduce_sum(v_x2);
x3 = v_reduce_sum(v_x3);
vx_cleanup();
return x;
}
};
#endif // !CV_SIMD128
template<typename ST>
struct moments_vx_load_expand16
{
static inline v_int32 fn(const ST* ptr, int x)
{ return vx_load_expand(ptr + x); }
};
template<>
struct moments_vx_load_expand16<ushort>
{
static inline v_int32 fn(const ushort* ptr, int x)
{ return v_reinterpret_as_s32(vx_load_expand(ptr + x)); }
};
template<typename ST>
struct MomentsInTile_SIMD_16u
{
int operator() (const ST * ptr, int len, int & x0, int & x1, int & x2, int64 & x3) const
{
int x = 0;
const int vlanes32 = VTraits<v_int32>::vlanes();
v_int32 v_delta = vx_setall_s32(vlanes32);
v_int32 v_ix0 = vx_load_iota_s32();
v_int32 v_x0 = vx_setzero_s32(), v_x1 = vx_setzero_s32(), v_x2 = vx_setzero_s32();
v_int64 v_x3 = vx_setzero_s64();
for ( ; x <= len - vlanes32; x += vlanes32 )
{
v_int32 v_src = moments_vx_load_expand16<ST>::fn(ptr, x);
v_x0 = v_add(v_x0, v_src);
v_x1 = v_add(v_x1, v_mul(v_src, v_ix0));
v_int32 v_ix1 = v_mul(v_ix0, v_ix0);
v_x2 = v_add(v_x2, v_mul(v_src, v_ix1));
v_ix1 = v_mul(v_ix0, v_ix1);
v_int32 v_src3 = v_mul(v_src, v_ix1);
v_int64 v_lo, v_hi;
v_expand(v_src3, v_lo, v_hi);
v_x3 = v_add(v_x3, v_add(v_lo, v_hi));
v_ix0 = v_add(v_ix0, v_delta);
}
x0 = v_reduce_sum(v_x0);
x1 = v_reduce_sum(v_x1);
x2 = v_reduce_sum(v_x2);
x3 = (int64)v_reduce_sum(v_x3);
vx_cleanup();
return x;
}
};
template <>
struct MomentsInTile_SIMD<ushort, int, int64> : MomentsInTile_SIMD_16u<ushort> {};
#endif // CV_SIMD || CV_SIMD_SCALABLE
#if (CV_SIMD_64F || CV_SIMD_SCALABLE_64F)
template <>
struct MomentsInTile_SIMD<float, double, double>
{
int operator() (const float * ptr, int len, double & x0, double & x1, double & x2, double & x3) const
{
int x = 0;
const int vlanesf = VTraits<v_float32>::vlanes();
const int vlanes64 = VTraits<v_float64>::vlanes();
if (vlanesf % vlanes64 != 0 || vlanesf != 2 * vlanes64)
return 0;
const int nhalfs = 2;
v_float64 v_block_delta = vx_setall_f64((double)vlanesf);
v_float64 v_ix0;
{
double CV_DECL_ALIGNED(CV_SIMD_WIDTH) buf[VTraits<v_float64>::max_nlanes];
for (int i = 0; i < vlanes64; ++i)
buf[i] = (double)i;
v_ix0 = vx_load(buf);
}
v_float64 v_x0 = vx_setzero_f64(), v_x1 = vx_setzero_f64();
v_float64 v_x2 = vx_setzero_f64(), v_x3 = vx_setzero_f64();
for ( ; x <= len - vlanesf; x += vlanesf )
{
v_float32 v_srcf = vx_load(ptr + x);
for (int h = 0; h < nhalfs; ++h)
{
v_float64 v_src = h == 0 ? v_cvt_f64(v_srcf) : v_cvt_f64_high(v_srcf);
v_float64 v_cur_ix = v_add(v_ix0, vx_setall_f64((double)(h * vlanes64)));
v_x0 = v_add(v_x0, v_src);
v_x1 = v_add(v_x1, v_mul(v_src, v_cur_ix));
v_float64 v_ix1 = v_mul(v_cur_ix, v_cur_ix);
v_x2 = v_add(v_x2, v_mul(v_src, v_ix1));
v_ix1 = v_mul(v_cur_ix, v_ix1);
v_x3 = v_add(v_x3, v_mul(v_src, v_ix1));
}
v_ix0 = v_add(v_ix0, v_block_delta);
}
x0 = v_reduce_sum(v_x0);
x1 = v_reduce_sum(v_x1);
x2 = v_reduce_sum(v_x2);
x3 = v_reduce_sum(v_x3);
vx_cleanup();
return x;
}
};
template <>
struct MomentsInTile_SIMD<double, double, double>
{
int operator() (const double * ptr, int len, double & x0, double & x1, double & x2, double & x3) const
{
int x = 0;
const int vlanes64 = VTraits<v_float64>::vlanes();
v_float64 v_delta = vx_setall_f64((double)VTraits<v_float64>::vlanes());
v_float64 v_ix0;
{
double CV_DECL_ALIGNED(CV_SIMD_WIDTH) buf[VTraits<v_float64>::max_nlanes];
for (int i = 0; i < vlanes64; ++i)
buf[i] = (double)i;
v_ix0 = vx_load(buf);
}
v_float64 v_x0 = vx_setzero_f64(), v_x1 = vx_setzero_f64();
v_float64 v_x2 = vx_setzero_f64(), v_x3 = vx_setzero_f64();
for ( ; x <= len - vlanes64; x += vlanes64 )
{
v_float64 v_src = vx_load(ptr + x);
v_x0 = v_add(v_x0, v_src);
v_x1 = v_add(v_x1, v_mul(v_src, v_ix0));
v_float64 v_ix1 = v_mul(v_ix0, v_ix0);
v_x2 = v_add(v_x2, v_mul(v_src, v_ix1));
v_ix1 = v_mul(v_ix0, v_ix1);
v_x3 = v_add(v_x3, v_mul(v_src, v_ix1));
v_ix0 = v_add(v_ix0, v_delta);
}
x0 = v_reduce_sum(v_x0);
x1 = v_reduce_sum(v_x1);
x2 = v_reduce_sum(v_x2);
x3 = v_reduce_sum(v_x3);
vx_cleanup();
return x;
}
};
#endif // CV_SIMD_64F || CV_SIMD_SCALABLE_64F
template<typename T, typename WT, typename MT>
#if defined(__GNUC__) && __GNUC__ >= 4 && (__GNUC__ > 4 || __GNUC_MINOR__ >= 5)
__attribute__((optimize("no-tree-vectorize")))
#endif
static void momentsInTile( const Mat& img, double* moments )
{
Size size = img.size();
int x, y;
MT mom[10] = {0,0,0,0,0,0,0,0,0,0};
MomentsInTile_SIMD<T, WT, MT> vop;
for( y = 0; y < size.height; y++ )
{
const T* ptr = img.ptr<T>(y);
WT x0 = 0, x1 = 0, x2 = 0;
MT x3 = 0;
x = vop(ptr, size.width, x0, x1, x2, x3);
if (x < size.width)
cv::momentsInTileAccumulateRow<T, WT, MT>(ptr, x, size.width, x0, x1, x2, x3);
WT py = y * x0, sy = y*y;
mom[9] += ((MT)py) * sy; // m03
mom[8] += ((MT)x1) * sy; // m12
mom[7] += ((MT)x2) * y; // m21
mom[6] += x3; // m30
mom[5] += x0 * sy; // m02
mom[4] += x1 * y; // m11
mom[3] += x2; // m20
mom[2] += py; // m01
mom[1] += x1; // m10
mom[0] += x0; // m00
}
for( x = 0; x < 10; x++ )
moments[x] = (double)mom[x];
}
static void momentsInTile8u( const Mat& img, double* moments )
{ CV_INSTRUMENT_REGION(); momentsInTile<uchar, int, int>(img, moments); }
static void momentsInTile16u( const Mat& img, double* moments )
{ CV_INSTRUMENT_REGION(); momentsInTile<ushort, int, int64>(img, moments); }
static void momentsInTile32f( const Mat& img, double* moments )
{ CV_INSTRUMENT_REGION(); momentsInTile<float, double, double>(img, moments); }
static void momentsInTile64f( const Mat& img, double* moments )
{ CV_INSTRUMENT_REGION(); momentsInTile<double, double, double>(img, moments); }
MomentsInTileFunc getMomentsInTileFunc(int depth)
{
static MomentsInTileFunc momentsInTileTab[CV_DEPTH_MAX] =
{
(MomentsInTileFunc)GET_OPTIMIZED(momentsInTile8u),
(MomentsInTileFunc)GET_OPTIMIZED(momentsInTile8u),
(MomentsInTileFunc)GET_OPTIMIZED(momentsInTile16u),
0,
0,
(MomentsInTileFunc)GET_OPTIMIZED(momentsInTile32f),
(MomentsInTileFunc)GET_OPTIMIZED(momentsInTile64f),
0
};
return momentsInTileTab[depth];
}
#endif // CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY
CV_CPU_OPTIMIZATION_NAMESPACE_END
} // namespace cv

View File

@@ -0,0 +1,85 @@
// 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 "perf_precomp.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/video/tracking.hpp"
namespace opencv_test {
using namespace perf;
typedef perf::TestBaseWithParam< tuple<Size, int> > MeanShiftFixture;
typedef perf::TestBaseWithParam< tuple<Size, int> > CamShiftFixture;
static void initProbImage(const Size& size, Mat& prob, Rect& window)
{
prob.create(size, CV_32FC1);
prob.setTo(0);
const Point center(size.width / 2, size.height / 2);
const int radius = std::max(8, std::min(size.width, size.height) / 8);
circle(prob, center, radius, Scalar(1.f), -1);
const int winSize = std::max(16, std::min(size.width, size.height) / 4);
window = Rect(center.x - winSize / 2, center.y - winSize / 2, winSize, winSize);
window &= Rect(0, 0, size.width, size.height);
}
PERF_TEST_P(MeanShiftFixture, meanShift,
testing::Combine(
testing::Values(szVGA, sz720p, sz1080p),
testing::Values(5, 10, 20)))
{
const Size size = get<0>(GetParam());
const int maxCount = get<1>(GetParam());
Mat prob;
Rect window, initWindow;
initProbImage(size, prob, initWindow);
declare.in(prob);
const TermCriteria criteria(TermCriteria::MAX_ITER | TermCriteria::EPS, maxCount, 1.0);
int iters = 0;
TEST_CYCLE()
{
window = initWindow;
iters = cv::meanShift(prob, window, criteria);
}
SANITY_CHECK(iters);
}
PERF_TEST_P(CamShiftFixture, CamShift,
testing::Combine(
testing::Values(szVGA, sz720p, sz1080p),
testing::Values(5, 10, 20)))
{
const Size size = get<0>(GetParam());
const int maxCount = get<1>(GetParam());
Mat prob;
Rect window, initWindow;
initProbImage(size, prob, initWindow);
declare.in(prob);
const TermCriteria criteria(TermCriteria::MAX_ITER | TermCriteria::EPS, maxCount, 1.0);
RotatedRect box;
TEST_CYCLE()
{
window = initWindow;
box = cv::CamShift(prob, window, criteria);
}
const float boxWidth = box.size.width;
const float boxHeight = box.size.height;
SANITY_CHECK(boxWidth);
SANITY_CHECK(boxHeight);
}
} // namespace opencv_test