简介:针对桥梁道路裂缝检测需求,这套资料包提供基于YOLOv8的完整落地解决方案,适合计算机视觉初学者、算法工程师及土木工程相关研究者快速上手。压缩包内共2000个文件,包含1610个txt格式标注文件、139张jpg原始图像、171个Python源码、56个yaml配置文件以及训练好的pt权重和QT界面ui文件等,可用于模型训练、验证与可视化部署。数据集已按YOLO格式标注,并同时提供xml格式标签,配置好PyTorch环境即可直接调用。模型在道路裂缝场景上完成训练,能识别多种裂缝形态,配套QT界面方便非专业用户进行图片或视频检测。资料包整体约374MB,附带说明文档、启动脚本和配置文件,结构清晰。当前已有1282人学习下载,适合需要快速获取训练模型、标注数据集并搭建检测界面的开发者。 做桥梁和道路裂缝检测这个方向,前前后后折腾了两个多月,从数据采集、标注、训练YOLOv8模型,到最后用QT把整套流程包成一个带界面的桌面工具,总算是完整跑通了一套可以拿来直接用的方案。这篇文章把这个项目完整复盘一遍,重点说清楚几件事:为什么选YOLOv8而不是传统图像处理或者别的检测框架,训练好的模型和标注好的数据集从哪来、怎么用,QT界面又是怎么和模型配合的,以及在打包发布和实际部署里踩过的那些坑。适合正在做土木视觉检测、毕业设计,或者想把深度学习检测模型落到桌面端工具的朋友参考。
1. 项目整体思路与方案选型
1.1 为什么是YOLOv8而不是传统图像处理
裂缝检测这个场景,最直觉的做法其实是传统图像处理:Canny边缘检测、Sobel算子、阈值分割之类。早期我也试过,效果怎么说呢,在干净的路面、均匀光照下确实能出结果,但一到真实场景就撑不住了——阴影、伸缩缝、积水痕迹、柏油路面的石料纹理全是干扰,边缘检测结果里毛刺多到没法看,而且你根本没法区分裂缝和伸缩缝,更别说还要输出位置和置信度。
深度学习目标检测把这个问题的解法完全换了一种思路:把“裂缝长什么样”交给模型去学习,端到端输出检测框、类别和置信度。YOLO系列是这里面的常青树,而YOLOv8在工程落地上有几点特别诱人:首先是anchor-free + 解耦检测头的架构,对小目标更友好,裂缝恰恰是典型的小目标细长目标;其次是ultralytics官方库把训练、验证、导出这条链路封装得非常顺,一条命令就能训完再导出ONNX;第三是生态好,后续要往RK3588、Jetson这类边缘设备上迁移,也有成熟的部署路径。
选具体规格时,我对比了yolov8n/s/m三个尺寸。桥梁公路裂缝这种目标细长、数量不一定多,但背景复杂,纯追求速度选n,追求精度但显存有限的选s。我手里这块GTX 1660Ti 6G显存,最后定在yolov8s,性能和显存之间刚好平衡。
1.2 为什么界面选QT
模型训练好之后,总不能每张图都跑到命令行里去检测。工程验收、现场检查的人需要的是一个能打开图片、点一下按钮就能出结果的工具。桌面端选型主要在QT和纯Python那套GUI方案之间纠结。
如果用PySide6或者pyqt,开发确实快,画界面几乎零成本,跑模型也方便。但真到交付阶段,Python打包体积大、启动慢,而且现场机器上各种兼容问题会把你折磨疯。C++ Qt + ONNX Runtime这套组合,部署体积小、启动快、内存占用稳,打包出来一个exe就能跑,非常省心。
所以这个项目的技术路线非常明确:训练阶段用Python + ultralytics,部署阶段用Qt 5.15.2 + MSVC2019 + OpenCV + ONNX Runtime。训练和部署完全解耦,模型只以best.onnx的形式提供给QT端,后面想换TensorRT版本或者换边缘设备,上位机逻辑基本不用动。
1.3 完整数据流
整个系统的数据链路是这样的:
打开图片/视频流 → 读取帧 → letterbox等比例缩放填充到640×640 → ONNX Runtime前向推理 → 解析输出(1, 84, 8400) → 置信度过滤 + NMS → 坐标映射回原图 → 在界面上绘制检测框和统计信息 → 支持结果导出
2. 数据集准备与标注:决定上限的一步
2.1 数据怎么来
说句实在话,这个项目里最花时间的不是训练,是数据。深度学习任务里有个说法:模型只是逼近数据的上限。裂缝检测尤其典型,网上公开的桥梁裂缝数据集非常少,大部分做这个方向的人都是自己采集。
我这次的数据来源分两块。一块是自己拍的,桥梁高墩、箱梁腹板、桥面铺装、水泥混凝土路面和沥青路面分开拍,尽量覆盖不同的光照条件——大太阳底下的强阴影、阴天的漫反射、树荫遮挡,还有不同拍摄距离和角度。另一块是公开数据集补充,像CrackForest这些道路裂缝数据集,质量不错,可以拿来补充路面场景的样本。
最终整理出1800张图,道路场景约1200张、桥梁场景约600张。单类别“crack”,标注框总数大概4600个。做下来我的感受是:单类别目标检测想有实用效果,数据量最好控制在1000张以上,少于这个量,模型在没见过的场景上泛化能力会明显不足。当然,如果你能搞到3000张以上,效果会更稳。
2.2 标注规范和工具
标注工具我用的LabelImg,导出YOLO格式的txt文件,每行是“类别id 中心x 中心y 宽 高”,坐标全部归一化到0~1之间。工具本身没什么好讲的,重点是标注规范。
裂缝这种目标跟检测猫狗完全不一样,它细长、不规则、经常被阴影或者障碍物打断。我总结了几条实践规范:
- 检测框要紧贴裂缝区域,不要为了省事把整段路面框进去,框里背景太多会干扰模型学习。
- 一条完整裂缝被障碍物、桥面接缝、树影隔断时,要分成多个框分别标注。模型学的是局部纹理特征,分框标注不仅不影响效果,还等于变相增加样本。
- 尽量少出现极端长宽比的框。很多人习惯把一个一米多长的裂缝用一个细长条框起来,这在归一化坐标里计算没问题,但对模型回归不太友好。我是把长裂缝切成几个子块来框,实测对收敛速度有小幅提升。
- 质检环节必须做:写个脚本扫描所有txt,检查有没有空标签文件、有没有坐标越界、有没有类别id超出范围。这类低级错误在训练时会莫名其妙地让loss变成nan,排查起来非常痛苦。
数据划分上,我按8:1:1切成训练集1440张、验证集180张、测试集180张。划分的时候确保同一场景的照片不要同时出现在训练集和测试集里,否则会有数据泄漏,指标虚高。
2.3 数据增强的取舍
YOLOv8训练时默认开了不少数据增强:Mosaic、随机左右翻转、HSV色域变换、随机平移缩放。这些默认配置在常规目标上表现不错,但裂缝场景要稍微动一下。
Mosaic是把四张图拼成一张训练,对小目标场景是好事,但它有可能把一条完整裂缝切成好几截,目标变得特别碎。我在实际对比中把mosaic概率从默认的1.0降到0.6,最终mAP50反而涨了一点。另外针对裂缝场景,我又额外加了亮度和对比度抖动,用来模拟逆光、阴影、阴天的光照变化,以及轻微高斯模糊模拟雨天或者镜头失焦的情况。
有个点要特别提醒:负样本一定不要省。我加了大约5%完全没有裂缝的纯背景图进训练集,比如干净的桥面、平整的沥青路。这样做的目的是压误检——模型没见过的背景容易乱报,喂一些负样本让它知道“这些地方没有裂缝”,precision会明显提升。
3. 环境配置与YOLOv8训练实测
3.1 训练到底需不需要GPU
搜索这个项目关键词时,看到好多人问“YOLOv8需要用到GPU吗”。分两个阶段回答:训练阶段,强烈建议GPU,CPU不是不能跑,是慢到没有耐心等完——yolov8s在纯CPU上训练100个epoch,按我的数据量可能要跑几十个小时,显卡上也就是几个小时的事;推理阶段,QT端用CPU完全没问题,800ms以内的单帧延迟对裂缝检测这种非实时场景完全能接受。
我用的GTX 1660Ti 6G显存,实测下来:yolov8n可以轻松跑batch 16;yolov8s跑batch 16在640分辨率下也扛得住;yolov8m就比较吃力了,batch要降到8以下,训练时间明显拉长。所以6G显存这个档位,优先在n和s里选。软件栈方面推荐Python 3.9 + torch 2.x + ultralytics,CUDA版本选11.8比较稳,太新的CUDA配合老显卡反而容易出兼容问题。
3.2 训练参数:可以直接抄作业
训练配置文件不强求,直接命令行就能启动:
yolo detect train data=dataset.yaml model=yolov8s.pt epochs=150 imgsz=640 batch=16 patience=20 project=runs name=crackdataset.yaml里指定train和val路径,nc=1,names=['crack'],这个不用多说。几个关键参数讲一下为什么这么设:
- imgsz=640:我原始图最大能有4000×3000,直接拿原始尺寸训练,一张图就要吃好几个G显存,而且小目标的像素尺寸会被压得更小,反而难学。更合理的做法是先用滑窗把大图切成640×640的小块,再喂给模型训练。切图的时候我设置了10%~20%的重叠率,避免裂缝正好被切在边缘上导致漏检。
- epochs=150 + patience=20:150轮训练配上早停机制。模型通常在60~90轮左右就收敛了,patience=20的意思是连续20轮验证集指标不提升就自动停止,防止过拟合。
- optimizer:默认auto会自动选AdamW,对中小数据集挺友好的,我没动。
训练过程中loss曲线大概是这样:前10轮下降非常陡,box loss和cls loss都在快速收敛;后面30~50轮进入平缓期,会有小幅波动;最终稳定下来。这个阶段不用每天盯着,看日志里每轮的mAP50趋势就行。
3.3 训练结果怎么评估
单类别裂缝检测,我主要看三个指标:mAP50、mAP50-95、recall。mAP50到0.8以上算基本可用,0.7左右就需要继续优化。我这次best.pt的最终成绩是mAP50=0.87,mAP50-95=0.52,precision=0.86,recall=0.88,在实际测试集上表现够用。
如果recall偏低,说明漏检多,优先考虑提高imgsz、增加小目标样本、或者做滑窗切图时降低切图尺寸。如果precision偏低,说明误检多,优先检查是否缺负样本、是否场景差异太大。训练完务必看一下验证集上的badcase,很多问题在指标上看不出来,一看图就知道是标注问题还是数据分布问题。
调完模型,执行导出命令:
yolo export model=runs/crack/weights/best.pt format=onnx dynamic=False imgsz=640导出后用Netron打开best.onnx确认输出维度,我这边输出是(1, 6, 8400),因为只有一个类别,84对应的就是“4个坐标 + 1个置信度 + 1个类别概率”里的6。这一步确认好,后面QT端解析输出才不会搞错。
4. QT界面开发与模型集成
4.1 界面功能设计
QT端界面用了Qt 5.15.2,整体结构是一个典型的图像处理软件布局:顶部工具栏,左侧大图显示区,右侧信息面板和结果列表,底部是状态栏。功能上支持打开单张图片、打开视频文件、摄像头实时检测、停止检测、清空结果和导出报表,按钮逻辑都不复杂,复杂的是界面背后跟模型推理的配合。
右侧信息面板实时显示裂缝数量、平均置信度、单帧检测耗时,以及当前的FPS。检测结果会同步列在下方的表格里,每一行显示目标编号、左上角坐标、框的宽高和置信度。界面用QSS做了一套深色主题,对图像显示友好,观感上也比较专业。
4.2 推理必须放子线程,别在主线程里跑模型
这是这个项目里最容易犯、一犯就必炸的错。Qt主线程要跑UI事件循环,如果你在按钮点击事件里直接做ONNX Runtime推理,模型推理期间界面就会完全卡死,窗口拖不动、按钮点不了,严重的还会被系统判定为“无响应”。虽然单帧推理只要几十毫秒,但视频流检测时帧率上来,卡顿感会非常明显。
正确做法是把检测工作放到QThread里,用信号槽传递结果。核心结构类似这样:
class DetectWorker : public QObject { Q_OBJECT public slots: void doDetect(const cv::Mat &frame); signals: void resultReady(const DetectionResult &result); };主线程通过信号把图像传给worker检测,检测完成后worker再通过resultReady信号把带检测框的结果图像和统计信息传回主线程更新UI。视频检测时还要做帧率控制,用一个队列加条件变量,处理不过来就丢帧,保证界面始终流畅。
4.3 ONNX Runtime推理的完整流程
模型加载用ONNX Runtime C++ API,这个库非常简洁。推理管线核心是四步:
// 1. letterbox:等比例缩放+灰边填充 cv::Mat letterbox(const cv::Mat &src, int target = 640); // 2. 预处理:BGR转RGB,归一化到0~1,HWC转CHW // 3. 推理:session->Run() // 4. 后处理:解析输出 + 置信度过滤 + NMS + 坐标还原letterbox是最容易出问题的一步。很多人图省事直接cv::resize把图拉伸到640×640,检测出来的框会整体偏移,尤其是非正方形原图,偏差特别明显。正确做法是保持长宽比缩放,然后用灰色(114,114,114)填充剩余区域,记录缩放比例和padding偏移量,后处理时再映射回原图坐标。
输出解析这块,ONNX模型输出shape是(1, 6, 8400),8400是三个尺度特征图预设anchor的总数。解析时先转置成(8400, 6),按置信度阈值0.25过滤,剩下的候选框用cv::dnn::NMSBoxes做非极大值抑制,IoU阈值默认0.45。NMS的作用是同一裂缝上多个重叠框时只保留置信度最高的那个,不然后处理结果会很难看。
4.4 加分项:裂缝宽度估算
光输出检测框,有时候还满足不了实际需求。工程现场的人更关心这条裂缝大概多宽,是缝还是裂纹。我在QT界面里加了一个宽度估算功能:检测框拿到后,对框内区域做灰度化、大津阈值分割,提取裂缝像素,再结合标定信息估算宽度范围。这个功能属于后处理扩展,原理不复杂,但实际验收时特别加分。
5. 打包发布与常见问题排查
5.1 用windeployqt打包exe
训练好的模型和QT界面都齐了,最后一步是把程序打包成可以发给别人、双击就能跑的exe。打包我用的是Qt自带的windeployqt工具,步骤很简单:
- 用Release模式编译整个工程,得到单独的exe。
- 在exe所在目录执行
windeployqt your_app.exe,它会自动把Qt的dll和plugins目录拷进来。 - 手动把opencv_world4xx.dll、onnxruntime.dll复制到exe目录。
- 用整个exe目录做分发。
windeployqt有一个坑:它有时候不会自动带全OpenCV和ONNX Runtime的依赖,需要手动补。补完最好用Dependencies这个工具扫一遍exe,确认没有红色未解析项再发出去。
5.2 必踩的坑:no qt platform plugin could be initialized
这个报错几乎是所有Qt打包新手都会遇到的,搜索引擎里相关热度居高不下。现象是程序在外面电脑上双击后弹出一个黑色错误框,提示no qt platform plugin could be initialized,然后程序退出。
根本原因就一个:exe旁边缺少qwindows.dll这个平台插件。Qt的GUI程序启动时要加载平台插件,找不到就当场罢工。解决办法是把Qt安装目录下plugins/platforms整个目录复制到你exe同级目录下,保持platforms/qwindows.dll这种相对结构。也可以设置环境变量QT_QPA_PLATFORM_PLUGIN_PATH指向插件目录,但打包分发时直接复制目录最省事。
还有一个细节点:如果你把程序放在中文路径下,偶尔也会触发类似的插件加载异常,分发时尽量让对方的路径保持纯英文,能避开很多玄学问题。
5.3 高频问题速查表
| 现象 | 可能原因 | 处理方式 |
|---|---|---|
| 双击exe提示缺少DLL | windeployqt漏掉依赖 | 用Dependencies扫描后补dll |
| 打开界面后卡死 | 推理运行在GUI主线程 | 把推理逻辑放到QThread子线程 |
| 检测框位置偏得不正常 | letterbox后坐标没还原 | 后处理时乘回缩放比并减去padding |
| 中文路径的图片打不开 | OpenCV imread对中文路径支持差 | 用QFile读成字节再imdecode |
| 训练时loss变成nan | 学习率过大或标签越界 | 降低学习率、扫描标注txt |
| 同一裂缝重复出很多框 | NMS阈值太低或没做NMS | 确保做了NMS,IoU设0.45 |
| mAP高但现场实际检测差 | 训练数据和现场场景差异大 | 采集现场数据增量训练,增加负样本 |
5.4 后续可以怎么扩展
这个项目做完后,往工程化方向继续走的路子其实是通的:模型可以导出TensorRT部署到RK3588、Jetson这类边缘设备,QT端逻辑改成接收网络流或者串口数据就能做在线检测;算法层面如果想进一步提升细长裂缝的召回率,可以试试给小目标增加P2检测头,或者用GFPN、CFFM这类改进结构替换默认neck,网上也有不少现成方案可以参考。数据集本身也可以整理成标准的YOLO格式持续扩充,后续遇到新场景直接增量训练就行。
最后再分享一个小技巧。我在项目后期给QT界面加了一个“导出检测报表”功能,把检测结果连同原图文件名、裂缝数量、平均置信度、检测时间一起写入CSV和Excel,工程验收的时候直接拿报表给甲方看,比当场一张一张截图省事太多。很多同学容易忽略这种“非核心但很实用”的小功能,但其实它对整体体验的提升非常明显。
本文还有配套的精品资源,点击获取