paddleOCRv3之四: rec识别部分用 tensorRT(C++)部署

news2024/11/19 8:24:28

文章目录

    • 1. 简介:
      • 速度测试
    • 2. paddle 模型转onnx
    • 3. onnx转为tensorRT的engine模型
    • 4. tensorRT在vs2017中的配置
    • 5. 源码

1. 简介:

tensorRT是nvdia GPU模型部署的一个框架,似乎只是部分开源,github地址.大多数时候用这个框架去部署模型效果提升还是比较好的。
整个项目依赖项版本如下

  • cuda10.2
  • cudnn 8.4.1
  • tensorRT 8.4.2.4

速度测试

  • GPU Quadro M4000

tensorRT8.4.2 cuda10.2, cuDNN8.4.1, Quadro M4000, 显存8GB。
实际测试时FP32和FP16的精度计算速度几乎一样,用trtexec.exe统计的推理时间如下

模型输入尺寸总耗时(ms)单张耗时(ms/张)
1x3x48x3209.0919.09
8x3x48x32057.4277.17
16x3x48x320109.8156.86
32x3x48x320212.7136.64
1x3x48x2007.687.68
8x3x48x20038.10064.76
16x3x48x20071.79764.48
32x3x48x200138.0244.31
1x3x48x1607.25587.26
8x3x48x16031.6143.95

为什么耗时这么高,比openVINO的都慢?原因是我的老爷显卡quadro M4000太老了,不管怎么测最低时间都是9ms,只能把batch提高来平摊,在quadro RTX4000显卡上1-2ms解决,但是我电脑上没有这块卡,所以只能测这个老爷卡的速度放着了。

  • GPU RTX4000

定位一次,识别一次,GPU显存占用0.6GB

真实图片尺寸耗时(ms)
1x3x128x2564.230
1x3x52x2263.507
1x3x95x1163.232
1x3x108x1513.249
1x3x130x1603.234
1x3x71x1563.512

用RTX4000测试的包含定位和识别的速度,定位用的yolo,识别部分分了两个模型一个1x3x48x320,一个1x3x48x160,比较长的图片走320的宽度的模型,短的走160的。定位加识别的速度比M4000快乐太多太多,MD 快给我换新显卡!

2. paddle 模型转onnx

这里参见paddleOCRv3之一: rec识别部分用 openVINO(C++)部署

3. onnx转为tensorRT的engine模型

这里可以采用onnxparser在代码里面转,也可以采用trtexec.exe转,因为engine模型是和GPU硬件绑定的,不同型号的显卡上转换的模型并不通用。所以一般来说用代码转换的方式是更通用的,这一部分如果以后有时间再加吧,这里先用trtexec来转。
tensorRT安装:官网下载下来后直接解压即可

  • 转换
    命令行模式进入tensorRT的 bin目录,设置临时环境变量指明tensoRT的动态库位置,(否则会报没有nvinfer.dll、xxx.dll错误)

path=%path%;G:\TensorRT-8.4.2.4\TensorRT-8.4.2.4\lib;

转换

trtexec.exe --onnx=K:\model\PaddleOCR\onnxv5\onnx_epoch42\PaddleOCRv3.onnx --saveEngine=K:\model\PaddleOCR\onnxv5\trt_epoch42\ppocr.engine --shapes=“x”:1x3x48x320

在这里插入图片描述

onnx模型是动态输入的,batchsize和width的维度都为-1,–shapes=“x”:1x3x48x320,表示把输入的尺寸固定为1x3x48x320,当然batchsize和width也可以设置为其他值例如:8x3x48x200. tensorRT支持动态尺寸推理,但是官网的原话说就是

Batch size can have a large effect on the optimizations TensorRT performs on our model.
Generally speaking, at inference, we pick a small batch size when we want to prioritize latency
and a larger batch size when we want to prioritize throughput. Larger batches take longer to
process but reduce the average time spent on each sample.
TensorRT is capable of handling the batch size dynamically if you don’t know until runtime
what batch size you will need. That said, a fixed batch size allows TensorRT to make
additional optimizations. For this example workflow, we use a fixed batch size of 64. For more
information on handling dynamic input size, see the NVIDIA TensorRT Developer Guide section
on dynamic shapes.

采用固定的尺寸可以更大程度的优化计算效率。更多关于动态尺寸输入的内容参考 this

4. tensorRT在vs2017中的配置

  • 附加包含目录
    在这里插入图片描述

  • 附加库目录
    在这里插入图片描述

  • 链接器\输入\附加依赖项
    在这里插入图片描述

  • dll路径配置
    这个可以选择直接拷贝把dll放入在运行项目旁边,或者添加系统环境变量,这里采用临时在vs工程中配置环境变量的方式。(不同dll项目路径用";"分号隔开,不能加换行符)

path=G:\TensorRT-8.4.2.4\TensorRT-8.4.2.4\lib;C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.2\bin;D:\opencv440\opencv\build\x64\vc15\bin;

在这里插入图片描述

5. 源码

现在只能提供一部分核心代码了,完整的没法发出来了

后处理部分也是把输出转为cv::Mat然后用CTC的方式解码,这部分参考paddleOCRv3之一: rec识别部分用 openVINO(C++)部署
下面是三个比较关键的步骤,初始化模型、推理、释放模型和其他资源,有了这些就已经很明显了,仔细看看下面的步骤结合tensorRT的文档就没有什么问题了。
在这里实际使用的时候可以用设计模式中的template method,定义一个虚基类实现OCR的初始化和推理接口,以及前处理、后处理的一些公共,再定义两个子类分别实现openVINO和TensorRT的推理平台的初始化和推理过程就行。

  • 头文件
/***
 * @brief : OCR recognition part with tensorRT backend.
 */
class OCR_tensorRT : public OCRInfer
{
public:
	OCR_tensorRT() {};
	/***
	 * @brief : Release the resource of GPU and CPU.
	 * @in param input_para : none
	 * @return : none
	 */
	virtual ~OCR_tensorRT();

	/***
	 * @brief : Input model path and load model to device, meanwhile
	 * set the input_size_ and output_size_ to specific size
	 * @in param model_path : model path
	 * @return : If initialize the model successfully ,return true, else return false.
	*/
	bool InitModel(const std::string &model_path);

	/***
	 * @brief : The  inference method of model.
	 * @in param input : The final input image.
	 * @out param output : Output tensor saved in output.
	 * @return : none
	 */
	virtual void Inference(const cv::Mat &input, cv::Mat &output);

protected:
	std::string input_node_name_ = "x";
	std::string output_node_name_ = "softmax_2.tmp_0";

	bool is_initiated_ = false;

	float* input_data_host_ = nullptr;//Host input blob
	float* output_data_host_ = nullptr;//Host output blob
	
	float* buffers_[2] = { nullptr,nullptr };;//Device input output buffers_
	int input_index_ = -1 ;;//buffers_[input_index_] indicates the input buffer's address on device
	int output_index_ = -1;;//buffers_[output_index_] indicates the output buffer's address on device
	
	nvinfer1::IRuntime* runtime_ = nullptr;
	nvinfer1::ICudaEngine* engine_ = nullptr;
	nvinfer1::IExecutionContext* context_ = nullptr;
	cudaStream_t cuda_stream_ = nullptr;;
};

#include "OCR_recognize_GPU.h"

//namespace {
//	void Logger::log(nvinfer1::ILogger::Severity severity, const char* msg) noexcept
//	{
//		// Suppress info-level messages
//		if (severity != Severity::kINFO)
//			std::cout << msg << std::endl;
//	}
//}


bool OCR_tensorRT::InitModel(const std::string &model_path)
{
	//Read tensorRT engine model
	std::ifstream file(model_path, std::ios::binary);
	if (!file.good())
	{
		is_initiated_ = false;
		return is_initiated_;
	}

	//Get model size
	size_t size = 0;
	file.seekg(0, file.end);//set the file pointer to the end	
	size = file.tellg();	
	file.seekg(0, file.beg);//set the file pointer to the start	
	
	//Read model
	char *model_stream = new char[size];
	file.read(model_stream, size);
	file.close();

	Logger logger;//tensorRT needs a logger to create inference engine

	//Prepare cuda resource
	runtime_ = nvinfer1::createInferRuntime(logger);
	engine_ = runtime_->deserializeCudaEngine(model_stream, size);
	context_ = engine_->createExecutionContext();
	delete[] model_stream;

	//Get model's input , output shape
	this->input_index_ = engine_->getBindingIndex(input_node_name_.c_str());
	this->output_index_ = engine_->getBindingIndex(output_node_name_.c_str());
	nvinfer1::Dims input_shape = engine_->getBindingDimensions(input_index_);//NCHW (1,3,48,320,...)
	nvinfer1::Dims output_shape = engine_->getBindingDimensions(output_index_);//NHW(1,40,67,...)
	
	size_t input_batch_size = input_shape.d[0];
	size_t input_channels = input_shape.d[1];
	size_t input_height = input_shape.d[2];
	size_t input_width = input_shape.d[3];
	size_t input_size = input_batch_size * input_channels * input_height * input_width;

	size_t output_batch_size = output_shape.d[0];
	size_t output_height = output_shape.d[1];
	size_t output_width = output_shape.d[2];
	size_t output_size = output_batch_size * output_height * output_width;

	//Set base class input_size_ output_size_
	std::vector<int> input_size_vec = { (int)input_batch_size,(int)input_channels,
		(int)input_height,(int)input_width };
	std::vector<int> output_size_vec = { (int)output_batch_size,(int)output_height,(int)output_width };
	this->SetInputSize(input_size_vec);
	this->SetOutputSize(output_size_vec);

	//Prepare input output blobs on host
	input_data_host_ = (float*)malloc(input_size * sizeof(float));
	output_data_host_ = (float*)malloc(output_size * sizeof(float));
	if (!input_data_host_ || !output_data_host_)
	{
		is_initiated_ = false;
		return is_initiated_;
	}

	//Prepare input output buffers_ on device
	cudaMalloc((void**)&buffers_[input_index_], input_size * sizeof(float));
	cudaMalloc((void**)&buffers_[output_index_], output_size * sizeof(float));
	
	if (!buffers_[input_index_] || !buffers_[output_index_])
	{
		is_initiated_ = false;
		return is_initiated_;
	}
	
	//Create a cuda stream
	cudaStreamCreate(&cuda_stream_);
	
	is_initiated_ = true;
	return is_initiated_;
}

void OCR_tensorRT::Inference(const cv::Mat &input, cv::Mat &output)
{
	//Prepare input blob on host
	size_t input_height = input.rows;
	size_t input_width = input.cols;
	size_t input_channels = input.channels();
	size_t image_size = input_height * input_width;
	for (size_t pid = 0; pid < image_size; ++pid)
	{
		for (size_t ch = 0; ch < input_channels; ++ch)
		{
			input_data_host_[image_size*ch + pid] = input.at<cv::Vec3f>(pid)[ch];
		}
	}

	//Copy input from host to device(GPU)
	size_t input_size = input_height * input_width * input_channels;
	cudaMemcpyAsync(buffers_[input_index_], input_data_host_,  input_size* sizeof(float), cudaMemcpyHostToDevice, cuda_stream_);

	//Inference
	context_->enqueueV2((void**)buffers_, cuda_stream_, nullptr);

	//Copy output from device to host 
	size_t output_height = this->output_size_[1];
	size_t output_width = this->output_size_[2];
	size_t output_size = output_height * output_width;
	cudaMemcpyAsync(output_data_host_, buffers_[output_index_], output_size * sizeof(float), cudaMemcpyDeviceToHost, cuda_stream_);

	cv::Mat outputMat(output_height, output_width, CV_32FC1, output_data_host_);
	output = outputMat;
	return ;
}

OCR_tensorRT::~OCR_tensorRT()
{
	//Destroy model
	if (context_)
	{
		context_->destroy();
		context_ = nullptr;
	}

	if (engine_)
	{
		engine_->destroy();
		engine_ = nullptr;
	}

	if (runtime_)
	{
		runtime_->destroy();
		runtime_ = nullptr;
	}
	
	//Release GPU memory
	if (buffers_[input_index_])
	{
		cudaFree(buffers_[input_index_]);
		buffers_[input_index_] = nullptr;
	}

	if (buffers_[output_index_])
	{
		cudaFree(buffers_[output_index_]);
		buffers_[output_index_] = nullptr;
	}

	//Destroy cuda stream
	if (cuda_stream_)
	{
		cudaStreamDestroy(cuda_stream_);
		cuda_stream_ = nullptr;
	}

	//Release host memory
	if (input_data_host_)
	{
		free(input_data_host_);
		input_data_host_ = nullptr;
	}

	if (output_data_host_)
	{
		free(output_data_host_);
		output_data_host_ = nullptr;
	}
}

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

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

相关文章

十九、Docker容器监控之CAdvisor+InfluxDB+Granfana

1、概述 Docker自带查询容器状态的命令&#xff1a;docker stats&#xff0c;可以看到容器的ID\名称、占用CPU、内存等信息 但是我们不能时时刻刻的盯着这个命令&#xff0c;并且这个都是实时数据不能留痕&#xff0c;如果这个时候某一个容器挂了&#xff0c;我们想查看下当时…

webpack性能优化

splitChunks webpack splitChunks minSize: 只有到目标文件超过这个minSize时才会分包。cacheGroups: 可以对某个第三方包进行单独分离出来 例如&#xff1a; splitChunks: {minSize: 300 * 1024&#xff0c;chunks: all,name: aaa,cacheGroups: {jquery: {name: jquery,test…

SCADA平台在风电场测量的应用,实现风电场的高效管理

一、应用背景 随着煤碳、石油等能源的逐渐枯竭&#xff0c;人类越来越重视可再生能源的利用。风能作为一种清洁的可再生能源日益受到世界各国的重视。中国风能储量大&#xff0c;分布面广&#xff0c;仅陆地上的风能储量就约2.53亿千瓦。我国的风电发展起步较晚&#xff0c;但…

大数据教学实训沙盘介绍

沙盘的作用主要有3个&#xff1a; 1、采集真实数据&#xff0c;解决教学中缺少真实数据的困扰&#xff1b; 2、形成从数据采集、预处理、挖掘建模、模型部署的业务闭环&#xff0c;可以把构建模型发布到沙盘系统上&#xff0c;根据模型产生真实的反馈不断的修正模型精度&#x…

DoIP协议从入门到精通系列——车辆声明

上篇文章对DoIP中物理连接做了说明和描述,介绍了以太网应用到车载网络中重要的两个组织: IEEE;OPEN联盟。本文主要对物理连接后,车辆进行自属信息声明过程做一个完整描述。 一、基础信息 DoIP协议标准由一个或多个DoIP实体实施,具体取决于车辆的网络架构。如下图是车辆网…

SuperMap iServer在不同系统中设置开机自启动--Windows篇

目录前言1.删除已有的 SuperMap iServer 系统服务2.注册 SuperMap iServer 系统服务3.设置 SuperMap iServer 系统服务开机自启动实例作者&#xff1a;kxj 前言 在成功部署SuperMap iServer之后&#xff0c;每次重启电脑都需要手动去启动iServer&#xff0c;如何能让iServer在…

HTML5 Web Worker(多线程处理)

文章目录HTML5 Web Worker(多线程处理)概述简单使用处理复杂数据HTML5 Web Worker(多线程处理) 概述 JavaScript的执行环境是单线程的&#xff0c;也就是一次只能执行一个任务。如果遇到多个任务时&#xff0c;只能排队依次执行。 在HTML5中&#xff0c;可以使用Web Worker创…

小程序集成Three.js,使用npm安装gsap动画库

0.视频演示 three.js集成gsap创建物体动画gsap作为简单易用的补间动画库&#xff0c;获得开发者一致好评。 在小程序中&#xff0c;我们集成了Three.js第三方库&#xff0c;可以创建和加载模型及场景&#xff0c;但是做动画还是需要第三方库的支持。 下面详细说明如何在小程序…

Java SPI机制详解

一、什么是SPI SPI全称Service Provider Interface&#xff0c;是Java提供的一种服务发现机制。实现服务接口和服务实现的解耦。 Java SPI 实际上是“基于接口的编程&#xff0b;策略模式&#xff0b;配置文件”组合实现的动态加载机制&#xff0c;实现不修改任何代码的情况下…

不错的一个麦肯锡信任公式

1&#xff09;可信度&#xff1a;这人是不是专家。 你是否让他人可以相信你这个人。这取决于你解决问题的能力、经验、专业知识、资源等等&#xff1b;这个人的专业能力是否真有别人说的那么出色&#xff0c;是否能够胜任这份工作呢&#xff1f;过往的履历中是否做过足以让我值…

函数指针到底需不需要解引用?类成员函数呢?

1、 普通函数指针 C函数指针有两点比较令人疑惑的做法&#xff1a; 函数名作为实参时&#xff0c;到底要不要取地址&#xff1f;通过函数指针调用函数时&#xff0c;到底要不要解引用&#xff1f; int add(int a, int b) {cout << "common function: " <…

ubuntu18安装、测试YOLOV3记录

官方教程&#xff1a; YOLO: Real-Time Object Detection 一、使用预训练模型进行检测 1、安装Darknet: git clone https://github.com/pjreddie/darknet cd darknet make 2、下载预训练权重https://pjreddie.com/media/files/yolov3.weights&#xff08;打开链接或wget&…

VSCode无密码连接远程服务器,并能debug python代码

1.官网下载VScode 官网 2.打开VScode&#xff0c;在扩展中搜索下载远程连接插件Remote-SSH 下载完毕会在侧边栏产生“远程资源管理器”图标①&#xff0c;打开远程资源管理器&#xff0c;点击右上角设置进入配置界面&#xff0c;并按照②添加远程服务器账号&#xff0c;输入…

Charles -证书过期失效处理方法

当出现环境配置正常但却无法抓包的时候&#xff0c;可能是因为证书失效了&#xff0c;这种情况移除旧证书&#xff0c;安装新的证书即可。 一、判断是否证书过期 iOS手机&#xff1a; 进入&#xff1a;设置 > 通用 > VPN与设备管理 > Charles Proxy CA... > 更多…

45. 含并行连结的网络(GoogLeNet)代码实现

1. Inception块 import torch from torch import nn from torch.nn import functional as F from d2l import torch as d2lclass Inception(nn.Module):# c1--c4是每条路径的输出通道数,c2,c3,c4是一个tuple元组def __init__(self, in_channels, c1, c2, c3, c4, **kwargs):su…

C# .Net MVC框架实现最简单的登陆

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录前言C#.net实现最简单登陆一、C#,.net是什么&#xff0c;相比较于C,java二、C# .net创建一个MVC框架工程1.步骤实现结果前言 C#.net实现最简单登陆 提示&#xff1…

linux-----基本操作指令(2)

将一个文件按照升序排序:注意这里面的S是大写 按照文件大小按照降序排列:ll -hS 按照文件大小按照升序排列:ll -hrS 一:cp(copy)表示复制&#xff0c;类似于windows系统上面的复制文件到指定文件夹的操作时类似的&#xff0c;拿鼠标一拖到指定路径 1)同时也就是说这个文件最终在…

Python实现的通用的二进制数据分析工具,分析任意格式的二进制数据,还能同时查看协议文档

这是一个通用的二进制数据分析工具。 完整程序代码下载地址&#xff1a;Python实现的通用的二进制数据分析工具 它能做什么 分析任意格式的二进制数据&#xff0c;还能同时查看协议文档逐字节、逐位分析手动、自动分析对分析结果建透视图&#xff0c;发现规律&#xff0c;学习…

IO流的节点流和处理流(缓冲流)and ZIP流使用

流的名称 字节流和字符流的区别 每次读写的字节数不同&#xff1b; 字符流是块读写&#xff0c;字节流是字节读写&#xff1b; 字符流带有缓存&#xff0c;字节流没有 java流在处理上分为字符流和字节流。字符流处理的单元为2个字节的Unicode字符&#xff0c;分别操作字符、…