
简介这份毕业设计资源围绕YOLOv9与DeepSORT的联合应用展开面向计算机视觉方向的学生与开发者解决多目标跟踪从检测到关联的完整落地问题。包内共8个文件以py源码、ipynb笔记本、yml环境配置、names类别文件及md说明为主另有gif效果演示压缩包约16.85MB结构覆盖数据读取、模型加载、跟踪流程与结果可视化等环节。YOLOv9负责逐帧检测并输出边界框DeepSORT借助卡尔曼滤波与深度特征度量完成跨帧目标关联源码中可看到特征提取网络配置、相似度度量与状态更新的实现细节。已有548人学习适合希望理解检测与跟踪如何衔接、并动手复现多目标跟踪流程的读者参考。1. YOLOv9 加 DeepSort 做目标跟踪从毕业设计到能跑通的工程链路目标跟踪这个方向每年毕业设计季都会被翻出来做一遍。原因很直接它既有视觉上的即时反馈——框跟着人走ID 不乱跳演示效果好又有足够的算法深度——检测、特征提取、数据关联、卡尔曼滤波每一层都能展开写论文。而 YOLOv9 加 DeepSort 这套组合恰好卡在一个甜点位上YOLOv9 负责每帧把目标框出来DeepSort 负责把跨帧的同一个目标串成一条轨迹。你拿到一份标着「YOLOv9DeepSort 目标跟踪算法 python 源码」的压缩包真正要解决的问题不是「跑不跑得起来」而是「跑起来之后 ID 为什么老切换、遮挡后为什么跟丢、换段视频为什么就崩」。这篇笔记就按这个顺序拆先讲清这套链路里每个模块在干什么再给一套能直接复现的最小实现然后把调参和踩坑摊开说。适合正在做毕设、需要一套能演示又能写进论文的跟踪方案的人也适合想把检测模型接上跟踪器做业务原型的工程师。2. 拆开 YOLOv9 与 DeepSort检测器和跟踪器各自负责什么2.1 YOLOv9 在跟踪链路里的真实角色很多人把 YOLOv9 当成「更准的 YOLOv8」来用但在跟踪任务里检测器的价值不只是 mAP 高。跟踪对检测的要求是「稳定」——同一目标在连续帧里的框要尽量一致框的抖动会直接传给卡尔曼滤波导致轨迹震荡。YOLOv9 相比前代引入的可编程梯度信息PGI和 GELAN 结构本质上是在有限参数量下把特征提取效率拉高这对跟踪场景的意义是小目标召回更稳遮挡边缘的框不容易突然消失。实际用的时候你不需要自己从头训 YOLOv9。常见做法是拿 COCO 或自定义数据集预训练权重在跟踪视频对应的类别上做微调。如果毕设场景是行人跟踪直接用 COCO 里 person 类的权重就能跑如果是车辆或特定工业目标就得自己标几百张图微调。这里有个容易被忽略的点跟踪用的检测置信度阈值不能照搬纯检测任务。纯检测追求 mAP阈值可以设 0.25跟踪里阈值太低会引入大量误检DeepSort 的匹配矩阵会被噪声撑爆ID 切换率反而上升。我一般从 0.4 起步再根据漏检情况往下调。YOLOv9 的输出格式也要注意。不同版本的推理脚本吐出来的可能是[x1,y1,x2,y2,conf,cls]也可能是[cx,cy,w,h,conf,cls]。DeepSort 需要的是左上右下坐标加置信度格式不对会在匹配阶段报维度错误或者更隐蔽地——框全偏了但程序不崩你盯着屏幕半天才发现坐标是中心点格式。2.2 DeepSort 的匹配逻辑与三个关键参数DeepSort 的核心是「卡尔曼滤波预测 匈牙利算法匹配 级联匹配」。卡尔曼滤波根据上一帧的轨迹预测当前帧目标位置匈牙利算法把预测框和检测框做关联级联匹配则优先处理最近出现过的轨迹避免新轨迹抢走老轨迹的匹配。这里面有三个参数直接决定跟踪质量参数含义典型值调大后果调小后果max_dist特征匹配最大余弦距离0.2误匹配增多ID 串匹配过严轨迹断裂max_iou_distanceIoU 匹配阈值0.7允许更大位移遮挡后易恢复快速移动目标跟丢max_age轨迹丢失后保留帧数30遮挡后能找回但可能保留错误轨迹遮挡即丢ID 切换这三个参数没有万能值。行人匀速走max_age 设 30 够用车辆高速移动max_iou_distance 要放宽到 0.8 以上否则两帧之间位移超过框大小IoU 直接归零。我见过最典型的翻车是用行人参数跑车辆视频车一加速 ID 就换还以为是模型不行。2.3 特征提取网络ReID 模型为什么不能随便换DeepSort 靠外观特征做匹配这个特征来自一个 ReID重识别网络。原版 DeepSort 用的是在行人数据集上训的宽残差网络输出 128 维特征向量。如果你跟踪的不是行人这个特征空间是错位的——车辆之间的外观差异和行人之间的差异分布完全不同余弦距离算出来没有区分度。常见做法是跟踪行人直接用原版权重跟踪车辆或其他目标要么换一个在对应数据集上训过的 ReID 模型要么退而求其次把max_dist调大、更多依赖 IoU 匹配。后者精度会降但比硬套行人特征强。毕设里如果只要求演示效果用 IoU 主导的匹配也能出图但论文里得说清楚这个取舍。3. 用 YOLOv9 加 DeepSort 跑通最小跟踪 demo3.1 环境准备与依赖安装这套链路对环境的敏感度中等主要坑在 PyTorch 和 torchvision 版本匹配。YOLOv9 官方实现一般要求 PyTorch 1.10 以上DeepSort 的 deep_sort_realtime 或原版 deep_sort 对 torch 版本要求更宽松。我一般用 conda 建一个干净环境避免和系统里的 CUDA 版本打架。conda create -n yolo9_deepsort python3.9 -y conda activate yolo9_deepsort # 安装 PyTorch按自己 CUDA 版本选这里以 CUDA 11.8 为例 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 # 跟踪与视觉依赖 pip install opencv-python numpy scipy filterpy scikit-learn pip install deep-sort-realtime逻辑说明filterpy是卡尔曼滤波的实现库DeepSort 内部会调用scikit-learn用于余弦距离计算deep-sort-realtime是一个封装较好的 DeepSort 实现比原版更容易接 YOLO 输出。参数上Python 3.9 是兼容性最好的版本3.11 以上部分旧版 DeepSort 会报collections相关错误。注意如果你拿到的源码包里自带requirements.txt先看里面有没有锁死版本号。锁死的版本往往和当前 CUDA 不匹配直接装会失败建议按上面的方式手动装核心依赖再补源码包特有的包。3.2 YOLOv9 推理输出转 DeepSort 输入格式YOLOv9 推理出来是一堆框DeepSort 需要的是[x1,y1,x2,y2,conf,cls]的 numpy 数组。这一步的转换看着简单但坐标格式和置信度过滤是两个高频出错点。import cv2 import numpy as np from ultralytics import YOLO from deep_sort_realtime.deepsort_tracker import DeepSort # 加载 YOLOv9 权重这里用 ultralytics 接口权重文件按自己训练或下载的填 model YOLO(yolov9-c.pt) # 初始化 DeepSort tracker DeepSort( max_age30, # 轨迹丢失后保留帧数 n_init3, # 连续命中多少帧才确认轨迹 max_iou_distance0.7, # IoU 匹配阈值 max_dist0.2, # 特征余弦距离阈值 embeddermobilenet, # 特征提取网络行人场景可用默认 halfTrue # 半精度推理GPU 上提速明显 ) cap cv2.VideoCapture(test_video.mp4) while cap.isOpened(): ret, frame cap.read() if not ret: break # YOLOv9 推理conf 阈值设 0.4跟踪场景不宜过低 results model(frame, conf0.4, verboseFalse)[0] detections [] for box in results.boxes: x1, y1, x2, y2 box.xyxy[0].cpu().numpy() conf float(box.conf[0].cpu().numpy()) cls int(box.cls[0].cpu().numpy()) # DeepSort 要求格式[左上x, 左上y, 宽, 高, 置信度, 类别] detections.append(([x1, y1, x2 - x1, y2 - y1], conf, cls)) # 更新跟踪器 tracks tracker.update_tracks(detections, frameframe) for track in tracks: if not track.is_confirmed(): continue track_id track.track_id ltrb track.to_ltrb() # 转回左上右下坐标 x1, y1, x2, y2 map(int, ltrb) cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(frame, fID {track_id}, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) cv2.imshow(Tracking, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()逻辑说明model(frame)返回的results.boxes里xyxy是左上右下坐标但 DeepSort 的update_tracks要的是左上角加宽高所以做了x2-x1和y2-y1的转换。n_init3表示轨迹连续命中 3 帧才输出能过滤掉一闪而过的误检。halfTrue在支持 FP16 的 GPU 上能提速 30% 左右CPU 上要关掉。参数说明max_age和max_iou_distance是调参重点后面避坑章节会展开。embedder如果跟踪的不是行人可以换成mobilenet以外的模型或者传入自定义 ReID 模型路径。3.3 把跟踪结果写成视频和轨迹文件跑通实时显示之后毕设通常还需要输出一段带跟踪框的视频以及每个 ID 的轨迹坐标文件方便画轨迹图或做定量分析。import csv # 在循环外初始化写入器 out cv2.VideoWriter(output.mp4, cv2.VideoWriter_fourcc(*mp4v), 30, (int(cap.get(3)), int(cap.get(4)))) traj_file open(trajectories.csv, w, newline) writer csv.writer(traj_file) writer.writerow([frame, track_id, x1, y1, x2, y2]) frame_idx 0 while cap.isOpened(): ret, frame cap.read() if not ret: break # ... 检测与跟踪逻辑同上 ... for track in tracks: if not track.is_confirmed(): continue ltrb track.to_ltrb() writer.writerow([frame_idx, track.track_id, *map(int, ltrb)]) out.write(frame) frame_idx 1 traj_file.close() out.release()逻辑说明VideoWriter的帧率要和源视频一致否则输出视频会快放或慢放。轨迹 CSV 按帧记录每个 ID 的框坐标后续可以用 pandas 读出来画热力图或计算轨迹平滑度。参数上mp4v编码在多数环境可用如果输出文件打不开换成XVID加.avi后缀。4. 跟踪效果调优参数、特征和场景适配4.1 置信度阈值与 NMS 的联动调整YOLOv9 默认带 NMS但跟踪场景下 NMS 的 IoU 阈值会影响密集目标。比如人群场景两个人挨得近NMS 阈值设 0.45 可能把其中一个人框删掉DeepSort 就少了一个检测轨迹断裂。常见做法是把 NMS IoU 阈值提到 0.6 到 0.7让重叠框都保留交给 DeepSort 去关联。置信度阈值和 NMS 要一起调。我一般先固定 NMS 为 0.6然后从 0.5 往下试 conf看漏检和误检的平衡点。如果视频里目标清晰、背景干净conf 可以到 0.5如果目标小、光照差降到 0.3 但要把max_dist收紧到 0.15防止误检被匹配成轨迹。4.2 卡尔曼滤波参数对轨迹平滑度的影响DeepSort 内部的卡尔曼滤波用的是匀速模型状态向量是[x, y, a, h, vx, vy, va, vh]其中 a 是宽高比h 是高。默认的过程噪声和观测噪声协方差是调好的一般不用动。但如果你的目标运动模式不是匀速——比如无人机视角下车辆转弯、行人突然变向——匀速模型会预测偏导致匹配 IoU 下降。这种情况可以调std_weight_position和std_weight_velocity把过程噪声调大让滤波器更相信观测值。代价是轨迹会抖一些。毕设里如果轨迹图要求平滑就保持默认如果要求跟得紧就适当调大过程噪声。这个参数在deep_sort_realtime里没有直接暴露需要改源码里的KalmanFilter初始化部分。4.3 换场景后必须重新验证的三件事一套参数跑通一个视频不代表能跑通所有视频。换场景后我一般按这个顺序检查第一看检测框是否稳定。把 YOLO 单独跑一遍可视化检测结果如果框本身就在跳DeepSort 再怎么调也没用。第二看 ReID 特征是否可区分。可以写个小脚本把同一目标不同帧的特征向量算余弦相似度如果相似度低于 0.5说明特征网络不适合这个场景。第三看轨迹 ID 切换频率。统计一段视频里 ID 总数和实际目标数如果 ID 数是目标数的三倍以上说明匹配参数需要放宽或收紧。5. YOLOv9 加 DeepSort 跟踪的避坑与排查记录5.1 现象ID 频繁切换一个目标出现多个 ID原因最常见的是max_dist太小外观特征匹配过严目标稍微转个身、光照变一点特征距离就超过阈值匹配失败新轨迹生成。其次是检测框抖动大IoU 匹配也不稳定。解决先把max_dist从 0.2 调到 0.3观察 ID 切换是否减少。如果还不行检查 ReID 模型是否匹配场景。行人场景用默认模型车辆场景换车辆 ReID 或把max_iou_distance调到 0.8让 IoU 主导匹配。5.2 现象遮挡后目标 ID 变了或者轨迹直接消失原因max_age太小目标被遮挡几帧后轨迹被删除重新出现时只能新建 ID。另外如果遮挡期间卡尔曼滤波预测位置偏太远重新出现时 IoU 匹配不上。解决把max_age从 30 提到 50 甚至 80给遮挡留足恢复时间。同时检查max_iou_distance遮挡后目标位移可能较大IoU 阈值要放宽。代价是错误轨迹保留时间变长需要在演示时手动清理。5.3 现象程序跑几帧后报维度错误或崩溃原因YOLO 输出格式和 DeepSort 输入格式不匹配。常见的是把[cx,cy,w,h]直接传进去或者置信度位置放错。另外空检测帧传入空列表时部分 DeepSort 版本会报错。解决在转换处加断言检查数组形状。空检测时传空列表[]不要传None。如果用的是自己改过的 YOLO 推理脚本打印一帧的results.boxes.data确认格式。5.4 现象GPU 利用率低帧率上不去原因YOLO 推理和 DeepSort 特征提取串行执行且每帧都在做 ReID 特征计算。如果检测框多ReID 网络调用次数线性增长。解决开halfTrue半精度把 ReID 特征提取批量处理而不是每个框单独调一次如果实时性要求不高可以隔帧做 ReID中间帧只用卡尔曼预测和 IoU 匹配。毕设演示一般 15 帧以上就够看不必追求 30 帧满帧。5.5 现象换视频后跟踪框全偏但程序不报错原因视频分辨率变了但代码里画框或坐标转换用了硬编码的缩放比例。或者 YOLO 推理时的imgsz和实际视频分辨率不匹配导致框坐标偏移。解决不要硬编码任何分辨率相关参数。YOLO 推理时imgsz设为 640 或 1280输出框坐标是基于原图的直接使用即可。画框前打印一帧的框坐标和图像尺寸确认没有越界。6. 把跟踪结果做成可写进论文的定量指标跑出演示视频只是第一步毕设答辩时老师更想看的是量化结果。目标跟踪常用的指标有 MOTA、MOTP、IDF1、ID Switch 次数。你不需要自己实现这些指标用py-motmetrics就能算。import motmetrics as mm import numpy as np # acc 累积器 acc mm.MOTAccumulator(auto_idTrue) # 假设 gt 和 pred 是按帧组织的列表 # gt: [[[x1,y1,x2,y2], ...], ...] pred: [[[x1,y1,x2,y2,id], ...], ...] for frame_gt, frame_pred in zip(gt_frames, pred_frames): gt_ids list(range(len(frame_gt))) pred_ids [p[4] for p in frame_pred] # 计算距离矩阵这里用 IoU 距离 distances mm.distances.iou_matrix( np.array(frame_gt), np.array([p[:4] for p in frame_pred]), max_iou0.5 ) acc.update(gt_ids, pred_ids, distances) mh mm.metrics.create() summary mh.compute(acc, metrics[mota, motp, idf1, num_switches], nameYOLOv9DeepSort) print(summary)逻辑说明MOTAccumulator按帧累积匹配结果iou_matrix计算检测框和真实框之间的 IoU 距离max_iou0.5表示 IoU 低于 0.5 视为不匹配。num_switches就是 ID Switch 次数这个指标直接反映跟踪稳定性。参数上max_iou要和 DeepSort 里的max_iou_distance区分开前者是评估用的匹配阈值后者是跟踪器内部的。我自己的习惯是每换一次参数就把 MOTA 和 ID Switch 记在一个表格里跑三组参数就能看出趋势。有一次我把max_age从 30 调到 60MOTA 涨了 4 个点但 ID Switch 也涨了因为错误轨迹保留太久。最后折中在 45。这种取舍没有公式只能靠实验数据说话。希望帮到你。本文还有配套的精品资源点击获取