真正搞懂均值模糊、中值模糊、高斯模糊、双边模糊
对于卷积概念和计算不熟悉的建议先看下这篇博客:直观理解深度学习卷积部分
滤波器有高通滤波器(HPF)和低通滤波器(LPF)两种。
- 高通滤波器:根据像素与周围的像素的亮度差值来提升改像素的亮度。
主要作用是锐化。- 低通滤波器:在像素与周围像素的亮度差值小于一个特定值时,平滑改像素的亮度。
主要作用是去噪和模糊化。下面要说的均值模糊、中值模糊、高斯模糊都属于低通滤波器。
一、均值模糊(归一化滤波)
cv2.blur
-
原型:blur(src, ksize, dst=None, anchor=None, borderType=None)
-
作用:对图像进行算术平均值模糊
-
参数:ksize:卷积核的大小。dst,若填入dst,则将图像写入到dst矩阵。borderType:填充方式
假设我们传入的,那么均值模糊的卷积核就是:
如果不理解,看看下面这段代码:
def blur_test():
img = np.array([[1, 1, 1, 1, 1],
[2, 2, 2, 2, 2],
[3, 3, 3, 3, 3],
[4, 4, 4, 4, 4],
[5, 5, 5, 5, 5]], dtype=np.float32)
print(img)
dst = cv2.blur(img, (3, 3), borderType=cv2.BORDER_CONSTANT)
print(dst)
blur_test()
output:
[[1. 1. 1. 1. 1.]
[2. 2. 2. 2. 2.]
[3. 3. 3. 3. 3.]
[4. 4. 4. 4. 4.]
[5. 5. 5. 5. 5.]]
[[0.6666667 1. 1. 1. 0.6666667]
[1.3333334 2. 2. 2. 1.3333334]
[2. 3. 3. 3. 2. ]
[2.6666667 4. 4. 4. 2.6666667]
[2. 3. 3. 3. 2. ]]
一个5*5的矩阵,经过blur的3*3卷积之后,
我们可以看出输出矩阵和原矩阵的shape是相同的(都是5*5);
但卷积操作应该会使矩阵变小的啊,那么blur()之后矩阵却没有变小,
那么一定是对原矩阵进行了填充。(如右图,虚线话的框框就是填充)
这里的填充方式为 borderType=cv2.BORDER_CONSTANT,默认把虚线框里填充上0。
其它的填充方式如下:
填充之后,然后按照卷积核滑动计算的方式得出结果。
二、中值模糊
cv2.medianBlur
-
原型:mediaBlur(src, ksize, dst=None)
-
作用:对图像进行中值模糊
-
参数:ksize:卷积核的大小。dst,若填入dst,则将图像写入到dst矩阵。
翻遍了网上的博客,也没找到有关中值模糊的具体计算。
(其实只是用了类似卷积核的这个矩阵,计算方式就是取中间位置的数)
medianBlur函数,输入的ksize是一个int型整数,不同于blur中的size。它的卷积核是个ksize*ksize的矩阵。
medianBlur中值模糊就是以滑动的方式从原矩阵取出ksize*ksize个数,然后进行排序,结果就是中间位置的数。
它没有填充方式的参数,它的默认填充方式为:(这个很重要)
BORDER_REPLICATE
Python: cv.BORDER_REPLICATE
aaaaaa|abcdefgh|hhhhhhh
首先我们看下面这段代码:
def blur_test():
img = np.array([[1, 1, 3],
[3, 2, 1],
[1, 1, 3]], dtype=np.uint8)
print(img)
dst = cv2.medianBlur(img, 3)
print(dst)
blur_test()
代码的输出结果是
[[1 1 3]
[3 2 1]
[1 1 3]]
[[1 1 2]
[1 1 2]
[1 1 2]]
其实它对原数组进行了下面的填充,然后按照3*3的卷积核把周围的9个数进行排序,取出中间的数作为结果。
[[1 1 3]
[3 2 1]
[1 1 3]]
(1) (1) (1) (3) (3)
(1) 1 1 3 (3)
(3) 3 2 1 (1)
(1) 1 1 3 (3)
(1) (1) (1) (3) (3)
三、高斯模糊
有了前面的知识,我们知道如果要想实现高斯模糊的特点,则需要通过构建对应的权重矩阵来进行滤波。
1、正态分布
正态分布中,越接近中心点,取值越大,越远离中心,取值越小。
计算平均值的时候,我们只需要将"中心点"作为原点,其他点按照其在正态曲线上的位置,分配权重,就可以得到一个加权平均值。正态分布显然是一种可取的权重分配模式。
2、高斯函数
如何反映出正态分布?则需要使用高函数来实现。
上面的正态分布是一维的,而对于图像都是二维的,所以我们需要二维的正态分布。
正态分布的密度函数叫做"高斯函数"(Gaussian function)。它的一维形式是:
其中,μ是x的均值,σ是x的方差。因为计算平均值的时候,中心点就是原点,所以μ等于0。
根据一维高斯函数,可以推导得到二维高斯函数:
有了这个函数 ,就可以计算每个点的权重了。
3、获取权重矩阵
假定中心点的坐标是(0,0),那么距离它最近的8个点的坐标如下:
更远的点以此类推。
为了计算权重矩阵,需要设定σ的值。假定σ=1.5,则模糊半径为1的权重矩阵如下:(把坐标值带入高斯公式)
这9个点的权重总和等于0.4787147,如果只计算这9个点的加权平均,还必须让它们的权重之和等于1,因此上面9个值还要分别除以0.4787147,得到最终的权重矩阵。
除以总值这个过程也叫做“归一问题”。
目的是让滤镜的权重总值等于1。否则的话,使用总值大于1的滤镜会让图像偏亮,小于1的滤镜会让图像偏暗。
这里有个知识点:
- 滤波器的权重总值等于1时,不会改变图像的亮度。
- 权重总值大于1的滤镜会让图像偏亮,小于1的滤镜会让图像偏暗。
- 权重总值等于0时:该滤波器属于边缘检测核,可以把边缘转化为白色,非边缘转化为黑色。
4、计算模糊值
有了权重矩阵,就可以计算高斯模糊的值了。
假设现有9个像素点,灰度值(0-255)如下:
每个点乘以自己的权重值:
得到
将这9个值加起来,就是中心点的高斯模糊的值。
对所有点重复这个过程,就得到了高斯模糊后的图像。对于彩色图片来说,则需要对RGB三个通道分别做高斯模糊。
def Gauss_blur():
img = np.array([[14, 15, 16], [24, 25, 26], [34, 35, 36]], dtype=np.float32)
blur = cv2.GaussianBlur(img, (3, 3), 1.5)
print(blur)
Gauss_blur()
# output:
[[20.771631 21.156027 21.540426]
[24.615604 25. 25.3844 ]
[28.45958 28.843975 29.228374]]