本文首发于公众号【DeepDriving】,欢迎关注。
0. 引言
RT-DETR
是百度开源的一个基于DETR
架构的实时端到端目标检测算法,在速度和精度上均超过了YOLOv5
、YOLOv8
等YOLO
系列检测算法,目前在YOLOv8
的官方代码仓库ultralytics
中也已支持RT-DETR
算法。
在上一篇文章《AI模型部署 | onnxruntime部署YOLOv8分割模型详细教程》中我介绍了如何使用onnxruntime框架来部署YOLOv8
分割模型,本文将介绍如何使用onnxruntime
框架来部署RT-DETR
模型,代码还是采用Python
实现。
1. 准备工作
安装onnxruntime
onnxruntime
分为GPU
版本和CPU
版本,均可以通过pip
直接安装:pip install onnxruntime-gpu#安装GPU版本pip install onnxruntime#安装CPU版本
注意:
GPU
版本和CPU
版本建议只选其中一个安装,否则默认会使用CPU
版本。下载
RT-DETR
模型Ultralytics
官方提供了用COCO
数据集训练的RT-DETR
模型权重,我们可以直接从GitHub
网站https://github.com/ultralytics/assets/releases
下载使用,本文使用的模型为rtdetr-l.pt
。转换为
onnx
模型调用下面的命令可以把
rtdetr-l.pt
模型转换为onnx
格式的模型:yolo export model=rtdetr-l.pt format=onnx opset=17 simplify=True
转换成功后得到的模型为
rtdetr-l.onnx
。这里设置opset=17
是为了能够直接导出LayerNormalization
算子,因为onnx
从版本17
开始才支持该算子。如果opset
小于17
,那么导出模型的时候会把LayerNormalization
算子拆分成一系列子算子进行导出。下面两幅图分别是设置opset=16
和opset=17
导出的onnx
模型中的结构:opset=16,红色框内是把LayerNormalization进行拆分的结果
opset=17:
另外,这里设置
simplify=True
是为了调用onnx-simplifier
工具对模型进行简化,否则生成的模型有一大堆零碎的算子,看起来就很麻烦。
2. 模型部署
2.1 加载onnx模型
首先导入onnxruntime
包,然后调用其API
加载模型即可:
import onnxruntime as ortsession = ort.InferenceSession("rtdetr-l.onnx", providers=["CUDAExecutionProvider"])
因为我使用的是GPU
版本的onnxruntime
,所以providers
参数设置的是"CUDAExecutionProvider"
;如果是CPU
版本,则需设置为"CPUExecutionProvider"
。
模型加载成功后,我们可以查看一下模型的输入、输出层的属性:
for input in session.get_inputs():print("input name: ", input.name)print("input shape: ", input.shape)print("input type: ", input.type)for output in session.get_outputs():print("output name: ", output.name)print("output shape: ", output.shape)print("output type: ", output.type)
结果如下:
input name:imagesinput shape:[1, 3, 640, 640]input type:tensor(float)output name:output0output shape:[1, 300, 84]output type:tensor(float)
从上面的打印信息可以知道,模型有一个尺寸为[1, 3, 640, 640]
的输入层和一个尺寸分别为[1, 300, 84]
的输出层。
2.2 数据预处理
数据预处理采用OpenCV
和Numpy
实现,首先导入这两个包
import cv2import numpy as np
用OpenCV
读取图片后,把数据按照与YOLOv8
一样的要求做预处理
image = cv2.imread("soccer.jpg")image_height, image_width, _ = image.shapeinput_tensor = prepare_input(image, model_width, model_height)print("input_tensor shape: ", input_tensor.shape)
其中预处理函数prepare_input
的实现如下:
def prepare_input(bgr_image, width, height):image = cv2.cvtColor(bgr_image, cv2.COLOR_BGR2RGB)image = cv2.resize(image, (width, height)).astype(np.float32)image = image / 255.0image = np.transpose(image, (2, 0, 1))input_tensor = np.expand_dims(image, axis=0)return input_tensor
处理流程如下:
1. 把OpenCV读取的BGR格式的图片转换为RGB格式;2. 把图片resize到模型输入尺寸640x640;3. 对像素值除以255做归一化操作;4. 把图像数据的通道顺序由HWC调整为CHW;5. 扩展数据维度,将数据的维度调整为NCHW。
经过预处理后,输入数据input_tensor
的维度变为[1, 3, 640, 640]
,与模型的输入尺寸一致。
2.3 模型推理
输入数据准备好以后,就可以送入模型进行推理:
outputs = session.run(None, {session.get_inputs()[0].name: input_tensor})
前面我们打印了模型的输入输出属性,可以知道模型只有一个输出分支,所以只要取outputs[0]
的数据进行处理:
output = np.squeeze(outputs[0])print("output shape: ", output.shape)
output shape:(300, 84)
2.4 后处理
从前文知道模型输出的维度为300x84
,其中300
表示模型在一张图片上最多能检测的目标数量,84
表示每个目标的参数包含4
个坐标属性(cx,cy,w,h
)和80
个类别置信度。每个目标的坐标信息都是做了归一化的,只要乘以原始图像的尺寸就可以把坐标恢复到在原始图像中的大小。RT-DETR
的检测结果不需要再做NMS
这些额外的后处理操作。后处理的代码如下:
for out in output:confidence = out[4:].max()if confidence < 0.5:continueclass_id = out[4:].argmax()cx, cy, bw, bh = out[:4]xmin = (cx - 0.5 * bw) * image_widthxmax = (cx + 0.5 * bw) * image_widthymin = (cy - 0.5 * bh) * image_heightymax = (cy + 0.5 * bh) * image_height
可以看到,RT-DETR
的后处理极其简单!
检测效果也是很不错的:
4. 参考资料
- 超越YOLOv8,飞桨推出精度最高的实时检测器RT-DETR
- https://docs.ultralytics.com/zh/models/rtdetr/