第12章(2)------项目九:基于gr.Video+RT-DETR的流目标检测
-
- [12.3 RT-DETR:实时端到端检测模型](#12.3 RT-DETR:实时端到端检测模型)
-
- [12.3.1 模型技术、架构与最新版本](#12.3.1 模型技术、架构与最新版本)
- [12.3.2 图像目标识别示例](#12.3.2 图像目标识别示例)
- [12.4 项目九:基于gr.Video+RT-DETR的流目标检测](#12.4 项目九:基于gr.Video+RT-DETR的流目标检测)
-
- [12.4.1 流目标检测函数及解读](#12.4.1 流目标检测函数及解读)
- [12.4.2 绘制边界框及界面实现](#12.4.2 绘制边界框及界面实现)
12.3 RT-DETR:实时端到端检测模型
在使用gr.Video+RT-DETR模型构建视频流目标检测系统之前,先了解下RT-DETR模型。先讲解模型的技术与架构,然后着眼于训练细节与性能表现,接着介绍其最新版本RT-DETRv2,最后通过图像目标识别示例演示其用法及列出下一步的学习资源。
12.3.1 模型技术、架构与最新版本
模型技术突破。RT-DETR(Real-Time DEtection TRansformer)模型在论文《DETRs Beat YOLOs on Real-time Object Detection》(🖇️链接12-5)中提出,它是一种专注于实现实时性能同时保持高精度的目标检测模型。该模型基于深度学习领域的Transformer架构,通过对图像进行处理来识别并定位其中的多个目标。
近年来,基于Transformer的端到端检测器(DETRs)已取得显著性能突破。然而,DETRs计算成本高的问题尚未得到有效解决,这阻碍了其充分发挥无需后处理的优势,如非极大值抑制NMS(non-maximum suppression)。鉴于现代实时目标检测器中NMS对推理速度的延迟影响,原论文建立了端到端速度基准并提出了首个实时端到端目标检测器RT-DETR。具体而言,RT-DETR模型在以下方面实现了突破:
- 设计高效混合编码器,通过解耦尺度内交互与跨尺度融合来高效处理多尺度特征。
- 提出IoU交并比感知查询选择机制,以优化目标查询的初始化。
- 提出的检测器支持通过灵活调整解码器层数(无需重新训练)来调节推理速度,提升了实时检测器的实用性。
模型架构与目标 。模型架构如图12-4所示,RT-DETR系统首先采用预训练的卷积神经网络(基于原始代码中的ResNet-D改进架构)对输入图像进行预处理,然后将主干网络的特征输入最后三个阶段。其处理过程如下所述:
(1)第一阶段,高效混合编码器通过基于注意力机制的AIFI(Intra-scale Feature Interaction,尺度内特征交互)和基于CNN的CCFF(Cross-scale Feature Fusion,跨尺度特征融合),将多尺度特征转换为图像特征序列。
(2)第二阶段,不确定性最小化查询选择机制,会选取固定数量的编码器特征作为解码器查询的初始对象。
(3)第三阶段,带有辅助预测头的解码器通过迭代优化查询,生成目标类别与边界框。

图12-4
关于RT-DETR请参阅:PekingU/rtdetr_r101vd_coco_o365🖇️链接12-6。
最新版本RT-DETRv2 。RT-DETR系列的最新版本是RT-DETRv2模型,它在论文《RT-DETRv2: Improved Baseline with Bag-of-Freebies for Real-Time Detection Transformer 》(🖇️链接12-7)中提出。RT-DETRv2模型介绍如下:
- 改进优化:相较于RT-DETR,引入可选择的多尺度特征提取,采用离散采样算子提升部署兼容性,以及改进训练策略(包括动态数据增强和尺度自适应超参数)。这些改进在保持实时性能的同时,显著增强了模型的灵活性和实用性。
- 训练过程:RT-DETRv2模型基于COCO train2017训练集进行训练,并在COCO val2017验证集进行性能验证,并采用标准AP指标以及APval50作为评估标准。
- 应用场景:非常适合需要实时目标检测的多种应用场景,包括自动驾驶、监控系统、机器人技术和零售分析。其增强的灵活性和部署友好型设计,使其既适用于边缘设备也能满足大规模系统需求,确保动态真实环境中实现高精度与高速检测。
使用RT-DETRv2代码,只需导入RT-DETRv2并替换模型名称,如下所示:
py
from transformers import RTDetrV2ForObjectDetection, RTDetrImageProcessor
image_processor = RTDetrImageProcessor.from_pretrained("PekingU/rtdetr_v2_r101vd")
model = RTDetrV2ForObjectDetection.from_pretrained("PekingU/rtdetr_v2_r101vd")
关于RT-DETRv2请参阅:PekingU/rtdetr_v2_r101vd🖇️链接12-8。
12.3.2 图像目标识别示例
本节展示使用RT-DETR进行图标目标识别的代码,如代码12-8所示:
代码12-8
py
import torch
import requests
from PIL import Image
from transformers import RTDetrForObjectDetection, RTDetrImageProcessor
url = 'http://images.cocodataset.org/val2017/000000039769.jpg'
image = Image.open(requests.get(url, stream=True).raw)
image_processor = RTDetrImageProcessor.from_pretrained("PekingU/rtdetr_r50vd")
inputs = image_processor(images=image, return_tensors="pt")
model = RTDetrForObjectDetection.from_pretrained("PekingU/rtdetr_r50vd")
with torch.no_grad():
outputs = model(**inputs)
results = image_processor.post_process_object_detection(outputs, target_sizes=torch.tensor([(image.height, image.width)]), threshold=0.3)
for result in results:
for score, label_id, box in zip(result["scores"], result["labels"], result["boxes"]):
score, label = score.item(), label_id.item()
box = [round(i, 2) for i in box.tolist()]
print(f"{model.config.id2label[label]}: {score:.2f} {box}")
代码比较简单,不再解释。输出如下所示:
bash
sofa: 0.97 [0.14, 0.38, 640.13, 476.21]
cat: 0.96 [343.38, 24.28, 640.14, 371.5]
remote: 0.95 [40.11, 73.44, 175.96, 118.48]
可使用以下代码进行可视化:
py
from PIL import ImageDraw
draw = ImageDraw.Draw(image)
for score, label, box in zip(results["scores"], results["labels"], results["boxes"]):
box = [round(i, 2) for i in box.tolist()]
x, y, x2, y2 = tuple(box)
draw.rectangle((x, y, x2, y2), outline="red", width=1)
draw.text((x, y), model.config.id2label[label.item()], fill="white")
12.4 项目九:基于gr.Video+RT-DETR的流目标检测
本节使用RT-DETR对用户上传视频进行实时目标检测,并借助Gradio视频流功能实现检测结果的实时传输。开始之前,先安装相关库并从Hub下载预训练模型:
py
from transformers import RTDetrForObjectDetection, RTDetrImageProcessor
image_processor = RTDetrImageProcessor.from_pretrained("PekingU/rtdetr_r50vd")
model = RTDetrForObjectDetection.from_pretrained("PekingU/rtdetr_r50vd").to("cuda")
另外,将模型通过代码to("cuda")加载至GPU显存以加速推理。本项目可部署在Spaces平台,利用其免费的ZeroGPU集群加速运行推理任务。本项目Spaces地址:gradio/rt-detr-object-detection🖇️链接12-9。
12.4.1 流目标检测函数及解读
以下是完整的推理函数代码,如代码12-9所示:
代码12-9
py
import spaces
import cv2
from PIL import Image
import torch
import time
import numpy as np
import uuid
from draw_boxes import draw_bounding_boxes
SUBSAMPLE = 2
@spaces.GPU
def stream_object_detection(video, conf_threshold):
cap = cv2.VideoCapture(video)
fps = int(cap.get(cv2.CAP_PROP_FPS))
desired_fps = fps // SUBSAMPLE # 帧率减半
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) // 2 # 尺寸缩小一半
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) // 2
output_video_name = f"output_{uuid.uuid4()}.mp4"
video_codec = cv2.VideoWriter_fourcc(*"mp4v")
output_video = cv2.VideoWriter(output_video_name, video_codec, desired_fps, (width, height))
iterating, frame = cap.read()
n_frames = 0
batch = []
while iterating:
frame = cv2.resize( frame, (0,0), fx=0.5, fy=0.5) # 尺寸缩小一半
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
if n_frames % SUBSAMPLE == 0: # 帧率减半
batch.append(frame)
if len(batch) == 2 * desired_fps: # 两秒为片段生成视频
inputs = image_processor(images=batch, return_tensors="pt").to("cuda")
with torch.no_grad():
outputs = model(**inputs) # RT-DETR模型检测对象
boxes = image_processor.post_process_object_detection(outputs,
target_sizes=torch.tensor([(height, width)] * len(batch)),
threshold=conf_threshold) # 边界框缩放到输入帧的原始尺寸
for i, (array, box) in enumerate(zip(batch, boxes)):
pil_image = draw_bounding_boxes(Image.fromarray(array), box, model, conf_threshold)
frame = np.array(pil_image)
# Convert RGB to BGR
frame = frame[:, :, ::-1].copy()
output_video.write(frame)
batch = []
output_video.release()
yield output_video_name
output_video_name = f"output_{uuid.uuid4()}.mp4"
output_video = cv2.VideoWriter(output_video_name, video_codec, desired_fps, (width, height))
iterating, frame = cap.read()
n_frames += 1
代码实现目标检测功能,下面逐段解析代码:
- 推理函数首先接收一个视频和用户设定的置信度阈值,目标检测模型会识别视频中多个对象并为每个对象分配置信度分数。
- 本应用将使用OpenCV处理视频,因为它是Python中处理视频的行业标准。通过OpenCV的VideoCapture型变量cap读取输入视频,每次调用cap.read()都会获取视频的下一帧。
- 由于存在基于时间的配额限制,所以需要在ZeroGPU上尽可能降低推理时间。将输出视频的原始帧率减半,并将尺寸缩小为原来一半以降低推理时间。
- 为了实现Gradio视频流传输,需要为每个输出视频"块(chunk)"生成不同的视频文件,这里使用output_video = cv2.VideoWriter(output_video_name, video_codec, desired_fps, (width, height))创建要写入的下一个视频文件。其中video_codec用于指定视频文件类型,目前仅支持"mp4"和"ts"格式的视频流。
- 对于视频中的每一帧,都会将其尺寸缩小一半。由于OpenCV以BGR格式读取文件,需要先转换为transformer预期的RGB格式。且每隔一帧采集一次,并将帧添加至批处理,从而使输出视频的帧率减半。当批处理视频内容累计达到两秒时,就会运行模型推理。选择两秒阈值(Gradio要求至少1秒)为了确保时间足够短,既能实现服务器上的视频流畅显示,又不需要进行过多独立的前向传播。
- 利用函数image_processor,将batch中图像处理成模型可以接受的格式。然后遍历每一帧图像并运行RT-DETR模型以进行对象检测。运行模型的前向传播后,通过后处理方法将检测到的边界框缩放到输入帧的原始尺寸。
- 通过自定义函数绘制边界框,draw_bounding_boxes会为帧中每个检测到的对象绘制边界框。之后需要将RGB格式转换回BGR格式,并将帧保存到新的输出视频中。完成当前批处理后,为下一批处理创建新的输出视频文件。
12.4.2 绘制边界框及界面实现
其中绘制边界框函数draw_bounding_boxes源码,如代码12-10所示:
代码12-10
py
# draw_boxes.py
from PIL import ImageDraw, ImageFont
import colorsys
def get_color(label):
hash_value = hash(label)
hue = (hash_value % 100) / 100.0
saturation = 0.7
value = 0.9
rgb = colorsys.hsv_to_rgb(hue, saturation, value)
return tuple(int(x * 255) for x in rgb)
def draw_bounding_boxes(image, results: dict, model, threshold=0.3):
draw = ImageDraw.Draw(image)
font = ImageFont.load_default()
for score, label_id, box in zip(
results["scores"], results["labels"], results["boxes"]):
if score > threshold:
label = model.config.id2label[label_id.item()]
box = [round(i, 2) for i in box.tolist()]
color = get_color(label)
# Draw bounding box
draw.rectangle(box, outline=color, width=3)
# Prepare text
text = f"{label}: {score:.2f}"
text_bbox = draw.textbbox((0, 0), text, font=font)
text_width = text_bbox[2] - text_bbox[0]
text_height = text_bbox[3] - text_bbox[1]
# Draw text background with color blocks
draw.rectangle(
[box[0], box[1] - text_height - 4, box[0] + text_width, box[1]],
fill=color)
draw.text((box[0], box[1] - text_height - 4), text, fill="white", font=font)
return image
该代码模块使用Pillow库(PIL)实现目标检测结果的可视化标注,主要功能有:
- 函数draw_bounding_boxes用于在图像上绘制目标检测结果,包括边界框、带透明背景的类别标签及置信度分数。核心功能包含:过滤低置信度检测结果(默认阈值0.3)、绘制彩色边界框(宽度3像素)、添加带背景的文本标签(类别名+置信度)、自动计算文本位置使其位于框体上方。
- 函数get_color为不同类别生成固定颜色。通过标签的哈希值计算HSV色彩,保证相同输入始终产生相同输出颜色。
Gradio界面实现 。该UI代码与标准Gradio应用基本一致,我们将采用经典的双栏布局,使用户可以并排查看输入和输出视频。要实现流式传输功能,必须在输出视频组件中设置streaming=True参数。另外参数autoplay虽然并非必需配置,但启用该选项可显著提升用户观感。注意事项:
(1)运行前,选装GPU版本的torch和torchvision,命令如下:
bash
# 查看本机cuda版本命令:nvidia-smi
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu128
(2)gr.Video的upload()可能不会自动触发(软件Bug),读者可替换为WebRTC。
设置Gradio界面代码如代码12-11所示:
代码12-11
py
import gradio as gr
with gr.Blocks() as app:
with gr.Row():
with gr.Column():
video = gr.Video(label="Video Source")
conf_threshold = gr.Slider(label="Confidence Threshold",
minimum=0.0, maximum=1.0, step=0.05, value=0.30)
with gr.Column():
otv = gr.Video(label="Processed Video", streaming=True, autoplay=True)
video.upload(fn=stream_object_detection,
inputs=[video, conf_threshold], outputs=[otv])
最终运行界面如图12-5所示:

图12-5