mirror of
https://github.com/opencv/opencv.git
synced 2026-09-11 04:43:22 -05:00
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:
committed by
GitHub
parent
e39c9a2411
commit
a15bcc7ea8
@@ -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)
|
||||
|
||||
52
modules/imgproc/perf/perf_matchShapes.cpp
Normal file
52
modules/imgproc/perf/perf_matchShapes.cpp
Normal 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
|
||||
@@ -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();
|
||||
|
||||
@@ -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, "" );
|
||||
|
||||
|
||||
22
modules/imgproc/src/moments.dispatch.cpp
Normal file
22
modules/imgproc/src/moments.dispatch.cpp
Normal 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
|
||||
358
modules/imgproc/src/moments.simd.hpp
Normal file
358
modules/imgproc/src/moments.simd.hpp
Normal 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
|
||||
85
modules/video/perf/perf_camshift.cpp
Normal file
85
modules/video/perf/perf_camshift.cpp
Normal 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
|
||||
Reference in New Issue
Block a user