边缘检测

  • 一、边缘检测
  • 二、边缘检测算子
    • 2.1、Sobel算子
    • 2.2、Scharr算子
    • 2.3、Laplacian算子
  • 三、Canny边缘检测
    • 3.1、Canny边缘检测的步骤
    • 3.2、Canny算法的实现

一、边缘检测

边缘是指图像中像素的灰度值发生剧烈变化的区域:
在这里插入图片描述
图像中的边缘主要有以下几种成因:

  1. 表面不连续:两个面的交界处会自然形成边缘
  2. 深度不连续:主要是视觉因素
  3. 颜色不连续:两种不同颜色的交汇处会形成边缘
  4. 照明不连续:受光线影响形成的阴影会产生边缘

边缘检测方法主要有以下两大类:

  • 通过灰度值曲线一阶导数的最大值来寻找边缘,如Sobel算子、Scharr算子、Prewitt算子、roberts算子等
  • 通过灰度值曲线二阶导数过零点来寻找边缘,如Laplacian算子、Canny边缘检测等

二、边缘检测算子

2.1、Sobel算子

Sobel算子是通过一阶导数的最大值进行边缘检测的,用Sobel算子进行边缘检测的步骤如下:

1. 将图像与x方向的Sobel算子进行卷积。x方向的Sobel算子(尺寸3*3)如下:

-1 -2  1
-2  0  2
-1  0  1

2. 将图像与y方向的Sobel算子进行卷积。y方向的Sobel算子(尺寸3*3)如下:

-1 -2 -10  0  01  2  1

3. 对图像中的像素计算近似梯度幅度:
4. 统计极大值所在位置,获得图像的边缘:

Sobel算子有着不同的尺寸和阶次。如果想自己生成Sobel算子,则可以用getDerivKernels()函数实现:

//生成边缘检测用的滤波器。ksize=CV_SCHARR时生成的是Scharr滤波器,其余情况下生成的是Sobel滤波器
void Imgproc.getDerivKernels(Mat kx, Mat ky, int dx, int dy, int ksize, boolean normalize, int ktype)
  • kx:行滤波器的输出矩阵,类型为ktype
  • ky:列滤波器的输出矩阵,类型为ktype
  • dx:x方向上导数的阶次
  • dy:y方向上导数的阶次
  • ksize:生成滤波器的尺寸,可选参数有CV_SCHARR或者1、3、5、7
  • normalize:是否对滤波器系数进行归一化。如果要滤波器的图像的数据类型是浮点型,则一般需要进行归一化;如果处理的是8位图像,结果存储在16位图像中并希望保留所有的小数部分,则需要将normalize设为false
  • ktype:滤波器系数的类型,可以是CV_32F或CV_64F

该函数智能生成Sobel或Scharr算子。事实上,Sobel()函数和Scharr()函数内部调用的就是这个函数。

//用Sobel算子进行边缘检测
void Imgproc.Sobel(Mat src, Mat dst, int ddepth, int dx, int dy, int ksize)
  • src:输入图像
  • dst:输出图像,和src具有相同的尺寸和通道数
  • ddepth:输出图像的深度
  • dx:x方向求导的阶数,通常只能是0或1。如果dx为0,则表示x方向上没有求导
  • dy:y方向求导的阶数,通常只能是0或1。如果dy为0,则表示y方向上没有求导
  • ksize:Sobel算子的尺寸,只能是1、3、5或7

由于图像的边缘可能从高灰度值变为低灰度值,也可能从低灰度值变为高灰度值,所以用Sobel()函数计算的结果可能为正也可能为负。为了正确地显示图像,还需要用convertScaleAbs()函数将计算结果转为绝对值:

//计算矩阵中数值的绝对值,并转换为8位数据类型,可在此过程中进行缩放。对矩阵中每个数据和函数依次执行三项操作:缩放、求绝对值、转换为CV_8U类型。如为多通道矩阵,函数则需对每个通道独立进行处理
void Core.convertScaleAbs(Mat src, Mat dst, double alpha)
  • src:输入矩阵
  • dst:输出矩阵
  • alpha:缩放因子,可选
public class Sobel {static {OpenCV.loadLocally(); // 自动下载并加载本地库}public static void main(String[] args) {//读取图像灰度图并显示Mat src = Imgcodecs.imread("F:/IDEAworkspace/opencv/src/main/java/demo/butterfly.png", Imgcodecs.IMREAD_GRAYSCALE);HighGui.imshow("src", src);HighGui.waitKey(0);Mat grad = new Mat();Mat gx = new Mat();Mat gy = new Mat();Mat abs_gx = new Mat();Mat abs_gy = new Mat();//提取x方向边缘Imgproc.Sobel(src, gx, -1, 1, 0);Core.convertScaleAbs(gx, abs_gx);//提取y方向边缘Imgproc.Sobel(src, gy, -1, 0, 1);Core.convertScaleAbs(gy, abs_gy);//显示x和y方向边缘HighGui.imshow("Sobel-X", gx);HighGui.waitKey(0);HighGui.imshow("Sobel-Y", gy);HighGui.waitKey(0);//计算整副图像的边缘并显示Core.addWeighted(abs_gx, 0.5, abs_gy, 0.5, 0, grad);HighGui.imshow("Sobel", grad);HighGui.waitKey(0);//直接计算整幅图像边缘Mat all = new Mat();Imgproc.Sobel(src, all, -1, 1, 1);HighGui.imshow("all", all);HighGui.waitKey(0);System.exit(0);}
}

原图灰度图:
在这里插入图片描述

X方向边缘:
在这里插入图片描述

Y方向边缘:
在这里插入图片描述

整图边缘:
在这里插入图片描述

一次计算整幅图边缘:
在这里插入图片描述

2.2、Scharr算子

用Sobel算计进行边缘检测的效率较高,但它有一个缺点:当Sobel算子尺寸较小时精度比较低。如果Sobel滤波器的尺寸为33且梯度方向接近水平或垂直方向,则问题会变得愈发明显。为了解决这个问题,OpenCV引进了Scharr算子。Scharr算子其实是一个特殊尺寸33的滤波器,在getDerivKernels()函数中将ksize设为CV_SCHARR时就是Scharr算子。当滤波器尺寸为3*3时,使用Scharr算子的速度与Sobel算子的速度一样,但是准确度更高。

//x方向的Scharr算子
-3  0  3
-10 0 10
-1  0  3//y方向Scharr算子
-3  -10  -30    0   03   10   3

Scharr算子的滤波器尺寸只能是3*3,因为它的产生就是为了解决Sobel算子在该尺寸的问题

//用Scharr算子进行边缘检测
void Imgproc.Scharr(Mat src, Mat dst, int ddepth, int dx, int dy)
  • src:输入图像
  • dst:输出图像,和src具有相同的尺寸和通道数
  • ddepth:输出图像的深度
  • dx:x方向求导的阶数,通常只能是0或1。如果dx为0,则表示x方向上没有求导
  • dy:y方向求导的阶数,通常只能是0或1。如果dy为0,则表示y方向上没有求导
public class Scharr {static {OpenCV.loadLocally(); // 自动下载并加载本地库}public static void main(String[] args) {//读取图像灰度图并显示Mat src = Imgcodecs.imread("/Users/acton_zhang/J2EE/MavenWorkSpace/opencv_demo/src/main/java/demo1/butterfly.png", Imgcodecs.IMREAD_GRAYSCALE);HighGui.imshow("src", src);HighGui.waitKey(0);Mat grad = new Mat();Mat gx = new Mat();Mat gy = new Mat();Mat abs_gx = new Mat();Mat abs_gy = new Mat();//提取x方向边缘Imgproc.Scharr(src, gx, -1, 1, 0);Core.convertScaleAbs(gx, abs_gx);//提取y方向边缘Imgproc.Scharr(src, gy, -1, 0, 1);Core.convertScaleAbs(gy, abs_gy);//在屏幕上显示X与Y方向边缘HighGui.imshow("Scharr-X", gx);HighGui.waitKey(0);HighGui.imshow("Scharr-Y", gy);HighGui.waitKey(0);//计算整幅图的边缘并显示Core.addWeighted(abs_gx, 0.5, abs_gy, 0.5, 0, grad);HighGui.imshow("Scharr", grad);HighGui.waitKey(0);System.exit(0);}
}

原图:
在这里插入图片描述

X方向边缘:
在这里插入图片描述

Y方向边缘;
在这里插入图片描述

整图边缘:

在这里插入图片描述

2.3、Laplacian算子

Sobel算子和Scharr算子进行边缘检测的效率较高,但是它们具有方向性,需要先分别在x方向和y方向求导,然后根据两个结果经计算后才可以得到图像的边缘。Laplacian算子则没有方向性,不需要分方向计算。Laplacian算子和Sobel算子、Scharr算子的另一个区别是:Laplacian算子是一个基于二阶导数的边缘检测算子。ksize=1时的Laplacian算子如下:

0  1  0
1 -4  1
0  1  1
//用Laplacian算子进行边缘检测
void Imgproc.Laplacian(Mat src, Mat dst, int ddepth, int ksize)
  • src:输入图像
  • dst:输出图像,和src具有相同的尺寸和通道数
  • ddepth:输出图像的深度
  • ksize:滤波器尺寸,必须为正奇数
public class Laplacian {static {OpenCV.loadLocally(); // 自动下载并加载本地库}public static void main(String[] args) {//读取图像灰度图并显示Mat src = Imgcodecs.imread("/Users/acton_zhang/J2EE/MavenWorkSpace/opencv_demo/src/main/java/demo1/butterfly.png", Imgcodecs.IMREAD_GRAYSCALE);HighGui.imshow("src", src);HighGui.waitKey(0);//高斯滤波后用Laplacian算子提取边缘Mat dst = new Mat();Imgproc.GaussianBlur(src, dst, new Size(3, 3), 5);Imgproc.Laplacian(src, dst, 0, 3);Core.convertScaleAbs(dst, dst);//显示HighGui.imshow("Laplacian", dst);HighGui.waitKey(0);System.exit(0);}
}

原图:

在这里插入图片描述

Laplacian算子边缘:
在这里插入图片描述

三、Canny边缘检测

Canny边缘检测算法源自John F.Canny于1986年发表的论文,论文中提出了以下3个评价最优边缘检测的标准:

  1. 准确检测:算法能尽可能多地标识出图像的实际边缘,而遗漏或错标的边缘点应尽可能少
  2. 精确定位:检测出的边缘点的位置应与实际边缘中心尽可能接近
  3. 单次响应:每个边缘位置只能标识一次

3.1、Canny边缘检测的步骤

1. 平滑降噪:

在Canny边缘检测中,一般使用高斯平滑滤波器进行平滑降噪。高斯滤波器考虑了像素离滤波器中心的距离因素,距离越近权重越大,距离越远权重越小。以下是一个5*5的高斯滤波器:

2  4  5  4  2
4  9 12  9  4
5 12 15 12  5    *   1/139
4  9 12  9  4
2  4  5  4  2

2. 梯度计算:
计算图像中每像素的梯度幅值和方向,主要分以下两步:

  1. 用Sobel算子分别检测x方向和y方向的边缘
  2. 计算梯度的幅值和方向。为了简化起见,梯度方向取0°、45°、90°和135°这四个值
    在这里插入图片描述

3. 非极大值抑制:

上一步得到的梯度图像存在边缘较粗及噪声干扰等问题,此时可以用非极大值抑制来影除非边缘的像素。Canny 中的非极大值抑制是沿着梯度方向对幅值进行比较,如图所示。图中A点位于边缘附近,箭头方向为梯度方向。选择梯度方向上A点附近的像素B和C来检验A点的梯度值是否为极大值,若为极大值,则A保留为(候选)边缘点,否则A点被抑制。由此可见,所谓非极大值抑制就是将不是极大值的 候选点予以剔除的过程。

在这里插入图片描述

4. 双阈值处理:

经过以上三步之后得到的边缘质量已经很高了,但还是存在一些伪边缘,因此Canny算法用双阈值法对边缘进行筛选。双阌值法设置 minVal 和 maxVal 两个阈值,当候选的边缘点的梯度幅值高于 maxVal 时被认为是真正的边界,当低于 minVal 时则被抛弃:如果介于两者之间,则要看这个点是否与某个被确定为真正的边界的像素相连,如果是,则认定为边界点,否则该点被抛弃。

如图所示,由于 A 点高于 maxVal,所以是真正的边界点;由C点虽然低于 maxVal 但高于minVal 并且与 A 点相连,所以也是真正的边界点,而 B 点介于 minVal 和 maxVal 之间,但没有与真正的边界点相连,因而被抛弃。为了达到较好的效果,选择合适的 maxVal 和 minVal 值非常重要。

在这里插入图片描述

3.2、Canny算法的实现

//用Canny算法进行边缘检测
void Imgproc.Canny(Mat image, Mat edges, double threshold1, double threshold2, int apertureSize)
  • image:8位输入图像
  • edges:输出的边缘图像,必须是8位单通道图像,尺寸与输入图像相同
  • threshold1:阈值1
  • threshold2:阈值2。threshold1和threshold2谁大谁小没有规定,系统会自动选择较大值为maxVal,较小值为minVal
  • apertureSize:Sobel算子的尺寸

Canny边缘检测的过程虽然较为复杂,但是经过OpenCV封装后的Canny()函数却非常简单:

public class Canny {static {OpenCV.loadLocally(); // 自动下载并加载本地库}public static void main(String[] args) {//读取图像灰度图并显示Mat src = Imgcodecs.imread("/Users/acton_zhang/J2EE/MavenWorkSpace/opencv_demo/src/main/java/demo1/butterfly.png", Imgcodecs.IMREAD_GRAYSCALE);HighGui.imshow("src", src);HighGui.waitKey(0);//进行Canny边缘检测并显示Mat dst = new Mat();Imgproc.GaussianBlur(src, src, new Size(3, 3), 5);Imgproc.Canny(src, dst, 60, 200);HighGui.imshow("Canny", dst);HighGui.waitKey(0);System.exit(0);}
}

原图:

在这里插入图片描述

Canny算法检测:

在这里插入图片描述

可以看出Canny边缘检测的效果非常好。无论是Sobel算子、Scharr算子还是Laplacian算子检测的边缘都比较模糊,而Canny算法得出的边缘非常请清晰。当然,为了得到较好的边缘,Canny算法耗费的时间也比较长。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/911900.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/911900.shtml
英文地址,请注明出处:http://en.pswp.cn/news/911900.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2506认证资讯|工信部出手整治多品牌充电宝,WMC上海稍逊往年,RED修订Common Charger,WiFi7 FCC测试

01 — 中国 工信部拟制定移动电源强制性国家标准 该标准将从以下方面全面提升移动电源安全性: 1. 拟在GB 31241、GB 4943.1基础上,新增或加严过充电、针刺等试验要求。 2. 拟提出影响电池安全的正负极材料、隔膜等关键材料要求。 3. 拟规范锂离子电池…

Linux Regulator 子系统核心逻辑与关键问题全解析

Linux Regulator 子系统核心逻辑与关键问题全解析 一、什么是 regulator 子系统?核心作用? regulator 子系统是 Linux 内核为板级/SoC 多路可控电源设计的统一电源管理框架。它的主要作用是: 为每一路可控电源(Buck、LDO、DCDC …

制造业官网3D应用,让产品会“说话”

在当今数字化时代,装备制造业正经历着前所未有的变革。随着消费升级和国内经济的蓬勃发展,中国社会的经济格局从传统的“工业经济”向多元化的“服务经济”转型。装备制造业作为制造业与服务业融合的核心领域,积极探索全新的“服务化”发展模…

SCAU15--气球狂欢节

15 气球狂欢节 Time Limit:1000MS Memory Limit:65535K 题型: 编程题 语言: G;GCC 描述: 一个充满魔法的国度中,存在一场年度的节日,名为“气球狂欢节”。在这个节日中,有一个传统的比赛,那就是“气球挑战赛”…

python打卡day56@浙大疏锦行

知识点回顾: 假设检验基础知识 原假设与备择假设P值、统计量、显著水平、置信区间 白噪声 白噪声的定义自相关性检验:ACF检验和Ljung-Box 检验偏自相关性检验:PACF检验 平稳性 平稳性的定义单位根ADF检验: 越小越平稳 季节性检验 ACF检验序列…

采集文章+原创AI处理+发布网站详细教程

简数采集器是新一代的网站文章采集和发布平台,完全在线配置和使用云采集,功能强大,操作简单,配置快捷高效。 简数不仅提供网页文章采集、数据批量处理、定时采集、定时定量自动发布等基本功能,还集成强大的SEO工具与接…

Hystrix超时降级机制全解析

Hystrix的超时降级实现主要通过以下核心机制完成,结合配置、注解和Fallback逻辑实现服务容错: 1. 超时触发条件 默认超时时间:Hystrix默认超时阈值为1秒,超过该时间未响应则触发降级。自定义配置:可通过HystrixComman…

6月份最新代发考试战报:思科华为HCIP HCSE 考试通过

6月份最新代发考试战报:思科华为HCIP HCSE 考试通过 H19-423 HCSA-Presales-IP Network 数通考试通过, H12-725 HCIP-Security安全 考试通过,H13-121 HCIP-Kunpeng Application Developer鲲鹏计算 考试通过,CCNP 350-401考试通过…

谈谈我的软考经历

我 2020 年高考进入大学,软件工程专业,去年(24年7月)毕业开始工作。我实习是在一家云计算公司,公司内部对软考的证书没有什么激励或补助之类的,我也一直认为计算机嘛,“talk is cheap&#xff0…

CVPR 2025革命性突破!可变形Mamba,刷新SOTA记录!

CVPR 2025上,众多创新研究展示了Mamba在图像分类、目标检测、语义分割等多个任务中的卓越表现。其中,可变形Mamba的最新研究成果正在不断刷新我们对视觉任务性能的认知。大连理工大学发布的DefMamba通过可变形扫描策略动态调整扫描路径,优先关…

蜂鸟代理IP+云手机:跨境电商多账号运营的“隐形风控引擎”

在亚马逊、TikTok Shop等平台的严苛风控下,跨境电商多账号运营长期面临“设备关联封号”“IP污染限流”“地域画像矛盾”三大痛点。传统方案账号存活率不足35%,而蜂鸟代理IP与云手机技术的协同,通过IP层隔离设备层虚拟化行为层仿真三重防护&a…

Boss:组件

能帮到你的话,就给个赞吧 😘 文章目录 组件Event Begin Play获取 Owner:不会报错吗,组件初始化的时候 Owner还不存在吧 Attack General:Boss普通攻击不可以连续触发:只有在当前动作为NoAction时才可以攻击 …

供应链数据可视化大屏

在全球化与数字化转型的双重浪潮下,供应链管理正面临前所未有的挑战:黑天鹅事件频发、多环节协同效率低下、库存与成本难以平衡……如何让供应链更透明、更敏捷、更具韧性?供应链数据可视化大屏应运而生,成为企业破解管理痛点的关…

XML读写数据-XPATH用法,快速定位元素

在XPath查询效率对比中,两种方式的性能差异如下: ‌绝对路径方案‌ /configuration/system.applicationHost/sites/site[nameWebSite1] 直接通过文档层级导航,避免全局扫描适合已知完整路径结构的场景,解析速度最快13 ‌相对路径…

Python 多版本与开发环境治理架构设计

Python 多版本治理理念(Windows 平台 零基础友好)-CSDN博客 Python 多版本开发环境治理:理论架构与实践-CSDN博客 Python 开发环境全栈隔离架构:从 Anaconda 到 PyCharm 的四级防护体系-CSDN博客 【零基础】Python 多版本虚拟环境…

IDE如何快速切换JLINK版本

JLINK是比较常用的调试器,因为产品维护,我们的电脑上可是装了好几个版本的JLINK,怎么进行快速的切换呢?方法如下: 1、使用Everything工具搜索JLinkDLLUpdater.exe,找到当前需要使用的JLINK版本安装目录下的…

WebSocket单例模式实现与使用

提示:记录工作中遇到的需求及解决办法 文章目录 前言一、代码二、功能说明三、使用场景 前言 前端通过WebSocket的单例模式实现实时通信效果 提示:以下是本篇文章正文内容,下面案例可供参考 一、代码 export default class SocketService …

【缓存技术】深入分析如果使用好缓存及注意事项

Java 架构师缓存深度实践指南:策略、陷阱与高并发场景实战 一、缓存设计核心策略 1. 缓存选型与场景适配 缓存选型需结合业务场景、数据规模、性能要求等多维度评估: 场景推荐方案工具/技术案例高频读、极少写本地缓存Caffeine、Guava Cache电商平台商…

wordpress Contact Form 7表单插件设置使用教程

在wordpress安装插件Contact Form 7后,前端的提示信息会根据后台的语言决定。如果你安装的是版本的wordpress程序,出来的提示信息就是英文的。如果你安装的是中文版的wordpress程序,出来的提示信息就是中文的。前端显示什么样的语言取决你安装…

Qt实现tcp通信(QTcpServer和QTcpSocket的应用)详细教程

Qt实现tcp通信(QTcpServer和QTcpSocket的应用)详细教程 服务端 监听地址和端口 ip可以是Ipv4Any,本机地址,也可以是固定的某个ip 端口号则作为服务端绑定的端口,客户端连接服务端时需要连接到服务端绑定的端口&#x…