Bilateral Filter双边滤波器

转载:http://blog.csdn.net/lemianli/article/details/51899529?locationNum=3&fps=1

http://blog.csdn.net/Coco825211943/article/details/75195799?locationNum=1&fps=1

图像处理--------------双边滤波

 

1.双边滤波

双边滤波很有名,使用广泛,简单的说就是一种同时考虑了像素空间差异与强度差异的滤波器,因此具有保持图像边缘的特性。

先看看我们熟悉的高斯滤波器

其中W是权重,i和j是像素索引,K是归一化常量。公式中可以看出,权重只和像素之间的空间距离有关系,无论图像的内容是什么,都有相同的滤波效果。

再来看看双边滤波器,它只是在原有高斯函数的基础上加了一项,如下

其中 I 是像素的强度值,所以在强度差距大的地方(边缘),权重会减小,滤波效应也就变小。总体而言,在像素强度变换不大的区域,双边滤波有类似于高斯滤波的效果,而在图像边缘等强度梯度较大的地方,可以保持梯度。

 

 

  1. //_src为输入原图像,_dst为双边滤波后的图像,d为滤波内核的大小,即领域的大小  
  2. // sigmaColor为相似度权值公式中的方差,即公式2中的σr  
  3. // sigmaSpace为距离权值公式中的方差,即公式1中的σd  
  4. // borderType表示用什么方式来处理加宽后的图像四周边界  
  5.  

 

原函数:

bilateralFilter( InputArray _src, OutputArray _dst, int d,  
                      double sigmaColor, double sigmaSpace,  
                      int borderType ) 

代码:

 

#include<iostream>  
#include <opencv2/core/core.hpp>  
#include <opencv2/highgui/highgui.hpp>  
#include<opencv2/imgproc/imgproc.hpp>  
#include<opencv.hpp>  
#include<vector>  
#include<opencv2/opencv.hpp>  
  
using namespace cv;  
using namespace std;  
  
//自定义编写的双边滤波器的全局函数声明   
void bilateral_Filter(InputArray src,OutputArray dst,int d,double sigmaColor,double sigmaSpace,  
int borderType );  
  
void _bilateral_Filter(Mat& _src, Mat& _dst, int d, double sigmaColor, double sigmaSpace,  
int borderType);  
  
int main()  
{  
    Mat src = imread("C:\\Users\\l\\Desktop\\2.jpg");  
    Mat dst;  
    Mat test;  
    int d = 15;  
    double sigmaColor=50;  
    double sigmaSpace=150;//在这里实现参数的定义  
  
    //调用自定义的双边滤波函数 使用默认的borderType  
    bilateral_Filter(src,dst,d,sigmaColor,sigmaSpace,BORDER_DEFAULT);  
    bilateralFilter(src, test, d, sigmaColor, sigmaSpace, BORDER_DEFAULT);  
  
    namedWindow("原图");  
    namedWindow("结果图");  
    namedWindow("自带函数结果图");  
  
    imshow("原图",src);  
    imshow("结果图", dst);  
    imshow("自带函数结果图", test);//opencv中自带函数与自定义函数的结果可以进行对比,实验结果一致  
      
    waitKey(0);  
    return 0;  
  
}  
  
//参数意义:d为过滤过程中每个像素领域的直径,如果<0,则根据sigmaSpace来确定  
//src为源图像,dst是结果图像  
//sigmaColor sigmaSpace 分别为正态分布函数中的sigma。一个表示颜色  一个表示空间  
void bilateral_Filter(InputArray src, OutputArray dst, int d, double sigmaColor, double sigmaSpace,  
    int borderType)  
{  
    Mat _src = src.getMat();  
    dst.create(_src.size(), _src.type());  
    Mat _dst = dst.getMat();//得到图像矩阵 对图像矩阵进行操作  
  
    //将图像矩阵传给_bilateral_Filter()  
    _bilateral_Filter(_src, _dst, d, sigmaColor, sigmaSpace,borderType);  
}  
  
void _bilateral_Filter(Mat& _src, Mat& _dst, int d, double sigmaColor, double sigmaSpace,  
    int borderType)  
{  
    //具体实现  
    int channel = _src.channels();//通道数  
    int r;//半径  
    int i, j;//i代表行 j代表列  
  
    r = d / 2;  
    r = MAX(r, 1);//如果不满一个像素则要填满 所以r的最小值是1  
    d = 2 * r + 1;//更改相应的直径  
  
    double sigmaColor_coef = -1 / (sigmaColor*sigmaColor * 2);  
    double sigmaSpace_coef = -1 / (sigmaSpace*sigmaSpace * 2);  
  
    Mat temp;//利用临时图像扩充边缘部分 便于统一处理  
    copyMakeBorder(_src, temp, r, r, r, r, borderType);  
  
    vector<float> color_weight(channel * 256);//记录颜色信息  
    vector<float> space_weight(d*d);//记录空间信息  
    vector<int> space_dis(d*d);//记录模板各点与锚点的偏移量  
    float* _color_weight = &color_weight[0];//初始化指针指向数组的开始,提高效率  
    float* _space_weight = &space_weight[0];  
    int* _space_dis = &space_dis[0];  
  
    //初始化颜色信息  
    for (int begin = 0; begin < channel*256;begin++)  
    {  
        _color_weight[begin] = (float)exp(begin*begin*sigmaColor_coef);  
    }  
  
    //初始化空间信息 和 偏移量  
    int count = 0;  
    for (i = -r; i <= r; i++)  
    {  
        for (j = -r; j <= r; j++)  
        {  
            double distance2 =sqrt((double)i*i + (double)j*j);  
            if (distance2 > r)  
                continue;  
            _space_weight[count] = (float)exp(distance2*distance2*sigmaSpace_coef);  
            //_space_dis[count++] = (int)(i*_src.step + j*channel);  
            _space_dis[count++] = (int)(i*temp.step + j*channel);  
        }  
    }  
  
    Size size = _src.size();  
  
    for (i = 0; i < size.height; i++)//对于每一行来说  
    {//初始化指针指向两个矩阵所对应的像素的位置  
        uchar* sptr = temp.data + (i + r)*temp.step + r*channel;  
        uchar* dptr = _dst.data + i * _dst.step;  
  
        if (channel == 1)//单通道 灰度图  
        {  
            for (j = 0; j < size.width; j++)  
            {  
                float sum = 0;//分子  
                float wsum = 0;//分母 用来归一化  
                int src_pix = sptr[j];  
                {  
                    for (int q = 0; q < count; q++)  
                    {  
                        int src_com_pix = sptr[j + space_dis[q]];  
                        float temp = _space_weight[q] * _color_weight[abs(src_pix - src_com_pix)];  
                        sum += temp*src_com_pix;  
                        wsum += temp;  
                    }  
                    dptr[j] = (uchar)cvRound(sum / wsum);  
                }  
            }  
        }  
        else if (channel == 3)//三通道 BGR  
        {  
            for (j = 0;j<size.width*3;j+=3)  
            {//按照BGR的顺序来  
                float sum_b = 0;  
                float sum_g = 0;  
                float sum_r = 0;  
                float wsum = 0;  
  
                int src_pix_b = sptr[j];  
                int src_pix_g = sptr[j + 1];  
                int src_pix_r = sptr[j + 2];  
                  
                for (int k = 0; k < count; k++)  
                {  
                    const uchar* p = sptr+j + space_dis[k];  
                    int b = p[0];  
                    int g = p[1];  
                    int r = p[2];  
  
                    //计算权值  
                    float temp = _space_weight[k] * _color_weight[abs(src_pix_b - b) + abs(src_pix_g - g) + abs(src_pix_r - r)];  
                    sum_b += temp*b;  
                    sum_g += temp*g;  
                    sum_r += temp*r;  
                    wsum += temp;  
                }  
                dptr[j] = (uchar)cvRound(sum_b / wsum);  
                dptr[j+1] = (uchar)cvRound(sum_g / wsum);  
                dptr[j+2] = (uchar)cvRound(sum_r / wsum);  
            }  
        }  
    }  
}

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值