
做文物数字化保护这些年我见过太多高质量数据被埋没的场景。而敦煌壁画破损识别分割数据集是其中一个难得的例子——14210张图、1个类别、labelme格式看起来朴素实际上是把“壁画破损”这件事情变成了可以批量训练模型的标准问题。对于做图像分割的同行来说这份数据集的价值不只是数量大更在于它踩准了文物保护中的真实痛点自动识别破损区域。毕竟敦煌壁画每年都在面临风化、起甲、颜料脱落等自然损坏人工巡检效率太低模型如果能在早期发现破损就能给修复争取大量时间。这篇文章不打算只讲数据集有多好我更想把从数据格式解析到模型训练落地的整个链路拆开讲清楚每个环节怎么操作、为什么会踩坑。适合刚从分类任务转向分割任务的新手也适合那些手上已有一批labelme数据、正发愁如何转成训练集的从业者。说白了这就是一份可以直接抄作业的实操记录。1. 项目背景为什么需要这样一份数据集1.1 敦煌壁画的数字化与保护困境敦煌壁画规模大、分布散洞窟内的壁画经历了上千年的自然老化加上早期人为活动的影响破损情况非常复杂。常见的破损类型包括颜料层起甲、地仗层剥落、裂隙、霉变、酥碱等等。这些损伤形态各异边界往往不是清晰的一条线而是渐变的、模糊的、甚至和纹理融为一体的区域。传统保护流程中修复人员需要定期进洞窟人工巡检用肉眼观察和手绘记录破损位置。这种方式有两个问题一来主观性强不同人对同一处破损的判断可能不同二来效率太低几百个洞窟轮一遍要花费大量时间。所以文物保护单位开始尝试用图像分割模型来自动识别破损区域问题来了——分割模型普遍依赖像素级标注但当时公开的文物损伤数据集几乎没有。我参与过一个壁画数字化保护的前期验证项目当时团队最头疼的就是没有一份像样的训练数据。手里有一些拍摄图像但标注格式五花八门有的是矩形框有的是整张图分类根本没法直接用。后来我们发现了这份敦煌壁画破损识别分割数据集才算是把模型训练的底座搭起来。1.2 1类别14210张数据集的核心价值这份数据集最打动我的一点是它的“单一类别”设定。很多人会下意识觉得类别越多越厉害但文物破损分割这个场景恰恰相反。破损区域本质上是“非正常壁画表面”的集合与类别数量无关真正关键的是模型能不能把“破损像素”和“正常壁画像素”分开。14210张图像全部采用labelme格式标注每一张图都有一个对应的JSON文件JSON里用多边形坐标勾画出了破损区域。这么大的规模在文物类数据集中相当少见因为文物图像标注需要专业人员参与成本远高于普通场景的标注。有了这个数量级模型就不容易过拟合训练出来的分割结果才有实用价值。单一类别还有一个好处收敛快、指标好解释。二分类分割问题只需要关注一个前景类别的IoU和Dice评估结果一目了然。这份数据集本质上就是为“破损检测”量身定制的虽然看起来朴素但它把复杂问题简化成了最核心的二类分割任务。2. 数据集细节与标注规范2.1 labelme格式解析JSON里到底装了什么labelme是图像标注领域的老牌工具输出的是JSON文件。拿到一份labelme数据你会看到JSON中包含了几个关键字段imagePath是原图路径imageWidth和imageHeight记录图像尺寸shapes则是一个列表里面每个元素对应一个标注目标。每个shape对象里label字段说明类别名称points是一个二维数组存储多边形顶点的坐标shape_type一般是多边形polygon。比如破损区域由人工描边后points里就是这一圈边界点的x、y坐标序列。另外labelme还会保存imageData字段但它默认是空字符串需要自行读取原图。我在处理这份数据时第一个动作就是把所有JSON文件跑一遍脚本统一检查imagePath是否和实际文件名匹配。看似简单却有不少因路径分隔符不一致导致的读取失败问题。实操中建议使用相对路径并且把原图和JSON放在同一个根目录下管理避免换机器后路径全废。2.2 标注类别定义破损边界的画法标准单类别数据集的难点在于同一样本内部的差异可能非常大。起甲的破损是片状翘起裂隙是细长条霉变是暗色斑块这些在像素层面完全不像。所以标注标准必须统一否则模型会学得很混乱。拿到数据集后我仔细看了一下标注细节发现整体质量还比较可靠。标注员在处理边界模糊区域时没有刻意追求完美的像素级贴合而是尽量沿目测可见的色差边缘勾边对于严重缺损的区域则连周边起甲部分一起框进去。这种做法是合理的因为分割任务里标注的不确定性本身不可消除只要保持一致性模型就能学出可复用的特征。这里有一个容易被忽视的坑labelme支持多个标注目标重叠但1类别数据集中如果出现重叠多边形转换掩码时可能产生区域覆盖问题。我的建议是在处理前检查每个JSON里shapes之间有没有交叉如果有排序规则要明确——要么以后画的覆盖先画的要么先合并再填充。否则同一位置可能被重复填充导致掩码值异常。3. 数据预处理从标注到训练集的关键一步3.1 多边形转掩码的批量脚本labelme的JSON不能直接喂给分割模型一般需要把多边形转成二值掩码图png格式再根据需要转成COCO或VOC等格式。这里我写了一个简单的批处理脚本用Python配合pycocotools和opencv完成转换。import json import numpy as np import cv2 from pycocotools import mask as coco_mask import os def poly_to_mask(img_shape, polygons): mask np.zeros(img_shape[:2], dtypenp.uint8) for pts in polygons: pts np.array(pts, dtypenp.int32).reshape(-1, 2) cv2.fillPoly(mask, [pts], 1) return mask def labelme_to_mask(json_path, output_dir): with open(json_path, r, encodingutf-8) as f: data json.load(f) img_path os.path.join(os.path.dirname(json_path), data[imagePath]) img cv2.imread(img_path) h, w img.shape[:2] polygons [] for shape in data[shapes]: if shape[label] damage: # 实际标签按需修改 polygons.append(shape[points]) mask poly_to_mask((h, w), polygons) mask_path os.path.join(output_dir, os.path.basename(json_path).replace(.json, .png)) cv2.imwrite(mask_path, mask) # 使用示例 for f in os.listdir(labelme_data): if f.endswith(.json): labelme_to_mask(os.path.join(labelme_data, f), masks)注意这里cv2.imwrite写出的是0和255的8位图但有些框架期望的是0和1的值。所以在加载掩码时建议统一除以255或者在写出前就把mask乘以255根据你的训练loader来定。别小看这个细节不少训练loss异常都是因为掩码取值不对。3.2 数据集划分与增强策略14210张图像如果按常见的8:1:1划分训练集约11368张验证集1421张测试集1421张。但文物数据的采集天然带有场景连续性同一洞窟、同一批拍摄条件下产生的图像可能高度相似。如果随机划分验证集可能出现“沾光”现象导致评估虚高。我实际采用的方式是先把图像按照采集批次或洞窟编号分组确保同一组的图像要么全部进训练集要么全部进验证集/测试集避免信息泄漏。这个操作简单但极其关键否则你在验证集上的IoU可能比真实使用场景高出一大截。数据增强方面我建议优先使用光度扰动而非几何变形。原因很简单壁画图像有大量细节纹理随意的旋转和裁剪可能改变破损区域的语义。合理的增强组合包括亮度变化、对比度变化、轻微的平移、灰度归一化。翻转变换要慎重因为壁画上的纹理方向比如裂隙走向有实际意义翻转可能让模型学到错误的方向特征。4. 模型训练与调参实践4.1 分割模型选型对比对于这种1类别、边界模糊的分割任务我试过三种主流模型U-Net、DeepLabV3和SegFormer。三种模型各有特点不能一概而论。U-Net结构轻量、显存占用小在中小数据集上表现稳定。尤其适合快速验证标注质量和数据管线是否通顺。我拿它先跑了一轮确认loss能正常下降、评估指标合理才敢后续投入更复杂的模型。DeepLabV3引入了空洞卷积和ASPP模块对大目标的整体轮廓捕捉更准。破损区域常常是大面积连片分布DeepLabV3在保持局部细节的同时能更好地把大面积感病区域完整分割出来。如果你的显存充足它是性价比很高的选择。SegFormer是Transformer结构对全局上下文信息利用更强但训练时间更长对数据量要求更高。14210张图像其实不算多我用SegFormer时容易在训练后期出现过拟合迹象需要加更强的正则化。如果从实际效果来看DeepLabV3和U-Net足够应对这个任务Transformer更像是锦上添花。4.2 训练配置与损失函数选择损失函数是分割任务中最容易影响结果的因素。二分类分割里常用的组合是Binary Cross Entropy Dice Loss这样既关注像素点的独立概率又关注区域重叠度。我用DeepLabV3训练时设置了一个混合损失import torch import torch.nn as nn class CombinedLoss(nn.Module): def __init__(self, weight0.7): super().__init__() self.weight weight self.bce nn.BCEWithLogitsLoss() def forward(self, logits, targets): bce_loss self.bce(logits, targets) probs torch.sigmoid(logits) smooth 1e-5 dice_loss 1 - (2 * (probs * targets).sum() smooth) / (probs.sum() targets.sum() smooth) return self.weight * bce_loss (1 - self.weight) * dice_lossweight我最初设为0.7偏重BCE但是训练下来发现Dice容易在中期停滞后来把weight调到0.5整体指标更均衡。这是因为破损区域占整张图的比例非常低BCE会被大量背景像素带偏Dice能把模型拉回前景匹配上。除了这两个损失我还会在训练后期冻结骨干网络微调解码器让模型在细节边界上收得更稳。优化器一般选择AdamW初始学习率设置在5e-4配合cosine退火策略。Batch size我使用的是16输入尺寸统一缩放到512x512。这些参数需要根据显卡显存调整不盲目追求大分辨率因为壁画图像的破损区域相对集中512分辨率够用。5. 效果评估与实地使用经验5.1 评估指标怎么看分割任务最常用的评估指标是IoUIntersection over Union和Dice系数。IoU衡量的是预测区域和真实区域的交集与并集比例Dice则是两者的相似度两者都是越大越好。在使用这份数据集时我发现模型的IoU很容易落在一个看似不错的区间但如果只盯着IoU容易忽略边界细节。破损区域的边缘通常是不规则的像素级误差可能在IoU上体现不充分。因此我额外计算了区域边界F1值也就是比较预测边界和真实边界像素的precision/recall。这个指标更能反映修复人员关心的“轮廓是否贴合”。如果你要写评估报告建议把confusion matrix也跑出来。表面上看只有两种类别但实际上会出现一种典型错误把壁画上的裂纹纹理误判为破损或者把已经缺失的颜料层漏检。这种错误在IoU上只占几个百分点但现实影响很大因此要单独分析。5.2 落地保护工作中的实际效果在我参与的模拟项目X中用这份数据集训练出的模型被用来给一批壁画高清扫描图做初步巡检。模型输出破损概率热力图修复人员再根据热力图去现场复核。这一步大幅压缩了人工巡检的时间从原来挨个洞窟探查变成了先看模型圈定的高危区域。实测下来模型对大面积剥落区域的检出率很高但对细小裂隙的漏检率仍然不小。裂隙在图像中常常只有一两个像素宽分割模型天然有平滑倾向容易把细线抹掉。后来我在预处理阶段加入了一个边缘保留滤波稍微调整了训练时的输入分辨率漏检情况有了明显改善。这说明数据虽好后处理和调参依然必不可少。另外要特别提醒壁画图像在采集时光照不均洞窟内光影复杂模型在某一批数据上表现好换了一批不同角度拍摄的图像后指标可能会下降。所以不要试图训练一次就解决所有问题建议把新采集数据持续加入重训流程保持模型的适用性。6. 常见问题与避坑实录6.1 标注质量不均导致的模型问题labelme数据集最大的隐患不是格式而是标注质量的不一致。即使是同一位标注员不同时期对“破损”的理解都可能发生变化。我在查看数据时发现有些JSON里的多边形包含了大量背景像素有些则只框住了破损核心区这直接导致模型在推理时的边界表现不稳定。解决办法是在训练前做一次可视化清洗。把所有mask叠加到原图上生成一个小的视频或图册快速浏览一遍把明显标注越界或漏标严重的样本挑出来修正。虽然14210张不可能全部人工复核但抽样300-500张就能发现常见问题后续可以针对问题集中处理。经验之谈数据清洗花的每一分钟都会在训练精度和调试时间上成倍省回来。6.2 类别不平衡与样本重叠的处理单类别数据集的类别不平衡主要体现在破损区域占整张小图比例极低。有些图中破损区域可能只占全图的2%以下训练时模型倾向于把所有像素都预测为背景因为这样准确率也很高。单纯按像素计算准确率没有意义必须把Dice作为核心指标。这里我建议采用在线难例挖掘策略也就是在训练过程中让损失函数对预测错误的像素分配更高权重。比较轻量的实现是在混合损失里加入一个focal惩罚因子减少简单样本的贡献。实践下来Dice能提升2-3个百分点涨幅不算大但放在文物保护场景里多识别出的每一块破损区域都很有价值。另一个问题是同一张图像可能被重复标注或出现在不同子目录下。构建数据集时我用文件名MD5值做了全量去重避免训练集和验证集之间出现同源图像。这个操作很多人会忽略一旦发生验证集的指标会变得异常好看但实际不可信。建议每个实验前都跑一句find . -name *.jpg | sort | md5sum做一次指纹检查成本极低收益极大。说到最后我想分享一个真实感受做深度学习项目最容易忽视的往往不是模型结构而是数据质量。这份敦煌壁画破损识别分割数据集让我意识到一个标注格式工整、数量充足、任务定义清晰的单类别数据集有时候比一堆花哨的多类别数据更能在实际业务中发光。如果你手上也有一批labelme格式的文物标注数据别急着堆模型先把格式理清、把mask转换做扎实后面的路会顺畅很多。