diff --git a/doc/py_tutorials/py_calib3d/py_calibration/py_calibration.markdown b/doc/py_tutorials/py_calib3d/py_calibration/py_calibration.markdown index 401fc45dfb..423f46a262 100644 --- a/doc/py_tutorials/py_calib3d/py_calibration/py_calibration.markdown +++ b/doc/py_tutorials/py_calib3d/py_calibration/py_calibration.markdown @@ -81,7 +81,7 @@ pass in terms of square size). So to find pattern in chess board, we can use the function, **cv.findChessboardCorners()**. We also need to pass what kind of pattern we are looking for, like 8x8 grid, 5x5 grid etc. In this example, we -use 7x6 grid. (Normally a chess board has 8x8 squares and 7x7 internal corners). It returns the +use 6x7 grid. (Normally a chess board has 8x8 squares and 7x7 internal corners). It returns the corner points and retval which will be True if pattern is obtained. These corners will be placed in an order (from left-to-right, top-to-bottom) @@ -107,9 +107,11 @@ import glob # termination criteria criteria = (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 30, 0.001) -# prepare object points, like (0,0,0), (1,0,0), (2,0,0) ....,(6,5,0) -objp = np.zeros((6*7,3), np.float32) -objp[:,:2] = np.mgrid[0:7,0:6].T.reshape(-1,2) +# prepare object points, like (0,0,0), (1,0,0), (2,0,0) ....,(5,6,0) +cols = 6 +rows = 7 +objp = np.zeros((cols*rows,3), np.float32) +objp[:,:2] = np.mgrid[0:cols,0:rows].T.reshape(-1,2) # Arrays to store object points and image points from all the images. objpoints = [] # 3d point in real world space @@ -122,7 +124,7 @@ for fname in images: gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY) # Find the chess board corners - ret, corners = cv.findChessboardCorners(gray, (7,6), None) + ret, corners = cv.findChessboardCorners(gray, (cols, rows), None) # If found, add object points, image points (after refining them) if ret == True: @@ -132,7 +134,7 @@ for fname in images: imgpoints.append(corners2) # Draw and display the corners - cv.drawChessboardCorners(img, (7,6), corners2, ret) + cv.drawChessboardCorners(img, (cols, rows), corners2, ret) cv.imshow('img', img) cv.waitKey(500) diff --git a/doc/py_tutorials/py_calib3d/py_pose/py_pose.markdown b/doc/py_tutorials/py_calib3d/py_pose/py_pose.markdown index fa36540d69..74d30e9c16 100644 --- a/doc/py_tutorials/py_calib3d/py_pose/py_pose.markdown +++ b/doc/py_tutorials/py_calib3d/py_pose/py_pose.markdown @@ -50,12 +50,14 @@ our X axis is drawn from (0,0,0) to (3,0,0), so for Y axis. For Z axis, it is dr (0,0,-3). Negative denotes it is drawn towards the camera. @code{.py} criteria = (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 30, 0.001) -objp = np.zeros((6*7,3), np.float32) -objp[:,:2] = np.mgrid[0:7,0:6].T.reshape(-1,2) +cols = 6 +rows = 7 +objp = np.zeros((cols*rows,3), np.float32) +objp[:,:2] = np.mgrid[0:cols,0:rows].T.reshape(-1,2) axis = np.float32([[3,0,0], [0,3,0], [0,0,-3]]).reshape(-1,3) @endcode -Now, as usual, we load each image. Search for 7x6 grid. If found, we refine it with subcorner +Now, as usual, we load each image. Search for 6x7 grid. If found, we refine it with subcorner pixels. Then to calculate the rotation and translation, we use the function, **cv.solvePnPRansac()**. Once we those transformation matrices, we use them to project our **axis points** to the image plane. In simple words, we find the points on image plane corresponding to @@ -65,7 +67,7 @@ to each of these points using our generateImage() function. Done !!! for fname in glob.glob('left*.jpg'): img = cv.imread(fname) gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY) - ret, corners = cv.findChessboardCorners(gray, (7,6),None) + ret, corners = cv.findChessboardCorners(gray, (cols, rows), None) if ret == True: corners2 = cv.cornerSubPix(gray,corners,(11,11),(-1,-1),criteria) diff --git a/hal/ipp/CMakeLists.txt b/hal/ipp/CMakeLists.txt index beaa91e7f3..531f82bfd3 100644 --- a/hal/ipp/CMakeLists.txt +++ b/hal/ipp/CMakeLists.txt @@ -23,6 +23,10 @@ add_library(ipphal STATIC "${CMAKE_CURRENT_SOURCE_DIR}/src/sum_ipp.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/src/color_ipp.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/src/filter_ipp.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/matchtemplate_ipp.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/canny_ipp.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/threshold_ipp.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/distancetransform_ipp.cpp" ) #TODO: HAVE_IPP_ICV and HAVE_IPP_IW added as private macro till OpenCV itself is @@ -49,6 +53,7 @@ target_include_directories(ipphal PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/src" ${CMAKE_SOURCE_DIR}/modules/core/include ${CMAKE_SOURCE_DIR}/modules/imgproc/include + ${CMAKE_SOURCE_DIR}/modules/geometry/include ${IPP_INCLUDE_DIRS} ) diff --git a/hal/ipp/include/ipp_hal_imgproc.hpp b/hal/ipp/include/ipp_hal_imgproc.hpp index 3c3cf6c111..aebe085795 100644 --- a/hal/ipp/include/ipp_hal_imgproc.hpp +++ b/hal/ipp/include/ipp_hal_imgproc.hpp @@ -52,13 +52,6 @@ int ipp_hal_warpPerspective(int src_type, const uchar *src_data, size_t src_step #endif // IPP_VERSION_X100 >= 202600 -int ipp_hal_remap32f(int src_type, const uchar *src_data, size_t src_step, int src_width, int src_height, - uchar *dst_data, size_t dst_step, int dst_width, int dst_height, - float* mapx, size_t mapx_step, float* mapy, size_t mapy_step, - int interpolation, int border_type, const double border_value[4]); -#undef cv_hal_remap32f -#define cv_hal_remap32f ipp_hal_remap32f - #if defined(HAVE_IPP_IW) int ipp_hal_resize(int src_type, const uchar *src_data, size_t src_step, int src_width, int src_height, uchar *dst_data, size_t dst_step, int dst_width, int dst_height, @@ -77,6 +70,13 @@ int ipp_hal_boxFilter(const uchar* src_data, size_t src_step, uchar* dst_data, s #define cv_hal_boxFilter ipp_hal_boxFilter #endif // defined(HAVE_IPP_IW) && !DISABLE_IPP_BOX_FILTER +int ipp_hal_remap32f(int src_type, const uchar *src_data, size_t src_step, int src_width, int src_height, + uchar *dst_data, size_t dst_step, int dst_width, int dst_height, + float* mapx, size_t mapx_step, float* mapy, size_t mapy_step, + int interpolation, int border_type, const double border_value[4]); +#undef cv_hal_remap32f +#define cv_hal_remap32f ipp_hal_remap32f + #if defined(HAVE_IPP_IW) && !DISABLE_IPP_FILTER2D int ipp_hal_filter2D(const uchar * src_data, size_t src_step, int src_type, uchar * dst_data, size_t dst_step, int dst_type, @@ -174,6 +174,42 @@ int ipp_hal_cvtLabtoBGR(const uchar * src_data, size_t src_step, uchar * dst_dat #define cv_hal_cvtLabtoBGR ipp_hal_cvtLabtoBGR #endif // !IPP_DISABLE_LAB_RGB -#endif //IPP_VERSION_X100 >= 700 +int ipp_hal_cvtRGBAtoMultipliedRGBA(const uchar * src_data, size_t src_step, uchar * dst_data, size_t dst_step, int width, int height); +#undef cv_hal_cvtRGBAtoMultipliedRGBA +#define cv_hal_cvtRGBAtoMultipliedRGBA ipp_hal_cvtRGBAtoMultipliedRGBA + +int ipp_hal_matchTemplate(const uchar* src_data, size_t src_step, int src_width, int src_height, + const uchar* templ_data, size_t templ_step, int templ_width, int templ_height, + float* result_data, size_t result_step, int depth, int cn, int method); +#undef cv_hal_matchTemplate +#define cv_hal_matchTemplate ipp_hal_matchTemplate + +int ipp_hal_threshold(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, + int width, int height, int depth, int cn, double thresh, double maxValue, int thresholdType); +#undef cv_hal_threshold +#define cv_hal_threshold ipp_hal_threshold + +int ipp_hal_distanceTransform(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, + int width, int height, int dst_type, int dist_type, int mask_size); +#undef cv_hal_distanceTransform +#define cv_hal_distanceTransform ipp_hal_distanceTransform + +#endif // IPP_VERSION_X100 >= 700 + +#define IPP_DISABLE_PERF_CANNY_MT 1 // cv::Canny OpenCV MT performance is better + +#if defined(HAVE_IPP_IW) +int ipp_hal_canny(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, + int width, int height, int cn, + double lowThreshold, double highThreshold, int ksize, bool L2gradient); +#undef cv_hal_canny +#define cv_hal_canny ipp_hal_canny + +int ipp_hal_canny_deriv(const short* dx_data, size_t dx_step, const short* dy_data, size_t dy_step, + uchar* dst_data, size_t dst_step, int width, int height, int cn, + double lowThreshold, double highThreshold, bool L2gradient); +#undef cv_hal_canny_deriv +#define cv_hal_canny_deriv ipp_hal_canny_deriv +#endif #endif //__IPP_HAL_IMGPROC_HPP__ diff --git a/hal/ipp/src/canny_ipp.cpp b/hal/ipp/src/canny_ipp.cpp new file mode 100644 index 0000000000..d11a580b73 --- /dev/null +++ b/hal/ipp/src/canny_ipp.cpp @@ -0,0 +1,95 @@ +// 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, BigVision LLC, all rights reserved. +// Third party copyrights are property of their respective owners. + +#include "ipp_hal_imgproc.hpp" + +#ifdef HAVE_IPP_IW + +#include +#include "precomp_ipp.hpp" + +int ipp_hal_canny(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, + int width, int height, int cn, + double lowThreshold, double highThreshold, int ksize, bool L2gradient) +{ + CV_HAL_CHECK_USE_IPP(); + +#if IPP_DISABLE_PERF_CANNY_MT + if(cv::getNumThreads() > 1) + return CV_HAL_ERROR_NOT_IMPLEMENTED; +#endif + + if(width <= 3 || height <= 3) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + + if(cn != 1) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + + IppiMaskSize kernel; + if(ksize == 3) + kernel = ippMskSize3x3; + else if(ksize == 5) + kernel = ippMskSize5x5; + else + return CV_HAL_ERROR_NOT_IMPLEMENTED; + + IppNormType norm = L2gradient ? ippNormL2 : ippNormL1; + + try + { + ::ipp::IwiImage iwSrc, iwDst; + iwSrc.Init(IwiSize{width, height}, ipp8u, cn, ::ipp::IwiBorderSize(), (void*)src_data, IwSize(src_step)); + iwDst.Init(IwiSize{width, height}, ipp8u, cn, ::ipp::IwiBorderSize(), dst_data, IwSize(dst_step)); + + CV_INSTRUMENT_FUN_IPP(::ipp::iwiFilterCanny, iwSrc, iwDst, (float)lowThreshold, (float)highThreshold, + ::ipp::IwiFilterCannyParams(ippFilterSobel, kernel, norm), ippBorderRepl); + } + catch (const ::ipp::IwException &) + { + return CV_HAL_ERROR_NOT_IMPLEMENTED; + } + + return CV_HAL_ERROR_OK; +} + +int ipp_hal_canny_deriv(const short* dx_data, size_t dx_step, const short* dy_data, size_t dy_step, + uchar* dst_data, size_t dst_step, int width, int height, int cn, + double lowThreshold, double highThreshold, bool L2gradient) +{ + CV_HAL_CHECK_USE_IPP(); + +#if IPP_DISABLE_PERF_CANNY_MT + if(cv::getNumThreads() > 1) + return CV_HAL_ERROR_NOT_IMPLEMENTED; +#endif + + if(width <= 3 || height <= 3) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + + if(cn != 1) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + + IppNormType norm = L2gradient ? ippNormL2 : ippNormL1; + + try + { + ::ipp::IwiImage iwSrcDx, iwSrcDy, iwDst; + iwSrcDx.Init(IwiSize{width, height}, ipp16s, cn, ::ipp::IwiBorderSize(), (void*)dx_data, IwSize(dx_step)); + iwSrcDy.Init(IwiSize{width, height}, ipp16s, cn, ::ipp::IwiBorderSize(), (void*)dy_data, IwSize(dy_step)); + iwDst.Init(IwiSize{width, height}, ipp8u, cn, ::ipp::IwiBorderSize(), dst_data, IwSize(dst_step)); + + CV_INSTRUMENT_FUN_IPP(::ipp::iwiFilterCannyDeriv, iwSrcDx, iwSrcDy, iwDst, (float)lowThreshold, (float)highThreshold, + ::ipp::IwiFilterCannyDerivParams(norm)); + } + catch (const ::ipp::IwException &) + { + return CV_HAL_ERROR_NOT_IMPLEMENTED; + } + + return CV_HAL_ERROR_OK; +} + +#endif diff --git a/hal/ipp/src/distancetransform_ipp.cpp b/hal/ipp/src/distancetransform_ipp.cpp new file mode 100644 index 0000000000..65ded9a5b6 --- /dev/null +++ b/hal/ipp/src/distancetransform_ipp.cpp @@ -0,0 +1,149 @@ +// 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, BigVision LLC, all rights reserved. +// Third party copyrights are property of their respective owners. + +#include "ipp_hal_imgproc.hpp" + +#include +#include +#include + +#include +#include + +#if IPP_VERSION_X100 >= 700 + +// Mirrors CV_IPP_MALLOC from modules/core/include/opencv2/core/private.hpp: ippicv (IPP >= 2017) +// exposes only the 64-bit ippMalloc_L. +#if IPP_VERSION_X100 >= 201700 +#define IPP_HAL_MALLOC(SIZE) ippMalloc_L(SIZE) +#else +#define IPP_HAL_MALLOC(SIZE) ippMalloc((int)(SIZE)) +#endif + +#ifndef IPP_DISABLE_PERF_TRUE_DIST_MT +#define IPP_DISABLE_PERF_TRUE_DIST_MT 1 +#endif + +// Fixed distance-transform mask weights, mirroring getDistanceTransformMask() in +// modules/imgproc/src/distransform.cpp. maskType == distTypeIndex + mask_size*10. +static bool ippGetDistanceTransformMask(int dist_type, int mask_size, float *metrics) +{ + int maskType = (dist_type == cv::DIST_C ? 0 : dist_type == cv::DIST_L1 ? 1 : 2) + mask_size * 10; + switch (maskType) + { + case 30: metrics[0] = 1.0f; metrics[1] = 1.0f; break; + case 31: metrics[0] = 1.0f; metrics[1] = 2.0f; break; + case 32: metrics[0] = 0.955f; metrics[1] = 1.3693f; break; + case 50: metrics[0] = 1.0f; metrics[1] = 1.0f; metrics[2] = 2.0f; break; + case 51: metrics[0] = 1.0f; metrics[1] = 2.0f; metrics[2] = 3.0f; break; + case 52: metrics[0] = 1.0f; metrics[1] = 1.4f; metrics[2] = 2.1969f; break; + default: + return false; + } + return true; +} + +int ipp_hal_distanceTransform(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, + int width, int height, int dst_type, int dist_type, int mask_size) +{ + CV_HAL_CHECK_USE_IPP(); + + IppiSize roi = { width, height }; + + // DIST_L1 with 8-bit output: fixed L1 / 3x3 metrics. + if (dst_type == CV_8U) + { + Ipp32s pMetrics[2] = { 1, 2 }; // L1, 3x3 mask + if (CV_INSTRUMENT_FUN_IPP(ippiDistanceTransform_3x3_8u_C1R, src_data, (int)src_step, + dst_data, (int)dst_step, roi, pMetrics) >= 0) + { + return CV_HAL_ERROR_OK; + } + return CV_HAL_ERROR_NOT_IMPLEMENTED; + } + + // All remaining paths produce a 32-bit float distance map. + if (dst_type != CV_32F) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + + if (mask_size == cv::DIST_MASK_PRECISE) + { + // 4097 can't square into a float. +#if IPP_DISABLE_PERF_TRUE_DIST_MT + if (!((cv::getNumThreads() <= 1 || ((size_t)width * height < (size_t)(1 << 14))) && + height < 4097 && width < 4097)) + { + return CV_HAL_ERROR_NOT_IMPLEMENTED; + } +#endif + + int bufSize = 0; + if (ippiTrueDistanceTransformGetBufferSize_8u32f_C1R(roi, &bufSize) < 0) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + + Ipp8u *pBuffer = (Ipp8u *)IPP_HAL_MALLOC(bufSize); + if (!pBuffer) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + + IppStatus status = CV_INSTRUMENT_FUN_IPP(ippiTrueDistanceTransform_8u32f_C1R, src_data, (int)src_step, + (Ipp32f *)dst_data, (int)dst_step, roi, pBuffer); + ippFree(pBuffer); + if (status < 0) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + + // https://github.com/opencv/opencv/issues/24082 + // There is probably a rounding issue that leads to non-deterministic behavior + // between runs on positions closer to zeros by x-axis in straight direction. + // As a workaround, we detect the distances that expected to be exact + // number of pixels and round manually. + static const float correctionDiff = 1.0f / (1 << 11); + for (int i = 0; i < height; ++i) + { + float* row = (float*)(dst_data + (size_t)i * dst_step); + for (int j = 0; j < width; ++j) + { + float rounded = static_cast(cvRound(row[j])); + if (std::fabs(row[j] - rounded) <= correctionDiff) + row[j] = rounded; + } + } + return CV_HAL_ERROR_OK; + } + + // DIST_MASK_3 / DIST_MASK_5 chamfer distances. + if (mask_size != cv::DIST_MASK_3 && mask_size != cv::DIST_MASK_5) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + + // IPP uses 32-bit signed intermediates; bail out when the image is too large. + bool has_int_overflow = (int64)width * height >= INT_MAX; + if (has_int_overflow) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + + float metrics[5] = {0}; + if (!ippGetDistanceTransformMask(dist_type, mask_size, metrics)) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + + if (mask_size == cv::DIST_MASK_3) + { + if (CV_INSTRUMENT_FUN_IPP(ippiDistanceTransform_3x3_8u32f_C1R, src_data, (int)src_step, + (Ipp32f *)dst_data, (int)dst_step, roi, metrics) >= 0) + { + return CV_HAL_ERROR_OK; + } + } + else // DIST_MASK_5 + { + if (CV_INSTRUMENT_FUN_IPP(ippiDistanceTransform_5x5_8u32f_C1R, src_data, (int)src_step, + (Ipp32f *)dst_data, (int)dst_step, roi, metrics) >= 0) + { + return CV_HAL_ERROR_OK; + } + } + + return CV_HAL_ERROR_NOT_IMPLEMENTED; +} + +#endif // IPP_VERSION_X100 >= 700 diff --git a/hal/ipp/src/matchtemplate_ipp.cpp b/hal/ipp/src/matchtemplate_ipp.cpp new file mode 100644 index 0000000000..7ff0807aa6 --- /dev/null +++ b/hal/ipp/src/matchtemplate_ipp.cpp @@ -0,0 +1,144 @@ +// 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, BigVision LLC, all rights reserved. +// Third party copyrights are property of their respective owners. + +#include "ipp_hal_imgproc.hpp" + +#include +#include "precomp_ipp.hpp" + +#if IPP_VERSION_X100 >= 700 + +using namespace cv; + +#if IPP_VERSION_X100 >= 201700 +#define IPP_HAL_MALLOC(SIZE) ippMalloc_L(SIZE) +#else +#define IPP_HAL_MALLOC(SIZE) ippMalloc((int)(SIZE)) +#endif + +typedef IppStatus (CV_STDCALL * ippimatchTemplate)(const void*, int, IppiSize, const void*, int, IppiSize, Ipp32f* , int , IppEnum , Ipp8u*); + +static bool ipp_crossCorr(const Mat& src, const Mat& tpl, Mat& dst, bool normed) +{ + IppStatus status; + + IppiSize srcRoiSize = {src.cols,src.rows}; + IppiSize tplRoiSize = {tpl.cols,tpl.rows}; + + int bufSize=0; + + int depth = src.depth(); + + ippimatchTemplate ippiCrossCorrNorm = + depth==CV_8U ? (ippimatchTemplate)ippiCrossCorrNorm_8u32f_C1R: + depth==CV_32F? (ippimatchTemplate)ippiCrossCorrNorm_32f_C1R: 0; + + if (ippiCrossCorrNorm==0) + return false; + + IppEnum funCfg = (IppEnum)(+ippAlgAuto | ippiROIValid); + if(normed) + funCfg |= ippiNorm; + else + funCfg |= ippiNormNone; + + status = ippiCrossCorrNormGetBufferSize(srcRoiSize, tplRoiSize, funCfg, &bufSize); + if ( status < 0 ) + return false; + + Ipp8u* buffer = bufSize > 0 ? (Ipp8u*)IPP_HAL_MALLOC(bufSize) : 0; + if (bufSize > 0 && buffer == 0) + return false; + + status = CV_INSTRUMENT_FUN_IPP(ippiCrossCorrNorm, src.ptr(), (int)src.step, srcRoiSize, tpl.ptr(), (int)tpl.step, tplRoiSize, dst.ptr(), (int)dst.step, funCfg, buffer); + + if (buffer) + ippFree(buffer); + return status >= 0; +} + +static bool ipp_sqrDistance(const Mat& src, const Mat& tpl, Mat& dst) +{ + IppStatus status; + + IppiSize srcRoiSize = {src.cols,src.rows}; + IppiSize tplRoiSize = {tpl.cols,tpl.rows}; + + int bufSize=0; + + int depth = src.depth(); + + ippimatchTemplate ippiSqrDistanceNorm = + depth==CV_8U ? (ippimatchTemplate)ippiSqrDistanceNorm_8u32f_C1R: + depth==CV_32F? (ippimatchTemplate)ippiSqrDistanceNorm_32f_C1R: 0; + + if (ippiSqrDistanceNorm==0) + return false; + + IppEnum funCfg = (IppEnum)(+ippAlgAuto | ippiROIValid | ippiNormNone); + status = ippiSqrDistanceNormGetBufferSize(srcRoiSize, tplRoiSize, funCfg, &bufSize); + if ( status < 0 ) + return false; + + Ipp8u* buffer = bufSize > 0 ? (Ipp8u*)IPP_HAL_MALLOC(bufSize) : 0; + if (bufSize > 0 && buffer == 0) + return false; + + status = CV_INSTRUMENT_FUN_IPP(ippiSqrDistanceNorm, src.ptr(), (int)src.step, srcRoiSize, tpl.ptr(), (int)tpl.step, tplRoiSize, dst.ptr(), (int)dst.step, funCfg, buffer); + + if (buffer) + ippFree(buffer); + dst = cv::max(dst, 0); // handle edge case from rounding in variance computation which can result in negative values + return status >= 0; +} + +int ipp_hal_matchTemplate(const uchar* src_data, size_t src_step, int src_width, int src_height, + const uchar* templ_data, size_t templ_step, int templ_width, int templ_height, + float* result_data, size_t result_step, int depth, int cn, int method) +{ + CV_HAL_CHECK_USE_IPP(); + + if(cn != 1) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + if(depth != CV_8U && depth != CV_32F) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + + Mat img(src_height, src_width, CV_MAKETYPE(depth, cn), (void*)src_data, src_step); + Mat templ(templ_height, templ_width, CV_MAKETYPE(depth, cn), (void*)templ_data, templ_step); + Mat result(src_height - templ_height + 1, src_width - templ_width + 1, CV_32FC1, (void*)result_data, result_step); + + // These functions are not efficient if template size is comparable with image size + if(templ.size().area()*4 > img.size().area()) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + + // CV_8U SQDIFF/SQDIFF_NORMED suffer from float32 catastrophic cancellation + // in IPP's internal accumulators; fall through to the double-precision path instead. + if(depth == CV_8U && (method == cv::TM_SQDIFF || method == cv::TM_SQDIFF_NORMED)) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + + if(method == cv::TM_SQDIFF) + { + if(ipp_sqrDistance(img, templ, result)) + return CV_HAL_ERROR_OK; + } + else if(method == cv::TM_CCORR_NORMED) + { + if(ipp_crossCorr(img, templ, result, true)) + return CV_HAL_ERROR_OK; + } + else if(method == cv::TM_SQDIFF_NORMED || method == cv::TM_CCORR || + method == cv::TM_CCOEFF || method == cv::TM_CCOEFF_NORMED) + { + // Raw cross-correlation; caller finishes SQDIFF_NORMED/CCOEFF/CCOEFF_NORMED via + // common_matchTemplate (TM_CCORR is already final). + if(ipp_crossCorr(img, templ, result, false)) + return CV_HAL_ERROR_OK; + } + + return CV_HAL_ERROR_NOT_IMPLEMENTED; +} + +#endif // IPP_VERSION_X100 >= 700 diff --git a/hal/ipp/src/threshold_ipp.cpp b/hal/ipp/src/threshold_ipp.cpp new file mode 100644 index 0000000000..352b240894 --- /dev/null +++ b/hal/ipp/src/threshold_ipp.cpp @@ -0,0 +1,128 @@ +// 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, BigVision LLC, all rights reserved. +// Third party copyrights are property of their respective owners. + +#include "ipp_hal_imgproc.hpp" + +#include +#include "precomp_ipp.hpp" + +#include +#include + +#if IPP_VERSION_X100 >= 700 + +int ipp_hal_threshold(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, + int width, int height, int depth, int cn, double thresh, double maxValue, + int thresholdType) +{ + CV_HAL_CHECK_USE_IPP(); + CV_UNUSED(maxValue); + + // IPP only implements these three types and depths; everything else falls back to OpenCV. + if (thresholdType != cv::THRESH_TRUNC && thresholdType != cv::THRESH_TOZERO && + thresholdType != cv::THRESH_TOZERO_INV) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + if (depth != CV_8U && depth != CV_16S && depth != CV_32F) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + + int roi_width = width * cn; // threshold is elementwise, so channels fold into the width + int roi_height = height; + int elemSize1 = (depth == CV_8U) ? 1 : (depth == CV_16S) ? 2 : 4; + + if (src_step == (size_t)roi_width * elemSize1 && dst_step == (size_t)roi_width * elemSize1) + { + roi_width *= roi_height; + roi_height = 1; + src_step = dst_step = (size_t)roi_width * elemSize1; + } + + IppiSize sz = { roi_width, roi_height }; + const bool inplace = (src_data == dst_data); + IppStatus status = ippStsErr; + + CV_SUPPRESS_DEPRECATED_START + switch (depth) + { + case CV_8U: + { + Ipp8u t = (Ipp8u)thresh; + switch (thresholdType) + { + case cv::THRESH_TRUNC: + if (inplace) + status = CV_INSTRUMENT_FUN_IPP(ippiThreshold_GT_8u_C1IR, dst_data, (int)dst_step, sz, t); + if (!inplace || status < 0) + status = CV_INSTRUMENT_FUN_IPP(ippiThreshold_GT_8u_C1R, src_data, (int)src_step, dst_data, (int)dst_step, sz, t); + break; + case cv::THRESH_TOZERO: + if (inplace) + status = CV_INSTRUMENT_FUN_IPP(ippiThreshold_LTVal_8u_C1IR, dst_data, (int)dst_step, sz, t + 1, 0); + if (!inplace || status < 0) + status = CV_INSTRUMENT_FUN_IPP(ippiThreshold_LTVal_8u_C1R, src_data, (int)src_step, dst_data, (int)dst_step, sz, t + 1, 0); + break; + case cv::THRESH_TOZERO_INV: + if (inplace) + status = CV_INSTRUMENT_FUN_IPP(ippiThreshold_GTVal_8u_C1IR, dst_data, (int)dst_step, sz, t, 0); + if (!inplace || status < 0) + status = CV_INSTRUMENT_FUN_IPP(ippiThreshold_GTVal_8u_C1R, src_data, (int)src_step, dst_data, (int)dst_step, sz, t, 0); + break; + } + break; + } + case CV_16S: + { + const Ipp16s* src = (const Ipp16s*)src_data; + Ipp16s* dst = (Ipp16s*)dst_data; + Ipp16s t = (Ipp16s)thresh; + switch (thresholdType) + { + case cv::THRESH_TRUNC: + if (inplace) + status = CV_INSTRUMENT_FUN_IPP(ippiThreshold_GT_16s_C1IR, dst, (int)dst_step, sz, t); + if (!inplace || status < 0) + status = CV_INSTRUMENT_FUN_IPP(ippiThreshold_GT_16s_C1R, src, (int)src_step, dst, (int)dst_step, sz, t); + break; + case cv::THRESH_TOZERO: + if (inplace) + status = CV_INSTRUMENT_FUN_IPP(ippiThreshold_LTVal_16s_C1IR, dst, (int)dst_step, sz, t + 1, 0); + if (!inplace || status < 0) + status = CV_INSTRUMENT_FUN_IPP(ippiThreshold_LTVal_16s_C1R, src, (int)src_step, dst, (int)dst_step, sz, t + 1, 0); + break; + case cv::THRESH_TOZERO_INV: + if (inplace) + status = CV_INSTRUMENT_FUN_IPP(ippiThreshold_GTVal_16s_C1IR, dst, (int)dst_step, sz, t, 0); + if (!inplace || status < 0) + status = CV_INSTRUMENT_FUN_IPP(ippiThreshold_GTVal_16s_C1R, src, (int)src_step, dst, (int)dst_step, sz, t, 0); + break; + } + break; + } + case CV_32F: + { + const Ipp32f* src = (const Ipp32f*)src_data; + Ipp32f* dst = (Ipp32f*)dst_data; + Ipp32f t = (Ipp32f)thresh; + switch (thresholdType) + { + case cv::THRESH_TRUNC: + status = CV_INSTRUMENT_FUN_IPP(ippiThreshold_GT_32f_C1R, src, (int)src_step, dst, (int)dst_step, sz, t); + break; + case cv::THRESH_TOZERO: + status = CV_INSTRUMENT_FUN_IPP(ippiThreshold_LTVal_32f_C1R, src, (int)src_step, dst, (int)dst_step, sz, nextafterf(t, std::numeric_limits::infinity()), 0); + break; + case cv::THRESH_TOZERO_INV: + status = CV_INSTRUMENT_FUN_IPP(ippiThreshold_GTVal_32f_C1R, src, (int)src_step, dst, (int)dst_step, sz, t, 0); + break; + } + break; + } + } + CV_SUPPRESS_DEPRECATED_END + + return status >= 0 ? CV_HAL_ERROR_OK : CV_HAL_ERROR_NOT_IMPLEMENTED; +} + +#endif // IPP_VERSION_X100 >= 700 diff --git a/hal/ipp/src/warp_ipp.cpp b/hal/ipp/src/warp_ipp.cpp index 17885d69cd..1eea3228ff 100644 --- a/hal/ipp/src/warp_ipp.cpp +++ b/hal/ipp/src/warp_ipp.cpp @@ -235,7 +235,7 @@ int ipp_hal_warpPerspective(int src_type, const uchar *src_data, size_t src_step {{0, 0}, {0, 0}, {0, 0}, {0, 1}}, //16U {{1, 1}, {0, 0}, {1, 0}, {1, 1}}, //16S {{1, 1}, {0, 0}, {1, 0}, {1, 1}}, //32S - {{0, 0}, {0, 0}, {0, 0}, {1, 0}}, //32F + {{0, 0}, {0, 0}, {0, 0}, {0, 0}}, //32F {{0, 0}, {0, 0}, {0, 0}, {0, 0}}}; //64F #endif diff --git a/hal/riscv-rvv/src/imgproc/resize.cpp b/hal/riscv-rvv/src/imgproc/resize.cpp index 79124bf85f..b04e0242d9 100644 --- a/hal/riscv-rvv/src/imgproc/resize.cpp +++ b/hal/riscv-rvv/src/imgproc/resize.cpp @@ -119,6 +119,8 @@ template<> struct rvv { static inline vfloat32m4_t vcvt0(vuint8m1_t a, size_t b) { return __riscv_vfcvt_f(__riscv_vzext_vf4(a, b), b); } static inline vuint8m1_t vcvt1(vfloat32m4_t a, size_t b) { return __riscv_vnclipu(__riscv_vfncvt_xu(a, b), 0, __RISCV_VXRM_RNU, b); } + static inline vint32m4_t vwiden(vuint8m1_t a, size_t b) { return __riscv_vreinterpret_v_u32m4_i32m4(__riscv_vzext_vf4(a, b)); } + static inline vuint8m1_t vnarrow(vint32m4_t a, size_t b) { return __riscv_vncvt_x(__riscv_vncvt_x(__riscv_vreinterpret_v_i32m4_u32m4(a), b), b); } static inline vuint8m1_t vloxei(const uchar* a, vuint16m2_t b, size_t c) { return __riscv_vloxei16_v_u8m1(a, b, c); } static inline void vloxseg2ei(const uchar* a, vuint16m2_t b, size_t c, vuint8m1_t& x, vuint8m1_t& y) { auto src = __riscv_vloxseg2ei16_v_u8m1x2(a, b, c); x = __riscv_vget_v_u8m1x2_u8m1(src, 0); y = __riscv_vget_v_u8m1x2_u8m1(src, 1); } static inline void vloxseg3ei(const uchar* a, vuint16m2_t b, size_t c, vuint8m1_t& x, vuint8m1_t& y, vuint8m1_t& z) { auto src = __riscv_vloxseg3ei16_v_u8m1x3(a, b, c); x = __riscv_vget_v_u8m1x3_u8m1(src, 0); y = __riscv_vget_v_u8m1x3_u8m1(src, 1); z = __riscv_vget_v_u8m1x3_u8m1(src, 2); } @@ -165,6 +167,8 @@ template<> struct rvv { static inline vfloat32m2_t vcvt0(vuint8mf2_t a, size_t b) { return __riscv_vfcvt_f(__riscv_vzext_vf4(a, b), b); } static inline vuint8mf2_t vcvt1(vfloat32m2_t a, size_t b) { return __riscv_vnclipu(__riscv_vfncvt_xu(a, b), 0, __RISCV_VXRM_RNU, b); } + static inline vint32m2_t vwiden(vuint8mf2_t a, size_t b) { return __riscv_vreinterpret_v_u32m2_i32m2(__riscv_vzext_vf4(a, b)); } + static inline vuint8mf2_t vnarrow(vint32m2_t a, size_t b) { return __riscv_vncvt_x(__riscv_vncvt_x(__riscv_vreinterpret_v_i32m2_u32m2(a), b), b); } static inline vuint8mf2_t vloxei(const uchar* a, vuint16m1_t b, size_t c) { return __riscv_vloxei16_v_u8mf2(a, b, c); } static inline void vloxseg2ei(const uchar* a, vuint16m1_t b, size_t c, vuint8mf2_t& x, vuint8mf2_t& y) { auto src = __riscv_vloxseg2ei16_v_u8mf2x2(a, b, c); x = __riscv_vget_v_u8mf2x2_u8mf2(src, 0); y = __riscv_vget_v_u8mf2x2_u8mf2(src, 1); } static inline void vloxseg3ei(const uchar* a, vuint16m1_t b, size_t c, vuint8mf2_t& x, vuint8mf2_t& y, vuint8mf2_t& z) { auto src = __riscv_vloxseg3ei16_v_u8mf2x3(a, b, c); x = __riscv_vget_v_u8mf2x3_u8mf2(src, 0); y = __riscv_vget_v_u8mf2x3_u8mf2(src, 1); z = __riscv_vget_v_u8mf2x3_u8mf2(src, 2); } @@ -332,6 +336,150 @@ static inline int resizeLinear(int start, int end, const uchar *src_data, size_t return CV_HAL_ERROR_OK; } +static const int INTER_RESIZE_COEF_BITS = 11; +static const int INTER_RESIZE_COEF_SCALE = 1 << INTER_RESIZE_COEF_BITS; + +// Bit-exact fixed-point vertical+horizontal linear combine for one channel, matching the reference +// HResizeLinear + VResizeLinear> in modules/imgproc/src/resize.cpp. +// Sxy are the four widened (int32) source corners; a0/a1 the horizontal weights (per column), +// b0/b1 the vertical weights (per row). The staged >>4 / >>16 / >>2 shifts (total 22) reproduce the +// reference exactly and keep the intermediates within int32. +template +static inline VI resizeLinearU8Combine(VI S00, VI S01, VI S10, VI S11, VI a0, VI a1, int b0, int b1, size_t vl) +{ + VI h0 = __riscv_vmacc(__riscv_vmul(S00, a0, vl), S01, a1, vl); // S[y0][x0]*a0 + S[y0][x1]*a1 + VI h1 = __riscv_vmacc(__riscv_vmul(S10, a0, vl), S11, a1, vl); // S[y1][x0]*a0 + S[y1][x1]*a1 + h0 = __riscv_vsra(__riscv_vmul(__riscv_vsra(h0, 4, vl), b0, vl), 16, vl); // (b0*(h0>>4))>>16 + h1 = __riscv_vsra(__riscv_vmul(__riscv_vsra(h1, 4, vl), b1, vl), 16, vl); // (b1*(h1>>4))>>16 + return __riscv_vsra(__riscv_vadd(__riscv_vadd(h0, h1, vl), 2, vl), 2, vl); // (t0 + t1 + 2) >> 2 +} + +// the algorithm is copied from imgproc/src/resize.cpp, HResizeLinear + VResizeLinear for uchar. +// The generic resizeLinear<> above computes in float, which is bit-exact only for 16U/32F; 8U +// INTER_LINEAR must use this fixed-point path to match the reference (float drift breaks tight +// consumers such as DNN blobFromImage preprocessing, issue #28870). +template +static inline int resizeLinearU8(int start, int end, const uchar *src_data, size_t src_step, int src_height, uchar *dst_data, size_t dst_step, int dst_width, double scale_y, const ushort* x_ofs0, const ushort* x_ofs1, const int* ialpha0, const int* ialpha1) +{ + for (int i = start; i < end; i++) + { + float my = (i + 0.5) * scale_y - 0.5; + int y_ofs = static_cast(std::floor(my)); + my -= y_ofs; + + int y_ofs0 = std::min(std::max(y_ofs , 0), src_height - 1); + int y_ofs1 = std::min(std::max(y_ofs + 1, 0), src_height - 1); + int b0 = saturate_cast((1.f - my) * INTER_RESIZE_COEF_SCALE); + int b1 = saturate_cast(my * INTER_RESIZE_COEF_SCALE); + + const uchar* S0 = src_data + y_ofs0 * src_step; + const uchar* S1 = src_data + y_ofs1 * src_step; + + int vl; + switch (cn) + { + case 1: + for (int j = 0; j < dst_width; j += vl) + { + vl = helper::setvl(dst_width - j); + auto ptr0 = RVV_SameLen::vload(x_ofs0 + j, vl); + auto ptr1 = RVV_SameLen::vload(x_ofs1 + j, vl); + auto a0 = RVV_SameLen::vload(ialpha0 + j, vl); + auto a1 = RVV_SameLen::vload(ialpha1 + j, vl); + + auto v00 = rvv::vwiden(rvv::vloxei(S0, ptr0, vl), vl); + auto v01 = rvv::vwiden(rvv::vloxei(S0, ptr1, vl), vl); + auto v02 = rvv::vwiden(rvv::vloxei(S1, ptr0, vl), vl); + auto v03 = rvv::vwiden(rvv::vloxei(S1, ptr1, vl), vl); + + auto d0 = resizeLinearU8Combine(v00, v01, v02, v03, a0, a1, b0, b1, vl); + helper::vstore(reinterpret_cast(dst_data + i * dst_step) + j, rvv::vnarrow(d0, vl), vl); + } + break; + case 2: + for (int j = 0; j < dst_width; j += vl) + { + vl = helper::setvl(dst_width - j); + auto ptr0 = RVV_SameLen::vload(x_ofs0 + j, vl); + auto ptr1 = RVV_SameLen::vload(x_ofs1 + j, vl); + auto a0 = RVV_SameLen::vload(ialpha0 + j, vl); + auto a1 = RVV_SameLen::vload(ialpha1 + j, vl); + + typename helper::VecType s0, s1; + rvv::vloxseg2ei(S0, ptr0, vl, s0, s1); + auto v00 = rvv::vwiden(s0, vl), v10 = rvv::vwiden(s1, vl); + rvv::vloxseg2ei(S0, ptr1, vl, s0, s1); + auto v01 = rvv::vwiden(s0, vl), v11 = rvv::vwiden(s1, vl); + rvv::vloxseg2ei(S1, ptr0, vl, s0, s1); + auto v02 = rvv::vwiden(s0, vl), v12 = rvv::vwiden(s1, vl); + rvv::vloxseg2ei(S1, ptr1, vl, s0, s1); + auto v03 = rvv::vwiden(s0, vl), v13 = rvv::vwiden(s1, vl); + + auto d0 = resizeLinearU8Combine(v00, v01, v02, v03, a0, a1, b0, b1, vl); + auto d1 = resizeLinearU8Combine(v10, v11, v12, v13, a0, a1, b0, b1, vl); + rvv::vsseg2e(reinterpret_cast(dst_data + i * dst_step) + j * 2, vl, rvv::vnarrow(d0, vl), rvv::vnarrow(d1, vl)); + } + break; + case 3: + for (int j = 0; j < dst_width; j += vl) + { + vl = helper::setvl(dst_width - j); + auto ptr0 = RVV_SameLen::vload(x_ofs0 + j, vl); + auto ptr1 = RVV_SameLen::vload(x_ofs1 + j, vl); + auto a0 = RVV_SameLen::vload(ialpha0 + j, vl); + auto a1 = RVV_SameLen::vload(ialpha1 + j, vl); + + typename helper::VecType s0, s1, s2; + rvv::vloxseg3ei(S0, ptr0, vl, s0, s1, s2); + auto v00 = rvv::vwiden(s0, vl), v10 = rvv::vwiden(s1, vl), v20 = rvv::vwiden(s2, vl); + rvv::vloxseg3ei(S0, ptr1, vl, s0, s1, s2); + auto v01 = rvv::vwiden(s0, vl), v11 = rvv::vwiden(s1, vl), v21 = rvv::vwiden(s2, vl); + rvv::vloxseg3ei(S1, ptr0, vl, s0, s1, s2); + auto v02 = rvv::vwiden(s0, vl), v12 = rvv::vwiden(s1, vl), v22 = rvv::vwiden(s2, vl); + rvv::vloxseg3ei(S1, ptr1, vl, s0, s1, s2); + auto v03 = rvv::vwiden(s0, vl), v13 = rvv::vwiden(s1, vl), v23 = rvv::vwiden(s2, vl); + + auto d0 = resizeLinearU8Combine(v00, v01, v02, v03, a0, a1, b0, b1, vl); + auto d1 = resizeLinearU8Combine(v10, v11, v12, v13, a0, a1, b0, b1, vl); + auto d2 = resizeLinearU8Combine(v20, v21, v22, v23, a0, a1, b0, b1, vl); + rvv::vsseg3e(reinterpret_cast(dst_data + i * dst_step) + j * 3, vl, rvv::vnarrow(d0, vl), rvv::vnarrow(d1, vl), rvv::vnarrow(d2, vl)); + } + break; + case 4: + for (int j = 0; j < dst_width; j += vl) + { + vl = helper::setvl(dst_width - j); + auto ptr0 = RVV_SameLen::vload(x_ofs0 + j, vl); + auto ptr1 = RVV_SameLen::vload(x_ofs1 + j, vl); + auto a0 = RVV_SameLen::vload(ialpha0 + j, vl); + auto a1 = RVV_SameLen::vload(ialpha1 + j, vl); + + typename helper::VecType s0, s1, s2, s3; + rvv::vloxseg4ei(S0, ptr0, vl, s0, s1, s2, s3); + auto v00 = rvv::vwiden(s0, vl), v10 = rvv::vwiden(s1, vl), v20 = rvv::vwiden(s2, vl), v30 = rvv::vwiden(s3, vl); + rvv::vloxseg4ei(S0, ptr1, vl, s0, s1, s2, s3); + auto v01 = rvv::vwiden(s0, vl), v11 = rvv::vwiden(s1, vl), v21 = rvv::vwiden(s2, vl), v31 = rvv::vwiden(s3, vl); + rvv::vloxseg4ei(S1, ptr0, vl, s0, s1, s2, s3); + auto v02 = rvv::vwiden(s0, vl), v12 = rvv::vwiden(s1, vl), v22 = rvv::vwiden(s2, vl), v32 = rvv::vwiden(s3, vl); + rvv::vloxseg4ei(S1, ptr1, vl, s0, s1, s2, s3); + auto v03 = rvv::vwiden(s0, vl), v13 = rvv::vwiden(s1, vl), v23 = rvv::vwiden(s2, vl), v33 = rvv::vwiden(s3, vl); + + auto d0 = resizeLinearU8Combine(v00, v01, v02, v03, a0, a1, b0, b1, vl); + auto d1 = resizeLinearU8Combine(v10, v11, v12, v13, a0, a1, b0, b1, vl); + auto d2 = resizeLinearU8Combine(v20, v21, v22, v23, a0, a1, b0, b1, vl); + auto d3 = resizeLinearU8Combine(v30, v31, v32, v33, a0, a1, b0, b1, vl); + rvv::vsseg4e(reinterpret_cast(dst_data + i * dst_step) + j * 4, vl, rvv::vnarrow(d0, vl), rvv::vnarrow(d1, vl), rvv::vnarrow(d2, vl), rvv::vnarrow(d3, vl)); + } + break; + default: + return CV_HAL_ERROR_NOT_IMPLEMENTED; + } + } + + return CV_HAL_ERROR_OK; +} + template static inline int resizeLinearExact(int start, int end, const uchar *src_data, size_t src_step, int src_height, uchar *dst_data, size_t dst_step, int dst_width, double scale_y, const ushort* x_ofs0, const ushort* x_ofs1, const ushort* x_val) { @@ -833,6 +981,37 @@ static inline int resizeLinear(int src_type, const uchar *src_data, size_t src_s return invoke(dst_height, {resizeLinearExact<4>}, src_data, src_step, src_height, dst_data, dst_step, dst_width, scale_y, x_ofs0.data(), x_ofs1.data(), x_val.data()); } } + else if (CV_MAT_DEPTH(src_type) == CV_8U) + { + // 8U INTER_LINEAR must be bit-exact fixed-point to match the reference implementation; + // computing it in float drifts and breaks tight consumers (e.g. DNN preprocessing, #28870). + std::vector ialpha0(dst_width), ialpha1(dst_width); + for (int i = 0; i < dst_width; i++) + { + float fx = (float)((i + 0.5) * scale_x - 0.5); + int x_ofs = static_cast(std::floor(fx)); + fx -= x_ofs; + if (x_ofs < 0) { fx = 0; x_ofs = 0; } + if (x_ofs >= src_width - 1) { fx = 0; x_ofs = src_width - 1; } + + x_ofs0[i] = static_cast(x_ofs) * cn; + x_ofs1[i] = static_cast(std::min(x_ofs + 1, src_width - 1)) * cn; + ialpha0[i] = saturate_cast((1.f - fx) * INTER_RESIZE_COEF_SCALE); + ialpha1[i] = saturate_cast(fx * INTER_RESIZE_COEF_SCALE); + } + + switch (src_type) + { + case CV_8UC1: + return invoke(dst_height, {resizeLinearU8}, src_data, src_step, src_height, dst_data, dst_step, dst_width, scale_y, x_ofs0.data(), x_ofs1.data(), ialpha0.data(), ialpha1.data()); + case CV_8UC2: + return invoke(dst_height, {resizeLinearU8}, src_data, src_step, src_height, dst_data, dst_step, dst_width, scale_y, x_ofs0.data(), x_ofs1.data(), ialpha0.data(), ialpha1.data()); + case CV_8UC3: + return invoke(dst_height, {resizeLinearU8}, src_data, src_step, src_height, dst_data, dst_step, dst_width, scale_y, x_ofs0.data(), x_ofs1.data(), ialpha0.data(), ialpha1.data()); + case CV_8UC4: + return invoke(dst_height, {resizeLinearU8}, src_data, src_step, src_height, dst_data, dst_step, dst_width, scale_y, x_ofs0.data(), x_ofs1.data(), ialpha0.data(), ialpha1.data()); + } + } else { std::vector x_val(dst_width); diff --git a/modules/core/include/opencv2/core/private.hpp b/modules/core/include/opencv2/core/private.hpp index c6b8514d52..8db9410609 100644 --- a/modules/core/include/opencv2/core/private.hpp +++ b/modules/core/include/opencv2/core/private.hpp @@ -197,7 +197,6 @@ T* allocSingletonNew() { return new(allocSingletonNewBuffer(sizeof(T))) T(); } // Temporary disabled named IPP region. Performance #define IPP_DISABLE_PERF_COPYMAKE 1 // performance variations #define IPP_DISABLE_PERF_TRUE_DIST_MT 1 // cv::distanceTransform OpenCV MT performance is better -#define IPP_DISABLE_PERF_CANNY_MT 1 // cv::Canny OpenCV MT performance is better #ifdef HAVE_IPP #include "ippversion.h" diff --git a/modules/core/src/matrix_operations.cpp b/modules/core/src/matrix_operations.cpp index d59b8b54c4..5d63a885cd 100644 --- a/modules/core/src/matrix_operations.cpp +++ b/modules/core/src/matrix_operations.cpp @@ -6,11 +6,8 @@ #include "opencv2/core/mat.hpp" #include "opencl_kernels_core.hpp" -#undef HAVE_IPP -#undef CV_IPP_RUN_FAST -#define CV_IPP_RUN_FAST(f, ...) -#undef CV_IPP_RUN -#define CV_IPP_RUN(c, f, ...) +#define IPP_DISABLE_REDUCE 1 +#define IPP_DISABLE_SORT 0 /*************************************************************************************************\ Matrix Operations @@ -498,7 +495,7 @@ typedef void (*ReduceFunc)( const Mat& src, Mat& dst ); #define reduceMinR32f reduceR_ > #define reduceMinR64f reduceR_ > -#ifdef HAVE_IPP +#if defined(HAVE_IPP) && !IPP_DISABLE_REDUCE static inline bool ipp_reduceSumC_8u16u16s32f_64f(const cv::Mat& srcmat, cv::Mat& dstmat) { int sstep = (int)srcmat.step, stype = srcmat.type(), @@ -587,7 +584,7 @@ static inline void reduceSumC_8u16u16s32f_64f(const cv::Mat& srcmat, cv::Mat& ds #define reduceSum2C32f32f reduceC_, OpSqr > #define reduceSum2C64f64f reduceC_,OpSqr > -#ifdef HAVE_IPP +#if defined(HAVE_IPP) && !IPP_DISABLE_REDUCE #define reduceSumC8u64f reduceSumC_8u16u16s32f_64f #define reduceSumC16u64f reduceSumC_8u16u16s32f_64f #define reduceSumC16s64f reduceSumC_8u16u16s32f_64f @@ -597,14 +594,14 @@ static inline void reduceSumC_8u16u16s32f_64f(const cv::Mat& srcmat, cv::Mat& ds #define reduceSumC16u64f reduceC_ > #define reduceSumC16s64f reduceC_ > #define reduceSumC32f64f reduceC_ > +#endif #define reduceSum2C8u64f reduceC_, OpSqr > #define reduceSum2C16u64f reduceC_,OpSqr > #define reduceSum2C16s64f reduceC_,OpSqr > #define reduceSum2C32f64f reduceC_,OpSqr > -#endif -#ifdef HAVE_IPP +#if defined(HAVE_IPP) && !IPP_DISABLE_REDUCE #define REDUCE_OP(favor, optype, type1, type2) \ static inline bool ipp_reduce##optype##C##favor(const cv::Mat& srcmat, cv::Mat& dstmat) \ { \ @@ -629,7 +626,7 @@ static inline void reduce##optype##C##favor(const cv::Mat& srcmat, cv::Mat& dstm } #endif -#ifdef HAVE_IPP +#if defined(HAVE_IPP) && !IPP_DISABLE_REDUCE REDUCE_OP(8u, Max, uchar, uchar) REDUCE_OP(16u, Max, ushort, ushort) REDUCE_OP(16s, Max, short, short) @@ -642,7 +639,7 @@ REDUCE_OP(32f, Max, float, float) #endif #define reduceMaxC64f reduceC_ > -#ifdef HAVE_IPP +#if defined(HAVE_IPP) && !IPP_DISABLE_REDUCE REDUCE_OP(8u, Min, uchar, uchar) REDUCE_OP(16u, Min, ushort, ushort) REDUCE_OP(16s, Min, short, short) @@ -1037,7 +1034,7 @@ template static void sort_( const Mat& src, Mat& dst, int flags ) } -#ifdef HAVE_IPP +#if defined(HAVE_IPP) && !IPP_DISABLE_SORT typedef IppStatus (CV_STDCALL *IppSortFunc)(void *pSrcDst, int len, Ipp8u *pBuffer); static IppSortFunc getSortFunc(int depth, bool sortDescending) @@ -1074,54 +1071,64 @@ static bool ipp_sort(const Mat& src, Mat& dst, int flags) if(!ippsSortRadix_I) return false; - if(sortRows) - { - AutoBuffer buffer; - int bufferSize; - if(ippsSortRadixGetBufferSize(src.cols, type, &bufferSize) < 0) - return false; - - buffer.allocate(bufferSize); - - if(!inplace) - src.copyTo(dst); - - for(int i = 0; i < dst.rows; i++) - { - if(CV_INSTRUMENT_FUN_IPP(ippsSortRadix_I, (void*)dst.ptr(i), dst.cols, buffer.data()) < 0) - return false; - } - } + int n, len; + if( sortRows ) + n = src.rows, len = src.cols; else + n = src.cols, len = src.rows; + + int bufferSize; + if(ippsSortRadixGetBufferSize(len, type, &bufferSize) < 0) + return false; + + // Each row/column sorts independently, so parallelize over n like sort_ does. + // IPP scratch (buffer) and per-column temporaries are thread-local; a shared flag + // records IPP failures since the lambda cannot return from ipp_sort. + volatile bool ok = true; + parallel_for_(Range(0, n), [&](const Range& range) { - AutoBuffer buffer; - int bufferSize; - if(ippsSortRadixGetBufferSize(src.rows, type, &bufferSize) < 0) - return false; - - buffer.allocate(bufferSize); - - Mat row(1, src.rows, src.type()); - Mat srcSub; - Mat dstSub; + AutoBuffer buffer(bufferSize); + Mat row; Rect subRect(0,0,1,src.rows); + if( !sortRows ) + row.create(1, src.rows, src.type()); - for(int i = 0; i < src.cols; i++) + for( int i = range.start; i < range.end; i++ ) { - subRect.x = i; - srcSub = Mat(src, subRect); - dstSub = Mat(dst, subRect); - srcSub.copyTo(row); + if( !ok ) + break; + void* ptr; + if( sortRows ) + { + uchar* dptr = dst.ptr(i); + if( !inplace ) + memcpy(dptr, src.ptr(i), src.cols * src.elemSize()); + ptr = dptr; + } + else + { + subRect.x = i; + Mat(src, subRect).copyTo(row); + ptr = row.ptr(); + } - if(CV_INSTRUMENT_FUN_IPP(ippsSortRadix_I, (void*)row.ptr(), dst.rows, buffer.data()) < 0) - return false; + if( CV_INSTRUMENT_FUN_IPP(ippsSortRadix_I, ptr, len, buffer.data()) < 0 ) + { + ok = false; + break; + } - row = row.reshape(1, dstSub.rows); - row.copyTo(dstSub); + if( !sortRows ) + { + Mat dstSub(dst, subRect); + row = row.reshape(1, dstSub.rows); + row.copyTo(dstSub); + row = row.reshape(1, 1); + } } - } + }); - return true; + return ok; } #endif @@ -1185,7 +1192,7 @@ template static void sortIdx_( const Mat& src, Mat& dst, int flags ) } } -#ifdef HAVE_IPP +#if defined(HAVE_IPP) && !IPP_DISABLE_SORT typedef IppStatus (CV_STDCALL *IppSortIndexFunc)(const void* pSrc, Ipp32s srcStrideBytes, Ipp32s *pDstIndx, int len, Ipp8u *pBuffer); static IppSortIndexFunc getSortIndexFunc(int depth, bool sortDescending) @@ -1276,7 +1283,9 @@ void cv::sort( InputArray _src, OutputArray _dst, int flags ) CV_Assert( src.dims <= 2 && src.channels() == 1 ); _dst.createSameSize( src, src.type() ); Mat dst = _dst.getMat(); +#if !IPP_DISABLE_SORT CV_IPP_RUN_FAST(ipp_sort(src, dst, flags)); +#endif static SortFunc tab[CV_DEPTH_MAX] = { @@ -1301,7 +1310,9 @@ void cv::sortIdx( InputArray _src, OutputArray _dst, int flags ) _dst.createSameSize( src, CV_32S ); dst = _dst.getMat(); +#if !IPP_DISABLE_SORT CV_IPP_RUN_FAST(ipp_sortIdx(src, dst, flags)); +#endif static SortFunc tab[CV_DEPTH_MAX] = { diff --git a/modules/geometry/include/opencv2/geometry/3d.hpp b/modules/geometry/include/opencv2/geometry/3d.hpp index 269666707d..a95b762a9d 100644 --- a/modules/geometry/include/opencv2/geometry/3d.hpp +++ b/modules/geometry/include/opencv2/geometry/3d.hpp @@ -832,7 +832,8 @@ CV_EXPORTS_W Vec3d RQDecomp3x3( InputArray src, OutputArray mtxR, OutputArray mt @param projMatrix 3x4 input projection matrix P. @param cameraMatrix Output 3x3 camera intrinsic matrix \f$\cameramatrix{A}\f$. @param rotMatrix Output 3x3 external rotation matrix R. -@param transVect Output 4x1 translation vector T. +@param transVect Output 4x1 vector representing the camera position in homogeneous coordinates. +To obtain the translation vector, use t = -rotMatrix * transVect[:3] @param rotMatrixX Optional 3x3 rotation matrix around x-axis. @param rotMatrixY Optional 3x3 rotation matrix around y-axis. @param rotMatrixZ Optional 3x3 rotation matrix around z-axis. diff --git a/modules/imgproc/include/opencv2/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc.hpp index 4ff78b5ac9..d02a1379d5 100644 --- a/modules/imgproc/include/opencv2/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc.hpp @@ -2178,9 +2178,11 @@ with the WARP_RELATIVE_MAP flag : where values of pixels with non-integer coordinates are computed using one of available interpolation methods. \f$map_x\f$ and \f$map_y\f$ can be encoded as separate floating-point maps in \f$map_1\f$ and \f$map_2\f$ respectively, or interleaved floating-point maps of \f$(x,y)\f$ in -\f$map_1\f$, or fixed-point maps created by using #convertMaps. The reason you might want to -convert from floating to fixed-point representations of a map is that they can yield much faster -(\~2x) remapping operations. In the converted case, \f$map_1\f$ contains pairs (cvFloor(x), +\f$map_1\f$, or fixed-point maps created by using #convertMaps. Fixed-point maps +use a more compact representation, which can reduce memory bandwidth and benefit +repeated remap calls that reuse the same map. Performance gains vary by hardware +and are typically modest; measure before converting. In the converted case, +\f$map_1\f$ contains pairs (cvFloor(x), cvFloor(y)) and \f$map_2\f$ contains indices in a table of interpolation coefficients. This function cannot operate in-place. @@ -2189,7 +2191,7 @@ This function cannot operate in-place. @param dst Destination image. It has the same size as map1 and the same type as src . @param map1 The first map of either (x,y) points or just x values having the type CV_16SC2 , CV_32FC1, or CV_32FC2. See #convertMaps for details on converting a floating point -representation to fixed-point for speed. +representation to fixed-point. @param map2 The second map of y values having the type CV_16UC1, CV_32FC1, or none (empty map if map1 is (x,y) points), respectively. @param interpolation Interpolation method (see #InterpolationFlags). The methods #INTER_AREA diff --git a/modules/imgproc/src/canny.cpp b/modules/imgproc/src/canny.cpp index 2e8e13a119..22f7a9c1d1 100644 --- a/modules/imgproc/src/canny.cpp +++ b/modules/imgproc/src/canny.cpp @@ -48,85 +48,6 @@ namespace cv { -#ifdef HAVE_IPP -static bool ipp_Canny(const Mat& src , const Mat& dx_, const Mat& dy_, Mat& dst, float low, float high, bool L2gradient, int aperture_size) -{ -#ifdef HAVE_IPP_IW - CV_INSTRUMENT_REGION_IPP(); - -#if IPP_DISABLE_PERF_CANNY_MT - if(cv::getNumThreads()>1) - return false; -#endif - - ::ipp::IwiSize size(dst.cols, dst.rows); - IppDataType type = ippiGetDataType(dst.depth()); - int channels = dst.channels(); - IppNormType norm = (L2gradient)?ippNormL2:ippNormL1; - - if(size.width <= 3 || size.height <= 3) - return false; - - if(channels != 1) - return false; - - if(type != ipp8u) - return false; - - if(src.empty()) - { - try - { - ::ipp::IwiImage iwSrcDx; - ::ipp::IwiImage iwSrcDy; - ::ipp::IwiImage iwDst; - - ippiGetImage(dx_, iwSrcDx); - ippiGetImage(dy_, iwSrcDy); - ippiGetImage(dst, iwDst); - - CV_INSTRUMENT_FUN_IPP(::ipp::iwiFilterCannyDeriv, iwSrcDx, iwSrcDy, iwDst, low, high, ::ipp::IwiFilterCannyDerivParams(norm)); - } - catch (const ::ipp::IwException &) - { - return false; - } - } - else - { - IppiMaskSize kernel; - - if(aperture_size == 3) - kernel = ippMskSize3x3; - else if(aperture_size == 5) - kernel = ippMskSize5x5; - else - return false; - - try - { - ::ipp::IwiImage iwSrc; - ::ipp::IwiImage iwDst; - - ippiGetImage(src, iwSrc); - ippiGetImage(dst, iwDst); - - CV_INSTRUMENT_FUN_IPP(::ipp::iwiFilterCanny, iwSrc, iwDst, low, high, ::ipp::IwiFilterCannyParams(ippFilterSobel, kernel, norm), ippBorderRepl); - } - catch (const ::ipp::IwException &) - { - return false; - } - } - - return true; -#else - CV_UNUSED(src); CV_UNUSED(dx_); CV_UNUSED(dy_); CV_UNUSED(dst); CV_UNUSED(low); CV_UNUSED(high); CV_UNUSED(L2gradient); CV_UNUSED(aperture_size); - return false; -#endif -} -#endif - #ifdef HAVE_OPENCL template @@ -803,8 +724,6 @@ void Canny( InputArray _src, OutputArray _dst, CALL_HAL(canny, cv_hal_canny, src.data, src.step, dst.data, dst.step, src.cols, src.rows, src.channels(), low_thresh, high_thresh, aperture_size, L2gradient); - CV_IPP_RUN_FAST(ipp_Canny(src, Mat(), Mat(), dst, (float)low_thresh, (float)high_thresh, L2gradient, aperture_size)) - if (L2gradient) { low_thresh = std::min(32767.0, low_thresh); @@ -879,7 +798,8 @@ void Canny( InputArray _dx, InputArray _dy, OutputArray _dst, Mat dx = _dx.getMat(); Mat dy = _dy.getMat(); - CV_IPP_RUN_FAST(ipp_Canny(Mat(), dx, dy, dst, (float)low_thresh, (float)high_thresh, L2gradient, 0)) + CALL_HAL(canny_deriv, cv_hal_canny_deriv, dx.ptr(), dx.step, dy.ptr(), dy.step, + dst.data, dst.step, dx.cols, dx.rows, dx.channels(), low_thresh, high_thresh, L2gradient); if (L2gradient) { diff --git a/modules/imgproc/src/distransform.cpp b/modules/imgproc/src/distransform.cpp index 1354cf95f8..a095b3f764 100644 --- a/modules/imgproc/src/distransform.cpp +++ b/modules/imgproc/src/distransform.cpp @@ -40,6 +40,7 @@ // //M*/ #include "precomp.hpp" +#include "hal_replacement.hpp" namespace cv { @@ -1004,19 +1005,8 @@ static void distanceTransform_L1_8U(InputArray _src, OutputArray _dst) _dst.create( src.size(), CV_8UC1); Mat dst = _dst.getMat(); -#ifdef HAVE_IPP - CV_IPP_CHECK() - { - IppiSize roi = { src.cols, src.rows }; - Ipp32s pMetrics[2] = { 1, 2 }; //L1, 3x3 mask - if (CV_INSTRUMENT_FUN_IPP(ippiDistanceTransform_3x3_8u_C1R, src.ptr(), (int)src.step, dst.ptr(), (int)dst.step, roi, pMetrics) >= 0) - { - CV_IMPL_ADD(CV_IMPL_IPP); - return; - } - setIppErrorStatus(); - } -#endif + CALL_HAL(distanceTransform, cv_hal_distanceTransform, src.ptr(), src.step, dst.ptr(), dst.step, + src.cols, src.rows, CV_8U, cv::DIST_L1, cv::DIST_MASK_3); distanceATS_L1_8u(src, dst); } @@ -1055,53 +1045,8 @@ void cv::distanceTransform( InputArray _src, OutputArray _dst, OutputArray _labe if( maskSize == cv::DIST_MASK_PRECISE ) { - -#ifdef HAVE_IPP - CV_IPP_CHECK() - { -#if IPP_DISABLE_PERF_TRUE_DIST_MT - // IPP uses floats, but 4097 cannot be squared into a float - if((cv::getNumThreads()<=1 || (src.total()<(int)(1<<14))) && - src.rows < 4097 && src.cols < 4097) -#endif - { - IppStatus status; - IppiSize roi = { src.cols, src.rows }; - Ipp8u *pBuffer; - int bufSize=0; - - status = ippiTrueDistanceTransformGetBufferSize_8u32f_C1R(roi, &bufSize); - if (status>=0) - { - pBuffer = (Ipp8u *)CV_IPP_MALLOC( bufSize ); - status = CV_INSTRUMENT_FUN_IPP(ippiTrueDistanceTransform_8u32f_C1R, src.ptr(), (int)src.step, dst.ptr(), (int)dst.step, roi, pBuffer); - ippFree( pBuffer ); - if (status>=0) - { - // https://github.com/opencv/opencv/issues/24082 - // There is probably a rounding issue that leads to non-deterministic behavior - // between runs on positions closer to zeros by x-axis in straight direction. - // As a workaround, we detect the distances that expected to be exact - // number of pixels and round manually. - static const float correctionDiff = 1.0f / (1 << 11); - for (int i = 0; i < dst.rows; ++i) - { - float* row = dst.ptr(i); - for (int j = 0; j < dst.cols; ++j) - { - float rounded = static_cast(cvRound(row[j])); - if (fabs(row[j] - rounded) <= correctionDiff) - row[j] = rounded; - } - } - CV_IMPL_ADD(CV_IMPL_IPP); - return; - } - setIppErrorStatus(); - } - } - } -#endif + CALL_HAL(distanceTransform, cv_hal_distanceTransform, src.ptr(), src.step, dst.ptr(), dst.step, + src.cols, src.rows, CV_32F, distType, cv::DIST_MASK_PRECISE); trueDistTrans( src, dst ); return; @@ -1121,38 +1066,16 @@ void cv::distanceTransform( InputArray _src, OutputArray _dst, OutputArray _labe { if( maskSize == cv::DIST_MASK_3 ) { -#if defined (HAVE_IPP) && (IPP_VERSION_X100 >= 700) - bool has_int_overflow = (int64)src.cols * src.rows >= INT_MAX; - if (!has_int_overflow && CV_IPP_CHECK_COND) - { - IppiSize roi = { src.cols, src.rows }; - if (CV_INSTRUMENT_FUN_IPP(ippiDistanceTransform_3x3_8u32f_C1R, src.ptr(), (int)src.step, dst.ptr(), (int)dst.step, roi, _mask) >= 0) - { - CV_IMPL_ADD(CV_IMPL_IPP); - return; - } - setIppErrorStatus(); - } -#endif + CALL_HAL(distanceTransform, cv_hal_distanceTransform, src.ptr(), src.step, dst.ptr(), dst.step, + src.cols, src.rows, CV_32F, distType, cv::DIST_MASK_3); temp.create(size.height + border*2, size.width + border*2, CV_32SC1); distanceTransform_3x3(src, temp, dst, _mask); } else { -#if defined (HAVE_IPP) && (IPP_VERSION_X100 >= 700) - bool has_int_overflow = (int64)src.cols * src.rows >= INT_MAX; - if (!has_int_overflow && CV_IPP_CHECK_COND) - { - IppiSize roi = { src.cols, src.rows }; - if (CV_INSTRUMENT_FUN_IPP(ippiDistanceTransform_5x5_8u32f_C1R, src.ptr(), (int)src.step, dst.ptr(), (int)dst.step, roi, _mask) >= 0) - { - CV_IMPL_ADD(CV_IMPL_IPP); - return; - } - setIppErrorStatus(); - } -#endif + CALL_HAL(distanceTransform, cv_hal_distanceTransform, src.ptr(), src.step, dst.ptr(), dst.step, + src.cols, src.rows, CV_32F, distType, cv::DIST_MASK_5); temp.create(size.height + border*2, size.width + border*2, CV_32SC1); distanceTransform_5x5(src, temp, dst, _mask); diff --git a/modules/imgproc/src/hal_replacement.hpp b/modules/imgproc/src/hal_replacement.hpp index 23887cd000..454360e0c6 100644 --- a/modules/imgproc/src/hal_replacement.hpp +++ b/modules/imgproc/src/hal_replacement.hpp @@ -1067,16 +1067,6 @@ inline int hal_ni_cvtRGBAtoMultipliedRGBA(const uchar * src_data, size_t src_ste */ inline int hal_ni_cvtMultipliedRGBAtoRGBA(const uchar * src_data, size_t src_step, uchar * dst_data, size_t dst_step, int width, int height) { return CV_HAL_ERROR_NOT_IMPLEMENTED; } -/** @brief Extract Y-plane from YUV 4:2:0 image. - @param src_data Source image data pointer (points to Y plane). - @param src_step Source step. - @param dst_data Destination data pointer. - @param dst_step Destination step. - @param width Image width. - @param height Image height (of the Y plane, not the full YUV image). - */ -inline int hal_ni_cvtColorYUV2Gray(const uchar * src_data, size_t src_step, uchar * dst_data, size_t dst_step, int width, int height) { return CV_HAL_ERROR_NOT_IMPLEMENTED; } - //! @cond IGNORED #define cv_hal_cvtBGRtoBGR hal_ni_cvtBGRtoBGR #define cv_hal_cvtBGRtoBGR5x5 hal_ni_cvtBGRtoBGR5x5 @@ -1110,7 +1100,6 @@ inline int hal_ni_cvtColorYUV2Gray(const uchar * src_data, size_t src_step, ucha #define cv_hal_cvtOnePlaneBGRtoYUVApprox hal_ni_cvtOnePlaneBGRtoYUVApprox #define cv_hal_cvtRGBAtoMultipliedRGBA hal_ni_cvtRGBAtoMultipliedRGBA #define cv_hal_cvtMultipliedRGBAtoRGBA hal_ni_cvtMultipliedRGBAtoRGBA -#define cv_hal_cvtColorYUV2Gray hal_ni_cvtColorYUV2Gray //! @endcond /** @@ -1252,6 +1241,24 @@ inline int hal_ni_threshold_otsu(const uchar* src_data, size_t src_step, uchar* #define cv_hal_threshold_otsu hal_ni_threshold_otsu //! @endcond +/** + @brief Calculates the distance to the closest zero pixel for each pixel of the source image + @param src_data Source image (8-bit single-channel) data + @param src_step Source image step + @param dst_data Destination image data + @param dst_step Destination image step + @param width Source image width + @param height Source image height + @param dst_type Type of the destination image (CV_8UC1 or CV_32FC1) + @param dist_type Type of distance (cv::DistanceTypes: DIST_L1, DIST_L2, DIST_C) + @param mask_size Size of the distance transform mask (cv::DistanceTransformMasks: DIST_MASK_3, DIST_MASK_5, DIST_MASK_PRECISE) +*/ +inline int hal_ni_distanceTransform(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, int width, int height, int dst_type, int dist_type, int mask_size) { return CV_HAL_ERROR_NOT_IMPLEMENTED; } + +//! @cond IGNORED +#define cv_hal_distanceTransform hal_ni_distanceTransform +//! @endcond + /** @brief Calculate box filter @param src_data Source image data @@ -1531,6 +1538,27 @@ inline int hal_ni_canny(const uchar* src_data, size_t src_step, uchar* dst_data, #define cv_hal_canny hal_ni_canny //! @endcond +/** + @brief Canny edge detector from image derivatives + @param dx_data Source image x-derivative data + @param dx_step Source image x-derivative step + @param dy_data Source image y-derivative data + @param dy_step Source image y-derivative step + @param dst_data Destination image data + @param dst_step Destination image step + @param width Source image width + @param height Source image height + @param cn Number of channels + @param lowThreshold low hresholds value + @param highThreshold high thresholds value + @param L2gradient Flag, indicating use L2 or L1 norma. +*/ +inline int hal_ni_canny_deriv(const short* dx_data, size_t dx_step, const short* dy_data, size_t dy_step, uchar* dst_data, size_t dst_step, int width, int height, int cn, double lowThreshold, double highThreshold, bool L2gradient) { return CV_HAL_ERROR_NOT_IMPLEMENTED; } + +//! @cond IGNORED +#define cv_hal_canny_deriv hal_ni_canny_deriv +//! @endcond + /** @brief Calculates a histogram of a set of arrays @param src_data Source imgage data @@ -1551,6 +1579,32 @@ inline int hal_ni_calcHist(const uchar* src_data, size_t src_step, int src_type, #define cv_hal_calcHist hal_ni_calcHist //! @endcond +/** + @brief Compares a template against overlapped image regions. + @param src_data Source image (single-channel, CV_8U or CV_32F) data + @param src_step Source image step + @param src_width Source image width + @param src_height Source image height + @param templ_data Template image data (same type as source) + @param templ_step Template image step + @param templ_width Template image width + @param templ_height Template image height + @param result_data Destination map data (single-channel CV_32F, size (src_width-templ_width+1) x (src_height-templ_height+1)) + @param result_step Destination map step + @param depth Depth of source and template images (CV_8U or CV_32F) + @param cn Number of channels + @param method Comparison method (cv::TemplateMatchModes) + @sa matchTemplate +*/ +inline int hal_ni_matchTemplate(const uchar* src_data, size_t src_step, int src_width, int src_height, + const uchar* templ_data, size_t templ_step, int templ_width, int templ_height, + float* result_data, size_t result_step, int depth, int cn, int method) +{ return CV_HAL_ERROR_NOT_IMPLEMENTED; } + +//! @cond IGNORED +#define cv_hal_matchTemplate hal_ni_matchTemplate +//! @endcond + //! @} #if defined(__clang__) diff --git a/modules/imgproc/src/templmatch.cpp b/modules/imgproc/src/templmatch.cpp index 73677bde73..45c38dc4ad 100644 --- a/modules/imgproc/src/templmatch.cpp +++ b/modules/imgproc/src/templmatch.cpp @@ -1034,135 +1034,6 @@ static void common_matchTemplate( Mat& img, Mat& templ, Mat& result, int method, } } - -#if defined HAVE_IPP -namespace cv -{ -typedef IppStatus (CV_STDCALL * ippimatchTemplate)(const void*, int, IppiSize, const void*, int, IppiSize, Ipp32f* , int , IppEnum , Ipp8u*); - -static bool ipp_crossCorr(const Mat& src, const Mat& tpl, Mat& dst, bool normed) -{ - CV_INSTRUMENT_REGION_IPP(); - - IppStatus status; - - IppiSize srcRoiSize = {src.cols,src.rows}; - IppiSize tplRoiSize = {tpl.cols,tpl.rows}; - - IppAutoBuffer buffer; - int bufSize=0; - - int depth = src.depth(); - - ippimatchTemplate ippiCrossCorrNorm = - depth==CV_8U ? (ippimatchTemplate)ippiCrossCorrNorm_8u32f_C1R: - depth==CV_32F? (ippimatchTemplate)ippiCrossCorrNorm_32f_C1R: 0; - - if (ippiCrossCorrNorm==0) - return false; - - IppEnum funCfg = (IppEnum)(+ippAlgAuto | ippiROIValid); - if(normed) - funCfg |= ippiNorm; - else - funCfg |= ippiNormNone; - - status = ippiCrossCorrNormGetBufferSize(srcRoiSize, tplRoiSize, funCfg, &bufSize); - if ( status < 0 ) - return false; - - buffer.allocate( bufSize ); - - status = CV_INSTRUMENT_FUN_IPP(ippiCrossCorrNorm, src.ptr(), (int)src.step, srcRoiSize, tpl.ptr(), (int)tpl.step, tplRoiSize, dst.ptr(), (int)dst.step, funCfg, buffer); - return status >= 0; -} - -static bool ipp_sqrDistance(const Mat& src, const Mat& tpl, Mat& dst) -{ - CV_INSTRUMENT_REGION_IPP(); - - IppStatus status; - - IppiSize srcRoiSize = {src.cols,src.rows}; - IppiSize tplRoiSize = {tpl.cols,tpl.rows}; - - IppAutoBuffer buffer; - int bufSize=0; - - int depth = src.depth(); - - ippimatchTemplate ippiSqrDistanceNorm = - depth==CV_8U ? (ippimatchTemplate)ippiSqrDistanceNorm_8u32f_C1R: - depth==CV_32F? (ippimatchTemplate)ippiSqrDistanceNorm_32f_C1R: 0; - - if (ippiSqrDistanceNorm==0) - return false; - - IppEnum funCfg = (IppEnum)(+ippAlgAuto | ippiROIValid | ippiNormNone); - status = ippiSqrDistanceNormGetBufferSize(srcRoiSize, tplRoiSize, funCfg, &bufSize); - if ( status < 0 ) - return false; - - buffer.allocate( bufSize ); - - status = CV_INSTRUMENT_FUN_IPP(ippiSqrDistanceNorm, src.ptr(), (int)src.step, srcRoiSize, tpl.ptr(), (int)tpl.step, tplRoiSize, dst.ptr(), (int)dst.step, funCfg, buffer); - dst = cv::max(dst, 0); // handle edge case from rounding in variance computation which can result in negative values - return status >= 0; -} - -static bool ipp_matchTemplate( Mat& img, Mat& templ, Mat& result, int method) -{ - CV_INSTRUMENT_REGION_IPP(); - - if(img.channels() != 1) - return false; - - // These functions are not efficient if template size is comparable with image size - if(templ.size().area()*4 > img.size().area()) - return false; - - // CV_8U SQDIFF/SQDIFF_NORMED suffer from float32 catastrophic cancellation - // in IPP's internal accumulators; fall through to the double-precision path instead. - if(img.depth() == CV_8U && (method == cv::TM_SQDIFF || method == cv::TM_SQDIFF_NORMED)) - return false; - - if(method == cv::TM_SQDIFF) - { - if(ipp_sqrDistance(img, templ, result)) - return true; - } - else if(method == cv::TM_SQDIFF_NORMED) - { - if(ipp_crossCorr(img, templ, result, false)) - { - common_matchTemplate(img, templ, result, cv::TM_SQDIFF_NORMED, 1); - return true; - } - } - else if(method == cv::TM_CCORR) - { - if(ipp_crossCorr(img, templ, result, false)) - return true; - } - else if(method == cv::TM_CCORR_NORMED) - { - if(ipp_crossCorr(img, templ, result, true)) - return true; - } - else if(method == cv::TM_CCOEFF || method == cv::TM_CCOEFF_NORMED) - { - if(ipp_crossCorr(img, templ, result, false)) - { - common_matchTemplate(img, templ, result, method, 1); - return true; - } - } - - return false; -} -} -#endif - //////////////////////////////////////////////////////////////////////////////////////////////////////// void cv::matchTemplate( InputArray _img, InputArray _templ, OutputArray _result, int method, InputArray _mask ) @@ -1196,7 +1067,22 @@ void cv::matchTemplate( InputArray _img, InputArray _templ, OutputArray _result, _result.create(corrSize, CV_32F); Mat result = _result.getMat(); - CV_IPP_RUN_FAST(ipp_matchTemplate(img, templ, result, method)) + { + int hal_res = cv_hal_matchTemplate(img.data, img.step, img.cols, img.rows, + templ.data, templ.step, templ.cols, templ.rows, + result.ptr(), result.step, depth, cn, method); + if (hal_res == CV_HAL_ERROR_OK) + { + if (method == cv::TM_SQDIFF_NORMED || method == cv::TM_CCOEFF || method == cv::TM_CCOEFF_NORMED) + common_matchTemplate(img, templ, result, method, 1); + return; + } + else if (hal_res != CV_HAL_ERROR_NOT_IMPLEMENTED) + { + CV_Error_(cv::Error::StsInternal, + ("HAL implementation matchTemplate ==> cv_hal_matchTemplate returned %d (0x%08x)", hal_res, hal_res)); + } + } bool use64f = (depth == CV_8U) && (method == cv::TM_SQDIFF || method == cv::TM_SQDIFF_NORMED); Mat result64f; diff --git a/modules/imgproc/src/thresh.cpp b/modules/imgproc/src/thresh.cpp index 7588d90808..d47e2bb1ba 100644 --- a/modules/imgproc/src/thresh.cpp +++ b/modules/imgproc/src/thresh.cpp @@ -193,57 +193,6 @@ thresh_8u( const Mat& _src, Mat& _dst, uchar thresh, uchar maxval, int type ) src_step = dst_step = roi.width; } -#if defined(HAVE_IPP) - CV_IPP_CHECK() - { - IppiSize sz = { roi.width, roi.height }; - CV_SUPPRESS_DEPRECATED_START - switch( type ) - { - case THRESH_TRUNC: - if (_src.data == _dst.data && CV_INSTRUMENT_FUN_IPP(ippiThreshold_GT_8u_C1IR, _dst.ptr(), (int)dst_step, sz, thresh) >= 0) - { - CV_IMPL_ADD(CV_IMPL_IPP); - return; - } - if (CV_INSTRUMENT_FUN_IPP(ippiThreshold_GT_8u_C1R, _src.ptr(), (int)src_step, _dst.ptr(), (int)dst_step, sz, thresh) >= 0) - { - CV_IMPL_ADD(CV_IMPL_IPP); - return; - } - setIppErrorStatus(); - break; - case THRESH_TOZERO: - if (_src.data == _dst.data && CV_INSTRUMENT_FUN_IPP(ippiThreshold_LTVal_8u_C1IR, _dst.ptr(), (int)dst_step, sz, thresh+1, 0) >= 0) - { - CV_IMPL_ADD(CV_IMPL_IPP); - return; - } - if (CV_INSTRUMENT_FUN_IPP(ippiThreshold_LTVal_8u_C1R, _src.ptr(), (int)src_step, _dst.ptr(), (int)dst_step, sz, thresh + 1, 0) >= 0) - { - CV_IMPL_ADD(CV_IMPL_IPP); - return; - } - setIppErrorStatus(); - break; - case THRESH_TOZERO_INV: - if (_src.data == _dst.data && CV_INSTRUMENT_FUN_IPP(ippiThreshold_GTVal_8u_C1IR, _dst.ptr(), (int)dst_step, sz, thresh, 0) >= 0) - { - CV_IMPL_ADD(CV_IMPL_IPP); - return; - } - if (CV_INSTRUMENT_FUN_IPP(ippiThreshold_GTVal_8u_C1R, _src.ptr(), (int)src_step, _dst.ptr(), (int)dst_step, sz, thresh, 0) >= 0) - { - CV_IMPL_ADD(CV_IMPL_IPP); - return; - } - setIppErrorStatus(); - break; - } - CV_SUPPRESS_DEPRECATED_END - } -#endif - int j = 0; const uchar* src = _src.ptr(); uchar* dst = _dst.ptr(); @@ -577,57 +526,6 @@ thresh_16s( const Mat& _src, Mat& _dst, short thresh, short maxval, int type ) src_step = dst_step = roi.width; } -#if defined(HAVE_IPP) - CV_IPP_CHECK() - { - IppiSize sz = { roi.width, roi.height }; - CV_SUPPRESS_DEPRECATED_START - switch( type ) - { - case THRESH_TRUNC: - if (_src.data == _dst.data && CV_INSTRUMENT_FUN_IPP(ippiThreshold_GT_16s_C1IR, dst, (int)dst_step*sizeof(dst[0]), sz, thresh) >= 0) - { - CV_IMPL_ADD(CV_IMPL_IPP); - return; - } - if (CV_INSTRUMENT_FUN_IPP(ippiThreshold_GT_16s_C1R, src, (int)src_step*sizeof(src[0]), dst, (int)dst_step*sizeof(dst[0]), sz, thresh) >= 0) - { - CV_IMPL_ADD(CV_IMPL_IPP); - return; - } - setIppErrorStatus(); - break; - case THRESH_TOZERO: - if (_src.data == _dst.data && CV_INSTRUMENT_FUN_IPP(ippiThreshold_LTVal_16s_C1IR, dst, (int)dst_step*sizeof(dst[0]), sz, thresh + 1, 0) >= 0) - { - CV_IMPL_ADD(CV_IMPL_IPP); - return; - } - if (CV_INSTRUMENT_FUN_IPP(ippiThreshold_LTVal_16s_C1R, src, (int)src_step*sizeof(src[0]), dst, (int)dst_step*sizeof(dst[0]), sz, thresh + 1, 0) >= 0) - { - CV_IMPL_ADD(CV_IMPL_IPP); - return; - } - setIppErrorStatus(); - break; - case THRESH_TOZERO_INV: - if (_src.data == _dst.data && CV_INSTRUMENT_FUN_IPP(ippiThreshold_GTVal_16s_C1IR, dst, (int)dst_step*sizeof(dst[0]), sz, thresh, 0) >= 0) - { - CV_IMPL_ADD(CV_IMPL_IPP); - return; - } - if (CV_INSTRUMENT_FUN_IPP(ippiThreshold_GTVal_16s_C1R, src, (int)src_step*sizeof(src[0]), dst, (int)dst_step*sizeof(dst[0]), sz, thresh, 0) >= 0) - { - CV_IMPL_ADD(CV_IMPL_IPP); - return; - } - setIppErrorStatus(); - break; - } - CV_SUPPRESS_DEPRECATED_END - } -#endif - #if (CV_SIMD || CV_SIMD_SCALABLE) int i, j; v_int16 thresh8 = vx_setall_s16( thresh ); @@ -799,40 +697,6 @@ thresh_32f( const Mat& _src, Mat& _dst, float thresh, float maxval, int type ) roi.height = 1; } -#if defined(HAVE_IPP) - CV_IPP_CHECK() - { - IppiSize sz = { roi.width, roi.height }; - switch( type ) - { - case THRESH_TRUNC: - if (0 <= CV_INSTRUMENT_FUN_IPP(ippiThreshold_GT_32f_C1R, src, (int)src_step*sizeof(src[0]), dst, (int)dst_step*sizeof(dst[0]), sz, thresh)) - { - CV_IMPL_ADD(CV_IMPL_IPP); - return; - } - setIppErrorStatus(); - break; - case THRESH_TOZERO: - if (0 <= CV_INSTRUMENT_FUN_IPP(ippiThreshold_LTVal_32f_C1R, src, (int)src_step*sizeof(src[0]), dst, (int)dst_step*sizeof(dst[0]), sz, nextafterf(thresh, std::numeric_limits::infinity()), 0)) - { - CV_IMPL_ADD(CV_IMPL_IPP); - return; - } - setIppErrorStatus(); - break; - case THRESH_TOZERO_INV: - if (0 <= CV_INSTRUMENT_FUN_IPP(ippiThreshold_GTVal_32f_C1R, src, (int)src_step*sizeof(src[0]), dst, (int)dst_step*sizeof(dst[0]), sz, thresh, 0)) - { - CV_IMPL_ADD(CV_IMPL_IPP); - return; - } - setIppErrorStatus(); - break; - } - } -#endif - #if (CV_SIMD || CV_SIMD_SCALABLE) int i, j; v_float32 thresh4 = vx_setall_f32( thresh );