深入理解Canny 边缘检测的原理、实现与应用
多阶段最优边缘检测算法
Canny 边缘检测算法由 John F. Canny 在 1986 年提出,旨在找到一个最优的边缘检测算法。Canny 将边缘检测问题转化为一个数学问题,并提出了三个评价边缘检测算法的标准:
不漏检、不误检
边缘点接近真实中心
单边缘单响应
使用高斯滤波器平滑图像,减少噪声影响。高斯核大小通常为 5×5,标准差σ一般取 1.4。
使用 Sobel 算子计算图像在 x 和 y 方向的梯度,然后计算梯度幅值和方向。
| Sobel X 核 | Sobel Y 核 |
|---|---|
|
|
梯度幅值: G = sqrt(Gx2 + Gy2)
| 步骤 | 输入 | 输出 | 目的 |
|---|---|---|---|
| 高斯滤波 | 原始图像 | 平滑图像 | 去除噪声 |
| 计算梯度 | 平滑图像 | 梯度图 | 找到边缘候选 |
| 非极大值抑制 | 梯度图 | 细化梯度 | 单像素边缘 |
| 双阈值 | 细化梯度 | 边缘点 | 连接真实边缘 |
梯度方向: theta = arctan(Gy/Gx)
沿梯度方向检查像素点,只保留梯度方向上的局部最大值,细化边缘到单像素宽度。
使用两个阈值区分强边缘、弱边缘和非边缘:
检查弱边缘像素的 8 邻域,如果存在强边缘像素,则将该弱边缘提升为强边缘。
| 参数 | 说明 | 典型值 | 影响 |
|---|---|---|---|
| 低阈值 | 弱边缘阈值 | 50-100 | 影响边缘连续性 |
| 高阈值 | 强边缘阈值 | 100-200 | 影响边缘准确性 |
| 高斯核大小 | 去噪程度 | 3 或 5 | 越大去噪越强但细节丢失 |
| 梯度方向 | 边缘法线方向 | 0°,45°,90°,135° | 用于非极大值抑制 |
import cv2
import numpy as np
import matplotlib.pyplot as plt
def canny_edge_detection(image_path, low_threshold=50, high_threshold=150):
"""
使用 OpenCV 实现 Canny 边缘检测
:param image_path: 输入图像路径
:param low_threshold: 低阈值
:param high_threshold: 高阈值
:return: 原图和边缘检测结果
"""
# 读取图像
img = cv2.imread(image_path)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 应用 Canny 边缘检测
edges = cv2.Canny(gray, low_threshold, high_threshold)
return img, edges
# 使用示例
if __name__ == "__main__":
img, edges = canny_edge_detection('image.jpg', 50, 150)
plt.figure(figsize=(12, 6))
plt.subplot(1, 2, 1)
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
plt.title('Original Image')
plt.subplot(1, 2, 2)
plt.imshow(edges, cmap='gray')
plt.title('Canny Edges')
plt.show()
Canny 边缘检测有两个重要阈值参数:
| 参数 | 作用 | 调节建议 | 效果 |
|---|---|---|---|
| 低阈值 TL | 低于此值的边缘被忽略 | 设为高阈值的 1/2 到 1/3 | 越低,检测到的边缘越多 |
| 高阈值 TH | 高于此值的边缘被接受 | 根据图像梯度分布调整 | 越高,检测到的边缘越少但更可靠 |
| 高斯σ | 控制平滑程度 | 通常取 1.0-2.0 | 越大,去噪越好但边缘越模糊 |
提取物体轮廓用于识别
作为分割的预处理步骤
器官边界提取
缺陷检测和尺寸测量
特征提取预处理
SLAM 等应用的基础