在一切开始之前,请允许我先隆重介绍一下在这篇文章里面用来做实验的图片,它也是我博客主页的封面:

新加坡-天际线
新加坡-天际线

“暮色漫染新加坡的天际线,金融区的钢铁丛林在余晖里苏醒。玻璃幕墙裁碎夕阳,将金芒洒向睡莲摇曳的镜面。建筑如未来棱晶,莲花池接住整座城的倒影 —— 这是钢铁与柔波的和鸣,都市脉搏在水面轻颤,每片莲叶都托着金融中心的璀璨,每缕暮光都织就花园城市的浪漫,滨海湾把 “硬核” 与 “诗意”,熬成一帧流动的、属于狮城的梦。”

Sobel算法:基于一阶导数的边缘检测

(一)算法背景

图像中的边缘通常对应着像素亮度的显著变化。Sobel算法是一种经典且高效的边缘检测方法,通过计算图像在水平和垂直方向上的梯度(一阶导数)来定位边缘。

(二)算法原理

Sobel算法的核心是使用两个特定的3x3卷积核,分别计算图像在水平(x)和垂直(y)方向上的近似梯度:

  • 水平方向卷积核 (检测垂直边缘):
    [
    G_x =
    \begin{bmatrix}
    -1 & 0 & 1 \
    -2 & 0 & 2 \
    -1 & 0 & 1 \
    \end{bmatrix}
    ]
  • 垂直方向卷积核 (检测水平边缘):
    [
    G_y =
    \begin{bmatrix}
    -1 & -2 & -1 \
    0 & 0 & 0 \
    1 & 2 & 1 \
    \end{bmatrix}
    ]

对图像中的每个像素,将其邻域像素值分别与这两个核进行卷积运算,得到梯度分量 (G_x) 和 (G_y)。

(三)边缘强度与方向

  • 边缘强度 (梯度幅值):综合两个方向的梯度计算每个像素点的边缘强度:
    [
    G = \sqrt{G_x^2 + G_y^2}
    ]
    为简化计算,实践中常用近似公式:
    [
    G \approx |G_x| + |G_y|
    ]
    值 (G) 越大,该点属于边缘的可能性越高。
  • 边缘方向:梯度方向指示了边缘的法线方向(垂直于边缘走向):
    [
    \theta = \arctan\left(\frac{G_y}{G_x}\right)
    ]
    (\theta) 的范围通常在 (-90^\circ) 到 (90^\circ) 之间。

Sobel函数:OpenCV实现

OpenCV提供了 cv2.Sobel() 函数方便地实现Sobel边缘检测。

(一)函数原型

cv2.Sobel(src, ddepth, dx, dy, dst=None, ksize=None, scale=None, delta=None, borderType=None)
  • src: 输入图像(通常为灰度图)。
  • ddepth: 输出图像的深度(常用 cv2.CV_64Fcv2.CV_8U)。
  • dx: x方向(水平)导数的阶数(通常设为1)。
  • dy: y方向(垂直)导数的阶数(通常设为1)。
  • dst: 输出图像(可选)。
  • ksize: Sobel核大小(常用3)。
  • scale: 缩放导数值(可选)。
  • delta: 导数值偏移量(可选)。
  • borderType: 边界填充方式(默认为 cv2.BORDER_DEFAULT)。

(二)使用示例

# Scharr()

import cv2 as cv

src = cv.imread("src.jpg")

# 对图像进行高斯模糊处理,以减少噪声对边缘检测的影响
src = cv.GaussianBlur(src, (3, 3), 0)

# 将图像从 BGR 格式转换为灰度格式
gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)

# 使用 Scharr 算法计算图像在水平方向的梯度
# cv.Scharr() 函数用于计算图像的梯度,比 Sobel 算法精度更高
# 参数 gray 表示输入图像,-1 表示输出图像深度与输入图像相同
# 参数 1 和 0 分别表示水平方向的导数阶数和垂直方向的导数阶数
gradx = cv.Scharr(gray, -1, 1, 0)

# 使用 Scharr 算法计算图像在垂直方向的梯度
# 参数 0 和 1 分别表示水平方向的导数阶数和垂直方向的导数阶数
grady = cv.Scharr(gray, -1, 0, 1)

# 将水平方向和垂直方向的梯度图像进行加权融合
# cv.addWeighted() 函数用于将两个图像进行加权融合
# 参数 0.5 和 0.5 分别表示两个图像的权重,最后一个参数 0 表示偏移量
grad = cv.addWeighted(gradx, 0.5, grady, 0.5, 0)

cv.imshow("Scharr_gradx.jpg", gradx)
cv.imshow("Scharr_grady.jpg", grady)
cv.imshow("Scharr_grad.jpg", grad)
cv.imwrite("Scharr_gradx.jpg", gradx)
cv.imwrite("Scharr_grady.jpg", grady)
cv.imwrite("Scharr_grad.jpg", grad)
cv.waitKey(0)
cv.destroyAllWindows()

水平方向梯度图像
水平方向梯度图像
垂直方向梯度图像
垂直方向梯度图像
融合后梯度图像
融合后梯度图像,展示了通过Scharr算子计算得到的图像梯度。

(三)特点

  • 优点:实现简单、计算效率高,能有效检测水平和垂直边缘。
  • 缺点:梯度计算是近似值,精度相对较低;对噪声有一定敏感性;只能检测特定方向的边缘。

Scharr算法:Sobel的优化版本

(一)算法背景

Sobel算法在检测非水平和垂直方向边缘时精度有所下降。Scharr算法通过优化卷积核设计,提供了更高的旋转对称性和梯度计算精度。

(二)算法原理

Scharr算法结构与Sobel相同,但使用了不同的卷积核系数,能更精确地近似图像梯度:

  • 水平方向卷积核:
    [
    G_x =
    \begin{bmatrix}
    -3 & 0 & 3 \
    -10 & 0 & 10 \
    -3 & 0 & 3 \
    \end{bmatrix}
    ]
  • 垂直方向卷积核:
    [
    G_y =
    \begin{bmatrix}
    -3 & -10 & -3 \
    0 & 0 & 0 \
    3 & 10 & 3 \
    \end{bmatrix}
    ]

边缘强度 ((G = \sqrt{G_x^2 + G_y^2}) 或 (G \approx |G_x| + |G_y|)) 和方向 ((\theta = \arctan(G_y / G_x))) 的计算方式与Sobel一致。

(三)算法优势

相比Sobel算子,Scharr算子在相同大小的核(3x3)下:

  • 精度更高:对边缘方向的响应更准确。
  • 旋转对称性更好:检测不同角度边缘的性能更均衡。
  • 抗噪性略优:系数设计有助于在保持精度的同时略微抑制噪声影响。

Scharr函数:OpenCV实现

OpenCV提供了专门的 cv2.Scharr() 函数。

(一)函数原型

cv2.Scharr(src, ddepth, dx, dy, dst=None, scale=None, delta=None, borderType=None)

参数含义与 cv2.Sobel() 类似(dxdy 通常也为1):

  • src, ddepth, dx, dy, dst, scale, delta, borderType.
  • 注意:Scharr函数没有 ksize 参数,因为它固定使用优化的3x3核。

(二)使用示例

# Laplacian()

import cv2 as cv

src = cv.imread("src.jpg")

# 将图像从 BGR 格式转换为灰度格式
# cv.cvtColor() 函数用于图像颜色空间的转换
# cv.COLOR_BGR2GRAY 表示从 BGR 转换为灰度
gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)

# 使用 Laplacian 算法计算图像的拉普拉斯梯度
# cv.Laplacian() 函数用于计算图像的拉普拉斯值,即二阶导数
# 参数 gray 表示输入图像
# 参数 -1 表示输出图像的深度与输入图像相同
# 默认情况下,Laplacian 使用 3x3 的卷积核进行计算
Laplacian_grad = cv.Laplacian(gray, -1)

cv.imshow("Laplacian_grad.jpg", Laplacian_grad)
cv.imwrite("Laplacian_grad.jpg", Laplacian_grad)
cv.waitKey(0)
cv.destroyAllWindows()

水平方向梯度图像
水平方向梯度图像
垂直方向梯度图像
垂直方向梯度图像
融合后梯度图像
融合后梯度图像,展示了通过Scharr算子计算得到的图像梯度。

(三)特点

  • 优点:在3x3核下精度显著优于Sobel;计算效率与Sobel相当;API简单易用。
  • 缺点:同样对噪声敏感;核大小固定为3x3,灵活性不如可调ksize的Sobel;本质上仍是基于一阶导数的近似。

Laplacian算法:基于二阶导数的边缘检测

(一)算法背景

Sobel和Scharr基于一阶导数(梯度变化率),而Laplacian算法基于二阶导数(梯度变化率的变化率)。它在图像灰度值发生突变(如边缘)的位置会产生零交叉或极值响应。

(二)算法原理

Laplacian算法直接计算图像的拉普拉斯算子(Laplacian Operator),即二阶导数的和:
[
\Delta f = \frac{\partial^2 f}{\partial x^2} + \frac{\partial^2 f}{\partial y^2}
]
离散图像中通过卷积核实现。常用核有:

  1. 4邻域 (更常用):
    [
    \begin{bmatrix}
    0 & 1 & 0 \
    1 & -4 & 1 \
    0 & 1 & 0 \
    \end{bmatrix}
    ]
  2. 8邻域 (对对角线边缘响应更强):
    [
    \begin{bmatrix}
    1 & 1 & 1 \
    1 & -8 & 1 \
    1 & 1 & 1 \
    \end{bmatrix}
    ]

(三)边缘检测

  1. 计算拉普拉斯响应:用上述核之一对图像进行卷积,得到每个像素的拉普拉斯值 (L)。
  2. 边缘定位
    • 理论边缘点位于 (L=0) 的位置(零交叉点)。
    • 实践中常通过寻找 (L) 的绝对值较大(超过阈值)的点来检测边缘,或者结合零交叉检测方法。

(四)算法特点

  • 优点:对图像中的灰度突变(如细线、孤立点、边缘)非常敏感;能检测任意方向的边缘(各向同性);无需分别计算x/y方向。
  • 缺点:对噪声极其敏感(二阶导数放大噪声);容易产生双像素宽边缘;检测到的边缘位置可能不如一阶方法精确。

Laplacian函数:OpenCV实现

OpenCV提供了 cv2.Laplacian() 函数。

(一)函数原型

cv2.Laplacian(src, ddepth, dst=None, ksize=None, scale=None, delta=None, borderType=None)
  • src: 输入图像(通常为灰度图)。
  • ddepth: 输出图像的深度(常用 cv2.CV_64F)。
  • dst: 输出图像(可选)。
  • ksize: 计算二阶导数使用的孔径大小(核大小)。关键参数ksize=1 表示使用上述4邻域核。常用 ksize=3 (默认) 或 ksize=5 (更平滑)。ksize=1 时实际使用 ksize=3 的4邻域核。
  • scale: 缩放计算得到的拉普拉斯值(可选)。
  • delta: 偏移量(可选)。
  • borderType: 边界填充方式(默认为 cv2.BORDER_DEFAULT)。

(二)使用示例

# Laplacian()

import cv2 as cv

src = cv.imread("src.jpg")

# 将图像从 BGR 格式转换为灰度格式
# cv.cvtColor() 函数用于图像颜色空间的转换
# cv.COLOR_BGR2GRAY 表示从 BGR 转换为灰度
gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)

# 使用 Laplacian 算法计算图像的拉普拉斯梯度
# cv.Laplacian() 函数用于计算图像的拉普拉斯值,即二阶导数
# 参数 gray 表示输入图像
# 参数 -1 表示输出图像的深度与输入图像相同
# 默认情况下,Laplacian 使用 3x3 的卷积核进行计算
Laplacian_grad = cv.Laplacian(gray, -1)

cv.imshow("Laplacian_grad.jpg", Laplacian_grad)
cv.imwrite("Laplacian_grad.jpg", Laplacian_grad)
cv.waitKey(0)
cv.destroyAllWindows()

拉普拉斯梯度图像
拉普拉斯梯度图像

(三)特点

  • 优点:API简洁;能检测各方向边缘;对突变敏感。
  • 缺点:结果对噪声非常敏感;需要仔细选择ksize和后续阈值处理;输出通常需要取绝对值并转换为8位图像显示。

Sobel, Scharr, Laplacian 算法对比

下表总结了三种边缘检测算法的主要特点与差异:

特性 Sobel 算法 Scharr 算法 Laplacian 算法
计算基础 一阶导数 (近似梯度) 一阶导数 (优化近似梯度) 二阶导数 (拉普拉斯算子)
主要目的 检测边缘 (梯度幅值大的地方) 检测边缘 (梯度幅值大的地方) 检测灰度突变 (零交叉/极值点)
方向性 能计算边缘方向 (θ) 能计算边缘方向 (θ) 各向同性 (无特定方向)
精度 (3x3核) 中等 (优于Sobel) 高 (对突变敏感)
抗噪性 低 (略优于Sobel) 非常低 (噪声放大严重)
边缘响应 单边缘响应 (单峰) 单边缘响应 (单峰) 双边缘响应 (零交叉)
检测边缘类型 主要水平和垂直方向 各方向 (精度更均衡) 所有方向
对细线/点响应
计算复杂度 低 (与Sobel相当)
OpenCV函数 cv2.Sobel() cv2.Scharr() cv2.Laplacian()
主要参数 dx, dy, ksize dx, dy (核固定3x3) ksize (控制平滑与核类型)
典型应用场景 快速初步边缘检测 需要更高精度的3x3核边缘检测 检测细线、孤立点、斑点、边缘增强

选择建议:

  • 需要快速检测基本边缘且方向明确? -> Sobel (可调ksize增加平滑)。
  • 需要更精确的3x3核边缘检测? -> Scharr
  • 需要检测所有方向的突变、细线或孤立点? -> Laplacian (但务必结合平滑滤波如高斯模糊,即LoG)。
  • 实际应用中,常将Sobel/Scharr的结果(梯度幅值)与Laplacian的零交叉信息结合,或使用更高级算法(如Canny)。

至于更高级的 Canny 算法 ,我们将在下一篇单独成文介绍。