外墙裂缝检测这块,我算是被现场工程师教育过好几轮才学乖。前几年接了个外立面巡检相关的项目,起初我以为裂缝识别嘛,OpenCV边缘检测随便跑跑就完事,结果到了现场,阳光一斜、墙面有点污渍,算法直接乱飙误报。后来换了深度学习方案,改到基于YOLOv8的外墙裂缝检测识别系统,这才算真正把检测精度和速度都提上来了。这套系统我做了中英文双版,界面、注释、部署文档都有双语版本,顺手整理好了完整源码和效果演示。如果你也在做建筑视觉检测这类偏土木与AI交叉的场景,这篇文章应该能帮你少走不少弯路。
我把整条链路拆开来讲,从数据集的坑、模型训练参数、中英文界面的实现,到部署实测的表现和翻车记录,一步都没漏。你按这个思路做,基本可以复现出一套能拿去现场小规模试点的裂缝检测系统。
1. 外墙裂缝检测为什么非做不可?从业务场景倒推选型
1.1 高空巡检的痛点与裂缝检测的真实需求
先说一个比较扎心的事实:外墙裂缝这种问题,靠人眼看是真的看不过来。一栋20层的住宅楼,外立面面积随随便便几千平方米,人工吊绳或者无人机拍摄回来的一堆照片,让工程师一张张放大找裂缝,眼睛都要看花,而且不同人看同一张照片,结果还不一样。裂缝这种东西又不会等你发现了再慢慢扩,外墙涂料层开裂、保温层裂缝、混凝土结构裂缝,一旦延展到渗水或者饰面层脱落,那问题就严重了。所以很多物业公司、检测机构都在找自动化的裂缝识别方案,需求很朴素:给我一批外墙照片,告诉我哪些有裂缝、裂缝大概在哪里。
这就引出了算法的核心任务:目标检测。它解决的是两件事,一是框出裂缝在图像中的位置,二是给出置信度。简单说,就是从“这张图有没有裂缝”升级到“裂缝在哪一块”,这对后续工程人员复核、统计裂缝密度、判断是否处理,价值完全不一样。
1.2 为什么是YOLOv8,而不是传统CV或者更重的检测框架
我做方案选型的时候,其实对比过三类路线,这里把我的真实想法写出来。
传统CV路线,就是Canny边缘检测加形态学处理,优点是快、部署简单,缺点是裂缝和阴影、砖缝、污渍的区分能力太弱。我在实验里用过一组下午强光照条件下的照片,Canny出来的边缘图几乎全是纹理噪声,裂缝目标反而被淹没。
两阶段检测模型,比如Faster R-CNN这类,精度确实有优势,但推理速度不太适合偏工程化的巡检场景。如果后面要把模型部署到边缘设备或者跑批量检测,一秒钟处理两三张肯定不够看。
YOLOv8胜在均衡。官方代码库用起来很顺,ultralytics包封装了训练、验证、导出一条龙,我这种经常需要快速验证想法的人,实在太友好了。精度上,用中等规模的yolov8s模型,在裂缝这种不算极其细微的目标上已经能到满意的mAP;速度上,单张640分辨率的图在普通显卡上能跑到几十毫秒级别,批量处理一段巡检视频也不成问题。
之所以强调“倒推选型”,是因为我发现很多人一开始就抱着“我要用最新的模型”的心态,但真实业务里如果数据质量不行,换什么模型都没用。YOLOv8真正打动我的,是它能让我把更多精力放在数据而不是造轮子上。
2. 数据才是裂缝检测的天花板:采集、标注和增强里的几个关键决定
做这个项目,我最大的体会是:算法模型本身没什么秘密,训练个把小时就能跑起来,真正决定系统上限的,是数据。裂缝这种目标,跟检测人、车完全不一样,它细长、背景纹理复杂、光照变化大,数据稍微没弄好,后面就算用再好的模型也救不回来。
2.1 数据从哪里来:现场实拍为主,公开数据补充
我的数据集主要由三部分构成。
第一部分是现场采集的真实外墙照片,占比大概七成。这部分是主力,因为外墙裂缝的形态和室内墙面裂缝差别很大,必须用真实场景照片。采集方式有两种,一种是无人机沿着楼宇外立面飞一圈拍的照片,另一种是吊篮里人工手持相机拍的局部特写。无人机拍的照片普遍视角大、裂缝所占像素少,难度更高;吊篮拍的局部图则更清晰,容易检测。
第二部分是公开的裂缝检测数据集,这类数据集网上能找到一些,比如某些高校或研究机构发布的混凝土裂缝、墙面裂缝数据。用公开数据有个好处,能快速扩充样本量,尤其是各种不同背景下的裂缝图片。
第三部分是我自己做了一定的负样本收集。这点很多人忽略。你不可能只给模型看“有裂缝”的图,还要让它看大量“没有裂缝但是很像裂缝”的图,比如墙面排水痕迹、砖缝、胶带痕迹、光影变化。这样才能逼着模型学会区分。
数据总量最终控制在四千多张,其中标注后的有效目标大概有一万多个。放在YOLOv8这种模型上,这个量级不算大,但配合上数据增强,训练出来效果完全够用了。如果你手头只有几百张图,也别急着放弃,后面我会提到离线增强的方法,可以硬撑一下。
2.2 标注细节:裂缝的框怎么打才算规范
数据标注这块,如果做得不规范,模型训练出来很容易出现一种病:明明裂缝就在那里,但预测框偏了半边。我总结出的经验是,裂缝框要尽量贴合裂缝的主体区域,边界不要扩太多,也不要把多条平行裂缝强行打在一个框里。
具体操作上,我用的是一款开源的图像标注工具,支持YOLO格式导出。标注类别就一类,名字叫crack。一块裂缝如果呈现明显的树枝状分叉,我通常的做法是拆成两三个框,而不是一个大框包住所有分叉。因为裂缝分叉之后,主干和支干的走向差异很大,一个大框会引入太多背景信息,模型学习时容易混乱。
还有几个特别容易踩的细节,截图放在一起讲更清楚,这里我用文字描述:
- 细裂缝在图像里如果只占两三个像素宽,标注时不要强求框到每一寸,先保证裂缝最清晰、最连续的中段被完整框住,太浅的端头宁可漏一点,也好过把背景框进去。
- 光照不均匀导致图片一半亮一半暗时,暗部的裂缝可能确实看不清,这时候不要硬标。喂给模型的标注重叠在模糊区域,反而干扰学习。
- 如果图像里同时存在几条裂缝,全部都要标,哪怕目标很小,也不要跳着标。模型有时候漏检,就是因为在训练数据里从未见过这种“多条并存”的场景。
2.3 数据增强:不止是常规操作,还得针对外墙场景定制
YOLOv8官方训练流程自带马赛克增强(Mosaic)、随机翻转、色调饱和度变化等,但我在实际调参中发现,这些常规增强不够“针对”。
外墙裂缝图像有三种很常见的变化:一是光照角度变化,早晨、中午、傍晚的阳光方向完全不同;二是拍摄距离变化,无人机远近不同,裂缝占画面大小差异很大;三是墙面材质差异,有光滑涂料、粗糙抹灰、面砖等。为了让模型应付这些变化,我在训练前做了离线增强。
离线增强主要包括这么几类:
- 亮度与对比度随机调整,模拟不同天气和时段,亮度范围控制在原图的0.7到1.3倍之间。
- 随机旋转与翻转,因为外立面照片本身方向就随意,模型不能对方向敏感。
- 高斯噪声和轻微模糊,模拟压缩失真或者轻微失焦的情况。
- 随机裁剪缩放,让模型在不同尺度下都能识别裂缝。
这里我额外提醒一句:马赛克增强虽然是YOLOv8默认开启的,但如果你训练到后期发现loss降不下去,可以适当降低mosaic的概率,甚至关掉。原因很简单,马赛克会拼接四张图,有时候会把裂缝切得七零八落,反而让模型学不到完整的裂缝形态。我用的是ultralytics提供的配置参数,直接把mosaic设为0.5左右,训练稳定很多。
2.4 训练集和验证集的划分:绝不能随机打乱就完事
数据划分这个问题,我刚做的时候图省事,直接用脚本随机划分train和val,结果训练出来的模型看上去指标挺好,一拿到现场就现原形。后来复盘才明白,问题出在随机划分上:同一个小区同一面墙的照片,前几帧分到了训练集,后几帧分到了验证集,模型相当于“背题”了。
正确的做法是,按拍摄场景划分。如果一批照片来自某栋楼、某次拍摄任务,就把它整体划到训练集或验证集中去,保证验证集里出现的场景基本没有在训练集里出现过。我当时把数据集分成train/val/test大概7:2:1,test单独留了一部分完全没参与过训练的照片,用来做最终演示效果评估。这样出来的指标才勉强算有说服力。
3. 训练与调参实战:从环境搭建到yolov8s模型落地
数据准备好以后,训练这块其实没太多玄学,但有几个关键步骤和参数,我晒出来给大家参考。
3.1 环境准备:版本锁定是省时间的必要操作
我用的环境是这样的,Python 3.10,PyTorch 2.0以上版本,CUDA 11.8。ultralytics库版本我锁定在8.1.x左右。这里特别说一句,ultralytics更新频率很高,小版本之间API偶尔会变动。比如早期版本和后来的版本,一些参数名和使用方式有差异,如果你直接装最新版跑我下面的命令,有可能碰到兼容性问题。所以有条件的话尽量让环境和我保持一致,或者用我当时用的虚拟环境配置文件。
如果跑CPU推理,也不是不行,但训练还是建议用带CUDA的显卡。我用了一块消费级显卡(24GB显存),batch size开到16,训练150个epoch,大概花了三个多小时。如果你显存只有8G或者12G,batch size要相应调小,比如8或者4,不然显存溢出。
3.2 数据集配置文件与训练命令
YOLOv8训练时需要一个数据集配置文件,YAML格式,我的大概长这样:
path: /home/user/crack-dataset train: images/train val: images/val test: images/test nc: 1 names: ['crack']这个文件内容比较简单,关键就是路径别搞错,别用相对路径,最好写绝对路径,省得到时候找不到数据报错。
训练命令我用的这一条:
yolo detect train data=data.yaml model=yolov8s.pt epochs=150 imgsz=640 batch=16 device=0 patience=20model=yolov8s.pt,这个起始权重是从官方预训练权重加载的,好处是可以借助在ImageNet等大规模数据集上学到的特征,迁移到裂缝检测上,收敛更快,效果也更好,比自己从零训练强很多。
patience=20表示如果20个epoch内验证集指标没有提升,训练就提前停止。这样做是为了防止过拟合,也能节省时间。
imgsz=640是训练分辨率。有人可能会问,裂缝那么细,要不要用1280或更高?确实高分辨率对小目标有好处,但训练显存占用也会跟着涨,推理速度也跟着变慢。我用640起步,训练好之后如果感觉检测小裂缝不够灵敏,再用1280微调几轮。这种两段式做法比一开始就用大分辨率稳定得多。
3.3 训练结果怎么看:别只盯着mAP,还要看PR曲线和混淆矩阵
训练完成之后,很多人习惯只看val集的mAP50和mAP50-95。我一开始也这样,后来发现这两个指标确实能反映整体好坏,但出问题的时候,比如某些裂缝漏检严重,单靠mAP是看不出来的。
我建议你训练完以后重点看两样东西:
第一是PR曲线。如果PR曲线比较“顶格”,说明模型在不同置信度阈值下都能同时维持较高的精确率和召回率。如果曲线明显弯得厉害,说明模型对裂缝的区分能力有限,要么是数据脏,要么是特征学得不够。
第二是混淆矩阵。虽然只有一类目标,但混淆矩阵依然可以看背景被误检成裂缝的频率。我当时发现背景误检率在部分浅色墙面上比较高,于是回到数据层面补了一批容易诱发误检的负样本,问题很快就缓解了。这个操作,只看mAP是逼不出来的。
3.4 模型导出:ONNX和TensorRT让推理更实用
训练完的PyTorch权重文件,部署起来不太方便,尤其别人没有深度学习环境的时候。我一般会导出成ONNX格式,这样在CPU上也能跑,或者再进一步转成TensorRT引擎文件,在NVIDIA显卡上跑起来更快。
导出ONNX的命令很简单:
yolo export model=runs/detect/train/weights/best.pt format=onnx imgsz=640导出之后,我用ONNX Runtime做了推理测试,速度比PyTorch原生推理快不少。如果你要在Windows电脑上打包一个给现场同事用的工具,ONNX格式配合ONNX Runtime是最稳妥的。后面界面系统里,我实际加载的就是这个ONNX模型,而不是原始权重。
4. 中英文双版界面:一轮300行的PySide6封装
模型训练好之后,摆在面前的问题就是怎么给别人用。直接扔一个命令行工具,很多工程人员玩不转;写个简单脚本又不够直观。所以我把整个检测过程封装成了可视化界面,而且做了中英文双语切换。
4.1 界面框架选型与双语言底座设计
界面这块我用的是PySide6,也就是Qt for Python。为什么不用Gradio那种Web界面?因为现场人员不一定有浏览器,也不一定懂怎么起服务,单机运行的桌面程序对他们来说更友好。而且PySide6做出来的界面,支持文件拖拽、批量检测、结果表格等,体验比网页表单舒服。
中英文双语实现的方式很简单:我定义了一个字典映射表,里面存了界面上所有控件的不同语言文字,切换语言时遍历更新控件文本。核心代码大概长这样:
LANG_ZH = { "window_title": "外墙裂缝检测识别系统", "select_images": "选择图片", "start_detect": "开始检测", "save_results": "保存结果", "detect_status": "检测状态", "no_crack_found": "未检测到裂缝", } LANG_EN = { "window_title": "Exterior Wall Crack Detection System", "select_images": "Select Images", "start_detect": "Start Detection", "save_results": "Save Results", "detect_status": "Detection Status", "no_crack_found": "No crack detected", }每次切换语言时,只需要根据当前语言键值重新setText就可以了。中英文双版并不是说要做两个完全分离的安装包,而是一个程序里动态切换语言,这样打包维护成本更低。
4.2 推理流程:ONNX模型加载、绘制检测框与结果统计
界面背后的推理流程,我用ONNX Runtime来实现。加载模型一次,之后批量检测图片,速度会快很多。关键步骤大概有这几步:
- 读取图片,把OpenCV的BGR格式转成RGB。
- 缩放到640x640,做letterbox填充,保持长宽比不变,多余的边用灰色填充。
- 输入模型得到预测结果,YOLOv8的输出是一个二维数组,包含目标框坐标、置信度和类别概率。
- 用置信度阈值过滤低分结果,再用NMS(非极大值抑制)去除重叠框。
- 在原始图上绘制矩形框和置信度文字。
用户从界面里选择图片之后,程序会显示原图和检测结果图,左侧或右侧还有检测列表,比如“图3 检测到1条裂缝,置信度0.87,用时45ms”。如果检测不到裂缝,也会明确显示“未检测到裂缝”,而不是什么都不输出。这个逻辑在工程场景里很重要,因为检测人员需要知道这个系统是真正分析过图片的,不是漏检了。
4.3 批量检测模式与结果导出
除了单张检测,我还加了批量检测模式。用户可以选择整个文件夹,程序自动遍历里面的图片,跑完之后生成一个汇总结果表格。表格里包含图片文件名、检测到裂缝数量、最高置信度、检测耗时,还支持一键导出成CSV文件,方便后续存档或者统计。
导出这块,实话说一开始很多次做出来的CSV格式不够友好,后来整理成了“文件名、裂缝数量、平均置信度、每张裂缝坐标列表”的格式,勉强能直接用。如果你后续要做裂缝密度统计,这个CSV还能再二次处理。
5. 实测效果与效果演示里最真实的几个教训
系统做好之后,我当然要在实际图片上跑一遍。效果演示看着简单,其实就是把一批没训练过的现场照片丢给系统,截图记录检测结果,但这个过程也暴露了不少问题。
5.1 演示效果的核心指标
我在验证集上跑出来的指标大概是这样:mAP50在0.87左右,mAP50-95在0.55左右。精确定率Recall接近0.82,这个水平不算顶级,但对外墙裂缝这种复杂纹理背景来说,已经能用了。
实际测试图片上,裂缝粗、对比度高的,基本都能一次命中,置信度一般都在0.8以上。无人机远距离拍摄的、裂缝只占很少像素的,偶尔会有漏检,这没办法,毕竟裂缝实在太细了。最让我满意的是误检控制,因为补了负样本,墙面雨水痕迹、砖缝、阴影这些干扰项,基本不会冒出来当裂缝。
5.2 光照阴影下的翻车现场与补救方案
演示过程中最难忘的一次翻车,是拿了一张下午逆光拍摄的外墙照片,墙面一大半在阴影里,裂缝恰好横穿阴影边界。模型在那张图上漏检了,检测框没有输出。
我排查了一下,问题出在训练数据里这类“裂缝跨明暗区域”的样本比较少。后来我补了一批这样的图片,直接复制原图并调整局部亮度,把裂缝区域一半压暗一半提亮,模拟出明暗交界效果。重新训练两轮之后,这种场景下的漏检明显改善。这也印证了我前面说的:数据的分布决定模型的边界,算法调参解决不了数据缺失的问题。
5.3 源码与演示怎么组织才方便复现
最后说下源码和演示效果的整理思路,方便你拿到之后能快速跑起来。
我的项目目录结构大概是这样的:
crack-detection-system/ ├── data/ │ ├── images/ │ ├── labels/ │ └── data.yaml ├── models/ │ └── best.onnx ├── ui/ │ ├── main_window.py │ ├── i18n.py │ └── styles/ ├── inference/ │ ├── detector.py │ └── utils.py ├── scripts/ │ ├── train.py │ ├── export_onnx.py │ └── batch_infer.py └── requirements.txtrequirements.txt里把关键依赖顶好,比如ultralytics版本、PySide6版本、onnxruntime版本。你用的时候先创建虚拟环境,安装依赖,然后把训练好的模型放到models目录,运行main_window.py就能打开中英文界面了。
效果演示文件我整理成了几张对比图和一个短视频片段,图片里左侧是原因,右侧是检测结果,红框标出裂缝位置并显示置信度。演示主要展示三种最典型的情况:清晰裂缝、细裂缝、带阴影干扰的裂缝,每张图都标注了检测耗时。这样别人看演示的时候能直观评估系统能力,而不是听我空口说指标多高。
关于源码引用和后续扩展的个人建议
如果你准备自己复现这套系统,我最诚恳的建议是先不要急着跑界面,用我已经标注好的现成数据集把模型训练一遍,拿到best.pt之后再去折腾界面部署。这样每一步的结果都可控,遇到问题也容易定位。
后续扩展方向,我目前更看重两个:一是把检测结果直接投影到建筑平面图上,形成裂缝分布热力图,这对工程巡检更有参考价值;二是把模型往边缘设备上迁移,比如用TensorRT转换后放到小型嵌入式设备上,无人机拍完直接在机载端做初步筛选,只把有裂缝的图片传回来,能省大量带宽。这些方向我都已经在慢慢试了,后续有新结果再拿出来分享。