基于labelme的公路隧道漏水分割:27张图小数据集训练与避坑指南
发布时间:2026/9/29 7:57:23 锦皓数字建站

1. 这个27张图的小数据集到底能干什么先说实话27张图、1个类别、labelme格式的公路隧道漏水分割数据集放在今天动辄几万张的公开数据集面前确实小得可怜。但小不代表没用关键看你怎么用、用在哪。我在实际项目里接手过不少类似的迷你数据集它们往往来自某个具体工程现场的抽检样本或者是某个检测单位为了验证算法可行性而手工标注的第一批数据。这类数据集的价值不在于训练一个通用大模型而在于快速验证技术路线、跑通标注到训练的全流程、以及作为后续大规模标注的模板参考。公路隧道漏水这个问题做基础设施检测的人应该都不陌生。隧道衬砌表面的渗漏水是运营期最常见的病害之一传统的检测方式是人工巡检加拍照记录效率低、主观性强、漏检率高。用语义分割的方式自动识别漏水区域本质上是要把哪里有水渍、哪里是干燥的混凝土表面这个判断交给算法。27张图虽然少但如果标注质量过关足够你验证一个分割模型能不能收敛、能不能在验证集上跑出合理的IoU也足够你判断这个方向值不值得继续投入标注资源。这个数据集适合谁用如果你是做土木工程检测方向的研究生想快速跑一个分割baseline写进论文的可行性验证章节它够用。如果你是做算法工程的想测试某个新出的分割网络在小样本条件下的表现它也能当个试金石。但如果你指望用它训练一个能直接上工程部署的模型那我劝你趁早放弃这个念头27张图的泛化能力几乎为零换个隧道、换个光照条件模型大概率直接崩掉。关键词里提到的labelme是这套数据的标注工具也是整个流程的起点。labelme这个工具在语义分割标注领域算是老牌选手了它的优势是轻量、开源、标注格式通用JSON而且支持多边形、矩形、圆形、线段、点等多种标注方式。对于漏水区域这种边界模糊、形状不规则的病害多边形标注是最合适的选择。后面我会详细讲labelme的安装、使用和格式转换以及怎么把这27张图的标注结果喂给主流分割网络。2. 数据集的核心构成与标注逻辑拆解2.1 为什么是27张图而不是270张27这个数字看起来随意但结合工程实际它很可能对应的是某个隧道某一区段的抽检样本。隧道衬砌检测通常按环或按段进行一个检测断面可能拍几十张高清图从中筛选出有明显漏水病害的27张作为标注对象。这种筛选逻辑本身就带有倾向性——数据集中正样本漏水区域的占比会远高于真实场景中的自然分布。这意味着你训练出来的模型在真实推理时面对大量无病害的隧道表面图像可能会产生大量误报。我在处理类似小数据集时第一件事就是统计正负样本比例。如果27张图里每张都有漏水区域那这个数据集实际上是一个全正样本集模型学到的只是漏水长什么样而没学到不漏水长什么样。解决办法有两个一是自己补充一些无漏水的隧道表面图作为负样本哪怕只有十几张也能显著降低误报率二是在训练时使用Focal Loss或Dice Loss这类对类别不平衡不敏感的损失函数让模型更关注难分样本。2.2 labelme的JSON标注格式到底存了什么labelme标注一张图后生成的JSON文件结构其实很清晰。核心字段包括imageData图像的base64编码可选、imagePath图像文件名、imageHeight和imageWidth图像尺寸、shapes标注形状列表。每个shape里包含label类别名、points多边形顶点坐标列表、shape_type标注类型如polygon、flags额外标记。对于漏水分割任务label字段通常就是leakage或漏水这样的单一类别名points则是一系列按顺序排列的边界点坐标。这里有个容易被忽略的细节labelme默认保存的JSON里imageData字段会把整张图编码成base64字符串塞进去导致JSON文件体积膨胀。27张图如果每张都是高清大图JSON文件加起来可能上百MB。实际使用时你完全可以在标注完成后用脚本批量移除imageData字段只保留标注信息这样JSON文件会小很多转换和传输都更方便。我一般会在labelme的保存设置里直接关掉Save Image Data选项从源头避免这个问题。2.3 单类别分割的标注一致性怎么保证只有一个类别漏水听起来标注很简单但实际操作中边界判定的一致性才是最大的坑。漏水区域从中心到边缘有一个渐变过程中心是明显的水渍或湿痕边缘是半干不干的过渡带。不同标注员对哪里算漏水、哪里不算的判定标准可能差出十几个像素。27张图如果由多人标注这种不一致会直接导致模型学到一个模糊的边界IoU上不去。我的经验是在标注前先定一个明确的规则以肉眼可见的明显水渍或湿痕边界为准半干过渡带不计入。如果同一张图有多个标注员参与先让他们各自标3张图然后交叉比对把分歧大的区域拿出来讨论统一标准后再批量标注。这个前置工作看起来费时间但能省掉后面反复修标注的麻烦。对于只有27张图的小数据集标注一致性比标注数量重要得多。3. labelme从安装到出标注的完整实操3.1 安装labelme时绕开PyQt5的坑labelme的安装本身不复杂但关键词里出现了labelme 无法安装 pyqt5和labelme error pyqt5-sip说明这个坑踩的人不少。labelme的图形界面依赖PyQt5而PyQt5在不同Python版本和操作系统上的兼容性确实有点脾气。我实测下来最稳的方案是用Python 3.8或3.9先单独安装PyQt5再装labelme。# 创建独立环境避免污染主环境 conda create -n labelme_env python3.9 conda activate labelme_env # 先装PyQt5指定版本 pip install PyQt55.15.9 # 再装labelme pip install labelme5.8.3如果你用的是清华镜像源命令后面加-i https://pypi.tuna.tsinghua.edu.cn/simple会快很多。遇到pyqt5-sip报错通常是版本冲突可以尝试先卸载再重装pip uninstall pyqt5-sip PyQt5 pip install pyqt5-sip12.13.0 PyQt55.15.9还有一个常见问题是labelme启动后界面空白或闪退这多半是显卡驱动或Qt平台插件的问题。在Linux下可以试试设置环境变量export QT_DEBUG_PLUGINS1来查看具体报错。Windows下如果遇到Could not load the Qt platform plugin windows把PyQt5的plugins目录路径加到系统PATH里通常能解决。3.2 标注漏水区域的实操细节打开labelme后Open Dir加载27张图所在的文件夹然后逐张标注。对于漏水区域选择Create Polygons工具沿着水渍边界逐点点击闭合后输入类别名。这里有几个实操技巧第一多边形顶点不要过于密集。有些人习惯沿着边界每几个像素点一下结果一个区域几百个点JSON文件巨大不说转换成的mask边缘还会因为点太密而出现锯齿。一般曲率大的地方点密一些直边的地方点稀一些一个漏水区域控制在20到50个点之间比较合适。第二遇到大面积漏水且形状复杂的情况不要试图用一个多边形包住所有区域。如果漏水区域中间有干燥的混凝土斑块应该用多个多边形分别标注或者用带孔洞的标注方式。labelme本身不直接支持孔洞但可以通过标注外轮廓和内轮廓两个shape在转换时用后处理的方式挖洞。第三标注完成后务必逐张检查。我习惯用Next Image和Prev Image快速过一遍重点看边界是否贴合、有没有漏标的小区域、类别名是否统一。27张图检查一遍也就十几分钟但能避免后面训练时发现标注问题再回头返工。3.3 批量转换JSON到分割masklabelme自带的labelme_json_to_dataset命令可以把单个JSON转成mask但27张图一张张转太慢而且默认输出的是16位灰度图需要额外处理。我一般用Python脚本批量转换import json import numpy as np import cv2 import os from labelme import utils def json_to_mask(json_path, output_dir): with open(json_path, r, encodingutf-8) as f: data json.load(f) img_shape (data[imageHeight], data[imageWidth]) mask np.zeros(img_shape, dtypenp.uint8) for shape in data[shapes]: label shape[label] points shape[points] polygon np.array(points, dtypenp.int32) # 漏水区域设为1背景为0 cv2.fillPoly(mask, [polygon], 1) base_name os.path.splitext(os.path.basename(json_path))[0] cv2.imwrite(os.path.join(output_dir, base_name .png), mask)这个脚本的核心逻辑是创建一个全零的mask然后对每个标注多边形用fillPoly填充为1。注意cv2.fillPoly的坐标格式要求是np.int32而且点顺序要正确否则填充结果会错乱。转换完成后你会得到27张二值mask图像素值只有0和1可以直接用于训练。注意如果你的JSON里imageData字段还在json.load会加载大量base64数据速度很慢。建议先用脚本批量移除这个字段或者标注时就关掉保存图像数据。4. 用这27张图训练分割模型的完整流程4.1 数据划分与增强策略27张图怎么划分训练集和验证集我的建议是20张训练、7张验证或者更激进一点22张训练、5张验证。但这里有个问题如果随机划分验证集里可能全是某种特定类型的漏水比如全是顶部渗水导致验证结果不能反映模型的真实泛化能力。更稳妥的做法是按漏水形态分层抽样确保训练集和验证集里都包含点状渗水、线状渗水、面状渗水等不同形态。数据增强是这个小数据集的救命稻草。我一般会用Albumentations库做在线增强包括水平翻转、垂直翻转、随机旋转±30度、随机裁剪、亮度对比度调整、高斯模糊等。对于漏水区域颜色抖动特别重要因为不同隧道的光照条件差异很大模型必须学会忽略光照变化专注于水渍的纹理特征。import albumentations as A transform A.Compose([ A.HorizontalFlip(p0.5), A.VerticalFlip(p0.5), A.RandomRotate90(p0.5), A.RandomBrightnessContrast(p0.3), A.GaussNoise(p0.2), A.Blur(blur_limit3, p0.1), ])增强的强度要控制好过度增强会让漏水区域变得面目全非模型反而学不到有用特征。我的经验是对于27张图这种量级增强后的等效数据量控制在200到300张左右比较合适再多就是重复样本的简单堆砌边际收益很低。4.2 模型选型为什么U-Net仍然是小数据集的优选在27张图的条件下选模型的核心原则是参数量要少、要有预训练权重、结构要适合小样本。U-Net及其变体如U-Net、Attention U-Net在这个场景下依然是最稳的选择。原因有三第一U-Net的编码器-解码器结构配合跳跃连接能同时利用浅层纹理信息和深层语义信息对漏水这种边界模糊的目标很友好第二U-Net的参数量相对较小在几百张增强数据上不容易过拟合第三U-Net有大量公开的预训练权重可用比如在ImageNet上预训练的ResNet编码器能显著加速收敛。如果你追求更高的精度可以试试在U-Net基础上加注意力机制或者用DeepLabV3配合MobileNetV2 backbone。但说实话27张图的条件下模型结构的差异远不如标注质量和增强策略的影响大。我试过用同一个U-Net在标注精细的数据上IoU能到0.75在标注粗糙的数据上只有0.5出头差距非常明显。4.3 训练参数设置与收敛判断小数据集训练最容易出现的问题是过拟合训练loss一路下降验证loss先降后升。我的应对策略是早停Early Stopping加上学习率衰减。具体参数上初始学习率设1e-4用Adam优化器batch size根据显存尽量设大比如8或16训练轮数设200但配合早停patience设20。损失函数用Dice Loss加BCE Loss的组合Dice Loss负责处理类别不平衡BCE Loss负责稳定训练。import torch import torch.nn as nn class DiceBCELoss(nn.Module): def __init__(self): super().__init__() self.bce nn.BCEWithLogitsLoss() def forward(self, pred, target): bce_loss self.bce(pred, target) pred_sigmoid torch.sigmoid(pred) intersection (pred_sigmoid * target).sum() dice_loss 1 - (2. * intersection 1e-6) / (pred_sigmoid.sum() target.sum() 1e-6) return bce_loss dice_loss判断收敛的指标不能只看loss还要看验证集上的IoU和Dice系数。我一般每5个epoch算一次验证指标如果连续3次验证IoU没有提升就触发早停。实测下来27张图增强到200多张后U-Net大概在50到80个epoch之间收敛验证IoU能到0.65到0.75之间具体取决于标注质量和增强策略。5. 常见问题与排查技巧实录5.1 labelme标注与转换环节的高频问题问题现象可能原因解决方法labelme启动报PyQt5错误PyQt5版本与Python不兼容降级Python到3.9安装PyQt55.15.9JSON转mask后全黑多边形点顺序错误或坐标越界检查points是否在图像尺寸范围内确保点按顺时针或逆时针顺序排列mask边缘锯齿严重多边形顶点过密或过疏调整顶点密度曲率大处加密直边处稀疏多张图标注类别名不一致多人标注未统一标准标注前统一类别名标注后批量检查并修正JSON文件过大imageData字段保存了图像base64标注时关闭Save Image Data或批量移除该字段5.2 训练过程中的典型故障排查训练loss不下降最常见的原因是学习率设得太大或太小。1e-4是个比较稳的起点但如果你的模型编码器是随机初始化的可能需要更小的学习率比如1e-5。另一个原因是数据增强太强把漏水区域增强得面目全非模型根本学不到东西。这时候可以先把增强关掉用原始27张图跑一遍确认模型能过拟合训练loss降到接近0再逐步加增强。验证IoU波动大通常是因为验证集太小只有5到7张图单张图的预测差异就会导致指标大幅波动。解决办法是增加验证集比例或者用交叉验证的方式把27张图分成5折每折轮流做验证最后取平均IoU。虽然计算量大了5倍但指标更可靠。模型在验证集上表现好但实际推理时误报多这几乎肯定是负样本不足导致的。27张图如果全是漏水样本模型没见过干燥的隧道表面自然会把一些纹理相似的区域误判为漏水。我的做法是从公开的隧道检测数据集里找一些无病害的图或者自己拍几十张干燥隧道表面的图作为负样本加入训练集。负样本不需要标注只需要mask全零即可。5.3 小数据集的独家避坑心得第一不要迷信复杂的模型。我见过太多人在几十张图的数据集上直接上Transformer分割网络结果训练loss都降不下去。小数据集的核心矛盾是样本不足不是模型不够强。先用U-Net跑通baseline再考虑换模型。第二标注质量比标注数量重要。27张精细标注的图效果可能比100张粗糙标注的图更好。如果你只有精力标27张那就把这27张标到极致边界贴合、类别统一、无漏标。第三验证集不要用来调参。小数据集的验证集本身就有很大随机性如果你反复在验证集上调超参数很快就会过拟合验证集。正确的做法是划出一部分数据作为测试集只在最后评估时用一次调参全靠训练集和验证集的loss曲线。第四保存训练日志和模型权重。小数据集训练的不确定性很大同样的参数跑两次结果可能差很多。把每次训练的配置、loss曲线、验证指标都记录下来方便回溯和对比。我一般用TensorBoard记录每50个epoch保存一次权重最后挑验证IoU最高的那个。6. 从27张图扩展到可用模型的路径27张图只是一个起点如果你真的想做一个能用的隧道漏水检测模型扩展数据是绕不开的。扩展的路径有三条一是继续在同一个隧道采集更多图像覆盖不同区段、不同光照、不同漏水形态二是找其他隧道的图像增加场景多样性三是用这27张图训练一个初步模型然后用模型去预测未标注的图像把高置信度的预测结果作为伪标签人工修正后加入训练集这就是半监督学习的思路。伪标签这条路我实际走过效果比预期好。具体做法是用27张图训练一个U-Net然后在几百张未标注的隧道图像上推理把预测概率高于0.9的区域作为正样本伪标签低于0.1的作为负样本伪标签中间不确定的区域留给人工标注。这样一轮下来能扩充出上百张有标签的数据再训练一轮IoU通常能提升5到10个百分点。当然伪标签的质量取决于初始模型的质量如果初始模型本身就很差伪标签会引入大量噪声反而有害。另一个扩展方向是迁移学习。找一些公开的裂缝检测数据集、道路病害数据集甚至医学图像分割数据集先用它们预训练一个分割模型再用27张漏水图微调。虽然领域不同但底层纹理特征边缘、纹理、灰度变化是相通的预训练能显著加速收敛减少对标注量的依赖。我试过用裂缝数据集预训练再微调漏水分割验证IoU比从头训练高了将近0.1。最后再分享一个小技巧如果你手头的27张图分辨率很高比如4000x3000不要直接缩放到512x512训练那样会丢失大量细节。更好的做法是切patch把大图切成512x512的小块有漏水区域的块保留全背景的块按比例采样。这样27张图能切出几百个patch等效数据量翻了好几倍而且模型学到的是局部纹理特征泛化能力更强。切patch的时候注意重叠采样避免漏水区域被切碎。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。