双边滤波原理和实现

双边滤波原理

         双边滤波(Bilateral filter)是一种非线性的滤波方法,是结合图像的空间邻近度和像素值相似度的一种折衷处理,同时考虑空域信息和灰度相似性,达到保边去噪的目的。

双边滤波器之所以能够做到在平滑去噪的同时还能够很好的保存边缘(Edge Preserve),是由于其滤波器的核由两个函数生成:

 

 

 

 

 

 一个函数由像素欧式距离决定滤波器模板的系数,另一个函数由像素的灰度差值决定滤波器的系数

 众所周知,高斯滤波器它仅仅是欧式距离进行滤波,这种对于边缘处理不是很好,而双边滤波结合了高斯滤波以及均值滤波的特点。很明显他效果要更好。

上述三个公式就是双边滤波的全部了,我给大家简单介绍一下他的参数含义,就很容易明白他到底是如何实现的了。

首先第一个公式,很明显他是一个指数形式下,某两个点之间的差值的平方,如下图:因为图像的基础单位是像素,所以每一个坐标中就存储着一个值或者三个值(也就是灰度图像和彩色图像的区别)。(k,l)就是模板中心坐标,比如下图(0,0)就是这个3*3模块窗口的中心,(i,j)就是模板窗口其他点,这里的σ是高斯函数的标准差。补充一句,这里面求的是像素差值

(-1,-1)(0,-1)
(0,0)(1,0)
(1,1)

第二个公式是距离模板。同样(k,l)是模板窗口中心点坐标,(i,j)是其他点坐标,这里求得是距离的平方。

第一个公式和第二个公式相乘就得到了我们最重要的权重。

 如上式,G(i,j)就是最终输出图像的每个位置的像素值。

双边滤波器的实现

 我们通常实现双边滤波器,其实只要调用opencv的API就可,里面帮我们封装好了函数。

std::string filePath_1 = "tmp.jpg";
	cv::Mat src0 = cv::imread(filePath_1, 0);
cv::Mat dst;
	bilateralFilter(src0, dst, 5, 50, 50);

实现起来还是蛮简单的,但是既然我们了解了它的原理不妨自己写一下试试:

我这里使用的是c语言写的 ,最后封装静态库。

代码如下: 

void bilateral_filter_ys(unsigned int* src, unsigned int* dst, int ksize, int channels, int cols, int rows, double space_sigma, double color_sigma)
{


	//Assert(channels == 1 || channels == 3);

//这部分是定义灰度值模板系数的。
	double space_coeff = -0.5 / (space_sigma * space_sigma);
	double color_coeff = -0.5 / (color_sigma * color_sigma);
	int radius = ksize / 2;

	

	double color_weight[256 * 3] = { 0 };
	double space_weight[49] = { 0 };

	//本文使用的方法是查表法,首先将距离模板和像素模板写入数组。之后再读取这些数据。这样可以节约时间成本,不需要定义较大的数组
	for (int i = 0; i < channels * 256; i++)//像素模板
	{
		color_weight[i] = exp(i * i * color_coeff);
	}

	int L = 0;

	


		for (int i = -radius; i < radius; i++)//距离模板
		{
			for (int j = -radius; j < radius; j++)
			{
				double distance = -sqrt(i * i + j * j);
				space_weight[L] = exp(distance * distance * space_coeff);
				L++;
			}
		}

//开始滤波,滤波部分
	if (channels == 3) {

		for (int i = radius; i < rows - radius; i++)
		{
			for (int j = radius; j < cols - radius; j++)
			{
				double sumb = 0, sumg = 0, sumr = 0, wsum = 0;
				int b0 = src[(i*cols + j) * 3 + 0];
				int g0 = src[(i*cols + j) * 3 + 1];
				int r0 = src[(i*cols + j) * 3 + 2];

				for (int p = i - radius; p <= i + radius; p++)
				{
					for (int q = j - radius; q <= j + radius; q++)
					{
						int b = src[(p * cols + q) * 3 + 0];
						int g = src[(p * cols + q) * 3 + 1];
						int r = src[(p * cols + q) * 3 + 2];
						double space_w = space_weight[(p + radius - i) * ksize + (q + radius - j)];
						double color_w = color_weight[abs(b - b0) + abs(g - g0) + abs(r - r0)];
						double weight = space_w * color_w;

						sumb += b * weight;
						sumg += g * weight;
						sumr += r * weight;
						wsum += weight;

					}
				}

				
				dst[(i * cols + j) * 3 + 0] = round(sumb/ wsum);//像素要取整
				dst[(i * cols + j) * 3 + 1] = round(sumg / wsum);
				dst[(i * cols + j) * 3 + 2] = round(sumr / wsum);
			}
		}
	}

	




	}

上述是一个三通道双线性滤波,我实现之后和opencv自带的对比。下述是实现过程,第一张代码图主要是转换成Mat类型,所以看着可能有点复杂,但其实只是在转换而已。

第二张是主函数的实现

void S(cv::Mat image_src,cv::Mat image_dst){
	//cv::copyMakeBorder(image_src, image_src, 2, 2, 2, 2, cv::BorderTypes::BORDER_REFLECT);
	int cols = image_src.cols;
	int rows = image_src.rows;
	int channels = image_src.channels();
	if (channels == 3)
	{
		//define matrix space memory
		unsigned int* image_src_rgb = (unsigned int*)malloc(rows * cols * sizeof(unsigned int) * 3);
		unsigned int* image_dst_rgb = (unsigned int*)malloc(rows * cols * sizeof(unsigned int) * 3);


		//value exchange
		for (int i = 0; i < rows; i++)
		{
			for (int j = 0; j < cols; j++)
			{
				image_src_rgb[(i * cols + j) * 3 + 0] = image_src.at<cv::Vec3b>(i, j)[2];
				image_src_rgb[(i * cols + j) * 3 + 1] = image_src.at<cv::Vec3b>(i, j)[1];
				image_src_rgb[(i * cols + j) * 3 + 2] = image_src.at<cv::Vec3b>(i, j)[0];
			}
		}

		//filtering
		bilateral_filter_ys(image_src_rgb, image_dst_rgb, 5, channels, cols, rows, 50, 50);

		//matrix 2 Mat

		for (int i = 0; i < rows; i++)
		{
			for (int j = 0; j < cols; j++)
			{
				image_dst.at<cv::Vec3b>(i, j)[2] = image_dst_rgb[(i * cols + j) * 3 + 0];
				image_dst.at<cv::Vec3b>(i, j)[1] = image_dst_rgb[(i * cols + j) * 3 + 1];
				image_dst.at<cv::Vec3b>(i, j)[0] = image_dst_rgb[(i * cols + j) * 3 + 2];
			}
		}

	}
	if (channels == 1) 
	{

		//define matrix space memory
		unsigned char* image_src_gray = (unsigned char*)malloc(rows * cols * sizeof(unsigned char) );
		unsigned char* image_dst_gray= (unsigned char*)malloc(rows * cols * sizeof(unsigned char));

		
		//value exchange
		for (int i = 0; i < rows; i++)
		{
			for (int j = 0; j < cols; j++)
			{
				image_src_gray[i*cols + j] = image_src.at<uchar>(i,j);
				
			}
		}

		//filtering

		bilateral_filter_ys1(image_src_gray, image_dst_gray, 5, channels, cols, rows, 50, 50);

		//matrix 2 Mat

		for (int i = 0; i < rows; i++)
		{
			for (int j = 0; j < cols; j++)
			{
				image_dst.at<uchar>(i,j) = image_dst_gray[i * cols + j];
			}
		}




	}



}
int main()
{
	
	std::string filePath_1 = "tmp.jpg";
	cv::Mat src0 = cv::imread(filePath_1, 1);
   cv::Mat dst1(src0.size(), src0.type());
	S(src0,dst1);
	
	cv::Mat dst;
	bilateralFilter(src0, dst, 5, 50, 50);
   }

 图像使用的是这张图。

在经过像素值的对比,得到以下结果。这是与opencv双边滤波结果图做差值产生的。这段代码有点长但是很简单。

单通道说实话实现效果要差一些,这里我就不放了代码了,看一下结果吧 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zzzlllqqqaaaa

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值