BoxMOT 中的 SAM2MOT:以分割掩码为核心的混合多目标跟踪器实战指南
【免费下载链接】boxmotBoxMOT: Pluggable Python and C++ SOTA multi-object tracking modules with support for axis-aligned and oriented bounding boxes项目地址: https://gitcode.com/GitHub_Trending/bo/boxmot
导读
本文深入解析 BoxMOT 仓库中对 SAM2MOT(SAM2MOT: A Novel Paradigm of Multi-Object Tracking by Segmentation,arXiv:2504.04519)这一"以分割为中心"的多目标跟踪范式的落地实现。文章以 docs/trackers/sam2mot.md 为骨架,结合 sam2mot.py、内置配置 sam2mot.yaml 与单元测试,完整讲解其运行前提、轨迹状态机、三阶段关联、跨目标交互(Cross-Object Interaction,COI)与帧外恢复机制,并给出可直接复用的参数配置与调用方式。读完本文,你将掌握:如何为检测器接入行对齐的实例分割掩码、SAM2MOT 在 BoxMOT 中"无需 SAM2 模型、无需 ReID"的实现原理,以及如何在 AABB / OBB 两种检测模式下使用该跟踪器。
一、背景:把分割放到多目标跟踪的中心
传统多目标跟踪(MOT)范式通常把分割当作检测的"附加产物"——先输出轴对齐或旋转的目标框,再为每个框补上掩码。SAM2MOT 论文提出了相反的思路:把分割掩码作为跟踪关联的核心信号。
- 用掩码驱动的跟踪(mask-driven tracking)取代单纯的框级 IoU 关联;
- 引入**轨迹管理器(Trajectory Manager)**统一负责目标的加入与移除;
- 引入跨目标交互(Cross-Object Interaction,COI)模块专门处理遮挡场景;
- 由此,掩码连续性成为首要的关联依据,而不是把分割当作框跟踪的视觉点缀。
在 BoxMOT 中,这一范式被实现为一个独立的混合型(hybrid)跟踪器Sam2Mot,注册名为sam2mot,位于 boxmot/trackers/hybrid/sam2mot/sam2mot.py。
事实边界说明:本文描述的是 BoxMOT 仓库当前实现的 SAM2MOT 跟踪器,而非原论文的完整复刻。命名上沿用了论文名称以便检索,但实现上有明确的工程取舍(详见下文)。
二、BoxMOT 对 SAM2MOT 的运行前提
根据 docs/trackers/sam2mot.md,在 BoxMOT 中使用 SAM2MOT 需要满足以下前提:
1. 检测器必须提供行对齐的分割掩码
每个检测结果都对应一个与检测框**逐行对齐(row-aligned)**的二值掩码。掩码与检测框共享同一索引顺序:第i行检测框对应第i个掩码。
2. 不需要 ReID 模型
这是本实现最关键的工程简化:BoxMOT 的 SAM2MOT 只消费通过update()传入的掩码,自身不实例化 SAM 2 模型,也不加载 ReID 权重。
从源码可以看到,_track_detections的签名是:
def _track_detections(self, dets, img, embs=None, masks=None): ... # embs: Ignored (no ReID).在 registry.py 中,sam2mot的定义没有声明needs_reid=True(默认为False),因此create_tracker不会为它构建 ReID 后端。这意味着掩码可以来自任何兼容的分割模型——Mask R-CNN、SOLO、YOLO-seg 等皆可,只要输出与检测框对齐的掩码数组即可。
3. 同时支持 AABB 与 OBB 检测
Sam2Mot类声明了两个能力标志(sam2mot.py):
supports_masks = True supports_obb = True- 掩码运算统一使用包围 AABB:所有掩码裁剪、掩码 IoU 计算都在掩码坐标系下的轴对齐框内完成;
- 旋转几何被保留用于关联与输出:OBB(
cx, cy, w, h, angle)参与关联相似度计算,并以 9 列 OBB 输出格式(cx, cy, w, h, angle, id, conf, cls, det_ind)返回。
4. 最佳适用场景
当以下条件同时成立时,SAM2MOT 最值得选用:
- 能够稳定获得可靠的实例分割掩码;
- 场景中目标重叠/遮挡频繁,仅靠框级 IoU 关联会产生歧义;
- 希望通过掩码连续性提升遮挡下的 ID 保持能力。
三、核心架构:状态机、轨迹管理器与跨目标交互
1. 五态轨迹状态机
TrackState(sam2mot.py)定义了五种轨迹状态:
| 状态 | 含义 | 判定依据 |
|---|---|---|
RELIABLE | 可靠轨迹 | 置信度 >tau_r |
PENDING | 待定轨迹 | 置信度 >tau_p且 ≤tau_r |
SUSPICIOUS | 可疑轨迹 | 置信度 >tau_s且 ≤tau_p |
LOST | 丢失轨迹 | 置信度 ≤tau_s,或失配帧数超过tolerance_frames |
FRAME_OUT | 出画轨迹 | 连续 10+ 帧未匹配(进入第三阶段恢复池) |
2. 轨迹管理器(_TrajectoryManager)
轨迹管理器负责目标生命周期的三个决策(sam2mot.py):
- 状态分类
classify_state(confidence):按tau_r > tau_p > tau_s三个置信度阈值把轨迹划分为可靠/待定/可疑/丢失四档; - 是否新增目标
should_add_detection:在掩码分辨率下计算"未跟踪区域(untracked mask)",若某未匹配检测框与未跟踪区域的交叠比例超过untracked_ratio_threshold,才允许为其新建轨迹——这一机制可避免在已被现有轨迹掩码覆盖的区域重复建轨; - 是否移除轨迹
should_remove(track):当lost_frames > tolerance_frames时移除。
compute_untracked_mask以掩码分辨率构造全 1 的图,然后依次把已有轨迹掩码区域和"守卫框(guard bboxes)"区域置 0,从而得到真正空闲的区域。
3. 跨目标交互模块(_CrossObjectInteraction)
COI 模块的目标是判定一对高度重叠的目标中谁被遮挡(sam2mot.py):
- bbox 重叠预过滤:先用快速框重叠检查(
_bboxes_overlap)筛掉大部分候选对,避免对所有 O(n²) 组合都做昂贵的掩码 IoU; - 掩码 IoU 判定:对框重叠的候选对计算掩码 IoU(
mask_iou),仅当mask_iou > miou_threshold(默认 0.8)时视为存在交互; - 遮挡方裁决:比较两目标置信度历史的均值与方差:
- 若均值差异 ≥ 方差差异,取均值较低者为被遮挡方;
- 否则取方差较高者为被遮挡方;
- 记忆跳过:被判定为遮挡的轨迹设置
skip_memory_current = True,其掩码在本帧被清空(track.mask = None),从而阻止遮挡目标继承错误的掩码记忆,防止掩码被污染后引发错误关联。
四、三阶段关联流水线
BoxMOT 的 SAM2MOT 将几何(框)与掩码 IoU 融合进两阶段关联,再叠加第三阶段的出画恢复,构成完整的逐帧处理流程(_track_detections,sam2mot.py)。
第 1 阶段:高置信度检测优先匹配
按det_thresh(默认 0.3)把检测拆分为高/低置信度两组。先用匈牙利算法(linear_sum_assignment)在"全部现有轨迹 × 高置信度检测"上求解最小代价匹配,代价矩阵由相似度取反得到;只有相似度 ≥iou_threshold的匹配才被接受。
第 2 阶段:低置信度检测补匹配 + 记忆恢复
- 先用低置信度检测与第 1 阶段未匹配的轨迹再跑一次匈牙利匹配,门槛放宽到
second_stage_iou_threshold(默认 0.3); - 对仍未匹配的轨迹,用其
last_matched_bbox/last_matched_obb(而非当前预测框)与剩余检测做use_last=True的相似度匹配,实现基于记忆的恢复。
相似度融合公式
_association_similarity(sam2mot.py)把几何 IoU 与掩码 IoU 线性融合:
similarity[row, col] = (1.0 - cost_weight) * geometry + cost_weight * mask_ioucost_weight默认 0.5,表示几何与掩码等权;- 当
cost_weight <= 0或没有掩码时,退化为纯几何关联; - OBB 模式下,几何项使用
AssociationFunction.iou_batch_obb(来自 boxmot/trackers/common/association/iou.py),并叠加轨迹的速度预测(bbox + velocity),角度做归一化(normalize_angle)。
第 3 阶段:出画轨迹恢复(frame-out recovery)
_frame_out_matching(sam2mot.py)处理被标记为FRAME_OUT的轨迹:
- 只有连续10 帧以上未匹配且非密集(
is_dense)的轨迹才进入出画池; - 出画池轨迹与剩余未匹配检测基于
last_matched_bbox/last_matched_obb计算 IoU 并匹配; - 恢复成功的轨迹状态置回
RELIABLE,重新获得掩码,继续参与后续帧。
轨迹更新中的掩码维护
匹配成功后,跟踪器会把检测掩码赋给轨迹(track.mask = det_masks[det_idx]);对某些需要重建质量的轨迹(如PENDING状态且置信度超过tau_r、或低密度二阶段匹配),会进入"重建队列"刷新掩码并把状态提升为RELIABLE。
密集场景的密度感知
_compute_density(target_idx, all_bboxes)(sam2mot.py)向量化计算某检测与其余所有框的交叠面积之和与其自身面积之比,作为"密度"指标:
- 密度 >
frame_out_d_thre(默认 0.6)的轨迹标记为is_dense; - 密集轨迹不会进入出画池,避免在人群密集帧中误判"出画";
- 密集区域的二阶段匹配跳过掩码重建(
density >= density_threshold时不重建)。
五、参数详解:默认值、含义与调优范围
以下参数全部来自 sam2mot.yaml(内置运行默认值 + 调优元数据)与Sam2Mot.__init__(sam2mot.py)。
1. 继承自基类的通用参数
| 参数 | 默认值 | 说明 |
|---|---|---|
det_thresh | 0.3 | 检测置信度阈值,决定第 1/2 阶段的检测分流 |
max_age | 60 | 轨迹最大存活帧数(超过则判定丢失) |
min_hits | 1 | 输出前所需的最少命中帧数 |
iou_threshold | 0.3 | 第 1 阶段几何关联的相似度门槛 |
per_class | False | 是否按类别分离轨迹状态 |
这些参数由 base.py 统一定义并约束(例如max_age >= max_obs时自动扩展max_obs)。
2. SAM2MOT 专有参数
| 参数 | 默认值 | 调优范围 | 类型 | 作用 |
|---|---|---|---|---|
tolerance_frames | 30 | [20, 60] | randint | 轨迹失配多少帧后判定为LOST并移除 |
memory_window | 25 | [5, 50] | randint | 置信度历史滑窗长度(用于 COI 的均值/方差裁决) |
cost_weight | 0.5 | [0.1, 0.9] | uniform | 掩码 IoU 在相似度中的权重,(1-w)*bbox_iou + w*mask_iou |
tau_r | 0.8 | [0.6, 0.95] | uniform | RELIABLE状态置信度下限 |
tau_p | 0.5 | [0.3, 0.7] | uniform | PENDING状态置信度下限 |
tau_s | 0.3 | [0.1, 0.5] | uniform | SUSPICIOUS状态置信度下限 |
density_threshold | 0.9 | [0.5, 1.5] | uniform | 二阶段密集匹配的掩码重建阈值 |
second_stage_iou_threshold | 0.3 | [0.1, 0.5] | uniform | 第 2 阶段(低置信度/记忆恢复)的相似度门槛 |
frame_out_d_thre | 0.6 | [0.3, 0.9] | uniform | 密集轨迹判定阈值(高于此值不进入出画池) |
miou_threshold | 0.8 | [0.5, 0.95] | uniform | COI 判定两目标存在交互的掩码 IoU 门槛 |
untracked_ratio_threshold | 0.5 | [0.3, 0.8] | uniform | 新建轨迹要求检测框与未跟踪区域的交叠比例下限 |
new_track_thresh | 0.5 | [0.4, 0.7] | uniform | 允许新建轨迹的检测置信度下限 |
obb_theta_damping | 0.8 | [0.0, 0.95] | uniform | OBB 角度更新的阻尼系数(详见下文) |
3. OBB 角度阻尼更新
_damped_obb_update(sam2mot.py)在 OBB 模式下对角度增量施加阻尼:
theta_gain = 1.0 - obb_theta_damping updated[4] = normalize_angle(ref[4] + theta_gain * theta_delta)obb_theta_damping越大,每帧允许的角度变化越小,输出角度越平滑。结合align_obb_measurement,可以消除 w/h 互换造成的 π/2 假转(等价 OBB 形式的对齐),并用角度归一化防止角度环绕跳变。
六、如何启用与调用 Sam2Mot
1. 通过注册表创建
sam2mot已注册在 registry.py 的TRACKER_DEFINITIONS中,因此可以直接用统一的工厂函数创建:
from boxmot.api import create_tracker tracker = create_tracker( tracker_type="sam2mot", device="cuda:0", per_class=False, )由于该定义needs_reid=False,无需也无法传入reid_weights——create_tracker不会为它构建 ReID 后端(参见 registry.py 的needs_reid分支)。
2. 逐帧更新并传入掩码
update()的公共入口定义在 base.py,SAM2MOT 消费其masks参数:
import numpy as np # dets: (N, 6) AABB [x1, y1, x2, y2, conf, cls] # 或 (N, 7) OBB [cx, cy, w, h, angle, conf, cls] dets = np.array([[100, 200, 150, 320, 0.85, 0]], dtype=np.float32) # masks: (N, H, W) 与 dets 行对齐的二值掩码(任意分辨率) masks = np.zeros((1, 480, 640), dtype=np.uint8) results = tracker.update(dets, img=frame, masks=masks) # results: TrackResults,包含 tracks 数组与行对齐的 output masks输入校验(base.py)会强制掩码为 3D 数组(N, H, W)且第一维与检测数一致;不满足时抛出ValueError。update返回的TrackResults同时携带轨迹数组与输出掩码(raw, output_masks),掩码与轨迹行保持对齐。
3. 掩码坐标与 letterbox 适配
实现假定掩码位于letterbox 后的模型空间(方形、带 padding),而不是与图像像素一一对应。代码在每帧计算缩放与偏移(sam2mot.py):
scale = min(mH / H, mW / W) self._mask_scale = scale self._mask_pad_x = (mW - int(W * scale)) / 2.0 self._mask_pad_y = (mH - int(H * scale)) / 2.0所有图像坐标到掩码坐标的映射(掩码裁剪、未跟踪区域计算、should_add_detection)都使用这组(scale, pad_y, pad_x)完成,保证了 AABB/OBB 检测与掩码之间的一致性。
4. 自定义配置覆盖
内置 sam2mot.yaml 提供运行默认值。可通过tracker_config传入自定义标量 YAML,或通过tracker_kwargs直接覆盖(配置解析逻辑见 boxmot/trackers/config.py):
tracker = create_tracker( tracker_type="sam2mot", tracker_kwargs={ "cost_weight": 0.7, # 更依赖掩码 IoU "new_track_thresh": 0.6, # 更保守地新建轨迹 "miou_threshold": 0.85, # 更严格的 COI 交互判定 }, )七、测试验证:源码中的行为契约
test_trackers.py 中有一组专门的test_sam2mot_*用例,可以作为行为契约参考:
| 测试 | 验证点 |
|---|---|
test_sam2mot_supports_obb_outputs_with_masks | OBB 检测输入 + 全 1 掩码时,输出前 5 列(cx, cy, w, h, angle)与检测一致,掩码正常返回 |
test_sam2mot_per_class_obb_keeps_zero_masks_row_aligned | 开启per_class后,空掩码行仍与输出轨迹保持行对齐(not np.any(output.masks[1])) |
test_sam2mot_track_record_prefers_native_obb_over_helper_bbox | 轨迹记录优先采用原生 OBB 几何而非辅助 AABB |
test_sam2mot_obb_association_distinguishes_orientation_and_uses_masks | OBB 关联能区分朝向差异,且掩码融合相似度生效(mask_fused == 1) |
test_sam2mot_aligns_equivalent_obb_forms_without_id_or_angle_jump | 等价 OBB 形式(w/h 互换 + π/2)对齐后不产生 ID 或角度跳变 |
test_sam2mot_obb_angle_update_is_damped | 角度更新受obb_theta_damping阻尼约束 |
这些用例与supports_obb/supports_masks声明、_damped_obb_update、_association_similarity的实现相互印证,是理解并回归验证该跟踪器行为的最佳入口。
八、适用边界与工程提示
- 掩码质量决定上限:SAM2MOT 的关联质量直接依赖掩码 IoU,掩码分辨率不必与图像一致(代码按 letterbox 缩放映射),但掩码必须与检测框行对齐、且语义正确;
- 没有 ReID 也可以工作:相比依赖 ReID 外观特征的 BotSort、StrongSort 等(见 registry.py 中
needs_reid=True的条目),SAM2MOT 用掩码连续性替代外观特征,适合无法加载 ReID 权重的部署场景; - 遮挡是它的主场:COI 模块 + 出画恢复 + 密度感知共同服务于高遮挡、目标频繁进出画面的场景;而在掩码不可靠、目标稀疏且互不遮挡的场景,纯几何跟踪器(如 ByteTrack、OCSort)可能更简单高效;
- OBB 输出注意点:OBB 模式下输出为 9 列
(cx, cy, w, h, angle, id, conf, cls, det_ind),掩码运算仍基于包围 AABB,旋转角度通过阻尼更新保持平滑; - 调参入口:所有 SAM2MOT 专有参数的默认值与调优范围集中在 sam2mot.yaml,可直接作为网格/贝叶斯搜索的搜索空间元数据使用。
总结
BoxMOT 的 SAM2MOT 实现以sam2mot注册名提供了一套完整的"掩码驱动"混合跟踪方案:通过轨迹管理器维护五态生命周期,通过几何+掩码融合的两阶段关联 + 第三阶段出画恢复保证连续性,通过 COI 模块处理遮挡下的掩码记忆污染,并通过supports_obb/supports_masks双能力同时支持 AABB 与 OBB 检测。它不依赖 SAM 2 模型与 ReID 权重,掩码可由任意兼容分割检测器提供,是处理遮挡密集、需要分割输出的多目标跟踪场景的即插即用选项。
【免费下载链接】boxmotBoxMOT: Pluggable Python and C++ SOTA multi-object tracking modules with support for axis-aligned and oriented bounding boxes项目地址: https://gitcode.com/GitHub_Trending/bo/boxmot
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考