ffmpegだけで、動画の各フレームから画像合成を行いたかった
背景差分動画を作る時の背景画像が欲しいけど、ffmpeg 以外入れたくないとか言う人向け。
動画から連番画像を生成 → 生成した連番画像を1枚ずつ結合していく という力技をシェルスクリプトで行います。
Windowsの人は、WSLを入れましょう。
違うブレンド方法にしたいという方等は、頑張ってスクリプトをいじってください・・・。
合成のコマンド ffmpeg -i out_mixing.png -i $in_file -filter_complex "[0][1]blend=all_mode=average" -y temp.png
は、「ffmpegを実行、 ストリーム[0]にout_mixing.pngを入力、ストリーム[1]に指定番号の連番画像を入力、ストリーム[0]と[1]を合成(blend)、対象色チャンネルは全て、合成方法は平均、temp.pngを出力(強制上書き)(入力がどちらも1コマなので、出力も1コマ)」です。
次のmvは、今の出力をリネームして次のループの入力にするため。
フレーム数は6桁まで対応(out000001.png と %06d ともに、1桁増やせば7桁にも対応可のはず。)
引数1:動画ファイル名
引数2:動画から連番画像を切り出す間隔をFPSで指定
引数3:結合する連番画像の枚数
出力ファイル:「./out_mixing.png」 固定
(処理途中で「./images_mixing_temp」というディレクトリを作成し、処理の最後で削除するので、同名のフォルダが無い所で実行してください。「./temp.png」 という画像も一時的に作られます。)
コマンド例
./image_mixing.sh input.mp4 1 60
(1分の動画であれば、1秒に1枚 合計60枚出力され、その60枚を1つの画像に結合します。)
#!/bin/bash
# 引数1 動画ファイル名
# 引数2 動画から連番画像を作る時のFPS 2なら動画1秒あたり2枚出力
# 引数2 結合するファイル数 100ならout000001.png ~ out000100.png までの100枚を合成
# 引数が2個で無ければエラー表示して終了
if [ $# -ne 3 ]; then
echo "引数の数が間違っています。"
echo "引数1: 動画ファイル名"
echo "引数2: 動画から連番画像を作る時のFPS"
echo "引数3: 結合する画像ファイル数"
exit 1
fi
# 引数を変数に格納
out_fps=$2
file_number=$3
# 一時フォルダを作成
mkdir -p images_mixing_temp
# 動画から連番画像を作成
ffmpeg -i $1 -vf "fps=$out_fps" images_mixing_temp/out%06d.png
# はじめの画像をコピーして out_mixing.png として使用
cp images_mixing_temp/out000001.png out_mixing.png
# 2番目以降の画像を全て合成していく
for i in `seq 2 $file_number`
do
echo "画像を合成中... $i / $file_number"
in_file="images_mixing_temp/out$(printf "%06d" $i).png"
ffmpeg -i out_mixing.png -i $in_file -filter_complex "[0][1]blend=all_mode=average" -y temp.png
mv temp.png out_mixing.png
done
# 一時フォルダを削除
rm -r images_mixing_temp
echo "すべての画像を合成しました。"
背景画像作成 (追記 2023/09/16)
上記の手順だと、必然的に、新しく合成する画像の比重が高くなり、積層されていった画像の比重が軽くなってしまうので、背景画像得るならこんな感じかなぁ。
opacity="1.0/$file_number"
で合成する濃さを求め、ffmpegの [0][1]blend=all_mode=average:all_opacity=$opacity
この部分で、合成比率を指定して合成するようになってます。
例えば、100枚なら、opacity=0.01 (元画像が0.99で、加える画像が0.01)で100回合成される。
(これも、完璧なブレンドになってない気もしますが。)
ちなみに、動画の先頭1分だけ背景画像欲しいとかなら、動画から連番静止画を吐くところ、
ffmpeg -i $1 -vf "fps=$out_fps" images_mixing_temp/out%06d.png
この-vfの前あたりにでも -t 60 とかを追加すればOKです。
#!/bin/bash
#!/bin/bash
# 引数1 動画ファイル名
# 引数2 動画から連番画像を作る時のFPS 2なら動画1秒あたり2枚出力
# 引数2 結合するファイル数 100ならout000001.png ~ out000100.png までの100枚を合成
# 引数が2個で無ければエラー表示して終了
if [ $# -ne 3 ]; then
echo "引数の数が間違っています。"
echo "引数1: 動画ファイル名"
echo "引数2: 動画から連番画像を作る時のFPS"
echo "引数3: 結合する画像ファイル数"
exit 1
fi
# 引数を変数に格納
out_fps=$2
file_number=$3
# 一時フォルダを作成
mkdir -p images_mixing_temp
# 動画から連番画像を作成
ffmpeg -i $1 -vf "fps=$out_fps" images_mixing_temp/out%06d.png
# はじめの画像をコピーして out_mixing.png として使用
cp images_mixing_temp/out000001.png out_mixing.png
# 2番目以降の画像を全て合成していく
for i in `seq 2 $file_number`
do
echo "画像を合成中... $i / $file_number"
in_file="images_mixing_temp/out$(printf "%06d" $i).png"
opacity="1.0/$file_number"
ffmpeg -i out_mixing.png -i $in_file -filter_complex "[0][1]blend=all_mode=average:all_opacity=$opacity" -y temp.png
mv temp.png out_mixing.png
done
2つの画像を結合し、1つの画像を出力 (追記 2023/09/16 その2)
最初からこうすりゃ良かった。
力技だけど、シンプルに2枚の画像を1枚に結合するスクリプト。
(中間ファイル「./temp.png」が作成されて削除されます。)
#!/bin/bash
# 引数1 画像ファイル1"
# 引数2 画像ファイル2"
# 引数3 出力ファイル名"
# 引数が2個で無ければエラー表示して終了
if [ $# -ne 3 ]; then
echo "Error: 引数の数が正しくありません"
echo "引数1: 画像ファイル1"
echo "引数2: 画像ファイル2"
echo "引数3: 出力ファイル名"
exit 1
fi
# 引数を変数に格納
file1=$1
file2=$2
file3=$3
ffmpeg -i $file1 -i $file2 -filter_complex "[0][1]blend=all_mode=average" -y temp.png
mv temp.png $file3