Files
contentm_agent/.workbuddy/skills/aigc-idea-impression/references/操作规范/视频抽帧与字幕规范.md
T

123 lines
7.4 KiB
Markdown
Raw Normal View 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`。横屏或字幕位置不同时按实际调整。
## 二、抽帧命令模板
```bash
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`