
简介这份航拍路面病害识别数据集面向从事目标检测与缺陷检测的深度学习开发者、科研人员及学生提供可直接投入训练的标注数据解决路面裂缝、坑槽等病害样本获取难、标注成本高的问题。资源包共约2000个文件以1999个txt标签文件和1个yaml配置文件为主压缩包大小约194MBtxt文件对应YOLO格式的边界框标注yaml文件则记录类别名称与数据路径等训练配置信息。数据集包含3151张航拍图像覆盖Longitudinal crack、Transverse crack、Alligator crack、Oblique crack、Repair、Block crack、Pothole共7个类别并同时提供VOC格式的xml标签图片与标签已按训练集、验证集、测试集划分完毕可直接用于YOLO系列、Faster R-CNN、SSD等模型训练。目前已有390人学习下载适合需要快速搭建路面病害检测基线、验证算法效果或开展对比实验的读者使用。1. 航拍路面病害识别数据集为什么标注质量比模型结构更决定上限去年帮一个市政养护团队做路面巡检方案他们原本用人工开车沿路拍一天覆盖不到 30 公里裂缝、坑槽、修补痕迹全靠肉眼判断漏检率保守估计三成以上。换成无人机航拍之后单架次 40 分钟能覆盖 8 到 10 公里路段影像分辨率能到厘米级问题变成了这么多图谁来标、怎么标、标完怎么训。航拍路面病害识别数据集本质是一批从无人机或高位视角拍摄的道路影像加上目标检测框标注类别通常包括横向裂缝、纵向裂缝、龟裂、坑槽、修补区域这几类。它和常规目标检测数据集最大的区别在于视角——俯视带来的透视畸变、光照不均、路面纹理干扰会让在 COCO 上跑得很好的模型直接翻车。适合谁用做市政巡检、道路养护、遥感目标检测方向的团队以及想拿真实场景练手目标检测的工程师。这一篇不讲空泛的综述只讲一件事拿到或自建这样一份数据集之后怎么把它变成能跑、能收敛、能落地的训练管线。从类别定义、标注规范到 YOLO 格式转换、训练参数、评估指标再到那些只有踩过才知道的坑我会按实际做过的顺序讲清楚。2. 数据集构建与标注规范类别定义决定模型天花板2.1 航拍视角下病害类别的划分逻辑很多人拿到航拍图第一反应是「裂缝就是裂缝」直接标一个类。这是最常见的翻车起点。路面病害在俯视影像里的形态差异极大横向裂缝是细长条龟裂是网状碎块坑槽是暗色不规则区域修补是颜色和纹理都不同的块状区域。如果全塞进一个类模型学到的特征会互相打架mAP 上不去而且部署时你根本没法根据检测结果决定养护优先级。我一般会按「养护动作」来定类别而不是按病害学名。养护队看到横向裂缝会灌缝看到坑槽会补料看到龟裂可能整段铣刨。所以类别划分建议至少拆成类别名英文建议典型形态养护动作横向裂缝transverse_crack垂直于行车方向的细长条灌缝纵向裂缝longitudinal_crack平行于行车方向的细长条灌缝龟裂alligator_crack网状、块状碎裂铣刨重铺坑槽pothole暗色不规则凹陷补料修补patch矩形或块状色差区域观察或重做类别数控制在 5 到 8 类之间比较稳。太少区分度不够太多标注一致性会崩。如果你们养护标准里还有「车辙」「泛油」可以加但每加一类标注成本大约增加 15% 到 20%而且类间边界会变模糊需要额外写标注细则。2.2 标注框的边界规则与一致性控制目标检测标注最怕的不是漏标是「同一个人前后标得不一致」和「不同人标得不一致」。航拍路面病害尤其严重因为裂缝边界本身就是渐变的一条裂缝从明显到消失可能跨几十个像素。我一般会定三条硬规则写进标注手册第一裂缝类标注框必须包住裂缝的可见主体两端各留 5 到 10 个像素余量但不要把旁边无关的纹理包进去。第二坑槽和修补类按实际边界标边界模糊时以颜色突变线为准。第三小于 16×16 像素的目标直接标为「忽略」不参与训练因为航拍图里这个尺度的目标连人眼都难确认强行标只会引入噪声。一致性控制靠抽检。每标完 200 张随机抽 20 张让另一个人重标算 IoU 一致性。如果同一类别的平均 IoU 低于 0.75说明标注细则有问题停下来改细则不要继续标。这个成本远低于训完发现 mAP 上不去再回头返工。提示标注工具用 LabelImg、CVAT 或 Roboflow 都可以但一定要导出成统一格式再进训练管线。中途换工具不换格式是后期数据清洗噩梦的源头。3. 从原始影像到 YOLO 格式转换脚本与四个边界坑3.1 目录结构与格式约定航拍数据集常见的原始格式有三种Pascal VOC 的 XML、COCO 的 JSON、以及直接导出的 YOLO txt。不管来源是什么进训练前统一成 YOLO 格式最省事因为 Ultralytics 系的训练脚本对这套格式支持最成熟。YOLO 格式的约定是每张图对应一个同名 txt每行一个目标格式为class_id x_center y_center width height全部归一化到 0 到 1。目录结构我一般这样组织dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── data.yamldata.yaml里写清楚路径、类别数和类别名path: ./dataset train: images/train val: images/val test: images/test nc: 5 names: - transverse_crack - longitudinal_crack - alligator_crack - pothole - patch3.2 VOC 转 YOLO 的转换脚本如果原始标注是 VOC XML用下面这个脚本转。注意它处理了图像尺寸读取和边界裁剪这两个点后面会讲为什么重要。import os import xml.etree.ElementTree as ET from PIL import Image # 类别映射必须和 data.yaml 里的顺序一致 CLASS_MAP { transverse_crack: 0, longitudinal_crack: 1, alligator_crack: 2, pothole: 3, patch: 4, } def voc_to_yolo(xml_dir, img_dir, out_dir): os.makedirs(out_dir, exist_okTrue) for xml_file in os.listdir(xml_dir): if not xml_file.endswith(.xml): continue tree ET.parse(os.path.join(xml_dir, xml_file)) root tree.getroot() # 用 PIL 读实际尺寸不要信 XML 里的 size 字段 img_name root.find(filename).text img_path os.path.join(img_dir, img_name) with Image.open(img_path) as im: w, h im.size lines [] for obj in root.findall(object): cls_name obj.find(name).text if cls_name not in CLASS_MAP: continue bbox obj.find(bndbox) xmin float(bbox.find(xmin).text) ymin float(bbox.find(ymin).text) xmax float(bbox.find(xmax).text) ymax float(bbox.find(ymax).text) # 裁剪到图像边界内防止越界导致归一化后为负 xmin max(0, min(xmin, w)) xmax max(0, min(xmax, w)) ymin max(0, min(ymin, h)) ymax max(0, min(ymax, h)) if xmax xmin or ymax ymin: continue xc (xmin xmax) / 2.0 / w yc (ymin ymax) / 2.0 / h bw (xmax - xmin) / w bh (ymax - ymin) / h lines.append(f{CLASS_MAP[cls_name]} {xc:.6f} {yc:.6f} {bw:.6f} {bh:.6f}) out_name os.path.splitext(xml_file)[0] .txt with open(os.path.join(out_dir, out_name), w) as f: f.write(\n.join(lines)) voc_to_yolo(./raw/xml, ./raw/images, ./dataset/labels/train)逻辑说明脚本先读 XML 里的 filename再用 PIL 打开原图拿真实宽高。参数说明CLASS_MAP必须和data.yaml的names顺序严格一致否则类别会错位训练出来的模型会把坑槽认成裂缝。边界裁剪那几行是血泪经验VOC 标注里经常出现 xmax 超过图像宽度的情况不裁的话归一化后坐标大于 1训练时会被静默丢弃或报错。3.3 四个容易翻车的边界坑第一个坑图像尺寸和标注尺寸不一致。有些标注工具会按缩放后的预览图记录坐标但 XML 里写的 size 是原图尺寸。如果你信了 XML 的 size 字段框会整体偏移。解决办法就是上面脚本里那样用 PIL 读实际尺寸。第二个坑类别名大小写和空格。Transverse_Crack和transverse_crack在映射表里对不上会被静默跳过导致某些类样本量骤减。转换后一定要统计每个类的框数量和原始标注对比。第三个坑空 txt 文件。有些图确实没有目标转换后会生成空 txt。YOLO 训练时空 txt 是合法的负样本但如果你把「没有目标」和「转换失败」混在一起就会把本该有目标的图当成负样本喂进去。建议转换后单独检查空 txt 对应的图确认是真的无目标。第四个坑中文路径。Windows 下用 OpenCV 或 PIL 读中文路径的图经常失败报错还不明显。数据集路径全用英文和数字这个习惯能省很多排查时间。4. 训练参数怎么设航拍小目标的 5 个必调项4.1 输入分辨率与 batch size 的权衡航拍路面病害的典型特点是目标小、长宽比极端。一条横向裂缝可能只占图像高度的 2% 到 3%但宽度能到 60%。默认的 640×640 输入会把细裂缝压得几乎看不见。我一般会把imgsz提到 1024 或 1280代价是显存占用按平方增长。以 YOLOv8 为例640 到 1024显存大约翻 2.5 倍。8GB 显存卡在 1024 下 batch size 只能给到 4 到 8。如果显存不够宁可降 batch 也不要降分辨率因为小目标丢了对这个任务就是致命的。梯度累积可以缓解小 batch 的不稳定Ultralytics 里没有直接参数但可以通过多次前向再反向模拟。yolo detect train \ data./dataset/data.yaml \ modelyolov8s.pt \ imgsz1024 \ batch8 \ epochs150 \ patience30 \ lr00.01 \ lrf0.01 \ mosaic0.5 \ scale0.3 \ degrees0.0 \ fliplr0.5 \ flipud0.5 \ project./runs \ nameroad_damage_v1参数说明imgsz1024是航拍小目标的底线再低裂缝就糊了。mosaic0.5比默认的 1.0 低因为航拍图拼接后透视关系混乱过强的 mosaic 会让模型学到不真实的场景。degrees0.0关掉旋转增强航拍视角下道路方向基本固定随机旋转反而引入噪声。flipud0.5打开上下翻转因为俯视视角上下翻转后物理上仍然合理。4.2 学习率和 warmup 的设置航拍数据集通常规模不大几千到几万张。这种量级下 lr0 给 0.01 比较稳太大前期会震荡太小收敛慢。lrf0.01是最终学习率比例配合 cosine 调度后期精细调整。warmup 用默认的 3 个 epoch 就够但如果你的数据集类别极不平衡warmup 可以拉到 5。类别不平衡在这个任务里很常见横向裂缝可能占 40%坑槽只占 5%。除了在数据层面补充坑槽样本损失函数层面可以给稀有类加权。Ultralytics 默认用 BCE不直接暴露类权重但可以通过复制稀有类样本来间接加权。我一般会把坑槽样本复制 2 到 3 倍同时确保验证集不复制否则评估指标会虚高。4.3 验证指标怎么看训练日志里重点看三个metrics/mAP50、metrics/mAP50-95、以及每个类的instances和mAP。航拍任务里 mAP50 到 0.7 以上算可用0.8 以上算好。但别只看总数要拆到每个类。如果横向裂缝 mAP 0.85 而坑槽只有 0.4说明坑槽样本不够或标注质量差回去补数据比调参有效。还有一个容易被忽略的指标验证集上的precision和recall曲线。养护场景通常更怕漏检所以 recall 优先。如果 precision 高 recall 低可以把置信度阈值从 0.25 降到 0.15牺牲一些误检换召回。这个阈值在部署时调不要在训练时硬编码。5. 避坑与排查航拍病害检测的 5 个真实翻车记录5.1 现象训练 loss 正常下降但 mAP 一直卡在 0.3原因标注框归一化坐标越界被训练脚本静默过滤实际参与训练的目标只有一半。航拍图边缘的病害经常被标到图像外转换时没裁剪。解决转换后跑一遍校验脚本检查所有 txt 里的坐标是否都在 0 到 1 之间越界的直接裁剪或丢弃。同时统计每张图的框数量和原始标注对比差异超过 5% 就停下来查。5.2 现象模型在验证集上表现好部署到新路段全崩原因数据集覆盖的路段太单一模型学到了「这段路的纹理」而不是「病害特征」。航拍数据集如果只从一个区域采集背景分布会高度集中模型过拟合到背景。解决采集时按路段、时段、天气分层。至少覆盖 3 个以上不同路段包含晴天、阴天、不同时段的光照。如果已经标完了用颜色抖动和亮度对比度增强来模拟光照变化但这只是补救不如采集时做好。5.3 现象裂缝类目标检测框大量重叠NMS 后只剩一个原因一条长裂缝被多人分段标注或者标注时框重叠严重。NMS 的 IoU 阈值默认 0.7重叠框会被合并。解决标注阶段就规定一条连续裂缝只标一个框不要分段。如果已经分段了训练前做框合并把同类别且 IoU 大于 0.5 的框合并成外接矩形。部署时如果确实需要分段输出把 NMS 的 IoU 阈值调高到 0.85但会引入更多重复框。5.4 现象训练到 50 epoch 后 mAP 突然掉下去原因学习率调度到了后期仍然偏高或者数据集里有少量标注错误的样本在后期被放大。航拍数据集里常见的错误是把阴影标成坑槽把车道线标成裂缝。解决先看掉下去的那几个 epoch 的验证集预测图定位是哪些图在捣乱。如果是标注错误修掉重训。如果标注没问题把 lrf 从 0.01 降到 0.001让后期更稳。patience 设 30 让早停机制生效不要硬跑满。5.5 现象推理速度在边缘设备上只有 2 FPS原因imgsz 设了 1280模型用了 yolov8l 或更大。航拍小目标确实需要高分辨率但边缘设备算力有限。解决训练时用大模型高分辨率保证精度部署时蒸馏到小模型或者用 TensorRT 量化。另一个思路是把大图切块推理每块 640×640重叠 20%再合并结果。切块会损失全局上下文但对裂缝这种局部特征为主的任务影响可控。6. 进阶技巧用切片推理把 mAP 再拉 5 个点航拍图普遍是 4000×3000 甚至更大直接缩到 1024 训练小目标信息损失严重。我后来固定用一套「训练大图 推理切片」的组合训练时用 1024 或 1280推理时把原图切成 1024×1024 的块块间重叠 256 像素每块单独推理最后用 NMS 合并。这套方法在路面裂缝任务上比直接整图缩放的 mAP50 高了 5 到 8 个点代价是推理时间线性增加。如果部署环境允许这个交换很值。切片推理的核心代码不复杂关键是重叠区域的处理和坐标还原import cv2 import numpy as np def slice_inference(model, img_path, slice_size1024, overlap256, conf0.25): img cv2.imread(img_path) h, w img.shape[:2] stride slice_size - overlap all_boxes [] for y in range(0, h, stride): for x in range(0, w, stride): x2 min(x slice_size, w) y2 min(y slice_size, h) x1 max(0, x2 - slice_size) y1 max(0, y2 - slice_size) patch img[y1:y2, x1:x2] results model(patch, confconf, verboseFalse) for box in results[0].boxes: # 坐标还原到原图 bx1, by1, bx2, by2 box.xyxy[0].tolist() all_boxes.append([ bx1 x1, by1 y1, bx2 x1, by2 y1, box.conf.item(), int(box.cls.item()) ]) # 跨块 NMS if not all_boxes: return [] boxes np.array([b[:4] for b in all_boxes]) scores np.array([b[4] for b in all_boxes]) classes np.array([b[5] for b in all_boxes]) indices cv2.dnn.NMSBoxes( boxes.tolist(), scores.tolist(), conf, 0.5 ) return [all_boxes[i] for i in indices]逻辑说明切片时用x1 max(0, x2 - slice_size)保证边缘块也能取满 slice_size避免最后一块过小。坐标还原时加上块的左上角偏移。跨块 NMS 的 IoU 阈值给 0.5比常规的 0.7 低因为重叠区域同一目标会被检出两次需要更激进地合并。参数说明overlap256是经验值太小会漏掉跨块目标太大推理时间浪费。conf0.25是切片推理的起始阈值实际部署时按 recall 需求调。还有一个细节切片推理后长裂缝会被切成多段NMS 合并时如果 IoU 不够高会留下多个框。我一般会额外做一步「同类别框合并」把距离很近且方向一致的框合并成外接矩形。这一步用简单的形态学判断就行不需要复杂逻辑。最后说个习惯每次训完模型我都会拿 20 张训练集里没有的图人工数一遍真实目标数再和模型检出数对比。这个「人肉评估」比看 mAP 曲线更能发现系统性问题。有一次就是靠这个发现模型把车道线阴影全检成了裂缝mAP 看着还行实际误检率爆表。数据集和模型之间永远隔着一层现实别让指标骗了自己。希望帮到你。本文还有配套的精品资源点击获取