当前位置: 首页 > news >正文

OpenCV联合C++/Qt 学习笔记(十三)----边缘检测

一、边缘检测原理

边缘(Edge)是指图像中局部灰度值(强度)发生显著变化的区域,通常存在于:

  • 目标与目标之间
  • 目标与背景之间
  • 区域与区域之间
  • 不同颜色或纹理之间

图像强度变化的两种典型形式:

  • 阶跃变化(Step Edge):图像灰度在某一点两侧发生突变,即像素值突然从一个灰度跳到另一个灰度。
  • 屋顶变化(Roof Edge)/线条变化:图像灰度突然升高,保持一小段距离后又下降回原值。
  • 前向差分(Forward Difference)计算梯度:

当前像素与左侧像素的差值 → 近似水平变化率

  • 改进后(中心差分)

二、Sobel算子边缘检测

/* 用途:用于计算图像的一阶或高阶梯度,通过Sobel卷积算子检测图像在 x方向或y方向的灰度变化,从而提取边缘信息 */ void cv::Sobel( InputArray src, OutputArray dst, int ddepth, int dx, int dy, int ksize = 3, double scale = 1, double delta = 0, int borderType = BORDER_DEFAULT ); /* src:输入图像 dst:输出图像,与输入图像具有相同的尺寸(但数据类型由ddepth决定) ddepth:输出图像的数据类型(深度) dx:x方向求导阶数 dy:y方向求导阶数 ksize:Sobel算子核大小(必须为1、3、5、7等奇数) scale:缩放系数,对计算结果进行比例放大或缩小 delta:偏移量,在结果中额外加上的值 borderType:像素外推法选择标志 */

三、Scharr算子边缘检测

/* 用途:用于计算图像在x方向或y方向的梯度,是Sobel算子的增强版本。 在使用3×3核时,Scharr算子相比Sobel具有更高的精度和更好的旋转对称性, 能更准确地检测图像边缘细节与灰度变化*/ void cv::Scharr( InputArray src, OutputArray dst, int ddepth, int dx, int dy, double scale = 1, double delta = 0, int borderType = BORDER_DEFAULT ); /* src:输入图像 dst:输出图像,与输入图像具有相同的尺寸(数据类型由ddepth决定) ddepth:输出图像的数据类型(深度) dx:x方向求导阶数 dy:y方向求导阶数 scale:缩放系数,对计算结果进行比例放大或缩小 delta:偏移量,在结果中额外加上的值 borderType:像素外推法选择标志 */

四、两种算子的生成

/* 用途:用于生成图像导数运算所需的一维卷积核(x方向与y方向), 这些卷积核通常用于Sobel、Scharr等梯度计算, 或与sepFilter2D配合实现自定义微分滤波。 通过该函数可灵活构造一阶导数、二阶导数等算子, 常用于边缘检测、梯度分析、特征提取以及高性能可分离卷积计算 */ void cv::getDerivKernels( OutputArray kx, OutputArray ky, int dx, int dy, int ksize, bool normalize = false, int ktype = CV_32F ); /* kx:行滤波器系数的输出矩阵,尺寸为ksize * 1 ky:列滤波器系数的输出矩阵,尺寸为ksize * 1 dx:X方向导数的阶次 dy:y方向导数的阶次 ksize:滤波器的大小,可以选择的参数为FILTER_SCHARR,1,3,5或7 normalize:是否对滤波器系数进行归一化的标志,默认值为false,表示不进行系数归一化 ktype:滤波器系数类型,可以选择CV_32F或CV_64F,默认参数为CV_32F */

五、示例代码

QString imgPath = QApplication::applicationDirPath() + "/Images"; cv::String s_imgPath = imgPath.toLocal8Bit().data(); Mat img = imread(s_imgPath + "/equalLena.jpg", IMREAD_ANYCOLOR); if (img.empty()) { qDebug() << "图片加载失败, 请确认图像文件名称是否正确"; return; } Mat resultX, resultY, resultXY; /*X方向一阶边缘*/ Sobel(img, resultX, CV_16S, 1, 0, 3); convertScaleAbs(resultX, resultX);/*求取绝对值*/ /*Y方向一阶边缘*/ Sobel(img, resultY, CV_16S, 0, 1, 3); convertScaleAbs(resultY, resultY); /*整幅图像的一阶边缘*/ resultXY = resultX + resultY; imshow("resultX", resultX); imshow("resultY", resultY); imshow("resultXY", resultXY); waitKey(0); /*X方向一阶边缘*/ Scharr(img, resultX, CV_16S, 1, 0); convertScaleAbs(resultX, resultX);/*求取绝对值*/ /*Y方向一阶边缘*/ Scharr(img, resultY, CV_16S, 0, 1); convertScaleAbs(resultY, resultY);/*求取绝对值*/ resultXY = resultX + resultY; imshow("resultX", resultX); imshow("resultY", resultY); imshow("resultXY", resultXY); waitKey(0); /*生成边缘检测器*/ Mat sobel_x1, sobel_y1;/*存放分离的sobel算子*/ Mat scharr_x, scharr_y;/*存放分离的scharr算子*/ Mat sobelX1, scharrX;/*存放最终算子*/ getDerivKernels(sobel_x1, sobel_y1, 1, 0, 3); sobel_x1 = sobel_x1.reshape(CV_8U, 1); sobelX1 = sobel_y1 * sobel_x1;/*计算滤波器*/ getDerivKernels(scharr_x, scharr_y, 1, 0, FILTER_SCHARR); scharr_x = scharr_x.reshape(CV_8U, 1); scharrX = scharr_y * scharr_x;/*计算滤波器*/ cout << "X sobel: " << endl << sobelX1 << endl; cout << "X scharr: " << endl << scharrX << endl; waitKey(0); destroyAllWindows();

六、Laplacian算子边缘检测

1、Laplacian算子

Laplacian算子是一种基于二阶导数的边缘检测算法,主要用于识别图像中的快速亮度变化,即边缘。由于它直接与图像的二阶导数相关,因此能够有效地找到边缘位置,但对噪声比较敏感。

Sobel和Scharr边缘检测算法存在的问题:

  • 分别计算两个方向边缘
  • 边缘与方向相关性较大
  • Laplacian算子:

  • 方向无关
  • 容易受到噪声的影响
  • 3 * 3的Laplacian算子:

​​​​​​​

2、Laplacian边缘检测函数

/* 用途:用于计算图像的二阶导数(Laplacian算子),通过检测灰度变化的“变化率”, 强调图像中灰度突变的位置,从而突出边缘信息。 与Sobel(一阶导数)相比,Laplacian对边缘方向不敏感, 能同时检测各个方向的边缘,但对噪声也更敏感 */ void cv::Laplacian( InputArray src, OutputArray dst, int ddepth, int ksize = 1, double scale = 1, double delta = 0, int borderType = BORDER_DEFAULT ); /* src:输入原图像,可以是灰度图像和彩色图像 dst:输出图像,与输入图像src具有相同的尺寸和通道数 ddepth:输出图像的数据类型(深度),根据输入图像的数据类型不同拥有不同的取值范围 ksize:滤波器的大小,必须为正奇数 scale:对导数计算结果进行缩放的缩放因子,默认系数为1,不进行缩放 delta:偏值,在计算结果中加上偏值 borderType:像素外推法选择标志 */

七、Canny算子边缘检测

1、Canny边缘检测原理介绍

Canny边缘检测的主要步骤:

1> 使用高斯滤波平滑图像

在进行边缘检测之前,首先需要对原始图像进行平滑处理。因为图像在采集、传输过程中不可避免会引入噪声,而边缘检测本质上依赖于灰度变化率(导数),这种运算会对噪声非常敏感,甚至会把噪声误判为边缘。

常用高斯滤波平滑图像滤波器:

​​​​​​​ ​​​​​​​ ​​​​​​​ ​​​​​​​

2> 计算图像中每个像素的梯度方向和幅度

在平滑后的图像上,计算每个像素点的灰度变化情况。通常通过Sobel算子分别计算水平方向和垂直方向的变化量,得到两个梯度分量 Ix 和 Iy​。

然后将这两个分量合成为:

  • 梯度方向 θ:表示变化发生的方向,用于确定边缘的走向
  • 梯度幅值 G:表示灰度变化的强度,用来判断该点是否可能是边缘

3> 应用非极大值抑制算法边缘检测带来的杂散响应

在上一步中得到的边缘通常是比较“粗”的带状结构,而不是理想的单像素边缘。因此需要进一步细化。

非极大值抑制的核心思想是:沿着梯度方向,只保留局部最大的那个像素点,其余的全部抑制为0。也就是说,如果某个像素点不是该方向上的“最高点”,就认为它不是最真实的边缘位置。

经过这一步处理后,边缘会从“宽线条”变成“细线条”,更加精确。

4> 应用双阈值法划分强边缘和弱边缘

经过非极大值抑制后,图像中仍然存在一些不确定的边缘,需要进一步筛选。Canny算法采用双阈值策略,而不是简单的单阈值。

具体做法是设置一个高阈值和一个低阈值:

  • 梯度值高于高阈值的像素 → 明确的强边缘
  • 梯度值介于两者之间 → 可能的弱边缘
  • 梯度值低于低阈值 → 直接认为不是边缘

这种方法的优势在于:既能保留明显边缘,又不会因为阈值过高导致边缘断裂。

5> 消除孤立的弱边缘(边缘连接 / 滞后处理)

在弱边缘中,既包含真实边缘(只是强度较低),也包含噪声。为了区分它们,Canny引入“边缘连接”策略。

具体规则是:

  • 如果一个弱边缘像素与强边缘相连(8邻域连接),则认为它属于真实边缘 → 保留
  • 如果一个弱边缘像素是孤立的,没有连接到强边缘 → 认为是噪声 → 删除

通过这一步,可以有效去除孤立噪声点,同时保证边缘的连续性。

2、Canny算法函数

/* 用途:用于进行高质量边缘检测,通过一整套优化流程(高斯滤波、梯度计算、 非极大值抑制、双阈值筛选和边缘连接)提取图像中的清晰边缘。 相比Sobel、Laplacian等基础算子,Canny能够有效抑制噪声、 保留真实边缘并去除伪边缘 */ void cv::Canny( InputArray image, OutputArray edges, double threshold1, double threshold2, int apertureSize = 3, bool L2gradient = false ); /* image:输入图像,必须是CV_8U单通道或者三通道图像 edges:输出图像,与输入图像具有相同尺寸的单通道图像,且数据类型为CV_8U threshold1:第一个滞后阈值(低阈值----弱边缘判定) threshold2:第二个滞后阈值(高阈值----强边缘判定) apertureSize:Sobel算子的直径 L2gradient:计算图像梯度幅值的标志(是否使用更精确的梯度计算方式) */

八、示例代码

QString imgPath = QApplication::applicationDirPath() + "/Images"; cv::String s_imgPath = imgPath.toLocal8Bit().data(); Mat img = imread(s_imgPath + "/equalLena.jpg", IMREAD_ANYCOLOR); if (img.empty()) { qDebug() << "图片加载失败, 请确认图像文件名称是否正确"; return; } Mat result, result_g, result_G; /*未滤波提取边缘*/ Laplacian(img, result, CV_16S, 3, 1, 0); convertScaleAbs(result, result); /*滤波后提取Laplacian边缘*/ GaussianBlur(img, result_g, Size(3, 3), 5, 0); Laplacian(result_g, result_G, CV_16S, 3, 1, 0); convertScaleAbs(result_G, result_G); imshow("result", result); imshow("result_G", result_G); waitKey(0); Mat resultHigh, resultLow, resultG; /*大阈值检测图像边缘*/ Canny(img, resultHigh, 100, 200, 3); /*小阈值检测图像边缘*/ Canny(img, resultLow, 20, 40, 3); /*高斯模糊后检测图像边缘*/ GaussianBlur(img, resultG, Size(3, 3), 5); Canny(resultG, resultG, 100, 200, 3); imshow("resultHigh", resultHigh); imshow("resultLow", resultLow); imshow("resultG", resultG); waitKey(0); destroyAllWindows();

九、四种边缘检测算子对比表

对比维度Sobel算子Scharr算子Laplacian算子Canny算子
算子类型一阶梯度一阶梯度(优化版)二阶导数多阶段算法
数学本质一阶偏导近似改进的一阶偏导二阶偏导(散度)梯度 + 优化流程
是否有方向性有(x/y)有(x/y)有(梯度方向)
核心公式梯度 ∇f梯度 ∇f(更精确)∇²f多步骤(非单一公式)
卷积核大小3×3 / 5×5固定3×33×3(常用)不固定(组合)
抗噪声能力中等(带平滑)中等(优于Sobel)差(极敏感)强(高斯滤波)
边缘定位精度中等高(但不稳定)很高
边缘连续性一般一般较差(易断裂)很好
是否产生双边缘可能可能容易产生基本不会
对细节敏感度很高(含噪声)高(受阈值控制)
计算复杂度较高
实时性一般
典型问题方向误差基本无明显问题噪声放大参数敏感
是否需要预处理可选可选必须平滑内置高斯
是否需要后处理可选可选可选已包含
OpenCV函数Sobel()Scharr()Laplacian()Canny()

精度优先:Canny > Scharr > Sobel > Laplacian
速度优先:Sobel ≈ Scharr ≈ Laplacian > Canny
稳定性:Canny > Scharr > Sobel > Laplacian

http://www.jsqmd.com/news/769446/

相关文章:

  • 论文写作技巧
  • 观察Taotoken在高并发测试下的API响应稳定性表现
  • 服务器双卡5090 配置深度学习环境
  • 当免费遇上专业:思源宋体如何让中文排版不再妥协
  • 2026年装修成品保护材料源头工厂直供指南:苏州、北京、上海等18城一站式采购方案 - 企业名录优选推荐
  • Claw-Social插件:为AI Agent构建语义社交发现与双轨通讯系统
  • 使用ContextAI统一管理AI编程助手上下文,提升开发效率与代码一致性
  • 终极指南:3步解决Windows老游戏兼容性,让经典游戏重获新生
  • VSCode 2026 + Trace32深度协同指南:实现AURIX TC4xx实时变量观测、CoreSight ETM流解析与UDS诊断会话一键触发(仅限首批内测License持有者公开)
  • iLogtail 从核心概念到实战的完整教程
  • ArcGIS新手避坑指南:从零开始,10分钟搞定你的第一张地图(附练习数据)
  • Stretchly完全指南:用开源工具构建科学的屏幕时间管理系统
  • 特朗普家族涉足AI!推出WorldRouter平台,还计划多产品布局,是割韭菜还是降价?
  • 基于NapCat的QQ机器人框架openclaw-NapCatQQ部署与开发指南
  • 【Python从入门到精通】第 001 篇:Python开发环境搭建完全指南(Windows / macOS / Linux)
  • 在Claude Code中配置Taotoken作为后端,获得更稳定经济的编程辅助
  • 快手校招怎么准备:别只刷 Go 八股,直播和推荐系统才是主线
  • ComfyUI-Manager完整指南:轻松管理你的AI工作流扩展
  • 抖音下载器:一键解锁批量内容管理的新时代
  • 别再只盯着代码了!从支付宝到王者荣耀,聊聊那些意想不到的移动端物理攻击与防御
  • Java SPI vs Spring SPI
  • 小升初的信息
  • 目录文件管理(mkdir、ls、tree、alias、rm)
  • 抖音下载器终极指南:告别手动操作,实现批量下载自动化
  • 月球基底建造 第三卷第一章 环月空间站奠基,深空全域值守体系与轨道文明中枢成型
  • NVIDIA Profile Inspector终极指南:免费解锁200+隐藏显卡设置,彻底解决游戏卡顿问题
  • 从NGSIM到自动驾驶仿真:如何用sEMA滤波为你的车辆模型提供‘干净’的轨迹真值?
  • PINN调参避坑指南:从N-S方程反演案例看TensorFlow 2.0梯度计算与模型收敛技巧
  • π0.7深度解析:为什么它不是“更大的机器人模型”,而是机器人基础模型的一次方法论转向
  • 23_ble_advertising