diff --git a/hal/ipp/CMakeLists.txt b/hal/ipp/CMakeLists.txt index f4a1b7fb75..9427d5924c 100644 --- a/hal/ipp/CMakeLists.txt +++ b/hal/ipp/CMakeLists.txt @@ -16,6 +16,7 @@ add_library(ipphal STATIC "${CMAKE_CURRENT_SOURCE_DIR}/src/cart_polar_ipp.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/src/transforms_ipp.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/src/warp_ipp.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/deriv_ipp.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/src/resize_ipp.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/src/sum_ipp.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/src/color_ipp.cpp" diff --git a/hal/ipp/include/ipp_hal_imgproc.hpp b/hal/ipp/include/ipp_hal_imgproc.hpp index c38bade2c1..dec9ec7ab8 100644 --- a/hal/ipp/include/ipp_hal_imgproc.hpp +++ b/hal/ipp/include/ipp_hal_imgproc.hpp @@ -15,6 +15,21 @@ int ipp_hal_warpAffine(int src_type, const uchar *src_data, size_t src_step, int int dst_height, const double M[6], int interpolation, int borderType, const double borderValue[4]); #undef cv_hal_warpAffine #define cv_hal_warpAffine ipp_hal_warpAffine + +int ipp_hal_sobel(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, + int width, int height, int src_depth, int dst_depth, int cn, + int margin_left, int margin_top, int margin_right, int margin_bottom, + int dx, int dy, int ksize, double scale, double delta, int border_type); +#undef cv_hal_sobel +#define cv_hal_sobel ipp_hal_sobel + +int ipp_hal_scharr(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, + int width, int height, int src_depth, int dst_depth, int cn, + int margin_left, int margin_top, int margin_right, int margin_bottom, + int dx, int dy, double scale, double delta, int border_type); +#undef cv_hal_scharr +#define cv_hal_scharr ipp_hal_scharr + #endif #if IPP_VERSION_X100 >= 202600 diff --git a/hal/ipp/src/deriv_ipp.cpp b/hal/ipp/src/deriv_ipp.cpp new file mode 100644 index 0000000000..74e928b20d --- /dev/null +++ b/hal/ipp/src/deriv_ipp.cpp @@ -0,0 +1,245 @@ +// 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, Intel Corporation, all rights reserved. + +#include "ipp_hal_imgproc.hpp" +#include "precomp_ipp.hpp" + +#include +#include + +#if IPP_VERSION_X100 >= 810 + +#ifdef HAVE_IPP_IW +#include "iw++/iw.hpp" + +static inline IppiMaskSize ippiGetMaskSize(int kx, int ky) +{ + return (kx == 1 && ky == 3) ? ippMskSize1x3 : + (kx == 1 && ky == 5) ? ippMskSize1x5 : + (kx == 3 && ky == 1) ? ippMskSize3x1 : + (kx == 3 && ky == 3) ? ippMskSize3x3 : + (kx == 5 && ky == 1) ? ippMskSize5x1 : + (kx == 5 && ky == 5) ? ippMskSize5x5 : + (IppiMaskSize)-1; +} + +static inline IwiDerivativeType ippiGetDerivType(int dx, int dy, bool nvert) +{ + return (dx == 1 && dy == 0) ? ((nvert)?iwiDerivNVerFirst:iwiDerivVerFirst) : + (dx == 0 && dy == 1) ? iwiDerivHorFirst : + (dx == 2 && dy == 0) ? iwiDerivVerSecond : + (dx == 0 && dy == 2) ? iwiDerivHorSecond : + (IwiDerivativeType)-1; +} + +// Build an IwiImage from a raw buffer, encoding the in-memory border (the OpenCV +// margins around the ROI) so that BORDER_*-with-physical-pixels works as in core. +// TODO: promote to precomp_ipp.hpp and unify with the raw-pointer ippiGetImage in +// transforms_ipp.cpp once more filter HALs share it +static inline ::ipp::IwiImage ippiGetImage(int depth, int channels, const uchar* data, size_t step, + int width, int height, + int margin_left, int margin_top, int margin_right, int margin_bottom) +{ + ::ipp::IwiImage image; + ::ipp::IwiBorderSize inMemBorder((IwSize)margin_left, (IwSize)margin_top, (IwSize)margin_right, (IwSize)margin_bottom); + image.Init({width, height}, ippiGetDataType(depth), channels, inMemBorder, (void*)data, step); + return image; +} + +// Translate the OpenCV border type into an IPP border, accounting for in-memory pixels. +// Returns (IppiBorderType)0 on unsupported configuration. +// TODO: promote to precomp_ipp.hpp once shared by other filter HALs (box/gaussian/bilateral/ +// sepFilter etc.). Note the shared ippiGetBorderType there does not map BORDER_REFLECT_101; +// widening it would also affect warp_ipp.cpp, so keep this filter-specific mapping separate +// (or add a dedicated filter border helper) when consolidating. +static inline IppiBorderType ippiGetBorder(::ipp::IwiImage &image, int ocvBorderType, ::ipp::IwiBorderSize &borderSize) +{ + int inMemFlags = 0; + int borderTypeNI = ocvBorderType & ~cv::BORDER_ISOLATED; + IppiBorderType border = borderTypeNI == cv::BORDER_CONSTANT ? ippBorderConst : + borderTypeNI == cv::BORDER_TRANSPARENT ? ippBorderTransp : + borderTypeNI == cv::BORDER_REPLICATE ? ippBorderRepl : + borderTypeNI == cv::BORDER_REFLECT_101 ? ippBorderMirror : + (IppiBorderType)-1; + if((int)border == -1) + return (IppiBorderType)0; + + if(!(ocvBorderType & cv::BORDER_ISOLATED)) + { + if(image.m_inMemSize.left) + { + if(image.m_inMemSize.left >= borderSize.left) + inMemFlags |= ippBorderInMemLeft; + else + return (IppiBorderType)0; + } + else + borderSize.left = 0; + if(image.m_inMemSize.top) + { + if(image.m_inMemSize.top >= borderSize.top) + inMemFlags |= ippBorderInMemTop; + else + return (IppiBorderType)0; + } + else + borderSize.top = 0; + if(image.m_inMemSize.right) + { + if(image.m_inMemSize.right >= borderSize.right) + inMemFlags |= ippBorderInMemRight; + else + return (IppiBorderType)0; + } + else + borderSize.right = 0; + if(image.m_inMemSize.bottom) + { + if(image.m_inMemSize.bottom >= borderSize.bottom) + inMemFlags |= ippBorderInMemBottom; + else + return (IppiBorderType)0; + } + else + borderSize.bottom = 0; + } + else + borderSize.left = borderSize.right = borderSize.top = borderSize.bottom = 0; + + return (IppiBorderType)(border|inMemFlags); +} + +// Shared worker for Sobel (useScharr == false) and Scharr (useScharr == true). +static int ipp_Deriv(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, + int width, int height, int src_depth, int dst_depth, int cn, + int margin_left, int margin_top, int margin_right, int margin_bottom, + int dx, int dy, int ksize, double scale, double delta, int borderType, + bool useScharr) +{ + IppDataType srcType = ippiGetDataType(src_depth); + IppDataType dstType = ippiGetDataType(dst_depth); + bool useScale = false; + + if(cn < 1 || cn > 4) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + if(src_depth < 0 || src_depth >= CV_DEPTH_MAX || dst_depth < 0 || dst_depth >= CV_DEPTH_MAX) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + + // Supported (source depth -> destination depth) x channels combinations. + // Index order: [src_depth][dst_depth][channel-1]; depths are 8U,8S,16U,16S,32S,32F,64F. + // IPP iwiFilterSobel/iwiFilterScharr provide only single-channel kernels for + // 8u->16s, 16s->16s and 32f->32f; 8u->8u is realized as an 8u->16s filter plus a 16s->8u scale. + // 8u->32f and 16s->32f are intentionally left disabled: IPP would need an extra full-image + // conversion pass there and is slower than OpenCV's fused sepFilter2D. + /* dst: 8U 8S 16U 16S 32S 32F 64F */ +#if defined(IPP_CALLS_ENFORCED) + const char impl[CV_DEPTH_MAX][CV_DEPTH_MAX][4] = { + /* src 8U */ {{1,0,0,0},{0,0,0,0},{0,0,0,0},{1,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}}, + /* src 8S */ {{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}}, + /* src 16U */ {{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}}, + /* src 16S */ {{0,0,0,0},{0,0,0,0},{0,0,0,0},{1,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}}, + /* src 32S */ {{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}}, + /* src 32F */ {{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{1,0,0,0},{0,0,0,0}}, + /* src 64F */ {{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}}}; +#else + const char impl[CV_DEPTH_MAX][CV_DEPTH_MAX][4] = { + /* src 8U */ {{1,0,0,0},{0,0,0,0},{0,0,0,0},{1,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}}, + /* src 8S */ {{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}}, + /* src 16U */ {{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}}, + /* src 16S */ {{0,0,0,0},{0,0,0,0},{0,0,0,0},{1,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}}, + /* src 32S */ {{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}}, + /* src 32F */ {{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{1,0,0,0},{0,0,0,0}}, + /* src 64F */ {{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}}}; +#endif // IPP_CALLS_ENFORCED + if(impl[src_depth][dst_depth][cn-1] == 0) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + + if(fabs(delta) > FLT_EPSILON || fabs(scale-1) > FLT_EPSILON) + useScale = true; + + // cv::Sobel accepts ksize == FILTER_SCHARR (-1, i.e. ksize <= 0) which selects the 3x3 + // Scharr derivative, so the Sobel entry point may legitimately request a Scharr filter. + if(ksize <= 0) + { + ksize = 3; + useScharr = true; + } + + IppiMaskSize maskSize = ippiGetMaskSize(ksize, ksize); + if((int)maskSize < 0) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + +#if IPP_VERSION_X100 <= 201703 + // Bug with mirror wrap + if(borderType == cv::BORDER_REFLECT_101 && (ksize/2+1 > width || ksize/2+1 > height)) + return CV_HAL_ERROR_NOT_IMPLEMENTED; +#endif + + IwiDerivativeType derivType = ippiGetDerivType(dx, dy, (useScharr)?false:true); + if((int)derivType < 0) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + + try + { + ::ipp::IwiImage iwSrc = ippiGetImage(src_depth, cn, src_data, src_step, width, height, + margin_left, margin_top, margin_right, margin_bottom); + ::ipp::IwiImage iwDst = ippiGetImage(dst_depth, cn, dst_data, dst_step, width, height, + 0, 0, 0, 0); + ::ipp::IwiImage iwSrcProc = iwSrc; + ::ipp::IwiImage iwDstProc = iwDst; + ::ipp::IwiBorderSize borderSize(maskSize); + ::ipp::IwiBorderType ippBorder(ippiGetBorder(iwSrc, borderType, borderSize)); + if(!ippBorder) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + + // IPP needs an extra iwiScale pass for 32f output with scale/delta, slower than OpenCV's fused approach + if(useScale && dstType == ipp32f) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + + if(srcType == ipp8u && dstType == ipp8u) + { + iwDstProc.Alloc(iwDst.m_size, ipp16s, cn); + useScale = true; + } + + if(useScharr) + CV_INSTRUMENT_FUN_IPP(::ipp::iwiFilterScharr, iwSrcProc, iwDstProc, derivType, maskSize, ::ipp::IwDefault(), ippBorder); + else + CV_INSTRUMENT_FUN_IPP(::ipp::iwiFilterSobel, iwSrcProc, iwDstProc, derivType, maskSize, ::ipp::IwDefault(), ippBorder); + + if(useScale) + CV_INSTRUMENT_FUN_IPP(::ipp::iwiScale, iwDstProc, iwDst, scale, delta, ::ipp::IwiScaleParams(ippAlgHintFast)); + } + catch (const ::ipp::IwException &) + { + return CV_HAL_ERROR_NOT_IMPLEMENTED; + } + + return CV_HAL_ERROR_OK; +} + +int ipp_hal_sobel(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, + int width, int height, int src_depth, int dst_depth, int cn, + int margin_left, int margin_top, int margin_right, int margin_bottom, + int dx, int dy, int ksize, double scale, double delta, int border_type) +{ + return ipp_Deriv(src_data, src_step, dst_data, dst_step, width, height, src_depth, dst_depth, cn, + margin_left, margin_top, margin_right, margin_bottom, + dx, dy, ksize, scale, delta, border_type, false); +} + +int ipp_hal_scharr(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, + int width, int height, int src_depth, int dst_depth, int cn, + int margin_left, int margin_top, int margin_right, int margin_bottom, + int dx, int dy, double scale, double delta, int border_type) +{ + return ipp_Deriv(src_data, src_step, dst_data, dst_step, width, height, src_depth, dst_depth, cn, + margin_left, margin_top, margin_right, margin_bottom, + dx, dy, 0, scale, delta, border_type, true); +} + +#endif // HAVE_IPP_IW + +#endif // IPP_VERSION_X100 >= 810 diff --git a/modules/imgproc/src/deriv.cpp b/modules/imgproc/src/deriv.cpp index e6a2c6754f..b909080e6e 100644 --- a/modules/imgproc/src/deriv.cpp +++ b/modules/imgproc/src/deriv.cpp @@ -181,103 +181,6 @@ cv::Ptr cv::createDerivFilter(int srcType, int dstType, kx, ky, Point(-1,-1), 0, borderType ); } - -#if defined HAVE_IPP -namespace cv -{ - -static bool ipp_Deriv(InputArray _src, OutputArray _dst, int dx, int dy, int ksize, double scale, double delta, int borderType) -{ -#ifdef HAVE_IPP_IW - CV_INSTRUMENT_REGION_IPP(); - - ::ipp::IwiSize size(_src.size().width, _src.size().height); - IppDataType srcType = ippiGetDataType(_src.depth()); - IppDataType dstType = ippiGetDataType(_dst.depth()); - int channels = _src.channels(); - bool useScale = false; - bool useScharr = false; - - if(channels != _dst.channels() || channels > 1) - return false; - - if(fabs(delta) > FLT_EPSILON || fabs(scale-1) > FLT_EPSILON) - useScale = true; - - if(ksize <= 0) - { - ksize = 3; - useScharr = true; - } - - IppiMaskSize maskSize = ippiGetMaskSize(ksize, ksize); - if((int)maskSize < 0) - return false; - -#if IPP_VERSION_X100 <= 201703 - // Bug with mirror wrap - if(borderType == BORDER_REFLECT_101 && (ksize/2+1 > size.width || ksize/2+1 > size.height)) - return false; -#endif - - IwiDerivativeType derivType = ippiGetDerivType(dx, dy, (useScharr)?false:true); - if((int)derivType < 0) - return false; - - // Acquire data and begin processing - try - { - Mat src = _src.getMat(); - Mat dst = _dst.getMat(); - ::ipp::IwiImage iwSrc = ippiGetImage(src); - ::ipp::IwiImage iwDst = ippiGetImage(dst); - ::ipp::IwiImage iwSrcProc = iwSrc; - ::ipp::IwiImage iwDstProc = iwDst; - ::ipp::IwiBorderSize borderSize(maskSize); - ::ipp::IwiBorderType ippBorder(ippiGetBorder(iwSrc, borderType, borderSize)); - if(!ippBorder) - return false; - - // IPP path for 8u->32f does an extra full-image conversion pass, OpenCV's fused sepFilter2D is better - if(srcType == ipp8u && dstType == ipp32f) - return false; - - // IPP could optimize more for 16s->32f (extra conversion overhead) - if(srcType == ipp16s && dstType == ipp32f) - return false; - - // IPP extra iwiScale pass for 32f output with scale/delta could be better than OpenCV's fused approach - if(useScale && dstType == ipp32f) - return false; - - if(srcType == ipp8u && dstType == ipp8u) - { - iwDstProc.Alloc(iwDst.m_size, ipp16s, channels); - useScale = true; - } - - if(useScharr) - CV_INSTRUMENT_FUN_IPP(::ipp::iwiFilterScharr, iwSrcProc, iwDstProc, derivType, maskSize, ::ipp::IwDefault(), ippBorder); - else - CV_INSTRUMENT_FUN_IPP(::ipp::iwiFilterSobel, iwSrcProc, iwDstProc, derivType, maskSize, ::ipp::IwDefault(), ippBorder); - - if(useScale) - CV_INSTRUMENT_FUN_IPP(::ipp::iwiScale, iwDstProc, iwDst, scale, delta, ::ipp::IwiScaleParams(ippAlgHintFast)); - } - catch (const ::ipp::IwException &) - { - return false; - } - - return true; -#else - CV_UNUSED(_src); CV_UNUSED(_dst); CV_UNUSED(dx); CV_UNUSED(dy); CV_UNUSED(ksize); CV_UNUSED(scale); CV_UNUSED(delta); CV_UNUSED(borderType); - return false; -#endif -} -} -#endif - #ifdef HAVE_OPENCL namespace cv { @@ -383,8 +286,6 @@ void cv::Sobel( InputArray _src, OutputArray _dst, int ddepth, int dx, int dy, CALL_HAL(sobel, cv_hal_sobel, src.ptr(), src.step, dst.ptr(), dst.step, src.cols, src.rows, sdepth, ddepth, cn, ofs.x, ofs.y, wsz.width - src.cols - ofs.x, wsz.height - src.rows - ofs.y, dx, dy, ksize, scale, delta, borderType&~BORDER_ISOLATED); - CV_IPP_RUN_FAST(ipp_Deriv(src, dst, dx, dy, ksize, scale, delta, borderType)); - sepFilter2D(src, dst, ddepth, kx, ky, Point(-1, -1), delta, borderType ); } @@ -435,8 +336,6 @@ void cv::Scharr( InputArray _src, OutputArray _dst, int ddepth, int dx, int dy, CALL_HAL(scharr, cv_hal_scharr, src.ptr(), src.step, dst.ptr(), dst.step, src.cols, src.rows, sdepth, ddepth, cn, ofs.x, ofs.y, wsz.width - src.cols - ofs.x, wsz.height - src.rows - ofs.y, dx, dy, scale, delta, borderType&~BORDER_ISOLATED); - CV_IPP_RUN_FAST(ipp_Deriv(src, dst, dx, dy, 0, scale, delta, borderType)); - sepFilter2D( src, dst, ddepth, kx, ky, Point(-1, -1), delta, borderType ); }