第 11 章

删除影像杂讯

建立平滑影像需认识的名词 · 均值滤波器 · 方框滤波器 · 中值滤波器 · 高斯滤波器 · 双边滤波器 · 2D 滤波核
建立平滑的影像,英文称为 Smoothing Images,这是计算机视觉中最基本的操作之一。一幅影像可能会有一些杂讯或噪音,经过平滑处理后可以降低噪音,或大幅消除杂讯。不过影像经过平滑处理后,伴随而来的可能是影像变得模糊,所以也称为 Blurring Images。影像处理专家有时也将影像平滑处理称为影像滤波,英文是 Images filtering。

在建立平滑影像时,会产生两种效果:一是降低噪音,另一是产生模糊。本章介绍的 OpenCV 平滑函数可以分成线性滤波器和非线性滤波器两类。

11-1

建立平滑影像需认识的名词

11-1-1 滤波核

以某一个像素为中心,这个像素与周围像素可以组成 n 列 m 行的 n x m 矩阵。当 n 等于 m 时,可以称为 n x n 矩阵。这样的矩阵称为滤波核,例如 3 x 35 x 5 滤波核。

滤波核大小参与计算的像素数量说明
3 x 39 个以中心像素和周围 8 个像素进行计算。
5 x 525 个参与计算的邻近像素更多,平滑效果更明显。
7 x 749 个可产生更强的模糊效果,也更容易损失细节。

11-1-2 影像噪音

细看影像时,有时可以发现某个像素与周围像素差异非常大,这个像素就是影像噪音,也称为影像杂讯。例如中心像素值是 20,而周围像素值介于 147155 之间,中心点颜色明显比周围深,就可以判断它是噪音。

11-1-3 删除噪音

当发现影像有噪音时,可以使用平滑处理或降噪处理,将异常像素修正为更接近周围像素的值。处理后,原本突兀的噪点会降低,影像整体看起来比较平滑。

11-1-4 影像降噪处理的方法

影像降噪处理的方法有许多,本章讲解下列常见方法。

11-2

均值滤波器

均值滤波器的英文是 Mean filter,是降低影像噪声的方法。直觉地说,就是删除影像中的杂讯。

11-2-1 理论基础

均值滤波器又称低通滤波器,基本上是将每一个像素当作滤波核的核心,然后计算滤波核内所有像素值的平均,最后让滤波核的核心等于此平均值。

3 x 3 滤波核为例,若中心像素是噪音,可以把 9 个像素相加再除以 9,得到新的中心像素值。若是 5 x 5 滤波核,则将 25 个像素值加总后取平均。

11-2-2 像素位于边界的考量

当像素位于影像边界时,滤波核有一部分会落在影像外。执行影像降噪时,常用的方法有两种:第一种是只取影像范围内实际存在的像素计算;第二种是扩展影像周围的像素,再用扩展后的影像进行计算。

11-2-3 滤波核与卷积

对于 5 x 5 均值滤波器而言,每个像素的权重相同,可以用一个每个元素都是 1 / 25 的矩阵表示权重。这个矩阵又称滤波核,有时候也称卷积核(Convolution kernel)。

原始影像与滤波核相乘的动作称为卷积计算,简称卷积(Convolution)。滤波核的列数和行数通常设为相等,且值越大,结果影像越模糊。

11-2-4 均值滤波器函数

OpenCV 的均值滤波器函数是 blur(),语法如下。

语法
dst = cv2.blur(src, ksize, anchor, borderType)
参数说明
dst返回结果的影像,或称目标影像。
src来源影像,或称原始影像。
ksize滤波核大小,格式是 (高度, 宽度)。由于需要计算核心像素值,建议使用奇数,例如 3 x 35 x 5
anchor可选参数,滤波核的锚点。默认值是 (-1, -1),表示锚点在滤波核中心。
borderType可选参数,边界样式,建议使用默认值。

程式实例 ch11_1.py:均值滤波器函数 blur() 的应用

# ch11_1.py import cv2 src = cv2.imread("hung.jpg") dst1 = cv2.blur(src, (3, 3)) dst2 = cv2.blur(src, (5, 5)) dst3 = cv2.blur(src, (7, 7)) cv2.imshow("src", src) cv2.imshow("dst 3 x 3", dst1) cv2.imshow("dst 5 x 5", dst2) cv2.imshow("dst 7 x 7", dst3) cv2.waitKey(0) cv2.destroyAllWindows()
执行结果
ch11_1.py 3x3、5x5、7x7 均值滤波结果
原始影像与不同大小均值滤波核的结果,参考原书第 11-8 页。

从执行结果可以看到,脸部杂讯变得比较弱。因为原始影像的杂质颗粒较大,使用 3 x 3 滤波核时,得到的是让杂讯变弱的效果;使用 5 x 57 x 7 滤波核时,降噪效果更好,但整体影像也会更模糊。

程式实例 ch11_2.py:使用 29 x 29 滤波核观察执行结果

# ch11_2.py import cv2 src = cv2.imread("hung.jpg") dst = cv2.blur(src, (29, 29)) cv2.imshow("src", src) cv2.imshow("dst 29 x 29", dst) cv2.waitKey(0) cv2.destroyAllWindows()
执行结果
ch11_2.py 29x29 均值滤波结果
使用 29 x 29 滤波核时,影像会产生强烈模糊,参考原书第 11-9 页。
11-3

方框滤波器

OpenCV 有提供方框滤波器,英文是 Box filter。这个方法可以选择是否对均值结果作归一化处理。

11-3-1 理论基础

3 x 3 滤波核为例,如果采用滤波核像素值的加总平均,则卷积计算所得到的结果与均值滤波器相同。若没有执行归一化,则只执行滤波核内像素加总,数值可能超过 255,输出影像会变得很亮,甚至接近白色。

11-3-2 方框滤波器函数

OpenCV 的方框滤波器函数是 boxFilter(),语法如下。

语法
dst = cv2.boxFilter(src, ddepth, ksize, anchor, normalize, borderType)
参数说明
src来源影像。
ddepth输出影像深度,若设为 -1,表示与来源影像相同。
ksize滤波核大小。
anchor锚点,默认 (-1, -1)
normalize是否正规化。默认 True;若为 False,只执行加总。
borderType边界样式,建议使用默认值。

程式实例 ch11_2_1.py:使用方框滤波器处理影像

# ch11_2_1.py import cv2 src = cv2.imread("hung.jpg") dst1 = cv2.boxFilter(src, -1, (2, 2), normalize=False) dst2 = cv2.boxFilter(src, -1, (3, 3), normalize=False) dst3 = cv2.boxFilter(src, -1, (5, 5), normalize=False) cv2.imshow("src", src) cv2.imshow("dst 2 x 2", dst1) cv2.imshow("dst 3 x 3", dst2) cv2.imshow("dst 5 x 5", dst3) cv2.waitKey(0) cv2.destroyAllWindows()
执行结果
ch11_2_1.py boxFilter 结果
未正规化的方框滤波会把邻域值累加,核变大后影像更容易变亮,参考原书第 11-12 页。
11-4

中值滤波器

中值滤波器的英文是 Median filter,也是降低影像噪音非常好的方法。

11-4-1 理论基础

中值滤波器与均值滤波器观念类似,不过这个方法不是计算平均值,而是将要处理的滤波核内所有像素排序,然后取中间值作为中心像素的新值。中值滤波器对椒盐噪声特别有效。

11-4-2 中值滤波器函数

OpenCV 的中值滤波器函数是 medianBlur(),语法如下。

语法
dst = cv2.medianBlur(src, ksize)
参数说明
src来源影像。
ksize滤波核大小,必须是大于 1 的奇数,例如 357

程式实例 ch11_3.py:使用简单的阵列了解中值滤波器的操作

# ch11_3.py import cv2 import numpy as np src = np.ones((3, 3), np.float32) * 150 src[1, 1] = 20 dst = cv2.medianBlur(src, 3) print("src =") print(src) print("dst =") print(dst)
执行结果
中值滤波排序取中间值示意
将滤波核内像素排序后取中间值,参考原书第 11-14 页。

程式实例 ch11_4.py:中值滤波器函数 medianBlur() 的应用

# ch11_4.py import cv2 src = cv2.imread("hung.jpg") dst1 = cv2.medianBlur(src, 3) dst2 = cv2.medianBlur(src, 5) dst3 = cv2.medianBlur(src, 7) cv2.imshow("src", src) cv2.imshow("dst 3 x 3", dst1) cv2.imshow("dst 5 x 5", dst2) cv2.imshow("dst 7 x 7", dst3) cv2.waitKey(0) cv2.destroyAllWindows()
执行结果
ch11_4.py medianBlur 执行结果
中值滤波可以有效消除黑白噪点,参考原书第 11-15 页。

如果将中值滤波器结果与均值滤波器比较,降噪效果更明显,很多噪音可以被删除。不过使用中值滤波器时需要排序处理,因此会需要较多计算时间。

11-5

高斯滤波器

高斯滤波器的英文是 Gaussian filter,也称高斯模糊(Gaussian blur)或高斯平滑。高斯滤波器最重要的观念是:越靠近滤波核核心的像素权重越大,距离越远的像素权重越小。

11-5-1 理论基础

在均值滤波器中,滤波核内每个像素权重相同;高斯滤波器的滤波核不再全部是 1,也就是权重值不再全部相同。滤波核中心附近的像素较重要,外围像素影响较小。

进行高斯滤波器计算时,同样是将原始影像与滤波核执行卷积计算。实际应用中,高斯滤波器的滤波核可以有不同高度与宽度,但是 OpenCV 使用时通常会指定奇数大小。

11-5-2 高斯滤波器函数

OpenCV 提供高斯滤波器函数 GaussianBlur(),语法如下。

语法
dst = cv2.GaussianBlur(src, ksize, sigmaX, sigmaY, borderType)
参数说明
src来源影像。
ksize滤波核大小,宽度和高度应为正奇数。
sigmaXX 方向的标准差,若设为 0,系统会依滤波核大小自动计算。
sigmaYY 方向的标准差,若省略或设为 0,会与 sigmaX 相同。
borderType边界样式,建议使用默认值。

程式实例 ch11_5.py:高斯滤波器函数 GaussianBlur() 的应用

# ch11_5.py import cv2 src = cv2.imread("hung.jpg") dst1 = cv2.GaussianBlur(src, (3, 3), 0) dst2 = cv2.GaussianBlur(src, (5, 5), 0) dst3 = cv2.GaussianBlur(src, (29, 29), 0) cv2.imshow("src", src) cv2.imshow("Gaussian 3 x 3", dst1) cv2.imshow("Gaussian 5 x 5", dst2) cv2.imshow("Gaussian 29 x 29", dst3) cv2.waitKey(0) cv2.destroyAllWindows()
执行结果
ch11_5.py GaussianBlur 执行结果
高斯滤波可以平滑噪声,滤波核越大影像越模糊,参考原书第 11-19 页。
11-6

双边滤波器

前面几节的均值滤波器、方框滤波器、中值滤波器和高斯滤波器,都可以降低噪音,不过也会让影像边界变得模糊。双边滤波器的特色是可以降低噪音,同时尽量保留边缘。

11-6-1 理论基础

双边滤波器在计算某一个像素的新值时,会同时考虑距离与色彩讯息。距离越近的像素权重越大;像素值越接近的像素权重也越大。如果某个邻近像素与中心像素的颜色差异太大,它的权重会被降低,因此边界比较容易被保留下来。

程式实例 ch11_6.py:使用均值滤波器与高斯滤波器处理黑白影像

# ch11_6.py import cv2 src = cv2.imread("border.jpg") dst1 = cv2.blur(src, (3, 3)) dst2 = cv2.blur(src, (7, 7)) dst3 = cv2.GaussianBlur(src, (3, 3), 0) dst4 = cv2.GaussianBlur(src, (7, 7), 0) cv2.imshow("mean 3 x 3", dst1) cv2.imshow("mean 7 x 7", dst2) cv2.imshow("Gauss 3 x 3", dst3) cv2.imshow("Gauss 7 x 7", dst4) cv2.waitKey(0) cv2.destroyAllWindows()
执行结果
ch11_6.py 均值滤波与高斯滤波边缘比较
均值滤波与高斯滤波处理边缘影像的比较,参考原书第 11-21 页。

从边缘影像可以观察到,均值滤波和高斯滤波都会让边界产生模糊。如果相同影像使用双边滤波器,边缘影像可以较好地被保留下来。

11-6-2 双边滤波器函数

OpenCV 提供双边滤波器函数 bilateralFilter(),语法如下。

语法
dst = cv2.bilateralFilter(src, d, sigmaColor, sigmaSpace, borderType)
参数说明
src来源影像。
d滤波时每个像素邻域的直径。
sigmaColor色彩空间的标准差。值越大,色彩差异越大的像素也会互相影响。
sigmaSpace坐标空间的标准差。值越大,距离较远的像素也会互相影响。
borderType边界样式,建议使用默认值。

程式实例 ch11_7.py:相同影像使用均值、高斯和双边滤波器处理

# ch11_7.py import cv2 src = cv2.imread("hung.jpg") dst_blur = cv2.blur(src, (15, 15)) dst_gaussian = cv2.GaussianBlur(src, (15, 15), 0) dst_bilateral = cv2.bilateralFilter(src, 15, 100, 100) cv2.imshow("src", src) cv2.imshow("blur", dst_blur) cv2.imshow("GaussianBlur", dst_gaussian) cv2.imshow("bilateralFilter", dst_bilateral) cv2.waitKey(0) cv2.destroyAllWindows()
执行结果
ch11_7.py bilateralFilter 执行结果
双边滤波可以降低噪音,同时影像边缘较清晰,参考原书第 11-23 页。
11-7

2D 滤波核

除了使用 OpenCV 内建滤波函数,也可以自行定义滤波核,然后使用设定好的滤波核执行降低影像噪音的工作。OpenCV 使用 filter2D() 执行自定义滤波核。

语法
dst = cv2.filter2D(src, ddepth, kernel, anchor, delta, borderType)
参数说明
src来源影像。
ddepth输出影像深度,若设为 -1 表示与来源影像相同。
kernel自定义滤波核。
anchor锚点,默认在滤波核中心。
delta计算结果加上的常数值。
borderType边界样式,建议使用默认值。

程式实例 ch11_8.py:使用自定义滤波核进行滤波处理

# ch11_8.py import cv2 import numpy as np src = cv2.imread("hung.jpg") kernel = np.ones((11, 11), np.float32) / 121 dst = cv2.filter2D(src, -1, kernel) cv2.imshow("src", src) cv2.imshow("filter2D", dst) cv2.waitKey(0) cv2.destroyAllWindows()
执行结果
ch11_8.py filter2D 自定义平均核结果
使用自定义平均核得到的平滑效果,参考原书第 11-25 页。
习题

1. 请扩充 ch11_3.py,观察中值滤波器的操作,建立 5 x 5 的矩阵,从左上到右下对角线建立较大的噪音值,然后观察排序后的中值。

2. 使用中值滤波器和高斯滤波器,以相同大小的 3 x 3 滤波核,对 antar.jpg 执行降噪处理,最后列出原始影像、中值滤波器与高斯滤波器处理结果影像。

3. 重新设计 ch11_6.py,但是将高斯滤波器改为双边滤波器,同时比较 3 x 37 x 7 滤波核、均值滤波器与双边滤波器的执行结果。

第 11 章习题 1 至 3 参考结果
习题 1 至 3 参考图,参考原书第 11-26 页。

4. 使用 unistar.jpg 影像重新设计 ch11_7.py,但是将滤波直径改为 9,同时列出执行结果。

第 11 章习题 4 参考结果
习题 4 参考图,参考原书第 11-27 页。