利用 TensorRT C++ API 调用GPU加速部署 YOLOv10 实现 500FPS 推理速率—— ...

打印 上一主题 下一主题

主题 872|帖子 872|积分 2616

​    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是一个强大的深度学习推理加速引擎,通过优化和部署深度学习模型,能够在各种应用场景中实现快速、高效的推理性能。

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

  • 消除非极大值克制(NMS):YOLOv10通过引入一致的双重分配策略,在训练时利用一对多的标签分配来提供丰富的监视信号,在推理时利用一对一的匹配,从而消除了对NMS的依靠。这一改进在保持高精度的同时,减少了推理耽误和盘算量。
  • 全面优化的模型架构:YOLOv10从推理服从和准确性的角度出发,全面优化了模型的各个构成部门。这包罗采用轻量级分类头、空间通道去耦下采样和等级引导块设计等,以减少盘算冗余并提高模型性能。
  • 引入大核卷积和部门自留意模块:为了提高性能,YOLOv10在不增加大量盘算成本的条件下,引入了大核卷积和部门自留意模块。
  • 多种模型尺寸可选:官方发布了从N到X各种型号的模型,以满足不同应用的需求。这些模型包罗超小型版本YOLOv10-N(用于资源极其有限环境)、小型版本YOLOv10-S(兼顾速率和精度)、中型版本YOLOv10-M(通用)、均衡型版本YOLOv10-B(宽度增加,精度更高)、大型版本YOLOv10-L(精度更高,但盘算资源增加)以及超大型版本YOLOv10-X(可实现最高的精度和性能)。
     通过广泛的实验验证,YOLOv10在多个模型尺度上实现了卓越的精度-耽误权衡。例如,在COCO数据集上,YOLOv10-S在相似精度下比其他实时目标检测方法更快,同时参数和浮点运算量也大幅减少。综上所述,YOLOv10通过消除NMS、优化模型架构和引入创新模块等策略,在保持高精度的同时明显降低了盘算开销,为实时目标检测范畴带来了新的突破。
<img alt="image-20240604135821296" loading="lazy">

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上的源码,输入以下指令:
  1. git clone https://github.com/THU-MIG/yolov10.git
  2. cd yolov10
复制代码
3.2  设置环境

     接下来安装模型下载以及转换环境,此处利用Anaconda进行程序集管理,输入以下指令创建一个yolov10环境:
  1. conda create -n yolov10 python=3.9
  2. conda activate yolov10
  3. pip install -r requirements.txt
  4. pip install -e .
复制代码
3.3  下载模型

     首先导出目标辨认模型,此处以官方预训练模型为例,首先下载预训练模型文件,然后调用yolo导出ONBNX格式的模型文件,最后利用 OpenVINO™ 的模型转换命令将模型转为IR格式,依次输入以下指令即可:
  1. wget https://github.com/jameslahm/yolov10/releases/download/v1.0/yolov10s.pt
  2. yolo export model=yolov10s.pt format=onnx opset=13 simplify
复制代码
4. engine模型转换

     首先界说ONNX模型转换Engine格式的代码,如下所示:
  1. #include "opencv2/opencv.hpp"
  2. #include <fstream>
  3. #include <iostream>
  4. #include "cuda.h"
  5. #include "NvInfer.h"
  6. #include "NvOnnxParser.h"
  7. class Logger : public nvinfer1::ILogger
  8. {
  9.     void log(Severity severity, const char* msg) noexcept override
  10.     {
  11.         if (severity <= Severity::kWARNING)
  12.             std::cout << msg << std::endl;
  13.     }
  14. } logger;
  15. void onnxToEngine(const char* onnxFile, int memorySize) {
  16.     // 将路径作为参数传递给函数
  17.     std::string path(onnxFile);
  18.     std::string::size_type iPos = (path.find_last_of('\\') + 1) == 0 ? path.find_last_of('/') + 1 : path.find_last_of('\\') + 1;
  19.     std::string modelPath = path.substr(0, iPos);//获取文件路径
  20.     std::string modelName = path.substr(iPos, path.length() - iPos);//获取带后缀的文件名
  21.     std::string modelName_ = modelName.substr(0, modelName.rfind("."));//获取不带后缀的文件名名
  22.     std::string engineFile = modelPath + modelName_ + ".engine";
  23.     // 构建器,获取cuda内核目录以获取最快的实现
  24.     // 用于创建config、network、engine的其他对象的核心类
  25.     nvinfer1::IBuilder* builder = nvinfer1::createInferBuilder(logger);  // 构建器,获取cuda内核目录以获取最快的实现,用于创建config、network、engine的其他对象的核心类
  26.     const auto explicitBatch = 1U << static_cast<uint32_t>(nvinfer1::NetworkDefinitionCreationFlag::kEXPLICIT_BATCH);  // 定义网络属性
  27.     nvinfer1::INetworkDefinition* network = builder->createNetworkV2(explicitBatch);  // 解析onnx网络文件,tensorRT模型类
  28.     nvonnxparser::IParser* parser = nvonnxparser::createParser(*network, logger);   // 将onnx文件解析,并填充rensorRT网络结构
  29.    
  30.     parser->parseFromFile(onnxFile, 2);  // 解析onnx文件
  31.     for (int i = 0; i < parser->getNbErrors(); ++i) {
  32.         std::cout << "load error: " << parser->getError(i)->desc() << std::endl;
  33.     }
  34.     printf("tensorRT load mask onnx model successfully!!!...\n");
  35.     // 创建推理引擎
  36.     nvinfer1::IBuilderConfig* config = builder->createBuilderConfig();  // 创建生成器配置对象。
  37.     config->setMaxWorkspaceSize(1024 * 1024 * memorySize);  // 设置最大工作空间大小。
  38.     config->setFlag(nvinfer1::BuilderFlag::kFP16);  // 设置模型输出精度
  39.     nvinfer1::ICudaEngine* engine = builder->buildEngineWithConfig(*network, *config);  // 创建推理引擎
  40.     // 将推理文件保存到本地
  41.     std::cout << "try to save engine file now~~~" << std::endl;
  42.     std::ofstream filePtr(engineFile, std::ios::binary);
  43.     if (!filePtr) {
  44.         std::cerr << "could not open plan output file" << std::endl;
  45.         return;
  46.     }
  47.     // 将模型转化为文件流数据
  48.     nvinfer1::IHostMemory* modelStream = engine->serialize();
  49.     // 将文件保存到本地
  50.     filePtr.write(reinterpret_cast<const char*>(modelStream->data()), modelStream->size());
  51.     // 销毁创建的对象
  52.     modelStream->destroy();
  53.     engine->destroy();
  54.     network->destroy();
  55.     parser->destroy();
  56.     std::cout << "convert onnx model to TensorRT engine model successfully!" << std::endl;
  57. }
复制代码
     在调用时也相对简朴,将相关变量传入即可,代码如下所示:
  1. onnxToEngine("E:\\Text_Model\\yolov10s.onnx", 50);
复制代码
5.2 结果后处理

     首先此处界说了一个结果类:
  1. void preProcess(cv::Mat *img, int length, float* factor, std::vector<float>& data) {
  2.         cv::Mat mat;
  3.     int rh = img->rows;
  4.     int rw = img->cols;
  5.     int rc = img->channels();
  6.         cv::cvtColor(*img, mat, cv::COLOR_BGR2RGB);
  7.     int maxImageLength = rw > rh ? rw : rh;
  8.     cv::Mat maxImage = cv::Mat::zeros(maxImageLength, maxImageLength,CV_8UC3);
  9.     maxImage = maxImage * 255;
  10.     cv::Rect roi (0, 0, rw, rh);
  11.     mat.copyTo(cv::Mat(maxImage, roi));
  12.         cv::Mat resizeImg;
  13.     cv::resize(maxImage, resizeImg, cv::Size(length, length), 0.0f, 0.0f, cv::INTER_LINEAR);
  14.         *factor = (float)((float)maxImageLength / (float)length);
  15.     resizeImg.convertTo(resizeImg, CV_32FC3, 1 / 255.0);
  16.     rh = resizeImg.rows;
  17.     rw = resizeImg.cols;
  18.     rc = resizeImg.channels();
  19.     for (int i = 0; i < rc; ++i) {
  20.         cv::extractChannel(resizeImg, cv::Mat(rh, rw, CV_32FC1, data.data() + i * rh * rw), i);
  21.     }
  22. }
复制代码
     然后界说模型的结果处理方式,代码如下所示:
  1. Mat frame = new frame();
  2. std::vector<float> inputData(640 * 640 * 3);
  3. float factor = 0;
  4. preProcess(&frame, 640, &factor, inputData);
复制代码
     最后为了让结果可视化,界说了结果绘制方法,代码如下所示:
  1. struct DetResult {
  2.     cv::Rect bbox;
  3.     float conf;
  4.     int lable;
  5.     DetResult(cv::Rect bbox,float conf,int lable):bbox(bbox),conf(conf),lable(lable){}
  6. };
复制代码
     上述方式调用仍旧十分容易,利用代码如下所示:
  1. std::vector<DetResult> postProcess(float* result, float factor, int outputLength) {
  2.     std::vector<cv::Rect> positionBoxes;
  3.     std::vector <int> classIds;
  4.     std::vector <float> confidences;
  5.     // Preprocessing output results
  6.     for (int i = 0; i < outputLength; i++)
  7.     {
  8.         int s = 6 * i;
  9.         if ((float)result[s + 4] > 0.2)
  10.         {
  11.             float cx = result[s + 0];
  12.             float cy = result[s + 1];
  13.             float dx = result[s + 2];
  14.             float dy = result[s + 3];
  15.             int x = (int)((cx)* factor);
  16.             int y = (int)((cy)* factor);
  17.             int width = (int)((dx - cx) * factor);
  18.             int height = (int)((dy - cy) * factor);
  19.             cv::Rect box(x, y, width, height);
  20.             positionBoxes.push_back(box);
  21.             classIds.push_back((int)result[s + 5]);
  22.             confidences.push_back((float)result[s + 4]);
  23.         }
  24.     }
  25.     std::vector<DetResult> re;
  26.     for (int i = 0; i < positionBoxes.size(); i++)
  27.     {
  28.         DetResult det(positionBoxes[i], confidences[i], classIds[i]);
  29.         re.push_back(det);
  30.     }
  31.     return re;
  32. }
复制代码
6. 模型推理实现

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

     首先读取上文中转换的Engine模型,并创建推理通道,用于后文的模型推理,实当代码如下所示:
  1. void drawBbox(cv::Mat& img, std::vector<DetResult>& res) {
  2.     for (size_t j = 0; j < res.size(); j++) {
  3.         cv::rectangle(img, res[j].bbox, cv::Scalar(255, 0, 255), 2);
  4.         cv::putText(img, std::to_string(res[j].lable) + "-" + std::to_string(res[j].conf),
  5.             cv::Point(res[j].bbox.x, res[j].bbox.y - 1), cv::FONT_HERSHEY_PLAIN,
  6.             1.2, cv::Scalar(0, 0, 255), 2);
  7.     }
  8. }
复制代码
6.2 Yolov10 推理代码

     下面联合一个视频推理,编写TensorRT推理YOLOv10的流程,代码如下所示:
[code]#include "opencv2/opencv.hpp"#include #include #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 messages        if (severity

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

宝塔山

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表