Yolov5+Deepsort+Fastreid:多目标跟踪与行人重识别实战

发布时间:2026/10/1 9:26:42

Yolov5+Deepsort+Fastreid:多目标跟踪与行人重识别实战 简介本资源面向计算机、人工智能、通信工程等专业的在校学生与开发者提供一套基于Yolov5-Deepsort-Fastreid重构的视频行人多目标跟踪MOT与行人重识别ReID特征提取完整方案适合毕业设计、课程设计、项目立项演示及进阶学习使用。压缩包共37个文件约38KB以31个Python源码文件为核心辅以2个yaml配置、2个txt依赖与说明、1个md文档及gitignore等涵盖模型定义、配置管理、特征提取接口与运行依赖目录结构清晰便于按模块阅读与二次开发。资源已通过测试运行功能完整可直接复现行人检测、跟踪与ReID特征提取流程并支持在此基础上修改扩展以实现其他功能。目前已有85人学习关注适合需要快速搭建MOT与ReID实验环境、理解Deepsort与Fastreid集成思路的读者参考使用。1. 从一段卡顿的跟拍视频说起Yolov5DeepsortFastreid 到底在解决什么你手上有一段园区门口的行人跟拍视频想统计每个人在画面里待了多久、去了哪个方向、有没有折返。直接拿 Yolov5 逐帧检测框会跳同一个人在第 30 帧是 id1第 31 帧可能变成 id7轨迹碎成一地。这就是 MOT多目标跟踪要解决的问题让同一个目标在连续帧里保持同一个身份。而 ReID行人重识别解决的是更狠的一层——当目标被遮挡、走出画面再回来甚至换了一个摄像头还能不能认出“这还是刚才那个人”。标题里的 Yolov5-Deepsort-Fastreid 就是这三件事的组合拳Yolov5 负责每帧把行人框出来Deepsort 负责把框和已有轨迹做关联、维持 ID 稳定Fastreid 负责提供比 Deepsort 原生外观特征更强的 ReID 特征让关联在遮挡和交叉时更抗造。这套组合在工程里非常常见因为它把检测、跟踪、特征提取拆成了三个可独立替换的模块你调其中一个不会把另外两个搞崩。适合谁看已经跑通过 Yolov5 推理、想把手里的检测结果升级成带 ID 的轨迹或者跑过 Deepsort 但发现 ID switch 太多、想换更强 ReID 特征的人。下面按“先跑通、再拆接口、再调参、最后避坑”的顺序讲代码和参数都给到能直接抄的程度。2. 三模块拆解与最小可跑链路Yolov5 出框、Deepsort 关联、Fastreid 提特征2.1 为什么不是端到端而是三段式拼接很多人第一反应是找个端到端 MOT 模型一把梭。但实际落地时三段式反而更稳。原因很直接Yolov5 的检测精度和速度你已经调熟了换检测器意味着重新标数据、重新调 anchorDeepsort 的卡尔曼滤波和匈牙利匹配逻辑是透明的出问题能定位到具体哪一步Fastreid 作为独立特征提取器可以单独换 backbone、单独在行人数据集上微调不影响检测和跟踪。三段式的数据流是这样的Yolov5 每帧输出[x1,y1,x2,y2,conf,cls]的检测框列表过滤出 person 类后送给 Deepsort。Deepsort 对每个框做卡尔曼预测同时用 Fastreid 提取该框对应的 128/256/512 维外观特征和轨迹库里已有特征做余弦距离匹配。匹配上就更新轨迹匹配不上就新建轨迹连续 N 帧没匹配上就删除轨迹。这里的关键接口是Deepsort 原本自带一个简单的 CNN 特征提取器你要做的是把它替换成 Fastreid 的推理接口。替换点通常在deep_sort/deep_sort/deep/feature_extractor.py或类似位置把_get_features方法的实现换成调用 Fastreid 的InferenceEngine。2.2 最小可跑链路从视频到带 ID 的输出先保证三段能串起来再谈优化。下面是一个最小推理脚本的骨架假设你已经装好 Yolov5、Deepsort、Fastreid 三个仓库。import cv2 import torch import numpy as np from yolov5.models.experimental import attempt_load from yolov5.utils.general import non_max_suppression, scale_coords from deep_sort.deep_sort import DeepSort from fastreid.config import get_cfg from fastreid.engine import DefaultPredictor # 1. 加载 Yolov5 yolo attempt_load(yolov5s.pt, map_locationcuda:0) yolo.eval() # 2. 加载 Fastreid 配置与权重 cfg get_cfg() cfg.merge_from_file(fastreid/configs/Market1501/bagtricks_R50.yml) cfg.MODEL.WEIGHTS fastreid_market1501.pth cfg.MODEL.DEVICE cuda:0 reid_predictor DefaultPredictor(cfg) # 3. 初始化 Deepsort传入 Fastreid 提取器 deepsort DeepSort( reid_predictor, # 关键把 Fastreid 作为特征提取器注入 max_dist0.2, # 余弦距离阈值越小越严格 min_confidence0.3, # 检测置信度下限 max_iou_distance0.7, # IoU 匹配阈值 max_age70, # 轨迹最大存活帧数 n_init3 # 连续命中多少帧才确认轨迹 ) cap cv2.VideoCapture(test.mp4) while cap.isOpened(): ret, frame cap.read() if not ret: break img cv2.resize(frame, (640, 640)) img_t torch.from_numpy(img).permute(2,0,1).float().unsqueeze(0).cuda() / 255.0 pred yolo(img_t)[0] pred non_max_suppression(pred, conf_thres0.4, iou_thres0.5)[0] if pred is not None: pred[:, :4] scale_coords(img_t.shape[2:], pred[:, :4], frame.shape).round() # 只保留 person 类COCO 里 person 的 cls0 person_mask pred[:, -1] 0 dets pred[person_mask][:, :4].cpu().numpy() confs pred[person_mask][:, 4].cpu().numpy() # 送入 Deepsort内部会调用 Fastreid 提特征 outputs deepsort.update(dets, confs, frame) for x1, y1, x2, y2, track_id in outputs: cv2.rectangle(frame, (int(x1), int(y1)), (int(x2), int(y2)), (0,255,0), 2) cv2.putText(frame, fID:{track_id}, (int(x1), int(y1)-5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0,255,0), 2) cv2.imshow(MOT, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()逻辑说明Yolov5 负责出框non_max_suppression做后处理去重scale_coords把 640 尺度下的框映射回原图。Deepsort 的update接收检测框、置信度和原图内部对每个框裁剪后调用 Fastreid 提特征再和轨迹库做匹配。参数说明max_dist0.2是余弦距离阈值调大召回高但容易串 ID调小则容易断轨max_age70表示轨迹丢失后保留 70 帧给遮挡后重现留缓冲n_init3表示连续 3 帧匹配上才确认新轨迹能过滤掉一闪而过的误检。2.3 Fastreid 特征提取接口的对接细节Fastreid 的DefaultPredictor接收的是 BGR 图像输出是归一化后的特征向量。Deepsort 内部裁剪出的行人框尺寸不一直接送进 Fastreid 会因 resize 导致特征不稳定。常见做法是在送入前统一 padding 到固定长宽比比如 256x128保持行人的宽高比不变短边补零。def extract_reid_feature(predictor, img, bbox): x1, y1, x2, y2 map(int, bbox) crop img[max(0,y1):y2, max(0,x1):x2] if crop.size 0: return np.zeros(2048, dtypenp.float32) # 统一 resize 到 256x128保持宽高比 h, w crop.shape[:2] target_h, target_w 256, 128 scale min(target_h/h, target_w/w) new_h, new_w int(h*scale), int(w*scale) resized cv2.resize(crop, (new_w, new_h)) canvas np.zeros((target_h, target_w, 3), dtypenp.uint8) top (target_h - new_h) // 2 left (target_w - new_w) // 2 canvas[top:topnew_h, left:leftnew_w] resized feat predictor(canvas) # Fastreid 输出归一化特征 return feat.flatten()这段代码的关键是 padding 而不是直接拉伸直接拉伸会改变行人身体比例ReID 特征会明显变差。Fastreid 输出的特征维度取决于 backboneR50 通常是 2048 维经过 BNNeck 后取 2048 维做余弦距离即可。如果你换更小的 backbone维度会变Deepsort 里的特征库维度要同步改否则会报维度不匹配。3. 把接口源码拆开改Deepsort 替换特征提取器与 Fastreid 配置调参3.1 Deepsort 里特征提取的调用链Deepsort 的跟踪逻辑在tracker.py特征提取在nn_matching.py和feature_extractor.py。原始 Deepsort 用的是一个简化版 CNN输入 128x64输出 128 维。你要替换成 Fastreid需要改三个地方一是feature_extractor.py里的__init__不再加载原 CNN而是接收 Fastreid predictor二是_get_features方法改成调用上面的extract_reid_feature三是nn_matching.py里的NearestNeighborDistanceMetric的维度参数要改成 Fastreid 的输出维度。# 修改后的 feature_extractor.py 核心部分 class FastReIDExtractor: def __init__(self, predictor, feature_dim2048): self.predictor predictor self.feature_dim feature_dim def __call__(self, imgs, bboxes): # imgs: 原图列表bboxes: 对应的框列表 features [] for img, bbox in zip(imgs, bboxes): feat extract_reid_feature(self.predictor, img, bbox) features.append(feat) return np.array(features)然后在tracker.py初始化时把self.extractor指向FastReIDExtractor实例。注意 Deepsort 的update方法里会调用self.extractor(imgs, bboxes)所以你的__call__签名要和原来一致否则会报参数错误。3.2 Fastreid 配置文件里必须改的四个参数Fastreid 的配置文件在configs/下以 Market1501 的bagtricks_R50.yml为例。直接拿来做推理有四个参数必须确认参数默认值推理时建议作用MODEL.WEIGHTS空指向你的 .pth不设就是随机权重特征全是噪声MODEL.DEVICEcudacuda 或 cpu和你的环境一致TEST.IMS_PER_BATCH641 或 2推理时 batch 太大会爆显存INPUT.SIZE_TEST[256,128][256,128]和训练时一致改了特征会漂如果你用的是自己训练的 ReID 模型还要确认MODEL.BACKBONE.NAME和MODEL.HEADS.NAME与训练时一致否则加载权重会报 key 不匹配。常见做法是先用官方 Market1501 权重跑通再换自己的模型。3.3 用 Yolov5 训练自己的数据集时检测类别要同步如果你的场景不是 COCO 的 person而是工服、安全帽等自定义类别Yolov5 需要重新训练。训练时注意data.yaml里的nc和names推理时过滤类别的索引要对应改。比如你只关心worker类且它在 names 里排第 0那person_mask pred[:, -1] 0就要改成对应索引。# data.yaml 示例 train: ./images/train val: ./images/val nc: 2 names: [worker, helmet]训练命令用python train.py --data data.yaml --weights yolov5s.pt --epochs 100 --batch-size 16 --img 640。训练完后推理脚本里attempt_load加载你的best.pt类别过滤索引改成worker对应的 0。这一步不做Deepsort 会把安全帽也当行人跟踪ID 会乱。4. 避坑与排查ID 跳变、特征维度不匹配、显存溢出4.1 现象同一个人 ID 频繁跳变轨迹碎成多段原因通常有三个一是max_dist设得太大不同人的特征被误匹配二是 Fastreid 特征没有做归一化余弦距离计算失真三是检测框抖动严重裁剪出的行人区域每帧差异大特征不稳定。解决先把max_dist降到 0.15 试确认 Fastreid 输出前做了 L2 归一化再检查 Yolov5 的conf_thres是否太低导致框在目标边缘跳动。如果还不行在 Deepsort 的匹配阶段加入 IoU 代价作为辅助而不是只靠外观距离。4.2 现象报错 “Feature dimension mismatch”原因Deepsort 的NearestNeighborDistanceMetric初始化时指定的维度和你实际提取的特征维度不一致。比如你初始化时写了 128但 Fastreid 输出 2048。解决在初始化DeepSort时把feature_dim参数改成 Fastreid 的实际输出维度。R50 是 2048R34 可能是 2048 或 512具体看MODEL.HEADS.EMBEDDING_DIM。改完还要清空轨迹库因为旧特征维度不对。4.3 现象跑几十帧后显存溢出原因Deepsort 的轨迹库会保留每条轨迹的历史特征max_age设得太大时轨迹数量膨胀特征库占用显存持续增长。另外 Fastreid 如果每帧对每个框都跑一次推理batch 虽小但调用频繁显存碎片会累积。解决把max_age从 70 降到 30 左右轨迹确认后只保留最近 10 帧的特征做匹配Fastreid 推理时用torch.no_grad()包住并定期torch.cuda.empty_cache()。如果还紧张把 Fastreid 换成更小的 backbone比如 ResNet34 或 OSNet。4.4 现象遮挡后重新出现ID 变成新的原因max_age太短轨迹在遮挡期间被删除了或者 ReID 特征在遮挡时提取的是被遮挡的部分特征质量差匹配不上。解决适当增大max_age到 50-70给遮挡留缓冲同时在 Deepsort 匹配时对丢失轨迹的匹配阈值放宽比如max_dist从 0.2 放到 0.3只对丢失轨迹放宽正常轨迹保持严格。这个策略在nn_matching.py里改distance计算时的阈值分支即可。4.5 现象树莓派 5 上部署时帧率掉到个位数原因Yolov5s 在树莓派 5 上 CPU 推理已经吃力再加 Fastreid R50 基本跑不动。解决Yolov5 换yolov5n并导出 ONNX 用 ONNX Runtime 推理Fastreid 换 OSNet x0.25 或 MobileNet backbone导出 ONNXDeepsort 的卡尔曼滤波部分用 numpy 实现不占 GPU。整体帧率能回到 5-8 FPS满足离线分析够用。如果要求实时建议用边缘加速棒或换更轻量的跟踪方案。5. 进阶技巧用 ReID 特征做跨摄像头轨迹拼接与验证单摄像头跑通后最有价值的进阶方向是跨摄像头轨迹拼接。思路是每个摄像头独立跑 Yolov5DeepsortFastreid得到带 ID 的轨迹和每条轨迹的特征均值。然后在后端把不同摄像头同一时间段的轨迹特征做余弦距离比对距离小于阈值的认为是同一个人把 ID 映射统一。具体做法对每条确认轨迹取最近 20 帧的 Fastreid 特征做平均得到该轨迹的“身份特征”。跨摄像头匹配时用匈牙利算法对两组轨迹特征做全局最优匹配阈值设在 0.25-0.3 之间。匹配上后把摄像头 B 的轨迹 ID 映射到摄像头 A 的 ID 上实现跨镜追踪。验证方法拿一段有重叠区域的视频人为标注几个人在两个摄像头里的出现时间段跑完拼接后看 ID 是否一致。如果 ID 一致率高但偶尔串检查是不是特征均值受遮挡帧污染可以改用中位数或只取质量最高的 10 帧特征。我自己的习惯是每次换 ReID 模型或改 Deepsort 参数先跑一段 30 秒的固定视频把 ID switch 次数和轨迹完整度记下来和上一版对比。没有这个基线调参就是玄学。希望帮到你。本文还有配套的精品资源点击获取
延伸阅读

更多相关文章

2026/10/1 9:26:42

Linux上使用Homebrew:跨发行版的开发者包管理实践

1. 为什么Linux用户需要Homebrew——它不是Mac的“专利”,而是跨平台包管理的务实选择Homebrew这个词,对很多刚从macOS转战Linux的朋友来说,第一反应往往是:“这不是苹果生态里的东西吗?Linux不是自带apt、dnf、zypper…

2026/10/1 9:26:42

localStorage 避坑指南:序列化、按 id 删除与 CRUD 封装

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/1 10:31:45

关键字新闻爬虫实战:百度与今日头条数据采集入库全方案

简介:这是一份基于 Java 实现的新闻爬虫项目,覆盖百度新闻与今日头条两个信源,支持按关键字批量抓取新闻并写入数据库,适合需要采集新闻数据的 Java 开发者、数据分析人员或爬虫初学者参考。压缩包共 20 个文件,包含 1…

2026/10/1 10:31:45

AMD花82亿美元买世界模型,图什么?

导读: 一家以芯片闻名的公司,拟用约82亿美元的股票买下一家做「世界模型」的公司,还要让李飞飞出任首席科学家。AMD图的是什么?目前能确认的是交易安排和标的方向;技术怎么接入产品、钱怎样赚回来,仍要看后…

2026/10/1 10:31:45

Java面试中Redis缓存一致性怎么答?

面试官问缓存一致性,不是想听你背“先删缓存再更新数据库”或者“先更新数据库再删缓存”这两句话。他想知道的是:你知不知道这两种顺序在并发场景下分别会出什么问题,以及你实际项目里怎么选、怎么兜底。先更新数据库,再删除缓存…

2026/10/1 10:31:45

溶解氧时间序列预测:LSTM与EEMD-LSTM完整源码实战

简介:面向溶解氧浓度时序预测的深度学习项目,源自个人期末大作业并获97分,含完整可运行源码与全部实验数据,适合正在做课程设计或期末作业的计算机相关专业学生,也适合希望实战时序预测的进阶学习者。项目覆盖从数据读…

2026/10/1 10:26:45

内容平台雷达系统:实时流量监测与规则变化预警

1. 这个项目到底是什么“PLFM_RADAR”这个代号,最初是我们团队内部对“平台信号雷达”的简称——PLFM 是 Platform 的缩写,RADAR 就是雷达。项目核心就一句话:做一个持续运转的内容平台监测系统,实时感知平台规则变化、流量波动、…

2026/10/1 5:21:14

东莞市品牌网站建设报价常见报错与解决

东莞品牌网站建设报价单背后:一份保姆级建站教程避坑实录 网站做好了没人访问,这大概是很多老板最头疼的事。花了大几万做的品牌站,上线后流量惨淡,比路边摊还冷清。别急着骂外包公司,很多“东莞品牌网站建设报价”里藏着不少猫腻,比如用模板站冒充定制…

2026/9/29 21:48:03

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解 【免费下载链接】spirula-studio Cross-vendor 3D Gaussian Splatting trainer - video to splat to mesh, Vulkan or CUDA. 项目地址: https://gitcode.com/GitHub_Trending/sp/spirula-studio Sp…

2026/9/29 7:00:49

SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南 模板网站太丑不够用?别急着加滤镜,那是治标不治本。很多老板盯着后台流量掉得眼红,却还在纠结首页Banner的圆角是不是3像素。这就像穿着西装去挖土,姿势不对,努力白费。我整理这份 速查手册…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑