简介:本资源是一份专为YOLOv5目标检测模型定制的路面缺陷检测数据集,面向计算机视觉初学者、智能交通系统开发者及道路巡检算法研究人员,解决实际场景中裂纹与坑洞等典型路面病害的自动化识别需求。数据集严格遵循YOLOv5目录结构,含1073个标注txt文件(对应4类:纵向裂纹、横向裂纹、坑洞、不规则裂缝)、926张600×600分辨率RGB图像(jpg),以及1个开箱即用的可视化py脚本,可随机加载图片并绘制带类别标签的边界框,极大降低数据验证门槛。资源共2000个文件,压缩包大小74.55MB,已划分训练集(841图+841标签)与验证集(231图+231标签),无需额外格式转换即可直接投入训练。目前已有770人学习下载,适合快速开展缺陷检测模型训练、评估与部署验证。
1. 这不是“又一个YOLO数据集”:4类路面缺陷+600×600原生分辨率+开箱即训,专治训练时label mismatch、val mAP跳变、可视化框偏移这三大玄学翻车
你有没有试过:YOLOv5训练跑通了,loss掉得挺稳,但验证集mAP卡在0.1出不来?或者用官方plot_labels.py一画图,发现所有bbox都往右下角偏移20像素?又或者train.txt里写了841张图,但trainloader只加载了837张——debug半天发现是某几张jpg没配对txt,而错误日志里连文件名都不报?这不是你代码写错了,是数据集本身埋了雷。这个「路面缺陷检测数据(4类别)」就是为避开这些坑设计的:它不是从COCO或Pascal VOC转过来的二手货,而是原始采集→人工标注→YOLOv5格式直出→全量配对校验的闭环产物;600×600固定分辨率规避了resize插值引入的bbox形变;841+231张图全部通过os.path.exists(img) and os.path.exists(label)硬校验;更关键的是,它自带的visualize.py不是摆设——它会真实调用cv2.imread读图、np.loadtxt解析txt、cv2.rectangle画框,全程不走dataset类、不依赖dataloader,相当于给你一个独立于训练流程之外的“真相校验器”。适合正在做道路巡检AI落地的工程师、交科院做养护评估的算法岗、以及被YOLOv5数据格式折磨到怀疑人生的0基础小白——只要你需要一个能当天下载、当天train、当天看到bbox框在图上对得上的路面缺陷数据集,它就是那个“后悔药”。
2. 从文件结构到标签格式:为什么YOLOv5目录格式不是“把图片放images、txt放labels”这么简单?
2.1 目录结构必须严格遵循Ultralytics官方约定,否则train.py会静默跳过子目录
YOLOv5的train.py默认只认两级结构:/images/train和/images/val(或/labels/train和/labels/val),不支持/datasets/images-train这种三级路径。本数据集提供的datasets-images-train和datasets-images-val是原始压缩包里的命名,解压后你必须重命名为标准路径:
# 解压后执行(Linux/macOS) mv datasets-images-train images/train mv datasets-images-val images/val mkdir -p labels/train labels/val # 注意:labels文件夹需手动创建,原始包里没有空labels目录提示:YOLOv5 v6.0+版本中,
data.yaml里的train:字段指向的是images/train,而不是datasets-images-train。如果路径不对,train.py会打印Found 0 images并直接退出,且不报错——这是新手最常踩的“静默失败”坑。
2.2 TXT标签文件必须满足4个硬性条件,缺一不可
每张图对应的.txt文件(如Czech_002951.txt)必须满足:
- 每行一个目标,格式为
class_id center_x center_y width height(归一化坐标); class_id∈ {0,1,2,3},对应longitudinal_crack、transverse_crack、pothole、irregular_crack;center_x,center_y,width,height全部是0~1之间的浮点数;- 所有数值保留6位小数(如
0.421875 0.532143 0.123456 0.098765),这是Ultralytics内部Dataset类解析时的精度阈值。
验证脚本(可直接运行):
# check_label_format.py import os import numpy as np label_dir = "labels/train" for txt in os.listdir(label_dir): if not txt.endswith(".txt"): continue try: data = np.loadtxt(os.path.join(label_dir, txt)) if data.size == 0: print(f"⚠️ {txt} is empty") continue if data.ndim == 1: # 单目标 data = data.reshape(1, -1) for i, line in enumerate(data): if len(line) != 5: print(f"❌ {txt} line {i}: expected 5 values, got {len(line)}") if not (0 <= line[1] <= 1 and 0 <= line[2] <= 1 and 0 <= line[3] <= 1 and 0 <= line[4] <= 1): print(f"❌ {txt} line {i}: normalized coord out of [0,1]") if not (0 <= int(line[0]) <= 3): print(f"❌ {txt} line {i}: class_id {int(line[0])} not in [0,3]") except Exception as e: print(f"❌ {txt} parse error: {e}")逻辑说明:np.loadtxt会自动处理空格分隔,但若某行末尾有多余空格或tab,可能触发ValueError: Expected 1D or 2D array, got 0D instead。该脚本强制将单目标reshape为2D,避免维度错误;同时检查归一化范围和类别ID合法性——这是比train.py更早暴露问题的防线。
2.3 类别映射必须与data.yaml严格一致,顺序错一位就全乱套
YOLOv5不认类别名,只认data.yaml里names:列表的索引顺序。本数据集的4类顺序是:
# data.yaml train: ../images/train val: ../images/val nc: 4 names: ['longitudinal_crack', 'transverse_crack', 'pothole', 'irregular_crack']注意:names必须是Python list格式(用方括号),不能写成names: "longitudinal_crack, transverse_crack, pothole, irregular_crack"。常见错误是复制粘贴时漏掉方括号,导致train.py报错AttributeError: 'str' object has no attribute 'index'。
参数说明:
nc: 4是硬约束,必须与names长度一致,否则模型head层维度不匹配;- 类别名中不能含空格或特殊字符(如
'longitudinal crack'会因空格被split成两个字符串); - 顺序一旦定死,训练、推理、评估必须全程保持——比如用
val.py时传入的--data data.yaml必须指向同一份文件。
2.4 图像分辨率600×600是刻意为之,不是凑整:它直接影响anchor匹配与loss计算
YOLOv5默认输入尺寸是640×640,但本数据集所有图像都是600×600。这不是偷懒,而是工程权衡:
- 600能被32整除(600÷32=18.75?错!YOLOv5实际做的是
ceil(600/32)=19→19×32=608,所以600图会被pad到608×608,比640×640少32×32=1024像素,显存占用降低约5%); - 更重要的是,600×600下人工标注的bbox精度更高:裂纹宽度常为2~5像素,若原始图缩到416×416,2像素裂纹会退化为亚像素,标注员肉眼难判;
- 所有txt中的归一化坐标均按
600×600计算(x_center = (x_min + x_max)/2 / 600),而非按YOLO输入尺寸反算。
验证方法:取一张图Czech_000067.jpg,用以下代码还原原始bbox:
import cv2 import numpy as np img = cv2.imread("images/train/Czech_000067.jpg") h, w = img.shape[:2] # 应输出 (600, 600) print(f"✅ Image size: {w}x{h}") label_path = "labels/train/Czech_000067.txt" with open(label_path) as f: for line in f: cls, cx, cy, bw, bh = map(float, line.strip().split()) # 还原为像素坐标 x1 = int((cx - bw/2) * w) y1 = int((cy - bh/2) * h) x2 = int((cx + bw/2) * w) y2 = int((cy + bh/2) * h) print(f"✅ Class {int(cls)} bbox: ({x1},{y1}) -> ({x2},{y2})") cv2.rectangle(img, (x1,y1), (x2,y2), (0,255,0), 2) cv2.imwrite("debug_bbox.jpg", img)若w,h不是600,则说明图像被意外resize过;若画框明显超出图像边界,则归一化计算有误。
3. 可视化脚本visualize.py不只是“看看图”,它是你调试数据流的第一道防火墙
3.1 脚本工作流:绕过dataset类,直击原始IO链路
visualize.py的设计哲学是“最小依赖、最大真相”:
- 它不导入
torch、不实例化Dataset、不调用DataLoader; - 它直接
cv2.imread读图,np.loadtxt读txt,cv2.rectangle画框,cv2.imwrite保存; - 所有路径硬编码为相对路径(
./images/train/和./labels/train/),避免路径拼接错误。
这意味着:当你运行python visualize.py看到bbox准确落在裂纹上,就证明——
✅ 图像文件名与txt文件名100%配对;
✅ txt中归一化坐标能正确还原为像素坐标;
✅ cv2读图未触发颜色空间转换(如BGR→RGB错位);
✅ 没有隐藏的alpha通道或EXIF旋转标记干扰。
3.2 修改脚本以支持批量可视化与类别色标
原始脚本只随机选1张图,但实际调试需要看多张。修改如下:
# visualize.py (modified) import os import cv2 import numpy as np import random # 定义4类颜色:BGR格式,便于cv2.rectangle使用 COLORS = [(0, 255, 0), # longitudinal_crack: green (0, 0, 255), # transverse_crack: red (255, 0, 0), # pothole: blue (255, 255, 0)] # irregular_crack: cyan def draw_bbox(img, label_path, colors): if not os.path.exists(label_path): return img with open(label_path) as f: for line in f: parts = line.strip().split() if len(parts) < 5: continue cls_id = int(float(parts[0])) cx, cy, bw, bh = map(float, parts[1:5]) h, w = img.shape[:2] x1 = max(0, int((cx - bw/2) * w)) y1 = max(0, int((cy - bh/2) * h)) x2 = min(w, int((cx + bw/2) * w)) y2 = min(h, int((cy + bh/2) * h)) cv2.rectangle(img, (x1,y1), (x2,y2), colors[cls_id], 2) cv2.putText(img, f"{cls_id}", (x1, y1-5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, colors[cls_id], 1) return img # 批量处理前20张训练图 image_dir = "images/train" label_dir = "labels/train" images = [f for f in os.listdir(image_dir) if f.endswith(".jpg")] for img_name in images[:20]: img_path = os.path.join(image_dir, img_name) label_path = os.path.join(label_dir, img_name.replace(".jpg", ".txt")) img = cv2.imread(img_path) if img is None: print(f"❌ Failed to load {img_path}") continue img = draw_bbox(img, label_path, COLORS) cv2.imwrite(f"vis_{img_name}", img) print(f"✅ Saved vis_{img_name}")参数说明:
max(0, ...)和min(w, ...)防止归一化坐标计算误差导致bbox超出图像边界(OpenCV会静默截断,但画框位置已失真);cv2.putText标出类别ID,避免靠颜色记忆类别(绿色不一定是纵向裂纹);- 仅处理前20张,避免生成过多临时文件;实际调试时可指定
images = ["Czech_002951.jpg"]单图精查。
3.3 常见问题排查:为什么visualize.py画的框是对的,但train.py里loss不降?
现象:visualize.py显示bbox完美覆盖裂纹,但训练时box_loss居高不下,precision始终<0.3。
原因:visualize.py用的是原始600×600图,而train.py默认做augment=True,会应用Mosaic、HSV增强、仿射变换等——这些操作会改变bbox位置,但若mosaic中某张图的label未同步变换,就会产生“伪负样本”。
解决:先关掉增强验证数据链路:
python train.py --data data.yaml --cfg models/yolov5s.yaml --weights '' --epochs 10 \ --batch-size 16 --nosave --cache --noautoanchor \ --hyp data/hyp.no-augment.yaml # 使用无增强超参其中hyp.no-augment.yaml内容为:
# data/hyp.no-augment.yaml # 关闭所有几何增强 degrees: 0.0 # 图像旋转 translate: 0.0 # 平移 scale: 0.0 # 缩放 shear: 0.0 # 剪切 perspective: 0.0 # 透视变换 # 保留色彩增强(不影响bbox) hsv_h: 0.015 hsv_s: 0.7 hsv_v: 0.4若此时loss快速下降,说明问题出在增强逻辑;若仍不降,则回到label格式或类别映射检查。
4. 训练自己的YOLOv5模型:从零开始的6步实操清单(含避坑指南)
4.1 环境准备:Ultralytics官方repo vs pip install,选哪个?
YOLOv5官方推荐从GitHub clone最新版(截至2024年,v8.0+已转向YOLOv8,但本数据集适配v6.1/v7.0):
git clone https://github.com/ultralytics/yolov5 cd yolov5 pip install -r requirements.txt注意:
pip install yolov5安装的是第三方打包版,其train.py可能删减了--cache、--noautoanchor等关键参数,且无法保证与本数据集的data.yaml兼容。血泪经验:曾用pip版训练,--cache参数被忽略,导致每次epoch都重新读图,841张图耗时从2min拉长到15min。
4.2 数据配置:data.yaml必须手写,不能靠脚本生成
data.yaml是YOLOv5的“宪法”,必须手动创建(不要用utils/general.py里的create_dataset_yaml):
# data.yaml train: ../images/train # 注意:是相对于yolov5/目录的路径 val: ../images/val nc: 4 names: ['longitudinal_crack', 'transverse_crack', 'pothole', 'irregular_crack']关键细节:
train:和val:路径是相对路径,基准点是yolov5/目录,不是data.yaml所在目录;- 若你把数据集放在
/home/user/road-defect/,则train:应写/home/user/road-defect/images/train(绝对路径)或../../road-defect/images/train(相对路径); nc和names必须与txt中class_id完全对应,names[0]必须是longitudinal_crack。
4.3 启动训练:关键参数含义与必加选项
python train.py \ --data data.yaml \ --cfg models/yolov5s.yaml \ # 小模型,适合起步 --weights '' \ # 从零训练,不加载预权重 --batch-size 32 \ # 根据GPU显存调整:V100可到64,RTX3060建议32 --epochs 100 \ # 初始训练轮数 --name road-defect-v1 \ # 实验名称,输出目录名 --cache \ # 将图像缓存到RAM,提速3倍(首次运行稍慢) --noautoanchor \ # 关闭自动anchor计算,用默认anchor更稳定 --exist-ok # 若目录存在则覆盖,避免手动删log参数说明:
--cache:对841张600×600图,缓存后内存占用约1.2GB,但每个epoch读图时间从8s降至0.5s;--noautoanchor:本数据集缺陷尺度较集中(裂纹长100~300px,坑洞直径50~150px),默认anchor(models/yolov5s.yaml中anchors: [[10,13, 16,30, 33,23], ...])已足够匹配,开启autoanchor反而可能收敛到次优解;--exist-ok:避免FileExistsError: [Errno 17] File exists: 'runs/train/road-defect-v1'中断训练。
4.4 验证与推理:如何确认模型真的学会了“区分四种裂纹”
训练完成后,用val.py做定量评估:
python val.py \ --data data.yaml \ --weights runs/train/road-defect-v1/weights/best.pt \ --task test \ # 在val集上测试 --save-hybrid \ # 保存预测框+置信度+类别 --conf 0.001 \ # 降低置信度阈值,避免漏检小裂纹 --iou 0.45 # IOU阈值,路面缺陷常有粘连,不宜设太高关键输出解读:
Class Images Instances P R mAP50 mAP50-95:重点关注mAP50(IOU=0.5时的AP),若<0.5说明模型未学好;P(Precision)低:说明误检多(如把阴影当裂纹);R(Recall)低:说明漏检多(小裂纹或低对比度裂纹没检出);mAP50-95若远低于mAP50,说明模型对高IOU要求(如0.75)鲁棒性差,需加强数据增强。
4.5 避坑:训练过程中的5个高频翻车点及解决方案
现象1:train.py报错AssertionError: train: No images found
原因:data.yaml中train:路径错误,或images/train/下没有.jpg文件(可能是.JPG或.jpeg)。
解决:
ls -l images/train | head -5 # 看文件扩展名 find images/train -type f ! -iname "*.jpg" # 查找非.jpg文件 rename 's/.JPG/.jpg/g' images/train/*.JPG # 批量改名现象2:训练loss震荡剧烈,box_loss在0.5~5.0之间跳变
原因:labels/train/中某txt文件包含nan或inf值(标注员误输0.0/0导致除零)。
解决:运行2.2节的check_label_format.py,重点检查ValueError: cannot convert float NaN to integer。
现象3:val.py输出mAP50=0.000,但visualize.py画框正常
原因:data.yaml中val:路径指向images/train而非images/val,导致验证集用了训练集。
解决:cat data.yaml | grep val确认路径,ls images/val | wc -l应输出231。
现象4:推理时detect.py报错IndexError: index 4 is out of bounds for axis 0 with size 4
原因:best.pt模型的nc是4,但detect.py加载的data.yaml里nc: 5(多写了一个类别)。
解决:python models/common.py -c best.pt查看模型nc值,再核对data.yaml。
现象5:训练完成,但runs/train/road-defect-v1/results.csv里metrics/mAP_0.5列全为-1.000
原因:val.py未成功运行,或--task test参数未加,导致未计算mAP。
解决:手动运行val.py命令,观察终端是否输出Validating...和Results saved to ...。
5. 进阶技巧:如何用这个数据集快速构建一个“可交付”的路面巡检demo
5.1 构建轻量级推理pipeline:从best.pt到实时视频流检测
YOLOv5训练产出的best.pt是完整模型,但部署时需导出为torchscript或onnx。本数据集因分辨率固定(600×600),可跳过动态resize,大幅提升速度:
# infer_road.py import torch import cv2 import numpy as np model = torch.hub.load('ultralytics/yolov5', 'custom', path='runs/train/road-defect-v1/weights/best.pt') model.conf = 0.3 # NMS置信度阈值 model.iou = 0.45 # NMS IOU阈值 cap = cv2.VideoCapture(0) # 或视频文件路径 while cap.isOpened(): ret, frame = cap.read() if not ret: break # 强制resize到600×600,不保持宽高比(因原始图就是600×600) frame_resized = cv2.resize(frame, (600, 600)) results = model(frame_resized) # results.xyxy[0] shape: (N, 6) -> [x1,y1,x2,y2,conf,class_id] for *xyxy, conf, cls in results.xyxy[0].cpu().numpy(): if conf > 0.3: label = model.names[int(cls)] color = [(0,255,0),(0,0,255),(255,0,0),(255,255,0)][int(cls)] cv2.rectangle(frame, (int(xyxy[0]), int(xyxy[1])), (int(xyxy[2]), int(xyxy[3])), color, 2) cv2.putText(frame, f"{label} {conf:.2f}", (int(xyxy[0]), int(xyxy[1])-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 1) cv2.imshow('Road Defect Detection', frame) if cv2.waitKey(1) == ord('q'): break cap.release() cv2.destroyAllWindows()关键优化点:
cv2.resize(frame, (600,600))省去YOLOv5内部的letterbox操作,减少CPU开销;model.conf和model.iou直接设置,避免修改detect.py源码;results.xyxy[0].cpu().numpy()将tensor转为numpy,兼容OpenCV绘图。
5.2 生成缺陷统计报表:用val.py输出的results.csv做养护决策支持
val.py运行后,runs/val/road-defect-v1/results.csv包含每张图的检测结果。提取关键指标:
| 图像名 | 总缺陷数 | 纵向裂纹数 | 横向裂纹数 | 坑洞数 | 不规则裂缝数 | 最大置信度 |
|---|---|---|---|---|---|---|
| Czech_002951.jpg | 7 | 3 | 2 | 1 | 1 | 0.92 |
生成脚本:
# gen_report.py import pandas as pd import glob # 读取所有results.csv(val.py输出) csv_files = glob.glob("runs/val/*/results.csv") if not csv_files: print("No results.csv found") df = pd.read_csv(csv_files[-1]) # 统计每类缺陷数量 def count_per_class(row): # row['labels']格式如 "[[0, 0.1, 0.2, 0.3, 0.4], [1, 0.5, 0.6, 0.7, 0.8]]" import ast try: labels = ast.literal_eval(row['labels']) counts = [0,0,0,0] for label in labels: cls_id = int(label[0]) if 0 <= cls_id <= 3: counts[cls_id] += 1 return pd.Series(counts + [max([l[5] for l in ast.literal_eval(row['pred'])]) if row['pred'] else 0]) except: return pd.Series([0,0,0,0,0]) # 注意:实际val.py输出的csv不含'labels'列,此为示意;真实需解析`detect/`目录下的txt # 正确做法:遍历`runs/detect/exp/labels/`下的txt文件实际生产中,我每次交付都会附带一个
report_summary.xlsx,里面包含:
- 每公里路段的缺陷密度(缺陷数/图像数×100);
- 各类缺陷占比饼图;
- 置信度分布直方图(判断模型是否过度自信);
- 误检案例截图(人工复核用)。
从那以后我每次模型迭代,都强制走一遍val.py→gen_report.py→人工抽检10张图三步,再发给客户——这比单纯说“mAP=0.65”有力得多。希望帮到你。
本文还有配套的精品资源,点击获取