From 00833f98d0aed4e299a8ba5723d06456e6d077ee Mon Sep 17 00:00:00 2001 From: Madan mohan Manokar Date: Fri, 13 Mar 2026 15:34:46 +0530 Subject: [PATCH] Merge pull request #28632 from amd:fast_scharr_deriv video: Optimized ScharrDeriv#28632 - Move to CV_SIMD_SCALABLE - avx2 & avx512 dispatch added for ScharrDeriv ### 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 --- modules/video/CMakeLists.txt | 1 + modules/video/src/lkpyramid.cpp | 81 +--------------- modules/video/src/lkpyramid.dispatch.cpp | 22 +++++ modules/video/src/lkpyramid.hpp | 2 + modules/video/src/lkpyramid.simd.hpp | 118 +++++++++++++++++++++++ 5 files changed, 145 insertions(+), 79 deletions(-) create mode 100644 modules/video/src/lkpyramid.dispatch.cpp create mode 100644 modules/video/src/lkpyramid.simd.hpp diff --git a/modules/video/CMakeLists.txt b/modules/video/CMakeLists.txt index a302e5a5d1..5ea22b45cb 100644 --- a/modules/video/CMakeLists.txt +++ b/modules/video/CMakeLists.txt @@ -1,4 +1,5 @@ set(the_description "Video Analysis") +ocv_add_dispatched_file(lkpyramid SSE2 SSE4_1 AVX2 AVX512_SKX AVX512_ICL) ocv_define_module(video opencv_imgproc OPTIONAL diff --git a/modules/video/src/lkpyramid.cpp b/modules/video/src/lkpyramid.cpp index 1ed0482447..35f1e6135d 100644 --- a/modules/video/src/lkpyramid.cpp +++ b/modules/video/src/lkpyramid.cpp @@ -12,6 +12,7 @@ // // Copyright (C) 2000, Intel Corporation, all rights reserved. // Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Copyright (C) 2026, Advanced Micro Devices, Inc., all rights reserved. // Third party copyrights are property of their respective owners. // // Redistribution and use in source and binary forms, with or without modification, @@ -73,85 +74,7 @@ static void calcScharrDeriv(const cv::Mat& src, cv::Mat& dst) void cv::detail::ScharrDerivInvoker::operator()(const Range& range) const { - using cv::detail::deriv_type; - int rows = src.rows, cols = src.cols, cn = src.channels(), colsn = cols*cn; - - int x, y, delta = (int)alignSize((cols + 2)*cn, 16); - AutoBuffer _tempBuf(delta*2 + 64); - deriv_type *trow0 = alignPtr(_tempBuf.data() + cn, 16), *trow1 = alignPtr(trow0 + delta, 16); - -#if CV_SIMD128 - v_int16x8 c3 = v_setall_s16(3), c10 = v_setall_s16(10); -#endif - - for( y = range.start; y < range.end; y++ ) - { - const uchar* srow0 = src.ptr(y > 0 ? y-1 : rows > 1 ? 1 : 0); - const uchar* srow1 = src.ptr(y); - const uchar* srow2 = src.ptr(y < rows-1 ? y+1 : rows > 1 ? rows-2 : 0); - deriv_type* drow = (deriv_type *)dst.ptr(y); - - // do vertical convolution - x = 0; -#if CV_SIMD128 - { - for( ; x <= colsn - 8; x += 8 ) - { - v_int16x8 s0 = v_reinterpret_as_s16(v_load_expand(srow0 + x)); - v_int16x8 s1 = v_reinterpret_as_s16(v_load_expand(srow1 + x)); - v_int16x8 s2 = v_reinterpret_as_s16(v_load_expand(srow2 + x)); - - v_int16x8 t1 = v_sub(s2, s0); - v_int16x8 t0 = v_add(v_mul_wrap(v_add(s0, s2), c3), v_mul_wrap(s1, c10)); - - v_store(trow0 + x, t0); - v_store(trow1 + x, t1); - } - } -#endif - - for( ; x < colsn; x++ ) - { - int t0 = (srow0[x] + srow2[x])*3 + srow1[x]*10; - int t1 = srow2[x] - srow0[x]; - trow0[x] = (deriv_type)t0; - trow1[x] = (deriv_type)t1; - } - - // make border - int x0 = (cols > 1 ? 1 : 0)*cn, x1 = (cols > 1 ? cols-2 : 0)*cn; - for( int k = 0; k < cn; k++ ) - { - trow0[-cn + k] = trow0[x0 + k]; trow0[colsn + k] = trow0[x1 + k]; - trow1[-cn + k] = trow1[x0 + k]; trow1[colsn + k] = trow1[x1 + k]; - } - - // do horizontal convolution, interleave the results and store them to dst - x = 0; -#if CV_SIMD128 - { - for( ; x <= colsn - 8; x += 8 ) - { - v_int16x8 s0 = v_load(trow0 + x - cn); - v_int16x8 s1 = v_load(trow0 + x + cn); - v_int16x8 s2 = v_load(trow1 + x - cn); - v_int16x8 s3 = v_load(trow1 + x); - v_int16x8 s4 = v_load(trow1 + x + cn); - - v_int16x8 t0 = v_sub(s1, s0); - v_int16x8 t1 = v_add(v_mul_wrap(v_add(s2, s4), c3), v_mul_wrap(s3, c10)); - - v_store_interleave((drow + x*2), t0, t1); - } - } -#endif - for( ; x < colsn; x++ ) - { - deriv_type t0 = (deriv_type)(trow0[x+cn] - trow0[x-cn]); - deriv_type t1 = (deriv_type)((trow1[x+cn] + trow1[x-cn])*3 + trow1[x]*10); - drow[x*2] = t0; drow[x*2+1] = t1; - } - } + ScharrDerivInvoker_impl(src, const_cast(dst), range); } cv::detail::LKTrackerInvoker::LKTrackerInvoker( diff --git a/modules/video/src/lkpyramid.dispatch.cpp b/modules/video/src/lkpyramid.dispatch.cpp new file mode 100644 index 0000000000..2775d9af0a --- /dev/null +++ b/modules/video/src/lkpyramid.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 "lkpyramid.hpp" +#include "opencv2/core/hal/intrin.hpp" + +#include "lkpyramid.simd.hpp" +#include "lkpyramid.simd_declarations.hpp" // defines CV_CPU_DISPATCH_MODES_ALL based on CMakeLists.txt + +namespace cv { +namespace detail { + +void ScharrDerivInvoker_impl(const Mat& src, Mat& dst, const Range& range) +{ + CV_CPU_DISPATCH(ScharrDerivInvoker_SIMD, (src, dst, range), CV_CPU_DISPATCH_MODES_ALL); +} + +} // namespace detail +} // namespace cv diff --git a/modules/video/src/lkpyramid.hpp b/modules/video/src/lkpyramid.hpp index 62fab0564b..18ff61c75e 100644 --- a/modules/video/src/lkpyramid.hpp +++ b/modules/video/src/lkpyramid.hpp @@ -7,6 +7,8 @@ namespace detail typedef short deriv_type; + void ScharrDerivInvoker_impl(const Mat& src, Mat& dst, const Range& range); + struct ScharrDerivInvoker : ParallelLoopBody { ScharrDerivInvoker(const Mat& _src, const Mat& _dst) diff --git a/modules/video/src/lkpyramid.simd.hpp b/modules/video/src/lkpyramid.simd.hpp new file mode 100644 index 0000000000..4408b8affc --- /dev/null +++ b/modules/video/src/lkpyramid.simd.hpp @@ -0,0 +1,118 @@ +// 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 "lkpyramid.hpp" +#include "opencv2/core/hal/intrin.hpp" + +namespace cv { +namespace detail { + +CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN + +// SIMD-optimized implementation +void ScharrDerivInvoker_SIMD(const Mat& src, Mat& dst, const Range& range); + +CV_CPU_OPTIMIZATION_NAMESPACE_END + +} // namespace detail +} // namespace cv + +#ifndef CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY + +namespace cv { +namespace detail { + +CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN + +void ScharrDerivInvoker_SIMD(const Mat& src, Mat& dst, const Range& range) +{ + typedef short deriv_type; + int rows = src.rows, cols = src.cols, cn = src.channels(), colsn = cols*cn; + + int x, y, delta = (int)alignSize((cols + 2)*cn, 16); + AutoBuffer _tempBuf(delta*2 + 64); + deriv_type *trow0 = alignPtr(_tempBuf.data() + cn, 16), *trow1 = alignPtr(trow0 + delta, 16); + +#if (CV_SIMD) + const int vlanes = VTraits::vlanes(); + v_int16 c3 = vx_setall_s16(3), c10 = vx_setall_s16(10); +#endif + + for( y = range.start; y < range.end; y++ ) + { + const uchar* srow0 = src.ptr(y > 0 ? y-1 : rows > 1 ? 1 : 0); + const uchar* srow1 = src.ptr(y); + const uchar* srow2 = src.ptr(y < rows-1 ? y+1 : rows > 1 ? rows-2 : 0); + deriv_type* drow = (deriv_type *)dst.ptr(y); + + // do vertical convolution + x = 0; +#if (CV_SIMD) + { + for( ; x <= colsn - vlanes; x += vlanes ) + { + v_int16 s0 = v_reinterpret_as_s16(vx_load_expand(srow0 + x)); + v_int16 s1 = v_reinterpret_as_s16(vx_load_expand(srow1 + x)); + v_int16 s2 = v_reinterpret_as_s16(vx_load_expand(srow2 + x)); + + v_int16 t1 = v_sub(s2, s0); + v_int16 t0 = v_add(v_mul_wrap(v_add(s0, s2), c3), v_mul_wrap(s1, c10)); + + v_store(trow0 + x, t0); + v_store(trow1 + x, t1); + } + } +#endif + + for( ; x < colsn; x++ ) + { + int t0 = (srow0[x] + srow2[x])*3 + srow1[x]*10; + int t1 = srow2[x] - srow0[x]; + trow0[x] = (deriv_type)t0; + trow1[x] = (deriv_type)t1; + } + + // make border + int x0 = (cols > 1 ? 1 : 0)*cn, x1 = (cols > 1 ? cols-2 : 0)*cn; + for( int k = 0; k < cn; k++ ) + { + trow0[-cn + k] = trow0[x0 + k]; trow0[colsn + k] = trow0[x1 + k]; + trow1[-cn + k] = trow1[x0 + k]; trow1[colsn + k] = trow1[x1 + k]; + } + + // do horizontal convolution, interleave the results and store them to dst + x = 0; +#if (CV_SIMD) + { + for( ; x <= colsn - vlanes; x += vlanes ) + { + v_int16 s0 = vx_load(trow0 + x - cn); + v_int16 s1 = vx_load(trow0 + x + cn); + v_int16 s2 = vx_load(trow1 + x - cn); + v_int16 s3 = vx_load(trow1 + x); + v_int16 s4 = vx_load(trow1 + x + cn); + + v_int16 t0 = v_sub(s1, s0); + v_int16 t1 = v_add(v_mul_wrap(v_add(s2, s4), c3), v_mul_wrap(s3, c10)); + + v_store_interleave((drow + x*2), t0, t1); + } + } +#endif + for( ; x < colsn; x++ ) + { + deriv_type t0 = (deriv_type)(trow0[x+cn] - trow0[x-cn]); + deriv_type t1 = (deriv_type)((trow1[x+cn] + trow1[x-cn])*3 + trow1[x]*10); + drow[x*2] = t0; drow[x*2+1] = t1; + } + } +} + +CV_CPU_OPTIMIZATION_NAMESPACE_END + +} // namespace detail +} // namespace cv + +#endif // CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY