capture-video-frames について
capture-video-framesは、YouTube動画から一定間隔でPNGフレームを取得し、エージェントによる説明、重複除去、選別の手順を示します。動画内容の視覚的な記録作成に使えます。
主な機能
- 01指定間隔でのフレーム取得
- 02タイムスタンプ付きマニフェスト作成
- 03サブエージェントによるフレーム説明
- 04視覚的に類似したフレームの重複除去
- 05話者の顔を基準にした選別
活用シーン
- 01動画の視覚的な概要作成
- 02記録用の代表フレーム選び
- 03取得フレーム内の話者の様子確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ pamelafox/ presentation-skills --skill capture-video-framesインストール後、Claude Code で/capture-video-framesを入力して読み込まれたか確認します。
同梱ファイルcapture_video_frames.py
詳細
動画フレームをキャプチャして説明する
ステップ 1:フレームをキャプチャする
capture_video_frames.py スクリプトを実行します。
uv run .agents/skills/capture-video-frames/capture_video_frames.py <youtube_url> <output_dir> [--interval SECONDS]
引数
youtube_url(必須):YouTube 動画の URL(extract-transcript スキルで受け付ける形式と同じです)。output_dir(必須):フレームとマニフェストファイルの保存先ディレクトリです。存在しない場合は作成されます。--interval(任意):キャプチャするフレーム間の秒数です。デフォルトは 30 です。
出力
- frame_0000.png、frame_0030.png、… — 秒単位のタイムスタンプをファイル名にした PNG 画像です(4 桁のゼロ埋め)。
- frames_manifest.md — 各フレームのタイムスタンプと説明用のプレースホルダーを記載した Markdown ファイルです。
frames_manifest.md の例:
| File | Timestamp | Description |
|------|-----------|-------------|
| frame_0000.png | [00:00] | |
| frame_0030.png | [00:30] | |
| frame_0060.png | [01:00] | |
前提条件
- yt-dlp:
brew install yt-dlpまたはpip install yt-dlp - ffmpeg:
brew install ffmpegまたはapt-get install ffmpeg
ステップ 2:describe-frame サブエージェントを使ってフレームを説明する
フレームをキャプチャした後、describe-frame カスタムエージェントをサブエージェントとして実行し、各フレームを説明します。サブエージェントの呼び出しごとにコンテキストが分離されるため、フレーム画像が蓄積してコンテキストウィンドウを使い果たすことはありません。
describe-frame エージェントは .github/agents/describe-frame.md で定義されています。
手順
- 出力ディレクトリの frames_manifest.md を読み、フレームの一覧を確認します。
- 各フレームについて、次の内容を含むプロンプトを指定して、
describe-frameエージェントをサブエージェントとして実行します。- 表示する現在のフレーム画像の絶対パス。
- 表示する前のフレーム画像の絶対パス(存在する場合)。
- 前のフレームの説明(テキスト。存在する場合)。
- サブエージェントはプレーンテキストの説明を返します(フレームが前のフレームと実質的に同一の場合は
(same as previous)を返します)。 - サブエージェントから応答が返るたびに、frames_manifest.md の該当行にある Description 列をすぐに更新します。
- すべてのフレームの説明が終わるまで続けます。
サブエージェントのプロンプトテンプレート
describe-frame サブエージェントを呼び出す際は、次の内容をプロンプトとして使用します(角括弧内の値を入力してください)。
Describe the current frame image at: [CURRENT_FRAME_ABSOLUTE_PATH]
[If previous frame exists, include these two lines:]
The previous frame image is at: [PREVIOUS_FRAME_ABSOLUTE_PATH]
The previous frame was described as: "[PREVIOUS_DESCRIPTION]"
出力例
すべてのフレームの説明が終わると、frames_manifest.md は次のようになります。
| File | Timestamp | Description |
|------|-----------|-------------|
| frame_0000.png | [00:00] | Title slide introducing "Building RAG apps with Python" |
| frame_0030.png | [00:30] | Speaker showing the agenda with four main topics |
| frame_0060.png | [01:00] | (same as previous) |
| frame_0090.png | [01:30] | Architecture diagram of a retrieval-augmented generation pipeline |
ステップ 3:フレームを重複排除し、最適な話者の顔を選択する
すべてのフレームの説明が終わると、連続する (same as previous) の行は、異なるタイミングでキャプチャされた同じ映像内容を表します。同じグループ内でも、話者の顔の状態は異なる場合があります。目が開いているか閉じているか、口が開いているか閉じているか、カメラを向いているか、横を向いているかなどです。
目標
重複したフレームの各グループについて、最適な話者の顔の状態を持つ 1 つのフレームだけを残し、残りを削除します。
最適な顔を選ぶ基準(優先順位順)
- 現在話している人の口が開いていること(話している最中)。そのタイムスタンプで誰が話しているかが(文字起こしから)分かる場合は、その話者を優先します。
- 目が開いていること — まばたきの途中のフレームは避けます。
- カメラを向いていること — 横を向いたり、うつむいたりしていないこと。
- 話者が映っていない場合(全画面のデモやウェブカメラ映像のないスライドなど)、グループ内のフレームはすべて同等です。最初のフレームを残します。
手順
- 説明が
(same as previous)である行が連続するグループをすべて特定します。各グループは、実際の説明がある「アンカー」フレームと、それに続く 1 つ以上の(same as previous)の行で構成されます。 - 各グループについて、
describe-frameサブエージェントを実行し、アンカーフレームと各重複フレームの顔を比較します。次のプロンプトテンプレートを使用します。
Compare these two frames focusing ONLY on the speaker faces visible in webcam feeds. Which frame has better speaker faces — eyes open, facing camera, mouth open (mid-speech), not mid-blink or turned away?
Frame A: [ANCHOR_FRAME_ABSOLUTE_PATH]
Frame B: [DUPLICATE_FRAME_ABSOLUTE_PATH]
Reply with ONLY one of:
- "A BETTER" if the anchor frame has better speaker faces
- "B BETTER" if the duplicate frame has better speaker faces
- "EQUAL" if both are equivalent
- "NO SPEAKERS" if no speaker faces are visible in either frame
Then add a brief reason.
- グループ内のすべての重複フレームをアンカーおよび現時点で最適なフレームと比較した後、最適なフレームを 1 つ決定します。
- 最適なフレームがアンカーではない場合:
- アンカーの説明を最適なフレームの行に移動します。
- 説明に顔の品質に関する注記を追加します。例:
(better speaker faces than frame_XXXX: eyes open, facing camera)
- マニフェストから、その他すべての
(same as previous)の行を削除します。 - アンカーがすでに最適なフレームである場合は、重複行を削除するだけです。
口が閉じている場合のフレームの再取得
重複排除後、グループ内で最適なフレームを選んでも話者の口が閉じている場合(両方の話者の口が閉じている場合)は、近くのタイムスタンプで再取得してみてください。
- 動画がまだない場合はダウンロードします。
yt-dlp -f "bestvideo[height<=720]" --no-playlist -o "<output_dir>/video.%(ext)s" "<youtube_url>" - フレームのタイムスタンプから+2秒、+5秒、+8秒、+10秒の時点で代替フレームをキャプチャします。
ffmpeg -ss <SECONDS> -i <output_dir>/video.mp4 -frames:v 1 -q:v 2 <output_dir>/alt_<FRAME>_<SECONDS>.png -y describe-frameサブエージェントを使い、いずれかの代替フレームで話者の口が開いているか、またスライド/デモの内容が同じままであるかを確認します。- より適切な代替フレームが見つかった場合は、フレームファイルを置き換えます(
cp alt_XXXX.png frame_XXXX.png)。 - 後片付けをします:
rm -f <output_dir>/alt_*.png <output_dir>/video.mp4