音画同步合成与修复
目的
把 N 张页面图与一条连续配音音轨合成为视频,使每页画面的切换时刻精确落在解说词的段落边界上。
同样适用于修复已有的错位视频:画面是 N 张图各显示固定时长,配音却是连续的一条,导致越往后偏得越多。
核心判断
不要用静音检测去猜切点。 实测中,配音带背景音乐时,静音检测会把段落边界判晚 1.8~3.5 秒,因为音乐盖住了说话人之间的停顿。这个误差在成片里表现为「解说已经在讲第 3 页,画面还停在第 2 页」。
正确做法是把 ASR 文本当作时间标尺:转写配音得到带时间戳的句子,与解说词原稿做字符级对齐,再把「原稿第 N 段结束」这个位置反查回时间轴。这个方法不受背景音乐影响,实测把逐页内容匹配度从 0.77 提升到 0.86,跨段污染从 2 页降到 1 页。
输入三件套
| 输入 | 说明 | 缺失时的后果 |
|---|---|---|
| 页面图 N 张 | 建议从 PPT 导出原生 1920×1080 PNG,命名有序 | 无法合成 |
| 配音音轨 | wav 或含音轨的视频 | 无法合成 |
| 解说词文本 | 一段一行,与页面一一对应 | 强烈建议提供。缺失则只能用兜底方案,精度显著下降 |
解说词是本技能精度的主要来源。它可以是当初生成配音时用的稿子,也可以是照着视频听写出来的。
执行流程
Step 0 依赖自检
python scripts/sync.py deps
缺什么装什么,最小依赖见 requirements.txt。只要跑对齐与合成(已有 asr.json 和切点),不需要装 faster-whisper。
Step 1 准备页面图
从 PPT 导出 PNG,优先用原生 1920×1080 而不是放大的 720p。命名要保证自然排序正确,「幻灯片2」必须排在「幻灯片10」前面,collect_images() 已处理这一点,也可用 --pattern "幻灯片%d.PNG" 显式指定。
必须确认页面数等于解说词段数。 不一致时画面会整体错位,align.py 会打印警告但不会自动修复。
Step 2 跑全流程
python scripts/sync.py all \
--video src.mp4 \
--images hd/ \
--script script.txt \
--out out.mp4 \
--pattern "幻灯片%d.PNG"
若已有 wav 音轨,用 --audio user.wav 代替 --video,跳过抽轨。中间产物落在 --work(默认 _sync_work):audio.wav、asr.json、cuts.txt、align_report.txt。
Step 3 检查切点报告
读 align_report.txt。每行末尾是置信度标记:
ok:该边界在 ASR 中有明确锚点low:附近锚点较远,可能有零点几秒误差fallback:对齐失败,退化为按字数比例切分,需要人工复核
出现 fallback 时不要直接出片,先检查解说词与配音是否真的对得上。
Step 4 合成
合成阶段由 render.py 完成,音轨原样封装不重采样,画面用 concat 硬切而非转场,保证画面总长与音轨严格相等。
Step 5 验证(不可跳过)
python scripts/verify.py --video out.mp4 --images hd/ --cuts cuts.txt --ref-audio src.mp4
三项检查:
- 页面顺序(每页中点抽帧)
- 切点是否真正生效(切点前后各抽一帧)— 这是关键项
- 音轨波形相关系数,>0.99 视为配音未被改动
第 2 项为什么重要:只验每页中点时,如果新旧切点差异小于半页时长,两版都会报「命中 11/11」,看起来都对。只有在切点前后各抽一帧,才能证明新切点真的落盘了。对不合格的切点,脚本会自动二分定位真实翻页时刻并给出偏差值。
分步执行
需要干预某一步时,用子命令单独跑,参数原样透传给对应脚本:
python scripts/sync.py asr --audio user.wav --out asr.json --model tiny
python scripts/sync.py align --asr asr.json --script script.txt --out cuts.txt --duration 123.37
python scripts/sync.py render --images hd/ --cuts cuts.txt --audio user.wav --out out.mp4
python scripts/sync.py verify --video out.mp4 --images hd/ --cuts cuts.txt
调参常见场景:
- 某一页翻早了或翻晚了:直接改
cuts.txt里对应数值,重跑render,30 秒出片 - 换更准的 ASR:
--model small,代价是模型体积与转写时间大幅增加 - 换分辨率:
--width 1080 --height 1920(竖版)
降级路径
无解说词文本、或 ASR 装不上时:
python scripts/vad_fallback.py --audio user.wav --segments 11 --counts 40,80,57,... --out cuts.txt
多阈值扫描静音点后用动态规划选切分。--counts 给各段字数能显著改善预测。
必须向用户说明:兜底方案的精度明显低于 ASR 对齐,在有背景音乐的素材上会出现 2 秒级偏移,出片后需要人工听一遍。
已知环境坑
遇到报错先查 references/pitfalls.md,已收录的四个高频问题:
- HuggingFace 下载模型 403(国内网络)→ 走 ModelScope
av库版本不兼容导致 transcribe 抛 TypeError → 绕过文件解码喂数组- 剪映草稿文件全加密,读不出片段位置 → 不要试图解析工程文件
- 验证脚本只抽中点造成假阳性 → 加切点前后抽帧
文件结构
scripts/
sync.py 流水线入口与依赖自检
_env.py 环境探测(ffmpeg / 模型目录 / 页面图 / 音频读取)
asr.py 配音转写
align.py 解说词与 ASR 对齐,求切点(核心)
render.py 按切点合成视频
verify.py 成片验证
vad_fallback.py 静音点 + DP 兜底切分
references/
pitfalls.md 环境坑与绕法
algorithm.md 对齐算法细节与调参