简介:面向目标检测与路面缺陷识别场景的YOLOV5目录格式数据集,内含纵向裂纹、横向裂纹、坑洞、不规则裂缝四类路面缺陷标注,图像统一为六百乘六百像素的RGB图片,适合路面巡检、智慧交通等方向的模型训练与算法验证。数据已按标准YOLOV5结构划分,训练集包含八百四十一张图片及对应txt标签,验证集包含二百三十一张图片及对应txt标签,类别文件明确记录四类缺陷,下载后可直接接入YOLOV5训练流程,免去格式转换与目录整理,也便于快速开展缺陷检测对比实验。资源包共两千个文件,以txt标签文件、jpg图像文件和Python可视化脚本为主,压缩包整体约七十四点五五兆字节,目录组织清晰便于检索。可视化脚本无需任何修改即可运行,随机传入一张图片便能绘制边界框并保存到当前目录,可快速核对标注效果,适合入门学习与项目验证。当前已有七百七十人浏览学习,适合土木工程检测、计算机视觉等专业学生作为实践数据集与入门项目素材。
1. 路面缺陷检测数据集:为什么模型总在真实路段翻车
做路面缺陷检测的同行应该都有过这种经历:模型在测试集上 mAP 跑到了 0.85,看着很漂亮,一部署到实地巡检车上,遇到裂缝漏检、把井盖当坑槽、雨天误报一堆——你以为是模型不行,其实九成是数据不行。路面缺陷检测这个方向,最缺的不是模型结构,而是质量靠谱、格式统一、类别定义清楚的数据集。我接触过好几拨做智慧养护的团队,最后全卡在数据上:有的从公开数据集里拼凑,有的自己标注标到崩溃,结果类别分类标准不一致,标注框有的贴边有的留白,模型全给学歪了。
这篇要聊的就是一个很具体的资源方向:YOLOV5 目录格式的路面缺陷检测数据集,4 个类别。它不是那种「下载下来就能跑」的玩具数据,而是需要你花半小时做一次体检、按自己场景微调格式、再做数据增强才能用得顺手的工业向数据集。适合谁?适合已经跑通过 YOLOV5 基础训练流程、正在找垂直场景数据做落地验证的开发者,也适合想快速搭建路面巡检 Demo 的团队。如果你只是想要一个能出指标好看的模型,这个方向可能不适合你;但如果你想部署到真实路段上少翻车,这篇值得看完。
2. 先验收数据再谈训练:路面缺陷数据集的三个硬指标
2.1 数据质量自检:用 10 分钟脚本看出 80% 的问题
拿到任何 YOLOV5 格式的数据集,第一件事不是解压就跑,而是先做数据体检。路面缺陷数据有个特点——背景极其复杂,沥青纹理、车道线、阴影、水渍都会干扰模型,如果标注本身有噪声,模型学到的就是噪声而不是缺陷。我拿到数据集后习惯先跑一个自检脚本,统计图片尺寸、标注框数量、类别分布。
import os from collections import Counter from PIL import Image dataset_root = "road_defect_dataset" split_dirs = ["images/train", "images/val", "labels/train", "labels/val"] # 检查目录结构是否完整 for d in split_dirs: full_path = os.path.join(dataset_root, d) if not os.path.isdir(full_path): print(f"[缺失] {full_path} 目录不存在") else: file_count = len([f for f in os.listdir(full_path) if f.endswith(".jpg") or f.endswith(".png")]) print(f"[正常] {full_path}: {file_count} 个文件") # 统计类别分布与标注框数量 label_dirs = ["labels/train", "labels/val"] class_counter = Counter() box_counts = [] for ld in label_dirs: label_path = os.path.join(dataset_root, ld) for label_file in os.listdir(label_path): if not label_file.endswith(".txt"): continue with open(os.path.join(label_path, label_file), "r") as f: lines = f.readlines() box_counts.append(len(lines)) for line in lines: parts = line.strip().split() if len(parts) == 5: class_counter[int(parts[0])] += 1 print("\n类别分布:", dict(class_counter)) print("平均每张图标注框数:", sum(box_counts) / len(box_counts)) print("标注框数分布:", sorted(set(box_counts)))这段脚本的核心是三个检查点:目录完整性、类别分布、单图框数。目录完整是底线,类别分布能看出数据是否平衡——路面缺陷数据集最常见的毛病是「裂缝」占 70% 以上,「坑槽」只有 5%,这种不平衡会让模型对小样本类别几乎失明。单图框数也有讲究,真实路面场景单图标注框一般不超过 15 个,如果大量图片标注框超过 30 个,要么是标注颗粒度太细,要么是缺陷碎片被拆成大量小框,这两种情况都会影响训练稳定性。
2.2 标注一致性验收:四类缺陷的边界到底怎么切
路面缺陷的类别定义看起来简单,实际标注时边界问题非常多。以 4 类为例,最常见的是「裂缝」和「网状裂缝」的区分——单条裂缝长度大于多少算裂缝?多条交叉裂缝的连通区域算网状裂缝还是多条裂缝?「坑槽」的边界怎么画,是画到破损边缘还是画到沥青剥落的外沿?这些标准如果不统一,模型会学到混乱的判别逻辑。我一般会随机抽 20 张训练图,把标注框和原图叠在一起人工过一遍,重点看三类问题:框是否贴合目标边缘、类别是否张冠李戴、是否存在该标未标的漏标。
标注贴合度有一个可量化的检查方法:计算标注框的面积与目标实际面积的 IoU。但这里有个现实问题——目标实际面积没有 Ground Truth 可供对比,所以更实用的做法是看标注框的边距。裂缝这类细长目标的框应该紧贴裂缝外边缘,留白不超过 3~5 个像素;坑槽这类面状目标的框可以略松,但也不能把相邻的正常路面圈进来。如果你发现大量标注框比目标大一圈,建议重新标注,否则模型学到的目标边界会偏大。
提示:拿到数据集后先问清楚标注规范。如果数据提供方没有给出类别定义文档,建议先自己写一版标注标准再开始用,不然后续增量标注时标准漂移会非常严重。
3. YOLOV5 目录格式拆解:从目录树到 txt 标注的完整映射
3.1 数据集根目录的标准结构:四个目录一个文件不能少
YOLOV5 的目录格式是社区沉淀下来的事实标准,不只是 YOLOV5 在用,YOLOV8、YOLOX 等框架都兼容这套格式。一个合格的路面缺陷数据集,解压后应该是下面这个结构:
road_defect_dataset/ ├── images/ │ ├── train/ # 训练图片,约 80% 数据量 │ │ ├── 000001.jpg │ │ ├── 000002.jpg │ │ └── ... │ └── val/ # 验证图片,约 20% 数据量 │ ├── 000101.jpg │ └── ... ├── labels/ │ ├── train/ # 训练标注,与 images/train 一一对应 │ │ ├── 000001.txt │ │ ├── 000002.txt │ │ └── ... │ └── val/ # 验证标注 │ ├── 000101.txt │ └── ... ├── classes.txt # 类别名文件,一行一个类别 └── dataset.yaml # YOLOV5 训练配置文件这里最关键的约束是一一对应关系:images/train/000001.jpg 必须对应 labels/train/000001.txt,文件名完全一致,只是扩展名从 .jpg/.png 换成 .txt。图片目录里不能有多余的图片,labels 目录里也不能有多余的标注文件。数据量方面,训练集建议不低于 2000 张,验证集不低于 300 张,否则路面这类高背景复杂度场景很难收敛。
3.2 txt 标注格式:五个数字决定一个目标框
YOLOV5 的标注格式是归一化的坐标,每一行代表一个目标,格式为:类别ID 中心点X 中心点Y 宽度 高度。注意,这里的坐标值不是像素值,而是相对图片宽高的归一化值,取值范围在 0 到 1 之间。
# labels/train/000001.txt 文件内容示例 0 0.542187 0.613889 0.118750 0.244444 2 0.148958 0.313889 0.058333 0.086111 3 0.756250 0.177778 0.087500 0.122222上面三行数据表示:图片 000001.jpg 中有三个目标,第一行是类别 0(假设是横向裂缝),中心点在图片宽度 54.2% 的位置,高度 61.4% 的位置,目标框宽度占整个图片的 11.9%,高度占 24.4%。这种格式的好处是尺寸无关——同一份标注可以适配任何分辨率的输入图片,YOLO 系模型在预处理时只需要做缩放,不需要重新计算标注。坏处也有——如果标注时用了错误的图片尺寸做归一化,坐标就会错位,目标框会整体偏移,这个坑后面专门讲。
3.3 classes.txt 与 dataset.yaml:让 YOLOV5 认识你的四类缺陷
classes.txt是一行一个类别的纯文本文件,它的行号就是类别 ID。YOLOV5 读取这个文件来决定模型输出头的类别数,以及训练时的类别名称映射。
# classes.txt crack_linear crack_网状 pothole patchdataset.yaml是 YOLOV5 训练时的入口配置。注意 YOLOV5 原版工具会自动读取classes.txt,所以dataset.yaml里可以不重复写类别名,但路径配置必须写对,尤其是path字段,建议用相对路径或者绝对路径,避免因为工作目录不对而找不到数据。
# dataset.yaml path: ../road_defect_dataset # 数据集根目录,相对于 yolov5 项目目录 train: images/train val: images/val # 类别定义(YOLOV5 实际会读取 classes.txt,此处注释说明) names: 0: crack_linear 1: crack_network 2: pothole 3: patchYOLOV5 训练时会先解析dataset.yaml,确认train和val的路径与实际目录结构匹配,然后从labels目录读取对应标注文件。路径配置可以写绝对路径,但同一份配置如果要给队友用,建议改成相对路径,这样换机器不用改配置。
4. 四类缺陷的训练策略:从类别不平衡到数据增强的完整方案
4.1 类别分布画像:用数据说话,别凭感觉配参数
路面缺陷的 4 类天然不平衡,这是数据集的固有属性,不是 bug。真实路面场景中,「裂缝」和「网状裂缝」出现的频率远高于「坑槽」和「修补区域」。一份标注质量过关的数据集,类别分布如果接近真实路面情况,那它就自带长尾属性。训练前必须做一次精确的分布统计,而不是凭感觉调节损失权重。
import collections def count_classes(label_dir): counter = collections.Counter() total_boxes = 0 for f in os.listdir(label_dir): if not f.endswith(".txt"): continue with open(os.path.join(label_dir, f), "r") as fh: for line in fh: parts = line.strip().split() if len(parts) == 5: counter[int(parts[0])] += 1 total_boxes += 1 return counter, total_boxes train_counter, train_total = count_classes("road_defect_dataset/labels/train") print(f"训练集标注框总数: {train_total}") for cls_id in range(4): count = train_counter[cls_id] print(f"类别 {cls_id}: {count} 个框, 占比 {count/train_total*100:.1f}%")这个统计的意义在于决定后续的两个策略:一是在dataset.yaml或训练参数中是否需要调整类别权重;二是判断是否需要针对小样本类别做复制粘贴式的增强。我见过一个实际数据集,裂缝占比 78%,坑槽只有 4%,直接按默认参数训练,坑槽的 mAP 只有 0.12,几乎不可用。但也不是所有不平衡都需要硬调,如果某个类别占比超过 30%,模型大概率能学到,重点还是补足占比低于 10% 的类别。
4.2 数据增强策略:路面场景的增强组合拳
YOLOV5 自带的增强参数在自然场景上表现不错,但路面缺陷数据有特殊性:缺陷目标通常偏小、对比度偏低、背景纹理复杂。直接套默认参数(hsv_h=0.015、fliplr=0.5等)能跑通,但效果一般。我会在训练脚本里额外调整一组参数,让增强策略更贴合路面场景。
python train.py --img 640 --batch 16 --epochs 100 \ --data road_defect_dataset/dataset.yaml \ --weights yolov5s.pt \ --hyp data/hyps/hyp.scratch-low.yaml \ --augment \ --multi-scale这里说几个关键参数的含义。--augment启用 YOLOV5 的增强策略,--multi-scale会在训练过程中动态改变输入尺寸,比如在 480 到 960 像素之间随机切换,这对路面缺陷检测很重要——因为巡检相机拍摄距离不固定,目标尺寸变化大,多尺度训练能显著提升不同距离下的检测稳定性。我一般会把hyp.scratch-low.yaml里的mosaic参数从默认的 1.0 调整到 0.7 左右,因为路面缺陷如果是小目标密集场景,过强的 mosaic 会把目标切碎,导致小目标特征丢失。
# 如果需要更激进的小目标增强,修改 hyp 文件后重新训练 python train.py --img 640 --batch 16 --epochs 100 \ --data road_defect_dataset/dataset.yaml \ --weights yolov5s.pt \ --hyp custom_hyp.yaml数据增强不是加得越猛越好。路面图片的纹理细节很丰富,如果hsv_h调得过大,颜色偏移会让沥青路面的灰色纹理失真,模型反而学到不真实的外观特征。flip_lr可以放心用,路面缺陷没有左右语义差别;flip_ud我一般开着但概率压低到 0.1 以下,因为真实采集时相机倒置的情况极少,翻太多会让模型对直立姿态的裂缝产生混淆。
4.3 类别合并与重标注:什么时候值得动手改标签
拿到 4 类数据集,第一步应该确认这 4 类的定义和你自己的业务场景是否一致。比如有的数据集把「修补区域」分成「补丁」和「修补裂缝」两类,有的把「龟裂」和「网状裂缝」混成一个类。如果类别定义不一致,直接训练会导致模型学到的语义边界和你业务需要的边界错位。我遇到过一种情况:数据集的「裂缝」包含了所有线性裂缝(包括横向、纵向、斜向),但我的巡检系统只需要区分「需要修复的裂缝」和「不需要处理的细微纹路」,这个需求靠现有 4 类做不到,需要按自己的规范做一次标签重映射。
import shutil def remap_labels(label_dir, mapping, output_dir): os.makedirs(output_dir, exist_ok=True) for f in os.listdir(label_dir): if not f.endswith(".txt"): continue src_path = os.path.join(label_dir, f) dst_path = os.path.join(output_dir, f) with open(src_path, "r") as fr, open(dst_path, "w") as fw: for line in fr: parts = line.strip().split() if len(parts) != 5: continue old_cls = int(parts[0]) if old_cls in mapping: new_cls = mapping[old_cls] fw.write(f"{new_cls} {parts[1]} {parts[2]} {parts[3]} {parts[4]}\n") # 举例:把类别 0(细裂缝)合并到类别 1(裂缝),类别 3(修补区域)降级为背景 mapping = {0: 1, 1: 1, 2: 2} # 注意:类别 3 不写入,表示丢弃 remap_labels("road_defect_dataset/labels/train", mapping, "road_defect_dataset_remapped/labels/train")类别合并的代码逻辑很直白:读取每一行标注,按照映射字典mapping改写类别 ID,不写入映射表的类别直接跳过。这个操作虽然简单,但有两个细节必须处理:第一,合并后classes.txt要同步修改;第二,如果丢弃了某些类别,原图里被丢弃的目标就变成了背景,如果一张图全部目标都被丢弃,那张图相当于没有标注,训练时会被当作纯背景图参与计算,如果数量太多会干扰训练,建议直接把这类图片从数据集中移除。
5. 训练避坑指南:路面缺陷数据集最容易翻车的四个位置
5.1 标注坐标越界:归一化坐标大于 1 或小于 0
现象:训练时 loss 在初期下降后就卡住不动了,或者训练能跑完但 mAP 极低;查看验证集预测结果,发现预测框偏到图片外面。
原因:标注 txt 里有坐标值超出 [0, 1] 范围。路面缺陷数据集的标注很多是半自动标注工具输出的,工具如果对裁剪后的图片做了标注,但没有同步更新归一化基准,就会出现坐标越界。还有一种情况是标注时图片尺寸和实际读取尺寸不一致,导致归一化计算错误。
解决:写脚本巡检所有标注文件,找出越界的行,按越界程度处理——轻微越界(比如 1.02)可以直接截断到边界,严重越界(比如 2.5 倍)则说明该行标注不可信,删除整行;如果某张图所有标注行都越界,删除该图及其标注,避免污染训练集。
def check_label_bounds(label_dir): bad_files = [] for f in os.listdir(label_dir): if not f.endswith(".txt"): continue with open(os.path.join(label_dir, f), "r") as fh: for line in fh: parts = line.strip().split() if len(parts) != 5: print(f"[格式异常] {f}: {line.strip()}") bad_files.append(f) else: x_center, y_center, w, h = map(float, parts[1:]) if not (0 <= x_center <= 1 and 0 <= y_center <= 1): print(f"[坐标越界] {f}: x={x_center}, y={y_center}")5.2 训练集和验证集数据泄漏:图片重复或高度相似
现象:训练集 mAP 接近 0.95,验证集 mAP 也很高,但测试集(或实地部署)的效果掉到 0.3 以下。
原因:数据切分时没有做去重。路面缺陷数据很多是视频抽帧得到的,相邻帧高度相似——如果切分时直接用随机数分配,同一段路面的相邻帧很容易被分到训练集和验证集,导致验证集「作弊」:模型见过这张图了。更隐蔽的是数据增强造成的伪泄漏,比如 mosaic 增强拼贴了验证集的图片。
解决:切分前先计算图片哈希或感知哈希做去重。我自己一般按拍摄路段分组来切分,保证同一条路段的所有帧只在训练集或只在验证集,而不是随机打散。这个习惯比任何增强技巧都重要,它保证了验证集的「陌生感」。如果你拿到的数据集已经切分好了,也要抽查一下——随机取 10 张验证图,在训练集里用感知哈希找最相似的图,如果相似度高于阈值,就得考虑重新切分。
# 用 Python 计算感知哈希做近似去重(核心代码) from PIL import Image import imagehash def compute_hash(path): img = Image.open(path) img = img.resize((8, 8)) return imagehash.average_hash(img)5.3 标注器类别混淆:标签文件和图片内容对不上
现象:训练过程 loss 不下降,或者验证集上某两个类别的预测结果互相混。比如裂缝被大量预测为网状裂缝,坑槽被预测为修补区域。
原因:这是一个比坐标越界更可怕的问题——标注文件本身没有错,但类别标错了。最常见的是「网状裂缝」和「裂缝」的混淆,因为裂缝数据集的类别定义往往不清晰。比如网状裂缝标注为多条短裂缝的集合,如果标注规范没写明,标注员会按自己的理解切分,有的切成多个「裂缝」框,有的整体标为「网状裂缝」。
解决:训练前人工抽检。不管数据集描述写得多么详细,一定要自己看 10~15 张原图和标注叠加的可视化结果。YOLOV5 仓库里提供了可视化脚本,也可以自己画框叠加。如果发现某一类别的标注混乱,按 5.3 的映射规则重标是必要的——这里没有捷径,模型学到的边界完全取决于标注的边界。还有一个小技巧:先用预训练权重训练少量 epoch,然后看混淆矩阵,如果某两类的混淆集中在同一种模式下,大概率是标注标准问题而不是模型问题。
5.4 忽略栅格化问题:YOLO 的小目标漏检根源
现象:表现为小裂缝目标检测不到,验证集上 mAP 不低但小目标的 recall 极低,单看大目标指标挺好。
原因:YOLO 系列的下采样倍数决定了它能感知的最小目标尺寸。YOLOV5 的 P3 层输出 stride 是 8,意味着最小能有效检测的目标框尺寸约为 8×8 像素(在 640 输入下)。路面上的细裂缝宽度可能只有 3~5 个像素,在 640 分辨率下,目标框宽度可能只有 4~6 个像素,已经低于模型的有效感知范围。很多数据集本身在图像采集时就丢失了细节——图片分辨率太低,导致裂缝边缘模糊,模型难以学到稳定的边缘特征。
解决:先确认输入分辨率。如果你手里的图片是 1080p 以上,训练时可以把--img调到 1280 或 1536,让深层特征映射层保留更多小目标细节;如果原图分辨率本身就低,建议先做超分或重新采集,靠模型很难弥补信息损失。另外,YOLOV5 的img参数和标注的归一化坐标是独立的,你把输入调大到 1280 不一定能解决小目标问题,真正起作用的是在输入尺寸内保留足够的原始像素信息。我的习惯是:小目标占比超过 20% 的数据集,优先考虑调高--img到 960 以上,并检查每一轮的 P3 层 loss 是否在下降。
6. 数据集验证的实战手法:训练一轮只看图不看指标
很多人拿到数据集就急着跑完整训练,其实不用。我更推荐先用小模型跑一个 10~20 轮的「冒烟训练」,训练完直接加载权重做预测,把预测结果和标注叠加在一起看。看什么?不是看 mAP,而是看三件事:小目标召回情况、遮挡目标、类别边界。之前一个团队用这套数据集的类似版本做训练,模型在验证集上 mAP 0.87,但实际部署时裂缝漏检严重——我把预测图拉出来一看,漏检的全是宽度在 5 个像素以下的细裂缝。这不是数据集的错,而是输入分辨率加上下采样把细节磨掉了。后来他们把输入尺寸从 640 提升到 1280,小目标 recall 从 0.41 涨到 0.63,代价只是推理速度降了 30%,对巡检场景完全可接受。
这个案例也说明了验证的核心思路:指标是辅助,可视化预测结果是主证据。你需要在验证集里挑 3~5 张有代表性的难例——比如强阴影下的裂缝、裂缝被井盖遮挡、修补区域与正常路面边界模糊——专门盯这些难例的预测结果。如果难例预测对了,模型的泛化能力大概率是够的;如果难例翻车,那就要回到数据增强或标注质量两个方向去找问题。这是 A 同学教我的习惯,他当时用它排查一个误报严重的检修机器人项目,最后发现是标注时把井盖边缘的裂纹标成了裂缝,而不是模型的问题——可视化让你更快定位到问题的真正主人,而不是盲目调参。
还有一个终极大招,等模型训练到第 80 轮接近收敛时,冻结主干重新训练一个更小的模型(比如 YOLOV5n),如果小模型也能在难例上表现不错,说明数据集的信息密度是够的,瓶颈在模型侧而不是数据侧;如果小模型崩得惨烈,那多半还是数据的问题。这个验证法能帮你判断值不值得继续在这个数据集上投算力,还是需要换数据源。路面缺陷检测难不在模型,而在模型有没有见过足够多「真实乱七八糟」的路面——希望这些经验能帮你少走点弯路,希望这篇能帮到你。
本文还有配套的精品资源,点击获取