简介:这是一份面向YOLO系列算法学习者的车辆检测与计数目标检测数据集,适用于yolov5、yolov8、yolov9、yolov7、yolov10及yolo11等主流框架,可直接用于模型训练与验证测试。数据集共1304张图像,覆盖汽车、摩托车、公共汽车、卡车四类目标,已完成训练集与验证集划分,并附带data.yaml配置文件,开箱即可投入实验。压缩包内共2000个文件,包含911个txt格式的YOLO标注与1089个xml格式的VOC标注,两种标签分别存放于独立文件夹,便于按需选用;YOLO标签采用归一化中心点与宽高比例,符合标准训练输入要求。资源包整体约44.02MB,体积轻量,适合快速搭建车辆检测与计数实验环境。目前已有88人学习下载,可作为课程设计、毕业设计或算法入门的实践素材,帮助读者省去数据采集与标注环节,将精力集中在模型调参与效果对比上。
1. 1304 张图、双标签格式:这份车辆检测数据集到底能不能直接开训
如果你正在找一份能直接跑 YOLO 车辆检测的数据集,大概率已经翻过不少仓库:要么只有图片没标签,要么标签格式对不上自己的训练脚本,要么类别定义混乱,光整理就要耗掉一整天。这份 1304 张图像的车辆检测与计数数据集,核心价值就在于「开箱即训」——图片、YOLO 格式 txt 标签、VOC 格式 xml 标签、data.yaml 配置文件全部齐活,类别覆盖汽车、摩托车、公共汽车、卡车四类道路常见目标。
它适合三类人:一是刚接触 yolo 系列算法、想拿一份干净数据跑通训练全流程的新手;二是需要快速验证模型改动效果、不想在数据清洗上浪费时间的熟手;三是做车辆计数、交通流量统计这类落地项目、需要一份结构规整的基线数据的开发者。数据集已经划分好训练集与验证集,标签坐标采用归一化格式,兼容 yolov5、yolov8、yolov9、yolov7、yolov10 以及 yolo11 等主流版本。下面我从目录结构、标签格式、训练配置到踩坑排查,把这份资源拆开讲清楚。
2. 目录结构与双标签格式:先搞懂每份文件是干什么的
2.1 拿到压缩包后先看什么
解压之后,你看到的不是一堆散图,而是一套有组织的目录。常见做法是 images 文件夹放图像,labels 文件夹放同名的 txt 标签,另外单独有一个 xml 文件夹存 VOC 格式标注,根目录下放 data.yaml。图像和标签必须同名,只是扩展名不同——比如 img_050_518.jpg 对应 img_050_518.txt,这是 YOLO 训练时能自动匹配的前提。
项目正文里列出的 img_050_518.txt、img_050_715.txt 这些文件名,就是标签文件的命名样例。命名规则本身不重要,重要的是「图像名 = 标签名」这个约束。如果你后续自己增补图片,务必保持这个对应关系,否则训练时会出现「找到图片但找不到标签」的警告,那些图会被直接跳过。
data.yaml 是整个数据集的入口配置文件,它告诉训练脚本三件事:训练集和验证集的路径、类别数量、类别名称。这份数据集已经帮你写好了,但路径部分通常需要根据你实际解压位置做一次修改。很多人第一次跑失败,就是栽在这个路径上。
2.2 YOLO 格式标签逐字段拆解
YOLO 格式的 txt 标签每行代表一个目标框,格式是:
<class> <x_center> <y_center> <width> <height>五个字段用空格分隔。<class>是类别索引,从 0 开始。这份数据集四类目标的索引通常是:0 对应汽车,1 对应摩托车,2 对应公共汽车,3 对应卡车。具体顺序以 data.yaml 里的 names 列表为准,不要凭记忆写。
后面四个值是归一化坐标,范围 0 到 1。<x_center>和<y_center>是框中心点相对于图像宽高的比例,<width>和<height>是框宽高相对于图像宽高的比例。举个例子,一张 640×480 的图,某个框中心在像素 (320, 240),宽 128、高 96,那么标签就是:
0 0.5 0.5 0.2 0.2这里 320/640=0.5,240/480=0.5,128/640=0.2,96/480=0.2。归一化的好处是图像缩放后标签不用改,这也是 YOLO 系列一直用这种格式的原因。
注意:归一化坐标如果出现大于 1 或小于 0 的值,说明标注越界了。训练前建议扫一遍,越界框会导致损失计算异常。
2.3 VOC 格式 xml 与 YOLO 格式的对应关系
xml 文件夹里是 VOC 格式标注,结构是标准的<annotation>根节点,下面有<filename>、<size>(宽高通道)、以及若干<object>,每个 object 里有<name>类别名和<bndbox>像素坐标(xmin、ymin、xmax、ymax)。
两种格式的区别很直接:VOC 用像素绝对坐标,YOLO 用归一化中心点加宽高。如果你用的是 YOLOv5 及以上版本,训练直接吃 txt;如果你要用某些只认 VOC 的评估脚本,或者想转 COCO 格式,xml 就派上用场了。常见做法是保留两套,训练用 txt,做可视化核对或跨框架迁移时用 xml 反查。
下面这段脚本可以把 VOC 的 xml 批量转成 YOLO 的 txt,方便你核对两套标签是否一致:
import os import xml.etree.ElementTree as ET # 类别名到索引的映射,必须和 data.yaml 里的 names 顺序一致 class_map = {"car": 0, "motorcycle": 1, "bus": 2, "truck": 3} def voc_to_yolo(xml_path, out_path): tree = ET.parse(xml_path) root = tree.getroot() size = root.find("size") w = int(size.find("width").text) h = int(size.find("height").text) lines = [] for obj in root.iter("object"): name = obj.find("name").text if name not in class_map: continue cls_id = class_map[name] box = obj.find("bndbox") xmin = float(box.find("xmin").text) ymin = float(box.find("ymin").text) xmax = float(box.find("xmax").text) ymax = float(box.find("ymax").text) # 转成归一化的中心点加宽高 x_center = (xmin + xmax) / 2.0 / w y_center = (ymin + ymax) / 2.0 / h bw = (xmax - xmin) / w bh = (ymax - ymin) / h lines.append(f"{cls_id} {x_center:.6f} {y_center:.6f} {bw:.6f} {bh:.6f}") with open(out_path, "w") as f: f.write("\n".join(lines)) # 批量处理 xml_dir = "xml" out_dir = "labels_check" os.makedirs(out_dir, exist_ok=True) for fn in os.listdir(xml_dir): if fn.endswith(".xml"): voc_to_yolo(os.path.join(xml_dir, fn), os.path.join(out_dir, fn.replace(".xml", ".txt")))逻辑说明:先读 xml 里的图像宽高,再把每个 object 的像素框转成归一化中心点加宽高。参数上,class_map必须和 data.yaml 的 names 严格对应,顺序错了类别就全乱。:.6f保留六位小数,精度足够。跑完拿生成的 txt 和数据集自带的 txt 对比,如果基本一致,说明两套标签是同步的;如果差异大,就要查是哪套标注有问题。
3. data.yaml 配置与训练启动:从改路径到跑通第一个 epoch
3.1 data.yaml 里每个字段怎么改
data.yaml 是训练脚本读取数据集的唯一入口。典型内容长这样:
train: ../train/images val: ../valid/images nc: 4 names: ['car', 'motorcycle', 'bus', 'truck']train和val是训练集、验证集图像目录的相对路径,相对于 data.yaml 所在位置。如果你把数据集挪了地方,这两行必须改,否则脚本找不到图。nc是类别数,这份数据集是 4。names是类别名列表,顺序决定类别索引,不能随意调换。
常见做法是把 data.yaml 里的路径写成绝对路径,省得相对路径算错。比如train: /home/user/dataset/train/images。虽然不够优雅,但能避免一大类「路径找不到」的报错。
提示:改完 data.yaml 后,先用几行 Python 验证路径是否存在,比直接开训再报错省时间。
3.2 用 YOLOv8 跑通训练的最小命令
假设你已经装好 ultralytics 包,训练命令就一行:
yolo detect train data=data.yaml model=yolov8n.pt epochs=100 imgsz=640 batch=16参数逐个说:data指向你的 yaml;model用预训练的 yolov8n.pt,小模型收敛快,适合先验证数据没问题;epochs=100是训练轮数,1304 张图这个量级,100 轮通常够看趋势;imgsz=640是输入分辨率,和大多数预训练权重匹配;batch=16看显存,显存小就降到 8 或 4。
如果你用的是 YOLOv5,命令结构类似,只是入口换成python train.py --data data.yaml --weights yolov5n.pt --epochs 100 --img 640 --batch 16。YOLOv9、v10、yolo11 的调用方式各有差异,但 data.yaml 的格式是通用的,这也是这份数据集兼容多版本的原因。
训练启动后,重点看第一轮输出里的几行:train: Scanning...后面跟的图像数量和标签数量。如果标签数量明显少于图像数量,说明有图没匹配到标签,回去查命名。val: Scanning...同理。这两个数字对不上,后面指标全是虚的。
3.3 训练中该盯哪些指标
跑起来之后,终端会打印每个 epoch 的 box_loss、cls_loss、dfl_loss 和 mAP。box_loss 是框回归损失,cls_loss 是分类损失,这两个应该整体下降。mAP50 和 mAP50-95 是精度指标,前者宽松后者严格,正常情况随训练上升。
1304 张图、四个类别,如果标注质量过关,mAP50 跑到 0.8 以上是合理预期。如果卡在 0.3 以下不动,先别怀疑模型,八成是标签或路径有问题。常见做法是拿训练完的模型在验证集上跑一次预测,把预测框画出来和原图对比,肉眼一看就知道是漏标还是错标。
yolo detect predict model=runs/detect/train/weights/best.pt source=valid/images save=True这条命令会把验证集图像的预测结果存下来,直接看可视化输出,比盯数字直观得多。
4. 避坑与排查:五条血泪经验
4.1 现象:训练报「No labels found」
原因:图像目录和标签目录没对应上,或者 data.yaml 里的路径指向了错误层级。YOLO 默认在 images 同级找 labels,如果你的目录结构是train/images和train/labels,它自己能推出来;但如果标签放在别处,就找不到。
解决:确认 images 和 labels 是同级目录且文件名一一对应。如果结构特殊,在 data.yaml 里显式写清楚,或者用脚本先做一次文件名匹配检查。
4.2 现象:类别全预测成同一类
原因:data.yaml 里 names 的顺序和标签里 class 索引的实际含义不一致。比如标签里 0 是 car,但 names 第一个写成了 bus,模型学出来的类别就整体错位。
解决:抽几个标签文件,看 class 索引分布,再对照 names 列表。必要时用脚本统计每个索引出现的次数,确认四类都有样本且索引合理。
4.3 现象:mAP 一直很低,loss 不降
原因:归一化坐标越界,或者存在大量空标签文件。空标签意味着这张图没有目标,但 YOLO 会把它当负样本,少量可以,大量会拖垮训练。
解决:扫一遍标签,统计空文件数量和坐标越界数量。空文件过多就考虑剔除对应图像,越界框要么修正要么删掉。
4.4 现象:显存爆了,batch 调小还是报错
原因:imgsz 设太大,或者用了大模型。1304 张图不算多,但 640 以上分辨率配 yolov8l 这类大模型,显存吃紧很正常。
解决:先把 imgsz 降到 416 或 320 跑通流程,确认数据没问题后再往上调。模型先用 n 或 s 版本,效果稳定了再换大的。
4.5 现象:验证集指标好,实际预测一塌糊涂
原因:训练集和验证集划分不合理,或者验证集图像和训练集高度相似,导致指标虚高。
解决:检查划分是否随机。如果数据集本身场景单一,考虑自己重新划分,或者补充更多场景的图像。指标只是参考,最终要看实际场景的预测效果。
5. 进阶技巧:用标签统计反查数据质量
跑通训练只是第一步,真正决定模型上限的是数据质量。我一般会在训练前做一次标签统计,用几十行代码把每个类别的框数量、每张图的平均目标数、框的宽高分布都算出来。这一步能提前暴露很多问题:某个类别样本极少、某些图目标密集到不合理、框的尺寸分布异常。
import os from collections import Counter label_dir = "labels/train" cls_counter = Counter() box_per_img = [] w_list, h_list = [], [] for fn in os.listdir(label_dir): if not fn.endswith(".txt"): continue with open(os.path.join(label_dir, fn)) as f: lines = [l.strip() for l in f if l.strip()] box_per_img.append(len(lines)) for line in lines: parts = line.split() cls_id = int(parts[0]) w = float(parts[3]) h = float(parts[4]) cls_counter[cls_id] += 1 w_list.append(w) h_list.append(h) print("类别分布:", dict(cls_counter)) print("平均每图目标数:", sum(box_per_img) / len(box_per_img)) print("最大单图目标数:", max(box_per_img)) print("框宽范围:", min(w_list), max(w_list)) print("框高范围:", min(h_list), max(h_list))这段脚本输出几个关键数字。类别分布能看出四类是否均衡,如果某一类只有几十个框,训练时这类基本学不好,需要考虑补充数据或做类别加权。平均每图目标数反映场景密度,车辆检测一般每图几个到十几个都正常,如果平均几十个,可能是标注过密或重复标注。框宽高范围能发现异常值,正常归一化后应该在 0 到 1 之间,出现接近 0 或超过 1 的就要查。
我自己的习惯是:每次拿到新数据集,先跑一遍统计,再抽十张图把标签画出来肉眼核对。画框用 OpenCV 几行就能搞定,把归一化坐标还原成像素坐标,在原图上画矩形。这一步花不了十分钟,但能避免后面几小时的无用训练。从那以后我每次开训前都强制走一遍「统计加可视化」,再也没出现过训到一半发现标签错位的情况。
这份数据集的双格式标签和现成配置,省掉了最枯燥的整理环节,但数据质量最终还得自己把关。希望这份拆解能帮你少走几个弯路,顺利把车辆检测模型跑起来。
本文还有配套的精品资源,点击获取