目录
前言
8.1 本章学习目标
8.2 前置理论
8.2.1 什么是边缘
8.2.2 为什么要先滤波
8.2.3 基础公共代码
原图
灰度图
8.3 Sobel 算子
8.4 Scharr 算子---Sobel 增强版
8.5 Laplacian 拉普拉斯算子
8.6 Canny 边缘检测 ---重要
8.7 四种边缘检测算子对比表
8.8 新手高频坑点
8.9 本章小结
前言
承接第 7 章图像滤波。边缘就是图像像素发生剧烈突变的位置,代表物体轮廓边界。边缘检测可以把物体轮廓提取出来,是图像分割、目标检测的前置步骤。
8.1 本章学习目标
- 理解边缘检测底层原理:图像梯度
- 掌握 Sobel 算子、Scharr 算子、Laplacian 拉普拉斯算子
- 重点掌握 Canny 边缘检测
- 理解阈值、高斯降噪对边缘检测的影响
- 能够对比不同算子的输出效果
8.2 前置理论
8.2.1 什么是边缘
图像中像素灰度值快速跳变的地方就是边缘。比如物体和背景交界,灰度从 0 直接跳到 255。
数学上用梯度描述灰度变化快慢:梯度越大,灰度变化越剧烈,越有可能是边缘。
8.2.2 为什么要先滤波
图像噪声会产生很多虚假边缘。边缘检测之前一般先做高斯滤波降噪,把噪点抹平,减少伪边缘。
处理流程:原图 → 高斯滤波降噪 → 边缘检测算子 → 输出边缘图像 |
8.2.3 基础公共代码
原图
import cv2 import numpy as np #读取图片,边缘检测一般使用灰度图 img = cv2.imread(r"D:\pythoncode2\bigdata_ai40\Image\girl.png") img = cv2.resize(img,(500,500)) gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) cv2.imshow("gray",gray) cv2.waitKey(0)灰度图
8.3 Sobel 算子
Sobel 算子是一种离散的微分算子,该算子结合了高斯平滑和微分求导运算。该算子利用局部差分寻找边缘,计算所得的是一个梯度的近似值。
Sobel算子包含2组3×3的矩阵,分别为横向和纵向模板,将之与图像作平面卷积,即可分别得出横向及纵向的亮度差分近似值。
Sobel 算子分别计算水平梯度(X 方向)、竖直梯度(Y 方向)。
- X 方向 Sobel:检测竖直边缘(左右灰度变化)
- Y 方向 Sobel:检测水平边缘(上下灰度变化)
cv2.Sobel(src, ddepth, dx, dy, ksize)
|
注意:梯度会出现负值。OpenCV 会自动截断负数变成 0,丢失一部分边缘,所以先使用CV_64F保存正负,再用cv2.convertScaleAbs()取绝对值。 |
# ========== Sobel算子 ========== # X方向梯度,检测竖直边缘 sobel_x = cv2.Sobel(gray, cv2.CV_64F, dx=1, dy=0, ksize=3) sobel_x_abs = cv2.convertScaleAbs(sobel_x) #Y方向梯度,检测水平边缘 sobel_y = cv2.Sobel(gray, cv2.CV_64F, dx=0, dy=1, ksize=3) sobel_y_abs = cv2.convertScaleAbs(sobel_y) #合并x、y两个方向的边缘 sobel_merge = cv2.addWeighted(sobel_x_abs,0.5,sobel_y_abs,0.5,0) cv2.imshow("Sobel‑X 竖直边缘",sobel_x_abs) cv2.imshow("Sobel‑Y 水平边缘",sobel_y_abs) cv2.imshow("Sobel合并全边缘",sobel_merge) cv2.waitKey(0)8.4 Scharr 算子---Sobel 增强版
原理
Scharr算子是Sobel算子的优化增强版本,OpenCV 提供独立原生函数cv2.Scharr(),无需嵌套 Sobel 使用。该算子优化了卷积核权重,放大梯度差值,对微弱、细碎、浅淡的灰度变化更敏感,能够提取 Sobel 无法识别的细边缘,细节更丰富。
核心语法:cv2.Scharr(src, ddepth, dx, dy)
|
- 检测方向规则:仅支持单方向求导,只能 dx=1/dy=0 或 dx=0/dy=1,不能同时为1
- 梯度处理规则:算子运算会产生正负梯度,OpenCV 默认截断负值丢失边缘,必须用cv2.CV_64F接收,再通过cv2.convertScaleAbs()取绝对值还原完整边缘
- 固定内核:内置固定 3×3 高精度权重核,无法自定义尺寸,是轻量化高精度边缘算子
示例代码
# ========== Scharr算子 ========== # X方向梯度:检测竖直边缘 scharr_x = cv2.Scharr(gray, cv2.CV_64F, dx=1, dy=0) scharr_x_abs = cv2.convertScaleAbs(scharr_x) #Y方向梯度:检测水平边缘 scharr_y = cv2.Scharr(gray, cv2.CV_64F, dx=0, dy=1) scharr_y_abs = cv2.convertScaleAbs(scharr_y) #加权融合双向边缘,得到完整轮廓 scharr_merge = cv2.addWeighted(scharr_x_abs, 0.5, scharr_y_abs, 0.5, 0) #展示结果 cv2.imshow("Scharr-X ", scharr_x_abs) cv2.imshow("Scharr-Y ", scharr_y_abs) cv2.imshow("Scharr ", scharr_merge) cv2.waitKey(0)Scharr与Sobel算子核心对比
- Sobel:核权重小,适合梯度变化明显的强边缘,抗噪相对好一点。
- Scharr:核权重更大,能捕捉微弱细边缘;缺点:对噪声更敏感,更容易提取伪边缘。
8.5 Laplacian 拉普拉斯算子
拉普拉斯算子计算二阶导数,可以同时捕捉 X、Y 两个方向边缘,对灰度变化更敏感。
缺点:对噪声非常敏感,必须提前降噪。
# ========== Laplacian拉普拉斯算子 ========== lap = cv2.Laplacian(gray,cv2.CV_64F,ksize=3) lap_abs = cv2.convertScaleAbs(lap) cv2.imshow("Laplacian",lap_abs) cv2.waitKey(0)8.6 Canny 边缘检测 ---重要
Canny 是效果最好的边缘检测算法,内部一共 4 步:
- 内部自动做高斯滤波,消除噪声
- 计算图像梯度,得到梯度大小、梯度方向
- 非极大值抑制:把粗的边缘线条 “压细”,只保留局部梯度最大值,边缘变细
- 双阈值筛选(高阈值、低阈值)
- 大于高阈值:确定是边缘,保留
- 小于低阈值:直接丢弃,不是边缘
- 介于两者中间:只有和确定边缘相连才保留,否则丢弃
# ========== Canny边缘检测 ========== # cv2.Canny(输入灰度图,低阈值,高阈值) canny_img = cv2.Canny(gray, threshold1=50, threshold2=150) cv2.imshow("Canny",canny_img) cv2.waitKey(0) cv2.destroyAllWindows()参数经验:
|
8.7 四种边缘检测算子对比表
算子 | 函数 | 特点 | 缺点 |
Sobel | cv2.Sobel(ksize=3) | 分开提取水平 / 竖直边缘,抗噪尚可 | 对微弱边缘不敏感,边缘线条较粗 |
Scharr | cv2.Sobel(ksize=cv2.SCHARR) | Sobel 增强,捕捉微弱细边缘 | 对噪声敏感,容易出现伪边缘 |
Laplacian | cv2.Laplacian() | 同时检测 xy 双方向,二阶导数 | 对噪声极其敏感 |
Canny | cv2.Canny() | 内部降噪 + 非极大抑制 + 双阈值,输出细而干净边缘 | 需要手动调试双阈值 |
8.8 新手高频坑点
- Sobel、Scharr、Laplacian 一定要设置cv2.CV_64F,再convertScaleAbs()取绝对值,否则丢失一半边缘。
- Laplacian、Scharr 对噪声敏感,使用前建议手动高斯滤波。
- Canny 输入是灰度图像,不要直接传入彩色图。
- Canny 阈值需要调试:阈值过大丢失边缘;阈值过小噪声大量混入。
- Sobel/Scharr 不要直接写dx=1,dy=1,效果很差;分开求 x、y 再addWeighted合并。
8.9 本章小结
- 边缘本质是灰度梯度突变。
- Sobel 适合强边缘;Scharr 是 Sobel 增强,捕捉微弱边缘,但更容易受噪声干扰。
- Laplacian 二阶导数,对噪声敏感。
- Canny 是工业最常用边缘检测,内部完成高斯降噪、非极大值抑制、双阈值筛选,输出干净细边缘。