转载: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 是像素的强度值,所以在强度差距大的地方(边缘),权重会减小,滤波效应也就变小。总体而言,在像素强度变换不大的区域,双边滤波有类似于高斯滤波的效果,而在图像边缘等强度梯度较大的地方,可以保持梯度。
- //_src为输入原图像,_dst为双边滤波后的图像,d为滤波内核的大小,即领域的大小
- // sigmaColor为相似度权值公式中的方差,即公式2中的σr
- // sigmaSpace为距离权值公式中的方差,即公式1中的σd
- // borderType表示用什么方式来处理加宽后的图像四周边界
原函数:
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);
}
}
}
}