拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

路面病害数据集实战:从解压清洗到YOLOv8训练全流程

路面病害数据集实战:从解压清洗到YOLOv8训练全流程 简介面向道路病害检测与智能巡检场景这份资料包提供了一套经过人工标注的道路病害标注数据覆盖城市街道、高速公路、乡村道路、住宅区等多种道路场景可用于道路病害的识别与定位。包内共2000个文件主体为1998个XML格式标注文件另附1个Python辅助脚本和1份说明文本压缩包总大小约411.72MB。XML标注由labelimg工具生成记录了每个病害实例的边界框位置、类别标签与属性信息可直接作为YOLO、SSD、Faster R-CNN等目标检测模型的训练标签附带的Python脚本可辅助用户进行数据处理说明文本则介绍了目录结构与标注格式便于直接对接自己的训练流程。整体省去了实地采集与人工标注的大量时间适合算法工程师、科研人员以及高校学生在道路病害识别、路面状况评估等项目中快速验证与工程落地。目前已有380人学习/下载具备较好的参考价值。1. 优质道路病害数据集5.zip是什么一份能结束“缺数据”拉扯的路面病害训练包别人丢过来一个优质道路病害数据集5.zip第一反应不该是“又来一个压缩包”而应该是“训练车可以启动了”。这个 zip 通常不是文档也不是随手攒的照片堆而是把路面病害检测最缺的原料打包好了成段的道路图像、对应的标注框、类别说明偶尔还带上切好的 train/val 划分。它面向的是 yolov8、yolov5 这类目标检测训练管线也能给 mmrotate 之类的旋转框任务当平替数据。适合谁适合正在做公路巡检、市政道路病害识别的算法工程师也适合拿不到现场数据的学生。下面不绕圈子直接把“拿到 zip 之后的三步”讲透拆包盘点、接进 YOLO、清洗验收。2. 拆包盘点这一步不能省先确认压缩包里是 YOLO txt、VOC xml 还是 COCO json2.1 解压前先建独立工作目录中文条目名与伪加密是第一个坑拿到优质道路病害数据集5.zip后我一般先单独建个目录不直接在下载目录里解压。原因很现实很多这类 zip 是标注完用 Windows 压缩的顶层目录名、图片文件名都可能带中文在 Linux 服务器上用unzip解出来轻则乱码重则整个目录树乱掉。不要用unzip xx.zip一把梭先建目录再解压失败成本最低。mkdir -p road_disease cd road_disease unzip -o ../优质道路病害数据集5.zip find . -maxdepth 2 -type d | sort-o表示覆盖已有文件适合反复解压的场景find只看目录结构不把几千张图刷到终端上。如果这里输出全是乱码目录名别急着改文件名先检查 zip 的注解编码常见做法是用unzip -O gbk指定 GBK 编码重解一次或者直接用 Python 的zipfile按条目重写文件名。还有一种更隐蔽的情况是伪加密unzip提示需要密码但实际文件没有加密只是 zip 头部的通用标志位被置位了。这种文件用7z x大概率能直接解开或者用 Python 的zipfile逐个 entry 读取后重写一个新 zip。数据目录这一层乱掉的代价比后面模型训不好还难折腾所以要在最开始处理掉。2.2 用后缀统计和标注头判断格式txt、xml、json 各走各的路解压完成后先不要打开图片凭肉眼判断数据质量。先把整个数据集的文件后缀频次统计一遍这决定了后面所有脚本怎么写。from pathlib import Path import collections root Path(road_disease) files [p for p in root.rglob(*) if p.is_file()] exts collections.Counter(p.suffix.lower() for p in files) print(exts.most_common(10))如果jpg/jpeg/png和txt数量接近大概率是 YOLO 格式标签如果图片旁边全是.xml则是 VOC 格式出现一堆.json则要区分是 COCO 标注还是超参文件。这个区分不是形式问题后面写 data.yaml、转格式、算类别数全都依赖这里。常见做法是只按照实际识别到的格式写后续逻辑而不是同时支持三种格式——那样反而容易出边界错误。接着抽一个标签文件看内容import random txts list(root.rglob(*.txt)) sample random.choice(txts) print(sample) print(sample.read_text(encodingutf-8, errorsreplace)[:300])看到0 0.6311 0.4681 0.0212 0.0193这样的行就是 YOLO 的相对坐标格式五个字段分别是类别 id、中心点 x、中心点 y、宽、高数值都在 0 到 1 之间。看到annotation标签就是 VOC XML看到images和annotations两个键则是 COCO JSON。这步挑出几个典型文件看前几行即可不用完整读一遍。如果某类文件特别少比如只有几十个 txt而图片有几千张那说明标签可能只覆盖了局部图片这类数据直接训练会漏检严重后面要重点检查。2.3 类别编号能不能直接信先用脚本把标签里的类别 id 拉出来“优质”两个字出现在标题里不等于类别编号就是干净连续的。我见过太多例子txt 标签里出现cls0、cls4但类别说明只写了三类还有cls0是背景框把行人标注框误当成路面裂缝。所以拿到标签后第一件事就是把整个数据集的类别 id 分布统计出来而不是直接去写 names。from collections import Counter from pathlib import Path counter Counter() for p in Path(road_disease).rglob(*.txt): for line in p.read_text(encodingutf-8, errorsreplace).splitlines(): parts line.split() if len(parts) 5 and parts[0].replace(., ).isdigit(): counter[int(float(parts[0]))] 1 print(counter)输出类似Counter({0: 12403, 1: 3421, 2: 188})。看到稀疏类别时就要有心理准备坑槽这类样本如果只有一百多个后续训练即使能跑通这一类别的 AP 也不会稳定。统计完之后把类别 id 与类别名做个映射存下来后面第 3 章的 data.yaml 直接照着填。如果发现 id 跳号比如只有 0 和 2没有 1YOLO 会在读取标签时直接报错。宁可自己写脚本把 2 改成 1也不要去 names 里留空洞。3. 把数据集接进 YOLOv8目录重整、data.yaml 与首轮训练命令3.1 目录重整无论原来长什么样都改成 images/labels 并行的结构YOLOv8 不会替你猜目录它只认images和labels两个根目录下面再按train、val分。路面病害数据集常见的原始布局有两种一种是train/images和train/labels已经并列另一种是把所有图放在一个大目录里旁边配一个split.txt写明哪些是训练、哪些是验证。如果是后者就写一个短脚本做分配。from pathlib import Path import random, shutil root Path(road_disease) split_file root / split.txt # 按 split.txt 分组没有 split.txt 就按 85/15 随机切 split_map {train: [], val: []} if split_file.exists(): for raw in split_file.read_text().strip().splitlines(): raw raw.strip() if not raw: continue if raw.startswith(val): split_map[val].append(raw) else: split_map[train].append(raw) # 收集所有图片 all_imgs list((root / images).glob(*.jpg)) list((root / images).glob(*.png)) if not split_map[train] and not split_map[val]: random.seed(0) random.shuffle(all_imgs) n_val int(len(all_imgs) * 0.15) split_map[val] all_imgs[:n_val] split_map[train] all_imgs[n_val:] for split_name, items in split_map.items(): img_dst root / images / split_name lbl_dst root / labels / split_name img_dst.mkdir(parentsTrue, exist_okTrue) lbl_dst.mkdir(parentsTrue, exist_okTrue) for item in items: src_img root / item if not src_img.exists(): continue shutil.copy(src_img, img_dst / src_img.name) src_lbl root / labels / src_img.stem .txt if src_lbl.exists(): shutil.copy(src_lbl, lbl_dst / src_lbl.name)这段脚本的规则很简单split.txt里以val开头的行进验证集其余进训练集。没有split.txt时随机切random.seed(0)保证可复现。复制而不是移动是为了给后面清洗留后悔药如果硬盘吃紧把shutil.copy换成os.symlink可以省一半空间。跑完之后用tree -L 2 images labels确认目录结构。目录结构对了还要验证图片和标签是否同名成对。YOLO 的做法是图片路径和标签路径仅在扩展名上不同a.jpg对a.txt否则训练时会疯狂跳过样本。for split in [train, val]: imgs {p.stem for p in (root / images / split).glob(*) if p.suffix.lower() in {.jpg, .jpeg, .png}} lbls {p.stem for p in (root / labels / split).glob(*) if p.suffix.lower() .txt} print(split, 图片, len(imgs), 标签, len(lbls), 缺标签, len(imgs - lbls), 无图标签, len(lbls - imgs))缺标签的图片要么删掉要么重新标注无图标签一般是上次导出时残留的脏文件可以直接删。这一步不做后面训练日志里出现“出现标签但图像缺失”这类警告时你会花更多时间到处找原因。3.2 data.yaml类别名必须跟标签编号对表YOLO 的配置文件是 YAML内容不多但每一个字段都直接影响训练。以第 2.3 节的类别统计为例假设最后确认有三类裂缝、坑槽、修补就写成下面这样。path: /workspace/experiments/road_disease train: images/train val: images/val nc: 3 names: 0: crack 1: pothole 2: patchpath写绝对路径最稳不要写相对路径否则换终端、换工作目录时 YOLO 会找不到数据。train和val相对于path写nc必须和names的长度一致。这里最容易翻车的是标签里的类别 id 是从 1 开始的而不是从 0 开始。如果你发现第 2.3 节的统计结果是{1: ..., 2: ..., 3: ...}不要直接把 names 从 1 写起而是写一个脚本把标签全体减 1让 id 归到0 ~ nc-1。YOLO 的类别索引是拿 id 直接查 names 列表的id 为 1 时它不会跳过第 0 位而是直接报索引越界。3.3 首轮训练用最小配置n 模型、1280 分辨率、30 个 epoch第一次训练千万不要直接上yolov8x也不要用 640 分辨率。路面裂缝是典型的细长小目标一条缝只有几个像素宽640 下采样后可能只剩一个点。我的首轮训练命令通常是cd /workspace/experiments/road_disease yolo detect train \ datadata.yaml \ modelyolov8n.pt \ epochs30 \ imgsz1280 \ batch8 \ patience10 \ close_mosaic10 \ device0yolov8n是参数量最小的版本先用来验证数据链路是否通跑一轮只需要十几分钟。imgsz1280对小目标召回率提升非常明显如果显存不够改成imgsz960再搭配rectTrue让图像按长宽比分组填充能省不少显存。batch8是 BN 层比较稳的下限不是越大越好。close_mosaic10的意思是最后 10 个 epoch 关闭马赛克增强避免模型在收敛后期还在看大量被切碎的目标。如果训练日志里 mAP 一路不涨先怀疑数据再怀疑超参不要先怀疑模型结构。4. 数据质量决定模型上限坏图清洗、标签纠偏与增强边界4.1 用 Laplacian 方差和“无可读标注”做第一轮清洗数据集标题里写“优质”但在实际项目里任何第三方数据集都要先过一遍图像质量筛查。道路病害图像大多来自车载相机或手持拍摄最容易出问题的是虚焦和过曝。虚焦的裂缝图人眼都看不清裂缝边界模型更不可能从里面学到有效特征。用 OpenCV 的 Laplacian 算子算灰度图的方差是成本最低的模糊检测方式。import cv2 from pathlib import Path img_dir Path(road_disease/images/train) for p in img_dir.glob(*): if p.suffix.lower() not in {.jpg, .jpeg, .png}: continue img cv2.imread(str(p), cv2.IMREAD_GRAYSCALE) if img is None: print(无法读取, p) continue lap cv2.Laplacian(img).var() if lap 30: print(疑似模糊, p, round(lap, 2))阈值 30 不是绝对标准。白天光照均匀的沥青路面清晰图像方差通常在 50 以上阴天或夜间可能偏低。我一般先跑一遍看分布再把低于整体 5% 分位数的图挑出来人工抽检。另外要注意过曝和反光太阳直射下沥青路面会产生大片白色高光裂缝被完全吞掉。这种图 Laplacian 方差可能很高但目标区域没有语义靠亮度直方图做一次过滤更稳妥。gray cv2.imread(str(p), cv2.IMREAD_GRAYSCALE) if gray is not None and (gray 250).mean() 0.5: print(高光面积过大, p)4.2 标签规范检查越界框、零面积框和小碎框一次揪清图像清洗之后是标签清洗这也是从“能跑”到“能用”最关键的一步。YOLO 标签是归一化坐标数值应该都在 0 到 1 之间但标注软件导出时偶尔会出现 1.0000001 这种浮点误差。训练时不一定会报错但边框计算会偏移。越界框、宽高为 0 的框、以及面积小到只剩几个像素的碎框都应该在训练前清掉。from pathlib import Path def clean_label(path, min_w0.005, min_h0.005): lines [] for raw in path.read_text(encodingutf-8, errorsreplace).splitlines(): parts raw.split() if len(parts) ! 5: continue cls, x, y, w, h parts x, y, w, h float(x), float(y), float(w), float(h) if w min_w or h min_h: continue if x - w / 2 0 or x w / 2 1 or y - h / 2 0 or y h / 2 1: continue lines.append(f{cls} {x:.6f} {y:.6f} {w:.6f} {h:.6f}) path.write_text(\n.join(lines), encodingutf-8) for p in Path(road_disease/labels).rglob(*.txt): clean_label(p)min_w和min_h取 0.005 意味着在 1280 分辨率下目标只有 6.4 个像素宽。比这更小的标注框基本是噪声保留只会让模型在 loss 计算时被极小框带偏。越界框的修正不是简单剪裁因为框的中心点可能已经在图像外直接保留会让模型学到错误的中心分布。我一般直接删掉这类框而不是 clamp因为路面病害很少恰好出现在图像边界处删掉几条边界样本影响很小。4.3 增强参数不是越高越好细裂缝数据集最怕过激变换很多同学拿到数据集后直接把 YOLOv8 默认增强拉满结果裂缝的 mAP 反而比不增强更低。原因是裂缝是细长结构过大的旋转和缩放会破坏裂缝的连续性。默认增强里的degrees0.0对路面病害其实是合理的车辆行驶方向决定了裂缝在图像中的姿态天然比较稳定不需要让模型去学一个“倒挂在天上”的裂缝。yolo detect train \ datadata.yaml \ modelyolov8n.pt \ epochs50 \ imgsz1280 \ batch8 \ hsv_h0.02 \ hsv_s0.5 \ hsv_v0.4 \ fliplr0.5 \ mosaic0.8hsv_h只给 0.02因为路面颜色本身变化不大色相大量偏移会让模型去学不存在的颜色分布hsv_s和hsv_v可以给大一点用来模拟光照变化和阴影。水平翻转保持 0.5 是安全的垂直翻转千万别开路面图像翻转后语义会完全反掉。马赛克增强保留 0.8但对裂缝这种小目标马赛克会把一条完整裂缝切成两半所以最后 10 个 epoch 记得用close_mosaic10关掉它让模型有机会看到完整的裂缝形态。4.4 类别不均衡小样本类别优先做轻度复制而不是粗暴过采样统计标签后如果发现坑槽只有两三百个框裂缝有一万多个框训练时小类别很容易被大类别淹没。常见做法是给坑槽类的图片做按比例复制但不要整图无脑复制。我一般把坑槽样本挑出来做水平翻转、小幅旋转±10 度、色彩抖动后再作为新样本放进训练集。这里有一个度同一条路不同位置拍的坑槽如果只是复制三份模型很可能过拟合到重复纹理而不是坑槽的真实形态。复制比例我控制在 3 倍以内效果不够就继续加 Mosaic 混合而不是继续复制。类别原始框数增强方式处理后框数crack12403弱增强12403pothole342翻转旋转色调抖动1368patch188复制马赛克混合752这张表只是示例实际类别和数量以第 2.3 节统计为准。总体原则是大类别增强不动小类别增强做加法最终比例尽量控制在 10:1 以内。5. 路面病害训练常见踩坑zip 伪加密、标签串类与小目标丢失的排查记录5.1 zip 能解压却一直要密码或者解出来目录乱码现象unzip在优质道路病害数据集5.zip上提示need password但问发布者又说没加密。或者解压后目录名、文件名全是类似ÎïÁϼ¯的乱码。原因前一种多半是 zip 伪加密文件头里的 general purpose bit flag 被置位了文件本身并没有加密后一种是 zip 内部文件名用了 GBK/GB2312 编码而 Linux 的 unzip 默认按 UTF-8 解码。解决伪加密用7z x一般能直接解开因为 7z 会读取实际数据流而不是只看标志位。中文乱码用unzip -O gbk重解一次如果你的 unzip 版本不支持-O就用 Python 的zipfile.ZipFile遍历条目逐条转码后写回磁盘。这里要特别提醒不要在解压乱码后手动一层层改文件名几十个目录还能忍几百个目录会改到怀疑人生。5.2 标签 id 看着连续实际含义串位现象训练时 loss 正常下降但验证集里裂缝的预测框大量落在护栏、行人或者车道线上查看混淆矩阵发现crack和background混在一起。原因这类数据集在制作时可能发生过一次“类别重映射”。比如原始标注里0是完好路面1是裂缝2是坑槽但整理者为了 YOLO 训练把完好路面删掉了却没有改裂缝的 id导致1仍然是裂缝names 表里却把0写成了裂缝。解决不要直接信任 names 表。用第 2.3 节的类别统计先看 id 范围再随机抽每个类别 20 张图把标注框画出来人工确认类别名。如果发现串位写一个全局映射脚本把标签里所有 id 做替换替换完再抽 5 张复查。这一步是纯体力活但漏掉它的代价是模型上线后误报率根本压不住。5.3 细裂缝完全检测不到尤其是远处小目标现象训练跑完mAP50 看着还行但拆开看单类指标裂缝的 recall 只有 0.3而且漏检的大多是图像下半部分远处的小裂缝。原因裂缝像素宽度只有一两个像素在 640 分辨率下经过下采样后目标响应非常弱再加上马赛克增强会把长裂缝切碎模型只学到了碎片特征自然认不出完整裂缝。解决第一招把imgsz提到 1280第二招把close_mosaic从默认 15 改成 20让模型在最后阶段看到更多完整目标第三招在损失函数上让小目标权重更大YOLOv8 里可以用scale相关配置但更直接的做法是把小目标类别的损失倍率调高。如果显存不够 1280就先用 960 跑通再逐级上调不要一开始就用 640 将就。5.4 验证集和训练集出现同一路段的不同帧mAP 虚高现象训练验证曲线很正常验证 mAP 有 0.85但把模型拿到另一条路上跑效果立刻跌到 0.5。原因很多道路病害数据集是按单帧图像随机的同一路段前后几十帧都被拆进了训练集和验证集。图片高度相似验证集失去了“没见过的路”的意义。解决拿到数据先看文件名。如果文件名里带时间戳或里程桩号就按路段分组再做 train/val 切分而不是按帧随机切如果文件名没有明显分组就按拍摄时间聚类把连续时间段的图像归到同一组。验证集切分的标准是“模型没见过这条路”而不是“模型没见过这一帧”。这是回灌模型性能最容易被高估的一环。5.5 训练集里没有负样本实拍时把新旧修补误报成坑槽现象模型在验证集上精度不错但部署到真实路段后把路面上的旧修补、油污、水渍全部框成 pothole。原因数据集的“优质”通常指正样本齐但缺少“看起来像病害但不是病害”的负样本。没有负样本参与训练模型会把所有暗色纹理和破损纹理当作目标。解决从实际巡检测算机里挑一段没有病害的连续视频抽帧后不标注任何框作为背景图混入训练集。YOLO 训练时没有目标框的图也参与背景分类能明显压低误报。我一般按 10% 的比例混入背景图跑完再拿那一段视频截图做验证误报率通常能降一半以上。这一步在公开数据集里经常被忽略但做工程项目时绕不开。6. 最终验收技巧单类 AP 和混淆矩阵比总 mAP 更值得看总 mAP 会掩盖很多问题裂缝类特别明显。裂缝样本多、背景难分少拉高总 mAP坑槽样本少、形态复杂单类 AP 惨不忍睹。所以我的验收流程从来都是先跑一次完整的 val再拆开看每一类。yolo detect val \ modelruns/detect/train/weights/best.pt \ datadata.yaml \ plotsTrue跑完目录里会多出confusion_matrix.png和results.png。先看混淆矩阵找两件事第一crack是不是被错分到pothole这类错分通常来自标签清洗不彻底第二哪一类被分到background背景列占比最高的类别就是你模型真正的短板。再看每个类别的召回率results.png里metrics/recall(B)是总体召回单独的每类召回还要额外算。yolo detect val \ modelruns/detect/train/weights/best.pt \ datadata.yaml \ class1class1会把验证集过滤成只含 pothole 一类直接看这个类别的 precision、recall 和 mAP。我一般要求每类 mAP50 在 0.7 以上只有一类低于 0.6 就认为数据集不足以支撑部署需要补样本或重新清洗。对于裂缝这种小目标额外加一条检测框宽高比大于 5:1 的目标单独算一次召回。很多模型总 mAP 好看但长条裂缝的召回率往往只有零点几这个指标没有可视化工具只能自己写过滤逻辑。最后把清洗脚本、data.yaml、划分方式连同模型权重一起存档一个月后模型迭代时你大概率会忘了当时用的哪一版数据。教训是所有“优质”数据集只有经过自己的清洗和验证才能算数。希望这些落地细节帮到你早点跑通自己的路面病害检测管线。本文还有配套的精品资源点击获取
返回列表