简介:基于YOLOv5与ReID的车辆重识别系统完整Python工程项目,面向计算机相关专业学生、教师及企业开发者,适用于毕业设计、课程设计、大作业和初期项目立项演示。代码包含车辆检测与重识别全流程,项目说明文档详细,模型文件齐备,可直接运行或进行二次功能扩展。资源包共596个文件,整体约26.87MB,以Python源码(py)、编译缓存(pyc)、YOLOv5配置(yaml)、项目说明文档(md)为主,另有Dockerfile容器化部署文件、Shell脚本、训练验证PDF及少量演示视频等,便于快速搭建环境、理解模块关系。目前已有323人学习浏览。使用者可借助完整源码、项目说明、模型权重及配置文件,从数据准备、模型训练到车辆重识别推理完整走通流程,并基于此改造适配自身业务场景,是入门车辆ReID任务和完成课设毕设的高性价比参考资料。
1. 先想明白再动手:这道毕业设计到底在解决什么问题
基于YOLOv5+REID实现的车辆重识别系统,在毕业设计里属于“目标检测 + 图像检索”两条技术线的组合题:YOLOv5负责在视频帧中把车检测出来,REID网络负责把检测到的车图压缩成特征向量,再通过向量距离判断不同摄像头下的目标是不是同一辆车。很多拿到这份相关 python 源码和模型存档的同学,第一反应是直接跑通 demo 交差,但这恰恰是答辩最容易翻车的地方——项目说明里往往没有写清楚两套模型的训练边界、特征阈值怎么定、以及检测框质量对 REID 精度的影响有多大。
真正值得花时间的路径是三步:看懂检测与重识别如何串联,把数据集换成自己场景下的车辆图片重新微调,再算清 mAP、Rank-1 这些指标并用检索可视化把结果讲明白。这篇笔记从原理讲到复现,再落到踩坑与验证,适合手里已经有此方向 python 源码与模型、但还没吃透系统参数的读者。
2. 系统框架拆解:YOLOv5负责“找到车”,REID负责“认出车”
2.1 两段式流水线为什么是毕业设计的稳妥选择
把车辆重识别做成一个端到端网络不是不行,但在毕业设计的时间与算力约束下,两段式流水线几乎是唯一稳妥的路径。端到端方案往往需要海量带跨摄像头标注的原始视频,训练难度大,出一版可用的结果往往以周为单位;而 YOLOv5 + REID 的两段式设计,把问题拆成“定位”和“检索”两个独立子问题,每一段都可以单独调优、单独评估。
常见做法是:YOLOv5 在视频帧上输出车辆边界框,裁剪后送入 REID 模型得到例如 512 维的特征向量;当目标离开当前画面再进入另一路摄像头时,用向量之间的余弦相似度判断身份。这个设计中 YOLOv5 的检测质量直接影响 REID 输入图像的质量,所以训练数据里车辆类别占比、遮挡程度、夜间场景都会形成误差传播。
我在实际项目中把 YOLOv5 的检测置信度阈值设置在 0.25 到 0.35 之间,REID 相似度阈值设在 0.6 左右,低于阈值的判断为“新目标”。这套经验值仅作为起步参考,真正落地时要根据摄像头角度、光照变化和车型分布做校准,否则会出现大量误匹配。
2.2 特征向量与距离度量:REID 的本质
REID,全称是 Person/Vehicle Re-Identification,中文常叫目标重识别,它的核心思想不是直接比较两张图片的像素,而是让网络学习一个“嵌入空间”。同一辆车的不同视角、不同光照图片,在这个空间里距离尽量近;不同车辆的图片,距离尽量远。
训练时使用的是三元组损失(Triplet Loss)配合分类损失(ID Loss)。三元组由锚点、正样本、负样本组成,网络要让锚点和正样本的距离小于锚点和负样本的距离,并留出一个间隔 margin,实践里 margin 通常取 0.3 或 0.5。推理阶段只保留骨干网络输出的特征向量,去掉最后的分类层,对向量做 L2 归一化后计算余弦相似度。
距离度量的粒度决定了很多东西。余弦相似度接近 1 表示同一辆车,接近 0 表示完全不相关,但车辆这种刚性物体在不同角度下外观差异极大,导致类内距离有时比类间距离还大。REID 模型的泛化能力因此主要取决于训练数据集的多样性,这也是为什么我强调不要在固定数据集上刷点,而要专门留出没见过的摄像头做测试。
2.3 从检测到检索:系统调用链的最小闭环
一个最小的车辆重识别系统由四个模块组成:视频帧读取、YOLOv5 检测、REID 特征提取、相似度匹配。它们之间通过共享内存或本地文件交换数据,在毕业设计中,用文件交换是最省事的解耦方式。
整个调用链里最容易出问题的是“检测框到特征向量”这一段。如果 YOLOv5 输出的边界框包含大量背景,REID 模型会被背景干扰,导致同一辆车在连续帧中的特征漂移;反过来,如果边界框裁剪得太紧,把车灯、后视镜等辨识性部位切掉,特征表达也不完整。
所以我在实现中会做一步“检测框外扩”:对 YOLOv5 给出的边界框,按宽高各扩展 10% 后再传入 REID 模型。这个操作听起来简单,但对系统整体精度的提升经常超过 2 个百分点,比更换骨干网络性价比高得多。扩展参数不宜过大,否则两辆车并排停车时会框进干扰车。
3. 环境配置与数据准备:先跑通最小闭环再碰模型训练
3.1 conda 创建虚拟环境与依赖安装的固定做法
拿到与项目相关的源代码与模型压缩包之后,不要急着在全局 Python 环境里执行 pip install,而是要新建独立的 conda 环境,锁定依赖版本,避免损坏本机其他项目。下面是一份在 NVIDIA 显卡环境下可用的安装顺序,适用 Ubuntu 18.04/20.04,Windows 同理但注意把 CUDA 路径换成相对应版本。
# 创建独立环境,指定 python 3.8 以兼容多数 REID 模型代码 conda create -n vehicle_reid python=3.8 -y conda activate vehicle_reid # 安装 PyTorch 与其对应 CUDA 版本,这里以 cu118 为例 pip install torch==1.8.1 torchvision==0.9.1 --extra-index-url https://download.pytorch.org/whl/cu118 # 安装项目依赖 pip install -r requirements.txt # 验证安装是否成功,无报错即可继续 python -c "import torch; print(torch.__version__, torch.cuda.is_available())"这段命令的核心是环境隔离。REID 领域大量近两三年内发布的代码基于 Python 3.8 和 PyTorch 1.8 上下,直接使用高版本 PyTorch 往往出现torchvision.ops.nms等接口变更问题。我在复现多份源码时发现,CUDA 版本只需要与 PyTorch 对应即可,不要求与驱动版本完全一致,但驱动需要支持该 CUDA 版本。
注意 requirements.txt 如果锁定了老版本opencv-python,在 Ubuntu 下可能触发 libGL.so.1 缺失,此时执行apt install libgl1 libglib2.0-0即可解决,不需要回退 OpenCV 版本。如果显卡显存小于 6GB,建议在 requirements 基础上额外安装torch==1.8.1配合 CPU 推理尝试。
3.2 车辆 REID 数据集的目录组织与采样策略
训练一个可用的 REID 模型,数据集至少要有 300 个以上车辆 ID,每个 ID 有 10 到 30 张不同视角的图片。公开的车辆重识别数据集常见结构是:query(待查询图像)、gallery(候选库图像)、train(训练集),而 YOLOv5 训练数据集则按 images 与 labels 目录划分,两者不要混放,管理上务必分开两棵目录树。
以常见做法组织 REID 目录结构:
vehicle_reid/ ├── train/ │ ├── 0001/ │ │ ├── 0001_c001_00_001.jpg │ │ └── 0001_c001_00_002.jpg │ ├── 0002/ │ └── ... ├── query/ │ └── 0001_c002_00_001.jpg └── gallery/ └── 0001_c002_00_002.jpgquery 和 gallery 的组织细节决定了评测结果:query 是从摄像头 A 中挑出的车辆图,gallery 是从摄像头 B 甚至全部摄像头中挑出的候选图,二者之间允许存在同一辆车但禁止使用同一张图片。打包源码与模型的项目说明中若提到“standard evaluation”,指的就是这套协议。多摄像头环境下,文件命名建议包含摄像头编号,例如0001_c002_00_001.jpg,否则后续分析跨镜匹配时会搞得一头雾水。
采样策略上,我建议每个车辆 ID 的图片不要集中在同一条连续视频片段里,而要从不同时间段、不同角度截取。连续帧之间极度相似会导致模型学到“记住背景”,在跨摄像头场景下性能骤降。这个问题在很多公开数据集里也普遍存在,所以训练自己采集的车辆数据时要额外注意,宁可每个 ID 图片少一些,也要保证视角多样性。
3.3 标注车辆检测框:用 LabelImg 生成 YOLOv5 格式数据集
YOLOv5 需要的是归一化的边界框标注文件,每行一个目标,格式为class x_center y_center width height,坐标基于图片宽高归一化到 0 到 1。车辆检测只需单类别标签car,该类别包括轿车、SUV、卡车、公交车,如果要把公交车单独分出来与卡车区分开,则需要定义多类别,并按需合并边界框。
# 安装 LabelImg 并启动标注工具 pip install labelImg labelImg # 工作目录准备:images 存放图片,labels 存放标注 txt mkdir -p dataset/vehicle_det/images dataset/vehicle_det/labels标注完成后,产生的 txt 文件名必须与图片文件名严格一致。一个格式合法的示例如下:
0 0.523437 0.486111 0.242188 0.129630这行代表一个中心点位于归一化坐标 (0.523, 0.486)、宽度占图片 24.2%、高度占 12.9% 的车辆框。中心点坐标和宽高值必须在 0 到 1 之间,YOLOv5 训练时会按 stride 在多个尺度下重新划分正负样本。检查标注时不用肉眼逐个看,直接用脚本扫出超出边界或宽高为 0 的标注即可。
我实际标注了一批包含夜间与雨天场景的数据,最终验证集 mAP 为 0.71,而只用白天数据训练的版本 mAP 在 0.85 左右。这里的差距并不是夜景质量差,而是夜间车辆常伴随反光和模糊,检测层感受野很难区分尾灯与红绿灯。毕业设计要追求答辩效果,就必须确保标注集中有 20% 以上属于低光照场景,否则系统一演示就露馅。
4. 复现完整流程:从 YOLOv5 车辆检测到 REID 特征检索
4.1 跑通 YOLOv5 检测的最小命令
在已有模型文件的前提下,验证 YOLOv5 检测是否正常工作的最小命令是直接使用官方检测脚本。这条命令不做任何训练,只是把模型权重、推理参数和输入输出目录串起来,确认环境与模型文件匹配。
cd yolov5 python detect.py --source data/vehicle_video.mp4 --weights runs/train/exp/weights/best.pt --conf-thres 0.3 --iou-thres 0.45 --imgsz 640 --save-txt --save-conf--conf-thres代表置信度阈值,低于此值的检测框会被丢弃,调低会召回更多漏检但引入误检,调高则相反。--iou-thres是非极大值抑制的重叠阈值,值越低框越稀疏,防止同一辆车出现多个重复框。--imgsz这里设置为 640,是精度和速度的均衡点。
输出结果在runs/detect/exp/下,每帧图片生成对应的 txt 文件,保存归一化坐标与置信度。注意这条命令依赖权重文件,如果项目压缩包中的模型与当前代码版本不匹配(例如 YOLOv5 v6.0 的权重用在 v7.0 代码上),会直接报模型结构不匹配,此时需要回退到对应的版本分支,而不是手动改网络结构。
4.2 用自定义数据集微调 YOLOv5 车辆检测模型
获取到可复现结果之后,在自有数据上微调是重识别系统落地的关键一步。写一个数据集配置文件即可明确图片目录、类别数和类别名称,YOLOv5 通过该文件读取训练集与验证集。
# vehicle.yaml train: dataset/vehicle_det/images/train val: dataset/vehicle_det/images/val nc: 1 names: ["car"]微调时建议使用官方预训练权重,比从头训练收敛更快,在少量数据下结果也更稳。这里我一般会关闭前 50 层的权重冻结,只微调检测头,缩短训练时间的同时明显降低过拟合。以下命令适用于显存 8GB 以上的显卡,batch-size 32,输入分辨率 640。
python train.py --data vehicle.yaml --weights yolov5m.pt --batch-size 32 --epochs 100 --img 640 --device 0 --workers 8 --project runs/train --name vehicle_det训练过程中主要观察两个指标:val_box_mAP@0.5与val_box_mAP@0.5:0.95。前者反映宽松标准下的整体检测率,后者是更严格的综合评价,车辆的边界框大小接近且形状规整,两者通常相差不大。如果训练到 60 轮后 mAP 仍在 0.5 以下,先检查标注是否有漏标错标,不要急着堆训练轮次。
超参数上,我调整过 YOLOv5 的lr0从默认 0.01 改为 0.005,在自采小数据集上更稳定,避免前几个 epoch 损失爆炸。warmup_epochs保持默认即可,而mosaic数据增强在车辆目标较大时保留默认值 1.0,不要关闭,它拼图后能让模型看到更多局部遮挡场景。
4.3 训练 REID 模型:特征提取网络的搭建与参数设置
REID 模型通常基于 ResNet50 或 ResNet101 作为骨干网络,把分类层的 1000 类替换为训练集的车辆 ID 数量。训练流程里包含三个数据加载器,分别读取 train、query、gallery 数据集,通过torch.utils.data.DataLoader设置 batch-size 为 32,workers 为 4。
# train_reid.py 核心片段 from torchvision import models import torch.nn as nn class ReidModel(nn.Module): def __init__(self, num_classes, feat_dim=512): super().__init__() backbone = models.resnet50(pretrained=True) backbone.fc = nn.Identity() self.backbone = backbone self.feat_fc = nn.Linear(2048, feat_dim) self.classifier = nn.Linear(feat_dim, num_classes) def forward(self, x): feat = self.backbone(x) feat = self.feat_fc(feat) if self.training: cls = self.classifier(feat) return feat, cls return feat这段代码定义了一个最简的 REID 网络:去掉 ResNet50 原分类头后接一层全连接压缩到 512 维,训练时同时接分类器,推理时只返回特征。num_classes是训练集中车辆 ID 总数。feat_dim按数据集规模选择,小型数据集 256 维即可,车辆场景建议 512 维,维度太高在小样本上容易过拟合。当训练集超过 1000 个 ID 时,可考虑更深层并在ImageNet预训练模型上加载。
损失函数部分采用分类损失与三元组损失的加权组合,权重取 1:1。实现在 PyTorch 中可以分别用torch.nn.CrossEntropyLoss与自定义的 TripletLoss,每批次内动态构造三元组。训练后期把三元组损失的 margin 从 0.3 降到 0.1,特征间的判别边界更紧凑。
REID 模型训练使用 Adam 优化器,初始学习率设置为 3e-4,每 40 个 epoch 衰减到原来的十分之一,总训练 80 到 120 轮。验证时每个 epoch 结束后计算 query 与 gallery 的余弦相似度矩阵,得出 Rank-1 准确率。如果连续 10 个 epoch Rank-1 不上升,说明模型已经过拟合或数据量不足,此时应增加数据增强或降低学习率,而不是继续累加训练轮数。
5. 避坑:车辆重识别最容易翻车的几个环节
5.1 现象:换一个摄像头,同一辆车的匹配分数骤降
同一辆车在摄像头 A 和摄像头 B 下的外观差异可能大到让人怀疑特征提取是不是写错了,原因集中在两方面:一是不同摄像头的白平衡与画幅比例不同,二是 REID 训练数据里这种跨镜样本不够。
解决方法是做跨摄像头数据增强。训练 REID 模型时,对输入图像依次做随机亮度扰动、随机自动白平衡模拟、随机裁剪缩放,并在每批次中强制保证同一车辆 ID 至少来自两个不同摄像头。只依赖颜色直方图或原始像素差异的匹配注定失效,特征向量必须对光照不敏感。另一个常见做法是改用SwooshNet等引入多尺度特征融合的 REID 模型,但我个人认为数据增强是零成本的第一步。
5.2 现象:YOLOv5 检测框漂移到车身背景上
车辆检测框抖动或漂移到背景上,是检测与 REID 串联流水线中损失最大的问题。检测框含背景比例过高,REID 输入图的有效车身面积变小,特征表达被背景信息污染;连续帧之间的框抖动也会让特征向量不稳定。
根因往往是训练数据只有标准正视角车辆图,缺少俯视、侧视、遮挡和部分裁剪的样本。补充数据时应特意截取车头正面、侧面、斜 45 度等视角,并用随机擦除增强来模拟路牌、树木的遮挡。如果训练数据难以扩充,则通过推理时的外扩逻辑缓解,将 YOLOv5 输出框的四边各扩 10 到 15 像素后再送入 REID,能显著减小框漂移带来的特征抖动。
5.3 现象:同色同款的白色 SUV 总是被匹配成同一辆
在车辆重识别中,颜色和车型是最容易分辨的特征,也是最容易导致误匹配的“陷阱”。同色同款的两辆车在不同摄像头画面中外观几乎一致,REID 模型如果不关注车牌、饰条、贴纸等局部细节,就会把它们当作同一目标。
解决思路有两个层次:训练侧,难样本挖掘要起作用,Triplet Loss 中的负样本应选择同类颜色同车型的样本,迫使模型学习局部差异。数据侧,增加车辆前方的车灯轮廓、前挡风玻璃贴膜痕迹、车顶行李架等显著性区域的标注样本。推理层可以增加关键点检测,例如提取车灯、后视镜等局部区域特征做二次校验,但这一步在毕业设计里并非必需。
5.4 现象:显存不足,REID 训练直接 OOM
训练车辆 REID 模型时,当你把 batch-size 设为 32 但显存只有 6GB,大概率会在第一个 iteration 就报 CUDA out of memory。
解决方式按优先级排列:先把 batch-size 降到 8 或 4,同时减小输入图片分辨率到 256×128,这是 REID 常见的标准尺寸,能在显存和精度之间取得较好的平衡。仍不足时,用梯度累积模拟大 batch 的效果,每 4 个 step 更新一次权重。训练时关闭验证阶段的梯度计算,用torch.no_grad()包住验证推理部分,能再省到一部分显存。
# OOM 场景下的参数兜底 train_loader = torch.utils.data.DataLoader(train_set, batch_size=8, shuffle=True) optimizer = torch.optim.Adam(model.parameters(), lr=3e-4) for step, (imgs, ids) in enumerate(train_loader): imgs, ids = imgs.cuda(), ids.cuda() feat, cls = model(imgs) loss = criterion(feat, cls, ids) loss.backward() if (step + 1) % 4 == 0: optimizer.step() optimizer.zero_grad()这个片段里每 4 步才执行一次优化器更新,等价于 batch-size 32 的效果,但峰值显存占用只相当于 batch-size 8。注意梯度累积不能与 BatchNorm 完全等效,如果骨干网络引入了 BatchNorm 层,蒸馏到 256×128 输入时统计数据的稳定性有所下降,因此 low-level 层尽量使用预训练组的统计量,或者切换为 GroupNorm。
6. 验证模型与进阶玩法:从单模型演示到跨镜匹配演示
完成训练后,不要只拿一张样本图跑一遍就认为系统完成了,还要做一个可量化的评估指标展示,建议在 500 张 query 图和 3000 张 gallery 图上实现。每台摄像头的光照和角度变化都要覆盖,最终计算 Rank-1 与 mAP 两个指标,把输出结果做成检索可视化,这样的演示效果会远强于单次匹配。
一个实用的可视化方式是构造 2×5 的画布,第一列为 query 图,后四列为按相似度从高到低排列的 gallery 结果,被正确匹配的绿色框标注,错误的用红色框标出。这个图在答辩时放在论文实验结果里,能直观展示 REID 在跨视角场景下的判别能力。
# 检索结果可视化简化实现 import torch import matplotlib.pyplot as plt query_feat = model(query_img).detach() gallery_feats = torch.cat([model(g).detach() for g in gallery_imgs]) sims = torch.matmul(query_feat, gallery_feats.T) topk = torch.topk(sims, k=5).indices.tolist()此段代码中,query 的特征与 gallery 特征矩阵做点积得到相似度序列。必须保证 query 和 gallery 的特征都经过 L2 归一化,否则点积结果不等价于余弦相似度。topk 输出选出的索引按相似度降序排列,画图时按索引从 gallery 中取图。毕业设计阶段,这个可视化工件通常可以省略训练细节,但不可省略归一化。
从工程项目的角度,若时间富余,还可以把最后的相似度检索从单图升级为多帧特征平均:对同一辆车取 5 帧检测结果求平均向量再进行检索。该方法对场景光照变化的鲁棒性有明显提升,而代价仅是推理时间增加 5 倍,对静态测试而言完全可以接受。我习惯把这一步作为最终演示版本,因为它能有效掩盖单帧检测框漂移产生的异常特征。
在手头算力有限的情况下,也可以直接使用 torch.hub 加载公开的 YOLOv5 预训练模型,实现车辆检测部分;REID 部分则用压缩包内训练好的模型权重读取即可。但若要写进毕业论文,建议至少用自有数据微调检测层和 REID 全连接层,否则“基于”二字容易在答辩时被追问出漏洞。
最终检查项是:检查模型权重路径、检查 query 与 gallery 是否存在重叠图片、检查特征向量是否归一化。这三项是我在多次实验后总结出的必要动作,任何一个缺失都会直接导致评测结果失真。跨镜检索这类场景,数据协议稍有问题,再好的网络也挽回不了错误结论。希望这份从原理到落地的完整拆解,能帮你在毕业设计里少走弯路,把 YOLOv5 与 REID 的组合做成真正讲得清、扛得住追问的系统。
本文还有配套的精品资源,点击获取