yolov5-7.0模型DNN加载函数及参数详解(重要)

yolov5-7.0模型DNN加载函数及参数详解(重要)

  • 引言
  • yolov5(v7.0)
    • 1,yolov5.h(加载对应模型里面的相关参数要更改)
    • 2,main主程序
      • (1)加载网络
      • (2)检测推理(forward)
        • (2.1)制作黑背景正方形,用于blobFromImage的无损缩放
        • (2.2)blobFromImage改变图像格式为网络出入格式
        • (2.3)输入setInput图像并forward推理
        • (2.4)结果解析
          • (2.4.0)方法0直接Mat读取解析(主要用于理解输出结构)
          • (2.4.1)方法1指针法Mat.data解析(快但不易理解)
        • (2.5)绘制结果

引言

  使用opencv的dnn模块对应yolov5(v7.0)的导出的onnx模型进行推理解析,明确各个参数的对应含义及结果。理论上,有了onnx模型,有了该网络模型的输入输出各个参数含义,就可以使用任意的可以读取onnx模型的部署框架进行部署推理。

yolov5(v7.0)

Yolov5(v7.0)
部署:使用opencv4.5.5(opencv4.5.4以上可以读取网络),dnn模块部署

已经导出的cpu、opset=12、640大小图像、默认静态
在这里插入图片描述

如上,输入输出层在后面的DNN模块推理会用到

1,yolov5.h(加载对应模型里面的相关参数要更改)

需要修改的相关参数如下:要根据自己的模型来进行
在这里插入图片描述
在这里插入图片描述

#include <fstream>
#include <sstream>
#include <iostream>
#include <opencv2/dnn.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
using namespace cv;
using namespace dnn;
using namespace std;class YOLO
{
public:struct Detection{int class_id;float confidence;Rect box;};
public:YOLO();~YOLO();bool loadNet(string model_path, bool is_cuda);Mat formatYolov5(const Mat& source);void detect(Mat& image, vector<Detection>& output);void drawRect(Mat& image, vector<Detection>& output);
private:Net m_net;//修改为训练时自己模型Img大小float inputWidth = 640;float inputHeight = 640;//修改 dimensions = 类别数 + 5const int dimensions = 6;//修改 通过Netron可查看,图片大小320,rows为6300,图片大小640,rows为25200const int rows = 25200;float scoreThreshold = 0.2;float nmsThreshold = 0.4;float confThreshold = 0.4;
public://修改为自己的类别数const vector<string> m_classNames = { "qrcode" };/*const std::vector<std::string> m_classNames = { "person", "bicycle", "car", "motorcycle", "airplane", "bus", "train", "truck", "boat", "traffic light","fire hydrant", "stop sign", "parking meter", "bench", "bird", "cat", "dog", "horse", "sheep", "cow","elephant", "bear", "zebra", "giraffe", "backpack", "umbrella", "handbag", "tie", "suitcase", "frisbee","skis", "snowboard", "sports ball", "kite", "baseball bat", "baseball glove", "skateboard", "surfboard","tennis racket", "bottle", "wine glass", "cup", "fork", "knife", "spoon", "bowl", "banana", "apple","sandwich", "orange", "broccoli", "carrot", "hot dog", "pizza", "donut", "cake", "chair", "couch","potted plant", "bed", "dining table", "toilet", "tv", "laptop", "mouse", "remote", "keyboard", "cell phone","microwave", "oven", "toaster", "sink", "refrigerator", "book", "clock", "vase", "scissors", "teddy bear","hair drier", "toothbrush" };*/const vector<Scalar> colors = { Scalar(255, 255, 0), Scalar(0, 255, 0), Scalar(0, 255, 255), Scalar(255, 0, 0) };
};

2,main主程序

int main(int argc, char** argv)
{Mat frame = imread("2.jpeg");YOLO yolov5;//加载模型yolov5.loadNet("qrcode.onnx", false);std::vector<YOLO::Detection> output;//进行检测//检测时间DWORD time_start, time_end;/* 获取开始时间 */time_start = GetTickCount(); //从操作系统启动经过的毫秒数yolov5.detect(frame, output);time_end = GetTickCount();int time = (time_end - time_start);cout << "Time = " << (time_end - time_start) << "ms\n ";//将所耗时间显示到图片上putText(frame, format("time:%dms", time), Point(20, 50), FONT_HERSHEY_SIMPLEX, 1.2, Scalar(255, 0,0), 2);//绘制结果yolov5.drawRect(frame, output);imshow("output", frame);waitKey(0);return 0;
}

(1)加载网络

加载已经导出的onnx网络,打印各个层级参数,此时网络读取成功(opencv要在4.5.4以上)
bool YOLO::loadNet(string model_path, bool is_cuda)
{try {m_net = readNet(model_path);//获取各层信息vector<string> layer_names = m_net.getLayerNames();		//此时我们就可以获取所有层的名称了,有了这些可以将其ID取出for (int i = 0; i < layer_names.size(); i++) {int id = m_net.getLayerId(layer_names[i]);			//通过name获取其idauto layer = m_net.getLayer(id);						//通过id获取layerprintf("layer id:%d,type:%s,name:%s\n", id, layer->type.c_str(), layer->name.c_str());	//将每一层的id,类型,姓名打印出来(可以明白此网络有哪些结构信息了)}}catch (const std::exception&) {cout << "load faild" << endl;return false;}if (is_cuda){cout << "Attempty to use CUDA\n";m_net.setPreferableBackend(DNN_BACKEND_CUDA);m_net.setPreferableTarget(DNN_TARGET_CUDA_FP16);}else{cout << "Running on CPU\n";m_net.setPreferableBackend(DNN_BACKEND_OPENCV);m_net.setPreferableTarget(DNN_TARGET_CPU);}return true;
}

在这里插入图片描述

(2)检测推理(forward)

推理前要对图像进行预处理,使其变为网络的输入格式

(2.1)制作黑背景正方形,用于blobFromImage的无损缩放

也可以不制作黑背景,但后面blobFromImage缩放计算可能结果有损失

//制作黑背景正方形,用于blobFromImage的无损缩放
//对于长宽比过大的图片,由于opencv的blobFromImage()函数在缩放的时候不是无损缩放,
//会导致图像变形严重导致结果错误或者漏检。虽然blobFromImage里面有个参数可以保持比例缩放,
//但是只会保留中间的部分,两边信息全部丢掉,所以如果你的目标全部在中间就可以无所谓,
//如果不是,那么需要简单的自己做个无损缩放,制作一张全黑的3通道正方形图片,边长为原图的长边,
//最后将原图放在(0,0)的位置上面,这样就可以直接输入blobFromImage里面就可以实现无损缩放了,
//而且不用对检测结果进行二次修正位置了。
//https ://blog.csdn.net/qq_34124780/article/details/116464727
Mat YOLO::formatYolov5(const Mat& source)
{int col = source.cols;int row = source.rows;int _max = MAX(col, row);Mat result = Mat::zeros(_max, _max, CV_8UC3);source.copyTo(result(Rect(0, 0, col, row)));return result;
}
(2.2)blobFromImage改变图像格式为网络出入格式
 Mat blob;auto input_image = formatYolov5(image);blobFromImage(input_image, blob, 1. / 255., Size(inputWidth, inputHeight), Scalar(), true, false);//1. / 255.数据归一化,图像大小变为输入格式(640*640)true进行通道交换,false不进行切片

此时经过blobFromImage的图像就变成4维了,在imagewatch中不显示
在这里插入图片描述
在这里插入图片描述

(2.3)输入setInput图像并forward推理
m_net.setInput(blob);
vector<Mat> outputs;
m_net.forward(outputs, m_net.getUnconnectedOutLayersNames());

Forward函数有很多,可以得到如下,有的是将所有的输出结果得到(有时输出层有多个),有的是将对应层的输出结果得到,这里直接得到所有最终输出结果就行了
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

实际这里推理只有1个输出结果

 cout << "outputs.size():" << outputs.size() << endl;

在这里插入图片描述

(2.4)结果解析

实际此时推理输出为[1252006]共三维的图像,使用指针的方法的话二维还是三维都会展开成一维指针进行读取
在这里插入图片描述

输出结果如下dims=3维度
在这里插入图片描述

将其转换成Mat可以显示的方式,用于查看对应的效果
在这里插入图片描述

效果如下
在这里插入图片描述

(2.4.0)方法0直接Mat读取解析(主要用于理解输出结构)

在这里插入图片描述

与指针相比,通过mat数组获取对应的第4列的数据是对应的起来的,如下应该使用32F数据格式即可(要将其结果转为(25200*6格式的数据保存到mat中))
在这里插入图片描述

上图最右侧的第6列的数据是类的分数(虽然显示1,但实际上分值是浮点数接近1的)
在这里插入图片描述

三维的
在这里插入图片描述

//使用Mat进行相关数据的保存解析(这里主要通过mat用于理解对应的输出结构)
void YOLO::detect2(Mat& image, vector<Detection>& output) {Mat blob;auto input_image = formatYolov5(image);blobFromImage(input_image, blob, 1. / 255., Size(inputWidth, inputHeight), Scalar(), true, false);//1. / 255.数据归一化,图像大小变为输入格式(640*640)true进行通道交换,false不进行切片m_net.setInput(blob);vector<Mat> outputs;m_net.forward(outputs, m_net.getUnconnectedOutLayersNames());//结果解析//方法0、使用Mat进行解析//***********输出测试//几个输出结果cout << "outputs.size():" << outputs.size() << endl;/*Mat detect_res = m_net.forward("output0");cout << "detect_res.channels():" << detect_res.size() << endl;cout << "outputs[0].channels():" << outputs[0].size() << endl;*///对其结果使用下方的重新复制一个MAT,这样的才能在imagewatch中看到,否则无法显示Mat d1 = outputs[0].clone();//重新定义一个Mat对象接收(将其转换成输出结构)(rows = 25200, dimensions=6)Mat detectionMat2(rows, dimensions, CV_32F, d1.ptr<float>());		//此处是初始化一个行为size[2],深度为浮点型,数据从detection中获取	//使用imagewatch可以看到6*25200的数组,单通道32F深度cout << "detect_res.rows:" << detectionMat2.rows << endl;//先得到原始用于blobFromImage归一化的input_image图像与/640的比例,后面用于目标框的结果返回float x_factor1 = float(input_image.cols) / inputWidth;   //用于blobFromImage归一化的input_image图像/640,得到对应的比例float y_factor1 = float(input_image.rows) / inputHeight;  //用于blobFromImage归一化的input_image图像/640vector<int> class_ids1;vector<float> confidences1;vector<Rect> boxes1;//25200行的数据(对应的onnx的输出参数)for (int i = 0; i < detectionMat2.rows; i++) {float confidence1 = detectionMat2.at<float>(i, 4);         //置信度方框box的得分分值//cout << "confidence1:" << confidence1 << endl;if (confidence1 >= confThreshold)    //求置信度>某个值{//cout << "confidence1:" << confidence1 << endl;//求类别得分将第一个类别后面的所有的类别作为一个mat一行//制作一个vector对象,//循环从类别后面截取各个类的分值,并保存vector<float> classes_scores1;for (int j = 5; j < detectionMat2.cols; j++) {classes_scores1.push_back(detectionMat2.at<float>(i, j));}//求classes_scores1的最大值及对应的索引auto max_class_score = max_element(classes_scores1.begin(), classes_scores1.end());int idMax = max_class_score - classes_scores1.begin();//cout << "类别的分数值:" << *max_class_score << "id:" << idMax << endl;if (*max_class_score > scoreThreshold){confidences1.push_back(confidence1);class_ids1.push_back(idMax);//前4行对应的(物体的x、y、w、h)(这里的x、y、w、h结果只是图像640*640的结果的,要返回)float x = detectionMat2.at<float>(i, 0);float y = detectionMat2.at<float>(i, 1);float w = detectionMat2.at<float>(i, 2);float h = detectionMat2.at<float>(i, 3);//将x、y、w、h结果返回(由640*640的结果返回到原图size()大小的结果)int left = int((x - 0.5 * w) * x_factor1);//(x_factor1*x)int top = int((y - 0.5 * h) * y_factor1);int width = int(w * x_factor1);             //宽高直接*对应比列即可int height = int(h * y_factor1);boxes1.push_back(Rect(left, top, width, height));   //得到最终原图对应的矩形框大小}}}cout << "boxes.size():" << boxes1.size() << endl;//NMS结果非极大值抑制(有些结果是重合的,此处通过nms去除)vector<int> nms_result; //保存对应的索引值NMSBoxes(boxes1, confidences1, scoreThreshold, nmsThreshold, nms_result);for (int i = 0; i < nms_result.size(); i++){int idx = nms_result[i];Detection result;result.class_id = class_ids1[idx];result.confidence = confidences1[idx];result.box = boxes1[idx];output.push_back(result);}
}
(2.4.1)方法1指针法Mat.data解析(快但不易理解)
//使用指针进行相关的结果解析(更快速,但不易理解)(相关取值计算过程和上方一样,可以看detect2进行理解)
void YOLO::detect(Mat& image, vector<Detection>& output)
{Mat blob;auto input_image = formatYolov5(image);blobFromImage(input_image, blob, 1. / 255., Size(inputWidth, inputHeight), Scalar(), true, false);//1. / 255.数据归一化,图像大小变为输入格式(640*640)true进行通道交换,false不进行切片m_net.setInput(blob);vector<Mat> outputs;m_net.forward(outputs, m_net.getUnconnectedOutLayersNames());//方法1、使用指针进行解析(1*25200*6)float x_factor = float(input_image.cols) / inputWidth;   //用于blobFromImage归一化的input_image图像/640,得到对应的比例float y_factor = float(input_image.rows) / inputHeight;  //用于blobFromImage归一化的input_image图像/640float* data = (float*)outputs[0].data;  //获取输出结果的初始指针vector<int> class_ids;vector<float> confidences;vector<Rect> boxes;for (int i = 0; i < rows; ++i){float confidence = data[4];          //置信度方框box的分值if (confidence >= confThreshold)    //求置信度{float* classes_scores = data + 5;   //求类别得分//求类别得分将第一个类别后面的所有的类别作为一个mat一行(x、y、w、h、confidence、class1、class2、、、)//一行,m_classNames.size()个长度Mat scores(1, m_classNames.size(), CV_32FC1, classes_scores);//求当前最大分数的类别及其索引Point class_id;double max_class_score;minMaxLoc(scores, 0, &max_class_score, 0, &class_id);if (max_class_score > scoreThreshold){confidences.push_back(confidence);class_ids.push_back(class_id.x);//boxfloat x = data[0];float y = data[1];float w = data[2];float h = data[3];int left = int((x - 0.5 * w) * x_factor);int top = int((y - 0.5 * h) * y_factor);int width = int(w * x_factor);int height = int(h * y_factor);boxes.push_back(Rect(left, top, width, height));}}data += dimensions;  //data隔着对应结果(类别数1+5(x、y、w、h))}//NMS结果非极大值抑制vector<int> nms_result;NMSBoxes(boxes, confidences, scoreThreshold, nmsThreshold, nms_result);for (int i = 0; i < nms_result.size(); i++){int idx = nms_result[i];Detection result;result.class_id = class_ids[idx];result.confidence = confidences[idx];result.box = boxes[idx];output.push_back(result);}
}
(2.5)绘制结果

就是将矩形框、分值等数据绘制到对应的图像上

void YOLO::drawRect(Mat& image, vector<Detection>& output)
{int detections = output.size();for (int i = 0; i < detections; ++i){auto detection = output[i];auto box = detection.box;auto classId = detection.class_id;const auto color = colors[classId % colors.size()];rectangle(image, box, color, 3);rectangle(image, Point(box.x, box.y - 40), Point(box.x + box.width, box.y), color, FILLED);string label = m_classNames[classId] + ":" + to_string(output[i].confidence);putText(image, label, Point(box.x, box.y - 5), FONT_HERSHEY_SIMPLEX, 1.5, Scalar(0, 0, 0), 2);}}

下面是Yolov5s.onnx对应模型的结果

在这里插入图片描述

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

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

相关文章

AVL树如何维持平衡

1.AVL树的特性 二叉搜索树虽可以缩短查找的效率&#xff0c;但如果数据有序或接近有序二叉搜索树将退化为单支树&#xff0c;查 找元素相当于在顺序表中搜索元素&#xff0c;效率低下。因此&#xff0c;两位俄罗斯的数学家G.M.Adelson-Velskii 和E.M.Landis在1962年 发明了一种…

【万字长文】Word2Vec计算详解(一)CBOW模型

【万字长文】Word2Vec计算详解&#xff08;一&#xff09;CBOW模型 写在前面 本文用于记录本人学习NLP过程中&#xff0c;学习Word2Vec部分时的详细过程&#xff0c;本文与本人写的其他文章一样&#xff0c;旨在给出Word2Vec模型中的详细计算过程&#xff0c;包括每个模块的计…

【redis-06】redis的stream流实现消息中间件

redis系列整体栏目 内容链接地址【一】redis基本数据类型和使用场景https://zhenghuisheng.blog.csdn.net/article/details/142406325【二】redis的持久化机制和原理https://zhenghuisheng.blog.csdn.net/article/details/142441756【三】redis缓存穿透、缓存击穿、缓存雪崩htt…

Auto-Animate:是一款零配置、即插即用的动画工具,可以为您的 Web 应用添加流畅的过渡效果

嗨&#xff0c;大家好&#xff0c;我是小华同学&#xff0c;关注我们获得“最新、最全、最优质”开源项目和高效工作学习方法 用户体验成为了检验产品成功与否的关键因素。而动画效果&#xff0c;作为提升用户体验的重要手段&#xff0c;在网页和应用开发中扮演着举足轻重的角色…

同望OA tooneAssistantAttachement.jsp 任意文件读取漏洞复现

0x01 产品简介 同望OA,即同望科技打造的智企云协同管理系统,是一款高效的企业协同移动办公系统。秉承“互联网++企业管理”理念,定位于以移动互联办公为基础的企业协同管理软件平台。它旨在通过内置常用标准模块与专项管理模块应用,安全快速地打通管理与业务通道,实现管理…

QT 实现QMessageBox::about()信息自定义显示

这是我记录Qt学习过程的第四篇心得文章&#xff0c;主要是方便自己编写的应用程序显示“关于信息”&#xff0c;对QMessageBox::about()输入信息进行规范&#xff0c;可以设置应用程序名称&#xff0c;通过定义宏从pro文件获取应用程序版本号&#xff0c;以及编译程序的QT版本、…

写一个代码:打印100~200之间的素数

我们要输出100-200之间的素数&#xff0c;首先我们先得输出100-200之间的数字&#xff0c;一般用于遍历循环的数字要用到for循环&#xff0c;同时在输出的100~200之间的数字进行判断是不是素数&#xff0c;我们知道素数的判断条件在于当一个数字从1开始到自己本身的时候&#x…

2024年最新(AI绘画)Stable Diffusion4.9下载及安装教程.

软件介绍 Stable Diffusion 是一款在图像生成领域具有重大影响力的软件。 从工作原理上看&#xff0c;它利用深度学习的先进算法&#xff0c;构建起复杂且强大的神经网络架构。其核心在于能够解读用户输入的文本信息&#xff0c;并将这些信息转化为图像的特征与细节。 在使用…

【C++网络编程】(一)Linux平台下TCP客户/服务端程序

文章目录 Linux平台下TCP客户/服务端程序服务端客户端相关头文件介绍 Linux平台下TCP客户/服务端程序 图片来源&#xff1a;https://subingwen.cn/linux/socket/ 下面实现一个Linux平台下TCP客户/服务端程序&#xff1a;客户端向服务器发送&#xff1a;“你好&#xff0c;服务…

攻防世界(CTF)~Reverse-easyRE1

题目介绍 下载附件后一个32位一个64位 64位的放到ExeinfoPE查看一下有无壳子&#xff08;无壳&#xff09; 放IDA看一下伪代码&#xff0c;习惯性看一下main函数&#xff0c;直接发现了flag flag{db2f62a36a018bce28e46d976e3f9864}

手撕数据结构 —— 单链表(C语言讲解)

目录 1.为什么要有链表 2.什么是链表 3.链表的分类 4.无头单向非循环链表的实现 SList.h中接口总览 具体实现 链表节点的定义 打印链表 申请结点 尾插 头插 尾删 头删 查找 在pos位置之前插入 在pos位置之后插入 删除pos位置 删除pos位置之后的值 5.完整代码…

理解Web3的互操作性:不同区块链的连接

随着Web3的迅速发展&#xff0c;互操作性成为区块链技术中的一个核心概念。互操作性指的是不同区块链之间能够无缝地交流和共享数据&#xff0c;从而实现更加高效和灵活的生态系统。本文将探讨Web3中互操作性的意义、面临的挑战以及未来的发展趋势。 1. 互操作性的意义 在Web…

如何用深度神经网络预测潜在消费者

1. 模型架构 本项目采用的是DeepFM模型&#xff0c;其结构结合了FM&#xff08;因子分解机&#xff09;与深度神经网络&#xff08;DNN&#xff09;&#xff0c;实现了低阶与高阶特征交互的有效建模。模型分为以下几层&#xff1a; 1.1 FM部分&#xff08;因子分解机层&#…

MinIO分片上传超大文件(纯服务端)

目录 一、MinIO快速搭建1.1、拉取docker镜像1.2、启动docker容器 二、分片上传大文件到MinIO2.1、添加依赖2.2、实现MinioClient2.3、实现分片上传2.3.0、初始化MinioClient2.3.1、准备分片上传2.3.2、分片并上传2.3.2.1、设置分片大小2.3.2.2、分片 2.3.3、分片合并 三、测试3…

Vscode+Pycharm+Vue.js+WEUI+django火锅(三)理解Vue

新创建的Vue项目里面很多文件&#xff0c;对于新手&#xff0c;老老实实做一下了解。 1.框架逻辑 框架的逻辑都是相通的&#xff0c;花点时间理一下就清晰了。 2.文件目录及文件 创建好的vue项目下&#xff0c;主要的文件和文件夹要先认识一下&#xff0c;并与框架逻辑对应起…

计算机网络803-(4)网络层

目录 1.虚电路服务 虚电路是逻辑连接 2.数据报服务 3.虚电路服务与数据报服务的对比 二.虚拟互连网络-IP网 1.网络通信问题 2.中间设备 3.网络互连使用路由器 三.分类的 IP 地址 1. IP 地址及其表示方法 2.IP 地址的编址方法 3.分类 IP 地址 &#xff08;1&#x…

使用 Go 和 Gin 框架构建简单的用户和物品管理 Web 服务

使用 Go 和 Gin 框架构建简单的用户和物品管理 Web 服务 在本项目中&#xff0c;我们使用 Go 语言和 Gin 框架构建了一个简单的 Web 服务&#xff0c;能够管理用户和物品的信息。该服务实现了两个主要接口&#xff1a;根据用户 ID 获取用户名称&#xff0c;以及根据物品 ID 获…

蓝桥杯【物联网】零基础到国奖之路:十七. 扩展模块之单路ADC和NE555

蓝桥杯【物联网】零基础到国奖之路:十七. 扩展模块之单路ADC和NE555 第一节 硬件解读第二节 CubeMx配置第三节 代码1&#xff0c;脉冲部分代码2&#xff0c;ADC部分代码![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/57531a4ee76d46daa227ae0a52993191.png) 第一节 …

EasyExcel读入数字类型数据时出现小数位丢失精度问题

这里写自定义目录标题 问题现象解决方案 问题现象 目前使用easyExcel读取导入文档时发现文档中的小数值4076204076.65会被读取为4076204076.6500001 尝试去查看了excel解压后的文件&#xff0c;发现这条数据在xml里存储的值就是4076204076.6500001&#xff0c;即是excel存储小…

利用 Python 爬虫采集 1688商品详情

1688是中国的一个大型B2B电子商务平台&#xff0c;主要用于批发和采购各种商品。对于需要从1688上获取商品详情数据、工程数据或店铺数据的用户来说&#xff0c;可以采用以下几种常见的方法&#xff1a; 官方API接口&#xff1a;如果1688提供了官方的API接口&#xff0c;那么可…