【YOLOv10】使用 TensorRT C++ API 调用GPU加速部署 YOLOv10 实现 500FPS 推理速度——快到飞起!

  NVIDIA ® TensorRT ™ 是一款用于高性能深度学习推理的 SDK,包含深度学习推理优化器和运行时,可为推理应用程序提供低延迟和高吞吐量。YOLOv10是清华大学研究人员近期提出的一种实时目标检测方法,通过消除NMS、优化模型架构和引入创新模块等策略,在保持高精度的同时显著降低了计算开销,为实时目标检测领域带来了新的突破。

  在本文中,我们将演示如何使用NVIDIA TensorRT C++ API 部署YOLOv10目标检测模型,实现模型推理加速。下面看一下YOLOv10模型在TensorRT上的运行效果吧:

YOLOv10实现500FPS推理速度,快到离谱!!——使用 TensorRT C++ API 调用GPU加速部署YOLOv10实现快速预测

1. 前言

  TensorRT是NVIDIA官方推出的一个高性能深度学习推理加速引擎,它能够使深度学习模型在GPU上进行低延迟、高吞吐量的部署。TensorRT是基于CUDA和cuDNN的,专门为NVIDIA的GPU进行了优化。TensorRT支持TensorFlow、PyTorch、Caffe、MxNet等深度学习框架。对于MxNet和PyTorch,需要先将其模型转换为中间模型ONNX格式。总的来说,TensorRT是一个强大的深度学习推理加速引擎,通过优化和部署深度学习模型,能够在各种应用场景中实现快速、高效的推理性能。

tensor-rt

  YOLOv10是清华大学研究人员近期提出的一种实时目标检测方法,该方法在Ultralytics Python包的基础上进行了多项创新和改进,主要有以下特点

  1. 消除非极大值抑制(NMS):YOLOv10通过引入一致的双重分配策略,在训练时使用一对多的标签分配来提供丰富的监督信号,在推理时使用一对一的匹配,从而消除了对NMS的依赖。这一改进在保持高精度的同时,减少了推理延迟和计算量。
  2. 全面优化的模型架构:YOLOv10从推理效率和准确性的角度出发,全面优化了模型的各个组成部分。这包括采用轻量级分类头、空间通道去耦下采样和等级引导块设计等,以减少计算冗余并提高模型性能。
  3. 引入大核卷积和部分自注意模块:为了提高性能,YOLOv10在不增加大量计算成本的前提下,引入了大核卷积和部分自注意模块。
  4. 多种模型尺寸可选:官方发布了从N到X各种型号的模型,以满足不同应用的需求。这些模型包括超小型版本YOLOv10-N(用于资源极其有限环境)、小型版本YOLOv10-S(兼顾速度和精度)、中型版本YOLOv10-M(通用)、平衡型版本YOLOv10-B(宽度增加,精度更高)、大型版本YOLOv10-L(精度更高,但计算资源增加)以及超大型版本YOLOv10-X(可实现最高的精度和性能)。

  通过广泛的实验验证,YOLOv10在多个模型尺度上实现了卓越的精度-延迟权衡。例如,在COCO数据集上,YOLOv10-S在相似精度下比其他实时目标检测方法更快,同时参数和浮点运算量也大幅减少。综上所述,YOLOv10通过消除NMS、优化模型架构和引入创新模块等策略,在保持高精度的同时显著降低了计算开销,为实时目标检测领域带来了新的突破。

2. 项目开发环境

  下面简单介绍一下项目的开发环境,开发者可以根据自己的设备情况进行配置:

  • 系统平台:Windows 11
  • 开发平台:Visual Studio 2022
  • CUDA:11.4
  • CUDNN:8.2.4
  • TensorRT:8.6
  • OpenCV:4.8.0

  此处代码开发平台使用的是C++,因此在项目配置时,需要配置第三方依赖库,分别是CUDA\CUDNN、TensorRT和OpenCV三个依赖库,其配置方式此处不做详述。

3. 模型获取

3.1 源码下载

  YOLOv10 模型需要源码进行下载,首先克隆GitHub上的源码,输入以下指令:

git clone https://github.com/THU-MIG/yolov10.git
cd yolov10

3.2 配置环境

  接下来安装模型下载以及转换环境,此处使用Anaconda进行程序集管理,输入以下指令创建一个yolov10环境:

conda create -n yolov10 python=3.9
conda activate yolov10
pip install -r requirements.txt
pip install -e .

3.3 下载模型

  首先导出目标识别模型,此处以官方预训练模型为例,首先下载预训练模型文件,然后调用yolo导出ONBNX格式的模型文件,最后使用 OpenVINO™ 的模型转换命令将模型转为IR格式,依次输入以下指令即可:

wget https://github.com/jameslahm/yolov10/releases/download/v1.0/yolov10s.pt
yolo export model=yolov10s.pt format=onnx opset=13 simplify

4. engine模型转换

  首先定义ONNX模型转换Engine格式的代码,如下所示:

#include "opencv2/opencv.hpp"
#include <fstream>
#include <iostream>
#include "cuda.h"
#include "NvInfer.h"
#include "NvOnnxParser.h"class Logger : public nvinfer1::ILogger
{void log(Severity severity, const char* msg) noexcept override{if (severity <= Severity::kWARNING)std::cout << msg << std::endl;}
} logger;void onnxToEngine(const char* onnxFile, int memorySize) {// 将路径作为参数传递给函数std::string path(onnxFile);std::string::size_type iPos = (path.find_last_of('\\') + 1) == 0 ? path.find_last_of('/') + 1 : path.find_last_of('\\') + 1;std::string modelPath = path.substr(0, iPos);//获取文件路径std::string modelName = path.substr(iPos, path.length() - iPos);//获取带后缀的文件名std::string modelName_ = modelName.substr(0, modelName.rfind("."));//获取不带后缀的文件名名std::string engineFile = modelPath + modelName_ + ".engine";// 构建器,获取cuda内核目录以获取最快的实现// 用于创建config、network、engine的其他对象的核心类nvinfer1::IBuilder* builder = nvinfer1::createInferBuilder(logger);  // 构建器,获取cuda内核目录以获取最快的实现,用于创建config、network、engine的其他对象的核心类const auto explicitBatch = 1U << static_cast<uint32_t>(nvinfer1::NetworkDefinitionCreationFlag::kEXPLICIT_BATCH);  // 定义网络属性nvinfer1::INetworkDefinition* network = builder->createNetworkV2(explicitBatch);  // 解析onnx网络文件,tensorRT模型类nvonnxparser::IParser* parser = nvonnxparser::createParser(*network, logger);   // 将onnx文件解析,并填充rensorRT网络结构parser->parseFromFile(onnxFile, 2);  // 解析onnx文件for (int i = 0; i < parser->getNbErrors(); ++i) {std::cout << "load error: " << parser->getError(i)->desc() << std::endl;}printf("tensorRT load mask onnx model successfully!!!...\n");// 创建推理引擎nvinfer1::IBuilderConfig* config = builder->createBuilderConfig();  // 创建生成器配置对象。config->setMaxWorkspaceSize(1024 * 1024 * memorySize);  // 设置最大工作空间大小。config->setFlag(nvinfer1::BuilderFlag::kFP16);  // 设置模型输出精度nvinfer1::ICudaEngine* engine = builder->buildEngineWithConfig(*network, *config);  // 创建推理引擎// 将推理文件保存到本地std::cout << "try to save engine file now~~~" << std::endl;std::ofstream filePtr(engineFile, std::ios::binary);if (!filePtr) {std::cerr << "could not open plan output file" << std::endl;return;}// 将模型转化为文件流数据nvinfer1::IHostMemory* modelStream = engine->serialize();// 将文件保存到本地filePtr.write(reinterpret_cast<const char*>(modelStream->data()), modelStream->size());// 销毁创建的对象modelStream->destroy();engine->destroy();network->destroy();parser->destroy();std::cout << "convert onnx model to TensorRT engine model successfully!" << std::endl;
}

  通过调用TensorRT模型优化器,可以将ONNX模型进行优化,结合本机GPU设备,进行优化加速,并转换成TensorRT模型支持的模型格式,这一步也可以在模型推理时进行,但是模型优化需要较长时间,因此最好先将模型进行转换。定义好代码后,在主函数中调用即可,如下所示:

onnxToEngine("E:\\Text_Model\\yolov10s.onnx", 50);

5. 定义YOLOv10 Process

5.1 数据预处理

  数据预处理此处通过OpenCV实现,将输入的图片数据转为模型需要的数据情况,代码如下所示:

void preProcess(cv::Mat *img, int length, float* factor, std::vector<float>& data) {cv::Mat mat;int rh = img->rows;int rw = img->cols;int rc = img->channels();cv::cvtColor(*img, mat, cv::COLOR_BGR2RGB);int maxImageLength = rw > rh ? rw : rh;cv::Mat maxImage = cv::Mat::zeros(maxImageLength, maxImageLength,CV_8UC3);maxImage = maxImage * 255;cv::Rect roi (0, 0, rw, rh);mat.copyTo(cv::Mat(maxImage, roi));cv::Mat resizeImg;cv::resize(maxImage, resizeImg, cv::Size(length, length), 0.0f, 0.0f, cv::INTER_LINEAR);*factor = (float)((float)maxImageLength / (float)length);resizeImg.convertTo(resizeImg, CV_32FC3, 1 / 255.0);rh = resizeImg.rows;rw = resizeImg.cols;rc = resizeImg.channels();for (int i = 0; i < rc; ++i) {cv::extractChannel(resizeImg, cv::Mat(rh, rw, CV_32FC1, data.data() + i * rh * rw), i);}
}

  在调用时也相对简单,将相关变量传入即可,代码如下所示:

Mat frame = new frame();
std::vector<float> inputData(640 * 640 * 3);
float factor = 0;
preProcess(&frame, 640, &factor, inputData);

5.2 结果后处理

  首先此处定义了一个结果类:

struct DetResult {cv::Rect bbox;float conf;int lable;DetResult(cv::Rect bbox,float conf,int lable):bbox(bbox),conf(conf),lable(lable){}
};

  然后定义模型的结果处理方式,代码如下所示:

std::vector<DetResult> postProcess(float* result, float factor, int outputLength) {std::vector<cv::Rect> positionBoxes;std::vector <int> classIds;std::vector <float> confidences;// Preprocessing output resultsfor (int i = 0; i < outputLength; i++){int s = 6 * i;if ((float)result[s + 4] > 0.2){float cx = result[s + 0];float cy = result[s + 1];float dx = result[s + 2];float dy = result[s + 3];int x = (int)((cx)* factor);int y = (int)((cy)* factor);int width = (int)((dx - cx) * factor);int height = (int)((dy - cy) * factor);cv::Rect box(x, y, width, height);positionBoxes.push_back(box);classIds.push_back((int)result[s + 5]);confidences.push_back((float)result[s + 4]);}}std::vector<DetResult> re;for (int i = 0; i < positionBoxes.size(); i++){DetResult det(positionBoxes[i], confidences[i], classIds[i]);re.push_back(det);}return re;
}

  最后为了让结果可视化,定义了结果绘制方法,代码如下所示:

void drawBbox(cv::Mat& img, std::vector<DetResult>& res) {for (size_t j = 0; j < res.size(); j++) {cv::rectangle(img, res[j].bbox, cv::Scalar(255, 0, 255), 2);cv::putText(img, std::to_string(res[j].lable) + "-" + std::to_string(res[j].conf), cv::Point(res[j].bbox.x, res[j].bbox.y - 1), cv::FONT_HERSHEY_PLAIN, 1.2, cv::Scalar(0, 0, 255), 2);}
}

  上述方式调用依旧十分容易,使用代码如下所示:

std::vector<float> output_data(300 * 6);
std::vector<DetResult> result = postProcess(output_data.data(), factor, 300);
drawBbox(frame, result);

6. 模型推理实现

6.1 模型读取与创建推理通道

  首先读取上文中转换的Engine模型,并创建推理通道,用于后文的模型推理,实现代码如下所示:

std::shared_ptr<nvinfer1::IExecutionContext> creatContext(std::string modelPath) {// 以二进制方式读取问价std::ifstream filePtr(modelPath, std::ios::binary);if (!filePtr.good()) {std::cerr << "文件无法打开,请确定文件是否可用!" << std::endl;return std::shared_ptr<nvinfer1::IExecutionContext>();}size_t size = 0;filePtr.seekg(0, filePtr.end);	// 将读指针从文件末尾开始移动0个字节size = filePtr.tellg();	// 返回读指针的位置,此时读指针的位置就是文件的字节数filePtr.seekg(0, filePtr.beg);	// 将读指针从文件开头开始移动0个字节char* modelStream = new char[size];filePtr.read(modelStream, size);// 关闭文件filePtr.close();nvinfer1::IRuntime* runtime = nvinfer1::createInferRuntime(logger);nvinfer1::ICudaEngine* engine = runtime->deserializeCudaEngine(modelStream, size);return std::shared_ptr<nvinfer1::IExecutionContext>(engine->createExecutionContext());
}

6.2 Yolov10 推理代码

  下面结合一个视频推理,编写TensorRT推理YOLOv10的流程,代码如下所示:

#include "opencv2/opencv.hpp"
#include <fstream>
#include <iostream>
#include "cuda.h"
#include "NvInfer.h"
#include "NvOnnxParser.h"
class Logger : public nvinfer1::ILogger
{void log(Severity severity, const char* msg) noexcept override{// suppress info-level messagesif (severity <= Severity::kWARNING)std::cout << msg << std::endl;}
} logger;void yolov10Infer() {const char* videoPath = "E:\\Text_dataset\\car_test.mov";const char* enginePath = "E:\\Text_Model\\yolov10s.engine";std::shared_ptr<nvinfer1::IExecutionContext> context = creatContext(enginePath);cv::VideoCapture capture(videoPath);// 检查摄像头是否成功打开if (!capture.isOpened()) {std::cerr << "ERROR: 视频无法打开" << std::endl;return;}cudaStream_t stream;cudaStreamCreate(&stream);void* inputSrcDevice;void* outputSrcDevice;cudaMalloc(&inputSrcDevice, 3 * 640 * 640 * sizeof(float));cudaMalloc(&outputSrcDevice, 1 * 300 * 6 * sizeof(float));std::vector<float> output_data(300 * 6);std::vector<float> inputData(640 * 640 * 3);while (true){cv::Mat frame;if (!capture.read(frame)) {break;}float factor = 0;preProcess(&frame, 640, &factor, inputData);cudaMemcpyAsync(inputSrcDevice, inputData.data(), 3 * 640 * 640 * sizeof(float), cudaMemcpyHostToDevice, stream);void* bindings[] = { inputSrcDevice, outputSrcDevice };context->enqueueV2((void**)bindings, stream, nullptr);cudaMemcpyAsync(output_data.data(), outputSrcDevice, 300 * 6 * sizeof(float),cudaMemcpyDeviceToHost, stream);cudaStreamSynchronize(stream);std::vector<DetResult> result = postProcess(output_data.data(), factor, 300);drawBbox(frame, result); imshow("读取视频", frame);cv::waitKey(10);	//延时30}cv::destroyAllWindows();
}

  通过上诉代码便可以实现使用NVIDIA TensorRT C++部署YOLOv10实现GPU加速。

7. 总结

  在本文中,我们将演示如何使用NVIDIA TensorRT C++ API 部署YOLOv10目标检测模型,实现模型推理加速。最后我们对模型推理速度进行了测试,测试结果如下所示:

PreProcessInferencePostProcess
Time (ms)7.591.356.95

  在上述中已经提供了项目实现的全部源码,但都是零散的,如果大家在使用中有疑问,可以下载项目源码文件,下载链接为:

https://download.csdn.net/download/Grape_yan/89396724

  最后如果各位开发者在使用中有任何问题,欢迎大家与我联系。

个人账号 - 2

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

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

相关文章

WWDC24即将到来,ios18放大招

苹果公司即将在下周开全球开发者大会(WWDC)&#xff0c;大会上将展示其人工智能技术整合到设备和软件中的重大进展,包括与OpenAI的历史性合作。随着大会的临近,有关iOS 18及其据称采用AI技术支持的应用程序和功能的各种泄露信息已经浮出水面。 据报道,苹果将利用其自主研发的大…

Java 8 中的 Stream API,用于处理集合数据

Java 8 引入了 Stream API&#xff0c;使得处理集合数据变得更加简洁和高效。Stream API 允许开发者以声明式编程风格操作数据集合&#xff0c;而不是使用传统的迭代和条件语句。 一、基本概念 1.1 什么是 Stream Stream 是 Java 8 中的一个新抽象&#xff0c;它允许对集合数…

创新实训2024.06.03日志:完善Baseline Test框架、加入对Qwen-14B的测试

1. Baseline Test框架重构与完善 在之前的一篇博客中&#xff08;创新实训2024.05.29日志&#xff1a;评测数据集与baseline测试-CSDN博客&#xff09;&#xff0c;我介绍了我们对于大模型进行基线测试的一些基本想法和实现&#xff0c;包括一些基线测试的初步结果。 后来的一…

PS初级|写在纸上的字怎么抠成透明背景?

前言 上一次咱们讲了很多很多很多的抠图教程&#xff0c;这次继续。。。最近有小伙伴问我&#xff1a;如果是写在纸上的字&#xff0c;要怎么把它抠成透明背景。 这个其实很简单&#xff0c;直接来说就是选择通道来抠。但有一点要注意的是&#xff0c;写在纸上的字&#xff0…

算法-分治策略

概念 分治算法&#xff08;Divide and Conquer&#xff09;是一种解决问题的策略&#xff0c;它将一个问题分解成若干个规模较小的相同问题&#xff0c;然后递归地解决这些子问题&#xff0c;最后合并子问题的解得到原问题的解。分治算法的基本思想是将复杂问题分解成若干个较…

Java使用GDAL来解析KMZ及KML实战

目录 前言 一、在GQIS中浏览数据 1、关于空间参考 2、属性表格 二、GDAL的相关驱动及解析实战 1、GDAL中的KMZ驱动 2、GDAL实际解析 三、数据解析成果 1、KML解析结果 2、KMZ文件入库 四、总结 前言 在前面的博客中讲过纯Java实现Google地图的KMZ和KML文件的解析&…

python - DataFrame查询数据操作

学习目标 掌握获取df一列或多列数据的方法 知道loc和iloc的区别以及使用方法 知道df的query函数的使用方法 知道isin函数的作用和使用方法 获取DataFrame子集的基本方法 1.1 从前从后获取多行数据 案例中用到的数据集在文章顶部 LJdata.csv 前景回顾 head() & tail(…

范闲获取到庆帝与神庙的往来信件,用AES进行破解

关注微信公众号 数据分析螺丝钉 免费领取价值万元的python/java/商业分析/数据结构与算法学习资料 在《庆余年2》中&#xff0c;范闲与庆帝和神庙之间的权谋斗争愈演愈烈。一次偶然的机会&#xff0c;范闲从庆帝的密室中获取到几封与神庙往来的密信。然而&#xff0c;这封信件…

美团面试:百亿级分片,如何设计基因算法?

尼恩说在前面 在40岁老架构师 尼恩的读者交流群(50)中&#xff0c;最近有小伙伴拿到了一线互联网企业如得物、阿里、滴滴、极兔、有赞、希音、百度、网易、美团的面试资格&#xff0c;遇到很多很重要的架构类/设计类的场景题&#xff1a; 1.说说分库分表的基因算法&#xff1f…

使用matplotlib绘制折线条形复合图

使用matplotlib绘制折线条形复合图 介绍效果代码 介绍 在数据可视化中&#xff0c;复合图形是一种非常有用的工具&#xff0c;可以同时显示多种数据类型的关系。在本篇博客中&#xff0c;我们将探讨如何使用 matplotlib 库来绘制包含折线图和条形图的复合图。 效果 代码 imp…

【Linux】进程2——管理概念,进程概念

1.什么是管理&#xff1f; 那在还没有学习进程之前&#xff0c;就问大家&#xff0c;操作系统是怎么管理进行进程管理的呢&#xff1f; 很简单&#xff0c;先把进程描述起来&#xff0c;再把进程组织起来&#xff01; 我们拿大学为例子 最典型的管理者——校长最典型的被管理…

短视频矩阵源码----如何做正规开发规则分享:

一、什么是SaaS化服务技术开发&#xff1f; &#xff08;短视频矩阵系统是源头开发的应该分为3个端口---- 总后台控制端、总代理端口&#xff0c;总商户后台&#xff09; SaaS是软件即服务&#xff08;Software as a Service&#xff09;的缩写。它是一种通过互联网提供软件应…

MySQL查询相邻两条记录的时间间隔

MySQL查询相邻两条记录的时间间隔。最近需要统计相邻两条记录的时间间隔&#xff0c;筛选出时间间隔大于2min的数据记录。因为是同一张表&#xff0c;又需要查询出相邻的数据&#xff0c;所以最开始想到使用子表来做&#xff0c;分别用t1、t2表示&#xff0c;但是实践后发现查询…

如何查看本地sql server数据库的ip地址

程序连线SQL数据库&#xff0c;需要SQL Server实例的名称或网络地址。 1.查询语句 DECLARE ipAddress VARCHAR(100) SELECT ipAddress local_net_address FROM sys.dm_exec_connections WHERE SESSION_ID SPID SELECT ipAddress As [IP Address]SELECT CONNECTIONPROPERTY(…

鸢尾花分类和手写数字识别(K近邻)

鸢尾花分类 from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split import pandas as pd import mglearn# 加载鸢尾花数据集 iris load_iris() X_train, X_test, y_train, y_test train_test_split(iris.data,iris.target,test_siz…

【Linux】Centos7升级内核的方法:yum更新(ELRepo)

&#x1f60e; 作者介绍&#xff1a;我是程序员洲洲&#xff0c;一个热爱写作的非著名程序员。CSDN全栈优质领域创作者、华为云博客社区云享专家、阿里云博客社区专家博主。 &#x1f913; 同时欢迎大家关注其他专栏&#xff0c;我将分享Web前后端开发、人工智能、机器学习、深…

路径

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 用于定位一个文件或者目录的字符串被称为一个路径。在程序开发时&#xff0c;通常涉及两种路径&#xff0c;一种是相对路径&#xff0c;另一种是绝对…

Intel VT-x怎么开启?如何解决VMware打开虚拟机报错问题?

许多小伙伴在安装完VMware不能打开虚拟机&#xff0c;每次打开都会出现一个“此主机支持 Intel VT-x&#xff0c;但 Intel VT-x 处于禁用状态”的报错&#xff0c;然后因此启动不了虚拟机。今天小编就带来如何解决这个报错的方法。 什么是Intel VT-x&#xff1f; 这是英特尔cp…

黑龙江等保测评流程

黑龙江的等保测评过程是一个系统严谨的过程&#xff0c;目的在于保证信息系统的安全与机密性符合国家规定的要求。下面将详细介绍黑龙江等保测评的流程&#xff1a; 一、定级与备案 首先&#xff0c;企业要依据自身的业务特点、信息系统的重要性和所承载的信息的敏感程度&…

【Text2SQL 论文】C3:使用 ChatGPT 实现 zero-shot Text2SQL

论文&#xff1a;C3: Zero-shot Text-to-SQL with ChatGPT ⭐⭐⭐⭐ arXiv:2307.07306&#xff0c;浙大 Code&#xff1a;C3SQL | GitHub 一、论文速读 使用 ChatGPT 来解决 Text2SQL 任务时&#xff0c;few-shots ICL 的 setting 需要输入大量的 tokens&#xff0c;这有点昂贵…