在OpenCV中基于深度学习的边缘检测

引言

如何在OpenCV中使用基于深度学习的边缘检测,它比目前流行的canny边缘检测器更精确。边缘检测在许多用例中是有用的,如视觉显著性检测,目标检测,跟踪和运动分析,结构从运动,3D重建,自动驾驶,图像到文本分析等等。

什么是边缘检测?

边缘检测是计算机视觉中一个非常古老的问题,它涉及到检测图像中的边缘来确定目标的边界,从而分离感兴趣的目标。最流行的边缘检测技术之一是Canny边缘检测,它已经成为大多数计算机视觉研究人员和实践者的首选方法。让我们快速看一下Canny边缘检测。

Canny边缘检测算法

1983年,John Canny在麻省理工学院发明了Canny边缘检测。它将边缘检测视为一个信号处理问题。其核心思想是,如果你观察图像中每个像素的强度变化,它在边缘的时候非常高。

在下面这张简单的图片中,强度变化只发生在边界上。所以,你可以很容易地通过观察像素强度的变化来识别边缘。

图片

现在,看下这张图片。强度不是恒定的,但强度的变化率在边缘处最高。(微积分复习:变化率可以用一阶导数(梯度)来计算。)

图片

Canny边缘检测器通过4步来识别边缘:

  1. 去噪:因为这种方法依赖于强度的突然变化,如果图像有很多随机噪声,那么会将噪声作为边缘。所以,使用5×5的高斯滤波器平滑你的图像是一个非常好的主意。

  2. 梯度计算:下一步,我们计算图像中每个像素的强度的梯度(强度变化率)。我们也计算梯度的方向。

图片

梯度方向垂直于边缘,它被映射到四个方向中的一个(水平、垂直和两个对角线方向)。

1、非极大值抑制:现在,我们想删除不是边缘的像素(设置它们的值为0)。你可能会说,我们可以简单地选取梯度值最高的像素,这些就是我们的边。然而,在真实的图像中,梯度不是简单地在只一个像素处达到峰值,而是在临近边缘的像素处都非常高。因此我们在梯度方向上取3×3附近的局部最大值。

图片

2、迟滞阈值化:在下一步中,我们需要决定一个梯度的阈值,低于这个阈值所有的像素都将被抑制(设置为0)。而Canny边缘检测器则采用迟滞阈值法。迟滞阈值法是一种非常简单而有效的方法。我们使用两个阈值来代替只用一个阈值:

高阈值 = 选择一个非常高的值,这样任何梯度值高于这个值的像素都肯定是一个边缘。

低阈值 = 选择一个非常低的值,任何梯度值低于该值的像素绝对不是边缘。

在这两个阈值之间有梯度的像素会被检查,如果它们和边缘相连,就会留下,否则就会去掉。

图片

迟滞阈值化

Canny 边缘检测的问题:

由于Canny边缘检测器只关注局部变化,没有语义(理解图像的内容)理解,精度有限(很多时候是这样)。

图片

Canny边缘检测器在这种情况下会失败,因为没有理解图像的上下文

语义理解对于边缘检测是至关重要的,这就是为什么使用机器学习或深度学习的基于学习的检测器比canny边缘检测器产生更好的结果。

OpenCV中基于深度学习的边缘检测

OpenCV在其全新的DNN模块中集成了基于深度学习的边缘检测技术。你需要OpenCV 3.4.3或更高版本。这种技术被称为整体嵌套边缘检测或HED,是一种基于学习的端到端边缘检测系统,使用修剪过的类似vgg的卷积神经网络进行图像到图像的预测任务。

HED利用了中间层的输出。之前的层的输出称为side output,将所有5个卷积层的输出进行融合,生成最终的预测。由于在每一层生成的特征图大小不同,它可以有效地以不同的尺度查看图像。

图片

网络结构:整体嵌套边缘检测

HED方法不仅比其他基于深度学习的方法更准确,而且速度也比其他方法快得多。这就是为什么OpenCV决定将其集成到新的DNN模块中。以下是这篇论文的结果:

图片

在OpenCV中训练深度学习边缘检测的代码

OpenCV使用的预训练模型已经在Caffe框架中训练过了,可以这样加载:

sh download_pretrained.sh

网络中有一个crop层,默认是没有实现的,所以我们需要自己实现一下。

class CropLayer(object):
    def __init__(self, params, blobs):
        self.xstart = 0
        self.xend = 0
        self.ystart = 0
        self.yend = 0

    # Our layer receives two inputs. We need to crop the first input blob
    # to match a shape of the second one (keeping batch size and number of channels)
    def getMemoryShapes(self, inputs):
        inputShape, targetShape = inputs[0], inputs[1]
        batchSize, numChannels = inputShape[0], inputShape[1]
        height, width = targetShape[2], targetShape[3]

        self.ystart = (inputShape[2] - targetShape[2]) // 2
        self.xstart = (inputShape[3] - targetShape[3]) // 2
        self.yend = self.ystart + height
        self.xend = self.xstart + width

        return [[batchSize, numChannels, height, width]]

    def forward(self, inputs):
        return [inputs[0][:,:,self.ystart:self.yend,self.xstart:self.xend]]

现在,我们可以重载这个类,只需用一行代码注册该层。

cv.dnn_registerLayer('Crop', CropLayer)

现在,我们准备构建网络图并加载权重,这可以通过OpenCV的dnn.readNe函数。

net = cv.dnn.readNet(args.prototxt, args.caffemodel)

现在,下一步是批量加载图像,并通过网络运行它们。为此,我们使用cv2.dnn.blobFromImage方法。该方法从输入图像中创建四维blob。

blob = cv.dnn.blobFromImage(image, scalefactor, size, mean, swapRB, crop)

其中:

image:是我们想要发送给神经网络进行推理的输入图像。

scalefactor:图像缩放常数,很多时候我们需要把uint8的图像除以255,这样所有的像素都在0到1之间。默认值是1.0,不缩放。

size:输出图像的空间大小。它将等于后续神经网络作为blobFromImage输出所需的输入大小。

swapRB:布尔值,表示我们是否想在3通道图像中交换第一个和最后一个通道。OpenCV默认图像为BGR格式,但如果我们想将此顺序转换为RGB,我们可以将此标志设置为True,这也是默认值。

mean:为了进行归一化,有时我们计算训练数据集上的平均像素值,并在训练过程中从每幅图像中减去它。如果我们在训练中做均值减法,那么我们必须在推理中应用它。这个平均值是一个对应于R, G, B通道的元组。例如Imagenet数据集的均值是R=103.93, G=116.77, B=123.68。如果我们使用swapRB=False,那么这个顺序将是(B, G, R)。

crop:布尔标志,表示我们是否想居中裁剪图像。如果设置为True,则从中心裁剪输入图像时,较小的尺寸等于相应的尺寸,而其他尺寸等于或大于该尺寸。然而,如果我们将其设置为False,它将保留长宽比,只是将其调整为固定尺寸大小。

在我们这个场景下:

inp = cv.dnn.blobFromImage(frame, scalefactor=1.0, size=(args.width, args.height),                 
                           mean=(104.00698793, 116.66876762, 122.67891434), swapRB=False,                 
                           crop=False)

现在,我们只需要调用一下前向方法。

net.setInput(inp)
out = net.forward()
out = out[0, 0]
out = cv.resize(out, (frame.shape[1], frame.shape[0]))
out = 255 * out
out = out.astype(np.uint8)
out=cv.cvtColor(out,cv.COLOR_GRAY2BGR)
con=np.concatenate((frame,out),axis=1)
cv.imshow(kWinName,con)

结果:

中间的图像是人工标注的图像,右边是HED的结果

中间的图像是人工标注的图像,右边是HED的结果

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

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

相关文章

多功能音乐沙漏的设计与实现

【摘要】随着当今社会快节奏生活的发展,当代大学生越来忽视时间管理的重要性,在原本计划只看几个视频只玩几个游戏的碎片化娱乐中耗费了大量的时光,对于自己原本的学习生活产生了巨大的影响。为更加有效的反映时间的流逝,特设计该…

解决VMware VCenter存储上传镜像文件失败

VMware VCSA6.7上传共享文件时提示操作失败,由于不确定的原因,操作失败。通常,当浏览器不信任证书时会发生此问题。如果您使用的是自签名证书或自定义证书,请在新的浏览器选项卡中打开下面的 URL并接受证书,然后重试操…

室内定位(WiFi/UWB/蓝牙等)技术方案概述

室内无法搜索到卫星,这样常规的GPS/北斗定位都无法使用,常规免费的只有运营商的基站定位LBS,但这个精度实在太差,一般都有几十米到几百米的偏差。因此,室内定位一直是个老大难问题。 截至目前,业界比较成熟…

深度学习黎明时期的LeNet:揭开卷积神经网络的序幕

在深度学习的历史长河中,Yann LeCun 的 LeNet 是一个里程碑式的研究成果,它为后来的卷积神经网络(Convolutional Neural Networks,CNN)的发展奠定了基础。LeNet 的诞生标志着深度学习黎明时期的到来,为人工…

在Linux中部署MeterSphere并且结合内网穿透实现远程访问本地管理页面——“cpolar内网穿透”

文章目录 前言1. 安装MeterSphere2. 本地访问MeterSphere3. 安装 cpolar内网穿透软件4. 配置MeterSphere公网访问地址5. 公网远程访问MeterSphere6. 固定MeterSphere公网地址 前言 MeterSphere 是一站式开源持续测试平台, 涵盖测试跟踪、接口测试、UI 测试和性能测试等功能&am…

VMware虚拟机网络配置详解

vmware为我们提供了三种网络工作模式,它们分别是:Bridged(桥接模式)、NAT(网络地址转换模式)、Host-Only(仅主机模式) 打开vmware虚拟机,我们可以在选项栏的“编辑”下的…

基于51单片机的超声波测距系统【程序+proteus仿真+参考论文+原理图+PCB等16个文件夹资料】

一、项目功能简介 整个设计系统由STC89C52单片机LCD1602显示模块声光报警模块存储模块超声波模块按键模块组成。 具体功能: 1、超声波测量距离,显示在LCD1602。 2、存储模块可以存储超声波报警值。 3、通过按键可设置报警值大小。 4、超声波报警距…

迁移redis数据库中的数据到另一台服务器

方案一 下面我使用的redis是用docker安装的,不是通过下载安装包安装的,所以和我安装方式不一样的小伙伴可以不看,因为很多操作是基于docker的 话不多说,直接开搞! 1.首先一定要确保两台服务器上面的redis版本要一致…

无人机遥控器方案定制_MTK平台无人设备手持遥控终端PCB板开发

随着科技的不断发展和无人机技术的逐步成熟,无人机越来越受到人们的关注。作为一种高新技术,无人机的应用范围不断拓展,包括农业、环境监测、城市规划、运输物流等领域。同时,无人机的飞行控制技术也得到了不断的优化和提升。 早…

[激光器原理与应用-15]:声光调制器(AOM:Acousto-optic modulator)

目录 第1章 概述 1.1 什么是AOM 1.2 AOM的主要参数 第2章 主要工作原理 2.1 光的调制技术 2.2 直接调制与间接调制 2.3 声光调制 2.4 声光调制工作原理 第3章 声光调制器件 3.1 声光调制器件的类型 3.2 应用 3.3 主要厂家 第4章 声光调制器系统 4.1 系统组成 …

Java(八)(可变参数,Collections,小案例:斗地主游戏小案例:斗地主游戏,Map集合,Stream流)

目录 可变参数 Collections 小案例:斗地主游戏 Map集合 Map的常用方法 map集合的遍历 键找值 键值对 Lambda 表达式 HashMap底层原理 集合的嵌套 Stream流 获取集合或数组的Stream流 Stream流的方法 可变参数 就是一种特殊的形参,定义在方法和构造器的形参列表中,…

Bitcoin SV 和 Bitcoin Core 之间首次跨链原子交换

我们已经执行了 Bitcoin SV 和 Bitcoin Core 之间的首次原子交换。 这一成就代表了比特币 SV 的重大进步,以去信任的方式促进了与其他区块链的无缝互操作性。 图片源自Gemini 在上一篇文章中,我们解释了原子交换的高级理论。 我们深入研究了使用哈希时间…

[设计模式] 常见的设计模式

文章目录 设计模式的 6 大设计原则设计模式的三大分类常见的设计模式有哪几种1. 单例模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点。(连接池)1. 饿汉式2. 懒汉式3. 双重检测 2. 工厂模式3. 观察者模式● 推模型● 拉…

Apache Doris 整合 FLINK 、 Hudi 构建湖仓一体的联邦查询入门

1.概览 多源数据目录(Multi-Catalog)功能,旨在能够更方便对接外部数据目录,以增强Doris的数据湖分析和联邦数据查询能力。 在之前的 Doris 版本中,用户数据只有两个层级:Database 和 Table。当我们需要连…

嵌入式八股 | 笔试面试 | 校招秋招 | 详细讲解

〇、前言 作者:赛博二哈 本嵌入式八股撰写初衷:当时求职翻遍了我能找到的所有八股,不论是嵌入式的,计算机基础的,C艹的,都很难看下去,细究其原因,有个最大的痛点: 大部…

Python读取Ansible playbooks返回信息

一.背景及概要设计 当公司管理维护的服务器到达一定规模后,就必然借助远程自动化运维工具,而ansible是其中备选之一。Ansible基于Python开发,集合了众多运维工具(puppet、chef、func、fabric)的优点&#x…

使用opencv的matchTemplate进行银行卡卡号识别

![字体文件](https://img-blog.csdnimg.cn/3a16c87cf4d34aceb0778c4b20ddadb2.png#pic_center import cv2 import numpy as npdef show_img(img, name"temp"):img cv2.resize(img, (0, 0), fx3, fy3)cv2.imshow(name, img)cv2.waitKey(0)cv2.destroyAllWindows()de…

242. 有效的字母异位词

这篇文章会收录到 :算法通关村第十二关-白银挑战字符串经典题目-CSDN博客 242. 有效的字母异位词 描述 : 给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。 注意:若 s 和 t 中每个字符出现的次数都相同,则称 s 和 t …

VsCode 调试 MySQL 源码

1. 启动 MySQL 2. 查看 MySQL 进程号 [root ~]# ps -ef | grep mysqld root 21479 1 0 Nov01 ? 00:00:00 /bin/sh /usr/local/mysql/bin/mysqld_safe --datadir/usr/local/mysql/data --pid-file/usr/local/mysql/data/mysqld.pid root 26622 21479 0 …

【JDK21】详解虚拟线程

目录 1.概述 2.虚拟线程是为了解决哪些问题 2.1.线程切换的巨大代价 2.2.哪些情况会造成线程的切换 2.3.线程资源是有限的 3.虚拟线程 4.适用场景 1.概述 你发任你发,我用JAVA8?JDK21可能要对这句话say no了。 现在Oracle JDK是每4个版本&#x…