概要・目的
5秒ごとに1スライドがある動画ファイルから画像を切り出したかった。
生成AIでざっくりスクリプトを書いてもらって、一部手直しをしたら使えたので記事にまとめておきます。用途は限定的ですが動作確認済みです。
写真をスライドショー的な動画ファイルにして、これで展開とかも使えそう。
動作確認
- macOS 14.4
- ffmpeg
ffmpeg version 7.1 Copyright (c) 2000-2024 the FFmpeg developers
built with Apple clang version 16.0.0 (clang-1600.0.26.4)
- bash
GNU bash, version 3.2.57(1)-release (arm64-apple-darwin23)
Copyright (C) 2007 Free Software Foundation, Inc.
スクリプト
#!/bin/bash -xe
# 動画ファイル名
input_video="input.mp4"
# 出力画像のディレクトリ
output_dir="output_images"
# 切り出す間隔 (秒)
interval=5
# 出力画像の形式
format="jpg"
# 出力画像のファイル名フォーマット
filename_format="image_%04d.%s"
# 出力ディレクトリを作成 (存在しない場合)
mkdir -p "$output_dir"
# 動画の長さを取得
duration=$(ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "$input_video")
# 切り出し回数を計算
count=$(printf "%.0f" $(echo "$duration / $interval" | bc -l))
# 切り出し処理
for ((i=0; i<$count; i++)); do
filename=$(printf "$filename_format" "$i" "$format")
ffmpeg -ss "$((i * interval))" -i "$input_video" -frames:v 1 -vf scale=-1:720 "$output_dir/${filename}"
done
解説など
以下の内容も確認済みです。
スクリプトの解説
-
変数定義:
-
input_video
: 入力動画ファイル名 -
output_dir
: 出力画像のディレクトリ -
interval
: 切り出す間隔 (秒) -
format
: 出力画像の形式 (jpg, pngなど) -
filename_format
: 出力画像のファイル名フォーマット (image_%04d.jpgのように連番で保存)
-
-
出力ディレクトリ作成:
mkdir -p
コマンドで出力ディレクトリを作成します。 -
動画の長さ取得:
ffprobe
コマンドで動画の長さを取得し、duration
変数に格納します。interval
で割って切り出し回数を計算します。 -
切り出し処理:
-
for
ループで、interval
秒ごとに動画から1フレームを切り出します。 - ファイル名を
printf
で連番で生成します。 - ffmpeg
-
-ss
: 動画の開始位置を指定します。 -
-frames:v 1
: 1フレームだけ出力します。 -
-vf scale=-1:720
: 出力画像の高さを720pxにリサイズします。幅は自動調整されます。
-
-
スクリプトの実行方法
- 上記のスクリプトをテキストエディタで作成し、
.sh
拡張子で保存します (例:extract_images.sh
)。 - ターミナルでスクリプトのあるディレクトリに移動します。
- 実行権限を与えます:
chmod +x extract_images.sh
- スクリプトを実行します:
./extract_images.sh
注意点
-
動画の長さ: 動画の長さが非常に長い場合は、処理時間がかかる可能性があります。
- 約18分の動画(1920x1080)で200枚の切り出しでしたが1分程度だった。
-
出力画像のサイズ:
-vf scale
オプションで出力画像のサイズを調整できます。 -
出力形式:
format
変数を変更することで、出力画像の形式を変更できます。
参考:
おわりに
ffmpeg も bash での制御文も、一から書くととても時間がかかる。
生成AIに依頼することでそれっぽいものができた。
エラーをつぶすだけだったのでかなり時間短縮ができた。
手で修正した箇所
for 部分
## 修正前
# 切り出し処理
for ((i=0; i<=`echo "$duration / $interval" | bc -l`; i++)); do
## 修正後
# 切り出し回数を計算
count=$(printf "%.0f" $(echo "$duration / $interval" | bc -l))
# 切り出し処理
for ((i=0; i<$count; i++)); do
修正前だと計算結果に小数点が出て for のシンタックスエラーになった。
事前に小数点を切り捨てるようにした。
ファイル名
## 修正前
for ...
ffmpeg -ss "$((i * interval))" -i "$input_video" -frames:v 1 "$output_dir/${filename_format}" -vf scale=-1:720
done
## 修正後
for ...
filename=$(printf "$filename_format" "$i" "$format")
ffmpeg -ss "$((i * interval))" -i "$input_video" -frames:v 1 -vf scale=-1:720 "$output_dir/${filename}"
done
ffmpeg の結果ファイル名を連番ファイル名にするべきところが $filename_format
だった。
ついでに ffmpeg のオプションの scale の位置がまちがっていた。