supervision 中的 ByteTrack 多目标跟踪器:API 参考、参数调优与迁移指南
【免费下载链接】supervisionWe write your reusable computer vision tools. 💜项目地址: https://gitcode.com/GitHub_Trending/su/supervision
本文聚焦 supervision 仓库内置的
sv.ByteTrack多目标跟踪封装,介绍其在视频帧序列中为每个被检目标分配稳定、持续 tracker ID 的用法与核心 API,完整解析五个构造参数的语义、底层两步关联与卡尔曼滤波原理,并给出该接口自supervision-0.28.0起被弃用、以及迁移到外部trackers包的完整路线。读完后你将能独立在任意检测/分割/关键点模型产出的Detections上接入目标跟踪,并根据场景调优参数、解释追踪结果。
ByteTrack 在 supervision 中的角色与现状
ByteTrack是一种流行的多目标跟踪(MOT)算法,其核心思想是:除了使用高置信度检测框做常规关联外,还会把被低置信度阈值过滤掉的目标也"物尽其用",参与二次匹配,从而显著缓解目标短暂漏检、遮挡导致的轨迹断裂与 ID 切换。
在 supervision 中,sv.ByteTrack定位为一个"模型无关"的检测结果跟踪封装:它本身不做目标检测,而是接收任何检测模型产出的Detections(只需包含xyxy边界框与confidence),逐帧调用后返回携带tracker_id的新Detections。
值得注意的是,在当前仓库版本中,该封装已被官方标记为弃用(Deprecated)。源码中类定义上方的装饰器即声明了生命周期(core.py):
@deprecated_class( target=TargetMode.NOTIFY, deprecated_in="0.28.0", remove_in="0.31.0", ) class ByteTrack:官方弃用说明 明确指出:sv.ByteTrack自supervision-0.28.0起弃用,并计划于supervision-0.31.0移除,官方建议安装独立的trackers包并使用其中的ByteTrackTracker替代(注意其更新方法由update_with_detections()更名为update())。
两个关键迁移注意点(务必先读):
| 维度 | sv.ByteTrack(当前仓库) | trackers.ByteTrackTracker(替代品) |
|---|---|---|
| 来源 | supervision 内置(supervision.tracker.byte_tracker.core) | 需执行pip install trackers的外部包 |
| 状态 | 0.28.0 弃用,0.31.0 计划移除 | 现行推荐方案 |
| 更新方法 | update_with_detections(detections) | update(detections) |
| 构造参数 | 见下表(track_activation_threshold等) | 同名对应参数 |
此外,已弃用功能清单 还记录了一段历史变更:ByteTrack更早版本使用的track_buffer、track_thresh、match_thresh三个参数自supervision-0.23.0起已被移除,如今必须改用lost_track_buffer、track_activation_threshold、minimum_matching_threshold这三个新名称。如果你的老代码仍在使用旧参数名,将直接报错。
快速开始:在逐帧回调中接入 ByteTrack
尽管ByteTrack已弃用,当前仓库源码仍保留其完整实现并可通过sv顶层命名空间按需惰性导入(见 supervision/__init__.py 的__getattr__逻辑),因此仓库主版本中import supervision as sv; sv.ByteTrack仍可用。
把跟踪器接入视频流的完整模式如下(该示例亦见于ByteTrack.update_with_detections()的 docstring,core.py):
import numpy as np import supervision as sv from rfdetr import RFDETRMedium model = RFDETRMedium() tracker = sv.ByteTrack() box_annotator = sv.BoxAnnotator() label_annotator = sv.LabelAnnotator() def callback(frame: np.ndarray, index: int) -> np.ndarray: detections = model.predict(frame[:, :, ::-1]) detections = tracker.update_with_detections(detections) labels = [f"#{tracker_id}" for tracker_id in detections.tracker_id] annotated_frame = box_annotator.annotate( scene=frame.copy(), detections=detections) annotated_frame = label_annotator.annotate( scene=annotated_frame, detections=detections, labels=labels) return annotated_frame sv.process_video( source_path="<SOURCE_VIDEO_PATH>", target_path="<TARGET_VIDEO_PATH>", callback=callback )流程要点:
- 在
callback中对每一帧运行一次目标检测,得到原始Detections; - 调用
tracker.update_with_detections(detections)让跟踪器做帧间数据关联,返回带tracker_id的Detections; - 把
tracker_id渲染成标签(配合sv.LabelAnnotator)或用于绘制轨迹(配合sv.TraceAnnotator)等,实现"同一个目标全程保持同一 ID"的可视化分析。
由于ByteTrack模型无关,上面示例中的rfdetr可以被任何能产出Detections的方案替换,例如sv.Detections.from_inference(...)、sv.Detections.from_ultralytics(...)等。完整的端到端演练(含 RF-DETR / Inference / Ultralytics 三种后端、分割与关键点模型的接入方式)可参考教程 如何跟踪对象。
构造参数详解:默认值与调优语义
ByteTrack构造函数的完整签名(core.py):
def __init__( self, track_activation_threshold: float = 0.25, lost_track_buffer: int = 30, minimum_matching_threshold: float = 0.8, frame_rate: float = 30, minimum_consecutive_frames: int = 1, ) -> None:五个参数的含义、默认值及调优方向,与官方 docstring 保持一致,整理如下:
| 参数 | 默认值 | 作用 | 调优建议 |
|---|---|---|---|
track_activation_threshold | 0.25 | 触发新轨迹激活的检测置信度门槛 | 调高可提升精度与稳定性,但可能漏掉真实目标;调低会提高召回率,但更容易引入噪声与不稳定轨迹 |
lost_track_buffer | 30 | 目标丢失后可缓冲的帧数 | 调高能显著增强遮挡处理能力,降低因短暂检测中断导致的轨迹碎裂或消失概率 |
minimum_matching_threshold | 0.8 | 轨迹与检测匹配所需的最小相似度(第一轮高置信度关联的阈值) | 调低倾向提升精度但可能产生碎片化轨迹;调高倾向提升完整性但会引入误匹配与漂移风险 |
frame_rate | 30 | 视频帧率,支持浮点值(如23.976、29.97) | 用于精确换算"丢失缓冲"对应的真实帧数(详见下节公式) |
minimum_consecutive_frames | 1 | 目标被连续跟踪多少帧后才被认定为"有效轨迹" | 调高可避免由误检或重复检测造成的偶然轨迹,但代价是更短的轨迹可能不被输出 |
其中frame_rate对lost_track_buffer的影响体现在构造函数内部的换算公式(core.py):
self.max_time_lost = int(frame_rate / 30.0 * lost_track_buffer)即以30 fps为基准把缓冲帧数归一化到实际帧率下,保证"丢失判定"在不同视频帧率下对应的时间长度一致。构造函数还派生了一个内部激活阈值:
self.det_thresh = self.track_activation_threshold + 0.1 if self.det_thresh > 1.0: self.det_thresh = self.track_activation_threshold该det_thresh用于控制"新轨迹"能否被正式激活(见下节算法流程第 4 步)。
核心 API 与返回值语义
update_with_detections(detections) —— 官方主入口
update_with_detections接收上一帧检测结果Detections,返回已更新的Detections。实现细节(core.py)透露了三个需要理解的约定:
- 必须提供置信度:若
detections.confidence is None,直接抛出ValueError("Detections confidence must be provided for tracking."); - 内部数据组织:将
xyxy边界框与confidence列拼接为(N, 5)的张量后交给底层算法:tensors = np.hstack( (detections.xyxy, detections.confidence[:, np.newaxis]) ) tracks = self.update_with_tensors(tensors=tensors) - 输出对齐与过滤:跟踪结果以检测框为锚,通过 IoU 二部匹配(
box_iou_batch+linear_assignment,代价阈值0.5)把算法返回的STrack与原始检测对应起来;所有检测默认被填充tracker_id = -1,只有成功匹配到轨迹的检测会被回填其external_track_id,最终仅返回tracker_id != -1的子集。因此输出帧中会天然剔除未被跟踪的检测。
此外在调用底层前,输入张量会经_valid_tracking_tensors(core.py)过滤:只保留元素全部有限(非NaN/inf)且框宽、框高均为正的检测,避免非法框污染跟踪状态。
reset() —— 重置跟踪状态
reset()(core.py)会清空内部全部跟踪数据——包括 tracked、lost、removed 三类轨迹列表、内部/外部 ID 计数器以及帧计数器。这在顺序处理多个视频时尤其有用:每个新视频开始时调用一次,可确保跟踪器以干净状态启动,避免 ID 跨视频"串线"。
update_with_tensors() —— 底层实现
update_with_tensors(tensors)(core.py)直接接收(N, 5)的[x1, y1, x2, y2, score]张量,内部维护tracked_tracks/lost_tracks/removed_tracks三类状态并返回当前帧所有已激活的STrack列表。它通常不需要用户直接调用,但在研究算法细节或移植时很有参考价值。
底层原理:从源码看 ByteTrack 如何工作
双阈值 + 两步关联
update_with_tensors内部完整实现了 ByteTrack 论文式流程(core.py),可分步概括为:
- 按分数分流检测:高于
track_activation_threshold的框进入第一轮(高分)候选池;分数处于0.1 ~ track_activation_threshold之间的"次高分"框被单独保留,等待第二轮二次匹配(core.py); - 第一轮关联:把已跟踪轨迹与丢失轨迹合并为
strack_pool,用卡尔曼滤波统一预测当前位置后,以 IoU 距离构造代价矩阵并融合检测分数(fuse_score),用阈值minimum_matching_threshold做线性指派求解; - 第二轮关联(ByteTrack 精髓):对第一轮未匹配的 tracked 轨迹,再与低置信度检测框做一次 IoU 匹配(阈值
0.5),成功者用于re_activate复活轨迹——这正是算法在目标被弱检测/短暂遮挡时仍能保持轨迹连续的原因(core.py); - 激活新轨迹:仍未被匹配的新检测,仅当分数不低于
det_thresh时才通过独立KalmanFilter正式activate(core.py); - 状态维护与清理:超过
max_time_lost帧仍未被找回的 lost 轨迹被置为 Removed;最后通过remove_duplicate_tracks(IoU 距离小于0.05判重)等辅助函数收敛轨迹列表,返回所有已激活轨迹。
辅助函数joint_tracks、sub_tracks、remove_duplicate_tracks定义于同一文件的尾部(core.py),分别负责轨迹表并集去重、差集剔除与重复轨迹剪枝。
匹配机制(matching.py)
匹配模块(matching.py)基于scipy.optimize.linear_sum_assignment求解代价矩阵的全局最优指派:
iou_distance把两个轨迹集的tlbr框两两计算 IoU,代价取1 - iou;fuse_score把"代价"反转为 IoU 相似度后乘以检测分数再取补,使高置信度检测获得更低的匹配代价;- 用户可见的
minimum_matching_threshold、内部第二轮的0.5与未确认轨迹轮的0.7,都通过indices_to_matches的阈值判断过滤最终匹配。
状态表示与卡尔曼滤波(single_object_track.py / kalman_filter.py)
每条轨迹由STrack(single_object_track.py)表示,其生命周期通过TrackState枚举(New/Tracked/Lost/Removed)流转,并提供predict、activate、re_activate、update以及tlbr/tlwh/xyah等框格式换算方法。activate与update中,仅当轨迹连续帧数达到minimum_consecutive_frames后is_activated才置位并为其分配对外可见的 ID(single_object_track.py)。
底层运动模型由KalmanFilter(kalman_filter.py)提供:8 维状态空间为(x, y, a, h, vx, vy, va, vh)——即框中心坐标、宽高比、高度及其各自速度,采用匀速运动模型,观测为线性直接观测。这正是 tracker 能在检测短暂缺失时外推框位置的机制。
内部 ID 与对外 ID 的区分
ByteTrack内部维护两个独立的IdCounter(utils.py):internal_id_counter从0开始,仅用于内部轨迹去重与合并(NO_ID = -1表示未分配);external_id_counter从1开始,负责生成对外返回的、连续唯一的tracker_id。构造器注释中特别提醒:若将内部 ID 也从 1 起算,会导致所有目标的轨迹被错误地串联(core.py),这解释了为何代码刻意保留两者的起点差异。
完整实践链路:从检测到轨迹标注
仅靠跟踪器本身还不够完成业务闭环,supervision 提供了配套的可视化与平滑工具。建议按如下链路组合(每个环节均有对应仓库内教程或实现):
- 检测:RF-DETR / Ultralytics / Inference 等任意模型产出
Detections; - 跟踪:
tracker.update_with_detections(detections)得到稳定tracker_id; - ID 标注:用
sv.BoxAnnotator+sv.LabelAnnotator渲染#<tracker_id> <class_name>标签; - 轨迹可视化:用
sv.TraceAnnotator按历史位置叠加移动路径; - 平滑增强(可选):经
sv.DetectionsSmoother(smoother)对框坐标做跨帧平滑,可让抖动明显的框更稳定; - 实例级跟踪(可选):ByteTrack 跟踪的是边界框;如需按实例掩膜上色,可将跟踪结果与
sv.MaskAnnotator组合,用tracker_id保证同一目标的颜色全程一致。
关于关键点(骨骼点)目标的跟踪,教程 如何跟踪对象 提供了完整路线:先由姿态模型产出sv.KeyPoints,再通过KeyPoints.as_detections()(可用selected_keypoint_indices挑选不易被遮挡的关键点子集)将其转为Detections,之后即可无缝接入 ByteTrack。该教程还以滑雪视频为例,一步步演示从纯关键点标注 → 转检测 → 跟踪 → 平滑的全过程,是理解本文 API 如何在真实项目中串起来的最佳配套阅读。
常见问题速查
如何在每帧间维持对象的同一 ID?对每一帧的Detections调用tracker.update_with_detections(detections)即可,跟踪器会为对象分配持久的tracker_id;如需可视化运动轨迹,再叠加sv.TraceAnnotator。注意弃用提示:sv.ByteTrack建议改用trackers包的ByteTrackTracker,其更新方法名是update()。
为什么弱检测也能保持轨迹连续?ByteTrack 在常规高置信度关联之外,额外引入低置信度检测框参与第二轮关联(见上节第 3 步),从而在被漏检、弱检的帧里尽量找回既有轨迹,减少碎片化。
ByteTrack 与任何检测模型兼容吗?兼容。ByteTrack 只依赖Detections中的边界框与置信度,与产出这些结果的模型/转换器无关,因此是模型无关的跟踪方案。
能跟踪实例掩膜而不是边界框吗?ByteTrack 本身跟踪框。如需掩膜级的一致着色,将跟踪 ID 与sv.MaskAnnotator结合使用,即可让同一跟踪 ID 的实例在整段视频中颜色统一。
迁移检查清单
若你正在维护基于sv.ByteTrack的旧代码,可对照以下清单完成向trackers包的平滑迁移:
pip install trackers,改用from trackers import ByteTrackTracker或相应导入路径;- 将调用
update_with_detections(...)的地方改名为update(...); - 确认构造参数使用现行名称(
track_activation_threshold/lost_track_buffer/minimum_matching_threshold/frame_rate/minimum_consecutive_frames),而非 0.23.0 之前已移除的track_buffer/track_thresh/match_thresh; - 同一视频处理流程中如需重置状态,记得调用对应
reset()以保证新视频不继承旧 ID; - 对仍停留在 supervision 0.28.0 ~ 0.31.0 之前版本的项目,
sv.ByteTrack依旧可用,但应在 0.31.0 移除前完成替换。
本文的算法细节均可在仓库源码中逐行复核:构造与关联流程见 src/supervision/tracker/byte_tracker/core.py、匹配实现见 matching.py、轨迹状态机见 single_object_track.py、运动模型见 kalman_filter.py,其行为可由 tests/tracker/test_byte_tracker.py 中的测试用例进一步佐证。
【免费下载链接】supervisionWe write your reusable computer vision tools. 💜项目地址: https://gitcode.com/GitHub_Trending/su/supervision
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考