【TensorRT】基于C#调用TensorRT 部署Yolov5模型 - 中篇:构建Nvinfer类

  NVIDIA TensorRT™ 是用于高性能深度学习推理的 SDK,可为深度学习推理应用提供低延迟和高吞吐量。详细安装方式参考以下博客: NVIDIA TensorRT 安装 (Windows C++)
在这里插入图片描述
  前文中已经介绍了在C++中利用TensorRT 部署Yolov5模型,但在实际应用中,经常会出现在C#中部署模型的需求,目前TensorRT无法直接在C#调用函数接口实现模型部署,此处利用动态链接库功能,构建TensorRTSharp,实现C#部署模型。

2. 构建Nvinfer类

2.1 新建C#类库

  右击解决方案,添加->新建项目,选择添加C#类库,项目名命名为csharp_tensorrt_class,项目框架根据电脑中的框架选择,此处使用的是.NET 5.0。新建完成后,然后右击项目,选择添加->新建项,选择类文件,添加Nvinfer.csNativeMethods.cs两个类文件。

2.2 引入dll文件中的方法

  在NativeMethods.cs文件下,我们通过[DllImport()]方法,将dll文件中所有的方法读取到C#中。模型转换方法读取方式如下:

[DllImport(tensorrt_dll_path, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
public extern static void onnx_to_engine(string onnx_file_path, string engine_file_path, int type);

  其中openvino_dll_path为dll文件路径,CharSet = CharSet.Unicode代表支持中文编码格式字符串,CallingConvention = CallingConvention.Cdecl指示入口点的调用约定为调用方清理堆栈。
上述所列出的为初始化推理模型,dlii文件接口在匹配时,是通过方法名字匹配的,因此,方法名要保证与dll文件中一致。其次就是方法的参数类型要进行对应,在上述方法中,函数的返回值在C++中为void* ,在C#中对应的为IntPtr类型,输入参数中,在C++中为wchar_t* 字符指针,在C#中对应的为string字符串。通过方法名与参数类型一一对应,在C#可以实现对方法的调用。其他方法在C#重写后如下:

// 读取本地engine模型,并初始化NvinferStruct
public extern static IntPtr nvinfer_init(string engine_filename, int num_ionode);
// 创建GPU显存输入/输出缓冲区
public extern static IntPtr creat_gpu_buffer(IntPtr nvinfer_ptr, string node_name, ulong data_length);
// 加载图片输入数据到缓冲区
public extern static IntPtr load_image_data(IntPtr nvinfer_ptr, string node_name, ref byte image_data, ulong image_size, int BN_means);
// 模型推理
public extern static IntPtr infer(IntPtr nvinfer_ptr);
// 读取推理数据
public extern static void read_infer_result(IntPtr nvinfer_ptr, string node_name_wchar, ref float result, ulong data_length);
// 删除内存地址
public extern static void nvinfer_delete(IntPtr nvinfer_ptr);

2.3 创建Nvinfer类

  为了更方便地调用我们通过dll引入的TensorRT 方法,减少使用时的函数方法接口,我们在C#中重新组建我们自己的推理类,命名为Class Nvinfer,其主要成员变量和方法如图Nvinfer类图所示。

public class Nvinfer{}

在这里插入图片描述

  在Nvinfer类中,我们只需要创建一个地址变量,作为Nvinfer类的成员变量,用于接收接口函数返回的推理核心指针,该成员变量我们只需要在当前类下访问,因此将其设置为私有变量:

private IntPtr ptr = new IntPtr();

  首先封装模型转换方法onnx_to_engine(),该方法主要用于将onnx模型转为engine格式,因为engine为基于本机配置转换的推理模型文件,因此该模型文件不具备通用性,需要自行转换。在该方法中,只需要输入本地onnx模型文件、转换后的engine本地保存路径以及转换后的模型精度类型,通过调用重写的NativeMethods.onnx_to_engine()方法即可。

public void onnx_to_engine(string onnx_file_path, string engine_file_path, AccuracyFlag type){
NativeMethods.onnx_to_engine(onnx_file_path, engine_file_path, (int)type);
}

  接下来,构建推理模型初始化方法init(),我们只需要输入engine模型文件路径地址以及输入输出节点数量即可,然后调用NativeMethods.nvinfer_init()方法,该方法可以实现本地读取engine模型,并初始化推理引擎结构体中的相关下成员变量。

public void init(string engine_filename, int num_ionode){
ptr = NativeMethods.nvinfer_init(engine_filename, num_ionode);
}

creat_gpu_buffer()主要实现在GPU显存创建输入/输出缓冲区,此处需要指定输入/输出节点名以及输入输出节点数据大小。

public void creat_gpu_buffer(string node_name, ulong data_length){
ptr = NativeMethods.creat_gpu_buffer(ptr, node_name, data_length);
}

load_image_data()该方法主要是是将带推理数据加载到推理模型中,该方法输入图片数据为转为矩阵的图片数据,方便图片数据在C++与C#之间进行传递,该方法中已经包括了图片数据预处理等步骤,因此在此处我们不需要再进行数据预处理。

public void load_image_data(string node_name, byte[] image_data, ulong image_size, BNFlag BN_means){
ptr = NativeMethods.load_image_data(ptr, node_name, ref image_data[0], image_size, (int)BN_means);
}

infer()步骤主要是调用模型推理方法将配置好的数据进行模型推理。

public void infer(){
ptr = NativeMethods.infer(ptr);
}

read_infer_result()主要实现了模型推理后推理结果数据的读取,目前对于结果的数据类型只支持浮点型数据的读取,后续如果有其他数据读取的要求,会根据需求进行更改。

public float[] read_infer_result(string node_name_wchar,ulong data_length){
float[] result = new float[data_length];
NativeMethods.read_infer_result(ptr, node_name_wchar, ref result[0], data_length);
return result;
}

  最后一步主要实现对内存数据的删除,放置占用太多的内存导致内存泄露。

public void delete(){
NativeMethods.nvinfer_delete(ptr);
}

2.4 编译Nvinfer类库

右击项目,点击生成/重新生成,出现如图所示,表示编译成功。
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/grape_yan/article/details/128551469