首页 > 代码库 > 快速高斯模糊

快速高斯模糊

高斯模糊是最常见的模糊方式之一,它在机器视觉领域有广泛的应用。

对一个图像做高斯模糊,就是用一个高斯窗和图像中每个点做卷积的计算,根据高斯窗大小的不同,可以得到不同精度的模糊效果。其中的主要运算过程就是卷积的运算,它涉及了大量的乘法和加法。因此,虽然高斯模糊在原理上非常简单,但在实际应用中,它却是图像处理中计算量的大头。因此,想办法去加速高斯模糊算法是非常有意义的。

由于图像是二维的,所以图像的高斯模糊必须也是在2D的维度(x和y)上进行的,假设图像pic有k个点,高斯窗的尺寸是n*n,那么直接对pic进行2D的高斯模糊则需要做k*n*n次乘法。

由于卷积的优异性质,对二维的图像做一次2D的高斯模糊其实是等价于对图像依次做x轴方向的一维高斯模糊和y方向的一维高斯模糊,由于一维的高斯窗的尺寸是n*1和1*n,所以对pic做两次1D的高斯模糊则需要2*k*n次算法。经过这样的改进,把算法是间复杂度从O(n^2)提高到了O(n)。

那么,能不能再提高计算速度,把时间复杂度从O(n)提高到常数级O(C)呢?

现在,我们先把高斯模糊放一边,看一下最为简单的模糊方式——均值模糊(box blur)。

均值模糊的原理与高斯模糊一样,它们的区别在于高斯窗中每个位置的值并不完全相等,而均值窗中的每个位置的值都是1(因为均值模糊的原理就是用一个点和它周围的点的平均值来计算这个点模糊后的值)。

假设均值模糊的窗口大小为n*1,那么第k+1个点要计算乘法结果中,就有n-2个值在第k个点的计算中已经算过了。

如果n=5,那么第k个点要计算(k-2)*1,(k-1)*1,(k-0)*1,(k+1)*1和(k+2)*1这五个值,而第k+1个点需要计算(k-1)*1,(k-0)*1,(k+1)*1,(k+2)*1和(k+3)*1这五个值,其中有三个是重复的,只有两个需要计算。

不考虑边界和初始化,一维均值模糊每个点只需要计算2次,所以它的时间复杂度就是O(C)。

如果高斯模糊也可以像均值模糊一样利用之前的计算结果来计算当前的点就好了,但是高斯窗的值并不完全相等,而是要计算类似(k-2)*1,(k-1)*3,(k-0)*5,(k+1)*3和(k+2)这样的结果,所以直接照搬均值模糊的方法肯定是行不通的。

但是,幸运的是我们可以用多次均值模糊来逼近高斯模糊的结果。这里,我不列举具体的公式,只从定性的角度上分析为什么可以这么做:一维上,均值模糊窗函数的函数图象就是一条直线(覆盖区域就是一个矩形),高斯模糊窗函数的图像是一个正态分布的图像,矩形和矩形的卷积是一个三角形,类推,矩形和三角形的卷式一个类似二次函数的上凸曲线...发现没,卷积的次数越多,图像就越接近正态分布额图像。

技术分享

技术分享

技术分享

均值滤波窗口的具体取值可以看参考文献以及相关的论文。

有相关的数据表明,经过4次参数合适的均值模糊后,模糊的结果和高斯模糊只有4%的误差。在工程实践中,我们一般采用3次均值模糊来逼近高斯模糊。

这样,再加上之前把2D的函数分解为两个1D的函数,对一个k个点的图像pic做高斯模糊一共需要2*2*3*k次乘法计算,时间复杂度缩小到了常数级O(C)。

Tips:

快速高斯模糊的实现不仅需要我们在算法上做优化,更需要我们在写代码的过程中每一步也考虑优化。比如,访问数组时按照数组在内存中的存放方式来访问以提高一级cache命中率,用乘法结合律把两次乘法和一次加法变成一次加法和一次乘法,计算时如果要2的n次方的乘法,尽量装换成位移计算,尽量避免浮点运算,把重复的运算转化成查表等等。总之,需要多思考,才能写出更快的代码。

参考

http://blog.ivank.net/fastest-gaussian-blur.html

https://fgiesen.wordpress.com/2012/07/30/fast-blurs-1/

https://fgiesen.wordpress.com/2012/08/01/fast-blurs-2/

快速高斯模糊