mirror of
https://github.com/opencv/opencv.git
synced 2026-09-11 04:43:22 -05:00
Merge pull request #29410 from Prasadayus:bilateral_filter_ipp_extract
Extract IPP integration as HAL function for bilateral_filter - #29410 Backport of https://github.com/opencv/opencv/pull/29409 **Performance Numbers on Intel(R) Core(TM) i9-11900K:** https://docs.google.com/spreadsheets/d/1rmNB3X_V8rWttUGBqXRs1FmkxeKR0O93x_ez5tVjutY/edit?usp=sharing ### 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:
committed by
GitHub
parent
8b006606b5
commit
524fbae162
@@ -19,6 +19,7 @@ add_library(ipphal STATIC
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/deriv_ipp.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/resize_ipp.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/box_filter_ipp.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/bilateral_filter_ipp.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/sum_ipp.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/color_ipp.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/filter_ipp.cpp"
|
||||
|
||||
@@ -66,6 +66,16 @@ 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
|
||||
|
||||
#if defined(HAVE_IPP_IW)
|
||||
|
||||
int ipp_hal_bilateralFilter_offset(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step,
|
||||
int width, int height, int depth, int cn,
|
||||
int margin_left, int margin_top, int margin_right, int margin_bottom,
|
||||
int d, double sigma_color, double sigma_space, int border_type);
|
||||
#undef cv_hal_bilateralFilter_offset
|
||||
#define cv_hal_bilateralFilter_offset ipp_hal_bilateralFilter_offset
|
||||
#endif // HAVE_IPP_IW
|
||||
|
||||
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,
|
||||
|
||||
162
hal/ipp/src/bilateral_filter_ipp.cpp
Normal file
162
hal/ipp/src/bilateral_filter_ipp.cpp
Normal file
@@ -0,0 +1,162 @@
|
||||
// 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 <opencv2/core.hpp>
|
||||
#include "precomp_ipp.hpp"
|
||||
|
||||
#define IPP_BILATERAL_PARALLEL 1
|
||||
|
||||
class ipp_bilateralFilterParallel: public cv::ParallelLoopBody
|
||||
{
|
||||
public:
|
||||
ipp_bilateralFilterParallel(::ipp::IwiImage &_src, ::ipp::IwiImage &_dst, int _radius, Ipp32f _valSquareSigma, Ipp32f _posSquareSigma, ::ipp::IwiBorderType _borderType, bool *_ok):
|
||||
src(_src), dst(_dst)
|
||||
{
|
||||
pOk = _ok;
|
||||
|
||||
radius = _radius;
|
||||
valSquareSigma = _valSquareSigma;
|
||||
posSquareSigma = _posSquareSigma;
|
||||
borderType = _borderType;
|
||||
|
||||
*pOk = true;
|
||||
}
|
||||
~ipp_bilateralFilterParallel() {}
|
||||
|
||||
virtual void operator() (const cv::Range& range) const CV_OVERRIDE
|
||||
{
|
||||
if(*pOk == false)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
::ipp::IwiTile tile = ::ipp::IwiRoi(0, range.start, dst.m_size.width, range.end - range.start);
|
||||
CV_INSTRUMENT_FUN_IPP(::ipp::iwiFilterBilateral, src, dst, radius, valSquareSigma, posSquareSigma, ::ipp::IwDefault(), borderType, tile);
|
||||
}
|
||||
catch(const ::ipp::IwException &)
|
||||
{
|
||||
*pOk = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
private:
|
||||
::ipp::IwiImage &src;
|
||||
::ipp::IwiImage &dst;
|
||||
|
||||
int radius;
|
||||
Ipp32f valSquareSigma;
|
||||
Ipp32f posSquareSigma;
|
||||
::ipp::IwiBorderType borderType;
|
||||
|
||||
bool *pOk;
|
||||
const ipp_bilateralFilterParallel& operator= (const ipp_bilateralFilterParallel&);
|
||||
};
|
||||
|
||||
int ipp_hal_bilateralFilter_offset(const uchar* src_data, size_t src_step,
|
||||
uchar* dst_data, size_t dst_step,
|
||||
int width, int height, int depth, int cn,
|
||||
int margin_left, int margin_top, int margin_right, int margin_bottom,
|
||||
int d, double sigma_color, double sigma_space, int border_type)
|
||||
{
|
||||
CV_HAL_CHECK_USE_IPP();
|
||||
|
||||
if(!((depth == CV_8U || depth == CV_32F) && (cn == 1 || cn == 3)))
|
||||
return CV_HAL_ERROR_NOT_IMPLEMENTED;
|
||||
|
||||
constexpr double eps = 1e-6;
|
||||
if(sigma_color <= eps || sigma_space <= eps)
|
||||
return CV_HAL_ERROR_NOT_IMPLEMENTED;
|
||||
|
||||
int bt = border_type & ~cv::BORDER_ISOLATED;
|
||||
IppiBorderType ippBorderType =
|
||||
bt == cv::BORDER_CONSTANT ? ippBorderConst :
|
||||
bt == cv::BORDER_REPLICATE ? ippBorderRepl :
|
||||
bt == cv::BORDER_REFLECT_101 ? ippBorderMirror :
|
||||
bt == cv::BORDER_TRANSPARENT ? ippBorderTransp :
|
||||
(IppiBorderType)-1;
|
||||
if((int)ippBorderType == -1)
|
||||
return CV_HAL_ERROR_NOT_IMPLEMENTED;
|
||||
|
||||
int radius = IPP_MAX(((d <= 0) ? cvRound(sigma_space*1.5) : d/2), 1);
|
||||
Ipp32f valSquareSigma = (Ipp32f)(sigma_color*sigma_color);
|
||||
Ipp32f posSquareSigma = (Ipp32f)(sigma_space*sigma_space);
|
||||
|
||||
IwSize marginLeft = margin_left;
|
||||
IwSize marginTop = margin_top;
|
||||
IwSize marginRight = margin_right;
|
||||
IwSize marginBottom = margin_bottom;
|
||||
|
||||
try
|
||||
{
|
||||
::ipp::IwiBorderSize inMemBorder(marginLeft, marginTop, marginRight, marginBottom);
|
||||
|
||||
::ipp::IwiImage iwSrc, iwDst;
|
||||
iwSrc.Init(IwiSize{width, height}, ippiGetDataType(depth), cn,
|
||||
inMemBorder, (void*)src_data, IwSize(src_step));
|
||||
iwDst.Init(IwiSize{width, height}, ippiGetDataType(depth), cn,
|
||||
IwiBorderSize(), dst_data, IwSize(dst_step));
|
||||
|
||||
// already have physical border
|
||||
int inMemFlags = 0;
|
||||
if(!(border_type & cv::BORDER_ISOLATED))
|
||||
{
|
||||
if(marginLeft)
|
||||
{
|
||||
if(marginLeft >= radius) inMemFlags |= ippBorderInMemLeft;
|
||||
else return CV_HAL_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
if(marginTop)
|
||||
{
|
||||
if(marginTop >= radius) inMemFlags |= ippBorderInMemTop;
|
||||
else return CV_HAL_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
if(marginRight)
|
||||
{
|
||||
if(marginRight >= radius) inMemFlags |= ippBorderInMemRight;
|
||||
else return CV_HAL_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
if(marginBottom)
|
||||
{
|
||||
if(marginBottom >= radius) inMemFlags |= ippBorderInMemBottom;
|
||||
else return CV_HAL_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
}
|
||||
|
||||
::ipp::IwiBorderType ippBorder((IppiBorderType)(ippBorderType | inMemFlags));
|
||||
|
||||
const int threads = ippiSuggestThreadsNum(iwDst, 2);
|
||||
if(IPP_BILATERAL_PARALLEL && threads > 1) {
|
||||
bool ok = true;
|
||||
cv::Range range(0, (int)iwDst.m_size.height);
|
||||
ipp_bilateralFilterParallel invoker(iwSrc, iwDst, radius, valSquareSigma, posSquareSigma, ippBorder, &ok);
|
||||
if(!ok)
|
||||
return CV_HAL_ERROR_NOT_IMPLEMENTED;
|
||||
|
||||
int maxTiles = (int)iwDst.m_size.height / radius;
|
||||
int numTiles = threads * 4;
|
||||
if(numTiles > maxTiles) {
|
||||
numTiles = (maxTiles / threads) * threads;
|
||||
}
|
||||
cv::parallel_for_(range, invoker, numTiles);
|
||||
|
||||
if(!ok)
|
||||
return CV_HAL_ERROR_NOT_IMPLEMENTED;
|
||||
} else {
|
||||
CV_INSTRUMENT_FUN_IPP(::ipp::iwiFilterBilateral, iwSrc, iwDst, radius, valSquareSigma, posSquareSigma, ::ipp::IwDefault(), ippBorder);
|
||||
}
|
||||
}
|
||||
catch(const ::ipp::IwException &)
|
||||
{
|
||||
return CV_HAL_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
return CV_HAL_ERROR_OK;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -325,120 +325,6 @@ bilateralFilter_32f( const Mat& src, Mat& dst, int d,
|
||||
CV_CPU_DISPATCH_MODES_ALL);
|
||||
}
|
||||
|
||||
#ifdef HAVE_IPP
|
||||
#define IPP_BILATERAL_PARALLEL 1
|
||||
|
||||
#ifdef HAVE_IPP_IW
|
||||
class ipp_bilateralFilterParallel: public ParallelLoopBody
|
||||
{
|
||||
public:
|
||||
ipp_bilateralFilterParallel(::ipp::IwiImage &_src, ::ipp::IwiImage &_dst, int _radius, Ipp32f _valSquareSigma, Ipp32f _posSquareSigma, ::ipp::IwiBorderType _borderType, bool *_ok):
|
||||
src(_src), dst(_dst)
|
||||
{
|
||||
pOk = _ok;
|
||||
|
||||
radius = _radius;
|
||||
valSquareSigma = _valSquareSigma;
|
||||
posSquareSigma = _posSquareSigma;
|
||||
borderType = _borderType;
|
||||
|
||||
*pOk = true;
|
||||
}
|
||||
~ipp_bilateralFilterParallel() {}
|
||||
|
||||
virtual void operator() (const Range& range) const CV_OVERRIDE
|
||||
{
|
||||
if(*pOk == false)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
::ipp::IwiTile tile = ::ipp::IwiRoi(0, range.start, dst.m_size.width, range.end - range.start);
|
||||
CV_INSTRUMENT_FUN_IPP(::ipp::iwiFilterBilateral, src, dst, radius, valSquareSigma, posSquareSigma, ::ipp::IwDefault(), borderType, tile);
|
||||
}
|
||||
catch(const ::ipp::IwException &)
|
||||
{
|
||||
*pOk = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
private:
|
||||
::ipp::IwiImage &src;
|
||||
::ipp::IwiImage &dst;
|
||||
|
||||
int radius;
|
||||
Ipp32f valSquareSigma;
|
||||
Ipp32f posSquareSigma;
|
||||
::ipp::IwiBorderType borderType;
|
||||
|
||||
bool *pOk;
|
||||
const ipp_bilateralFilterParallel& operator= (const ipp_bilateralFilterParallel&);
|
||||
};
|
||||
#endif
|
||||
|
||||
static bool ipp_bilateralFilter(Mat &src, Mat &dst, int d, double sigmaColor, double sigmaSpace, int borderType)
|
||||
{
|
||||
#ifdef HAVE_IPP_IW
|
||||
CV_INSTRUMENT_REGION_IPP();
|
||||
|
||||
constexpr double eps = 1e-6;
|
||||
if( sigmaColor <= eps || sigmaSpace <= eps )
|
||||
{
|
||||
src.copyTo(dst);
|
||||
return true;
|
||||
}
|
||||
|
||||
int radius = IPP_MAX(((d <= 0)?cvRound(sigmaSpace*1.5):d/2), 1);
|
||||
Ipp32f valSquareSigma = (Ipp32f)(sigmaColor*sigmaColor);
|
||||
Ipp32f posSquareSigma = (Ipp32f)(sigmaSpace*sigmaSpace);
|
||||
|
||||
// Acquire data and begin processing
|
||||
try
|
||||
{
|
||||
::ipp::IwiImage iwSrc = ippiGetImage(src);
|
||||
::ipp::IwiImage iwDst = ippiGetImage(dst);
|
||||
::ipp::IwiBorderSize borderSize(radius);
|
||||
::ipp::IwiBorderType ippBorder(ippiGetBorder(iwSrc, borderType, borderSize));
|
||||
if(!ippBorder)
|
||||
return false;
|
||||
|
||||
const int threads = ippiSuggestThreadsNum(iwDst, 2);
|
||||
if(IPP_BILATERAL_PARALLEL && threads > 1) {
|
||||
bool ok = true;
|
||||
Range range(0, (int)iwDst.m_size.height);
|
||||
ipp_bilateralFilterParallel invoker(iwSrc, iwDst, radius, valSquareSigma, posSquareSigma, ippBorder, &ok);
|
||||
if(!ok)
|
||||
return false;
|
||||
|
||||
// Tile height can't be smaller than the radius.
|
||||
// Otherwise, the second tile has mixed top border (pixels from both
|
||||
// inmem and outside should be used), which is not supported in IPP.
|
||||
int maxTiles = (int)iwDst.m_size.height / radius;
|
||||
int numTiles = threads * 4;
|
||||
if (numTiles > maxTiles) {
|
||||
// Keep the tiles number as multiple of threads for the better workload balance.
|
||||
numTiles = (maxTiles / threads) * threads;
|
||||
}
|
||||
parallel_for_(range, invoker, numTiles);
|
||||
|
||||
if(!ok)
|
||||
return false;
|
||||
} else {
|
||||
CV_INSTRUMENT_FUN_IPP(::ipp::iwiFilterBilateral, iwSrc, iwDst, radius, valSquareSigma, posSquareSigma, ::ipp::IwDefault(), ippBorder);
|
||||
}
|
||||
}
|
||||
catch (const ::ipp::IwException &)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
#else
|
||||
CV_UNUSED(src); CV_UNUSED(dst); CV_UNUSED(d); CV_UNUSED(sigmaColor); CV_UNUSED(sigmaSpace); CV_UNUSED(borderType);
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
void bilateralFilter( InputArray _src, OutputArray _dst, int d,
|
||||
double sigmaColor, double sigmaSpace,
|
||||
int borderType )
|
||||
@@ -454,10 +340,20 @@ void bilateralFilter( InputArray _src, OutputArray _dst, int d,
|
||||
|
||||
Mat src = _src.getMat(), dst = _dst.getMat();
|
||||
|
||||
CALL_HAL(bilateralFilter, cv_hal_bilateralFilter, src.data, src.step, dst.data, dst.step, src.cols, src.rows, src.depth(),
|
||||
src.channels(), d, sigmaColor, sigmaSpace, borderType);
|
||||
{
|
||||
Point ofs;
|
||||
Size wsz(src.cols, src.rows);
|
||||
if( !(borderType & BORDER_ISOLATED) )
|
||||
src.locateROI( wsz, ofs );
|
||||
|
||||
CV_IPP_RUN_FAST(ipp_bilateralFilter(src, dst, d, sigmaColor, sigmaSpace, borderType));
|
||||
CALL_HAL(bilateralFilter, cv_hal_bilateralFilter_offset, src.data, src.step, dst.data, dst.step,
|
||||
src.cols, src.rows, src.depth(), src.channels(),
|
||||
ofs.x, ofs.y, wsz.width - src.cols - ofs.x, wsz.height - src.rows - ofs.y,
|
||||
d, sigmaColor, sigmaSpace, borderType & (~BORDER_ISOLATED));
|
||||
}
|
||||
|
||||
CALL_HAL(bilateralFilter, cv_hal_bilateralFilter, src.data, src.step, dst.data, dst.step,
|
||||
src.cols, src.rows, src.depth(), src.channels(), d, sigmaColor, sigmaSpace, borderType);
|
||||
|
||||
if( src.depth() == CV_8U )
|
||||
bilateralFilter_8u( src, dst, d, sigmaColor, sigmaSpace, borderType );
|
||||
|
||||
@@ -1204,6 +1204,35 @@ inline int hal_ni_bilateralFilter(const uchar* src_data, size_t src_step, uchar*
|
||||
#define cv_hal_bilateralFilter hal_ni_bilateralFilter
|
||||
//! @endcond
|
||||
|
||||
/**
|
||||
@brief Calculate bilateral filter for input tile with optional margins for submatrix. See https://homepages.inf.ed.ac.uk/rbf/CVonline/LOCAL_COPIES/MANDUCHI1/Bilateral_Filtering.html
|
||||
@param src_data Source image 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 depth Depths of source and destination image. Should support CV_8U and CV_32F
|
||||
@param cn Number of channels
|
||||
@param margin_left Left margins for source image
|
||||
@param margin_top Top margins for source image
|
||||
@param margin_right Right margins for source image
|
||||
@param margin_bottom Bottom margins for source image
|
||||
@param d Diameter of each pixel neighborhood that is used during filtering. If it is non-positive, it is computed from sigmaSpace
|
||||
@param sigma_color Filter sigma in the color space
|
||||
@param sigma_space Filter sigma in the coordinate space. When d>0, it specifies the neighborhood size regardless of sigmaSpace. Otherwise, d is proportional to sigmaSpace
|
||||
@param border_type border mode used to extrapolate pixels outside of the image
|
||||
*/
|
||||
inline int hal_ni_bilateralFilter_offset(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step,
|
||||
int width, int height, int depth, int cn,
|
||||
int margin_left, int margin_top, int margin_right, int margin_bottom,
|
||||
int d, double sigma_color, double sigma_space, int border_type)
|
||||
{ return CV_HAL_ERROR_NOT_IMPLEMENTED; }
|
||||
|
||||
//! @cond IGNORED
|
||||
#define cv_hal_bilateralFilter_offset hal_ni_bilateralFilter_offset
|
||||
//! @endcond
|
||||
|
||||
/**
|
||||
@brief Calculates adaptive threshold
|
||||
@param src_data Source image data
|
||||
|
||||
Reference in New Issue
Block a user