图像预处理(2)-滤波处理
5. 各种滤波
5.1 线性滤波
线性滤波包括:方框滤波,均值滤波,高斯滤波。总的来说,这三种线性滤波原理:每个像素的输出值是输入像素的加权和。其处理方式都是将图像像素与相应的核进行卷积,核即是权重,其作用是将原图像素按权重进行分配。
5.1.1 方框滤波和均值滤波的卷积核
如下所示,k即为方框滤波的核,可以看到无论a为何值,矩阵内的每一个元素都相等,卷积时对像素的权重都一样。均值滤波是方框滤波归一化后的特殊处理,下式中,当normalize=true时的k为均值滤波的核。
其中:
5.1.2 高斯滤波的卷积核
高斯滤波是一种线性平滑滤波,适用于消除高斯噪声,广泛应用于图像处理的减噪过程。通俗地讲高斯滤波就是对整幅图像进行加权平均的过程,每一个像素点的值,都由其本身和邻域内的其他像素值经过加权平均后得到。
高斯模糊,我们常说的高斯模糊就是使用高斯滤波器完成的,高斯模糊是低通滤波的一种,也就是滤波函数是低通高斯函数,但是高斯滤波是指用高斯函数作为滤波函数,至于是不是模糊,要看是高斯低通还是高斯高通。
如下所示即为标准差为
越小,关注的区域越集中
为了降低计算量,我们可以将二维高斯核进行拆分:
这样我们就可以将一个二维卷积拆分成两个一维卷积,行卷积和列卷积。
5.1.3 线性滤波的代码实现
我们使用如下图例进行演示:
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('lena.png')
#均值滤波
blur = cv2.blur(img,(5,5)) #参数为(输入图像,卷积核大小)
cv_show(blur) #cv_show()函数在3.3代码实现中给出定义
输出结果为:
#方框滤波
box = cv2.boxFilter(img,-1,(3,3),normalize=True)
#参数(输入图片,输出结果与原颜色通道是否一致-1为一致,卷积核大小,是否做归一化)
#归一化是指在卷积操作中是否取平均,如果取平均则与均值滤波一致,如果不去发生越界则值为255
cv_show(box)
输出结果为:
#高斯滤波
#高斯模糊的卷积核里的数值是满足高斯分布,相当于更重视中间的
aussian = cv2.GaussianBlur(img,(5,5),1)
cv_show(aussian)
输出结果为:
5.2 非线性滤波
5.2.1 中值滤波
中值滤波的基本思想就是用像素点邻域灰度值的中值来代替该像素点的灰度值,如下图所示,将中心点周围被称为邻域的像素点进行排序,取排序后的中值作为当前新的像素值。
中值滤波可以有效地去除椒盐噪声。
5.2.2 双边滤波
基于空间分布的高斯滤波函数,其核为一个与空间距离相关的高斯函数与一个灰度距离相关的高斯函数相乘所得。简单说就是高斯滤波的加强版,在高斯滤波中,只有关于空间距离的权重,离中心点越远,权重越小,而双边滤波在此基础上添加了关于灰度距离的权重,邻域中的像素灰度值越接近中心点灰度值,权重越大。
空间距离:指的是当前点与中心点的欧氏距离。空间域高斯函数其数学形式为:
其中
灰度距离:指的是当前点灰度与中心点灰度的差的绝对值。值域高斯函数其数学形式为:
将上述两式相乘就可以得到依赖数据的双边滤波权重函数:
至此,即得到了双边滤波的权重,最后再将权重与像素对应相乘相加再除以权重和,即求出最后输出像素值。
最后求像素值的步骤其实是在求卷积,双边滤波也是先求出一个核,再用核进行求卷积。
5.2.3 非线性滤波的代码实现
我们继续使用5.1.3中的图例来进行演示
#中值滤波
median = cv2.medianBlur(img,5)
# img1 = cv2.GaussianBlur(median,(5,5),1)
cv_show(median)
输出结果为:
#双边滤波
bilateral_filter_img1 = cv2.bilateralFilter(img, 9, 200, 200)
'''
cv2.bilateralFilter(src,d,sigmaColor,sigmaSpace)
src:输入图像
d:过滤时周围每个像素图像领域的直径
sigmaColor:颜色空间过滤器的sigma值对应上式中sigma_r,参数越大会有越远的像素被混合到一起
sigmaSpace:坐标空间滤波器的sigma值对应上式中sigma_d,参数越大,那些颜色足够相近的颜色影响越大
'''
cv_show(bilateral_filter_img1)
输出结果为:
5.3 基于梯度的边缘检测算子
5.3.1 Sobel算子
sobel算子的核心是像素矩阵的卷积,而卷积本质就是对指定的图像区域的像素值进行加权求和的过程,其计算过程为图像区域中的每个像素值分别与卷积模板的每个元素对应相乘,将卷积的结果做求和运算,运算的和就是卷积运算的结果。
sobel算子包含垂直和水平两个方向的卷积模板,如下所示
垂直(左)卷积模板:
水平(右)卷积模板:
若
改变后的灰度值有两种计算方式:
最后设置一个阈值,运算后的像素值大于该阈值则输出为0,小于该阈值则输出为255。
Sobel算法只采用水平和垂直两个方向模板,对其他方向的边缘不敏感,对于纹理较复杂、斜向边缘较多的图像轮廓提取不是很理想,抗噪声能力也比较低。可以在原有水平和垂直方向上增加两个对角线方向上的模板进行改进。
5.3.2 Canny算子
Canny算子进行边缘检测的步骤
- 对图像进行二维高斯滤波:通过使用高斯滤波器与图像进行卷积,来减少噪声对边缘检测结果的影响和平滑图像,以减少边缘检测器上明显的噪声影响。
- 通过一阶微分计算图像的灰度梯度幅值和方向:通过梯度方向确定边缘的方向,就可以把边缘的梯度方向大略分成几种角度(如0°、45°、90°、135°),并可以找到这个像素梯度方向的邻接像素。该梯度的计算和Sobel计算过程一样,改变后的灰度值有两种计算方式:
- 对计算出的梯度幅值进行非极大值抑制(Non-MaximaSuppression,NMS):由于梯度值最大不一定为边缘点,需要对一阶微分计算后的图像数据进行非极大值抑制,只保留幅值局部变化最大的点。将当前像素的梯度强度与沿正负梯度方向上的两个像素进行比较。如果当前像素的梯度强度与另外两个像素相比最大,则该像素点保留为边缘点,否则该像素点将被抑制。
- 通过人为设定的高低阈值确定图像的边缘。为了解决噪声和颜色变化引起的一些边缘像素,必须用弱梯度值过滤边缘像素,并保留具有高梯度值的边缘像素,可以通过选择高低阈值来实现。如果边缘像素的梯度值高于高阈值,则将其标记为强边缘像素;如果边缘像素的梯度值小于高阈值并且大于低阈值,则将其标记为弱边缘像素;如果边缘像素的梯度值小于低阈值,则会被抑制。阈值的选择取决于给定输入图像的内容。
由于梯度计算对噪声非常敏感,并且采用高斯滤波需要人为设定方差,所以容易出现漏检现象。
改进方法:
利用双线性滤波代替高斯滤波,同时在非极大值抑制时将梯度幅值极大值点进行保留并作为候选边缘点,并且进行自适应阈值选取。
5.3.3 Roberts算子
Roberts算子和Sobel算子原理基本一样。只是Roberts算子使用的偶数模板,如
垂直(左):
水平(右):
若
梯度两种计算方式为:
最后设置一个阈值,运算后的像素值大于该阈值输出为0,小于该阈值输出为255。
Roberts算子对图像边缘检测而言定位精度高,在水平和垂直方向效果较好。但是该算子对噪声比较敏感,无法消除局部干扰,对图像中目标和背景灰度差异表现并不显著的弱边缘却很难检测识别,这将导致提取的目标边缘出现间断。并且该算子的阈值需要人为设定,使得传统的Roberts算子在提取不同目标轮廓时具有很大的局限性。
改进方法:采用
5.3.4 Prewitt算子
Prewitt算子采用
垂直(左):
水平(右):
若
梯度的两种计算方式为:
最后设置一个阈值,运算后的像素值大于该阈值输出为0,小于该阈值输出为255。
由于Prewitt算子模板只能对水平和垂直方向的边缘进行检测,无法检测斜侧方向,使边缘检测不完整。
改进方法:可以通过添加左右斜侧方向模板,是边缘检测更加完整。
5.3.5 Scharr算子
scharr算子与Sobel算子思想相同,只是卷积核的系数不同,scharr算子提取边界也更加灵敏,能提取到更小的细节,但要注意的是越是灵敏越可能误判。下面为scharr算子的卷积核。
垂直(左):
水平(右):
5.3.6 基于梯度的边缘检测算子的代码实现
img = cv2.imread('test1.png',cv2.IMREAD_GRAYSCALE)
#Sobel算子
sobelx = cv2.Sobel(img,-1,1,0,ksize=3)
'''
cv2.Sobel(src,ddepth,dx,dy,ksize)
ddepth:图像的深度
dx和dy:分别表示水平和垂直方向
ksize:Sobel算子的大小
'''
cv_show(sobelx)
图像深度是指存储每个像素所用的位数,也用于量度图像的色彩分辨率.图像深度确定彩色图像的每个像素可能有的颜色数,或者确定灰度图像的每个像素可能有的灰度级数.它决定了彩色图像中可出现的最多颜色数,或灰度图像中的最大灰度等级.比如一幅单色图像,若每个像素有8位,则最大灰度数目为2的8次方,即256.一幅彩色图像RGB的3个分量的像素位数分别为4,4,2,则最大颜色数目为2的4+4+2次方,即1024,就是说像素的深度为10位,每个像素可以是1024种颜色中的一种.
输出结果为:
因为白到黑是正数,而黑到白是负数,所有的负数会被截断成0,所以要取绝对值。
sobelx = cv2.Sobel(img,cv2.CV_32F,1,0,ksize=3)
sobelx= cv2.convertScaleAbs(sobelx)
cv_show(sobelx)
输出结果为:
同理我们在垂直方向进行处理:
sobely = cv2.Sobel(img,cv2.CV_32F,0,1,ksize=3)
sobely = cv2.convertScaleAbs(sobely)
cv_show(sobely)
输出结果为:
我们分别计算
sobelxy = cv2.addWeighted(sobelx,.5,sobely,.5,0)
cv_show(sobelxy)
输出结果为:
#Canny算子
img = cv2.imread('bird.png',cv2.IMREAD_GRAYSCALE)
v1 = cv2.Canny(img,80,150)
#cv2.Canny(输入图像,minVal,maxVal)即人为设定的高低阈值
v2 = cv2.Canny(img,50,100)
res = np.hstack((v1,v2))
cv_show(res)
输出结果为:
可以看出minVal的增大可以提高一些细节的边缘识别,当然具体的参数要根据不同的需要以及图片效果来进行设定。
#Roberts算子
'''
通过cv2.filter2D(src,ddepth,kernel,dst=None,anchor=None,delta=None,borderType=None)
来实现Roberts算子的边缘检测
'''
#创建水平和垂直方向的Roberts算子
kernelx = np.array([[-1, 0], [0, 1]], dtype=int)
kernely = np.array([[0, -1], [1, 0]], dtype=int)
x = cv2.filter2D(img, cv2.CV_16S, kernelx)
y = cv2.filter2D(img, cv2.CV_16S, kernely)
absX = cv2.convertScaleAbs(x)
absY = cv2.convertScaleAbs(y)
Roberts = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)
cv_show(Roberts)
输出结果为:
#Prewitt算子
kernelx = np.array([[1,1,1],[0,0,0],[-1,-1,-1]],dtype=int)
kernely = np.array([[-1,0,1],[-1,0,1],[-1,0,1]],dtype=int)
x = cv2.filter2D(img, cv2.CV_16S, kernelx)
y = cv2.filter2D(img, cv2.CV_16S, kernely)
absX = cv2.convertScaleAbs(x)
absY = cv2.convertScaleAbs(y)
Prewitt = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)
cv_show(Prewitt)
输出结果为:
#Scharr算子
scharrx = cv2.Scharr(img,cv2.CV_32F,1,0)
scharry = cv2.Scharr(img,cv2.CV_32F,0,1)
scharrx = cv2.convertScaleAbs(scharrx)
scharry = cv2.convertScaleAbs(scharry)
scharrxy = cv2.addWeighted(scharrx,.5,scharry,.5,0)
cv_show(scharrxy)
输出结果为:
5.4 Laplacian(拉普拉斯)滤波
5.4.1 Laplacian算子定义
该二阶导数的通用数字近似为:
和
因而有:
通过对图像和下面的空间掩膜做卷积操作,该表达式可以在图像中的所有点
数字二阶导数的另一种定义是考虑对角线元素,然后使用如下掩膜来实现:
有时定义两个导数的符号可能与此处使用的符号相反,从而得到与前面两个掩膜正好相反的结果。使用Laplacian算子增强图像的基本公式为
其中
5.4.2 Laplacian作用
由于拉普拉斯是一种微分算子,它的应用可以增强图像中灰度突变的区域,减弱灰度的缓慢变化区域。因此,锐化处理可选择Laplacian算子对原图像进行处理,产生描述灰度突变的图像,再将拉普拉斯图像与原始图像叠加而产生锐化图像。
这种简单的锐化方法既可以产生Laplacian锐化处理的效果,同时又能保留背景信息,将原始图像叠加到Laplacian变换的处理结果中去,可以使图像中的各灰度值得到保留,使灰度突变处的对比度得到增强,最终结果是在保留图像背景的前提下,凸显出图像中小的细节信息。
5.4.3 Laplacian算子的代码实现
我们将继续使用5.3.6中的图片示例继续进行演示:
#Laplacian算子
laplacian = cv2.Laplacian(img,cv2.CV_32F)
laplacian = cv2.convertScaleAbs(laplacian)
cv_show(laplacian)
输出结果为: