ARTICLE DETAIL

资讯详情

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

航拍路面病害识别数据集实战:目标检测、小目标提升与避坑指南

航拍路面病害识别数据集实战:目标检测、小目标提升与避坑指南 简介这是一套面向目标检测与缺陷检测任务的航拍路面病害识别数据集覆盖纵向裂缝、横向裂缝、龟裂、斜裂缝、修补、块裂、坑槽等7个常见病害类别对应3151张航拍原始图片。数据提供YOLO与VOC两种标注格式txt标签适配YOLO系列算法xml标签可满足Faster RCNN、SSD等模型需求同时附有yaml类别配置文件图片与txt标签已按比例划分为训练集、验证集和测试集下载后无需额外格式转换即可直接训练和验证。压缩包共2000个文件以txt标签为主1999个另含1个yaml类别配置整体约194.08MB文件命名与目录结构清晰方便批量读取与划分。目前已有390人浏览学习适用于道路巡检、智慧交通、基础设施养护等场景也可作为深度学习目标检测实战练习数据借助该数据集可节省数据采集与标注时间快速评估模型在航拍路面病害识别上的性能表现对低空无人机巡检、车载道路检测等应用有直接参考价值。1. 航拍路面病害识别数据集这是个目标检测问题但难点全在“航拍”两个字想从无人机拍回的巡路画面里自动找出裂缝和坑槽最容易被低估的不是检测模型而是数据。航拍路面病害识别数据集这类项目解决的是一个标准目标检测问题给出一张俯拍路面图像模型输出每个病害的位置框和类别常见类别不外乎横向裂缝、纵向裂缝、网状裂缝、坑槽和修补。模型框架到处都是换个结构跑通只需要一晚上但数据这一步要是没做对后面调参全是白费。和车载相机拍的平视图像不同航拍图里病害小、对比度低、阴影多直接拿通用预训练权重去跑小目标AP通常惨不忍睹。它适合两类人一类是做市政巡检、智慧城市或土木检测的工程师想快速验证这个方向能不能投入另一类是刚接触目标检测、想找一个“能落地但没那么简单”的练手场景的同学。下面从数据集构成、训练流程到翻车点按我平时落地的顺序拆开讲。2. 破解航拍路面病害数据集的常见构成类别、标注与采集参数2.1 病害类别怎么设5类起步先分清“细条”和“块状”公开渠道能见到的航拍路面病害识别数据集类别设计普遍不复杂多数在5类左右。按形态分得再细最终还是要归到几个大方向上我一般先按下面这张表对齐任务类别形态特征目标检测难点横向裂缝细长大致垂直于行车方向长宽比极大框内正样本占比低纵向裂缝细长大致平行于行车方向同上且常被车道线、伸缩缝干扰网状裂缝龟裂纹连片出现边界难以闭合框内纹理破碎坑槽不规则块状颜色加深边缘不干净阴影比重高修补补丁状颜色和纹理异于原路面形状多变和阴影易混淆有的数据集会把裂缝按D00–D44之类的编码继续细分本质上也是把龟裂、横向、纵向分开。真正关键的不是类别多而是类别之间形态差异大裂缝是细长条坑槽是块状修补是形状不规则的“疤”。检测头一视同仁地去回归容易在长宽比上互相拖累。拿到标签后我做的第一件事永远不是训练而是统计类别频率。用一行命令就能知道数据是不是极端不平衡cd labels/train for i in 0 1 2 3 4; do echo class $i: $(grep -rE ^$i . | wc -l) done这条命令统计每个类别在YOLO文本标签中出现的行数。注意grep模式里的^$i带行首锚定和空格因为YOLO标签每行是“类别ID 中心x 中心y 宽 高”不加锚定会把前导空格也算进去统计结果会虚高。结果如果某个类别占比不到5%后面训练就要处理否则加再多的mAP都会被大类平均稀释掉。2.2 航拍为什么难视角、尺度与阴影同一条裂缝车载平视相机拍出来有几十上百像素宽航拍俯视图里可能只有3–5个像素宽。这里的关键参数是GSDGround Sample Distance地面采样距离。同样一条10cm宽的裂缝GSD是1cm/pixel时它占10pxGSD是5cm/pixel时它只占2px。航拍数据集普遍受“小目标”折磨根源就在数据采集时的GSD。另外一个容易被忽略的问题是阴影。无人机在上午或下午飞行时路面上的树影、桥影、车影会直接把病害盖住甚至阴影边缘和裂缝在局部纹理上几乎一样。这类数据集如果采集时没挑时间段模型学到的是“哪里有深色条状区域”而不是“哪里有裂缝”换了光线和场景就翻车。对比遥感图像目标检测航拍路面病害识别没有“人造结构”的先验形状可用。船舶、车辆、建筑物都有固定的几何结构路面病害是弱纹理、随机形状的目标检测模型的感受野和特征提取方式都得重新适配。这也是为什么不要指望拿COCO预训练的模型零调参直接用的原因——分布差异不在模型结构在图像纹理尺度。2.3 自己补数据和标注采集参数、裁剪与格式转换如果公开数据集不符合你的设备视角自己采集也来得及。航拍采集参数我一般压在这几个范围飞行高度30–60m航向重叠率70%以上、旁向重叠率50%以上尽量选阴天或多云天拍摄避免强阴影。回去后把大图裁成1024或1280的patch再用标注工具画框。目标检测常用标注工具里我留了两个顺手的一个CVAT适合团队在线标注另一个是X-AnyLabeling适合单机快速出框。导出格式尽量选VOC XML或COCO JSON然后再转YOLO txt。这个转换我自己写过无数次核心逻辑就一段import xml.etree.ElementTree as ET from pathlib import Path def voc_to_yolo(xml_path: Path, out_dir: Path, class_names: list) - None: 把单张VOC XML标注转成YOLO txt按类别顺序输出ID。 tree ET.parse(xml_path) root tree.getroot() img_w float(root.findtext(size/width)) img_h float(root.findtext(size/height)) lines [] for obj in root.iter(object): name obj.findtext(name) if name not in class_names: continue cls_id class_names.index(name) box obj.find(bndbox) x1, y1 float(box.findtext(xmin)), float(box.findtext(ymin)) x2, y2 float(box.findtext(xmax)), float(box.findtext(ymax)) cx ((x1 x2) / 2) / img_w cy ((y1 y2) / 2) / img_h bw (x2 - x1) / img_w bh (y2 - y1) / img_h # 夹到[0,1]防止标注越界导致训练loss异常 cx min(max(cx, 0.0), 1.0) cy min(max(cy, 0.0), 1.0) bw min(max(bw, 0.0), 1.0) bh min(max(bh, 0.0), 1.0) lines.append(f{cls_id} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}) if lines: out_dir.mkdir(parentsTrue, exist_okTrue) (out_dir / (xml_path.stem .txt)).write_text( \n.join(lines), encodingutf-8) # 类别顺序要固定之后训练、验证、推理都用这一份 class_names [横向裂缝, 纵向裂缝, 网状裂缝, 坑槽, 修补] for xml_file in Path(annotations).glob(*.xml): voc_to_yolo(xml_file, Path(labels_all), class_names)代码里几个容易出错的地方VOC的xmin/xmax是像素坐标YOLO需要归一化的中心点和宽高类别顺序必须全局唯一一旦训练开始后中途改顺序旧标签全部作废min/max夹紧那一步看似多余实际上很多自动标注工具会在图像边缘画出越界框不夹紧的话模型会在loss阶段报NaN或者边界回归异常。2.4 训练前先抽查一遍框画歪了远比没画全危害大漏标和错标对检测模型的影响不一样。漏标最多让那一个框不参与训练错标直接给模型喂了错误正样本会让模型在同类目标上反复横跳。我拿到任何数据集都会先随机抽20到30张图把框画在原图上肉眼过一遍重点看三类问题裂缝框是否只框住半截、修补区域是否漏了小块、坑槽边界是否框得过大把正常路面包进来。这段抽查不用写复杂脚本直接用OpenCV读图、读txt、画矩形就能看。抽查时间大概半小时省下来的调参时间通常是十倍不止。3. 用YOLOv8跑通航拍路面病害识别数据集处理、划分与第一次训练3.1 划分数据集的正确姿势按场景切不按单张切很多人在这个环节第一次翻车。无人机巡路时是连续拍摄同一个路段的病害在同一秒内会出现在好几张图上。如果按单张随机划分同一路段的图像同时进了训练集和验证集验证指标会被严重虚高模型看起来mAP很高一上真实新路段就崩。正确的做法是先看文件名或目录里有没有场景标记没有就按拍摄批次分组再把整个批次放进同一个集。一个简化的脚本长这样import random from pathlib import Path import shutil random.seed(42) images sorted(Path(images_all).glob(*.jpg)) random.shuffle(images) n len(images) # 按8:1:1切分这里假设shuffle后场景分布足够分散 splits { train: images[: int(n * 0.8)], val: images[int(n * 0.8): int(n * 0.9)], test: images[int(n * 0.9):], } for split, imgs in splits.items(): img_dir Path(fimages/{split}) lbl_dir Path(flabels/{split}) img_dir.mkdir(parentsTrue, exist_okTrue) lbl_dir.mkdir(parentsTrue, exist_okTrue) for img in imgs: shutil.copy(img, img_dir / img.name) label Path(labels_all) / (img.stem .txt) if label.exists(): shutil.copy(label, lbl_dir / label.name)如果数据集里同一路段的图像是连续编号的比如sec01_0001.jpg、sec01_0002.jpg上面这段随机划分就不够稳妥。更稳的做法是按文件名前缀分组后再划分先把前缀分组再对组做shuffle。这一步值得花十分钟写否则后面验证集形同虚设。3.2 目录结构与data.yaml一个能直接训练的最小工程YOLOv8的工程结构要求很固定数据侧就三层image目录、label目录和一份yaml。目录建好之后data.yaml长这样# 数据集根目录相对路径也可以训练命令里给绝对路径更稳 path: ./road_pavement_aerial train: images/train val: images/val test: images/test nc: 5 names: 0: 横向裂缝 1: 纵向裂缝 2: 网状裂缝 3: 坑槽 4: 修补注意yaml里的names顺序必须和2.3转换脚本里的class_names完全一致这个顺序决定标签里数字的含义。train/val/test字段可以留空valUltralytics会默认从train里切20%出来但我强烈不建议这么做航拍连续帧的问题会让自动划分直接失真。3.3 单卡跑通第一次训练先取安全参数再谈精度第一次跑这个航拍路面病害识别数据集我不建议一上来就追求精度。目标是把从数据到权重的完整链路跑通用最小代价确认标签能不能学。命令如下yolo detect train \ dataroad_pavement_aerial/data.yaml \ modelyolov8s.pt \ epochs100 \ imgsz1280 \ batch8 \ patience20 \ projectruns/aerial_pavement参数选择逻辑模型用yolov8sn太小容易欠拟合s是航拍小目标场景里性价比最高的档位imgsz直接给1280因为640在航拍病害这种小目标任务上普遍吃亏后面细讲batch8是按12G显存余量估算的显存小就4显存大就16patience20是早停防止训练白跑。这几项全部用默认增强不要在这阶段动mixup和mosaic先把基线跑通。训练过程中要看三样东西box_loss是否持续下降、val的mAP0.5是否随之上升、最后20个epoch有没有早停。如果训练正常best.pt会保存在runs/aerial_pavement/train/weights/下。3.4 第一次验证先看PR曲线再决定要不要继续训练结束后用测试集做一次独立验证yolo detect val \ dataroad_pavement_aerial/data.yaml \ modelruns/aerial_pavement/train/weights/best.pt \ splittest \ imgsz1280 \ batch8跑完不要只盯终端打出来的mAP数字先去runs/aerial_pavement/train下看PR_curve.png和confusion_matrix.png。PR曲线的右上方越鼓代表模型在高置信度下也能维持较高召回混淆矩阵里最容易看出裂缝之间互相错分的程度。最后随手从val_batch_pred.jpg里挑几张图重点看小裂缝有没有被成片漏掉。这一步看得越仔细后面调参越有方向。4. 精度上不去的三个开关输入分辨率、小目标策略与评价指标4.1 目标检测评价指标航拍病害场景先看哪些数字目标检测评价指标里最常被抄作业的就是mAP但航拍路面病害这种小目标场景光看mAP会骗人。我一般按优先级看这几项指标含义在航拍病害场景的判读mAP0.5IoU0.5下的平均AP只反映“框差不多准”裂缝框偏大也能拿高分mAP0.5:0.95多个IoU阈值下的平均AP框的定位精度越高这个值越高更接近真实可用度AP_s / AP_m小目标/中目标单列AP航拍病害主要是AP_s这个值低说明分辨率策略有问题Precision / Recall精确率与召回率病害场景宁可误报也不漏报Recall门槛要卡住F1P/R的平衡点选conf阈值时最实用直接决定部署时拦截多少框实操上我只看两个数mAP0.5:0.95和AP_s。前者反映定位质量后者反映小目标能力。如果mAP0.5很高但0.5:0.95掉一半说明框画得“大差不差但不够准”优先调输入分辨率和NMS参数如果mAP整体都不行问题大概率在数据质量而非模型结构。4.2 输入分辨率从640到1280最省事的一次提升航拍路面病害数据集如果GSD偏大、病害像素少把输入从640提到1280通常能让mAP0.5:0.95提升30%以上这是这类任务里最明显的利润点。原理很直接YOLOv8在640下对一张1280的大图做等比例缩放原图中只有10像素宽的裂缝缩到5像素特征图上的响应几乎消失提到1280后GPU显存消耗约翻4倍但小目标的可用特征明显变多。显存不够的折中方案是把1280改成960或者把mosaic增强关掉腾出显存。注意imgsz不仅影响训练还会影响验证训练用1280验证却用640指标会异常偏高或偏低两边必须保持一致。4.3 裂缝太细、坑槽太少先把类别不平衡拉到可学范围类别不平衡在航拍路面病害数据集里几乎是标配。横向裂缝和修补这类容易采集的类别很多坑槽和网状裂缝很少。如果直接拿原始比例训模型会偏向把不确定区域都判成大类。我一般先做数据层面的处理而不是改loss。最省事的是对小类图像做重复采样把含坑槽、网状裂缝的图像在训练集里复制2–3份让每个epoch里这些小类参与更新的次数变多。另一个实用技巧是只对小类开更强的数据增强比如对坑槽做额外的旋转和尺度扰动模拟不同角度和高度采集的形态。Ultralytics里也可以调损失权重但效果不如数据层面直接。真要调cls损失权重默认0.5可以先提高到0.7左右试两轮改动小、动作明确适合在数据增强已经做满之后做精调。4.4 模型微调崩了怎么排查loss正常但指标不动目标检测模型微调崩了这事我见过不少次典型症状是训练loss曲线很漂亮mAP却在几十个epoch里纹丝不动。排查顺序固定如下先确认验证集的标签和训练集是同一份类别顺序再检查lr是不是默认值、有没有在冻结层上训练然后看混淆矩阵如果一个类别完全不出现在预测里多半是标签里这个类别的目标数量太少回4.3处理最后看验证集图片里有没有大量标注缺失漏标会让模型“学会了漏”。不要一上来就换模型结构。航拍路面病害场景下YOLOv8s的容量完全够用先崩的都是数据问题和分辨率问题结构是最后才考虑的事。5. 航拍路面病害数据集的避坑实录五个翻车现场5.1 不抽样检查直接开训最后发现错标把loss带飞现象训练loss降得挺好验证集上mAP0.5也有0.7但把模型放到新路段上一测裂缝检测框总是比真实裂缝大一圈。原因抽查后发现原始标注里一半以上的裂缝框是标注员按“裂缝周围区域”画的框边缘离裂缝本体有20像素以上的富余。模型被这些错误正样本带偏学到的边界回归目标本身就偏大。解决把所有框向内收缩一定比例再重新训练。实测对裂缝这类长条目标把bbox宽度收缩10%–15%能明显拉回边界精度。收缩操作在转换脚本里加一行即可不要动原始XML保留原始标注用于其他试验。5.2 小目标全灭AP_s几乎为零现象验证集上mAP0.5:0.95看着有0.4但按类别细看横向裂缝的AP只有0.1跑测试集发现所有细裂缝都被漏掉只有修补和坑槽检出。原因直接在imgsz640下训练而数据集里最细的裂缝在原始图上只有5–8像素宽缩放到640后变成2–4像素特征图上一两层的感受野就把它“模糊”掉了。解决按4.2提到的把imgsz提到1280同时把mosaic增强对裂缝这类小目标的影响调低。mosaic会用四张图拼贴让目标更小航拍病害场景下mosaic1.0反而有害建议先降到0.5甚至0。5.3 同一场景连续帧泄漏验证集虚高现象训练时mAP0.5能到0.8一上真机新路段就跌到0.3差距大到明显不正常。原因数据划分用单张随机切分同一路段连续拍摄的30帧里24帧进训练、6帧进验证验证集里全是和训练集几乎一样的画面。解决回3.1按场景分组划分。判定泄漏最直接的方法是看混淆矩阵里的val精度是不是每类都异常均匀再翻20张val图片如果大量和训练集来自同一条路基本就是泄漏。5.4 只盯mAP0.5漏掉定位精度现象终期报告写“mAP0.5 0.85”部署后发现病害在画面中只定位到“大概位置”没法给现场复核人员精确定位。原因mAP0.5只要求IoU过半就算命中裂缝这种长条目标框画歪30%仍然能保住0.5的IoU于是模型的定位误差全被这个指标掩盖。解决把部署验收标准改成mAP0.5:0.95并额外统计框中心点偏差的中位数。中位数超过目标直径的20%就要继续压IoU阈值。5.5 数据增强盲开数据集小直接过拟合现象训练集只有几百张图开着全部默认增强跑200个epochtrain loss降到很低val指标在第60个epoch后不再上升。原因增强太强导致训练分布和验证分布偏离小数据集本身也容易让模型记住个别样本。解决先看val曲线找早停点不要盲跑200 epoch然后按6:1的比例缩小增强强度特别是mosaic和mixup。航拍路面病害数据集的本质是“样本少、目标密”增强策略应该往Copy-Paste方向走把稀疏的病害目标复制到干净路面上比平移翻转有效得多。6. 不做最后一步验证前面都算白干滑窗推理实测未标注大图很多人训练完在验证集上溜一圈就收工。但航拍路面病害识别数据集真正的验收场景是一张没切割过的5000×6000大图——如果模型不能在大图上稳定地滑窗推理前面的精度全是实验室里的数字。我习惯在做任何正式结论前用一段脚本把完整流程走一遍import cv2 import numpy as np from ultralytics import YOLO model YOLO(runs/aerial_pavement/train/weights/best.pt) big_img cv2.imread(aerial_tile_5000x6000.jpg) H, W big_img.shape[:2] patch_size 1280 step 1040 # 步长小于patch保留240像素重叠避免裂缝正好被切在窗口边缘 conf 0.25 all_boxes [] for y in range(0, H, step): for x in range(0, W, step): y2 min(y patch_size, H) x2 min(x patch_size, W) patch big_img[y:y2, x:x2] res model(patch, imgsz1280, confconf, verboseFalse)[0] for box in res.boxes: x1, y1, x2b, y2b box.xyxy.cpu().numpy().ravel().tolist() score float(box.conf) cls_id int(box.cls) # 关键把patch内的坐标转换回大图坐标 all_boxes.append([x1 x, y1 y, x2b x, y2b y, score, cls_id]) # 用NMS合并重叠框输出去重后的最终框 from ultralytics.utils.ops import non_max_suppression # noqa: E402 bboxes np.array([b[:4] for b in all_boxes], dtypenp.float32) scores np.array([b[4] for b in all_boxes], dtypenp.float32) keep non_max_suppression( np.concatenate([bboxes, scores[:, None]], axis1)[None, :], conf_thresconf, iou_thres0.45)[0] keep keep.cpu().numpy() if keep is not None else np.empty((0, 6))这段逻辑里最容易漏的是坐标偏移模型输出的框是相对patch的必须加回窗口起点(x, y)。重叠区域的重复检测交给NMS合并step取得越小合并压力越大但漏检率越低。在1280窗口下步长960–1040是我试下来性价比合适的区间裂缝被切断的概率已经降到可接受范围。跑完滑窗推理后把检测结果画回大图人工挑三个场景复核一段新路面的连续帧、一段带阴影的树影区域、一段修补密集路段。对照这三个场景的漏检率基本就能判断这个航拍路面病害识别数据集方案值不值得继续投入。如果换了几组分辨率和小目标策略这三个场景依旧不稳定我会退回数据采集端查GSD和光照而不是继续在模型参数上耗时间。我自己早期的习惯是训练完了先写报告后来发现报告里的mAP和现场复核的漏检率往往对不上才把滑窗实测固定成收尾动作。航拍病害这个方向数据、分辨率、验证顺序每一步都比模型结构更能决定成败。希望这篇文章能帮你在跑这个数据集时少走两趟弯路。本文还有配套的精品资源点击获取
返回列表