Communitygithub.com

GIF/动图透明背景抠图

从GIF动图、图片序列和短视频中移除背景,导出透明GIF + WebP + APNG格式。适用于“把背景透明”、“抠图”、“去背景”等需求。

动图透明背景抠图란 무엇인가요?

这是一个专注于GIF动图、图片序列和短视频背景抠除的专业技能。它利用BiRefNet模型进行分割,结合引导滤波、拓扑清理等后处理技术,输出透明GIF、WebP和APNG三种格式。该技能特别注重对主体浅色部件与背景色接近时的可解性判断,通过运行solvability阶段提前预警可能存在的分辨率瓶颈,避免无谓耗时。支持CPU处理(约17.6秒/帧),并提供了详细的验收流程,包括逐帧对比、散点检测和颜色量化误差分析。该技能严格遵循一系列硬规则,例如禁止使用binary_closing以防止焊死肢体缝隙,禁止对alpha通道做非线性压缩等。适用于Claude Code、Cursor等支持自定义技能的AI代理平台。

지원 대상~Claude Code~Codex CLI~Cursor
npx skills add qiwen-bot/gif-background-removal

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

GIF / 动图 透明背景抠图

需收集的输入

  • 源文件: GIF、PNG 序列还是视频。视频走 ffmpeg 抽帧(脚本已内置)
  • 看图底色: 用户是在什么背景上看这张图的?深色界面还是白底页面会暴露完全不同的残留。 不知道就默认做全套底色验证(脚本已内置),不要跳过
  • 主体是否有接近背景色的浅色部件(白手套、白靴、白边)? 这是能力天花板,见下方 "先做可解性判断"

先做可解性判断(跳过这步会白干几十分钟)

抠图是信息恢复问题,不是算法调优问题。当主体浅色部件与背景在颜色上接近时,后处理 无法把它们分开 —— 色度、平滑度、时间中位数、背景曲面拟合全都会误伤主体。

别靠肉眼估颜色,跑一下:

.venv-bg\Scripts\python scripts\cutout.py --input <源文件> --work work --stage solvability

它用"相机静止 + 主体会动"分离出背景板和主体,输出三个数:

  • 背景板中位色 / 主体最亮色调 / 两者差值
  • 落在背景色 ±25/255 以内的主体像素占比 —— 这个数最可解释

实测这份机器人素材:背景 (212,207,210),主体最亮 (250,251,250),差值 40.7, 但32.6% 的主体像素落在背景色 25 以内 → 打印 AMBIGUOUS。

看到 AMBIGUOUS 就要先告诉用户:

这份素材的浅色部件和背景颜色非常接近,纯抠图做不到像素级干净,顶多做到"放大后有极细残留"。 有源工程的话,换高饱和纯绿/品红幕布重渲一次,我就能做到完全无残留; 只有成品文件的话我只能做到逼近干净。

然后问用户要不要继续,别闷头跑 20 分钟。

注意:这个检查是预警信号,不是判决。最终以跑完之后的残留量化为准 —— "能分"也可能因为模型边缘不准而出残留,"不能分"也可能实际抠得很干净。

流程

  1. 建工作区并装环境 python -m venv .venv-bg && .venv-bg\Scripts\python -m pip install rembg onnxruntime pillow scipy (scipy 来自 rembg 依赖链,引导滤波要用它 —— 见下)

  2. 跑管线

    .venv-bg\Scripts\python scripts\cutout.py --input <源文件> --outdir out --scale 2
    

    --input 接受视频文件、GIF/PNG 文件,或一个已抽好帧的 PNG 序列目录。 --scale 2 不是可选项:肢体与躯干之间的缝只有 5–15px,720p 下分割模型根本看不见。 个别帧还顽固就 --stage matte 单独重跑那几帧、--scale 3。

    纯 CPU 下 70 帧 720p 跑 2× 约 20 分钟(约 17.6s/帧,onnxruntime 无 GPU)。 提前告诉用户这个量级,别让人以为卡死。

  3. 验收(见下节),不通过就定位阶段重跑,不要整体重跑。

  4. 交付: GIF + WebP + APNG 三份都给,并说明各自适用场景。

硬规则(违反过、代价都很大)

  • 拓扑处理只用 opening,绝不用 closing。binary_closing 会填掉 ≤2px 的背景缝, 把手臂和躯干之间的窄缝焊死,之后再多的处理也救不回来。实测这一条比其它所有问题加起来 造成的返工都多。
  • 空洞填充阈值取小值(60px 量级,不是几百)。真正的肢体缝隙有几千像素,针孔只有几十。
  • 引导滤波半径 ≤2,且默认不要中值滤波。3×3 中值和半径 4 的引导滤波都会抹掉几像素宽的缝。
  • 不要对 alpha 做非线性压缩。曾经写过 clip((clip((a-0.04)/0.92)-0.06)/0.88) 这种映射, 意图是收紧边缘,实际在 alpha≈0.7 处反而抬高了 alpha,等于亲手把残留补得更实。
  • GIF 不要用有序抖动(Bayer)。静帧上边缘更柔,但那个网点锁在屏幕坐标系里, 轮廓一动穿过去就"沸腾"闪烁 —— 动画里反而更糟。GIF 走清理后的硬阈值, 柔边 alpha 留给 WebP / APNG。
  • 模型选 BiRefNet。isnet / u2net 会把地面镜面倒影判成实体抠进去;换成 birefnet-general-lite 后 alpha 在靴底处直接归零。
  • 不要为了"更干净"去擦背景色。用背景曲面/色度判据大面积擦残留,会连主体浅色部件一起擦掉。 残留的正确解法是提高 matte 分辨率,不是改阈值。

输出契约

  • out/<name>.gif —— 透明索引 255、transparency=255、disposal=2,1-bit 硬边。 拓扑清理(去散点)只作用于 GIF
  • out/<name>.webp —— 保留连续 alpha,不做拓扑清理,所以会留一层柔边暖色毛边。 叠复杂背景优先用它,这个差异是刻意的,别当成 bug
  • out/<name>.png —— APNG,浏览器兼容性最好
  • 保持源片的画布尺寸、帧数、每帧时长、循环设置不变;在回复里列出这几项的实测值 (动画 WebP 的逐帧时长 PIL 读不出来,遇到这种情况照实说"以 GIF 实测为准",别编)
  • 用 <media> 标签交付文件,并说明 GIF 与 WebP 各自的适用场景

失败处理

  • GIF 在浏览器里显示成灰底: 先别改文件。查 VP8X 的 ALPHA 位与 ANIM 背景色 (verify.py 会打印),两者都正常的话是查看器给透明区铺了底色。 若 verify.py 报 ANMF ... blend=0,那才是真问题 —— cutout.py 导出时会自动改成 blend=1,如果这份文件的 blend 是 0,说明它不是本脚本产的,或走了外部工具。
  • 主体被啃掉: 几乎都是背景判据太宽。回退到 final/ 那一层重新导出,不要在擦除逻辑上继续调参。
  • 某几帧缝隙没扣开: --stage matte --scale 3 只重跑那几帧,单帧成本约 20 秒。
  • rembg 模型下载慢: rembg 2.x 把模型放在 ~/.rembg/models/<model>/<model>.onnx (不是老版的 ~/.u2net),birefnet-general-lite 约 214MB。首次运行要等下载。

验收(这一步不能省)

必须在用户真实观看的那块底色上逐帧看,否则等于没验:

.venv-bg\Scripts\python scripts\verify.py out\<name>.gif --backdrop grey --ref work\final

它会输出:散点数、逐帧不透明占比(判断有无闪烁)、最低不透明行、GIF 相对源 matte 的 颜色量化误差、WebP 容器标志,并生成四底色对比图 + 全帧接触表 + 指定区域放大图。

判断标准:

指标通过
散点数0
不透明占比逐帧极差< 0.05
最低不透明行逐帧一致(反射残留会让它忽高忽低或顶到画面底)
颜色量化平均误差< 3/255(缩略图看不出丢色相,只能靠这个数)
四底色对比每块底色都能透出来,没有统一浅灰
接触表逐帧无残影、无噪点、肢体缝隙通透

肉眼只看单帧、或只在黑底上看,都会漏掉问题 —— 暖色残留叠在深灰底上才明显。

Windows (win32) 平台说明

  • 解释器用 py 或 python,python3 在 Windows 上不存在;别用 PATH 上的 python(可能指向无 pip 的 hermes venv),优先用 venv 里的 .venv-bg\Scripts\python.exe
  • 路径含中文时,用 Write/Read 工具写脚本文件,不要用 Set-Content 拼内容
  • PIL 的 ImageFilter.GaussianBlur 不支持 mode "F" 和 float RGB;对浮点图做模糊用 scipy.ndimage.gaussian_filter(rembg 已经装了 scipy)
  • Image.fromarray(float32_array, "RGB") 会直接报错,先 .astype(np.float32) 再按通道处理
  • 不要用 Remove-Item 清理;cutout.py --clean 已经封装了目录清理

示例

输入: "把这段 GIF 的背景抠透明" → cutout.py --input 动作.gif --outdir out --scale 2,再 verify.py, → 交付 GIF/WebP/APNG 三份,列出 720×720 / 70 帧 / 每帧 100ms / 总 7000ms。

输入: "抠干净点,边缘还有噪点,手臂和身体之间还有没扣的地方" → 不要去调阈值。先跑 verify.py --zoom <框选坐标> 确认缺陷位置, → 散点多就加 --median 3,缝隙不通用就 --stage matte --scale 3 重跑对应帧, → 再验收。回复里说清是哪一类缺陷、用了哪条修复,别只说"已优化"。

관련 스킬