
YOLOv10 多目标跟踪源码解析BOTrack 与 BOTSORT 实现详解【免费下载链接】yolov10YOLOv10: Real-Time End-to-End Object Detection [NeurIPS 2024]项目地址: https://gitcode.com/GitHub_Trending/yo/yolov10本文以 ultralytics/trackers/bot_sort.py 为线索逐层拆解 YOLOv10 仓库中多目标跟踪的两大核心类——BOTrack与BOTSORT。读完本文你将理解 BoT-SORT 追踪器在检测框输入、卡尔曼滤波预测、IoU/外观特征关联、全局运动补偿GMC这条完整链路上的具体实现掌握其全部配置参数并能在自己的项目中正确启用与调优这一追踪器。一、追踪体系概览BOTrack 与 BOTSORT 的位置在 YOLOv10 仓库中目标跟踪被组织在 ultralytics/trackers/ 目录下共分四层basetrack.py定义BaseTrack基类与TrackState状态枚举New、Tracked、Lost、Removed是所有轨迹的公共抽象byte_tracker.py实现STrack单目标轨迹基于卡尔曼滤波的状态估计与BYTETrackerBYTE 关联算法的追踪器主干bot_sort.py在STrack与BYTETracker之上扩展出BOTrack与BOTSORTtrack.py将追踪器以回调方式注册进预测流程其中 TRACKER_MAP 将配置名映射到实现类{bytetrack: BYTETracker, botsort: BOTSORT}。因此从源码结构看BoT-SORT 并非独立实现而是对 BYTE 追踪范式的强化BOTrack在STrack上叠加了外观特征管理BOTSORT在BYTETracker上叠加了 ReID 外观关联与全局运动补偿。二、BOTrack带外观特征与共享卡尔曼滤波的轨迹类BOTrack定义在 bot_sort.py 的 L14-L122继承自STrack。它的职责是在每条轨迹中保存目标的外观特征并通过指数滑动平均EMA平滑特征使目标在帧间的身份更稳定。2.1 类级共享卡尔曼滤波shared_kalman KalmanFilterXYWH()与STrack.shared_kalman KalmanFilterXYAH()不同BOTrack使用 KalmanFilterXYWH。两者状态空间的差异是本文后续理解 BoT-SORT 的关键滤波器8 维状态空间适用追踪器KalmanFilterXYAH(x, y, aspect_ratio, h, vx, vy, va, vh)BYTETracker / STrackKalmanFilterXYWH(x, y, w, h, vx, vy, vw, vh)BOTSORT / BOTrackKalmanFilterXYWH直接以目标宽、高作为状态分量而非纵横比并以常量速度模型建模运动_motion_mat中对角线上每个位置分量后紧跟dt1的速度耦合。这种以宽高而非纵横比建模的方式在检测框尺度变化剧烈时往往能给出更贴合的状态预测。2.2 初始化与外观特征历史__init__签名L45-L54def __init__(self, tlwh, score, cls, featNone, feat_history50): super().__init__(tlwh, score, cls) self.smooth_feat None self.curr_feat None if feat is not None: self.update_features(feat) self.features deque([], maxlenfeat_history) self.alpha 0.9需要说明的是虽然形参名写作tlwh但实际由BOTSORT.init_track传入的是(x, y, w, h, idx)形式的检测框数组5 或 6 元含索引STrack的构造函数会断言长度在[5, 6]内。关键属性含义feat_history外观特征历史队列的最大长度默认 50用于约束内存占用alpha 0.9EMA 平滑系数数值越大历史特征对新特征的平滑惯性越强smooth_feat/curr_feat分别保存平滑后与当前帧的 L2 归一化特征向量featuresdeque队列保存逐帧原始特征供 ReID 关联时回溯。2.3 特征更新EMA 平滑update_features 是 BOTrack 相对 STrack 的核心增量def update_features(self, feat): feat / np.linalg.norm(feat) # L2 归一化 self.curr_feat feat if self.smooth_feat is None: self.smooth_feat feat else: self.smooth_feat self.alpha * self.smooth_feat (1 - self.alpha) * feat self.features.append(feat) self.smooth_feat / np.linalg.norm(self.smooth_feat)每次喂入新特征前先做 L2 归一化再以smooth_feat α·smooth_feat (1-α)·feat做指数滑动平均最后再次归一化。这样得到的smooth_feat综合了多帧外观信息比单帧特征更能抵抗遮挡与光照抖动带来的噪声。2.4 预测、更新与重新激活predict()L67-L74若轨迹不处于Tracked状态将速度分量mean_state[6]、mean_state[7]对应宽、高的速度置零后再执行卡尔曼预测避免丢失目标在无观测时速度持续累积发散re_activate(new_track, frame_id, new_idFalse)L76-L80失而复得的轨迹重新激活前若新检测携带特征则先update_features融合外观update(new_track, frame_id)L82-L86匹配成功的轨迹更新时同样先融合特征再调用父类STrack.update完成卡尔曼校正tlwh属性L88-L95从卡尔曼均值mean[:4]中心点 xywh还原为左上角坐标格式(tlx, tly, w, h)convert_coords/tlwh_to_xywhL113-L122将 tlwh 坐标转为(center_x, center_y, w, h)供卡尔曼滤波器作为观测输入。2.5 批量预测multi_predictL97-L111把整批轨迹的均值和协方差堆叠成矩阵调用shared_kalman.multi_predict做向量化预测再写回各轨迹。这是每帧关联前对当前目标大概在何处的统一运动外推避免逐条轨迹循环预测的性能开销。三、BOTSORT集成 ReID 与 GMC 的追踪器BOTSORT定义在 bot_sort.py 的 L125-L200继承自BYTETracker在保持 BYTE 两阶段关联框架的同时引入外观距离与全局运动补偿。3.1 构造函数与阈值参数def __init__(self, args, frame_rate30): super().__init__(args, frame_rate) self.proximity_thresh args.proximity_thresh # IoU 空间邻近阈值 self.appearance_thresh args.appearance_thresh # ReID 外观阈值 if args.with_reid: # Havent supported BoT-SORT(reid) yet self.encoder None self.gmc GMC(methodargs.gmc_method)proximity_thresh空间邻近度阈值。get_dists中 IoU 距离大于该阈值的轨迹-检测对被判定为空间上不可能匹配后续在外观融合时强制置为不可行appearance_thresh外观相似度阈值超过该阈值的 ReID 距离被钳制为1.0最大代价with_reidReID 开关。注意源码注释明确尚未支持 BoT-SORT(reid)即使开启self.encoder也被置为None因此当前版本实际不会走外观特征分支这一点与 botsort.yaml 中with_reid: False的默认值一致gmc_method全局运动补偿算法默认sparseOptFlow。此外get_kalmanfilter 重写了父类方法返回KalmanFilterXYWH()与 BOTrack 的shared_kalman保持一致。3.2 init_track构造 BOTrack 列表init_track 将原始检测xywh 框、得分、类别逐条包装为BOTrack仅当with_reidTrue且encoder可用时才调用encoder.inference(img, dets)提取 ReID 特征并传入BOTrack(xyxy, s, c, f)。由于当前encoder恒为None实际执行的是无特征分支。3.3 get_distsIoU 与外观双通道距离融合get_dists 是 BOTSORT 与 BYTETracker 在数据关联上的核心区别dists matching.iou_distance(tracks, detections) dists_mask dists self.proximity_thresh dists matching.fuse_score(dists, detections) # 与检测得分融合 if self.args.with_reid and self.encoder is not None: emb_dists matching.embedding_distance(tracks, detections) / 2.0 emb_dists[emb_dists self.appearance_thresh] 1.0 emb_dists[dists_mask] 1.0 # 空间上不可能的配对强制失效 dists np.minimum(dists, emb_dists) # 取代价更小者 return dists其逻辑可概括为计算 IoU 距离代价矩阵1 - IoU记录空间上不可能的掩码dists_mask用fuse_score将检测置信度融合进代价得分越高代价越低优先匹配高分框若启用 ReID则计算余弦距离矩阵embedding_distance除以 2 缩放超过appearance_thresh的置为1.0空间掩码覆盖的置为1.0最后与 IoU 代价逐元素取min。也就是说BoT-SORT 的关联代价同时容纳了位置多近IoU与长得像不像ReID 外观两路信号二者取最优。该代价矩阵随后交给 matching.linear_assignment 做线性指派默认使用lap.lapjv找不到lap包时自动安装lapx0.5.2并回退 scipy。3.4 与 BYTETracker 的关系BOTSORT 复用了 BYTETracker.update 的完整五步流程按track_high_thresh/track_low_thresh把检测分为高分框与次高分框两档第一轮关联高分框与激活轨迹 丢失轨迹做匹配匹配前先multi_predict外推位置并在img可用时用 GMC 得到的单应矩阵warp校正轨迹STrack.multi_gmc见 byte_tracker.py L292-L295第二轮关联未匹配的激活轨迹与次高分框在match_thresh0.5下用纯 IoU 再匹配未确认轨迹只有首帧与剩余检测在0.7阈值下匹配未匹配的删除剩余未匹配检测中得分高于new_track_thresh的激活为新轨迹并清理超过max_time_lost由track_buffer换算的丢失轨迹。BOTSORT 对父类的增强点集中在get_dists加入外观与邻近阈值、get_kalmanfilter改用 XYWH 状态空间、multi_predict转发到BOTrack.multi_predict以及reset()时额外调用self.gmc.reset_params()重置 GMC 内部状态L197-L200。四、GMC 全局运动补偿GMC类位于 ultralytics/trackers/utils/gmc.py用于估计相邻帧之间的全局刚体运动如摄像机平移、旋转并以 2×3 单应矩阵H校正卡尔曼预测的轨迹位置显著降低镜头移动场景下的 ID 跳变。支持的method与实现要点gmc.py L36-L78method原理关键实现orbORB 特征点匹配FastFeatureDetectorORB_create 汉明距离 BFMatcherRANSAC 估计仿射siftSIFT 特征点匹配SIFT_create L2 距离 BFMatchereccECC 强度配准cv2.findTransformECC欧氏运动模型最多 5000 次迭代sparseOptFlow稀疏光流goodFeaturesToTrackcalcOpticalFlowPyrLKRANSAC 估计仿射默认none/None不补偿返回单位矩阵np.eye(2, 3)GMC.apply(raw_frame, detections)对首帧做初始化后返回单位矩阵后续帧计算并返回Hdownscale2的默认降采样在保证精度的同时降低计算量仿射矩阵的平移分量在估计后按downscale倍数还原到原图尺度。特征点方法还支持用detections在掩码中挖掉目标区域避免运动目标本身干扰全局背景运动估计gmc.py L182-L185。五、配置文件 botsort.yaml全部参数说明默认配置位于 ultralytics/cfg/trackers/botsort.yaml参数表如下参数默认值作用tracker_typebotsort追踪器类型仅支持botsort/bytetracktrack.py 会做断言校验track_high_thresh0.5第一轮关联使用的高分检测阈值track_low_thresh0.1第二轮关联使用的次高分检测阈值低于此值的框直接丢弃new_track_thresh0.6未匹配检测得分高于此值才激活为新轨迹track_buffer30丢失轨迹的存活缓冲实际换算为max_time_lost int(frame_rate / 30 * track_buffer)见 byte_tracker.py L249match_thresh0.8第一轮线性指派的有效匹配代价阈值gmc_methodsparseOptFlow全局运动补偿算法可选orb/sift/ecc/sparseOptFlow/noneproximity_thresh0.5空间邻近度阈值IoU 距离大于该值的配对在外观融合时强制失效appearance_thresh0.25ReID 外观距离阈值超过即视为不可能匹配当前未启用with_reidFalse是否启用 ReID 外观关联当前实现尚未支持恒为 False 生效配置文件中还注释了min_box_area与mot20两个保留参数源码中标注用于追踪器评测暂未使用。六、实战如何在项目中启用 BOTSORT6.1 命令行方式yolo track modelyolov10n.pt sourcepath/to/video.mp4 trackerbotsort.yaml6.2 Python APIfrom ultralytics import YOLO model YOLO(yolov10n.pt) results model.track(sourcepath/to/video.mp4, trackerbotsort.yaml, persistTrue) for result in results: # 每个 result 包含 boxes.id轨迹 ID、boxes.xyxy、boxes.conf 等 ids result.boxes.idpersistTrue使追踪器跨视频帧持续存在切换视频文件时追踪器会自动reset()。6.3 底层调用链model.track()底层由 track.py 中的两个回调 驱动on_predict_start读取predictor.args.tracker指定的 yaml实例化BOTSORT(argscfg, frame_rate30)按 batch 大小创建stream 模式下每个视频流一个实例on_predict_postprocess_end取出当前帧检测结果调用tracker.update(det, im0s[i])得到(xyxy, track_id, score, cls, idx)数组再回写进predictor.resultsregister_tracker负责把上述回调注册到模型上。因此自定义追踪器只需在 TRACKER_MAP 中注册新类并继承BYTETracker或BOTSORT重写get_dists/init_track等方法即可。6.4 测试用例验证仓库在 tests/test_python.py 的 test_track_stream 中直接验证了 BOTSORT 的可用性model.track(video_url, imgsz160, trackerbotsort.yaml, save_framesTrue)并循环改写botsort.yaml中的gmc_method为orb、sift、ecc三种方法逐一运行覆盖了 GMC 各算法的回归测试。测试注释还提示追踪场景建议imgsz160以获得更高的置信度与更好的匹配效果。七、总结从源码可以清晰地看出BOTSORT 之所以能提供比纯 BYTE 追踪更稳定的身份保持是因为它做了三处关键升级状态空间升级BOTrack使用KalmanFilterXYWH以宽、高直接建模目标尺度运动外观记忆BOTrack.update_features用 EMA 平滑维护每条轨迹的多帧外观表征尽管 ReID 编码器在 bot_sort.py 中尚未接入运动补偿BOTSORT通过GMC估计全局单应变换在关联前校正轨迹位置。配合 botsort.yaml 中track_high_thresh、track_low_thresh、new_track_thresh、track_buffer、match_thresh、gmc_method等参数开发者可以在不修改一行源码的前提下完成追踪效果的调优。若需继续深入可进一步阅读 byte_tracker.py、basetrack.py、matching.py 与 kalman_filter.py以及仓库的 track 模式文档 获取完整追踪 API 说明。【免费下载链接】yolov10YOLOv10: Real-Time End-to-End Object Detection [NeurIPS 2024]项目地址: https://gitcode.com/GitHub_Trending/yo/yolov10创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考