雙邊濾波器的實現
雙邊濾波器的目的就是保邊去噪。主要是在高斯濾波的基礎上增加對於像素差的考慮,如果像素差過大則利用高斯函數降低影響,只有相近的像素差纔會具有較大的權重,對於中心像素的值有較大影響 。
具體實現函數如下:
其中,權重因子由兩部分組成,一部分是定義域(中心像素與領域像素的距離差):
另一部分是值域(中心像素與領域像素的像素差:)
綜合起來就是:
即爲第一個式子的權重因子。
具體的實現代碼如下(用OpenCV實現):
#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);
}
}
}
}
實驗結果: