feat: 新增 SDMatte 精细抠图节点,实测复现官方效果

基于 SDMatte(vivo 相机研究院,ICCV 2025)的交互式抠图节点,
擅长发丝、绒毛、玻璃、烟雾等常规抠图模型处理不好的边缘。

实现要点:
- 严格照搬官方 configs/SDMatte.py 的推理配置(bbox 视觉提示、fp32、1024 分辨率),
  不做任何启发式后处理,输出即模型原始 alpha
- 内置官方 LongfeiHuang/SDMatte 的配置文件,无需下载 SD 2.1 权重,也无需联网。
  官方 load_weight=False 只用 config 搭骨架,全部权重由 checkpoint 覆盖;
  原版 SD 2.1 的 config 缺 bbox_time_embed_dim 等三个专有字段,缺字段时直接报错而非猜测
- 同时支持官方 .pth(12.1GB)与社区 .safetensors(5.19GB)。两者模型权重实测
  逐像素完全相同,pth 多出的 6.5GB 是 detectron2 的优化器状态;读 pth 时以受限
  Unpickler 只解析 model 段,内存占用与 safetensors 相当
- 自动适配 transformers 5.x 移除 text_model 包装层导致的键名漂移,
  避免 text_encoder 的 372 个权重被静默丢弃
- 加载后校验 1316 个张量全部对齐,有任何未覆盖/未使用的权重即中止报错
- 默认开启注意力分片,1024 下显存峰值由约 15.5GB 降至 9.1GB,速度反而略快

实测(官方效果图中的羊驼,对比官方公布 alpha):MAD=0.0113。
同图同权重下 ComfyUI-SDMatte 为 MAD=0.0884,相差 7.8 倍,主因是其
aux_input="trimap" —— 官方 aux_input_list 只含 point_mask/bbox_mask/mask,
trimap 从未作为视觉提示参与训练,且该分支坐标恒为 [0,0,1,1]、定位信息丢失。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
rui40000
2026-07-16 11:16:36 +08:00
co-authored by Claude Opus 4.8
parent 1e42521fec
commit e1a575fcda
19 changed files with 100207 additions and 0 deletions
+146
View File
@@ -23,6 +23,7 @@ Rui-Node🐶 是一个功能丰富的 ComfyUI 节点集合,提供图像处理
### 🤖 AI模型类
- [千问编辑图像生成 / Qwen Edit Image Generation](#4-千问编辑图像生成--qwen-edit-image-generation)
- [SDMatte 精细抠图 / SDMatte Interactive Matting](#17-sdmatte-精细抠图--sdmatte-interactive-matting)
### 📝 文本处理类
- [镜头分词器 / Shot Splitter](#5-镜头分词器--shot-splitter)
@@ -537,6 +538,151 @@ Rui-Node🐶 是一个功能丰富的 ComfyUI 节点集合,提供图像处理
6. 所有节点名称采用中英双语显示
7. 遮罩处理节点自动处理尺寸不匹配问题
### 17. SDMatte 精细抠图 / SDMatte Interactive Matting
基于 [SDMatte](https://github.com/vivoCameraResearch/SDMatte)(vivo 相机研究院,ICCV 2025)的交互式抠图节点。
擅长发丝、绒毛、玻璃、烟雾等常规抠图模型处理不好的边缘。
包含两个节点:
| 节点 | 作用 |
|---|---|
| **SDMatte 加载器** | 载入权重,构建网络并常驻显存 |
| **SDMatte 精细抠图** | 用视觉提示(框/掩码/点)驱动模型输出 alpha |
#### 模型准备
把权重放到 `ComfyUI/models/SDMatte/` 下即可,两种格式任选其一:
- `SDMatte_plus.pth` — 官方发布,12.1GB,[LongfeiHuang/SDMatte](https://huggingface.co/LongfeiHuang/SDMatte)
- `SDMatte_plus.safetensors` — 社区转换,5.19GB,[1038lab/SDMatte](https://huggingface.co/1038lab/SDMatte)
> **这两个文件的模型权重逐比特完全相同**,不必纠结选哪个。
> 已实测比对全部 1316 个张量:键名、形状、精度(均为 F32)、数值全部一致,无一例外。
> 官方 pth 是 detectron2 的训练检查点,顶层为 `{"model", "trainer", "iteration"}`,
> 多出的约 6.9GB 是 `trainer` 里的优化器状态与梯度缩放器,推理不参与。
> 换用 pth **不会带来任何质量提升**。本节点两种格式都支持,读 pth 时只解析 `model` 段,
> 内存占用与 safetensors 相当。
**不需要下载 Stable Diffusion 2.1 的权重。** SDMatte 虽以 SD 2.1 为骨架,但官方推理配置
(`configs/SDMatte.py` 中 `load_weight=False`)只用配置文件搭出网络结构,全部权重随后由
SDMatte 检查点覆盖。官方 HuggingFace 仓库本身也只发布 `.pth` 加若干 `config.json`,
不含任何 SD 权重。所需配置已随本节点一起分发,开箱即用、无需联网。
#### 参数说明
**SDMatte 加载器**
| 参数 | 说明 |
|---|---|
| `ckpt_name` | `models/SDMatte/` 下的权重文件 |
| `precision` | `fp32`(默认,与官方测试配置一致)/ `fp16`(省显存,但 SD 2.1 的 VAE 半精度下易溢出) |
| `device` | `auto` / `cpu` |
| `attention_slicing` | 默认开启。1024 下显存峰值从约 **15.5GB 降到 9.1GB**,实测速度反而略快,输出差异仅 1e-6 量级 |
> 显存参考(fp32 @ 1024,实测于 RTX 5090):开分片约 **9.1GB**,关分片约 **15.5GB**。
> 12GB 显存的卡请保持分片开启。
**SDMatte 精细抠图**
| 参数 | 说明 |
|---|---|
| `mask` | 指示抠哪个目标的提示掩码,**不必精确**,粗略覆盖主体即可 |
| `prompt_type` | 视觉提示类型,见下表 |
| `inference_size` | 默认 `1024`,与官方测试一致 |
| `is_transparent` | 玻璃、纱、烟雾等透明物体**务必打开** |
| `caption` | 可选文本描述,留空即官方默认行为 |
| `point_radius` | 仅 `point_mask` 生效,默认 35 |
`prompt_type` 选择:
| 取值 | 含义 | 适用 |
|---|---|---|
| `bbox_mask` | 取掩码外接框作为提示 | **默认,官方测试脚本的主路径,通常最稳** |
| `mask` | 直接用掩码本身 | 已有较准的粗分割时 |
| `point_mask` | 在掩码内随机取 10 个点 | 复现论文的点提示实验 |
| `auto_mask` | 不给定位信息 | 画面只有单一主体 |
#### 典型接法
```
加载图像 ──────────────┬──> SDMatte 精细抠图 ──> alpha (MASK)
│ ▲ └──> cutout (IMAGE)
任意分割节点 ──> mask ──┘ │
SDMatte 加载器 ───────────────────┘
```
`mask` 可以来自任何粗分割来源(SAM、rembg、手绘遮罩皆可)——SDMatte 的职责正是把粗糙边缘细化。
#### 实测数据
用官方效果图中的羊驼原图(绒毛边缘)跑本节点,与官方给出的 GT alpha 对比:
| 指标 | 数值 |
|---|---|
| MAD(平均绝对误差) | 0.0113 |
| MSE | 0.0026 |
| SAD | 0.807 千像素 |
(GT 取自官方效果图截图,含有损压缩与水印,故存在固有误差下限。)
各配置对输出的实际影响(透明玻璃杯,差异像素指偏差 > 0.05 的占比):
| 对照项 | 平均差 | 差异像素占比 |
|---|---|---|
| `inference_size` 1024 vs 512 | 0.082 | 32.4% |
| `is_transparent` 关 vs 开 | 0.059 | 25.0% |
| 官方 `[F,T,F]` vs 误用 `[T,T,T]` 条件分配 | 0.026 | 18.8% |
结论:**分辨率影响最大,建议保持 1024**;抠透明物体时 `is_transparent` 必须打开。
#### 与 ComfyUI-SDMatte 的横向实测
同一张图、同一份权重、同一台机器,对跑 [ComfyUI-SDMatte](https://github.com/flybirdxx/ComfyUI-SDMatte)
与本节点,以官方公布的 alpha 为参照:
| 实现 | 配置 | MAD ↓ |
|---|---|---|
| **本节点** | 官方 `configs/SDMatte.py`,bbox 提示,fp32 | **0.0113** |
| ComfyUI-SDMatte | 默认(trimap 提示 + `mask_refine`) | 0.0884 |
| ComfyUI-SDMatte | 关闭 `mask_refine` | 0.0885 |
**相差 7.8 倍**,且其输出肉眼可见地发灰、边缘晕开。
主因是**视觉提示类型**:官方 `configs/SDMatte.py` 固定 `aux_input="bbox_mask"`,
而其 `aux_input_list` 只含 `point_mask` / `bbox_mask` / `mask` —— **trimap 从未作为视觉提示参与训练**。
ComfyUI-SDMatte 传 `aux_input="trimap"`,把模型推到了没训练过的输入模式上,
且该分支的 `trimap_coords` 恒为 `[0,0,1,1]`,定位信息全部丢失。
开不开它的 `mask_refine` 几乎不影响这一结论(0.0884 vs 0.0885),说明问题不在后处理。
#### 实现要点
若与其它 SDMatte 实现效果对不上,按影响从大到小排查:
1. **视觉提示类型**(影响最大)。必须用官方训练过的 `bbox_mask` / `mask` / `point_mask`,
并传入真实的归一化坐标。用 trimap 当视觉提示是模型没见过的用法。
2. **UNet 配置来源**。SDMatte 在标准 SD 2.1 的 UNet 配置上额外定义了
`bbox_time_embed_dim` / `point_embeddings_input_dim` / `bbox_embeddings_input_dim` 三个字段。
误用原版 SD 2.1 的 `config.json` 会缺这些字段,只能猜默认值,猜错则相应权重被
`strict=False` 静默丢弃。本节点直接分发官方配置,并在缺字段时**直接报错而非猜测**。
3. **transformers 版本**。官方权重用 transformers 4.x 保存,`CLIPTextModel` 内部裹了一层
`text_model`;transformers 5.x 起该层被移除,导致 text_encoder 的 372 个权重键名对不上、
被整体静默丢弃、停留在随机初始化。本节点会按当前环境自动增删该前缀。
4. **条件分配**。官方 `use_encoder_hidden_states_list=[False, True, False]` 决定 UNet
下采样/中间/上采样三段各接收哪种条件,漏传会退化成 `[True, True, True]`。
实测单独影响不大(羊驼 MAD 0.01135 → 0.01148),透明物体上更明显。
5. **权重对齐校验**。本节点在加载后校验键的完整性,一旦有权重未被覆盖或未被使用就**中止并报错**。
这类问题不会让模型崩溃,只会让输出质量悄悄下降,是最难排查的一类,因此宁可停下也不放行。
6. 全程 fp32、1024 分辨率,且**不做任何启发式后处理**(不做阈值裁剪、对比度拉伸之类的"优化"),
输出即模型原始 alpha。
---
## 🐕 关于 Rui-Node🐶
Rui-Node🐶 致力于为 ComfyUI 用户提供实用、高效的节点工具集。🐶 是我们的项目标志,代表着忠诚、友好和可靠。