ARTICLE DETAIL

资讯详情

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

基于YOLOv5的裂缝检测系统:数据标注到边缘部署全流程

基于YOLOv5的裂缝检测系统:数据标注到边缘部署全流程 简介这是一套基于Python与Yolov5的路面、桥梁及墙体裂缝检测识别系统完整提供文档与源码面向计算机视觉、自动化、电子信息等专业的学生、教师及算法开发者可用于课程设计、毕业设计或项目初期演示。压缩包共53个文件大小约2.06MB22个yaml文件负责模型与数据集参数配置8个Python源码含对应pyc覆盖核心检测逻辑配合shell脚本辅助权重获取、Dockerfile简化环境部署另有docx、pptx、md说明文档及多张示例图片目录结构清晰便于按需查阅。代码风格整洁、注释到位包含数据配置、模型构建、图片检测及摄像头实时识别等完整流程既方便学习者理解Yolov5目标检测原理也可直接改造用于实际裂缝巡检场景。目前已有292人浏览学习项目经导师认可、运行验证适合不同水平的开发者下载使用。1. 裂缝检测为什么要用 YOLOv5先说清楚这套方案解决什么拿着相机在桥底拍一天回来对着屏幕把裂缝一条条框出来这种事干过的人都知道眼睛酸是小事漏检几条细裂缝才是要命的问题。基于 PythonYolov5 的路面、桥梁、墙体裂缝检测识别系统目标就是把混凝土表面的裂缝自动找出来、框出来按置信度排序再交给人工复核。这套方案自带文档和源码从数据标注、模型训练到推理部署都有可落地的路径。适合两类人一是做桥梁、隧道、道路巡检的工程团队想把人工排查变成半自动辅助二是视觉方向的学生和初级工程师需要一个能跑通、能改动、能扩展的 YOLOv5 真实项目作为起点。接下来按数据、训练、部署这条线把能复现的细节和踩过的坑一次讲清。2. Yolov5 裂缝检测的选型逻辑为什么最后没选边缘检测和语义分割2.1 裂缝检测的三种技术路线边缘检测、语义分割与目标检测先从传统路线说起。路面、桥梁和墙体的裂缝在图像上表现为低亮度、高长宽比的连续纹理这天然会让人想到 Canny、Sobel 这类边缘检测。但在真实巡检场景里混凝土表面从来不是干净的钢筋阴影、施工缝、水渍、气泡和集料纹理都会产生伪边缘。传统算法对光照和尺度极度敏感同一张照片晴天拍和阴天拍参数就得重新调一遍很难作为一个无人值守的系统交付。语义分割是第二条路。U-Net、DeepLabV3 这类模型能逐像素输出裂缝掩码理论上连裂缝宽度、走向都能提取物理量纲完整。代价是标注成本陡然上升一个矩形框 3 秒能标完逐像素描一条几十厘米长的裂缝要 3 分钟而且裂缝越细描边越费眼睛。训练分割模型还要单独处理类别不均衡——裂缝占整张图的像素比通常不到 1%损失函数不特殊设计模型很容易把整张图预测成背景。目标检测是第三条路也最符合工程交付的现实标注只要一个矩形框单类crack就能跑通训练、推理、部署链路在 YOLOv5 里是现成的把一张巡检照片喂进去输出坐标、置信度就能接巡检报告。代价也明确检测框给不了裂缝宽度细碎的裂纹在框内断裂、合并也需要后处理补救。三条路线放在一起对比会更直观。技术路线标注成本细裂缝完整度部署难度主要瓶颈边缘检测几乎为零断裂严重简单误检率高、参数敏感、跨场景泛化差语义分割像素级、极高较好中等标注耗时、类不均衡、推理偏慢目标检测矩形框、低取决于框简单测不了宽度、框内断裂需后处理这张表基本说明了为什么很多落地项目最终选了目标检测不是因为它最能还原裂缝的真实形态而是因为它能在预算和人力约束下最快形成一条可运行的流水线。至于宽度测量我一般这样处理“检测先定位、框内再测宽”用检测框把裂缝找到后在框内跑阈值分割或形态学算宽度而不是让检测模型直接输出宽度值。2.2 YOLOv5 还是 YOLOv5-seg先想清你要的是“位置”还是“宽度”确定走目标检测之后还有个选择摆在前面用矩形框的 YOLOv5还是带分割头的 YOLOv5-seg。这个决定会在标注环节就影响你的工作量提前想清楚能少走不少弯路。如果巡检报告只需要“哪面墙的哪块区域有裂缝、大致几条”YOLOv5 的矩形框就足够了。标签类别甚至可以不区分横向裂缝和纵向裂缝直接统一为 crack减少标注歧义。如果业务要输出裂缝宽度、面积来评估结构损伤等级那要么上 YOLOv5-seg 直接出 mask要么用检测框加框内像素后处理。我的建议是后者YOLOv5-seg 的 mask 对粗大裂缝还算顺滑对细裂缝边缘往往毛刺严重而且 mask 后处理会拉低推理帧率用检测框定位再在框内用自适应阈值处理反而更容易控制精度。这背后是模型结构的取舍。YOLOv5 的检测头输出的是框回归和分类置信度框对裂缝只负责“它在这一片”而 seg 模型多一个 32×32 的 mask 分支训练时要额外拟合轮廓对小目标的 mask 监督容易在长尾数据上失效。所以选型逻辑很直接位置优先选 YOLOv5宽度优先也要先想清楚 mask 精度是否够用再决定要不要把标注成本翻倍。多数裂缝检测项目最终用的是前者配合一个几百行的宽度测量后处理脚本。2.3 数据从哪来公开裂缝数据集、自采照片与数量估算聊完模型选型数据可能是整个项目里最花时间的一环。公开可用的裂缝数据集中比较常被提起的有 CFD、DeepCrack、Crack500 这类学术数据集它们适合做预训练和算法验证但直接用于工程部署会有一个问题拍摄距离、光照、器材都和你的实际巡检场景不一致迁移到桥底、隧道等暗光环境时精度掉得很明显。自采照片永远是最可靠的数据来源。采集时带上标定尺控制拍摄距离让裂缝在画面里占 3% 到 10% 的面积角度尽量覆盖正视和斜视每个结构面拍 100 到 300 张再从中筛掉模糊、逆光、过曝的废片。数量上单类目标检测在 500 到 1000 个标注实例以上就能有可以用的效果实体框数量可以超过图片数量——一条长裂缝切成多段标注不是坏事反而对增强小目标鲁棒性有帮助。这里要记住框数量与图片数量是两回事。裂缝这种纹理型目标对数据纯度特别敏感宁可图少也不要图脏这是后面几个章节能执行的前提。3. 裂缝数据集的构建与标注从原始照片到 YOLO 格式3.1 照片采集与初筛光照、距离与拍摄角度怎么控制自采照片的坑往往不在拍摄而在筛选。拿着相机对着一面墙连拍几十张回来一看一半是虚的另一半被光线干扰这种事很常见。我的习惯是先拍一张包含结构整体的场景图再走近拍裂缝细部两张一组存好编号。这样做有两个好处一是细部图能确认裂缝所在的结构部位二是场景图可以作为负样本背景加入训练帮助模型区分“裂缝”和“墙面上正常的纹理”。光照是裂缝图像最大的变量。顺光时裂缝阴影清晰逆光时裂缝可能完全消失在阴影里。现场条件允许的话尽量选择阴天或多云天气采集光线均匀、阴影干扰少如果只能在晴天作业要避免阳光直射墙面斜射光下的裂缝会有投影模型很容易把投影边缘也学进去。初筛这一步别偷懒。把模糊、过曝、欠曝、对焦错误的照片全部删掉只保留裂缝纹理清晰、边缘分明的图片。经验值是一次采集 500 张筛完能剩 300 张已经不错。宁缺毋滥后面标注和训练省下的时间远远超过采集时多跑一趟的成本。3.2 标注工具与标签规范矩形框、多边形与单类还是多类标注工具的选择依赖模型选型。走矩形框检测路线用 LabelImg 就够了如果临时决定做 YOLOv5-seg则要换 Labelme 画多边形。我一般建议项目组统一用一个工具避免混用导致标注格式混乱。类别体系上裂缝检测项目不需要复杂的多分类。路面裂缝、桥梁裂缝、墙体裂缝在图像特征上没有本质区别统一标成 crack 最简单。如果设计中要区分横向裂缝、纵向裂缝、网状裂缝也可以按这个维度标但前提是每个子类别都要有足够数量——少于 300 个实例的类别在训练时很容易被模型忽略。对多数巡检场景单类别是性价比最高的选择。标注规范比工具本身更影响模型质量。矩形框要正好包住裂缝主体宁紧勿松不要把周边背景大面积包进来。一条弯曲的长裂缝不要试图用一个框完整框住切成两段或三段、分别标注训练时模型更容易学到局部特征。标注完成后抽 10% 的图做二次复核重点检查漏标和框过大这两类问题在裂缝标注里最常见。3.3 VOC/COCO 转 YOLO 格式一段能直接用的转换脚本与边界坑LabelImg 默认输出 VOC 格式的 XMLYOLOv5 训练需要的是 txt 文本标注格式为“类别id 中心点x归一化 中心点y归一化 宽度w归一化 高度h归一化”。下面这段脚本可以把单张 VOC XML 转换成 YOLO txt是项目里最常用的工具脚本。import xml.etree.ElementTree as ET from pathlib import Path def voc_to_yolo(xml_path, label_map, out_dir): 把单张VOC XML转换成YOLO txt标注。 xml_path: VOC标注文件路径 label_map: 类别名到id的映射如 {crack: 0} out_dir: 输出的labels目录 tree ET.parse(xml_path) root tree.getroot() size root.find(size) img_w int(size.find(width).text) img_h int(size.find(height).text) lines [] for obj in root.findall(object): name obj.find(name).text if name not in label_map: continue cls_id label_map[name] box obj.find(bndbox) x1 float(box.find(xmin).text) y1 float(box.find(ymin).text) x2 float(box.find(xmax).text) y2 float(box.find(ymax).text) # 坐标合法性检查宽高为负或为0的框直接跳过 if x2 x1 or y2 y1: print(fskip bad box: {xml_path}) continue # 越界修正标注偶尔会出现超出图像范围的坐标 x1 max(0, x1); y1 max(0, y1) x2 min(img_w, x2); y2 min(img_h, y2) # 归一化到YOLO的 x_center y_center w h 格式 x_center (x1 x2) / 2.0 / img_w y_center (y1 y2) / 2.0 / img_h w (x2 - x1) / img_w h (y2 - y1) / img_h w min(w, 1.0); h min(h, 1.0) lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) if lines: txt_path Path(out_dir) / (Path(xml_path).stem .txt) txt_path.write_text(\n.join(lines), encodingutf-8)这段脚本有三个关键点。第一注释里做了坐标合法性检查正常的标注工具不会产生负宽高的框但人工手滑或工具异常时经常出现不检查会让 YOLOv5 训练时直接报锚框相关的错误。第二越界修正调用min和max之后坐标范围被限制在图像范围内避免归一化后出现大于 1 或小于 0 的值。第三输出文件与图片同名只是扩展名从.xml变成.txt这是 YOLOv5 查找标注文件的约定。转换完成后目录结构要按 YOLOv5 的约定组织。常见做法是images/train、images/val、labels/train、labels/val四个目录图片放一张同名 txt 放对应的 labels 目录下。脚本处理完之后检查一下 txt 数量和图片数量是否一一对应空 txt 文件要单独排查——YOLOv5 遇到图片没有对应标注时会跳过该图并给出警告数量太多说明标注阶段漏标严重需要回炉。注意我自己踩过这个坑——转换脚本跑完图片 300 张、txt 只有 260 个排查发现 40 张图在标注时被跳过了。所以转换后第一件事是对数量不是直接去训练。4. 用 YOLOv5 训练裂缝检测模型从环境配置到超参数调优4.1 环境与仓库选择Python 版本、依赖与稳定 tag环境配置这块我先说结论Python 用 3.8 或 3.9PyTorch 按自己的 CUDA 版本选YOLOv5 仓库固定一个稳定 tag 再开始不要追 master。原因很简单master 分支更新频繁昨天能跑通的代码今天可能因为一个依赖变动就报错固定版本能保证项目可复现也方便团队协作时大家用的代码一致。conda create -n crack python3.8 conda activate crack # 按你的CUDA版本选择cu118/cu121等CPU环境可省略 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 git clone -b v7.0 https://github.com/ultralytics/yolov5.git cd yolov5 pip install -r requirements.txt依赖安装完成后先别急着训练用仓库自带的yolov5s.pt权重跑一次官方 COCO 检测确认环境能正常推理。这一步能识别大部分环境问题——常见的是 OpenCV 版本冲突、PyTorch 和 CUDA 不匹配。环境能推理之后再进入裂缝数据集的训练。4.2 最小可训练配置crack.yaml 与数据集目录结构训练裂缝模型前先写数据配置文件。YOLOv5 通过一个 yaml 文件告诉训练脚本数据放在哪里、有几类、类别名是什么。下面是一个最小可用的配置。# datasets/crack/crack.yaml path: ../datasets/crack # 相对yolov5仓库目录的路径写绝对路径更稳妥 train: images/train # 训练图片目录 val: images/val # 验证图片目录 nc: 1 # 类别数量只有裂缝一类 names: [crack] # 类别名这里容易出问题的是path字段。YOLOv5 在解析 yaml 时path的相对路径是相对于当前运行train.py的目录不是相对于 yaml 文件所在的目录。我一般直接写绝对路径或者在yolov5仓库目录下运行命令、用../datasets/crack这样的相对路径。如果path配错训练启动时会报AssertionError: train: No images found排查时先看这个字段。4.3 训练命令与关键超参数img、batch、epochs、hyp 怎么调投入训练之前把关键超参数的含义理清楚。下面是一条典型的裂缝检测训练命令。python train.py \ --data ../datasets/crack/crack.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 150 \ --hyp data/hyps/hyp.scratch-low.yaml \ --cache ram--img是输入分辨率裂缝这种细长目标比较依赖分辨率640 起步如果发现细裂缝漏检率高可以提到 800代价是显存占用和训练时间上升。--batch由显存决定8G 显存用默认的 16 比较稳显存不足就往 8 调。--epochs对 500 到 1000 张图的小数据集150 轮左右足够收敛不是越多越好。--hyp选hyp.scratch-low.yaml而不是 high 版本增强太强的小数据集容易把裂缝这种弱纹理目标“增强没”。参数常见值说明--img640 / 800输入分辨率细裂缝多试 800--batch8 / 16受显存限制8G 以上用 16--epochs100 ~ 200小数据集 150 左右即可--hypscratch-low小数据集慎用 high 增强--cacheram / disk数据集不大用 ram加速明显--workers4 ~ 8训练慢先查 CPU 和 IO训练过程中看 loss 曲线时很多人会陷入“loss 降到 0 就是收敛”的误区。裂缝数据集的 loss 不追求越低越好重点看 val 曲线是否同步下降。训练日志里还有一个容易被忽略的输出自动 anchor 计算的 kmeans 结果。如果日志显示 anchor 的长宽比和你的裂缝标注框差距很大可以在train.py里关闭--noautoanchor并使用自定义 anchor但多数情况下先跑自动计算就够用。4.4 训练完先看四样东西PR 曲线、混淆矩阵、mAP 与 loss 曲线训练结束后runs/train/exp目录下会生成一组结果图我每次都会先看四样PR_curve.png、confusion_matrix.png、results.png、val_batch0_pred.jpg。PR 曲线看整体能力裂缝检测的 mAP50 能到 0.85 以上就算可用。mAP50-95 对裂缝这种小目标天然偏低不用慌。混淆矩阵重点看“背景被预测成裂缝”这一格这个数值高说明误检会很多需要回到数据层面处理。results.png里的 val loss 曲线如果比 train loss 明显高并且随着训练持续上升就是过拟合信号。val_batch0_pred.jpg是验证集的可视化结果肉眼看一遍比任何指标都直观——框是否贴合裂缝、有没有明显漏检一眼就能看出来。5. 裂缝检测落地避坑误检、漏检与转模型前的自查清单5.1 细裂缝被忽略或整段断裂输入分辨率与 anchor 的影响现象模型对粗大裂缝检测得很好对细裂缝几乎没有召回或者一条连续裂缝被切成好几段只框出其中一截。原因有两个。一是输入分辨率不够细裂缝在 640 分辨率下经过下采样之后可能只剩几个像素宽特征在深层网络里被抹掉了。二是默认 anchor 是根据整体标注框统计出来的如果数据里粗裂缝占多数细裂缝的长宽比和 anchor 匹配不上。解决把--img从 640 提到 800 或 960同时把--batch调小在train.py开启--multi-scale让模型适应不同尺度如果细裂缝占比特别低把含细裂缝的图片在标注时多切几段增加实例数。另外可以观察训练日志里的 anchor 统计必要时用--noautoanchor配合人工指定的 anchor 重新训练。5.2 阴影和水渍被当成裂缝负样本、置信度阈值与后处理过滤现象precision 不高预测结果里大量框落在阴影边缘、水渍纹理、钢筋轮廓上置信度还不低。原因这些干扰纹理在视觉上和裂缝一样都是暗色细长结构模型没学会区分“真正的开裂”和“看起来像开裂”本质是训练数据里这类负样本太少。解决分三步第一训练集里混入不含裂缝的背景图不标注任何目标让模型见过“干净的墙”长什么样第二数据增强里加大亮度、对比度扰动避免模型只靠颜色深浅做判断第三推理时把conf_thres从默认 0.25 提到 0.5 左右并加一个长宽比过滤——真实裂缝的长度宽度比通常大于 3误检目标的长宽比往往更接近 1。这个后处理不复杂效果立竿见影。5.3 训练和验证同源导致 mAP 虚高按采集批次划分数据现象训练时 mAP 到了 0.95团队很高兴拿去现场一测准确率掉到 0.7 以下。原因训练集和验证集是随机划分的而同一条裂缝的连续拍摄照片非常相似几乎算是同一张图的轻微变化。模型在训练时已经见过验证集里的目标验证分数自然虚高。这种情况属于典型的数据划分泄漏。解决划分数据集时不按图片随机分而是按采集批次或结构部位分。比如今天采集的 A 桥和 B 桥都进训练集C 桥全部进验证集或者同一面墙的照片不能既在训练集又在验证集。保证验证集是模型从未见过的拍摄对象mAP 才有参考价值。5.4 loss 不降或 val loss 反弹先查数据再调模型现象训练了二三十轮train loss 一直不降或者 train loss 降得很好但 val loss 从某轮开始持续反弹。原因loss 不降大概率是数据问题而非模型问题。常见的有标注文件缺失导致模型学不到某些图、XML 转换时坐标错位、类别 id 不连续val loss 反弹则多半是过拟合信号尤其是小数据集上训练轮数过多。解决先检查 labels 文件和 images 数量是否一致随机抽几张图叠加标注可视化确认框的位置正确再统计每种类别的实例数量类别极不均衡时优先做数据补充而不是调参。过拟合则缩短 epochs把hyp.scratch-low.yaml里的增强参数适当调低或者增加一些真实采集的数据。训练不收敛这种事九成是数据问题不要急着去改学习率。5.5 转 ONNX 后推理结果对不上固定形状与前处理一致性现象PyTorch 里检测正常的图片导出 ONNX 后用 onnxruntime 推理置信度变了、框偏移了甚至完全检测不到。原因导出时默认支持动态输入尺寸但 ONNX Runtime 对动态 shape 的处理和 PyTorch 不完全一致另一个更隐蔽的原因是 letterbox 预处理不一致——YOLOv5 训练时会给图片加灰边 resize 到方形如果导出和推理时用的填充方式有差异坐标换算就会出错。解决导出时固定输入尺寸命令里加--dynamic False --imgsz 640推理代码里复用 YOLOv5 仓库utils/augmentations.py里的letterbox函数保证预处理逻辑完全一致。这一步做完ONNX 结果和 PyTorch 结果基本就对上了。6. 从能跑到能用ONNX 导出、INT8 量化与边缘部署的取舍6.1 让模型适配边缘设备从固定 shape 导出到 INT8 量化拿到能用的best.pt之后下一步是导出部署格式。常见做法是转 ONNX再按目标设备选择量化方案。导出命令如下。python export.py \ --weights runs/train/exp/weights/best.pt \ --include onnx \ --opset 12 \ --dynamic False \ --imgsz 640导出时留意日志里输出的输出张量 shape。不带 NMS 的 ONNX 模型输出的是[1, 25200, 85]的原始预测张量需要在部署代码里自己实现解码和 NMS。如果换到 RK3588、Jetson 这类 NPU 平台INT8 量化几乎是必经之路但裂缝检测对量化特别敏感细裂缝的宽度往往只有几个像素量化误差可能直接把目标抹掉。根据项目精度要求优先考虑 FP16其次再试 INT8并且量化后要重新跑一遍验证集对比 mAP 和单张可视化结果不要只看指标。6.2 部署侧检查清单先跑通单张图再上板边缘部署最容易翻车的不是模型本身而是代码里的“小差别”。我的习惯是先在 PC 上用 ONNX Runtime 跑通单张推理确认输出和 PyTorch 一致再迁移到边缘设备。checklist 很固定输入图像是否做了 letterbox、RGB/BGR 通道顺序是否正确、归一化是否除以 255、坐标是否从归一化转回原图尺寸。每一步错了表现都是框偏、漏检排查起来却要花半天。最后提一个真实的教训有一版模型在验证集上 recall 偏低我把推理置信度阈值从 0.25 降到 0.1 去救召回结果现场跑起来满屏误检框后来靠长宽比过滤和面积过滤才把误检压下去。阈值不能乱调每个阈值背后都应该有验证集统计支撑。这套链路走到这里你的裂缝检测项目已经从能跑变成了能用希望帮到你。本文还有配套的精品资源点击获取
返回列表