fix: 治理抠图白边——推荐改用模型 alpha,新增收边与颜色反溢出
用户反馈边缘一圈白边。诊断:按 alpha 分层统计 RGB 均值, alpha 0.05~0.3 的边缘像素 RGB 高达 0.989(角色本体只有 0.363), 证实边缘掺了背景白 —— 我只设了 alpha 却没动 RGB。 新增两个参数: - decontaminate 颜色反溢出,按 观察色=前景×a+白×(1-a) 反解前景色 - edge_shrink 把边缘那圈几乎全是背景的半透明像素收掉 实测白边强度(合成到深绿底,量主体外缘3像素环带比背景亮多少): 都关闭 0.0483 / 只反溢出 0.0466 / 只收边 0.0283 / 两者 0.0261 / 狠收边 0.0164 即:收边是主力(-41%),反溢出单独几乎无效(-3.5%)——因为观察色 本身就太白,反解出来还是白。这点如实写进 tooltip 与 README。 但收边暴露了颜色阈值法的死穴:它按「离白色多远」估 alpha, 白衬衫接近白、alpha 天生偏低,收边会把衬衫啃出破洞(肉眼可见背景 透过衣服)。这不是调参能解决的,故改推荐语义级抠图: - bg_mode 默认改为「已带透明通道(推荐·上游接抠图节点)」, 选项名直接把取舍写进去,旧选项名保留在 _BG_ALIAS 里做兼容 - edge_shrink 默认 0.2(配模型 alpha 够用;配阈值法需 0.35+) - 示例工作流插入 Lucida 节点:加载视频 → Lucida → 拆分(masks 输入) 实测 Lucida 一次识别整张雪碧图全部 8 个角色(各格前景占比 0.18~0.24, 中间空格 0.002);同等白边水平下主体被啃面积比阈值法少 35%,白衬衫完好。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
9c674bac42
commit
2a6fd42c57
@@ -1046,12 +1046,36 @@ WAS Node Suite 的「Text Multiline」会把 `#` 开头的行**当注释删除**
|
||||
|
||||
**输入参数**:
|
||||
- `images` (IMAGE): 视频转出的序列帧
|
||||
- `masks` (MASK, 可选): **上游抠图节点的遮罩,强烈建议接上**(见下方「透明通道怎么来」)
|
||||
- `grid_cols` / `grid_rows` / `empty_cells`: 网格布局。3×3 中间留空即 `empty_cells=4`(序号行优先、从 0 开始)
|
||||
- `direction_names` (STRING): 按「跳过空格后的先后顺序」命名,默认 `SW,S,SE,W,E,NW,N,NE`,对应行 1 面向观众、行 3 背对观众的排布
|
||||
- `bg_mode` / `bg_threshold` / `edge_softness`: 白底转透明
|
||||
- `bg_mode` / `bg_threshold`: 透明通道来源,见下
|
||||
- `edge_shrink` / `decontaminate`: 治白边,见下
|
||||
- `fragment_threshold` (FLOAT): 清掉面积不足主体这一比例的连通碎片
|
||||
- `expand_beyond_cell` (BOOLEAN): **务必开启**,允许角色超出格子边界
|
||||
- `auto_crop` / `crop_padding`: 按内容裁剪
|
||||
|
||||
**透明通道怎么来(关系到成品质量,别用默认凑合)**:
|
||||
|
||||
| bg_mode | 原理 | 代价 |
|
||||
|:--------|:-----|:-----|
|
||||
| **已带透明通道**(默认推荐)| 用上游 Lucida / FeyNobg 的语义级 alpha | 需要跑模型 |
|
||||
| 白底转透明 | 纯颜色阈值 + 边缘连通性 | **角色身上的白衣服会被啃出破洞** |
|
||||
| 不处理 | 输出不透明(仍按角色范围裁剪不切断)| — |
|
||||
|
||||
颜色阈值法的死穴在于它按「离白色多远」估 alpha,白衬衫本身就接近白、alpha 天生偏低,一旦收边压白边,衬衫就被啃穿。实测同一素材、同等白边水平下:
|
||||
|
||||
| 方案 | 白边强度 | 主体被啃面积 |
|
||||
|:-----|--------:|-----------:|
|
||||
| 颜色阈值法(收边 0.35)| 0.0261 | 0.0373 |
|
||||
| **Lucida alpha(收边 0.2)** | **0.0256** | **0.0242(少 35%)** |
|
||||
|
||||
Lucida 一次就能识别整张雪碧图的全部 8 个角色(各格前景占比 0.18~0.24,中间空格 0.002),肉眼比对:模型 alpha 的白衬衫完好,阈值法的衬衫上布满背景色斑块。
|
||||
|
||||
**治白边的两个参数**:
|
||||
- `edge_shrink`(主力):把边缘那圈「几乎全是背景」的半透明像素收掉。白底素材的边缘像素本就掺了白,不收掉贴到深色背景就发白。实测白边强度:**0 → 0.048;0.2 → 0.026;0.5 → 0.016**。配模型 alpha 用 0.15~0.25,配阈值法要 0.35 以上
|
||||
- `decontaminate`(辅助):颜色反溢出,按 `观察色 = 前景×a + 白×(1-a)` 反解真正的前景色。**单独用只改善约 3%**(因为观察色本身已经太白,反解出来还是白),必须和收边配合
|
||||
|
||||
**输出**: `dir_1` ~ `dir_8` (IMAGE,**4 通道 RGBA**) + `info` (STRING)
|
||||
|
||||
**三个关键设计**:
|
||||
|
||||
+87
-13
@@ -34,9 +34,16 @@ except Exception: # 理论上 ComfyUI 环境
|
||||
MAX_DIRS = 8
|
||||
|
||||
_BG_MODES = {
|
||||
# 键名直接把取舍写清楚:颜色阈值法快但会啃掉白色衣物,
|
||||
# 接模型抠图才是干净的做法(实测对比见 README)
|
||||
"已带透明通道(推荐·上游接抠图节点)": "keep",
|
||||
"白底转透明(快,但白色衣物会被啃)": "white",
|
||||
"不处理(输出不透明)": "none",
|
||||
}
|
||||
# 早先版本保存的工作流里的旧选项名,不进下拉、只做解析兼容
|
||||
_BG_ALIAS = {
|
||||
"白底转透明(推荐)": "white",
|
||||
"已带透明通道": "keep",
|
||||
"不处理(输出不透明)": "none",
|
||||
}
|
||||
|
||||
# 与 3×3 中间留空的常见排布对应:行 1 面向观众、行 3 背对观众
|
||||
@@ -93,6 +100,40 @@ def _drop_fragments(alpha, ratio):
|
||||
return np.where(keep[lab], alpha, 0.0).astype(np.float32)
|
||||
|
||||
|
||||
def _shrink_alpha(alpha, shrink):
|
||||
"""
|
||||
把边缘那圈「几乎全是背景」的半透明像素收掉。
|
||||
|
||||
白底图上,角色边缘的抗锯齿像素本就是前景与白色的混合,而从单张图
|
||||
反推 alpha 是欠定问题(一个方程多个未知数),浅色边缘的 alpha 往往
|
||||
被估低,于是残留一圈混了白的半透明像素——合成到深色背景就是白边。
|
||||
这里把 alpha 低于 shrink 的判为背景,其余重新拉伸到满值,
|
||||
相当于平滑地向内收一点边,比形态学腐蚀不容易产生锯齿。
|
||||
"""
|
||||
if shrink <= 0:
|
||||
return alpha
|
||||
s = min(0.95, float(shrink))
|
||||
return np.clip((alpha - s) / (1.0 - s), 0.0, 1.0).astype(np.float32)
|
||||
|
||||
|
||||
def _decontaminate(rgb, alpha, strength, bg=1.0):
|
||||
"""
|
||||
颜色反溢出:把混进边缘像素的背景色剥掉。
|
||||
|
||||
观察色 = 前景色 × a + 背景色 × (1-a),于是
|
||||
前景色 = (观察色 - 背景色 × (1-a)) / a
|
||||
不做这一步,边缘像素的 RGB 里就一直掺着白,贴到深色背景上必然发白。
|
||||
a 极小时该式会放大噪声,故对分母设下限。
|
||||
"""
|
||||
if strength <= 0:
|
||||
return rgb
|
||||
a = alpha[..., None]
|
||||
fg = (rgb - bg * (1.0 - a)) / np.maximum(a, 0.08)
|
||||
fg = np.clip(fg, 0.0, 1.0)
|
||||
s = float(min(1.0, strength))
|
||||
return (rgb * (1.0 - s) + fg * s).astype(np.float32)
|
||||
|
||||
|
||||
def _assign_blocks(alpha_full, ybnd, xbnd, cols, frag_ratio):
|
||||
"""
|
||||
全图连通标记 + 按质心把每块归属到格子。
|
||||
@@ -186,16 +227,41 @@ class RuiEightDirSplit:
|
||||
"名字只用于 info 与你自己辨认,不影响画面内容。"
|
||||
}),
|
||||
"bg_mode": (list(_BG_MODES.keys()), {
|
||||
"default": "白底转透明(推荐)",
|
||||
"tooltip": "webm 要保留透明就必须先把白底转成 alpha。\n"
|
||||
"转换只把与画面边缘相连的白色判为背景,\n"
|
||||
"角色身上的白衣服、白高光会被保留。"
|
||||
"default": "已带透明通道(推荐·上游接抠图节点)",
|
||||
"tooltip": "webm 要保留透明就必须先有 alpha。三种来源:\n\n"
|
||||
"【已带透明通道】把上游抠图节点(Lucida / FeyNobg)的\n"
|
||||
" 遮罩接到 masks 输入。**强烈推荐**:模型是按语义判断的,\n"
|
||||
" 白衣服不会被误判成背景。实测同等白边水平下,\n"
|
||||
" 主体被啃掉的面积比阈值法少 35%。\n"
|
||||
" 没接 masks 时会自动退回颜色阈值法,不会报错。\n\n"
|
||||
"【白底转透明】纯颜色阈值 + 边缘连通性判断,不需要模型、\n"
|
||||
" 很快。但它靠「离白色多远」估 alpha,角色身上的白色衣物\n"
|
||||
" 天生 alpha 偏低,收边时会被啃出破洞 —— 素材里有白衣服、\n"
|
||||
" 白高光时别用这个。\n\n"
|
||||
"【不处理】输出不透明。仍会按角色范围裁剪、不切断。"
|
||||
}),
|
||||
"bg_threshold": ("FLOAT", {
|
||||
"default": 0.92, "min": 0.5, "max": 1.0, "step": 0.005,
|
||||
"tooltip": "白底判定阈值:像素三通道最小值高于它才算「接近白」。\n"
|
||||
"背景没扣干净就调低,角色边缘被啃掉就调高。"
|
||||
}),
|
||||
"edge_shrink": ("FLOAT", {
|
||||
"default": 0.2, "min": 0.0, "max": 0.95, "step": 0.01,
|
||||
"tooltip": "【治白边的主力参数】把边缘那圈「几乎全是背景」的\n"
|
||||
"半透明像素收掉,相当于平滑地向内收一点边。\n"
|
||||
"白底素材的边缘像素本就掺了白,不收掉贴到深色背景就发白。\n\n"
|
||||
"配模型 alpha(bg_mode=已带透明通道):0.15~0.25 就够。\n"
|
||||
"配颜色阈值法:要 0.35 以上才压得住白边,代价是白色衣物\n"
|
||||
" 会被啃出破洞 —— 这也是推荐用模型 alpha 的原因。\n"
|
||||
"0 = 完全不收(白边明显)。"
|
||||
}),
|
||||
"decontaminate": ("FLOAT", {
|
||||
"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.05,
|
||||
"tooltip": "【治白边的第二道】颜色反溢出:把混进边缘像素的白色剥掉。\n"
|
||||
"原理是按 观察色 = 前景×a + 白×(1-a) 反解出真正的前景色。\n"
|
||||
"1.0 = 完全反解(推荐),0 = 不处理。\n"
|
||||
"与上面的收边配合使用,单靠任一个都不够干净。"
|
||||
}),
|
||||
"edge_softness": ("FLOAT", {
|
||||
"default": 1.0, "min": 0.1, "max": 4.0, "step": 0.05,
|
||||
"tooltip": "边缘过渡宽度。原图边缘带抗锯齿,过渡太硬会有锯齿白边;\n"
|
||||
@@ -249,10 +315,10 @@ class RuiEightDirSplit:
|
||||
return True
|
||||
|
||||
def split(self, images, grid_cols, grid_rows, empty_cells,
|
||||
direction_names, bg_mode, bg_threshold, edge_softness,
|
||||
fragment_threshold, expand_beyond_cell, auto_crop, crop_padding,
|
||||
masks=None):
|
||||
mode = _BG_MODES.get(bg_mode, "white")
|
||||
direction_names, bg_mode, bg_threshold, edge_shrink,
|
||||
decontaminate, edge_softness, fragment_threshold,
|
||||
expand_beyond_cell, auto_crop, crop_padding, masks=None):
|
||||
mode = _BG_MODES.get(bg_mode) or _BG_ALIAS.get(bg_mode, "white")
|
||||
B, H, W, C = images.shape
|
||||
cols, rows = int(grid_cols), int(grid_rows)
|
||||
total = cols * rows
|
||||
@@ -364,9 +430,12 @@ class RuiEightDirSplit:
|
||||
outs[di][b, ..., 3] = 1.0
|
||||
continue
|
||||
|
||||
outs[di][b, ..., :3] = rgb
|
||||
if blk:
|
||||
outs[di][b, ..., 3] = af[y0:y1, x0:x1] * m
|
||||
ac = _shrink_alpha(af[y0:y1, x0:x1] * m, edge_shrink)
|
||||
outs[di][b, ..., :3] = _decontaminate(rgb, ac, decontaminate)
|
||||
outs[di][b, ..., 3] = ac
|
||||
else:
|
||||
outs[di][b, ..., :3] = rgb
|
||||
|
||||
for di in range(n_dir):
|
||||
y0, y1, x0, x1 = final[di]
|
||||
@@ -404,8 +473,13 @@ class RuiEightDirSplit:
|
||||
y0, y1, x0, x1 = 0, ch, 0, cw
|
||||
stack = np.empty((len(frames), y1 - y0, x1 - x0, 4), dtype=np.float32)
|
||||
for fi, (rgb, alpha) in enumerate(frames):
|
||||
stack[fi, ..., :3] = rgb[y0:y1, x0:x1]
|
||||
stack[fi, ..., 3] = alpha[y0:y1, x0:x1]
|
||||
rc = rgb[y0:y1, x0:x1]
|
||||
ac = alpha[y0:y1, x0:x1]
|
||||
if mode != "none":
|
||||
ac = _shrink_alpha(ac, edge_shrink)
|
||||
rc = _decontaminate(rc, ac, decontaminate)
|
||||
stack[fi, ..., :3] = rc
|
||||
stack[fi, ..., 3] = ac
|
||||
outs.append(torch.from_numpy(stack))
|
||||
notes.append(f"{di + 1}.{names[di]} 格{cell_ids[di]} "
|
||||
f"{x1 - x0}×{y1 - y0}")
|
||||
|
||||
+222
-146
@@ -1,14 +1,14 @@
|
||||
{
|
||||
"id": "5f8c3d21-7b94-4e06-a1d8-9c2e4b7f350a",
|
||||
"revision": 0,
|
||||
"last_node_id": 19,
|
||||
"last_link_id": 17,
|
||||
"last_node_id": 20,
|
||||
"last_link_id": 19,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 1,
|
||||
"type": "VHS_LoadVideo",
|
||||
"pos": [
|
||||
-2260,
|
||||
-2680,
|
||||
340
|
||||
],
|
||||
"size": [
|
||||
@@ -24,7 +24,8 @@
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
1
|
||||
1,
|
||||
2
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -61,44 +62,93 @@
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"type": "RuiEightDirSplit",
|
||||
"type": "RuiLucida",
|
||||
"pos": [
|
||||
-1850,
|
||||
-2270,
|
||||
340
|
||||
],
|
||||
"size": [
|
||||
360,
|
||||
320
|
||||
340,
|
||||
250
|
||||
],
|
||||
"flags": {},
|
||||
"order": 1,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"name": "image",
|
||||
"type": "IMAGE",
|
||||
"link": 1
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "alpha",
|
||||
"type": "MASK",
|
||||
"links": [
|
||||
3
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "cutout",
|
||||
"type": "IMAGE",
|
||||
"links": []
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"aux_id": "rui40000/Ruinode",
|
||||
"Node name for S&R": "RuiLucida"
|
||||
},
|
||||
"widgets_values": [
|
||||
"lucida.safetensors",
|
||||
"fp16",
|
||||
"auto",
|
||||
0.5,
|
||||
1.0,
|
||||
false,
|
||||
false
|
||||
],
|
||||
"color": "#232",
|
||||
"bgcolor": "#353",
|
||||
"title": "② Lucida 抠图(一次抠整张,8 个角色都能识别)"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"type": "RuiEightDirSplit",
|
||||
"pos": [
|
||||
-1860,
|
||||
340
|
||||
],
|
||||
"size": [
|
||||
380,
|
||||
400
|
||||
],
|
||||
"flags": {},
|
||||
"order": 2,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 2
|
||||
},
|
||||
{
|
||||
"name": "masks",
|
||||
"type": "MASK",
|
||||
"link": 3
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "dir_1",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
2,
|
||||
3
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "dir_2",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
4,
|
||||
5
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "dir_3",
|
||||
"name": "dir_2",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
6,
|
||||
@@ -106,7 +156,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "dir_4",
|
||||
"name": "dir_3",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
8,
|
||||
@@ -114,7 +164,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "dir_5",
|
||||
"name": "dir_4",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
10,
|
||||
@@ -122,7 +172,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "dir_6",
|
||||
"name": "dir_5",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
12,
|
||||
@@ -130,7 +180,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "dir_7",
|
||||
"name": "dir_6",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
14,
|
||||
@@ -138,13 +188,21 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "dir_8",
|
||||
"name": "dir_7",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
16,
|
||||
17
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "dir_8",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
18,
|
||||
19
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "info",
|
||||
"type": "STRING",
|
||||
@@ -160,23 +218,25 @@
|
||||
3,
|
||||
"4",
|
||||
"SW,S,SE,W,E,NW,N,NE",
|
||||
"白底转透明(推荐)",
|
||||
"已带透明通道(推荐·上游接抠图节点)",
|
||||
0.92,
|
||||
0.2,
|
||||
1.0,
|
||||
1.0,
|
||||
0.05,
|
||||
true,
|
||||
true,
|
||||
8
|
||||
16
|
||||
],
|
||||
"color": "#232",
|
||||
"bgcolor": "#353",
|
||||
"title": "② 八方向拆分(拆分+编号+分组)"
|
||||
"title": "③ 八方向拆分(拆分+编号+分组)"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"id": 4,
|
||||
"type": "VHS_VideoCombine",
|
||||
"pos": [
|
||||
-1380,
|
||||
-1400,
|
||||
-160
|
||||
],
|
||||
"size": [
|
||||
@@ -184,13 +244,13 @@
|
||||
460
|
||||
],
|
||||
"flags": {},
|
||||
"order": 2,
|
||||
"order": 3,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 2
|
||||
"link": 4
|
||||
},
|
||||
{
|
||||
"name": "audio",
|
||||
@@ -235,10 +295,10 @@
|
||||
"title": "1. SW → 透明 webm"
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"id": 5,
|
||||
"type": "SaveImage",
|
||||
"pos": [
|
||||
-1020,
|
||||
-1040,
|
||||
-160
|
||||
],
|
||||
"size": [
|
||||
@@ -246,13 +306,13 @@
|
||||
300
|
||||
],
|
||||
"flags": {},
|
||||
"order": 3,
|
||||
"order": 4,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 3
|
||||
"link": 5
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
@@ -267,10 +327,10 @@
|
||||
"title": "1. SW → 透明 PNG 序列帧"
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"id": 6,
|
||||
"type": "VHS_VideoCombine",
|
||||
"pos": [
|
||||
-1380,
|
||||
-1400,
|
||||
170
|
||||
],
|
||||
"size": [
|
||||
@@ -278,13 +338,13 @@
|
||||
460
|
||||
],
|
||||
"flags": {},
|
||||
"order": 4,
|
||||
"order": 5,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 4
|
||||
"link": 6
|
||||
},
|
||||
{
|
||||
"name": "audio",
|
||||
@@ -327,10 +387,10 @@
|
||||
"title": "2. S → 透明 webm"
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"id": 7,
|
||||
"type": "SaveImage",
|
||||
"pos": [
|
||||
-1020,
|
||||
-1040,
|
||||
170
|
||||
],
|
||||
"size": [
|
||||
@@ -338,13 +398,13 @@
|
||||
300
|
||||
],
|
||||
"flags": {},
|
||||
"order": 5,
|
||||
"order": 6,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 5
|
||||
"link": 7
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
@@ -359,10 +419,10 @@
|
||||
"title": "2. S → 透明 PNG 序列帧"
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"id": 8,
|
||||
"type": "VHS_VideoCombine",
|
||||
"pos": [
|
||||
-1380,
|
||||
-1400,
|
||||
500
|
||||
],
|
||||
"size": [
|
||||
@@ -370,13 +430,13 @@
|
||||
460
|
||||
],
|
||||
"flags": {},
|
||||
"order": 6,
|
||||
"order": 7,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 6
|
||||
"link": 8
|
||||
},
|
||||
{
|
||||
"name": "audio",
|
||||
@@ -419,10 +479,10 @@
|
||||
"title": "3. SE → 透明 webm"
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"id": 9,
|
||||
"type": "SaveImage",
|
||||
"pos": [
|
||||
-1020,
|
||||
-1040,
|
||||
500
|
||||
],
|
||||
"size": [
|
||||
@@ -430,13 +490,13 @@
|
||||
300
|
||||
],
|
||||
"flags": {},
|
||||
"order": 7,
|
||||
"order": 8,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 7
|
||||
"link": 9
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
@@ -451,10 +511,10 @@
|
||||
"title": "3. SE → 透明 PNG 序列帧"
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"id": 10,
|
||||
"type": "VHS_VideoCombine",
|
||||
"pos": [
|
||||
-1380,
|
||||
-1400,
|
||||
830
|
||||
],
|
||||
"size": [
|
||||
@@ -462,13 +522,13 @@
|
||||
460
|
||||
],
|
||||
"flags": {},
|
||||
"order": 8,
|
||||
"order": 9,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 8
|
||||
"link": 10
|
||||
},
|
||||
{
|
||||
"name": "audio",
|
||||
@@ -511,10 +571,10 @@
|
||||
"title": "4. W → 透明 webm"
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"id": 11,
|
||||
"type": "SaveImage",
|
||||
"pos": [
|
||||
-1020,
|
||||
-1040,
|
||||
830
|
||||
],
|
||||
"size": [
|
||||
@@ -522,13 +582,13 @@
|
||||
300
|
||||
],
|
||||
"flags": {},
|
||||
"order": 9,
|
||||
"order": 10,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 9
|
||||
"link": 11
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
@@ -543,10 +603,10 @@
|
||||
"title": "4. W → 透明 PNG 序列帧"
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"id": 12,
|
||||
"type": "VHS_VideoCombine",
|
||||
"pos": [
|
||||
-1380,
|
||||
-1400,
|
||||
1160
|
||||
],
|
||||
"size": [
|
||||
@@ -554,13 +614,13 @@
|
||||
460
|
||||
],
|
||||
"flags": {},
|
||||
"order": 10,
|
||||
"order": 11,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 10
|
||||
"link": 12
|
||||
},
|
||||
{
|
||||
"name": "audio",
|
||||
@@ -603,10 +663,10 @@
|
||||
"title": "5. E → 透明 webm"
|
||||
},
|
||||
{
|
||||
"id": 12,
|
||||
"id": 13,
|
||||
"type": "SaveImage",
|
||||
"pos": [
|
||||
-1020,
|
||||
-1040,
|
||||
1160
|
||||
],
|
||||
"size": [
|
||||
@@ -614,13 +674,13 @@
|
||||
300
|
||||
],
|
||||
"flags": {},
|
||||
"order": 11,
|
||||
"order": 12,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 11
|
||||
"link": 13
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
@@ -635,10 +695,10 @@
|
||||
"title": "5. E → 透明 PNG 序列帧"
|
||||
},
|
||||
{
|
||||
"id": 13,
|
||||
"id": 14,
|
||||
"type": "VHS_VideoCombine",
|
||||
"pos": [
|
||||
-1380,
|
||||
-1400,
|
||||
1490
|
||||
],
|
||||
"size": [
|
||||
@@ -646,13 +706,13 @@
|
||||
460
|
||||
],
|
||||
"flags": {},
|
||||
"order": 12,
|
||||
"order": 13,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 12
|
||||
"link": 14
|
||||
},
|
||||
{
|
||||
"name": "audio",
|
||||
@@ -695,10 +755,10 @@
|
||||
"title": "6. NW → 透明 webm"
|
||||
},
|
||||
{
|
||||
"id": 14,
|
||||
"id": 15,
|
||||
"type": "SaveImage",
|
||||
"pos": [
|
||||
-1020,
|
||||
-1040,
|
||||
1490
|
||||
],
|
||||
"size": [
|
||||
@@ -706,13 +766,13 @@
|
||||
300
|
||||
],
|
||||
"flags": {},
|
||||
"order": 13,
|
||||
"order": 14,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 13
|
||||
"link": 15
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
@@ -727,10 +787,10 @@
|
||||
"title": "6. NW → 透明 PNG 序列帧"
|
||||
},
|
||||
{
|
||||
"id": 15,
|
||||
"id": 16,
|
||||
"type": "VHS_VideoCombine",
|
||||
"pos": [
|
||||
-1380,
|
||||
-1400,
|
||||
1820
|
||||
],
|
||||
"size": [
|
||||
@@ -738,13 +798,13 @@
|
||||
460
|
||||
],
|
||||
"flags": {},
|
||||
"order": 14,
|
||||
"order": 15,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 14
|
||||
"link": 16
|
||||
},
|
||||
{
|
||||
"name": "audio",
|
||||
@@ -787,10 +847,10 @@
|
||||
"title": "7. N → 透明 webm"
|
||||
},
|
||||
{
|
||||
"id": 16,
|
||||
"id": 17,
|
||||
"type": "SaveImage",
|
||||
"pos": [
|
||||
-1020,
|
||||
-1040,
|
||||
1820
|
||||
],
|
||||
"size": [
|
||||
@@ -798,13 +858,13 @@
|
||||
300
|
||||
],
|
||||
"flags": {},
|
||||
"order": 15,
|
||||
"order": 16,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 15
|
||||
"link": 17
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
@@ -819,10 +879,10 @@
|
||||
"title": "7. N → 透明 PNG 序列帧"
|
||||
},
|
||||
{
|
||||
"id": 17,
|
||||
"id": 18,
|
||||
"type": "VHS_VideoCombine",
|
||||
"pos": [
|
||||
-1380,
|
||||
-1400,
|
||||
2150
|
||||
],
|
||||
"size": [
|
||||
@@ -830,13 +890,13 @@
|
||||
460
|
||||
],
|
||||
"flags": {},
|
||||
"order": 16,
|
||||
"order": 17,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 16
|
||||
"link": 18
|
||||
},
|
||||
{
|
||||
"name": "audio",
|
||||
@@ -879,10 +939,10 @@
|
||||
"title": "8. NE → 透明 webm"
|
||||
},
|
||||
{
|
||||
"id": 18,
|
||||
"id": 19,
|
||||
"type": "SaveImage",
|
||||
"pos": [
|
||||
-1020,
|
||||
-1040,
|
||||
2150
|
||||
],
|
||||
"size": [
|
||||
@@ -890,13 +950,13 @@
|
||||
300
|
||||
],
|
||||
"flags": {},
|
||||
"order": 17,
|
||||
"order": 18,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 17
|
||||
"link": 19
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
@@ -911,24 +971,24 @@
|
||||
"title": "8. NE → 透明 PNG 序列帧"
|
||||
},
|
||||
{
|
||||
"id": 19,
|
||||
"id": 20,
|
||||
"type": "Note",
|
||||
"pos": [
|
||||
-2260,
|
||||
-2680,
|
||||
880
|
||||
],
|
||||
"size": [
|
||||
700,
|
||||
900
|
||||
740,
|
||||
1120
|
||||
],
|
||||
"flags": {},
|
||||
"order": 18,
|
||||
"order": 19,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [],
|
||||
"properties": {},
|
||||
"widgets_values": [
|
||||
"八方向行走动画 —— 完整管线\n\n【前置步骤(在本工作流之外完成)】\n 角色图 →(GPTimage2)→ 八方向静态图 →(Seedance 首尾帧)→ 循环行走视频\n 视频每一帧上都排布着 8 个朝向的角色。\n\n【本工作流做的事】\n ① VHS「Load Video」把视频转成序列帧\n · force_rate 就是帧率选择。设 0 用源视频帧率;\n 设 12 可减半出帧、素材更小,走路动画通常 8~12 帧一循环就够\n · frame_load_cap 可限制总帧数,先小批量试参数很有用\n ② 本仓库「八方向序列拆分」:切格 + 编号 + 按方向分组,一次给出 8 条序列\n ③ 每个方向各接一个 VHS「Video Combine」出透明 webm\n ④ 每个方向各接一个 SaveImage 出透明 PNG 序列帧\n\n【拆分节点的关键参数】\n · grid_cols / grid_rows / empty_cells\n 3×3 中间留空即 empty_cells=4。序号行优先、从 0 开始。\n · direction_names\n 按「跳过空格后的先后顺序」命名,默认对应下面这个排布:\n SW S SE ← 行1:面向观众\n W E ← 行2:侧面\n NW N NE ← 行3:背对观众\n 与你的素材不符就直接改这行,名字只影响标注不影响画面。\n · bg_mode / bg_threshold\n 白底转透明。只把「与画面边缘相连」的白色判为背景,\n 所以角色身上的白衬衫、白高光不会被掏空。\n 背景没扣净就调低阈值,角色边缘被啃就调高。\n · expand_beyond_cell(务必开启)\n 允许角色超出格子边界。关闭时按格线严格切分,角色只要探出格线\n 就会被切断(最典型的是脚被削掉)。开启后格子只用来判定方向,\n 实际范围由角色自身的连通区域决定。\n · auto_crop\n 裁掉多余空白。裁剪框取该方向所有帧的并集,\n 因此整条序列尺寸一致(能合成视频),角色也不会在帧间跳动。\n\n【为什么不用连通区域拆分(本仓库的「素材拆分」节点)】\n 那个节点按包围盒排序,角色走动时位置浮动,一旦跨过排序行界方向就会错乱;\n 且每个 sprite 按各自 bbox 裁剪、尺寸不一,无法直接合成视频。\n 固定网格没有这两个问题 —— 但它更适合单张静态合图,两者各有用途。\n\n【webm 透明的关键】\n Video Combine 的 format 必须选 video/webm,且 pix_fmt 选 yuva420p。\n 注意:alpha 存放在 WebM 的独立边带里,所以用 ffprobe 看主流会显示\n yuv420p,这是正常的。播放器/引擎(走 libvpx 解码)能正确读到透明通道;\n 若要用 ffmpeg 自行验证,需显式加 -c:v libvpx-vp9 解码,\n 否则内置 vp9 解码器不会输出 alpha,会让人误以为透明丢了。\n\n【实测(97 帧 834×1112 的素材)】\n 拆分耗时约 4 秒,8 个方向各 97 帧,尺寸 172×370 ~ 206×327。"
|
||||
"八方向行走动画 —— 完整管线\n\n【前置步骤(在本工作流之外完成)】\n 角色图 →(GPTimage2)→ 八方向静态图 →(Seedance 首尾帧)→ 循环行走视频\n 视频每一帧上都排布着 8 个朝向的角色。\n\n【本工作流做的事】\n ① VHS「Load Video」把视频转成序列帧\n · force_rate 就是帧率选择。设 0 用源视频帧率;\n 设 12 可减半出帧、素材更小,走路动画通常 8~12 帧一循环就够\n · frame_load_cap 可限制总帧数,先小批量试参数很有用\n ② Lucida 抠图,为拆分提供语义级 alpha(见下方「为什么要接抠图节点」)\n ③ 八方向拆分:切格 + 编号 + 按方向分组,一次给出 8 条序列\n ④ 每个方向各接一个 VHS「Video Combine」出透明 webm\n ⑤ 每个方向各接一个 SaveImage 出透明 PNG 序列帧\n\n■ 为什么要接抠图节点,而不是让拆分节点自己扣白底 ————\n\n拆分节点自带的「白底转透明」是纯颜色阈值法:按「离白色多远」估 alpha。\n它的死穴是角色身上的白色衣物 —— 白衬衫本身就接近白,alpha 天生偏低,\n一旦收边压白边,衬衫就会被啃出破洞(实测会看到背景色透过衣服)。\n\nLucida 是按语义判断的,知道那是衣服而不是背景。实测(同一素材、\n同等白边水平):主体被啃掉的面积比阈值法少 35%,白衬衫完好。\nLucida 一次就能把整张雪碧图的 8 个角色全部识别出来\n(各格前景占比 0.18~0.24,中间空格 0.002)。\n\n素材里确实没有白色元素时,可以把 bg_mode 改成「白底转透明」省掉模型开销;\n此时 edge_shrink 要调到 0.35 以上才压得住白边。\n\n■ 治白边的两个参数 ————————————————————\n\n · edge_shrink(主力)把边缘那圈几乎全是背景的半透明像素收掉。\n 配模型 alpha 用 0.15~0.25;配阈值法要 0.35+。\n 实测:0 → 白边强度 0.048;0.2 → 0.026;0.5 → 0.016。\n · decontaminate(辅助)颜色反溢出,按\n 观察色 = 前景×a + 白×(1-a) 反解真正的前景色。\n 单独用改善很小(约 3%),要和收边配合。\n\n■ 拆分节点的其他关键参数 ————————————————\n\n · grid_cols / grid_rows / empty_cells\n 3×3 中间留空即 empty_cells=4。序号行优先、从 0 开始。\n · direction_names\n 按「跳过空格后的先后顺序」命名,默认对应:\n SW S SE ← 行1:面向观众\n W E ← 行2:侧面\n NW N NE ← 行3:背对观众\n 与素材不符就直接改这行,名字只影响标注不影响画面。\n · expand_beyond_cell(务必开启)\n 允许角色超出格子边界。关闭时按格线严格切分,角色只要探出格线\n 就会被切断(最典型的是脚被削掉)。开启后格子只用来判定方向,\n 实际范围由角色自身的连通区域决定。三种 bg_mode 都有效。\n · auto_crop\n 裁剪框取该方向所有帧的并集,因此整条序列尺寸一致(能合成视频),\n 角色也不会在帧间跳动。\n\n■ webm 透明的关键 ————————————————————\n\n Video Combine 的 format 必须选 video/webm,pix_fmt 必须选 yuva420p。\n 注意:alpha 存放在 WebM 的独立边带里,用 ffprobe 看主流会显示 yuv420p,\n 这是正常的。播放器/引擎(走 libvpx 解码)能正确读到透明通道;\n 若要用 ffmpeg 自行验证,需显式加 -c:v libvpx-vp9 解码,\n 否则内置 vp9 解码器不输出 alpha,会让人误以为透明丢了。"
|
||||
],
|
||||
"color": "#432",
|
||||
"bgcolor": "#653"
|
||||
@@ -944,141 +1004,157 @@
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
2,
|
||||
2,
|
||||
0,
|
||||
3,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
3,
|
||||
2,
|
||||
0,
|
||||
4,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
4,
|
||||
2,
|
||||
1,
|
||||
5,
|
||||
0,
|
||||
3,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
3,
|
||||
2,
|
||||
0,
|
||||
3,
|
||||
1,
|
||||
"MASK"
|
||||
],
|
||||
[
|
||||
4,
|
||||
3,
|
||||
0,
|
||||
4,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
5,
|
||||
2,
|
||||
3,
|
||||
0,
|
||||
5,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
6,
|
||||
3,
|
||||
1,
|
||||
6,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
6,
|
||||
2,
|
||||
2,
|
||||
7,
|
||||
3,
|
||||
1,
|
||||
7,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
7,
|
||||
2,
|
||||
8,
|
||||
3,
|
||||
2,
|
||||
8,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
8,
|
||||
2,
|
||||
9,
|
||||
3,
|
||||
2,
|
||||
9,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
9,
|
||||
2,
|
||||
10,
|
||||
3,
|
||||
3,
|
||||
10,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
10,
|
||||
2,
|
||||
4,
|
||||
11,
|
||||
3,
|
||||
3,
|
||||
11,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
11,
|
||||
2,
|
||||
12,
|
||||
3,
|
||||
4,
|
||||
12,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
12,
|
||||
2,
|
||||
5,
|
||||
13,
|
||||
3,
|
||||
4,
|
||||
13,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
13,
|
||||
2,
|
||||
14,
|
||||
3,
|
||||
5,
|
||||
14,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
14,
|
||||
2,
|
||||
6,
|
||||
15,
|
||||
3,
|
||||
5,
|
||||
15,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
15,
|
||||
2,
|
||||
16,
|
||||
3,
|
||||
6,
|
||||
16,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
16,
|
||||
2,
|
||||
7,
|
||||
17,
|
||||
3,
|
||||
6,
|
||||
17,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
17,
|
||||
2,
|
||||
18,
|
||||
3,
|
||||
7,
|
||||
18,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
19,
|
||||
3,
|
||||
7,
|
||||
19,
|
||||
0,
|
||||
"IMAGE"
|
||||
]
|
||||
],
|
||||
"groups": [],
|
||||
"config": {},
|
||||
"extra": {
|
||||
"ds": {
|
||||
"scale": 0.55,
|
||||
"scale": 0.5,
|
||||
"offset": [
|
||||
2400,
|
||||
2820,
|
||||
100
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user