11篇--图像边缘检测
图像梯度
要学习图像边缘检测,要先了解图像梯度的概念,我们正是通过梯度值来区分边缘像素点的
处于边缘附近的像素点与周围像素点的差距很大(不然不会有边缘呈现),所以给边缘附近的的梯度之变化很快,通过计算梯度值,来进行边缘检测。
通常有如下两种方式处理计算得到的新值:
- 截断处理:将小于0的值设置为0,将大于255的值设置为255。这种方法简单直接,但可能会导致图像在极端值处出现不自然的截断。
- 归一化处理:将计算得到的值线性映射到0到255的范围内。这种方法可以保留更多的细节信息,但可能需要额外的计算。
梯度处理方式
cv2.filter2D()函数
功能:用于对图像进行卷积操作。卷积是图像处理中的一个基本操作,它通过一个称为卷积核(或滤波器)的小矩阵在图像上滑动,并对每个位置进行加权求和,从而得到新的图像。
参数:
- src:输入图像,可以是灰度图像或彩色图像。
- ddepth:输出图像的所需深度。对于输入图像和输出图像具有相同深度的情况,该值通常设置为 -1。否则,你可以选择一个特定的深度,如 cv2.CV_8U、cv2.CV_16U、cv2.CV_32F 等。
- kernel:卷积核,一个二维数组或矩阵。卷积核的大小通常是奇数,如 3x3、5x5 等。卷积核中的每个元素都是一个权重,用于在卷积过程中与图像像素相乘。
- dst:输出图像(可选)。
- anchor:卷积核的锚点(可选)。
- delta:一个可选的附加值,它将被加到卷积结果上。这可以用于调整结果的亮度或对比度。
- borderType:边界填充。
进行边缘检测的方向取决于选取的卷积核kernel
当kernel取k1时,检测方向为垂直方向
当kernel取k2时,检测方向为水平方向
当然kernel的取值并非只有上面两种,该函数通过应用自定义的卷积核对图像进行滤波处理,可以实现各种线性滤波效果。卷积核可以看作是一种特殊的算子,但本质是函数。
注意:上面的k1,k2,正是Sobel算子(下面会讲)需要使用的卷积核,所以cv2.filter2D()函数选用上面的k1,k2作为参数kernel的取值,那么它与Sobel算子的作用效果是一样的。
那么卷积核是如何运作的呢,还跟之前一样要提前填充边缘吗?放心,不需要,计算很简单。
如下图,以垂直方向为例:
卷积核进行操作的是单信道图像,灰度图或二值图,两个极端0(黑),255(白),该卷积核进行操作目的是为进行边缘检测,所以将两个极端进行重新赋值,以便及那个边缘区分开来,下面介绍的Sobel算子,也是相同的操作,不在赘述。
实线框部分是第一次卷积,左右两边(中间列除外)各自与对赢得系数相乘,然后进行相加
(196-247)+2*(199-241)+(35-190)= -290 结果<=0,归零,将中间一列重新赋值为零
卷积核滑动,虚线部分为第二次卷积,
(243-0)+2*(245-0)+(197-0)=930 结果>0,归255,将中间列重新赋值为255
示例代码
import cv2
import numpy as np# 读取一张图
img = cv2.imread("./shudu.png")# 进行垂直梯度处理
kernel = np.array([[-1, 0, 1],[-2, 0, 2],[-1, 0, 1]])
img_filter = cv2.filter2D(img, -1, kernel)# 进行水平梯度处理
kernel1 = np.array([[-1, -2, -1],[0, 0, 0],[1, 2, 1]])
img_filter_level = cv2.filter2D(img, -1, kernel1)cv2.imshow('image', img)
cv2.imshow('img_filter', img_filter)
cv2.imshow('img_filter_level', img_filter_level)cv2.waitKey(0)
效果对比
Sobel算子
上面的两个卷积核都叫做Sobel算子,只是方向不同,它先在垂直方向计算梯度:
cv2.Sobel()函数
功能:用于计算图像梯度(gradient)的函数
参数:
- src: 输入图像,它应该是灰度图像。
- ddepth: 输出图像的所需深度(数据类型)。通常,你可以使用 -1 来表示与输入图像相同的深度,或者使用如 cv2.CV_64F 等来指定特定的深度。由于梯度计算可能产生负值,因此建议使用能够包含负数的数据类型。
- dx: x 方向上的导数阶数。如果你想要计算 x 方向上的梯度,设置这个参数为 1;如果你不关心 x 方向上的梯度,设置这个参数为 0。
- dy: y 方向上的导数阶数。如果你想要计算 y 方向上的梯度,设置这个参数为 1;如果你不关心 y 方向上的梯度,设置这个参数为 0。通常,你不会同时设置 dx 和 dy 都为 0。
- ksize: Sobel 核的大小。它必须是 1、3、5、7 或 9 之一。这个参数决定了用于计算梯度的滤波器的大小。大小为 1 时表示使用最小的滤波器,但通常你会使用更大的滤波器来平滑梯度计算。
- scale: 可选参数,表示计算梯度时的缩放因子。默认值为 1,表示不进行缩放。你可以通过调整这个参数来放大或缩小梯度的结果。
- delta: 可选参数,表示在将结果存储到目标图像之前要添加到结果中的可选增量值。默认值为 0,表示不添加增量。
- borderType: 像素外推方法,例如 cv2.BORDER_DEFAULT、cv2.BORDER_REFLECT 等。这个参数决定了在图像边界处如何处理像素外推。
示例代码
import cv2# 读取一张图
img = cv2.imread("./shudu.png")# 使用sobel算子
# 水平梯度
img_sobel = cv2.Sobel(img, -1, 0, 1, ksize=3)
# 垂直梯度
img_sobel_2 = cv2.Sobel(img, -1, 1, 0, ksize=3)cv2.imshow('image', img)
cv2.imshow('img_sobel', img_sobel)
cv2.imshow('img_sobel_2', img_sobel_2)cv2.waitKey(0)
效果对比
其他算子
Laplacian算子
下面为推导过程,了解即可可直接跳过,我们只关心最后的卷积核
在此基础上考虑斜对角情况,该算子的图像卷积模板如下:
cv2.Laplacian()函数
功能:用于计算图像的拉普拉斯算子(Laplacian)
参数:
- src: 输入图像,它应该是灰度图像。
- ddepth: 输出图像的所需深度。这个参数决定了输出图像的深度(数据类型)。通常,你可以使用 -1 来表示与输入图像相同的深度,或者使用 cv2.CV_64F 等来指定特定的深度。由于拉普拉斯算子可能产生负值,因此通常建议使用能够包含负数的数据类型,如 cv2.CV_64F。
- ksize: 算子的大小。它必须是 1、3、5 或 7 之一。这个参数决定了用于计算拉普拉斯算子的滤波器的大小。大小为 1 时表示使用 4 邻域拉普拉斯算子,其他大小则使用更大的滤波器。
- scale: 可选参数,表示计算拉普拉斯算子时的缩放因子。默认值为 1,表示不进行缩放。你可以通过调整这个参数来放大或缩小拉普拉斯算子的结果。
- delta: 可选参数,表示在将结果存储到目标图像之前要添加到结果中的可选增量值。默认值为 0,表示不添加增量。
- borderType: 像素外推方法,例如 cv2.BORDER_DEFAULT、cv2.BORDER_REFLECT 等。这个参数决定了在图像边界处如何处理像素外推。当 ksize 大于 1 时,这个参数才有意义。
示例代码
import cv2# 读取一张图
img = cv2.imread("./shudu.png")# 使用拉普拉斯算子
img_lap = cv2.Laplacian(img, -1, ksize=3)cv2.imshow('image', img)
cv2.imshow('img_lap', img_lap)cv2.waitKey(0)
效果对比
小结
Sobel算子是二阶边缘检测的典型代表
Laplacian算子是二阶边缘检测的典型代表
不过 一 / 二 阶边缘检测各有优缺点,大家可自行了解。
图像边缘检测
边缘检测要用到Canny算法,Canny边缘检测方法 常被誉为 边缘检测 的最优方法。
首先,Canny算法 处理的是 图像的二值化结果,接收到二值化图像后,需要按照如下步骤进行:
- 高斯滤波。
- 计算图像的梯度和方向。
- 非极大值抑制。
- 双阈值筛选。
下面我来介绍一下这四步
1. 高斯滤波
在前面的文章里历经详细介绍了高斯滤波,遗忘的同学,链接如下:
10篇--图像噪点消除-CSDN博客
之前提到过,低通滤波器是模糊,高通滤波器是锐化。
而边缘检测本身属于锐化操作,对噪点比较敏感,需要进行平滑处理。所以用到的高斯滤波为高通滤波器。这里使用的是一个5*5的高斯核对图像进行消除噪声:
2. 计算图像的梯度与方向
2.1 计算梯度
这里使用了Sobel算子(核值固定的卷积核)来计算图像的梯度值,如下所示:
这些是高数中二阶偏导数相关的概念,就不做赘述了。不理解也没关系,重点不在这,接着往下看
2.2 计算方向
这个角度值其实就是当前边缘的梯度的方向,与边缘的方向刚好垂直。
通过这个公式我们就可以计算出图片中所有的像素点的梯度值与梯度方向,然后根据梯度方向获取边缘的方向,获得θ。得到θ的值之后,就可以对边缘方向进行分类,一般将其归为四个方向:
水平方向、垂直方向、45°方向、135°方向:
- 当θ值为-22.5°~22.5°,或-157.5°~157.5°,则认为边缘为水平边缘;
- 当法线方向为22.5°~67.5°,或-112.5°~-157.5°,则认为边缘为45°边缘;
- 当法线方向为67.5°~112.5°,或-67.5°~-112.5°,则认为边缘为垂直边缘;
- 当法线方向为112.5°~157.5°,或-22.5°~-67.5°,则认为边缘为135°边缘;
3. 非极大值抑制
通过上面的操作,已经初步筛选出了边缘,把他们连起来不久OK了,齐活?
NO,NO,NO!本系列第10篇提到过,锐化都容易损坏边缘信息,使边缘模糊,导致经过第二步后得到的边缘像素点非常多,因此我们需要对其进行一些过滤操作。其中非极大值抑制就是一个很好的方法。
在边缘检测中,非极大值抑制的主要目的是细化边缘。具体来说,它通过对梯度图像中的像素值进行比较和筛选,只保留梯度方向上局部最大的像素值,而将其他非最大的像素值抑制为零。这样,边缘就变得更加细化和清晰,减少了冗余的边缘信息。假设当前像素点为(x,y),其梯度方向是0°,梯度值为G(x,y),那么我们就需要比较G(x,y)与两个相邻像素的梯度值:G(x-1,y)和G(x+1,y)。如果G(x,y)是三个值里面最大的,就保留该像素值,否则将其抑制为零。
并且如果梯度方向不是0°、45°、90°、135°这种特定角度,那么就要用到插值算法来计算当前像素点在其方向上进行插值的结果了,然后进行比较并判断是否保留该像素点。这里使用的是单线性插值,通过A1和A2两个像素点获得dTmp1与dTmp2处的插值,然后与中心点C进行比较。
4. 双阈值筛选
经过非极大值抑制之后,我们还需要设置阈值来进行筛选。
- 当阈值设的太低,就会出现假边缘
- 而阈值设的太高,一些较弱的边缘就会被丢掉
因此使用了双阈值来进行筛选,推荐高低阈值的比例为2 : 1到3 : 1之间,其原理如下图所示:
- 当某一像素位置的幅值超过最高阈值时,该像素必是边缘像素;
- 幅值处于最高像素与最低像素之间时,如果它能连接到一个高于阈值的边缘时,则被认为是边缘像素,否则就不会被认为是边缘;
- 当幅值低于最低像素时,该像素必不是边缘像素。
也就是说,上图中的A和C是边缘,B不是边缘。因为C虽然不超过最高阈值,但其与A相连,所以C就是边缘。
至此,Canny边缘检测就完成了。
cv2.Canny()函数
功能:用于边缘检测的函数
参数:
- image: 输入图像,它应该是一个灰度图像(单通道)。
- threshold1: 第一个阈值,用于边缘检测的滞后过程。这个值较低,用于确定边缘的初始点。
- threshold2: 第二个阈值,用于边缘检测的滞后过程。这个值较高,用于确定边缘的最终点。如果某个像素点的梯度值高于这个阈值,它被认为是边缘;如果低于这个值但高于threshold1,并且与高于threshold2的像素点相连,它也被认为是边缘。
- edges: 输出图像,与输入图像大小相同,但通常是二值图像(即只包含边缘和非边缘的像素)。
- apertureSize(可选,默认为3): Sobel算子的大小,它决定了梯度计算的邻域大小。它必须是1、3、5或7之一。
- L2gradient(可选,默认为False): 一个布尔值,指示是否使用更精确的L2范数进行梯度计算。如果为True,则使用L2范数(即欧几里得距离);如果为False,则使用L1范数(即曼哈顿距离)。L2范数通常更精确,但计算成本也更高。
示例代码
import cv2img = cv2.imread("kabuto.jpg")# 灰度化
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# 二值化
_, img_binary = cv2.threshold(img_gray, 127, 255,cv2.THRESH_BINARY + cv2.THRESH_OTSU)# 进行高斯滤波
img_blur = cv2.GaussianBlur(img_binary, (3,3), 3)# 边缘检测
img_canny = cv2.Canny(img_blur, 10, 70)cv2.imshow('img', img)
cv2.imshow('img_canny', img_canny)
cv2.waitKey(0)
效果对比
相关文章:

11篇--图像边缘检测
图像梯度 要学习图像边缘检测,要先了解图像梯度的概念,我们正是通过梯度值来区分边缘像素点的 处于边缘附近的像素点与周围像素点的差距很大(不然不会有边缘呈现),所以给边缘附近的的梯度之变化很快,通过…...

宝塔SSL证书申请失败,报错:申请SSL证书错误 module ‘OpenSSL.crypto‘ has no attribute ‘sign‘(已解决)
刚安装宝塔申请SSL就报错:申请SSL证书错误 module OpenSSL.crypto has no attribute sign 面板、插件版本:9.2.0 系统版本:Alibaba Cloud Linux 3.2104 LTS 问题:申请SSL证书错误 module OpenSSL.crypto has no attribute sign…...

(已开源) 详解4D Radar数据集K-Radar
本文介绍一个4D Radar公开数据集:KAIST-Radar(简称K-Radar,由AVELab提供)是一个新型的大规模目标检测数据集和基准测试集,包含35000帧4D雷达张量(4DRT)数据。本文主要贡献有: 本文提…...

基于RK3588机器人控制器+3D视觉传感器的送餐机器人解决方案
送餐机器人 通过搭载3D视觉传感器信迈机器人控制器,送餐机器人可以在复杂的餐厅环境中灵活避障通行,极大地提升餐品配送效率,改善用户用餐体验,并显著降低店家经营成本。 高峰期送餐难,曾一直是送餐机器人的行业痛点。…...

基于Qwen2-VL模型针对LaTeX OCR任务进行微调训练 - 数据处理
基于Qwen2-VL模型针对LaTeX OCR任务进行微调训练 - 数据处理 flyfish 基于Qwen2-VL模型针对LaTeX_OCR任务进行微调训练_-_LoRA配置如何写 基于Qwen2-VL模型针对LaTeX_OCR任务进行微调训练_-_单图推理 基于Qwen2-VL模型针对LaTeX_OCR任务进行微调训练_-_原模型_单图推理 基于Q…...

Microi吾码|开源低代码.NET、VUE低代码项目,表单引擎介绍
Microi吾码|开源低代码.NET、VUE低代码项目,表单引擎介绍 一、摘要二、Microi吾码介绍2.1 功能介绍2.2 团队介绍2.3 上线项目案例 三、Microi吾码表单引擎是什么?四、Microi吾码表单引擎功能4.1 模块引擎 - 由表单引擎驱动4.2 流程引擎 - 由表…...
[Ubuntu] Linux命令收集
1、移动文件夹内的所有文件和子文件夹: 如果你想移动一个文件夹内的所有内容到另一个目录,但不移动该文件夹本身,你可以使用以下命令: 源:/home/ubuntu/www/demo/web下的所有文件及文件夹; 目标…...

鸿蒙应用ArkTS开发-利用axios进行网络请求(实现前后端交互)
引言: 我们上一章实现了简单的登录注册页面,今天小编来带着大家实现完整的登录注册功能。 一、后端的搭建 Spring Boot介绍:Spring Boot是一个用于简化Spring应用程序开发的开源框架。它通过自动配置、内置服务器和预设的最佳实践࿰…...

【开源】使用环信UIKit for uniapp 做一个IM即时聊天应用
环信单群聊 UIKit 是基于环信即时通讯云 IM SDK 开发的一款即时通讯 UI 组件库,提供各种组件实现会话列表、聊天界面、联系人列表及后续界面等功能,帮助开发者根据实际业务需求快速搭建包含 UI 界面的即时通讯应用。 本文教大家使用环信 uniapp UIKit 快…...

计算机网络知识点全梳理(一.TCP/IP网络模型)
目录 TCP/IP网络模型概述 应用层 什么是应用层 应用层功能 应用层协议 传输层 什么是传输层 传输层功能 传输层协议 网络层 什么是网络层 网络层功能 网络层协议 数据链路层 什么是数据链路层 数据链路层功能 物理层 物理层的概念和功能 写在前面 本系列文…...
神州数码DCME-320 online_list.php存在任意文件读取漏洞
免责声明: 本文旨在提供有关特定漏洞的深入信息,帮助用户充分了解潜在的安全风险。发布此信息的目的在于提升网络安全意识和推动技术进步,未经授权访问系统、网络或应用程序,可能会导致法律责任或严重后果。因此,作者不对读者基于本文内容所采取的任何行为承担责任。读者在…...

神经网络基础-神经网络搭建和参数计算
文章目录 1.构建神经网络2. 神经网络的优缺点 1.构建神经网络 在 pytorch 中定义深度神经网络其实就是层堆叠的过程,继承自nn.Module,实现两个方法: __init__方法中定义网络中的层结构,主要是全连接层,并进行初始化。…...

Linux入门攻坚——41、Linux集群系统入门-lvs(2)
lvs-dr:GATEWAY Director只负责请求报文,响应报文不经过Director,直接由RS返回给Client。 lvs-dr的报文路线如上图,基本思路就是报文不会回送Director,第①种情况是VIP、DIP、RIP位于同一个网段,这样&…...
音视频入门基础:MPEG2-TS专题(17)——FFmpeg源码中,解析TS program map section的实现
一、引言 由《音视频入门基础:MPEG2-TS专题(16)——PMT简介》可以知道,PMT表(Program map table)由一个或多个段(Transport stream program map section,简称TS program map sectio…...
了解https原理,对称加密/非对称加密原理,浏览器与服务器加密的进化过程,https做了些什么
最开始的加密 浏览器与服务器之间需要防止传输的数据被黑客破解。因此,浏览器在发送数据时会对数据进行加密,并把加密的密钥(或密钥的某些部分)放在数据的某一个区域中。服务器收到数据后,会提取密钥并用它来解密数据…...
山西省第十八届职业院校技能大赛高职组 5G 组网与运维赛项规程
山西省第十八届职业院校技能大赛高职组 5G 组网与运维赛项规程 一、赛项名称 赛项编号:GZ035 赛项名称:5G 组网与运维 赛项组别:高职学生组、教师组 二、竞赛目的 2019 年 6 月 6 日,5G 牌照正式发放,标志着我国全面进…...
tcpdump编译 wireshark远程抓包
https://github.com/westes/flex/releases/download/v2.6.4/flex-2.6.4.tar.gz tar -zxvf flex-2.6.4.tar.gz ./configure CFLAGS-D_GNU_SOURCE make sudo make installwget http://ftp.gnu.org/gnu/bison/bison-3.2.1.tar.gz ./configure make sudo make install以上两个库是…...

Web开发 -前端部分-CSS
CSS CSS(Cascading Style Sheet):层叠样式表,用于控制页面的样式(表现)。 一 基础知识 1 标题格式 标题格式一: 行内样式 <!DOCTYPE html> <html lang"en"><head><meta…...

用 Python Turtle 绘制流动星空:编程中的璀璨星河
用 Python Turtle 绘制流动星空:编程中的璀璨星河 🐸 前言 🐸🐞往期绘画>>点击进所有绘画🐞🐋 效果图 🐋🐉 代码 🐉 🐸 前言 🐸 夜空中繁星…...

Java从入门到工作2 - IDEA
2.1、项目启动 从git获取到项目代码后,用idea打开。 安装依赖完成Marven/JDK等配置检查数据库配置启动相关服务 安装依赖 如果个别依赖从私服下载不了,可以去maven官网下载补充。 如果run时提示程序包xx不存在,在项目目录右键Marven->Re…...

【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器
一.自适应梯度算法Adagrad概述 Adagrad(Adaptive Gradient Algorithm)是一种自适应学习率的优化算法,由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率,适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...

蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练
前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...

Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具
文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...
在四层代理中还原真实客户端ngx_stream_realip_module
一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡(如 HAProxy、AWS NLB、阿里 SLB)发起上游连接时,将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后,ngx_stream_realip_module 从中提取原始信息…...
第25节 Node.js 断言测试
Node.js的assert模块主要用于编写程序的单元测试时使用,通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试,通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...

3-11单元格区域边界定位(End属性)学习笔记
返回一个Range 对象,只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意:它移动的位置必须是相连的有内容的单元格…...
Fabric V2.5 通用溯源系统——增加图片上传与下载功能
fabric-trace项目在发布一年后,部署量已突破1000次,为支持更多场景,现新增支持图片信息上链,本文对图片上传、下载功能代码进行梳理,包含智能合约、后端、前端部分。 一、智能合约修改 为了增加图片信息上链溯源,需要对底层数据结构进行修改,在此对智能合约中的农产品数…...

云原生安全实战:API网关Kong的鉴权与限流详解
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关(API Gateway) API网关是微服务架构中的核心组件,负责统一管理所有API的流量入口。它像一座…...

关于easyexcel动态下拉选问题处理
前些日子突然碰到一个问题,说是客户的导入文件模版想支持部分导入内容的下拉选,于是我就找了easyexcel官网寻找解决方案,并没有找到合适的方案,没办法只能自己动手并分享出来,针对Java生成Excel下拉菜单时因选项过多导…...

《Docker》架构
文章目录 架构模式单机架构应用数据分离架构应用服务器集群架构读写分离/主从分离架构冷热分离架构垂直分库架构微服务架构容器编排架构什么是容器,docker,镜像,k8s 架构模式 单机架构 单机架构其实就是应用服务器和单机服务器都部署在同一…...