基于 YOLO + ByteTrack 的实时人车流量计数系统实现
前言
在很多商业和工业场景中,“目标检测”往往只是第一步。比如在景区、商场或交通路口,仅仅知道“画面里有多少人和车”是不够的,客户通常需要更精确的数据:
- “今天有多少人进入了商场,多少人离开了?”
- “这个路口从左往右开过去了多少辆车?”
要实现这种统计,我们需要把目标检测(Object Detection)升级为多目标跟踪(Multi-Object Tracking, MOT),并在帧与帧之间记住每一个目标的身份(ID)。
本文将带你使用 YOLO (v8/v11) 与当前最前沿的 ByteTrack 跟踪算法,配合巧妙的几何数学计算,从零实现一个人流与车流双向越界实时计数系统。
一、核心技术原理
1. 为什么选择 ByteTrack?
传统的跟踪算法(如 DEEPSORT)依赖昂贵的外观特征提取网络,不仅运行慢,而且在目标被遮挡时容易丢失 ID。
ByteTrack(发表于 ECCV 2022)的核心思想非常优雅:“不放弃低置信度的检测框”。
- 第一步(Association 1):在高置信度检测框中与已有的跟踪轨迹进行匹配。
- 第二步(Association 2):对于那些因为遮挡、模糊导致置信度降低的低分框,再次与上一帧的未匹配轨迹进行二次匹配。
这种“变废为宝”的设计,让 ByteTrack 在小目标、遮挡和运动模糊场景下拥有极强的鲁棒性,且无需任何深度特征提取器,推理速度极快。
2. 双向越界判定:向量叉乘法
我们要统计“跨过某条虚拟线段”的物体数量,如何通过代码精准判定?
如果使用简单的坐标大小对比,由于目标移动速度不同,很容易在跨线瞬间发生“漏检”或“重复计数”。最严谨、工业界最通用的方法是几何向量叉乘法。
假设我们在屏幕上绘制了一条虚拟检测线段,起点为 $A(x_A, y_A)$,终点为 $B(x_B, y_B)$。
对于物体的中心点(或足部中点),我们在前一帧的坐标为 $P_{prev}$,当前帧的坐标为 $P_{curr}$。
要判定运动线段 $P_{prev}P_{curr}$ 是否穿过了检测线段 $AB$,我们需要满足两个基本条件:
- 快速排斥实验:两条线段的包围盒必须相交。
- 跨立实验:线段 $AB$ 的两个端点在线段 $P_{prev}P_{curr}$ 的两侧,且线段 $P_{prev}P_{curr}$ 的两个端点也在线段 $AB$ 的两侧。
这可以通过向量的**叉乘(Cross Product)**正负号来高效判定。当叉乘结果发生符号翻转时,即说明物体“跨越”了线段。
二、开发环境搭建
只需安装以下三个 Python 核心库即可开始:
1 | pip install ultralytics opencv-python numpy |
注意:
ultralytics库中已经内置封装了 ByteTrack 跟踪器,我们不需要下载额外的 ByteTrack 代码仓库,这极大简化了工程部署难度。
三、完整实现源码
新建文件 object_counting.py,粘贴以下经过工业级优化的完整代码。代码中包含了平滑轨迹绘制、动态双向计数和优雅的 OpenCV 半透明看板展示:
1 | import cv2 |
四、核心代码精讲
1. model.track 究竟做了什么?
在上一篇文章中,我们使用的是普通的检测方法 model(frame)。在这段代码里,我们将其换成了支持持续跟踪的 model.track() 方法。
1 | results = model.track(source=frame, persist=True, tracker="bytetrack.yaml") |
persist=True:这是激活跟踪器的关键。它告诉 YOLO 在处理当前帧时,要参考上一帧的跟踪状态,并确保同一个物体的box.id保持不变。tracker="bytetrack.yaml":Ultralytics 内置了两种跟踪器,即"bytetrack.yaml"和"botsort.yaml"。相比之下,ByteTrack 速度更快,适合实时性要求高、算力相对有限的场景。
2. 为什么选择足部中心点 cy = int(y2) 而不是几何中心?
对于人流和车流,它们都是在地面上移动的。由于相机的透视投影关系,物体的上半身容易在跨线前发生倾斜,从而导致越界判定不准。
因此,选用**检测框底边中点(足部/轮胎贴地位置)**作为基准点,其跨线判定的物理位置最为准确。
3. 如何解决“同个 ID 往返跨线”的情况?
代码中使用了一个 counted_ids 集合进行去重:
1 | if ... and track_id not in counted_ids: |
这是一种单次越界去重机制。在一些更为复杂的场景中,若需要允许同一个目标在往返跨越时多次计数,可以改为记录目标的“前侧状态”(例如用一个字典记录 {track_id: "IN_SIDE"})。一旦检测到物体的状态从 IN_SIDE 变为 OUT_SIDE,则触发一次计数并更新状态,从而支持往返多次累计计数。
五、工业部署调优与提速策略
多线程读取与跳帧机制
对于高分辨率摄像头,可以使用多线程方式读取视频流,避免主推理线程因为 OpenCV 的read()发生 I/O 阻塞。同时,在保证精度的前提下采用隔帧检测(例如每 2 帧调用一次 YOLO 推理,其余帧采用快速光流或在上一帧的跟踪位置上预测),可大幅将 FPS 提高 50% 以上。合理控制置信度与早停阈值
ByteTrack 的强大在于匹配。但如果视频中背景极为嘈杂,过低的第一阶段阈值会引入过多负样本噪声。在model.track()中,建议将第一层过滤置信度设为0.3~0.4之间。多摄像头并行与服务器端集成
在企业落地时,可通过multiprocessing为每个摄像头启动一个独立的 Python 推理进程,最终通过 Redis 消息队列或 RabbitMQ 将计数数据实时写入后端的 PostgreSQL 数据库或报警后台。
结语
通过 YOLO + ByteTrack + 几何叉乘判定,我们只用了 100 多行代码就构建起了一套逻辑严密、抗遮挡、不惧漏检的高性能计数系统。
下一篇文章中,我们将带大家把 YOLO 塞入更宏大的工业级安防场景——结合 GB28181 和 JT1078 国标监控协议的视频流,实现多路高并发实时 AI 安全告警系统,敬请期待!