简介面向建筑视觉检测与图像分割方向研究者、算法工程师及高校学生的建筑墙壁损伤缺陷分割数据集以labelme标注格式提供可用于训练和评估裂缝、剥落、锈蚀等损伤的自动识别模型服务于建筑健康监测与智能巡检场景。资源包共1个docx文件约2.1MB内含数据集说明与使用指引7820张640×640像素jpg图片均配有同名json标注文件采用多边形框精细描绘损伤区域覆盖涂鸦、支座、潮湿斑迹、锈蚀、剥落、泛碱、空洞区域、风化、残留模板、空腔、外露钢筋、伸缩缝、裂缝、排水、活动裂缝、接头胶带、岩窝、防护设备、水侵蚀混凝土及物体共20个类别其中锈蚀、剥落、空腔等类别标注量较大。已有103人学习关注。读者可据此了解数据组织方式与类别分布借助labelme 5.5.0查看编辑标注并自行转换为mask、YOLO或COCO格式用于语义分割或实例分割实验为建筑损伤检测研究提供准确合理的图像基础。1. 建筑墙壁损伤分割数据集7820 张 labelme 标注到底能撑起什么样的毕设做建筑缺陷检测方向的同学十有八九卡在第一步数据从哪来。自己扛着相机去拍拍回来还要一张张画多边形7820 张图光标注就能耗掉一整个学期。这份建筑墙壁损伤缺陷分割数据集正好卡在这个痛点上——7820 张 640×640 的 jpg配 7820 个 labelme 5.5.0 的 json20 个损伤类别全部是多边形 polygon 标注。它适合三类人做语义分割/实例分割毕设的学生、要给建筑巡检做算法原型的从业者、以及需要一份结构完整的分割数据集方案做课程设计的读者。下面按「需求怎么立、数据怎么摸清、格式怎么转、模型怎么训、最后怎么验收」这条线把整套方案拆开讲。2. 需求与数据摸底20 类损伤的分布决定了你的方案上限拿到一份数据集最忌讳上来就train.py一把梭。血泪经验是先花半天把类别分布、标注质量、长尾情况摸清楚后面能省掉一周的返工。这一章先把「这份数据能做什么、不能做什么」讲透再谈模型。2.1 从建筑巡检场景反推任务定义建筑外墙和结构面的病害巡检人工方式是搭脚手架或者无人机拍完人工看图效率低且漏检率高。自动化方案的核心任务就是像素级分割给定一张墙面图输出每个像素属于哪类损伤。这份数据集的 20 个类别恰好覆盖了巡检报告里最常见的病害项——Crack裂缝、Spalling剥落、Rust锈蚀、Efflorescence泛碱、ExposedRebars外露钢筋、Hollowareas空洞区域这些是结构安全相关的重点Graffiti涂鸦、Drainage排水、PEquipment防护设备则偏外观与附属设施。任务定义上要先做一个决策走语义分割还是实例分割。语义分割只关心「这个像素是不是裂缝」实例分割还要区分「这是第几条裂缝」。从标注形式看labelme 的 polygon 天然支持实例级因为每个 shape 是独立对象但从工程落地看巡检报告通常只需要「有没有、在哪、多大面积」语义分割足够且训练成本低一半。我一般建议毕设走语义分割把实例分割作为加分项在答辩时提一句「数据支持扩展」。提示object类只有 13 个标注基本是标注过程中的杂项兜底类训练时建议直接过滤掉否则会引入噪声梯度。2.2 类别分布的长尾问题必须先量化把每个类别的标注数拉出来看问题立刻暴露类别标注数占比定位Rust 锈蚀14665头部Spalling 剥落9849头部Cavity 空腔9222头部Weathering 风化4621中段Efflorescence 泛碱3956中段Crack 裂缝3612中段Graffiti 涂鸦2197中段ExposedRebars 外露钢筋1993中段PEquipment 防护设备1892中段Wetspot 潮湿点1657中段Drainage 排水1649尾部Hollowareas 空洞区域1538尾部Bearing 支座1201尾部JTape 接头胶带1076尾部Restformwork 残留模板1019尾部Spalling 之外的 Cavity 已列——Rockpocket 岩窝616长尾EJoint 伸缩缝506长尾ACrack 活动裂缝423长尾WConccor 水侵蚀混凝土407长尾object 物体13噪声头部 Rust 是尾部 ACrack 的 34 倍这个比例意味着如果直接用交叉熵训练模型会倾向于把所有像素预测成 Rust 和背景尾部类别的 IoU 会趋近于 0。方案上必须提前设计应对——要么在损失函数里加权要么在采样时做类别平衡要么在评估时单独报告尾部类别。这一步不做后面调参全是玄学。2.3 标注质量抽检与清洗规则labelme 5.5.0 的 json 结构里每个 shape 有label、points、shape_type。抽检时重点看三件事一是shape_type是否全是polygon如果有rectangle混入转 mask 时会出问题二是 points 是否闭合合理少于 3 个点的多边形要丢弃三是同一区域是否被重复标注成两个类别比如裂缝和活动裂缝 ACrack 容易混。清洗脚本可以这样写跑一遍全量 json输出问题清单import json, os, glob def audit_labelme(json_dir): issues [] for fp in glob.glob(os.path.join(json_dir, *.json)): with open(fp, r, encodingutf-8) as f: data json.load(f) for i, shape in enumerate(data.get(shapes, [])): pts shape.get(points, []) # 规则1多边形至少3个点 if shape.get(shape_type) ! polygon or len(pts) 3: issues.append((fp, i, bad_shape, shape.get(label))) # 规则2坐标越界检查图片640x640 for x, y in pts: if not (0 x 640 and 0 y 640): issues.append((fp, i, out_of_bound, shape.get(label))) break return issues if __name__ __main__: problems audit_labelme(./jsons) print(f问题标注数: {len(problems)}) for p in problems[:20]: print(p)这段脚本做的是「体检」而不是「治疗」bad_shape说明该 shape 无法转成有效 mask训练前要剔除out_of_bound说明坐标超出 640×640可能是标注时图片被缩放导致的需要人工复核。跑完拿到问题清单再决定是批量修复还是直接丢弃对应图片。通常 7820 张里问题样本在几十张量级丢弃对总量影响可忽略。3. 格式转换方案从 labelme json 到 mask / YOLO / COCO 的三条路径数据集只给 jpg json不含 mask这是最容易被低估的工作量。转换做错后面训练全白搭。这一章把三条主流路径讲清楚并给出可直接复用的转换代码。3.1 语义分割路径json 转单通道 mask语义分割需要的是每张图对应一张 mask像素值等于类别 id。转换核心是用PIL.ImageDraw.polygon把每个 shape 填充到对应类别 id 上。注意两个坑一是填充顺序后填的会覆盖先填的重叠区域要以「更具体的类别优先」为原则排序二是背景设为 0类别 id 从 1 开始避免和背景混淆。import json, os import numpy as np from PIL import Image, ImageDraw # 20类映射背景0object类不参与训练可跳过 CLASS_MAP { Graffiti: 1, Bearing: 2, Wetspot: 3, Rust: 4, Spalling: 5, Efflorescence: 6, Hollowareas: 7, Weathering: 8, Restformwork: 9, Cavity: 10, ExposedRebars: 11, EJoint: 12, Crack: 13, Drainage: 14, ACrack: 15, JTape: 16, Rockpocket: 17, PEquipment: 18, WConccor: 19, # object 不映射视为背景 } def json_to_mask(json_path, out_path, size(640, 640)): with open(json_path, r, encodingutf-8) as f: data json.load(f) mask Image.new(L, size, 0) # 0 背景 draw ImageDraw.Draw(mask) # 按类别id升序填充保证重叠时高id覆盖低id可按业务调整 shapes sorted(data[shapes], keylambda s: CLASS_MAP.get(s[label], 0)) for shape in shapes: cid CLASS_MAP.get(shape[label]) if cid is None: continue pts [tuple(p) for p in shape[points]] if len(pts) 3: draw.polygon(pts, fillcid) mask.save(out_path) if __name__ __main__: os.makedirs(./masks, exist_okTrue) for fp in os.listdir(./jsons): if fp.endswith(.json): json_to_mask(f./jsons/{fp}, f./masks/{fp.replace(.json, .png)})CLASS_MAP是整条流水线的「字典」训练、评估、可视化都必须引用同一份建议单独存成classes.json避免多处硬编码不一致。size固定 640×640 与原始图片一致不要在这里做缩放缩放留给训练时的 data augmentation。填充顺序按类别 id 排序是一种简化策略如果业务上「裂缝」应该覆盖「风化」就把 Crack 的 id 调大或者单独写优先级字段。3.2 实例分割路径转 COCO 格式如果要做实例分割COCO 是事实标准。转换要点每个 shape 变成一个 annotationsegmentation存多边形点列表bbox由多边形外接矩形算出category_id与 CLASS_MAP 对齐。COCO 的iscrowd设为 0area用多边形面积。import json, os from shapely.geometry import Polygon def build_coco(json_dir, out_file): coco {images: [], annotations: [], categories: []} for name, cid in CLASS_MAP.items(): coco[categories].append({id: cid, name: name, supercategory: defect}) ann_id 1 for img_id, fp in enumerate(sorted(os.listdir(json_dir)), start1): if not fp.endswith(.json): continue with open(os.path.join(json_dir, fp), r, encodingutf-8) as f: data json.load(f) coco[images].append({ id: img_id, file_name: fp.replace(.json, .jpg), width: 640, height: 640 }) for shape in data[shapes]: cid CLASS_MAP.get(shape[label]) pts shape[points] if cid is None or len(pts) 3: continue poly Polygon(pts) if not poly.is_valid: poly poly.buffer(0) # 自相交多边形修复 xs [p[0] for p in pts]; ys [p[1] for p in pts] coco[annotations].append({ id: ann_id, image_id: img_id, category_id: cid, segmentation: [[c for p in pts for c in p]], bbox: [min(xs), min(ys), max(xs)-min(xs), max(ys)-min(ys)], area: poly.area, iscrowd: 0 }) ann_id 1 with open(out_file, w, encodingutf-8) as f: json.dump(coco, f, ensure_asciiFalse)poly.buffer(0)是处理自相交多边形的常用手段labelme 手画的多边形偶尔会出现「8 字形」直接算面积会报错或得到负值。segmentation的扁平化写法[x1,y1,x2,y2,...]是 COCO 对单多边形的标准格式别写成嵌套列表。3.3 YOLO 路径与三条路径的取舍YOLO 分割格式YOLOv8-seg要求每张图一个 txt每行是class_id x1 y1 x2 y2 ...坐标归一化到 0~1。转换逻辑与 COCO 类似只是输出形态不同。三条路径怎么选给一张对照表路径适用任务训练框架转换成本毕设推荐度mask 单通道语义分割U-Net / DeepLab / SegFormer低高最稳COCO实例分割Mask R-CNN / YOLOv8-seg中中加分项YOLO txt实例分割YOLOv8-seg / YOLOv11-seg中中部署友好我的建议是主线走 mask 语义分割保证能出结果如果时间充裕再补一条 COCO 做实例分割对比。两条线共用同一份 CLASS_MAP评估指标口径一致答辩时能讲出「同一数据两种任务范式」的深度。4. 训练方案设计类别不平衡下的分割模型落地数据准备好了接下来是模型。这一章不讲模型原理讲的是「在这份 20 类、长尾严重、640×640 的数据上怎么配出一套能跑出合理结果的训练方案」。4.1 基线模型选型与输入尺寸策略640×640 的输入对分割模型是友好的——不算大单卡 8G 显存能跑 batch size 8 左右。基线我一般选两个U-NetResNet34 或 EfficientNet-B0 编码器作为「能跑通、好解释」的保底方案SegFormer-B0 作为「指标更好看」的进阶方案。两者都在 640 输入下工作良好不需要额外 resize。为什么不建议一上来就上大模型7820 张图对分割任务属于中等偏小规模大模型如 SegFormer-B5容易过拟合且训练时间长毕设周期内调参空间被压缩。先用 B0 跑出 baseline IoU再决定要不要升级这是更稳的节奏。数据划分建议 8:1:1即训练 6256、验证 782、测试 782。划分时按图片随机分但要检查每个类别的标注在三个子集里的分布是否大致一致——尾部类别ACrack 423 个标注如果测试集里一个都没有测试指标就没意义。可以写个脚本统计划分后各类别标注数偏差超过 20% 就重新分。4.2 损失函数与类别加权的具体配置针对长尾方案是「交叉熵 Dice」组合损失并对交叉熵做类别加权。权重按1 / sqrt(freq)计算比1/freq温和避免尾部类别权重爆炸导致训练不稳定。import numpy as np import torch import torch.nn as nn # 各类别标注数来自数据集统计object已排除 counts np.array([14665, 9849, 9222, 4621, 3956, 3612, 2197, 1993, 1892, 1657, 1649, 1538, 1201, 1076, 1019, 616, 506, 423, 407], dtypenp.float32) # 1/sqrt 加权再归一化到均值1 weights 1.0 / np.sqrt(counts) weights weights / weights.mean() class_weights torch.tensor(weights, dtypetorch.float32) ce_loss nn.CrossEntropyLoss(weightclass_weights, ignore_index255) def dice_loss(pred, target, smooth1.0): # pred: [B, C, H, W] logits; target: [B, H, W] long pred torch.softmax(pred, dim1) target_onehot torch.nn.functional.one_hot(target, pred.shape[1]).permute(0, 3, 1, 2).float() inter (pred * target_onehot).sum(dim(2, 3)) union pred.sum(dim(2, 3)) target_onehot.sum(dim(2, 3)) dice (2 * inter smooth) / (union smooth) return 1 - dice.mean() def total_loss(pred, target): return ce_loss(pred, target) dice_loss(pred, target)ignore_index255是为边界像素预留的——如果后续做边界忽略训练把边界像素标成 255 即可。weights归一化到均值 1 是为了让学习率不用跟着权重整体缩放重新调。Dice 损失负责拉高重叠度CE 负责像素级分类两者相加是分割任务里最稳的组合之一。4.3 训练配置与显存控制优化器用 AdamWlr 初始 1e-4weight decay 1e-4cosine 退火到 1e-6训练 60~80 epoch。batch size 8配合 AMP 混合精度8G 显存能稳住。数据增强用水平翻转、随机裁剪裁到 512 再 resize 回 640、亮度对比度扰动、轻微旋转±10°。注意建筑病害有方向语义比如裂缝走向旋转幅度不要太大否则会破坏「竖向裂缝」这类先验。验证指标用 mIoU 和每类 IoU每 5 个 epoch 存一次 checkpoint保留验证 mIoU 最高的那个。训练日志里单独记录尾部 5 类Rockpocket、EJoint、ACrack、WConccor、Restformwork的 IoU这是判断加权策略是否生效的关键信号。5. 避坑与排查这份数据集最容易翻车的五个点这一章是踩坑记录每条按「现象 → 原因 → 解决」写都是这类 labelme 数据集转分割任务时高频出现的问题。5.1 转出来的 mask 全是背景现象训练 loss 不降预测图全黑mIoU 接近 0。原因json 里shapes的label字段与 CLASS_MAP 的 key 大小写或空格不一致get全部返回 None所有 shape 被跳过。解决转换前先跑一遍set(shape[label] for ...)打印实际标签集合与 CLASS_MAP 做差集比对。这份数据集的标签是驼峰命名如ExposedRebars、WConccor手敲映射表极易写错建议直接从 json 里程序化提取标签生成映射表初稿。5.2 训练到一半 loss 变 NaN现象前 10 个 epoch 正常之后 loss 突然 NaN。原因Dice 损失在某个 batch 里某类完全没出现union为 0虽然加了 smooth 但混合精度下仍可能数值溢出或者学习率偏高导致梯度爆炸。解决Dice 的 smooth 从 1.0 提到 1e-5 量级不够建议保持 1.0 但把 AMP 的 GradScaler 初始 scale 调低同时把 lr 降到 5e-5 重跑。另一个排查点是检查 mask 里是否有超出类别数的像素值比如转换脚本 bug 写入了 20 以上的 id用np.unique(mask)抽查几张。5.3 验证集指标虚高但测试集崩盘现象验证 mIoU 0.6换测试集掉到 0.3。原因划分时没做分层验证集恰好集中了头部类别Rust、Spalling尾部类别几乎没进验证集。解决按类别做分层划分保证每个类别在验证/测试集里至少有 10% 的标注量尾部类别样本太少时用「按图片分组、组内含尾部类别则整组进验证」的策略。划分完必须打印三个子集的类别分布表核对。5.4 可视化时颜色对不上类别现象预测图和 mask 叠在一起颜色和类别图例完全错位。原因可视化脚本用的调色板顺序与 CLASS_MAP 的 id 顺序不一致或者 mask 存的是 0~19 而训练用的是 1~20。解决把「类别名 → id → RGB」做成唯一一份配置训练、转换、可视化全部 import 同一份禁止各写各的。这类问题不报错但会让人在答辩现场被问住。5.5 显存够但训练速度异常慢现象batch size 8 能跑但每个 epoch 要两三个小时。原因数据加载是瓶颈——每张图实时读 json 转 maskIO 和 CPU 解码占满。解决转换阶段就把 mask 落盘成 png训练时直接读 pngDataLoader 的num_workers设到 CPU 核数的 70% 左右pin_memoryTruepersistent_workersTrue。这一步做完同样配置下 epoch 时间通常能砍掉一半以上。6. 验收与答辩用一张指标表把方案讲圆最后落到验收。毕设或交付方案的验收不是「模型跑起来了」就算完而是要有一套可复现、可解释的评估口径。我一般会准备一张「总表 分表」总表给 mIoU、像素准确率、各类平均 Dice分表按头部/中段/尾部三档分别报告 IoU并附上混淆矩阵里最容易混的三对类别。评估脚本的核心是逐类累计混淆矩阵最后一次性算 IoU避免逐 batch 平均带来的偏差import numpy as np def compute_iou(conf_matrix): # conf_matrix: [C, C]行是真值列是预测 ious {} for c in range(conf_matrix.shape[0]): tp conf_matrix[c, c] fp conf_matrix[:, c].sum() - tp fn conf_matrix[c, :].sum() - tp ious[c] tp / (tp fp fn 1e-6) miou np.mean(list(ious.values())) return miou, ious答辩时怎么讲这份方案给几个具体建议。第一开场不要讲模型先讲「建筑巡检人工成本高、漏检率高」这个需求再用类别分布表说明「这份数据的难点在长尾」让评委先认可问题定义。第二讲转换流水线时把「json → mask → 训练 → 评估」画成一条线强调 CLASS_MAP 单一来源这个工程细节这是区分「跑通」和「做对」的地方。第三指标汇报一定要带尾部类别主动说「ACrack 只有 423 个标注IoU 0.2 是数据量限制而非模型问题」比被问出来强。第四准备一个失败案例的可视化——比如某张图把 Wetspot 误判成 Rust分析原因是两者颜色接近、标注边界模糊这体现的是分析能力。验收标准可以量化成这样整体 mIoU ≥ 0.4520 类长尾数据下这是合理线头部 5 类 IoU ≥ 0.6尾部 5 类 IoU ≥ 0.15推理单张 640×640 在单卡上 ≤ 50ms。达不到就回到第 4 章调损失权重和增强策略而不是换模型。从那以后我每次接分割数据集的项目都强制先跑一遍类别分布统计和标注抽检再动一行训练代码——这份 7820 张、20 类的数据集尤其如此长尾和格式转换这两关过了后面的模型调参才有意义。希望帮到你。本文还有配套的精品资源点击获取