使用opencv的Canny算子实现图像边缘检测

 

1 边缘检测介绍

图像边缘检测技术是图像处理和计算机视觉等领域最基本的问题,也是经典的技术难题之一。如何快速、精确地提取图像边缘信息,一直是国内外的研究热点,同时边缘的检测也是图像处理中的一个难题。早期的经典算法包括边缘算子方法、曲面拟合的方法、模板匹配方法、阈值法等。

近年来,随着数学理论与人工智能技术的发展,出现了许多新的边缘检测方法,如Roberts、Laplacan、Canny等图像的边缘检测方法。这些方法的应用对于高水平的特征提取、特征描述、目标识别和图像理解有重大的影响。然而,在成像处理的过程中投影、混合、失真和噪声等会导致图像模糊和变形,这使得人们一直致力于构造具有良好特性的边缘检测算子。

1.1 什么是边缘检测

边缘检测是图像处理和计算机视觉中的基本问题,边缘检测的目的是标识数字图像中亮度变化明显的点。图像属性中的显著变化通常反映了属性的重要事件和变化,包括深度不连续、表面方向不连续、物质属性变化和场景照明变化。边缘检测特征是提取中的一个研究领域。图像边缘检测大幅度地减少了数据量,并且剔除了可以认为不相关的信息,保留了图像重要的结构属性。

ba30c8c30a4f4769b79dce231980dfc9.png

1.2 边缘检测的方法

人类视觉系统认识目标的过程分为两步:首先,把图像边缘与背景分离出来;然后,到图像的细节,辨认出图像的轮廓。计算机视觉正是模仿人类视觉的过程。

因此,在检测物体边缘时先对轮廓点进行粗略检测,然后通过链接规则把原来检测到的轮廓点连接起来,同时检测和连接遗漏的边界点及去除虚假的边界点。图像的边缘是图像的重要特征,是计算机视觉、模式识别等的基础,因此边缘检测是图像处理中一个重要的环节。然而,边缘检测是图像处理中的一个难题,因为实际景物图像的边缘往往是各种类型的边缘及它们模糊化后结果的组合,且实际图像信号存在噪声。噪声和边缘都属于高频信号,很难用频带做取舍。

边缘是指图像周围像素灰度有阶跃变化或屋顶状变化的像素集合,存在于目标与背景、目标与目标、区域与区域、基元与基元之间。边缘具有方向和幅度两个特征,沿边缘走向,像素值变化比较平缓;垂直于边缘走向,像素值变化比较剧烈,可能呈现阶跃状,也可能呈现斜坡状。因此,边缘可以分为两种:

  • 一种为阶跃性边缘,两边的像素灰度值有着明显的不同;

  • 另一种为屋顶状边缘,位于灰度值从增加到减少的变化转折点。

对于阶跃性边缘,二阶方向导数在边缘处呈零交叉;对于屋顶状边缘,二阶方向导数在边缘处取极值。有许多方法可以用于边缘检测,绝大部分可以划分为两类:基于搜索的一类和基于零穿越的一类。

  • 基于搜索:通过寻找图像一阶导数中的最大值来检测边界,然后利用计算结果估计边缘的局部方向,通常采用梯度的方向,并利用此方向找到局部梯度模的最大值,代表算法是Sobel算子和Scharr算子。

ee342b69286545f89d6153c9d55bde58.png

  • 基于零穿越:通过寻找图像二阶导数零穿越来寻找边界,代表算法是Laplacian算子。

91276000e2b84fd49624342fac4045ee.png

1.3 典型算子比较

算子优缺点
Roberts对具有陡峭的低噪声的图像处理效果较好,但利用Roberts算子提取边缘的结果是边缘比较粗,因此边缘定位不是很准确
Sobel对灰度渐变和噪声较多的图像处理效果比较好,Sobel算子对边缘定位比较准确
Kirsch对灰度渐变和噪声较多的图像处理效果较好
Prewitt 对灰度渐变和噪声较多的图像处理效果较好
Laplacian对图像中的阶跃性边缘点定位准确,对噪声非常敏感,丢失一部分边缘的方向信息,造成一些不连续的检测边缘
LoGLoG算子经常出现双边缘像素边界,而且该检测算法对噪声比较敏感,所以很少用LoG算子检测边缘,而是用来判断边缘像素是位于图像的明区还是暗区
Canny此方法不容易受噪声的干扰,能够检测到真正的弱边缘。在edge函数中,最有效的边缘检测方法是Canny方法。该方法的优点在于使用两种不同的阈值分别检测强边缘和弱边缘,并且仅当弱边缘和强边缘相连时,才将弱边缘包含在输出图像中。因此,这种方法不容易被噪声”填充“,更容易检测出真正的弱边缘。

2 使用opencv的Canny算子实现边缘检测

Canny边缘检测是一种经典的边缘检测算法,由John F.在1986年提出。它被广泛应用于计算机视觉和图像处理领域,是一种多阶段的边缘检测算法,能够有效地检测图像中的边缘并抑制噪声,得到清晰准确的边缘信息,并且对噪声具有一定的鲁棒性。Canny被认为是最优的边缘检测算法。

2.1 检测原理

2.1.1 应用高斯滤波去除图像噪声

由于图像边缘非常容易受到噪声的干扰,因此为了避免检测到错误的边缘信息,通常需要对图像进行滤波以去除噪声。滤波的目的是平滑一些纹理较弱的非边缘区域,以便得到更准确的边缘。在实际处理过程中,通常采用高斯滤波去除图像中的噪声。

图 10-1 演示了使用高斯滤波器 T 对原始图像 O 中像素值为 226 的像素点进行滤波,得到该点在滤波结果图像 D 内的值的过程。

3e8861d555544853a5a73a01e90689e9.png

在滤波过程中,我们通过滤波器对像素点周围的像素计算加权平均值,获取最终滤波结果。对于高斯滤波器 T,越临近中心的点,权值越大。在图 10-1 中,对图像 O 中像素值为 226 的像素点,使用滤波器 T 进行滤波的计算过程及结果为:

结果 = 156×(197×1+25×1+106×2+156×1+159×1
 +149×1+40×3+107×4+5×3+71×1
 +163×2+198×4+226×8+223×4+156×2
 +222×1+37×3+68×4+193×3+157×1
 +42×1+72×1+250×2+41×1+75×1)
 = 138

 当然,高斯滤波器(高斯核)并不是固定的,例如它还可以是:

0a6f73bb537a44faa86dd5b806c58934.png
滤波器的大小也是可变的,高斯核的大小对于边缘检测的效果具有很重要的作用。滤波器的核越大,边缘信息对于噪声的敏感度就越低。不过,核越大,边缘检测的定位错误也会随之增加。通常来说,一个 5×5 的核能够满足大多数的情况。

2.1.2 计算梯度幅值和方向

前面一节讲了如何计算图像梯度的幅度。在这里,我们关注梯度的方向,梯度的方向与边缘的方向是垂直的。

边缘检测算子返回水平方向的Gx和垂直方向的Gy。梯度的幅度𝐺和方向𝛩(用角度值表示)为:

4727f5e6240a40dd94fe5ec6c59d2ce0.png

e672aca992c243778bd6af160e74e330.png

如果某个像素点是边缘,则其梯度方向总是垂直与边缘垂直。梯度方向被归为四类:垂直,水平,和两个对角线方向。

因此,在计算梯度时,我们会得到梯度的幅度和角度(代表梯度的方向)两个值。

图 10-2 展示了梯度的表示法。其中,每一个梯度包含幅度和角度两个不同的值。为了方便观察,这里使用了可视化表示方法。例如,左上角顶点的值“2↑”实际上表示的是一个二元数对“(2, 90)”,表示梯度的幅度为 2,角度为 90°。
493b6d6998914dd2a5a5e0a1042f127b.png

2.1.3 非极大值抑制

在获得了梯度的幅度和方向后,遍历图像中的像素点,去除所有非边缘的点。

在具体实现时,逐一遍历像素点,判断当前像素点是否是周围像素点中具有相同梯度方向的最大值,并根据判断结果决定是否抑制该点。

通过以上描述可知,该步骤是边缘细化的过程。针对每一个像素点:

  • 如果该点是正/负梯度方向上的局部最大值,则保留该点。

  • 如果不是,则抑制该点(归零)。

在图 10-3 中,A、B、C 三点具有相同的方向(梯度方向垂直于边缘)。判断这三个点是否为各自的局部最大值:如果是,则保留该点;否则,抑制该点(归零)。
1a5248863af24cbd97938b0d2478a9d7.png

经过比较判断可知,A 点具有最大的局部值,所以保留 A 点(称为边缘),其余两点(B和 C)被抑制(归零)。

在图 10-4 中,黑色背景的点都是向上方向梯度(水平边缘)的局部最大值。因此,这些点会被保留;其余点被抑制(处理为 0)。这意味着,这些黑色背景的点最终会被处理为边缘点,而其他点都被处理为非边缘点。
e34279f0fb5c444b8b1e98b73897c8d0.png

“正/负梯度方向上”是指相反方向的梯度方向。例如,在图 10-5 中,黑色背景的像素点都是垂直方向梯度(向上、向下)方向上(即水平边缘)的局部最大值。这些点最终会被处理为边缘点。

9e0057f6f0624bb692869bd66de87c3e.png

经过上述处理后,对于同一个方向的若干个边缘点,基本上仅保留了一个,因此实现了边缘细化的目的。

2.1.4 应用双阈值确定边缘

完成上述步骤后,图像内的强边缘已经在当前获取的边缘图像内。但是,一些虚边缘可能也在边缘图像内。这些虚边缘可能是真实图像产生的,也可能是由于噪声所产生的。对于后者,必须将其剔除。

设置两个阈值,其中一个为高阈值 maxVal,另一个为低阈值 minVal。根据当前边缘像素的梯度值(指的是梯度幅度,下同)与这两个阈值之间的关系,判断边缘的属性。具体步骤为:

  • 如果当前边缘像素的梯度值大于或等于 maxVal,则将当前边缘像素标记为强边缘。

  • 如果当前边缘像素的梯度值介于 maxVal 与 minVal 之间,则将当前边缘像素标记为虚边缘(需要保留)。

  • 如果当前边缘像素的梯度值小于或等于 minVal,则抑制当前边缘像素。

在上述过程中,我们得到了虚边缘,需要对其做进一步处理。一般通过判断虚边缘与强边
缘是否连接,来确定虚边缘到底属于哪种情况。通常情况下,如果一个虚边缘:

  • 与强边缘连接,则将该边缘处理为边缘。

  • 与强边缘无连接,则该边缘为弱边缘,将其抑制。

在图 10-6 中,左图显示的是三个边缘信息,右图是对边缘信息进行分类的示意图,具体划分如下:

  • A 点的梯度值值大于 maxVal,因此 A 是强边缘。

  • B 和 C 点的梯度值介于 maxVal 和 minVal 之间,因此 B、C 是虚边缘。

  • D 点的梯度值小于 minVal,因此 D 被抑制(抛弃)。

d784430883c84d0d81fcca9a179a9907.png

图 10-7 显示了对图 10-6 中的虚边缘 B 和 C 的处理结果。其中:

  • B 点的梯度值介于 maxVal 和 minVal 之间,是虚边缘,但该点与强边缘不相连,故将其抛弃。

  • C 点的梯度值介于 maxVal 和 minVal 之间,是虚边缘,但该点与强边缘 A 相连,故将其保留。

c4dd17db3da14d95a43dbd9912396eaa.png

注意,高阈值 maxVal 和低阈值 minVal 不是固定的,需要针对不同的图像进行定义。

10-8 给出了一个 Canny 边缘检测的效果图。

697ae07add454ada9b8dc6ed0bc88140.png

2.1.5 检测边缘连接

Canny边缘检测的边缘连接是指将非极大值抑制得到的边缘点连接成连续的边缘线。这个过程是Canny边缘检测算法中的最后一步,它的目的是去除由于非极大值抑制产生的间断的边缘点,从而得到更加准确和连续的边缘检测结果。

Canny边缘检测的边缘连接过程:

  • 遍历梯度幅值图像:首先,遍历经过非极大值抑制后的梯度幅值图像,即只有边缘上的像素点的梯度幅值被保留,其他像素点的梯度幅值为零。

  • 标记边缘点:对于每个强边缘像素点(梯度幅值大于高阈值),将其标记为边缘点。强边缘像素点是图像中梯度值较大的像素点,它们代表了图像中明显的边缘。

  • 边缘连接:对于每个边缘点的相邻像素点,如果其梯度幅值大于低阈值,并且没有被标记为边缘点,则将其标记为弱边缘点,并递归地进行边缘连接。这一步的目的是将与强边缘像素点相邻的弱边缘像素点连接到边缘线上。

  • 递归连接:在边缘连接过程中,如果某个弱边缘像素点被标记为边缘点,则会继续检查该像素点的相邻像素点,以便将所有与强边缘像素点相邻的弱边缘像素点连接到边缘线上。

  • 结束条件:边缘连接的递归过程会一直进行,直到所有与强边缘像素点相邻的弱边缘像素点都被标记为边缘点,没有更多的像素点可以连接。

  • 非边缘点处理:经过边缘连接后,所有未被标记为边缘点的像素点视为非边缘点,并抑制其梯度幅值为零,从而得到最终的边缘图像。

2.2 Canny函数原型

edges = cv.Canny( image, threshold1, threshold2[, apertureSize[, L2gradient]])

参数:

  • edges 为计算得到的边缘图像。

  • image 为 8 位输入图像。

  • threshold1 表示处理过程中的第一个阈值。

  • threshold2 表示处理过程中的第二个阈值。

  • apertureSize 表示 Sobel 算子的孔径大小。

  • L2gradient 为计算图像梯度幅度(gradient magnitude)的标识。其默认值为 False。如果为 True,则使用更精确的 L2 范数进行计算(即两个方向的导数的平方和再开方),否则使用 L1 范数(直接将两个方向导数的绝对值相加)。

 

2.3 检测代码

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt

# 1 读取图像
img = cv.imread('../data/cat_dog.jpg')

# 2 将图片由BGR转为RGB
img = cv.cvtColor(img, cv.COLOR_BGR2RGB)

# 2 边缘检测,canny算子,阈值128-200,低于128的像素点认为是边缘,
# 高于200的像素点认为是边缘,中间值的像素点如果与边缘点相连则认为是边缘
edge = cv.Canny(img, 128, 200)

# 3 图像显示
plt.figure(figsize=(10, 8), dpi=100)
plt.subplot(121), plt.imshow(img), plt.title('original')
plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(edge, cmap=plt.cm.gray), plt.title('Canny')
plt.xticks([]), plt.yticks([])
plt.show()

运行代码显示:

5009fa7d451d4101abdc43c6a4d50c70.png

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/248600.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

使用set和emit在uni-app中实现响应式属性和自定义事件

在uni-app中,我们经常需要动态设置响应式属性,并且通过自定义事件来实现组件间的通信。这时,我们可以使用set和emit来轻松实现这些功能。 使用$set动态设置响应式属性 在Vue中,我们可以使用来动态设置响应式属性。在uniapp中使用…

java基础知识④:设计模式

目录 一、设计模式 1️⃣创建型设计模式(常用:单例、工厂、抽象工厂) 2️⃣结构型设计模式(常用:适配器、装饰者、外观、代理) 3️⃣行为型设计模式(常用:观察者、策略、模板方法、命…

如何使用ArcGIS Pro裁剪影像

对影像进行裁剪是一项比较常规的操作,因为到手的影像可能是多种范围,需要根据自己需求进行裁剪,这里为大家介绍一下ArcGIS Pro中裁剪的方法,希望能对你有所帮助。 数据来源 本教程所使用的数据是从水经微图中下载的影像和行政区…

Springboot的火车票订票系统(有报告)。Javaee项目,springboot项目。

演示视频: Springboot的火车票订票系统(有报告)。Javaee项目,springboot项目。 项目介绍: 采用M(model)V(view)C(controller)三层体系结构&#…

Spring Boot整合Sharding-JDBC实现数据脱敏

目录 背景ShardingSphere脱敏规则sharding-jdbc数据脱敏数据脱敏配置数据分片 数据脱敏配置 背景 对互联网公司、传统行业来说,数据安全一直是极为重视和敏感的话题。数据脱敏是指对某些敏感信息通过脱敏规则进行数据的变形,实现敏感隐私数据的可靠保护…

【Avue】点击新增再点击表单得radio选项出现新表单,且编辑页面关不掉新表单处理方法

一、问题描述 1、点击新增 2、 点击radio选择值 1、点击否得时候没反应 2、点击是得时候出现新表单 2.1、旧代码 {label: 是否危险源,prop: isBigdanger,searchLabelWidth: 120,overHidden: true,span: 24,rules: [{required: true,message: 请选择是否重大危险源,trigger: bl…

Mapreduce小试牛刀(2)--java api

1.同hdfs 的java api,我们首先要在IDE中建立一个maven项目 pom.xml中配置如下&#xff1a; <dependencies><dependency><groupId>org.apache.hadoop</groupId><artifactId>hadoop-mapreduce-client-common</artifactId><version>3.…

磁盘坏道扫描工具 Macrorit Disk Scanner v6.7.0 中文免费版 -供大家学习研究参考

非常方便实用的磁盘坏道修复软件。Wipe Bad Disk功能强大好用&#xff0c;通过特殊的算法来强制将硬盘的坏道删除清空格式化&#xff0c;从而拯救因产生坏道而不敢继续使用的硬盘!要注意的是经过这块软件清空的硬盘数据基本上是不能被恢复的&#xff0c;所以操作前请一定要备份…

武林风云之linux组软raid0

小y可喜欢玩文明系列的游戏了&#xff0c;因为小y也一直喜欢造轮子&#xff0c;属于自己的轮子。 每次小y听到”要向雄鹰一样&#xff0c;定要遨游于天际。”感觉自己给自己打了一针强心剂&#xff0c;要求自己拼搏进取。 众所周知&#xff0c;文明是个原生的linux游戏&#xf…

Linux 系统 yum 安装 jdk1.8

1、首先检查是否存在jdk java -version上图这样就是系统没有找到已经安装的jdk 2.查看jdk版本列表 yum -y list java*执行此命令会显示所有版本 jdk 安装包 3、下载安装jdk 这里安装的是jdk1.8 yum install java-1.8.0-openjdk-devel.x86_64这里输入回车y继续安装 4、再次检…

学习Django从零开始之三

搭建虚拟python环境 搭建开发环境有多种方式&#xff0c;其中包括本地直接安装Python的可执行文件&#xff0c;使用virtualenv&#xff0c;以及使用Anaconda和Miniconda等工具。这些工具在创建Python虚拟环境方面各有特点。具体不同之处感兴趣的同学可以自行查阅相关资料。 简…

docker consul 容器的自动发现与注册

consul相关知识 什么是注册与发现 服务注册与发现是微服务架构中不可或缺的重要组件。起初服务都是单节点的&#xff0c;不保障高可用性&#xff0c;也不考虑服务的压力承载&#xff0c;服务之间调用单纯的通过接口访问。直到后来出现了多个节点的分布式架构&#xff0c;起初的…

大创项目推荐 深度学习 opencv python 实现中国交通标志识别

文章目录 0 前言1 yolov5实现中国交通标志检测2.算法原理2.1 算法简介2.2网络架构2.3 关键代码 3 数据集处理3.1 VOC格式介绍3.2 将中国交通标志检测数据集CCTSDB数据转换成VOC数据格式3.3 手动标注数据集 4 模型训练5 实现效果5.1 视频效果 6 最后 0 前言 &#x1f525; 优质…

通义千问 Qwen-72B-Chat在PAI-DSW的微调推理实践

01 引言 通义千问-72B&#xff08;Qwen-72B&#xff09;是阿里云研发的通义千问大模型系列的720亿参数规模模型。Qwen-72B的预训练数据类型多样、覆盖广泛&#xff0c;包括大量网络文本、专业书籍、代码等。Qwen-72B-Chat是在Qwen-72B的基础上&#xff0c;使用对齐机制打造的…

String类的hashCode()方法源码分析

Object类中的hashCode()方法&#xff1a; 同一个对象&#xff0c;hashCode必须相同&#xff1b;如果两个对象的equals相等&#xff0c;那么hashCode也必须要相等&#xff01;hashCode()方法是native本地方法&#xff0c;是C代码&#xff0c;hashCode的值&#xff0c;不一定是…

高级前端开发工程师

岗位需求 熟练掌握前端主流框架Vue、React、Angular,至少熟练掌控Vue全家桶 文章目录 岗位需求前言一、Vue框架二、React框架三、Angular框架四、什么是Vue全家桶前言 -那就看你表哥的电脑里有没有硬盘 -我不敲键盘 一、Vue框架 Vue(读音为/vjuː/,类似于"view"…

生产环境_Spark处理轨迹中跨越本初子午线的经度列

使用spark处理数据集&#xff0c;解决gis轨迹点在地图上跨本初子午线的问题&#xff0c;这个问题很复杂&#xff0c;先补充一版我写的 import org.apache.spark.{SparkConf, SparkContext} import org.apache.spark.sql.{Row, SparkSession} import org.apache.spark.sql.func…

Unity inspector绘制按钮与Editor下生成与销毁物体的方法 反射 协程 Editor

应美术要求&#xff0c;实现一个在编辑环境下&#xff0c;不运行&#xff0c;可以实例化预制体的脚本 效果如上图所示 1.去实现一个简单的 行、列实例化物体脚本 2.在Inspector下提供按钮 3.将方法暴露出来&#xff08;通过自定义标签实现&#xff09; 需求一 using System.C…

单片机Freertos入门(二)任务的创建、删除

1、串口配置 首先将串口进行配置&#xff0c;后续经常会应用&#xff0c;具体步骤点击&#xff1a;串口配置。 2、任务 创建一个任务&#xff0c;就是开辟一个空间、每个任务中都会有while&#xff08;1&#xff09;死循环。 2.1相关函数 动态创建&#xff1a;xTaskCreate…

PostgreSQL常用命令

数据库版本 :9.6.6 注意 :PostgreSQL中的不同类型的权限有 SELECT,INSERT,UPDATE,DELETE,TRUNCATE,REFERENCES,TRIGGER,CREATE,CONNECT,TEMPORARY,EXECUTE 和 USAGE。 1. 登录PG数据库 以管理员身份 postgres 登陆,然后通过 #psql -U postgres #sudo -i -u postgres …