计算机视觉技术 - 边缘检测

计算机视觉技术 - 边缘检测


边缘就是像素值急剧变化的地方,即梯度大的地方。通常只能在单一通道,即灰度图像上检测边缘,当然,也可以为RGB图像的每个通道检测边缘。检测边缘必须经过浮点运算,否则会丢失很多边缘。检测得到的浮点型矩阵再转化为整型。

Sobel, Laplacian 边缘检测

Sobel 算子是一种差分算子,用于检测像素值有显著变化的地方,因此它更抗噪声。我们可以指定要采用的导数方向,垂直或水平(分别通过参数yorder和xorder)。还可以通过参数 ksize 指定内核的大小。

Sobel(src, depth, dx, dy[, ksize[, scale[, delta[, borderType]]]]])

  • depth:输出图像的深度,为保存计算梯度中的精度,取CV_64F。
  • dx:x方向求导阶数,例如1,2。
  • dy:y方向求导阶数,例如1,2。
  • ksize:卷积核大小,默认为3。
  • Scale:放缩尺度,默认为1。
  • delta:目标图像像素增加值,默认为0。

Sobel函数返回的是一个浮点数矩阵,需要转化到 0-255 之间。

Laplacian(src, depth[, dst[, ksize[, scale[, delta[, borderType]]]]])
Laplacian 算子具有旋转不变性,能同时检测XY方向的边缘

Δ s r c = ∂ 2 s r c ∂ x 2 + ∂ 2 s r c ∂ y 2 \Delta src = \frac{\partial ^2{src}}{\partial x^2} + \frac{\partial ^2{src}}{\partial y^2} Δsrc=x22src+y22src

ksize = 1时是以下所示 3X3 卷积核

k e r n e l = [ 0 1 0 1 − 4 1 0 1 0 ] kernel = \begin{bmatrix} 0 & 1 & 0 \\ 1 & -4 & 1 \\ 0 & 1 & 0 \end{bmatrix} kernel= 010141010

操作如下:下面的代码显示了单个图表中的 Laplacian, SobelX 与 SobelY 算子运算的结果。所有内核都是 5x5 大小。

参考代码

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
imgColor = cv.imread('../data/colorStone.jpg')
#RGB2BGR
imgColor = imgColor[:,:,::-1]
imgGray = cv.cvtColor(imgColor, cv.COLOR_BGR2GRAY)
plt.figure(figsize=(8,6))
laplacian = cv.Laplacian(imgGray,cv.CV_64F)
# 增强显示结果
lap = np.uint8(np.absolute(laplacian)) + 110
sobelx = cv.Sobel(imgGray,cv.CV_64F,1,0,ksize=5)
sobely = cv.Sobel(imgGray,cv.CV_64F,0,1,ksize=5)
plt.subplot(2,2,1),plt.imshow(imgColor)
plt.title('Original'), plt.xticks([]), plt.yticks([])
plt.subplot(2,2,2),plt.imshow(lap,cmap = 'gray')
plt.title('Laplacian'), plt.xticks([]), plt.yticks([])
plt.subplot(2,2,3),plt.imshow(sobelx,cmap = 'gray')
plt.title('Sobel X'), plt.xticks([]), plt.yticks([])
plt.subplot(2,2,4),plt.imshow(sobely,cmap = 'gray')
plt.title('Sobel Y'), plt.xticks([]), plt.yticks([])
plt.show()

在这里插入图片描述
图 1. Laplacian, SobelX 与 SobelY 运算边缘的结果

边缘运算的结果主要是要在透过图像阈值来操作,这样就可以有效的消除背景。将上述结果在透过图像阈值可以得到以下图形。

参考代码

plt.figure(figsize=(15,5))
#二值化处理 ret,thresh1 = cv.threshold(img,127,255,cv.THRESH_BINARY)
ret,threshLap = cv.threshold(lap, 127,255,cv.THRESH_BINARY_INV)
ret,threshSobelx = cv.threshold(np.uint8(np.absolute(sobelx)), 127,255,cv.THRESH_BINARY_INV)
ret,threshSobelY = cv.threshold(np.uint8(np.absolute(sobely)), 127,255,cv.THRESH_BINARY_INV)
plt.subplot(131),plt.imshow(threshLap, plt.cm.gray),plt.title('Laplacian')
plt.xticks([]), plt.yticks([])
plt.subplot(132),plt.imshow(threshSobelx, plt.cm.gray),plt.title('Sobel X')
plt.xticks([]), plt.yticks([])
plt.subplot(133),plt.imshow(threshSobelY, plt.cm.gray),plt.title('Sobel Y')
plt.xticks([]), plt.yticks([])
plt.show()

在这里插入图片描述
图 2. 经过图像阈值运算的结果

Canny 边缘检测

Canny Edge Detection是一种流行的边缘检测算法。它由John F. Canny发明。

  1. 这是一个多阶段算法。
  2. 降噪,由于边缘检测容易受到图像中噪声的影响,因此第一步是使用5x5高斯滤波器消除图像中的噪声。

查找图像的强度梯度,然后使用 Sobel 核在水平和垂直方向上对平滑的图像进行滤波,以在水平方向(Gx)和垂直方向(Gy)上获得一阶导数。如下所示:
E d g e _ G r a d i e n t    ( G ) = G x 2 + G y 2 A n g l e    ( θ ) = tan ⁡ − 1 ( G y G x ) Edge\_Gradient \; (G) = \sqrt{G_x^2 + G_y^2} \\ Angle \; (\theta) = \tan^{-1} \bigg(\frac{G_y}{G_x}\bigg) Edge_Gradient(G)=Gx2+Gy2 Angle(θ)=tan1(GxGy)

非极大值抑制在获得梯度大小和方向后,将对图像进行全面扫描,以去除可能不构成边缘的所有不需要的像素。为此,在每个像素处,检查像素是否是其在梯度方向上附近的局部最大值。点 A 在边缘(垂直方向)上。渐变方向垂直于边缘。点 B 和 C 在梯度方向上。因此,将 A 点与 B 点和 C 点进行检查,看是否形成局部最大值。如果是这样,则考虑将其用于下一阶段,否则将其抑制(置为零)。简而言之,得到的结果是带有“细边”的二进制图像。查看下面的图片:

在这里插入图片描述
图 3. Canny边缘检测的概念

磁滞阈值:该阶段确定哪些边缘全部是真正的边缘,哪些不是。为此,我们需要两个阈值 minVal 和 maxVal。强度梯度大于 maxVal 的任何边缘必定是边缘,而小于 minVal 的那些边缘必定是非边缘,因此将其丢弃。介于这两个阈值之间的对象根据其连通性被分类为边缘或非边缘。如果将它们连接到“边缘”像素,则将它们视为边缘的一部分。否则,它们也将被丢弃。见下图。边缘A在 maxVal 之上,因此被视为“确定边缘”。尽管边C低于 maxVal ,但它连接到边A,因此也被视为有效边,我们得到了完整的曲线。但是边缘B尽管在 minVal 之上并且与边缘C处于同一区域,但是它没有连接到任何“确保边缘”,因此被丢弃。因此,非常重要的一点是我们必须相应地选择 minVal 和 maxVal 以获得正确的结果。

在这里插入图片描述
图 4.磁滞阈值 minVal 和 maxVal

OpenCV 将以上所有内容放在单个函数 Canny() 中。第一个参数是我们的输入图像。第二个参数是我们的 minVal (如果一个像素的梯度大于它,就将其认为是边缘)和 maxVal(如果一个像素的梯度大于它并且与第一个阈值相连,则将其认为是边缘) 。第三个参数是 apertureSize 。它是用于查找图像渐变的 Sobel 内核的大小。默认情况下为 3。最后一个参数是 L2gradient,它指定用于查找梯度幅度的方程式。如果为 True ,则使用L2更精确的范数计算梯度,否则使用以下函数L1,默认情况下,它为 False 。Canny() 返回一个二值图像。

E d g e _ G r a d i e n t    ( G ) = ∣ G x ∣ + ∣ G y ∣ Edge\_Gradient \; (G) = |G_x| + |G_y| Edge_Gradient(G)=Gx+Gy

操作如下:下面的代码显示了单个图表中的原图、Canny运算与二值化的结果。

参考代码

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
imgColor = cv.imread('../data/colorStone.jpg')
#RGB2BGR
imgColor = imgColor[:,:,::-1]
imgGray = cv.cvtColor(imgColor, cv.COLOR_BGR2GRAY)
plt.figure(figsize=(15,5))
edges = cv.Canny(imgGray,100,200)
reverseEdges = 1 + np.asarray(edges)
plt.subplot(1,3,1),plt.imshow(imgColor)
plt.title('Original'), plt.xticks([]), plt.yticks([])
plt.subplot(1,3,2),plt.imshow(edges,cmap = 'gray')
plt.title('Edge Image'), plt.xticks([]), plt.yticks([])
plt.subplot(1,3,3),plt.imshow(reverseEdges,cmap = 'gray')
plt.title('Reverse Edge Image'), plt.xticks([]), plt.yticks([])
plt.show()

在这里插入图片描述
图 5.原图、Canny运算与二值化的结果

参考资料

  • OpenCV API, https://docs.opencv.org/
  • matplotlib.pyplot.subplot, https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.subplot.html#matplotlib.pyplot.subplot
  • matplotlib.pyplot.imshow, https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.imshow.html
  • OpenCV——Canny边缘检测(cv2.Canny()), https://blog.csdn.net/weixin_42575505/article/details/129527159
  • OpenCV 边缘检测之Canny算法(代码应用), https://yangyongli.blog.csdn.net/article/details/122290704
  • Canny边缘检测算法的实现, https://www.cnblogs.com/mightycode/p/6394810.html
  • sobel和canny边缘检测算子, https://blog.csdn.net/bblingbbling/article/details/109364102

猜你喜欢

转载自blog.csdn.net/m0_50614038/article/details/129495262