简介:基于YOLOV8的路面桥梁墙体裂缝识别项目,面向计算机视觉与深度学习方向的在校学生、研究者及工程开发者,聚焦道路、桥梁、墙体表面裂缝的自动检测,适合作为算法实验、课程设计或工程落地的参考基线。压缩包共78个文件,约2.55MB,包含26个yaml配置(模型结构、训练参数)、21个py脚本(检测预测、数据预处理、工具函数)、18个pyc编译文件及2个md说明文档,并附有若干png、jpeg、jpg示例图片,目录划分清晰,便于按需查阅。源码均已本地编译通过,评审分达95分以上,难度适中,覆盖从数据集配置、模型加载到预测输出的完整流程;其中predict.py等脚本可直接运行,配合可视化截图可快速验证识别效果。目前已有130人学习下载,适合希望快速上手YOLOv8目标检测应用的学习者参考借鉴。
1. 裂缝识别项目,拿 YOLOv8 源码跑通之前先想清楚这三件事
一个叫“基于YOLOV8的路面桥梁墙体裂缝识别Python源码+文档说明”的项目包,本质上是一个表面缺陷检测工程。路面、桥梁、墙体三类裂缝图片,背景差异大:路面有车道线干扰,桥梁有粗糙混凝土纹理,墙体有阴影和水渍。很多人拿到源码后第一件事就是跑 demo,等画框脚本能出结果,就以为项目收工了。真正决定分数和验收质量的,其实是后面三件事:标签规范有没有定死,小目标漏检有没有控制住,以及训练出来的权重能不能从笔记本挪到评测环境。这篇文章就顺着“原理 → 数据准备 → 训练 → 推理部署 → 排坑”的顺序,给你一套可以照着复现的 YOLOv8 裂缝识别落地流程。适合正在做毕设、课程设计,或者想快速评估这个方向值不值得投入的从业者。
2. 把 YOLOv8 裂缝识别拆开看:网络结构、检测头和标签体系怎么定
2.1 为什么用 YOLOv8 而不是老版 YOLO 做裂缝检测
裂缝是细长、稀疏、方向随机的目标,很多裂纹宽度只有几个像素,长度却可能跨越大半个画面。YOLOv5 用 anchor 机制回归目标框,碰到长宽比极端的裂缝,anchor 匹配阶段就容易把目标当成背景。YOLOv8 改为 anchor-free 检测头,直接回归中心点和宽高,配合 TaskAlignedAssigner 按分类和定位的综合分数分配正样本,细长目标也能稳定拿到监督信号。这是它在这类场景下的第一个优势。
网络结构上,YOLOv8 依然由三块组成:Backbone 用 CSPDarknet 做多尺度特征提取,Neck 用 PAN-FPN 把深层语义和浅层纹理融合,Head 用解耦结构把分类和边界框回归分开。裂缝的纹理细节主要在浅层特征,而判断“这是裂缝还是阴影”要靠更深层的语义;PAN-FPN 自顶向下、自底向上的路径恰好能把两边都照顾到。在网上搜“yolov8网络结构图”,能看到很多带通道数标注的版本,你在源码包里找到对应文档后,先对着结构图确认自己用的权重是哪一层输出的融合结果,再开始调参数,方向会清楚很多。
模型体积的选择也有讲究。YOLOv8n 跑得快,但浅层感受野有限,对极细裂缝容易丢。YOLOv8s 和 YOLOv8m 更适合做裂缝检测的默认起点,显存紧就选 s,精度优先选 m。如果你的机器只有 CPU,没有 NVIDIA 显卡,一开始别碰 x 或 l 版本,否则一个 epoch 要跑十几分钟,调参基本没法进行。环境相关的问题,放到第三章一起解决。
2.2 裂缝目标不是“一条线”,而是“一个框”:标签约定要先定死
目标检测模型的输出永远是矩形边界框,哪怕裂缝长得再扭曲,最终也要用外接矩形表达。很多人在标注时习惯沿着裂缝画精细的 polygon,然后在转换脚本里用最小外接矩形生成 bbox。这样做的副作用是:裂缝细长,矩形框里会包进大量背景。包进去的背景越多,模型越难学,训练出来的置信度普遍偏低,NMS 之后还可能把相邻裂缝误合并。
常见做法是,先定一套标签规范再动手标。我的习惯是:只保留长度大于 50 像素、宽度在 2 到 200 像素之间的目标;同一条连续裂缝只画一个框,不让它因为弯折被拆成多段;同一张图上,网状裂缝按网格单元分框,但每个框内必须存在明显裂缝线。类别定义最好和项目文档里的清单保持一致,比如“横向裂缝、纵向裂缝、网状裂缝、剥落”四类,或者直接一个 crack 类加背景。对于“基于YOLOV8的路面桥梁墙体裂缝识别”,我建议先做单类 crack,跑通之后再按场景细分,不然类别间样本数量不平衡会拖累整体效果。
下面是一份可以直接写进文档说明的标签约定参考:
| 类别 | 定义 | 标注规则 | 备注 |
|---|---|---|---|
| crack | 路面/桥梁/墙体表面裂缝 | 矩形框紧密包裹裂缝主体,留 2-3 像素余光即可 | 不做像素级分割 |
| background | 无裂缝区域 | 不标注 | 由模型自动学习负样本 |
| 阴影 | 所有被判为阴影的区域 | 不单独建类,用增强消除 | 见第五章排查 |
表格归表格,真正决定数据质量的是转换脚本里的过滤逻辑。如果源码包里已经带了 Labelme 或 labelimg 的标注数据,下一步要把 polygon 转成 YOLO 格式;如果文档说明里只有示例代码,也要自己写一遍,否则无法应对自己采集的数据。
2.3 预训练权重、冻结骨干和训练参数含义
YOLOv8 官方预训练权重是在 COCO 上训练的,裂缝的外观和自然图像差异很大,但它学到的边缘、纹理、形状特征依然可以迁移。合理做法是加载预训练权重后,先冻结 Backbone 前 10 层训练 30 个 epoch,让检测头先适应裂缝框的输出分布;再解冻全部层,用更小的学习率微调 50 到 100 个 epoch。这样能避免前期梯度太大,把预训练特征破坏掉。
这里集中说几个最常见的训练参数含义,也就是网上常被搜的“yolov8模型训练参数含义”。imgsz 决定输入图片缩放尺寸,默认 640,但对小目标裂缝通常建议 960 或 1280,显存不够就配合 batch 降小。batch 是一次迭代送入的图片数量,CPU 环境 4-8 即可。lr0 是初始学习率,默认 0.01,数据量小的时候可以降到 0.005。patience 是早停轮数,例如 patience=10 代表验证指标连续 10 轮不提升就停止。mosaic 数据增强对裂缝数据集不算友好,它会把四张图拼在一起,生成大量奇怪的拼接背景;在训练后半段最好关闭它。
这些参数不是孤立的。imgsz 调大,相当于变相增加了小目标在特征图上的像素占比,mAP50 通常会有肉眼可见的涨幅,但训练时间和显存占用也随之上涨。batch 调大,梯度更稳定,但学习率也要适当提高,否则收敛速度明显变慢。拿到源码包后,先不改模型结构,只在这几个参数上做小范围搜索,往往比魔改网络更容易拿到高分。
3. 用 YOLOv8 训练自己的裂缝数据集:从 Labelme 标注到 train.py 的命令
3.1 数据集目录结构与 Labelme 标注转 YOLO 格式脚本
YOLOv8 的训练脚本通过 YAML 文件组织数据集,你要先把图片和标注整理成下面这种结构。这是网上各种“处理数据集用于yolov8训练”教程的标准布局,源码包里一般也会出现相同的目录名。
datasets/crack/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── crack.yamlimages 下放原图,labels 下放同名 txt。txt 里每行代表一个目标,格式是class x_center y_center width height,其中中心点和宽高都除以了图片宽高,做了归一化。如果同时有路面、桥梁、墙体图片,建议都放进同一个 train 目录,让模型自己学习不同背景,但 train 与 val 的划分要按场景分层抽,避免验证集清一色是墙面。
Labelme 导出的 JSON 里存的是多边形点位,下面这个脚本能把 polygon 转成 YOLO 的矩形框:
import json import os from glob import glob def labelme2yolo(json_path, out_dir, class_map): with open(json_path, 'r', encoding='utf-8') as f: data = json.load(f) img_w = data['imageWidth'] img_h = data['imageHeight'] lines = [] for shape in data['shapes']: label = shape['label'] if label not in class_map: continue pts = shape['points'] xs = [p[0] for p in pts] ys = [p[1] for p in pts] x_min, x_max = min(xs), max(xs) y_min, y_max = min(ys), max(ys) # 过滤太细或太小的标注,减少噪声框 if x_max - x_min < 16 or y_max - y_min < 16: continue x_center = (x_min + x_max) / 2 / img_w y_center = (y_min + y_max) / 2 / img_h w = (x_max - x_min) / img_w h = (y_max - y_min) / img_h lines.append(f"{class_map[label]} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}\n") if not lines: return txt_path = os.path.join(out_dir, os.path.basename(json_path).replace('.json', '.txt')) with open(txt_path, 'w', encoding='utf-8') as f: f.writelines(lines) # 调用示例 # labelme2yolo('data/001.json', 'datasets/crack/labels/train', {'crack': 0})这段脚本的关键点有三个。第一,用 polygon 所有点的最小/最大坐标生成轴对齐矩形框,不考虑旋转,简单但有效。第二,16 像素的过滤阈值是经验值,小于它的目标大多是噪点,硬训练只会让模型去学图像噪声。第三,class_map 里的索引顺序必须和最终的 crack.yaml 完全一致,顺序错了,评估阶段的标签就等于整体错位。
转换完之后,还要处理图片划分。常见做法是写一个随机抽样脚本,按 8:2 把图片路径分到 train.txt 和 val.txt;但更稳妥的是按文件夹划分,采集时就把路面、桥梁、墙体分开,最后在训练目录里混合。我自己会额外统计 labels 里每个类别的框数量,如果某类只有几十个框,训练效果基本可以提前判定为不合格。
3.2 在 ubuntu20.04 搭建 CPU 环境与最小训练命令
网上搜“ubuntu20.04搭建yolov8环境cpu版本”的教程很多,核心步骤其实就三条命令。Ubuntu 20.04 自带 Python 3.8,建议用 conda 新建一个 3.10 的独立环境,避免和系统 Python 打架。CPU 版 PyTorch 从官方源安装,再装 ultralytics。
conda create -n yolov8-crack python=3.10 -y conda activate yolov8-crack pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu pip install ultralytics==8.0.200这里强调一下,ultralytics 版本会影响后续导出 ONNX 的算子,不定版本的话,不同机器上训练的权重可能互相不兼容。CPU 版 torch 只拿来验证和跑小数据集,真要训练大图片,还是建议去云 GPU 或者本地独显机器。不过环境配置这一步,CPU 和 GPU 完全一致,也就是先建环境再装包。
数据集配置用 YAML 描述,下面是最小可运行的 crack.yaml:
path: ./datasets/crack train: images/train val: images/val names: 0: crackpath 写工程根目录下的相对路径,train 和 val 是相对 path 的目录。这里最容易错的是 names 的索引拿 1 开头,YOLOv8 的类索引必须从 0 连续递增。如果你的文档说明里把“裂缝”和“剥落”分了两类,这里就写0: crack和1: spalling。
训练脚本推荐写成 Python 文件固定下来,不推荐每次在终端敲一长串参数。源码包里常见的 train.py 核心逻辑如下:
from ultralytics import YOLO model = YOLO('yolov8s.pt') model.train( data='crack.yaml', epochs=100, imgsz=960, batch=8, device='cpu', lr0=0.01, patience=10, save_period=10, project='runs/crack', name='exp1', )这里逐项说明:yolov8s.pt 是官方预训练权重,第一次运行会自动下载;imgsz=960 是考虑到裂缝小目标调高的,如果内存不足就回退到 640;batch=8 在 CPU 上已经偏保守,内存继续爆就改成 4;device='cpu' 表明不用显卡;patience=10 是早停,防止过拟合后还硬跑;save_period=10 表示每 10 轮保存一次权重,训练中断可以直接用 last.pt 续跑。把训练命令写进脚本而不是手敲,最大好处是参数变动有记录,复现时不会靠记忆。
3.3 训练日志与损失曲线图怎么读
训练完成后,runs/crack/exp1 目录下会生成 results.png、confusion_matrix.png、PR_curve.png 等文件。网上常说的“yolov8画损失函数曲线图”,指的就是 results.png 里那几条 loss 曲线。这张图里有 train/box_loss、val/box_loss、mAP50、mAP50-95 等子图,你不一定每条都看,但要抓住两个信号。
第一个信号是 val loss 有没有在训练中途掉头上升。如果 train loss 一直降,val loss 在第 40 轮开始反弹,基本就是过拟合。常见应对是降低 lr0 到 0.003,增大增强强度,或者把 epochs 调短。第二个信号是 mAP50 曲线有没有平台期。裂缝数据集通常不会像 COCO 那样平滑上升,经常是前期涨到 0.3,然后卡住很久,再在某次学习率下降到后突然跳到 0.6,这种正常,别中途放弃。
真正容易被忽略的是 best.pt 和 last.pt 的差异。best.pt 是验证集上 mAP 最高的权重,last.pt 是最后一轮的权重。裂缝数据噪声大时,二者差距可能很大,推理和后续导出一定要固定用 best.pt。如果文档说明里没有明确标注,自己训练完也要按这个习惯来,省得踩到“权重选错”的坑。
4. 裂缝识别的推理、评估和导出:best.pt 到 ONNX 与板端推理
4.1 用训练好的 best.pt 批量跑图片并输出带框结果
训练完之后的推理脚本比训练脚本更短。输出的带框结果会保存到 project 对应目录,同时生成和标签同格式的 txt,方便做对比或转成报表。核心代码:
from ultralytics import YOLO import glob model = YOLO('runs/crack/exp1/weights/best.pt') images = glob.glob('test_images/*.jpg') for img_path in images: results = model.predict( source=img_path, conf=0.25, iou=0.45, imgsz=960, save=True, save_txt=True, project='runs/predict', name='test_render', line_width=2, )conf 是置信度阈值,裂缝检测里漏检多的时候就把它降到 0.1,先看可视化再决定;iou 是 NMS 合并阈值,建议维持 0.45,别为了消除重复框调到 0.7,否则两条平行裂缝会被并成一条。line_width=2 是针对高分辨率大图设置的,检测框细了图片放大后看不清。save_txt=True 保存的预测 txt 文件直接放在 runs/predict/test_render/labels 下。
推理这里最容易翻车的是大图。桥梁和墙体巡检照片动不动就是 4000x3000,如果直接整体缩放到 960,裂缝宽度在缩放图上可能只剩 1 个像素,模型根本看不见。常见做法是切片推理:把大图切成 960x960 的窗口,相邻窗口重叠 20% 到 30%,所有窗口检测完再按原始坐标合并,NMS 去重。切片会造成同一目标在多个窗口里重复出现,但合并后通常只保留置信度最高的一个,漏检率会大幅下降。
4.2 mAP 精度评估:裂缝指标不要只看大框
用 Ultralytics 自带的 val 方法评估,会输出一整套指标。命令如下:
model = YOLO('runs/crack/exp1/weights/best.pt') metrics = model.val(data='crack.yaml', imgsz=960, conf=0.001, iou=0.6) print(metrics.box.map50, metrics.box.map)这里 conf=0.001 很关键。评估时如果也用 0.25,PR 曲线会少掉低置信度这一段,mAP 偏高但不可信。iou=0.6 是验证时计算正负样本的 IOU 阈值,不要随意改。metrics.box.map50 是 IOU=0.5 的 mAP,metrics.box.map 是 mAP50-95。
裂缝场景的指标要小心读。因为裂缝框细长,预测框只要上下偏移 5 个像素,IOU 可能就从 0.7 跌到 0.5 以下。所以 mAP50-95 通常偏低,甚至只有 0.2,但这不代表模型不能用。正确的做法是打开 PR_curve.png 看曲线下面积,再打开 confusion_matrix.png 看有没有系统性误检。对单类裂缝模型,如果 PR 曲线在 0.8 置信度附近掉了,说明大量低质量预测来自背景纹理,后面增强或清洗的方向就很明确。
4.3 导出 ONNX 并在 RK3588 上做板端部署
源码包最后的落地步骤,通常是把 PyTorch 权重导出成 ONNX,再转到板端推理。CPU 环境一样可以导出 ONNX,只是推理速度快慢而已。导出命令:
yolo export model=runs/crack/exp1/weights/best.pt format=onnx opset=12opset=12 是兼容性比较稳的选择,RKNN 工具链和 onnxruntime 都支持。导出后如果出现算子报错,先检查 ultralytics 和 torch 的版本,常见做法是保持和训练环境一致,不要升级到新版本再导出。
拿到 ONNX 后,在 PC 上先用 onnxruntime 验证一遍,确认精度没有浮动,再转换到 RKNN:
import onnxruntime as ort session = ort.InferenceSession('best.onnx') # 推理前必须做 letterbox,保持和训练时相同的数据预处理这里最容易忽略的是归一化参数。YOLOv8 训练时像素除以 255,RKNN 转换工具不会自动识别这一点,你要在 rknn-toolkit2 的配置里显式设置 mean=0, scale=0.0039215,否则上板之后检测结果要么大面积漏检,要么对所有背景都报目标。另一个坑是量化校准集。RK3588 部署时一般用 INT8 量化,校准集只要挑一两百张最有代表性的裂缝图片,不需要全量图片。量化后 mAP 下降 3 到 5 个点是正常的,如果跌了 15 个点以上,优先检查和量化校准集的代码路径匹配。
5. 裂缝识别常见坑与排查:光照、小目标和过拟合的三个踩坑现场
5.1 训练 loss 正常但 mAP 低:先查标注框的最小宽高
现象:训练 100 轮之后,loss 曲线平滑,但 mAP50 只有 0.2 上下,可视化结果全是巨大的框,一条小裂缝被笼统框起来。
原因:标注规范没有统一。多份数据来自不同人员,有人把整片网状裂缝画成一个巨大的 polygon,有人只标了局部,bbox 宽高差异超过十倍,模型学到的目标尺寸分布是分裂的。
解决:写一个数据清洗脚本,读 labels 下所有 txt,过滤掉宽或高小于 8 像素、面积小于 256 像素的框;对同一张图上高度重叠的框做合并,保留面积最大的。清洗之后重新训练,mAP 往往会回到 0.5 以上。注意过滤阈值不要定太高,否则细裂缝会被全部删掉,反而造成小目标缺失。
5.2 阴影和水渍被误检成裂缝:只靠 bbox 特征不够
现象:在墙面和桥墩图片中,接缝阴影总被框成裂缝,真正裂缝漏检,预测结果看着像在乱猜。
原因:裂缝和阴影在灰度纹理上高度相似。训练集如果以明亮混凝土墙面为主,阴影区域成为最强的视觉干扰,检测头分不清边缘轮廓到底是真实裂纹还是遮挡。
解决:在推理阶段加入 CLAHE 对比度增强,优先压掉阴影像素。
import cv2 img = cv2.imread('test_wall.jpg') gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8)) enhanced = clahe.apply(gray) enhanced_bgr = cv2.cvtColor(enhanced, cv2.COLOR_GRAY2BGR) # 用 enhanced_bgr 代替原图送入模型clipLimit 建议取 1.0 到 2.0,太大反而放大图像噪声,让模型把石头纹理也当成裂缝。这种增强治标不治本,根治办法是训练集里加入无阴影干扰的裂缝图,或者对原图做光照归一化。但作为项目“文档说明”里的可选项,加一个开关就能让演示效果好不少。
5.3 三类表面数据混训时,墙体裂缝被识别成路面裂缝
现象:训练集同时包含路面、桥梁、墙体裂缝,单独每类的验证准确率正常,但融合验证时墙体图片大量被归类到其他类别,场景之间的分类边界混乱。
原因:类别定义和背景纹理强耦合。模型没有学会“裂缝本身”,而是学会了“路面背景=路面裂缝”“墙体背景=墙体裂缝”。
解决:按场景分层划分数据集,训练集和验证集里三类图片比例尽量接近 1:1:1。如果某个场景样本实在少,就不要在统一模型里硬分三类,改为每个场景各训一个单类检测器,在推理时用场景分类器先判断来源,再路由到对应模型。这个思路在工程初验阶段比盲目扩数据更省时间。
5.4 大图上漏检:输入尺寸和切片策略没有配合
现象:一张 4000x3000 的桥梁大图,imgsz=640 跑完,画框位置偏移明显,人眼可见的裂缝模型完全没输出。
原因:resize 后裂缝宽度在特征图上可能已经小于 1 个像素;另外,模型训练时看到的是 640x640 的局部上下文,推理时整图被压缩,目标和周围纹理的比例变了。
解决:先把推理 imgsz 提到 960 或 1216,再配合滑动窗口切片,窗口重叠率保持 15% 到 30%。检测完的框按窗口偏移映射回原图,再用 NMS 合并。对桥梁巡检这类场景,切片推理基本是必选项。不要嫌多写十几行代码,漏检率往往能从 30% 压到 5% 以内。
5.5 CPU 内存不足和训练中断:环境配置的第二层坑
现象:在 ubuntu20.04 用 CPU 训练,数据集加载几轮后进程被杀,提示 OOM;或者训练中断,best.pt 没有更新,只能从零再来。
原因:Ultralytics 默认数据加载器会缓存图片加速读取,CPU 机器内存本来就紧张,Mosaic 增强又会同时拼接多张原图到显存,叠加之后内存直接爆掉。
解决:训练参数里显式加 cache=False,并用 workers=0 关闭多进程加载。这样速度慢了但不会崩。中断恢复时,用 last.pt 作为预训练权重继续训练,原本的 epochs 改成剩余轮数。示例配置:
model = YOLO('runs/crack/exp1/weights/last.pt') model.train(epochs=30, cache=False, workers=0)注意这一点很像玄学,但很多源码包默认参数都是为 GPU 环境写的,跑到 CPU 上不调整就会翻车。把 cache 和 workers 这两个参数调低是 CPU 训练的第一优先动作。
6. 裂缝数据不够时的进阶招:伪标签和宽度统计,把识别结果变成“量”
6.1 用伪标签把原始图片变成训练集
当训练数据不足的时候,一个非常实用的招数是伪标签。用训练好的 best.pt 对一批未标注的原始图片做低置信度推理,比如把 conf 设为 0.3,让模型先把有明显裂缝的框圈出来。紧接着打开这些框做人工复核,确认可行的直接保留,错误的手工修正,这一批带框图片再混进训练集重新训练。裂缝标注本来就费眼力,用模型预生成初稿能把单张标注时间压到原来的三分之一。
这个办法要控制伪标签数量,别一次性加太多。每次只加本轮模型最有把握的图片,让训练集与模型能力同步进化。我在实际操作时会写一个独立脚本,把预测 txt 和图片复制到 labels 目录,并记录哪些来自伪标签,方便后续回滚。用这个思路,原始样本只有几百张的项目也能把 mAP 再往上推一截。
6.2 把检测框升级为裂缝宽度统计
检测框给的是位置和类别,但工程验收常常还要裂缝宽度。既然模型已经稳定输出 bbox,就可以在框内做二次处理,把“有没有裂缝”变成“裂缝有多宽”。常见做法是用局部阈值分割加距离变换:
import cv2 def crack_width(crop_gray): _, binary = cv2.threshold(crop_gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) dist = cv2.distanceTransform(binary, cv2.DIST_L2, 3) width_px = 2 * float(dist.max()) return width_px这段代码的原理是,二值化后裂缝区域的最大内切圆半径乘二,近似等于裂缝宽度像素。用在 bbox 局部裁剪图上,前提是框内裂缝清晰、背景光滑;如果背景有大量水渍,先做形态学开运算过滤小斑块,再算宽度。最后结合标定比例,把像素宽度换算成毫米,结果存成 CSV,给土木工程师直接读。这套后处理不涉及 YOLOv8 结构改动,是源码包文档说明里很值得补的一段。
收尾这个项目时,我习惯在工程里保留三样东西:数据清洗脚本、固定参数的训练脚本、只依赖 ONNX 推理的最小服务文件。有了这三样,半年后换一台机器也能把结果复现出来,不用再赌当时的 Python 环境。希望帮到你。
本文还有配套的精品资源,点击获取