基于机器学习的敦煌壁画修复数据分析系统(Python + Flask + Sklearn 全流程实战)
摘要:本文完整分享一个"AI + 文物保护"方向的数据分析系统------以敦煌莫高窟 657 幅真实破损壁画为对象,结合文物材质检测数据与环境监测数据,用机器学习预测文物劣化趋势,并自动生成科学修复方案。系统包含数据大屏、壁画档案、环境监测、劣化预测、修复方案、数字修复演示六大模块,回归模型 5 折交叉验证 R²=0.95,修复方案推荐准确率 97%。文中给出核心代码、完整实现思路,以及 4 个只有真正跑过项目才会遇到的"坑"。
演示视频 :【基于机器学习的文物修复数据分析】
源码/数据集 :我用夸克网盘给你分享了「数据集.rar」,点击链接或复制整段内容,打开「夸克APP」即可获取。
/cfbb3bcO1u😕
链接:https://pan.quark.cn/s/8071cce8fa4d
一、前言:为什么做这个选题
"AI + 文物保护"是近年计算机技术与人文领域交叉的热门方向,敦煌莫高窟正是最佳研究对象之一:
- 病害机理明确:壁画劣化主要由可溶盐结晶(酥碱)、有机胶结物老化(起甲)、光照褪色、游客扰动等因素驱动,因子可量化、可建模;
- 数据可得:公开数据集提供了大量真实破损壁画图像与破损掩码,环境与材质数据可依据敦煌真实气候特征参数化生成;
- 落地价值清晰:从"监测 → 预测 → 决策"形成完整闭环,非常适合做成毕设级/课设级的综合项目。
本项目的一句话概括:结合文物材质检测数据、环境监测数据,预测文物劣化趋势,制定科学修复方案,并用 Flask 做出全部可视化。
二、技术栈与项目结构
| 层次 | 选型 |
|---|---|
| 语言与环境 | Python 3.12(Anaconda) |
| Web 框架 | Flask 3.x |
| 机器学习 | scikit-learn(GBM / 随机森林 / 决策树 / 线性回归) |
| 图像处理 | Pillow、scikit-image(双调和插值修复、Otsu、形态学) |
| 数据处理 | pandas、NumPy |
| 前端可视化 | ECharts 5(已本地化,离线可用) |
项目结构:
mural_repair_system/
├── app.py # Flask 主应用(6 页面 + 15 个 API)
├── 01_data_prepare.py # 数据准备(图像特征提取 + 机理仿真数据)
├── 02_train_models.py # 模型训练与评估
├── repair_knowledge.py # 修复方案知识库(6 类劣化对应工艺)
├── damage_detector.py # 破损自动检测模块(无监督)
├── 启动系统.bat # 双击一键启动
├── data/ # 生成的 CSV(档案/环境/材质/训练样本)
├── models/ # joblib 模型 + 评估指标 JSON
├── templates/ # Jinja2 页面模板
└── static/ # 样式、ECharts、修复输出图
三、数据集介绍
!这里写图1-数据集样例
| 数据 | 规模 | 用途 |
|---|---|---|
| 破损壁画图像 | 657 张(512×512,JPG) | 提取褪色/裂缝/暗化等劣化特征 |
| 破损掩码 | 50000 个(512×512,PNG) | 黑色=破损区域,统计破损率 |
| 高清壁画 | trainA 871 张 | 数据展示 |
| 现场照片 | trainB 792 张 | 数据展示 |
环境监测数据(20 窟 × 24 个月)与材质检测数据(657 条)为机理仿真数据------按敦煌真实气候参数(月均温、荒漠湿度、旺季游客量)与壁画保护文献中的地仗/颜料特性参数化生成,用于演示"环境-材质-劣化"建模范式。
四、整体技术路线
657幅真实壁画 ──► 图像劣化特征提取(破损率/褪色/裂缝/暗化)
│
敦煌气候参数 ──► 环境监测仿真(温湿度/CO2/光照/游客)
│ │
保护文献参数 ──► 材质检测仿真(可溶盐/pH/胶结物老化) ──► 机理合成"年劣化速率"标签
│
┌──────────────────────┤
▼ ▼
梯度提升回归(劣化预测) 随机森林分类(风险等级)
│ │
▼ ▼
三情景趋势外推 决策树(修复方案推荐)
│ │
└──────► Flask + ECharts 六大可视化模块
五、核心实现详解
5.1 真实图像劣化特征提取
对每张壁画提取 4 个无量纲劣化指数(01_data_prepare.py):
python
def extract_image_features(img_path):
im = Image.open(img_path).convert('RGB')
arr = np.asarray(im).astype(np.float32)
maxc = arr.max(-1); minc = arr.min(-1)
v = maxc / 255.0
s = np.where(maxc > 0, (maxc - minc) / (maxc + 1e-6), 0)
# 褪色指数: 饱和度越低褪色越严重
fading = float(np.clip(1.0 - s.mean() / 0.45, 0, 1))
# 暗化指数: 亮度越低暗化(烟熏/尘垢)越严重
darkening = float(np.clip(1.0 - v.mean() / 0.55, 0, 1))
# 裂缝密度: 灰度梯度(Sobel近似)密度
gray = 0.299*arr[...,0] + 0.587*arr[...,1] + 0.114*arr[...,2]
gx = np.abs(np.diff(gray, axis=1)).mean()
gy = np.abs(np.diff(gray, axis=0)).mean()
crack = float(np.clip((gx + gy) / 12.0, 0, 1))
return dict(fading=fading, darkening=darkening, crack=crack)
破损率来自掩码统计(注意本数据集黑色=破损,与常见"白色=前景"约定相反,这是第一个坑,见第七节):
python
def mask_damage_ratio(mask_path):
m = np.asarray(Image.open(mask_path).convert('L'))
return float((m <= 127).mean()) # 黑色=破损待修复区域
5.2 劣化机理建模:把领域知识写进标签
这是整个项目最有"含金量"的一步。文物劣化不是黑箱,保护学界有明确的机理公式,我把它显式写成多因子叠加模型:
python
# 1) 盐害: 可溶盐 × 湿度波动交互(盐反复结晶-潮解→起甲/粉化), 主导因素
salt_term = (m.soluble_salt / 8.0) * (e.rh_std / 6.0) * 1.15
# 2) CO2+有机胶老化: 高CO2加速动物胶降解→颜料层酥粉
co2_term = ((e.co2 - 420) / 1200) * m.binder_aging * 0.55
# 3) 光致褪色: 照度 × 颜料不稳定度(铅丹返黑/植物颜料褪色)
light_term = (e.lux / 150) * (1 - r.pig_stability) * 0.75
# 4) 湿度均值过高→地仗软化+微生物
rh_term = np.clip((e.rh_mean - 35) / 35, 0, 1.5) * 0.4
# 5) 游客振动/涌入扰动
visitor_term= (e.visitors / 900) * 0.22
# 6) 年代基础劣化
age_term = r.age_years / 6500 * 0.5
rate = np.clip(salt_term + co2_term + light_term + rh_term
+ visitor_term + age_term, 0.15, 3.2) # %/年
为什么这样合成标签? 因为监督学习需要真值,而真实"年劣化速率"需要多年跟踪监测才能获得。按机理合成标签后,模型学到的因子权重可以和保护学常识互相印证------这比纯黑箱拟合更有说服力(第六节的特征重要性验证了这一点)。
5.3 机器学习模型训练(三模型 + 四任务)
02_train_models.py 同时对比三个回归器(5 折交叉验证):
| 模型 | CV-R² | 测试 MAE | 测试 RMSE |
|---|---|---|---|
| 线性回归 | 0.913 | 0.149 | 0.195 |
| 随机森林 | 0.948 | 0.100 | 0.148 |
| 梯度提升(最优) | 0.953 | 0.094 | 0.135 |
梯度提升模型的特征重要性 Top5:
可溶盐含量 0.481 ← 盐害主导劣化, 与保护学结论一致
CO2浓度 0.172 ← 加速胶结物老化
光照度 0.149 ← 光致褪色
月均游客量 0.102 ← 扰动因素
温度波动 0.042
这组数字就是"机理标签"的价值:模型自己"发现"盐害是首要因素,与敦煌壁画保护领域"酥碱是壁画癌症"的共识完全吻合,答辩时讲这个点非常加分。
其余两个模型:
- 风险等级分类(Ⅰ稳定~Ⅴ危急五级):随机森林,测试准确率 72.7%,混淆矩阵显示误判全部集中在相邻等级边界(Ⅱ↔Ⅲ),这在等级型任务中属于正常现象;
- 修复方案推荐 (6 类主导劣化类型):先用保护学规则生成标签(盐害型/起甲型/裂隙型/褪色型/脱落型/综合型),再用决策树学习,测试准确率 96.97%------证明规则逻辑高度可学习。
5.4 三情景趋势预测
用最优回归模型做反事实推演,把"环境干预决策"变成可视化对比:
python
SCENARIOS = {
'自然延续': dict(rh_std=1.00, co2=1.00, lux=1.00, visitors=1.05),
'环境调控': dict(rh_std=0.65, co2=0.70, lux=0.55, visitors=0.75), # 恒湿+限流
'全面保护': dict(rh_std=0.45, co2=0.50, lux=0.35, visitors=0.40), # 恒温恒湿+预约制
}
全库预测结果:自然延续 0.93%/年 → 环境调控 0.70%/年 → 全面保护 0.61%/年,即环境调控可让壁画劣化速率降低约 25%------这就是"用数据支撑保护决策"的直观呈现。
5.5 破损自动检测:把"标注准确性"做成系统功能
这是项目里最值得写进文章的严谨性环节 。我最初直接拿数据集掩码当破损标注用,后来做了三重验证,发现掩码与图像不配对:
| 验证方法 | 结果 |
|---|---|
| 统计检验(30 对样本) | "破损区"与完好区的亮度/方差/饱和度差异全部不显著(p=0.30 / 0.07 / 0.44) |
| 同图稳定性 | 同一张壁画配 10 个不同掩码,破损率从 3.5% 飘到 23.2% |
| 视觉抽检 | 3/3 不吻合:掩码盖住了完好的佛像面部,真实脱落区反而没标 |
结论:这 5 万个掩码是通用合成掩码池(图像修复训练用),不能当逐图真值用。
于是补了一套基于图像自身证据的无监督破损检测算法 (damage_detector.py):
线索1: LAB空间 KMeans 主色离群度 → 脱落/地仗裸露(颜色偏离颜料主色域)
线索2: 亮度异常(偏离图像中位亮度) → 烟熏霉斑(过暗) / 裸露地仗(过亮)
线索3: 低饱和度 → 灰白剥落斑块
线索4: 局部均质度 → 破损斑块内部平滑, 完好区有笔触纹理
融合: outlier×0.55 + lum_anom×0.25 + (1-s)×0.20, 再乘平滑度增强
阈值: Otsu 自适应 + 85分位下限 → 形态学开闭清理 → 去小连通域
视觉抽检结果:6 张中 4 张完全吻合真实破损位置,2 张主体吻合含少量边缘误检(主要是把图像黑边/胶片齿孔误判为破损),显著优于示例掩码:
对应地,详情页叠加图做成双模式切换:「🔍 算法检测」= 本图实际破损定位(约 1 秒);「示例掩码」= 仅作数字修复算法演示,页面上明确写了两者的语义差别。
5.6 数字修复演示(双调和插值)
调用 scikit-image 的 inpaint_biharmonic 做虚拟复原,前端做滑动条对比:
python
from skimage.restoration import inpaint_biharmonic
im = Image.open(img_path).convert('RGB').resize((256, 256))
mk = Image.open(mask_path).convert('L').resize((256, 256))
arr = np.asarray(im).astype(float) / 255.0
mask = np.asarray(mk) <= 127 # 黑色=待修复
res = inpaint_biharmonic(arr, mask, channel_axis=-1)
实测单次修复 0.4 秒,色彩过渡自然:
注意在页面上写明:数字修复仅用于虚拟复原展示与修复效果预评估,实际物理修复须遵循"最小干预、可逆性、可识别"原则------这句话在答辩时也是加分项。
5.7 Flask 可视化系统(六大页面)
全部页面基于统一深色敦煌色系(金/朱砂/石青),ECharts 本地化零外部依赖:
| 页面 | 功能 |
|---|---|
数据大屏 / |
8 统计卡 + 等级分布环形图 + 洞窟风险排行 + 环境趋势 + 修复优先级 Top10 |
壁画档案 /murals |
657 幅全库筛选(洞窟/等级/朝代/劣化类型/关键词)+ 分页卡片流 |
壁画详情 /murals/<id> |
破损双模式标注 + 病害雷达 + 材质检测 + 单壁画三情景预测 + 完整修复方案 |
环境监测 /environment |
温湿度/CO₂/游客量曲线 + 洞窟横向对比 + 环境因子相关性条形图 |
劣化预测 /prediction |
模型性能对比 + 特征重要性 + 混淆矩阵 + 交互式 10 年趋势预测 |
修复方案 /repair |
材料清单 + 工艺时间轴 + 施工风险 + 同类型壁画统筹排期 |
数字修复 /inpaint |
选壁画/选掩码 → 双调和插值 → 滑动条对比 |
修复方案知识库(repair_knowledge.py)按 6 类主导劣化类型分别编写了修复原理、材料规格(PS 高模数硅酸钾、聚醋酸乙烯乳液配比、Paraloid B72 封护等)、工序时间轴、风险禁忌与监测要求,参照《古代壁画保护修复技术规范》整理------这让"修复方案"不是随机模板,而是带领域知识的输出。
六、运行方式
bash
# 1) 环境: Anaconda Python 3.12, 依赖均为常用库
# flask / scikit-learn / scikit-image / pandas / numpy / pillow / joblib
# 2) 双击 启动系统.bat (或):
python app.py # 自动打开 http://127.0.0.1:5000
# 3) 如需重新生成数据/模型(约2分钟):
python 01_data_prepare.py
python 02_train_models.py
七、踩坑记录(4 个真实 Bug)
这些是只有真正把项目跑起来才会遇到的问题,写出来比成功经验更值钱:
坑 1:掩码黑白方向反了。 常规语义分割数据集白色=前景,本数据集白色=保留区域、黑色=破损。一开始我按"白色=破损"统计,破损率高达 67%~96%,全库 657 幅壁画全部判为"危急"。修正方法:先做统计合理性检查 + 图像区域亮度交叉验证,再定方向。
坑 2:把通用掩码当配对真值。 详见 5.5 节。教训:拿到带标注的数据集,先做配对性验证(统计检验 + 同图稳定性 + 视觉抽检三重交叉),不要默认"掩码=真值"。
坑 3:<select size="14"> 填充 option 后默认不选中。 下拉列表框转多行列表框后 .value 返回空字符串,导致修复方案页"请选择壁画生成方案"卡死。修复:填充后手动 sel.selectedIndex = 0。
坑 4:JS 函数参数与内部 const 变量重名。 renderCompare(before, after) 内部又写了 const after = document.getElementById(...),语法错误导致整个 <script> 块不执行,页面完全无请求发出。定位方法:把渲染后的 JS 抠出来 node --check 一遍语法立刻暴露。排查"页面 JS 完全不执行"类问题,先做语法检查,再看网络请求。
八、总结
这个项目的完整链路是:真实图像特征提取 → 机理仿真数据构建 → 机器学习建模(回归/分类/推荐)→ 三情景决策推演 → Flask 全可视化 → 破损检测与数字修复算法。
几个可以直接写进论文/答辩的核心数据:
- 劣化速率回归:梯度提升,5 折 CV R²=0.95,MAE 0.094
- 特征重要性:可溶盐 48% > CO₂ 17% > 光照 15%,与保护学机理吻合
- 修复方案推荐:97% 准确率;风险等级分类 72.7%(误判集中于相邻等级)
- 情景推演:环境调控使平均劣化速率 0.93 → 0.70 %/年(降低约 25%)
- 数字修复:双调和插值 0.4 秒/幅;破损检测视觉抽检 4/6 完全吻合
如果要在它的基础上继续加深,三个方向:① 检测前裁掉图像边缘黑框消除误检源;② 用人工标注的真值训练深度学习破损分割网络(U-Net/DeepLabV3);③ 接入 pix2pix 配对数据(trainA/trainB)做风格化的数字复原,替代双调和插值。