
做建筑墙面缺陷检测最头疼的往往不是模型选型而是手里根本没有一套能用的数据。这里想聊的是我参与整理并实际用于多个项目的一套墙面缺陷检测数据集规模是7374张图像同时带VOC和YOLO两种标注格式覆盖裂缝、渗水、剥落、空鼓等常见墙面病害。这套数据从采集、标注到格式转换、模型训练整套流程我都完整跑过一遍中间踩了不少坑也摸索出一套相对稳定的做法。这篇就顺着项目实际推进顺序把关键细节和可复现的脚本一起放出来。1. 项目概述与需求拆解1.1 墙面缺陷检测到底在解决什么问题传统墙面巡检主要靠人眼要么搭脚手架要么吊绳下去看效率低且危险。近两年无人机和自动化爬壁设备普及后越来越多团队开始用目标检测模型做墙面病害识别核心任务就变成了输入一张墙面图像输出缺陷的位置和类别。这个任务的特点是缺陷形态差异非常大。裂缝是细长条状宽度可能只有几个像素渗水是片状晕染边界模糊剥落往往有清晰的边缘但形状很不规则空鼓从表面看可能只是微微鼓起甚至肉眼都很容易漏掉。这些特性决定了数据集的标注规范和模型训练策略不能直接套用通用目标检测方案。7374张这个规模不是拍脑袋定的。做监督学习训练数量太少容易过拟合数量太多采集和标注成本又会失控。在实际项目中这个量级配合数据增强已经能让YOLO系模型在墙面病害场景里达到可用的识别效果同时数据整理和人工复核的工作量也在可控范围内。1.2 数据集的类别构成与标注口径数据集里的缺陷类别我建议按实际质检关注点来定而不是堆一堆花哨的标签。这套数据主要包含五类类别典型表现标注建议crack细线状裂缝可能单条或多条交织每条主裂缝单独框出次裂缝如果与主裂缝相连可并入同一框spalling表层剥落、水泥块脱落框住整个剥落区域边缘破损部分包含在内efflorescence泛碱、盐析一般呈白色粉状区域按可见白色区域外扩几个像素框出water_stain渗水/水渍通常伴随深色晕染框住明显变色区域不包括正常材质纹理hollow空鼓表面鼓起或已起壳以鼓起区域可见边界为准边界不明显时按敲击范围人工判定参考标注这里特别要注意渗水和泛碱经常会出现在同一个区域。标注阶段如果不约定主次后面模型训练时很容易出现同一样本在不同图上标签冲突。我们当时的约定是如果水渍和泛碱重叠面积超过单边区域的30%只标主要病害类别另一个类别不再额外框出。这样既保证了类别独立性也避免了同一区域被两个框重叠造成训练时的矛盾信号。1.3 双格式的价值VOC和YOLO为什么同时保留VOC格式是XML文件每个标注框包含类别名和四个顶点坐标YOLO格式是TXT文件每行一条框内容是类别索引和归一化的中心点坐标、宽高。两者本质上描述的是同一批标注但应用场景不同。VOC格式适合人类阅读和跨框架转换。很多通用标注工具原生保存成VOC格式它包含文件名、图像路径、尺寸、物体列表信息完整适合做数据复核、二次编辑也方便转成COCO等其他格式。YOLO格式则几乎是为训练量身定做的模型训练读取TXT效率高文件体积小不需要额外解析XML层级。保留双格式的最大好处是训练时直接用YOLO格式修改、复检时用VOC格式两种需求互不干扰。项目交付给其他合作方时也避免了对方不一定兼容单一格式的问题。2. 数据集的采集、筛选与标注规范2.1 图像来源与筛选原则图像来源主要分三个渠道无人机航拍墙面、人工手持设备拍摄、爬壁机器人拍摄。不同采集方式带来的图像尺度差异很大无人机拍的墙面裂缝可能只占几百像素甚至更少手持设备拍的裂缝则会非常清晰、占画面比例大。筛选数据时刻意保留这种尺度多样性是有价值的它能让模型对拍摄距离的鲁棒性更好。但多样性也要有边界。一些明显的无效图像必须直接剔除严重过曝或欠曝墙面细节完全丢失的运动模糊导致缺陷边缘无法判断的缺陷面积小于整张图1%且分辨率过低、人眼都看不清的图像里包含大面积非墙面区域天空、地面、人员等且缺陷不在画面主体内的。初筛可以先用脚本按像素亮度方差和模糊程度批量过滤再人工快速过一遍。7374张最终入库数据实际是从一万多张原始图里筛出来的过滤比例并不低。这类数据采集还要注意脱敏问题。墙面图像偶尔会拍到窗户、门牌、行人和车辆商用和对外发布时务必做局部裁剪处理避免隐私合规风险。涉及具体项目现场的画面对外分享前也要确认是否允许公开。2.2 标注规约与边界判定标注没统一标准后面模型效果一定乱。项目开工前我建议先写一份简单的标注规约至少约定以下几条第一裂缝框定方式。裂缝是细长目标水平框会框入大量背景。YOLO这类原生检测器不直接支持旋转框我们的做法是画一个尽可能贴合裂缝方向的最小水平外接矩形。如果裂缝接近对角方向这个框可能包含较多背景这是水平框方案的固有限制不必为了贴合裂缝而把框旋转后续训练时模型自己会学特征。但如果裂缝呈明显多段走向应该拆成多个框而不是用一个巨大框包住所有分支。第二遮挡和模糊处理。缺陷被脚手架、植被遮挡超过50%的不标注整张图像模糊导致边界不可判断的不标注。只标仍然能可靠判断的目标宁缺毋滥。第三贴边目标处理。缺陷延伸到图像边缘时按图像内可见部分标注不需要脑补图像外区域。第四交叉验证机制。多人协作标注时每人完成一批后另一个人抽检不低于10%对类别归属明显错误的样本退回重标。实际经验是渗水和水渍这类边界模糊的类别最容易标得五花八门交叉验证环节必不可少。2.3 工具选型与多人协作流程标注工具我们最终用的是开源的labelImg和X-anylabeling。labelImg保存成VOC格式对Windows和Linux都友好缺点是连续标注时效率偏低。X-anylabeling支持更多导出格式适合需要多格式输出的场景。工具不重要重要的是团队内部统一不要一队人用A工具、另一队人用B工具后期合并时格式字段差异会非常折磨。建议工作流是这样的先由负责人建立类别列表和2-3个典型样例图标注员对照样例开工每天结束时导出一批XML做格式和坐标范围校验每周做一次全量标注抽检。这套流程跑下来后期格式转换几乎没有返工。还有一个很多人忽略的细节labelImg保存的XML里图像路径是默认的绝对路径。数据集拷贝到别的机器时路径全部失效。转换前先批量把XML里的路径字段统一修正或者干脆无视路径字段只用XML同级的图像文件名来关联能省很多事。3. VOC格式转YOLO格式的完整转换流程3.1 VOC与YOLO两种格式的本质差异VOC的XML结构里bndbox给出的是xmin、ymin、xmax、ymax四个绝对值单位是像素以图像左上角为原点。YOLO训练需要的是归一化的中心点坐标和归一化宽高即x_center (xmin xmax) / 2 / image_widthy_center (ymin ymax) / 2 / image_heightwidth (xmax - xmin) / image_widthheight (ymax - ymin) / image_height所有数值都在0到1之间。这样定义的框与原图分辨率无关训练时无论输入尺寸怎么缩放标注都能正确映射。转换时的几个关键细节类别名称必须转成类别索引且全数据集索引一致坐标计算必须用图像真实宽高不能用XML里可能存在的缓存size归一化坐标要防止小于0或大于1浮点误差和标注边界越界需要统一截断TXT文件名必须与图像文件名完全一致包括不带扩展名。3.2 目录结构与数据集划分数据集目录结构我采用YOLO训练框架最常见的布局dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ ├── dataset.yaml └── classes.txt训练框架在训练时会自动去labels目录里找与images下图像同名的TXT文件。如果图像在images/train/xxx.jpg标签必须放在labels/train/xxx.txt。目录命名可以自定义但images和labels的对应层级必须一致。划分策略要特别注意按墙面或楼栋分组切分而不是纯随机打散。我的做法是先给每张图加一个楼栋ID的隐含字段然后保证同一楼栋的所有图像只出现在同一个集合里。原因是同一面墙的连续拍摄图像高度相似如果同一栋楼的图同时进训练集和验证集验证分数会虚高一旦更换新场景真实效果立刻打回原形。这个坑我在早期项目里踩过一次验证集mAP高得漂亮到了实际现场惨不忍睹。3.3 转换脚本与执行细节这里放一个可以直接用的转换脚本支持批量处理整目录的VOC XML文件。import xml.etree.ElementTree as ET from pathlib import Path # 类别映射表必须与标注时定义的类别顺序完全一致 CLASS_MAP { crack: 0, spalling: 1, efflorescence: 2, water_stain: 3, hollow: 4, } def voc_to_yolo(xml_path: Path, out_dir: Path): tree ET.parse(xml_path) root tree.getroot() # 优先从XML的size节点读取图像宽高 size_node root.find(size) img_w int(size_node.find(width).text) img_h int(size_node.find(height).text) if img_w 0 or img_h 0: print(f[skip] invalid size in {xml_path.name}) return lines [] for obj in root.iter(object): name obj.find(name).text.strip() if name not in CLASS_MAP: print(f[warn] unknown class {name} in {xml_path.name}, skipped) continue class_id CLASS_MAP[name] bndbox obj.find(bndbox) xmin float(bndbox.find(xmin).text) ymin float(bndbox.find(ymin).text) xmax float(bndbox.find(xmax).text) ymax float(bndbox.find(ymax).text) # 防御性处理忽略非法框 if xmax xmin or ymax ymin: continue x_center (xmin xmax) / 2.0 / img_w y_center (ymin ymax) / 2.0 / img_h box_w (xmax - xmin) / img_w box_h (ymax - ymin) / img_h # 截断到[0, 1]区间防止标注越界导致训练报错 x_center max(0.0, min(1.0, x_center)) y_center max(0.0, min(1.0, y_center)) box_w max(0.0, min(1.0, box_w)) box_h max(0.0, min(1.0, box_h)) # 保留6位小数精度足够且文件体积可控 lines.append(f{class_id} {x_center:.6f} {y_center:.6f} {box_w:.6f} {box_h:.6f}) # 即使没有目标也生成一个空txt文件与图像一一对应 txt_path out_dir / (xml_path.stem .txt) txt_path.write_text(\n.join(lines), encodingutf-8) # 批量转换示例 def batch_convert(xml_dir: Path, yolo_label_dir: Path): yolo_label_dir.mkdir(parentsTrue, exist_okTrue) for xml_path in sorted(xml_dir.glob(*.xml)): voc_to_yolo(xml_path, yolo_label_dir) if __name__ __main__: batch_convert(Path(VOC/Annotations), Path(YOLO/labels))脚本里有几个细节值得说明。读取宽高时优先用XML的size节点因为有的标注工具在图像缩放后没有同步刷新这个节点直接去读图片信息反而更稳妥。类别映射表如果和标注时的定义顺序不一致训练时会出现类别错位检测结果完全错乱。空TXT文件必须保留不能因为是没有目标就直接跳过否则训练框架会因为图像找不到对应标签而自动把图丢弃。3.4 转换后的校验清单转换完不能直接开训先跑一遍校验脚本对比images/train与labels/train的文件名集合是否完全一致抽查若干TXT确认每行坐标在0~1之间逐类别统计样本数量确认没有类别丢失随机挑20张图用可视化脚本把TXT框画回原图检查框的位置和类别是否与原XML一致。这一步可视化检查很关键我曾经因为粗心把x_center和y_center的顺序弄反训练时损失函数照样收敛但预测框全部错位。可视化一画问题立刻暴露。4. 用这份数据集训练YOLO检测模型4.1 模型选型与训练环境墙面缺陷检测属于中小目标检测场景相对固定不需要最重的模型。我优先推荐从YOLOv8s起步它在精度和速度之间比较均衡单张1080Ti级别显卡就能跑实际部署到无人机机载设备或边缘盒子上也够用。如果目标是在极低算力设备上跑实时检测可以换成YOLOv8n如果对精度要求很高且算力充足再考虑YOLOv8m或更大模型。选型思路上不建议一上来就追最新的大模型。检测任务里数据质量和标注一致性对效果的影响远大于模型多几个参数。先把小型模型在自己的数据集上跑通得到一条效果基线再逐步升级模型规模反而更高效。训练环境建议用PyTorch ultralytics库显卡至少8GB显存。如果显存不够可以调小batch size或降低输入分辨率但分辨率这个参数后面单独说不要轻易降。4.2 数据集配置文件与训练命令YOLO训练需要一个YAML文件描述数据集路径和类别名。以我的目录结构为例# dataset.yaml path: /absolute/path/to/dataset train: images/train val: images/val test: images/test names: 0: crack 1: spalling 2: efflorescence 3: water_stain 4: hollow启动训练yolo detect train datadataset.yaml modelyolov8s.pt epochs200 imgsz640 batch16 patience50 device0如果担心类别不均衡可以在ultralytics框架的配置里加class_weights参数或者前置处理时对样本少的类别做过采样复制。实际操作中除非某些类别占比极低我更倾向于先不加权重跑一轮看效果再决定。4.3 关键超参数与踩坑建议imgsz是最值得调的超参数。墙面裂缝和早期渗水痕迹都很小640分辨率下可能只占几十个像素特征非常弱。我实际测试下来把imgsz从640提高到896或1024小目标召回率往往有明显提升代价是训练和推理时间增加。如果部署设备性能有限可以在推理阶段用SAHI这类切片推理工具把大图切成小块分别检测再拼回结果效果接近高分辨率直接推理但对设备更友好。epochs不建议照搬默认的100。墙面病害样本类内差异大100轮往往没收敛充分我给到200轮并配合早停patience50如果连续50轮验证集指标没有提升就自动停止。数据增强方面默认的Mosaic增强对墙面场景基本有效但不宜把增强强度调得过高。墙面病害的背景主要是混凝土、涂料和纹理表面过度改变色调会让模型学到错误的色彩关联真实场景不同光照下一测就露馅。翻转增强可以开但墙面上的渗水痕迹有方向性吗其实大多数病害不具备严格的方向语义做水平翻转问题不大。垂直翻转建议谨慎因为无人机巡检图像通常有固定的上概念。训练过程中建议开启plotsTrue这样每轮结束后会自动保存预测结果可视化图方便随时检查模型在验证集上的表现。我观察训练曲线时除了看loss下降还会重点看验证集PR曲线里各类别是否有明显落后。哪一类AP明显偏低就去检查那一类的标注和样本量。4.4 评估指标解读目标检测常用指标是mAP50和mAP50-95。mAP50是IoU阈值取0.5时的平均精度对框位置要求相对宽松mAP50-95是在0.5到0.95多个IoU阈值上的平均精度更严格对框的定位精度更敏感。墙面缺陷场景里裂缝这类细长目标的框天然包含大量背景与GT框的IoU很容易偏低mAP50-95往往不好看。这时候应该以mAP50和可视化为主要评估依据不要因为mAP50-95偏低就急着推翻模型。真正有效的优化手段是前面说的提高输入分辨率、使用切片推理以及保证标注框尽可能贴合真实缺陷。还要看每个类别的单独AP值而不是只盯总mAP。空鼓和泛碱样本少AP可能明显低于裂缝和渗水这是典型的类别不均衡现象后面单独讲。5. 常见问题与排错实录5.1 训练阶段的高频报错下面这张表是训练过程中实际碰到最多的问题和解决思路问题现象可能原因排查与解决启动训练时报错 class out of rangelabels里的类别索引超出names列表长度检查CLASS_MAP和dataset.yaml的names顺序是否严格一致训练日志提示图像没有标签被跳过labels目录下缺少同名txt文件检查images和labels目录对应关系空标注文件也要保留验证集mAP很高但实测效果差数据集划分没有按建筑分组造成数据泄漏按楼栋或墙体分组划分train/val/test损失下降但mAP一直不动标注框坐标归一化错误或数据增强强度过大可视化标签框检查坐标范围调低增强参数某些样本训练时loss突刺个别XML标注框越界或类别名拼写错误跑一遍转换脚本的统计日志逐类核对5.2 检测效果不佳的排查思路模型训练完了如果效果不理想不要立刻调模型按这个顺序排查第一步先看训练日志里的混淆矩阵。如果裂缝被大量误检成渗水大概率是标注时主次类别定义不清样本本身长得很像。第二步随机挑验证集图像的可视化预测结果观察漏检和误检都集中在什么场景。第三步对比训练集和测试集的图像风格差异。如果测试集是无人机拍的远距离画面训练集里却主要是手持近拍图那尺度差异就是最核心的问题此时优先做多尺度增强而不是换模型。一个很经典的案例早期模型在室内走廊墙面检测效果很好一到户外建筑外墙就疯狂漏检。原因是训练数据里户外图太少增强策略又偏爱亮度变化模型始终没有真正见过外墙纹理。后来从原始库里补充了约1200张户外墙面图mAP立刻提升了近4个百分点。数据和场景匹配度比模型结构重要得多。5.3 类别不均衡与缺陷目标细长的处理墙面缺陷数据集有个天然问题裂缝和渗水样本多空鼓和剥落样本少。空鼓表面特征弱往往要靠敲击声音判断肉眼可见的程度差异大采集和标注都很困难。7374张图里各类别数量可能差异到两三倍。处理上我建议不追求数量上的绝对均衡但要保证每个类别不少于500个标注实例样本量少的类别训练前用它们多生成一些裁剪增强样本而不是简单复制整张图每个epoch打乱时对少样本类别做加权采样。裂缝类虽然样本充足但检测精度受限于其细长形态和水平框的先天不足。除了提高输入分辨率还有一种思路是直接用分割模型如YOLOv8-seg输出缺陷掩膜再用掩膜外接矩形作为检测结果。如果项目允许适当增加推理成本分割路线在细长缺陷上通常比纯检测框效果更好。5.4 从数据集扩展到完整交付当模型在验证集上的效果稳定后实际落地环节还有几个容易被忽略的点。推理结果需要做后处理如去除低置信度碎片框、合并重叠框检测出来的裂缝和渗水区域要反算回原图坐标才能在地图上标注位置。如果配合无人机巡检还需要加入飞行路径和图像拼接的逻辑把每张图的检测框坐标映射到楼栋的整体平面图上。输出交付物建议包含训练好的权重文件、每类的PR曲线和混淆矩阵、数据集的详细标注统计表。这样后续接手的人能快速判断模型边界在哪里不至于拿着模型盲目套到全新场景里。最后再分享一点个人体会。这类缺陷检测项目做到后面瓶颈几乎都出在数据和标注一致性上而不是模型算法上。我曾经因为一批数据标注标准不一致有的人按可见裂缝标、有的人按潜在裂缝区域标重标后mAP直接提升超过10个百分点。所以如果你刚准备开始做墙面缺陷检测我建议前两周老老实实把标注规范定死、把格式转换脚本做健壮后面训练和调参会顺利很多这也算是这套流程里最值钱的经验了。