Files
contentm_agent/.workbuddy/skills/aigc-idea-impression/references/操作规范/视频抽帧与字幕规范.md
T
WorkBuddy df56c2c137 初始化提交:contentm_agent 工作区全量快照
内容分四块:
1、产品规划产出 —— MCN 短视频整合营销工作台的①段五份(1a 需求/1b 竞品/1c 画像/1d 策略/1e 场景)、②段两份(2a 功能/2b 布局)、③段界面(DESIGN.md 契约与令牌表 + mcn-workbench.html 原型 + 实测/会诊/审查三份 + 23 张闸门截图)。
2、开源竞品调研 —— 5 个内容工作台项目的取证原始件与 1b 系列分析文档。
3、参考资料 —— 竞品视频抽帧 1145 张 + 2 个源视频 + 功能点截图。
4、机制侧 —— 协作脚本与状态台账、工作区记忆日志、抽帧/OCR 脚本。

.gitignore 只排运行时日志、脚本备份副本与一次性探针输出,其余按原样入库。
2026-10-08 08:13:02 +08:00

7.4 KiB
Raw Blame History

视频抽帧与字幕规范(SOP)

抖音等平台视频下载后的抽帧与字幕提取标准流程。基于 2026-08-30 实测(《当队伍里有两只贪吃的猪》70.3s)沉淀。 应用场景:视频理解、画面反推提示词、字幕转录、分镜分析。


⚠️ 核心铁律(任何抽帧/字幕任务执行前必读,违反即漏帧 / 全批失败)

本规范结论必须逐条执行,不是参考建议。历史上已两次因"未按此执行"导致字幕大面积丢失(教训 #006、#010)。执行前请逐项勾选:

  1. 双因素 + 首尾 + 补帧,缺一不可:① 画面 scene 0.3 ② 字幕区 scene 0.1(漏这条 = 画面静止但字幕换句的帧全丢) ③ scene 0.05 补漏 ④ 首尾帧 ⑤ >5s 长间隔补中点。
  2. 字幕区必须 crop 后再 scene 检测,crop 按实际分辨率底部 15%:1080p 横屏 crop=1920:162:0:918;720p crop=1280:108:0:612;竖屏 720×960 crop=720:240:0:720。先抽一帧确认字幕位置再 crop。
  3. 字幕 OCR 必须逐帧:对每帧字幕区都做 OCR,再按时间滚动去重(相似度 ≥0.80 合并保留最长),禁止只对每个聚类「代表帧」OCR(代表帧模糊/过渡/fade → 整句丢失)。
  4. CRLF 防护:Python/Windows 生成时间戳列表须 newline='\n',否则行尾带 \r → ffmpeg -ss 报 Invalid duration 全批失败。
  5. 禁 select='eq(t,...)' 精确时间匹配抽帧(浮点误差命中率极低),一律用 -ss 逐点抽。
  6. 反推 ≠ 规则:禁止从旧产物(scene_*.txt / frames_combined / kf_*)反推流程当规则;执行以本规范为准,文档缺失先补文档再执行。

一、抽帧策略(双因素 + 首尾 + 补帧,⭐ 权威方案)

核心原则:只抽"有信息量的帧"——画面变化点 + 字幕变化点 + 首尾帧 + 长间隔补帧,避免固定节奏(fps=1)浪费静止帧。

因素 命令 阈值 捕捉内容
① 画面变化 select='gt(scene,0.3)' 0.3 镜头切换/画面突变
② 字幕变化 crop=W:底1/4:0:Y + select='gt(scene,0.1)' 0.1 字幕换句(含画面静止时)
③ 低阈值补漏 select='gt(scene,0.05)' 0.05 中等缓变(0.05~0.3 盲区,如 53.93s 案例)
④ 首帧/尾帧 -ss 0 / -ss DUR-0.3 — 开场/结尾画面(scene 不触发)
⑤ 长间隔强制补帧 帧间隔 >5s → 该段中点强制抽 1 帧 — 物理兜底,杜绝长盲区(如 8.4s 空隙)

执行流程:

  1. 画面 scene 检测(0.3)→ 时间点 A
  2. 字幕区 scene 检测(0.1)→ 时间点 B
  3. 低阈值 scene 检测(0.05)→ 时间点 C(补中等缓变盲区,不是可选补救,是标准步骤)
  4. 合并去重 A∪B∪C → 基础帧集
  5. 长间隔强制补帧:排序后检查帧间隔,任一 >5s 则在该段中点补 1 帧(物理上无盲区)
  6. 必加首帧(0s)+ 尾帧(DUR-0.3 或 -ss 69.9)
  7. 逐时间点 -ss 抽帧(精确时间匹配 eq(t,xx) 命中率低,勿用)

字幕区裁剪(竖屏 720×960):字幕通常在底部 1/4 → crop=720:240:0:720。横屏或字幕位置不同时按实际调整。

二、抽帧命令模板

FF="/path/ffmpeg.exe"; VIDEO="video.mp4"; mkdir -p frames_combined

# ① 画面变化点
ffmpeg -i "$VIDEO" -vf "select='gt(scene,0.3)',showinfo" -vsync vfr -f null - 2>&1 \
  | grep -oE "pts_time:[0-9.]+" | cut -d: -f2 | sort -n -u > scene_03.txt

# ② 字幕变化点(竖屏底部 1/4 区域)
ffmpeg -i "$VIDEO" -vf "crop=720:240:0:720,select='gt(scene,0.1)',showinfo" -vsync vfr -f null - 2>&1 \
  | grep -oE "pts_time:[0-9.]+" | cut -d: -f2 | sort -n -u > subtitle_01.txt

# ③ 低阈值复查(scene=0.05,找中等缓变盲区)
ffmpeg -i "$VIDEO" -vf "select='gt(scene,0.05)',showinfo" -vsync vfr -f null - 2>&1 \
  | grep -oE "pts_time:[0-9.]+" | cut -d: -f2 | sort -n -u > scene_005.txt

# ④ 合并 + 长间隔强制补帧(间隔 >5s 补中点,物理杜绝盲区)
sort -n -u scene_03.txt subtitle_01.txt scene_005.txt > times.txt
echo "0" >> times.txt   # 首帧
DUR=$(ffprobe -v error -show_entries format=duration -of csv=p=0 "$VIDEO")
# 长间隔补帧:遍历排序后时间点,间隔>5s 插入中点
prev=""
for t in $(sort -n -u times.txt); do
  if [ -n "$prev" ]; then
    gap=$(echo "$t - $prev" | bc)
    if [ "$(echo "$gap > 5" | bc)" = "1" ]; then
      mid=$(echo "($t + $prev) / 2" | bc)
      echo "$mid" >> times.txt
    fi
  fi
  prev="$t"
done
sort -n -u times.txt > times_final.txt

# ⑤ 逐时间点抽帧(尾帧用 DUR-0.3 防超界)
i=0
for t in $(cat times_final.txt); do
  [ "$(echo "$t > $DUR" | bc)" = "1" ] && t=$(echo "$DUR - 0.3" | bc)
  i=$((i+1))
  ffmpeg -y -ss "$t" -i "$VIDEO" -frames:v 1 -q:v 2 "frames_combined/c_$(printf %02d $i)_${t}s.jpg" 2>/dev/null
done

三、帧命名规则

  • frames_combined/c_XX_时间s.jpg(如 c_01_2.233333s.jpg)
  • 时间戳保留原始精度(scene 检测输出浮点),便于按帧对应视频时刻

四、时间区间划分(每帧代表的时间段)

  • 相邻帧中点划分(Voronoi):帧 i 代表 [中点(i-1,i), 中点(i,i+1)]
  • 首帧从 0 起、尾帧到视频结束(DUR)
  • 用于:分镜时间轴、字幕时间线、画面-台词对应

五、字幕提取

  • 抖音字幕是烧录在画面上的(非独立字幕轨)→ 从帧画面转录(OCR / 视觉读取)
  • 字幕区 scene 检测已保证每个换句时刻都有一帧(含画面静止时)
  • 🔴 必须逐帧 OCR 字幕区(见顶部核心铁律第 3 条):对每一帧都做 OCR,再按时间滚动去重,保留最完整版本。禁止只对每个聚类「代表帧」OCR——代表帧一旦模糊/过渡/fade,整句字幕会永久丢失(#010 事故根因)。
  • 转录产出:字幕稿_日期.md,格式 = 时间线表(时间 | 台词 | 帧文件)
  • 长间隔段(如 8~9s 无变化):该段字幕可能持续同句,读帧确认即可

六、三版抽帧对比(为什么不用固定节奏)

方案 帧数(70s) 感知画面 感知字幕 结论
I 帧 12 ❌ ❌ 编码关键帧,漏字幕
fps=1 70 ❌ ✅ 固定节奏,浪费 56%
双因素+首尾+补帧 31 ✅ ✅ 精准全覆盖,省帧

七、常见坑

  • select='eq(t,2.233333)' 精确时间匹配命中率极低(浮点误差)→ 用 -ss 逐点抽
  • 尾帧 -ss DUR 会超出视频末尾 → 用 DUR-0.3 或 -ss 69.9(70s 视频)
  • scene=0.05 会产生大量抖动点(相邻 <0.05s)→ 与基础帧集比对,近似点(<0.05s 差)不算遗漏
  • 字幕区域随平台/画幅变化(抖音竖屏底部 1/4;B 站横屏底部 10-15%)→ 先抽一帧确认字幕位置再 crop
  • 字幕区 crop 参考值:抖音竖屏 720×960 → crop=720:240:0:720;B 站横屏 1080p → crop=1920:162:0:918(底部 15%)
  • CRLF 行尾坑:时间戳列表若由 Python/Windows 生成(\n 被转成 \r\n),bash while read 的变量带 \r → ffmpeg -ss "0.0\r" 报 Invalid duration for option ss 全批失败。修复:先 tr -d '\r' < times.txt > times_unix.txt 再用,或 Python 写文件用 newline='\n'
  • 逐点 -ss 抽帧耗时:700+ 帧约 7 分钟(每帧一次 ffmpeg 启动),务必后台执行(run_in_background);帧序号用 4 位 c_%04d