简介:这份压缩包聚焦 ComfyUI、AnimateDiff、ControlNet 与 Openpose+Depth 协同的动画生成流程,面向希望掌握 AI 动画制作的中高级创作者、数字媒体学习者与研究者,可用于理解场景搭建、帧间平滑过渡、行为控制及人体姿态与深度约束等关键环节。包内共 202 个文件,以 200 张 jpg 帧序列图为主体,另含 1 个 mp4 演示视频和 1 个 json 配置文件,整体约 8.27MB,便于按帧观察生成效果并对照视频复现流程。目前已有 787 人学习,可见关注度。读者可借助帧序列分析动画连续性,结合演示视频理解 Openpose 姿态捕捉与 Depth 深度信息在角色运动中的表现,并通过 json 文件辅助梳理参数组织与工作流结构,为后续动画实验和方案复现提供参考。
1. 三段约束叠加后减少画面不稳:这套包解决的是帧间一致性
先把场景摆出来:手搓一条 3 秒的角色动画,最让人头疼的往往不是单帧画质,而是第 12 帧角色突然换了张脸、胳膊穿进身体、背景像水波一样抖。这套 ComfyUI + AnimateDiff + ControlNet(Openpose + Depth)的整合包,走的正是把这类抖动压下去的路子——AnimateDiff 负责帧与帧之间的运动连贯,Openpose 把骨骼姿态钉死,Depth 锁住前后遮挡层次,三条约束同时挂在同一次采样上,比只挂一个运动模块稳得多。它适合已经能跑通 ComfyUI 基础出图、想做角色动作短片(走路、舞蹈、镜头推移)的人;不适合指望导入就出成片、完全没碰过节点图的新手。整个包的核心不是某个模型,而是那条已经连好的条件注入链路——理解它,你才知道哪个参数该动、哪个动了会翻车。
2. 三条约束的分工:AnimateDiff 与双 ControlNet 各自管什么
很多人第一次打开这类工作流,看到采样器前面挂了两路 ControlNet、中间又插了 AnimateDiff loader,第一反应是照抄参数。问题是这套链路里每个节点都在解决一个互不重叠的失败模式,你不动手拆一遍,参数只能靠猜。这一章把三者的职责边界讲清楚,再落到工作流文件的依赖核对上。
2.1 运动模块补的是帧间一致性,不是动作正确性
AnimateDiff 的做法是在 UNet 的每个 attention 层里插一个时序注意力模块,把原本只处理单张图的 2D 特征,扩展成沿时间轴做注意力。它的训练数据是大量短视频片段,学到的是「相邻帧之间特征应该怎么平滑变化」这个先验。所以它能压住的是闪烁、纹理跳变、噪点呼吸这类帧间不连续。
但它不知道「手应该抬到哪里」。你把一段完全没有姿态约束的提示词丢进去,它照样能生成一段连贯的、但动作随机的视频。这就是为什么单挂 AnimateDiff 出来的东西经常「很流畅但不知道在动什么」。运动模块解决的是连贯性下限,不是动作可控性上限。
另一个必须建立的概念是上下文窗口(context length)。运动模块的时序注意力不是全序列注意力,它一次只看固定长度的帧窗口,窗口内的帧互相做注意力,窗口之间靠重叠帧过渡。这个设计直接决定了显存占用和长视频的接缝质量,第 4 章会专门算这笔账。
2.2 Openpose 与 Depth 的边界:一个管骨架,一个管前后遮挡
两路 ControlNet 的互补关系,用一句话说:Openpose 告诉模型「关节在哪」,Depth 告诉模型「谁在前面」。
只挂 Openpose 的典型失败是这样:姿态完全正确,但手臂挥到身前时,模型分不清手臂该盖住身体还是被身体盖住,于是出现手臂半透明、衣服和手臂互相渗透的糊状区域。因为人体骨架图里根本没有深度信息,21 个关键点连线只是一张二维线稿。
只挂 Depth 的失败则反过来:空间层次很干净,前后关系清楚,但角色的关节位置每帧都在漂,膝盖位置飘来飘去,脚底打滑。深度图对「精确关节位置」这件事是模糊的,它只给相对远近。
两路一起上,等于同时给了模型硬约束(关节坐标)和软约束(空间次序),这就是这套包的核心价值。实际配置时的权重配比和生效区间不是随便填的,后面会给出参考值。
| 控制路 | 输入图类型 | 主要约束 | 典型失效场景 | 建议权重区间 |
|---|---|---|---|---|
| Openpose | 骨架线稿(黑底彩线) | 关节坐标、肢体朝向 | 遮挡关系混乱、手臂穿透 | 0.6 ~ 0.9 |
| Depth | 灰度深度图 | 前后层次、体积感 | 关节漂移、脚底打滑 | 0.4 ~ 0.7 |
| AnimateDiff | 无(模块注入) | 帧间特征平滑 | 动作随机、幅度失控 | 由 motion_scale 控制 |
注意表里的权重是「单路独立生效」时的经验区间,两路同时开启时,总和过高会让画面变僵——人物像被钉在骨架上的木偶,皮肤细节和布料褶皱全部丢失。这一点在后面调参时会反复回到。
2.3 打开工作流先数节点:依赖核对清单
拿到包之后别急着点运行。先解压,把工作流 JSON 用编辑器打开,搜一遍class_type字段,把所有非内置节点列出来,再对着custom_nodes目录逐个核对。这一步花五分钟,能省掉后面半小时对着红色报错框发呆的时间。
# 进入 ComfyUI 根目录,先看已装的自定义节点 cd ComfyUI ls custom_nodes/ # 从工作流 JSON 里抽出所有节点类型,去重排序 python - <<'PY' import json, collections wf = json.load(open("workflow_animatediff_pose_depth.json")) types = collections.Counter() # 兼容 UI 格式(nodes 数组)与 API 格式(dict) if isinstance(wf, dict) and "nodes" in wf: for n in wf["nodes"]: types[n.get("type", "?")] += 1 else: for n in wf.values(): types[n.get("class_type", "?")] += 1 for t, c in sorted(types.items()): print(f"{c:>3} {t}") PY第一段只是列目录,肉眼确认ComfyUI-AnimateDiff-Evolved、comfyui_controlnet_aux、ComfyUI-Advanced-ControlNet这几个目录在不在。第二段脚本做的是节点类型统计——它输出的清单里,凡是带ADE_前缀的是 AnimateDiff Evolved 的节点,带ACN_前缀的是 Advanced ControlNet 的节点,带DWPreprocessor/MiDaS字样的是预处理器节点。缺哪个就补哪个,别指望 ComfyUI 自动装。
内置节点(CheckpointLoaderSimple、KSampler、VAELoader这类)不需要额外装,但如果清单里出现你不认识的前缀,先去 Manager 里搜节点名确认来源,不要随便装一个同名的第三方包顶替——节点内部的输入签名一旦对不上,工作流加载时是静默丢连线,不报错,你会在出图后才发现某一路 ControlNet 压根没生效。
模型文件的摆放同样是硬性核对项,路径错了不会报错,只会退化成「无约束生成」:
# 三类模型各自的落点,路径必须一字不差 ls -lh models/checkpoints/ # 底模,SD1.5 系 ls -lh models/controlnet/ # openpose 与 depth 两个 controlnet ls -lh custom_nodes/ComfyUI-AnimateDiff-Evolved/models/ # 运动模块 ckpt运动模块放错目录是最高频的翻车点之一。很多人习惯性丢进models/checkpoints/,结果 AnimateDiff loader 的下拉框里永远是空的。Evolved 版本读的是自己插件目录下的models/,这是设计如此。
3. 从参考视频到条件序列:预处理链路怎么搭
工作流里那两路 ControlNet 需要的不是原始视频,而是逐帧对齐的骨架序列和深度序列。这一步做得好不好,直接决定成片的上限——条件图抖,后面采样器再强也救不回来。这一章把抽帧、批量导出、首轮验证串成一条可复现的链路。
3.1 素材挑选与抽帧步长的取舍
参考视频的挑选有三条硬标准,违反任意一条都会让后面的调参变成无用功。
第一条,主体在画面里的占比要稳定。如果人物从远处走到近处,骨架尺度变化剧烈,Openpose 提取出来的线稿粗细会跟着变,模型会把这种尺度变化理解成「人在膨胀」,出来就是诡异的变形。
第二条,避免大幅度快速运镜。镜头本身在动的时候,Depth 图会把背景的运动也当成深度变化,两路约束打架,画面会出现背景撕裂。
第三条,帧率不要太高。原始 60fps 的素材抽成 8fps 就够用了,多余帧只会让显存爆得更快,而运动信息并没有增加多少。抽帧步长(stride)这个参数我一般按「目标帧率 = 源帧率 / stride」倒推。
# 源视频 30fps,想要 8fps 的序列 => stride 约等于 4 # 先看源视频基本信息,确认帧率和总帧数 ffprobe -v error -select_streams v:0 \ -show_entries stream=r_frame_rate,nb_frames,width,height \ -of default=nw=1 source_clip.mp4r_frame_rate输出形如30/1,nb_frames是总帧数。两个数一除就知道时长,再乘目标帧率就是要生成的帧数。这一步别省,很多人拿一段 10 秒素材直接扔进 16 帧的工作流,等于只用了开头 0.5 秒,后面全靠模型瞎猜,动作自然对不上。
抽帧本身用 ffmpeg 就够,不需要额外脚本:
# 输出到 frames/ 目录,编号 00001.png 起,步长 4 mkdir -p frames ffmpeg -i source_clip.mp4 -vf "select='not(mod(n\,4))'" -vsync 0 frames/%05d.pngselect里的mod(n,4)是取模表达式,n是源帧序号,-vsync 0保证输出帧号连续不重复。文件名用五位补零是为了后面按字典序读入时顺序正确——用%d.png的话,第 10 帧会排在第 2 帧前面,这种排序错误在批量处理里非常隐蔽。
3.2 用脚本批量导出 pose 与 depth 序列
ComfyUI 里当然可以挂预处理器节点逐帧跑,但对于几十上百帧的序列,走节点图反而慢且不好断点续跑。我习惯先用脚本离线把两套条件图生成好,存成图片序列,工作流里直接改成从目录批量加载。这样调参时不用反复跑预处理,改一个权重就能重采样。
import os, cv2, torch from controlnet_aux import OpenposeDetector, MidasDetector SRC = "frames" # 3.1 抽出来的原始帧 POSE = "cond/pose" # 骨架输出 DEPTH = "cond/depth" # 深度输出 os.makedirs(POSE, exist_ok=True) os.makedirs(DEPTH, exist_ok=True) # 两个检测器共用一个权重仓库,首次运行会自动下载 openpose = OpenposeDetector.from_pretrained("lllyasviel/Annotators") midas = MidasDetector.from_pretrained("lllyasviel/Annotators") names = sorted(os.listdir(SRC)) for i, name in enumerate(names): img = cv2.imread(os.path.join(SRC, name)) if img is None: # 跳过损坏文件,别让整批中断 print("skip", name); continue img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # detect_resolution 控制检测器内部缩放,512~768 足够 # image_resolution 是输出图边长,必须和采样分辨率一致 pose = openpose(img, detect_resolution=512, image_resolution=512) depth, _ = midas(img, detect_resolution=512, image_resolution=512) pose.save(os.path.join(POSE, f"{i:05d}.png")) depth.save(os.path.join(DEPTH, f"{i:05d}.png")) print(f"[{i+1}/{len(names)}] {name}")几个参数的实际含义值得说清楚。detect_resolution是检测网络内部的推理分辨率,调大能让手指、手腕这些细关节更准,但速度按平方下降;512 对小尺寸人物够用,全身特写才需要往 768 走。image_resolution是最终存下来的图尺寸,这个必须和后面采样器里潜空间的分辨率成整数倍关系,否则 ControlNet 内部会做一次重采样,骨架线会糊掉,权重再高也救不回来。
输出的两个目录里,文件名必须严格一一对应(都是00000.png到00NNN.png),这是后面「从目录批量加载」节点能按顺序配对的前提。脚本里没有做任何排序纠正,靠的就是五位补零加sorted(),这条约定别破坏。
跑完之后抽样看三帧:第一帧、中间帧、最后一帧。检查骨架是否完整覆盖全身(缺脚踝是最常见的),深度图里主体和背景的灰度差是否足够。这两张图不合格,后面所有调参都是在错误的地基上加盖。
3.3 第一次出片:16 帧、512 方图、batch 1 的验证套路
第一次跑通不要贪。把工作流里的总帧数改成 16,分辨率 512×512,batch size 设 1,采样步数 20 左右。这个配置在 8GB 显存的机器上也能挤进去,目的是验证链路而不是验证效果。
判断「链路通了」的依据不是画面好看,而是三条:
第一,AnimateDiff loader 加载成功,采样日志里能看到运动模块的注入层数,没有出现「no motion module found」之类的提示。
第二,两路 ControlNet 都生效。验证方法很土但有效:把 Depth 那一路的权重临时改成 0,再跑一次 16 帧,对比两次结果。如果画面没有任何变化,说明这一路根本没接进采样器,回去查节点连线是不是接到了ControlNetApplyAdvanced的positive上而不是悬空。
第三,帧序列输出完整,16 张图没有重复帧、没有全黑帧。输出节点如果是VHS_VideoCombine,注意它的frame_rate要和你的目标播放帧率一致,默认 8 在这个阶段够用。
首轮跑通之后再谈质量。倒过来做的人,往往一上手就 512×768、64 帧、步数 30,等到 OOM 或者画面崩了,分不清是参数问题还是链路问题。
4. 参数表:帧数、上下文窗口与双路权重怎么配
原型跑通之后,真正决定成片质量的就剩下三组参数:时间维度的窗口配置、两路 ControlNet 的权重与生效区间、运动幅度的控制。这一章把它们拆开算清楚,给出可以照着改的数值。
4.1 total frames 与 context_length:显存换连贯度的换算
context_length是一次时序注意力覆盖的帧数,total frames是最终要生成的总帧数。当总帧数小于等于窗口长度时,所有帧在一个窗口里做注意力,连贯性最好,显存占用也最高。当总帧数超过窗口长度,插件会把序列切成多个窗口,相邻窗口之间保留一部分重叠帧(overlap)来过渡。
显存和窗口长度基本是线性关系,但和总帧数是阶梯关系——总帧数翻倍不一定显存翻倍,因为切窗口之后单次前向的规模没变。真正撑爆显存的是窗口长度和分辨率的乘积。
| 显存档位 | 分辨率 | context_length | overlap | 单批可用总帧数 | 备注 |
|---|---|---|---|---|---|
| 8 GB | 512×512 | 16 | 4 | 16 ~ 32 | 必须 batch 1,关掉多余预览 |
| 12 GB | 512×512 | 16 | 4 | 32 ~ 48 | 可开一路 ControlNet 高分 |
| 16 GB | 512×768 | 24 | 6 | 48 ~ 64 | 双路全开的安全线 |
| 24 GB | 512×768 | 32 | 8 | 96 以上 | 再长建议改分段接力 |
overlap 这个参数容易被忽视。它太小(比如 1),窗口接缝处会出现明显的姿态跳变——前一窗最后一帧和后一窗第一帧的关节位置对不上。它太大,等于有效帧数变少、耗时变长。经验值是 context_length 的四分之一,16 帧窗口配 4 帧重叠,24 帧配 6 帧。
还有一点:total frames最好是(context_length - overlap)的整数倍,否则最后一窗会填充重复帧,结尾容易出现定格。
4.2 两路 ControlNet 的权重与生效区间
权重只是第一层,start_percent和end_percent才是精细控制。默认 0 到 1,意味着一路约束从第一步采样一直管到最后一步。实际使用中,两路的最优区间并不相同。
Openpose 建议全程生效,但尾段略降:start 0.0 / end 1.0,权重 0.7~0.85。骨架是硬约束,一旦中途撤掉,后面几步采样会迅速把姿态拉回「模型觉得合理」的位置,前面白干了。
Depth 建议前段高、后段低:start 0.0 / end 0.7,权重 0.5~0.65。深度管的是空间层次,这个信息在采样早期建立构图时最有用,到了后期主要是补细节,此时过强的深度约束会让皮肤、布料失去质感,画面发灰发闷。
两路权重之和不要超过 1.6,超过就是前面说的木偶感。如果发现人物姿态对了但画面很「平」,优先降 Depth 而不是降 Openpose;如果发现人物细节丰富但动作走形,优先降 Depth 权重同时把 Openpose 的end_percent保持在 1.0。
另一处常被忽略的是 controlnet 的预处理开关。工作流里如果用的是Load Image直接读已经生成好的骨架图,预处理器节点的detect要关掉,否则会把骨架图再检测一遍,输出一张空图或乱线。
4.3 motion_scale 与提示词:幅度失控的两个来源
动作幅度过大的锅,一半在motion_scale,一半在提示词。
motion_scale是 AnimateDiff 里直接缩放运动模块输出的系数,默认 1.0。调到 1.2 以上,动作会明显变大,但也更容易出现肢体撕裂和形变;调到 0.8 以下,画面会趋于静止,接近「轻微呼吸」的效果。这个参数和 ControlNet 权重是耦合的——骨架约束强的时候,motion_scale 可以放心往上走,因为姿态被钉住了;骨架权重低的时候,高 motion_scale 就是灾难。
提示词这边,忌讳的是堆砌动作动词。「跳舞、旋转、挥手、跳跃」全写上去,模型会在每一帧尝试满足所有动作,输出就是抽搐。正确做法是把动作交给 Openpose 序列,提示词只描述主体外观、服装、场景光照和整体风格。动作描述保留一个概括词即可,比如「a person dancing」。
负面提示词里加上与时间稳定性相关的项是有帮助的,比如画面撕裂、多帧重影、肢体重复这类描述,但不要指望它替代参数调整。
批量试参的时候,手动改节点太慢。工作流导成 API 格式(Save (API Format)),用脚本改参数提交,一次跑一组:
import json, copy, requests API = "http://127.0.0.1:8188/prompt" base = json.load(open("workflow_api.json")) def patch(wf, node_id, **kw): """按节点 id 覆盖 inputs 字段,返回新的工作流副本""" wf = copy.deepcopy(wf) wf[node_id]["inputs"].update(kw) return wf # 节点 id 需要在你自己的工作流里确认,这里只是示意 runs = [ {"pose_w": 0.85, "depth_w": 0.55, "motion": 1.0}, {"pose_w": 0.70, "depth_w": 0.65, "motion": 1.1}, {"pose_w": 0.90, "depth_w": 0.45, "motion": 1.2}, ] for i, cfg in enumerate(runs): wf = patch(base, "12", strength=cfg["pose_w"]) # Openpose 应用节点 wf = patch(wf, "15", strength=cfg["depth_w"]) # Depth 应用节点 wf = patch(wf, "8", motion_scale=cfg["motion"]) # AnimateDiff loader wf = patch(wf, "20", seed=1000 + i) # 固定种子便于对比 r = requests.post(API, json={"prompt": wf}) print(i, r.status_code, r.json().get("prompt_id"))patch函数做的是浅拷贝加深拷贝组合——deepcopy保证每轮修改不污染原始模板,update只覆盖指定字段,其余保持工作流原样。节点 id 必须从你自己的 API 格式文件里读,不同工作流的 id 完全不同,硬编码别人的 id 是跑不通的。seed固定这一点很关键,三组对比必须同种子,否则你分不清画面差异来自参数还是来自随机数。
5. 避坑排查:五类常见问题的现象、成因与处置
参数调完之后,剩下的时间基本都花在排查上。这一章记录五个我反复遇到的典型故障,每条按「现象 → 原因 → 解决」写,能对上号就直接照做。
5.1 画面类故障:闪烁、鬼影与肢体融化
现象一:输出视频整体在抖,背景纹理像水面一样持续波动,人物轮廓边缘持续跳动。
原因通常有两个。一是context_length设得比总帧数小太多,切窗频繁且 overlap 不足,窗口接缝处特征不连续。二是运动模块的 ckpt 和底模版本不匹配——用 SD1.5 的底模配了一个为 SDXL 训练的运动模块,时序先验完全对不上,模型只能靠猜,表现出来就是高频抖动。
解决顺序是先查版本匹配,再调窗口。把context_length提到 16,overlap提到 4,重跑同一段 32 帧对比。如果抖动减弱但没消失,基本可以确认是版本问题,换运动模块文件。判断版本不用猜,看底模的 latent 通道数——SD1.5 是 4 通道,SDXL 不同,加载日志里会打印。
现象二:手臂挥到身前时出现半透明重影,像有两层手臂叠在一起。
这是典型的遮挡关系失败,本质是 Depth 那一路没有生效或者权重过低。先去确认 Depth ControlNet 的输入图不是全白或全黑的——预处理阶段如果深度图输出异常(比如背景和主体灰度几乎一致),这一路等于没给信息。
解决方法是检查深度图对比度,必要时在预处理阶段把输出的深度图做一次归一化拉伸,让主体与背景拉开灰度差。同时把 Depth 权重提到 0.6 以上,end_percent保持在 0.7 左右。
现象三:脚部和小腿糊成一团,看不出脚踝结构,人物像踩在棉花上。
原因多半是骨架图本身缺关键点。Openpose 在人物下半身被遮挡或者画面裁切到脚踝时,检测不到脚部关键点,输出的骨架图下半段是空的,模型只能自由发挥。这不是参数能修的。
解决办法是回到素材层面,选一个全身完整入画的参考视频,或者把detect_resolution提到 768 再检测一次,看关键点能不能补回来。补不回来就换素材,别在这上面耗。
5.2 运行类故障:OOM、静默失效与帧率错位
现象四:跑了几十帧之后突然报显存不足,前面十几帧都正常。
原因不是显存总量不够,而是 ComfyUI 在长序列生成时会累积中间张量,尤其是启用了多个预览节点或者VHS_VideoCombine在每帧都做一次编码。这类泄漏在 16 帧的测试里完全看不出来。
解决方法是关掉所有中间预览节点,把VHS_VideoCombine的save_output改成只在最后执行,或者在启动时加上--lowvram参数让它更激进地换出。另外可以主动把总帧数切段,用第 6 章的分段接力方式跑,单段不超过 32 帧。
现象五:成片动作明显比预期快或慢,抽帧得到的姿态和画面不同步。
这是帧率错位。三个地方的帧率必须一致:抽帧时的目标帧率、工作流里VHS_VideoCombine的frame_rate、以及最终播放/合成的帧率。任何一处不一致,动作速度就会整体缩放。表现出来往往是「动作是对的但节奏不对」,很容易被误判成 motion_scale 的问题,白调半天。
排查方法是数帧:拿成片的帧数除以时长,和源素材的目标帧率对一遍。不一致就改合成那一步的-r参数,不要去动采样参数。
6. 从 16 帧到长镜头:分段接力与一致性验证
16 帧能跑通,不等于能做 5 秒以上的镜头。直接拉长总帧数,显存先崩;就算勉强跑出来,长序列中段的姿态漂移也会累积到无法接受。真正可用的做法是分段接力:把一条长动作拆成若干个互相重叠的片段,逐段生成,再用重叠帧做衔接。
分段的切法有讲究。假设总长 96 帧,单段 32 帧、重叠 8 帧,那么分段起点分别是 0、24、48、64,每段覆盖到起点加 32。重叠区的作用是提供视觉连续性参照——后一段的起始帧应该和前一段的末尾帧在姿态上高度接近,如果你发现某个接缝处姿态跳变明显,说明该处的参考素材本身在快速运动,需要把重叠加宽到 12 帧。
| 参数 | 单段值 | 说明 |
|---|---|---|
| segment_frames | 32 | 单次生成长度,受显存限制 |
| overlap | 8 | 相邻段共享帧数,快速动作建议 12 |
| context_length | 16 | 段内仍按窗口切 |
| seed | 固定 | 所有段用同一个种子,减少风格跳变 |
| pose 权重 | 0.85 | 接力时骨架要更硬,防止累积漂移 |
种子必须固定这件事值得单独强调。同一批分段如果每段换种子,画面的色调、光照、皮肤质感会在接缝处突变,看起来像两个不同的镜头硬剪在一起。固定种子之后,模型的初始噪声分布一致,风格漂移会小很多。代价是可能重复出现同一个瑕疵,那就挑一个瑕疵最少的种子。
合成用 ffmpeg 最简单,把每段输出的图片序列按顺序拼起来,重叠区做裁剪:
# 每段已经导出为 seg_00/00000.png 形式的序列 # 先按段拼成视频,再裁掉重叠部分 for d in seg_00 seg_01 seg_02 seg_03; do ffmpeg -framerate 8 -i "$d/%05d.png" -c:v libx264 -crf 16 -pix_fmt yuv420p "$d.mp4" done # concat 列表,注意裁剪重叠帧需要按段单独处理,这里给的是最简拼接 printf "file '%s'\n" seg_*.mp4 > list.txt ffmpeg -f concat -safe 0 -i list.txt -c copy rough.mp4-framerate 8必须和生成时的目标帧率一致,-crf 16是接近无损的质量档,中间产物不要用高压缩,反复编码会累积块效应。直接concat拼接会带着重叠帧,成片在接缝处有短暂停顿,严格的裁剪要按段计算偏移,用trim滤镜逐段处理,这里先给出能跑的骨架版本,等你确认姿态衔接没问题再做精细裁剪。
验证一致性我用两个笨办法。第一个是抽帧比对:把成片每隔 8 帧导出一张静帧,横排拼成一张对比图,肉眼扫一遍姿态曲线是不是平滑。第二个是看差分图:把相邻两帧做灰度相减,正常运动下差分图的亮区应该集中在运动部位,如果整幅图都在亮,说明有全局抖动或曝光漂移,回去查 context 窗口和种子。
我最早做这类动画时,图省事直接一次性拉满 64 帧,跑完之后画面抖得没法看,还以为是模型不行,换了三四个运动模块才发现是窗口配置的问题。从那以后我每次改总帧数,都强制先跑一遍 16 帧的同种子对照,确认链路没断、参数没被覆盖,再往上加长度。这个习惯看着啰嗦,但省下来的重跑时间远比它多。希望帮到你。
本文还有配套的精品资源,点击获取