Spine 模型超分

Spine 模型超分

因为本人苦于这个spine太糊,想要让它变清晰,自己随便找个超分模型效果又不好,于是去寻求大肥鱼帮我超分图片得到了以下经验,在站上找好像又没有这一类的经验,于是发出来给大伙省token,具体一切操作由ai进行,本人一窍不通,有问题问ai。

超分前

自己用超分模型超分图片后直接载入spine模型,会有如上图的零件边线

ai一番操作,超分后

使用的spine显示软件为Live2dviewerEX,配置json的选项里有个着色器,如果显示泛白可以选Straight Alpha着色器,一般默认是不带括号的

从「为什么糊」到「怎么修好」,包含选型逻辑、六个坑的原理、和一套能直接跑的流程。
全部结论来自实测(figure_1978 326 个零件、风织香彩夏夜引光 457 个零件)。


0. 先看结论

你可能遇到的问题 真正的原因 怎么解决
模型整体发糊、放大更糊 atlas 声明 4096,贴图 PNG 只有 2048 把 PNG 补到 4096,atlas 不用改
边缘一圈黑边 图集是预乘 alpha(PMA),但没按 PMA 处理 先反预乘 → 超分 → 再预乘
发丝出现紫色杂色、皮肤有白斑 相邻零件互相串色 按零件逐个处理(有间隙的图集)
零件连接处有锯齿、硬阶梯 ① 对 alpha 做了「收紧」 ② 无缝图集被按矩形切断 alpha 别动;无缝图集改整图处理
头发渐变变成一块一块的 超分模型偏好赛璐璐风格,把渐变压平 混入一部分插值结果(--blend 0.2)
黑色纱网/条纹变粗或消失 1px 周期纹理被模型当噪声处理 该零件改用 NEAREST 放大保频率

一条命令跑完(大部分模型都适用):

chaofen\spine_upscale_kit
python spine_upscale.py --dir "你的模型目录" --out "输出目录"

1. 为什么你的 Spine 模型是糊的

1.1 一句话版本

Spine 只看 .atlas 里写的尺寸,不看你的图片实际多大。 尺寸对不上,图就被硬拉,越拉越糊。

1.2 具体是怎么回事

把 .atlas 文件用记事本打开,开头通常长这样:

figure_1978.png
size: 4096,4096
format: RGBA8888
filter: Linear,Linear
repeat: none

size: 4096,4096 是在告诉运行时:「这张图是 4096×4096 的,你按这个尺寸算坐标」。

然后再看你目录里的 figure_1978.png —— 右键属性一看,2048×2048。

于是运行时干的事就是:把一张 2048 的图,当成 4096 的用。等于在你每次显示的时候,先把图放大一倍,再去切零件。放大一倍靠的是显卡的双线性插值,结果就是糊。

打个比方:atlas 是地图,png 是实景照片。地图上标着「这一片区域 4096 米宽」,但你的照片只拍了 2048 米的范围,剩下的靠"想象"补 —— 当然糊。

1.3 所以修法很简单

把 PNG 补到 4096,atlas 一个字都不用改。

因为 atlas 里的坐标(每个零件的 xy、size)本来就是按 4096 空间的坐标系写的,你把图补到 4096,坐标刚好全对。

反过来,如果你去改 atlas 把 4096 改成 2048,那么所有坐标都要除以 2 —— 而且要处理旋转零件的宽高互换,非常容易出错。别动 atlas。

1.4 怎么快速确认自己的模型是不是这个情况

python spine_probe.py "你的模型目录"

输出里会直接告诉你:

atlas 声明尺寸 : 4096 x 4096
贴图实际尺寸   : 2048 x 2048
=> 需要放大倍数 : 2x   (目标 4096x4096)

如果显示 1x,说明本来就没问题,糊的原因在别处(可能是渲染尺寸、缩放倍数、或者显示器)。


2. 需要准备什么

2.1 软件环境

组件 版本要求 说明
Python 3.10+ 64 位
torch 2.x(CPU 版就行) 不用装 CUDA,实测 CPU 跑 300~500 秒/模型
Pillow / numpy / scipy 任意较新版本 scipy 只用到 distance_transform_edt

安装:

pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu
pip install pillow numpy scipy

为什么 CPU 就够? 一个 326 零件的图集,CPU 8 线程大约 7 分钟。装了 CUDA 能快 10 倍,但为了这几分钟去折腾驱动不值得。真正慢的是"来回试参数",不是算力。

2.2 工具包

chaofen\spine_upscale_kit\:

spine_probe.py      探测:结构 / 倍数 / PMA / 零件统计
spine_upscale.py    主程序:超分
spine_verify.py     质检:三背景合成 + 差异统计 + 特写
upscale_main.py     底层(反预乘、分块推理)
atlas_util.py       atlas 解析
rrdb.py             网络结构定义
models/anime6B.pth  超分模型权重(17MB)
README.md           本教程的精简版

2.3 超分模型怎么选(很重要)

模型 能用吗 原因
anime6B(Real-ESRGAN anime 6B) :white_check_mark: 首选 二次元专用,干净无幻觉,速度最快
RealESRGAN_x4plus :cross_mark: 会在平滑区域凭空画出文字状伪影(我实测出来的,一片皮肤上突然冒出一排"字")
AnimeVideo v3 :warning: 会把肤色洗白、发丝边缘生成紫色杂色
waifu2x :warning: 保真好但细节提升有限,且权重格式不通用

为什么模型选择比参数调优重要 10 倍:参数只能微调风格,模型选错了会直接产生原图里根本不存在的东西(幻觉)。幻觉在静态图上看不出来,动起来会非常明显。


3. 第一步:探测(决定策略)

跑之前先探测,它会告诉你三件决定成败的事:

python spine_probe.py "D:\解包\sprine\风织香彩夏夜引光"
模型目录   : D:\解包\sprine\风织香彩夏夜引光
配置 json  : 风织香彩夏夜引光.json
骨架       : Figure_2067.skel.asset (1901334 B)
atlas      : Figure_2067.atlas.asset (50727 B)
贴图       : Figure_2067.png(2304830B)
shader_type: 0   (0=PMA着色器, 1=Straight Alpha着色器)

--- page 0: Figure_2067.png ---
atlas 声明尺寸 : 4096 x 4096
贴图实际尺寸   : 2048 x 2048
=> 需要放大倍数 : 2x   (目标 4096x4096)
region 数量    : 457  (rotate=65)
覆盖率(按atlas坐标) : 57.9%
alpha 分布     : 全透明 69.5%  不透明 24.4%  半透明 6.1%
PMA 检测       : 是(预乘存储)

三个关键数字怎么读

① 需要放大倍数 — 就是 atlas 声明尺寸 ÷ 贴图实际尺寸。1x 不用处理,2x 是常态。

② PMA 检测 — 这是黑边的元凶。往下看第 5 节有详细解释。

③ alpha 分布 — 半透明占比越高,说明这个图集里"发光层"“纱”"渐变遮罩"越多,越需要小心处理 alpha 通道(不能收紧)。


4. 三种模式怎么选(核心)

这是整套流程里唯一需要你做判断的地方。

4.1 先看一张判断图

零件之间有间隙吗?(用 spine_upscale.py 的日志自动判断)
│
├─ 有间隙(零件边界是透明的)
│   └─ 用【逐零件模式 region】
│       典型:figure_1978(只有 1.5% 的零件边界上有内容)
│
└─ 没间隙(零件紧贴,边界上就是不透明像素)
    └─ 用【整图模式 whole】
        典型:风织香彩夏夜引光(89.4% 的零件边界上都有内容)

--layout auto(默认)会自动算这个比例,超过 30% 就走整图模式,你不用管。

日志里会直接印出来:

无缝边界占比 89.4%  ->  采用 whole 模式

4.2 为什么会有这个区别

打个比方:

  • 有间隙的图集 = 一张桌子上摆了很多分开的贴纸。你把每张贴纸单独拿起来处理,互不干扰,完美。
  • 无缝图集 = 一幅完整的拼贴画,画面本来是连续的,只是被 atlas 用矩形"框"成了很多块。

对第二种情况,如果你按矩形去剪,剪开的地方就是画面中间——你把画剪破了,放大后当然能看到裂口(锯齿)。

所以第二种必须整张一起处理。

4.3 两种模式各自的代价

逐零件 region 整图 whole
优点 绝不会串色,边界干净 不会切断内容,画面连贯
缺点 无缝图集上会切断内容 有间隙的图集上,相邻零件颜色会渗到彼此的半透明边缘
内存 很小(一次一个零件) 大一些(512 分块,块间有重叠)
耗时 300~900 秒 200~300 秒

关键洞察:这两种模式不是"哪个更先进",而是针对两种不同的图集结构。选错了就会出现你遇到的那两种症状之一。

4.4 覆盖范围不够的情况

有些图集会有一小部分像素不属于任何零件(导出时溢出的边缘,或者 padding)。逐零件模式下这些像素会被漏掉,脚本会自动用插值补齐,日志里会写:

兜底填充未声明像素: 32446 px (0.77%)

0.77% 属于正常,如果超过 5% 就要怀疑 atlas 解析有问题了。


5. 六个坑(每个都讲清原理)

坑 1:PMA(预乘 alpha)—— 黑边的真正原因

现象:超分后所有零件边缘多了一圈黑色描边或"脏边"。

原理:

图片的 RGBA 里,RGB 是颜色,A 是透明度。存法有两种:

存法 半透明像素的 RGB 值 怎么合成到背景
Straight(直通) 存真实颜色(比如纯白 255) 最终 = 颜色×alpha + 背景×(1-alpha)
PMA(预乘) 存已经乘过 alpha 的颜色(纯白 53% 透明 → 存 135) 最终 = 颜色 + 背景×(1-alpha)

很多图集导出工具默认用 PMA 存(因为渲染更快),但 .atlas 里往往不写 pma:true。

于是超分模型看到的"透明区域"其实是 RGB=(0,0,0), A=0。模型不知道那是"没有东西",会把它当成黑色去处理,把黑色和边缘的彩色混在一起 —— 放大后就成了一圈黑边。

解决办法:

反预乘(把颜色还原成真实颜色)→ 超分 → 按需要再预乘回去

脚本自动做了这件事(探测显示 PMA 检测: True 就会走这条路)。

怎么判断自己遇到的是不是这个:看黑边是不是只有边缘一圈、而且颜色是灰黑偏冷。如果是四周一整片黑,那是别的问题(尺寸错、通道错)。

坑 2:零件串色 —— 杂色斑点的原因

现象:发丝边缘出现紫色/绿色的小杂色,皮肤上冒出白色斑块。

原理:图集是在一张大图上把几百个零件挤在一起铺,最小间隙可能只有 1~2 像素。你如果拿整张图去超分,模型在处理某个零件边缘时,会把紧挨着的隔壁零件的颜色也"参考"进来——因为对模型来说它们是连在一起的像素。

粉色的头发旁边是紫色的衣服,模型就把两地颜色一混,你的发丝边缘就紫了。

解决办法:按零件一块一块单独处理。每块裁出来后,四周用镜像的方式补一圈边(把自己边缘的像素翻出去),模型看到的全是"自己的颜色",绝不会看邻居。

np.pad(rgb, ((pad, pad), (pad, pad), (0, 0)), mode="reflect")   # 镜像扩边

坑 3:无缝图集被切断 —— 锯齿的第二个原因

现象:零件连接的地方有硬阶梯,放大看像楼梯。

原理:见 4.2 的比喻。零件是拼贴画被切成了很多矩形,你在切缝处裁切就是破坏画面。

怎么自动发现:统计每个零件矩形四条边上有没有不透明像素。

# 对每个零件,看它的上/下/左/右四条边
m = max(上边最大alpha, 下边最大alpha, 左边最大alpha, 右边最大alpha)
if m > 40:  # 边界上有东西 → 说明是被切断的
    hit += 1

实测数据:

模型 边界有内容的零件占比 结论
figure_1978 5 / 326 = 1.5% 有间隙,用逐零件
风织香彩夏夜引光 406 / 454 = 89.4% 无缝,必须用整图

解决办法:整图模式。整张图切成 512×512 的块去处理,块之间留 32 像素重叠,最后只取每块中间的部分拼回去 —— 这样每条块边界两侧的像素都享受了同样的上下文,拼起来没有任何接缝。

坑 4:alpha 被"收紧" —— 锯齿的头号原因(我踩过的坑)

现象:所有零件的边缘都变得像剪纸,过渡消失,放大看是一圈硬阶梯。

原理:

真实的美术图里,零件边缘不是"有/没有"两种状态,而是有一条几个像素宽的渐变带(alpha 从 255 慢慢降到 0)。正是这条渐变带让边缘看起来柔和。

早期的做法会想:既然要"锐利",那我给 alpha 套一个对比度拉伸,把渐变带压窄,边缘不就清楚了?

alpha' = clip((alpha - 0.5) × 1.72 + 0.5, 0, 1)   // strength=0.6 时的效果

压缩了 42% 的过渡带。结果呢:

alpha 收紧强度 半透明像素占比 ALPHA 差异>60 观感
0.6(旧) 1.44% 17451 px 边缘像剪纸,有台阶
0.35 1.65% — 还是偏硬
0.20 2.01% — 可以接受
0(正确) 4.55% 863 px 过渡自然

结论:alpha 通道绝对不要做任何"锐化/收紧",原样放大就好。

那边缘的清晰感从哪来?从杜绝串色和不切断内容来 —— 不是从数学上压窄渐变带来。

坑 5:周期纹理 —— 纱网变粗或消失

现象:原本很细的纱网/条纹布/网点,超分后变成粗细不均的条带(像摩尔纹),或者干脆糊没了。

原理:1 像素宽的条纹,周期就是 1px。你把它放大 2 倍,它应该变成 2 像素宽 —— 但这需要精确复制。超分模型是"看着图猜细节",它看到一片高频条纹会判断成噪声,要么平滑掉(消失),要么按自己的理解重画(变粗、粗细不均)。

解决办法:这类零件不让模型处理,直接用 NEAREST(最近邻)放大:

  • 1px 的条纹 → 精确变成 2px 的条纹,频率一点不变
  • 整图模式下,模型会把条纹糊掉,所以要在整图处理完之后用 NEAREST 的结果覆盖回去

怎么自动识别:算零件的边缘密度和方向性。

if 面积 > 20000 and 边缘密度 > 0.40 and 水平梯度/垂直梯度 > 5:
    → 判定为周期性垂直条纹,改走 NEAREST

实测在 457 个零件里只命中 1 个(0B_Scene1_4,那条粉白竖条纹),耗时 0.03 秒。

坑 6:块化 —— 头发"细节丢失"

现象:原本细腻的渐变(发丝内部从亮到暗的过渡)变成了一块一块平涂的颜色。

原理:anime6B 是用动画帧训练的。动画的特点是赛璐璐风格——大面积平涂 + 硬边高光。模型学会这个风格之后,遇到你原图里那种细腻的写实渐变,它会"帮你去掉多余的过渡",压成色块。

缓解办法:把模型输出和纯插值放大的结果混合一下:

最终 = 模型结果 × (1 - blend) + 插值结果 × blend    # blend = 0.2

插值结果虽然不清晰,但它忠实地保留了原图的低频信息(渐变走向)。混 20% 进来,既能保住清晰度,又不会把渐变压平。

实测 figure_1978 的 p99 差异从 81.8 降到 57.4(极端偏差减少)。

注意:blend 越大越软。0.2 是温和值,0.4 以上会明显感觉到"没变清晰"。


6. 完整操作流程

第 1 步:备份

# 把原始 png 复制一份
Copy-Item "模型目录\贴图.png" "模型目录\贴图_2048_原始.png"

脚本本身不会改源目录,但你会手动替换文件做对比,所以先备份。

第 2 步:探测

python spine_probe.py "模型目录"

确认三件事:需要几倍 / 是不是 PMA / 半透明占比多少。

第 3 步:超分

# 最简(auto 自动选模式)
python spine_upscale.py --dir "模型目录" --out "模型目录_4096"

# 显式指定模式(确认过结构时)
python spine_upscale.py --dir "模型目录" --out "模型目录_4096" --layout whole

# 保渐变(渐变面积大的插画风模型)
python spine_upscale.py --dir "模型目录" --out "模型目录_4096" --blend 0.2

输出目录是一个完整可用的模型目录(json、skel、atlas 都复制好了)。

第 4 步:质检

python spine_verify.py --orig "模型目录\贴图.png" ^
                       --res  "模型目录_4096\贴图.png" ^
                       --atlas "模型目录\xxx.atlas" ^
                       --mode pma --out "_check"

看三个数字(见第 7 节)。

第 5 步:实机验证

打开 Live2DViewerEX → EX Studio → 选模型文件夹 → 点 .json。

验证技巧:新旧版本用同一个视图对比。 换图之后点一下文件列表里的任意文件,EX Studio 会重新加载纹理,视图保持不变,这样对比才公平。


7. 质检怎么看

spine_verify.py 会输出合成预览图和一组数字。

7.1 三背景合成(最直观)

生成 bg_orig_mag.png / bg_res_mag.png(品红背景)以及黑色、白色版本。

为什么要三种背景:黑边在黑背景下看不见,白边在白背景下看不见。品红最刺眼,能暴露所有问题。

7.2 三个数字

指标 含义 健康值
RGB 差异>30 占比 颜色改动幅度较大的像素比例 < 2% 健康,< 1% 优秀
RGB p99 99 分位的颜色差异(极端偏差) < 40
ALPHA 差异>60 透明度改动较大的像素数 越接近 0 越好,直接对应"边缘有没有被改坏"

为什么 ALPHA 差异是关键指标:透明度的改动会直接改变零件轮廓。数字大了,说明边缘被动过,很容易就是锯齿。

7.3 前后特写

cmp_NN_零件名.png 是面积最大的几个零件的前后对比,直接看就行。


8. 实战数据

8.1 两个真实模型

模型 模式 零件数 耗时(CPU 8线程) RGB diff>30 RGB p99 ALPHA diff>60
figure_1978 region / alpha 0.6(旧) 326 423 s 6.57% 81.8 17451
figure_1978 region / alpha 0 + blend 0.2(新) 326 932 s 7.16% 57.4 863
风织香彩夏夜引光 region(旧,89% 边界被切断) 454 294 s 8.98% 69.0 7904
风织香彩夏夜引光 whole + 周期保护(新) 454 229 s 0.81% 29.0 0

读法:

  • 第一行到第二行:只改 alpha 策略,ALPHA 差异 从 17451 掉到 863(降 95%),这就是"锯齿消失"的量化证据
  • 第三行到第四行:只改模式(region → whole),RGB 差异 从 8.98% 掉到 0.81%(降 91%),alpha 差异归零

8.2 文件体积参考

模型 原图 4096 结果
figure_1978 2.6 MB / 2048² 14.3 MB / 4096²
风织香彩夏夜引光 2.2 MB / 2048² 6.7 MB / 4096²

体积涨 3~6 倍是正常的(像素数涨 4 倍,PNG 压缩率因内容而异)。


9. EX Studio 着色器必须配对

这是**唯一会让"图明明对的却出现白边"**的地方。

.json 里有个 options.shader_type:

shader_type 着色器名称 期望的贴图数据
0 Skeleton 预乘(PMA)
1 Skeleton (Straight Alpha) 直通(straight)

用错的后果(实测:边缘纯白像素,alpha=0.53,背景深灰,正确显示值应该是 153):

组合 显示值 后果
straight 数据 + Straight 着色器 153 :white_check_mark: 正确
straight 数据 + PMA 着色器 255 :cross_mark: 边缘爆白
PMA 数据 + PMA 着色器 153 :white_check_mark: 正确
PMA 数据 + Straight 着色器 89 :cross_mark: 边缘变暗

傻瓜判断法:

  • 超分时用了 --mode pma(默认)→ 配置不用动,和原图一样
  • 超分时用了 --mode straight → 脚本会自动把 shader_type 改成 1

换回 2048 原图对比时记得切回去(原图集通常是 PMA 数据,要配 Skeleton),否则你会看到一圈暗边,误以为是图坏了。


10. 排查表

现象 原因 解决
模型整体糊、放大更糊 贴图尺寸小于 atlas 声明 跑探测,按倍数超分
边缘一圈黑 没反预乘 确认探测显示 PMA 检测: True
边缘一圈白/亮线 着色器与数据不配对 见第 9 节
零件连接处锯齿 ① alpha 被收紧 ② 无缝图集用了逐零件模式 --alpha-strength 0;--layout whole
发丝/皮肤出现杂色斑点 零件串色 有间隙的图集用 --layout region
某些零件残缺/变透明 零件矩形重叠,后写的覆盖了先写的 脚本已用「alpha 大者优先」;检查是否 --limit 截断
条纹/纱网变粗或消失 周期纹理被模型重画 确认 --periodic-guard 是开的
大面积渐变被"块化" 模型风格化 --blend 0.2
局部冒出文字状/奇怪的形状 模型幻觉 换 anime6B,别用 x4plus
超分后和原来没区别 atlas 声明 == png 尺寸 本来就不用超分
结果全空白 输入的 png 已经是 4096,又被放大一次导致坐标错位 用 2048 原图重新跑

11. 附录:为什么"看起来更清晰"这件事本身有上限

这一段是给想深究的人看的,跳过也不影响使用。

你截的图是从 EX Studio 的渲染画面截的,而渲染画面有固定尺寸(比如 1793×1009)。模型在画面里占的区域可能只有 400×600 像素。

这时候:

  • 2048 的贴图 → 显示时缩小约 1.2 倍 → 几乎 1:1 采样,细节全在
  • 4096 的贴图 → 显示时缩小约 2.4 倍 → 引擎会走 mipmap,用降过采样率的层级,多出来的细节被平均掉了

所以:在"缩小显示"的场景下,超分的收益主要是"抗锯齿更好"和"放大时更清晰",而不是"同一个缩放下看得更细"。

想让超分的效果明显,两种办法:

  1. 把 EX Studio 的缩放调到 1.3 以上(模型变大,纹理按 1:1 或放大采样,4096 的优势立刻体现)
  2. 提高各向异性过滤等级:EX Studio 的 选项 → 各向异性过滤等级,默认 2,可以试 4(官方文档提到这个值太大会出现"嘴角黑边",所以别拉满)

这也是为什么经常会出现"明明超分了怎么还是差不多"的感觉——不是超分没用,是看的姿势不对。

5 个赞