把商业 Live2D 模型当免费标注机用:渲染器本来就知道每一小块贴图属于哪个部位、被谁挡住、画在第几层,所以不需要人工就能拿到「透视」监督。拿这批数据微调 SDXL,学会把一张平面插画拆成 19 个已补全遮挡区域的 RGBA 部件图层,再微调 Marigold 预测每个部件的「伪深度」(其实就是图层叠放顺序归一化成 0–1 的浮点数)。19 个图层之间像视频扩散的帧一样做 attention,保证内容不漏不重。
这篇论文的词都很抽象,但在它的具体任务里每个词都有一个很实在的对应物:
| 论文用词 | 在这篇论文里具体是什么 |
|---|---|
| 2.5D | Live2D 那种做法:2D 图层 + 变形器 + 叠放顺序,不是真的 3D 网格 |
| ArtMesh | Live2D 里一小块贴着图的三角网格,粒度是「一撮头发」「一条丝带」。一个角色有几百个 |
| 语义图层 \(L_k\) | 一张 1024×1024 的 RGBA 图,内容是 19 类里的某一类(头发 / 脸 / 上衣 / …),包含被遮住的部分,由模型脑补 |
| pseudo-depth | 不是真实 3D 深度。是 ArtMesh 的绘制顺序整数索引做 min–max 归一化后的 [0,1] 浮点数,只表达「谁画在谁前面」 |
| see-through | 推断被挡住区域的画面内容(occlusion completion),也就是替美术做脑补 |
| stratification / sandwich | 同一语义类必须既在某层之前又在它之后,典型就是前发 / 脸 / 后发 |
| stratum(分层片) | 把一个语义图层按伪深度用 K-Means(K=2)切成「前片 / 后片」后的其中一片 |
| transparency decoder \(D_a\) | 额外训练的解码器,从 SDXL 的 latent 预测 alpha 通道,让冻结的 SDXL VAE 能输出 RGBA |
| Body Part Consistency Module | 插在 U-Net 每个空间 attention 之后的一层 attention,沿「部件」这个维度做,让 19 个图层互相看见彼此当前的预测 |
没有任何现成数据集提供「19 类全身精细解析 + 遮挡区域 + 碎片级绘制顺序」,所以论文一半的篇幅在造数据。整条链路是弱监督起手、几何量化、自我迭代:
flowchart LR
L2D["Live2D model"] --> AM["ArtMesh visibility masks"]
L2D --> Z["Draw order index"]
L2D --> IMG["Rendered illustration"]
IMG --> TAG["Danbooru tagger"]
TAG --> CAM["GradCAM++ maps"]
CAM --> VOTE["Per-mesh score voting"]
AM --> VOTE
VOTE --> SEED["Seed labels"]
SEED --> SAM["SAM-HQ 19 decoders"]
SAM -->|"self refine"| SEED
SAM --> PROJ["Project back to meshes"]
PROJ --> HID["Hierarchy propagation"]
HID --> GUI["Human verify GUI"]
Z --> PD["Pseudo depth 0 to 1"]
GUI --> GT["2.5D ground truth"]
PD --> GT
先用 wd-eva02-large-tagger-v3 给图打 Danbooru 标签,再把过细的标签按人工定义的层级映射到 19 类(blonde_hair、ponytail、ahoge 全归到 hair)。对每个标签算 GradCAM++ 激活图,聚合成类别响应。这些响应低分辨率又弥散,直接逐像素 max-pooling 会得到破碎噪声的分割。关键一步是用 Live2D 的 ArtMesh mask 做几何量化:对每个 mesh 碎片,在它的可见区域内对 19 类分别算空间平均激活分数,取最高分的类作为整个碎片的标签。噪声被平均掉,边界自动贴齐美术原始笔画。
种子标签还会把马尾、肩带、小丝带混淆。论文把 SAM-HQ 改造成闭集语义分割器:删掉原来的可提示解码器,换成 19 个独立 mask decoder,每个专管一类;训练时把 prompt embedding 直接置零,逼每个 decoder 自己从图像 embedding 里学类别特征。然后跑自我精炼循环——模型预测新标签 → 新标签当监督更新训练集 → 每轮都重做一次 ArtMesh 边界贴齐。模型和数据互相抬升。
可见碎片按像素多数投票直接拿到类别;完全被挡住的碎片拿不到任何监督(后脑头发、衣服底下的手臂)。论文用三级启发式沿 ArtMesh 层级传播:
PartHair、PartHand 这类与已标注碎片共有的子串,就继承那个标签。最后人工复核:论文自制了带「透视」能力的标注 GUI,可以按语义类或按层级切换任意碎片的可见性、预览选中碎片及其同类组,所以只需要改少数几个碎片。全数据集人工时间合计约 12 小时。
| 项目 | 数值 |
|---|---|
| 模型总数(姿态增强后) | 9,102 |
| 训练 / 验证 / 测试 | 7,404 / 851 / 847 |
| 语义类别数 | 19 |
| 来源平台 | ArtStation, Booth, DeviantArt |
| 增强方式 | 跑 Live2D 标准「角度跟随」动画,插值出上下左右与对角朝向,产生非刚性形变。ArtMesh 的语义身份在形变下不变,所以多姿态标签完全免费 |
伪深度的定义很朴素。设模型的 ArtMesh 集合为 \(M=\{m_1,\dots,m_M\}\),\(z(m_i)\) 是碎片 \(m_i\) 的整数绘制顺序索引,则
\[ d(m_i) = \frac{z(m_i) - z_{\min}}{z_{\max} - z_{\min}} \in [0,1] \]关键是这个量记在碎片级而不是 19 类的语义级——正因为粒度细到单根头发,模型才学得到「同一语义层内部还有前后关系」。
flowchart LR
IN["Single illustration"] --> UNET["SDXL UNet stage 2"]
CLS["19 class embeddings"] --> UNET
UNET --> LAT["19 stacked latents"]
LAT --> VAE["Frozen SDXL decoder"]
VAE --> TD["Transparency decoder"]
TD --> RGBA["19 inpainted RGBA layers"]
IN --> MG["Marigold stage 2"]
CLS --> MG
MG --> PDM["19 pseudo depth maps"]
RGBA --> KM["KMeans K=2 split"]
PDM --> KM
KM --> LAMA["LaMa inpaint new holes"]
LAMA --> OUT["Layered PSD / 2.5D rig"]
骨干选 SDXL,图的是原生 1024×1024 分辨率和对动漫高频细节(线稿、平涂)的保真度。透明通道沿用 LayerDiffusion 的 Latent Transparency 思路,但做了简化:由于扩散过程本身不需要透明输入,SDXL 的 VAE 编码器 \(E_x\) 与解码器 \(D_x\) 完全不动。只把部件的 RGB 部分编码成 \(z_k = E_x(L^{(c)}_k)\),再新训一个透明解码器 \(D_a\),同时以 latent 和重建的 RGB 为条件,预测 alpha 和「padded Gaussian」图(\(\alpha=0\) 处 RGB 无定义,用迭代高斯模糊填进去,改善合成时的抗锯齿):
\[ [\hat P_k, \hat L^{(\alpha)}_k] = D_a\!\left(z_k,\ \hat L^{(c)}_k\right) \]损失是 L2 重建加一项 PatchGAN 判别器损失(\(\lambda_{\text{disc}}=0.01\))让边界更锐利:
\[ \mathcal{L}_{\text{dec}} = \big\lVert L^{(c)}_k - \hat L^{(c)}_k \big\rVert_2 + \big\lVert L^{(\alpha)}_k - \hat L^{(\alpha)}_k \big\rVert_2 + \lambda_{\text{disc}} \mathcal{L}_{\text{disc}} \]直观理解:把透明度整个外包给一个小解码器,latent 空间分布原封不动,所以后续扩散微调不用适应新分布,难度大降。\(D_a\) 还是从 sd-forge-layerdiffuse 初始化的,自带通用透明先验。
把 SDXL U-Net 微调成条件去噪器。不用自由文本 prompt——19 个语义类各用一个离散 embedding \(\tau_C(c_k)\) 经 cross-attention 注入;源插画经图像条件编码器 \(\tau_I(I)\) 用 zero-convolution(ControlNet 式)注入编码器层。标准 \(\epsilon\)-prediction:
\[ \mathcal{L}_{\text{stage1}} = \mathbb{E}_{I,k,t,\epsilon}\left[\left\lVert \epsilon - \epsilon_\theta\!\left(z^{(t)}_k, t;\ \tau_I(I), \tau_C(c_k)\right)\right\rVert_2^2\right] \]这一阶段主要是抹掉原版 SDXL 的域差,把单个部件抠得高保真。
单部件抠得好,但19 个部件合起来不成立:模型会把某些语义层留成近乎空白,把有歧义的区域全丢给视觉上相邻的部件。论文的例子是上衣和袖子颜色纹理接近,上衣层直接被忽略。诊断是:模型缺少对「哪些区域已经被别的图层解释掉了」的全局视野。
解法把分解重写成联合去噪问题:不再独立预测每个 \(z_k\),而是把 19 个部件 latent 沿新增的部件维 \(c\) 堆成 \(Z^{(t)}=[z^{(t)}_1,\dots,z^{(t)}_N]\) 一起去噪——这个维度在数学上就相当于视频扩散里的时间维。在 U-Net 每个空间 attention 块之后插入 Body Part Consistency Module,沿 \(c\) 做 attention,让每层看见其他所有层的当前预测:
\[ \mathcal{L}_{\text{stage2}} = \mathbb{E}_{I,t,\epsilon}\left[\left\lVert \epsilon - \epsilon_\phi\!\left(Z^{(t)}, t;\ \tau_I(I), \tau_C(C)\right)\right\rVert_2^2\right] \]一个有意思的设计选择:沿 \(c\) 维不加任何卷积,只有 attention。理由是语义层之间基本独立、只在遮挡边界处耦合,所以需要的是全局重分配机制而非局部混合;而每片自己的类 embedding \(\tau_C(c_k)\) 充当局部语义约束,防止内容串到别的部件去。全局机制 + 局部约束,刚好对应「把歧义区域分配出去」和「每片仍然是它该是的部件」这两件事。
伪深度预测复用完全相同的配方,只换骨干、去掉透明 VAE:骨干是 Marigold(仿射不变单目深度估计器)。阶段一按类别条件 \(c_k\) 微调出单部件的稠密伪深度 \(D_k\),让它的几何先验适配动漫拓扑(发丝相对于脸的曲率);阶段二解决各图独立预测导致的尺度不一致——把 19 张深度图的 latent 堆起来,重用 Body Part Consistency Module 沿部件维做 attention,得到统一、全局有序的深度层级。
值得注意的是:伪深度不代表任何真实 3D 深度,但自然图像上训出的深度先验依然有效,说明「谁在前谁在后」这个序关系的几何直觉是跨域可迁移的。
有了 RGBA 图层和伪深度,原则上可以直接 z-buffer 合成,但那会把一个语义部件打碎成一堆互不相连的像素块,2.5D 管线无法操作。另一个极端是给每个部件一个代表深度值整体排序,那就表达不了类内交错的「三明治」。
折中办法:把语义层切成少量深度片。论文观察到自遮挡部件的伪深度天然呈分离的众数(前发 vs 后发),于是在 alpha mask 内部对伪深度值跑 K-Means(默认 K=2)切成前后两片。导出 PSD 时对 Hair、Handwear、Topwear、Bottomwear 应用此规则。切开会产生新的遮挡边界,所以额外跑一次通用 inpainting(LaMa)填新露出的区域——这里的错误通常落在三明治的最后一片,合成后基本看不出来。
这点容易被 Figure 1(d) 和 Figure 8/9 的动图误导。本文模型的输出只有两样:19 张补全好的语义 RGBA 图层,和 19 张伪深度图。既不生成任何网格,也不生成任何运动。文中出现的 mesh 和动画分别来自三个不同的地方:
| 产物 | 谁做的 | 依据 |
|---|---|---|
| 19 层补全 RGBA + 伪深度(含分片与 LaMa 补洞) | 本文模型(SDXL + Marigold 两条微调支路) | 第 4 节全部内容;导出为分层 PSD |
| 训练集里的 ArtMesh 与绘制顺序 | 美术做的——从 ArtStation / Booth / DeviantArt 收来的商业 Live2D 模型,渲染器只负责把它读成标注 | 第 3.1.1 节:美术先在 Photoshop / Krita 里分层作画,Live2D 再编译成 ArtMesh |
| Figure 8 木偶动画的网格与绑定 | Animated Drawings 管线自动做——它自己从角色 mask 三角化、预测关节、ARAP 变形;本文只是把分层塞进去替掉它原来的单层输入 | 第 5.2 节:“We integrate our stratified layers into an Animated Drawings [40] pipeline” |
| Figure 8 的运动 | 外部视频序列驱动,加确定性 2D 形变、合成与实时物理(弹簧头发)。全自动,但这套机制不是本文的贡献 | 第 5.2 节:“driven by a video sequence”“deterministic 2D deformation and compositing” |
| Figure 9 talking-head | 作者自己实现的实时系统:3D 面部关键点驱动 2D 变形器 | 第 5.2 节:“We drive our decomposed layers using 3D facial landmarks” |
| 附录 D 的动画草稿与视觉效果 | 画师手做:6 位画师拿导出的 PSD 自己打关键帧与时序,一份高质量草稿约 30–60 分钟 | 附录 D:“author animation timings and representative visual effects based on the layer stack” |
论文自己把这条边界划得很死:结尾把「从分解结果预测自动动画时序」列为 future work;画师要求的形变区域提示、运动曲线、时序模板,原话是 we do not address these aspects in the current work。
所以准确的定位是:这篇出的是生产资产(分层 PSD + z 序),不是动画,也不是 rig。运动要么交给现成的确定性 2D 变形管线,要么交给人。
把「图层叠放顺序」从离散的图结构改写成碎片级的连续伪深度,交错遮挡就变成一个可回归的稠密预测问题;而这个真值在每个商业 Live2D 模型的渲染器里本来就是现成的。
评测的主体是自己数据集的 847 个测试模型:用预测的图层和伪深度重新合成整张角色图,与输入比重建质量(LPIPS / PSNR / SSIM / FID),同时用真值可见性 mask 比部件级分割精度(Dice loss / Mask MSE)。
| 方法 | LPIPS ↓ | PSNR ↑ | SSIM ↑ | Mask Dice ↓ | Mask MSE ↓ | FID ↓ |
|---|---|---|---|---|---|---|
| SAM + LaMa | 0.2880 | 12.2802 | 0.8445 | 0.4336 | 0.1020 | 81.1419 |
| Mask-conditioned (SAM mask) | 0.2143 | 14.6300 | 0.9090 | 0.8469 | 0.1470 | 74.1500 |
| Ours 去掉 Consistency Module | 0.1952 | 16.2350 | 0.9053 | 0.6480 | 0.0640 | 16.7069 |
| Ours 完整版 | 0.1549 | 18.2965 | 0.9230 | 0.3855 | 0.0354 | 18.3700 |
高亮为该列最优。注意 FID 是唯一反向的一项:加上 Consistency Module 后 FID 略微变差(16.71 → 18.37),论文的解释是强制全局一致会压缩输出多样性——这个解释是合理的,但也意味着该模块是在用一点生成多样性换完整性。
| 阶段 | 硬件与时长 | 超参 |
|---|---|---|
| 多解码器 SAM 微调 | 4× RTX 4090, ~68 h | 16k steps, bs 32, AdamW lr 2e-4, wd 0.1 |
| 图层扩散 阶段一 | 8× H200, ~24 h | 20k steps, bs 64, lr 4e-5, wd 0.01, warmup 500, 1024² |
| 图层扩散 阶段二 | 8× H200, ~129 h | 同上,含 Consistency Module 联合训练 |
| 伪深度 阶段一 | 8× H200, ~16 h | 768², 其余同上 |
| 伪深度 阶段二 | 8× H200, ~115 h | 同上 |
| 推理(单图) | 1× RTX 4090 | 图层分解 ~74 s(1024²)+ 深度 ~10 s |
两个「加全局一致性」的阶段分别吃掉 129 h 和 115 h,是阶段一的 5–7 倍。沿部件维 attention 把有效 batch 放大了 19 倍,代价确实不小。推理 84 s 对资产预处理来说完全可接受。