简介:这份资源面向计算机视觉方向的学生与开发者,提供一套基于Python与Yolov5实现路面、桥梁裂缝检测识别的完整项目源码及配套模型权重,可用于毕业设计、期末大作业、课程设计等场景,难度适中,适合具备一定深度学习基础、希望快速跑通目标检测全流程的读者。压缩包共85个文件,约1.58MB,以py脚本、yaml配置、pyc缓存为主,辅以sh运行脚本、Dockerfile部署文件及少量jpg、png、jpeg示例图片,涵盖模型定义、数据配置、训练与推理等模块,目录结构清晰,便于按功能定位代码。资源已经本地编译验证可运行,评审分达98分,内容经助教老师审定,读者可据此完成数据准备、模型训练、图片与摄像头检测推理,并参考权重下载脚本与配置参数快速复现结果,同时理解Yolov5在裂缝识别任务中的工程组织方式与调参思路。目前已有179人学习关注,可作为同类检测项目的实践参考。
1. 路面桥梁裂缝检测为什么值得用 Python+Yolov5 重做一遍
桥梁和公路养护单位每年花在人工巡检上的钱不少,但真正落到"发现裂缝并记录"这一步的效率却很低。一个三人小组带着望远镜和裂缝观测仪上桥,一天能覆盖的桥面长度有限,遇到高墩、箱梁内部、桥塔这些位置,人根本靠不近。更麻烦的是,人工判读带主观性,同一条裂缝两个人量出来的宽度可能差 0.2 毫米,而规范里 0.2 毫米恰好是区分"需要观测"和"需要处理"的分界线。这就是为什么"基于 Python+Yolov5 路面桥梁裂缝检测识别"这个方向一直有人在做——它要解决的不是"能不能识别裂缝",而是"能不能用一套可复现的代码,把巡检从人眼判断变成模型输出"。
Yolov5 在这个场景里被反复选中,原因很实际:它是单阶段检测器,推理速度快,一张 640×640 的桥面图在普通显卡上能跑到几十毫秒;它的工程化程度高,数据标注、训练、导出 ONNX、部署到边缘设备这条链路都有现成脚本;它的社区资料足够多,遇到问题能搜到答案。对于做课程设计、毕业设计或者单位内部小工具的人来说,拿到一份能跑通的源码和预训练模型,比从零复现一篇论文要现实得多。这篇文章面向的就是这类读者:手里有裂缝图片或者能拍到裂缝,想用 Python 和 Yolov5 把检测跑起来,但不确定数据怎么标、参数怎么调、模型怎么落到实际巡检流程里。下面按"先立住原理和选型,再动手复现,最后讲坑和进阶"的顺序展开。
2. 裂缝检测任务拆解与 Yolov5 选型:从标注格式到模型结构
2.1 裂缝检测到底在检测什么:分类、检测、分割的边界
很多人一上来就说"我要做裂缝检测",但没想清楚输出是什么。裂缝任务通常分三个层次:图像分类是判断这张图有没有裂缝;目标检测是给出裂缝的边界框和类别;语义分割是逐像素标出裂缝区域。Yolov5 属于第二类,输出的是矩形框。这对裂缝来说有个天然矛盾——裂缝是细长的,边界框会包含大量背景。如果裂缝横跨整张图,一个框几乎等于整张图,框的定位精度就没意义了。
那为什么还用 Yolov5?因为实际巡检里,操作人员需要的往往不是精确的像素级宽度,而是"这个区域有裂缝,位置在这里,置信度多少"。把桥面切成若干子区域,每个区域里检测裂缝片段,再按位置拼接,就能得到裂缝的走向和大致范围。如果确实需要宽度测量,常见做法是在检测框内再做一次分割或者用传统图像处理提取骨架,这是后话。选 Yolov5 的理由是它在"有裂缝/无裂缝"和"裂缝位置"这两件事上足够快、足够稳,而且训练成本低。
另一个要提前想清楚的是类别设计。有的项目只分一类"crack",有的会分"横向裂缝、纵向裂缝、网状裂缝、坑槽"多类。类别越多,标注一致性越难保证,模型也越容易混淆。我一般建议第一版只做单类,把"有没有裂缝"跑通,再考虑细分。单类还有个好处:公开数据集和自建数据可以混着用,标注标准统一。
2.2 为什么是 Yolov5 而不是 Yolov8 或 Faster R-CNN
Yolov8 确实更新,精度和易用性都有提升,但 Yolov5 在裂缝这个场景里仍有它的位置。第一,Yolov5 的 6.0/7.0 版本代码结构清晰,models、utils、data 三个目录分工明确,改网络结构、换数据增强、调损失函数都能找到对应文件,适合需要"看懂再改"的课程设计场景。第二,Yolov5 的预训练权重和导出工具链成熟,转 ONNX、TensorRT、OpenVINO 的脚本都在 export.py 里,部署到 Jetson 或者工控机时省事。第三,网上针对 Yolov5 的裂缝数据集和训练配置最多,遇到问题搜索命中率高。
Faster R-CNN 这类两阶段检测器精度可能略高,但推理速度慢一个量级,不适合现场巡检的实时或准实时需求。Yolov8 的 API 更封装,对想深入理解检测头、锚框机制的人来说反而隔了一层。所以选型结论是:如果目标是快速跑通并部署,Yolov5 够用;如果目标是发论文刷精度,可以考虑 Yolov8 或 RT-DETR,但那是另一条路。
2.3 数据标注格式:VOC、COCO 和 YOLO 格式的转换关系
Yolov5 训练用的是 YOLO 格式的标签:每张图对应一个 txt 文件,每行是class_id x_center y_center width height,坐标都归一化到 0 到 1 之间。但很多人手里拿到的标注是 VOC 的 XML 或者 COCO 的 JSON,需要转换。VOC 的 XML 里是绝对坐标的 xmin、ymin、xmax、ymax,转换时要先算宽高再除以图像尺寸。COCO 的 JSON 里是[x, y, width, height]绝对坐标,同样要归一化。
转换脚本不难写,但有几个边界坑:图像尺寸要从 XML 的 size 字段读,不能假设都是 640×640;有些标注框会超出图像边界,归一化后可能小于 0 或大于 1,需要裁剪;类别名到 id 的映射要固定,不能每次按出现顺序编号,否则训练和推理的类别对不上。下面这段代码是 VOC 转 YOLO 的常见写法,我加了边界裁剪和类别映射。
import xml.etree.ElementTree as ET import os from PIL import Image # 固定类别映射,避免顺序变化导致 id 漂移 CLASS_MAP = {"crack": 0, "spalling": 1, "rebar": 2} def voc_to_yolo(xml_path, img_dir, out_dir): tree = ET.parse(xml_path) root = tree.getroot() # 从 XML 读图像尺寸,不假设固定值 size = root.find("size") w = int(size.find("width").text) h = int(size.find("height").text) lines = [] for obj in root.iter("object"): name = obj.find("name").text if name not in CLASS_MAP: continue cls_id = CLASS_MAP[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) # 裁剪到图像范围内,防止归一化越界 xmin = max(0, min(xmin, w)) xmax = max(0, min(xmax, w)) ymin = max(0, min(ymin, h)) ymax = max(0, min(ymax, h)) if xmax <= xmin or ymax <= ymin: continue x_center = (xmin + xmax) / 2.0 / w y_center = (ymin + ymax) / 2.0 / h bw = (xmax - xmin) / w bh = (ymax - ymin) / h lines.append(f"{cls_id} {x_center:.6f} {y_center:.6f} {bw:.6f} {bh:.6f}") base = os.path.splitext(os.path.basename(xml_path))[0] with open(os.path.join(out_dir, base + ".txt"), "w") as f: f.write("\n".join(lines)) # 批量处理 for xml_file in os.listdir("annotations"): if xml_file.endswith(".xml"): voc_to_yolo(os.path.join("annotations", xml_file), "images", "labels")这段代码的关键点有三个:CLASS_MAP 必须固定,不能动态生成;坐标裁剪要在归一化之前做;空标签文件也要生成,Yolov5 训练时会把没有目标的图当作负样本,如果直接跳过会导致负样本丢失。参数上,x_center和y_center保留六位小数足够,Yolov5 默认读取时不会再做精度截断。
2.4 数据集划分与 Yolov5 目录结构
Yolov5 要求的数据集结构是images/train、images/val、labels/train、labels/val四个目录,外加一个 data.yaml 描述路径和类别。划分比例常见 8:1:1 或 7:2:1,裂缝数据我一般用 8:1:1,因为验证集和测试集各留 10% 就够看趋势。划分时要注意同一座桥、同一段路的图片不能同时出现在训练和验证里,否则模型会"记住"背景而不是裂缝,指标虚高。
data.yaml 的写法:
path: /home/user/crack_dataset train: images/train val: images/val nc: 1 names: ['crack']nc是类别数,names要和 CLASS_MAP 的顺序一致。如果后面要加类别,改这里和标注文件里的 class_id 即可,但已经训练好的权重不能直接复用,需要重新训练最后一层。
3. 用 Yolov5 训练裂缝模型:环境、命令与参数调优
3.1 环境配置:conda 建环境与依赖安装
Yolov5 对环境不算挑剔,但版本对不上会出各种玄学问题。我一般用 conda 建一个 Python 3.8 或 3.9 的环境,PyTorch 选 1.12 到 2.0 之间的版本,CUDA 版本和显卡驱动匹配。下面这套命令在 Ubuntu 和 Windows 的 conda 里都能跑,Windows 下把source activate换成conda activate。
conda create -n crack python=3.9 -y conda activate crack # 安装 PyTorch,根据 CUDA 版本选,这里以 CUDA 11.3 为例 pip install torch==1.12.1+cu113 torchvision==0.13.1+cu113 -f https://download.pytorch.org/whl/torch_stable.html # 克隆 Yolov5 并安装依赖 git clone https://github.com/ultralytics/yolov5 cd yolov5 pip install -r requirements.txt装完之后用python -c "import torch; print(torch.cuda.is_available())"验证 GPU 是否可用。如果返回 False,先检查驱动和 CUDA 版本,不要急着改代码。requirements.txt 里有时会锁死某些包的版本,如果和已有环境冲突,可以手动装核心包:pip install opencv-python matplotlib pyyaml tqdm seaborn pandas。
3.2 训练命令与关键参数含义
Yolov5 的训练入口是 train.py,最简命令是:
python train.py --data data/crack.yaml --weights yolov5s.pt --img 640 --batch 16 --epochs 100 --name crack_s这条命令里每个参数都影响结果。--weights指定预训练权重,yolov5s 最小最快,yolov5m/l 精度更高但显存吃得多。--img是输入尺寸,裂缝细长,640 是默认值,如果裂缝在图中占比很小,可以提到 1024,但显存和速度会变差。--batch根据显存调,8G 显存跑 yolov5s 加 640 可以到 16,跑 1024 可能只能到 4。--epochs一般 100 到 300,裂缝数据集小的话 100 够看趋势,但要注意早停。
Yolov5 的超参数在 data/hyp.scratch.yaml 里,常用的几个:lr0初始学习率默认 0.01,小数据集可以降到 0.001;lrf最终学习率比例默认 0.01;momentum0.937;weight_decay0.0005;warmup_epochs3。数据增强方面,mosaic默认 1.0,把四张图拼成一张,对小数据集很有效;flipud和fliplr是上下左右翻转,裂缝方向有意义的场景要慎用上下翻转;hsv_h、hsv_s、hsv_v控制颜色抖动,桥面光照变化大时可以适当调高。
如果训练时 loss 不降,先看学习率是不是太大,再看标注有没有问题。一个实用技巧是先用 10 张图跑 10 个 epoch,看模型能不能过拟合到这 10 张,如果能,说明代码和数据格式没问题,再上全量数据。
3.3 训练过程监控与指标解读
训练启动后,Yolov5 会在 runs/train/ 下生成结果目录,里面有 loss 曲线、mAP 曲线、混淆矩阵和验证集预测图。重点看三个指标:box_loss 是边界框回归损失,cls_loss 是分类损失,obj_loss 是目标置信度损失。三个 loss 都应该下降并趋于平稳。如果 box_loss 震荡大,可能是学习率偏高或 batch 太小。
mAP@0.5 是 IoU 阈值 0.5 时的平均精度,裂缝检测里这个值到 0.7 以上算可用,0.85 以上算不错。mAP@0.5:0.95 更严格,一般会比 mAP@0.5 低 0.2 左右。精确率和召回率要一起看:精确率高召回率低,说明模型保守,漏检多;召回率高精确率低,说明误检多。巡检场景里漏检的代价通常比误检大,所以可以适当调低置信度阈值来提高召回。
验证集预测图在 runs/train/crack_s/val_batch0_pred.jpg 这类文件里,直接打开看模型框得准不准。如果发现大量框在背景上,可能是负样本不够或者标注里有漏标。如果框的位置偏移,检查标注坐标有没有算错。
4. 推理、部署与裂缝检测的工程化落地
4.1 用 detect.py 跑单张图和批量推理
训练完的权重在 runs/train/crack_s/weights/best.pt,推理命令:
python detect.py --weights runs/train/crack_s/weights/best.pt --source test_images --img 640 --conf 0.25 --iou 0.45 --save-txt--source可以是单张图、文件夹、视频或者摄像头编号。--conf是置信度阈值,默认 0.25,裂缝检测可以降到 0.15 提高召回。--iou是 NMS 的 IoU 阈值,默认 0.45,裂缝重叠多的时候可以调到 0.5 到 0.6。--save-txt会把检测框的坐标和置信度存成 txt,方便后续做宽度估算或者和巡检记录关联。
批量推理时,如果图片多,可以加--nosave只输出 txt 不存图,节省磁盘。视频推理加--source video.mp4,Yolov5 会逐帧检测并输出带框的视频。摄像头实时检测用--source 0,但要注意帧率和延迟,工控机上跑 yolov5s 加 640 一般能到 20 到 30 FPS。
4.2 导出 ONNX 和 TensorRT 做边缘部署
现场巡检设备往往是 Jetson 或者低功耗工控机,直接跑 PyTorch 效率不高,需要导出成 ONNX 或 TensorRT。Yolov5 的 export.py 支持多种格式:
python export.py --weights runs/train/crack_s/weights/best.pt --include onnx --img 640 --batch 1 python export.py --weights runs/train/crack_s/weights/best.pt --include engine --img 640 --batch 1 --device 0ONNX 导出后可以用 onnxruntime 推理,跨平台性好。TensorRT 导出需要在有 TensorRT 环境的机器上做,导出后的 engine 文件在 Jetson 上加载快、延迟低。注意导出时的--img要和训练时一致,否则精度会掉。--batch设为 1 适合实时推理,设为更大值适合批量处理。
导出后建议用一张已知结果的图对比 PyTorch 和 ONNX 的输出,确认框的位置和置信度差异在可接受范围内。如果差异大,检查导出时的 opset 版本和动态轴设置。
4.3 把检测结果接到巡检记录:坐标还原与裂缝编号
检测输出的 txt 里是归一化坐标,要还原成原图上的像素坐标才能和巡检记录对应。还原公式是x_pixel = x_center * img_width,y_pixel = y_center * img_height,宽高同理。如果图片在推理前被 resize 过,还要按比例还原回原图尺寸。
裂缝编号可以按检测框的中心点排序,从左到右、从上到下依次编号,这样同一段桥面的多次巡检结果可以按编号对比。如果要做裂缝变化追踪,可以把每次检测的框坐标和置信度存进数据库,按位置聚类,观察同一位置裂缝的宽度和长度变化。这部分用 Python 的 sqlite3 或者 pandas 就能做,不需要额外框架。
5. 裂缝检测项目避坑:标注、训练和部署里的常见翻车点
5.1 标注框太松导致模型学不到裂缝特征
现象:训练 loss 正常下降,但验证集预测框很大,几乎覆盖整张图,mAP 上不去。原因:标注时为了省事,框画得比裂缝大很多,模型学到的是"大框里有裂缝",而不是裂缝本身的特征。解决:重新标注,框尽量贴紧裂缝边缘,细长裂缝可以分段标注,每段一个框。如果裂缝太细,框的宽度至少覆盖裂缝两侧各几个像素,但不要扩大到周围背景。
5.2 训练集和验证集背景重复导致指标虚高
现象:验证集 mAP 到 0.95,但拿新拍的图测试效果很差。原因:划分数据时按图片随机分,同一座桥的相似背景同时进了训练和验证,模型记住了背景而不是裂缝。解决:按桥、按路段、按拍摄时间划分,确保验证集的背景和训练集不重叠。如果数据量少,可以用按拍摄日期划分,早期数据训练,后期数据验证。
5.3 置信度阈值设太高导致漏检
现象:推理时很多明显裂缝没框出来,调低--conf后才出现。原因:默认 0.25 对裂缝这种小目标偏高,模型对细长裂缝的置信度天然偏低。解决:把--conf降到 0.1 到 0.15,同时看精确率是否可接受。如果误检太多,再微调。另一个办法是训练时增加小目标检测层,或者提高输入分辨率。
5.4 导出 ONNX 后精度下降
现象:PyTorch 推理正常,ONNX 推理框偏移或置信度变化大。原因:导出时的输入尺寸、归一化方式或 opset 版本和推理时不一致。解决:导出和推理用同一套预处理,--img保持一致,opset 用 11 或 12。如果还是不对,用 onnxruntime 的调试工具逐层对比输出。
5.5 显存不足导致训练中断
现象:训练到一半报 CUDA out of memory。原因:batch 太大、输入尺寸太大或者模型太大。解决:先降 batch,再降--img,最后换更小的模型。Yolov5 支持梯度累积,可以用--accumulate模拟大 batch,但速度会慢。另外,训练时关掉其他占显存的程序,用nvidia-smi看占用。
6. 让裂缝模型真正可用的两个进阶技巧
第一个技巧是用滑动窗口处理高分辨率桥面图。桥面照片往往几千像素宽,直接缩到 640 会丢失裂缝细节,直接跑大图又爆显存。常见做法是把原图切成有重叠的小块,每块 640×640,重叠 128 像素,逐块推理后再把框映射回原图坐标,用 NMS 去重。这样既能保留细节,又能控制显存。切块时要注意边缘的裂缝可能被切断,重叠就是为了缓解这个问题。映射回原图时,框的坐标要加上切块的偏移量,再除以原图尺寸做归一化。
第二个技巧是用训练好的模型做半自动标注。先用手工标一小批数据训练一个初版模型,然后用它推理未标注的图片,把置信度高的检测框导出成 YOLO 格式的标签,人工只做修正。这样标注效率能提高几倍。修正时重点看漏检和误检,漏检的补框,误检的删框。迭代两三轮,数据集规模就能上来。这个流程在裂缝这种标注成本高的场景里特别实用。
我自己的习惯是每训练一版模型,都留一份权重和对应的 data.yaml,命名里带日期和关键参数,比如crack_s_20240601_img640_batch16.pt。这样后面对比效果或者回滚时有后悔药。裂缝检测这个方向,模型结构不是门槛,数据和部署才是。把标注做扎实,把推理链路跑通,比追新模型更有用。希望帮到你。
本文还有配套的精品资源,点击获取