Tobatsu · PIP 合成 · 可行性验证

给数字人扣图加描边 —— 能做,而且好做

把背景抠掉的透明数字人,在和游戏画面拼 PIP 的时候,沿人物轮廓描一圈白色粗边;圆形浮窗也能加可开关的描边。两个需求本地用纯 ffmpeg 全跑通了。

源素材:fal pixelcut/video-background-removal 输出的透明 webm · 本地 ffmpeg 4.4.2 实测 · 2026-06-17
一句话结论

结论先行

能做
两个需求都验证通过
好做
纯 ffmpeg 滤镜 无需模型
Rendi 直跑
pip-compose-rendi 就是 ffmpeg
1 个坑
VP9 alpha 解码器要选对

这次要做的两件事,本质都是"沿一个形状描一圈边":一个形状是人物剪影(扣图轮廓),一个形状是浮窗容器(圆/方)。两者都不需要 AI、不需要额外服务,ffmpeg 一条滤镜链就能出,Rendi 现成能跑。粗细 = 一个像素参数,颜色 = 一个颜色参数,自然语言可以直接映射。

需求拆解

两个需求,两种描边

需求描边对象触发场景默认
① 扣图人物描边人物身体轮廓(跟着头发/肩膀/手走)透明扣图人物参与 PIP 合成时白色 · 粗线
② 浮窗描边浮窗容器形状(圆形 / 异形)非上下分屏、是 overlay 圆形/异形浮窗时不描边;开启后默认白色粗线

需求 ② 要支持自然语言控制:是否描边、描边颜色都可以用一句话说(如"给浮窗加个红色描边")。下面分别给实现原理和实测画面。

前置发现 · 必看

坑:VP9 透明视频要用对解码器

fal 输出的是 VP9 webm 带 alpha(文件头 ALPHA_MODE=1)。但 ffmpeg 默认的原生 vp9 解码器会把 alpha 丢掉,解出来是不透明的 rgb24,alphaextract 直接报 "Requested planes not available"。

必须显式指定 libvpx-vp9 解码器,才能拿到 rgba(带透明):

# 错:原生 vp9 解码器丢 alpha → rgb24
ffmpeg -i in.webm ...
# 对:libvpx-vp9 才解出 rgba(透明保留)
ffmpeg -c:v libvpx-vp9 -i in.webm ...
⚠️ 接进 pip-compose-rendi 时,凡是输入是这种透明 webm,解码必须走 libvpx-vp9。这是唯一一个不写就直接坏的点。
alpha matte
用 libvpx-vp9 解码后抽出的 alpha 通道:干净的白色人物剪影 + 黑色透明区。描边就建在这个剪影上。
需求 ① · 实现 + 实证

扣图人物描边:膨胀剪影,填色,盖回原图

四步,全是 ffmpeg 滤镜:

[0:v]format=rgba,split[p][a];
[a]alphaextract,dilation×N[edge];     # 剪影向外膨胀 N 像素
color=white:s=WxH,format=rgba[c];
[c][edge]alphamerge[stroke];           # 膨胀剪影填白
[bg][stroke]overlay[t];[t][p]overlay   # 描边在下,原人物盖上

前 / 后(深色背景上,12px 白色粗线)

no stroke
无描边:扣图人物直接贴上,边缘和背景容易糊在一起。
white stroke
12px 白色描边:贴合头发/肩膀/手的轮廓,人物从背景里"跳"出来。

动起来也稳(这是视频,不是单帧)

逐帧重算描边,人物说话/动手时白边一直贴着轮廓,无抖动、无残影。

颜色 = 一个参数,自然语言直接映射

yellow stroke
同一条链,把 color=white 换成 color=0xFFD400 就是黄色描边。"黄色描边/红色描边"这种自然语言只要映射到一个颜色值。粗细同理 = 膨胀像素数。
需求 ② · 实现 + 实证

浮窗描边:圆形容器加一圈环

同一个"描边 = 膨胀形状垫底"的思路,只是形状换成圆(异形同理,换 mask 公式):一块白圆盘垫底,人物裁成略小的圆盖上,只露出外圈 = 白色粗环。默认不描边,开启后默认白色粗线。

circle no stroke
默认:圆形浮窗,无描边。
circle white ring
开启:白色粗环。颜色/粗细同样参数化。
📌 一个设计点要定:扣图人物放进圆里时,圆内人物周围那圈(原来被抠掉的透明区)露出来的是什么?上图我先填了黑。真实产品三选一:(a) 填纯色(头像风)· (b) 让游戏画面透出来(人物悬浮在圆窗里、背景是游戏)· (c) 不裁圆、直接用需求①的身体描边。建议默认 (b) 或 (c),(a) 当可选。
接入建议

接进 pip-compose-rendi 的契约

建议在 pip / overlay 配置下加一个 stroke 块,自然语言由 Mai gateway 映射成字段:

"pip": {
  "layout": "overlay",
  "overlay_shape": "circle",
  "stroke": {
    "enabled": false,        # 需求②默认 false;需求①(扣图人物)可默认 true
    "color":   "white",        # 自然语言→颜色值,默认白
    "width_px": 12             # "粗线"≈10–14px
  }
}
自然语言→ 字段
"给浮窗加描边"stroke.enabled=true
"红色描边" / "描边用品牌色"stroke.color="red" / "#1B365D"
"细一点 / 更粗"stroke.width_px 调小/调大
(不提)需求②保持 enabled=false;需求①人物描边按你定的默认开

哪些 layout 生效:需求①的身体描边适用于任何用透明扣图人物的布局;需求②的浮窗环只在 overlay(圆/异形)生效,split_v(上下分屏)不涉及。

代价 / 注意

要知道的几点