资讯动态

道路坑洞检测YOLO数据集与YOLOv8训练避坑指南

发布时间:2026/9/24 18:53:04 来源:尧图企业网站定制
简介这是面向道路坑洞检测的YOLO系列目标检测数据集内含1990张标注图像适合算法工程师、科研人员及计算机视觉学习者快速搭建模型训练与验证环境可用于道路病害检测、无人驾驶辅助等场景。压缩包共2000个文件其中1420个为VOC格式的xml标签580个为YOLO格式的txt标签整体大小约为166.52MB数据已按训练集、验证集、测试集划分好并附有data.yaml配置文件。目前已有148人学习或下载过该资源。两种标签格式分别存放于独立文件夹txt采用标准YOLO格式记录类别、中心点坐标、宽高均为归一化比例值可直接被YOLOv5、YOLOv8、YOLOv9、YOLOv7、YOLOv10、YOLOv11等主流模型读取xml为VOC格式便于在LabelImg等工具中二次标注或转换。资源能显著减少数据处理与格式转换的时间帮助使用者快速开展道路坑洞检测实验及实际项目部署。1. 道路坑洞检测为什么绕不开这个 1990 张的 YOLO 数据集做道路巡检的人应该都有同感坑洞检测的模型精度卡的从来不是网络结构而是数据本身。网上能搜到的公开坑洼数据集要么是几百张的欧州路况照片、标注风格五花八门要么干脆只有图片没有标签下载回来还得自己拿 labelling 重标一遍。这个 1990 张带 YOLO 标签的道路坑洞数据集属于「拿到就能训练」的那种标签是标准的 YOLO txt 格式喂到 YOLOv5、YOLOv8 里不用写转换脚本。对做市政道路养护、农村公路巡检、智慧城市项目预研的人来说这是少有的能把一天工作量压缩到一小时的资资源。更关键的是它给出了一个相对合理的坐标基准——坑洞检测最怕的不是模型召回低而是标注的框「一会儿贴边一会儿包一圈」这会让模型的边界回归完全乱套。这个数据集的标签风格是否统一拿到手先验一下货后面训练会省掉大量来回试错的时间。下面从数据集结构、标签格式、预检脚本、训练参数和避坑这五条线完整讲一遍。2. 数据集内部结构目录、标签命名和 YOLO 格式的换算关系2.1 拿到压缩包后第一件事核对目录和文件命名解压之后不要急着开训练先去看目录结构和命名规则是否符合你的预期。按最常见的打包习惯1990 张图像会被拆成训练集和验证集两个目录结构大概长这样。pothole-dataset/ ├── data.yaml ├── images/ │ ├── train/ │ │ ├── 001.jpg │ │ ├── 002.jpg │ │ └── ... │ └── valid/ │ ├── 1601.jpg │ └── ... └── labels/ ├── train/ │ ├── 001.txt │ └── ... └── valid/ ├── 1601.txt └── ...先看三个地方训练集和验证集的图是不是放在同一个 root 下面并被 images/train、images/valid 区分标签文件名和图像文件名是否完全一致只差扩展名以及 data.yaml 里的路径写的是绝对路径还是相对路径。这三个点里最坑的是路径很多标注工具导出时把路径写死了你在自己机器上解压位置不同就报 Dataset not found。2.2 一行标签五个数字class 和归一化坐标的含义打开任意一个 txt 标签文件每行是五个浮点数这是 YOLO 系列的标准格式。0 0.523437 0.614583 0.123437 0.0874997从左到右分别是类别 ID、目标中心点的 x 坐标归一化、中心点的 y 坐标归一化、目标宽度归一化、目标高度归一化。这里的归一化意思是所有的数值都被除以了图片的宽或高强制落到 0 到 1 之间。如果这个数据集的类别只有坑洞一类第一列基本全是 0少量数据可能坑洞标注为 1 或 2这取决于打标时建了几个类别。这里特别说明一下坑洞检测场景里我建议类别定义为单类 pothole或 road_pothole不要多个类别——因为破裂、坑槽、沉陷在视觉上边界模糊分成了多类只会让模型在类别预测上浪费大量 capacity而对定位精度没有实质帮助。数据集的标签如果已经是单类直接填 id 0 即可。2.3 标签来源是 VOC 或 COCO把矩形坐标算术还原成 YOLO 格式有的数据集不会直接给 YOLO 格式的 txt而是给一个 via_region_data.jsonVIA 标注工具导出格式或者 coco 的 annotation.json。YOLO 格式和 VOC/COCO 格式唯一的区别是VOC/COCO 存的是矩形框左上角和右下角的绝对像素坐标YOLO 存的是中心点和宽高的归一化比例。一次四项除法就能转过去。import json, os from PIL import Image def convert_via_to_yolo(json_path, img_dir, out_dir): with open(json_path, r, encodingutf-8) as f: data json.load(f) for img_id, regions in data.items(): img_path os.path.join(img_dir, regions[filename]) img Image.open(img_path) img_w, img_h img.size base_name os.path.splitext(regions[filename])[0] out_lines [] for region in regions[regions]: shape region[shape_attributes] xmin shape[x] ymin shape[y] w shape[width] h shape[height] # YOLO 要求归一化后的中心点坐标与宽高 x_center (xmin w / 2.0) / img_w y_center (ymin h / 2.0) / img_h norm_w w / img_w norm_h h / img_h out_lines.append(f0 {x_center:.6f} {y_center:.6f} {norm_w:.6f} {norm_h:.6f}) with open(os.path.join(out_dir, base_name .txt), w) as f: f.write(\n.join(out_lines))转的时候重点检查两个地方。第一个是坐标系——VIA 导出的是以图像左上角为原点的像素坐标直接算就行但如果原始标注是从 GIS 或地形图导出的经纬度坐标得先做投影换算。第二个是浮点精度——YOLO 格式对精度要求不高小数点后六位足够但如果你干脆用整型除法宽度为 0 的异常框会被正确识别出来。2.4 验证集占比和类别分布为什么值得看1990 张图像的标注正常拆分比例是 1600 张训练 390 张验证约 8:2或者 1791 训练 199 验证约 9:1。拆分不要随手改关键是验证集里必须有不同光线下、不同路面材质沥青、水泥、砖石的坑洞照片否则模型验证集的 loss 会很低但拿到真实道路上效果很一般。这个现象在坑洞检测里极为常见本质是验证集分布和训练集太像模型相当于「背题」。类别分布方面看看 txt 里文件的行数分布是不是偏的厉害——有的图只有 1 个框有的图有 7、8 个框。如果同一场景的连续帧占了一大半要注意模型可能把「同一坑洞的多角度照片」当成多个样本这会让训练集的有效多样性和数量不符。实际情况中这 1990 张图大概率是行车记录仪或手机拍摄同一路段的坑洞可能出现多帧去重比单纯扩样本量有意义。3. 训练前先验货用脚本把标签越界、漏标、坏图全部拦下来3.1 为什么 1990 张的标签纸面没问题训练还是总翻车上一节说了看目录和格式但真正要跑到训练阶段之前还得做一个自动化的「体检」。标签越界是坑洞数据集翻车最常见的原因——标注框的坐标可能超过图片宽高比如 x_center1.03这会导致 loss 计算里的 anchor 匹配逻辑直接挂掉训练出来的模型在推理阶段对边界处的坑洞完全没有召回能力。有没有一种可能数据集在打标时工具本身允许这种越界是的LabelImg 在放大图片边缘标注时随手拖出边界的情况非常普遍。有一个非常隐蔽的问题图片尺寸不一致。如果数据集里同时有 1280x960 和 1920x1080 两种分辨率但标注软件按原始尺寸归一化后虽然 YOLO 格式的坐标值不受影响但 data.yaml 中的 width、height 参数如果写死成某一种训练时 OpenCV 做 resize 就会把宽高比拉变形坑洞变成椭圆几何特征被破坏。这个必须提前分辨。3.2 写一个完整的数据体检脚本我一般会在拿到任何 YOLO 数据集后先跑这一小段脚本把标签越界、空标签、类别 id 越界、图像文件损坏这四类问题一次性找出来。import os from PIL import Image from collections import Counter IMG_DIR images/train LABEL_DIR labels/train valid_cls_ids {0} # 假设单类 pothole issues [] empty 0 cls_counter Counter() for fname in os.listdir(LABEL_DIR): if not fname.endswith(.txt): continue img_name fname.replace(.txt, .jpg) img_path os.path.join(IMG_DIR, img_name) label_path os.path.join(LABEL_DIR, fname) # 检查 1图片能否正常打开损坏或截断的图直接标记 try: img Image.open(img_path) img.verify() img_w, img_h Image.open(img_path).size except Exception: issues.append(f坏图: {img_path}) continue lines open(label_path, r).read().strip().splitlines() if len(lines) 0: empty 1 issues.append(f空标签: {label_path}) for line in lines: parts line.strip().split() if len(parts) ! 5: issues.append(f字段数不对: {label_path}: {line}) continue cls_id int(float(parts[0])) x_c, y_c, w, h map(float, parts[1:]) if cls_id not in valid_cls_ids: issues.append(f类别ID越界: {label_path}: {cls_id}) # 检查 2归一化坐标与宽高越界检查 if x_c 0 or x_c 1 or y_c 0 or y_c 1: issues.append(f中心点越界: {label_path}: {line}) if w 0 or h 0 or x_c w / 2 1.001 or x_c - w / 2 -0.001: issues.append(f边界溢出: {label_path}: {line}) if y_c h / 2 1.001 or y_c - h / 2 -0.001: issues.append(f边界溢出: {label_path}: {line}) cls_counter[cls_id] 1 print(f总标注框数: {sum(cls_counter.values())}) print(f类别分布: {dict(cls_counter)}) print(f空标签文件: {empty}) if issues: print(f发现 {len(issues)} 个问题前 20 条:) for it in issues[:20]: print(it) else: print(数据体检通过)这段脚本的逻辑分三层图片校验、标签行结构校验、坐标几何校验。图片校验用 PIL 的 verify 方法它在文件读取阶段就能捕获 I/O 损坏不用每次都完整解码。坐标校验的边界特意放宽到 0.001因为在 resize 和图像增强时会有微小抖动严格等于 1 会误伤边缘框。如果脚本输出空标签文件数量大于 0说明打标时漏标了一部分图像这些负样本其实可以被模型当成背景参考但训练时 YOLO 会忽略它们最好补标或删除否则训练集和验证集的背景分布不一致。3.3 体检发现问题后怎么处理如果越界框只有两三个直接手工改对应 txt 的坐标——把越界方向的中心点和宽度压回 1 以内就行。如果空标签超过 5%建议整张图像从数据集里拿掉。还有一类问题叫「小框过多」有的图上坑洞只有 12x12 像素在 640x640 输入下缩到 5x5对任何检测模型都是纯噪音。这类样本不要硬留删掉或者把整张图放大后重新标注。默认 YOLOv8 的输入尺寸是 640x640你的训练图如果不统一建议在 data.yaml 里设成统一的尺寸但前提是脚本已经帮你确认没有宽高比严重畸变的图。4. 用 YOLOv8 在本地跑通道路坑洞检测训练参数设置与推理阈值调优4.1 环境与目录准备训练这个 1990 张的数据集不需要上大显卡常规做法是直接用ultralytics这个 pip 包依赖简单训练和推理都在同一套命令体系下。Anaconda 环境下建一个干净的虚拟环境比较稳妥。conda create -n yolo python3.10 -y conda activate yolo pip install ultralyticsultralytics会连带安装 torch、torchvision、opencv-pythoncup 版本能跑得动但很慢训练建议提前确认 nvidia-smi 能看到 GPU。这块坑最多的时版本不匹配torch 2.x 配 CUDA 12 是当前常见组合。数据集的 yaml 文件按下面的方式配置路径用绝对路径更省心。# pothole.yaml train: /your/path/pothole-dataset/images/train val: /your/path/pothole-dataset/images/valid nc: 1 names: [pothole]这里的nc是类别数names列表里的标签名称会出现在推理可视化文件的角标上。如果你的数据集实际是多类标签改成对应数量和名字就行。4.2 训练命令、起始权重和几个必调参数最简单的训练命令是直接指定yolo子命令和taskdetectyolo detect train datapothole.yaml modelyolov8s.pt epochs120 imgsz640 batch16 patience20 projectrun/pothole nameexp1站在一线工程角度我对刚拿到手的数据集有几句实在话不要一上来就用yolov8x这类大模型1990 张图训大模型非常容易过拟合最后验证集 mAP 很好看一到真实道路场景就露馅。yolov8s是这个数据量下性价比最稳的起点。如果你机器显存只有 6G把batch降到 8如果训练时损失nan或者 loss 曲线一直不降把lr0从默认的 0.01 降到 0.005。这里关键参数的含义分别是imgsz640是输入分辨率坑洞属于中小目标别降到 320否则小坑直接消失patience20是早停耐心值验证集指标连续 20 个 epoch 不涨就提前停能省一半时间epochs120对这个小数据集是足够的跑满 120 轮单卡大约一个半小时到两小时。我自己跑这个量级的数据一般 60 轮左右精度就收敛了后面都是平台期。训练完会在run/pothole/exp1/weights/下生成best.pt和last.pt。用哪个永远只用best.pt——它保存的是验证集表现最好的一轮last.pt是为了断点续训用的。4.3 推理参数置信度门限和 NMS 的 IoU 阈值怎么调训练完之后直接跑推理先不急着用默认参数而是针对坑洞场景调整两个推理门限。YOLOv8 的 predict 默认conf0.25置信度门限iou0.7NMS 去重的 IoU 阈值。实际在道路坑洞检测场景里默认参数表现不稳定。YOLO 置信度门限的含义一个框被判定为目标的概率门槛。坑洞检测和通用目标检测不一样——坑洞的形状和阴影、裂缝、水渍高度相似模型输出的置信度天然偏低。conf0.25会漏掉很多浅色水泥路面上的浅坑但如果你直接拉到 0.05整条路面的裂缝和黑色轮胎印都会被当成坑洞标出来。我习惯的做法是conf0.08到0.15之间先跑一遍宁可多框一点再用后处理器面积、位置、宽高比过滤。比如用下面的命令yolo detect predict modelrun/pothole/exp1/weights/best.pt sourcetest_images/ conf0.12 iou0.5 saveTrueiou0.5的意思是在 NMS 阶段两个框的 IoU 超过 0.5 就合并。坑洞检测里坑洞之间经常挨得很近或者在同一个大坑里标注了多个小坑如果 IoU 阈值太高0.7NMS 会把两个并排挨着的坑洞合并成一个导致召回率莫名其妙下降。这三个参数conf、iou、imgsz是坑洞检测上线前必调的一张参数速查表帮你们省时间参数默认值坑洞场景建议原因conf0.250.08 - 0.15坑洞与阴影视觉相似置信度天然偏低门限太高漏检iouNMS0.70.4 - 0.5相邻坑洞合并严重降低阈值保住召回imgsz640640 或 800小坑洞需要足够像素320 会让小目标完全消失5. 道路坑洞训练避坑五个 I 踩过的具体问题5.1 训练集会报 Dataset not found 或 labels 找不到现象按教程写了 data.yaml训练启动时报AssertionError: train dataset not found。原因三个可能——yaml 里的路径用了相对路径但当前工作目录不对Windows 上路径分隔符写成了反斜杠 转义符把路径搞乱了或者标签和图片目录的命名和代码预期的images/train对不上比如你用的是train/images而代码找images/train。解决把 data.yaml 的 train/val 全部改成绝对路径并在终端先ls确认目录真的存在。不要用~/pothole这种缩写YAML 不会替你展开波浪号。5.2 训练 loss 不下降或出现 NaN现象训练跑到第 3 个 epochtotal loss 卡在 9 点几不往下走更糟的是直接变 NaN。原因最常见的是标签越界坐标溢出导致在 anchor 匹配阶段出现除零或 log(0)其次是学习率太大YOLOv8 默认提供lr00.01碰到不规则标注分布的时候直接发散。解决先跑第 3 节的体检脚本把越界坐标清掉然后把lr0降到0.005batch从 16 降到 8。如果还是 NaN检查标签文件里是否有单行只有 class 没有坐标的情况——某些标注工具保存时会把空框写成0一个字符。5.3 验证集 mAP 很高拿到现实道路上全是误检现象训练结束 mAP0.5 到了 0.9但用行车记录仪拍一段路面全路段标记了二十几个「坑洞」实际只有三个。原因典型的过拟合 验证集分布与训练集太像。你的验证集都是从同一批照片里抽出来的光线、拍摄高度、路面颜色高度一致mAP 根本不能反映泛化能力。另一个原因是灰度分布太窄——很多数据集的图是行车记录仪拍的颜色偏灰偏暗模型把一切暗色区域都学成了坑洞。解决这是落地阶段最血泪的一环。我后来测模型效果一律不用 mAP而是直接拿一段没见过的道路视频最好是另一台设备拍的逐帧跑推理人工统计误检率。坑洞检测的正样本在真实场景里是稀疏的正确率就算只有 30%只要误检框密度低每公里少于 5 个就可以过初审。5.4 沥青修补块和阴影被当成坑洞且占了误检的大头现象新铺的修补块边缘颜色和旁边路面颜色有差别模型全都框出来标成 pothole。原因坑洞和修补块在视觉上高度相似——都是深色调都有不规则边缘。更重要的是路面阴影方向树影和建筑物阴影投在平坦的路面上模型照样置信度 0.9 输出。这不是数据集标注错是任务的本质难点。解决拉高 conf 到 0.3阴影和修补块会被过滤掉一部分然后加一层规则过滤比如「检修孔的圆形度 0.7 且面积 500 像素判定为井盖」「外接矩形宽高比 3判定为裂缝/伸缩缝」。模型负责召回规则负责精度这是市政巡检车项目的常规做法。千万别指望单靠模型解决全部误检。5.5 一张图上同一个坑洞被标注了 2 到 4 次训练时 NMS 直接崩现象loss 一度不降然后 NMS 把相邻框合并后 mAP 虚高。原因打标工人在坑洞边缘反复标记一张图里同一个坑洞出现好几个框彼此 IoU 0.6-0.9。训练本身对这种重叠标签并不惩罚因为 YOLO 的 loss 对这种冗余 label 进行了容忍处理但验证时 NMS 一压预测框被合并看起来 mAP 高其实模型学的是「同一目标多分辨率冗余预测」而不是这个坑洞本身。解决完全的解决办法是数据清洗但这个 1990 张的图集没法嘻哈地全查一遍。好用的取巧方式是训练时开启mosaic0并提高iou0.5来缓解te训练完成后用权重本身跑一遍训练集把所有框置信度大于 0.5 的预测框和 gt 的 IoU 算出来IoU 大于 0.9 的 gt 视为重复标注从标签文件里删掉。这样既保住标注信息又把冗余降下来了。6. 从训练到能用的最后一步用真实道路视频验证和防误检规则如果你只是想把模型跑通交差看到 mAP 高就够了但如果是真想装到巡检车或无人机里用你得验证三件事小坑洞的召回率、阴影下的误检率、以及连续视频帧的稳定性。我自己会准备一个「验证专用」目录里面放几段不同路面刚铺好的沥青路、旧水泥路、有大面积树荫的路段的短视频。用 best.pt 跑一遍观察输出帧里能不能找到 20 米外的小坑洞——那是最容易被 conf0.12 过滤掉的。如果小坑全部没检出把 conf 降到 0.05 重新跑统计误检率升多少。这个通过「召回优先、规则擦后」的平衡才是坑洞检测上线的核心调参哲学。防误检规则我搭过一套简单但有效的把模型输出的每个框在灰度图上做一次局部方差计算方差小于阈值的区域视为路面纹理接近直接去掉。坑洞内部通常是破碎的纹理方差比平整沥青路高一截但比裂缝区低这个特征维度比单纯颜色可靠得多。有条件的话可以对检测框区域做第二个二分类模型坑洞 / 非坑洞输入是一张裁剪好的 64x64 灰度图那个小模型在真实场景能滤掉将近一半的阴影和修补块误检。关于锚框提一句YOLOv8 已经是 anchor-free 架构了不需要再用 K-means 聚类算 9 个锚框很多网上老教程还在教 YOLOv5 的锚框聚类放 v8 上是无效动作。如果你确实想提升小坑洞召回优先加一个注意力层或者把imgsz提到 800比调任何锚框参数都直接。最后落到一个小习惯每一次改完 conf、iou 或规则我都会把完整参数存在结果文件夹的名字里比如exp1_conf012_iou050_imgsz800。坑洞检测这种任务配方变一个数结果差出一大截参数黑匣子记录清楚回头排查玄学问题的时候能省出好几个小时。这套从验货到训练的流程希望对初次碰道路坑洞数据集的朋友有点帮助。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价