ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

VeRi数据集全解析:从车辆ReID到YOLO格式转换

VeRi数据集全解析:从车辆ReID到YOLO格式转换 简介VeRi数据集是专为车辆识别设计的大型公开数据集面向计算机视觉、深度学习的算法工程师与科研人员适用于车辆检测、分类、重识别及跨摄像头检索等任务。资源为zip压缩包下载页显示文件总数2000解压后含51036张JPG图像以及12个TXT、2个XML标注文件整体约945.51MB。图像多来自真实监控场景覆盖不同车型、颜色、角度、光照和距离可帮助模型适应复杂环境TXT文件提供类别与唯一标识等标签XML文件保存边界框定位信息便于直接用于目标检测与度量学习。配合PyTorch或TensorFlow框架及ResNet、YOLO等模型能够完成数据增强、迁移学习到性能评估的完整流程适合毕业设计、论文实验及智能交通项目预研。目前已有4208人学习下载是入门车辆识别领域值得参考的基准数据集。 搞车辆视觉方向的同学十有八九绕不开这个zip包的名字VeRi.zip。第一次看到它我以为就是个普通的压缩文件解压完才发现里面装的是车辆重识别Vehicle Re-Identification领域最经典的监控场景数据集。在论文和代码仓库里它一般被写成 VeRi 或 VeRi-776全称就是 Vehicles 识别数据集和 VehicleID、VeRi-Wild 一起是车辆检索方向绕不开的三大基准之一。这个数据集好在哪它不是在封闭场地摆拍的干净图而是来自真实城市监控网络的多摄像头视频帧。车是同一辆车但出现在不同摄像头、不同时间、不同角度、不同光照下你要让模型学会“认出这是同一辆车”。这种跨镜头的身份关联能力就是 ReID 的核心问题也是智慧交通、安防布控、停车管理这类场景的底层需求。如果你最近正愁没有合适的数据集来跑通目标检测、车辆检索或者多任务学习的流程这个压缩包确实值得花一下午折腾。这篇文章我会从数据集的定位、文件结构、目录整理、ReID 训练、YOLO 格式转换一路讲到实际踩坑尽量让刚接触的人少走弯路也让已经在跑实验的人能对照着优化自己的流程。1. VeRi 数据集到底是个什么定位、规模与文件结构1.1 从数据角度看 VeRi规模、标注和采集方式VeRi 数据集由北京邮电大学的研究团队发布采集自真实城市监控网络覆盖 20 个左右的摄像头拍摄时间跨度覆盖白天和夜间。整个数据集包含 776 辆车的图像总计超过 5 万张。这里有个细节值得注意它不是把每辆车单独拍几张照片了事而是为每辆车提供了跨摄像头、跨时间段的连续轨迹片段也就是 tracklet。这意味着什么同一辆车在摄像头 A 里是正脸在摄像头 B 里变成屁股到了摄像头 C 可能只有侧后方再加上光照变化、遮挡、雨天反光、行人干扰整个数据分布非常接近真实监控环境。模型要学到的不是“这张图长什么样”而是“这辆车在这些形态变化下仍然属于同一个身份”。标注信息也是个大亮点。每个车辆的标注除了身份 ID还包括车型sedan、SUV、van 等、颜色black、white、silver 等甚至保留了车牌区域信息部分版本。每个 bounding box 都带有摄像头 ID 和时间戳方便研究者做跨摄像头时空分析。这种结构让 VeRi 不仅能做 ReID还能扩展出车型分类、颜色识别、时空重排序等任务。1.2 VeRi 能用来做什么不只是 ReID很多人一提 VeRi 就只知道车辆重识别实际上把它用好的话能拆出好几件事车辆 ReID 基准评测训练集测试集querygallery 的划分非常规范可以直接用来跑 mAP 和 CMC 曲线和论文对比结果。车型分类与颜色识别标注里自带车型和颜色标签做多任务学习很方便一个 backbone 同时输出 ID、车型、颜色。跨摄像头轨迹关联由于每个框都带 CameraID 和 Timestamp可以研究时空约束、轨迹重排序这类更上层的算法。目标检测适配虽然它本身不是检测数据集但标注里带了 bbox转成 YOLO 格式做单类或多类检测完全可行很多初学 YOLO 的人就把它当练手数据。适合谁来用如果你是做行人 ReID 的想平移经验到车辆域VeRi 是首选如果你在捣鼓 YOLOv5/YOLOv8 训练流程需要一个带规范标注的数据集来验证VeRi 转格式后也能直接用如果你想研究监控场景下的细粒度车辆理解这个数据集的多属性标注价值更高。1.3 同门对比VeRi、VeRi-Wild 与 VehicleID 怎么选名字相近的数据集很容易让人搞混我当初就把 VeRi 和 VeRi-Wild 当成同一个结果训练脚本的路径都写错了。这里直接放个对比表数据集车辆数图像规模摄像头数采集环境主要特点VeRi (VeRi-776)776 辆约 5 万张约 20 个城市监控网络标注丰富含车型颜色和时空信息VeRi-Wild约 4 万辆约 40 万张多个跨度更大真实监控更开放规模大更贴近真实开放场景VehicleID约 2.6 万辆约 22 万张多个短时摄像头监控短片切帧短时间段内跨摄像头检索选哪个取决于需求想快速跑通实验、验证想法、对比论文结果VeRi 最合适因为它的规模、类别数、评测协议都很清晰。想冲高难度、看模型在大规模数据上的泛化能力上 VeRi-Wild。想专门研究短时间窗口内的车辆检索选 VehicleID。我个人的做法是先在 VeRi 上调通 pipeline再往 VeRi-Wild 上迁移省时省力。2. 下载、解压与目录整理正式开工前的一小时2.1 拿到压缩包后第一步校验与解压VeRi.zip 可以从官网或 GitHub 上相关仓库的 README 里找到下载链接通常是一个几百 MB 的 zip。下载完成后别急着解压先算一下 MD5 或 SHA 值和发布方提供的校验值比对。这一步很关键监控数据集文件大传输过程中容易出问题解压到一半报“文件损坏”才去查就晚了。# Linux/macOS 下校验 sha256sum VeRi.zip # 校验通过后解压 unzip VeRi.zip -d VeRiWindows 下用 7-Zip 或者 WinRAR 都行但注意解压路径里不要带中文也不要带空格。VeRi 提供的标注 XML 里用的是相对路径如果你把整个项目放在C:\Users\张三\我的数据集\VeRi后面 Python 脚本处理路径时大概率会踩编码的坑。我的习惯是统一放D:\datasets\VeRi这种纯英文路径。2.2 标注文件与图像文件名的解析规则解压后里面主要是图像文件夹和 XML 标注文件。图像文件名长这样0001_c001_00019630_0.jpg拆开来看0001是车辆 IDc001是摄像头 ID00019630是时间戳帧号末尾的0是帧状态标志。不同版本对这个状态的解释略有差异有的表示“是否模糊”有的表示“是否为关键帧”所以用之前抽样看几张图判断一下别默认它就是某种含义。XML 标注文件的核心结构大致如下vehicle VehicleID0001/VehicleID VehicleModelSedan/VehicleModel VehicleColorBlack/VehicleColor CameraID001/CameraID TimeStamp00019630/TimeStamp FrameID0/FrameID bndbox x150/x y200/y width120/width height180/height /bndbox /vehicle每个Vehicle节点代表一辆车节点内记录车型、颜色、摄像头、时间戳以及这一帧里车辆在图像中的 bounding box 坐标。这个 bbox 坐标是像素单位的绝对坐标转换到 YOLO 格式时要做归一化处理后面会详细讲。注意不同镜像站或二次打包的 VeRi 版本XML 字段名可能略有差别。写脚本之前先打印一个 XML 文件看看实际字段别拿旧代码硬套。2.3 构建标准目录结构训练、查询、检索库的划分VeRi 官方在论文里给出了评测协议但 zip 包里的原始目录结构不一定直接就是 train/query/gallery 三个文件夹有些版本放的是image_train、image_test、image_query有些是直接按像素级图像文件平铺。现实里更常见的情况是你得按照官方提供的 train/test split 文件自己把数据整理成下面的标准结构VeRi/ ├── images/ │ ├── 0001_c001_00019630_0.jpg │ ├── 0001_c002_00024000_0.jpg │ └── ... ├── train_label.xml ├── test_label.xml ├── query_label.xml ├── train_split.txt ├── test_split.txt ├── query_split.txt └── gallery_split.txt处理方式也不难读 split 文件里的图像名把对应的图片复制或软链到新目录。Python 脚本几分钟就能搞定import shutil from pathlib import Path src Path(VeRi/images) dst Path(VeRi/standard) dst.mkdir(exist_okTrue) for split in [train, query, gallery]: (dst / split).mkdir(exist_okTrue) split_file Path(fVeRi/{split}_split.txt) for line in split_file.read_text().strip().splitlines(): img_name line.strip() shutil.copy(src / img_name, dst / split / img_name)整理完目录后建议顺手抽几组图拼一张 collage看看同一辆车在不同摄像头下长什么样。这个步骤不花多少时间但能帮你直观理解任务难度也方便后面做可视化时对比。3. 核心实操一用 VeRi 训练车辆 ReID 模型3.1 预处理与数据加载从图像列表到训练批次ReID 模型训练和普通分类不太一样核心是“批次内采样”的逻辑。常规做法是每次采样 P 个身份每个身份采样 K 张图也就是 P×K 的 batch。假如 P8、K4一个 batch 就有 32 张图其中每个身份有 4 张样本。这样设计的目的是让模型在一个 batch 里同时看到“类间差异”和“类内差异”从而更高效地学习判别性特征。数据加载器实现上我用的是 PyTorch 自带的Dataset和DataLoader关键点在于重写__getitem__时要保证每次迭代返回一个完整 batch 的索引而不是一张图。通常的做法是class VeRiDataset(Dataset): def __init__(self, img_paths, labels, transformNone): self.img_paths img_paths self.labels labels self.transform transform def __len__(self): return len(self.img_paths) def __getitem__(self, idx): img Image.open(self.img_paths[idx]).convert(RGB) if self.transform: img self.transform(img) return img, self.labels[idx]注意这里我故意没写采样逻辑因为实际使用时我用的是RandomIdentitySampler这个类它负责在迭代时按 P×K 策略生成索引列表。图像尺寸我习惯用 256×256 或者 288×288。监控场景下的车辆框比例不太固定直接 resize 会拉伸变形但 ReID 模型通常能适应这种几何变换问题不大。数据增强方面RandomHorizontalFlip、ColorJitter、RandomErasing是标配后两者对光照变化和遮挡特别有效。3.2 模型与损失函数ResNet50 为底ID Loss 加 Triplet LossReID 的模型结构一般分两块backbone 提取特征head 输出特征向量和分类 logits。以 ResNet50 为例把最后一层全连接层去掉得到 2048 维的全局特征再接一个 BNNeck也就是先接一个 BatchNorm1d 层再接分类层。BNNeck 有什么作用它能把特征分布归一化让分类损失和度量学习损失在优化目标上不冲突。损失函数是 ReID 训练的灵魂。我用的是常见的组合交叉熵作为 ID Loss加上 Triplet Loss。交叉熵让模型学会区分不同车辆 IDTriplet Loss 则强制让同一个 ID 的特征距离更近、不同 ID 的特征距离更远。损失写成loss nn.CrossEntropyLoss()(logits, labels) triplet_loss(features, labels)Triplet Loss 的实现我会直接用torchreid里的版本margin 设为 0.3。为什么不自己写因为 Triplet Loss 的采样策略很讲究简单随机采样会导致大量 easy tripletloss 很快刷到很低但特征没什么判别力。用 batch hard 采样也就是在每个 batch 里挑最难的 positive 和 negative效果会好很多。这一块用成熟框架实现更稳。训练配置方面初始学习率我常用 3.5e-4优化器用 Adamcosine 退火调度或者在第 40、70 个 epoch 降学习率都行。VeRi 数据规模不大80 到 120 个 epoch 基本就能收敛。单卡 2080Ti 或 3060 上跑一个实验大概一到两小时性价比很高。3.3 评测协议mAP 和 CMC Top-1 到底怎么算评测是 ReID 最容易搞错的地方。VeRi 的标准评测分 query 集和 gallery 集query 里是我们要检索的车辆图片gallery 是候选库。算法提取每张图的特征计算两两之间的余弦距离或欧氏距离按距离从小到大排序然后算两个指标CMC Top-1在 query 的每个样本结果里第一个命中正确 ID 的概率。mAP对每个 query按排序结果计算 Average Precision再对所有 query 取平均。mAP 考虑的不只是 Top-1 准不准而是正确结果在排序中的整体位置。这里有个隐藏的坑gallery 集合里可能包含同一辆车的多张图计算 CMC 时如果第一张命中的是正确 ID 就算对但算 mAP 时要小心同一个 ID 的所有正确匹配都要参与计算不能只看第一张。另外VeRi 的原始评测协议里query 中的图像也可能出现在 gallery 里这就涉及 query 与 gallery 的“互斥”问题很多论文里都会提一句去除 query 本身否则 mAP 会被虚高。我用过的评测代码是torchreid里的softmax评估函数它返回 CMC 和 mAP。如果自己实现流程大概是这样def evaluate(distmat, query_ids, gallery_ids): m, n distmat.shape ap np.zeros(m) cmc np.zeros((m, max(gallery_ids) 1)) for i in range(m): order np.argsort(distmat[i]) matches (gallery_ids[order] query_ids[i]).astype(int) # 计算 AP 和 CMC return cmc.mean(0), ap.mean()细节很多所以我强烈建议直接用现成评测库自己写很容易在“去掉 query 自身”这一步上漏掉样本最后结果一对比发现莫名其妙多出几个点。4. 核心实操二把 VeRi 改造成目标检测/属性识别数据集4.1 从 ReID 标注到 YOLO 格式的转换很多人做目标检测会满世界找 VOC 或 COCO 数据集其实 VeRi 自带 bbox完全可以转换成 YOLO 格式练手。YOLO 需要的标注是每个图片对应一个 txt内容格式是class_id x_center y_center width height其中 x_center、y_center、width、height 都是相对于图片宽度和高度的归一化值。VeRi 的 XML 里给的是绝对像素坐标所以转换脚本要做的就是把 bbox 从 (x, y, w, h) 转成中心点坐标再除以图像尺寸。import xml.etree.ElementTree as ET def convert_annotation(xml_path, img_w, img_h, out_txt): tree ET.parse(xml_path) root tree.getroot() lines [] for vehicle in root.iter(vehicle): # 这里假设 XML 中的 bndbox 是 vehicle 的子节点 bbox vehicle.find(bndbox) x float(bbox.find(x).text) y float(bbox.find(y).text) w float(bbox.find(width).text) h float(bbox.find(height).text) # 转归一化中心点坐标 x_center (x w / 2) / img_w y_center (y h / 2) / img_h # 类别可以统一为 0也可以按车型映射 cls_id 0 lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w / img_w:.6f} {h / img_h:.6f}) with open(out_txt, w) as f: f.write(\n.join(lines))转换完成后用 YOLOv5 或 YOLOv8 的datasets目录组织方式建一个images和labels两个文件夹并写一个data.yamltrain: /path/to/VeRi_yolo/images/train val: /path/to/VeRi_yolo/images/val nc: 1 names: [vehicle]这里有个细节VeRi 的 bbox 基本上是紧贴车身的矩形但监控视角下会有车牌区域、后视镜等细碎部件如果你不打算做细粒度识别把类别统一成vehicle就行。想玩多类检测就把车型字段映射成 3 到 5 个类别这样更贴合实际场景。4.2 属性标签的利用颜色和车型分类除了检测VeRi 的车型和颜色属性非常适合做多任务学习或者属性识别。实际工程里很多场景并不需要知道具体是哪辆车但需要快速知道“来了一辆白色 SUV”或者“红色轿车停留超过 2 分钟”这类结构化信息在智慧停车、物业安防里非常常见。利用方式也很简单把模型改成多分支头backbone 提取特征后分别接一个颜色分类头和一个车型分类头。标签直接从 XML 里读color_map {black: 0, white: 1, silver: 2, blue: 3, red: 4, green: 5, yellow: 6, gray: 7} model_map {sedan: 0, suv: 1, van: 2, bus: 3, truck: 4}损失函数设置成多任务加权loss ce_id 0.5 * ce_color 0.5 * ce_model为什么要加权因为 ID 分类任务的数据分布更均衡而颜色和车型类别分布不均权重太大容易干扰 ReID 主任务。我实测下来 0.5 的权重比较稳你可以根据自己的 batch 和任务重点调。4.3 数据增强与多任务训练注意事项属性识别任务里颜色特征很容易被光照影响。同一个黑色车在强光下可能看起来像深蓝色这时ColorJitter增强就很重要它会模拟不同光照条件让模型学到颜色本身而不是绝对像素值。另外对车辆图像做随机裁剪时要小心别把车身的颜色区域裁没了避免颜色分类头失去关键信息。多任务训练时还有一个容易被忽略的点不同任务的学习难度不同收敛速度也不同。ID 分类头可能 30 个 epoch 就收敛了颜色分类头可能还在波动。我建议用一个简单的动态加权策略或者干脆固定权重记录每个分支的 loss 曲线如果某个分支长期不降优先检查是否标签错了而不是调权重。5. 常见问题与排查技巧实录5.1 解压和路径相关的坑VeRi.zip 解压时如果遇到“文件 CRC 失败”大概率是下载不完整。重下太费时间可以先试试unzip -t VeRi.zip检查哪些文件损坏用支持恢复的下载工具重新拉取。路径问题同样值得提一下XML 和图片名之间是相对的脚本里尽量用pathlib.Path而不是字符串拼接否则 Windows 和 Linux 环境切换时你会被反斜杠折磨到怀疑人生。5.2 训练时类别不平衡怎么处理VeRi 里不同车辆的样本数差异很大有的车在监控里过境快只有 10 来张图有的车停在一个摄像头前不动有上百张。ReID 训练通常不会做重采样而是依赖 P×K 采样保证每个 batch 里身份均衡所以问题不大。但如果你把 VeRi 拿来做分类比如按车辆 ID 分类那就要考虑样本均衡问题了。最简单的做法是限制每个类别的最大样本数或者用加权采样让大类不压过小类。5.3 评测不严谨导致虚高结果我最开始跑 VeRi 的时候mAP 直接冲到 92%比很多论文还高后来一查发现评测代码没有把 query 在 gallery 中对应的同一张图去掉。VeRi 官方协议里query 和 gallery 的划分是重叠的必须把 query 里每张图在 gallery 中的自身样本从结果里剔除再算 CMC 和 mAP。否则这个结果连参考价值都没有。5.4 常见问题速查表症状可能原因解决办法解压文件 CRC 失效下载不完整重下或用工具修复损坏 zipPython 找不到图片路径路径含中文或反斜杠统一用英文路径和 Path 对象训练 loss 不降学习率太高或标签错乱调低 lr抽查 XML 和图像对应关系mAP 过高评测时未排除 query 自身按 official protocol 过滤 gallery多任务中颜色分支不准ColorJitter 过强或标签错减弱增强幅度人工校验少量样本转 YOLO 后框偏移bbox 转归一化时未除以图片尺寸检查 img_w/img_h 是否取的是实际尺寸写在最后的实际操作体会VeRi 这个数据集好上手但很容易让人低估它的难度。我一开始以为跨摄像头车辆识别跟图像检索差不多结果被同一个 ID 在不同摄像头下的形变打脸。后来我发现一个特别实用的习惯每迭代一轮实验就把模型的 Top-1 检索结果拼成一张网格图左侧是 query右侧是排序结果。你不需要看指标就能一眼判断模型是在学轮胎、车灯还是车顶的行李架。这个习惯让我少走了很多弯路强烈建议你也试试。另外一个个人建议VeRi 虽然经典但它的数据规模在今天看来不算大。别只守着它一个数据集后续可以往 VeRi-Wild 上迁移或者自己用道路监控视频抽帧按同样的 tracklet 和属性标注方式建一套数据。毕竟真实业务的图像分布永远比你手里的公开数据集更野。本文还有配套的精品资源点击获取
返回列表