自学内容网 自学内容网

【OpenCV】(九)—— 边缘检测

边缘检测是图像处理和计算机视觉中的一个重要技术,它用于识别数字图像中亮度发生急剧变化的点,这些点通常对应于物体的边界。在 OpenCV 中,有几种常用的边缘检测方法,其中最著名的是 Canny 边缘检测算法。

Sobel算子

边缘检测算子,如果你学过卷积操作的话就能理解,计算机想要获取图像的边缘信息,就是利用一个卷积核进行计算,在计算机视觉中称其为图像的梯度,简单理解为两个物体的边缘就是梯度大的地方,人用肉眼可以观察出图像中的物品边缘,计算机则需要使用卷积来判断不同区域的梯度问题

sobel算子说的就是两个卷积核,一个用于水平方向的检查,一个用于垂直方向的检查

在这里插入图片描述

sobel方法在opencv中的函数原型如下:

cv2.Sobel(src, ddepth, dx, dy[, dst[, ksize[, scale[, delta[, borderType]]]]]) -> dst
  • rc:输入图像,通常是单通道的灰度图像。
  • ddepth:输出图像的深度。常见的值有 cv2.CV_8U, cv2.CV_16S, cv2.CV_32F 等。选择合适的深度可以避免溢出问题。
  • dx:x 方向上的导数阶数。例如,1 表示计算 x 方向上的梯度。
  • dy:y 方向上的导数阶数。例如,1 表示计算 y 方向上的梯度。
  • dst:输出图像,与输入图像具有相同的尺寸和通道数。
  • ksize:Sobel 算子的大小,可以是 1、3、5 或 7。默认值为 3。
  • scale:缩放导数的因子,默认值为 1。
  • delta:在结果中添加的值,默认值为 0。
  • borderType:边界的填充方式,默认值为 cv2.BORDER_DEFAULT

sobel算子关注图像中的强度变化,而不是色彩信息,通常使用灰度图作为输入,我们用的示例图如下:

在这里插入图片描述

sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3)
# 计算梯度就是边缘右侧信息减去左侧信息,如果为负数的话置零,使用绝对值保留所有结果
sobelx = cv2.convertScaleAbs(sobelx)

sobely = cv2.Sobel(img,cv2.CV_64F,0,1,ksize=3)
sobely = cv2.convertScaleAbs(sobely)

# 把垂直和水平方向获取的边缘线的都进行整合
res = cv2.addWeighted(sobelx,0.5,sobely,0.5,0)
cv_show('res',res)

运行结果:

在这里插入图片描述

边缘检测有不同的算子,不同的算子卷积核不同(针对的内容不同),此处不详细介绍,简单实用一下Scharr和Laplacian算子,观察一下不同算子进性边缘检测的结果:

# 不同的算子
sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3)
sobelx = cv2.convertScaleAbs(sobelx)
sobely = cv2.Sobel(img,cv2.CV_64F,0,1,ksize=3)
sobely = cv2.convertScaleAbs(sobely)
sobelxy = cv2.addWeighted(sobelx,0.5,sobely,0.5,0)

scharrx = cv2.Scharr(img,cv2.CV_64F,1,0)
scharry = cv2.Scharr(img,cv2.CV_64F,0,1)
scharrx = cv2.convertScaleAbs(scharrx)
scharry = cv2.convertScaleAbs(scharry)
scharrxy = cv2.addWeighted(scharrx,0.5,scharry,0.5,0)

laplacian = cv2.Laplacian(img,cv2.CV_64F)
laplacian = cv2.convertScaleAbs(laplacian)

res = np.hstack((sobelxy,scharrxy,laplacian))
cv_show('res',res)

运行结果:

在这里插入图片描述

Canny边缘检测

Canny 边缘检测算法是一种多阶段的边缘检测算法,由 John F. Canny 在 1986 年提出。Canny 算法因其良好的性能和鲁棒性而成为图像处理领域中最常用的一种边缘检测方法。该算法通过以下五个主要步骤来检测图像中的边缘:

  1. 噪声去除

由于边缘检测对噪声非常敏感,因此首先需要使用高斯滤波器对图像进行平滑处理,以减少噪声的影响。这一步骤可以通过 cv2.GaussianBlur 函数实现。

  1. 计算梯度强度和方向

使用 Sobel 算子计算图像的水平和垂直方向的梯度。梯度的大小反映了像素点的强度变化,而梯度的方向则指示了边缘的方向。这一步骤可以通过 cv2.Sobel 函数实现,但通常在 Canny 算法中直接使用 cv2.Canny 函数时会自动完成。

  1. 非极大值抑制

在这一步中,算法会检查每个像素点的梯度方向,并只保留那些在梯度方向上局部最大的像素点,从而消除边缘检测中的细小斑点。这一步骤确保了边缘的细长和连续。

  1. 双阈值检测

设置两个阈值:高阈值和低阈值。任何高于高阈值的像素点都被认为是强边缘;低于低阈值的像素点被抛弃;介于两个阈值之间的像素点如果与强边缘相连,则被认为是弱边缘,否则被抛弃。这一步骤有助于连接边缘并减少虚假边缘。

  1. 边缘跟踪

通过双阈值检测后,算法会从强边缘开始,沿着边缘路径跟踪,将所有与强边缘相连的弱边缘标记为最终的边缘。这一步骤确保了边缘的完整性和连贯性。

canny通过更多阶段的处理来实现比sobel更精细的效果。

# canny
can = cv2.Canny(img, threshold1=30, threshold2=100)
cv_show('can',can)

在这里插入图片描述

我们进行的边缘检测都是基于灰度图像的,且都是单纯提供结果,我们可以同时画出RGB彩色图像和轮廓,这样更加便于观察。

首先实用函数cv2.findContours找出canny处理后的所有轮廓线集合,其函数原型如下:

contours, hierarchy = cv2.findContours(image, mode, method[, contours[, hierarchy[, offset]]])

参数说明

  • image:输入图像,通常是经过二值化的图像(即黑白图像)。图像类型应该是 np.uint8
  • mode:轮廓检索模式,决定了轮廓的组织方式。常见的模式有:
    • cv2.RETR_EXTERNAL:只检索最外层的轮廓。
    • cv2.RETR_LIST:检索所有轮廓,但不建立层级关系。
    • cv2.RETR_CCOMP:检索所有轮廓,并将它们组织成两层:顶层是外部轮廓,第二层是孔洞。
    • cv2.RETR_TREE:检索所有轮廓,并重建完整的嵌套轮廓层次。
  • method:轮廓近似方法,决定了轮廓的存储方式。常见的方法有:
    • cv2.CHAIN_APPROX_NONE:保存轮廓上所有的点。
    • cv2.CHAIN_APPROX_SIMPLE:压缩水平、垂直和对角线方向的轮廓,只保留端点。
    • cv2.CHAIN_APPROX_TC89_L1cv2.CHAIN_APPROX_TC89_KCOS:使用 Teh-Chin 链近似算法。
  • contours:输出参数,用于存储找到的轮廓。每个轮廓是一个包含点的 numpy 数组。
  • hierarchy:输出参数,用于存储轮廓的层级信息。每个轮廓的信息是一个四元组 [Next, Previous, First_Child, Parent]
  • offset:轮廓点的偏移量。如果需要将轮廓点偏移到图像的不同位置,可以使用这个参数。

返回值

  • contours:找到的轮廓列表。每个轮廓是一个包含点的 numpy 数组。
  • hierarchy:轮廓的层级信息。每个轮廓的信息是一个四元组 [Next, Previous, First_Child, Parent]

我们可以在原图上实用函数cv2.drawContours来为其画上轮廓线,其函数原型如下:

cv2.drawContours(image, contours, contourIdx, color[, thickness[, lineType[, hierarchy[, maxLevel[, offset]]]]]) → img
  • image:要在其上绘制轮廓的图像。这通常是彩色图像(BGR 格式)或者灰度图像。

  • contours:轮廓列表。这是一个由多个轮廓组成的 Python 列表,每个轮廓是一个包含点的 numpy 数组。

  • contourIdx:要绘制的轮廓索引。如果设置为 -1,则绘制所有轮廓。

  • color:轮廓的颜色。对于 BGR 图像,颜色是一个三元组,例如 (0, 255, 0) 表示绿色。对于灰度图像,颜色是一个单通道值。

  • thickness:轮廓线条的厚度。如果设置为负值(如 -1),则填充轮廓。

  • lineType

    :轮廓线条的类型。常见的选项有:

    • cv2.LINE_4:4 邻接线
    • cv2.LINE_8:8 邻接线(默认)
    • cv2.LINE_AA:抗锯齿线
  • hierarchy:轮廓的层级信息。通常在使用 cv2.findContours 函数时会返回这个参数。

  • maxLevel:绘制轮廓的最大层级。如果设置为 0,只绘制当前层级的轮廓;如果设置为 1,绘制当前层级及其子轮廓;依此类推。

  • offset:轮廓点的偏移量。如果需要将轮廓绘制到图像的不同位置,可以使用这个参数。

把上面两步合并起来即可:

contours, hierarchy = cv2.findContours(can, cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
# 画轮廓,直接改变原图,尽量使用copy
img = cv2.imread('lena.jpg')
draw_img = img.copy()
res = cv2.drawContours(draw_img,contours,-1,(0,0,255),2)
cv_show('res',res)

在这里插入图片描述

模板匹配

模板匹配就是寻找图中的信息,例如在一张照片中找到人脸的位置。基本思想是在目标图像中滑动一个小的模板图像,并计算每个位置的匹配度。匹配度可以通过不同的方法来计算,例如平方差、相关系数等。最终,函数会返回一个匹配度矩阵,矩阵中的每个值表示对应位置的匹配度。

OpenCV 提供了 cv2.matchTemplate 函数来实现这一功能,其函数原型如下:

result = cv2.matchTemplate(image, templ, method[, result[, mask]])

参数说明

  • image:目标图像,即需要在其中搜索模板的大图像。这个图像不能有通道数(颜色通道),所以如果是彩色图像,通常需要先转换成灰度图像。
  • templ:模板图像,即要搜索的小图像。这个图像也应该是单通道的。
  • method:指定匹配方法的标志符,可以是以下值之一:
    • cv2.TM_SQDIFF:平方差匹配法,最佳匹配位置的值最小。
    • cv2.TM_SQDIFF_NORMED:归一化平方差匹配法,最佳匹配位置的值最小。
    • cv2.TM_CCORR:相关匹配法,最佳匹配位置的值最大。
    • cv2.TM_CCORR_NORMED:归一化相关匹配法,最佳匹配位置的值最大。
    • cv2.TM_CCOEFF:相关系数匹配法,最佳匹配位置的值最大。
    • cv2.TM_CCOEFF_NORMED:归一化相关系数匹配法,最佳匹配位置的值最大。
  • result:输出的匹配结果图像,类型为 np.float32。这是一个二维数组,其尺寸为 (W-w+1, H-h+1),其中 WH 分别是目标图像的宽度和高度,wh 分别是模板图像的宽度和高度。
  • mask(可选):掩码图像,与模板图像具有相同的尺寸。如果提供了掩码,则只在掩码为非零的区域进行匹配。

返回值

  • result:匹配结果图像,每个像素值表示该位置的匹配程度。对于 TM_SQDIFFTM_SQDIFF_NORMED 方法,最佳匹配位置的值最小;对于其他方法,最佳匹配位置的值最大。

【示例】使用的原图和模板如下,利用opencv找到模板在原图中的位置:

在这里插入图片描述

在这里插入图片描述

res = cv2.matchTemplate(lena, face,cv2.TM_CCOEFF)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
top_left = max_loc
w, h = face.shape[::-1]
bottom_right = (top_left[0] + w, top_left[1] + h)
cv2.rectangle(lena, top_left, bottom_right, 255, 2)
cv2.imshow('Matched Area', lena)
cv2.waitKey(0)
cv2.destroyAllWindows()

在这里插入图片描述


原文地址:https://blog.csdn.net/dao_cao_renshuai/article/details/143029841

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!