墙壁缺陷分割实战:Labelme数据集转换与模型训练全流程
发布时间:2026/9/29 2:00:04来源:尧图网络
简介建筑墙壁损伤缺陷分割数据集说明文档面向计算机视觉与建筑检测领域的研究者、算法工程师及相关专业学生帮助快速了解数据集构成为训练和评估图像分割算法提供参考。内容覆盖数据集全貌共7820张640×640的jpg图片及对应labelme json标注文件包含20种墙壁损伤类别如裂缝、剥落、泛碱、锈蚀、水侵蚀混凝土等并列出各类别的标注框数量分布。同时说明LabelMe 5.5.0使用、多边形标注规则以及可自行转换为mask、YOLO或COCO格式用于语义/实例分割的注意事项还附有图片预览与标注示例便于直观理解。资源包内含1个docx说明文件大小约2.1MB已有101人学习。借助这份说明可快速评估该数据集是否适配自身分割任务据此规划标注转换与模型训练流程节省前期调研时间。1. 建筑墙壁损伤缺陷分割数据集7820张Labelme标注到底能做什么建筑墙壁损伤缺陷分割数据集Labelme格式7820张20个类别不是单纯的图片包它把墙面裂缝、剥落、空鼓、渗水、霉变这类缺陷从人工目检搬到模型自动分割的训练原料。做建筑检测算法的人最常卡在标注上没有标好掩码分割模型就是空中楼阁有了这一批现成的多类别Labelme标注可以直接走标注JSON到掩码再到训练的完整链路省下几千张的人工标注时间。下面按工程顺序讲先拆Labelme格式讲清楚怎么把它转成可训练的数据、怎么选分割模型以及我在训练这类小目标缺陷时踩过的坑。这套流程对正在做建筑缺陷检测或语义分割落地的工程师可以直接抄对想快速搭一套墙面缺陷分割流程的研发同学也可以按步骤跑通。2. Labelme格式拆解与批量转换JSON怎么变成训练用的掩码图2.1 Labelme的JSON结构shapes、points、imageData里存了什么Labelme标注一个“裂缝”多边形保存下来的是一个JSON文件而不是一张图。这个JSON的核心字段包括imagePath原图文件名注意它往往只存文件名不存绝对路径imageDataBase64编码的原图数据可能在打标签时被写进去也可能为空写上时JSON会非常大所以很多版本默认不写imageHeight / imageWidth标注时的图片尺寸这是转换掩码时最该信任的尺寸shapes标注对象数组每个元素有label类别名、points多边形顶点坐标列表、shape_typepolygon或rectangle。以墙面裂缝为例标注的一个shapes元素是这样的{ label: crack, points: [[1023, 2046], [1024, 2060], [1022, 2071]], shape_type: polygon, group_id: null }这里的points是像素坐标顺序是(x, y)单位是像素。转换掩码时这句坐标就是cv2.fillPoly的直接输入。建议你拿到数据集后先随机打开两个JSON直接打印出shapes里的label集合确认20个类名字和你预想的一致再做批量转换。因为Labelme本身不做类名校验内容完全靠标注人员手打很容易出现“crack”和“Crack”两种写法后面训练时都会被当成未知类跳过。2.2 从JSON批量转成PNG掩码转换脚本与关键参数常见做法是写一个Python脚本把JSON批量变成单通道的PNG掩码。下面是我常用的脚本骨架重点在类别映射和填充方式。import json import os import cv2 import numpy as np # 类别映射表按数据集实际的20个类别填写 CLASS_MAPPING { background: 0, crack: 1, spalling: 2, hollow_drum: 3, water_stain: 4, mold: 5, # ... 其余14个类别按数据集说明补全 } def convert_labelme_json(json_path, mask_dir): 把单个labelme json转成同名png掩码 with open(json_path, r, encodingutf-8) as f: data json.load(f) h data[imageHeight] w data[imageWidth] mask np.zeros((h, w), dtypenp.uint8) for shape in data[shapes]: label shape.get(label, ) class_id CLASS_MAPPING.get(label, 0) if class_id 0 and label ! background: print(f[warning] unknown label: {label} in {json_path}) continue points np.array(shape[points], dtypenp.int32) if shape.get(shape_type) rectangle: # rectangle只有两个对角点转成四边形的四个点再填充 x1, y1 points[0] x2, y2 points[1] points np.array([[x1, y1], [x2, y1], [x2, y2], [x1, y2]], dtypenp.int32) cv2.fillPoly(mask, [points], class_id) out_name os.path.splitext(os.path.basename(json_path))[0] .png cv2.imwrite(os.path.join(mask_dir, out_name), mask) # 批量入口 json_dir labelme_jsons mask_dir masks os.makedirs(mask_dir, exist_okTrue) for file in os.listdir(json_dir): if file.endswith(.json): convert_labelme_json(os.path.join(json_dir, file), mask_dir)逻辑说明先用imageHeight和imageWidth建一张uint8的空白掩码背景默认是0。循环shapes时按label查类别ID然后用fillPoly把多边形内部填充成对应的类别ID。rectangle类型要手动补齐四个顶点因为fillPoly只接受面。参数说明CLASS_MAPPING里的数值不是随意定的。语义分割训练时交叉熵损失会把类别ID当作索引所以ID要从0开始连续递增不要跳号。比如背景0、裂缝1、剥落2中间不要直接跳到5否则类别数和你模型输出通道数会对不上。如果数据集自带class_names.txt优先照它的顺序建映射。逐个文件处理的速度其实很快7820张大概两三分钟就能跑完瓶颈主要在磁盘IO而不是脚本本身。2.3 20类缺陷的像素值映射类别编码为什么不能乱跳转换完成后的掩码是单通道图片像素值就是类别ID。一般数据集会提供一份20类的类别顺序常见的是把背景和缺陷放在一起编号。我建议你建一张映射表CSV或JSON把类别名字、ID、中文含义、标注颜色都写进去给训练和可视化共用一份。类别ID英文标签示例中文含义可视化颜色0background背景/完好墙面黑色1crack裂缝红色2spalling表层剥落绿色3hollow_drum空鼓蓝色4water_stain渗水水渍黄色5mold霉变紫色这里我强调一个容易翻车的地方Labelme的label字段是字符串图像掩码里不能直接存字符串所以必须做编码映射。如果20个类别里某些是“背景-小石子”“背景-栏杆”这类干扰物也要单独编号不要全部塞进0。否则模型学到的边界是糊的现场换一面墙就漏检。我在项目里吃过这个亏一开始把“墙面其他附着物”全标成0裂缝和附着物贴在一起时模型输出就很脏后来单独给附着物建了一个类效果立刻好了。提示类别ID一旦确定就不要中途改。训练到一半如果调整映射旧模型的输出通道和新数据对不上之前所有epoch都白跑。2.4 掩码与原图叠加检查一张图看转换是否成功批量转换完不要直接开训练。先抽20到50张做叠加检查我一般把掩码按调色板着色后半透明叠到原图上肉眼确认每个缺陷的轮廓和原图对得上。import numpy as np import cv2 def make_colormap(num_classes21): # 生成一组可区分的颜色避免手写20行调色板 palette np.zeros((num_classes, 3), dtypenp.uint8) for i in range(1, num_classes): palette[i] np.array([ (i * 73) % 256, (i * 151) % 256, (i * 197) % 256 ]) return palette COLOR_MAP make_colormap(21) # 第0类是背景保持黑色 def visualize_mask(image_path, mask_path, out_path): img cv2.imread(image_path) mask cv2.imread(mask_path, cv2.IMREAD_GRAYSCALE) colored COLOR_MAP[mask] # 按像素值查表上色 overlay cv2.addWeighted(img, 0.6, colored, 0.4, 0) cv2.imwrite(out_path, overlay)这段代码把掩码像素值当作COLOR_MAP的下标直接查出RGB颜色然后用addWeighted做透明叠加。检查时重点看三点墙面上裂缝是否贴合、有没有多标了背景、小块的剥落区域在掩码里是不是连成了一片。如果某一张图上缺陷整体偏移多半是尺寸不一致问题回到2.2去校验imageHeight和imageWidth。如果只是局部缺口通常是原标注里多边形自相交或点数过少这类图我会单独挑出来重新编辑JSON再转换。3. 墙壁分割模型选型与训练跑通从U-Net到DeepLabV33.1 模型选型逻辑裂缝、空鼓、水渍各自适合什么网络有了掩码图下一步是选分割模型。建筑墙面缺陷和公开Cityscapes场景不太一样有三个明显特点一是裂缝细长可能只有1到3个像素宽但它在缺陷类型里又特别重要二是背景面积占比极高正常墙面占了画面80%以上三是墙面颜色单一易受光照和阴影干扰。常见做法是优先用U-Net这类编码器-解码器结构跑基线原因是结构简单、对小目标相对友好而且显存占用可控。如果对精度有更高要求再换DeepLabV3它靠空洞卷积把感受野撑大对大范围的空鼓和渗水区域分割更完整。SegFormer这类Transformer模型在墙面场景里也能跑但训练周期长数据只有7820张时收益不一定比DeepLabV3明显。这里多说一句为什么用语义分割而不是实例分割。Labelme格式既可以做语义分割也能转成实例分割但20类缺陷大多是重叠或邻接的比如裂缝穿过剥落区域实例分割要求把每个对象单独编号标注量和工作量都翻倍。而墙面缺陷检测的核心是“哪里坏了、什么病”语义分割已经能回答这两个问题。所以一般落地项目都按语义分割处理。我一般这样选型第一次训练用ResNet-50做骨架的U-Net跑通流程先看baseline mIoU如果背景和缺陷边界糊换DeepLabV3并把输出步长设为16如果需要部署到GPU先把输入缩到512x512再考虑轻量骨干如MobileNetV3。用一张表说清楚模型骨干适合场景显存约需512输入U-NetResNet-50baseline、小裂缝6-8GDeepLabV3ResNet-50空鼓/渗水大区域8-10GDeepLabV3MobileNetV3边缘部署4-6GSegFormerMiT-B2需要强语义边界8-10G注意这里显存按batch size8估算。实际墙拍图往往是4K甚至更高不裁剪直接输入会撑爆显存所以训练时都要先Resize再RandomCrop。512x512属于保底1024x1024对裂缝更友好。3.2 用MMSegmentation跑通训练的最小配置训练我用的是MMSegmentation因为它把数据加载、增强、评估和分布式训练都封装好了省去很多自己写DataLoader的功夫。先准备目录结构data/wall_defect/ ├── img_dir/ │ ├── train/ │ └── val/ ├── ann_dir/ │ ├── train/ │ └── val/对应MMSeg的CustomDataset要求图片放在img_dir掩码放在ann_dir两边文件名一致。用Labelme转出来的掩码是PNG文件名和原图同名正好符合。配置文件的训练部分我用的是Python形式的config关键片段如下dataset_type CustomDataset data_root data/wall_defect/ img_norm_cfg dict( mean[123.675, 116.28, 103.53], std[58.395, 57.12, 57.375], to_rgbTrue) train_pipeline [ dict(typeLoadImageFromFile), dict(typeLoadAnnotations), dict(typeResize, img_scale(1024, 1024), ratio_range(0.5, 2.0)), dict(typeRandomCrop, crop_size(512, 512)), dict(typeRandomFlip, prob0.5), dict(typeNormalize, **img_norm_cfg), dict(typeDefaultFormatBundle), dict(typeCollect, keys[img, gt_semantic_seg]), ] test_pipeline [ dict(typeLoadImageFromFile), dict(typeLoadAnnotations), dict(typeResize, img_scale(1024, 1024), keep_ratioFalse), dict(typeNormalize, **img_norm_cfg), dict(typeDefaultFormatBundle), dict(typeCollect, keys[img, gt_semantic_seg]), ] data dict( traindict( typedataset_type, data_rootdata_root, img_dirimg_dir/train, ann_dirann_dir/train, pipelinetrain_pipeline), valdict( typedataset_type, data_rootdata_root, img_dirimg_dir/val, ann_dirann_dir/val, pipelinetest_pipeline))这里img_scale设成1024是为了保住裂缝细节但显存不够时可以连同RandomCrop的crop_size一起降到512。ratio_range里的0.5到2.0表示随机缩放范围缩得太小会丢失细裂缝建议下限不低于0.5。Normalize的mean和std是ImageNet统计值不是必须换用自己的均值影响也不大。除了数据pipelinedecode_head里还有一个关键参数是num_classes必须严格等于类别数包括背景。20类就填20填错会在训练到评估阶段突然报shape mismatch排错很耗时。优化器方面我用SGD momentum0.9加poly学习率衰减初始lr设0.01如果换AdamWlr降到1e-4。batch size看显存单卡能塞下8就8不要硬上16然后梯度爆炸。启动训练命令python tools/train.py \ configs/wall_defect/deeplabv3plus_r50.py \ --work-dir work_dirs/wall_defect单卡用这个即可多卡可以加--launcher pytorch配合torch.distributed.launch使用nproc_per_node按卡数设。跑起来后盯着loss和mIoU两个量training loss前20轮正常会从高位大幅下降如果一直停在3.0以上不降先检查掩码类别数是否正确、是不是大面积背景被当成错误类别。3.3 数据增强墙面缺陷的尺度多样性和光照变化分割模型过拟合在7820张的数据集里很常见尤其墙面背景纹理少。我在增强上重点做三件事。第一多尺度缩放。裂缝宽度在现场会因为拍摄距离变化而变化训练时让模型看到0.8倍、1.2倍、1.5倍的同类裂缝比固定单一尺度鲁棒。MMSeg里Resize的ratio_range就是在做这个。第二颜色抖动。室内灯光偏黄、室外阴影发蓝墙面又是大面积纯色颜色抖动能防止模型把“颜色”当“缺陷”。我用albumentations写增强时一般是这样的组合import albumentations as A train_aug A.Compose([ A.RandomScale(scale_limit(0.5, 2.0)), A.RandomCrop(width512, height512), A.ColorJitter(brightness0.3, contrast0.3, saturation0.3), A.RandomBrightnessContrast(), A.HorizontalFlip(), ])第三复制-粘贴增强。对数量少、面积小的缺陷类别比如空鼓从其他图里把这一类缺陷的掩码区域裁剪出来随机粘贴到当前图的完好墙面上掩码同步贴过去。这个做法能直接把少数类样本数量翻倍我在小数据集上试过空鼓类IoU提升了3到5个点。要注意粘贴时避开已有缺陷区域否则两个类别重叠会产生不合理的标注。4. 训练避坑指南多类别墙壁缺陷分割里最容易翻车的5个细节4.1 类别不平衡导致模型只会预测背景现象训练二三十个epoch验证集mIoU看起来还行但把预测结果可视化出来发现几乎所有像素都被预测成背景只有个别大面积水渍被标出来。原因20类里背景占据了绝大多数像素裂缝和空鼓可能只占图幅的1%到3%。标准交叉熵损失对每个像素一视同仁地平均背景像素数量碾压缺陷像素模型根本学不到少数类。解决改用带权重的交叉熵权重按类别像素频率的倒数设置或者直接换Dice Loss这类区域重叠损失。常见做法是两者按0.5:0.5混合我用的是import torch.nn.functional as F class SoftDiceLoss(nn.Module): def __init__(self, smooth1.0): super().__init__() self.smooth smooth def forward(self, logits, targets): probs F.softmax(logits, dim1) # targets: [B, H, W]转成one-hot: [B, C, H, W] targets_onehot F.one_hot( targets, num_classesprobs.shape[1]).permute(0, 3, 1, 2) intersection (probs * targets_onehot).sum(dim(2, 3)) union probs.sum(dim(2, 3)) targets_onehot.sum(dim(2, 3)) dice (2 * intersection self.smooth) / (union self.smooth) return 1 - dice.mean()smooth用来防止分母为0返回的是1减Dice系数所以是损失值而不是分数。加了Dice Loss后即使背景占比90%模型也会为了把裂缝区域和标注重叠而被迫往前推进而不是一股脑输出背景。如果你在意Dice系数的数值mean计算和per-class计算二选一per-class方式更平均不容易被大类带偏。4.2 掩码错位原图和标注尺寸对不上现象转换出来的掩码叠到原图上缺陷轮廓整体偏移或局部对不齐有时候裂缝位置差了十几个像素。原因Labelme的JSON里imageHeight和imageWidth记录的是标注时加载的那张图的尺寸。如果原数据集图片被批量压缩过但JSON没跟着更新或者标注时用的是缩略图那么用JSON里的尺寸建掩码就会和最终图片错位。解决转换前先校验JSON尺寸和图片实际尺寸。我在脚本里加了这一段def check_size(json_path, image_dir): with open(json_path, r, encodingutf-8) as f: data json.load(f) img cv2.imread(os.path.join(image_dir, data[imagePath])) actual_h, actual_w img.shape[:2] json_h, json_w data[imageHeight], data[imageWidth] if (actual_h, actual_w) ! (json_h, json_w): print(fsize mismatch: {json_path}, json{json_w}x{json_h}, image{actual_w}x{actual_h}) scale_y actual_h / json_h scale_x actual_w / json_w # 把points乘上缩放系数后再fillPoly等比缩放时只乘系数不做强制拉伸否则裂缝宽高比会失真。差1到2个像素时可以忽略超过5个像素就要整批检查因为很可能整个数据集都被二次压缩过。真遇到这种情况宁可脚本里统一按图片实际尺寸缩放掩码也不要手工一张张修。4.3 裂缝等小目标缺陷丢失几个像素宽的标注怎么保住现象裂缝本身只有2到3个像素宽预测结果里要么完全消失要么断成虚线。原因两个操作会吃掉小目标。一是Resize到512后原图4K的裂缝宽度被缩到不到1像素二是下采样过程中高频细节被卷积平均掉。解决输入分辨率不要一味压低。我通常把训练输入保持在1024附近同时配合多尺度训练。在损失层面加强对小目标的权重做法是按每个连通域的面积给像素权重面积越小的区域权重越大。不要只按类别权重因为同类是裂缝宽裂缝和细裂缝难度完全不同。如果显存紧张可以用OHEM在线难例挖掘每轮只回传loss最高的那部分像素裂缝恰好是高loss区域效果直接。4.4 验证集mIoU高但现场效果差现象验证集mIoU有0.72看起来不错一到地下车库的现场照片大量漏检。原因数据集采集样本以居民楼外墙为主现场是地下车库光照、墙面材质、拍摄视角分布不一致。mIoU是像素级指标背景占九成时全预测背景也能拿到虚高分数。解决验证集要和现场分布对齐至少单独留一组“现场测试集”用不同时段、不同光线条件下的图评估。训练结束后跑全图推理把预测结果单独染色输出让不写代码的检测工程师肉眼确认漏检。mIoU之外我还会统计每个类别的IoU和F1优先看裂缝这一类的分数。另外可以算一个“非背景IoU”把背景类剔除后再平均这样能在报告里暴露全预测背景的问题。4.5 Labelme环境安装问题pyqt5和sip版本冲突现象pip install labelme装完命令行输入labelme提示缺PyQt5或直接报错cannot import name sip还有人遇到Labelme打开后闪退。原因labelme依赖PyQt5PyQt5对sip版本敏感Python 3.10和3.11尤其容易版本错配。pip默认拉新版PyQt5和已装的sip版本不一致启动时就会在黑匣子里报错。解决常见做法是用清华镜像安装并显式指定PyQt5版本pip install -i https://pypi.tuna.tsinghua.edu.cn/simple labelme5.8.3 pip install -i https://pypi.tuna.tsinghua.edu.cn/simple PyQt55.15.10 PyQt5-sip12.13.0如果用的是conda环境可以直接conda install -c conda-forge labelmeconda会把配套的pyqt版本一起解析省去手动配平。装完后先运行labelme --version确认能启动再开始标注避免工作到一半发现工具坏了。当然这个坑主要影响新装环境的同学数据集本身是现成标注好的安装问题只在你想打开JSON检查或做二次标注时才出现。5. 半自动标注与迭代让7820张Labelme数据集持续增值5.1 用颜色表批量目检掩码质量7820张人工一张张看不现实。我一般抽5%的图做叠加检查同时生成一张拼接图把原图、掩码、叠加结果三列排开一次性浏览50张。重点看四点类别是不是标错了、多边形边缘是不是粗糙、有没有把墙面阴影当成缺陷、裂缝是不是连成一片。不需要额外写复杂代码把2.4的visualize_mask循环跑一遍输出到一个目录即可。注意用cv2.IMREAD_GRAYSCALE读掩码避免把PNG的通道顺序搞错导致查表颜色错乱。下面是一个快速统计每个类别像素占比的脚本我每次拿到数据集都会跑import cv2 import numpy as np import os total_counts {} mask_dir masks for name in os.listdir(mask_dir): mask cv2.imread(os.path.join(mask_dir, name), cv2.IMREAD_GRAYSCALE) unique, counts np.unique(mask, return_countsTrue) for cls_id, cnt in zip(unique, counts): total_counts[int(cls_id)] total_counts.get(int(cls_id), 0) int(cnt) total_pixels sum(total_counts.values()) for cls_id, cnt in sorted(total_counts.items()): print(fclass {cls_id}: pixel ratio {cnt / total_pixels:.4f})这个像素占比表直接决定损失函数要不要加权。如果发现背景占了95%以上基本可以断定训练时用普通交叉熵会出4.1的问题。如果某些类占比连0.1%都不到那就要考虑复制-粘贴增强或者收集更多该类样本。5.2 模型预标注Labelme手动修正半自动闭路循环当你有了一版粗糙模型预测结果后可以把预测掩码反向转换成Labelme JSON导入Labelme让人工修正。这个闭环特别适合持续积累新缺陷数据比如工地项目里每周都会新增现场照片全部手工标不现实。import json import cv2 import numpy as np def mask_to_labelme(image_name, mask, class_names, out_json): shapes [] for class_id in np.unique(mask): if class_id 0: continue binary (mask class_id).astype(np.uint8) contours, _ cv2.findContours( binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) for contour in contours: # approxPolyDP简化轮廓避免labelme打开卡顿 epsilon 2.0 simplified cv2.approxPolyDP(contour, epsilon, True) if len(simplified) 3: continue points simplified.reshape(-1, 2).astype(float).tolist() shapes.append({ label: class_names[class_id], points: points, shape_type: polygon, group_id: None, }) output { version: 5.8.3, flags: {}, shapes: shapes, imagePath: image_name, imageData: None, imageHeight: int(mask.shape[0]), imageWidth: int(mask.shape[1]), } with open(out_json, w, encodingutf-8) as f: json.dump(output, f, ensure_asciiFalse)逻辑说明先对每个类别二值化用findContours提取轮廓再用approxPolyDP压缩轮廓点密度。epsilon设得越大轮廓点越少Labelme打开越流畅但太大会让多边形边角变平丢失裂缝细节。墙面裂缝这种细长目标我一般取1.5到3.0像素。label字段必须和原数据集20类保持一致否则下一步训练时CLASS_MAPPING找不到对应标签。这个流程里有个细节预测掩码可能带有小碎块转换前先用面积阈值过滤只留下大于min_area的连通域否则生成的JSON里会有一堆只有几个点的垃圾多边形人工修起来比从零标还烦。半自动标注的省钱逻辑是裂缝密集的图像修正轮廓比从零画多边形快得多预测结果准的时候一晚上能修两三百张。5.3 针对墙面场景的部署后处理模型输出的是20通道概率图部署时先用argmax取类别再做两步后处理连通域过滤和形态学连接。小碎块噪点用面积阈值清掉细裂缝被断裂的地方用闭运算补上。def postprocess(pred_mask, min_area50): # pred_mask: HxW, 0为背景 cleaned np.zeros_like(pred_mask) for class_id in np.unique(pred_mask): if class_id 0: continue binary (pred_mask class_id).astype(np.uint8) num_labels, labels, stats, _ cv2.connectedComponentsWithStats(binary) for i in range(1, num_labels): if stats[i, cv2.CC_STAT_AREA] min_area: cleaned[labels i] class_id return cleanedmin_area是保留的最小连通域面积单位是像素。墙面裂缝再细在4K照片里对应的像素数也不会太少所以这个阈值能有效减少误报。如果整张图分辨率高或者墙面瑕疵很多min_area可以相应调大避免小噪点被保留成缺陷。形态学闭运算对裂缝断点比较有用可以放在连通域过滤之后但要注意结构元大小太大会把两个相邻缺陷糊在一起。6. 验证技巧快速确认分割模型真正能用的三个动作训练完模型后的第一件事不是看mIoU而是拿三组现场照片做鲁棒性测试同一面墙拍全景、走近拍局部、侧面打光各拍一组分别跑推理。mIoU被平均值骗过太多次了。第一组检查尺度如果全景里2像素宽的裂缝识别不到局部图能识别到说明模型对尺度敏感回到训练端把多尺度增强加大或者在推理时做多尺度融合把原图缩放0.6、1.0、1.4三档分别推理再取平均。这个操作对细长裂缝很有效代价是推理时间约增两倍所以我只在验收阶段用部署时固定单尺度。第二组检查光照正光和逆光效果差异大多半是颜色扰动不够补一组HSV抖动再重训。第三组检查类别混淆把“渗水”和“墙面阴影”重叠区域放大看总把阴影当渗水的话在后处理里加一条亮度约束渗水区域平均亮度通常低于正常墙面能挡掉很大一部分阴影误报。另外我习惯最终用全景墙面图输出一张对比图把原图、预测掩码、叠加结果用np.hstack拼一起拼接前先resize到同一高度。自己先看五分钟再拿给检测工程师看。裂缝表现不好时先别急着换更大的模型把输入分辨率从1024提到1536试一次显存够用的话往往比换Backbone更直接。这是我从7820张数据集上反复调下来的习惯先固定流程跑通再谈精度验收时不只看数字要看能不能在一面没见过的墙上可靠地圈出缺陷。希望这些转换、训练和排查思路帮到你少走我走过的弯路。本文还有配套的精品资源点击获取
网站建设高端定制企业官网