雙邊濾波器

雙邊濾波器的實現

        雙邊濾波器的目的就是保邊去噪。主要是在高斯濾波的基礎上增加對於像素差的考慮,如果像素差過大則利用高斯函數降低影響,只有相近的像素差纔會具有較大的權重,對於中心像素的值有較大影響 。

具體實現函數如下:


其中,權重因子由兩部分組成,一部分是定義域(中心像素與領域像素的距離差):


另一部分是值域(中心像素與領域像素的像素差:)


綜合起來就是:

 即爲第一個式子的權重因子。

具體的實現代碼如下(用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);
			}
		}
	}
}


實驗結果:




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章