OpenCV 提供了四种不同的边缘检测算子,或者说高通滤波器:Sobel,Scharr 和 Laplacian 算子以及Canny 算子,具体检测步骤如下:

  • 图像滤波:边缘检测的算法主要是基于图像灰度值的一阶和二阶导数,但导数通常对噪声很敏感,因此必须采用滤波器来改善与噪声有关的边缘检测器的性能。常用的高斯平滑滤波(所谓 平滑 就是取某一点附近的加权平均值代替该点的像素值)。
  • 增强边缘:可以将图像灰度点邻域值有显著变化的点凸显出来。在具体编程实现时,可通过计算梯度幅值来确定。
  • 边缘检测:对图像中的各个点求梯度,梯度值较大的地方往往是 edge。

一、Sobel、Scharr 和 Laplacian 算子

  • Sobel 算子
    • 高斯平滑与微分操作的结合体,所以它的抗噪声能力很好,在图像的任何一点使用此算子,将会产生对应的梯度矢量或是其法矢量。
    • 可设定卷积核的大小(ksize), 若 k s i z e = − 1 ksize=-1 ksize=1,则会使用 3 × 3 3 \times 3 3×3 的Scharr 滤波器(千年备胎),它的效果要比 3 × 3 3 \times 3 3×3 的Sobel 滤波器好。
    • 求 Sobel 算子水平和垂直方向的梯度,其实就是用相应的卷积核和原始图像逐像素卷积
  • Laplacian 算子
    • OpenCV 在计算拉普拉斯算子时直接调用 Sobel X 和 Y 方向的算子
    • k s i z e = 1 ksize = 1 ksize=1时,算子使用默认的卷积核( 3 ∗ 3 3*3 33)
  • 代码及结果显示
img = cv2.imread('D:/dave.jpg')

# 参数1,0 为只在x方向求一阶导数,最大可以求2阶导数
sobelx = cv2.Sobel(img, -1, 1, 0, ksize=3)
# 参数0,1 为只在y方向求一阶导数,最大可以求2阶导数
sobely = cv2.Sobel(img, -1, 0, 1, ksize=3)

laplacian = cv2.Laplacian(img, -1, ksize=3)

这里写图片描述


二、Canny 边缘检测步骤

  • 高斯平滑滤波:消除噪声

  • 对图像中的每个像素点计算其梯度幅值和方向

    • 对平滑后的图像使用 Sobel 算子计算水平方向和竖直方向的一阶导数(图像梯度 G x G_x Gx G y G_y Gy)
    • 根据( G x G_x Gx G y G_y Gy)计算得到每个像素的梯度幅值和方向:
      E d g e _ G r a d i e n t ( G ) = G x 2 + G y 2 Edge\_Gradient(G) = \sqrt{G_x^2 + G_y^2} Edge_Gradient(G)=Gx2+Gy2
      A n g l e ( θ ) = t a n − 1 ( G y G x ) Angle(\theta )=tan^{-1}(\frac{G_y}{G_x}) Angle(θ)=tan1(GxGy)
    • 梯度的方向一般总是与边界垂直。梯度方向被归为四类:垂直,水平,和两个对角线。
  • 非极大值抑制(Non-maximum Suppression)

    • 对每一个像素进行检查,看这个点的梯度是不是周围具有相同梯度方向的点中梯度幅值最大的,如果是就保留,不是极大值就抑制(如设置为0)。
    • 由于梯度变化较大的区域通常比较宽,所以通过NMS可以进一步排除边缘附近的像素点,仅仅保留了一些细线条(候选边缘)。
      这里写图片描述
    • NMS算法在物体检测中应用的主要目的是:消除多余(交叉重复)的窗口,找到最佳物体检测位置。
      这里写图片描述
  • 滞后阈值(Hysteresis Thresholding)

    • 若某像素(A)的梯度幅值超过高阈值(maxVal),该像素被保留为边缘像素。
    • 若某像素(D)梯度幅值小于低阈值(minVal),该像素被排除。
    • 若某像素(C)的梯度幅值在两个阈值之间,,该像素仅仅在连接到一个高于高阈值的像素时,才被认为是边缘像素,否则该像素(B)被排除。
      这里写图片描述
  • OpenCV 中的 Canny 边缘检测:cv2.Canny(img, minVal, maxVal)

    • 算法先比较 gradient 与这两个 threshold 的关系,如果 gradient > maxVal,就承认这是一个 edge point
    • 如果 gradient < minVal,就断定这不是 edge point
    • 对于其他的点,如果与 edge point 相连接,那么这个点被认为也是 edge point,否则不是
import cv2
import numpy as np
import matplotlib.pyplot as plt

img = cv2.imread('messi5.jpg')

# 低阈值 (minVal) 和 高阈值 (maxVal):这两个阈值的选择对边缘检测结果至关重要。
# 通常,minVal(低阈值)设置为较小的值,maxVal(高阈值)设置为较大的值。
# 这样可以确保只有图像中强烈的边缘被标记为边缘,弱边缘需要与强边缘相连才能被保留
edges = cv2.Canny(img, minVal, maxVal)  # 如 minVal=100, maxVal=200

plt.subplot(121), plt.imshow(img, cmap='gray')
plt.title('Original Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(edges, cmap='gray')
plt.title('Edge Image'), plt.xticks([]), plt.yticks([])

plt.show()

三、参考资料

1、OpenCV 边缘检测:Canny算子、Sobel算子、Laplace算子以及Scharr滤波器
2、OpenCV-Python Tutorials

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐