☰
Python+Yolov5路面桥梁裂缝检测实战:原理、训练与避坑指南
2026/10/5 11:46:46 网站建设 项目流程

简介:面向计算机相关专业学生的Python+YoloV5路面桥梁裂缝检测识别项目,提供毕业设计级完整源代码、预训练模型及配套配置,适合正在完成课程设计、期末大作业,或希望积累目标检测实战经验的学习者。项目由导师指导完成,评审得分99分,代码结构完整、可直接运行,即使基础薄弱也能按照现有工程完成环境配置和推理调试。压缩包共85个文件,组成上以Python源码(.py)、YAML配置文件、模型下载/训练Shell脚本、测试图像以及Dockerfile为主,其中Python代码对应检测入口、模型定义与工具函数,YAML文件用于模型超参和数据集配置,Shell脚本可快速获取预训练权重,方便复现实验。整体体积仅1.6MB,模块按检测入口、模型定义、数据配置、工具函数等划分明确,结构紧凑、便于局部修改和二次开发。当前已有80人参与学习,从数据准备、模型推理到结果可视化均有覆盖,可作为毕业设计、课程设计或期末大作业的高分参考实现。

1. 为什么拿 Python+Yolov5 做路面桥梁裂缝检测:先把毕设的底牌看清

毕业设计如果锁定“Python+Yolov5 路面桥梁裂缝检测识别”这个组合,核心优势在于:Yolov5 有相当成熟的训练和推理闭环,源代码管理、模型导出都有现成路径,不存在“造轮子”导致的失控风险;而路面和桥梁裂缝这类目标,背景干扰少、纹理特征集中,是目标检测领域少有的容易出效果、展示性也强的场景。你把它做成一个能上传图片就输出裂缝框的小系统,评委一眼就能看懂你的工作内容和工程能力。整条链路按“原理→环境→数据→训练→踩坑→推理验证”的次序展开,下面一步步说清楚。

2. 裂缝检测不是图像分类:Yolov5 目标检测原理与选题理由

2.1 分类、分割与检测的边界:裂缝任务为什么选检测而不是分割

做裂缝识别,常被问“为什么不用语义分割”。这个问题背后其实是任务边界没分清:分割要逐像素给裂缝轮廓,理论上能给出更精细的裂缝形态,但代价也很直接——标注成本翻好几倍,得用多边形或多段线描线,训练难度也更高,稍微欠拟合就出现大面积漏检。而目标检测只需要一个矩形框把裂缝主体框住,标注工具用 LabelImg 就能完成,训练完输出的是“在哪里、什么类别、置信度多少”,对毕设答辩来说信息量完全足够。

任务类型输出形式标注成本毕设展示性
图像分类整图标签最低只能答“有无裂缝”
目标检测矩形框 + 坐标 + 置信度中直观且可量化
语义分割像素级 mask最高视觉精细但训练难

我一般会建议选目标检测的另一个理由是可解释性。分割结果是一张 mask 图,评委很难直观判断“为什么这里算裂缝”;检测框则自带坐标和置信度,能清楚展示模型关注的位置。桥梁裂缝通常是细长的线条,矩形框在视觉上会框住裂缝的延伸范围,也能方便地统计裂缝数量或密度分布。对毕设而言,检测方案在“效果可视化”与“工程量可控”之间是最均衡的。

还有一类方案是图像分类,但那只能回答“这图有没有裂缝”,无法回答“裂缝在哪”,在桥梁检测里基本不够用。所以最终选择 Yolov5 目标检测路线,不是因为分割做不到,而是因为检测在毕设的交付周期内能稳定出好结果。先想明白这一点,后面所有流程才有依据。

2.2 Yolov5 网络结构里的三个关键件:Backbone、Neck、Head

Yolov5 的网络结构图在网上很好找,但真正决定裂缝检测效果的只有三个部分。Backbone 用 CSPDarknet 做特征提取,连续卷积和下采样把输入图片逐渐抽象成高层语义特征;Neck 用 PANet 结构把高层语义和底层纹理做多尺度融合,让网络同时保留“裂缝是细线”的空间细节和“这是桥面还是路面”的上下文;Head 则是三个不同尺度的检测头,分别负责小目标、中目标和大目标的框回归与分类。

对路面桥梁裂缝来说,最麻烦的其实是细小裂缝。如果裂缝宽度只有几个像素,经过多次下采样后,在高层特征图上可能只剩一两个像素的响应。好在 Yolov5 的三个检测头里,大尺寸特征图那一路就是专门用来召回小目标的。这也是为什么训练时--img 640甚至--img 1280会比小分辨率好很多,分辨率上去了,细小裂缝占的像素比例才够高。

后处理也是 Yolov5 的一个重要环节。网络输出的是大量冗余候选框,需要经过置信度阈值过滤和 NMS(非极大值抑制)去重。我调试时会特别关注 NMS 的 IoU 阈值:默认 0.45,在裂缝密集区域容易出现两个框互相压制,这时候把阈值调到 0.3,或者遇到裂缝边缘不完整时调高到 0.5,要根据实际效果权衡。这个参数直接决定最终画出来的框是干净还是糊成一片。

2.3 数据标注格式:YOLO txt 格式与 COCO json 之间的坑

复现这个项目时遇到的第一道坎,往往是数据集格式不匹配。网上公开的路面裂缝数据集不少是 COCO 格式,标注存成 json;而 Yolov5 训练要的是每个图片对应一个同名 txt 文件,每行是class x_center y_center width height,全部归一化到 0~1。换算不复杂,但有几个细节容易翻车。

import os import json def coco_json_to_yolo_txt(json_path, img_dir, out_dir): with open(json_path, 'r', encoding='utf-8') as f: coco = json.load(f) # 建立 image_id 到图片信息的映射,避免重复读图 img_map = {img['id']: img for img in coco['images']} if not os.path.exists(out_dir): os.makedirs(out_dir) for ann in coco['annotations']: img = img_map[ann['image_id']] # 注意:宽高必须取 json 里记录的原始值,不要自己重新读图 w, h = img['width'], img['height'] # COCO 的 bbox 是 [x, y, width, height],左上角为原点 x, y, bw, bh = ann['bbox'] x_center = (x + bw / 2.0) / w y_center = (y + bh / 2.0) / h bw_norm = bw / w bh_norm = bh / h # 类别 id 从 0 开始;COCO 数据集如果从 1 开始,要减 1 cls_id = ann['category_id'] - 1 txt_path = os.path.join(out_dir, os.path.splitext(img['file_name'])[0] + '.txt') with open(txt_path, 'a', encoding='utf-8') as f: f.write(f"{cls_id} {x_center:.6f} {y_center:.6f} {bw_norm:.6f} {bh_norm:.6f}\n") # 用法示例:把 COCO 标注转成 YOLO 格式 coco_json_to_yolo_txt('annotations.json', 'images', 'labels')

这段代码的关键,第一是拿到图片的宽高后直接使用 json 里记录的原始值,而不是自己重新读图;第二是保存归一化坐标时保留 6 位小数,如果只保留到 2 位,小裂缝的框会抖动得很明显;第三是一个图片有多条标注时用追加模式写文件,不要覆盖前一条。如果你重复运行这段脚本,记得先清空 out_dir,否则同一个文件的标注会重复追加。

如果你用的是 LabelImg,它默认生成的也是 YOLO txt 格式,但类别名称需要预先在classes.txt里定义好。这里还有个小坑:LabelImg 的 class id 从 0 开始,和 Yolov5 一致,但有些标注工具后台从 1 开始,转的时候一定要检查一遍,否则类别错位会让训练完全跑偏。

3. 从零跑通环境:Python 安装、Yolov5 克隆与数据集目录组织

3.1 Python 与 PyTorch 安装:先定 CUDA 再装框架

不少人在 Python 安装教程这一步就卡住了。我的建议是:先把 CUDA 定下来,再装 PyTorch,最后装 Yolov5 的依赖。Python 版本建议选 3.8~3.11,不要一上来就用 3.12,太新的版本会让部分依赖在编译环节出问题。装好 Python 后建一个虚拟环境,把 Yolov5 的依赖隔离进去,后面换项目不至于互相污染。

确定 GPU 环境时,在命令行执行nvidia-smi能看到驱动支持的 CUDA 版本,然后按这个版本来装 PyTorch。比如驱动支持 CUDA 11.8,就用:

pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118

如果只是 CPU 机器,直接pip install torch torchvision也能跑,但训练速度会让人失去耐心,我建议至少有一块入门级 GPU,哪怕 4G 显存也比 CPU 强。装完以后用一条命令验证环境:

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

看到True说明 CUDA 可用,后面训练才会走 GPU。这里还有一个容易忽略的点:如果torch.cuda.is_available()返回 False,大概率是 PyTorch 版本和驱动不匹配,或者装成了 CPU 版,重新按 CUDA 版本装一遍即可。

3.2 获取 Yolov5 代码库与预训练模型

Yolov5 的代码结构相当简练,train.py、detect.py、models、data 一目了然。我不建议自己重新实现一个检测框架,直接 clone 官方仓库是最稳妥的,源代码管理上也最透明,出了问题一搜就是成堆的解答。常见做法是:

git clone https://github.com/ultralytics/yolov5 cd yolov5 pip install -r requirements.txt

requirements.txt 里的依赖以 numpy、opencv-python、matplotlib、pyyaml、tqdm 为主。装的时候如果遇到 opencv 版本冲突,把 requirements.txt 里的 opencv-python 改成opencv-python-headless再装,可以省掉一堆 GUI 库的麻烦。预训练权重不需要手动满网找,第一次运行 train.py 或 detect.py 时,脚本会根据--weights指定的名字自动下载,比如 yolov5s.pt。下载慢就自己想办法放到位,放到当前目录即可,脚本会在当前目录查找。

3.3 组织数据集目录:images 与 labels 的一一对应关系

数据目录组织是老生常谈,但翻车率最高。Yolov5 训练时读的是--data指定的 yaml 文件里的train和val路径,它会在这些路径下找同名图片和同名 txt。我习惯把目录建成这样:

datasets/crack/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── crack.yaml

这里最容易踩的坑:图片是 001.jpg 时,标注必须是 001.txt,前缀完全一致,扩展名无所谓。还有,train 和 val 两个目录里千万不要有同名的图片,否则验证时会串数据,mAP 虚高。另外,如果某个 txt 文件是空的,训练时会把这张图当成背景,如果你的裂缝图没有标注,模型就学不到这条裂缝,还会给背景类制造噪声。

我还会在训练前写个小脚本检查标注和图片是否一一对应。把下面的代码存成 check_data.py 跑一遍,能快速定位问题文件:

import os for split in ['train', 'val']: img_dir = f'images/{split}' lab_dir = f'labels/{split}' imgs = {os.path.splitext(f)[0] for f in os.listdir(img_dir)} labs = {os.path.splitext(f)[0] for f in os.listdir(lab_dir)} print(split, '缺少标注的图片:', sorted(imgs - labs)[:5]) print(split, '空标注文件数:', sum(1 for f in os.listdir(lab_dir) if os.path.getsize(os.path.join(lab_dir, f)) == 0))

这段脚本的输出里,如果“缺少标注的图片”不为空,说明有图片没有对应的 txt;如果“空标注文件数”不为零,说明存在背景图或漏标。这两种情况都要先处理再训练,否则后面调参没有任何意义。数据切分我一般按 7:2:1 随机切,但切的时候避免同一座桥或同一条路的连续帧同时落到训练集和验证集,否则验证分数虚高。

4. 训练自己的裂缝模型:yolov5 超参数、训练命令与结果评估

4.1 数据集配置文件:先写 crack.yaml

训练入口是 train.py,但它不直接认文件夹,而是要一个 yaml 配置文件。这个文件告诉 Yolov5 数据集在哪、有几类、类名是什么。裂缝检测一般只有一个类别crack,配置文件内容如下:

# datasets/crack/crack.yaml path: datasets/crack # 数据集根目录,相对于 yolov5 根目录 train: images/train val: images/val nc: 1 names: ['crack']

这里有几个细节。path字段在 Yolov5 7.0 以后是必需的,它相当于一个公共前缀,下面的train和val都相对于它来写。nc和names必须一致,类别顺序决定了训练时 class id 的映射,不能随意改动。如果你想把“无裂缝”也作为一类,那nc设为 2,names写成['background', 'crack'],但通常不需要显式建模背景,Yolov5 会把没有目标的图片当作负样本处理。

4.2 train.py 关键参数:epoch、batch-size、img-size、weights

最基本的训练命令只需要一行:

python train.py \ --data datasets/crack/crack.yaml \ --weights yolov5s.pt \ --epochs 100 \ --batch-size 16 \ --img 640 \ --device 0

参数说明:

  • --weights加载预训练权重。yolov5s.pt 是在 COCO 上预训练的小模型,导入后可以把 Backbone 里学到的通用特征迁移到裂缝检测上,比从零训练收敛快很多。这里也回答了“为什么要用源代码+模型”这个问题:模型不只是最终交付物,更是训练的起点。
  • --epochs单类目标 100 个 epoch 基本够用;如果数据量不足 500 张,50 个 epoch 也可能收敛。关键看验证集 mAP 是否还在上升。
  • --batch-size受显存限制。16 在 8G 显存下跑 640 分辨率比较稳,显存不够就降到 8 或 4。
  • --img训练尺度。裂缝是小目标,起步建议 640,显卡扛得住就上 1280,细小裂缝的召回会有肉眼可见的改善。

训练过程中会自动生成runs/train/exp目录,每个 epoch 的权重都存下来,最终模型在exp/weights/best.pt。训练完我一般会紧接着跑一条验证命令:

python val.py --data datasets/crack/crack.yaml --weights runs/train/exp/weights/best.pt --img 640

val.py 会输出每个类别的 precision、recall、mAP50、mAP50-95。对裂缝这类目标,mAP50 比 mAP50-95 更容易好看,答辩时可以两个都提,但心里要清楚 mAP50-95 对框定位精度的要求更高。

4.3 超参数调节:yolov5 超参数文件里真正值得动的三个

yolov5 超参数一般写在data/hyps/hyp.scratch.yaml里,新手建议从默认值开始,不要一上来就大改。真正值得调的,我一般看三个:lr0初始学习率、mosaic数据增强、fl_gamma焦点损失 gamma。学习率默认 0.01 在迁移训练时往往偏大,裂缝数据集如果只有几百张,建议调到 0.005 甚至 0.001,否则 loss 在最开始几个 epoch 会震荡得很厉害。mosaic把四张图拼成一张,能让小目标更鲁棒,但如果你的裂缝样本本身密集,mosaic 会让框与框之间互相重叠、标注混乱,这种情况关掉反而更好。

还有一个容易被忽略的参数是--patience,它配合早停机制使用,验证集 mAP 连续多轮不提升,训练就提前终止。我一般设为 30,不至于在无效训练上浪费电费。遇到 loss 曲线很漂亮但验证 mAP 上不去的“玄学”情况,第一步不是加数据,而是检查验证集里有没有和训练集重叠的图片,这个问题比超参数更能解释奇怪的结果。

4.4 训练过程监控:从 loss 曲线到混淆矩阵

训练跑起来以后,不要只看终端打印。Yolov5 会在 runs/train/exp 下生成results.png,里面有 Box_Loss、Cls_Loss、mAP 曲线,这是判断训练状态的第一手资料。如果 loss 在降,但验证集 recall 一直很低,说明很多裂缝没有被检出来。这时翻出confusion_matrix.png,看背景类是不是被大量预测为裂缝,如果是,说明假阳性太多,需要提高置信度阈值或增加负样本。

我也会在训练结束后打开val_batch0_pred.jpg这种可视化文件,Yolov5 会把预测框画在原图上。真正用来答辩的展示图往往是从这一批可视化里挑出来的。但要注意一个“自我欺骗”:模型经过阈值过滤后,可视化结果通常看着都不错,真正反映水平的是整个验证集的 mAP,所以答辩 PPT 上贴成功图的同时,最好也拿出一张漏检图分析一下原因,反而显得工作更扎实。

5. 裂缝检测避坑指南:从数据标注到训练推理的五个常见问题

这里把跑这个项目最常见的翻车现场列出来,每一条都是我实际调试时遇到过的。

5.1 现象:训练 loss 一直不掉,在初始值附近震荡

很多人会在第一个 epoch 结束时发现 Box_Loss 高得离谱。原因往往是标注格式错了,比如把中心点坐标写成了左上角坐标,或者归一化时用了像素值而不是比例。Yolov5 对非法坐标通常不会直接报错,而是把 loss 计算成极大值,表现出来就是 loss 曲线完全不动。解决的办法是先随机抽查 3~5 个标注 txt,把坐标反算回像素坐标画框,对照原图看一眼框是不是贴住了裂缝。另外,预训练权重没加载成功时日志里会有警告,这等于从零开始训练,loss 会明显偏高,也要先排除。

5.2 现象:细小裂缝完全检不到,recall 接近 0

路面桥梁裂缝的宽度往往只有几个像素,训练时统一缩放到 640,很多裂缝在输入图上已经不足 4 像素宽。原因不是模型能力不够,而是目标太小,特征在下采样过程中丢掉了。解决思路有几个:一是把--img提到 1280,让目标在图上变大;二是在数据增强时关闭mosaic和mixup,因为它们会让小目标更模糊;三是选模型时用 yolov5s 而不是 yolov5n,nano 版参数量小,对极端小目标的拟合能力偏弱。我实际跑下来,img 从 640 升到 1280,细小裂缝的 recall 能提升 10 个点以上,代价是训练时间大约翻倍。

5.3 现象:GPU 显存溢出,训练中断

显存溢出时最先要看的是--batch-size,但很多人会忽略--img的影响。分辨率从 640 升到 1280,显存占用是平方级增长的,batch size 减一半都不一定补得回来。还要注意不要习惯性开启--cache,这个参数会把图片预加载进内存或显存,对小显存环境更不友好。显存实在不够时,把 batch size 降到 4,同时加--accumulate 4让梯度累积 4 步再更新,等效 batch size 还是 16,但显存压力小得多。另外,--workers也不要设太大,数据加载线程过多会把 CPU 内存打满,很多人把“内存不足”误判成显存问题。

5.4 现象:训练集 mAP 很高,验证集 mAP 低很多

这是过拟合的典型特征。裂缝数据集如果只有两三百张图,模型很容易把训练集里的背景细节背下来。解决思路按性价比排序:先检查 train 和 val 是否有图片重叠;然后增加数据增强,比如随机旋转和 HSV 扰动;再考虑从 yolov5s 换到 yolov5n 减小模型容量;最后才是加数据。我还见过一种情况:验证集里某些图是从训练视频里连续抽帧出来的,相邻帧几乎一样,这也会让人误以为模型很好;反过来,验证集和训练集场景差异太大,分数又会虚低。所以数据切分时最好按“桥面/路面”这种场景来划分,而不是完全随机。

5.5 现象:推理时框偏移明显,置信度极低

训练没问题,但用 detect.py 推理时发现框的位置不对。最常见的原因是推理时的--img分辨率和训练时不一致。训练用了 1280,推理用 640,模型看到的特征尺度完全不同,输出自然不可靠。解决办法是让推理分辨率和训练分辨率接近,最好是同一个值,且保持 32 的倍数。另一个原因是图片里裂缝太多,NMS 后互相压制,可以打开--agnostic-nms,对单类模型来说这个参数会减少误删。还有一个容易忽略的点:手机拍摄的图片 Exif 里带旋转信息,OpenCV 读图不会自动纠正方向,框就会整体偏 90 度或 180 度,需要在读图后根据 Exif 先旋转再做推理。

6. 让模型真正用起来:detect.py 推理、性能指标与量化部署方向

训练完拿到 best.pt 只是第一步,毕设要演示,还得把它接到新的输入上做推理。Yolov5 的 detect.py 是最直接的入口:

python detect.py \ --weights runs/train/exp/weights/best.pt \ --source data/images --img 640 \ --conf 0.25 --iou 0.45 \ --save-txt --save-conf

这条命令会遍历data/images下的图片,在runs/detect/exp下生成带框图片、txt 结果文件和置信度。--conf是置信度阈值,裂缝检测一般设 0.25 比较合适,低于这个值会漏掉细小裂缝,高于这个值则容易漏检;--save-conf会在 txt 里输出每个框的置信度,恰好可以拿来统计裂缝数量或计算框的面积占比,作为裂缝严重程度的量化指标去展示。

再往后走一步就是模型导出。官方代码库里的 export.py 可以导出 ONNX 或 TensorRT 引擎。如果毕设里想强调工程能力,我建议至少导出 ONNX,然后用 ONNX Runtime 写一个几十行的推理脚本,彻底脱离 Yolov5 的 Python 依赖,做成一个 web 接口。我平时还会用 export.py 导出成 TensorRT 的 .engine 文件,在 NVIDIA 显卡上把推理延迟压到几十毫秒。像 RK3568 这类 ARM 板卡上跑 Yolov5,一般也是先导出 ONNX,再转成 rknn 模型,量化到 INT8 后可以实时处理视频流,这一步在边缘计算相关的毕设方向里是很加分的。

最后说一个我自己的教训:最早做裂缝检测时,总以为“模型精度高等于答辩稳”,于是把所有精力都花在刷 mAP 上,忽略了推理链路。后来发现评委真正感兴趣的,是你能不能输入一张图,立刻输出一个带框带坐标带置信度的结果,甚至是一个实时视频的检测窗口。所以后来的习惯是,训练大约花 80% 时间,剩下 20% 一定留给导出和推理链路的打通。模型精度决定你分数的下限,而“能不能用起来、能不能演示”往往决定分数的上限。

把数据切分做对、标注格式查一遍、训练参数选稳,再配上完整的推理可视化链路,这个 Python+Yolov5 的路面桥梁裂缝检测毕设,拿高分是水到渠成的事。希望这些实战细节能帮到你。

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

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

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

立即咨询