☰
敦煌壁画破损识别分割数据集:从labelme标注到模型训练全流程指南
2026/10/20 12:04:03 网站建设 项目流程

做文物数字化保护这些年,我见过太多高质量数据被埋没的场景。而敦煌壁画破损识别分割数据集,是其中一个难得的例子——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], dtype=np.uint8) for pts in polygons: pts = np.array(pts, dtype=np.int32).reshape(-1, 2) cv2.fillPoly(mask, [pts], 1) return mask def labelme_to_mask(json_path, output_dir): with open(json_path, 'r', encoding='utf-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, weight=0.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_loss

weight我最初设为0.7,偏重BCE,但是训练下来发现Dice容易在中期停滞,后来把weight调到0.5,整体指标更均衡。这是因为破损区域占整张图的比例非常低,BCE会被大量背景像素带偏,Dice能把模型拉回前景匹配上。除了这两个损失,我还会在训练后期冻结骨干网络,微调解码器,让模型在细节边界上收得更稳。

优化器一般选择AdamW,初始学习率设置在5e-4,配合cosine退火策略。Batch size我使用的是16,输入尺寸统一缩放到512x512。这些参数需要根据显卡显存调整,不盲目追求大分辨率,因为壁画图像的破损区域相对集中,512分辨率够用。

5. 效果评估与实地使用经验

5.1 评估指标怎么看

分割任务最常用的评估指标是IoU(Intersection 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转换做扎实,后面的路会顺畅很多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询