基于YOLOv5的桥梁裂缝检测实战:从数据标注到模型部署
2026/9/23 4:42:27 网站建设 项目流程

简介:一项基于Python与YOLOv5的桥梁路面裂缝检测识别项目,提供可直接运行的源码,并附有模型权重下载脚本,面向计算机、土木工程等专业的学生,可满足毕业设计、课程设计及期末大作业需求,也适合目标检测初学者作为实战参考。资源采用ZIP压缩包形式,总计85个文件,包含Python源码、YOLOv5配置参数、Shell辅助脚本、Dockerfile容器部署文件以及用于验证的示例图片,压缩包整体大小仅1.58MB,轻巧便携。项目源码已经本地编译并通过验证,评审得分高达98分,难度适中,内容覆盖数据配置、模型搭建、训练与推理等核心环节,目录中包含models、utils、data、runs等模块,结构清晰,便于学习者对照调试。目前已有179人学习,对于希望快速掌握裂缝自动检测流程,或需要完成相关课业任务的学习者而言,是一份高质量且易于上手的参考资料。

1. 凌晨四点在桥底拍完最后一张裂缝照片,我决定用YOLOv5重做检测

凌晨的桥检车上,头顶是横梁,脚下是施工缝,手里的相机一晚上拍回几千张混凝土表面照片。这些照片里,有的是结构性裂缝,有的是养护不到位导致的水渍纹,还有的只是模板接缝的影子。靠人眼一张张去分,真正干活的时间会被拖到天亮;而传统阈值分割和边缘检测,在阴影和粉尘面前又脆弱得离谱。这就是我后来把路面桥梁裂缝检测从纯视觉方法切换到深度学习的原因,选来选去最终落到一套基于Python和YOLOv5的源码模型上。

YOLOv5在通用目标检测里名气大,但真正让它适合裂缝识别的是单阶段推理速度和可调的输入分辨率。桥梁裂缝目标细长、背景脏、尺度变化大,YOLOv5的FPN结构能从不同尺度捕捉小目标。配上源码模型,从数据标注、训练到部署,每一步都看得见摸得着。这个方向适合两类人:一类是有标注数据但不知道怎么用的检测工程师,另一类是刚学Python不久、想拿真实项目练手的学生。接下来我就按自己跑通一版裂缝检测的流程,把数据准备、训练参数、踩坑记录和部署验证一起讲明白。

2. 裂缝检测数据准备:从现场照片到YOLOv5能直接吃下的数据集

2.1 裂缝数据长什么样:分辨率、光照和标注风格

裂缝检测和一般的行人检测有个明显差别:裂缝在图像里通常只占很小一块面积,而且形态是细长条,一条两米长的裂缝在640×640的画面里可能只有60×5像素。如果现场照片分辨率很高,比如4000×3000,直接缩放到640,细小裂缝几乎会被压没。常见做法是先对原图做切片,把大图切成若干640×640的块,保留裂缝的原始像素密度,再喂给网络。我一般会写一个滑窗切片脚本,重叠率设10%左右,避免裂缝正好被切在边缘。

光照问题更麻烦。桥梁底面光照不均,有些裂缝在阴影里,有些在强反光里。YOLOv5的数据增强里有HSV抖动,但对这种真实场景帮助有限,最可靠的办法是在采集数据时尽量覆盖不同时刻、不同角度。标注风格方面,裂缝检测有两种主流:一种是框标注,框住裂缝的明显可见段;另一种是分割掩码,标出每一个裂缝像素。做目标检测项目,用框标注就够了,但要注意框的紧致程度。框太大,会把背景纹理包进来;框太小,裂缝断点太多,模型反而学不到整体结构。经验值是标到裂缝可见段的外接矩形,稍微往外扩2到3个像素,保证框内裂缝连续。

2.2 用LabelImg做框标注的要点

标注工具我常用LabelImg,虽然界面老一点,但胜在轻量、格式直接。启动后先选PascalVOC格式,后面再转成YOLO的txt。裂缝标注有个容易犯的错:一条裂缝分成好几段,有人会一段标一个框。这种做法会导致同一目标被拆成多个碎片,训练时锚框匹配非常混乱。正确思路是,如果裂缝在视觉上是连续一条线,即使中间有一点被阴影遮挡,也应尽量用一个框覆盖完整段,只要框的长宽比别太极端就行。YOLOv5对长条目标是有能力处理的,但框的长宽比超过20比1时,锚框效果会明显下降。

标注完记得检查两个地方:一是是否有标签名称拼写错误,比如“crack”和“crackk”混用;二是是否出现了空标签文件。空标签文件在实际中很常见,可能是手误点了一下“Save”没标任何框。这些空txt文件在训练时会影响数据加载,需要清理。我习惯在标注结束跑一个python脚本,把所有空的label文件列出来,直接删除,同时把对应的图像从训练集里移除。

2.3 数据划分脚本:train/val/test按目录摆放

YOLOv5的data配置只需要图片路径,标签文件会自动去同名目录找。标准目录结构是这样的:

dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ └── labels/ ├── train/ ├── val/ └── test/

注意images和labels的根目录必须同级,且训练集和验证集里的图片文件名一一对应,否则YOLOv5会报“label not found”。写一个划分脚本,把原始图片和标注后的txt按比例随机分配。这里要注意随机数种子,避免每次运行结果不一样,后面复现训练时对不上。

import random import shutil from pathlib import Path random.seed(42) img_src = Path("original_images") # 现场裁剪后的图片 label_src = Path("original_labels") # LabelImg导出的txt,需先转成YOLO格式 train_ratio = 0.7 val_ratio = 0.2 # test_ratio用剩余比例 images = list(img_src.glob("*.jpg")) assert images, "没有找到图片文件,请检查目录路径" # 先打乱,再按比例切分 random.shuffle(images) total = len(images) train_idx = int(total * train_ratio) val_idx = int(total * (train_ratio + val_ratio)) splits = {} splits["train"] = images[:train_idx] splits["val"] = images[train_idx:val_idx] splits["test"] = images[val_idx:] for split, img_list in splits.items(): img_out = Path("dataset/images") / split label_out = Path("dataset/labels") / split img_out.mkdir(parents=True, exist_ok=True) label_out.mkdir(parents=True, exist_ok=True) for img in img_list: shutil.copy(img, img_out / img.name) label_file = label_src / (img.stem + ".txt") if label_file.exists(): shutil.copy(label_file, label_out / label_file.name) else: print(f"警告: {img.name} 没有对应的txt文件,已跳过标签")

逻辑说明:脚本先固定随机种子,保证每次运行划分结果一致。切分时把图片路径列表按比例切成三段,分别拷贝到对应目录。关键点是“label_file.exists()”的判断,它会把缺失标签的图片单独提示出来,避免训练时因为标签缺失而静默忽略该图。参数方面,train_ratio和val_ratio需要根据数据量调整,如果总图片数少于200张,建议把val_ratio降到0.1,否则验证集太小,评估指标波动很大。

2.4 数据增强怎么加才不会让细裂缝学歪

YOLOv5默认的增强已经很强,但对裂缝这种细长目标,有些增强要谨慎。比如旋转角度,默认hyp里有degrees=0,可以试着开到5到10度。注意裂缝不像车辆,方向是有物理意义的:桥梁横缝和纵缝代表不同受力状态,标注时类别区分可能就是这个。如果旋转角度太大,横缝转成斜缝,类别就乱了。翻转增强fliplr和flipud可以开,但要注意裂缝位置在桥梁墩柱上可能有左右对称性,一般没问题。

尺度增强scale对裂缝很关键。裂缝尺度本身就细,如果scale设得太大,有些裂缝会被缩成几个像素,小目标检测基本失效。我一般把scale控制在0.4到0.6之间,让网络在不同尺度下都能看到清晰的裂缝纹理。另一项是mosaic增强,默认开启,它会拼四张图,对提高小目标泛化能力有帮助,但mosaic会把四张图的上下文混在一起,训练周期较长。如果数据集只有几百张,建议mosaic一直开着,因为裂缝样本本身太少,靠增强扩充是唯一出路。如果已经训练了50轮还是过拟合,再考虑关闭mosaic做微调。

3. 环境搭建与训练:用YOLOv5训练自己的裂缝检测模型

3.1 conda yolov5环境搭建和依赖安装

先说环境。YOLOv5要求Python版本3.8以上,PyTorch 1.8以上。用conda建独立环境是最稳的,避免和系统的其他Python项目冲突。网上很多“conda yolov5”教程会直接让你pip install -r requirements.txt,但如果你是在国内网络,一定要先配好pip镜像,否则下载大依赖包时容易超时。

conda create -n yolov5 python=3.9 -y conda activate yolov5 pip install torch==1.13.1 torchvision==0.14.1 --index-url https://download.pytorch.org/whl/cu117 pip install -r requirements.txt

逻辑说明:第一条命令创建名为yolov5的虚拟环境,指定Python 3.9;第二条命令激活环境;第三条安装CPU或GPU版PyTorch,这里用的是CUDA 11.7对应的版本,你在操作时需要用nvidia-smi查自己机器的CUDA版本,再选择对应的PyTorch。最后一条安装YOLOv5的依赖包,包含numpy、opencv、matplotlib等。参数说明:requirements.txt里有一个坑,它会自动安装最新版opencv-python,如果你本机已经有摄像头权限问题,建议装opencv-python-headless替代。

训练前我需要检查GPU是否可用:

python -c "import torch; print(torch.cuda.is_available())"

如果输出True,说明GPU正常。如果输出False,也可以继续用CPU训练,但裂缝数据集至少几百张,CPU训练会非常慢。真没有GPU,建议把img size降到480,batch size降到8,先用小模型试跑通,再去云上租卡训练。

3.2 数据配置文件与yaml写法

YOLOv5训练前要准备两个yaml文件:一个是数据集配置文件,描述图片路径和类别;一个是模型配置文件,描述网络结构。对新手来说,模型配置直接用默认的yolov5s.yaml或yolov5m.yaml,不用改。数据集配置必须自己写,它长这样:

# bridge.yaml train: dataset/images/train val: dataset/images/val nc: 2 names: ['crack', 'seam']

这段配置最简单。train和val指向图片目录,YOLOv5会自动识别同级的labels目录。nc是类别数,这里假设两种:裂缝和施工缝。names是类别名字列表,顺序必须和标注txt里的类别ID一致。不要小看这个顺序,LabelImg导出的txt第一列是0还是1,取决于你给标签分配ID的顺序。如果names写反了,训练出的模型不会报错,但推理结果会张冠李戴。

3.3 训练命令与关键超参数

数据准备好了,环境也通了,接下来就是训练。我的标准命令:

python train.py \ --data bridge.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 100 \ --cache

各参数说明:--data指向刚才写好的bridge.yaml;--weights用yolov5s.pt做预训练权重,YOLOv5会从官方仓库下载,也可以第一次训练不指定,用COCO预训练会大大加快收敛,对裂缝这种目标也算迁移学习;--img设为640,这个值不能拍脑袋,需要去看数据集中裂缝的宽度。如果裂缝在切片后宽度只有几个像素,可以把img设到800或960,对小目标更友好,但显存占用会明显增加;--batch设为16,在24GB显存的显卡上,img=640时16是安全的。如果显存只有8GB,batch要降到4或2,否则会报CUDA out of memory;--epochs设为100,但配合早停,实际会在60轮左右停止。注意epochs不是越多越好,裂缝数据集普遍很小,训练太久会过拟合到背景纹理上。

还有一个隐藏参数是--hyp,用来指定超参数文件。默认的是data/hyps/hyp.scratch.yaml,里面包含学习率、权重衰减、增强系数等。我经常改的只有三个:

lr0: 0.01 weight_decay: 0.0005 mosaic: 1.0

lr0是初始学习率,数据量大可以保持0.01,但裂缝这种小数据集我降到0.005,防止前几轮loss发飘。weight_decay控制正则化,0.0005是默认值,如果验证集mAP已经在乱跳,可以试着调到0.001,压制过拟合。

3.4 怎么看训练曲线和早停

训练过程中会生成runs/train目录,里面有每轮的结果。主要看两个文件:results.png和weights/。results.png里有损失曲线和mAP曲线。我习惯重点看val/box_loss和val/cls_loss,如果两个loss在缓慢下降,说明训练正常;如果train_loss还在降,val_loss已经开始震荡上升,就是过拟合信号,需要立刻调早停参数。

早停参数是--patience。默认值是100,意味着100轮内没有提升就停止。对裂缝检测,我把它设成20,因为小数据集从第30轮开始mAP就趋于平稳,再等太长没有意义。看权重文件时,best.pt是验证集mAP最高的权重,last.pt是最后一轮的权重。如果训练中断,可以用--resume last.pt继续。这个“后悔药”对长训练特别有用,不要傻傻从头开始。另外,如果数据集中裂缝数量特别少,网络可能提示“WARNING: no labels found in train path”,这时候要回头检查labels目录下的txt内容是否为空,或者图片和标签的stem是否一致。

4. 避坑:裂缝检测训练与推理的5个高频翻车现场

4.1 边界框偏移:标注了整条裂缝,却只识别出一小段

现象:训练完成后,在测试图上推理,框只框住了裂缝的一小段,而不是完整裂缝。有时候一段裂缝被标出了四五个框,看起来像是把一条线切碎了。

原因:锚框匹配机制。YOLOv5使用固定锚框,默认锚框是为通用目标设计的,长宽比最大接近5比1。裂缝框长宽比经常超过10比1,锚框和真实框的IoU太低,网络只能勉强匹配到部分区域,导致输出框严重不完整。

解决:在训练时使用--noautoanchor参数的选项。YOLOv5默认会在开始训练前自动重新聚类锚框,但因为有预训练权重,它可能不会完全重算。我一般先关闭自动锚框,用--noautoanchor跑一个短迭代,输出新的锚框结果,然后拷贝到yolov5s.yaml里,再正式训练。这一步对裂缝检测几乎是必须的,能有效降低边界框偏移。另外,把标注时的框适当拉长,尽量包住裂缝两端,能改善长宽比问题。

4.2 把细小裂缝当背景:模型过拟合到纹理上

现象:训练集上识别很准,到了验证集,很多细小裂缝直接被漏掉,但一些明显的模板接缝却全被检测出来。看热力图,模型关注的是粗糙纹理区域,而不是裂缝的连续性。

原因:裂缝样本太少,背景纹理太强。混凝土表面的气孔、模板纹、砂眼都和裂缝在灰度上有相似性。网络在有限样本下选择了一条捷径,学习背景特征而不是裂缝的边缘断裂特征。

解决:增加负样本。从现场多拍一些完全没有裂缝的混凝土表面,加入训练集,标签为空文件,网络被迫学习“没有裂缝”的样本。另一个做法是数据增强里提高HSV的颜色扰动强度,让模型更依赖灰度梯度而不是绝对颜色。还有个小技巧是,在训练时把--cls类别损失权重提高一点,让模型在类别判断上更保守,宁可少检也不误检。注意,漏检和误检是一对矛盾,单靠参数调整已经到瓶颈了,最有效还是补充标注数据。

4.3 显存不足:batch size和img size怎么搭配

现象:训练开始几秒,直接报错“RuntimeError: CUDA out of memory”。有人立刻把batch size降到1,结果发现loss仍然波动很大,训练极不稳定。

原因:只调batch不调img size。YOLOv5的内存占用大约与图片面积成正比,另外多尺度训练会在每个epoch随机切换640、704等尺寸,显存峰值出现在最大尺寸时。如果batch size 4配合img 960,峰值占用可能是batch 16配合img 640的好几倍。

解决:先把img size降到640,然后用一个经验公式:显存总大小除以2,大约能跑的batch size。比如16GB显存,batch size可以设8;8GB显存,batch size设4。如果还想再小,就要用--cache参数把数据预加载到显存,但这一步会额外占用显存。另一个能救急的办法是开启梯度累积,也就是--batch 64--accumulate 4的写法,但YOLOv5没有直接的accumulate参数,需要手动改源码或使用nohup分割训练。这里提醒一句,真的不要长期用batch 1训练,BN统计量都算不准,模型会像喝醉了一样收敛不稳定。

4.4 预测结果错位:多类别标成单类别

现象:模型推理时,输出框位置很准,但框里的内容分类错。比如把结构性裂缝的框标成了“施工缝”,或者反过来。更隐蔽的是,同一张图,同一个位置,两次推理返回的类别不同。

原因:检查数据标注时发现,有人用LabelImg把class name设置成1和2,而不是0和1。YOLO格式要求类别ID从0开始。如果txt里写的是1和2,而配置文件的nc=2,网络会把1当正常类,2当越界类,训练时产生“IndexError”不报,但权重更新混乱。

解决:写个脚本批量修正所有txt,类别ID统一减1,并打印类别分布。另外,验证集里的类别分布如果很不均衡,比如裂缝有2000个框,施工缝只有20个框,网络会把施工缝当异常噪声,推理时就乱猜。这种情况要么多补施工缝的样本,要么用--weight参数给不同类别分配损失权重。

4.5 模型跑起来了但检测速度慢:瓶颈在预处理

现象:训练好的模型在检测图片时,单张耗时可能200毫秒,感觉不快。有人就认为是模型结构太复杂,马上换小模型或做剪枝。但后来用torch.profiler一查,发现80%的时间花在图像缩放和颜色转换上。

原因:YOLOv5的detect.py会内置letterbox,也就是等比例缩放填充。如果输入图片特别大,比如4000×3000,第一步缩放就要花费大量CPU时间,尤其你用CPU推理时特别明显。GPU推理中,瓶颈也会在数据预处理上。

解决:推理前在代码里手动把图片缩放到与训练时一致的尺寸,再做归一化,避免detect脚本里重复的resize。另一个方式是导出TensorRT或ONNX,让推理引擎接管预处理。实际项目中,一般会先用--source指定一个视频,看FPS,如果FPS低,先检查预处理,不要急着换模型。另外,--half参数可以启用FP16推理,显存减半,速度提升也很明显,前提是GPU支持FP16。

5. 部署与验证:把YOLOv5裂缝模型跑在图片和视频上

5.1 本地推理命令与置信度参数选择

训练完,best.pt就是最终的产物。推理命令很简单:

python detect.py --source test_images/ --weights runs/train/bridge/weights/best.pt --conf-thres 0.35 --img 640

这里--source可以是图片文件夹,也可以是一个视频文件。--conf-thres是置信度阈值,我习惯先设0.35,如果测试图上有明显漏检,再往下降;如果一堆误检框,就往上升。裂缝检测和行人检测不一样,漏检比误检更危险,因为现场的裂缝需要被记录和复核,宁可多框一些疑似区域。推理结束后,输出图片会保存到runs/detect目录,打开看一遍,不要只看数值,因为mAP指标无法反映一些细小的视觉误差。

5.2 导成ONNX模型做更轻量的部署

如果要在边缘设备或手机上部署,YOLOv5支持导出ONNX:

python export.py --weights runs/train/bridge/weights/best.pt --include onnx --opset 11

导出成功后会生成best.onnx。移植到其他推理框架时,记得把输出层的维度顺序理解清楚。YOLOv5的原始输出是三个尺度的feature map,每个尺度有num_anchors×(5+num_classes)个预测。用ONNX跑推理时,要把这三个输出拼起来再做NMS,不要直接拿输出当最终结果。很多踩坑贴都是卡在NMS这一步。

5.3 切片检测:用小图检测微小裂缝

最后分享一个我常用的验证技巧:对原图先做切片推理。把一张2000×2000的测试图切成9个800×800的块,重叠50像素,分别检测,再把结果映射回原图。这一步能验证模型的真实小目标能力,也适合在采集端直接用来处理大分辨率相机图。切片后的检测精度通常比直接缩放高不少,代价是推理次数增加。如果现场只关心局部细节,这个技巧非常实用。我在桥检车上就是这么干的,把相机连到电脑,边拍照边切片检测,当场标出可疑区域,效率比回去再处理高很多。

说回习惯,我现在每次训练完都会先跑一遍切片验证,再考虑是否调优。这比看训练曲线可靠得多。视觉模型往往在小数据集上表现很好,但放到不同光照和角度的现场就会露馅。希望你也能把这个步骤当成默认动作,希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询