视频抽帧工具箱(Video Frames Toolbox)
把视频变成图片序列:一条命令完成抽帧,产出按时间戳命名的图片文件,并打印每张图对应的视频时间点,拿到就能直接用(封面、预览、素材、找画面)。
脚本位置:scripts/extract_frames.py(依赖 ffmpeg/ffprobe,调用前无需加载任何参考文件)。
四种模式速查
| 模式 | 参数 | 行为 | 典型用途 |
|---|---|---|---|
every |
--every 5 |
每 5 秒一张,全片覆盖 | 定时截图、批量素材、间隔摄影 |
scene |
--threshold 0.3 |
只在画面突变处取帧 | 找转场/高能画面/关键帧,粗剪选点 |
at |
--at 00:01:30,00:02:00 |
指定时间点精确截图 | 按时间截图、用户指名要某时刻画面 |
count |
--count 10 |
整段均匀取 N 张(取等分区间中点) | 封面候选、视频预览九宫格 |
通用选项:--width 1280(等比缩放)、--format jpg|png(默认 jpg)、-d 目录(自动创建,默认 ./frames)。
工作流程
- 确认输入:拿到用户给的视频路径(本地文件)。文件不存在时报错并让用户确认路径;用户只有视频链接时,先用下载类技能(如 videodl-download)落盘再抽帧。
- 选模式(按用户目的判断,不确定时直接问一句):
- 要"封面/预览图/缩略图" →count(均匀取样最不挑画面)
- 要"每隔 X 秒一张/全部截图" →every
- 要"找转场、精彩瞬间、关键画面" →scene
- 用户给出明确时刻("第 1 分 30 秒那张") →at - 执行命令:输出目录建议放在 workspace 内用户可见的位置(如
./frames_<视频名>),并按需加--width(封面一般 1280 即可)。 - 汇报结果:脚本结束会打印"序号 + 文件名 + 时间点"清单。向用户复述:共几张、在哪个目录、关键帧分布在哪些时间点。
- 后续加工:用户若要拼成一张长图/九宫格 → img-stitch-toolbox;若图片太大 → img-compress-toolbox;若只要其中一张做封面 → 报告对应时间点让用户确认。
命令参考
# 每 5 秒一张
python3 <技能目录>/scripts/extract_frames.py video.mp4 -m every --every 5 -d ./shots
# 场景切换抽帧(阈值 0~1,越小抽得越多;找不到帧时先降到 0.2)
python3 <技能目录>/scripts/extract_frames.py video.mp4 -m scene --threshold 0.3 -d ./keyframes
# 指定时间点截图(支持 00:01:30 / 90 / 90.5 三种写法,逗号分隔)
python3 <技能目录>/scripts/extract_frames.py video.mp4 -m at --at 00:01:30,00:02:00,00:03:15 -d ./stills
# 均匀抽 5 张封面候选,统一 1280 宽,png 无损
python3 <技能目录>/scripts/extract_frames.py video.mp4 -m count --count 5 --width 1280 --format png -d ./covers
输出约定:
- 文件按时间戳命名:整秒如
frame_000030.jpg(第 30 秒),含小数如frame_000012.500.jpg(第 12.5 秒);时间点取自 ffmpeg 实际输出的帧时间戳,不是估算值。 - 极少数情况下时间戳解析失败会回退为序号命名并打印
[警告],此时清单中的时间点仅供参考。 - 目录中若已有同名文件,自动追加序号(
frame_000030_1.jpg)避免覆盖。
示例对话
示例 1:做封面候选
> 用户:帮我从这个视频里挑几张适合当封面的图。
> 助手:用 count 模式均匀抽 8 张:
> extract_frames.py lecture.mp4 -m count --count 8 --width 1280 -d ./frames_lecture
> 然后把清单(文件+时间点)报给用户,让用户按时间点挑选,或直接交付 8 张图。
示例 2:找视频里的转场/高能画面
> 用户:这个视频剪过好几段,帮我找到切换点在哪几秒。
> 助手:用 scene 模式:extract_frames.py vlog.mp4 -m scene --threshold 0.3 -d ./cuts
> 结果如 frame_000125.jpg → 00:02:05.000,即可告诉用户"2 分 05 秒处有一次切换";帧太少就降到 0.2 重跑,太多就升到 0.4。
示例 3:定期截图
> 用户:每隔 30 秒给我截一张,我发群 里播进度。
> 助手:extract_frames.py demo.mp4 -m every --every 30 --width 960 -d ./progress,交付序列图并说明每张对应的时刻。
运行环境
- 脚本:
scripts/extract_frames.py(相对技能根目录) - Python:3.9+
- 外部命令:
ffmpeg、ffprobe
开工前先跑一次自检,缺什么当场补,别等跑到一半报错:
command -v ffmpeg >/dev/null 2>&1 || echo "缺 ffmpeg,请先安装再重试"
command -v ffprobe >/dev/null 2>&1 || echo "缺 ffprobe,请先安装再重试"
> 缺依赖时用中文告诉用户缺什么、装完就能用,不要把 Python 的英文 traceback 原样贴给用户。
注意事项
- scene 的边界:场景检测对硬切最有效;淡入淡出、缓慢运镜可能低于阈值而漏检(调低
--threshold),而高频闪动画面会多抽(调高阈值)。第一帧永远不算场景切换。 - count 不取首尾:为避免 seek 失败,取的是每个等分区间的中点(如 15 秒抽 5 张 → 1.5/4.5/7.5/10.5/13.5 秒),不含 0 秒和结尾帧。
- at 超时长的时间点会打印警告并跳过,不会中断整个任务;
HH:MM:SS是时分秒(如00:09:00= 9 分钟),不要当"分:秒"传。 - 格式选择:jpg 体积小适合大批量;png 无损适合精修或后续加字。需要指定画质时可用
-q:v 2~5(脚本未暴露,可自行在 ffmpeg 参数层面扩展)。 - 性能:4K 长视频全量抽帧很慢且占磁盘,优先
--width缩放到 ≤1280 再抽;scene/every模式是单次解码全片,at/count按点 seek 更快。 - 依赖:ffmpeg 与 ffprobe 必须在 PATH 中(本机位于 /usr/bin);脚本无需网络、无需 API Key。
- 测试产物:开发验证时的测试视频与输出放在 /tmp(如 /tmp/vft/),不占用 workspace。