病害检测里,剥落和裂缝为什么非要走分割路线
做了几年视觉检测相关的项目,我越来越觉得“能不能看清”和“能不能测准”是两回事。拿建筑结构病害来说,墙面裂缝、混凝土剥落、内墙起鼓这类问题,传统巡检靠人眼和卷尺,效率低不说,不同人看同一个裂缝,量出来的宽度能差出一倍。后来大家开始上用目标检测框来做,一个框把裂缝圈出来,确实能自动发现“这里有病害”,但框不能告诉你裂缝到底多宽、剥落面积多大、走向是什么样——而这些恰恰是结构评估里最要命的指标。
我最近在做的这个项目,就是基于YOLO系列的分割版本,按我们内部的迭代习惯叫它“v13”——其实是延续YOLO思路做的一整套分割识别管线,专门用来做建筑物剥落、墙面裂缝、混凝土裂缝的分割识别。项目编号是第10417期,属于我们长期病害检测系列里的一期。这套管线核心解决的是三个问题:一是把裂缝从背景里干净地分离出来,二是把剥落区域的边界画准,三是给后续的宽度、面积量化提供像素级依据。
这篇文章,我会把从选型、数据准备、训练、后处理到落地部署的完整链路写清楚,尤其是那些文档上不写、但实际跑起来一定会踩的坑。如果你也在做结构病害检测、路面裂缝识别、桥梁混凝土剥落检测这类项目,这里面大部分经验可以直接搬走。
1. 分割模型选型:为什么没用纯语义分割网络
1.1 目标检测和实例分割在病害场景里的分工
做病害检测之前,我先把需求拆了一遍。墙面裂缝、混凝土剥落、内墙剥落,这些目标有共同点:形状不规则、边界模糊、背景纹理复杂。目标检测框只能给一个矩形包围盒,但裂缝是细长条,一个框里可能90%都是背景;剥落区域边缘又不规则,框根本贴合不上。所以当时我就确定:必须走分割路线,用像素级掩码输出病害轮廓。
分割又有两条路,一条是纯语义分割,像U-Net、DeepLabV3这类,直接对整张图做像素分类;另一条是沿着YOLO的分割分支做实例分割。我最终选了后者,核心原因是检测效率和多任务结构更适合实地的巡检场景。YOLO的分割版本是在检测框架上加了掩码分支,输出既是框又是掩码,一鱼两吃。而纯语义分割网络往往需要图像缩放得很规整,对多尺度和密集小目标不如YOLO系灵活。
那为什么项目名叫“yolov13”?其实没有官方这个版本号,这是我们团队内部对项目迭代的称呼。因为基于YOLO系列框架演进,做到第13期大版本,各类损失函数、数据增强、推理逻辑都已经深度定制了。你也可以理解为“基于YOLO分割分支深度改造的内部版本”。
1.2 分割网络在建筑病害上的三点优势
第一点是量化能力。语义分割输出的掩码可以直接算像素面积,结合相机标定就能换算成实际裂缝宽度、剥落面积。我用实测数据验证过:一条宽度0.3毫米的裂缝,在拍摄距离1米、分辨率4096x2160的图上,大约能占到6到8个像素宽。有了像素宽度,再配合拍摄距离和相机内参,就能算出物理宽度。这个能力是目标检测框完全给不了的。
第二点是抗干扰。墙面上的水管、钢筋阴影、模板缝、浮浆,这些在检测框里都会被圈进来,造成误检。但分割掩码有像素级的形状信息,后处理可以把面积、长宽比、骨架方向作为过滤条件,误检率能压到很低,后面我会专门讲。
第三点是端到端训练友好。YOLO分割分支的损失函数结合了框损失、类别损失、掩码损失三项,训练时彼此约束,小目标召回率比纯检测模型明显好。特别是裂缝这种长条目标,框回归很难定准,但掩码分支能通过像素级loss把细裂缝拉回来。
1.3 一张表看懂选型对比
| 方案 | 输出形式 | 裂缝宽度量化 | 剥落面积统计 | 小目标召回 | 推理效率 |
|---|---|---|---|---|---|
| 目标检测框 | 矩形框 | 不准 | 不准 | 中等 | 高 |
| 经典语义分割 | 像素分类 | 可行但工程量大 | 可行 | 低 | 中 |
| YOLO分割分支(本项目) | 框+掩码 | 直接支持 | 直接支持 | 高 | 高 |
| Transformer分割网络 | 掩码 | 可行 | 可行 | 高 | 低 |
实际跑完一轮项目后的体感是:在建筑病害这种“目标细长、背景杂乱、需要量化输出”的场景里,YOLO分割分支的性价比是最高的。后面所有技术细节都是围绕这套管线展开的。
2. 数据采集与标注:裂缝这类长尾小目标最吃细节
2.1 什么样的数据喂进去,直接决定模型上限
很多人一上来就问模型结构、训练参数,但以我的经验,病害检测项目里决定成败的首先是数据。混凝土裂缝、墙面剥落这类目标,和COCO数据集里的猫猫狗狗完全不同,它有极强的领域特殊性。前期如果不好好设计采集规范和标注规范,后面训练再折腾也救不回来。
采集这块我总结了几条硬性标准。第一条叫“多距离覆盖”:同一面墙的裂缝,至少要采集三个距离段——1米以内特写、2到3米中景、5米以上全景。原因是模型在推理时要面对不同尺度的目标,如果训练数据只集中在特写尺度,到现场拍全景图就会漏检。第二条是“光照对抗”:墙面在不同光线条件下,裂缝对比度差异巨大。直射强光下裂缝可能是亮缝,阴天漫射光下是暗缝。我专门挑过清晨、正午、傍晚、夜间补光四种条件去采集,让模型见过足够多的光照形态。第三条是“材质覆盖”:混凝土、水泥砂浆、砖墙、腻子墙面、瓷砖表面,裂缝形态都不一样,剥落的表现也完全不同。瓷砖表面剥落往往是整块崩落,砖墙剥落是碎屑状,混凝土剥落是块状分层。
2.2 标注不是画框,是勾勒病害的真实形态和边界
标注规范直接决定掩码质量。第一次做这个项目的同事按目标检测资料的思路去标,裂缝用矩形框,结果训练出来掩码边界糊成一片。后来我重新定了规范,核心几条:
- 裂缝标注必须沿实际裂缝骨架勾勒,不能为了省事用多边形框包住一大片区域。裂缝的掩码要尽量贴合骨干部位的像素。
- 剥落区域标注要包含“已经掉落”和“即将掉落”两部分,前者是明显空洞,后者是起鼓、空鼓区域。因为后期评判时,起鼓区域是需要预警的。
- 模糊区域宁可不标,不要乱标。如果图片严重过曝或者对焦不准,裂缝边界根本看不清,硬标的话会给模型引入大量噪声。
- 掩码边缘允许有1到2个像素的误差,但在分支、交叉口、裂缝端点这些位置要仔细勾勒,因为宽度量化对端点位置的敏感性很高。
我们一期项目大概标了上万张图,每张图平均1到3处目标。标注工具用的是常见开源标注软件,支持多边形和掩码模式。这里给个建议:标注软件导出的格式统一为COCO格式或YOLO分割格式,后期转训练集时省很多事。
2.3 数据增强的用力方向
图像分割任务里,数据增强不能瞎用。病害检测有个特点:目标形态本身是随机的,但背景结构(墙体、道路)是相对规整的。所以增强策略要“偏重目标形态扰动,弱化背景结构扭曲”。
我实际用的增强组合就是这几种:随机亮度对比度调整,模拟不同光照;随机旋转、缩放、裁剪,模拟不同拍摄角度和距离;轻微模糊和噪声,模拟手持设备抖动;还有随机灰度化,因为部分老旧监控设备是黑白的。比较关键的是,不要用大幅度的透视变换。墙体本身是平面,大幅透视会把直线裂缝扭成曲线,让模型学到错误形状先验。
训练时我还会用一种叫“马赛克增强”的策略,把四张图拼在一起训练。这个对提升小目标召回特别有效,因为拼接后每张图的尺寸变小了,模型被迫去适应小尺度目标。但代价是训练时间变长,需要自己权衡。
3. 训练细节:v13项目里的损失函数、归一化与关键trick
3.1 损失函数怎么调,裂缝宽度才不会飘
分割任务最常见的问题就是边界不齐。裂缝的掩码如果边缘锯齿严重,量化出来的宽度就会忽大忽小。我试过几种损失组合,最后稳定下来的是“交叉熵损失+骰子损失”加权。交叉熵保证像素分类的准确性,骰子损失专门解决前景背景不均衡——裂缝像素在整张图里往往只占百分之一不到,纯交叉熵会让模型倾向把所有像素预测为背景。
骰子损失的计算逻辑其实很直白:它直接比较预测掩码和真实掩码的重合度。公式是2乘以交集像素数,除以两个掩码像素数之和。取值范围0到1,越接近1越好。训练时把它从损失函数里剥离出来单独看,能直观反映分割质量。但这个损失在目标极小的时候梯度不稳定,所以要和交叉熵按权重混用。我这边权重大约是交叉熵0.6、骰子损失0.4,实测稳定性和边界质量都比较好。
3.2 输入尺寸、归一化和batchsize的配合
分割任务对输入分辨率极其敏感。分辨率太低,裂缝细碎部分直接就没了;分辨率太高,显存放不下。我这里用的是1088x1088,这是YOLO系列比较常见的训练尺寸,在精度和显存占用之间做得比较均衡。推理时则用原始分辨率,就是拍摄的原始图片直接喂进去,而不是先缩放再推理,这样可以最大化保留细小裂缝细节。
归一化方面我有个习惯,在训练集的均值、方差统计一下,用数据集的真实统计值做归一化,而不是用ImageNet的默认值。原因很实际:墙面、混凝土、砖石的色彩分布和自然图像差异很大,用ImageNet统计值归一化会让模型在早期训练时更难收敛。这个操作也只有一两行代码,收益却很明显,训练前期loss下降明显变快。
3.3 训练trick:冻结预训练权重与学习率策略
病害检测任务有一个明显优势:可以加载COCO预训练权重。YOLO分割分支的主干部分在自然图像上学到的特征提取能力是通用的,边缘、纹理、颜色这些底层特征可以直接复用。但直接全部微调有一个问题:早期训练阶段,损失波动巨大,掩码输出经常出现全黑或全白的情况。
我的做法是先冻结主干前若干层,只训练分割头和检测头。跑大概两三千步后,等分割头基本能输出形状合理的掩码了,再解冻主干全部参与训练,并用更小的学习率。这样训练过程稳定很多,最终mIoU也更高。学习率方面用的是余弦退火,初始学习率0.001,最小衰减到0.00001,搭配预热策略,前500步从0.0001线性升到0.001。这套参数在多组实验里表现都稳定,可以参考。
3.4 归一化的坑:BN层的batchsize陷阱
这里必须单独提醒一个坑:批量归一化在batchsize较小时会出问题。分割任务如果显存有限,batchsize往往只能开到8甚至4,这时候BN层统计的均值和方差噪声大,训练不稳定。我就是在这个项目里踩过一次,batchsize从16降到4之后,验证集的mIoU直接掉了四五个点。
解决方式有两种。一种是用分组归一化替代批次归一化,把通道分组计算统计值,不受batchsize影响;另一种是保持BN但显存不足时用梯度累积,相当于把多个小batch的梯度攒起来再更新一次,等效batchsize不变。我按梯度累积方式,设了4个累积步,等效batchsize 16,效果和直接开16的batchsize基本没有差异。如果你也在调分割训练,这个点值得优先检查。
4. 后处理量化:从掩码到裂缝宽度、剥落面积的工程换算
4.1 掩码后处理三步走:连通域、去噪、形态学重建
模型输出的原始掩码是概率图,不能直接拿去算宽度面积。我一般先做一个温度系数缩放,让概率接近0和1,然后取0.5作为阈值转成二值掩码。再之后是三步操作。
第一步是连通域分析,把所有独立的掩码区域找出来,并剔除面积小于设定阈值的碎片。第二步是形态学处理,用开运算去掉掩码边缘的毛刺,再用闭运算填补掩码内部的小空洞。第三步是骨架提取。骨架就是用细化算法把掩码区域压缩成单像素宽度的线条,这条骨架就是裂缝的中心线,也是后续宽度测量的基准线。
我在项目里用的是基于距离变换的骨架提取方法,比传统形态学细化更适合分支复杂的裂缝。算法逻辑是:先计算掩码内每个像素到背景的距离,距离值最大的那些像素构成“脊线”,就是骨架。分支点、端点都能正确保留,不会像形态学细化那样在交叉点产生收缩变形。
4.2 裂缝宽度怎么算:垂直骨架方向的像素统计
裂缝宽度是整个量化过程里最核心的指标,结构工程师要根据宽度判断裂缝是否超限。计算方法其实很直观:在骨架上每个像素位置,计算该点切线方向的法线,统计二值掩码在该法线上经过的像素数量,这个数量就是裂缝在该位置的像素宽度。对所有骨架点做一遍统计,得到宽度数组,它的最大值、平均值、分位数就直接对应裂缝的宽度描述。
要让像素宽度变成物理宽度,必须先标定。做法是拍摄一张已知物理尺寸的标定板,算单位像素对应的物理尺寸。比如标定板宽度100毫米,在图片里占2000个像素,那每个像素对应0.05毫米。当时我拍摄距离是1.2米,焦距50毫米,实测每像素对应0.048毫米,和理论计算值很接近。有了这个标定系数,裂缝宽度就可以直接输出毫米级别,后续就不用再拿尺子复核了。
但这里有一个很容易被忽略的细节:拍摄面必须和墙面尽量平行。如果相机倾斜角度太大,同一个裂缝在不同位置的像素宽度会变化,测出来的数据就失真了。所以我在采集规范里要求拍摄时相机尽量正对墙面。
4.3 剥落面积和占比计算
剥落区域的量化相对简单。掩码区域的总像素数乘以标定面积系数,就是剥落面积。但工程项目里更关心的是剥落面积在所在墙面区域里的占比。这就需要在检测时同时输出墙体区域分割结果,和剥落掩码做叠加计算。
我实现的方式是:先用分割模型把墙体区域提取出来,统计墙体总像素数;再对剥落掩码做同样统计;两者相除就是剥落占比。这个指标对评估维修优先级很有用,占比越高的地方越需要立刻处理。
还有个细节:混凝土剥落往往伴随露筋。露筋区域的检测比较特殊,钢筋表面反光强、颜色偏锈红,和混凝土背景区分比较明显。我在模型里单独加了一个类别叫“露筋”,训练数据里专门标注,实际应用时露筋区域会被单独评估,维修方案完全不同。刚开始做的时候觉得多标一个类别没什么,后来发现露筋的漏检率比剥落高不少,因为样本量少、反光变化大,后面针对性地做了数据补充才算稳定。
5. 实测效果与常见误检:钢筋阴影、浮浆、环氧树脂修补痕
5.1 在建筑和道路两类场景里的实测数据
项目跑完一轮,我统计了一些关键指标。建筑墙面裂缝分割的mIoU大约在0.82,剥落区域mIoU在0.86,裂缝宽度测量的平均绝对误差在0.05毫米左右。道路混凝土路面的裂缝分割mIoU稍低一些,在0.78,主要原因是路面纹理更粗糙、裂缝更细碎,加上车辆碾压造成的边缘起砂。
推理速度上,用一块常见的中端显卡,输入1088x1088的图像,帧率大约35帧每秒,完全满足巡检车连续拍摄的实时性需求。如果换到更边缘的设备,可以把输入降到640x640,帧率能到50帧以上,但小裂缝的召回率会下降五到六个百分点。所以我倾向于在边缘设备上对原始图做切片推理,而不是直接缩小,下一节会详细讲。
5.2 误检案例拆解:为什么钢筋阴影会被当成裂缝
这个项目里误检最多的一个来源就是钢筋阴影。钢筋混凝土构件表面,钢筋位置处的保护层厚度不均匀,光线照射后会在墙面形成一条细长的暗色阴影,形态上非常接近裂缝。一开始模型的误检率里,这个场景占了四成左右。
我的处理思路是三步。第一步,在训练数据里加入大量带钢筋阴影的负样本图,让模型见过各种阴影形态。第二步,在后处理中加入颜色特征过滤。裂缝在图像里通常呈现连续暗色,而且裂缝内部的灰度波动较小;钢筋阴影往往会有渐变边界,灰度波动更大。我用掩码区域的灰度标准差做阈值过滤,标准差过大的区域直接弃掉。第三步,用几何特征辅助判断:真实裂缝的骨架曲率变化是渐进的,而钢筋阴影的骨架通常是直线,曲率几乎为0。设置了最小曲率波动阈值之后,大量直线状阴影被过滤掉了。
5.3 浮浆和环氧树脂修补痕迹的干扰处理
混凝土表面的浮浆,说白了就是水泥浆堆积形成的薄层,在图像上和剥落区域边界很像。如果剥落定义里包含了“即将脱落区域”,那浮浆在灰度、纹理上确实容易混进来。我的处理方法是把决策边界换成“边缘突变强度”。剥落区域的边缘往往有明确的深度突变,在图像上表现为灰度阶跃;浮浆区域的边缘是渐变的,灰度过渡平缓。在掩码后处理阶段,我用边缘梯度响应做一次筛选,梯度均值低的区域大概率是浮浆污染。
修补痕迹是另一个方向。很多旧墙面用环氧树脂补过裂缝,补完后的表面是一道深褐色的条带,灰度上和裂缝非常像,但轮廓不连续、纹理均匀。模型非常容易把修补痕迹识别成裂缝。我目前的做法是在标注时单独加一个“修补痕迹”的类别,让模型把这类目标单独分出来。虽然增加了标注成本,但如果项目要求高精度,这一成本省不了。加了这一类之后,修补痕迹被误分到裂缝的比例下降了近九成。
6. 部署落地:从桌面端到边缘设备的裁剪与加速
6.1 模型裁剪的两种套路:通道剪枝与TensorRT加速
开发和验证阶段用中端显卡,但实际巡检车上的设备往往只是一块低功耗边缘计算板,算力差了一个数量级。直接跑原始模型根本跑不动,必须做裁剪和加速。
通道剪枝是我第一个用的方法。原理是找出一组权重冗余度高的通道,把它们从网络里移除,再用蒸馏的方式用原始模型指导剪枝后的模型训练,恢复精度。在这个分割模型上,我剪掉了主干部分约三成通道,精度下降不到两个点,推理速度提升约四成。要注意的是,剪枝后的模型必须重新测试边界目标召回率,尤其是细小裂缝,不能只看mIoU。
第二个方法是TensorRT加速。它的核心是把模型里的算子做层融合和精度校准,计算图优化之后推理速度能有明显提升。我实际对比过,同一个模型在边缘设备上,TensorRT FP16推理比原始PyTorch模型快了约2.8倍。但FP16精度在极小目标上会有损失,所以我对模型做了两类导出:一类是FP16用于快速巡检初筛,一类是FP32用于可疑区域复核。采集前端先把可疑区域标记下来,再用高精度模型做一次精分割,这样速度和精度都能保住。
6.2 切片推理:不改输入分辨率,用小图保住细裂缝
边缘设备算力有限,直接把大图缩小到640x640推理,细裂缝基本全丢。我实测过:4096x2160的原始图缩小到640x640后,原本能占8个像素的裂缝,缩成了不到2个像素,分割掩码变得断断续续,宽度统计也完全不可信。
所以我在部署管线里用了切片推理方案。把原始图按重叠滑窗切成若干块,比如每块960x960,重叠64像素,每块分别推理出掩码,再按坐标拼回完整掩码图。重叠区域可以避免目标在切片边界被切断。拼接时,重叠区域直接就取两块预测的平均值,能消除边缘效应。算过一笔账,同样一张4096x2160的图,整图缩到640x640推理,小裂缝召回率约65%;切片推理时召回率能到88%左右,推理总耗时会增加到原来的两三倍,但换来的是可用的检测精度,值得。
切片尺寸的选取也有讲究。尺寸太大,等于没解决算力问题;尺寸太小,裂缝这种长条目标会被切成好几段,拼接逻辑复杂。我试过512、640、768、960、1088五组尺寸,最终平衡点落在960。
6.3 输出到结构评估系统的数据格式
模型推理完,输出的结果要能直接给下游用。我这里定义了一套简单的JSON输出结构,每条病害记录包含:病害类型、置信度、掩码(以多边形点序列保存)、骨架点坐标、裂缝宽度数组、面积、占比。另外还会附带拍摄位置和时间戳。
这套格式对接结构评估系统时很顺手。修建立面评估模块可以读取裂缝宽度数组,判断最宽处是否超过规范限定值;剥落面积占比超标的区域自动排进优先维修列表。掩码可以通过多边形坐标直接重新叠加回原图,方便人工复核时一键查看。
对精度有疑虑的项目,建议在输出端做一个“人工复核抽检”流程。随机抽一定比例的检测结果由人工复核,用复核结果修正统计口径,这样既能保证数据可靠性,又不至于让人工完全重复检测一遍。
6.4 实际投入运行时的两个稳定性提醒
第一点是设备温度。边缘设备长时间在烈日下运行,推理速度会明显下降。我遇到过设备温度升高后,TensorRT推理延迟从35毫秒涨到80毫秒的情况。后来在部署方案里加了降频策略——温度超过阈值时,自动把输入分辨率从960降到768,用精度换稳定性。
第二点是标定系数会随拍摄距离变化。巡检车在不同距离采集的图像,像素对应的物理尺寸完全不同。如果统一用一个标定系数,宽度数据就失真了。方案有两个:要么固定拍摄距离,把相机装在刚性支架上;要么引入测距模块,每个检测结果附带距离信息,后处理时根据距离动态标定。
项目做到这个阶段,我个人体会最深的一点是:模型本身在今天已经不是最大的瓶颈了,真正拉开差距的地方在于数据质量、后处理量化和部署细节。同样的模型结构,前处理和后处理的方式不同,现场实际效果可能差出一大截。病害检测这类任务尤其如此,因为下游要根据检测结果做维修决策,一个宽度数据错了,可能导致该修的没修、不该修的反复施工。
目前这个v13管线的能力集中在外墙裂缝、内墙剥落、混凝土露筋这几个方向上,后续如果要扩展,可以考虑融合不同角度拍摄的多帧图像做立体验证、对连续帧做病害变化的时序追踪,这样同一个位置的宽度变化趋势就能自动记录,对维修前后的效果对比非常有用。做结构健康监测的话,数据累积比单次测量更有价值。