mirror of
https://github.com/opencv/opencv.git
synced 2026-07-21 19:33:03 +04:00
Merge pull request #29464 from Akansha-977:templatematch_IPP_4.x
Extracting IPP to HAL for matchTemplate function in 4.x #29464 ### 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 - [x] There is a reference to the original bug report and related work - [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [x] The feature is well documented and sample code can be built with the project CMake
This commit is contained in:
@@ -22,6 +22,7 @@ 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"
|
||||
)
|
||||
|
||||
#TODO: HAVE_IPP_ICV and HAVE_IPP_IW added as private macro till OpenCV itself is
|
||||
|
||||
@@ -138,6 +138,12 @@ int ipp_hal_cvtRGBAtoMultipliedRGBA(const uchar * src_data, size_t src_step, uch
|
||||
#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
|
||||
|
||||
#endif // IPP_VERSION_X100 >= 700
|
||||
|
||||
#endif //__IPP_HAL_IMGPROC_HPP__
|
||||
|
||||
@@ -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 <opencv2/core.hpp>
|
||||
#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<Ipp32f>(), (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<Ipp32f>(), (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
|
||||
@@ -1572,6 +1572,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__)
|
||||
|
||||
@@ -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<Ipp8u> 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<Ipp32f>(), (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<Ipp8u> 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<Ipp32f>(), (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<float>(), 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;
|
||||
|
||||
Reference in New Issue
Block a user