图像平滑处理
在尽量保留图像原有信息的情况下,过滤掉图像内部的噪声,这一过程称为对图像的平滑 处理,所得的图像称为平滑图像。
一,均值滤波
均值滤波是指用当前像素点周围 N·N 个像素值的均值来代替当前像素值。使用该方法遍 历处理图像内的每一个像素点,即可完成整幅图像的均值滤波。
(1)基本原理
在进行均值滤波时,首先要考虑需要对周围多少个像素点取平均值。通常情况下,我们会 以当前像素点为中心,对行数和列数相等的一块区域内的所有像素点的像素值求平均。得到的平均值作为该点的值。卷积核的大小可以为3x3,5x5等奇数的正方形。
如果要处理的点不满足卷积核的大小该怎么办呢?比如要处理的点位于左上角,如下图:
遇到这种情况,我们只需要扩充一下外边界,扩充到卷积核可以使用的大小:
我们可以在新添的行列中加入值,这样就可以继续卷积操作了。
上面我们多次提及卷积核的概念,那么卷积核是什么呢?
回顾均值滤波的操作,我们不难发现这个过程本质上就是将原图像的矩阵与一个充满1/n元素的nxn(n一般为奇数)矩阵点乘的结果:
化简得到:
图中右侧就称为卷积核,卷积核也不一定就全是一样的值,后面我们将提及不同的卷积核(方框滤波,高斯滤波等),你可以认为卷积核内的大小意味着权重大小,均值滤波显然是等权重的。
一般形式为:
,M 和 N 分别对应高度和宽度。一般情况下,M 和 N 是相等的,例如比较常用的 3×3、5×5、 7×7 等。如果 M 和 N 的值越大,参与运算的像素点数量就越多,图像失真越严重。
(2)函数语法
dst = cv2.blur( src, ksize, anchor, borderType )
- dst 是返回值,表示进行均值滤波后得到的处理结果。
- src 是需要处理的图像,即原始图像。它可以有任意数量的通道,并能对各个通道独立 处理。图像深度应该是 CV_8U、CV_16U、CV_16S、CV_32F 或者 CV_64F 中的一种。
- ksize 是滤波核的大小。 anchor 是锚点,其默认值是(-1, -1),表示当前计算均值的点位于核的中心点位置。该 值使用默认值即可,在特殊情况下可以指定不同的点作为锚点。
- borderType 是边界样式,该值决定了以何种方式处理边界,其值如表 7-1 所示。一般情 况下不需要考虑该值的取值,直接采用默认值即可。
一般来说,该函数只要管src和ksize就可以了,其他采用默认就行。
(3)程序示例
python
import cv2
import matplotlib.pyplot as plt
#图像平滑处理
img = cv2.imread('lenaNoise.png')
#均值滤波
#简单的平均卷积操作
blur = cv2.blur(img,(3,3))
merge = np.hstack((img,blur))
cv2.imshow("merge",merge)
cv2.waitKey(0)
我们对比发现噪声的确减少了,不过图像也稍微失真了,这是卷积核必然带来的,我们可以调节卷积核大小来减少失真。
二,方框滤波
OpenCV 还提供了方框滤波方式,与均值滤波的不同在于,方框滤波不会计算像素均值。 在均值滤波中,滤波结果的像素值是任意一个点的邻域平均值,等于各邻域像素值之和除以邻 域面积。而在方框滤波中,可以自由选择是否对均值滤波的结果进行归一化,即可以自由选择 滤波结果是邻域像素值之和的平均值,还是邻域像素值之和。
(1)基本原理
方框滤波很好理解,简而言之就是方框滤波可以使用均值滤波的卷积核(归一化),也可以使用求和作用的卷积核(全是1的mxn的卷积核)。
之后的滤波都是通过不停改变卷积核内元素大小的方式来实现不同作用,就不赘述了。
(2)函数语法
dst = cv2.boxFilter( src, ddepth, ksize, anchor, normalize, borderType )
- dst 是返回值,表示进行方框滤波后得到的处理结果。
- src 是需要处理的图像,即原始图像。它能够有任意数量的通道,并能对各个通道独立 处理。图像深度应该是 CV_8U、CV_16U、CV_16S、CV_32F 或者 CV_64F 中的一种。
- ddepth 是处理结果图像的图像深度,一般使用-1 表示与原始图像使用相同的图像深度。
- ksize 是滤波核的大小。滤波核大小是指在滤波处理过程中所选择的邻域图像的高度和 宽度。anchor 是锚点,其默认值是(-1, -1),表示当前计算均值的点位于核的中心点位置。该 值使用默认值即可,在特殊情况下可以指定不同的点作为锚点。
- normalize 表示在滤波时是否进行归一化(这里指将计算结果规范化为当前像素值范围 内的值)处理,该参数是一个逻辑值,可能为真(值为 1)或假(值为 0)。
- 当参数 normalize=1 时,表示要进行归一化处理,要用邻域像素值的和除以面积。
- 当参数 normalize=0 时,表示不需要进行归一化处理,直接使用邻域像素值的和。
实际使用中,ddepth基本不动,只需要管src,ksize,normalize(一般不需要归一化,要不然直接使用均值滤波)。
(3)程序示例
python
import cv2
import matplotlib.pyplot as plt
#图像平滑处理
img = cv2.imread('lenaNoise.png')
#方框滤波
box_1 = cv2.boxFilter(img,-1,(3,3),normalize=False)#一片白,表示当相加结果超过255,当255算
merge = np.hstack((img, box_1))
cv2.imshow("merge", merge)
cv2.waitKey(0)
方框滤波使用的是求和的卷积核,但当大小大于255时会发生截止,所以会是一片白。
三,高斯滤波
(1)基本原理
换汤不换药,上图即为高斯滤波的卷积核,且权重不一样。当然,实际使用时往往需要进行归一化。严格来讲,使用没有进行归一化 处理的卷积核进行滤波,得到的结果往往是错误的。
(2)函数语法
dst = cv2.GaussianBlur( src, ksize, sigmaX, sigmaY, borderType )
- dst 是返回值,表示进行高斯滤波后得到的处理结果。
- src 是需要处理的图像,即原始图像。它能够有任意数量的通道,并能对各个通道独立 处理。图像深度应该是 CV_8U、CV_16U、CV_16S、CV_32F 或者 CV_64F 中的一种。
- ksize 是滤波核的大小。滤波核大小是指在滤波处理过程中其邻域图像的高度和宽度。 需要注意,滤波核的值必须是奇数。
- sigmaX 是卷积核在水平方向上(X 轴方向)的标准差,其控制的是权重比例。例如, 图 7-25 中是不同的 sigmaX 决定的卷积核,它们在水平方向上的标准差不同。
- sigmaY 是卷积核在垂直方向上(Y 轴方向)的标准差。如果将该值设置为 0,则只采用 sigmaX 的值;如果 sigmaX 和 sigmaY 都是 0,则通过 ksize.width 和 ksize.height 计算得 到。 其中:
sigmaX = 0.3×[(ksize.width-1)×0.5-1] + 0.8
sigmaY = 0.3×[(ksize.height-1)×0.5-1] + 0.8
- borderType 是边界样式,该值决定了以何种方式处理边界。一般情况下,不需要考虑该第 7 章 图像平滑处理 159 值,直接采用默认值即可。 在该函数中,sigmaY 和 borderType 是可选参数。sigmaX 是必选参数,但是可以将该参数 设置为 0,让函数自己去计算 sigmaX 的具体值。
(3)程序示例
python
import cv2
import matplotlib.pyplot as plt
#图像平滑处理
img = cv2.imread('lenaNoise.png')
#高斯滤波
gaussian_filter = cv2.GaussianBlur(img,(3,3),0,0)
merge = np.hstack((img, gaussian_filter))
cv2.imshow("merge", merge)
cv2.waitKey(0)
四,中值滤波
中值滤波与前面介绍的滤波方式不同,不再采用加权求均值的方式计算滤波结果。它用邻 域内所有像素值的中间值来替代当前像素点的像素值。
(1)基本原理
中值滤波会取当前像素点及其周围临近像素点(一共有奇数个像素点)的像素值,将这些 像素值排序,然后将位于中间位置的像素值作为当前像素点的像素值。
中值滤波就是将上图九个元素进行排序然后取中值作为目标值,如图,中值排序后得到时93:
(2)函数语法
dst = cv2.medianBlur( src, ksize)
- dst 是返回值,表示进行中值滤波后得到的处理结果。
- src 是需要处理的图像,即源图像。它能够有任意数量的通道,并能对各个通道独立处 理。图像深度应该是 CV_8U、CV_16U、CV_16S、CV_32F 或者 CV_64F 中的一种。
- ksize 是滤波核的大小。滤波核大小是指在滤波处理过程中其邻域图像的高度和宽度。 需要注意,核大小必须是比 1 大的奇数,比如 3、5、7 等。
(3)程序示例
python
import cv2
import matplotlib.pyplot as plt
#图像平滑处理
img = cv2.imread('lenaNoise.png')
#中值滤波
median_filter = cv2.medianBlur(img,5)
merge = np.hstack((img, median_filter))
cv2.imshow("merge", merge)
cv2.waitKey(0)
五,双边滤波
双边滤波是综合考虑空间信息和色彩信息的滤波方式,在滤波过程中能够有效地保护图像 内的边缘信息。
(1)基本原理
前述滤波方式基本都只考虑了空间的权重信息,这种情况计算起来比较方便,但是在边缘 信息的处理上存在较大的问题。
从图 7-31 可以看到,经过高斯滤波处理后,边缘信息变得很模糊,均值滤波处理也会造 成类似的问题。边界模糊是滤波处理过程中对邻域像素取均值所造成的结果,上述滤波处理过 程单纯地考虑空间信息,造成了边界模糊和部分信息的丢失。 双边滤波在计算某一个像素点的新值时,不仅考虑距离信息(距离越远,权重越小),还 考虑色彩信息(色彩差别越大,权重越小)。双边滤波综合考虑距离和色彩的权重结果,既能 够有效地去除噪声,又能够较好地保护边缘信息。
在双边滤波中,当处在边缘时,与当前点色彩相近的像素点(颜色距离很近)会被给予较 大的权重值;而与当前色彩差别较大的像素点(颜色距离很远)会被给予较小的权重值(极端 情况下权重可能为 0,直接忽略该点),这样就保护了边缘信息。
(2)函数语法
dst = cv2.bilateralFilter( src, d, sigmaColor, sigmaSpace, borderType )
- dst 是返回值,表示进行双边滤波后得到的处理结果。
- src 是需要处理的图像,即原始图像。它能够有任意数量的通道,并能对各个通道独立 处理。图像深度应该是 CV_8U、CV_16U、CV_16S、CV_32F 或者 CV_64F 中的一种。
- d 是在滤波时选取的空间距离参数,这里表示以当前像素点为中心点的直径。如果该值 为非正数,则会自动从参数 sigmaSpace 计算得到。如果滤波空间较大(d>5),则速度 较慢。因此,在实时应用中,推荐 d=5。对于较大噪声的离线滤波,可以选择 d=9。
- sigmaColor 是滤波处理时选取的颜色差值范围,该值决定了周围哪些像素点能够参与到 滤波中来。与当前像素点的像素值差值小于 sigmaColor 的像素点,能够参与到当前的 滤波中。该值越大,就说明周围有越多的像素点可以参与到运算中。该值为 0 时,滤波 失去意义;该值为 255 时,指定直径内的所有点都能够参与运算。
- sigmaSpace 是坐标空间中的 sigma 值。它的值越大,说明有越多的点能够参与到滤波计 算中来。当 d>0 时,无论 sigmaSpace 的值如何,d 都指定邻域大小;否则,d 与 sigmaSpace 的值成比例。
- borderType 是边界样式,该值决定了以何种方式处理边界。一般情况下,不需要考虑该 值,直接采用默认值即可。
为了简单起见,可以将两个 sigma(sigmaColor 和 sigmaSpace)值设置为相同的。如果它 们的值比较小(例如小于 10),滤波的效果将不太明显;如果它们的值较大(例如大于 150), 则滤波效果会比较明显,会产生卡通效果。 在函数 cv2.bilateralFilter()中,参数 borderType 是可选参数,其余参数全部为必选参数。
(3) 程序示例
python
import cv2
import matplotlib.pyplot as plt
#图像平滑处理
img = cv2.imread('lenaNoise.png')
#双边滤波
bilateral = cv2.bilateralFilter(img,25,100,100)
merge = np.hstack((img, bilateral))
cv2.imshow("merge", merge)
cv2.waitKey(0)
可以看出效果并不是很理想,这是因为双边滤波主要运用在明显的边界情况下,比如上面的黑白色差比较大的情况。
六,2D卷积
上面我们了解的滤波方式虽然丰富,但是都是规定好的卷积核,总会存在需要特定卷积核的情况,这是我们可以使用2D卷积来自定义我们的卷积核来实现特定要求。
语法如下:
dst = cv2.filter2D( src, ddepth, kernel, anchor, delta, borderType )
- dst 是返回值,表示进行方框滤波后得到的处理结果。
- src 是需要处理的图像,即原始图像。它能够有任意数量的通道,并能对各个通道独立 处理。图像深度应该是 CV_8U、CV_16U、CV_16S、CV_32F 或者 CV_64F 中的一种。
- ddepth 是处理结果图像的图像深度,一般使用-1 表示与原始图像使用相同的图像深度。
- kernel 是卷积核,是一个单通道的数组。如果想在处理彩色图像时,让每个通道使用不 同的核,则必须将彩色图像分解后使用不同的核完成操作。
- anchor 是锚点,其默认值是(-1, -1),表示当前计算均值的点位于核的中心点位置。该 值使用默认值即可,在特殊情况下可以指定不同的点作为锚点。
- delta 是修正值,它是可选项。如果该值存在,会在基础滤波的结果上加上该值作为最 终的滤波处理结果。
- borderType 是边界样式,该值决定了以何种情况处理边界,通常使用默认值即可。 在通常情况下,使用滤波函数 cv2.filter2D()时,对于参数锚点 anchor、修正值 delta、边界 样式 borderType,直接采用其默认值即可。因此,函数 cv2.filter2D()的常用形式为:
dst = cv2.filter2D( src, ddepth, kernel )