ARTICLE DETAIL

资讯详情

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

基于YOLOv5的桥梁路面裂缝检测:从数据集标注到模型部署全流程

基于YOLOv5的桥梁路面裂缝检测:从数据集标注到模型部署全流程 简介这份资源是面向计算机、人工智能、自动化等专业学生与开发者的Yolov5路面桥梁裂缝检测识别项目包可作为毕业设计、课程设计、作业或项目立项演示的完整参考。项目基于Python与Yolov5实现裂缝目标检测代码均经过实际运行测试功能完整适合具备一定深度学习基础、希望快速上手目标检测实战的读者学习与二次开发。压缩包共85个文件约1.6MB以py源码、yaml模型与数据配置、pyc编译文件为主另含sh权重下载脚本、Dockerfile部署文件及jpg、png、jpeg结果截图覆盖训练、推理与部署环节。目前已有271人学习下载。读者可获得可运行的检测源码、模型权重获取方式、数据配置、结果截图与文档说明便于理解Yolov5工程结构、复现检测效果并在此基础上修改扩展功能。1. 路面桥梁裂缝检测从一张巡检照片到可复现的 YOLOv5 方案桥梁和路面巡检最头疼的不是拍不到裂缝而是拍回来几百张照片没人愿意一张张标。人工判读一条 0.2mm 的细微裂缝在强光反射和阴影干扰下十个人能给出三种结论。基于 Python YOLOv5 的裂缝检测方案本质是把这件事变成一个可训练、可复现、可迭代的工程问题用标注好的裂缝数据集训练一个目标检测模型让它在巡检图上自动框出裂缝位置和类别再配合源代码、文档说明和结果截图形成完整闭环。这套东西适合三类人做道路桥梁养护检测的技术人员、想拿裂缝检测练手 YOLOv5 的算法入门者、以及需要快速验证检测方案可行性的工程团队。它不解决“裂缝宽度精确到微米”的计量问题但能解决“从海量巡检图里快速筛出有裂缝的图并定位”的效率问题。下面按数据准备、模型训练、推理部署、避坑排查、进阶技巧的顺序把这条路走通。2. 裂缝数据集怎么准备从原始巡检图到 YOLO 格式标注2.1 裂缝检测的数据特点与采集建议裂缝检测和常规目标检测最大的区别在于目标形态。裂缝是细长、不规则、对比度低的线状目标在 YOLOv5 的 anchor 机制下长宽比极端的目标本身就难匹配。我一般建议采集时注意三件事第一拍摄距离控制在 0.5 到 2 米太远裂缝在图上只有几个像素标注框会严重失真第二尽量包含不同光照条件——顺光、逆光、阴影、潮湿路面都要有否则模型一到阴天就集体翻车第三同一处裂缝至少拍两张不同角度增加样本多样性。数据量方面裂缝检测不像 COCO 那种通用检测需要几十万张。实际做下来单类裂缝检测在 800 到 1500 张标注图就能出一个可用的基线模型。如果分横向裂缝、纵向裂缝、网状裂缝三类每类至少 300 张有效样本。这里说的“有效”是指裂缝在图中清晰可辨、标注框贴合裂缝走向不是随手拍的路面图凑数。注意裂缝数据集的类别不平衡非常常见。网状裂缝因为形态复杂标注成本高往往样本最少但恰恰是模型最难学的类别。建议在采集阶段就有意识多拍网状裂缝。2.2 用 LabelImg 标注并转成 YOLO 格式YOLOv5 用的是 YOLO 格式标注每张图对应一个同名 .txt 文件每行格式为类别索引 中心x 中心y 宽度 高度所有坐标都是相对图像宽高的归一化值0 到 1 之间。用 LabelImg 标注时默认输出的是 PASCAL VOC 的 XML 格式需要转换。import xml.etree.ElementTree as ET import os # 类别映射根据你的数据集实际类别修改 classes [transverse_crack, longitudinal_crack, mesh_crack] def convert_annotation(xml_path, output_dir, img_w, img_h): 将单个 VOC XML 转为 YOLO txt 格式 tree ET.parse(xml_path) root tree.getroot() txt_name os.path.basename(xml_path).replace(.xml, .txt) with open(os.path.join(output_dir, txt_name), w) as f: for obj in root.iter(object): cls_name obj.find(name).text if cls_name not in classes: continue cls_id classes.index(cls_name) 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) # 归一化并转为中心点宽高 x_center (xmin xmax) / 2.0 / img_w y_center (ymin ymax) / 2.0 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h f.write(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}\n)这段代码的关键点在于归一化。YOLOv5 训练时读取的是相对坐标如果你的标注框坐标没有除以图像宽高训练时 loss 会直接爆炸或者模型完全不收敛。img_w和img_h需要从对应图像读取可以用 OpenCV 或 PIL 获取。类别索引从 0 开始顺序必须和后续 data.yaml 里的 names 列表一致否则模型学出来的类别全是错的。转换完成后目录结构建议组织成 YOLOv5 标准格式dataset/ images/ train/ (约 80% 的图) val/ (约 20% 的图) labels/ train/ (对应的 txt) val/2.3 data.yaml 配置与数据集划分的坑YOLOv5 训练需要一个 data.yaml 描述数据集路径和类别path: /home/user/crack_dataset train: images/train val: images/val nc: 3 names: [transverse_crack, longitudinal_crack, mesh_crack]这里最常见的翻车点是路径。path写绝对路径最稳妥train和val相对于path。如果训练时提示 “No labels found”九成是 labels 目录结构和 images 不对应或者 txt 文件名和图片名没对上包括扩展名大小写。另一个坑是数据集划分不要随机打乱后按 8:2 分因为同一处裂缝的多角度照片如果同时出现在训练集和验证集验证指标会虚高。正确做法是按拍摄位置或拍摄批次划分确保验证集里的裂缝场景训练时没见过。3. YOLOv5 训练裂缝检测模型环境配置与参数调优3.1 环境搭建conda 创建隔离环境YOLOv5 对环境版本有一定要求直接装在系统 Python 里容易和已有包冲突。我一般用 conda 建一个独立环境conda create -n crack_yolo python3.9 -y conda activate crack_yolo # 克隆 YOLOv5 仓库使用你手头源代码包中的版本 cd yolov5 pip install -r requirements.txt # 验证环境 python -c import torch; print(torch.__version__, torch.cuda.is_available())Python 版本建议 3.8 到 3.10太高或太低都可能遇到依赖包不兼容。torch.cuda.is_available()返回 True 说明 GPU 可用返回 False 则只能用 CPU 训练——裂缝检测用 CPU 训练基本不可行一张图前向传播就要好几秒。如果显卡显存低于 6GB训练时需要把 batch size 降到 4 或 8同时用--img 416降低输入分辨率。提示requirements.txt 里的 torch 版本可能和你的 CUDA 驱动不匹配。如果安装后torch.cuda.is_available()为 False去 PyTorch 官网按你的 CUDA 版本重新安装 torch 和 torchvision。3.2 训练命令与关键参数含义环境就绪后训练命令本身不复杂但参数设不对结果差很多python train.py \ --data data/crack.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 150 \ --hyp data/hyps/hyp.scratch-low.yaml \ --project runs/train \ --name crack_exp1逐项说明--weights yolov5s.pt表示从预训练权重开始迁移学习裂缝检测样本量不大从头训练几乎不可能收敛到可用精度用 COCO 预训练权重是标准做法。--img 640是输入分辨率裂缝是细长目标分辨率太低裂缝直接糊成一条灰线640 是精度和显存的平衡点显存够可以上 1024。--batch 16是批大小根据显存调整显存不够就降。--epochs 150是训练轮数裂缝检测通常 100 到 300 轮收敛看验证集 mAP 不再上升就可以停。--hyp指定超参数文件低配环境用hyp.scratch-low.yaml数据量充足可以用默认的hyp.scratch.yaml。训练过程中重点看三个指标box_loss是否稳定下降、mAP0.5是否上升、验证集 loss 是否开始反弹。如果训练集 loss 降但验证集 loss 升说明过拟合了需要加数据增强或减少模型复杂度。3.3 裂缝检测的 anchor 调整与数据增强策略YOLOv5 默认 anchor 是基于 COCO 数据集聚类的对裂缝这种极端长宽比目标并不最优。如果训练几个 epoch 后 mAP 一直在低位徘徊可以重新聚类 anchorpython utils/autoanchor.py --data data/crack.yaml --img 640这个脚本会根据你的标注框分布重新计算 anchor 尺寸。裂缝的宽高比经常到 1:10 甚至更极端默认 anchor 里没有这种比例导致正样本匹配太少模型学不动。数据增强方面YOLOv5 默认开启了 mosaic、HSV 增强和随机翻转。对裂缝检测来说mosaic 增强要谨慎——它把四张图拼成一张裂缝这种细长目标拼在一起容易产生不真实的交叉裂缝反而干扰学习。我一般会把 mosaic 的概率从 1.0 降到 0.5同时开启mixup增强提升泛化。HSV 增强里的饱和度调整对裂缝帮助不大但亮度调整很有用能模拟不同光照条件。4. 推理与部署让训练好的模型跑在巡检流程里4.1 用 detect.py 做单图和批量推理训练完成后权重保存在runs/train/crack_exp1/weights/best.pt。推理命令python detect.py \ --weights runs/train/crack_exp1/weights/best.pt \ --source ./test_images \ --img 640 \ --conf 0.25 \ --iou 0.45 \ --save-txt \ --project runs/detect \ --name crack_result--source可以是单张图、文件夹或视频。--conf 0.25是置信度阈值低于这个值的检测框被过滤。裂缝检测里这个值很关键设太高会漏掉细微裂缝设太低会把路面纹理误判成裂缝。我的经验是先在验证集上画 P-R 曲线找到 F1 分数最高的阈值通常在 0.2 到 0.35 之间。--iou 0.45是 NMS 的 IoU 阈值裂缝框重叠多的时候可以适当调高到 0.5。--save-txt会把检测结果保存成 YOLO 格式的 txt方便后续做裂缝统计。4.2 模型导出与推理速度优化如果要把模型部署到巡检设备或边缘计算盒子上需要导出成 ONNX 或 TensorRT# 导出 ONNX python export.py --weights best.pt --include onnx --img 640 --batch 1 # 导出 TensorRT需要 NVIDIA GPU 和 tensorrt 环境 python export.py --weights best.pt --include engine --img 640 --device 0ONNX 格式通用性好CPU 上也能跑但速度一般。TensorRT 在 NVIDIA GPU 上能提速 2 到 3 倍适合实时巡检场景。导出时--img要和训练时一致否则精度会掉。导出后建议用几张验证集图片对比 PyTorch 和导出模型的检测结果确认没有精度损失。推理速度方面YOLOv5s 在 640 分辨率下RTX 3060 上单张约 7msJetson Nano 上约 80ms。如果部署在树莓派 5 上建议用 ONNX Runtime 加 OpenCV DNN 模块单张推理在 200ms 左右勉强能跑离线巡检。4.3 结果可视化与裂缝统计输出检测结果不只是一张画框的图巡检场景需要的是结构化数据。可以在 detect.py 基础上加一段后处理import cv2 import numpy as np def analyze_cracks(txt_path, img_path): 读取检测结果统计各类裂缝数量和位置 img cv2.imread(img_path) h, w img.shape[:2] stats {} with open(txt_path) as f: for line in f: parts line.strip().split() cls_id int(parts[0]) x_center, y_center, bw, bh map(float, parts[1:5]) # 还原到像素坐标 x1 int((x_center - bw/2) * w) y1 int((y_center - bh/2) * h) x2 int((x_center bw/2) * w) y2 int((y_center bh/2) * h) stats.setdefault(cls_id, []).append((x1, y1, x2, y2)) return stats这段代码把归一化坐标还原成像素坐标按类别统计裂缝位置。实际巡检报告里除了框的位置还需要裂缝的像素长度和宽度估算。可以用检测框的对角线长度近似裂缝长度但要注意这只在裂缝沿对角线方向时准确横向和纵向裂缝需要分别用宽或高来估算。5. 裂缝检测避坑排查那些让模型翻车的细节5.1 训练 loss 不下降或变成 NaN现象训练开始后 box_loss 一直在高位震荡或者几个 epoch 后突然变成 NaN。原因最常见的是学习率太大或标注坐标没有归一化。如果标注文件里的坐标是像素值而不是 0 到 1 的相对值loss 计算时梯度会爆炸。另一个原因是 data.yaml 里的 nc 和实际类别数不一致导致类别 loss 计算越界。解决先检查一个标注 txt 文件确认所有坐标都在 0 到 1 之间。然后确认 data.yaml 的 nc 等于 names 列表长度。如果都没问题把学习率降到 0.001 再试。YOLOv5 默认用 SGD 优化器学习率对裂缝这种小数据集比较敏感。5.2 验证集 mAP 很高但实际检测漏检严重现象训练日志里 mAP0.5 到了 0.85 以上但拿新拍的巡检图测试很多明显裂缝检测不出来。原因数据集划分时同一场景的图片泄漏到了验证集。比如同一段路的裂缝训练集和验证集里都有模型只是记住了这个场景不是学会了裂缝特征。另一个原因是验证集图片和训练集图片的拍摄条件太接近没有覆盖实际巡检中的光照和角度变化。解决按拍摄批次重新划分数据集确保验证集的场景训练时完全没见过。同时增加一些困难样本——逆光、阴影、潮湿路面的裂缝图这些在实际巡检中占比很高但标注时容易被忽略。5.3 模型把路面接缝、阴影误判为裂缝现象检测结果里出现大量假阳性把路面伸缩缝、标线边缘、树影都框成裂缝。原因裂缝和这些干扰项在低分辨率下纹理特征相似。YOLOv5s 的浅层特征提取能力有限区分不了细粒度的纹理差异。解决三个方向。第一在训练数据里加入这些干扰项的负样本让模型学会区分。第二提高输入分辨率到 1024让裂缝和干扰项在像素层面拉开差距。第三换更大的模型YOLOv5m 或 YOLOv5l 的特征提取能力更强但推理速度会下降。如果部署环境算力有限优先用加负样本的方式。5.4 推理时 GPU 显存溢出现象训练时正常推理时提示 CUDA out of memory。原因推理时默认 batch size 可能比训练时大或者输入分辨率设得比训练时高。另外如果同时加载了多个模型实例显存会叠加。解决推理时加--batch-size 1确保单张推理。如果还是溢出把--img降到 416 或 320。用nvidia-smi查看显存占用确认没有其他进程占用 GPU。5.5 导出的 ONNX 模型检测结果和 PyTorch 不一致现象PyTorch 模型检测正常导出 ONNX 后框的位置偏移或置信度明显下降。原因导出时的输入尺寸和训练时不一致或者 ONNX 推理时的预处理归一化、通道顺序和 PyTorch 不一致。YOLOv5 的预处理是把像素值除以 255 再减均值除方差ONNX 推理时如果漏了这一步结果会完全不对。解决导出时--img必须和训练时一致。ONNX 推理时严格按照 YOLOv5 的预处理流程BGR 转 RGB、归一化到 0 到 1、再按 ImageNet 均值方差标准化。建议用 YOLOv5 自带的detect.py加--onnx参数做对比测试确认精度一致后再部署。6. 把裂缝检测做扎实几个让模型真正可用的技巧训练出一个 mAP 好看的模型不难难的是让它在实际巡检中稳定可用。我踩过最深的坑是拿验证集指标当最终标准结果现场测试时漏检率高达 30%。后来总结出几个真正管用的做法。第一个技巧是用滑动窗口推理处理大图。巡检拍的路面图往往是 4000×3000 甚至更大直接缩到 640 会丢失裂缝细节。正确做法是把大图切成有重叠的小块每块单独推理再合并结果。重叠区域取置信度最高的框避免裂缝在切块边界被截断。def sliding_window_detect(model, img, window640, stride512, conf0.25): 滑动窗口推理大图stride 小于 window 保证重叠 h, w img.shape[:2] all_boxes [] for y in range(0, h, stride): for x in range(0, w, stride): patch img[y:ywindow, x:xwindow] if patch.shape[0] window or patch.shape[1] window: continue results model(patch, sizewindow) for box in results.xyxy[0]: if box[4] conf: continue # 把局部坐标还原到全图坐标 x1, y1, x2, y2 box[:4].tolist() all_boxes.append([x1x, y1y, x2x, y2y, box[4].item(), box[5].item()]) # 用 NMS 合并重叠框 return all_boxesstride设为window的 0.8 倍左右保证相邻块有 20% 重叠。重叠太少裂缝会被切断重叠太多推理时间翻倍。这个方案在 4000×3000 的巡检图上比直接缩放推理的召回率提升 15% 以上。第二个技巧是用测试时增强TTA提升召回。推理时把图片水平翻转、多尺度缩放各跑一遍合并所有检测框再做 NMS。YOLOv5 的 detect.py 自带--augment参数开启 TTA代价是推理时间增加 3 倍左右。对漏检率要求严格的场景值得开。第三个技巧是建立误检样本回流机制。每次现场测试后把误检和漏检的图收集起来人工修正标注后加入训练集重新训练。这个循环跑三轮模型在实际场景的 F1 分数通常能从 0.7 提升到 0.85 以上。裂缝检测没有一劳永逸的模型持续迭代才是正路。最后一个习惯每次训练完不要只看 mAP 数字一定把验证集的检测结果图导出来肉眼过一遍。我见过太多次 mAP 0.9 但框全偏的模型指标好看不代表能用。导出结果图、对比标注框、看漏检和误检的具体案例这个动作比调参重要得多。希望帮到你。本文还有配套的精品资源点击获取
返回列表