OpenCV每日函数 图像过滤模块 (1) bilateralFilter函数(双边滤波)

一、概述

        过滤可能是图像处理和计算机视觉中最基本的操作。在术语“过滤”的最广义上,过滤图像在给定位置的值是输入图像在同一位置的小邻域中的值的函数。

        例如,高斯低通滤波计算邻域中像素值的加权平均值,其中权重随着与邻域中心的距离而减小。尽管可以给出这种权重下降的正式和定量解释,但直觉是图像通常在空间上变化缓慢,因此附近的像素可能具有相似的值,因此将它们平均在一起是合适的。破坏这些附近像素的噪声值相互之间的相关性低于信号值,缓慢空间变化的假设在边缘处失败,因此被线性低通滤波模糊。

        我们如何防止在边缘进行平均,同时仍然在平滑区域内进行平均? 许多努力致力于减少这种不希望的影响。双边滤波是一种简单的、非迭代的边缘保持平滑方案。

        该函数对输入图像应用双边过滤。如下网址所述,双边过滤器可以很好地减少不需要的噪声,同时保持边缘相当清晰。 但是,与大多数过滤器相比,它非常慢。Bilateral Filteringhttp://www.dai.ed.ac.uk/CVonline/LOCAL_COPIES/MANDUCHI1/Bilateral_Filtering.html

二、bilateralFilter函数

1、函数原型

void cv::bilateralFilter(InputArray src,OutputArray dst,int d, double sigmaColor,double sigmaSpace,int borderType = BORDER_DEFAULT )	

        Sigma 值:为简单起见,您可以将 2 个 sigma 值设置为相同。 如果它们很小(< 10),则滤镜不会有太大的效果,而如果它们很大(> 150),它们会产生非常强烈的效果,使图像看起来“卡通”。

        过滤器大小:大过滤器(d > 5)非常慢,因此建议在实时应用中使用 d=5,对于需要大量噪声过滤的离线应用,建议使用 d=9。

2、参数详解 

src 源 8 位或浮点、1 通道或 3 通道图像。
dst 与 src 大小和类型相同的目标图像。
d 过滤期间使用的每个像素邻域的直径。 如果它是非正数,则从 sigmaSpace 计算。
sigmaColor 在颜色空间中过滤 sigma。 较大的参数值意味着像素邻域内更远的颜色(参见 sigmaSpace)将混合在一起,从而产生更大的半等色区域。
sigmaSpace 在坐标空间中过滤 sigma。 较大的参数值意味着更远的像素将相互影响,只要它们的颜色足够接近(参见 sigmaColor )。 当 d>0 时,它指定邻域大小,而不考虑 sigmaSpace。 否则,d 与 sigmaSpace 成正比。
borderType 用于推断图像外部像素的边框模式,请参阅 BorderTypes

三、OpenCV源码 

1、源码路径

opencv\modules\imgproc\src\bilateral_filter.dispatch.cpp

2、源码代码

void bilateralFilter( InputArray _src, OutputArray _dst, int d,
                      double sigmaColor, double sigmaSpace,
                      int borderType )
{
    CV_INSTRUMENT_REGION();

    CV_Assert(!_src.empty());

    _dst.create( _src.size(), _src.type() );

    CV_OCL_RUN(_src.dims() <= 2 && _dst.isUMat(),
               ocl_bilateralFilter_8u(_src, _dst, d, sigmaColor, sigmaSpace, borderType))

    Mat src = _src.getMat(), dst = _dst.getMat();

    CV_IPP_RUN_FAST(ipp_bilateralFilter(src, dst, d, sigmaColor, sigmaSpace, borderType));

    if( src.depth() == CV_8U )
        bilateralFilter_8u( src, dst, d, sigmaColor, sigmaSpace, borderType );
    else if( src.depth() == CV_32F )
        bilateralFilter_32f( src, dst, d, sigmaColor, sigmaSpace, borderType );
    else
        CV_Error( CV_StsUnsupportedFormat,
        "Bilateral filtering is only implemented for 8u and 32f images" );
}
static void
bilateralFilter_8u( const Mat& src, Mat& dst, int d,
    double sigma_color, double sigma_space,
    int borderType )
{
    CV_INSTRUMENT_REGION();

    int cn = src.channels();
    int i, j, maxk, radius;

    CV_Assert( (src.type() == CV_8UC1 || src.type() == CV_8UC3) && src.data != dst.data );

    if( sigma_color <= 0 )
        sigma_color = 1;
    if( sigma_space <= 0 )
        sigma_space = 1;

    double gauss_color_coeff = -0.5/(sigma_color*sigma_color);
    double gauss_space_coeff = -0.5/(sigma_space*sigma_space);

    if( d <= 0 )
        radius = cvRound(sigma_space*1.5);
    else
        radius = d/2;
    radius = MAX(radius, 1);
    d = radius*2 + 1;

    Mat temp;
    copyMakeBorder( src, temp, radius, radius, radius, radius, borderType );

    std::vector<float> _color_weight(cn*256);
    std::vector<float> _space_weight(d*d);
    std::vector<int> _space_ofs(d*d);
    float* color_weight = &_color_weight[0];
    float* space_weight = &_space_weight[0];
    int* space_ofs = &_space_ofs[0];

    // initialize color-related bilateral filter coefficients

    for( i = 0; i < 256*cn; i++ )
        color_weight[i] = (float)std::exp(i*i*gauss_color_coeff);

    // initialize space-related bilateral filter coefficients
    for( i = -radius, maxk = 0; i <= radius; i++ )
    {
        j = -radius;

        for( ; j <= radius; j++ )
        {
            double r = std::sqrt((double)i*i + (double)j*j);
            if( r > radius )
                continue;
            space_weight[maxk] = (float)std::exp(r*r*gauss_space_coeff);
            space_ofs[maxk++] = (int)(i*temp.step + j*cn);
        }
    }

    CV_CPU_DISPATCH(bilateralFilterInvoker_8u, (dst, temp, radius, maxk, space_ofs, space_weight, color_weight),
        CV_CPU_DISPATCH_MODES_ALL);
}
static void
bilateralFilter_32f( const Mat& src, Mat& dst, int d,
                     double sigma_color, double sigma_space,
                     int borderType )
{
    CV_INSTRUMENT_REGION();

    int cn = src.channels();
    int i, j, maxk, radius;
    double minValSrc=-1, maxValSrc=1;
    const int kExpNumBinsPerChannel = 1 << 12;
    int kExpNumBins = 0;
    float lastExpVal = 1.f;
    float len, scale_index;

    CV_Assert( (src.type() == CV_32FC1 || src.type() == CV_32FC3) && src.data != dst.data );

    if( sigma_color <= 0 )
        sigma_color = 1;
    if( sigma_space <= 0 )
        sigma_space = 1;

    double gauss_color_coeff = -0.5/(sigma_color*sigma_color);
    double gauss_space_coeff = -0.5/(sigma_space*sigma_space);

    if( d <= 0 )
        radius = cvRound(sigma_space*1.5);
    else
        radius = d/2;
    radius = MAX(radius, 1);
    d = radius*2 + 1;
    // compute the min/max range for the input image (even if multichannel)

    minMaxLoc( src.reshape(1), &minValSrc, &maxValSrc );
    if(std::abs(minValSrc - maxValSrc) < FLT_EPSILON)
    {
        src.copyTo(dst);
        return;
    }

    // temporary copy of the image with borders for easy processing
    Mat temp;
    copyMakeBorder( src, temp, radius, radius, radius, radius, borderType );

    // allocate lookup tables
    std::vector<float> _space_weight(d*d);
    std::vector<int> _space_ofs(d*d);
    float* space_weight = &_space_weight[0];
    int* space_ofs = &_space_ofs[0];

    // assign a length which is slightly more than needed
    len = (float)(maxValSrc - minValSrc) * cn;
    kExpNumBins = kExpNumBinsPerChannel * cn;
    std::vector<float> _expLUT(kExpNumBins+2);
    float* expLUT = &_expLUT[0];

    scale_index = kExpNumBins/len;

    // initialize the exp LUT
    for( i = 0; i < kExpNumBins+2; i++ )
    {
        if( lastExpVal > 0.f )
        {
            double val =  i / scale_index;
            expLUT[i] = (float)std::exp(val * val * gauss_color_coeff);
            lastExpVal = expLUT[i];
        }
        else
            expLUT[i] = 0.f;
    }

    // initialize space-related bilateral filter coefficients
    for( i = -radius, maxk = 0; i <= radius; i++ )
        for( j = -radius; j <= radius; j++ )
        {
            double r = std::sqrt((double)i*i + (double)j*j);
            if( r > radius || ( i == 0 && j == 0 ) )
                continue;
            space_weight[maxk] = (float)std::exp(r*r*gauss_space_coeff);
            space_ofs[maxk++] = (int)(i*(temp.step/sizeof(float)) + j*cn);
        }

    // parallel_for usage
    CV_CPU_DISPATCH(bilateralFilterInvoker_32f, (cn, radius, maxk, space_ofs, temp, dst, scale_index, space_weight, expLUT),
        CV_CPU_DISPATCH_MODES_ALL);
}

四、效果图像示例

原图

d = sigmaColor = sigmaSpace = 33
d = 33;sigmaColor = sigmaSpace = 150

猜你喜欢

转载自blog.csdn.net/bashendixie5/article/details/125185429