欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

OpenCV 从零学习 · 第 7 讲:边缘检测(Sobel / Laplacian / Canny,找图像的"轮廓线")

OpenCV 从零学习 · 第 7 讲:边缘检测

函数:cv2.Sobel / cv2.Laplacian / cv2.Canny | 难度:★★☆☆☆

"边缘"就是图像里亮度突然变化的地方(物体边界、纹理交界)。边缘检测把一张普通图变成一张"只有线条"的图,是轮廓查找、目标识别、车道线检测的核心前置。本讲讲两种思路:梯度类(Sobel / Laplacian)和智能双阈值类(Canny)。


一、Sobel 梯度

Sobel 用一个 3×3 卷积核算"亮度变化率"(梯度)。变化越剧烈,输出值越大——也就是边缘。

函数签名

dst = cv2.Sobel(src, ddepth, dx, dy, ksize=3, scale=1, delta=0)
参数 含义
src单通道灰度图
ddepth输出深度,算梯度用 cv2.CV_64F(见下方坑)
dx / dyx / y 方向求导阶数。dx=1,dy=0 求水平方向梯度
ksize核大小,1 / 3 / 5 / 7,默认 3
场景 A · 理解 dx / dy 的差异
dx=1,dy=0 对"左右亮度变化"敏感 → 突出垂直边缘(如柱子、门框);dx=0,dy=1 对"上下亮度变化"敏感 → 突出水平边缘(如地平线、桌沿)。两者是不同方向,不可混用。
import cv2
gray = cv2.imread('box.jpg', cv2.IMREAD_GRAYSCALE)

# 注意 ddepth=CV_64F,再转回 8 位
sobelx = cv2.Sobel(gray, cv2.CV_64F, 1, 0, ksize=3)
sobely = cv2.Sobel(gray, cv2.CV_64F, 0, 1, ksize=3)
sobelx = cv2.convertScaleAbs(sobelx)
sobely = cv2.convertScaleAbs(sobely)

# 合并:取绝对值后相加,得到"总边缘强度"
sobel_all = cv2.addWeighted(sobelx, 0.5, sobely, 0.5, 0)
⚠ 新手必踩坑(Sobel 头号坑)
梯度有正有负。若 ddepth 用默认的 cv2.CV_8U,负值会被截断成 0,边缘只显示一半(只剩一边)。正确做法:ddepth=cv2.CV_64F 算出带符号结果,再用 cv2.convertScaleAbs() 取绝对值转回 8 位显示。

二、Laplacian(二阶梯度)

Laplacian 是二阶导,对"亮度跳变"更敏感,能同时抓水平和垂直边缘,但也更怕噪声。常用于"提取所有边缘"的快速场合。

lap = cv2.Laplacian(gray, cv2.CV_64F, ksize=3)
lap = cv2.convertScaleAbs(lap)
场景 B · 噪声敏感对比
同一张带噪点的图:Laplacian 把噪点也当边缘画得密密麻麻;先 GaussianBlur 去噪再 Laplacian,边缘就干净了。结论:边缘检测前先平滑几乎总是对的。

三、Canny(最常用)

Canny 是工程里用得最多的边缘检测器:先高斯平滑去噪,再用"双阈值 + 滞后连接"只保留"真正连成线"的边缘,效果比单纯 Sobel 干净很多。

函数签名

edges = cv2.Canny(image, threshold1, threshold2, apertureSize=3, L2gradient=False)
参数 含义
image单通道灰度图(彩色会报错)
threshold1低阈值:梯度低于它的一定不是边缘
threshold2高阈值:梯度高于它的一定是边缘
apertureSizeSobel 核大小,3 / 5 / 7

双阈值怎么工作:梯度在 threshold1~threshold2 之间的像素,只有当它连着一条"高于 threshold2 的强边缘"时才保留。这样能抑制孤立噪点、保留完整轮廓线。经验比例 threshold1 : threshold2 ≈ 1 : 2 或 1 : 3

场景 C · 车道线 / 物体外轮廓
先转灰度、再轻微高斯模糊,然后 Canny(gray, 50, 150)。出来的二值"线条图"正好可作为第 8 讲轮廓查找的输入。
场景 D · 调参看差异
阈值取 (100, 200) 边缘少而干净;取 (20, 50) 边缘多但杂带噪点。阈值越低抓得越多越乱,越高越干净越少——按你的任务取平衡。
import cv2
img  = cv2.imread('scene.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (5, 5), 0)     # 先去噪
edges = cv2.Canny(blur, 50, 150)             # 输出是黑白边缘图
⚠ 新手必踩坑
  • Canny 输入必须是单通道灰度图;直接传彩色三通道会报错。
  • Canny 的 threshold1/threshold2双阈值,不是"一个阈值"——别和 threshold() 的单个 thresh 混淆。
  • 边缘检测前务必先平滑(高斯模糊),否则噪点会被当成边缘。

本讲要点

  • Sobel 求一阶梯度:ddepth=cv2.CV_64F + convertScaleAbs 才是正确姿势;dx/dy 控制方向。
  • Laplacian 对噪声更敏感,记得先平滑。
  • Canny = 平滑 + 双阈值,是工程首选;输入灰度图,阈值比约 1:2~1:3。

下一讲:第 8 讲 · 轮廓查找与绘制(findContours / drawContours)——把 Canny 的线条"连成"一个个物体,数个数、画框框。

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功