OpenCV4-python 学习笔记 之 图像平滑处理

  1. 在尽量保留图像原有信息的情况下,过滤掉图像内部的噪声,这一过程称之为对图像的平滑处理。
  2. 图像平滑处理会对图像中与周围像素点像素值差异较大的像素点(即所谓噪声)进行处理,将其值调整为周围像素点像素值的近似值。举例如下,下图中大部分像素值位于[144,149]区间内,只有位于第三行第三列的像素点的值[22]不在此范围中
    OpenCV4-python 学习笔记 之 图像平滑处理_第1张图片
    反应于图像上近似如下效果
    OpenCV4-python 学习笔记 之 图像平滑处理_第2张图片
    除第三行第三列的[22]像素点呈现近似黑色外,其他像素点都是灰度点,故为可以看出这里的[22]像素点可能为噪声,需要将其值调整为周围像素点的近似值,即进行平滑处理。
  3. 对于一整幅图进行平滑处理就能够有效的去除图像内的噪声信息。图像平滑处理的基本原理为将噪声所在的像素点的像素值处理为其周围像素点的近似值,取近似值的方法有很多下面将分别进行介绍

1 均值滤波

均值滤波是指用当前像素点周围N-N个像素值的均值来代替当前的像素值。使用该方法遍历处理图像内的每一个像素点,即可完成整幅图像的均值滤波。

1.1 基本原理

  1. 在进行均值滤波时首先考虑需要对周围多少个像素点取均值(通常情况下取以当前像素点为中心的3x3区域或是5x5区域)

  2. 对于图像边缘的像素点,只取图像内存在的周围邻域点的像素均值
    OpenCV4-python 学习笔记 之 图像平滑处理_第3张图片

  3. 除此之外,还可以扩展当前图像周围的像素带点。例如可以将当前9x7大小的图像扩展为13x11大小的图像,如下图所示
    OpenCV4-python 学习笔记 之 图像平滑处理_第4张图片
    完成图像边缘扩展后,可以在新增的列内填充不同的像素值。在此基础上,再针对9x7的原始图像计算其5x5邻域内像素点的像素值均值。

  4. 在进行均值滤波时,以图中(5, 5)处的像素点为例,其运算过程相当于图像中取均值的的部分与一个内部值均为1/25的5x5矩阵进行相乘运算,从而得到均值滤波的结果为126,其对应关系如下图所示:
    OpenCV4-python 学习笔记 之 图像平滑处理_第5张图片
    根据上述运算对于每一个像素点均有以下运算过程:
    OpenCV4-python 学习笔记 之 图像平滑处理_第6张图片
    将使用的5x5矩阵进行一般化可以得到如图结果
    OpenCV4-python 学习笔记 之 图像平滑处理_第7张图片
    而在OpenCV中上图右侧的矩阵一本被称为卷积核,其一般形式为:
    OpenCV4-python 学习笔记 之 图像平滑处理_第8张图片
    式中,M和N分别对应取值区域的高度和宽度。一般情况下M和N是相等的,例如比较常用的3x3、5x5、7x7等,M和N越大,即参与运算的像素点越多,图像失真越严重

1.2 函数语法

在OpenCV中实现均值滤波的函数为cv2.blur(),其语法格式如下:

dst = cv2.blur(src,
               ksize,
               anchor,
               borderType)

式中:
(1). dst为返回值,进行滤波后得到的结果图像
(2). src为原始图像,它可以有任意数量的通道,并可以对各个通道进行独立处理。图像深度应该是CV_8U, CV_16U, CV_16S, CV_32F或者CV_64F中的一种
关于图像深度抛出两个链接:
图像深度(Image Depth)
opencv关于图像深度转换的一点理解
(3). ksize为滤波核的大小。滤波核的大小是指在在均值处理过程中其邻域图像的高度和宽度
(4). anchor为锚点,其默认值为(-1, -1),表示当前计算均值的点位于核中心点位置(使用默认即可)
(5). borderType是边界样式,改置决定了以何种方式处理边界,其取值如下图所示,不过一般取默认值即可
OpenCV4-python 学习笔记 之 图像平滑处理_第9张图片
由于默认值存在的缘故,函数cv2.blur()的一般形式为

dst = cv2.(src,keize)

1.3 程序示例

读取一幅噪声图像并使用函数cv2.blur()对图像进行均值滤波处理并显示

from cv2 import cv2

img = cv2.imread(r"D:\anaconda\vscode-python\pic\zaosheng.png")
blur = cv2.blur(img,(5,5))

cv2.imshow("img",img)
cv2.imshow("blur",blur)

cv2.waitKey()
cv2.destroyAllWindows()

效果如下

此时的卷积核为5x5,再将其换为30x30又会如何呢?

from cv2 import cv2

img = cv2.imread(r"D:\anaconda\vscode-python\pic\zaosheng.png")
blur_5x5 = cv2.blur(img,(5,5))
blur_30x30 = cv2.blur(img,(30,30))

cv2.imshow("img",img)
cv2.imshow("blurblur_5x5",blur_5x5)
cv2.imshow("blurblur_30x30",blur_30x30)

cv2.waitKey()
cv2.destroyAllWindows()

效果如下

图片异常光滑,简直就和近视摘了眼镜一样
这也说明此时的图像失真相较于5x5的卷积核更为明显
总结一下,卷积核越大,参与到均值运算中的像素值也就越多,去噪效果更明显,花费的计算时间更大,失真更加严重,实际操作中卷积核大小的选择要在失真和去噪效果中取得平衡

2 方框滤波

与均值滤波不同在于,方框滤波不会计算像素均值。在均值滤波中滤波结果的像素值是任意一个点邻域的平均值,而在方框滤波中,可以自由选择是否对均值滤波结果进行归一化,即可以自由选择滤波结果是邻域像素值之和还是其平均值。

2.1 基本原理

以5x5的邻域为例,进行方框滤波时,如果计算的是邻域像素的均值,则滤波关系及相应的卷积核与均值滤波相同,而如果计算的是邻域像素之和,则滤波关系如下图所示
OpenCV4-python 学习笔记 之 图像平滑处理_第10张图片
其卷积核
OpenCV4-python 学习笔记 之 图像平滑处理_第11张图片

2.2 函数语法

在OpenCV中实现方框滤波的函数为cv2.boxFilter(),其语法格式如下:

dst = cv2.boxFilter(src,
					ddepth,
					ksize,
					anchor,
					normalize,
					bordType)

式中:
(1). dst为返回值,进行滤波后得到的结果图像
(2). src为原始图像,它可以有任意数量的通道,并可以对各个通道进行独立处理。图像深度应该是
CV_8U, CV_16U, CV_16S, CV_32F或者CV_64F中的一种
(3). ddepth为处理结果的图像深度,一般用-1表示与原始图像相同的图像深度
(4). ksize为滤波核的大小。滤波核的大小是指在在均值处理过程中其邻域图像的高度和宽度
(5). anchor为锚点,其默认值为(-1, -1),表示当前计算均值的点位于核中心点位置(使用默认即可)
(6). normalize表示在滤波的时候是否进行归一化(此处指将计算结果规范化为当前像素范围内的值)处理,该参数为一逻辑值且默认为1。
当其为1时,表示要进行归一化处理,即要用邻域像素除以面积
当其为0时,表示不需要进行归一化处理,直接使用邻域像素值的和
特别的,针对5x5邻域,当normalize=1时函数cv2.bosFilter()的作用与函数cv2.blur()的作用是一样的
(7). borderType是边界样式,改置决定了以何种方式处理边界,其取值一般取默认值即可

由于默认值(anchor、noamalize和borderType)的存在,函数cv2.boxFilter()的格式一般为

dst = cv2.boxFilter(src, ddepth, ksize)

2.3 程序示例

针对噪声图片进行方框滤波并显示

from cv2 import cv2

img = cv2.imread(r"D:\anaconda\vscode-python\pic\zaosheng.png")
bf = cv2.boxFilter(img,-1,(5,5))

cv2.imshow("img",img)
cv2.imshow("bf",bf)

cv2.waitKey()
cv2.destroyAllWindows()

由于使用了特定的参数,其最终效果与cv2.blur()函数的效果完全一致

下面将默认为1的normalize的值置为0

from cv2 import cv2

img = cv2.imread(r"D:\anaconda\vscode-python\pic\zaosheng.png")
bf = cv2.boxFilter(img,-1,(5,5),normalize=0)

cv2.imshow("img",img)
cv2.imshow("bf",bf)

cv2.waitKey()
cv2.destroyAllWindows()

至于效果嘛…

当然是白板啦hhh,毕竟5x5=25个格子都加在一起了,满了(255)不就白了吗
下面把卷积核变小点(2x2)

from cv2 import cv2

img = cv2.imread(r"D:\anaconda\vscode-python\pic\zaosheng.png")
bf = cv2.boxFilter(img,-1,(2,2),normalize=0)

cv2.imshow("img",img)
cv2.imshow("bf",bf)

cv2.waitKey()
cv2.destroyAllWindows()


好歹有个轮廓了不是(不一定>255),对于方框滤波,归纳下就是其对噪声不是那么敏感,能够较好的消除椒盐噪声,但是容易导致图像的不连续性

3 高斯滤波

在进行均值滤波和方框滤波时,其邻域内每个像素点的权重是相等的。在高斯滤波中会将中心点的权重加大,远离中心点的权重值减小,再此基础上计算邻域内各个像素值不同权重的和

3.1 基本原理

在高斯滤波时,卷积核的值不再都是1,一个3x3的卷积核可能如下图所示
OpenCV4-python 学习笔记 之 图像平滑处理_第12张图片
对于前文中的图想(灰度数值)(4, 3)位置上像素值为226的像素点进行高斯卷积,其运算规则如下
OpenCV4-python 学习笔记 之 图像平滑处理_第13张图片
在实际计算时使用的卷积核如下图
OpenCV4-python 学习笔记 之 图像平滑处理_第14张图片
在实际使用中,高斯滤波的卷积核可能有多种不同大小都保比如3x3、5x5、7x7等
OpenCV4-python 学习笔记 之 图像平滑处理_第15张图片
在高斯滤波中,核的宽度和高度可以不相同,但是它们必须均为奇数

每一种尺寸的卷积核都可以有多种不同形式的权重比例,如下图这两种不同的权重比
OpenCV4-python 学习笔记 之 图像平滑处理_第16张图片
在实际计算中,卷积核是归一化处理的,这种处理既可以表示为小数形式的卷积核也可以表示为分数形式的卷积核(如前文),严格意义上,使用没有经历归一化处理的卷积核进行滤波,其结果往往是错误的

3.2 函数语法

在OpenCV中实现高斯滤波的函数为cv2.GaussianBlur(),其语法格式如下:

dst = cv2.GaussianBlur(src,
					   ksize,
					   sigmaX,
					   sigmaY,
					   bordType)

式中:
(1). dst为返回值,进行滤波后得到的结果图像
(2). src为原始图像,它可以有任意数量的通道,并可以对各个通道进行独立处理。图像深度应该是
CV_8U, CV_16U, CV_16S, CV_32F或者CV_64F中的一种
(3). ksize为滤波核的大小。滤波核的大小是指在在均值处理过程中其邻域图像的高度和宽度
(4). sigmaX为卷积核在水平方向(X轴方向)上的标准差,其控制的是权重比例
(5). sigmaY为卷积核在水平方向(Y轴方向)上的标准差,如果将该值设置为0,则只能采用sigmaX的值,如果sigmaX和sigmaY均为0,则其值则通过ksize.wigth和ksize.height计算得到,其中
sigmaX = 0.3 x [(ksize.width - 1) x 0.5 - 1] +0.8
sigmaY = 0.3 x [(ksize.height - 1) x 0.5 - 1] +0.8
(6). borderType是边界样式,改置决定了以何种方式处理边界,其取值一般取默认值即可

由于默认值的存在(但此处最好显式的指出ksize、sigmaX和sigmaY的默认值0),函数cv2.GaussianBlur()的格式一般为

dst = cv2.GaussianBlur(src, ksize, 0, 0)

3.3 程序示例

照旧展示一下效果

from cv2 import cv2

img = cv2.imread(r"D:\anaconda\vscode-python\pic\zaosheng.png")
gs = cv2.GaussianBlur(img,(5,5),0,0)

cv2.imshow("img",img)
cv2.imshow("gs",gs)

cv2.waitKey()
cv2.destroyAllWindows()

效果:

看上去不太明显,再换个图像
OpenCV4-python 学习笔记 之 图像平滑处理_第17张图片
与均值滤波及方框滤波相比,高斯滤波在对图像进行平滑的同时,同时能够更多的保留图像的总体灰度分布特征

4 中值滤波

中值滤波和前面介绍的滤波方式不同,不再采用加权求均值的方式计算滤波结果,其采用邻域内所有像素值的中间值来代替当前像素点的像素值

4.1 基本原理

中值滤波会取当前的像素点及其周围临近的像素点(一共奇数个像素点)的像素值,将这些像素值排序然后将位于中间位置的像素值作为当前像素点的像素值,例如下图,如果想要计算(4, 4)位置的像素点中值滤波后的的像素值
OpenCV4-python 学习笔记 之 图像平滑处理_第18张图片
可以将其邻域设置为3x3大小,并将邻域内的所有值进行升序排序如下
[66, 78, 90, 91, 93, 94, 95, 97, 101] 可见该序列中位于中心点处的值为[93],因此用该像素值代替原来的[78]如下图所示
OpenCV4-python 学习笔记 之 图像平滑处理_第19张图片

4.2 函数语法

在OpenCV中实现中值滤波的函数是cv2.medianBlur(),其语法格式如下:

dst = cv2.medianBlur(src,
					 ksize)

式中:
(1). dst为返回值,进行滤波后得到的结果图像
(2). src为原始图像,它可以有任意数量的通道,并可以对各个通道进行独立处理。图像深度应该是
CV_8U, CV_16U, CV_16S, CV_32F或者CV_64F中的一种
(3). ksize为滤波核的大小。滤波核的大小是指在在均值处理过程中其邻域图像的高度和宽度

4.3 程序示例

from cv2 import cv2

img = cv2.imread(r"D:\anaconda\vscode-python\pic\k.jpg")
md = cv2.medianBlur(img,3)

cv2.imshow("img",img)
cv2.imshow("md",md)

cv2.waitKey()
cv2.destroyAllWindows()

效果
OpenCV4-python 学习笔记 之 图像平滑处理_第20张图片
从图中可以看到,由于没有进行均值处理,中值滤波不存在均值滤波等滤波方法带来的细节模糊等问题。在中值滤波的处理中,噪声成分很难被选上,所以可以在几乎不影响原有图像的基础上将噪声全部去掉,但是由于需要排序等操作,故而中值滤波需要的计算量较大

5 双边滤波

双边滤波是综合考虑空间信息和色彩信息的滤波方式,在滤波过程中能够有效的保护图像内的边缘信息。

5.1 基本原理

前述滤波方式基本都只考虑了空间上的权重信息,这种情况计算起来比较方便,但是在边缘信息的处理上存在较大的问题。如下图
OpenCV4-python 学习笔记 之 图像平滑处理_第21张图片
左侧黑色,右侧白色,中间有着明显的边缘,而在均值滤波、方框滤波、高斯滤波中,都会计算边缘上个各个像素点的加权平均值,从而模糊边缘信息,比如下面的高斯滤波
OpenCV4-python 学习笔记 之 图像平滑处理_第22张图片
边界模糊问题是滤波过程中对邻域像素值取均值造成的,是只考虑了空间信息而忽略了色彩信息的结果,会造成一定程度上的信息丢失

双边滤波在计算某一像素点的新值的时候不仅考虑距离信息(距离越远,权重越小),还考虑了色彩信息(色差越大,权重越小)。由于综合考虑了距离和色彩的权重,故而既能有效的去除噪声又能够很好的保护边缘信息

5.2 函数语法

在OpenCV中实现双边滤波的函数为cv2.bilateralFilter(),其语法格式如下:

dst = cv2.bilateralFilter(src,
					      d,
					      sigmaColor,
					      sigmaSpace,
					      bordType)

式中:
(1). dst为返回值,进行滤波后得到的结果图像
(2). src为原始图像,它可以有任意数量的通道,并可以对各个通道进行独立处理。图像深度应该是
CV_8U, CV_16U, CV_16S, CV_32F或者CV_64F中的一种
(3). d是在滤波时选取的空间的距离参数,这里表示以当前像素点为中心的直径。如果该值为非正数,则会自动从参数sigmaSpace计算得到。如果滤波空间较大(d>5),则速度将会变慢。因此,在实时应用中推荐d=5。对于较大噪声的离线滤波可以选择d=9
(4). sigmaColor为滤波处理时选取的颜色差值的范围,该值决定了周围哪些像素点能够参与到滤波中来。与当前像素点的像素值差值小于sigmaColor的像素点能够参与到当前的滤波中来。该值越大,说明周围有越多的像素点参与到运算中。当该值为0时滤波失去意义,为255时指定范围内的所有点都能参与进来
(5). sigmaSpace为坐标空间中的sigma值。它的值越大,说明有越多的点参与到滤波计算中来。当d>0时,无论sigmaSpace值如何,都为指定邻域大小,否则d与SigmaSpace的值成比例
(5). borderType是边界样式,改置决定了以何种方式处理边界,其取值一般取默认值即可

为了简单起见,可以将两个sigma(sigmaColor、sigmaSpace)的值设置为相同的。如果它们的值比较小(比如<10),滤波的效果将不太明显,如果其值较大(例如>150),则滤波的效果将比较明显,但将会产生卡通效果。另外,在函数cv2,bilateFilter()中,除了borderType为可选参数外,其余均为必选参数

关于这里的卡通效果: 计算机视觉 OpenCV (26) 卡通效果

5.3 程序示例

from cv2 import cv2

img = cv2.imread(r"D:\anaconda\vscode-python\pic\border.png")
bf = cv2.bilateralFilter(img,25,100,100)

cv2.imshow("img",img)
cv2.imshow("bf",bf)

cv2.waitKey()
cv2.destroyAllWindows()

先看看其对边缘信息的处理如何吧
OpenCV4-python 学习笔记 之 图像平滑处理_第23张图片
可以明显看到,前面的高斯滤波相比,中值滤波很好的保留的边缘信息
那么滤波效果呢?

可以看到,虽然处理结果很明显,但是实际效果(去除噪声)并不是很好,故而双边滤波的优势主要体现在对边缘信息的处理上

6 2D卷积

6.1 介绍

OpenCV提供了多种滤波方式来实现图像平滑的效果,例如上面几种方式。大多数滤波方式使用的卷积核都具有一定的灵活性,能够方便的设置卷积核的大小及数值。但是有时我们希望使用特定的卷积核实现卷积操作,而前面介绍到的滤波函数都无法将卷积核确定为我们想要的形式,这时就需要使用到OpenCV的自定义卷积函数了

6.2 函数语法

在OpenCV中,允许用户自定义卷积核实现卷积操作的函数是cv2.filter2D(),其语法格式如下:

dst = cv2.filter2D(src,
				   ddepth,
				   kernel,
				   anchor,
				   delta,
				   borderType)

式中:
(1). dst为返回值,进行滤波后得到的结果图像
(2). src为原始图像,它可以有任意数量的通道,并可以对各个通道进行独立处理。图像深度应该是 CV_8U, CV_16U, CV_16S, CV_32F或者CV_64F中的一种
(3). ddepth为处理结果的图像深度,一般用-1表示与原始图像相同的图像深度
(4). kernel为卷积核,是一个单通道的数组。如果想在处理彩色图像时让每个通道使用不同的核,则必须将彩色图像分解后再使用不同的核完成操作
(5). anchor为锚点,其默认值为(-1, -1),表示当前计算均值的点位于核中心点位置(使用默认即可)
(6). delta为修正值,它是可选项。如果该值存在,则会在基础滤波的结果上加上该值作为最终的滤波处理结果
(7). borderType是边界样式,改置决定了以何种方式处理边界,其取值一般取默认值即可

在通常情况下,使用滤波函数cv2.filter2D()时对于锚点anchor、修正值delta、边界样式borderType直接采用默认值即可。因此函数cv2.filter2D()的一般形式为

dst = cv2.filter2D(src, ddepth, kernel)

6.3 程序示例

使用如下卷积核对图像进行滤波
OpenCV4-python 学习笔记 之 图像平滑处理_第24张图片
易知使用numpy库中的ones()函数即可创建该卷积核
完整程序如下:

from cv2 import cv2 
import numpy as np

img = cv2.imread(r"D:\anaconda\vscode-python\pic\zaosheng.png")
kernel = np.ones((9,9),np.float32)/81
r = cv2.filter2D(img,-1,kernel)

cv2.imshow("img",img)
cv2.imshow("r",r)

cv2.waitKey()
cv2.destroyAllWindows()

由卷积核可知,此程序会将原始图像以9x9邻域进行均值滤波
效果如下:

虽然此处效果与直接使用均值滤波语句

r = cv2.blur(img,(9,9))

是一样的,但在实际应用中可以定义更加复杂的卷积核来进行自定义滤波操作

你可能感兴趣的:(openCV-python)