yolo的tensorrt加速,转化的trt/engine模型文件的加载和推理

文章详细描述了如何在Python脚本中通过TensorRT将ONNX模型转换为可执行的引擎,并在推理过程中处理输出内存问题,包括绑定和数据复制操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

0x00 经过yolo文件夹中的export.py文件实现onnx转化

0x01 onnx实现到trt文件的转化

参考博客:tensorRT加速遇到的若干问题

0x02 yolo文件夹中detect.py文件能实现trt和engine的加载和推理,但是新建单独的py文件如何加载和推理,直接给出代码。

import tensorrt as trt
import pycuda.driver as cuda
import pycuda.autoinit
import time
import cv2
import numpy as np
import torch
np.bool = np.bool_

f = open("best32.trt", "rb")
runtime = trt.Runtime(trt.Logger(trt.Logger.WARNING))
engine = runtime.deserialize_cuda_engine(f.read())
context = engine.create_execution_context()

# 加载图像
image_path = "left_2.jpg"
input_image = cv2.imread(image_path)  # 使用 OpenCV 加载图像
# 调整图像大小和通道顺序,以适应模型输入
input_image = cv2.resize(input_image, (640, 480))  # 调整大小为模型输入大小
input_image = cv2.cvtColor(input_image, cv2.COLOR_BGR2RGB)  # 调整通道顺序为 RGB
input_image = np.transpose(input_image, (2, 0, 1))  # 调整通道顺序为 CxHxW
input_image = input_image / 255.0
# 添加批处理维度
input_tensor = np.expand_dims(input_image, axis=0)
input_tensor = np.ascontiguousarray(input_tensor, dtype=np.float32)

output = np.empty([1, 18900, 6], dtype=np.float32)

# allocate device memory
d_input = cuda.mem_alloc(1 * input_tensor.nbytes)
d_output = cuda.mem_alloc(1 * output.nbytes)

bindings = [int(d_input), int(d_output)]
stream = cuda.Stream()

def predict(preprocessed_images):  # result gets copied into output
    # transfer input data to device
    cuda.memcpy_htod_async(d_input, preprocessed_images, stream)  # execute model
    context.execute_async_v2(bindings, stream.handle, None)  # transfer predictions back
    cuda.memcpy_dtoh_async(output, d_output, stream)  # syncronize threads
    stream.synchronize()
    d_input.free()
    d_output.free()
    return output

t0 = time.time()
pred = predict(input_tensor)
print(pred)
print(pred.shape)

t1 = time.time()
print(f'One frame spends time = ({t1 - t0:.3f}s)')


运行此代码出现报错:
[04/11/2024-13:29:04] [TRT] [E] 3: [executionContext.cpp::enqueueInternal::622] Error Code 3: API Usage Error (Parameter check failed at: runtime/api/executionContext.cpp::enqueueInternal::622, condition: bindings[x] || nullBindingOK
)
或者
condition: binding[x] != nullptr
且输出打印出来全为0

[04/11/2024-13:29:04] [TRT] [E] 3: [executionContext.cpp::enqueueInternal::622] Error Code 3: API Usage Error (Parameter check failed at: runtime/api/executionContext.cpp::enqueueInternal::622, condition: bindings[x] || nullBindingOK
)
[[[0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0.]
  ...
  [0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0.]]]
(1, 18900, 6)
One frame spends time = (0.036s)

Process finished with exit code 0

参考博客:
TensorRT推理过程出现condition: binding[x] != nullptr,output全0

我们在代码:

f = open("best32.trt", "rb")
runtime = trt.Runtime(trt.Logger(trt.Logger.WARNING))
engine = runtime.deserialize_cuda_engine(f.read())
context = engine.create_execution_context()

后面加入代码:

for binding in engine:
    dims = engine.get_binding_shape(binding)
    size = trt.volume(dims)
    print("The size of binding is", size)
    print("The dimension of binding is", dims)
    print(binding)
    print("input = ", engine.binding_is_input(binding))
    print("dtype =", trt.nptype(engine.get_binding_dtype(binding)))

输出可以看到:

The size of binding is 921600
The dimension of binding is (1, 3, 480, 640)
images
input =  True
dtype = <class 'numpy.float32'>
The size of binding is 86400
The dimension of binding is (1, 3, 60, 80, 6)
onnx::Sigmoid_456
input =  False
dtype = <class 'numpy.float32'>
The size of binding is 21600
The dimension of binding is (1, 3, 30, 40, 6)
onnx::Sigmoid_509
input =  False
dtype = <class 'numpy.float32'>
The size of binding is 5400
The dimension of binding is (1, 3, 15, 20, 6)
onnx::Sigmoid_562
input =  False
dtype = <class 'numpy.float32'>
The size of binding is 113400
The dimension of binding is (1, 18900, 6)
output
input =  False
dtype = <class 'numpy.float32'>

可以看出输入一个(1, 3, 480, 640),输出不止一个(1, 18900, 6)
最开始的代码

image_path = "left_2.jpg"
input_image = cv2.imread(image_path)  # 使用 OpenCV 加载图像
# 调整图像大小和通道顺序,以适应模型输入
input_image = cv2.resize(input_image, (640, 480))  # 调整大小为模型输入大小
input_image = cv2.cvtColor(input_image, cv2.COLOR_BGR2RGB)  # 调整通道顺序为 RGB
input_image = np.transpose(input_image, (2, 0, 1))  # 调整通道顺序为 CxHxW
input_image = input_image / 255.0
# 添加批处理维度
input_tensor = np.expand_dims(input_image, axis=0)
input_tensor = np.ascontiguousarray(input_tensor, dtype=np.float32)
output = np.empty([1, 18900, 6], dtype=np.float32)

# allocate device memory
d_input = cuda.mem_alloc(1 * input_tensor.nbytes)
d_output = cuda.mem_alloc(1 * output.nbytes)

bindings = [int(d_input), int(d_output)]

需要进行修改,不废话了,给出正确代码。也就是给另外的三个输出分配存储空间。给出完整代码。

import tensorrt as trt
import pycuda.driver as cuda
import pycuda.autoinit
import time
import cv2
import numpy as np
import torch
np.bool = np.bool_

f = open("best32.trt", "rb")
runtime = trt.Runtime(trt.Logger(trt.Logger.WARNING))
engine = runtime.deserialize_cuda_engine(f.read())
context = engine.create_execution_context()

# for binding in engine:
#     dims = engine.get_binding_shape(binding)
#     size = trt.volume(dims)
#     print("The size of binding is", size)
#     print("The dimension of binding is", dims)
#     print(binding)
#     print("input = ", engine.binding_is_input(binding))
#     print("dtype =", trt.nptype(engine.get_binding_dtype(binding)))

# 加载图像
image_path = "left_2.jpg"
input_image = cv2.imread(image_path)  # 使用 OpenCV 加载图像
# 调整图像大小和通道顺序,以适应模型输入
input_image = cv2.resize(input_image, (640, 480))  # 调整大小为模型输入大小
input_image = cv2.cvtColor(input_image, cv2.COLOR_BGR2RGB)  # 调整通道顺序为 RGB
input_image = np.transpose(input_image, (2, 0, 1))  # 调整通道顺序为 CxHxW
input_image = input_image / 255.0
# 添加批处理维度
input_tensor = np.expand_dims(input_image, axis=0)
input_tensor = np.ascontiguousarray(input_tensor, dtype=np.float32)
# print(input_tensor.shape)
# print(input_tensor)

output = np.empty([1, 18900, 6], dtype=np.float32)
output1 = np.empty([1, 3, 60, 80, 6], dtype=np.float32)
output2 = np.empty([1, 3, 30, 40, 6], dtype=np.float32)
output3 = np.empty([1, 3, 15, 20, 6], dtype=np.float32)


# allocate device memory
d_input = cuda.mem_alloc(1 * input_tensor.nbytes)
d_output = cuda.mem_alloc(1 * output.nbytes)
d1_output = cuda.mem_alloc(1 * output1.nbytes)
d2_output = cuda.mem_alloc(1 * output2.nbytes)
d3_output = cuda.mem_alloc(1 * output3.nbytes)
bindings = [int(d_input), int(d1_output), int(d2_output), int(d3_output), int(d_output)]
stream = cuda.Stream()

def predict(preprocessed_images):  # result gets copied into output
    # transfer input data to device
    cuda.memcpy_htod_async(d_input, preprocessed_images, stream)  # execute model
    context.execute_async_v2(bindings, stream.handle, None)  # transfer predictions back
    cuda.memcpy_dtoh_async(output, d_output, stream)  # syncronize threads 只需要d_output的结果。
    stream.synchronize()
    d_input.free()
    d_output.free()
    return output

t0 = time.time()

pred = predict(input_tensor)

print(pred)
print(pred.shape)

# 之后的pred 直接使用detect.py里面的non_max_suppression函数就好了  
# 显示输出图片之类的用detect.py里面的for i, det in enumerate(pred): 

t1 = time.time()
print(f'One frame spends time = ({t1 - t0:.3f}s)')


### 使用 TensorRT 加速 YOLO 模型推理的最佳实践 #### 选择合适的编程语言 对于较小的YOLO模型(如YOLOV5-s),C++版本的TensorRT实现相比Python版本能够提供显著的速度提升,大约可以达到三倍的加速效果[^1]。因此,在追求高性能的情况下,建议优先考虑使用C++来部署YOLO模型。 #### 准备工作 为了利用TensorRTYOLO模型进行优化并生成高效的执行引擎,首先需要安装NVIDIA提供的TensorRT库以及相应的依赖项[^2]。这通常涉及到配置CUDA环境,并确保系统支持所需的硬件特性。 #### 转换模型格式 将训练好的YOLO模型转换成适合TensorRT处理的形式是一个重要的步骤。一般而言,这意味着要先将其导出为ONNX格式文件,然后再通过`trtexec`工具或者API接口加载文件创建对应的TensorRT引擎。 ```bash python -m onnxruntime.tools.convert_onnx_models_to_trt --input_model yolov5s.onnx --output_model yolov5s.trt ``` 这段命令展示了如何借助onnxruntime中的内置功能快速完成从ONNX到TRT Engine的转变过程。 #### 编写推理代码 编写具体的推理程序时,无论是采用Python还是C++ API,都需要初始化TensorRT运行时对象、解析已保存下来的序列化形式engine数据流、分配输入/输出缓冲区空间等操作。下面给出了一段简单的基于PyTorchpaddledetector封装后的伪代码片段用于演示目的: ```python import tensorrt as trt from pycuda import driver, compiler, gpuarray import numpy as np def load_engine(engine_file_path): TRT_LOGGER = trt.Logger(trt.Logger.WARNING) with open(engine_file_path, 'rb') as f, \ trt.Runtime(TRT_LOGGER) as runtime: return runtime.deserialize_cuda_engine(f.read()) class HostDeviceMem(object): def __init__(self, host_mem, device_mem): self.host = host_mem self.device = device_mem def __str__(self): return "Host:\n" + str(self.host) + "\nDevice:\n" + str(self.device) def __repr__(self): return self.__str__() def allocate_buffers(engine): inputs = [] outputs = [] bindings = [] stream = cuda.Stream() for binding in engine: size = trt.volume(engine.get_binding_shape(binding)) * engine.max_batch_size dtype = trt.nptype(engine.get_binding_dtype(binding)) # Allocate host and device buffers. host_mem = cuda.pagelocked_empty(size, dtype) device_mem = cuda.mem_alloc(host_mem.nbytes) # Append the device buffer to device bindings. bindings.append(int(device_mem)) # Append to appropriate list based on whether this is an input or output. if engine.binding_is_input(binding): inputs.append(HostDeviceMem(host_mem, device_mem)) else: outputs.append(HostDeviceMem(host_mem, device_mem)) return inputs, outputs, bindings, stream if __name__ == '__main__': ENGINE_PATH = './yolov5s.trt' INPUT_IMAGE_PATH = './test.jpg' image = cv2.imread(INPUT_IMAGE_PATH).astype(np.float32)[np.newaxis,...] with load_engine(ENGINE_PATH) as engine,\ engine.create_execution_context() as context: inputs, outputs, bindings, stream = allocate_buffers(engine) # Transfer data from CPU to GPU memory. [np.copyto(i.host, d.reshape(-1)) for i,d in zip(inputs,[image])] [cuda.memcpy_htod_async(inp.device, inp.host, stream) for inp in inputs] # Run inference. context.execute_async_v2(bindings=bindings, stream_handle=stream.handle) # Transfer predictions back from GPU. [cuda.memcpy_dtoh_async(out.host, out.device, stream) for out in outputs] stream.synchronize() results = [out.host for out in outputs] print(results) ``` 上述脚本实现了基本的数据预处理、内存管理及异步调用机制等功能模块,使得整个流程更加高效流畅。 #### 测试与验证 最后一步是对经过TensorRT优化之后的新版YOLO检测器进行全面测试,包括但不限于精度评估、性能测量等方面的工作。只有当确认新方案能够在保持原有识别能力的同时带来预期之外的时间消耗减少时才算真正成功完成了此次迁移改造任务。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值