简介:这是一份面向计算机相关专业学生、教师及企业开发者的深度学习毕设项目,基于Python与Yolov5实现路面及桥梁裂缝的自动检测与识别。资源从数据准备、模型训练到结果可视化均有覆盖,适用于毕业设计、课程设计、项目初期演示或作为目标检测方向的进阶学习素材。压缩包共85个文件,大小仅1.6MB,核心内容包括23个python源码用于训练与检测流程、23个yaml配置文件用于模型参数与数据集定义、5个shell脚本辅助环境配置,另有预训练模型、结果截图与说明文档,结构清晰便于二次开发。该资源已吸引271人学习下载,代码经作者测试运行成功,作为毕设项目具有较高完成度。下载后还可参考其中目录组织方式,快速复用检测流程与模型调优思路,对希望从零搭建裂缝检测系统或了解Yolov5实际应用细节的读者均有实用价值。
1. 裂缝检测不是黑匣子:这套Yolov5方案能直接落到你的验收报告里
很多检测单位一听到“深度学习裂缝识别”就发怵,觉得那是算法工程师的活。实际上,用 Python 和 Yolov5 做路面桥梁裂缝检测识别,是当前工程上最省力的一条路:你只需要准备一批裂缝图片,标注好位置,跑一段开源训练代码,拿到模型后就能批量识别新图,输出带框截图和置信度。这套流程不需要你从零写 CNN,也不需要读懂论文里的数学推导。对于市政养护、桥检机构、研究生做课题,它都能在两周内跑出可验收的结果。我见过有人用传统边缘检测做了几个月,最后被一行 Yolov5 命令秒了。这篇笔记就把数据准备、训练、推理、踩坑一路讲完,你按着步骤能复现。
2. 先弄懂 Yolov5 怎么认裂缝:从理论到选型,为什么不用传统 Canny 或 Faster R-CNN
2.1 裂缝在图像里到底是什么:目标检测视角下的难点
裂缝在图像上并不是一个“标准的框内目标”。比起猫狗、汽车这种有清晰轮廓和封闭形状的对象,裂缝有几个让算法头疼的特征:细长且断续,方向任意,宽度可能只有几个像素;对比度低,尤其是沥青路面在阴天拍摄时,缝和背景灰度差很小;背景噪声强,石子纹理、路面磨损、水渍、阴影都会伪装成裂缝。用目标检测的思路去框裂缝时,框往往是“细长矩形”或“围绕一段裂缝的局部框”,这跟 Yolov5 默认的 anchor 形状差异很大,后面会专门说怎么调。
另一个现实问题是形态多样性。横向裂缝、纵向裂缝、网状裂缝、块状裂缝的视觉特征完全不同。桥梁伸缩缝、结构缝也不是真正意义上的损伤,但看起来就是一条线。如果数据里混入这些,模型会学得“见线就框”,最后误检爆炸。所以要先明确:你要检测的不是“所有线条”,而是“特定损伤裂缝”。这决定了数据集标注时的正负样本边界。
2.2 Yolov5 之于裂缝检测的取舍:速度与精度的平衡点
传统 Canny 边缘检测为什么在裂缝场景翻车?因为它只计算梯度,对噪声极其敏感。路面上的石子边缘、胎痕、接缝都会产生连续响应,裂缝反而因为断续而断裂。你需要在 Canny 之后做形态学闭运算、连通域筛选、骨架提取,这一套调参过程非常玄学,换个路面材质,阈值全废。而 Yolov5 是端到端的目标检测,它学习的是“裂缝区域”的整体模式,对局部亮度变化有更强的鲁棒性。
那为什么不选 Faster R-CNN?它两阶段精度高,对小目标更友好,但推理速度慢,一张图在 CPU 上要几秒甚至十几秒。桥梁检测现场往往要批量处理上千张照片,用 Yolov5s 在普通 GPU 上能跑到毫秒级,CPU 也能做到每张几秒内出结果。Yolov5 提供了 s/m/l/x 几个规格,我一般用 s 起步:模型小,显存占用低,精度在裂缝任务上已经够用;如果框出的裂缝位置偏差大,再升级到 m。另一个重要原因是生态:Yolov5 的标注格式、训练脚本、部署工具体验最完整,遇到问题搜一下能找到大量现成方案。
但 Yolov5 不是没有短板。它对极细长目标的召回率天然偏低,因为默认 anchor 的宽高比集中在常见物体范围内。裂缝宽度 5 像素、长度 300 像素的细线,如果不做调整,模型很难学到位。好在 Yolov5 有 autoanchor 机制,会在训练前重新聚类出适合你数据的 anchor,后面我会建议你手动检查聚类结果,必要时人工修正。
2.3 自己的数据集 vs 公开数据集:怎么判断这套方案够不够用
网上能搜到不少裂缝检测数据集,比如 CrackForest、DeepCrack、某些市政公开数据包。但我的建议是:拿来跑通流程可以,直接用于验收不行。原因很简单:不同相机、不同分辨率、不同拍摄角度下裂缝的形态差异很大。别人模型在德国高速路上训练得好,拿到你的城市道路或桥梁箱梁里,光照、混凝土表面、标线都变了,性能会明显下滑。
所以“基于 Python 和 Yolov5 做裂缝检测”这个项目,核心不是下载一个预训练模型就结束,而是建立自己的数据集。判断数据够不够,我有个简单标准:裂缝类别下标注框数量至少 1000 个,且覆盖横向、纵向、网状、块状四类形态;误检类别(伸缩缝、施工缝、水渍、阴影)也单独截一些作为背景图。如果只有几十张图片,训练出来的模型就是碰运气。
Yolov5 的训练标签是一行文字:类别编号 + 归一化中心坐标 + 归一化宽高。比如一张 1280×720 的图上有个裂缝框,左上角为 (200, 300),右下角为 (600, 500),对应的 txt 内容是:
0 0.3125 0.5556 0.3125 0.2778这行的四个小数分别是cx/w、cy/h、w/w、h/h,把绝对像素坐标转成了 0~1 的比例。你不必手写这些数字,用标注工具导出即可,但理解这个格式很重要,因为后面转数据、检查标注错误都会用到。经验不足的人总以为模型问题,其实八成是标签格式或框位置算错了。
3. 数据集准备与预处理:把原始图像变成 Yolov5 能吃的格式
3.1 采集与标注:一线工地的实际约束
采集阶段最重要的不是数量,而是规范。手机拍照也行,但必须给裂缝一个“特写”的定位,而不是整幅路面全景。我一般要求拍摄距离控制在 0.5~1.5 米,让裂缝占图像高度的 20% 以上。分辨率至少 1080p,太低了细裂缝在缩放到 640 后直接消失。同一道裂缝可以多角度、多光照拍多张,这比换场景拍十张不清晰的图有用。
拍摄时尽量把裂缝放在画面中央,但也不要每张都居中,否则模型学到的位置偏向性比裂缝特征本身更重。最好在易误检的位置也拍一些负样本:桥梁伸缩缝、施工缝、沥青修补边界、车轮痕迹、反光水渍。这些在验收时会成为“魔鬼题”,负样本越多,模型越沉稳。
标注工具我推荐 LabelImg(输出 XML)或 Labelme(输出 JSON)。两者都行,但 LabelImg 的英文界面和快捷键对批量操作更友好。标注时有一个关键原则:不要试图把一条 60 厘米长的贯通裂缝框在一个超长框里,那样宽高比可能到 1:20,模型根本学不动。正确做法是把长裂缝切成 2~4 段,每段画一个框,框稍微包含裂缝周围一点背景作为上下文信息。
3.2 用 LabelImg/Labelme 产出 XML/JSON 再转 YOLO txt
标注完的格式不能直接用于训练,Yolov5 需要同名 txt 文件放在和图片同一个目录下。我通常用一段 Python 脚本做转换,这里给出核心逻辑:
import os import xml.etree.ElementTree as ET def convert_xml_to_yolo(xml_path, out_dir, class_list): tree = ET.parse(xml_path) root = tree.getroot() img_w = int(root.find("size/width").text) img_h = int(root.find("size/height").text) txt_name = os.path.splitext(os.path.basename(xml_path))[0] + ".txt" with open(os.path.join(out_dir, txt_name), "w") as f: for obj in root.iter("object"): cls = obj.find("name").text if cls not in class_list: continue # 跳过没用的类别 cls_id = class_list.index(cls) 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) cx = (x1 + x2) / 2 / img_w cy = (y1 + y2) / 2 / img_h w = (x2 - x1) / img_w h = (y2 - y1) / img_h f.write(f"{cls_id} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}\n") # 例:class_list 里只有 crack 一个类别 convert_xml_to_yolo("img_001.xml", "labels", ["crack"])这段脚本把 Pascal Voc 的 XML 转换成 Yolov5 的 txt 标签。逻辑是先读出图片尺寸,再遍历每个 object,把左上角和右下角坐标换算成归一化的中心坐标和宽高。注意脚本里我加了img_w和img_h是从 XML 里读取的,不推荐用其他方式获得尺寸,有些图片被旋转过或截图过,XML 里的信息才和标注一致。
转换后一定要抽样检查,看 txt 里的坐标是否正确。检查方法很简单:写一个小脚本读 txt 画框,或者直接用 Yolov5 自带的check_labels.py。常见错误是 XML 中name大小写不一致,导致类别 ID 配错。我一般会统一小写后再映射。
3.3 数据增强:别小看 Mosaic 和随机亮度,裂缝样本通常不够
很多人在小数据集上直接训练,结果过拟合到连训练集都背下来了。裂缝检测的现场照片往往只有几百张,这时数据增强的权重比模型结构更大。Yolov5 默认打开mosaic=1.0,它会把四张图随机裁剪拼成一张,这能显著提升模型对尺度和小目标的鲁棒性。但你也要注意,如果原图里裂缝本身很小,经过 Mosaic 缩放后可能变得几乎不可见,所以我建议前 10 个 epoch 不要用太强的增强,先用原图把基础特征学住。
我会在hyp.yaml里调这三个参数:
hsv_h: 0.015 hsv_s: 0.7 hsv_v: 0.4这是 Yolov5 自带的数据增强超参数,分别控制色相、饱和度、亮度的随机扰动范围。路面和混凝土的颜色一般不复杂,色相扰动不用大,但hsv_v调到 0.4 可以模拟阴天、逆光、夜间补光三种光照变化。注意:裂缝的灰度对比度是核心特征,如果hsv_v太大,裂缝和背景可能直接融到一起去,模型反而学不到区分度。所以从 0.4 起步,如果训练集 AP 不高,可以再调小。
另外强烈建议加一个 RandomPerspective 增强,Yolov5 训练时自动开启。它对图像做随机旋转、平移、缩放、错切,模拟无人机或手持相机拍摄时的角度变化。不过要小心:如果图片里裂缝被裁到画面边缘,增强时可能被裁掉,导致标签越界,模型学到一个“看不见的部分也算正样本”的错误模式。Yolov5 会自动过滤越界框,但过滤太多会降低正样本数。我会在训练日志里观察gt数目,如果比标注框少了 20% 以上,就该减小增强幅度了。
4. 环境搭建与训练:用 Yolov5 在本地跑通裂缝检测的最小命令
4.1 环境版本搭配:Python 3.8 + torch + CUDA 的坑
把这套方案形容成“黑匣子”一点也不夸张,很多人就垮在环境安装上。Yolov5 官方要求的 Python 版本是 3.8 以上,但也不是越新越好。我遇到过 Python 3.11 装 torch 后出现AttributeError,因为某些旧版本依赖不兼容。稳妥的方案是 Python 3.8 或 3.10,配 torch 2.x。如果你跟本站不住,直接去官方仓库下载requirements.txt,然后一行命令:
pip install -r requirements.txt但这条命令在 Windows 上经常翻车,因为 torch 的预编译包需要和 CUDA 版本匹配。我常用这个方式装:
pip install torch==2.1.0 torchvision==0.16.0 --index-url https://download.pytorch.org/whl/cu121这里指定了 CUDA 12.1 版本的 torch 和 torchvision。安装前先用nvidia-smi确认你的显卡驱动支持哪个 CUDA 版本。如果驱动是 CUDA 11.8,就把cu121改成cu118。没有 NVIDIA 显卡也不要放弃,纯 CPU 也能训练,只是慢十倍。此时把上面命令中的cu121去掉,直接装默认 CPU 版即可。
装完之后别急着训练,先跑一段检测命令验证环境。用官方预训练权重对一张图做推理,能出框说明基本环境没问题。这一步就像是买来新设备先通电,省得后面训练时报错你分不清是环境问题还是代码问题。我的血泪经验是:一定要先跑推理,再跑训练,否则你可能会在一个坏掉的 PyTorch 上浪费一下午。
4.2 修改数据集 YAML 和超参数,开始训练
Yolov5 训练前需要确认两个文件:数据集描述文件.yaml和训练脚本train.py。数据集 yaml 大概长这样:
path: ./datasets/crack # 数据集根目录,注意是相对于运行目录 train: images/train val: images/val nc: 1 names: ['crack']这里的path是根目录,train和val是图片子目录的路径。Yolov5 会自动在同级目录下找labels对应的标签。注意:如果你把标签放在labels/train,那么图片在images/train,这个对应关系不能错。我最初把标签放反了,训练时 loss 一直在 7 左右不降,后来才发现 Yolov5 根本没读到任何 GT 标签。
训练命令最短是这个:
python train.py --data crack.yaml --weights yolov5s.pt --img 640 --batch 16 --epochs 100每个参数都有讲究。--img 640是输入分辨率,裂缝检测我建议至少用 640,有条件用 1280。Yolov5 会把大图直接 resize 到 640,如果原图里裂缝宽度只有 5 像素,resize 后可能只有 2 像素,模型根本看不见。所以我把--img 1280作为裂缝任务的默认值,代价是显存占用约是 640 的四倍,没有 8G 以上显存会很难受。
--batch 16表示每批 16 张图。这个值不是越大越好,它取决于显存。Yolov5 会自动--auto-batch吗?不会,你需要自己调整。如果训练时报RuntimeError: CUDA out of memory,把 batch 降到 8 或 4。如果降 batch 后还是爆显存,就把--img从 1280 降到 960。
--epochs 100对几百张数据集来说已经足够,但不要盲目拉长。过多的 epoch 会让模型记住训练图上每道裂缝的精确纹理,在验证集上反而掉点。我一般设置 100,同时开启--patience 20,它表示 20 个 epoch 内 mAP 没有提升就提前停止,省时间也防过拟合。
4.3 训练日志怎么看:loss、mAP、PR 曲线别只看准确率
训练一跑起来,终端会刷出一堆指标:box_loss、obj_loss、cls_loss、mAP50、mAP50-95。不懂的人只知道看 mAP,但其实这里的cls_loss在单类别任务里几乎恒等于 0,因为你只有一个类别,分类损失没有意义。对裂缝检测来说,关键看obj_loss和box_loss。obj_loss是判断某个 anchor 位置有没有目标的损失,裂缝细长、前景占比小,这个 loss 会偏高,如果它一直降不下去,说明模型学不到“裂缝在哪”。
训练结束后,runs/train/exp目录下会生成results.png和confusion_matrix.png。我建议先看confusion_matrix:如果 background 被预测为 crack 的比例超过 20%,说明模型分不清裂缝和背景纹理,需要更多负样本或更强的背景抑制。还要看labels_correlogram.jpg,它展示所有标注框的中心点分布和尺寸分布。如果你的裂缝框全是居中、大小一样,说明标注偏了,模型学到的位置先验会限制泛化。
另外要注意验证集和训练集不能有重复图片。有人为了凑数量,把同一张图旋转一下同时放进 train 和 val,这样 mAP 虚高到 0.95,一上现场立刻现原形。我一般从现场拍回来的照片按“拍摄地点”划分:同一地点、同一道裂缝的所有照片归入训练或验证,避免一张裂缝的前后两张照片被拆开。
5. 避坑:裂缝检测最常见的 6 个拦路虎(现象 → 原因 → 解决)
5.1 裂缝断成两截或漏检:正样本太少还是 Anchor 不合适?
现象:预测框只覆盖了裂缝的一部分,比如一道贯穿整图的裂缝,模型只框出中间一段。或者一条裂缝断断续续被框成三四个碎片。
原因:第一,标签里长裂缝被切成多段后,每段框的宽高比依然很极端,Yolov5 原版 anchor 宽高比上限才 2,不支持 1:10 以上的目标。第二,裂缝中部对比度低,模型把那里当成了背景。
解决:在train.py前先运行python utils/autoanchor.py --data crack.yaml,查看 K-means 聚类生成的 anchor 宽高比。主流做法是手动修改模型 yaml 文件里的 anchor 定义,改成适合细长目标的宽高比。比如把[[10,13, 16,30, 33,23]]这类默认值换成[[8,80, 16,120, 32,200]]。另外,把原始图片切块处理后训练也能改善漏检:将大图切成 640×640 的块,每块重叠 20%,这样裂缝在每个块里占比更高,模型更容易学到整体形状。
5.2 训练 loss 不降或出现 NaN:学习率、batch、梯度问题
现象:训练到前几个 epoch,box_loss一直在 0.1 上下震荡,甚至出现NaN直接崩掉。
原因:常见是学习率太大,或 batch 内图片差异过大,导致梯度爆炸。另一个隐蔽原因是标签里有越界框,负值或大于 1 的归一化坐标会让损失函数算出来的值变成非法。
解决:首先检查标签:写个脚本遍历 txt,如果坐标值不在[0,1]之间,重新标注。然后是学习率:Yolov5 默认lr0=0.01是给 COCO 这种大数据集用的,小数据集容易震荡,我一般降到0.001左右。用命令python train.py --lr0 0.001。如果还崩,把--batch降到 8,并使用--amp开启混合精度训练,这能减少梯度异常。注意:AMP 在旧显卡上可能不稳定,如果开了NaN更多,关掉--amp再试。
5.3 模型在测试图上一堆误检:背景类与裂缝形态
现象:模型把桥梁伸缩缝、施工缝、沥青路面上的黑色胶带都框成裂缝,置信度还不低。
原因:这些“像缝但不是损伤”的目标在训练数据里没有作为负样本出现,或者出现了但没被标注。Yolov5 在单类别模式下,默认把所有没标注的区域当作背景,但它学到的背景特征太宽泛,一旦出现和裂缝形状相似的连续边缘,就会触发检测。
解决:第一,增加负样本图片,这些图里包含伸缩缝、施工缝、裂缝修补痕迹,但标签文件为空(txt 里不写任何内容)。第二,如果二分类不够,干脆把易混淆的内容单独设一个类别,比如background_crack,让模型区分“真裂缝”和“假裂缝”。第三,后处理中把置信度阈值从默认的 0.25 提高到 0.5,牺牲少量召回换取干净输出。做验收时,置信度低于 0.5 的框不如不画,画多了反而要逐个解释。
5.4 显存不足:batch、imgsz、amp、梯度累积的取舍
现象:训练到一半报CUDA out of memory,进程被杀死。
原因:输入分辨率太高、batch 太大、模型太大,三者叠加超过显存上限。我用 6G 显存的卡跑--img 1280 --batch 8就爆了。
解决:优先级从高到低,先降 batch 到 2,再降--img到 960,最后换轻量模型yolov5s。如果显存还是很紧张,可以启用梯度累积:在train.py源码里设置每 4 个 batch 更新一次,这样模拟 batch=8 的效果而显存只占 2。我不建议用--device cpu硬扛,一条裂缝可能跑十分钟,你会发现自己的时间比电费贵得多。如果条件允许,租个 24G 显存的卡训练一次,省下的时间够你把论文写一半。另外注意:训练完推理时,把--img和训练时保持一致,否则目标尺度偏移会导致精度下降。
5.5 结果截图与报告:怎么输出带框的裂缝图片和置信度
现象:模型识别正确,但输出的图片没有画框,也没法导出坐标信息,汇报时只能对着图口头说“这里有一条缝”。
原因:只跑了官方detect.py,保存的是save_dir里的整张图,没有把结构化数据导出成表格。
解决:在detect.py推理保存时,模型返回的每个框都有xyxy坐标和置信度,把它们写入 CSV。常见做法是修改plot_one_box的调用位置,在保存图像的同时把数据追加到一个 list,最后用pandas写出。这里的关键是别只在控制台打印,打印内容一旦滚动就找不回来了。我在实际项目中会让输出包含四列:图片名、裂缝框中心 X、中心 Y、置信度。这样报告里能统计整座桥所有裂缝的数量和位置密度。如果你连代码都不想改,可以用官方detect.py加--save-txt参数,它会生成和图片同名的 txt 文件,里面就是坐标和置信度。
5.6 模型文件太大部署难:Yolov5s 轻量化的方向
现象:训练完的best.pt有 40~200M,放到树莓派或现场盒子设备里跑不动,每帧推理要好几秒。
原因:yolov5l或yolov5x本身参数量大;但很多人以为yolov5s已经够小,仍然有 14M 参数,如果转成 FP32 格式,体积在 28M 左右。这还只是权重文件,推理时占用的内存更大。
解决:先确认你选的是yolov5s.pt作为预训练权重。如果是yolov5m,换成s能立竿见影。然后做半精度导出:python export.py --weights best.pt --include torchscript --half,导出后的模型体积约减一半,推理速度提升 30%~50%。做验收够用,如果做嵌入式部署,我建议用--int8量化,但要注意量化后精度会掉 2~5 个点,裂缝这种小目标受影响明显,最好是导出后在真实数据上重新测一遍 mAP。树莓派 4B 跑yolov5s的 FP16 版本,大概每张 640 图 2~3 秒,勉强可用;如果不行,就得考虑切图加检测的策略,而不是换更小的模型。
6. 最后一步:用训练好的模型批量识别并输出结构化结果
6.1 推理脚本:读取文件夹、调用模型、保存结果截图
当你有了best.pt,剩下的动作就是把一个文件夹里的现场图片全部过一遍。官方大概率自带detect.py,但它输出散落。我会写一段更短的脚本,方便直接集成到报告流程里:
import torch import glob import cv2 import os model = torch.hub.load('ultralytics/yolov5', 'custom', path='best.pt', source='local') img_dir = 'on_site_photos' out_dir = 'detect_results' os.makedirs(out_dir, exist_ok=True) for img_path in glob.glob(os.path.join(img_dir, '*.jpg')): img = cv2.imread(img_path) results = model(img, size=1280) # 保持和训练一致的分辨率 results.save(save_dir=out_dir) # 自动生成带框截图 data = results.pandas().xyxy[0] # 拿到坐标和置信度 if len(data): print(f"{os.path.basename(img_path)}: {len(data)} cracks")torch.hub.load从本地加载best.pt,避免联网下载权重。注意source='local'是指定从本地yolov5目录加载代码,没有写死版本。推理时size=1280必须和训练一致,如果你训练时用过--img 960,这里也用 960,否则模型看到的尺度和训练时不同。data是一个 DataFrame,包含xmin, ymin, xmax, ymax, confidence, class,后续统计都从这里取。
6.2 结果整理:生成标注框坐标、面积和置信度,方便写报告
识别完只是第一步,桥检报告需要可量化的数据。我会在上面的循环里多加一段:
for _, row in data.iterrows(): x1, y1, x2, y2 = row[['xmin', 'ymin', 'xmax', 'ymax']] pixel_area = (x2 - x1) * (y2 - y1) conf = row['confidence'] # 计算像素面积换算成实际面积需要每像素毫米标定 print(f" ({x1:.0f},{y1:.0f})-({x2:.0f},{y2:.0f}) conf={conf:.2f} area={pixel_area:.0f}px")这里算的是像素面积。现场标定一般要放参照物,比如在桥面上贴一张已知长度的标尺,然后在图像里手动量出每毫米像素比。如果没有标尺,只能给出裂缝框的像素长度占比,这也能作为招标或初评的参考。最终输出到 CSV 即可。
最后说一个我的教训:拿到模型后不要直接对满幅大图做检测,先把大图按 50% 重叠切成 1280 子图,检测完后把框坐标映射回原图坐标。否则一条贯穿全图的裂缝会在原图 resize 后变得极细,识别率惨不忍睹。这个切图脚本不难写,但一定要先调好坐标映射,否则画框位置和裂缝对不上,返工两次你就懂了。希望这篇笔记能帮你把裂缝检测项目稳稳落地。
本文还有配套的精品资源,点击获取