边缘检测及Canny算法
对边缘的直观理解

边缘有助于我们对图像进行语义理解。直观上,边缘发生在图像强度值变化剧烈的地方
如何描述变化?自然是用导数/梯度

如上图,我们对图中的信号在水平方向上求导,可以得到右侧的导数图像,可以看到,它在边缘处由于信号发生剧烈变化,导数产生了极值。因此,导数的极值点能帮助我们定位边缘所在。
用卷积描述导数
对于一个二元的连续函数f(x,y)其偏导数可由如下极限得到:
\]
而对于离散的图像数据,我们通常使用如下表达式近似偏导数,也就是有限差分的方式:
\]
而这种计算方式,可以用卷积核的形式进行处理:
x方向上的导数可理解为右边的像素减去我自己,转化成卷积核长这样:

y方向上的导数可理解为上面的像素减去我自己,转化成卷积核长这样:

用这种卷积核对图像做卷积,得到每个像素点在x和y方向上的导数:

其他几种表示导数的卷积核
除了上面这种”极简“导数卷积核外,我们常用的还有以下几种卷积核:

Prewitt
该算子考虑了上下两层的信息,增强了对弱边缘的捕获能力,更具有鲁棒性。
Sobel
该算子在其中心列/行上权重为2,也就是说更关注自身所在列/行,能够抵御一定的噪声干扰。
这个卷积核可以分解为
1 \\
2 \\
1
\end{bmatrix}
\cdot
\begin{bmatrix}
-1 & 0 & 1
\end{bmatrix}
\]
可以把卷积分解成两次小的卷积操作,降低计算复杂度。
Roberts
可以检测45°/135°边缘。
从工程实践来说,Sobel算子具有良好的抗噪性和运算效率,其综合性能最优,是最常用的求导卷积核。OpenCV等库的默认实现。为了解决其在方向上的局限性(x,y两个方向),也常组合使用多方向核(如8方向Sobel扩展)。
图像2D上的导数---梯度
1D上叫导数,2D上叫梯度。把函数对x和y的偏导数放到一个向量中,该向量就表示了梯度信息。
\]

这个梯度向量的方向,就表达了当前像素点主要是在这个方向上变化
\]
向量的长度,就是在这个方向上变化的快慢
\]
该点的梯度长度,决定了该点是不是边缘,边缘点的梯度变化大,非边缘的梯度小。
梯度的方向,垂直于边。
把梯度的模算出来,画在图像上,就得到了梯度强度图,取一个阈值,就能取到边缘上的点。
(也可以理解为取极值点,但极值点可能比较离散,连不成一条线)

真实场景的应用---高斯偏导核
对于实际图像来说,由于噪声存在,如果直接使用上面的导数卷积核,就会造成如下情况:

导数的极值到处都是。如何处理噪声?我们可以做高斯平滑。
如下图所示,我们先做平滑,再进行求导

这两步又可以合成一步(卷积的交换律,结合律),也就是
\]
高斯平滑加导数,等效于直接对高斯核做导数,然后与之卷积
也就是高斯偏导核

二维的高斯偏导核图像如图:

注意:这里的x方向的高斯偏导核是不可以再分解成两个高斯核的,可以从高斯函数公式的导数公式看出。
原高斯核可以分成两个方向的高斯核相乘。也就可以先对x方向求卷积,再对y方向求卷积。
高斯偏导核的σ影响:下图σ为1,3,7

可以看到σ越小,检测到的边缘越精细,越大,检测到的边缘的尺度越大。
高斯核 vs 高斯偏导核
高斯核:
- 高频率波,低通滤波
- 权值为正
- 加权和为正
高斯偏导核
- 高斯核的导数
- 可为负
- 加权和为0
- 高对比度(边缘)处有高绝对值的加权值
Canny算法

通过上文中的方法,我们首先计算高斯偏导核的卷积结果,得到梯度模的图像

设置阈值对边缘点进行筛选:

这里存在一个问题:由于做了高斯模糊,阈值以上部分边缘看起来很粗

如上图,我们设置阈值之后,阈值以上的部分是一段相对较粗的区域,边缘的具体位置不精确。而实际上我们更期望得到的边缘是更精准的一条线。这里我们需要引入非最大化抑制的方法。
非最大化抑制 non-maximun suppression

我们在每个像素的梯度方向上判断前后两个点和自身模的大小,如果自己是最大的则保留,否则不保留。
如上图所示,q点的梯度方向上,前后两个点分别是r和p,梯度大小可以通过像素点上的梯度插值得到。判断p、q、r的梯度大小,如果q是最大的,则q点保留下来;如果q不是最大的,则q点舍弃。
由于梯度方向和边缘是垂直关系,这样从整体来看,我们最终留下了那个边缘垂线方向上的极值点。
然而这里又产生了新问题:求得结果不连通

如图中人脸下巴位置处,理应有一条边缘连接起来,而由于光照等因素影响,使得该处梯度变化不明显,检测出的边缘无法连通;而如果我们降低阈值,有可能引入更多的噪声因素。
此时,我们可以引入双阈值检测方法。
双阈值检测 Hysteresis Thresholding

该方法的核心思路在于设置高低两种阈值,筛选出可信的边缘,再利用边缘的连通性补全弱边缘信息。
- 用高阈值检测出图中粗的部分,这部分梯度变化剧烈且稳定,受噪声干扰小,比较鲁棒。
- 用低阈值检测弱边缘,这部分可能包含较多的候选点,也包含更多的噪声。
- 这里引入一个先验条件,有效的弱边缘一定与高阈值检测出的强边缘连通。因此,我们可以检查弱边缘像素候选点的8邻域像素:
- 若邻域内存在强边缘像素,则该弱边缘点标记为有效
- 若邻域内不存在强边缘像素,则该弱边缘点视为噪声,舍弃掉。
下图为通过双阈值检测得到的结果:

总结Canny算法步骤
使用高斯导数过滤图像
首先,对图像进行高斯滤波,以减少噪声并平滑图像,为后续的边缘检测做好准备。找到梯度的大小和方向
计算图像的梯度,确定每个像素点的梯度幅度和方向。梯度幅度表示边缘的强度,梯度方向表示边缘的方向。非最大抑制(Non-maximum suppression)
将宽的“脊”缩小到单个像素宽度。通过比较每个像素点的梯度幅度与其相邻像素点,保留局部最大值,从而细化边缘。双阈值检测
定义高低两个阈值:- 使用高阈值初步检测边缘曲线,确保鲁棒性强的边缘被保留。
- 使用低阈值来链接边缘曲线,把和强边缘相接的弱边缘连接起来。
边缘检测及Canny算法的更多相关文章
- OpenCV探索之路(六):边缘检测(canny、sobel、laplacian)
边缘检测的一般步骤: 滤波--消除噪声 增强--使边界轮廓更加明显 检测--选出边缘点 Canny算法 Canny边缘检测算法被很多人推崇为当今最优秀的边缘检测算法,所以我们第一个就介绍他. open ...
- 边缘检测之Canny
1. 写在前面 最近在做边缘检测方面的一些工作,在网络上也找了很多有用的资料,感谢那些积极分享知识的先辈们,自己在理解Canny边缘检测算法的过程中也走了一些弯路,在编程实现的过程中,也遇到了一个让我 ...
- [OpenCV入门教程之十二】OpenCV边缘检测:Canny算子,Sobel算子,Laplace算子,Scharr滤波器合辑
http://blog.csdn.net/poem_qianmo/article/details/25560901 本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog ...
- 学习 opencv---(11)OpenC 边缘检测:Canny算子,Sobel算子,Laplace算子,Scharr滤波器
本篇文章中,我们将一起学习OpenCV中边缘检测的各种算子和滤波器——Canny算子,Sobel算子,Laplace算子以及Scharr滤波器.文章中包含了五个浅墨为大家准备的详细注释的博文配套源代码 ...
- Canny算法检测边缘
Canny算法是边缘检测的一个经典算法,比单纯用一些微分算子来检测的效果要好很多,其优势有以下几点: 边缘误检与漏检率低. 边缘定位准确,且边界较细. 自带一定的滤噪功能,或者说,对噪声的敏感度要比单 ...
- 边缘检测:Canny算子,Sobel算子,Laplace算子
1.canny算子 Canny边缘检测算子是John F.Canny于 1986 年开发出来的一个多级边缘检测算法.更为重要的是 Canny 创立了边缘检测计算理论(Computational the ...
- opencv目标检测之canny算法
canny canny的目标有3个 低错误率 检测出的边缘都是真正的边缘 定位良好 边缘上的像素点与真正的边缘上的像素点距离应该最小 最小响应 边缘只能标识一次,噪声不应该标注为边缘 canny分几步 ...
- Canny算法源码,欢迎交流
http://blog.csdn.net/jianxiong8814/article/details/1563109 http://blog.csdn.net/assuper/article/deta ...
- (22)Canny算法
基础知识,主要是看这个博客:https://blog.csdn.net/qq_41167777/article/details/84863351
- opencv笔记4:模板运算和常见滤波操作
time:2015年10月04日 星期日 00时00分27秒 # opencv笔记4:模板运算和常见滤波操作 这一篇主要是学习模板运算,了解各种模板运算的运算过程和分类,理论方面主要参考<图像工 ...
随机推荐
- C#反序列化时,反射类型“XXX”时出错,提示:InvalidOperationException
C#反序列化时,反射类型"XXX"时出错,提示:InvalidOperationException的原因是因为: 一个类必须至少有一个空的默认构造函数,才能由XmlSerializ ...
- Mac_工具使用汇总
Mac安装低版本的Xcode后会出现各种问题,现汇总如下: 1. python3: error: unable to find utility "python3", not a ...
- vue el-select封装一个滚动加载更多下拉选项的自定义指令
没有什么讲究,直接上代码 模板部分 <el-select v-model="operator" filterable remote size="small" ...
- w3cschool-Docker 入门到实践
https://www.w3cschool.cn/reqsgr/ 什么是 Docker Docker 是一个开源项目,诞生于 2013 年初,最初是 dotCloud 公司内部的一个业余项目.它基于 ...
- MQ---消息队列概念和使用场景
消息队列概念和使用场景 声明:本文转自:MQ入门总结(一)消息队列概念和使用场景 写的很好,都不用自己在整理了,非常感谢该作者的用心. 一.什么是消息队列 消息即是信息的载体.为了让消息发送者和消息 ...
- 多线程的创建方式一:继承于Thread类
* 多线程的创建,方式一:继承于Thread类* 1. 创建一个继承于Thread类的子类* 2. 重写Thread类的run() --> 将此线程执行的操作声明在run()中* 3. 创建Th ...
- 记一次LLVM平行宇宙修包实战
最近加入了LLVM平行宇宙计划小组,在小组内提交了一定数量的PR.这个计划究竟是做什么的呢?LLVM平行宇宙计划是基于LLVM技术栈构建openEuler软件包,大白话讲就是原本一个软件包是用gc ...
- 第11章 其他 XML 技术
第11章 其他 XML 技术 概述 System.Xml 命名空间由以下命名空间和核心类型构成: System.Xml.* XmlReader 和 XmlWriter:高性能.前向读写的 X ...
- EIP和NAT结合如何实现统一公网出口IP
本文分享自天翼云开发者社区<EIP和NAT结合如何实现统一公网出口IP>,作者:j****n 通过EIP和NAT网关结合,为已绑定EIP的ECS实例统一公网出口IP,有利于更高效的管理互联 ...
- InfluxDB UI 设置保留策略
InfluxDB UI 设置保留策略 创建Bucket时设置保留策略 在InfluxDB 2.x的UI中创建Bucket时,你可以直接设置其保留策略.以下是一个基本的步骤指南: 登录到InfluxDB ...