图像预处理-图像噪点消除
一.基本介绍
噪声:指图像中的一些干扰因素,也可以理解为有那么一些点的像素值与周围的像素值格格不入。常见的噪声类型包括高斯噪声和椒盐噪声。
滤波器:也可以叫做卷积核
- 低通滤波器是模糊,高通滤波器是锐化
- 低通滤波器就是允许低频信号通过,在图像中边缘和噪点都相当于高频部分,所以低通滤波器用于去除噪点、平滑和模糊图像。高通滤波器则反之,用来增强图像边缘,进行锐化处理。
注意:椒盐噪声可以理解为斑点,随机出现在图像中的黑点或白点;高斯噪声可以理解为拍摄图片时由于光照等原因造成的噪声。
这是高斯噪声
这是椒盐噪声,有很多黑白的或者孤立的小点
1.1 均值滤波
cv.blur(img, ksize)
参数:
- ksize:代表卷积核的大小
取的是卷积核区域内元素的均值。
对于边界的像素点,则会进行边界填充,以确保卷积核的中心能够对准边界的像素点进行滤波操作。在OpenCV中,默认的是使用BORDER_REFLECT_101的方式进行填充,下面的滤波方法中除了中值滤波使用的是BORDER_REPLICATE进行填充之外,其他默认也是使用这个方式进行填充
import cv2 as cvimg = cv.imread('../images/lvbo2.png')# 均值滤波,用3*3的卷积核
blur = cv.blur(img, (3, 3))cv.imshow('img', img)
cv.imshow('blur', blur)
cv.waitKey(0)
cv.destroyAllWindows()
1.2 方框滤波
cv.boxFilter(img, ddepth,ksize, normalize)
参数:
- ksize:代表卷积核的大小
- ddepth:输出图像的深度,-1代表使用原图像的深度。
指在每个像素点所使用的位数(bit depth),也就是用来表示图像中每一个像素点的颜色信息所需的二进制位数。图像深度决定了图像能够表达的颜色数量或灰度级。
- normalize:当normalize为True的时候,方框滤波就是均值滤波,权重就等于1/9;normalize为False的时候,每个像素权重都是1,相当于求区域内的像素和,超出部分做取模运算。
import cv2 as cvimg = cv.imread('../images/lvbo2.png')# 方框滤波,用3*3的卷积核,类似均值滤波
box = cv.boxFilter(img, -1,(3, 3),normalize=True)# 真正的方框滤波
box1 = cv.boxFilter(img, -1,(3, 3), normalize=False)cv.imshow('img', img)
cv.imshow('box', box)
cv.imshow('box1', box1)
cv.waitKey(0)
cv.destroyAllWindows()
跟均值滤波很像
可以看到这里的方框滤波显示图片是很接近白色的,因为其将像素点周围的像素权重都设为1,然后相加得到该像素的值,所以很多都超过了255而进行了取模操作。
1.2 高斯滤波
cv.GaussianBlur(img, ksize, sigmaX)
参数:
- sigmaX:
就是高斯函数里的值,σx值越大,模糊效果越明显。高斯滤波相比均值滤波效率要慢,但可以有效消除高斯噪声,能保留更多的图像细节,所以经常被称为最有用的滤波器。
通过使用高斯函数(正态分布)作为卷积核来对图像进行模糊处理。
import cv2 as cvimg = cv.imread('../images/lvbo2.png')# 高斯,用3*3的卷积核
Gauss = cv.GaussianBlur(img, (3, 3), 1)cv.imshow('img', img)
cv.imshow('Gauss', Gauss)cv.waitKey(0)
cv.destroyAllWindows()
1.3 中值滤波
cv.medianBlur(img, ksize)
中值滤波没有核值,而是在原图中从左上角开始,将卷积核区域内的像素值进行排序,并选取中值作为卷积核的中点的像素值。就是用区域内的中值来代替本像素值,所以那种孤立的斑点,如0或255很容易消除掉,适用于去除椒盐噪声和斑点噪声
import cv2 as cv# 导入椒盐噪声图片
img = cv.imread('../images/lvbo3.png')# 中值滤波,注意这里的3
median = cv.medianBlur(img, 3)cv.imshow('img', img)
cv.imshow('median', median)cv.waitKey(0)
cv.destroyAllWindows()
其实你会注意到,中值滤波的ksize为啥是个整数呢,前面的都是(x,x)。因为中值滤波是非线性的,且没有核值不依赖卷积核权重。
1.4 双边滤波
cv.bilateralFilter(img,ksize,d,sigmaColor,sigmaSpace)
参数:
- d:过滤时周围每个像素领域的直径,这里已经设置了核大小。d=9===>9x9
- sigmaColor:在color space(值域空间)中过滤sigma。参数越大,那些颜色足够相近的的颜色的影响越大。较大的sigmaColor意味着更大的颜色差异将被允许参与到加权平均中.
- sigmaSpace:在coordinate space(坐标空间)中过滤sigma。这个参数是坐标空间中的标准差,决定了像素位置对滤波结果的影响程度。
双边滤波的基本思路是同时考虑将要被滤波的像素点的空域(空间)信息(周围像素点的位置的权重)和值域信息(周围像素点的像素值的权重)。因为在边缘处,临近的像素点差异会比较大,如果只是使用空域信息来进行滤波的话,得到的结果必然是边缘被模糊了,这样我们就丢掉了边缘信息。这也是一种非线性滤波。
import cv2 as cvimg = cv.imread('../images/lvbo2.png')# 双边滤波
sb = cv.bilateralFilter(img, 9, 100, 100)cv.imshow('img', img)
cv.imshow('sb', sb)cv.waitKey(0)
cv.destroyAllWindows()
注意:
关于2个sigma参数:
简单起见,可以令2个sigma的值相等;
如果他们很小(小于10),那么滤波器几乎没有什么效果;
如果他们很大(大于150),那么滤波器的效果会很强,使图像显得非常卡通化。
关于参数d:
过大的滤波器(d>5)执行效率低。
对于实时应用,建议取d=5;
对于需要过滤严重噪声的离线应用,可取d=9;
二.图像梯度处理
2.1 图像梯度
把图片想象成连续函数,因为边缘部分的像素值是与旁边像素有明显区别,所以对图片局部求极值,就可以得到整幅图片的边缘信息了。不过图片是二维的离散函数,导数就变成了差分,这个差分就称为图像的梯度。
2.2 垂直边缘提取
这个核是用来提取图片中的垂直边缘(右侧边缘,若提取左边缘卷积核中正负号换一下就好)的,中间像素就是这个卷积核卷积的结果。
当前列左右两侧的元素进行差分,由于边缘(当前列)的值明显小于(或大于)周边像素,所以边缘(当前列)的差分结果会明显不同,这样就提取出了垂直边缘。简单理解,就是在‘1’位置右边的像素值相对于‘-1’位置左边的像素值的差距会显示在中间的像素中,所以中间的列就是旁边两列的垂直差分。
来介绍一下二维卷积函数:
cv2.filter2D(src, ddepth, kernel)
- src: 输入图像,一般为numpy数组。
- ddepth: 输出图像的深度,可以是负值(表示与原图相同)、正值或其他特定值(常用-1 表示输出与输入具有相同的深度)。
- kernel: 卷积核,一个二维数组(通常为奇数大小的方形矩阵),用于计算每个像素周围邻域的加权和。
import cv2 as cv
import numpy as np# 模拟一张图像,灰度图
img=np.array([[100,102,109,110,98,20,19,18,21,22],[109,101,98,108,102,20,21,19,20,21],[109,102,105,108,98,20,22,19,19,18],[109,98,102,108,102,20,23,19,20,22],[109,102,105,108,98,20,22,19,20,18],[100,102,108,110,98,20,19,18,21,22],[109,101,98,108,102,20,22,19,20,21],[109,102,108,108,98,20,22,19,19,18],],dtype=np.float32)
# 定义卷积核,
kernel=np.array([[-1,0,1],[-2,0,2],[-1,0,1]],dtype=np.float32)
# 二维卷积操作
img2=cv.filter2D(img,-1,kernel)
# 打印卷积后的图
print(img2)
[[ 0. -4. 30. -14. -356. -320. -6. 2. 12. 0.]
[ 0. -17. 28. -10. -354. -317. -5. -3. 7. 0.]
[ 0. -26. 29. -10. -352. -312. -4. -10. 3. 0.]
[ 0. -22. 32. -14. -352. -310. -4. -11. 4. 0.]
[ 0. -7. 30. -24. -354. -310. -5. -5. 5. 0.]
[ 0. 1. 29. -23. -356. -314. -6. 0. 9. 0.]
[ 0. -15. 28. -12. -354. -315. -5. -5. 7. 0.]
[ 0. -24. 26. -12. -352. -312. -4. -10. 2. 0.]]
这里的值就是用上面的卷积核计算的,其中边缘由于使用边界反射101进行填充,左右两边的差分为0,也就是无梯度变化。然后中间两列有着超出255的最大数值,这就会作为边缘被提取出来。
差分后值的符号正负代表梯度的方向,差分值实际应该取绝对值,毕竟我们只是想看数值大小来提取边缘。值超过了255都算(+-)255,即使是负号。
如果使用的是一张真正的图片进行特征提取,建议转化为灰度图(单通道)方便计算。