简介:这是一份基于Python与YOLOv5的路面桥梁裂缝检测识别项目,源于导师指导下的高分毕业设计,评审分99分,适合计算机相关专业学生用于毕业设计、课程设计或期末大作业,也适合希望动手实践YOLO目标检测的初学者。整套资源以zip压缩包形式提供,共85个文件,大小仅1.6MB,包含23个Python源文件、23个YAML配置文件、Shell脚本、Dockerfile及示例图像;其中py文件负责模型定义、训练与推理,yaml文件用于配置网络结构和数据集,sh脚本用于获取预训练权重,Dockerfile可快速搭建一致运行环境。代码库按模型、工具、数据、脚本等模块组织,提供图片与摄像头两种推理脚本,方便对图片或摄像头画面进行裂缝识别;同时附有多个YOLOv5模型配置与权重下载脚本,可灵活切换不同检测规模。目前已有80人学习/下载,对有课程设计或毕业设计需求、想直接运行并二次开发的初学者较为友好。
1. 路面桥梁裂缝用Python+Yolov5检测识别:这套毕设方案先解决三个被低估的问题
混凝土路面和桥梁检测是结构检测的必修项,裂缝一旦漏检,轻则影响耐久性,重则危及结构安全。人工巡检效率低、主观性强,因此很多毕业设计选Python+Yolov5路面桥梁裂缝检测识别这个方向——数据集可获取、模型可训练、源码能直接跑通,看起来是一条稳妥路径。但一个反直觉的事实是:这类项目的难点不在模型精度,而在三个被低估的环节——数据标注的一致性、细长裂缝的漏检控制、最终评估口径的严谨性。如果这三件事没想清楚,哪怕源码包和预训练权重都准备得很顺利,最后收获的也只是“模型能跑,但答辩站不住”的结果。这篇笔记顺着这个标题,把环境搭建、训练调参、常见翻车点走一遍,新手能照着复现,熟手也能对照检查自己的参数设置。
2. Yolov5落地面裂检测先看模型机理:网络结构、检测输出与三个对比选型理由
2.1 路面和桥梁裂缝检测到底在检测什么
裂缝检测和行人检测这类常规目标检测任务有一个本质区别:裂缝不是“一团独立物体”,而是背景上的局部断裂纹理。沥青路面的裂缝往往呈现黑色细长条,水泥混凝土桥梁的裂缝则可能伴随渗水痕迹、泛碱区甚至长期车辆磨损后的黑色车辙带,这些干扰物在视觉上和裂缝高度相似。任务定义一旦不清晰,标注和评估就会出现系统性偏差。
从任务形态看,裂缝检测既可以用目标检测框来做,也可以用语义分割来做。毕设和多数现场巡检系统采用Yolov5检测框有几个实际原因:裂缝框只需要表达“这里有一处缺陷”,不需要逐像素抠出裂缝形态;标注成本大幅低于分割标注;后续统计裂缝数量、大致长度时,通过检测框的宽高比和坐标就能近似估算。分割方案比如U-Net能给出更精细的形态,但训练样本需求大,而且对桥梁上密集的网状裂缝,分割标注能把人标到崩溃。
我一般会把裂缝按形态粗分为三类来建类:横向缝、纵向缝、网状/块状缝。如果你的数据集里已经有现成标签,先看一眼类别之间的数量是否悬殊——常见翻车是横缝占了八成,纵缝和网状缝的召回率惨不忍睹。
2.2 Yolov5的结构、推理流程与检测头输出
Yolov5的源码结构有几个关键模块,跑裂缝检测前至少要清楚它们各自干什么。Backbone部分负责提取特征,U版Yolov5用的是CSPDarknet结构,加上SPP模块扩大感受野,让网络能同时看到较大范围的背景纹理;Neck部分采用PANet结构,自顶向下和自底向上的特征融合路径交叉进行,目的是让浅层的高分辨率特征和深层的语义特征互相补充;Head部分在三个不同尺度上输出预测结果。以小目标为主的裂缝检测,需要特别关注的是P2/浅层特征——很多漏检就发生在模型完全依赖Deep层语义特征、丢失了细裂缝的空间细节。
推理时的工作流也要心里有数。输入图片先做letterbox缩放,保持宽高比并填充边缘,避免形变破坏裂缝的几何形态;接着进入网络得到三个尺度的原始预测张量,每个张量的通道数由公式(类别数 + 5) * 3决定,5对应center_x, center_y, width, height, objectness;最后做NMS按类别过滤重叠框。这里有个裂缝场景特有的坑:NMS对细长裂缝框特别敏感,如果长宽比极端的两个框中心接近,NMS很容易把其中一个当成冗余框抹掉,导致联网状裂缝漏检。
检测头的输出从代码层面看,是经过解码后的归一化坐标。以detect.py为例,开启--save-txt后,每一行文本的前几个数字就是归一化的中心点和宽高,乘以原图尺寸才能还原真实像素位置。这个还原关系如果不搞清楚,后续统计裂缝位置、计算占比时很容易算错,而且错得悄无声息。
2.3 与Faster R-CNN、SSD、Yolov8的对比:为什么这张牌打起来最顺手
常见对手是两阶段检测器Faster R-CNN(或其变种,如经典的Cascade R-CNN)和单阶段的SSD,再新一点还有Yolov8、YoloX。
Faster R-CNN的RPN + 两阶段级联在密集小目标上确实能捡回不少召回,但训练速度、显存占用和部署成本对本科毕设并不友好。一个 200 张裂缝图的训练集,在同样的1050显卡上,Faster R-CNN要跑到 3 至 4 个小时一轮,Yolov5s只要不到 1 小时,而且调参涉及的先验框、NMS策略、候选框采样逻辑比Yolov5复杂得多——排查问题时的难度成倍上升。
SSD的优势是快,但对小目标先天弱势,裂缝这种宽高比极端、像素占比小的目标在SSD的单尺度特征上很容易被当成背景。
Yolov8的问题不是精度,而是封装程度。它的训练接口更“一体化”,许多底层改了要绕好几层代码,对新手来说像黑匣子;Yolov5的代码结构相对直白,从数据加载、损失函数到NMS,都能在utils/目录下对应到具体文件。答辩时老师问“你的检测框怎么来的”,你能直接指出model/yolo.py里的解码逻辑,这一点比“我用现成框架跑的”有说服力得多。
3. 跑通最小项目:Python环境搭建与源码目录的每一个关键开关
3.1 Python环境与依赖安装:先把torch和CUDA对应关系锁死
拿到源码包后的第一步不是急着跑train.py,而是先顺一遍Python环境。Yolov5的官方requirements.txt通常会列出一组依赖版本,但源码包经过多次迭代,依赖清单不一定和你的显卡驱动匹配。常见做法是先建独立虚拟环境,避免把系统Python搞乱。
我常用的环境是 Python 3.8 搭配 PyTorch 1.10 及以上(如1.10.2)与 CUDA 11.3,这个组合在NVIDIA 20/30系显卡上都能稳定跑。如果你手里是没有独显的机器或Mac,纯CPU也能训练,只是速度慢几倍,建议把--batch-size调小到4以内。建环境和装依赖的典型命令如下:
# 1. 用conda建独立环境,python版本建议3.8/3.9/3.10 conda create -n crack_detect python=3.8 -y conda activate crack_detect # 2. 先装torch全家桶,再装项目其余依赖 pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu113 # 3. 进入源码目录,安装requirements里的剩余依赖 cd yolov5-master pip install -r requirements.txt第一步创建的虚拟环境让依赖隔离,避免系统里其他项目的包版本冲突;第二步指定cu113索引是为了让torch与CUDA 11.3配套,如果版本错配,运行时会报CUDA error: no kernel image available for execution on the device;第三步的requirements.txt里会装上 numpy、opencv、matplotlib、tensorboard 等运行训练和可视化的基础库。
装完先跑一个自检命令python -c "import torch; print(torch.cuda.is_available())",输出True再继续。这个检查是后面所有训练的前提,免得在开了GPU的代码里浪费一整晚才发现CPU在硬扛。
3.2 源码目录结构与关键文件不看会跑错
U版Yolov5的核心代码文件并不多,但作为毕设,你需要知道每个目录在项目里扮演什么角色。models/下的yolov5s.yaml、yolov5m.yaml等文件是模型结构定义,它们不是权重,只是描述网络层数和通道数的配置;utils/里是数据加载、损失计算、指标评估、增强策略这些工具方法;data/下放数据集配置文件;weights/一般放预训练权重或你自己的训练结果。
很多第一次跑的人会犯的错是:把权重文件放到models/里,然后--weights models/yolov5s.pt报错找不到模块。权重文件默认放在项目根目录或weights/,结构定义文件才在models/。还有一个常见坑:源码包里的runs/目录是训练输出,包括每次的日志、权重和检测结果图,如果你连续训练多次,注意runs/train/expN的编号递增,别总是盯着runs/train/exp看,还以为自己没训练出结果。
我想强调的是,不要在拿到源码包后马上就改代码。先原样跑通一次推理或训练,确认环境无误,再逐步增加自己的数据集和改动,这个顺序能省大量排查时间。
3.3 用预训练权重跑一次检测:detect.py参数逐项说明
拿到一个能用的模型文件(如yolov5s.pt)后,先验证推理链路。detect.py是推理入口,常用命令长这样:
python detect.py --weights yolov5s.pt \ --source data/images/bridge_01.jpg \ --conf-thres 0.25 --iou-thres 0.45 \ --img-size 640 \ --save-txt --save-conf --project runs/detect/ --name demo_crack用途说明:--weights指定权重路径,可以是官方预训练权重,也可以是你之后自己训练的best.pt;--source可以是单张图片、视频文件甚至摄像头,传文件夹可以批量推理;--conf-thres是置信度阈值,低于该值的预测将被丢弃,调低它能找回更多潜在的裂缝区,但同时也引入误检;--iou-thres是NMS的IoU阈值,它控制两个重叠框的去留,对裂缝这种长条目标,一般控制在0.4到0.5之间比较稳妥;--img-size是推理时letterbox缩放到的大小,针对桥梁裂缝这种细节目标,建议直接用960,代价是显存占用上升;--save-txt会把每个目标的类别、置信度和归一化坐标写进txt文件,--save-conf则额外把置信度数值写入;--project与--name指定输出目录。
跑完会在输出目录看到带检测框的图片和同名txt文件。如果这时候画出来的框全是“一把梭”——一个大框套住半张图,大概率是阈值太低或模型过拟合了背景,而不是参数出了问题。先不要急着换模型,把--conf-thres提到0.5再看,筛选出真正的裂缝框,这一步也是毕设里经常被问到的“后处理逻辑从哪里体现”。
4. 训练自己的裂缝检测模型:数据集协调、YOLO格式转换与五个必调超参数
4.1 裂缝数据集准备:从现场图到可用训练集的三个原则
如果是毕设,多数人拿到的都是已有的裂缝图片集,但“有图”和“能训练”之间隔着挖掘整理。第一个原则是宁少勿偏,图片数少但覆盖多种光照、拍摄角度的训练集,比全是清晰正光图片的大数据集更可靠。第二个原则是控制类别边界,路面裂缝和桥梁裂缝如果只是拍摄对象不同、形态相似,没有必要拆成两个类,除非你确实需要区分“部位”来支撑后续维护决策。第三个原则是加入负样本,没有裂缝的干净路面、有水渍和阴影的混凝土面各放一部分,标注为背景。这能大幅减少模型把阴影当裂缝的翻车概率。
数据增强方面,Yolov5训练时默认开启Mosaic增强,把多张图拼成一张喂给网络。对裂缝这种长条形目标,Mosaic在早期能提升抗干扰能力,但要注意:如果拼图时把裂缝截断,或让两个长裂缝交叉,标注框会被强行合并,反而引入错误标签。因此如果你用的是自己的数据,建议把训练配置里的mosaic系数保持在0.5左右,不要为了增强而增强。
4.2 标注与YOLO格式:一行txt的坐标陷阱
标注工具我一般用LabelImg,它导出的是Pascal VOC的XML格式,需要再转成YOLO的txt格式。YOLO格式每行对应一个目标,内容为class_id center_x center_y width height,所有值都归一化到0到1之间。随手截一个真实标注中可能出现的错误例子:
# 标签文件 bridge_001.txt,类别0为横向裂缝 0 0.318759 0.245871 0.076599 0.024489 0 0.703214 0.658112 0.054821 0.011935每一行前一个数字是类别ID,后面四个是归一化坐标。这里最容易出错的点是:width和height是“框的宽和高”,不是“右侧坐标减左侧坐标再取绝对值”那么简单。如果写反了宽高,或者忘了归一化而直接写了像素值,训练出来的模型会在验证时mAP极低,且框的位置整体偏移。
一个稳妥的转换习惯是:先把XML里的xmin, ymin, xmax, ymax读出来,再统一用(xmin + xmax) / 2 / img_width计算中心点x坐标,用(xmax - xmin) / img_width计算宽度,所有坐标和尺寸都除以对应的图片宽或高,严格区分分母。同时做一步数据校验:对每个txt文件读一遍,检查所有值是否都在0到1之间,mAP在某个点突然崩塌,八成是这类低级问题。
4.3 训练启动命令与超参数怎么调才不玄学
数据准备完成后,需要写一个数据配置文件data/crack.yaml。内容示例如下:
# 数据配置:train/val路径按自己实际的目录写 train: data/crack/train.txt # 训练集图片路径列表 val: data/crack/val.txt # 验证集图片路径列表 nc: 2 # 类别数,例如横向缝+纵向缝 names: ['horizontal', 'vertical']train和val既可以是图片目录,也可以是包含图片路径的txt文件,两种写法Yolov5都认;nc必须和你标注txt里的最大类别ID+1一致,如果标注时用了ID 0和1,那么nc: 2;names列表顺序要和标注ID严格对应,否则训练出来类别名错乱。
训练启动命令是:
python train.py --data data/crack.yaml \ --weights yolov5s.pt \ --img 640 --batch 16 --epochs 100 \ --device 0 --workers 4 \ --patience 20 --cache各参数在裂缝场景下的选择逻辑:--img是训练时的输入分辨率,如果想提高小裂缝召回,调到960,但显存占用会翻倍;--batch直接受显存限制,8G显存选--img 640 --batch 16通常是安全的,显存溢出就降到8;--epochs结合下面--patience使用,我一般设100起步,配合早停,让模型自己决定几轮收敛;--workers是数据加载线程数,在Windows下超过4偶发假死,Linux上可以调到8;--cache会把图片一次性读入内存,显著减少每次epoch的等待时间,但前提是训练集总大小小于内存容量;--patience 20表示连续20个epoch验证mAP没有提升就自动停止,这是防过拟合的后悔药。
训练过程中,.pt会保存为last.pt(每轮结束的最新状态)和best.pt(验证集表现最好的权重)。要断点续训,执行:
python train.py --data data/crack.yaml --weights runs/train/exp/weights/last.pt --resume runs/train/exp--resume接训练输出目录,它会自动读取该目录下配置和上次的epoch状态继续跑,而不是从头再来。能否成功续训的关键是:不要手动改这个目录里的opt.yaml,里面的参数和命令行参数冲突时,训练会直接从旧配置启动。想要改参数就老实新建一次训练。
4.4 训练日志解读:loss降得漂亮不代表mAP好看
训练日志里最少有两个数要盯:box_loss和val/obj_loss,另外留意集中输出的[email protected]。裂缝数据集下有一条血泪经验:box_loss持续下降,[email protected]却不动甚至下跌,多半是模型在过拟合刷训练集,而不是真正学懂了裂缝形态。意思是它对训练图里的光照分布和背景记熟了,换到新图就失灵。
每轮epoch结束,Yolov5会在日志里打印类似P:0.713 W:0.862 mAP@.5:0.622这样的指标。mAP是多个数据集共同评估的结果,对于裂缝这类长条形、尺度变化大的目标,建议看mAP@.5(IoU=0.5时算作命中)而不是mAP@.5:.95。因为@.5:.95对框的位置偏差极其敏感,同一批裂缝框,人眼看着已经框到裂缝上,但 IoU 达不到0.75,指标被误杀。想用更严苛的指标来支撑毕设结论,也先确认你的数据集能承受这个标准;否则答辩时被质疑指标虚高,更被动。
5. 裂缝检测项目必踩的坑:过拟合、小目标漏检与评估失真
5.1 现象:训练后mAP不错,但验证集图片上满屏红框
现象是模型在训练集表现极佳,在验证集却满图框,甚至把没有任何裂缝的水泥纹路全框出来。原因通常是训练时conf-thres设置过低或数据增强过度,让模型学到了背景纹理的细节而不是裂缝的结构。解决方法是先看验证集预测时的阈值,把--conf-thres提到0.3以上,再检查增强配置里的fliplr=0.5是否把纵向裂缝翻转成横向标注,产生物理上虚假的样本。此类问题用负样本图跑一遍预测,能快速定位是过拟合还是标签混乱。
5.2 现象:细长裂缝漏检,而粗裂缝框得挺好
桥面细小裂缝只有1到2像素宽,下采样到深层特征图上可能只剩一个点,自然丢失。解决思路有三条:一是直接提高训练与推理分辨率,--img 960甚至1280;二是对原图做切图推理,把一张大图切成512大小的小图块分别检测再合并结果,这个做法能显著提高召回,代价是推理时间翻倍;三是标注时要对长裂缝分段标注,不要把一整条几十厘米的裂缝框成一个极大长条框,而是按可视段拆成多个框,这既适合检测任务,也减轻NMS对长宽比极端框的压制。如果这三条都做了,还漏检,再看锚框,Yolov5默认锚框并不适配极长条目标,可以在标注数据上运行utils/autoanchor.py重新聚类出自己的锚框,再写回模型配置。
5.3 现象:训练刚跑几步就报CUDA out of memory
这个报错多数是显存容量撞墙。连续跑几次都被中断,不是代码问题,是配置和显卡不匹配。常见处理:把--batch从16降到8或4;把--img从960降到640;关闭--cache,因为把数据缓存进内存在某些版本里会连带占用更多显存临时缓冲;再不行就启用混合精度训练,加--amp,一般能把显存占用压低三到四成。如果整机内存不够,先把workers调小,避免数据加载进程抢占系统内存。这类问题在裂缝数据集上特别频繁,因为图片多是现场拍摄的高分辨率照片,一张就接近十MB,预处理开销不小。
5.4 现象:测试集指标高,一到现场新图就失灵
这是割裂较严重的一类翻车:模型的指标是在“自己人”数据集上测出来的,而现场巡检的图片,亮度、拍摄角度、相机型号全变了。裂缝检测里常见的误检是阴影、伸缩缝、水渍被识别为裂缝,因为这些区域的局部对比度和真实裂缝很接近。解决常用两板斧:一是负样本补数据,把没有裂缝的桥梁、路面图片按10%左右的比例混进训练集;二是推理前做一次统一预处理,固定缩放到同一亮度范围,减少相机型号带来的分布偏移。我自己会在训练集里故意加入一些过曝和欠曝的裂缝样本,这比加入更多正常光照图对泛化能力的提升更明显。
5.5 现象:打印的mAP挺高,但保存的文本检测框数量对不上
detect.py里--save-txt保存的是NMS之后、置信度大于--conf-thres的目标框,它和画面里画出来的框应该是一一对应的。对不上通常有两个原因:第一,你同时开了--save-conf却没有解析最后一列置信度,导致多读少读;第二,输出的txt里坐标是归一化的,如果你直接拿像素坐标去画框,得到的框位置就会偏。建议统一用结果图旁边的txt做后处理,不要用Python另读一遍原始预测张量。分析裂缝统计时,以txt里保存的坐标为准,根据框的宽高比区分横向缝和纵向缝,再按置信度过滤一次,才算把检测输出用扎实了。
6. 从“会跑”到“能答辩”:自己做一张结果统计卡并解释清楚置信度的含义
做毕业设计,最常被问的一句话是“你的系统到底比人工目检好在哪”。如果只甩两张对比图,老师很难信服。这里我分享一个实用习惯:写一段脚本把检测结果统一汇总成表,让每个裂缝框、置信度、类别都有据可查。
import os, glob def parse_detect_txt(txt_path, img_w, img_h): boxes = [] with open(txt_path, 'r') as f: for line in f: parts = line.strip().split() # 格式:class_id cx cy w h [conf] cls_id = int(parts[0]) cx, cy = float(parts[1]), float(parts[2]) w, h = float(parts[3]), float(parts[4]) conf = float(parts[5]) if len(parts) > 5 else -1 # 还原为像素坐标 x1 = int((cx - w / 2) * img_w) y1 = int((cy - h / 2) * img_h) x2 = int((cx + w / 2) * img_w) y2 = int((cy + h / 2) * img_h) boxes.append((cls_id, x1, y1, x2, y2, conf)) return boxes # 示例:遍历某个检测输出目录 for txt in glob.glob('runs/detect/demo_crack/labels/*.txt'): boxes = parse_detect_txt(txt, 1920, 1080) print(f'{os.path.basename(txt)}: {len(boxes)} 个裂缝框')这段代码把Yolov5保存的归一化坐标还原成像素框,再按文件统计数量。你可以在此基础上继续统计横向缝和纵向缝的比例、平均置信度、每张图的拍摄位置——这些数据凑成一张统计表,再配合几张现场图,答辩时就能把“检测结果可解释”说清楚。
最后从实施角度强调两件事:第一,最终报告里写清楚你的conf-thres默认值和调整后的值,因为置信度阈值对裂缝检测的数量影响极大,不写阈值直接写“检测出872条裂缝”会被当作无效结论;第二,保留训练日志和验证集预测结果图,它们是模型训练的原始证据。我在这个项目上吃过亏:当时只留了最后结果图,被追问训练过程中的mAP曲线时拿不出东西,临时补跑又是好几天。现在做任何模型迭代,都习惯把每次训练的results.png和opt.yaml归档,这是最便宜的后悔药。裂缝检测没有想象中难,但把每个环节的坑填平,比追求一次跑通更有价值,希望这些经验对你有用。
本文还有配套的精品资源,点击获取