在机器视觉和图像处理领域,边缘检测(Edge Detection)是最基础、最重要的图像分析技术之一。无论是目标检测、轮廓提取、尺寸测量还是缺陷检测,很多算法都会先进行边缘检测,以突出图像中的结构信息。

本文将介绍 OpenCV 中最经典的 Canny 边缘检测算法,并结合 Python 给出完整的代码示例。

一、什么是边缘检测?

边缘是指图像中灰度值发生明显变化的位置。

例如,一张黑底白色矩形图片:

□□□□□□□□□□□□
□□■■■■■■■■□□
□□■■■■■■■■□□
□□■■■■■■■■□□
□□□□□□□□□□□□

经过边缘检测后,只会保留矩形边界:

□□□□□□□□□□□□
□□████████□□
□□█□□□□□□█□□
□□████████□□
□□□□□□□□□□□□

这样可以大幅减少数据量,同时保留目标最重要的几何特征。

二、为什么需要边缘检测?

边缘检测能够突出目标轮廓,在工业视觉中具有重要作用,例如:

  • 产品外形检测
  • 标签定位
  • PCB 元器件检测
  • 缺陷检测
  • 尺寸测量
  • OCR 字符定位
  • 轮廓分析

很多视觉算法都是在边缘检测的基础上继续完成后续分析。

三、Canny 边缘检测原理

OpenCV 中最常使用的是 Canny 边缘检测算法。

整个算法主要包括以下几个步骤:

1、图像去噪

首先使用高斯滤波去除噪声。

原图
 ↓
Gaussian Blur
 ↓
平滑图像

因为噪声容易被误认为边缘。

2、计算图像梯度

利用 Sobel 算子计算:

  • X方向梯度
  • Y方向梯度

得到每个像素的梯度大小。

梯度越大,说明灰度变化越剧烈。

3、非极大值抑制

保留真正的边缘。

将较宽的边缘压缩成只有一个像素宽。

这样得到更加清晰的边缘。

4、双阈值检测

设置两个阈值:

  • 高阈值
  • 低阈值

梯度:

  • 大于高阈值 → 一定是边缘
  • 小于低阈值 → 一定不是边缘
  • 中间区域 → 根据连通性判断

这种方式能够有效降低噪声干扰。

四、OpenCV 边缘检测函数

OpenCV 提供:

edges = cv2.Canny(
    image,
    threshold1,
    threshold2
)

参数说明:

参数 说明
image 输入灰度图
threshold1 低阈值
threshold2 高阈值

返回结果:

一张二值图像:

  • 白色(255)表示边缘
  • 黑色(0)表示背景

五、基础代码示例

import cv2

# 读取图片
img = cv2.imread("test.png")

# 转为灰度图
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# Canny 边缘检测
edges = cv2.Canny(gray, 50, 150)

cv2.imshow("Original", img)
cv2.imshow("Edges", edges)

cv2.waitKey(0)
cv2.destroyAllWindows()

运行结果:

原图:

████████
██目标██
████████

边缘图:

□□□□□□□□
□□████□□
□□█□□█□□
□□████□□
□□□□□□□□

六、阈值如何选择?

Canny 的效果主要由两个阈值决定。

例如:

edges = cv2.Canny(gray, 30, 80)

特点:

  • 检测边缘较多
  • 容易出现噪声
edges = cv2.Canny(gray, 100, 200)

特点:

  • 边缘较少
  • 更稳定
  • 噪声更少

一般建议:

低阈值 = 高阈值 × 0.4~0.5

例如:

edges = cv2.Canny(gray, 50, 120)

七、边缘检测前进行高斯滤波

工业视觉中,通常不会直接进行 Canny。

而是先去噪:

blur = cv2.GaussianBlur(
    gray,
    (5,5),
    0
)

edges = cv2.Canny(
    blur,
    50,
    150
)

这样能够减少大量噪声边缘。

八、寻找边缘轮廓

边缘检测完成后,可以直接寻找轮廓。

contours, _ = cv2.findContours(
    edges,
    cv2.RETR_EXTERNAL,
    cv2.CHAIN_APPROX_SIMPLE
)

draw = img.copy()

cv2.drawContours(
    draw,
    contours,
    -1,
    (0,255,0),
    2
)

这样即可得到所有目标轮廓。

九、完整实战案例

下面给出一个完整流程。

import cv2

def detect_edge(image_path):

    img = cv2.imread(image_path)

    if img is None:
        print("读取图片失败")
        return

    # 灰度化
    gray = cv2.cvtColor(
        img,
        cv2.COLOR_BGR2GRAY
    )

    # 高斯滤波
    blur = cv2.GaussianBlur(
        gray,
        (5,5),
        0
    )

    # 边缘检测
    edges = cv2.Canny(
        blur,
        50,
        150
    )

    # 查找轮廓
    contours, _ = cv2.findContours(
        edges,
        cv2.RETR_EXTERNAL,
        cv2.CHAIN_APPROX_SIMPLE
    )

    result = img.copy()

    for cnt in contours:

        area = cv2.contourArea(cnt)

        if area < 300:
            continue

        x, y, w, h = cv2.boundingRect(cnt)

        cv2.rectangle(
            result,
            (x, y),
            (x + w, y + h),
            (0,255,0),
            2
        )

    cv2.imshow("Edges", edges)
    cv2.imshow("Result", result)

    cv2.waitKey(0)
    cv2.destroyAllWindows()

if __name__ == "__main__":
    detect_edge("test.png")

十、工业视觉中的应用

边缘检测在工业视觉中的应用十分广泛,例如:

  • 标签边缘定位
  • 产品轮廓提取
  • 零件尺寸测量
  • PCB 元件检测
  • 焊点检测
  • OCR 字符定位
  • 模板匹配前预处理
  • 缺陷检测

通常,边缘检测会与轮廓分析、霍夫变换、模板匹配等算法结合使用,以实现更加稳定的检测效果。

十一、边缘检测的优缺点

优点:

  • 算法速度快
  • 实现简单
  • 定位精度高
  • 能有效提取目标轮廓
  • 广泛应用于工业视觉

缺点:

  • 对噪声较敏感,需要配合滤波使用
  • 阈值需要根据场景调整
  • 光照变化较大时,检测效果可能下降
  • 无法直接识别目标类别,需要结合其他算法进一步分析

十二、总结

Canny 边缘检测是 OpenCV 中最经典、最常用的边缘检测算法之一,也是工业视觉项目中不可或缺的预处理步骤。通过计算图像梯度、抑制非极大值以及双阈值筛选,能够快速提取出清晰的目标轮廓。

在实际项目中,边缘检测通常不会单独使用,而是与高斯滤波、阈值分割、轮廓检测、模板匹配等算法结合,构建完整的视觉检测流程。熟练掌握 Canny 算法及其参数调节,对于提高工业视觉检测系统的稳定性和准确性具有重要意义。

Logo

CANN开发者社区旨在汇聚广大开发者,围绕CANN架构重构、算子开发、部署应用优化等核心方向,展开深度交流与思想碰撞,携手共同促进CANN开放生态突破!

更多推荐