yolov8 模型部署--TensorRT部署-c++服务化部署

news2024/12/23 22:37:41

写目录

  • yolov8 模型部署--TensorRT部署
    • 1、模型导出为onnx格式
    • 2、模型onnx格式转engine 部署

yolov8 模型部署–TensorRT部署

1、模型导出为onnx格式

  • 如果要用TensorRT部署YOLOv8,需要先使用下面的命令将模型导出为onnx格式:

    yolo export model=yolov8n.pt format=onnx 
    
  • YOLOv8的3个检测头一共有80x80+40x40+20x20=8400个输出单元格,每个单元格包含x,y,w,h这4项再加80个类别的置信度总共84项内容,所以通过上面命令导出的onnx模型的输出维度为1x84x8400

  • 模型输出维度
    在这里插入图片描述

  • 这样的通道排列顺序有个问题,那就是后处理的时候会造成内存访问不连续。

  • 为了解决这个问题,我们可以修改一下代码,具体做法是把ultralytics/nn/modules.py文件中的代码做如下修改,交换一下张量y的通道顺序:

    def forward(self, x):
        shape = x[0].shape  # BCHW
        for i in range(self.nl):
            x[i] = torch.cat((self.cv2[i](x[i]), self.cv3[i](x[i])), 1)
        if self.training:
            return x
        elif self.dynamic or self.shape != shape:
            self.anchors, self.strides = (x.transpose(0, 1) for x in make_anchors(x, self.stride, 0.5))
            self.shape = shape

        x_cat = torch.cat([xi.view(shape[0], self.no, -1) for xi in x], 2)
        if self.export and self.format in ('saved_model', 'pb', 'tflite', 'edgetpu', 'tfjs'):  # avoid TF FlexSplitV ops
            box = x_cat[:, :self.reg_max * 4]
            cls = x_cat[:, self.reg_max * 4:]
        else:
            box, cls = x_cat.split((self.reg_max * 4, self.nc), 1)
        dbox = dist2bbox(self.dfl(box), self.anchors.unsqueeze(0), xywh=True, dim=1) * self.strides
        y = torch.cat((dbox, cls.sigmoid()), 1)
        # 修改模型输出维度
        y=y.permute(0,2,1)
        return y if self.export else (y, x)

在这里插入图片描述

  • 这样修改后再执行上面的模型导出命令,模型的输出维度变为1x8400x84
    在这里插入图片描述

2、模型onnx格式转engine 部署

  • 配置好TensorRTNVIDIA环境
  • 使用trtexec 转换格式
    trtexec --onnx=coco/best.onnx --saveEngine=coco/best.onnx.engine --workspace=32 
    
  • 模型部署部分代码-c++
    #ifndef MyController_hpp
    #define MyController_hpp
    
    #include <ctime>
    #include <chrono>
    #include <sstream>
    #include <iomanip>
    
    #include <iostream>
    #include <numeric>
    #include <vector>
    
    #include "oatpp/web/server/api/ApiController.hpp"
    #include "oatpp/core/macro/codegen.hpp"
    #include "oatpp/core/macro/component.hpp"
    
    #include "opencv2/opencv.hpp"
    #include "../dto/DTOs.hpp" // 定义数据格式,用于在不同组件之间传输数据
    
    #include "../yoloApp/simple_yolo.hpp"
    #include "../byteTrackApp/logging.h"
    #include "../byteTrackApp/BYTETracker.h"
    
    // high performance
    #include "../yoloHighPer/cpm.hpp"
    #include "../yoloHighPer/infer.hpp"
    #include "../yoloHighPer/yolo.hpp"
    
    #	include <dirent.h>
    #	include <sys/types.h>
    #	include <sys/stat.h>
    #	include <unistd.h>
    # include <stdarg.h>
    
    
    using namespace std;
    using namespace cv;
    
    #include OATPP_CODEGEN_BEGIN(ApiController) //<-- Begin Codegen
    
    
    static bool exists(const string& path){
    
    #ifdef _WIN32
        return ::PathFileExistsA(path.c_str());
    #else
        return access(path.c_str(), R_OK) == 0;
    #endif
    }
    
    static std::vector<std::string> cocolabels = {
    	"car", "excavator", "loader", "dumpTruck", "person"
    };
    
    class InferInstance{
    public:
      InferInstance(std::string onnx_model_path, std::string trt_model_path){
        onnx_model = onnx_model_path;
        trt_model = trt_model_path;
        startup();
      }
    
    	bool startup(){
    		// if(!exists(trt_model)){
    		// 	SimpleYolo::compile(
    		// 		SimpleYolo::Mode::FP32,                 // FP32、FP16、INT8
    		// 		SimpleYolo::Type::V8, 
    		// 		1,            // max batch size
    		// 		onnx_model,                  // source 
    		// 		trt_model,                   // save to
    		// 		1 << 30,
    		// 		"inference"
    		// 	);
    		// }
    		infer_ = yolo::load(trt_model, yolo::Type::V8);
    		return infer_ != nullptr;
    	}
    
    	int inference(const Mat& image_input, yolo::BoxArray& boxarray){
    		if(infer_ == nullptr){
    			// INFOE("Not Initialize.");
    			return 1;
    		}
    		if(image_input.empty()){
    			// INFOE("Image is empty.");
    			return 1;
    		}
        boxarray = infer_->forward(cvimg(image_input));
    		return 0;
    	}
    
    private:
    	yolo::Image cvimg(const cv::Mat &image) { return yolo::Image(image.data, image.cols, image.rows);}
    	
    private:
      std::string onnx_model = "best.onnx";
      std::string trt_model = "best.onnx.engine";
    	shared_ptr<yolo::Infer> infer_;
    };
    
    
    ///
    std::string onnx_model = "coco/best.onnx";
    std::string engine_label = "coco/best.onnx.engine";
    std::unique_ptr<InferInstance> infer_instance1(new InferInstance(onnx_model, engine_label));
    
    int frame_rate = 10;
    int track_buffer = 30;
    std::unique_ptr<BYTETracker> tracker_instance1(new BYTETracker(frame_rate, track_buffer));
    
    
    ///
    
    /**
     * 建议使用 Api 控制器,而不是使用裸 HttpRequestHandler 为每个新端点创建新的请求处理程序。
     * API 控制器通过为您生成样板代码,使添加新端点的过程变得更加容易。 它还有助于组织您的端点,
     * 将它们分组到不同的 API 控制器中。
     */
    
    /**
     * Sample Api Controller.
     */
    class MyController : public oatpp::web::server::api::ApiController {
    protected:
      /**
       * Constructor with object mapper.
       * @param objectMapper - default object mapper used to serialize/deserialize DTOs.
       */
      MyController(const std::shared_ptr<ObjectMapper>& objectMapper)
        : oatpp::web::server::api::ApiController(objectMapper)
      {}
    
    
    public:  
      static std::shared_ptr<MyController> createShared(OATPP_COMPONENT(std::shared_ptr<ObjectMapper>, 
                                                                         objectMapper)){
        return std::shared_ptr<MyController>(new MyController(objectMapper));
      }
    
      // TODO Insert Your endpoints here !!!
      /--data--
    
      // 多目标追踪
      ENDPOINT_ASYNC("POST", "/car1", tracker1){
        ENDPOINT_ASYNC_INIT(tracker1)
        Action act() override {
          return request->readBodyToStringAsync().callbackTo(&tracker1::returnResponse);
        }
    
        Action returnResponse(const oatpp::String& body_){
            auto response = tracker_inference(*infer_instance1, *tracker_instance1, body_, controller);
            return _return(response);
          }
      };
      //
    
    
    public:
    
      // 多目标追踪
      static std::shared_ptr<OutgoingResponse> tracker_inference(InferInstance& infer_, BYTETracker& track_infer, std::string body_, auto* controller){
        auto base64Image = base64_decode(body_);
        if(base64Image.empty()){
          return controller->createResponse(Status::CODE_400, "The image is empty!");
        }
    
    
        std::vector<char> base64_img(base64Image.begin(), base64Image.end());
        cv::Mat image = cv::imdecode(base64_img, 1);
     
       // 获取程序开始时间点
        auto start_time = std::chrono::high_resolution_clock::now();
    
        // 推理
        yolo::BoxArray boxarray;
        CV_Assert(0 == infer_.inference(image, boxarray));
    
        // 获取程序结束时间点
        auto end_time = std::chrono::high_resolution_clock::now();
    
        // 计算运行时间
        auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time);
    
        // 打印运行时间(以微秒为单位)
        // std::cout << "程序运行时间: " << duration.count() << " 毫秒" << std::endl;
    
        // 结果处理
        vector<Objects> objects;
        objects.resize(boxarray.size());
    
        int index = 0;
        for(auto& box : boxarray) {
            objects[index].rect.x = box.left;;
            objects[index].rect.y = box.top;
            objects[index].rect.width = box.right - box.left;
            objects[index].rect.height = box.bottom - box.top;
            objects[index].prob = box.confidence;
            objects[index].label = box.class_label;
            index++;
            std::cout << "left: " << box.left << ", top: " << box.top
                    << ", right: " << box.right << ", bottom: " << box.bottom
                    << ", confidence: " << box.confidence << ", class_label: " << box.class_label << std::endl;
        }
    
        auto yoloDto = TrackYoloDto::createShared();
        auto boxList = TrackBoxList::createShared();
    
        std::vector<STrack> output_stracks = track_infer.update(objects);
    
        for (int i = 0; i < output_stracks.size(); i++)
    		{
          auto trackBoxDto = TrackerBboxes::createShared();
    			vector<float> tlwh = output_stracks[i].tlwh; // 方框的位置
    
          trackBoxDto->class_id = cocolabels[output_stracks[i].class_id];
          trackBoxDto->track_id = output_stracks[i].track_id;
    
          trackBoxDto->x        = tlwh[0];
          trackBoxDto->y        = tlwh[1];
          trackBoxDto->width    = tlwh[2];
          trackBoxDto->height   = tlwh[3];
          
          boxList->push_back(trackBoxDto);
    			
    		}
        output_stracks.clear();
        yoloDto->data = boxList;
        yoloDto->status = "successful";
        yoloDto->time = currentDateTime();
        return controller->createDtoResponse(Status::CODE_200, yoloDto);
    
      }
    
    
      static std::string currentDateTime(){
          auto now = std::chrono::system_clock::now();
          auto now_c = std::chrono::system_clock::to_time_t(now);
          auto now_ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) % 1000;
    
          std::stringstream ss;
          ss << std::put_time(std::localtime(&now_c), "%Y-%m-%d %H:%M:%S") << '.' << std::setfill('0') << std::setw(3) << now_ms.count();
          return ss.str();
      }
    
    
      static unsigned char from_b64(unsigned char ch){
          /* Inverse lookup map */
          static const unsigned char tab[128] = {
              255, 255, 255, 255,
              255, 255, 255, 255, /*  0 */
              255, 255, 255, 255,
              255, 255, 255, 255, /*  8 */
              255, 255, 255, 255,
              255, 255, 255, 255, /*  16 */
              255, 255, 255, 255,
              255, 255, 255, 255, /*  24 */
              255, 255, 255, 255,
              255, 255, 255, 255, /*  32 */
              255, 255, 255, 62,
              255, 255, 255, 63, /*  40 */
              52,  53,  54,  55,
              56,  57,  58,  59, /*  48 */
              60,  61,  255, 255,
              255, 200, 255, 255, /*  56   '=' is 200, on index 61 */
              255, 0,   1,   2,
              3,   4,   5,   6, /*  64 */
              7,   8,   9,   10,
              11,  12,  13,  14, /*  72 */
              15,  16,  17,  18,
              19,  20,  21,  22, /*  80 */
              23,  24,  25,  255,
              255, 255, 255, 255, /*  88 */
              255, 26,  27,  28,
              29,  30,  31,  32, /*  96 */
              33,  34,  35,  36,
              37,  38,  39,  40, /*  104 */
              41,  42,  43,  44,
              45,  46,  47,  48, /*  112 */
              49,  50,  51,  255,
              255, 255, 255, 255, /*  120 */
          };
          return tab[ch & 127];
      }
    
    
      static std::string base64_decode(const std::string& base64){
          if(base64.empty())
              return "";
    
          int len = base64.size();
          auto s = (const unsigned char*)base64.data();
          unsigned char a, b, c, d;
          int orig_len = len;
          int dec_len = 0;
          string out_data;
    
          auto end_s = s + base64.size();
          int count_eq = 0;
          while(*--end_s == '='){
              count_eq ++;
          }
          out_data.resize(len / 4 * 3 - count_eq);
    
          char *dst = const_cast<char*>(out_data.data());
          char *orig_dst = dst;
          while (len >= 4 && (a = from_b64(s[0])) != 255 &&
                  (b = from_b64(s[1])) != 255 && (c = from_b64(s[2])) != 255 &&
                  (d = from_b64(s[3])) != 255) {
              s += 4;
              len -= 4;
              if (a == 200 || b == 200) break; /* '=' can't be there */
              *dst++ = a << 2 | b >> 4;
              if (c == 200) break;
              *dst++ = b << 4 | c >> 2;
              if (d == 200) break;
              *dst++ = c << 6 | d;
          }
          dec_len = (dst - orig_dst);
    
          // dec_len必定等于out_data.size()
          return out_data;
      }
    };
    
    #include OATPP_CODEGEN_END(ApiController) //<-- End Codegen
    
    #endif /* MyController_hpp */
    
    
  • 启动模型
    在这里插入图片描述
  • 请求接口进行推理

yolov8 模型部署测试

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

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

相关文章

二维前缀和

导言 当我们需要求到某个矩阵的子矩阵的和时,就可以使用二维前缀和 这是一个矩阵, 就是左上角区域的所有数之和 ...... 如果要 求中间的子矩阵的和,(x,y)为左上角 ...... ...... ,(i,j)为右下角,那么只需要算 - - ------这一…

QT第五天

void Widget::on_show_clicked() { QString sql "select * from myTable" ; QSqlQuery querry; if(!querry.exec(sql)) { QMessageBox::information(this,"失败","展示失败"); return; } //此时&…

如何用 DAP 仿真器下载程序

1.仿真器简介 本书配套的仿真器为 Fire-Debugger&#xff0c;遵循 ARM 公司的 CMSIS-DAP 标准&#xff0c;支持所有基于 Cortex-M内核的单片机&#xff0c;常见的 M3、M4 和 M7 都可以完美支持。 Fire-Debugger 支持下载和在线仿真程序&#xff0c;支持XP/WIN7/WIN8/WIN10 这…

c++ day 4

1、仿照string类&#xff0c;完成myString 类 #include <iostream> #include<cstring>using namespace std;class myString { private:char *str; //记录c风格的字符串int size; //记录字符串的实际长度 public://无参构造myString():size(10…

JVM优化(OOM,内存溢出),查看线程快照,堆内存情况等问题

1&#xff1a;堆大小 新生代 老年代&#xff0c;新生代 ( Young ) 与老年代 ( Old ) 的比例的值为 1:2 ( 该值可以通过参数 –XX:NewRatio 来指定 ) 2&#xff1a;-Xmn参数总是应当小于-Xmx参数&#xff0c;否则就会触发OOM错误 3&#xff1a;jvm优化与查看gc回收情况&#x…

数据通信网络之OSPFv3基础

文章及资源归档至【AIShareLab】&#xff0c;回复 通信系统与网络 可获取。 文章目录 一、目的二、拓扑三、需求四、步骤 一、目的 掌握路由器的IPv6 基础配置。掌握OSPFv3&#xff08;单区域&#xff09;的基础配置。 二、拓扑 如图1 所示&#xff0c;三台路由器R1、R2 和R…

网络传输方式

1. 单播 1.1. 定义 单播是指一种向单个目标地址传送数据的方式&#xff0c;即单独的一对一通讯方式。 1.2. 可使用协议 UDP、TCP等协议 1.3. 常见的场景 发送电子邮件传输文件 2. 广播 2.1. 定义 一种向本地网络中所有设备发送数据的方式。 2.2. 常见的场景 电视和电…

仅做笔记用:Stable Diffusion 通过 ControlNet 扩展图片 / 扩图

发觉之前的 Outpainting 脚本效果仍旧不是很理想。这里又找了一下有没有效果更好的途径来扩图。于是就找到了通过 ControlNet 的方式来实现效果更好的扩图。这里临时记录一下在 Stable Diffusion 怎么使用 ControlNet 来扩展图片。 下载 control_v11p_sd15_inpaint_fp16.safet…

Sql注入详解(原理篇)

一、简介 SQL 注入漏洞非常复杂&#xff0c;区分各种数据库类型&#xff0c;提交方法&#xff0c;数据类型等注入&#xff0c;同样此类漏洞是WEB安全中严重的安全漏洞&#xff0c;学习如何利用&#xff0c;挖掘&#xff0c;修复也是很重要的 二、SQL注入原理 1、什么是SQL注…

嵌入式学习之链表

对于链表&#xff0c;要重点掌握链表和数组区别和实现&#xff0c;链表静态添加和动态遍历&#xff0c;链表中pointpoint-next,链表节点个数的查找&#xff0c;以及链表从指定节点后方插入新节点的知识。

Scrum敏捷开发流程及敏捷研发关键环节

Scrum是一个迭代式增量软件开发过程&#xff0c;是敏捷方法论中的重要框架之一。它通常用于敏捷软件开发&#xff0c;包括了一系列实践和预定义角色的过程骨架。Scrum中的主要角色包括Scrum主管&#xff08;Scrum Master&#xff09;、产品负责人&#xff08;Product Owner&…

Mybatis -- 读取 DATE 类型字段时可能遇到的问题(夏令时问题)

在使用 MYBATIS 读取数据库字段的时候&#xff0c;我们一般需要为查询字段指定数据类型。特别是当我们使用 mybatis generator 去生成对应的接口代码时&#xff0c;会自动按照数据库字段类型生成响应映射规则的代码。   如下&#xff0c;左侧是 date 类型生成的字段映射规则&…

如何在Excel中创建VBA程序--基于Office 365版本

目录 一 VBA程序简介二 为何选择VBA程序1. 高效便捷2. 自定义程度高3. 兼容性好 三 如何创建VBA程序1. 插入VBA程序&#xff1a;2. 使用VBA程序&#xff1a; 四 VBA程序应用场景示例1. 数据处理与分析2. 自定义函数3. 数据验证 五 VBA程序的优缺点及未来发展优点缺点 了解如何在…

计算机网络第四章——网络层(下)

长相思兮长相忆&#xff0c;短相思兮无穷极 文章目录 RIP和OSPE在于使用的算法是不一样的&#xff0c;RIP使用的是距离向量&#xff0c;适用于比较小的网络&#xff0c;OSPE使用的是链路状态&#xff0c;适用于比较大的网络 每一个路由器都有一个路由表&#xff0c;路由表的表…

Kerberos 身份验证

简介 Kerberos 是一种由 MIT&#xff08;麻省理工大学&#xff09;提出的一种基于加密 Ticket 的身份认证协议。它旨在通过使用密钥加密技术为客户端/服务器应用程序提供强身份验证&#xff0c;用于验证用户或主机的标识。。 适用范围&#xff1a;Windows Server 2022、Window…

Go基础八股

【Go面试】Go slice深拷贝和浅拷贝_哔哩哔哩_bilibili 基础篇 1.Go方法值接收者和指针接收者的区别 简单言之就是是否会影响调用的结构体&#xff0c;方法接收者是指针会影响 2.返回局部变量的指针 一般来说&#xff0c;局部变量会在函数返回后被销毁&#xff0c;因此被返回…

【距离注意残差网络:超分】

DARN: Distance Attention Residual Network for Lightweight Remote-Sensing Image Superresolution &#xff08;DARN&#xff1a;用于轻量级遥感图像超分辨率的距离注意残差网络&#xff09; 单图像超分辨率技术在遥感领域的应用具有重要意义。尽管基于卷积神经网络&#…

Pycharm 安装第三方库numpy,显示超时?

一、配置终端Terminal中的镜像源 1.更改pip源&#xff0c;在终端输入如下命令 pip config set global.index-url https://pypi.tuna.tshua.edu.cn/simple2.在终端使用pip install 安装第三方库 例如: pip install numpy二、配置仓库镜像源 1.第一步: 2.第二步&#xff1a;输…

JVM-垃圾回收器详解、参数配置

相关概念 并行和并发 并行&#xff08;Parallel&#xff09; 指多条垃圾收集线程并行工作&#xff0c;但此时用户线程仍然处于等待状态。 并发&#xff08;Concurrent&#xff09; 指用户线程与垃圾收集线程同时执行&#xff08;但不一定是并行的&#xff0c;可能会交替执行…

2023-9-11 高斯消元解异或线性方程组

题目链接&#xff1a;高斯消元解异或线性方程组 #include <iostream> #include <algorithm>using namespace std;const int N 110;int n; int a[N][N];int gauss() {int c, r;for(c r 0; c < n; c ){int t r;for(int i r; i < n; i )if(a[i][c]){t i;b…