在视频上运行推理
在视频文件和实时摄像头画面上运行 YOLO26,以有界内存处理视频、跟踪对象并确保每个对象只计数一次,同时检查模型能否跟上帧率。
大多数已部署的视觉系统都在分析视频:监控传送带、门口和道路的摄像头。视频带来两个单张图像没有的问题。内存:无法将长时间录制的视频全部放进 RAM。身份:检测器在 60 帧中看到同一个人,就会报告 60 个人。
在本课中,你将使用预测模式逐帧处理视频,添加对象跟踪以确保每个人只计数一次,编写自己的摄像头循环,并测量模型能否跟上处理速度。
实践操作
示例:对视频文件运行预测#
视频和图像一样,都通过预测模式处理。下面这个 62 帧片段展示了人们穿过中庭的场景:
yolo predict model=yolo26n.pt source=https://github.com/ultralytics/assets/releases/download/v0.0.0/solutions_ci_demo.mp4video 1/1 (frame 1/62) solutions_ci_demo.mp4: 384x640 22 persons, 27.3ms
...
video 1/1 (frame 62/62) solutions_ci_demo.mp4: 384x640 22 persons, 54.8ms
Results saved to runs/detect/predictframe 1/62:Ultralytics 解码视频,并像处理图像一样逐帧处理。22 persons:该帧中的检测结果。同一批行人会在每一帧中被重复计数。27.3ms...54.8ms:每帧耗时会因机器上其他任务而变化。
标注后的视频是 runs/detect/predict/solutions_ci_demo.mp4。你自己的模型也可以用同样的方式运行:将 yolo26n.pt 替换为 runs/detect/pills_v1/weights/best.pt。
处理长视频和实时视频源#
如果要编写处理检测结果的代码,可以循环读取视频流。每次迭代都会生成一帧的 Results,随后将其丢弃:
from ultralytics import YOLO
model = YOLO("yolo26n.pt")
source = "https://github.com/ultralytics/assets/releases/download/v0.0.0/solutions_ci_demo.mp4"
frames, people = 0, 0
for result in model(source, stream=True, classes=[0], verbose=False): # one Results object per frame
frames += 1
people += len(result.boxes)
print(f"{frames} frames, {people / frames:.1f} people per frame")62 frames, 22.2 people per frame同一个循环也接受网络摄像头索引(source=0)或摄像头 URL(例如 rtsp://camera.local/stream),并一直运行到视频流结束或你停止循环。添加 vid_stride=2 可隔帧处理。若要为这个循环构建浏览器界面,Streamlit 实时推理指南会将其封装到应用中。
示例:每个人只计数一次#
“每帧 22 个人”不是大家真正想知道的;大家想知道的是“有多少人经过”。多目标跟踪会跨帧关联边界框,而跟踪模式会为 boxes.id 中的每个对象分配一个 ID:
from ultralytics import YOLO
model = YOLO("yolo26n.pt")
source = "https://github.com/ultralytics/assets/releases/download/v0.0.0/solutions_ci_demo.mp4"
seen = set()
for result in model.track(source, stream=True, persist=True, classes=[0], tracker="bytetrack.yaml", verbose=False):
if result.boxes.is_track:
seen.update(result.boxes.id.int().tolist())
print(f"{len(seen)} unique people tracked")32 unique people tracked在尚未确认任何跟踪目标的帧中,is_track 是 False,此时 boxes.id 为 None。你可以使用这些 ID 进行计数、记录轨迹,或实现“这个对象已经在这里停留了 3 秒”的逻辑。

| 跟踪器 | 速度 | 鲁棒性 |
|---|---|---|
bytetrack.yaml | 更快、更简单 | 对象短暂消失时更容易丢失 ID |
botsort.yaml | 更慢 | 摄像头移动时更能保持 ID 稳定(运动补偿;外观 ReID 可通过 with_reid 选择启用) |
省略 tracker 时,Ultralytics 会使用当前默认的 tracktrack.yaml;跟踪模式还提供 ocsort.yaml、deepocsort.yaml 和 fasttrack.yaml。如何选择和调整跟踪器,以及如何基于跟踪器构建对象计数、热力图和速度估计,请参阅使用 ByteTrack、BoT-SORT 和 TrackTrack 跟踪对象。
示例:编写自己的摄像头循环#
实际应用通常会自行读取帧——以便调整大小、裁剪、添加时间戳,或与其他代码共享摄像头——然后将每一帧传给模型。此时,persist=True 负责在调用之间保持跟踪器运行:
import cv2
from ultralytics import YOLO
model = YOLO("yolo26n.pt")
cap = cv2.VideoCapture("solutions_ci_demo.mp4") # saved locally by the commands above; or 0, or an rtsp:// URL
seen = set()
while cap.isOpened():
ok, frame = cap.read()
if not ok:
break
result = model.track(frame, persist=True, classes=[0], tracker="bytetrack.yaml", verbose=False)[0]
if result.boxes.is_track:
seen.update(result.boxes.id.int().tolist())
cap.release()
print(f"{len(seen)} unique people, highest ID {max(seen)}")32 unique people, highest ID 67和 track.py 一样,共有 32 个人。请注意最大 ID 是 67,而不是 32:跟踪器也会为未能确认的暂定跟踪目标分配 ID,因此 ID 会有间隔。
跟上视频源的速度#
30 FPS 的摄像头每 33 毫秒输出一帧,而实际帧率取决于解码、推理和写入三者中最慢的一项。在我们的笔记本电脑 CPU 上,yolo26n 每帧处理 384×640 的图像需要 26 到 46 毫秒;不同运行的耗时会因机器上其他任务而变化:已达到或超过预算,没有余量。如果你的设备跟不上,可以按顺序尝试以下方法:
| 优化手段 | 效果 |
|---|---|
使用更小的模型(用 n 替代 s) | 通常可快 2–3 倍 |
降低 imgsz(从 640 降到 480) | 像素更少,计算量更小;小目标最先受到影响 |
vid_stride=2 | 工作量减半;适用于运动缓慢的场景 |
| 使用 GPU 或优化后的导出模型 | 提升幅度最大——在 NVIDIA 上使用 TensorRT 引擎,或使用 INT8;详见下一课 |
如何同时处理多个摄像头及相关线程,请参阅多流推理。
避免这些错误#
症状:处理长录像时内存不断增长,直到进程崩溃;或者实时摄像头调用一直不返回。原因:不设置该参数时,Python 调用会在返回前为每一帧收集一个 Results 对象。解决方法:传入 stream=True 并循环遍历生成器——在你开始迭代之前,它不会执行任何操作。
症状:在不带 persist=True 的 cv2_loop.py 中,计数降到 27,最高 ID 也为 27:ID 每帧都会重置,因此“唯一”计数其实只是目标最多的那一帧。解决方法:每帧调用一次 track 时,都传入 persist=True。
症状:32 个人却得到 67 的计数。原因:未确认的跟踪目标会导致 ID 不连续。解决方法:像 track.py 中那样,统计 ID 集合的大小。
默认跟踪器可能随版本更新而改变。传入 tracker="bytetrack.yaml"(或你选择的跟踪器),以便不同版本的计数保持可比。
显示解答
import sys
from ultralytics import YOLO
model = YOLO("yolo26n.pt")
seen, times = set(), []
for result in model.track(sys.argv[1], stream=True, persist=True, tracker="bytetrack.yaml", verbose=False):
times.append(result.speed["inference"])
if result.boxes.is_track:
seen.update(result.boxes.id.int().tolist())
print(f"{len(seen)} unique objects, {sum(times) / len(times):.1f} ms inference per frame")使用 python track_any.py your_video.mp4 运行。
32 unique objects, 30.5 ms inference per frame (26-46 ms across runs on a laptop CPU)
Source is 30 FPS -> 33 ms budget: fits only on a quiet machine, with no headroom
First lever: export to OpenVINO or ONNX for this CPU, then vid_stride=2 if still short