diff --git a/modules/imgproc/CMakeLists.txt b/modules/imgproc/CMakeLists.txt index d969d3b573..e29ffde2a4 100644 --- a/modules/imgproc/CMakeLists.txt +++ b/modules/imgproc/CMakeLists.txt @@ -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) diff --git a/modules/imgproc/perf/perf_matchShapes.cpp b/modules/imgproc/perf/perf_matchShapes.cpp new file mode 100644 index 0000000000..83095a341a --- /dev/null +++ b/modules/imgproc/perf/perf_matchShapes.cpp @@ -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 > MatchShapesFixture; + +static void generateContour(int npoints, Mat& contour) +{ + RNG& rng = theRNG(); + contour.create(npoints, 1, CV_32SC2); + Point* pts = contour.ptr(); + 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 diff --git a/modules/imgproc/perf/perf_moments.cpp b/modules/imgproc/perf/perf_moments.cpp index 5d9c0366a1..c995b5839c 100644 --- a/modules/imgproc/perf/perf_moments.cpp +++ b/modules/imgproc/perf/perf_moments.cpp @@ -15,7 +15,7 @@ typedef perf::TestBaseWithParam 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(); diff --git a/modules/imgproc/src/moments.cpp b/modules/imgproc/src/moments.cpp index 3459ede418..ddb55ec41d 100644 --- a/modules/imgproc/src/moments.cpp +++ b/modules/imgproc/src/moments.cpp @@ -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 +#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 -struct MomentsInTile_SIMD -{ - int operator() (const T *, int, WT &, WT &, WT &, MT &) - { - return 0; - } -}; - -#if CV_SIMD128 - -template <> -struct MomentsInTile_SIMD -{ - 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 -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::max_nlanes> g_ix0_init; -} - -template <> -struct MomentsInTile_SIMD -{ - MomentsInTile_SIMD() {} - - int operator() (const ushort * ptr, int len, int & x0, int & x1, int & x2, int64 & x3) - { - int x = 0; - const int vlanes32 = VTraits::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 -#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 vop; - - for( y = 0; y < size.height; y++ ) - { - const T* ptr = img.ptr(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(const uchar*, int, int, int&, int&, int&, int&); +template void momentsInTileAccumulateRow(const ushort*, int, int, int&, int&, int&, int64&); +template void momentsInTileAccumulateRow(const float*, int, int, double&, double&, double&, double&); +template void momentsInTileAccumulateRow(const double*, int, int, double&, double&, double&, double&); + +template +struct MomentsInTile_SIMD +{ + int operator() (const T *, int, WT &, WT &, WT &, MT &) + { + return 0; + } +}; + +template +#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 vop; + + for( y = 0; y < size.height; y++ ) + { + const T* ptr = img.ptr(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; + func = getMomentsInTileFunc(CV_8U); else if( depth == CV_16U ) - func = momentsInTile; + func = getMomentsInTileFunc(CV_16U); else if( depth == CV_16S ) func = momentsInTile; else if( depth == CV_32F ) - func = momentsInTile; + func = getMomentsInTileFunc(CV_32F); else if( depth == CV_64F ) - func = momentsInTile; + func = getMomentsInTileFunc(CV_64F); else CV_Error( cv::Error::StsUnsupportedFormat, "" ); diff --git a/modules/imgproc/src/moments.dispatch.cpp b/modules/imgproc/src/moments.dispatch.cpp new file mode 100644 index 0000000000..6922ad8111 --- /dev/null +++ b/modules/imgproc/src/moments.dispatch.cpp @@ -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 diff --git a/modules/imgproc/src/moments.simd.hpp b/modules/imgproc/src/moments.simd.hpp new file mode 100644 index 0000000000..158184eb3b --- /dev/null +++ b/modules/imgproc/src/moments.simd.hpp @@ -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 +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 +struct MomentsInTile_SIMD +{ + int operator() (const T *, int, WT &, WT &, WT &, MT &) const + { + return 0; + } +}; + +#if CV_SIMD128 + +template <> +struct MomentsInTile_SIMD +{ + 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::max_nlanes]; + IotaInit() { for (int i = 0; i < VTraits::max_nlanes; ++i) data[i] = i; } + }; + static const IotaInit init; + return vx_load(init.data); +} + +#if !CV_SIMD128 + +template <> +struct MomentsInTile_SIMD +{ + int operator() (const uchar * ptr, int len, int & x0, int & x1, int & x2, int & x3) const + { + int x = 0; + const int vlanes32 = VTraits::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 +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 +{ + static inline v_int32 fn(const ushort* ptr, int x) + { return v_reinterpret_as_s32(vx_load_expand(ptr + x)); } +}; + +template +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::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::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 : MomentsInTile_SIMD_16u {}; + +#endif // CV_SIMD || CV_SIMD_SCALABLE + +#if (CV_SIMD_64F || CV_SIMD_SCALABLE_64F) + +template <> +struct MomentsInTile_SIMD +{ + int operator() (const float * ptr, int len, double & x0, double & x1, double & x2, double & x3) const + { + int x = 0; + const int vlanesf = VTraits::vlanes(); + const int vlanes64 = VTraits::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::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 +{ + int operator() (const double * ptr, int len, double & x0, double & x1, double & x2, double & x3) const + { + int x = 0; + const int vlanes64 = VTraits::vlanes(); + + v_float64 v_delta = vx_setall_f64((double)VTraits::vlanes()); + v_float64 v_ix0; + { + double CV_DECL_ALIGNED(CV_SIMD_WIDTH) buf[VTraits::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 +#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 vop; + + for( y = 0; y < size.height; y++ ) + { + const T* ptr = img.ptr(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(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(img, moments); } + +static void momentsInTile16u( const Mat& img, double* moments ) +{ CV_INSTRUMENT_REGION(); momentsInTile(img, moments); } + +static void momentsInTile32f( const Mat& img, double* moments ) +{ CV_INSTRUMENT_REGION(); momentsInTile(img, moments); } + +static void momentsInTile64f( const Mat& img, double* moments ) +{ CV_INSTRUMENT_REGION(); momentsInTile(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 diff --git a/modules/video/perf/perf_camshift.cpp b/modules/video/perf/perf_camshift.cpp new file mode 100644 index 0000000000..0894ce43ed --- /dev/null +++ b/modules/video/perf/perf_camshift.cpp @@ -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 > MeanShiftFixture; +typedef perf::TestBaseWithParam< tuple > 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