第一次接触YOLOv8和RT-DETR时,我被它们的强大功能所震撼。作为目标检测领域的两个重量级选手,它们在实际应用中展现出了惊人的性能。YOLOv8作为YOLO系列的最新版本,继承了前代产品的优势,在速度和精度之间取得了更好的平衡。而RT-DETR则是百度推出的实时目标检测模型,基于Transformer架构,在保持高精度的同时实现了实时检测。
这两个模型虽然架构不同,但都被集成到了Ultralytics生态中,这意味着我们可以用相似的API来调用它们。这种设计大大降低了学习成本,开发者可以轻松地在两个模型之间切换,根据具体需求选择更适合的解决方案。
在实际项目中,我发现YOLOv8特别适合需要极致速度的场景,比如实时视频分析。而RT-DETR则在处理复杂场景时表现更稳定,特别是当目标之间存在大量遮挡时。不过无论选择哪个模型,最终都会得到一个results对象,这是我们连接模型预测和业务逻辑的关键桥梁。
使用YOLOv8或RT-DETR进行预测和追踪非常简单。下面是一个完整的示例代码:
python复制from ultralytics import YOLO
# 加载预训练模型
model = YOLO('yolov8n.pt') # 或者使用RT-DETR模型
# 视频文件预测+追踪
results = model.track(
source='input_video.mp4',
stream=True, # 启用流式处理
tracker='botsort.yaml', # 使用BoTSORT追踪器
conf=0.5, # 置信度阈值
iou=0.7, # IoU阈值
device='cuda:0' if torch.cuda.is_available() else 'cpu'
)
# 处理每一帧结果
for frame_idx, r in enumerate(results):
# 获取检测框信息
boxes = r.boxes
# 获取掩码信息(如果做实例分割)
masks = r.masks
# 获取关键点(如果做姿态估计)
keypoints = r.keypoints
# 在这里添加你的业务逻辑处理代码
这段代码展示了最基本的预测和追踪流程。其中stream=True参数对于处理长视频特别重要,它可以防止内存溢出。在实际应用中,我发现这个参数能显著提升程序的稳定性,特别是在处理高清视频时。
处理视频流时,有几个关键点需要注意:
内存管理:长时间运行的视频处理程序很容易出现内存泄漏。除了使用stream=True外,建议定期检查内存使用情况,必要时手动释放不再需要的资源。
性能优化:可以通过调整vid_stride参数来跳过一些帧,这在实时性要求不高的场景下可以大幅提升处理速度。例如,设置vid_stride=2表示每两帧处理一帧。
多线程处理:对于高帧率视频,可以考虑使用多线程来处理results对象,将检测和业务逻辑分离到不同线程中。
我曾经在一个工地监控项目中遇到过一个典型问题:程序运行一段时间后会突然崩溃。经过排查发现是内存泄漏导致的。解决方法是在处理完每一帧后,显式地调用gc.collect()进行垃圾回收,同时限制视频处理的时长,定期重启处理程序。
results对象是我们从模型获取的所有信息的容器。理解它的结构对于后续的业务逻辑开发至关重要。一个完整的results对象包含以下关键属性:
其中boxes对象又包含多个有用的属性和方法:
python复制# 获取所有检测框的坐标(像素单位)
boxes_xyxy = boxes.xyxy # [x1, y1, x2, y2]格式
boxes_xywh = boxes.xywh # [x, y, width, height]格式
# 获取归一化坐标(0-1之间)
boxes_xyxyn = boxes.xyxyn
boxes_xywhn = boxes.xywhn
# 获取其他信息
confidences = boxes.conf # 置信度
class_ids = boxes.cls # 类别ID
track_ids = boxes.id # 追踪ID(仅追踪时存在)
在实际项目中,我们经常需要对results数据进行进一步处理。以下是一些常见场景的解决方案:
场景1:统计特定类别的数量
python复制# 统计画面中人的数量
person_class_id = 0 # 假设0对应'person'类
person_count = (boxes.cls == person_class_id).sum()
场景2:计算目标之间的距离
python复制import numpy as np
# 获取所有检测框的中心点
centers = boxes.xywh[:, :2].cpu().numpy()
# 计算两两之间的距离矩阵
dist_matrix = np.sqrt(np.sum((centers[:, None] - centers) ** 2, axis=-1))
# 找出距离小于安全阈值的对
safe_threshold = 100 # 像素单位
unsafe_pairs = np.argwhere(dist_matrix < safe_threshold)
场景3:检测违规行为
python复制# 检测是否有人未戴安全帽
person_boxes = boxes[boxes.cls == 0] # 人
helmet_boxes = boxes[boxes.cls == 1] # 安全帽
for p_box in person_boxes:
p_center = p_box.xywh[0, :2]
# 检查附近是否有安全帽
has_helmet = False
for h_box in helmet_boxes:
h_center = h_box.xywh[0, :2]
distance = torch.norm(p_center - h_center)
if distance < 50: # 阈值
has_helmet = True
break
if not has_helmet:
print(f"发现未戴安全帽的人员!位置:{p_box.xyxy}")
将原始检测结果转化为业务决策是项目成功的关键。以工地安全监控为例,我们需要实现以下功能:
实现这些功能的核心在于对results对象的深度解析。下面是一个完整的业务逻辑处理示例:
python复制def process_frame(results, frame_idx):
# 初始化业务数据
biz_data = {
'person_count': 0,
'no_helmet': 0,
'danger_zone': 0,
'alert_list': []
}
boxes = results.boxes
if boxes is None or len(boxes) == 0:
return biz_data
# 获取各类别检测框
person_boxes = boxes[boxes.cls == 0]
helmet_boxes = boxes[boxes.cls == 1]
vehicle_boxes = boxes[boxes.cls == 2]
# 统计人员数量
biz_data['person_count'] = len(person_boxes)
# 检查安全帽佩戴情况
for p_box in person_boxes:
p_xyxy = p_box.xyxy[0]
p_center = p_box.xywh[0, :2]
# 检查是否有安全帽
has_helmet = False
for h_box in helmet_boxes:
h_center = h_box.xywh[0, :2]
if torch.norm(p_center - h_center) < 50:
has_helmet = True
break
if not has_helmet:
biz_data['no_helmet'] += 1
biz_data['alert_list'].append({
'type': 'no_helmet',
'position': p_xyxy.tolist(),
'frame': frame_idx
})
# 检查危险区域入侵
danger_zones = [(100, 100, 300, 300)] # 定义危险区域坐标
for zone in danger_zones:
zone_x1, zone_y1, zone_x2, zone_y2 = zone
for p_box in person_boxes:
x1, y1, x2, y2 = p_box.xyxy[0]
# 简单检查是否相交
if not (x2 < zone_x1 or x1 > zone_x2 or y2 < zone_y1 or y1 > zone_y2):
biz_data['danger_zone'] += 1
biz_data['alert_list'].append({
'type': 'danger_zone',
'position': p_box.xyxy[0].tolist(),
'frame': frame_idx
})
return biz_data
在实际部署中,我们需要考虑更多工程细节:
results.plot()方法可以快速生成可视化结果,但有时需要自定义绘制逻辑:python复制# 自定义绘制方法
def custom_plot(results, frame):
plotted = results.plot() # 基础绘制
# 添加自定义元素
cv2.putText(plotted, f"Person: {person_count}", (10, 30),
cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
# 绘制危险区域
for zone in danger_zones:
cv2.rectangle(plotted, (zone[0], zone[1]),
(zone[2], zone[3]), (0, 0, 255), 2)
return plotted
python复制import json
from datetime import datetime
def save_results(results, frame_idx):
timestamp = datetime.now().isoformat()
data = {
'timestamp': timestamp,
'frame': frame_idx,
'person_count': len(results.boxes[results.boxes.cls == 0]),
'alerts': process_frame(results, frame_idx)['alert_list']
}
with open(f'results/{frame_idx}.json', 'w') as f:
json.dump(data, f)
python复制def check_alerts(biz_data):
if biz_data['no_helmet'] > 0:
trigger_alert("安全帽警报", biz_data['no_helmet'])
if biz_data['danger_zone'] > 0:
trigger_alert("危险区域入侵", biz_data['danger_zone'])
def trigger_alert(alert_type, count):
# 这里可以实现邮件、短信、API等各种报警方式
print(f"[警报] {alert_type} 数量: {count}")
# 示例:播放警报音
import winsound
winsound.Beep(1000, 500)
在实际项目中,我发现将业务逻辑模块化非常重要。每个功能应该独立成单独的模块或函数,这样既方便调试,也便于后期维护和扩展。比如可以将人员计数、安全装备检查、区域监测等功能分开实现,然后在主流程中组合调用。