基本概念

(2w+1)×(2w+1),高斯分布的标准差为 σ,则高斯核可以表示为矩阵的形式 

python 高斯模糊 PIL 高斯模糊算法原理_浮点数

由于高斯分布的概率密度函数的非零值区间主要集中在 (−3σ,3σ) 内,所以为了保证选取的高斯核的完整性,一般取 w≈3σ。

X,输出图片为 Y,第 i 行第 j 列的数据表示为 X(i,j) 和 Y(i,j),则使用窗口大小为 (2w+1)×(2w+1),标准差为 σ


python 高斯模糊 PIL 高斯模糊算法原理_python 高斯模糊 PIL_02


可分离核形式实现

但是,注意到,高斯核的表达式是可分离的。下面为了表示方便,令 


python 高斯模糊 PIL 高斯模糊算法原理_python 高斯模糊 PIL_03



python 高斯模糊 PIL 高斯模糊算法原理_python 高斯模糊 PIL_04

python 高斯模糊 PIL 高斯模糊算法原理_浮点数_05


Y=⎡⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢6101725334148517101725334148518111826344249529121927354350531013202836445154111421293745525512152230384653561215223038465357⎤⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥


符合局部性原则的内存访问加速

G2 和 X 计算 Z 的过程中,内存访问都是连续的,都是从左到右的形式。但是在利用 G1 和 Z 计算 Y 的过程中,取出每一列中的相邻数据,需要跨行。如果需要处理的图片宽度比较大,跨行访问数据可能会导致 Cache Miss,这是违反了内存访问局部性原则的。为了解决这一问题,利用 G1 和 Z 计算 Y

G1 和 Z 计算 Y 同样可以按行的方式计算。为了表述方便,以计算 Y 的第 2 行(下标从 0 开始)Y(2,⋅)为例, 


Y(2,⋅)=G1(0)Z(0,⋅)+G1(1)Z(1,⋅)+G1(2)Z(2,⋅)+G1(3)Z(3,⋅)+G1(4)Z(4,⋅)

其中 

G1(i)  表示  G1  的第  i  个元素, Z(i,⋅)  表示  Z  的第  i  行。

Y(2,⋅),初始化一个长度为 8 的浮点数行向量 T,令里面的值全等于零,然后用遍历行元素的方式进行如下计算 


TTTTT=T+G1(0)Z(0,⋅)=T+G1(1)Z(1,⋅)=T+G1(2)Z(2,⋅)=T+G1(3)Z(3,⋅)=T+G1(4)Z(4,⋅)



最后将 

T  中的浮点数的值四舍五入赋值给  Y(2,⋅) 。这样就避免了内存访问跨行的问题。注意,为了满足内存访问的局部性,增加了内存使用量,多用了  T 。

Y(0,⋅),初始化一个长度为 8 的浮点数行向量 T,令里面的值全等于零,然后用遍历行元素的方式进行如下计算 


TTTTT=T+G1(0)Z(2,⋅)=T+G1(1)Z(1,⋅)=T+G1(2)Z(0,⋅)=T+G1(3)Z(1,⋅)=T+G1(4)Z(2,⋅)



最后将 

T  中的浮点数的值四舍五入赋值给  Y(0,⋅) 。

扩展与总结

本文中所讲述的高斯模糊的计算方法,可以扩展到任意尺寸可分离核的滤波的实现。

X,hX 行 wX 列,滤波核为 K,(2hK+1) 行 (2wK+1) 列,使用 K 对 X 进行二维滤波的结果是 Y。而直接采用二维循环的原始计算方法,需要进行 (2hK+1)×(2wK+1) 次乘法计算和 (2hK+1)×(2wK+1)−1 次加法计算。计算的时间复杂度是 O(wKhK)

K 是可分离核,可以写成列向量 Kvertical 和行向量 Kvertical 相乘的形式,即 K=Kvertical×Khorizontal。那么在计算滤波结果 Y 的时候,可以先用 Khorizontal 对 X 进行行滤波计算,将计算结果保存到 Z 中,计算 Z 中的每一个数值需要 (2wK+1) 次乘法计算和 2wK 次加法计算。再使用 Kvertical 对 Z 进行列滤波计算,得到最终结果 Y。在 Z 的基础上计算 Y 中的每一个数值需要 (2hK+1) 次乘法计算和 2hK 次加法计算。总的来说,根据 X 计算 Y 中的一个数值,需要进行 (2hK+2wK+2) 次乘法计算和 2hK+2wK 次加法计算。计算的时间复杂度从 O(wKhK)降至 O(wK+hK)。

列滤波的过程还可以考虑内存访问的局部性原则,以提高程序的运行效率。

可分离核的实现方法和列滤波的内存访问加速的实现方法,都需要消耗额外的内存,用空间复杂度的提高换取时间复杂度和效率的改进。