OpenCV + Python3
第九章 图像梯度
图像梯度计算的是图像变化的速度。对于图像的边缘部分,其灰度值变化比较大,梯度值也比较大;相反,对于图像中比较平滑的部分,其灰度值变化较小,相应的梯度值也较小。图像梯度计算的是图像的边缘信息。 需要说明的是,滤波器通常是指由一幅图像根据像素点(x, y)临近的区域计算得到另外一副新图像的算法。因此,滤波器是由邻域及预定义的操作构成的。滤波器规定了滤波时所采用的形状以及该区域里像素值的组成规律。滤波器也被称为“掩模”、“核”、“模板”、“窗口”、“算子”等,一般信号领域将其称为“滤波器”,数学领域称之为“核”。本章中出现的滤波器多为“线性滤波器”,也就是说,滤波的目标像素点的值等于原始像素值及其周围像素值的加权和。这种基于线性核的滤波,也就是我们所熟悉的卷积。 本章中所说的“Sobel”算子通常是指Sobel滤波器。
1.Sobel理论基础cv2.Sobel(src, ddepth, dx, dy, ksize)
是一种离散的微分算子,该算子结合了高斯平滑和微分求导运算。该算子利用局部差分寻找边缘,计算所得的是一个梯度的近似值。
1)计算水平方向偏导数的近似值(当Sobel算子大小为3 * 3时)
2)计算垂直方向偏导数的近似值(当Sobel算子大小为3 * 3时)
ddepth:代表输出图像的深度,由分析可得,为了让偏导数正确的显示出来,需要将值为负数的近似偏导数转换为正数。即,要将偏导数取绝对值,以保证偏导数能正确显示。在实际操作中,计算梯度值可能会出现负数。通常处理的图像是8位图类型,如果结果也是该类型,那么所有负数会自动截断为0,发生信息丢失。所以,为了避免发生信息丢失,我们在计算时使用最高的数据类型cv2.CV_64F,再通过取绝对值将其映射为cv2.CV_8U(8位图)类型。
dx代表x方向上的求导分数
dy代表y方向上的求导分数
其中,dx和dy有多种不同的组合:
· 计算x方向边缘(梯度):dx = 1, dy = 0 cv2.Sobel(src, ddepth, 1, 0)
· 计算y方向边缘(梯度):dx = 0, dy = 1 cv2.Sobel(src, ddepth, 0, 1)
· 参数dx和dy均为1:dx = 1, dy = 1 cv2.Sobel(src, ddepth, 1, 1)
·计算x方向和y方向的边缘叠加:通过组合方式实现
cv2.Sobel(src, ddepth, 0, 1)
cv2.Sobel(src, ddepth, 1, 0)
cv2.adWeighted(src1, alpha, src2, belta, gamma)
ksize代表Sobel核的大小,该值为-1时,则会使用Scharr算子进行运算
2.Scharr算子及函数使用cv2.Scharr(src, ddepth, dx, dy)
在使用3 * 3的Sobel算子时,可能计算结果并不太精准。OpenCV提供了Scharr算子,该算子具有和Sobel算子同样的速度,且精度更高。其核通常为:
OpenCV提供了函数cv2.Scharr()来计算Scharr算子
cv2.Scharr(src, ddepth, dx, dy) = cv2.Sobel(src, ddepth, dx, dy, -1)
此外在函数cv2.Scharr()中,要求dx和dy满足条件:
dx >= 0 && dy >= 0 && dx+dy == 1
所以参数dx和dy的组合方式有:
· 计算x方向边缘(梯度):dx = 1, dy = 0
· 计算y方向边缘(梯度):dx = 0, dy = 1
· 计算x和y方向的边缘叠加:通过组合方式实现
比较Sobel算子和Scharr算子:
当其核比较小时,Sobel算子的精确度不高,而Scharr算子具有更高的精度
3.Laplacian算子及其函数使用cv2.Laplacian(src, ddepth)
拉普拉斯算子是一种二阶导数算子,其具有旋转不变性,可以满足不同方向的图像边缘锐化(边缘检测)的要求。通常情况下,其算子的系数之和需要为零。(3 * 3的Laplacian算子)
Laplacian算子类似于二阶Sobel导数,需要计算两个方向的梯度值。需要注意这里求得的导数也可能为负数,故需要取绝对值以保证后续运算和显示都是正确的。
dst = cv2.Laplacian(src, ddepth, ksize, scale, delta, borderType)
该函数分别对x, y方向进行二次求导,具体为:
上式是当ksize大于1时的情况。当ksize的值为1时,Laplacian算子计算时采用的3 * 3的核如下:
通过从图像内减去它的Laplacian图像,可以增加图像的对比度,此时其算子(扩展算子)为:
算子总结:
Sobel算子和Scharr算子和Laplacian算子都可以用作边缘检测
Sobel算子和Scharr算子计算的都是一阶近似导数的值,通常情况下,可以将他们表示为:
Sobel = |左 - 右| / |下 - 上|
Scahrr = |左 - 右| / |下 - 上|
|左 - 右|表示左侧像素值减去右侧像素值的结果的绝对值,|下 - 上|表示下方像素值减去上方像素值的结果的绝对值
Laplacian算子计算的是二阶近似导数,可以表示为:
Laplacian = |左 - 右| + |左 - 右| + |下 - 上| + |下 - 上|
通过公式可以发现,Sobel算子和Scharr算子各计算了一次|左 - 右|和|下 - 上|的值,而Laplacian算子分别计算了两次。
第十章 Canny边缘检测
Canny边缘检测是一种使用多级边缘检测算法检测边缘的方法,OpenCV提供了函数cv2.Canny()实现Canny边缘检测
1.Canny边缘检测基础
Canny边缘检测分为如下几个步骤:
1>. 去噪。噪声会影响边缘检测的准确性,因此首先要将噪声过滤掉
2>. 计算梯度的幅度和方向
3>. 非极大值抑制,即适当的让边缘“变瘦”
4>. 确定边缘,使用双阈值算法确定最终的边缘信息
1)应用高斯滤波去除图像噪声
滤波的目的是平滑一些纹理较弱的非边缘区域,以得到更准确的边缘,在实际处理中,通常采用高斯滤波去除图像中的噪声。
高斯核的大小对于边缘检测的效果具有重要的作用。滤波器的核越大,边缘信息对于噪声的敏感度就越低。不过,核越大,边缘检测的定位错误也会随之增加。
2)计算梯度
之前,我们知道了怎么计算图像梯度的幅度,这里,我们关注梯度的方向,梯度的方向与边缘的方向是垂直的。边缘检测算子返回水平方向的Gx和垂直方向的Gy。梯度的幅度G和方向θ(用角度值表示)为:
梯度的方向总是与边缘垂直的,通常就近取值为水平(左,右)、垂直(上,下)、对角线(右上,右下,左上,左下)等8个方向(也就是说最后θ的值会被就近取为0°,45°,90°,135°,180°,225°,270°,315°这8个值其中的一个)。因此,在计算梯度时,我们会得到梯度的幅度和角度两个值。
3)非极大值抑制
在获得了梯度的幅度和方向后,遍历图像中的像素点,去掉所有非边缘的点。在具体实现时,逐一遍历像素点,判断当前像素点是否是周围像素点中具有相同梯度方向的最大值,并根据判断结果决定是否抑制该点。该步骤是边缘细化的过程,针对每一个像素点:
· 如果该点是正/负梯度方向上的局部最大值,则保留该点
· 如果不是,则抑制该点(归零)
4)应用双阈值确定边缘
完成上述步骤以后,图像内的强边缘已经在当前获取的边缘图像内,但是一些虚边缘可能也在边缘图像内,这些虚边缘可能是真实图像产生的,也可能是由于噪声产生的。对于后者,必须剔除。
设置两个阈值,其中一个为高阈值maxVal,另一个为低阈值minVal。根据当前边缘梯度值与这两个阈值之间的关系,判断边缘的属性:
· 如果当前边缘像素的梯度值大于或等于maxVal,则将当前边缘像素标记为强边缘
· 如果当前边缘像素的梯度值在maxVal和minVal之间,则将当前像素点标记为虚边缘(需要保留)
· 如果当前边缘像素的梯度值小于或等于minVal,则抑制当前像素点
在上述过程中,我们得到了虚边缘,而我们还需要做进一步处理,一般情况下,如果一个虚边缘:
· 与强边缘无连接,则该边缘为弱边缘,将其抑制
· 与强边缘有连接,则将该边缘处理为强边缘
注意,高阈值maxVal和低阈值minVal不是固定的,需要针对不同的图像进行定义。
2.Canny函数及使用cv2.Canny(src, threshold1, threshold2)
src为8位输入图像
Threshold1表示处理过程中的第一个阈值
Threshold2表示处理过程中的第二个阈值
当函数cv2.Canny()的参数threshold1和threshold2的值较小时,能够捕获更多的边缘信息。
若有侵权,请联系删除
本读书笔记来源于教材。1
李立宗. OpenCV轻松入门: 面向python[M].北京. 电子工业出版社: 李立宗. 2019.5 ↩︎