bilareralFilter双边滤波函数

C++: void **bilateralFilte**r(InputArray src, OutputArray dst, int d, double sigmaColor, double sigmaSpace, int borderType=BORDER_DEFAULT )¶ 
表示对图像进行双边滤波,它能很好地对图像进行滤波,去除噪声的能力很好,并且增强边缘,但是运行慢相对于其他的滤波器 
参数的详解: 
src:表示输入的图像 
OutputArray dst:表示输出的图像 
int类型的d,表示在过滤过程中每个像素邻域的直径。如果这个值我们设其为非正数,那么OpenCV会从第五个参数sigmaSpace来计算出它来。 
double类型的sigmaColor,颜色空间滤波器的sigma值。这个参数的值越大,就表明该像素邻域内有更宽广的颜色会被混合到一起,产生较大的半相等颜色区域。 
double类型的sigmaSpace坐标空间中滤波器的sigma值,坐标空间的标注方差。他的数值越大,意味着越远的像素会相互影响,从而使更大的区域足够相似的颜色获取相同的颜色。当d>0,d指定了邻域大小且与sigmaSpace无关。否则,d正比于sigmaSpace。 
int类型的borderType,用于推断图像外部像素的某种边界模式。注意它有默认值BORDER_DEFAULT。

简单地说它是以被处理像素与其周围像素的距离作为权重而进行的一种加权平均过程。 
高斯距离权值为: 
这里写图片描述

其中,d(ξ,x)=d(ξ-x)=||ξ-x||表示的是两个像素ξ和x之间的距离。 
但该权值仅仅考虑的是距离,而对像素本身的亮度信息没有考虑,因此高斯滤波的结果是使整幅图像都模糊了,即图像的边缘信息(高频部分)被严重削弱了。我们知道当图像中邻域像素亮度与被处理像素的亮度差异很大时,邻域像素与该像素的关系很小的,即两者相似性很差。因此双边滤波还考虑了领域像素的亮度信息,通过计算相似度来赋予领域像素一定的权重,下面就是高斯相似度的权值:

这里写图片描述

其中,σ(f(ξ),f(x))=||f(ξ)-f(x)||表示两个像素亮度之差。最终的双边滤波的公式为:

这里写图片描述

其中,这里写图片描述,Σ表示的是邻域范围,及滤波内核大小,f(ξ)表示原图。

代码详解:

<ol start="1" class="dp-cpp" style="padding: 0px; border: none; color: rgb(92, 92, 92); font-family: Consolas, 'Courier New', Courier, mono, serif; font-size: 12px; line-height: 26px; margin: 0px 0px 1px 45px !important;"><li class="alt" style="border-style: none none none solid; border-left-width: 3px; border-left-color: rgb(108, 226, 108); list-style: decimal-leading-zero outside; color: inherit; line-height: 18px; margin: 0px !important; padding: 0px 3px 0px 10px !important;"><span style="margin: 0px; padding: 0px; border: none; color: black; background-color: inherit;"><span class="keyword" style="margin: 0px; padding: 0px; border: none; color: rgb(0, 102, 153); font-weight: bold; background-color: inherit;">void</span><span style="margin: 0px; padding: 0px; border: none; background-color: inherit;"> cv::adaptiveBilateralFilter( InputArray _src, OutputArray _dst, Size ksize,  </span></span></li><li style="border-style: none none none solid; border-left-width: 3px; border-left-color: rgb(108, 226, 108); list-style: decimal-leading-zero outside; line-height: 18px; margin: 0px !important; padding: 0px 3px 0px 10px !important; background-color: rgb(248, 248, 248);"><span style="margin: 0px; padding: 0px; border: none; color: black; background-color: inherit;">                                  <span class="datatypes" style="margin: 0px; padding: 0px; border: none; color: rgb(46, 139, 87); font-weight: bold; background-color: inherit;">double</span><span style="margin: 0px; padding: 0px; border: none; background-color: inherit;"> sigmaSpace, </span><span class="datatypes" style="margin: 0px; padding: 0px; border: none; color: rgb(46, 139, 87); font-weight: bold; background-color: inherit;">double</span><span style="margin: 0px; padding: 0px; border: none; background-color: inherit;"> maxSigmaColor, Point anchor, </span><span class="datatypes" style="margin: 0px; padding: 0px; border: none; color: rgb(46, 139, 87); font-weight: bold; background-color: inherit;">int</span><span style="margin: 0px; padding: 0px; border: none; background-color: inherit;"> borderType )  </span></span></li><li class="alt" style="border-style: none none none solid; border-left-width: 3px; border-left-color: rgb(108, 226, 108); list-style: decimal-leading-zero outside; color: inherit; line-height: 18px; margin: 0px !important; padding: 0px 3px 0px 10px !important;"><span style="margin: 0px; padding: 0px; border: none; color: black; background-color: inherit;">{  </span></li><li style="border-style: none none none solid; border-left-width: 3px; border-left-color: rgb(108, 226, 108); list-style: decimal-leading-zero outside; line-height: 18px; margin: 0px !important; padding: 0px 3px 0px 10px !important; background-color: rgb(248, 248, 248);"><span style="margin: 0px; padding: 0px; border: none; color: black; background-color: inherit;">    <span class="comment" style="margin: 0px; padding: 0px; border: none; color: rgb(0, 130, 0); background-color: inherit;">//得到输入图像矩阵和与其尺寸类型一致的输出图像矩阵</span><span style="margin: 0px; padding: 0px; border: none; background-color: inherit;">  </span></span></li><li class="alt" style="border-style: none none none solid; border-left-width: 3px; border-left-color: rgb(108, 226, 108); list-style: decimal-leading-zero outside; color: inherit; line-height: 18px; margin: 0px !important; padding: 0px 3px 0px 10px !important;"><span style="margin: 0px; padding: 0px; border: none; color: black; background-color: inherit;">    Mat src = _src.getMat();  </span></li><li style="border-style: none none none solid; border-left-width: 3px; border-left-color: rgb(108, 226, 108); list-style: decimal-leading-zero outside; line-height: 18px; margin: 0px !important; padding: 0px 3px 0px 10px !important; background-color: rgb(248, 248, 248);"><span style="margin: 0px; padding: 0px; border: none; color: black; background-color: inherit;">    _dst.create(src.size(), src.type());  </span></li><li class="alt" style="border-style: none none none solid; border-left-width: 3px; border-left-color: rgb(108, 226, 108); list-style: decimal-leading-zero outside; color: inherit; line-height: 18px; margin: 0px !important; padding: 0px 3px 0px 10px !important;"><span style="margin: 0px; padding: 0px; border: none; color: black; background-color: inherit;">    Mat dst = _dst.getMat();  </span></li><li style="border-style: none none none solid; border-left-width: 3px; border-left-color: rgb(108, 226, 108); list-style: decimal-leading-zero outside; line-height: 18px; margin: 0px !important; padding: 0px 3px 0px 10px !important; background-color: rgb(248, 248, 248);"><span style="margin: 0px; padding: 0px; border: none; color: black; background-color: inherit;">    <span class="comment" style="margin: 0px; padding: 0px; border: none; color: rgb(0, 130, 0); background-color: inherit;">//该算法只能处理8位二进制的灰度图像和三通道的彩色图像</span><span style="margin: 0px; padding: 0px; border: none; background-color: inherit;">  </span></span></li><li class="alt" style="border-style: none none none solid; border-left-width: 3px; border-left-color: rgb(108, 226, 108); list-style: decimal-leading-zero outside; color: inherit; line-height: 18px; margin: 0px !important; padding: 0px 3px 0px 10px !important;"><span style="margin: 0px; padding: 0px; border: none; color: black; background-color: inherit;">    CV_Assert(src.type() == CV_8UC1 || src.type() == CV_8UC3);  </span></li><li style="border-style: none none none solid; border-left-width: 3px; border-left-color: rgb(108, 226, 108); list-style: decimal-leading-zero outside; line-height: 18px; margin: 0px !important; padding: 0px 3px 0px 10px !important; background-color: rgb(248, 248, 248);"><span style="margin: 0px; padding: 0px; border: none; color: black; background-color: inherit;">    <span class="comment" style="margin: 0px; padding: 0px; border: none; color: rgb(0, 130, 0); background-color: inherit;">//得到滤波内核的锚点</span><span style="margin: 0px; padding: 0px; border: none; background-color: inherit;">  </span></span></li><li class="alt" style="border-style: none none none solid; border-left-width: 3px; border-left-color: rgb(108, 226, 108); list-style: decimal-leading-zero outside; color: inherit; line-height: 18px; margin: 0px !important; padding: 0px 3px 0px 10px !important;"><span style="margin: 0px; padding: 0px; border: none; color: black; background-color: inherit;">    anchor = normalizeAnchor(anchor,ksize);  </span></li><li style="border-style: none none none solid; border-left-width: 3px; border-left-color: rgb(108, 226, 108); list-style: decimal-leading-zero outside; line-height: 18px; margin: 0px !important; padding: 0px 3px 0px 10px !important; background-color: rgb(248, 248, 248);"><span style="margin: 0px; padding: 0px; border: none; color: black; background-color: inherit;">    <span class="keyword" style="margin: 0px; padding: 0px; border: none; color: rgb(0, 102, 153); font-weight: bold; background-color: inherit;">if</span><span style="margin: 0px; padding: 0px; border: none; background-color: inherit;">( src.depth() == CV_8U )  </span></span></li><li class="alt" style="border-style: none none none solid; border-left-width: 3px; border-left-color: rgb(108, 226, 108); list-style: decimal-leading-zero outside; color: inherit; line-height: 18px; margin: 0px !important; padding: 0px 3px 0px 10px !important;"><span style="margin: 0px; padding: 0px; border: none; color: black; background-color: inherit;">        adaptiveBilateralFilter_8u( src, dst, ksize, sigmaSpace, maxSigmaColor, anchor, borderType );  </span></li><li style="border-style: none none none solid; border-left-width: 3px; border-left-color: rgb(108, 226, 108); list-style: decimal-leading-zero outside; line-height: 18px; margin: 0px !important; padding: 0px 3px 0px 10px !important; background-color: rgb(248, 248, 248);"><span style="margin: 0px; padding: 0px; border: none; color: black; background-color: inherit;">    <span class="keyword" style="margin: 0px; padding: 0px; border: none; color: rgb(0, 102, 153); font-weight: bold; background-color: inherit;">else</span><span style="margin: 0px; padding: 0px; border: none; background-color: inherit;">  </span></span></li><li class="alt" style="border-style: none none none solid; border-left-width: 3px; border-left-color: rgb(108, 226, 108); list-style: decimal-leading-zero outside; color: inherit; line-height: 18px; margin: 0px !important; padding: 0px 3px 0px 10px !important;"><span style="margin: 0px; padding: 0px; border: none; color: black; background-color: inherit;">        CV_Error( CV_StsUnsupportedFormat,  </span></li><li style="border-style: none none none solid; border-left-width: 3px; border-left-color: rgb(108, 226, 108); list-style: decimal-leading-zero outside; line-height: 18px; margin: 0px !important; padding: 0px 3px 0px 10px !important; background-color: rgb(248, 248, 248);"><span style="margin: 0px; padding: 0px; border: none; color: black; background-color: inherit;">        <span class="string" style="margin: 0px; padding: 0px; border: none; color: blue; background-color: inherit;">"Adaptive Bilateral filtering is only implemented for 8u images"</span><span style="margin: 0px; padding: 0px; border: none; background-color: inherit;"> );  </span></span></li><li class="alt" style="border-style: none none none solid; border-left-width: 3px; border-left-color: rgb(108, 226, 108); list-style: decimal-leading-zero outside; color: inherit; line-height: 18px; margin: 0px !important; padding: 0px 3px 0px 10px !important;"><span style="margin: 0px; padding: 0px; border: none; color: black; background-color: inherit;">}  </span></li></ol>
在这里我们以处理 8 为二进制图像为例,因此它调用了 bilateralFilter_8u 函数:

  1. static void  
  2. bilateralFilter_8u( const Mat& src, Mat& dst, int d,  
  3.     double sigma_color, double sigma_space,  
  4.     int borderType )  
  5. {  
  6.   
  7.     int cn = src.channels();        //得到图像的通道数,即是灰度图像还是彩色图像  
  8.     int i, j, maxk, radius;  
  9.     Size size = src.size();         //得到图像的大小尺寸  
  10.     //处理之前再次检查图像中的相关信息是否正确  
  11.     CV_Assert( (src.type() == CV_8UC1 || src.type() == CV_8UC3) &&  
  12.               src.type() == dst.type() && src.size() == dst.size() &&  
  13.               src.data != dst.data );  
  14.     //如果在函数调用时给出的高斯公式中的两个σ值小于0,则为1  
  15.     if( sigma_color <= 0 )  
  16.         sigma_color = 1;  
  17.     if( sigma_space <= 0 )  
  18.         sigma_space = 1;  
  19.     //计算两个高斯公式中的系数,即e指数部分的分母  
  20.     double gauss_color_coeff = -0.5/(sigma_color*sigma_color);  
  21.     double gauss_space_coeff = -0.5/(sigma_space*sigma_space);  
  22.   
  23.     if( d <= 0 ) //如果调用双边滤波函数时给出的滤波内核大小小于等于0  
  24.         radius = cvRound(sigma_space*1.5);      //根据σd来自动给出内核的半径  
  25.     else            //否则得到内核的半径  
  26.         radius = d/2;  
  27.     radius = MAX(radius, 1);        //保证内核半径大于1  
  28.     d = radius*2 + 1;       //重新得到内核大小尺寸  
  29.     //为了在图像边界处得到更好的处理效果,需要对图像四周边界做适当的处理  
  30.     //把原图的四周都加宽为内核半径的宽度,而加宽部分的像素值由borderType值决定  
  31.     //待处理的图像由src换成了temp  
  32.     Mat temp;  
  33.     copyMakeBorder( src, temp, radius, radius, radius, radius, borderType );  
  34.   
  35. #if defined HAVE_IPP && (IPP_VERSION_MAJOR >= 7)  
  36.     if( cn == 1 )  
  37.     {  
  38.         bool ok;  
  39.         IPPBilateralFilter_8u_Invoker body(temp, dst, sigma_color * sigma_color, sigma_space * sigma_space, radius, &ok );  
  40.         parallel_for_(Range(0, dst.rows), body, dst.total()/(double)(1<<16));  
  41.         if( ok ) return;  
  42.     }  
  43. #endif  
  44.   
  45.     //无论是距离权值还是相似度权值都是事先计算后,并保持在相应的矢量中  
  46.     vector<float> _color_weight(cn*256);      //保持相似度权值的矢量  
  47.     vector<float> _space_weight(d*d);     //保持距离权值的矢量  
  48.     vector<int> _space_ofs(d*d);              //距离的偏移量  
  49.     //得到各个矢量的第一个数据的地址指针  
  50.     float* color_weight = &_color_weight[0];  
  51.     float* space_weight = &_space_weight[0];  
  52.     int* space_ofs = &_space_ofs[0];  
  53.   
  54.     // initialize color-related bilateral filter coefficients  
  55.     //计算亮度相似度权值  
  56.     /*如果是灰度图像,亮度是从0~255,因此两个灰度值的差值也是在0~255之间,事先计算这些值把它们保持在color_weight中,当要用到相似度权值时,只要知道两个灰度值的差值,通过color_weight,就很容易得到权值;而如果是彩色图像,则是把一个像素中的红、绿、蓝三色加在一起后得到一个差值,而不是一个像素分别有红、绿、蓝三个差值,因此彩色图像两个像素的差值范围是在0~256×3之间。*/  
  57.     for( i = 0; i < 256*cn; i++ )  
  58.         color_weight[i] = (float)std::exp(i*i*gauss_color_coeff);       //高斯公式  
  59.   
  60.     // initialize space-related bilateral filter coefficients  
  61.     //计算距离权值  
  62.     for( i = -radius, maxk = 0; i <= radius; i++ )  
  63.     {  
  64.         j = -radius;  
  65.   
  66.         for( ;j <= radius; j++ )  
  67.         {  
  68.             double r = std::sqrt((double)i*i + (double)j*j);        //距离范数  
  69.             if( r > radius )         //如果距离大于内核半径,则抛弃该值  
  70.                 continue;  
  71.             space_weight[maxk] = (float)std::exp(r*r*gauss_space_coeff);    //高斯公式  
  72.             //得到偏移量,在后面通过该偏移量来找到相应的像素  
  73.             space_ofs[maxk++] = (int)(i*temp.step + j*cn);  
  74.         }  
  75.     }  
  76.     //通过实例化BilateralFilter_8u_Invoker类计算得到双边滤波的结果  
  77.     BilateralFilter_8u_Invoker body(dst, temp, radius, maxk, space_ofs, space_weight, color_weight);  
  78.     parallel_for_(Range(0, size.height), body, dst.total()/(double)(1<<16));  
  79. }  
BilateralFilter_8u_Invoker 类中的 operator()

  1. virtual void operator() (const Range& range) const  
  2. {  
  3.     int i, j, cn = dest->channels(), k;  
  4.     Size size = dest->size();  
  5.     #if CV_SSE3  
  6.      ……略……  
  7.     #endif  
  8.   
  9.     for( i = range.start; i < range.end; i++ )  
  10.     {  
  11.         const uchar* sptr = temp->ptr(i+radius) + radius*cn; //得到原图数据指针  
  12.         uchar* dptr = dest->ptr(i);  //输出图像指针  
  13.   
  14.         if( cn == 1 )       //灰度图像  
  15.         {  
  16.             for( j = 0; j < size.width; j++ )  
  17.             {  
  18.                 float sum = 0, wsum = 0;  
  19.                 int val0 = sptr[j]; //内核的中心像素,即待处理的像素值  
  20.                 k = 0;  
  21.                 #if CV_SSE3  
  22.                 ……略……  
  23.                 #endif  
  24.                 //遍历这个内核  
  25.                 for( ; k < maxk; k++ )  
  26.                 {  
  27.                     int val = sptr[j + space_ofs[k]];   //得到邻域像素值  
  28.                     //计算公式3中的k中的一个值  
  29.                     float w = space_weight[k]*color_weight[std::abs(val - val0)];  
  30.                     //计算公式3中的分子部分  
  31.                     sum += val*w;  
  32.                     //计算公式3中的分母部分  
  33.                     wsum += w;  
  34.                 }  
  35.                 // overflow is not possible here => there is no need to use CV_CAST_8U  
  36.                 //得到处理后的结果  
  37.                 dptr[j] = (uchar)cvRound(sum/wsum);  
  38.             }  
  39.         }  
  40.         else        //彩色图像  
  41.         {  
  42.             assert( cn == 3 );  
  43.             for( j = 0; j < size.width*3; j += 3 )  
  44.             {  
  45.                 float sum_b = 0, sum_g = 0, sum_r = 0, wsum = 0;  
  46.                 int b0 = sptr[j], g0 = sptr[j+1], r0 = sptr[j+2];  
  47.                 k = 0;  
  48.                 #if CV_SSE3  
  49.                 ……略……  
  50.                 #endif  
  51.   
  52.                 for( ; k < maxk; k++ )  
  53.                 {  
  54.                     const uchar* sptr_k = sptr + j + space_ofs[k];  //得到邻域像素  
  55.                     //分别得到邻域像素中的蓝、绿、红分量  
  56.                     int b = sptr_k[0], g = sptr_k[1], r = sptr_k[2];  
  57.                     //计算公式3中的k值,  
  58.                     //其中相似度权值中的像素差值是红、绿、蓝分量差值之和  
  59.                     float w = space_weight[k]*color_weight[std::abs(b - b0) +  
  60.                                                      std::abs(g - g0) + std::abs(r - r0)];  
  61.                     //利用公式3分别得到红、绿、蓝三个分量值  
  62.                     sum_b += b*w; sum_g += g*w; sum_r += r*w;  
  63.                     wsum += w;  
  64.                 }  
  65.                 wsum = 1.f/wsum;  
  66.                 b0 = cvRound(sum_b*wsum);  
  67.                 g0 = cvRound(sum_g*wsum);  
  68.                 r0 = cvRound(sum_r*wsum);  
  69.                 dptr[j] = (uchar)b0; dptr[j+1] = (uchar)g0; dptr[j+2] = (uchar)r0;  
  70.             }  
  71.         }  
  72.     }  
  73. }  

Opencv中的双边滤波的源码分析就到这里,另外根据Opencv的文档中介绍,两个高斯公式的σ可以相同,而且如果σ小于10,则滤波效果不明显,如果大于150,则会有强烈的卡通效果。当实时处理时,内核尺寸d推荐为5;如果在非实时处理情况下,而且有较强的噪声时,d9效果会较好。



MATLAB代码:

clear all;
close all;
clc;

img=imread('lena.jpg');
img=mat2gray(img);
[m n]=size(img);
imshow(img);

r=10;        %模板半径
imgn=zeros(m+2*r+1,n+2*r+1);
imgn(r+1:m+r,r+1:n+r)=img;
imgn(1:r,r+1:n+r)=img(1:r,1:n);                 %扩展上边界
imgn(1:m+r,n+r+1:n+2*r+1)=imgn(1:m+r,n:n+r);    %扩展右边界
imgn(m+r+1:m+2*r+1,r+1:n+2*r+1)=imgn(m:m+r,r+1:n+2*r+1);    %扩展下边界
imgn(1:m+2*r+1,1:r)=imgn(1:m+2*r+1,r+1:2*r);       %扩展左边界

sigma_d=2;
sigma_r=0.1;
[x,y] = meshgrid(-r:r,-r:r);
w1=exp(-(x.^2+y.^2)/(2*sigma_d^2));     %以距离作为自变量高斯滤波器

h=waitbar(0,'wait...');
for i=r+1:m+r
    for j=r+1:n+r        
        w2=exp(-(imgn(i-r:i+r,j-r:j+r)-imgn(i,j)).^2/(2*sigma_r^2)); %以周围和当前像素灰度差值作为自变量的高斯滤波器
        w=w1.*w2;
        
        s=imgn(i-r:i+r,j-r:j+r).*w;
        imgn(i,j)=sum(sum(s))/sum(sum(w));
    
    end
    waitbar(i/m);
end
close(h)

figure;
imshow(mat2gray(imgn(r+1:m+r,r+1:n+r)));

opencv代码:

#include "cv.h"
#include "highgui.h"
#include <iostream>

using namespace std;
using namespace cv;

int main(int argc, char* argv[])
{
        Mat src = imread("misaka.jpg");
        Mat dst;

        //参数是按顺序写的

        //高斯滤波
        //src:输入图像
        //dst:输出图像
        //Size(5,5)模板大小,为奇数
        //x方向方差
        //Y方向方差
        GaussianBlur(src,dst,Size(5,5),0,0);
        imwrite("gauss.jpg",dst);
        
        //中值滤波
        //src:输入图像
        //dst::输出图像
        //模板宽度,为奇数
        medianBlur(src,dst,3);
        imwrite("med.jpg",dst);
        
        //均值滤波
        //src:输入图像
        //dst:输出图像
        //模板大小
        //Point(-1,-1):被平滑点位置,为负值取核中心
        blur(src,dst,Size(3,3),Point(-1,-1));
        imwrite("mean.jpg",dst);

        //双边滤波
        //src:输入图像
        //dst:输入图像
        //滤波模板半径
        //颜色空间标准差
        //坐标空间标准差
        bilateralFilter(src,dst,5,10.0,2.0);
        imwrite("bil.jpg",dst);

        waitKey();

        return 0;
}

你可能感兴趣的:(bilareralFilter双边滤波函数)