ffmpegだけを使って、動画or複数の画像を1枚に合成する方法

## 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つの画像に結合します。)

```image_mixing.sh
#!/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です。


```create_background.sh
#!/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
```


## ２つの画像を結合し、１つの画像を出力 (追記 2023/09/16 その２)

最初からこうすりゃ良かった。
力技だけど、シンプルに2枚の画像を1枚に結合するスクリプト。
（中間ファイル「./temp.png」が作成されて削除されます。）

```image_composition.sh
#!/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
```