解决Opencv dnn模块无法使用onnx模型的问题(将onnx的动态输入改成静态)

一、问题来源

最近做人脸识别项目,想只用OpenCV自带的人脸检测和识别模块实现,使用OpenCV传统方法:Haar级联分类器人脸检测+LBPH算法人脸识别的教程已经有了,于是想着用OpenCV中的dnn模块来实现,dnn实现人脸检测也有(详细教程可见我的这篇博客https://blog.csdn.net/weixin_42149550/article/details/131474284),问题就是基于cnn的人脸识别咋用opencv的dnn模块实现?一番搜索,发现OpenCV的dnn模块在加载YuNet模型时会报错
从官网下载的模型文件:
在这里插入图片描述

# 加载人脸检测模型
faceDetector = cv2.FaceDetectorYN.create('./opencvmodels/yunet.onnx', '', (320, 320))
# 加载人脸识别模型
faceRecognizer = cv2.FaceRecognizerSF_create(model='./opencvmodels/face_recognizer_fast.onnx', config='')

yunet模型加载会报错,face_recognizer_fast加载没有问题
报错提示:
在这里插入图片描述
重点看最后一句话:
error: (-215:Assertion failed) !isDynamicShape in function ‘cv::dnn::dnn4_v20221220::ONNXImporter::parseShape’

这句报错是说加载的onnx模型是动态的,但是OpenCV的cv2.FaceDetectorYN.create()不支持加载动态的模型,这里的动态指的是模型没有指定固定的输入参数。

我们通过netron(https://netron.app/)来查看yunet.onnx的模型结构(部分)
在这里插入图片描述
在这里插入图片描述
从图中可以看到,官网上下载的模型结构batch_size,height,width都是未知的,这在OpenCV中是不支持的,虽然问题知道了,但是在网上查了几天都没找到解决办法!!!
PS:遇到bug真的不能陷进去,放一放,平静一下,第二天说不定就能解决了

二、解决办法

非常感谢这几篇文章,让我对onnx模型有了更多的了解,最后终于摸索出来(把自己给感动了)
如何修改已有的ONNX模型 https://tool.4xseo.com/a/15892.html
模型部署入门教程(五):ONNX 模型的修改与调试
https://zhuanlan.zhihu.com/p/516920606?utm_medium=social&utm_oi=800114666985648128&utm_id=0
官网讨论
https://github.com/opencv/opencv/issues/23288


先看onnx解释:
ONNX 在底层是用 Protobuf 定义的。Protobuf,全称 Protocol Buffer,是 Google 提出的一套表示和序列化数据的机制。使用 Protobuf 时,用户需要先写一份数据定义文件,再根据这份定义文件把数据存储进一份二进制文件。可以说,数据定义文件就是数据类,二进制文件就是数据类的实例。

神经网络本质上是一个计算图。计算图的节点是算子,边是参与运算的张量。而通过可视化 ONNX 模型,我们知道 ONNX 记录了所有算子节点的属性信息,并把参与运算的张量信息存储在算子节点的输入输出信息中。事实上,ONNX 模型的结构可以用类图大致表示如下:
在这里插入图片描述
我们把yunet.onnx模型先加载进来

# 使用onnx模块
model = onnx.load('./opencvmodels/yunet.onnx')
onnx.checker.check_model(model) # 验证Onnx模型是否准确

通过验证说明OpenCV提供的onnx模型是没有问题的。
接着我们打印出yunet的计算图

# 计算图太大了
print(model.graph)

在这里插入图片描述
可以看到initializer中储存了一个节点的数据信息,其中的名字name都是跟netron中看到的一一对应,因为数据太多了,看不全,我们只把计算图的ninput输入部分打印出来,看看是怎么样的

print(model.graph.input)

[name: “input”
type {
tensor_type {
elem_type: 1
shape {
dim {
dim_param: “batch_size”
}
dim {
dim_value: 3
}
dim {
dim_param: “height”
}
dim {
dim_param: “width”
}
}
}
}
]

可以看到输入结构在onnx中是一个可迭代的列表容器,包含了name名字,type类型,type中又定义了输入的维度,现在的思路就是要把shape中的dim_param: "height"和dim_param: "width"换成固定的值即dim_value,我们一层一层来打印:

for input_node in model.graph.input:print(input_node)

name: “input”
type {
tensor_type {
elem_type: 1
shape {
dim {
dim_param: “batch_size”
}
dim {
dim_value: 3
}
dim {
dim_param: “height”
}
dim {
dim_param: “width”
}
}
}
}

我们遍历输入结构,可以看到外层的括号去掉了,我们接着向下层索引:

for input_node in model.graph.input:print(input_node.type)

tensor_type {
elem_type: 1
shape {
dim {
dim_param: “batch_size”
}
dim {
dim_value: 3
}
dim {
dim_param: “height”
}
dim {
dim_param: “width”
}
}
}

for input_node in model.graph.input:print(input_node.type.tensor_type.shape)

dim {
dim_param: “batch_size”
}
dim {
dim_value: 3
}
dim {
dim_param: “height”
}
dim {
dim_param: “width”
}

那么思路就有了,我们只要索引到dim这一层,然后把其中的dim_param: "height"和dim_param: "width"替换成固定的dim_value: 320就可以了

for input_node in model.graph.input:# print(input_node.type.tensor_type.shape)input_node.type.tensor_type.shape.dim[2].dim_value = 320input_node.type.tensor_type.shape.dim[3].dim_value = 320print(input_node.type.tensor_type.shape)

想要的结果出现了!!!

dim {
dim_param: “batch_size”
}
dim {
dim_value: 3
}
dim {
dim_value: 320
}
dim {
dim_value: 320
}

接下来我们重新验证模型有没有问题,然后将模型保存成新的模型

onnx.checker.check_model(model)
onnx.save(model, 'opencvmodels/new_yunet.onnx')

我们重新在netron中查看一下模型结构
在这里插入图片描述
新的yunet模型的输入已经改成固定输入尺寸,下面我们来测试一下模型能不能用

recognize_net = cv2.dnn.readNetFromONNX('./opencvmodels/new_yunet.onnx')
dnn_faceDetector = cv2.FaceDetectorYN_create(model="./opencvmodels/new_yunet.onnx", config="", input_size=(320, 320))

没有报错!!! 呜呜呜~~~~

来看一下人脸识别效果
(借用彭于晏哥哥的照片)
在这里插入图片描述
人脸对齐
cv2.FaceDetectorYN还封装了人脸对齐,超级方便有木有

face1_align = faceRecognizer.alignCrop(image1, face1_detect[1])

在这里插入图片描述
人脸检测
检测的准确率很高!!

在这里插入图片描述
人脸匹配结果,判断出这是同一个人,这里使用了两种距离匹配方法:余弦距离和L2距离,结果一样。
大功告成!!!

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

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

相关文章

Fisher辨别分析

问题要求 在UCI数据集上的Iris和Sonar数据上验证算法的有效性。训练和测试样本有三种方式(三选一)进行划分: (一) 将数据随机分训练和测试,多次平均求结果 (二)K折交叉验证 &…

【苍穹外卖 | 项目日记】第三天

前言: 今天状态不错,kuku就是写接口,很舒服 目录 前言: 今日完结任务: 今日收获: 杂项知识点: 总结: 今日完结任务: 实现了新增菜品接口实现了菜品分页查询接口实现…

如何基于先进视频技术,构建互联网视频监控安全管理平台解决方案

一、建设思路 依托互联网,建设一朵云,实现各类二三类视频资源统一接入,实现天网最后100米、10米、1米的全域覆盖。 依托人工智能与互联网技术,拓展视频资源在政府、社会面等多领域的全面应用;建设与运营模式并存&…

软件测试工具有什么作用?有哪些好用的测试工具推荐?

软件测试工具是现代软件测试中不可或缺的重要组成部分,指的是一系列在软件开发过程中使用的工具,用于帮助测试人员进行测试活动,提高测试效率,减少测试成本。选择并使用合适的软件测试工具,可提高软件质量和效率。 一…

WebRTC 系列(四、多人通话,H5、Android、iOS)

WebRTC 系列(三、点对点通话,H5、Android、iOS) 上一篇博客中,我们已经实现了点对点通话,即一对一通话,这一次就接着实现多人通话。多人通话的实现方式呢也有好几种方案,这里我简单介绍两种方案…

应用在SMPS中的GaN/氮化镓

开关模式电源(Switch Mode Power Supply,简称SMPS),又称交换式电源、开关变换器,是一种高频化电能转换装置,是电源供应器的一种。其功能是将一个位准的电压,透过不同形式的架构转换为用户端所需…

【2023】M1/M2 Mac 导入Flac音频到Pr的终极解决方案

介绍 原作者链接:https://github.com/fnordware/AdobeOgg 很早之前就发现了这个插件,超级好用,在windows上完全没有问题,可惜移植到mac就不行了(然后我给作者发了一个Issue,后来就有大佬把m1的编译出来了&…

②. GPT错误:图片尺寸写入excel权限错误

꧂问题最初 ꧁ input输入图片路径 print图片尺寸 大小 长宽高 有颜色占比>0.001的按照大小排序将打印信息存储excel表格文件名 表格路径 图片大小 尺寸 颜色类型 占比信息input输入的是文件就处理文件 是文件夹📁就处理文件。路径下的图片 1. 是处理本路径图片 …

狄拉克函数及其性质

狄拉克函数及其性质 狄拉克函数 近似处理 逼近近似 积分近似 狄拉克函数的性质 狄拉克函数的Hermite展开

构建图像金字塔:探索 OpenCV 的尺度变换技术

构建图像金字塔:探索 OpenCV 的尺度变换技术 引言什么是图像金字塔?为什么需要图像金字塔?构建高斯金字塔构建拉普拉斯金字塔图像金字塔的应用示例:在不同尺度下检测图像中的边缘 结论 引言 在计算机视觉领域,图像金字…

ROS-PX4仿真笔记_1

offbord模式测试 rosrun offboard_pkg position stablelize模式 lqr控制器实验 roslaunch px4 fast_test.launch 无人机起飞1.5-2m sh mybot_gazebo.sh先点击mode,再点击cmd,才能打开offbord模式 minijerk实验 roslaunch px4 fast_test.launch sh …

电子科大软件系统架构设计——系统架构设计

文章目录 系统架构设计系统设计概述系统设计定义系统设计过程系统设计活动系统设计基本方法系统设计原则系统设计方法分类面向对象系统分析与设计建模过程 系统架构基础系统架构定义系统架构设计定义系统架构作用系统架构类型系统总体架构系统拓扑架构系统拓扑架构类型系统拓扑…

读书笔记:多Transformer的双向编码器表示法(Bert)-4

多Transformer的双向编码器表示法 Bidirectional Encoder Representations from Transformers,即Bert; 第二部分 探索BERT变体 从本章开始的诸多内容,以理解为目标,着重关注对音频相关的支持(如果有的话)…

Docker基础操作容器

启动容器有两种方式,一种是基于镜像新建一个容器并启动,另外一个是将在终止状态(exited)的容器重新启动。 因为 Docker 的容器实在太轻量级了,很多时候用户都是随时删除和新创建容器。 新建并启动 所需要的命令主要…

使用tailwindcss来构建以及引入外部组件

使用tailwindcss来构建以及引入外部组件 使用tailwindcss来构建以及引入外部组件 前言构建组件 核心思想可行方案不可行方案 可行方案详解 custom css selector Functions & Directivesadd prefixadd scoped不打包 构建demo链接相关issues 前言 我们在日常的开发中&am…

1、AM64xx的SDK重新编译lib文件

当需要修改AM64XX的SDK提供的源文件时,如果要在自己的工程使用,需要重新编译出lib,下面是编译lib的具体方法: 因为没有ccs编译出lib的工程,所以需要再命令行模式下生成lib文件 1、配置好gmake环境 如果安装了ccs&am…

隔离上网,安全上网

SDC沙盒数据防泄密系统(安全上网,隔离上网) •深信达SDC沙盒数据防泄密系统,是专门针对敏感数据进行防泄密保护的系统,根据隔离上网和安全上网的原则实现数据的代码级保护,不会影响工作效率,不…

SP605官方开发板不能扫到链的问题

很早之前的板子,近些天需要重新搞FPGA,所以又拿出来,应该以前都是在win7下开发,现在都win10了,vivado都不支持sp6,所以先得下载一个14.7版本,但是出现了新的问题,就是不能扫到链。 …

本文整理了Debian 11在国内的几个软件源。

1.使用说明 一般情况下,将/etc/apt/sources.list文件中Debian默认的软件仓库地址和安全更新仓库地址修改为国内的镜像地址即可,比如将deb.debian.org和security.debian.org改为mirrors.xxx.com,并使用https访问,可使用…

sqli-lab靶场通关

文章目录 less-1less-2less-3less-4less-5less-6less-7less-8less-9less-10 less-1 1、提示输入参数id,且值为数字; 2、判断是否存在注入点 id1报错,说明存在 SQL注入漏洞。 3、判断字符型还是数字型 id1 and 11 --id1 and 12 --id1&quo…