导向滤波的原理及实现

一、双边滤波

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

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


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

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

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

二、引导滤波

引导滤波是近三年才出现的滤波技术,知道的人还不多。它与双边滤波最大的相似之处,就是同样具有保持边缘特性。在引导滤波的定义中,用到了局部线性模型,至于该模型,可以暂时用下图简单的理解

导向滤波的原理及实现_第1张图片

该模型认为,某函数上一点与其邻近部分的点成线性关系,一个复杂的函数就可以用很多局部的线性函数来表示,当需要求该函数上某一点的值时,只需计算所有包含该点的线性函数的值并做平均即可。这种模型,在表示非解析函数上,非常有用。

同理,我们可以认为图像是一个二维函数,而且没法写出解析表达式,因此我们假设该函数的输出与输入在一个二维窗口内满足线性关系,如下

其中,q是输出像素的值,I是输入图像的值,i和k是像素索引,a和b是当窗口中心位于k时该线性函数的系数。其实,输入图像不一定是待滤波的图像本身,也可以是其他图像即引导图像,这也是为何称为引导滤波的原因。对上式两边取梯度,可以得到


即当输入图像I有梯度时,输出q也有类似的梯度,现在可以解释为什么引导滤波有边缘保持特性了。

下一步是求出线性函数的系数,也就是线性回归,即希望拟合函数的输出值与真实值p之间的差距最小,也就是让下式最小

这里p只能是待滤波图像,并不像I那样可以是其他图像。同时,a之前的系数(以后都写为e)用于防止求得的a过大,也是调节滤波器滤波效果的重要参数。通过最小二乘法,我们可以得到

导向滤波的原理及实现_第2张图片

其中,是I在窗口w_k中的平均值,是I在窗口w_k中的方差,是窗口w_k中像素的数量,是待滤波图像p在窗口w_k中的均值。

在计算每个窗口的线性系数时,我们可以发现一个像素会被多个窗口包含,也就是说,每个像素都由多个线性函数所描述。因此,如之前所说,要具体求某一点的输出值时,只需将所有包含该点的线性函数值平均即可,如下

导向滤波的原理及实现_第3张图片

这里,w_k是所有包含像素i的窗口,k是其中心位置。

当把引导滤波用作边缘保持滤波器时,往往有 I = p ,如果e=0,显然a=1, b=0是E(a,b)为最小值的解,从上式可以看出,这时的滤波器没有任何作用,将输入原封不动的输出。如果e>0,在像素强度变化小的区域(或单色区域),有a近似于(或等于)0,而b近似于(或等于),即做了一个加权均值滤波;而在变化大的区域,a近似于1,b近似于0,对图像的滤波效果很弱,有助于保持边缘。而e的作用就是界定什么是变化大,什么是变化小。在窗口大小不变的情况下,随着e的增大,滤波效果越明显。

在滤波效果上,引导滤波和双边滤波差不多,在一些细节上,引导滤波较好。引导滤波最大的优势在于,可以写出时间复杂度与窗口大小无关的算法(打算在之后的文章中讨论),因此在使用大窗口处理图片时,其效率更高。

三、opencv实现

Mat guidedfilter(Mat &img, Mat &dst, int r, double esp);
int main()
{
	Mat image, dst_image, temp_image,P,Q;
	vectorsrc, dst(3);
	image = imread("D:\\cv_study\\Exercise\\导向滤波\\lena.jpg");
	split(image, src);
	for (int i = 0; i < 3; i++)
	{
		src[i].convertTo(temp_image, CV_64FC1,1.0/255.0);
		P = temp_image.clone();
		dst_image = guidedfilter(temp_image, P, 5, 0.01);
		dst_image.convertTo(dst_image, CV_8UC1, 255.0);
		dst[i] = dst_image;
    }
	merge(dst, Q);

	waitKey();
    return 0;
}
Mat guidedfilter(Mat &img, Mat &dst, int r, double esp)
{
	int row = img.rows;
	int col = img.cols;
	img.convertTo(img, CV_64FC1);
	dst.convertTo(dst, CV_64FC1);
	Mat boxResult,mean_I,mean_P,result,mean_IP, cov_IP, mean_II, var_I;
	Mat a, b, mean_a, mean_b;
	boxFilter(Mat::ones(Size(row,col), img.type()), boxResult, CV_64FC1, Size(r, r));//计算均值N
	boxFilter(img, mean_I, CV_64FC1, Size(r, r));
	mean_I = mean_I / boxResult;//计算导向均值mean_I 
	boxFilter(dst, mean_P, CV_64FC1, Size(r, r));
	mean_P = mean_P / boxResult;//计算原始均值mean_P 
	boxFilter(img.mul(dst), mean_IP, CV_64FC1, Size(r, r));
	mean_IP = mean_IP / boxResult;//计算互相关均值mean_IP 
	cov_IP = mean_IP - mean_I.mul(mean_P);
	boxFilter(img.mul(img), mean_II, CV_64FC1, Size(r, r));
	mean_II = mean_II / boxResult;//计算自相关均值mean_II
	var_I = mean_II - mean_I.mul(mean_I);//计算var_I 
	a = cov_IP / (var_I + esp);//计算相关系数 
	b = mean_P - a.mul(mean_I);
	boxFilter(a, mean_a, CV_64FC1, Size(r, r));
    boxFilter(b, mean_b, CV_64FC1, Size(r, r));
	mean_a = mean_a / boxResult;//计算mean_a 
	mean_b = mean_b / boxResult;//计算mean_b
	result = mean_a.mul(img) + mean_b;
	return result;
}

结果如下:

导向滤波的原理及实现_第4张图片

导向滤波的原理及实现_第5张图片

导向滤波的原理及实现_第6张图片

导向滤波的原理及实现_第7张图片

导向滤波的原理及实现_第8张图片

听说导向滤波还有去雾的作用,感兴趣的大家可以看看。

你可能感兴趣的:(opencv)