ffmpeg コマンドラインツール入門 第2回

こんにちは、前回に引き続きシニアリサーチャーの佐藤です。

今回と次回は、ffmpeg のフィルタ処理についての話です。これを使えば、コマンドラインから様々な画像処理を動画に施すことができ、とても便利です。ただ、フィルタの記述方法は分かりにくいので、まずはどうやって使うのか、今回はその概要を伝えたいと思います。

なお、前回の記事はこちらです。

動画フィルタ

動画に簡単な加工を施したいことがある。例を挙げれば、

  • フレームの一部を切り取って拡大する
  • 2つの動画をアルファブレンドする

といった処理である。ffmpeg のフィルタを用いれば、この種の加工をコマンドラインから行うことができる。-s オプションで動画のサイズを変更するといったことなども、実態はフィルタの簡易表記である。

簡単なフィルタ

簡単な例として、入力動画の中心80%をクロップした後、1.25倍拡大して元のサイズに戻す処理を考える。図示すると以下のようになる。

f:id:morphotech:20200625154029p:plain

ffmpeg では、このように1入力の簡単な加工処理を記述するために、出力オプション -filter:v / -vf が用意されている。このオプションを用いるとこの処理は

ffmpeg -i input.mp4 -vf "crop=iw*0.8:ih*0.8, scale=iw/0.8:ih/0.8" output.mp4

と書ける。-vf に続けて、個々のフィルタを , で区切って順番に並べただけである。ここでは、空白をエスケープするために " で囲っている。今はフィルタが2つだけだが、3つ以上になったときも , で並べていけばよい。ここで使っているフィルタ cropscale は、それぞれクロップと拡大縮小を行うフィルタである。それぞれのフィルタごとの具体的なオプションは、= に続けて記述する。

オプションの指定方法を説明するために、ここでは crop フィルタを例に取る。このフィルタには6つのオプションがあり、それを列挙すると以下のようになる。

# オプション名 説明 デフォルト
1 w / out_w クロップサイズ 入力動画と同じ
2 h / out_h クロップサイズ 入力動画と同じ
3 x クロップ枠左上隅の位置 クロップ枠が入力動画の中心に来るように調整
4 y クロップ枠左上隅の位置 クロップ枠が入力動画の中心に来るように調整
5 keep_aspect 1 ならアスペクト比を保持する、0 ならしない 0
6 exact 1 ならサブピクセル精度で切り取りを行う、0 なら行わない 0

/ で区切って並べているものは別名、# で示している数字は何番目のオプションかを示す番号である。オプションの指定に使える定数がいくつか用意されているが、全てを列挙することはせず、主要なものを挙げるだけに留める。

定数名 説明
in_w / iw 入力動画サイズ
in_h / ih 入力動画サイズ
out_w / ow クロップサイズ
out_h / oh クロップサイズ
n フレームカウント
t タイムスタンプ(秒)

具体的なオプションの指定方法だが、

crop=オプション1:オプション2:オプション3:...:オプション6

: で区切って順番に並べればよい。未指定の部分はデフォルトが使用される。上の例だと、クロップ枠として入力動画の80%の大きさのものを用意するように設定している。オプション3以降は未指定なので、デフォルトのままになる。また、

crop=h=ih*0.8:w=iw*0.8

のようにオプション名を直接指定して設定することも可能である。前者の指定方法だと、特定のオプションだけを設定する方法がないので、後ろの方のオプションだけ設定したい場合はこの方法が適切である。

scale フィルタについては次回解説するが、

scale=iw/0.8:ih/0.8

が1.25倍の拡大に対応することは想像可能かと思う。

実はズームを行う zoompan というフィルタが存在するが、かなり変なフィルタなので使用を避けるのが賢明。

複雑なフィルタ

アルファブレンドなど入力動画が複数になった場合は、以上のやり方だと対応できない。複数入力処理の記述には、-filter_complex オプションを用いる。ここで、例として以下のナンセンスな処理を考える。

f:id:morphotech:20200625154255p:plain

これを ffmpeg フィルタで表現すると、

ffmpeg -t 10 -i input1.mp4 -t 10 -i input2.mp4 -filter_complex "
    [1:v]hflip[tmp];
    [0:v][tmp]blend=all_expr='(A+B)/2', vflip
" output.mp4

となる。なお、改行は見やすさのために入れているだけである。まず、-i で指定する入力ファイルが2つになっていることが分かる。入力オプションはそれぞれの -i の手前に書けばよい。この例だと、冒頭の10秒間を切り出している。他に新しい要素として、[] で囲ったタグが登場している。図と比較すれば分かるが、これはグラフのエッジに対応しており、フィルタ名の左側に入力エッジを並べ、右側に出力エッジを並べる。[0:v][1:v] は特殊であり、それぞれ入力動画1と入力動画2に対応したノードに繋がっている。入力が増えれば、[2:v], [3:v] とカウントアップされていく。なお、v というのは動画を意味している。これは、-filter_complex では音声のフィルタも記述できるので、区別のためである。

もう少し細かく解説する。まずは最初の処理である。

[1:v]hflip[tmp];

これは、入力動画2を左右反転させ、その結果に [tmp] というタグを付けている。このタグ名は自由に選ぶことができる。なお、タグを付けた場合はフィルタの後に , ではなく ; を書く。

[0:v][tmp]blend=all_expr='(A+B)/2',

次に、入力動画1と先程の結果 [tmp] をアルファブレンドさせている。blend フィルタの all_expr オプションを使えば、このように数式としてブレンド方法を指定できるので便利である。A が1つめの入力、つまり [0:v] の画素値、B が2つめ、[tmp] の画素値に対応する。タグが付かずに , で終わっているのは、簡単なフィルタの時と同様、このフィルタの結果をそのまま次のフィルタに渡すからである。つまり、フィルタの結果が次のフィルタに直接つながる場合は , で区切り、そうでない場合はタグを付けて ; で区切る。

基本的に、設定する数式は常に '特殊文字エスケープさせておくのが賢明である。そしてその場合、常にフィルタ全体を " で囲んでおくのがよい。なぜかと言うと、まずフィルタ全体、次に数式の中と二段階でパースされるので、数式だけを囲っていると、フィルタ全体の解析の時点で引用符がとられてしまうからである。そして、(おそらく)" は空白文字類だけをエスケープするので、数式に使うには機能不足である。つまり数式で ' を使うためには、その周りをさらに " で囲む必要がある。

vflip

そして上下反転を行う。このフィルタの入力には直前のフィルタの結果が直接渡されるので、入力側にはタグがない。また、最後に作用させたフィルタの結果が自動的に最終結果となるので、出力側にもない。

なお、以下の場合のように、複数の入力を必要とするフィルタであっても、入力タグの指定を省略できる場合がある。

ffmpeg -i input1.mp4 -i input2.mp4 -filter_complex "blend=all_expr='(A+B)/2'" output.mp4

この場合だと、[0:v][1:v]blend とみなされる。大抵の場合、[0:v] 等の v も省略できる。ただし、あまり省略しすぎないほうが安全である。また、処理の途中で複数のタグが必要な場合でも、入力動画が1つであれば -vf オプションで記述できる。これは次回例を挙げる。

次回予告

今回はここまでです。次回は、いくつかのフィルタの解説と、それを使った処理例を紹介します。例えば、このように動画をぐるぐる回転させる方法や、

Star Wars 風に画面を切り替える方法などを解説する予定です。