图像处理——边缘检测

1 概述

  边缘检测是图像处理和计算机视觉中的一项基本技术,用于识别图像中亮度变化剧烈的像素点,这些像素点通常对应于物体的边界。它通过检测图像中亮度或颜色变化显著的区域,提取出物体的轮廓,常用于计算机视觉、图像处理和模式识别等领域。

  边缘检测的原理是通过计算图像中每个像素点与其周围像素点的亮度或颜色差异来确定该像素点是否为边缘。通常使用卷积操作来实现边缘检测,通过计算像素点与其周围像素点的差异来判断该像素点是否为边缘。

  判断边缘的方法很多,基本上分为两类:

  1. 基于搜索的方法: 基于搜索的边缘检测方法首先计算边缘强度,通常用一阶导数表示,例如梯度模;然后,用计算估计边缘的局部方向,通常采用梯度的方向,并利用此方向找到局部梯度模的最大值。
  2. 基于零交叉的方法: 基于零交叉的方法找到由图像得到的二阶导数的零交叉点来定位边缘.通常用拉普拉斯算子或非线性微分方程的零交叉点。

  而具体到实际场景中,具体的步骤为:

  1. 平滑图像: 减少噪声,噪声可能会被误认为是边缘。常用的平滑滤波器包括高斯滤波器。
  2. 计算导数: 计算图像中每个像素点的导数。根据导数的大小和方向,可以确定边缘的方向和强度。
  3. 非极大值抑制: 细化边缘,只保留梯度方向上的局部最大值。
  4. 阈值处理: 使用阈值来区分边缘像素和非边缘像素。

2 不同的边缘检测算法类型

2.1 一阶算子

  一阶算子是一种基于图像梯度的边缘检测方法,它通过计算图像灰度值的一阶导数来检测边缘,即利用像素灰度值的变化率来找到边缘位置。一阶算子通过梯度的大小和方向来确定边缘的位置和方向。其中:

  • 梯度:图像梯度表示灰度值在空间中的变化率,是一阶导数的体现。
  • 梯度大小:梯度大小表示边缘的强度,是梯度的模。梯度值大的区域通常对应图像中的边缘。
  • 梯度方向:梯度方向表示边缘的方向,是梯度的方向。

  图像的一阶梯度计算比较简单:
G x ( x , y ) = I ( x + 1 , y ) − I ( x , y ) G y ( x , y ) = I ( x , y + 1 ) − I ( x , y ) \begin{aligned} G_x(x, y) &= I(x+1, y) - I(x, y) \\ G_y(x, y) &= I(x, y+1) - I(x, y) \end{aligned} Gx(x,y)Gy(x,y)=I(x+1,y)I(x,y)=I(x,y+1)I(x,y)
  其中, I ( x , y ) I(x, y) I(x,y) 表示图像的灰度值, G x ( x , y ) G_x(x, y) Gx(x,y) G y ( x , y ) G_y(x, y) Gy(x,y) 分别表示图像在 x x x y y y 方向上的梯度。梯度的方向为:
θ ( x , y ) = arctan ⁡ G y ( x , y ) G x ( x , y ) \theta(x, y) = \arctan{\frac{G_y(x, y)}{G_x(x, y)}} θ(x,y)=arctanGx(x,y)Gy(x,y)
  梯度的大小为:
G ( x , y ) = G x ( x , y ) 2 + G y ( x , y ) 2 G(x, y) = \sqrt{G_x(x, y)^2 + G_y(x, y)^2} G(x,y)=Gx(x,y)2+Gy(x,y)2

  常见的一阶算子包括Sobel算子、Prewitt算子、Roberts算子等。

  • 优点
    • 计算简单,速度快。
    • 能够有效检测边缘位置。
    • Sobel 算子等加入平滑操作,抗噪声能力较强。
  • 缺点
    • 对噪声较为敏感(尤其是 Prewitt 和 Roberts 算子)。
    • 边缘定位精度不高,容易丢失细节。

2.2 二阶算子

  二阶算子是一种基于图像灰度值的二阶导数的边缘检测方法,它通过计算图像灰度值的二阶导数来检测边缘,即利用像素灰度值的变化率的变化率来找到边缘位置。二阶算子通过拉普拉斯算子来计算图像的二阶导数,然后通过阈值处理来确定边缘的位置和方向。二阶算子的计算公式为:
L ( x , y ) = ∇ 2 I ( x , y ) = ∂ 2 I ( x , y ) ∂ x 2 + ∂ 2 I ( x , y ) ∂ y 2 L(x, y) = \nabla^2 I(x, y) = \frac{\partial^2 I(x, y)}{\partial x^2} + \frac{\partial^2 I(x, y)}{\partial y^2} L(x,y)=2I(x,y)=x22I(x,y)+y22I(x,y)
  其中, I ( x , y ) I(x, y) I(x,y) 表示图像的灰度值, L ( x , y ) L(x, y) L(x,y) 表示图像的拉普拉斯算子。拉普拉斯算子是一个二阶导数算子,它可以检测图像中的边缘。

  • 优点
    • 能够有效检测边缘位置。
    • 对噪声较为敏感。
    • 能够检测出图像中的细节。
  • 缺点
    • 计算复杂,速度慢。
    • 对噪声较为敏感。

  常见的边缘检测算法包括Sobel算子、Prewitt算子、Canny算子等。

3 传统算法

3.1 Sobel算子

  Sobel 算子通过计算图像在水平和垂直方向上的梯度来寻找图像中的边缘。 梯度代表了图像中亮度变化的强度和方向,边缘通常对应于梯度较大的地方。Sobel 算子使用两个 3x3 的卷积核,分别用于计算水平方向 (Gx) 和垂直方向 (Gy) 的梯度。 这两个卷积核如下:

  • 水平方向 (Gx):
[-1 0 1-2 0 2-1 0 1]
  • 垂直方向 (Gy):
[-1 -2 -10  0  01  2  1]

  Sobel算子的特点:

  • 简单易实现: Sobel 算子的计算过程相对简单,容易在各种图像处理平台上实现。
  • 考虑了像素距离的影响: Sobel 算子在计算梯度时,对中心像素周围的像素赋予了不同的权重,更靠近中心像素的权重更高,这使得 Sobel 算子对噪声具有一定的抑制作用。
  • 对噪声敏感: 虽然 Sobel 算子具有一定的抗噪能力,但仍然对噪声比较敏感。 在处理噪声较大的图像时,通常需要先进行平滑处理,例如使用高斯滤波器。
  • 能够检测水平和垂直方向的边缘: Sobel 算子能够同时检测图像中水平和垂直方向的边缘。
  • 计算量相对较小: 相对于其他一些更复杂的边缘检测算法,Sobel 算子的计算量较小,适合对实时性要求较高的应用。
import cv2
import numpy as np# 读取图像 (以灰度模式)
img = cv2.imread('source.jpg', cv2.IMREAD_GRAYSCALE)
img = cv2.resize(img, (720, 480))
cv2.imwrite('source.jpg', img)
# 使用 Sobel 算子计算梯度
sobelx = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=3) # 水平方向
sobely = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize=3) # 垂直方向# 计算梯度幅值
sobel = np.sqrt(sobelx**2 + sobely**2)
sobel = np.uint8(sobel) # 转换为 8 位无符号整数# 显示结果
cv2.imshow('Original', img)
cv2.imshow('Sobel', sobel)
cv2.imwrite('sobel.jpg', sobel)
cv2.waitKey(0)
cv2.destroyAllWindows()

3.2 Roberts 算子

  Roberts 算子是一种非常简单且古老的边缘检测算子。 它的核心思想是利用局部差分来近似图像的梯度,从而检测图像中的边缘。Roberts 算子使用两个 2x2 的卷积核,分别计算图像在对角线方向上的梯度。 这两个卷积核如下:

Gx:
[1  0;0 -1]
Gy:
[0  1;-1 0]
  • 卷积: 将图像分别与 Gx 和 Gy 卷积核进行卷积运算。 卷积运算的目的是计算图像中每个 2x2 区域在对角线方向上的梯度值。
  • 梯度幅值: 计算每个像素点的梯度幅值 (Magnitude)。 梯度幅值表示该像素点处亮度变化的强度。

  Roberts算子的特点:

  • 计算简单: Roberts 算子的计算过程非常简单,只需要几个加减运算即可。
  • 对噪声非常敏感: 由于 Roberts 算子只使用了 2x2 的卷积核,它对噪声非常敏感。 图像中的任何微小噪声都可能导致梯度值的剧烈变化,从而被误认为是边缘。
  • 定位精度较高: 由于 Roberts 算子使用了较小的卷积核,因此其边缘定位精度相对较高。
    无法检测水平和垂直方向的边缘: Roberts 算子只能检测对角线方向的边缘,无法检测水平和垂直方向的边缘。
  • 不常用: 由于其对噪声过于敏感,且无法检测所有方向的边缘,因此在实际应用中,Roberts 算子并不常用。 通常会选择更鲁棒的边缘检测算子,例如 Sobel 算子或 Canny 算法。
import numpy as np
import cv2def roberts(img):"""使用 Roberts 算子进行边缘检测。Args:img: 输入图像 (灰度图像).Returns:边缘检测后的图像."""# Roberts 算子卷积核kernelx = np.array([[1, 0], [0, -1]])kernely = np.array([[0, 1], [-1, 0]])# 获取图像尺寸height, width = img.shape# 创建输出图像roberts_img = np.zeros((height, width), dtype=np.uint8)# 遍历图像像素for x in range(height - 1):for y in range(width - 1):# 提取 2x2 区域block = img[x:x+2, y:y+2]# 计算梯度gx = np.sum(block * kernelx)gy = np.sum(block * kernely)# 计算梯度幅值magnitude = np.sqrt(gx**2 + gy**2)# 将梯度幅值赋值给输出图像roberts_img[x, y] = magnitudereturn roberts_img# 读取图像 (以灰度模式)
img = cv2.imread('imgs/source.jpg', cv2.IMREAD_GRAYSCALE)# 应用 Roberts 算子
roberts_img = roberts(img)# 显示结果
cv2.imshow('Original', img)
cv2.imshow('Roberts', roberts_img)
cv2.destroyAllWindows()
cv2.imwrite('imgs/roberts.jpg', roberts_img)

  Roberts 算子只适用于以下情况:

  • 图像噪声非常小: 如果图像噪声非常小,可以使用 Roberts 算子进行简单的边缘检测。
  • 对计算速度要求极高: 如果对计算速度要求极高,且可以容忍一定的误差,可以使用 Roberts 算子。
  • 需要检测对角线方向的边缘: 如果只需要检测对角线方向的边缘,可以使用 Roberts 算子。

3.3 Prewitt 算子

  Prewitt 算子是一种用于图像边缘检测的离散微分算子。 它和 Sobel 算子类似,都是通过计算图像在水平和垂直方向上的梯度来检测边缘。 但是,Prewitt 算子使用的卷积核与 Sobel 算子略有不同。Prewitt 算子使用两个 3x3 的卷积核,分别用于计算水平方向 (Gx) 和垂直方向 (Gy) 的梯度。 这两个卷积核如下:

Gx:
[-1 0 1;-1 0 1;-1 0 1]
Gy:
[-1 -1 -1;0  0  0;1  1  1]

  Prewitt算子特点:

  • 简单易实现: Prewitt 算子的计算过程相对简单,容易在各种图像处理平台上实现。
  • 对噪声敏感: Prewitt 算子对噪声比较敏感。 在处理噪声较大的图像时,通常需要先进行平滑处理,例如使用高斯滤波器。
  • 能够检测水平和垂直方向的边缘: Prewitt 算子能够同时检测图像中水平和垂直方向的边缘。
  • 计算量相对较小: 相对于其他一些更复杂的边缘检测算法,Prewitt 算子的计算量较小,适合对实时性要求较高的应用。
  • 权重相同: Prewitt 算子在计算梯度时,对中心像素周围的上下或左右像素赋予了相同的权重。 这与 Sobel 算子不同,Sobel 算子对更靠近中心像素的像素赋予了更高的权重。
      与 Sobel 算子的比较:Prewitt 算子和 Sobel 算子都是常用的梯度算子,它们的主要区别在于卷积核的权重分配不同。
  • Sobel 算子: 对中心像素周围的像素赋予了不同的权重,更靠近中心像素的权重更高。 这种权重分配使得 Sobel 算子对噪声的抑制能力更强。
  • Prewitt 算子: 对中心像素周围的像素赋予了相同的权重。
      一般来说,Sobel 算子的效果比 Prewitt 算子略好,因为 Sobel 算子考虑了像素距离的影响,对噪声的抑制能力更强。
import cv2
import numpy as npdef prewitt(img):"""使用 Prewitt 算子进行边缘检测。Args:img: 输入图像 (灰度图像).Returns:边缘检测后的图像."""# Prewitt 算子卷积核kernelx = np.array([[-1, 0, 1], [-1, 0, 1], [-1, 0, 1]])kernely = np.array([[-1, -1, -1], [0, 0, 0], [1, 1, 1]])# 使用 filter2D 函数进行卷积prewittx = cv2.filter2D(img, cv2.CV_64F, kernelx)prewitty = cv2.filter2D(img, cv2.CV_64F, kernely)# 计算梯度幅值prewitt = np.sqrt(prewittx**2 + prewitty**2)prewitt = np.uint8(prewitt)return prewitt# 读取图像 (以灰度模式)
img = cv2.imread('imgs/source.jpg', cv2.IMREAD_GRAYSCALE)# 应用 Prewitt 算子
prewitt_img = prewitt(img)# 显示结果
cv2.imshow('Original', img)
cv2.imshow('Prewitt', prewitt_img)
cv2.destroyAllWindows()
cv2.imwrite('imgs/prewitt.jpg', prewitt_img)

  Prewitt 算子适用于以下情况:

  • 需要快速进行边缘检测: Prewitt 算子的计算量较小,适合对实时性要求较高的应用。
  • 图像噪声较小: 如果图像噪声较小,可以使用 Prewitt 算子进行边缘检测。
  • 对边缘检测精度要求不高: 如果对边缘检测精度要求不高,可以使用 Prewitt 算子。

3.4 拉普拉斯算子

  普拉斯算子计算的是图像的二阶导数,这使得它对图像中的细节更加敏感。拉普拉斯算子计算的是图像的标量场的散度(divergence)的梯度(gradient)。 在二维图像中,拉普拉斯算子可以表示为:
∇ 2 f = ∂ 2 f / ∂ x 2 + ∂ 2 f / ∂ y 2 ∇²f = ∂²f/∂x² + ∂²f/∂y² 2f=2f/x2+2f/y2
  其中, ∇ 2 ∇² 2 是拉普拉斯算子的符号。 f ( x , y ) f(x, y) f(x,y) 是图像在 ( x , y ) (x, y) (x,y) 处的像素值。 ∂ 2 f / ∂ x 2 ∂²f/∂x² 2f/x2 f ( x , y ) f(x, y) f(x,y) x x x 的二阶偏导数。 ∂ 2 f / ∂ y 2 ∂²f/∂y² 2f/y2 f ( x , y ) f(x, y) f(x,y) y y y 的二阶偏导数。在离散图像中,拉普拉斯算子可以使用卷积核来近似。 常用的拉普拉斯算子卷积核如下:

  • 标准形式:
[0  1  0;1 -4  1;0  1  0]
  • 对角线形式:
[1  1  1;1 -8  1;1  1  1]

  拉普拉斯算子的特点:

  • 各向同性: 拉普拉斯算子是各向同性算子,即对图像旋转不敏感。 无论图像如何旋转,拉普拉斯算子都能检测到相同的边缘。
  • 对噪声敏感: 拉普拉斯算子对噪声非常敏感。 由于拉普拉斯算子计算的是二阶导数,噪声会被放大,导致检测到大量的假边缘。
  • 容易检测到双边缘: 拉普拉斯算子容易检测到双边缘。 这是因为在边缘两侧,拉普拉斯值会发生剧烈的变化,从而产生两个零交叉点。
  • 无法检测边缘方向: 拉普拉斯算子只能检测边缘的位置,无法检测边缘的方向。
  • 常用于与其他方法结合使用: 由于拉普拉斯算子对噪声敏感,且容易检测到双边缘,因此在实际应用中,通常会与其他方法结合使用,例如先进行高斯滤波。
import cv2
import numpy as np# 读取图像 (以灰度模式)
img = cv2.imread('imgs/source.jpg', cv2.IMREAD_GRAYSCALE)# 应用拉普拉斯算子
laplacian = cv2.Laplacian(img, cv2.CV_64F)# 将结果转换为 8 位无符号整数 (取绝对值)
laplacian = np.uint8(np.abs(laplacian))# 显示结果
cv2.imshow('Original', img)
cv2.imshow('Laplacian', laplacian)
cv2.destroyAllWindows()
cv2.imwrite('imgs/lap.jpg', laplacian)

  拉普拉斯算子适用于以下情况:

  • 需要检测各向同性的边缘: 如果需要检测各向同性的边缘,可以使用拉普拉斯算子。
  • 需要增强图像的细节: 拉普拉斯算子可以增强图像的细节,使其更加清晰。
  • 与其他方法结合使用: 拉普拉斯算子通常会与其他方法结合使用,例如先进行高斯滤波,然后再使用拉普拉斯算子进行边缘检测。

3.5 LoG (Laplacian of Gaussian) 算子

  由于拉普拉斯算子对噪声敏感,因此通常会先使用高斯滤波器对图像进行平滑处理,然后再使用拉普拉斯算子进行边缘检测。 这种方法称为 LoG (Laplacian of Gaussian) 算子。

import cv2
import numpy as np# 读取图像 (以灰度模式)
img = cv2.imread('imgs/source.jpg', cv2.IMREAD_GRAYSCALE)# 高斯滤波
gaussian = cv2.GaussianBlur(img, (5, 5), 0)# 应用拉普拉斯算子
laplacian = cv2.Laplacian(gaussian, cv2.CV_64F)# 将结果转换为 8 位无符号整数 (取绝对值)
laplacian = np.uint8(np.abs(laplacian))# 显示结果
cv2.imshow('Original', img)
cv2.imshow('LoG', laplacian)
cv2.destroyAllWindows()
cv2.imwrite('imgs/log.jpg', laplacian)

3.6 DoG (Difference of Gaussians) 算子

  DoG (Difference of Gaussians) 算子的核心思想是用两个不同方差(标准差)的高斯模糊图像的差分来逼近 LoG (Laplacian of Gaussian) 算子。 由于 DoG 算子在计算上比 LoG 算子更有效率,因此在许多应用中被广泛使用。DoG 算子的数学表达式如下:
D o G ( x , y , σ 1 , σ 2 ) = G ( x , y , σ 1 ) − G ( x , y , σ 2 ) DoG(x, y, σ1, σ2) = G(x, y, σ1) - G(x, y, σ2) DoG(x,y,σ1,σ2)=G(x,y,σ1)G(x,y,σ2)

  其中, D o G ( x , y , σ 1 , σ 2 ) DoG(x, y, σ1, σ2) DoG(x,y,σ1,σ2) 是DoG算子在 $(x, y) 处的输出值。 处的输出值。 处的输出值。G(x, y, σ)$是高斯模糊函数,定义为:
G ( x , y , σ ) = ( 1 / ( 2 π σ 2 ) ) ∗ e x p ( − ( x 2 + y 2 ) / ( 2 σ 2 ) ) G(x, y, σ) = (1 / (2πσ²)) * exp(-(x² + y²) / (2σ²)) G(x,y,σ)=(1/(2πσ2))exp((x2+y2)/(2σ2))

   σ 1 σ1 σ1 σ 2 σ2 σ2 是两个不同方差(标准差)的高斯模糊函数的参数。 通常, σ 2 σ2 σ2 > σ 1 σ1 σ1。DoG 算子可以用来近似 LoG 算子。 理论证明,当 σ 2 = k σ 1 σ2 = kσ1 σ2=1,且 k 接近于 1 时,DoG 算子可以很好地逼近 LoG 算子:
D o G ≈ ( k − 1 ) σ 2 ∇ 2 G DoG ≈ (k - 1)σ² ∇²G DoG(k1)σ22G
  其中, ∇ 2 G ∇²G 2G是 LoG 算子。计算过程:

  • 高斯模糊: 使用两个不同方差(σ1 和 σ2)的高斯滤波器对图像进行模糊处理,得到两个模糊图像。
  • 差分: 将两个模糊图像相减,得到 DoG 图像。
  • 零交叉点检测 (可选): 可以通过检测 DoG 图像中的零交叉点来确定边缘的位置。

  DoG算子的特点:

  • 计算效率高: DoG 算子的计算效率比 LoG 算子更高。 这是因为 DoG 算子只需要进行两次高斯模糊和一次减法运算,而 LoG 算子需要计算二阶导数。
  • 尺度空间分析: DoG 算子可以用于尺度空间分析。 通过改变高斯滤波器的方差,可以检测到不同尺度的特征。
  • 边缘检测: DoG 算子可以用于边缘检测。 DoG 图像中的零交叉点对应于图像中的边缘。
import cv2
import numpy as npdef dog(img, sigma1, sigma2):"""使用 DoG 算子进行图像增强和边缘检测。Args:img: 输入图像 (灰度图像).sigma1: 第一个高斯滤波器的标准差.sigma2: 第二个高斯滤波器的标准差.Returns:DoG 图像."""# 高斯模糊gaussian1 = cv2.GaussianBlur(img, (0, 0), sigma1)gaussian2 = cv2.GaussianBlur(img, (0, 0), sigma2)# 差分dog_img = gaussian1 - gaussian2return dog_img# 读取图像 (以灰度模式)
img = cv2.imread('imgs/source.jpg', cv2.IMREAD_GRAYSCALE)# 应用 DoG 算子
dog_img = dog(img, 1, 2)# 显示结果
cv2.imshow('Original', img)
cv2.imshow('DoG', dog_img)
cv2.imwrite('imgs/dog.jpg', dog_img)
cv2.destroyAllWindows()

  DoG 算子适用于以下情况:

  • 需要快速进行边缘检测: DoG 算子的计算效率高,适合对实时性要求较高的应用。
  • 需要进行尺度空间分析: DoG 算子可以用于尺度空间分析,检测不同尺度的特征。
  • 需要近似 LoG 算子: DoG 算子可以很好地逼近 LoG 算子。

3.7 Canny算子

  Canny 算子是一种被广泛认为是最优的边缘检测算法之一。 它由 John Canny 在 1986 年开发,旨在提供清晰、准确的边缘检测结果。 Canny 算子不仅能检测到图像中真实的边缘,还能最大限度地减少噪声的影响,并提供精确的边缘定位。
  Canny 边缘检测算法是一个多步骤的过程,主要包括以下几个步骤:

  • 高斯滤波 (Gaussian Filter):
    • 目的: 降低图像噪声的影响。 噪声会影响边缘检测的准确性,因此首先需要对图像进行平滑处理。
    • 方法: 使用高斯滤波器对图像进行卷积。 高斯滤波器是一种线性平滑滤波器,它可以有效地抑制高频噪声。
    • 参数: 高斯滤波器的标准差 (σ) 是一个重要的参数,它决定了滤波器的平滑程度。 较大的 σ 值会导致更强的平滑效果,但也可能模糊图像中的细节。
  • 计算梯度幅值和方向 (Gradient Calculation):
    • 目的: 检测图像中的边缘。 边缘通常对应于图像中亮度变化剧烈的地方,即梯度较大的地方。
    • 方法: 使用 Sobel 算子或其他梯度算子计算图像在水平和垂直方向上的梯度 (Gx 和 Gy)。 然后,计算梯度幅值 (G) 和方向 (θ):
      • 梯度幅值: G = sqrt(Gx^2 + Gy^2)
      • 梯度方向: θ = arctan(Gy / Gx)
      • 梯度方向量化: 将梯度方向量化为四个方向之一:0°, 45°, 90°, 135°。 这是为了方便后续的非极大值抑制。
  • 非极大值抑制 (Non-Maximum Suppression - NMS):
    • 目的: 细化边缘,消除梯度方向上的非最大值,只保留最强的边缘像素。
    • 方法: 遍历图像中的每个像素,如果该像素的梯度幅值在其梯度方向上不是局部最大值,则将其梯度幅值设置为 0。
    • 步骤:
      • 对于每个像素,比较其梯度幅值与其梯度方向上的两个相邻像素的梯度幅值。
      • 如果该像素的梯度幅值不是局部最大值,则将其梯度幅值设置为 0。
  • 双阈值处理 (Double Threshold):
    • 目的: 将像素分为三类:强边缘像素、弱边缘像素和非边缘像素。
    • 方法: 使用两个阈值:高阈值 (highThreshold) 和低阈值 (lowThreshold)。
      • 如果像素的梯度幅值大于高阈值,则将其标记为强边缘像素。
      • 如果像素的梯度幅值小于低阈值,则将其标记为非边缘像素。
      • 如果像素的梯度幅值介于高阈值和低阈值之间,则将其标记为弱边缘像素。
  • 边缘连接 (Edge Tracking by Hysteresis):
    • 目的: 连接弱边缘像素,形成完整的边缘。
    • 方法: 遍历图像中的每个弱边缘像素。 如果该弱边缘像素的周围 8 个像素中存在强边缘像素,则将其标记为边缘像素。 否则,将其标记为非边缘像素。
    • 滞后阈值处理: 这种边缘连接方法称为滞后阈值处理,它可以有效地连接边缘,并减少噪声的影响。

   Canny 算子的特点包括:

  • 优秀的边缘检测效果: Canny 算子能够检测到清晰、准确的边缘。
  • 抗噪能力强: Canny 算子首先使用高斯滤波器降低噪声,然后使用非极大值抑制和双阈值处理进一步抑制噪声。
  • 精确的边缘定位: Canny 算子使用非极大值抑制细化边缘,从而提供精确的边缘定位。
  • 可调节的参数: Canny 算子有多个可调节的参数,例如高斯滤波器的标准差、高阈值和低阈值。 可以根据具体的应用场景调整这些参数,以获得最佳的边缘检测效果。
  • 计算复杂度较高: Canny 算子的计算复杂度相对较高,但由于其优秀的边缘检测效果,仍然被广泛应用于各种图像处理和计算机视觉应用中。
import cv2
import numpy as np# 读取图像 (以灰度模式)
img = cv2.imread('imgs/source.jpg', cv2.IMREAD_GRAYSCALE)# 应用 Canny 算子
edges = cv2.Canny(img, 100, 200)# 显示结果
cv2.imshow('Original', img)
cv2.imshow('Canny', edges)
cv2.destroyAllWindows()
cv2.imwrite('imgs/canny.jpg', edges)

  Canny 算子适用于各种需要高质量边缘检测的场景,例如:

  • 图像分割: 通过检测图像中的边缘,可以将图像分割成不同的区域。
  • 目标检测: 边缘可以作为图像的特征,用于目标检测。
  • 图像识别: 边缘可以作为图像的特征,用于图像识别。
  • 医学图像分析: Canny 算子可以用于医学图像的边缘检测,例如肿瘤的边缘检测。
  • 自动驾驶: Canny 算子可以用于自动驾驶系统中,检测道路和车辆的边缘。

3.8 罗盘算子

  罗盘算子 (Compass Operator)通过使用一组预定义的卷积核来检测特定方向上的边缘。 与 Sobel 或 Prewitt 等算子不同,罗盘算子不是简单地计算水平和垂直梯度,而是计算多个方向上的梯度响应,从而能够更精确地检测具有特定方向的边缘。罗盘算子的核心思想是使用一组卷积核,每个卷积核对应一个特定的方向(例如,北、东北、东、东南等)。 将图像与每个卷积核进行卷积,得到该方向上的梯度响应。 然后,选择具有最大梯度响应的方向作为该像素点的边缘方向。

  常见的罗盘算子:

  • Kirsch 算子: 使用 8 个卷积核,分别对应 8 个方向(0°, 45°, 90°, 135°, 180°, 225°, 270°, 315°)。
  • Robinson 算子: 类似于 Kirsch 算子,也使用 8 个卷积核,但卷积核的系数略有不同。

  Kirsch 算子使用 8 个 3x3 的卷积核,每个卷积核对应一个方向。 这些卷积核如下:

N (北):
[-3 -3  5;-3  0  5;-3 -3  5]NE (东北):
[-3  5  5;-3  0  5;-3 -3 -3]E (东):
[ 5  5  5;-3  0 -3;-3 -3 -3]SE (东南):
[ 5  5 -3;5  0 -3;-3 -3 -3]S (南):
[ 5 -3 -3;5  0 -3;5 -3 -3]SW (西南):
[-3 -3 -3;5  0 -3;5  5 -3]W (西):
[-3 -3 -3;-3  0  5;-3  5  5]NW (西北):
[-3 -3 -3;-3  0 -3;5  5  5]

  计算过程:

  • 卷积: 将图像与每个卷积核进行卷积运算,得到 8 个方向上的梯度响应。
  • 最大响应: 对于每个像素点,选择 8 个梯度响应中的最大值作为该像素点的梯度幅值。
  • 边缘方向: 记录具有最大梯度响应的卷积核对应的方向作为该像素点的边缘方向。

  罗盘算子的特点:

  • 能够检测特定方向的边缘: 罗盘算子能够检测具有特定方向的边缘,这使得它在某些应用中比其他边缘检测算子更有效。
  • 对噪声敏感: 类似于其他梯度算子,罗盘算子也对噪声比较敏感。
  • 计算量较大: 由于需要进行多次卷积运算,罗盘算子的计算量相对较大。
import cv2
import numpy as npdef kirsch(img):"""使用 Kirsch 算子进行边缘检测。Args:img: 输入图像 (灰度图像).Returns:边缘检测后的图像和边缘方向图像."""# Kirsch 算子卷积核kernels = [np.array([[-3, -3, 5], [-3, 0, 5], [-3, -3, 5]]),  # Nnp.array([[-3, 5, 5], [-3, 0, 5], [-3, -3, -3]]),  # NEnp.array([[5, 5, 5], [-3, 0, -3], [-3, -3, -3]]),  # Enp.array([[5, 5, -3], [5, 0, -3], [-3, -3, -3]]),  # SEnp.array([[5, -3, -3], [5, 0, -3], [5, -3, -3]]),  # Snp.array([[-3, -3, -3], [5, 0, -3], [5, 5, -3]]),  # SWnp.array([[-3, -3, -3], [-3, 0, 5], [-3, 5, 5]]),  # Wnp.array([[-3, -3, -3], [-3, 0, -3], [5, 5, 5]])   # NW]# 获取图像尺寸height, width = img.shape# 创建输出图像magnitude = np.zeros((height, width), dtype=np.uint8)direction = np.zeros((height, width), dtype=np.uint8)# 遍历图像像素for x in range(1, height - 1):for y in range(1, width - 1):# 提取 3x3 区域block = img[x-1:x+2, y-1:y+2]# 计算每个方向上的梯度响应responses = [np.sum(block * kernel) for kernel in kernels]# 找到最大响应和对应的方向max_response = np.max(responses)max_index = np.argmax(responses)# 赋值给输出图像magnitude[x, y] = max_responsedirection[x, y] = max_index * 45  # 将索引转换为角度return magnitude, direction# 读取图像 (以灰度模式)
img = cv2.imread('imgs/source.jpg', cv2.IMREAD_GRAYSCALE)# 应用 Kirsch 算子
magnitude, direction = kirsch(img)# 显示结果
cv2.imshow('Original', img)
cv2.imshow('Kirsch Magnitude', magnitude)
cv2.imshow('Kirsch Direction', direction)
cv2.imwrite('imgs/kirsch_magnitude.jpg', magnitude)
cv2.destroyAllWindows()

  罗盘算子适用于以下情况:

  • 需要检测特定方向的边缘: 例如,在检测建筑物或道路的边缘时,可以使用罗盘算子来检测特定方向的直线。
  • 需要获取边缘方向信息: 罗盘算子可以提供边缘的方向信息,这对于某些应用非常有用。

4 深度学习边缘检测

  除了传统的边缘检测算子,深度学习也可以用于边缘检测。 深度学习方法通常使用卷积神经网络 (CNN) 来学习图像中的边缘特征。 这些 CNN 可以通过大量的训练数据来学习图像中的边缘特征。 然后,这些 CNN 可以用于边缘检测。相较于传统的基于梯度或手工设计的算子,深度学习方法能够学习更复杂的图像特征,从而实现更准确、更鲁棒的边缘检测。

  深度学习边缘检测的步骤:

  • 数据准备: 收集大量的图像数据,并将其标记为边缘或非边缘。
  • 模型训练: 使用 CNN 来训练模型。 通常,CNN 由多个卷积层、池化层和全连接层组成。 这些层可以学习图像中的边缘特征。
  • 边缘检测: 使用训练好的模型来检测图像中的边缘。

  深度学习边缘检测的优点:

  • 能够学习更复杂的图像特征: 深度学习方法可以学习更复杂的图像特征,从而实现更准确的边缘检测。
  • 能够处理复杂的图像: 深度学习方法可以处理复杂的图像,例如具有复杂背景的图像。

  深度学习边缘检测的缺点:

  • 计算量较大: 深度学习方法需要大量的计算资源,尤其是在处理大规模图像时。
  • 训练数据量较大: 深度学习方法需要大量的训练数据,这需要大量的时间和计算资源。

  深度学习边缘检测算法比较多,常见的有U-Net,Faster R-CNN,YOLO等。这里只简单较少其中的HED。HED 的核心思想是利用深度卷积神经网络学习图像的多尺度特征表示,并在网络的多个层次上进行边缘预测。 通过将这些不同尺度的预测结果融合起来,HED 能够有效地利用图像的上下文信息,从而提高边缘检测的准确性。HED 的网络结构基于 VGG16 网络,并进行了一些修改,使其更适合边缘检测任务。 主要的修改包括:在 VGG16 网络的每个卷积块 (convolutional block) 之后添加一个侧边输出 (side output)。 每个侧边输出都连接到一个卷积层,用于预测该尺度下的边缘图。将所有侧边输出的预测结果融合起来,得到最终的边缘图。
  HED 使用加权交叉熵损失函数 (weighted cross-entropy loss) 来训练网络。 加权交叉熵损失函数可以有效地解决类别不平衡问题,即边缘像素数量远小于非边缘像素数量。损失函数定义如下:
L o s s = Σ ( β ∗ y i ∗ l o g ( p i ) + ( 1 − β ) ∗ ( 1 − y i ) ∗ l o g ( 1 − p i ) ) Loss = Σ (β * y_i * log(p_i) + (1 - β) * (1 - y_i) * log(1 - p_i)) Loss=Σ(βyilog(pi)+(1β)(1yi)log(1pi))
其中:

  • y i y_i yi 是像素 i 的真实标签 (0 或 1)。
  • p i p_i pi 是网络预测的像素 i 为边缘的概率。
  • β β β 是一个权重,用于平衡边缘像素和非边缘像素的损失。 通常,β 设置为边缘像素数量与非边缘像素数量的比值。
      HED 的总损失函数是所有侧边输出层损失函数和融合层损失函数的加权和:
    T o t a l L o s s = Σ w m ∗ L o s s m + w f u s e ∗ L o s s f u s e Total Loss = Σ w_m * Loss_m + w_fuse * Loss_fuse TotalLoss=ΣwmLossm+wfuseLossfuse
    其中:
  • L o s s m Loss_m Lossm 是第 m 个侧边输出层的损失函数。
  • L o s s f u s e Loss_fuse Lossfuse 是融合层的损失函数。
  • w m w_m wm w f u s e w_fuse wfuse 是权重,用于平衡不同层次的损失。

5 参考文献

  • 边缘检测
  • HED
  • Github-HED
  • 基于深度学习的图像边缘和轮廓提取方法介绍

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/903511.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

c语言的常用的预处理指令和条件编译

c语言的常用的预处理指令和条件编译 预处理详解预定义符号#define#define 定义标识符#define 定义宏带副作用的宏参数宏和函数的对比#define命名约定和#undef移除宏 # 和 ## 参数插入字符串字符串的自动连接#宏参数 命令行定义条件编译#if和#endif多分支条件编译#if、#elif、#e…

TTL、RS-232 和 RS-485 串行通信电平标准区别解析

TTL、RS-232 和 RS-485 是三种常见的串行通信电平标准,它们各自有不同的协议特点,适用于不同的应用场景。以下是它们的主要特点对比: ​​1. TTL(Transistor-Transistor Logic)​​ ​​主要特点​​ ​​单端信号​…

SwinTransformer改进(6):与Dual Cross-Attention结合的视觉模型

在计算机视觉领域,Transformer架构正逐渐取代传统的CNN成为主流。 本文将深入解析一个结合了Swin Transformer和Dual Cross-Attention(DCA)的创新模型实现。 模型概述 这个实现的核心是将Swin Transformer(一种高效的视觉Transformer)与创新的Dual Cross-Attention模块相结…

Dify框架面试内容整理-Dify框架

什么是Dify框架? Dify框架是一个开源的AI应用开发平台,专注于帮助开发者和非技术人员快速构建、部署和管理基于大语言模型(如GPT系列、国产开源模型)的应用。 Dify框架的特点:

道可云人工智能每日资讯|“人工智能科技体验展”在中国科学技术馆举行

道可云元宇宙每日简报(2025年4月28日)讯,今日元宇宙新鲜事有: 《2025年提升全民数字素养与技能工作要点》发布 近日,中央网信办、教育部、工业和信息化部、人力资源社会保障部联合印发《2025年提升全民数字素养与技能…

基于javaweb的SpringBoot新闻发布系统设计与实现(源码+文档+部署讲解)

技术范围:SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容:免费功能设计、开题报告、任务书、中期检查PPT、系统功能实现、代码编写、论文编写和辅导、论文…

苍穹外卖心得体会

1 登录认证 技术点:JWT令牌技术(JSON Web Token) JWT(JSON Web Token)是一种令牌技术,主要由三部分组成:Header头部、Payload载荷和Signature签名。Header头部存储令牌的类型(如JW…

车载功能测试-车载域控/BCM控制器测试用例开发流程【用例导出方法+优先级划分原则】

目录 1 摘要2 位置灯手动控制简述2.1 位置灯手动控制需求简述2.2 位置灯手动控制逻辑交互图 3 用例导出方法以及优先级原则3.1 用例导出方法3.1.1 用例导出方法介绍3.1.2 用例导出方法关键差异分析 3.2 优先级规则3.2.1 优先级划分的核心原则3.2.2 具体等级定义与判定标准 3.3 …

Linux系统基础:基础指令简介(网络概念部分)

简介:Linux 是一种开源的类 Unix 操作系统内核,由 Linus Torvalds 于 1991 年首次发布。经过多年发展,它已成为服务器、嵌入式设备和个人计算机领域的重要操作系统。 网络基础概念 初始协议 简单来说,协议是一种约定&#xff0…

多模态(3):实战 GPT-4o 视频理解

最近,OpenAI 团队的 GPT-4o 模型,在多模态方面的能力有了大幅提升,这次我们就使用 GPT-4o 完成一个视频理解的实战。 1. 环境搭建 1.1 安装 FFmpeg 做视频处理,我们需要用到 FFmpeg 这款功能强大的开源多媒体处理工具。FFmpeg…

(27)VTK C++开发示例 ---将点坐标写入 STL文件

文章目录 1. 概述2. CMake链接VTK3. main.cpp文件4. 演示效果 更多精彩内容👉内容导航 👈👉VTK开发 👈 1. 概述 此示例使用 vtkSTLWriter 将存储在 vtkPolyData 对象中的 3D 几何数据保存到 STL 文件,并读取stl文件显示…

2. python协程/异步编程详解

目录 1. 简单的异步程序 2. 协程函数和协程对象 3. 事件循环 4. 任务对象Task及Future对象 4.1 Task与Future的关系 4.2 Future对象 4.3 全局对象和循环事件对象 5. await关键字 6. 异步上下文管理 7.异步迭代器 8. asyncio的常用函数 8.1 asyncio.run 8.2 asyncio.get…

智慧园区IOT项目与AI时代下的机遇 - Java架构师面试实战

在互联网大厂的Java求职者面试中,面试官通常会针对实际业务场景提出一系列问题。以下是关于智慧园区IOT项目及AI时代下的机遇的面试模拟对话。 第一轮提问 面试官:马架构,请简要介绍下智慧园区IOT项目的整体架构设计。 马架构:…

论文导读 - 基于特征融合的电子鼻多任务深度学习模型研究

基于特征融合的电子鼻多任务深度学习模型研究 原论文地址:https://www.sciencedirect.com/science/article/pii/S0925400524009365 引用此论文(GB/T 7714-2015): NI W, WANG T, WU Y, et al. Multi-task deep learning model f…

AI超级智能体项目教程(二)---后端项目初始化(设计knif4j接口文档的使用)

文章目录 1.选择JDK的版本和相关配置2.添加依赖信息2.1指定lombok版本信息2.2引入hutool工具类2.3了解knif4j依赖2.4引入knif4j依赖 3.contrller测试3.1完成yml文件配置3.2修改默认扫描路径3.3controller具体的内容3.4配置接口和访问路径3.5如何访问3.6调试接口3.6调试接口 1.选…

linux blueZ 第四篇:BLE GATT 编程与自动化——Python 与 C/C++ 实战

本篇聚焦 BLE(Bluetooth Low Energy)GATT 协议层的编程与自动化实践,涵盖 GATT 基础、DBus API 原理、Python(dbus-next/bleak)示例、C/C++ (BlueZ GATT API)示例,以及自动发现、读写特征、订阅通知、安全配对与脚本化测试。 目录 BLE GATT 基础概念 BlueZ DBus GATT 模…

kafka与flume的整合、spark-streaming

kafka与flume的整合 前期配置完毕,开启集群 需求1: 利用flume监控某目录中新生成的文件,将监控到的变更数据发送给kafka,kafka将收到的数据打印到控制台(三个node01中运行) 1.在kafka中建立topic kafka…

redis高级进阶

1.redis主从复制 redis主从复制1 2.redis哨兵模式 哔哩哔哩视频 redis哨兵模式1 redis哨兵模式2 redis哨兵模式3 3.redis分片集群 redis分片集群1 redis分片集群2 redis分片集群3

uniapp: 低功耗蓝牙(BLE)的使用

在微信小程序中实现蓝牙对接蓝牙秤的重量功能,主要依赖微信小程序提供的低功耗蓝牙(BLE)API。以下是一个清晰的步骤指南,帮助你完成从连接蓝牙秤到获取重量数据的开发流程。需要注意的是,具体实现可能因蓝牙秤的协议和…

3D架构图软件 iCraft Editor 正式发布 @icraft/player-react 前端组件, 轻松嵌入3D架构图到您的项目

安装 pnpm install icraft/player-react --saveimport { ICraftPlayer } from "icraft/player-react";export default function MyScene() {return <ICraftPlayer srcyour-scene.iplayer />; }icraft/player-react 为开发者提供了一站式的3D数字孪生可视化解决…