Skip to content
Back to skills

Video Frame Extract

ASecurity

视频抽帧——用 ffmpeg 从视频里按时间点截图、每隔 N 秒抽一张、只抽关键帧或按场景变化抽帧,还能拼缩略图、转 GIF。当用户说「视频截图」「每隔几秒截一张」「抽关键帧」「做视频封面」「视频转GIF」「让AI看视频」时使用。

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 13, 2026
ai-agentspythonshellbash

Security analysis

A100/100

Pro scans all 5 files and shows the line behind each finding

Scanned October 13, 2026

npx -y skills add po-et/workbuddy-skills --skill video-frame-extract --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Video Frame Extract?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Video Frame Extract
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/po-et-video-frame-extract/badge)](https://www.skillsdirectory.com/skills/po-et-video-frame-extract)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: video-frame-extract
description: "视频抽帧——用 ffmpeg 从视频里按时间点截图、每隔 N 秒抽一张、只抽关键帧或按场景变化抽帧,还能拼缩略图、转 GIF。当用户说「视频截图」「每隔几秒截一张」「抽关键帧」「做视频封面」「视频转GIF」「让AI看视频」时使用。"
author: Captain
version: 0.1.2
display_name: "视频抽帧"
display_name_en: "Video Frame Extract"
description_zh: "用 ffmpeg/ffprobe 从视频里截图与抽帧:先看时长、帧率、分辨率与旋转,再按时间点、固定间隔、关键帧、场景变化抽取,拼缩略图、用调色板两步法转 GIF;讲清 -ss 位置、每 N 秒取哪一帧、竖屏旋转、文件编号这些常见坑。"
description_en: "Extract stills and frame sequences from video with ffmpeg: single timestamps, fixed intervals, keyframes, scene changes, contact sheets and palette-based GIFs, with seeking, rotation and file-numbering pitfalls covered."
tags:
  - "视频抽帧"
  - "视频截图"
  - "ffmpeg"
  - "关键帧"
  - "缩略图"
  - "视频转GIF"
  - "视频封面"
  - "frame extraction"
examples_zh:
  - "帮我给这段视频截图,要第 1 分 23 秒那一帧做封面"
  - "每隔 5 秒截一张,我想让AI看视频讲了什么"
  - "把录屏第 10 到 13 秒做成动图,视频转GIF 别太大"
examples_en:
  - "Grab the frame at 1:23 of this video as a cover image"
  - "Take one frame every 5 seconds so an AI can see what the video is about"
  - "Turn seconds 10 to 13 of this screen recording into a GIF that isn't too big"
metadata:
  openclaw:
    requires:
      bins: [ffmpeg, ffprobe]
---

# 视频抽帧

适用于从视频里拿静态画面:做封面、截素材、给 AI 看内容、做动图。全部用 ffmpeg/ffprobe 完成,命令按 macOS/Linux 终端写,`in.mp4` 换成你的文件名。先跑 `ffmpeg -version` 确认已安装;没装请用户自己从官方渠道安装。

## 何时使用

主要场景:

- 「帮我给这段视频截图,要第 1 分 23 秒那一帧做封面」
- 「每隔 5 秒截一张,我想让 AI 看视频讲了什么」
- 「把录屏第 10 到 13 秒做成动图,别太大」
- 「这个视频按镜头切成几张」「给长视频做一张缩略图拼图」

辅助场景(已经在抽,遇到问题):

- 「截出来的图是躺着的/被压扁了」「抽出来的张数和时间对不上」「ffmpeg 报 Cannot write more than one file」

调用方式:在对话里给出视频路径和用途;在本机终端直接运行下文的 ffmpeg 命令,或用附带脚本 `python3 scripts/frames.py <模式> 视频文件`——同一组命令,外加自动建目录、拒绝悄悄覆盖旧图、核对张数、生成「文件名—时间点」对照表。

不适用(遇到时这样处理):

- 剪辑、配字幕、整片转码 → 不在本技能范围;只处理声音(提取音轨、剪录音)转「音频剪辑」类技能(如 audio-editing)。
- 绕过 DRM、翻录付费课程或影视内容再传播 → 不做,并说明原因。
- 人脸识别、辨认或跟踪画面里的人 → 不做。

## 先判断什么

| 用户要的 | 抽法 | 关键参数 |
|---|---|---|
| 某一刻的画面、封面 | 时间点截图 | `-ss` 放在 `-i` 前 |
| 不知道哪帧好看的封面 | 让 ffmpeg 挑代表帧 | `thumbnail` |
| 均匀素材、给 AI 看 | 每 N 秒一张 | `fps=1/N:round=up` |
| 快速过一遍长视频 | 只抽关键帧 | `-skip_frame nokey` |
| 按镜头切分、录屏翻页 | 场景变化抽帧 | `gt(scene,0.3)` |
| 一张图看全片 | 缩略图拼图 | `tile` |
| 动图 | 调色板两步法 | `palettegen` + `paletteuse` |

给 AI 看视频:总数控制在 8–16 张,宽度缩到 768 左右,每张都附上时间点;镜头切换多的内容改用场景抽帧;想少传几张图,就交一张 4×3 拼图。

## 第 1 步:先看视频参数

```bash
ffprobe -v error -select_streams v:0 -show_entries stream=width,height,r_frame_rate,nb_frames:stream_side_data=rotation:format=duration -of default=nw=1 in.mp4
```

看四样:`duration`(总秒数,决定间隔)、`r_frame_rate`(帧率)、`width/height`(存储宽高)、`rotation`(出现 90 或 -90 说明是竖拍,实际画面宽高要对调)。脚本版 `python3 scripts/frames.py probe in.mp4` 会直接算出实际画面宽高,并提示竖拍和 10-bit/HDR。

## 第 2 步:按用途抽(均在 ffmpeg 9.0 实测)

```bash
# 单张:第 1 分 23.5 秒;-q:v 2 是 JPG 高质量(2 最好、31 最差),要无损就把输出改成 .png
ffmpeg -ss 00:01:23.5 -i in.mp4 -frames:v 1 -q:v 2 cover.jpg
# 封面候选:第 60–80 秒每秒取 2 帧共 40 帧,挑最有代表性的一张,通常能避开黑场和转场
# (thumbnail 会把整批帧放进内存:实测 720p 直接挑 500 帧,ffmpeg 占 812 MB;先降帧只占 144 MB)
ffmpeg -ss 60 -t 20 -i in.mp4 -vf "fps=2,thumbnail=40" -frames:v 1 cover_pick.jpg
# 每 5 秒一张并缩到 768 宽,编号从 0 起:第 k 张 = 第 k×5 秒
mkdir -p frames && ffmpeg -i in.mp4 -vf "fps=1/5:round=up,scale=768:-2" -start_number 0 -q:v 2 frames/f_%04d.jpg
# 只要关键帧:先列时间点,再抽图(解码器直接跳过非关键帧,长视频最快)
ffprobe -v error -select_streams v:0 -skip_frame nokey -show_entries frame=pts_time -of default=nw=1:nk=1 in.mp4
mkdir -p key && ffmpeg -skip_frame nokey -i in.mp4 -fps_mode vfr -q:v 2 key/k_%04d.jpg
# 场景变化:连第一帧一起抽,并打印每张的秒数;抽太多就把 0.3 调到 0.4–0.5
mkdir -p scene && ffmpeg -i in.mp4 -vf "select='eq(n,0)+gt(scene,0.3)',showinfo" -fps_mode vfr -q:v 2 scene/s_%03d.jpg 2>&1 | grep -o 'pts_time:[0-9.]*'
# 4×3 缩略图拼图:按总时长均分 12 张
D=$(ffprobe -v error -show_entries format=duration -of csv=p=0 in.mp4)
ffmpeg -i in.mp4 -vf "fps=12/$D:round=up,scale=320:-2,tile=4x3:padding=4:margin=4" -frames:v 1 -q:v 3 sheet.jpg
```

转 GIF 分两步:先为这一段画面生成专属 256 色调色板,再按它上色。一步直转用的是通用色表,真实画面容易出现色带和噪点。

```bash
ffmpeg -ss 10 -t 3 -i in.mp4 -vf "fps=12,scale=480:-1:flags=lanczos,palettegen=stats_mode=diff" palette.png
ffmpeg -ss 10 -t 3 -i in.mp4 -i palette.png -lavfi "fps=12,scale=480:-1:flags=lanczos[x];[x][1:v]paletteuse=dither=bayer:bayer_scale=5" out.gif
```

GIF 太大按这个顺序减:缩短时长 → 降到 8–10 帧/秒 → 宽度降到 320 → 改 `dither=none`。

## 一条命令版:scripts/frames.py

`scripts/frames.py` 把上面的命令按模式包好:只用 Python 标准库加 ffmpeg/ffprobe,直接调用程序、不经过 shell(不依赖 `mkdir -p`、`$(...)`、`grep`);每次都打印实际执行的完整命令。

| 模式 | 等价于 | 示例 |
|---|---|---|
| `probe` | 第 1 步的 ffprobe | `python3 scripts/frames.py probe in.mp4` |
| `shot` | 单张截图 | `python3 scripts/frames.py shot in.mp4 --at 1:23.5` |
| `pick` | thumbnail 挑封面 | `python3 scripts/frames.py pick in.mp4 --start 60 --duration 20` |
| `interval` | 每 N 秒一张,或 `--count` 按总时长均分 | `python3 scripts/frames.py interval in.mp4 --count 12` |
| `keyframes` | 只抽关键帧 | `python3 scripts/frames.py keyframes in.mp4` |
| `scene` | 场景变化抽帧 | `python3 scripts/frames.py scene in.mp4 --threshold 0.3` |
| `sheet` | 缩略图拼图 | `python3 scripts/frames.py sheet in.mp4 --cols 4 --rows 3` |
| `gif` | 调色板两步法 | `python3 scripts/frames.py gif in.mp4 --start 10 --duration 3` |

比直接敲命令多做的事:时间点超出视频长度先报错;数值必须有限,封面候选帧率为每秒大于 0 且不超过 50 帧;固定间隔预计超过 1000 张时要求增大间隔;输出目录自动建;目录里已有同前缀的旧图、`index.csv` 或同名输出时先停下(`--overwrite` 才覆盖);抽完核对张数和分辨率,把「文件名、秒、时间码」写进输出目录的 `index.csv`;场景抽帧只出 1 张或出得太多时给出调阈值的建议;`--dry-run` 只打印命令;单条命令默认 1800 秒超时(`--timeout` 可改)。

拼图按从左到右、从上到下排列,第 k 格对应约 `k×总时长/(列数×行数)` 秒,从第 0 秒起。先用相同张数的 `interval --count` 生成 `index.csv`,可将时间点与拼图一起交给 AI;拼图本身不带文字标签。画面采样最多早一帧,不能据此推断没有拍到的发言或画面。

## 信息不全或出错时

| 情况 | 怎么处理 | 对用户说的话(模板) |
|---|---|---|
| 缺关键信息:没给视频路径,或只说「截几张」 | 先要路径和用途(封面/给 AI 看/素材/动图);间隔、张数、宽度按「先判断什么」的默认值,标 [待确认] | 「把视频文件路径发我,再说一下用途:做封面、给 AI 看,还是做动图?张数我先按给 AI 看的默认 12 张、宽 768 来。」 |
| 输入不对或自相矛盾:时间点超过视频长度;说「每秒一张」又要「总共 10 张」 | 先 probe 看时长,指出冲突,给两种做法 | 「这段只有 32 秒,第 40 秒不存在;你是要最后一帧,还是时间记错了?」「每秒一张会有 32 张,和 10 张冲突:按间隔还是按总数?」 |
| 超出范围:剪辑配字幕、整片转码、DRM 或付费内容翻录、人脸识别 | 说明不做;剪辑类转对应工具;只要声音的转音频剪辑类技能 | 「这段是付费课程的加密视频,我不能帮忙绕过保护;如果是你自己录的屏,可以接着抽。」 |
| 时间紧,只要最小可用版 | 最小版 = 一条命令 + 输出位置 + 张数;对照表和拼图随后补 | 「先给最小版:`python3 scripts/frames.py interval in.mp4 --count 12`,图在 frames/,12 张,时间点在 index.csv 里。」 |
| 坚持越界:要破解 DRM、截取他人隐私画面公开发布、识别画面里的人 | 守住:不做;有证件、聊天记录等隐私画面提醒先打码 | 「这一步我不做。画面里有别人的证件号,发布前请先打码。」 |
| 张数与预期差得多:场景抽帧只出 1 张、关键帧只有几张 | 先说明原因再调参重跑:阈值高或画面变化小就调低阈值;关键帧稀疏就改用每 N 秒一张 | 「只抽到第一帧,说明画面变化没超过 0.3;我把阈值调到 0.2 再跑一次。」 |
| 竖屏、HDR、10-bit | 竖屏缩放只定一边;HDR/10-bit 截图可能发灰时如实说明,不给未经验证的参数 | 「这是 10-bit 视频,截图可能发灰;要校色需要色调映射,我不套用没验证过的参数。」 |

`scripts/frames.py` 退出码与 ffmpeg 常见报错:

| 退出码 / 报错 | 原因 | 修正办法 |
|---|---|---|
| 0 | 成功 | 按输出清单和 index.csv 交付 |
| 1「参数错误」「视频只有 X 秒,--at … 超出范围」 | 时间写法不对或超出时长;阈值不在 0–1 | 先 `probe` 看时长;时间写 83.5、1:23.5 或 00:01:23.5 |
| 2「找不到视频文件」「读不出视频轨」「…已存在」 | 路径不对;不是视频、只有音频或已损坏;输出已存在 | 路径加引号;换文件;换 `--out`/`--out-dir` 或加 `--overwrite` |
| 3「没找到 ffmpeg、ffprobe」 | 没装或不在 PATH | 用户从官方渠道安装,`ffmpeg -version` 能打印版本即可 |
| 4「ffmpeg 执行失败」「超过 N 秒还没跑完」 | 参数被拒、文件损坏、长视频超时 | 看打印的最后几行报错;长视频加大 `--timeout` 或先用 `keyframes` |
| 5「没有产出图片」 | 时间范围里没有帧,或输出被同名文件挡住 | 核对时间范围;换输出位置 |
| 130「已中断」 | 按了 Ctrl+C | 输出目录可能留有部分图片,换目录或加 `--overwrite` 重跑 |
| `Cannot write more than one file with the same name` | 抽多张却写成 out.jpg | 输出写成 `%04d` 编号模板 |
| 写输出时 `No such file or directory` | 输出目录不存在 | 先 `mkdir -p`(脚本会自动建) |
| `Not overwriting - exiting` 或 `File '…' already exists. Exiting.` | 同名文件已存在,非交互环境或加了 `-n`;此时退出码仍是 0(ffmpeg 9.0.1 实测) | 换文件名,或确认后加 `-y`;别只看退出码,要核对输出文件 |
| `Nothing was written into output file` | `-ss` 超过了视频长度 | 先看 duration,时间点要小于它 |

## 输出契约

交付物按以下顺序:

1. **实际执行的完整命令**(脚本会逐条打印「执行:…」)。
2. **输出清单**:绝对路径、张数、分辨率——用 `ls` 和 ffprobe 或脚本的输出核对,不凭估算。
3. **每张的时间点**:固定间隔按 k×N 秒算(`round=up` 取的是该时刻或之前最近的一帧,最多早一帧),场景抽帧和关键帧取 showinfo 打印的 pts_time;脚本写在 `index.csv`。
4. **给 AI 用时附「文件名—时间点」对照表**(Markdown 表或 index.csv)。
5. **异常说明**:张数与预期差得多(例如场景抽帧只出 1 张),先说明原因和调整办法再重跑。

占位符:只有用户知道的(视频路径、要的时间点)写 [待补];按默认值定的参数(间隔、张数、宽度、阈值)写 [待确认:默认值]。

交付前自检(逐条答是/否):

- [ ] 截图命令的 `-ss` 在 `-i` 前?每 N 秒的用了 `round=up`?
- [ ] 张数、分辨率是实际核对过的,不是估算?
- [ ] 竖屏视频的缩放只定了一边,没有写死宽高、没加 `-noautorotate`?
- [ ] 输出目录是新的或确认过可以覆盖,没有混进上次的旧图?
- [ ] 给 AI 用时附了对照表,总数 8–16 张、宽 768 左右?
- [ ] 视频是用户有权使用的;画面里有证件、聊天记录等隐私时提醒了打码?

## 示例

以下为 0.1.1 版的历史实测示例:视频是用 ffmpeg 自带测试图案生成的 32 秒样片,输出是实际运行脚本的结果,绝对路径截短为 `…/`。0.1.2 保留更多参数有效位数;当前实测见本地会议复盘素材演示。

**用户**:每隔 5 秒截一张,我想让 AI 看视频讲了什么。(附:demo.mp4)

**助手**:先看参数——32 秒、640x360、不旋转。每 5 秒一张只有 7 张,给 AI 看建议 8–16 张,所以改成按总时长均分 12 张 [待确认:如果一定要每 5 秒,就用 `--every 5`]。

```bash
python3 scripts/frames.py interval demo.mp4 --count 12 --out-dir ai_frames
```

**助手交付**:

```text
执行:ffmpeg -hide_banner -nostats -n -i demo.mp4 -vf fps=1/2.6667:round=up,scale=768:-2,showinfo -start_number 0 -frames:v 12 -q:v 2 …/ai_frames/f_%04d.jpg
输出:…/ai_frames,共 12 张,768x432;对照表 …/ai_frames/index.csv
| 文件 | 时间点(秒) | 时间码 |
|---|---|---|
| f_0000.jpg | 0.000 | 00:00:00.000 |
| f_0001.jpg | 2.667 | 00:00:02.667 |
| f_0002.jpg | 5.333 | 00:00:05.333 |
| …(共 12 行,见 index.csv) | | |
| f_0011.jpg | 29.334 | 00:00:29.334 |
说明:时间点是每隔约 2.67 秒的时刻,实际画面是该时刻或之前最近的一帧(最多早 0.04 秒)
```

更多完整示例:[给 AI 看视频:均分、拼图与场景抽帧](examples/ai-video-frames.md)、[封面、竖屏截图与 GIF](examples/cover-and-gif.md)、[本地会议复盘素材演示(合成测试)](examples/meeting-review-local-demo.md)。

## 常见问题(FAQ)

**Q:`-ss` 放在 `-i` 前面会不会截不准?**
A:截图不会。放在前面是先跳到附近关键帧、再解码到准确位置,又快又准;老教程说「放前面不准」,说的是 `-c copy` 剪片。

**Q:每 5 秒一张,为什么第一张不是第 0 秒?**
A:`fps=1/5` 默认取每段中间那帧(实测第 1、2 张在 2.48、7.48 秒);加 `:round=up` 才正好是 0、5、10 秒。

**Q:给 AI 看视频抽多少张合适?**
A:总数 8–16 张,宽度 768 左右,每张附时间点;镜头切换多的用场景抽帧;想少传几张就交一张 4×3 拼图。

**Q:竖屏视频截出来是躺着的,或者被压扁了?**
A:别加 `-noautorotate`,让 ffmpeg 自动转正;缩放只定一边,如 `scale=-2:720`。

**Q:GIF 太大怎么办?**
A:按顺序减:缩短时长 → 8–10 帧/秒 → 宽度 320 → `dither=none`。

**Q:HDR 视频截图发灰怎么办?**
A:HDR、10-bit 视频截图可能发灰;需要色调映射时如实说明,不套用未经验证的参数。

**Q:能帮我截网站上的视频或付费课程吗?**
A:只处理你有权使用的视频文件;不绕过 DRM,不翻录付费课程或影视内容再传播。

**Q:没有 Python 能用吗?**
A:能,直接用第 2 步的 ffmpeg 命令;脚本只是多做了建目录、防覆盖、核对张数和对照表。

## 常见错误(反模式)

| 错误做法 | 为什么错 | 正确做法 |
|---|---|---|
| 截图时把 `-ss` 放在 `-i` 后面,或以为放前面不准 | 放后面要从头解码,越靠后越慢:实测 10 分钟视频取第 512.36 秒,放前 0.04 秒、放后 0.42 秒,两张画面逐像素一致;「放前面不准」说的是 `-c copy` 剪片(只能从关键帧起拷数据) | 截图一律 `-ss` 放在 `-i` 前 |
| 每 N 秒用 `fps=1/N`,不加 `round=up` | 默认取每段中间那帧,实测第 1、2 张在 2.48、7.48 秒,文件名和时间对不上 | `fps=1/N:round=up` 加 `-start_number 0`,第 k 张 = 第 k×N 秒 |
| 竖屏写死 `scale=1280:720`,或加 `-noautorotate` | 手机竖拍常以横向存储、靠 rotation 元数据竖过来;ffmpeg 默认自动转正,但 ffprobe 报的 width/height 仍是横向数值——写死宽高会压扁,关掉自动旋转画面会躺着 | 只定一边:`scale=-2:720` |
| 抽多张写成 `out.jpg`、编号不补零、不先建目录 | 报 `Cannot write more than one file with the same name`;编号默认从 1 开始、不补零就排序错乱;目录不存在报 `No such file or directory` | 用 `%04d` 模板,需要时 `-start_number 0`,先 `mkdir -p` |
| 重跑时不想清楚覆盖策略,只看退出码 | 同名文件已存在时,非交互环境下 ffmpeg 直接放弃(Not overwriting - exiting),退出码却是 0(ffmpeg 9.0.1 实测) | 想覆盖加 `-y`,想跳过加 `-n`;跑完用 `ls` 核对产出 |
| 以为 `-n` 能保护上次抽的编号图片 | `-n` 只检查模板名本身,`f_0000.jpg` 这类编号文件照样被悄悄重写(ffmpeg 9.0.1 实测) | 每次抽到新目录,或确认后再覆盖;脚本发现旧图会先停下 |
| 给 AI 一次塞几百张原尺寸截图 | 张数太多、图太大,重点反而淹没 | 8–16 张、宽 768 左右、附时间点;镜头多用场景抽帧 |
| 场景抽帧只出 1 张就直接交付 | 说明阈值太高或画面变化小,结果没法用 | 先说明原因,调低阈值再跑;抽太多就调到 0.4–0.5 |

## 边界与不做什么

- 只处理用户有权使用的视频;不帮忙绕过 DRM、翻录付费课程或影视内容再传播;截图要公开发布时,提醒注意版权与肖像权。
- 不做人脸识别、身份辨认或跟踪他人;画面里有证件、聊天记录等隐私信息,提醒先打码。
- 只负责抽帧与做动图;剪辑、配字幕、整片转码不在本技能范围。
- 不代装软件;HDR、10-bit 视频截图可能发灰,需要色调映射时如实说明,不给未经验证的参数。

Files in this skill

  • SKILL.md19.1 KB
  • examples/ai-video-frames.md4 KB
  • examples/cover-and-gif.md3.8 KB
  • examples/meeting-review-local-demo.md4.4 KB
  • scripts/frames.py20.5 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.