name: bozo-scene description: 本地视频分镜(场景)分析与拆分工具。基于 PySceneDetect 自动检测视频中的场景切换点,支持两类核心任务:(1) 分镜首尾帧提取——为每个分镜分别导出一张首帧 + 一张尾帧图片;(2) 分镜视频导出——把每个分镜切成一段独立的 mp4 片段,并自动减去 1 帧时长避免「上一段尾帧=下一段首帧」的常见问题。当用户提到"分镜"、"场景切换"、"镜头切换"、"视频拆段"、"按分镜切视频"、"提取分镜首尾帧"、"拆分分镜视频"、"分镜预览图"、"按画面变化把视频切开"、"按镜头分段"、需要把一段长视频按场景变化切成多段、或需要批量分析视频的分镜结构时,必须使用此技能。即使用户没有明确提到 PySceneDetect,只要意图是「按画面变化把视频拆开」或「分析视频里有几个分镜」,都应优先使用此技能而不是让用户手动写 ffmpeg 命令。
基于 PySceneDetect + ffmpeg,对本地视频文件做两件事:
分镜首尾帧提取(scripts/scene_frames.py)
分析视频分镜结构,为每个分镜单独提取一张首帧 + 一张尾帧。
常用于:分镜脚本预览、AI 绘图/视频生成的关键帧参考、镜头风格评估。
分镜视频导出(scripts/export_scenes.py)
把每个分镜切成一段独立的 mp4 片段。
常用于:二次剪辑素材准备、短视频批量分发、给每个分镜单独加特效后再合并。
终端日志会打印每个分镜的精确时间段(HH:MM:SS.mmm 毫秒精度),方便后续在剪辑软件或 ffmpeg 命令里直接复用。
| 用户表达 | 调用脚本 |
|---|---|
| "把这段视频按分镜切开" / "按场景拆段" | scripts/export_scenes.py |
| "拆分分镜视频" / "按镜头切视频" | scripts/export_scenes.py |
| "提取每个分镜的首尾帧" / "我要分镜预览图" | scripts/scene_frames.py |
| "帮我分析下这段视频有几个分镜" | 任一脚本都会先打印分镜列表 |
| "把视频切成单独的镜头片段" | scripts/export_scenes.py |
| "我要给每个分镜做关键帧" | scripts/scene_frames.py |
| "批量分析这个文件夹下所有视频的分镜" | 循环调用两个脚本之一 |
如果环境没装 scenedetect,先帮用户装:
pip install scenedetect opencv-python
python scripts/scene_frames.py <video> [options]
参数:
- <video>:视频文件路径(必填)
- --output, -o:输出目录,默认 ./scene_frames
- --threshold:分镜检测灵敏度,默认 27.0,值越小越敏感(检出更多分镜),典型范围 20~35
- --min-scene-len:最短分镜时长(秒),默认 0.6,比这短的分镜会被丢弃
- --tail-offset:尾帧提前偏移(秒),默认 0.05,避免尾帧落在下一分镜首帧
输出命名(每个分镜两张 jpg):
{视频名}_scene01_首帧.jpg
{视频名}_scene01_尾帧.jpg
{视频名}_scene02_首帧.jpg
{视频名}_scene02_尾帧.jpg
...
python scripts/export_scenes.py <video> [options]
参数:
- <video>:视频文件路径(必填)
- --output, -o:输出目录,默认 ./scene_clips
- --name, -n:片段文件名前缀,默认用视频文件名
- --threshold:分镜检测灵敏度,默认 27.0
- --min-scene-len:最短分镜时长(秒),默认 0.6
- --no-trim-frame:禁用「减 1 帧」尾帧修正(默认开启,不建议关闭)
输出命名(每个分镜一段 mp4):
{name}_01.mp4
{name}_02.mp4
...
关键实现:默认开启「减 1 帧」尾帧修正——PySceneDetect 返回的 scene.end 是「下一分镜首帧」的时间戳(半开区间),如果直接按 end-start 切,下一分镜首帧会出现在本片段末尾。脚本自动把每个片段时长减去 1/fps(30fps 下约 33ms),保证片段末帧正好停在本分镜的真实末帧。如果用户明确不需要这个修正,加 --no-trim-frame。
| 视频类型 | 推荐值 |
|---|---|
| 默认 / 商业视频 / TikTok 短视频 | 27.0 |
| 画面变化平缓(长镜头、监控、纪录片) | 30~35(避免误判) |
| 剪辑很碎(MV、广告、运动集锦) | 20~25(避免漏检) |
如果检测出来的分镜数明显偏多/偏少,先调这个。
0.6 秒,过滤抖动和闪屏1.0~2.0日志里所有时间都是 HH:MM:SS.mmm(毫秒精度),可以直接复制到 ffmpeg 命令里用:
ffmpeg -ss 00:00:01.767 -i input.mp4 -t 1.366 -c copy out.mp4
{base}_scene{NN}_首帧.jpg / {base}_scene{NN}_尾帧.jpg{name}_{NN}.mp4NN 从 01 开始,两位数补零--threshold(小→更敏感→更多分镜)--min-scene-len如果用户给的是文件夹而不是单个文件,列出文件夹内所有 .mp4 / .mov / .avi / .mkv / .flv / .wmv 然后循环调用脚本:
for v in /path/to/folder/*.mp4; do
python scripts/export_scenes.py "$v" --output ./out
done
或者在 Python 里直接调用公共模块函数(见下)。
scripts/scene_common.py 暴露了可复用函数,便于在批量任务或更复杂流程里直接 import:
import sys
sys.path.insert(0, "path/to/bozo-scene/scripts")
from scene_common import detect_scenes, extract_frame, cut_segment, fmt_time
scenes, fps = detect_scenes("video.mp4", threshold=27.0, min_scene_len_sec=0.6)
for i, (s, e) in enumerate(scenes, 1):
print(f"分镜 {i}: {fmt_time(s)} ~ {fmt_time(e)}")
extract_frame("video.mp4", s, f"out/scene{i:02d}_first.jpg")
cut_segment("video.mp4", s, e, fps, f"out/scene{i:02d}.mp4")
| 函数 | 用途 |
|---|---|
fmt_time(seconds) |
秒数格式化为 HH:MM:SS.mmm |
probe_duration(video_path) |
通过 ffprobe 获取视频总时长 |
detect_scenes(video_path, threshold, min_scene_len_sec) |
返回 (scenes, fps) |
extract_frame(video_path, timestamp_sec, output_path) |
提取指定时间点的单帧 |
cut_segment(video_path, start_sec, end_sec, fps, output_path, trim_one_frame=True) |
切分视频片段(默认减 1 帧避免尾帧泄漏) |
调 --threshold。多→调大(30~35),少→调小(20~25)。
不应该出现——脚本默认已经减去 1 帧时长。如果仍出现:
1. 检查是否手动加了 --no-trim-frame,去掉它
2. 源视频可能是 VFR(可变帧率),先用 ffmpeg 转成 CFR:
bash
ffmpeg -i input.mp4 -vsync cfr -r 30 -c:v libx264 -crf 18 output.mp4
源视频在该时间点附近是关键帧,ffmpeg 用 -ss 做快速 seek 可能从最近关键帧开始。脚本默认已经用 -c:v libx264 重编码(不是流拷贝),通常不会有这问题;若仍出现,先转 CFR 再处理。
pip install scenedetect opencv-python
分镜检测阶段是 CPU 密集(PySceneDetect 逐帧解码对比),无法绕过。切分阶段每段约等于源视频同码率重编码时间。如果只是要分镜列表不要切视频,跑 scene_frames.py 比 export_scenes.py 快很多。
scene_common.py,分镜检测逻辑只在一处实现,修改一处即可同时影响两个工具scene.end 是「下一分镜首帧」,所有切片/取帧操作都会做相应偏移这个工具质量不错,能可靠地帮你把视频按场景切开或提取分镜首尾帧。文档写得很清楚,参数调节有明确的建议,常见的坑(如片段尾帧重复)也有处理。上手容易,但需要自己安装 ffmpeg 和相关依赖,没有自动安装脚本,对新手不太友好。