TensorRT学习笔记--基于FCN-ResNet101推理引擎实现语义分割

article/2025/10/6 14:56:23

目录

前言

1--Pytorch模型转换为Onnx模型

2--Onnx模型可视化及测试

2-1--可视化Onnx模型

2-2--测试Onnx模型

3--Onnx模型转换为Tensor RT推理模型

4--基于Tensor RT使用推理引擎实现语义分割


前言

        基于Tensor RT的模型转换流程:Pytorch → Onnx → Tensor RT;本笔记基于 Tensor RT 官方 Github 仓库的语义分割 Demo(Tensor RT 官方Demo链接) 进行实现,首先将训练好的 Pytorch 模型转换为 Onnx 模型,之后基于Tensor RT将 Onnx 模型转换为推理引擎 engine,最后使用Tensor RT的推理引擎 engine 实现语义分割。

1--Pytorch模型转换为Onnx模型

        利用 torch.hub.load() 加载预训练的 FCN-ResNet101 模型,利用 torch.onnx.export()导出Onnx模型;

from PIL import Image
from io import BytesIO
import requests
import matplotlib.pyplot as plt
import torch
import torch.nn as nn
import numpy as np# 下载并保存网络输入的图片
download_image = "./input.ppm" # 保存的路径,保存为ppm格式
response = requests.get("https://pytorch.org/assets/images/deeplab1.png") # 下载图片
with Image.open(BytesIO(response.content)) as img:ppm = Image.new("RGB", img.size, (255, 255, 255))ppm.paste(img, mask=img.split()[3])ppm.save(download_image) # 保存图片plt.imshow(Image.open(download_image)) 
plt.show()# 创建并导出模型
output_onnx="./fcn-resnet101.onnx" # 导出的Onnx模型路径
class FCN_ResNet101(nn.Module): # 定义模型def __init__(self):super(FCN_ResNet101, self).__init__()# 下载并导入 fcn_resnet101 模型self.model = torch.hub.load('pytorch/vision:v0.6.0', 'fcn_resnet101', pretrained=True)def forward(self, inputs):x = self.model(inputs)['out']x = x.argmax(1, keepdims=True) # 增加 argmax 模块,组成最终的模型return xmodel = FCN_ResNet101()
model.eval()# 定义网络的输入
input_tensor = torch.rand(4, 3, 224, 224) # 导出Onnx模型
torch.onnx.export(model, input_tensor, output_onnx,opset_version=12,do_constant_folding=True,input_names=["input"],output_names=["output"],dynamic_axes={"input": {0: "batch", 2: "height", 3: "width"},"output": {0: "batch", 2: "height", 3: "width"}},verbose=False)

        上面利用 torch.hub.load() 下载预训练模型时,因网速的原因可能会比较慢,可根据链接(红框)下载对应的模型放置到本地相应的路径(绿框)当中,以节省时间。

2--Onnx模型可视化及测试

2-1--可视化Onnx模型

        利用 netron 第三方库可视化导出的 Onnx 模型,以查看模型的输入输出维度;

# 终端依次执行
pip install netronpythonimport netronnetron.start("./fcn-resnet101.onnx")

2-2--测试Onnx模型

        使用 Onnxruntime 测试导出的 Onnx 推理模型,参考Tensor RT官方 Demo 设计相应的前处理和后处理函数;

import numpy as np
from PIL import Image
import onnx
import matplotlib.pyplot as plt
import onnxruntime# 前处理
def preprocess(image):# Mean normalizationmean = np.array([0.485, 0.456, 0.406]).astype('float32')stddev = np.array([0.229, 0.224, 0.225]).astype('float32')data = (np.asarray(image).astype('float32') / float(255.0) - mean) / stddev# Switch from HWC to to CHW orderreturn np.moveaxis(data, 2, 0)# 后处理
def postprocess(data):num_classes = 21# create a color palette, selecting a color for each classpalette = np.array([2 ** 25 - 1, 2 ** 15 - 1, 2 ** 21 - 1])colors = np.array([palette*i%255 for i in range(num_classes)]).astype("uint8")# plot the segmentation predictions for 21 classes in different colorsimg = Image.fromarray(data.astype('uint8'), mode='P')img.putpalette(colors)return imgif __name__ == "__main__":# 加载并可视化网络输入input_file = "./input.ppm" with Image.open(input_file) as img:input_image = preprocess(img) # 前处理image_width = img.widthimage_height = img.heightplt.imshow(Image.open(input_file)) plt.show()# 调整输入图片的维度,以适配Onnx模型 input_data = input_image[np.newaxis, :]# 导入Onnx模型Onnx_file = "./fcn-resnet101.onnx"Model = onnx.load(Onnx_file)onnx.checker.check_model(Model) # 验证Onnx模型是否准确# 使用onnxruntime推理model = onnxruntime.InferenceSession(Onnx_file, providers=['TensorrtExecutionProvider', 'CUDAExecutionProvider', 'CPUExecutionProvider'])input_name = model.get_inputs()[0].name # 对应可视化onnx模型时,网络的输入名称: inputoutput_name = model.get_outputs()[0].name # 对应可视化onnx模型时,网络的输出名称: outputprint(input_name)output = model.run([output_name], {input_name:input_data}) # onnxruntime的输入input_data需要为numpy类型,Tensor类型会报错# 后处理output = postprocess(np.reshape(output, (image_height, image_width)))# 保存并可视化推理结果output_file = "output_onnx.ppm"output.convert('RGB').save(output_file, "PPM")plt.imshow(Image.open(output_file))plt.show()

3--Onnx模型转换为Tensor RT推理模型

        基于 Tensor RT 将导出的 Onnx 模型转换为推理模型 Engine,这里博主基于 Tensor RT 8.2.5.1 提供的 trtexec 可执行文件;

./trtexec --onnx=/path/fcn-resnet101.onnx --fp16 --workspace=4096 --minShapes=input:1x3x256x256 --optShapes=input:1x3x1026x1282 --maxShapes=input:1x3x1440x2560 --buildOnly --saveEngine=/path/fcn-resnet101.engine

        --onnx 和 --saveEngine 需根据实际设置正确的模型路径;之前的Tensor RT 7.x 版本将--workspace设置为64,当使用Tensor RT 8.x 版本时,workspace的空间将不足会出现上图的错误,因此需将 --workspace=64 设置为 --workspace=4096;

4--基于Tensor RT使用推理引擎实现语义分割

        基于Tensor RT加载 Fcn-resnet101.engine 推理引擎,设计相应的前处理和后处理函数,实现语义分割;

import numpy as np
import os
import pycuda.driver as cuda
import pycuda.autoinit
import tensorrt as trtimport matplotlib.pyplot as plt
from PIL import Image# 前处理
def preprocess(image):# 均值归一化(Mean normalization)mean = np.array([0.485, 0.456, 0.406]).astype('float32')stddev = np.array([0.229, 0.224, 0.225]).astype('float32')data = (np.asarray(image).astype('float32') / float(255.0) - mean) / stddev# H×W×C -> C×H×Wreturn np.moveaxis(data, 2, 0) # 将维度2移动到维度0,不是交换# 后处理
def postprocess(data):num_classes = 21# 创建一个调色板,为每一个类选择一种颜色palette = np.array([2 ** 25 - 1, 2 ** 15 - 1, 2 ** 21 - 1])colors = np.array([palette*i%255 for i in range(num_classes)]).astype("uint8")# 上色img = Image.fromarray(data.astype('uint8'), mode='P')img.putpalette(colors)return img# 导入推理引擎engine
def load_engine(engine_file_path):assert os.path.exists(engine_file_path) # 推理引擎 engine 存在print("Reading engine from file {}".format(engine_file_path))with open(engine_file_path, "rb") as f, trt.Runtime(TRT_LOGGER) as runtime:return runtime.deserialize_cuda_engine(f.read()) # 反序列化加载引擎def infer(engine, input_file, output_file):print("Reading input image from file {}".format(input_file))with Image.open(input_file) as img: # 读取网络输入的图片input_image = preprocess(img) # 前处理image_width = img.widthimage_height = img.heightwith engine.create_execution_context() as context: # 构建 Context,用于计算的 GPU 上下文,类比 cpu 上的进程概念,是执行推理引擎的主体。# Set input shape based on image dimensions for inferencecontext.set_binding_shape(engine.get_binding_index("input"), (1, 3, image_height, image_width))# engine.get_binding_index("input"): 返回的索引index为0,即网络的输入# context.set_binding_shape: 执行上下文绑定,在索引0设置维度大小# 分配内存缓冲区bindings = []for binding in engine:binding_idx = engine.get_binding_index(binding) # 遍历获取对应的索引,例如input对应0,output对应1size = trt.volume(context.get_binding_shape(binding_idx))# context.get_binding_shape(binding_idx): 获取对应索引的Shape,例如input的Shape为(1, 3, H, W)# trt.volume(shape): 根据shape计算分配内存 dtype = trt.nptype(engine.get_binding_dtype(binding))# engine.get_binding_dtype(binding): 获取对应index或name的类型# trt.nptype(): 映射到numpy类型if engine.binding_is_input(binding): # 当前index为网络的输入inputinput_buffer = np.ascontiguousarray(input_image) # 将内存不连续存储的数组转换为内存连续存储的数组,运行速度更快input_memory = cuda.mem_alloc(input_image.nbytes) # cuda.mem_alloc()申请内存bindings.append(int(input_memory))else:output_buffer = cuda.pagelocked_empty(size, dtype)output_memory = cuda.mem_alloc(output_buffer.nbytes)bindings.append(int(output_memory))stream = cuda.Stream() # 创建Cuda流# 拷贝数据到GPU (host -> device)cuda.memcpy_htod_async(input_memory, input_buffer, stream) # 异步拷贝数据# 推理context.execute_async_v2(bindings=bindings, stream_handle=stream.handle)# 将GPU得到的推理结果 拷贝到主机(device -> host)cuda.memcpy_dtoh_async(output_buffer, output_memory, stream)# 同步Cuda流stream.synchronize()# 执行后处理,先把内存中的推理result 还原成 imagewith postprocess(np.reshape(output_buffer, (image_height, image_width))) as img:print("Writing output image to file {}".format(output_file))img.convert('RGB').save(output_file, "PPM")if __name__ == "__main__":TRT_LOGGER = trt.Logger() # 创建日志记录器engine_file = "./fcn-resnet101.engine"input_file  = "./input.ppm"output_file = "./output_trt.ppm"plt.imshow(Image.open(input_file))plt.show()print("Running TensorRT inference for FCN-ResNet101")with load_engine(engine_file) as engine: # 调用定义的load_engine()函数,通过反序列化加载推理引擎infer(engine, input_file, output_file)plt.imshow(Image.open(output_file))plt.show()


http://chatgpt.dhexx.cn/article/GDqKoxms.shtml

相关文章

迁移学习之ResNet50和ResNet101(图像识别)

文章目录 1.实现的效果:2.主文件TransorResNet.py: 1.实现的效果: 实际的图片: (1)可以看到ResNet50预测的前三个结果中第一个结果为:whippet(小灵狗) (2)Re…

Mask-RCNN(2)Resnet101

1. 对应着图像中的CNN部分,其对输入进来的图片有尺寸要求,需要可以整除2的6次方。在进行特征提取后,利用长宽压缩了两次、三次、四次、五次的特征层来进行特征金字塔结构的构造。Mask-RCNN使用Resnet101作为主干特征提取网络 2.ResNet101有…

Pytorch-预训练网络

预训练网络 我们可以把预训练的神经网络看作一个接收输入并生成输出的程序,该程序的行为是由神经网络的结构以及它在训练过程中所看到的样本所决定的,即期望的输入-输出对,或者期望输出应该满足的特性。我们可以在Pytorch中加载和运行这些预…

基于ResNet-101深度学习网络的图像目标识别算法matlab仿真

目录 1.算法理论概述 1.1、ResNet-101的基本原理 1.2、基于深度学习框架的ResNet-101实现 1.3网络训练与测试 2.部分核心程序 3.算法运行软件版本 4.算法运行效果图预览 5.算法完整程序工程 1.算法理论概述 介绍ResNet-101的基本原理和数学模型,并解释其在图…

【深度学习】ResNet网络详解

文章目录 ResNet参考结构概况conv1与池化层残差结构Batch Normalization总结 ResNet 参考 ResNet论文: https://arxiv.org/abs/1512.03385 本文主要参考视频:https://www.bilibili.com/video/BV1T7411T7wa https://www.bilibili.com/video/BV14E411H7U…

【使用Pytorch实现ResNet网络模型:ResNet50、ResNet101和ResNet152】

使用Pytorch实现Resnet网络模型:ResNet50、ResNet101和ResNet152 介绍什么是 ResNet?ResNet 的架构使用Pytorch构建 ResNet网络 介绍 在深度学习和计算机视觉领域取得了一系列突破。尤其是随着非常深的卷积神经网络的引入,这些模型有助于在图…

使用PyTorch搭建ResNet101、ResNet152网络

ResNet18的搭建请移步:使用PyTorch搭建ResNet18网络并使用CIFAR10数据集训练测试 ResNet34的搭建请移步:使用PyTorch搭建ResNet34网络 ResNet34的搭建请移步:使用PyTorch搭建ResNet50网络 参照我的ResNet50的搭建,由于50层以上几…

Java中的数组

数组 1.什么是数组 数组就是存储相同数据类型的一组数据,且长度固定 基本数据类型4类8种:byte/char/short/int/long/float/double/boolean 数组,是由同一种数据类型按照一定的顺序排序的集合,给这个数组起一个名字。是一种数据类型&#…

java输出数组(java输出数组)

多维数组在Java里如何创建多维数组? 这从第四个例子可以看出,它向我们演示了用花括号收集多个new表达式的能力: Integer[][] a4 { { new Integer (1), new Integer (2)}, { new Integer (3), new Integer (4)}, { new Integer (5), new…

java怎么输出数组(Java怎么给数组赋值)

Java中数组输出的三种方式。第一种方式,传统的for循环方式,第二种方式,for each循环,  第三种方式,利用Array类中的toString方法. 定义一个int类型数组,用于输出 int[] array={1,2,3,4,5}; 第一种方式,传统的for循环方式 for(int i=0;i {System.out.println(a[i]); } 第…

数组的输入与输出

前言: 我们知道对一个字符数组进行输入与输出时会用到: 输入:scanf,getchar,gets 输出:printf,putchar,puts 然而可能还有很多的朋友对这些还不是很了解,今天让我们共同学习数组的输入与输出吧。 %c格式是用于输入…

Java二维数组的输出

1. Java二维数组的输出<1> (1) 输出结果右对齐"%5d" public class HelloWorld {public static void main(String[] args){int myArray[ ][ ] { {1,2}, {7,2}, {3,4} };for(int i0; i<3; i){for (int j0; j<2; j)System.out.printf("%5d",my…

Java中数组的输入输出

数组的输入 首先声明一个int型数组 int[] a 或者 int a[] 给数组分配空间 anew int[10]; 和声明连起来就是int[] anew int[10]; 或者是 int a[]new int[10]; 给数组赋值 a[0]1;//0代表的是数组的第1个元素 ,元素下标为0 a[1]1;//1代表的是数组的第2个元素 ,元素下标为0 …

Java 数组的输入输出

Java中要对控制台进行输入操作的话要调用Scanner类&#xff0c;定义一个扫描的对象&#xff0c;例&#xff1a; //要导入java.util.Scanner; Scanner scanner new Scanner(System.in); 这样便打开了输入流&#xff0c;接下来定义数组&#xff1a; int[] n new int[4];//使…

Java中字符串数组的输入与输出

今天刷题遇到一个坑&#xff0c;老是接收不到字符串数组。即用str[i]sc.nextLine();这样的方式去接收数组的话&#xff0c;打印的时候总是会少一个。 import java.util.Scanner;public class test {public static void main(String[] args) {Scanner sc new Scanner(System.i…

java中打印输出数组内容的三种方式

今天输出数组遇到问题&#xff0c;学习一下打印输出数组内容的几种方式 错误示范&#xff1a;System.out.println(array);  //这样输出的是数组的首地址&#xff0c;而不能打印出数组数据。&#xff08;唉&#xff0c;我开始就是这么写的。。。&#xff09; 一维数组&#…

NTP协议之旅

NTP协议之旅 What---啥是NTPWhy---为什么需要NTPHow---NTP实现原理Do---NTP实战使用HCL 华三模拟器进行NTP配置抓包分析 What—啥是NTP NTP是在分布式网络中&#xff0c;进行时钟同步的协议&#xff0c;其具有较高的时间同步精度。所使用的传输层协议为UDP&#xff0c;使用端口…

ntrip协议

https://blog.csdn.net/wandersky0822/article/details/88558456这篇介绍的是RTK精确定位的原理&#xff0c;及影响精确定位的各种条件。 这一篇介绍的就比较细&#xff0c;仅仅介绍RTK 差分信息的 产生 申请与分发。 最近要做一个GPS RTK基站&#xff0c;也就是为RTK客户端提…

Ntrip协议简介

Ntrip通讯协议1.0 1 什么是Ntrip&#xff1f; CORS&#xff08;Continuously Operating Reference Stations&#xff09;就是网络基准站&#xff0c;通过网络收发GPS差分数据。用户访问CORS后&#xff0c;不用单独架设GPS基准站&#xff0c;即可实现GPS流动站的差分定位。 访问…

NTP技术介绍

NTP 简介 NTP&#xff08;Network Time Protocol&#xff0c;网络时间协议&#xff09;是由RFC 1305定义的时间同步协议&#xff0c;用来在分布式时间服务器和客户端之间进行时间同步。NTP基于UDP报文进行传输&#xff0c;使用的UDP端口号为123。 使用NTP的目的是对网络内所…