本地视频 → 序列帧动画精灵图

0 阅读11分钟

一句话:把你电脑上的一段视频 / GIF(不管是拍的、下的,还是本地 AI 模型生成的),
变成游戏引擎里能直接用的「序列帧精灵图」——全程在你自己的机器上跑,不上云。

⚠️ 项目性质说明:本文档所属的 fs7744/meowcute
项目仅用于个人实验与研究,不是商业产品、不提供任何保证,也不构成可用于生产的方案。
文中涉及到的模型权重、脚本与流程仅作技术探索与学习用途;若用于实际项目或商用,
请自行核验相关模型与素材的许可证与授权。


1. 先搞懂三个词

很多人第一次听到「序列帧精灵图」会懵,其实拆开看就三样东西:

  • 精灵图(Sprite Sheet):把很多张小图按网格拼成的一张大图。游戏里角色的所有动作帧都塞在一张 PNG 里,引擎按坐标「切」出来播放,省内存、加载快。
  • 序列帧(Sequence Frames):上面那些小图是有顺序的——第 1 张抬脚、第 2 张迈步……按顺序循环播放,看着就像在动。本质就是小时候的手翻书 / 翻页动画。
  • 本地(Local):指整个过程不调用任何云端 API。视频在你硬盘上,计算在你 CPU/GPU 上,成品也留在你机器里。好处是:不花钱、不联网、素材不外泄、想跑几遍跑几遍。

把它们连起来:本地视频 → 拆成一摞帧 → 挑出关键帧 → 拼成一张精灵图 → 循环播放验证。这就是本工具做的事。


2. 为什么要把「视频」变成「精灵图」?

视频和精灵图其实是同一段动作的两种存法:

视频(mp4 / gif)精灵图(png + 索引)
播放者播放器 / 浏览器游戏引擎
能不能套骨骼不能能(配合骨骼动画)
能不能逐帧改难容易(单独导出每帧)
进 Unity / Godot要转直接吃

所以当你手上已经有了「想要的动作」(比如一段走路、挥剑、待机),但缺的是「游戏里能用的素材」时,这条路就派上用场了:

  • 复用现成动作:录屏、动捕片段、别处下载的 GIF,不用重新手绘每一帧。
  • AI 生成的动作也能用:用本地运行的视频模型(如 HunyuanVideo、Wan)生成一段动画,再转成精灵图——创意和预览很快,正式素材也能落地。
  • 像素 / 复古风兜底:拼出来的帧可以再走像素精修,直接当 16-bit 像素素材。

3. 整条流水线长什么样

屏幕截图 2026-10-25 120525

┌─────────────┐   ┌──────────────┐   ┌─────────────┐   ┌──────────────┐   ┌──────────┐
│ ① 选择视频   │ → │ ② 拆解帧 + 挑帧 │ → │ ③ 精灵图     │ → │ ④ 动画预览    │ → │ 导出成品  │
│ 视频 / GIF   │   │ 两把筛子去重    │   │ 网格拼接+索引 │   │ 循环播放看效果 │   │ PNG/JSON │
└─────────────┘   └──────────────┘   └─────────────┘   └──────────────┘   └──────────┘

每一步都对应一个画布节点,可以单独调参数,也可以整条一键跑。

① 选择视频

导入 mp4 / mov / webm / gif,先原地播一遍确认片段对不对——别等拆完才发现导错了片。

② 拆解帧 + 挑帧

把视频按「帧率上限(默认 12fps)」「最大帧数(默认 120)」「起止时间」拆成一摞帧。
然后两把筛子帮你把 200 帧压到 20 帧左右:

  • 跳帧步长(step):每 N 帧只留 1 张。动作慢就调大,动作快就调小。
  • 近似度过滤(simPct):和上一张「长得差不多」的自动丢掉。阈值 0 表示不过滤;拆完节点会告诉你「这段素材帧间差异的中位数」,照着它调最省事。

筛完之后最后还是你说了算——逐张勾选 / 反选 / 只留筛选结果。为什么不让程序自动抽 8 张就完事?因为「哪几个姿势构成一个完整循环」是审美决定,只有你自己看得到。

③ 精灵图

选中的帧按网格拼成一张 PNG,附带一份索引 JSON(记录每帧坐标 + 锚点)。

  • cols = 0 时自动排成接近正方形的网格(ceil(√帧数)),不会拉成一条超长条。
  • 可设帧间距(给 Unity 自动切片留 2px 透明间隔)和外边距。
  • 共享缩放:所有帧按同一比例缩放,抬腿的帧不会被莫名放大,整段循环里角色尺寸恒定。
  • 锚点(pivot)默认底部居中,方便在引擎里对齐脚底。

④ 动画预览

画布上按你设的帧率(默认 12fps)循环播放,可调显示倍率、逐帧步进,检查动作顺不顺、接缝对不对。


4. 背后那点「魔法」(通俗版)

近似度为什么能「拖滑杆即时出结果」?

后端给每一帧算了一份 16×16 灰度的「指纹」(256 字节的 base64)。前端拿到这串指纹,在浏览器里实时比较两帧的差异,完全不用再回后端。所以你拖阈值滑块,去重结果立刻刷新。

白底视频抠出来为什么会有「白边」?

白色背景的视频,人物边缘的像素其实是「角色色 + 背景白」的混合色(不是半透明,是混色)。普通抠图只按颜色距离判断,会把这种混色边当成背景留下,看着就是不干净的白圈。
本工具改用混合比例判据:|边缘色 - 背景| / |角色内侧色 - 背景| < 0.85 的就判定为溢色、修掉。这样不管白底灰底,边缘都能清干净。

为什么「共享缩放」很重要?

如果每张帧单独缩放到固定单元格,抬腿那一帧因为动作幅度大,角色会被放大;不抬腿的帧又偏小。循环播放时角色会一胀一缩。共享缩放先算出「所有帧里角色的最大包围盒」,统一按它缩放,尺寸就稳了。


5. 项目经验:我们做这个工具时踩过的坑

下面这些不是教科书结论,是实打实调出来的。每一个都附带当时的现象、根因和修复后的实测,给后来者省时间。

坑 1:白底视频抠出来一圈白边,怎么都去不干净

  • 现象:白色背景的人物视频,抠完边缘总留一圈白,看着很脏。
  • 先误诊:以为是半透明羽化没处理好。结果量化发现 hard=True 时 alpha 严格 0/255,半透明像素占比实测 0.0%——白边不是羽化问题。
  • 真根因:那圈像素的 RGB 本身就是「角色色 × α + 白底 ×(1−α)」的混色,不是半透明。旧判据用绝对颜色距离(门槛 50),但一个混了 40% 白的边缘像素到白底的距离约 200,永远判不进来。换「离背景近还是离内侧近」也不行:α≈0.5 的混色像素两边等距,越该修越判不出。
  • 修法:改用混合比例判据 |边缘色 − 背景| / |角色内侧色 − 背景| < 0.85(混进 >15% 背景才修),一套阈值通吃深底/浅底/彩底。
  • 实测(用户素材 640² 白底 21 帧):去污像素 1716 → 3994;边缘环均值 RGB [75,74,76] → [13,11,13](白边变回描边色),环里「距白底 < 40」的占比 0.5% → 0.1%。

坑 2:近似度去重,「连续静止段」压不掉

  • 现象:视频里常有连续 5 帧几乎不动(起步/停步),但逐帧比对只丢掉了 4 帧,整段静止还占着位置。
  • 根因:判据是「和上一帧比」。一旦某帧和前一帧差很小就被删,但静止段里每一帧都「约等于上一帧」,结果只丢掉少量。
  • 修法:判据改成「和上一张保留帧比」。连续不动的整段会被压成 1 帧。
  • 顺带一个交互教训:过滤只改显示,不自动改已勾选。否则你精心挑好的 12 帧,手一滑滑杆就全被换掉——这点绝对不能接受,必须点了「应用筛选结果」才采纳。

坑 3:去重凭什么能「拖滑杆零延迟」?

  • 做法:拆帧时后端给每一帧算一份 16×16 灰度的「指纹」(256 字节 base64),120 帧总共才约 30KB。前端拿到指纹在浏览器里实时算两帧差异,完全不回后端。所以拖阈值滑块,结果立刻刷新。
  • 为什么不用大模型/不回后端:去重是高频交互(拖一次滑杆算一遍),一旦走网络就会有几百毫秒卡顿,体验直接废掉。指纹够小、足够分辨「是不是同一个姿势」,再大只是浪费。

坑 4:cols=0 差点把精灵图拉成 2736×136 的超长条

  • 现象:21 帧、128px 单元格,如果一行排完,得到 2736×136(长宽比 20:1)的横条。某些引擎/采样不友好,看着也诡异。
  • 修法:cols=0 语义改成「自动方形」——列数取 ceil(√帧数),21 帧变成 5×5 的 656×656,显存一样但采样友好。要横排(Godot hframes / Unity 单行)就在节点上显式给列数,UI 提供「自动方形 / 横排一行」两个按钮。注意:老的 spritesheet 节点保持一行排完(那是每方向一条横条,语义不同)。

坑 5:像素提示词写「对齐网格线」,模型真画出格子

  • 现象:想让出图是像素块,提示词写「边界严格对齐网格线」,结果模型在每块之间画出了网格线/格子边框,像 Excel 表格而不是连续画面。
  • 修法:改成「边界落在网格上、但不要画出网格线本身」,并补一句「整幅图是连续的画面,不是一个一个方格」。出图立刻干净了。
  • 教训:提示词里「不要画 X」比「对齐 X」安全得多;模型会把抽象约束具象化成你没想到的东西。

坑 6:长任务一测就卡死,原来是测试方式错了

  • 现象:用接口测试工具跑「拆帧 + 全帧抠图」,进度停在「21 帧就绪」再也不动,doneAt 永不设置。但直接 await 跑同一个函数 20 秒就完事。
  • 根因:测试客户端(TestClient)每个请求新建一个事件循环,后台协程随请求结束被丢弃。秒级任务没问题,但抠图/视频这类长任务会永久卡死。
  • 规矩:秒级任务可用测试客户端;长任务(抠图、视频生成)必须拉起真实服务再验证(项目里有 tools/test_clip_api.py:临时数据目录 + 随机端口,跑完 kill,不污染数据)。

坑 7:视频生成模型,别让它当正式生产的唯一来源

  • 现象:用 HunyuanVideo / Wan 直接生成「行走视频」再拆帧,做出来会闪烁、角色漂移、耗时还忽长忽短(重试机制会让一次任务跑三次视频生成)。
  • 定位:视频模型适合做动作探索 / 预览,不适合当正式游戏精灵的唯一生产方式。同一段动作,正式生产应走「角色参考图 → 标准 2D 骨骼 → 确定性渲染精灵帧」,动作能套到多个角色、可编辑、不漂移。
  • 分工:本工具把视频定位成「动作来源」——不管是拍的、下的、还是 AI 生成的,转成精灵图后再进引擎或像素精修。这条分工最稳。

6. 上手小抄

  • 循环动画首尾别选重复姿势:末帧和首帧太像,播起来会「卡一下」。
  • 先播再拆:源片段确认对了再动手,省得拆完发现导错片。
  • 信任自己的眼睛做最后一刀:筛子只是帮你减负,真正挑哪些帧由你定。
  • 要做透明底素材:默认就开了 u2net 抠图;白底视频记得留意上面的「白边」处理已经帮你做了。
  • 要进 Unity / Godot:留 2px 帧间距 + 用导出的索引 JSON,引擎自动切片最省事。
  • 多方向行走:这条链是「已有一段视频 → 转精灵图」;要做八方向,可用画布里的「行走视频 / 序列帧 / 精灵整图」节点自己搭。

7. 它和「AI 生成正式素材」的关系

视频生成模型适合做动作探索,不一定适合当正式游戏精灵的唯一生产方式
(会有闪烁、角色漂移、耗时波动)。本工具把视频定位成「动作来源」,
转成精灵图后再进引擎或像素精修——这条分工更稳。

如果你想要的是「角色参考图 → 标准骨骼 → 确定性渲染精灵帧」的正式生产链路,
见同目录的技术评审文档 Meowcute 视频生成序列帧方案分析.md。


8. 一句话总结

本地视频转序列帧精灵图 = 把你已有的(或本地 AI 生成的)动作视频,
离线拆帧、挑帧、拼图,变成游戏能直接吃的精灵图与索引文件。

不联网、不花钱、素材不出本机,挑哪几帧永远是你说了算。