Ultralytics YOLO27: 加入候补名单。
Get Started
课程初级

在视频上运行推理

在视频文件和实时摄像头画面上运行 YOLO26,以有界内存处理视频、跟踪对象并确保每个对象只计数一次,同时检查模型能否跟上帧率。

大多数已部署的视觉系统都在分析视频:监控传送带、门口和道路的摄像头。视频带来两个单张图像没有的问题。内存:无法将长时间录制的视频全部放进 RAM。身份:检测器在 60 帧中看到同一个人,就会报告 60 个人。

在本课中,你将使用预测模式逐帧处理视频,添加对象跟踪以确保每个人只计数一次,编写自己的摄像头循环,并测量模型能否跟上处理速度。

学习成果

你可以通过 YOLO26 以有界内存处理视频或摄像头画面,使用跟踪统计不同对象的数量,并根据测得的每帧毫秒数判断处理速度是否跟得上视频源。

快速通道
如果你已经熟悉相关内容,以下是简要版。
  1. model(video, stream=True) 会以有界内存为每帧生成一个 Results。

  2. model.track(video, stream=True, persist=True, tracker='bytetrack.yaml') 会添加 boxes.id。

  3. 使用 ID 集合统计不同对象的数量,绝不要根据最大 ID 计数。

  4. 预算 = 1000 ÷ 视频源 FPS;将其与 result.speed['inference'] 对比。

实践操作

示例:对视频文件运行预测#

视频和图像一样,都通过预测模式处理。下面这个 62 帧片段展示了人们穿过中庭的场景:

yolo predict model=yolo26n.pt source=https://github.com/ultralytics/assets/releases/download/v0.0.0/solutions_ci_demo.mp4
video 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/predict
  • frame 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 秒”的逻辑。

Ultralytics YOLO 在高速公路上进行跟踪:每辆汽车、公交车和卡车都带有持久 ID 标签和运动轨迹

跟踪器速度鲁棒性
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;详见下一课

如何同时处理多个摄像头及相关线程,请参阅多流推理。

避免这些错误#

忘记设置 stream=True

症状:处理长录像时内存不断增长,直到进程崩溃;或者实时摄像头调用一直不返回。原因:不设置该参数时,Python 调用会在返回前为每一帧收集一个 Results 对象。解决方法:传入 stream=True 并循环遍历生成器——在你开始迭代之前,它不会执行任何操作。

在自己的循环中忘记设置 persist=True

症状:在不带 persist=True 的 cv2_loop.py 中,计数降到 27,最高 ID 也为 27:ID 每帧都会重置,因此“唯一”计数其实只是目标最多的那一帧。解决方法:每帧调用一次 track 时,都传入 persist=True。

按最高 ID 计数

症状:32 个人却得到 67 的计数。原因:未确认的跟踪目标会导致 ID 不连续。解决方法:像 track.py 中那样,统计 ID 集合的大小。

明确指定跟踪器

默认跟踪器可能随版本更新而改变。传入 tracker="bytetrack.yaml"(或你选择的跟踪器),以便不同版本的计数保持可比。

试试看

在演示片段上运行 track_any.py(见解决方案),然后再处理一段你自己的短视频。提交一份三行报告:不同对象的数量、每帧的平均推理时间(毫秒),以及该速度能否满足视频源的帧时间;如果不能,说明你首先会尝试的优化手段。使用 yolo track ... save=True 保存一个标注后的输出,并记录 ID 在遮挡期间是否保持稳定;如果 ID 闪烁,试试 tracker=botsort.yaml。

完成条件
满足以下所有条件,即表示你已完成本课程。
  • 你已经使用 yolo predict 或 yolo track 保存了标注后的视频。

  • 你的流处理脚本会根据一组 boxes.id 值输出不同对象的数量。

  • 你已将测得的每帧耗时(毫秒)与视频源的帧时间进行了比较,并指出了首先会尝试的提速方法。

显示解答
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
接下来做什么

你的模型已经能在 Python 中处理图像和视频。最后一步:将模型导出为生产运行时可以加载的格式。

登录以保存学习进度