什么是图像平滑处理
在尽量保留图像原有信息的情况下,过滤掉图像内部的噪声,这一过程我们称之为图像的平滑处理,所得到的图像称为平滑图像。
那么什么是图像的噪声呢?
图像的噪声就是图像中与周围像素点差异较大的像素点。噪声的处理就是将其更改为临近像素点的近似值,使图像更平滑。
图像平滑处理的噪声取值的方式有以下6种:
(1)均值滤波
(2)方框滤波
(3)高斯滤波
(4)中值滤波
(5)双边滤波
(6)2D卷积(自定义滤波)
均值滤波
均值滤波是指用当前像素点周围N*N个像素点的均值来代替当前像素值。使用该方法遍历处理图像内的每一个像素点,即可完成整幅图像的均值滤波。
在进行均值滤波处理时,我们需要考虑对周围多少个像素点取平均值。通常情况下,我们会以当前像素点为中心,对行数和列数相等的一块区域内的所有像素点取平均值。
但是边缘像素点可能不能这样做,毕竟比如左上角的像素点是没有左上像素点的,这个时候我们常常会取图像内存在的周围邻域点的平均值。
在OpenCV中,它给我们提供的均值滤波函数为cv2.blur(),其完整定义如下:
def blur(src, ksize, dst=None, anchor=None, borderType=None):
src:原始图像
kszie:滤波中心的大小,也就是取平均值的周围像素点的高度与宽度,比如(5,5),就是取5*5邻域像素点均值作为结果。
anchor:锚点,其默认值为(-1,1),表示当前计算均值的点位于核的中心点位置。一般使用默认值即可。
borderType:边界样式,该值决定了以何种方式处理边界,一般情况下不需要更改。
了解了该函数的定义,下面我们简单的来完成一个去噪图像,具体代码如下所示:
import cv2
img = cv2.imread("5.jpg")
result_5img = cv2.blur(img, (5, 5))
result_30img= cv2.blur(img, (30, 30))
cv2.imshow("img", img)
cv2.imshow("result_5img", result_5img)
cv2.imshow("result_30img", result_30img)
cv2.waitKey()
cv2.destroyAllWindows()
运行之后,效果如下所示:
从上图可以看出来,使用(5,5)卷积进行均值滤波处理后图像虽然模糊,但还可以辨认。而使用(30,30)卷积进行均值滤波,图像失真非常严重。
所以,我们可以得出来,卷积核越大,去噪效果越好,花费的时间越长,同时图像失真也越严重。而实际的处理中,我们需要在失真与去噪之间取得平衡,选取合适的卷积大小。
方框滤波
方框滤波与均值滤波的不同之处在于,方框滤波不会计算像素均值,它可以自由选择是否对均值滤波的结果进行归一化,即可以自由选择滤波结果是邻域像素值之和的平均值,还是邻域像素值之和。
在OpenCV中,它提供cv2.boxFilter()函数来实现方框滤波,其完整定义如下:
def boxFilter(src, ddepth, ksize, dst=None, anchor=None, normalize=None, borderType=None):
src:原始图像
ddepth:处理结果图像的图像深度,一般使用-1表示与原图像使用相同的图像深度
ksize:滤波核心的大小
normalize:是否在滤波时进行归一化处理。当它为1时,表示要进行归一化处理,也就是邻域像素值的和除以面积,比如(3,3),公式如下:
当它为0时,表示不需要进行归一化处理,直接使用邻域像素值的和。
下面,我们来用程序分别实现归一化与不归一化的效果,代码如下:
import cv2
img = cv2.imread("5.jpg")
result1 = cv2.boxFilter(img, -1, (5, 5))
result2 = cv2.boxFilter(img, -1, (30, 30))
result3 = cv2.boxFilter(img, -1, (2, 2),normalize=0)
cv2.imshow("img", img)
cv2.imshow("result1", result1)
cv2.imshow("result2", result2)
cv2.imshow("result3", result3)
cv2.waitKey()
cv2.destroyAllWindows()
运行之后,显示的效果如下所示:
可以看到,左下角不需要归一化处理,这里只取(2,2),如果你取大了,可以试试。因为范围大了,和一般都会大于255,那么就会造成图像全是白色。
高斯滤波
在进行均值滤波与方框滤波时,其邻域内每个像素的权重是相等的。而高斯滤波会将中心点的权重加大,远离中心点的权重减小,以此来计算邻域内各个像素值不同权重的和。
在OpenCV中,它给我们提供cv2.GaussianBlur()函数进行高斯滤波,其完整定义如下:
def GaussianBlur(src, ksize, sigmaX, dst=None, sigmaY=None, borderType=None):
src:原始图像
ksize:滤波核的大小
sigmaX:卷积和在水平方向上(X轴方向)的标准差,其控制的是权重比例
sigmaY:卷积和在垂直方向上(Y轴方向)的标准差,也是控制的是权重比例。如果它为0,只采用sigmaX的值,如果sigmaX与sigmaY都是0,则通过ksize.width和ksize.height计算得到(可选参数)
下面,我们来使用高斯滤波看看效果,代码如下所示:
import cv2
img = cv2.imread("5.jpg")
result = cv2.GaussianBlur(img, (3, 3), 0, 0)
cv2.imshow("img", img)
cv2.imshow("result", result)
cv2.waitKey()
cv2.destroyAllWindows()
运行之后,效果如下所示:
中值滤波
中值滤波与前面的三种滤波都不同,它不在采用加权求均值的方式计算滤波结果,而是用邻域内所有像素值的中间值来代替当前像素点的像素值。
简单点说,就是取当前像素点及其周围临近像素点的像素值,将这些值进行排序后,取中间位置的像素值作为当前位置的像素值。
在OpenCV中,它提供给我们cv2.medianBlur()函数来进行中值滤波,其完整定义如下:
def medianBlur(src, ksize, dst=None):
src:原始图像
kszie:滤波核的大小
参数就两个,下面我们来用代码测试一下:
import cv2
img = cv2.imread("5.jpg")
result = cv2.medianBlur(img, 3)
cv2.imshow("img", img)
cv2.imshow("result", result)
cv2.waitKey()
cv2.destroyAllWindows()
运行之后,显示效果如下:
可以看到,这里我们将脸上的红点去掉了。需要特别注意的是,滤波核的大小必须是奇数,矩阵中心点向外衍生必然是奇数,不信可以随便矩阵取一点试试。
双边滤波
双边滤波是综合考虑空间信息和色彩信息的滤波方式,在滤波的过程中能够有效地保护图像内的边缘信息。
前面滤波方式基本只考虑了空间的权重信息,这种情况计算起来比较方便,但是边缘信息的处理上存在较大问题。而双边滤波在处理边缘时,与当前点色彩相近的像素点给与较大的权重值,而与当前像素点色彩差别大的会给较小的权重,这样就保护了边缘信息。
简单点概括,双边滤波在计算某一个像素点的新值时,不仅考虑距离信息,还考虑色彩信息。双边滤波即能有效地去除噪声,又能很好地保护边缘信息。
在OpenCV中,它给我们提供cv2.bilateralFilter()函数来实现,其完整定义如下:
def bilateralFilter(src, d, sigmaColor, sigmaSpace, dst=None, borderType=None):
src:原始图像
d:在滤波时选取的空间距离参数,这里表示以当前像素点为中心点的直径。如果该值为非正数,则会从参数sigmaSpace计算得到。如果滤波空间较大,比如d>5,则速度较慢。因此,在实际的应用中,推荐d=5。对于噪声较大的离线滤波,可以选择d=9。
sigmaColor:在滤波处理时,选择的颜色范围,该值决定了周围哪些像素点能够参与到滤波中来。与当前像素点的像素值差值小于sigmaColor的像素点,能够参与到当前的滤波中。该值越大,就说明周围有越多的像素点可以参与到运算中。该值为0时,滤波失去意义;该值为255,指定直径内的所有点都能够参与运算。
sigmaSpace:坐标空间中的sigma值。它的值越大,说明有越多的点能够参与到滤波计算中来。当d>0时,无论sigmaSpace的值如何,d都指定邻域大小;否则,d域sigmaSpace的值成比例。
为了简单起见,博主这里将两个sigmaColor与sigmaSpace值设置为相同的。如果它们的值比较小,比如小于10,滤波的效果不太明显;如果它们的值较大,比如大于150,则滤波效果会比较明显。
代码如下所示:
import cv2
img = cv2.imread("5.jpg")
result = cv2.bilateralFilter(img,25,50,50)
cv2.imshow("img", img)
cv2.imshow("result", result)
cv2.waitKey()
cv2.destroyAllWindows()
运行之后,显示效果如下所示:
2D卷积
在OpenCV中,除了提供上面这些常用的滤波方式之外,还允许用户自定义卷积核实现卷积操作。这个函数是cv2.Filter2D(),其完整定义如下:
def filter2D(src, ddepth, kernel, dst=None, anchor=None, delta=None, borderType=None):
src:原始图像
ddepth:处理结果图像的深度,-1与原图像一致。
kernel:卷积核,是一个单通道数组。如果想在处理彩色图像时,让每个通道使用不同的核,则必须将彩色图像分解后使用不同的核完成。
delta:修正值,可选参数。如果该值存在,会在基础滤波的结果上加上该值作为最终的滤波结果。
下面,我们来使用这个函数看看效果,具体代码如下所示:
import cv2
import numpy as np
img = cv2.imread("5.jpg")
kernel = np.ones((9,9), np.float32) / 81
result = cv2.filter2D(img, -1, kernel)
cv2.imshow("img", img)
cv2.imshow("result", result)
cv2.waitKey()
cv2.destroyAllWindows()
运行之后,效果如下所示: