OpenCV-Python快速入门(六):图像平滑
前言
- 本文是个人快速入门OpenCV-Python的电子笔记,由于水平有限,难免出现错漏,敬请批评改正。
- 更多精彩内容,可点击进入
OpenCV-Python快速入门专栏或我的个人主页查看
前提条件
- 熟悉Python
实验环境
- Python 3.x (面向对象的高级语言)
- OpenCV 4.0(python第三方库)
pip3 install opencv-python
图像平滑
- 图像平滑:在保留图像原有信息的情况下,过滤掉图像内部的噪声。
- 图像平滑会对图像中与周围像素点的像素值差异较大的像素点进行处理,将其值调整为周围像素点像素值的近似值。
- 例如,一张3×3图像img: [ 120 120 120 120 20 120 120 120 120 ] \left[ \begin{matrix} 120 & 120 & 120\\ 120 & 20 & 120 \\ 120 & 120 & 120 \end{matrix} \right] ⎣ ⎡12012012012020120120120120⎦ ⎤
图像中间的像素,明显与周围的像素差异较大,有可能是噪声,将该像素点调整为
周围像素值的近似值,平滑后,该点的像素值由 20 变为 120: [ 120 120 120 120 120 120 120 120 120 ] \left[ \begin{matrix} 120 & 120 & 120\\ 120 & 120 & 120 \\ 120 & 120 & 120 \end{matrix} \right] ⎣ ⎡120120120120120120120120120⎦ ⎤
可以看到,在[1,1]像素点进行平滑后,图像内所有像素趋于一致。- 图像平滑的基本原理是,将噪声所在像素点的像素值处理为其周围临近像素点的值的
近似值。- 常用的取近似值的方法有均值滤波、方框滤波、高斯滤波、中值滤波、双边滤波、2D 卷积(自定义滤波)。
- 图像平滑处理通常伴随图像模糊操作,因此图像平滑处理有时也被称为图像模糊处理
- 图像滤波允许在图像上进行各种各样的操作,因此有时我们也会把图像平滑处理称为图像滤波
均值滤波(cv2.blur())
- 均值滤波是指用当前像素点周围 N×N 个像素值的均值来代替当前像素值。使用该方法遍 历处理图像内的每一个像素点,即可完成整幅图像的均值滤波。
- 例如,一张3×3图像img: [ 120 120 120 120 21 120 120 120 120 ] \left[ \begin{matrix} 120 & 120 & 120\\ 120 & 21 & 120 \\ 120 & 120 & 120 \end{matrix} \right] ⎣ ⎡12012012012021120120120120⎦ ⎤
对[1,1]做均值滤波,img[1,1]=(120+120+120)+(120+21+120)+(120+120+120)/(3×3)=109: [ x x x x 109 x x x x ] \left[ \begin{matrix} x & x & x \\ x & 109 & x \\ x & x & x \end{matrix} \right] ⎣ ⎡xxxx109xxxx⎦ ⎤
这里只针对img[1,1]像素点进行计算示例,其余未计算的,这里为了矩阵美观,用x占位,有兴趣的,可以自行计算其余x的值分别为多少。
- 其中, K = 1 9 [ 1 1 1 1 1 1 1 1 1 ] K=\frac{1}{9}\left[ \begin{matrix} 1 & 1 & 1\\ 1 & 1 & 1 \\ 1 & 1 & 1 \end{matrix} \right] K=91⎣ ⎡111111111⎦ ⎤称为滤波器,也称为卷积核
import cv2
img1=cv2.imread("1.jpg")
img1_resize=cv2.resize(img1,(400,400))
# dst = cv2.blur(img, ksize, anchor, borderType )
dst = cv2.blur(img1_resize,(5,5))
cv2.imshow("origin",img1_resize)
cv2.imshow("mean_blur",dst)
cv2.waitKey()
cv2.destroyAllWindows()
方框滤波(cv2.boxFilter())
- 方框滤波不会计算像素均值。
- 在方框滤波中,可以自由选择是否对均值滤波的结果进行归一化,即可以自由选择
滤波结果是邻域像素值之和的平均值,还是邻域像素值之和。- 也就是说,可选择如下图所示的两种输出结果的其中之一。
import cv2
img1=cv2.imread("1.jpg")
img1_resize=cv2.resize(img1,(400,400))
# dst = cv2.boxFilter( img, ddepth, ksize, anchor, normalize, borderType )
# ddepth=-1 表示与原始图像使用相同的图像深度
dst = cv2.boxFilter(img1_resize,-1,(5,5))
cv2.imshow("origin",img1_resize)
cv2.imshow("cv2.boxFilter",dst)
cv2.waitKey()
cv2.destroyAllWindows()
高斯滤波(cv2.GaussianBlur())
- 在高斯滤波中,会将中心点的权重值加大,远离中心点的权重值减小,在此基础上计算邻域内各个像素值不同权重的和。
- 高斯滤波,计算如下图所示。
计算方式为 ( 120 ∗ 0.05 + 120 ∗ 0.1 + 120 ∗ 0.5 ) + ( 120 ∗ 0.1 + 25 ∗ 0.4 + 120 ∗ 0.1 ) ( 120 ∗ 0.05 + 120 ∗ 0.1 + 120 ∗ 0.5 ) = 70 (120*0.05+120*0.1+120*0.5)+(120*0.1+25*0.4+120*0.1)(120*0.05+120*0.1+120*0.5)=70 (120∗0.05+120∗0.1+120∗0.5)+(120∗0.1+25∗0.4+120∗0.1)(120∗0.05+120∗0.1+120∗0.5)=70- 注意:实际使用时往往需要进行归一化。严格来讲,使用没有进行归一化处理的卷积核进行滤波,得到的结果往往是错误的。
import cv2
img1=cv2.imread("1.jpg")
img1_resize=cv2.resize(img1,(400,400))
# dst = cv2.GaussianBlur( img, ksize, sigmaX, sigmaY, borderType )
dst = cv2.GaussianBlur(img1_resize,(5,5),0,0)
cv2.imshow("origin",img1_resize)
cv2.imshow("cv2.GaussianBlur",dst)
cv2.waitKey()
cv2.destroyAllWindows()
中值滤波(cv2.medianBlur())
- 中值滤波会取当前像素点及其周围临近像素点(一共有奇数个像素点)的像素值,将这些像素值排序,然后将位于中间位置的像素值作为当前像素点的像素值。
import cv2
img1=cv2.imread("1.jpg")
img1_resize=cv2.resize(img1,(400,400))
# dst = cv2.medianBlur(img, ksize)
dst = cv2.medianBlur(img1_resize,3)
cv2.imshow("origin",img1_resize)
cv2.imshow("cv2.medianBlur",dst)
cv2.waitKey()
cv2.destroyAllWindows()
双边滤波(cv2.bilateralFilter())
- 双边滤波是综合考虑空间信息和色彩信息的滤波方式,在滤波过程中能够有效地保护图像内的边缘信息。
- 经过高斯滤波处理后,边缘信息变得很模糊,均值滤波处理也会造成类似的问题。边界模糊是滤波处理过程中对邻域像素取均值所造成的结果,上述滤波处理过程单纯地考虑空间信息,造成了边界模糊和部分信息的丢失。
- 双边滤波在计算某一个像素点的新值时,不仅考虑距离信息(距离越远,权重越小),还考虑色彩信息(色彩差别越大,权重越小)。双边滤波综合考虑距离和色彩的权重结果,既能够有效地去除噪声,又能够较好地保护边缘信息。
- 在双边滤波中,当处在边缘时,与当前点色彩相近的像素点(颜色距离很近)会被给予较大的权重值;而与当前色彩差别较大的像素点(颜色距离很远)会被给予较小的权重值(极端情况下权重可能为 0,直接忽略该点),这样就保护了边缘信息。
import cv2
img1=cv2.imread("1.jpg")
img1_resize=cv2.resize(img1,(400,400))
'''
(1)d 是在滤波时选取的空间距离参数,这里表示以当前像素点为中心点的直径。
如果该值为非正数,则会自动从参数 sigmaSpace 计算得到。
如果滤波空间较大(d>5),则速度较慢。
因此,在实时应用中,推荐 d=5。对于较大噪声的离线滤波,可以选择 d=9。
(2)sigmaColor 是滤波处理时选取的颜色差值范围,该值决定了周围哪些像素点能够参与到滤波中来。
与当前像素点的像素值差值小于 sigmaColor 的像素点,能够参与到当前的滤波中。
该值越大,就说明周围有越多的像素点可以参与到运算中。
该值为 0 时,滤波失去意义;
该值为 255 时,指定直径内的所有点都能够参与运算。
(3)sigmaSpace 是坐标空间中的 sigma 值。
它的值越大,说明有越多的点能够参与到滤波计算中来。
当 d>0 时,无论 sigmaSpace 的值如何,d 都指定邻域大小;
否则,d 与 sigmaSpace的值成比例。
'''
# dst = cv2.bilateralFilter( img, d, sigmaColor, sigmaSpace, borderType )
dst = cv2.bilateralFilter(img1_resize,25,100,100)
cv2.imshow("origin",img1_resize)
cv2.imshow("cv2.bilateralFilter",dst)
cv2.waitKey()
cv2.destroyAllWindows()
卷积运算(cv2.filter2D())
- 上述的均值滤波、方框滤波、高斯滤波、中值滤波等。大多数滤波方式所使用的卷积核都具有一定的灵活性,能够方便地设置卷积核的大小和数值。但是,我们有时希望使用特定的卷积核实现卷积操作,就需要用到cv2.filter2D()函数,其允许用户自定义卷积核实现卷积操作。
- 例如,我们用cv2.filter2D()函数,实现如下图所示卷积运算。
import cv2
import numpy as np
img = np.ones((3,3),np.float32)
img[0:3,0:3]=120
img[1,1]=21
print('img=\n',img)
kernel = np.ones((3,3),np.float32)/9
print('kernel=\n',kernel)
# dst = cv2.filter2D( img, ddepth, kernel, anchor, delta, borderType )
dst = cv2.filter2D(img,-1,kernel)
print('dst=\n',dst)
import cv2
import numpy as np
img1=cv2.imread("1.jpg")
img1_resize=cv2.resize(img1,(400,400))
kernel = np.ones((3,3),np.float32)/9
# dst = cv2.filter2D( img, ddepth, kernel, anchor, delta, borderType )
dst = cv2.filter2D(img1_resize,-1,kernel)
cv2.imshow("origin",img1_resize)
cv2.imshow("cv2.filter2D",dst)
cv2.waitKey()
cv2.destroyAllWindows()
参考文献
[1] https://opencv.org/
[2] 李立宗. OpenCV轻松入门:面向Python. 北京: 电子工业出版社,2019
- 更多精彩内容,可点击进入
OpenCV-Python快速入门专栏或我的个人主页查看