PaddleSeg学习4——paddle模型使用TensorRT推理(c++)

news2024/9/27 15:28:47

paddle模型使用TensorRT推理

  • 1 模型末端添加softmax和argmax算子
  • 2 paddle模型转onnx模型
  • 3 onnx模型转TensorRT模型
    • 3.1 安装TensorRT-8.5.3.1
    • 3.2 使用 trtexec 将onnx模型编译优化导出为engine模型
  • 4 TensorRT模型推理测试
  • 5 完整代码
  • 6 测试结果

1 模型末端添加softmax和argmax算子

前文 PaddleSeg c++部署OCRNet+HRNet模型中的语义分割模型输出为float32类型,模型不含softmax和argmax处理,导致在项目应用过程中后处理耗时较高。
通过PaddleSeg/tools/export.py在网络末端增加softmax和argmax算子,解决应用中的后处理耗时问题。

参考文档PaddleSeg/docs/model_export_cn.md导出预测模型。将导出的预测模型文件保存在output/inference_model文件夹中,如下。模型输出类型为int32

./output/inference_model
  ├── deploy.yaml            # 部署相关的配置文件,主要说明数据预处理的方式
  ├── model.pdmodel          # 预测模型的拓扑结构文件
  ├── model.pdiparams        # 预测模型的权重文件
  └── model.pdiparams.info   # 参数额外信息,一般无需关注网络输出类型为int32。
python tools/export.py \
       --config  configs\ocrnet\ocrnet_hrnetw18_cityscapes_1024x512_160k_lovasz_softmax.yml\
       --model_path output\iter_12000\model.pdparams \
       --save_dir output\inference_model
       --output_op argmax

PaddleSeg v2.0以前export.py中不含argmaxsoftmax参数选项,可通过以下代码在模型末端增加softmaxargmax算子。

import argparse
import os
import paddle
import yaml
from paddleseg.cvlibs import Config
from paddleseg.utils import logger

def parse_args():
    parser = argparse.ArgumentParser(description='Model export.')
    # params of training
    parser.add_argument(
        "--config",
        dest="cfg",
        help="The config file.",
        default=None,
        type=str,
        required=True)
    parser.add_argument(
        '--save_dir',
        dest='save_dir',
        help='The directory for saving the model snapshot',
        type=str,
        default='./output')
    parser.add_argument(
        '--model_path',
        dest='model_path',
        help='The path of model for evaluation',
        type=str,
        default=None)

    return parser.parse_args()
    
class SavedSegmentationNet(paddle.nn.Layer):
    def __init__(self, net, without_argmax=False, with_softmax=False):
        super().__init__()
        self.net = net
        self.post_processer = PostPorcesser(without_argmax, with_softmax)

    def forward(self, x):
        outs = self.net(x)
        outs = self.post_processer(outs)
        return outs

class PostPorcesser(paddle.nn.Layer):
    def __init__(self, without_argmax, with_softmax):
        super().__init__()
        self.without_argmax = without_argmax
        self.with_softmax = with_softmax

    def forward(self, outs):
        new_outs = []
        for out in outs:
            if self.with_softmax:
                out = paddle.nn.functional.softmax(out, axis=1)
            if not self.without_argmax:
                out = paddle.argmax(out, axis=1)
            new_outs.append(out)
        return new_outs

def main(args):
    os.environ['PADDLESEG_EXPORT_STAGE'] = 'True'
    cfg = Config(args.cfg)
    net = cfg.model

    if args.model_path:
        para_state_dict = paddle.load(args.model_path)
        net.set_dict(para_state_dict)
        logger.info('Loaded trained params of model successfully.')

    # 增加softmax、argmax处理
    new_net = SavedSegmentationNet(net, True,True)
    
    new_net.eval()
    new_net = paddle.jit.to_static(
        new_net,
        input_spec=[
            paddle.static.InputSpec(
                shape=[None, 3, None, None], dtype='float32')
        ])
    save_path = os.path.join(args.save_dir, 'model')
    paddle.jit.save(new_net, save_path)

    yml_file = os.path.join(args.save_dir, 'deploy.yaml')
    with open(yml_file, 'w') as file:
        transforms = cfg.export_config.get('transforms', [{
            'type': 'Normalize'
        }])
        data = {
            'Deploy': {
                'transforms': transforms,
                'model': 'model.pdmodel',
                'params': 'model.pdiparams'
            }
        }
        yaml.dump(data, file)

    logger.info(f'Model is saved in {args.save_dir}.')

if __name__ == '__main__':
    args = parse_args()
    main(args)

2 paddle模型转onnx模型

参考文档 PaddleSeg/docs/model_export_onnx_cn.md
参考文档Paddle2ONNX

(1)安装Paddle2ONNX

pip install paddle2onnx

(2)模型转换
执行如下命令,使用Paddle2ONNXoutput/inference_model文件夹中的预测模型导出为ONNX格式模型。将导出的预测模型文件保存为model.onnx

paddle2onnx --model_dir output/inference_model \
            --model_filename model.pdmodel \
            --params_filename model.pdiparams \
            --opset_version 12 \
            --save_file model.onnx \
            --enable_dev_version True

3 onnx模型转TensorRT模型

3.1 安装TensorRT-8.5.3.1

参考TensorRt安装

3.2 使用 trtexec 将onnx模型编译优化导出为engine模型

由于是动态输入,因此指定了输入尺寸范围和最优尺寸。将导出的预测模型文件保存为model.trt

trtexec.exe 
	--onnx=model.onnx 
	--explicitBatch --fp16 
	--minShapes=x:1x3x540x960 
	--optShapes=x:1x3x720x1280 
	--maxShapes=x:1x3x1080x1920 
	--saveEngine=model.trt

4 TensorRT模型推理测试

参考TensorRt动态尺寸输入的分割模型测试

5 完整代码

namespace TRTSegmentation {

	class Logger : public nvinfer1::ILogger
	{
	public:
		Logger(Severity severity = Severity::kWARNING) :
			severity_(severity) {}

		virtual void log(Severity severity, const char* msg) noexcept override
		{
			// suppress info-level messages
			if (severity <= severity_) {
				//std::cout << msg << std::endl;
			}
		}

		nvinfer1::ILogger& getTRTLogger() noexcept
		{
			return *this;
		}
	private:
		Severity severity_;
	};

	struct InferDeleter
	{
		template <typename T>
		void operator()(T* obj) const
		{
			delete obj;
		}
	};

	template <typename T>
	using SampleUniquePtr = std::unique_ptr<T, InferDeleter>;

	class LaneSegInferTRT
	{
	public:
		LaneSegInferTRT(const std::string seg_model_dir = "") {
			this->seg_model_dir_ = seg_model_dir;
			InitPredictor();
		}

		~LaneSegInferTRT()
		{
			cudaFree(bindings_[0]);
			cudaFree(bindings_[1]);
		}
		void PredictSeg(
			const cv::Mat &image_mat, 
			std::vector<PaddleSegmentation::DataLane> &solLanes /*实线*/,
			std::vector<PaddleSegmentation::DataLane> &dasLanes /*虚线*/,
			std::vector<double>* times = nullptr);
	private:
		void InitPredictor();
		// Preprocess image and copy data to input buffer
		cv::Mat Preprocess(const cv::Mat& image_mat);
		// Postprocess image
		void Postprocess(int rows, 
						int cols, 
						std::vector<int> &out_data,
						std::vector<PaddleSegmentation::DataLane> &solLanes,
						std::vector<PaddleSegmentation::DataLane> &dasLanes);

	private:
		//static const int num_classes_ = 15;
		std::shared_ptr<nvinfer1::ICudaEngine> mEngine_;
		SampleUniquePtr<nvinfer1::IExecutionContext> context_seg_lane_;
		std::vector<void*> bindings_;
		std::string seg_model_dir_;
		int gpuMaxBufSize = 1280 * 720; // output
	};

}//namespace PaddleSegmentation
#include "LaneSegInferTRT.hpp"
namespace {
	class Logger : public nvinfer1::ILogger
	{
	public:
		Logger(Severity severity = Severity::kWARNING) :
			severity_(severity) {}

		virtual void log(Severity severity, const char* msg) noexcept override
		{
			// suppress info-level messages
			if (severity <= severity_) {
				//std::cout << msg << std::endl;
			}
		}

		nvinfer1::ILogger& getTRTLogger() noexcept
		{
			return *this;
		}
	private:
		Severity severity_;
	};
}

namespace TRTSegmentation {

#define CHECK(status)                                                                                                  \
    do                                                                                                                 \
    {                                                                                                                  \
        auto ret = (status);                                                                                           \
        if (ret != 0)                                                                                                  \
        {                                                                                                              \
            std::cerr << "Cuda failure: " << ret << std::endl;                                                         \
		}                                                                                                              \
	} while (0)

	void LaneSegInferTRT::InitPredictor()
	{
		if (seg_model_dir_.empty()) {
			throw "Predictor must receive seg_model!";
		}

		std::ifstream ifs(seg_model_dir_, std::ifstream::binary);
		if (!ifs) {
			throw "seg_model_dir error!";
		}

		ifs.seekg(0, std::ios_base::end);
		int size = ifs.tellg();
		ifs.seekg(0, std::ios_base::beg);

		std::unique_ptr<char> pData(new char[size]);
		ifs.read(pData.get(), size);

		ifs.close();

		// engine模型
		Logger logger(nvinfer1::ILogger::Severity::kVERBOSE);

		SampleUniquePtr<nvinfer1::IRuntime> runtime{nvinfer1::createInferRuntime(logger.getTRTLogger()) };
		mEngine_ = std::shared_ptr<nvinfer1::ICudaEngine>(
			runtime->deserializeCudaEngine(pData.get(), size), InferDeleter());
			
		this->context_seg_lane_ = SampleUniquePtr<nvinfer1::IExecutionContext>(mEngine_->createExecutionContext());

		bindings_.resize(mEngine_->getNbBindings());

		CHECK(cudaMalloc(&bindings_[0], sizeof(float) * 3 * gpuMaxBufSize));    // n*3*h*w
		CHECK(cudaMalloc(&bindings_[1], sizeof(int) * 1 * gpuMaxBufSize));      // n*1*h*w
	}
	
	cv::Mat LaneSegInferTRT::Preprocess(const cv::Mat& image_mat)
	{
		cv::Mat img;
		cv::cvtColor(image_mat, img, cv::COLOR_BGR2RGB);

		if (true/*is_normalize*/) {
			img.convertTo(img, CV_32F, 1.0 / 255, 0);
			img = (img - 0.5) / 0.5;
		}
		return img;
	}

void LaneSegInferTRT::PredictSeg(
			const cv::Mat &image_mat,
			std::vector<PaddleSegmentation::DataLane> &solLanes ,
			std::vector<PaddleSegmentation::DataLane> &dasLanes,
			std::vector<double>* times)
	{
		// Preprocess image
		cv::Mat img = Preprocess(image_mat);		
		int rows = img.rows;
		int cols = img.cols;
		this->context_seg_lane_->setBindingDimensions(0, nvinfer1::Dims4{ 1, 3 , rows, cols });
		int chs = img.channels();
		std::vector<float> input_data(1 * chs * rows * cols, 0.0f);
		hwc_img_2_chw_data(img, input_data.data());		
		CHECK(cudaMemcpy(bindings_[0], static_cast<const void*>(input_data.data()), 3 * img.rows * img.cols * sizeof(float), cudaMemcpyHostToDevice));

		// Run predictor 推理
		context_seg_lane_->executeV2(bindings_.data());
		// Get output tensor		
		std::vector<int> out_data(1 * 1 * rows * cols);
		CHECK(cudaMemcpy(static_cast<void*>(out_data.data()), bindings_[1], out_data.size() * sizeof(int), cudaMemcpyDeviceToHost));
		// Postprocessing
		Postprocess(rows, cols, out_data, solLanes,dasLanes);
	}

	void LaneSegInferTRT::Postprocess(int rows, int cols, vector<int>& out_data,std::vector<PaddleSegmentation::DataLane> &solLanes,
		std::vector<PaddleSegmentation::DataLane> &dasLanes)
	{
		PaddleSegmentation::LanePostProcess laneNet(rows, cols);
		laneNet.lanePostprocessForTRT(out_data,solLanes,dasLanes);
	}	

}//namespace PaddleSegmentation

6 测试结果

在这里插入图片描述
在这里插入图片描述

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

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

相关文章

C++编码规范:JSF-AV(未完待续)

联合打击战斗机计划&#xff08;英语&#xff1a;Joint Strike Fighter Program&#xff0c;简称JSF&#xff09;是一个由美国和其盟国发起的新一代战斗机发展和采购项目。该项目旨在取代大量已老化的战斗机、战斗轰炸机和攻击机。该项目计划在未来取代各种西方主力战机&#x…

开通微信商家转账到零钱怎么做?场景模板

商家转账到零钱是什么&#xff1f; 商家转账到零钱功能是指商家可以通过支付平台将资金直接转账到用户的零钱账户中。在这种情况下&#xff0c;商家不需要用户提供银行账户信息&#xff0c;而是使用支付平台的转账功能将资金直接转移到用户的零钱账户中。 商家转账到零钱的使…

改进YOLOv8注意力系列四:结合中心化特征金字塔EVCBlock、大核卷积注意力LKA_Attention、全局注意力MobileViTAttention

改进YOLOv8注意力系列三:结合CrissCrossAttention、ECAAttention、EMAU期望最大化注意力 代码大核卷积注意力LKA_Attention中心化特征金字塔EVCBlock全局注意力MobileViTAttention加入方法各种yaml加入结构本文提供了改进 YOLOv8注意力系列包含不同的注意力机制以及多种加入方…

公司官网,选全站定制还是模板建站?

最近更新了公司网站&#xff0c;总算了了一件大事。 虽然很久以前也做网站&#xff0c;但随着技术的发展&#xff0c;以前经常用的dreamwaver、table等形式&#xff0c;不知道被升级了多少代。现在前端同事说起的各种架构&#xff0c;对我来说是云里雾里。只能看懂一点点。 这…

使用 Docker 进行 Go 应用程序引导指南

为在 Docker 中部署的 Go 应用程序做准备 在使用 Go 开发 Web 应用程序时&#xff0c;无论是用于 HTTP 还是其他类型的服务&#xff0c;部署到不同的阶段或环境&#xff08;本地开发、生产环境等&#xff09;都是一个常见的考虑因素。在本文中&#xff0c;我们将探讨在 Docker …

Canopen学习笔记——sync同步报文增加数据域(同步计数器)

1.Canfestival同步报文sync的设置 在OD表中的配置如下&#xff1a; 如果0x1006索引的同步报文循环周期时间设置为0则禁用同步报文&#xff0c;这里要注意的就是&#xff0c;上面第一张图也提到了&#xff0c;时间单位是us。第二张图&#xff0c;我的0x1006就设置为0xF4240,也就…

【C++】- 类和对象(运算符重载!!const!!详解!!)

类和对象③ 介绍运算符重载赋值运算符重载运算符重载const 在学习C语言时&#xff0c;我们首先接触的就是变量&#xff0c;再深入学习&#xff0c;我们可以利用运算符对变量进行操作&#xff0c;当我们使用C编写程序时&#xff0c;经常会遇到一些需要对特殊的例如自定义数据类型…

8.云原生存储之Ceph集群

1. 私有云实战之基础环境搭建 2. 云原生实战之kubesphere搭建 3.云原生之kubesphere运维 4. 云原生之kubesphere基础服务搭建 5.云原生安全之kubesphere应用网关配置域名TLS证书 6.云原生之DevOps和CICD 7.云原生之jenkins集成SonarQube 8.云原生存储之Ceph集群 文章目录 为什么…

Linux 内核学习 3a - 如何查看虚拟内存和物理内存,以及虚拟内存和物理内存之间转换

/proc/iomem, ioremap(), mmap() The kernel manages device resources like registers as physical addresses(物理地址). These are the addresses in /proc/iomem. The physical address is not directly useful to a driver; it must use ioremap() to map the space and …

出租车费 C语言xdoj697

问题描述 某城市普通出租车计费标准如下&#xff1a; 起步里程为 3 公里&#xff0c;起步费 10 元&#xff1b; 超起步里程后 10 公里内&#xff0c;每公里 2 元&#xff1b; 超过 10 公里以上的部分&#xff0c;每公里加收 50%的回空补贴费&#xff1b; 营运过程中&#xff0c…

若依基于jsencrypt实现前后端登录密码加密

若依虽然有加密解密功能&#xff0c;然后只有前端有&#xff0c;在用户点击保存密码的时候&#xff0c;会将密码保存到本地&#xff0c;但是为了防止密码泄露&#xff0c;所以在保存的时候&#xff0c;进行加密&#xff0c;在回显密码的时候进行解密显示&#xff0c;用户在登录…

【iOS】数据持久化(四)之FMDB基本使用

正如我们前面所看到的&#xff0c;原生SQLite API在使用时还是比较麻烦的&#xff0c;于是&#xff0c;开源社区就出现了一系列将SQLite API进行封装的库&#xff0c;其中FMDB的被大多数人所使用 FMDB和SQLite相比较&#xff0c;SQLite比较原始&#xff0c;操作比较复杂&#…

AI副业拆解:人像卡通化,赋予你的形象全新生命力

大家好我是在看&#xff0c;记录普通人学习探索AI之路。 &#x1f525;让你的形象瞬间穿越二次元&#xff01;&#x1f680;人像卡通化&#xff0c;捕捉你的独特魅力&#xff0c;让真实与梦幻在此刻交融。&#x1f3a8; 今天为大家介绍如何免费把人像卡通化--漫画风 https://w…

关系型数据库和MySQL概述

关系型数据库概述 数据持久化 - 将数据保存到能够长久保存数据的存储介质中,在掉电的情况下数据也不会丢失。数据库发展史 - 网状数据库、层次数据库、关系数据库、NoSQL 数据库、NewSQL 数据库。1970年,IBM的研究员E.F.Codd在_Communication of the ACM_上发表了名为_A Rela…

PyCharm连接服务器(利用PyCharm实现远程开发)

利用PyCharm实现远程开发 注&#xff1a;该功能只有在PyCharm专业版下才可以使用&#xff0c;并且必须是官方的正版许可&#xff0c;破解版的是不可以使用的&#xff01;&#xff01;&#xff01;可以通过免费教育许可申请使用权限&#xff08;申请流程&#xff09;。 pycharm…

【算法分析与设计】最短路径和

题目&#xff1a; 给定一个包含非负整数的 m x n 网格 grid &#xff0c;请找出一条从左上角到右下角的路径&#xff0c;使得路径上的数字总和为最小。 说明&#xff1a;每次只能向下或者向右移动一步。 示例&#xff1a; 示例 1&#xff1a; 输入&#xff1a;grid [[1,3,1],…

基于JAVA开发的数字化智慧工地管理平台源码,可私有化部署、带可视化大屏

智能工地应用价值 智慧工地现场构建了基于物联网的智能化数据传感器通用的管理平台。利用计算机、人工智能、无线通信&#xff0c;全天候现场监视、施工检查、质量管理、服务&#xff0c;提高数字化管理、安全、绿色、施工等现场管理能力&#xff0c;标志着现场管理进入信息化时…

Mysql如何优化慢查询

如何优化慢查询 慢 SQL 的优化&#xff0c;主要从两个方面考虑&#xff0c;SQL 语句本身的优化&#xff0c;以及数据库设计的优化。 1、避免不必要的列 覆盖索引会导致回表&#xff0c;且增大了IO 2、分页优化 深分页解决方案 使用子查询in 使用连接表 left join 使用游标&a…

IDEA新建SpringBoot工程时java版本只有17和21

解决方法&#xff1a;替换源 参考博客&#xff1a;https://www.kuazhi.com/post/712799571.html

C#编程-实现重写

实现重写 实现派生类中基类的成员称为重写。在C#中,可以重写方法、属性和索引器。 重写是多态性的一种形式,因为它使您能够创建具有相同名称和不同功能的不同代码块。 重写函数 在面向对象编程中,子类可以提供超类中已定义的专门版本的函数。这称为函数重写。 函数重写是…