数式で線や円、模様が描ける geq


ffmpeg のフィルタの中でおそらく最も使い方が難解な geq(generic equation) フィルタの使い方。YUV と RGB、A のそれぞれを設定により色を変えたり、回転・反転させたり、グラデーションや線、円や矩形も描ける。さらにマスクも作れるのでトランジションへの応用もできるが処理速度はかなり遅い。その書き方はすべて縦横の座標とその値を 評価式 で計算する。

コマンド例

YUV、RGB どちらにも使える何も変化しないコマンド。ただし処理速度はかなり落ちる
ffplay input -vf geq='p(X,Y):p(X,Y):p(X,Y)'
左右反転。hflip フィルタと同じ
ffplay -f lavfi testsrc2,geq='p(W-X,Y)'
2×2 の分割表示
ffplay -f lavfi -i testsrc2,geq='p(mod(2*X,W),mod(2*Y,H))'
上は負荷が大きいので scale,split,vstack,split,hstack を併用した方がよい
ffplay -f lavfi -i testsrc2,scale=iw/2:-1:flags=neighbor,split,vstack,split,hstack
π/3 に傾けて、100ピクセル幅のサイン波を表示する
ffplay -f lavfi -i color,geq='128+100*sin(2*(PI/100)*(cos(PI/3)*(X-50*T)+sin(PI/3)*Y)):128:128'
エンボス効果
ffplay -f lavfi testsrc2,geq='(p(X,Y)+(256-p(X-4,Y-4)))/2'
RGB映像を座標によって RGB の値を変える
ffplay -f lavfi testsrc,geq=r='X/W*r(X,Y)':g='(1-X/W)*g(X,Y)':b='(H-Y)/H*b(X,Y)'
中央を明るく、四隅を次第に暗くする vignette フィルタのような効果
ffplay -f lavfi -i color,geq='255*gauss((X/W-0.5)*3)*gauss((Y/H-0.5)*3)/gauss(0)/gauss(0):128:128'

geq フィルタは処理が重たいので映像に変化がないなら trim フィルタで1フレームだけ取り出して loop フィルタで繰り返した方がよい
ffplay -f lavfi -i color,geq='st(0,between(X,W/3,2*W/3)*between(Y,H/3,2*H/3));ld(0)*255:128:128',trim=end_frame=1,loop=-1:1:0,setpts=N/FRAME_RATE/TB
公式ドキュメント:FFmpeg Filters Documentation : geq

オプション

数値を指定すれば全画面が指定した色になる。評価式を指定すれば座標毎に色を変えたり動かしたりできる。

  • lum_expr, lum[string]
    輝度(Y)の設定。YUV の値を変えるなら必須項目
    既定値:未設定
  • cb_expr, cb[string]
    彩度(U)の設定
    既定値:lum と同じ値
  • cr_expr, cr[string]
    彩度(V)の設定
    既定値:lum と同じ値
  • alpha_expr, a[string]
    透過(A)の設定
    既定値:元映像のまま
  • red_expr, r[string]
    赤(R)の設定。RGB の値を変えるなら RGB のどれか1つが必須項目
    既定値:元映像のまま
  • green_expr, g[string]
    緑(G)の設定。RGB の値を変えるなら RGB のどれか1つが必須項目
    既定値:元映像のまま
  • blue_expr, b[string]
    青(B)の設定。RGB の値を変えるなら RGB のどれか1つが必須項目
    既定値:元映像のまま

使える評価式

  • N
    0 から始まるフレーム番号
  • X, Y
    入力映像の横と縦の解像度で 0 からはじまり W, H までを返す
  • W, H
    入力映像の横と縦の解像度。固定値
  • SW, SH
    ピクセルフォーマットに合わせた横と縦の解像度比。YUV 4:2:0 の輝度は 1, 1、彩度は 0.5, 0.5 になる
  • T
    0 から始まるタイムスタンプ秒
  • p(x, y)
    入力映像の該当チャンネルの横と縦の値を指定し、その座標の値を返す
  • lum(x, y)
    入力映像の輝度(Y)チャンネルの指定した横と縦の値を返す
  • cb(x, y)
    入力映像の彩度(U)チャンネルの指定した横と縦の値を返す
  • cr(x, y)
    入力映像の彩度(V)チャンネルの指定した横と縦の値を返す
  • r(x, y)
    入力映像の赤チャンネルの指定した横と縦の値を返す
  • g(x, y)
    入力映像の緑チャンネルの指定した横と縦の値を返す
  • b(x, y)
    入力映像の青チャンネルの指定した横と縦の値を返す
  • alpha(x, y)
    入力映像の透過チャンネルの指定した横と縦の値を返す

マスクにも使えるコマンドサンプル

0 と 255 だけにマスクするのなら YUV でも RGB でもよいが、グラデーションにする場合 YUV だと限定レンジを超えた値(Y だと15以下と236以上、UV だと 15以下と241以上)で RGB 変換するとその値がグラデーションにならないのに注意する。RGB に変換せずに グラデーションを使うには A(アルファチャンネル) をつかう。

矩形

横*縦 で範囲を決めて最後に *255 で Y の値を指定している。矩形を狭くすれば縦や横のラインになる

矩形

ffplay -f lavfi -i color,geq='(between(X,W/3,2*W/3)*between(Y,H/3,2*H/3))*255:128:128'
横のライン
ffplay -f lavfi -i color,geq='(between(X,10,2*100)*between(Y,5,10))*255:128:128'
縦のライン
ffplay -f lavfi -i color,geq='(between(X,5,10)*between(Y,10,2*100))*255:128:128'
加算すれば矩形を追加できる
ffplay -f lavfi -i color,geq='(between(X,10,2*100)*between(Y,5,2*5)+between(X,200,2*150)*between(Y,50,55))*255:128:128'

drawbox

drawbox フィルタと同じことも出来る。矩形を書いてその中心を反対の色で埋める

drawbox

ffplay -f lavfi -i color,geq='(between(X,10,2*100)*between(Y,10,100))*255+(between(X,15,195)*between(Y,15,95)):128:128'

pow() の第一引数で横と縦の座標を決め lt() の第二引数で円の半径の2乗を指定する。80*80 を pow() に変えてもよい


ffplay -f lavfi -i color,geq='lte(pow(X-(W/2),2)+pow(Y-(H/2),2),80*80)*255':128:128
drawbox と同様に中を抜いて線で円を描くことも出来る


ffplay -f lavfi -i color,geq='st(0,pow(X-(W/2),2)+pow(Y-(H/2),2));lte(ld(0),80*80)*255+lte(ld(0),79*79)':128:128

破線

最初2つの between() で座標を指定し、3番目の between(mod()) で何ピクセル毎に白黒を繰り返すか指定する。例では20ピクセル毎に1から10までは白、11から20までは黒になる

破線

ffplay -f lavfi -i color,geq='between(X,0,W)*between(Y,5,10)*between(mod(X,20),0,9)*255:128:128'

ffplay -f lavfi -i color,geq='between(X,5,10)*between(Y,0,H)*between(mod(Y,20),0,9)*255:128:128'

グラデーション

右から左、上から下またはその逆や特定座標から左右上下にグラデーションする

グラデーション

ffplay -f lavfi -i color,geq=r='ceil(255*X/W)':g='255-ceil(255*X/W)':b='255-ceil(255*Y/H)'

左から右に黒から白
ffplay -f lavfi -i color,geq='r=ceil(255*X/W):g=ceil(255*X/W):b=ceil(255*X/W)'
左から右に白から黒
ffplay -f lavfi -i color,geq='r=255-ceil(255*X/W):g=255-ceil(255*X/W):b=255-ceil(255*X/W)'
上から下に黒から白
ffplay -f lavfi -i color,geq='r=ceil(255*Y/H):g=ceil(255*Y/H):b=ceil(255*Y/H)'
上から下に白から黒
ffplay -f lavfi -i color,geq='r=255-ceil(255*Y/H):g=255-ceil(255*Y/H):b=255-ceil(255*Y/H)'
200ピクセル右にずらし左右にグラデーション

オフセット座標のグラデーション

ffplay -f lavfi -i color,geq='r=clip(255-sqrt(pow(X-200,2)),0,255):g=clip(255-sqrt(pow(X-200,2)),0,255):b=clip(255-sqrt(pow(X-200,2)),0,255)'
100ピクセル下にずらし上下にグラデーション
ffplay -f lavfi -i color,geq='r=clip(255-sqrt(pow(Y-100,2)),0,255):g=clip(255-sqrt(pow(Y-100,2)),0,255):b=clip(255-sqrt(pow(Y-100,2)),0,255)'
RGB に変換せず A を使ってもグラデーションになる
ffplay -f lavfi -i color,format=yuva420p,geq=128:a='clip(255-sqrt(pow(X,2)+pow(Y,2)),0,255)',alphaextract

円グラデーション

円グラデーション

中心が左上ffplay -f lavfi -i color,geq=r='clip(255-sqrt(pow(X,2)+pow(Y,2)),0,255)':g='clip(255-sqrt(pow(X,2)+pow(Y,2)),0,255)':b='clip(255-sqrt(pow(X,2)+pow(Y,2)),0,255)'
中心が左下
ffplay -f lavfi -i color,geq='r=clip(255-sqrt(pow(X,2)+pow((Y-H),2)),0,255):g=clip(255-sqrt(pow(X,2)+pow((Y-H),2)),0,255):b=clip(255-sqrt(pow(X,2)+pow((Y-H),2)),0,255)'
中心が右上
ffplay -f lavfi -i color,geq='r=clip(255-sqrt(pow((X-W),2)+pow(Y,2)),0,255):g=clip(255-sqrt(pow((X-W),2)+pow(Y,2)),0,255):b=clip(255-sqrt(pow((X-W),2)+pow(Y,2)),0,255)'
中心が右下
ffplay -f lavfi -i color,geq='r=clip(255-sqrt(pow((X-W),2)+pow((Y-H),2)),0,255):g=clip(255-sqrt(pow((X-W),2)+pow((Y-H),2)),0,255):b=clip(255-sqrt(pow((X-W),2)+pow((Y-H),2)),0,255)'
sqrt の倍数を小さくするとグラデーションが広くなる
ffplay -f lavfi -i color,geq=r='clip(255-0.5*sqrt(pow(X,2)+pow(Y,2)),0,255)':g='clip(255-0.5*sqrt(pow(X,2)+pow(Y,2)),0,255)':b='clip(255-0.5*sqrt(pow(X,2)+pow(Y,2)),0,255)'
255/(W/2)*sqrt で左右の端が0になる
ffplay -f lavfi -i color,geq='r=st(0,pow(X-(W/2),2)+pow(Y-(H/2),2));st(1,clip(255-255/(W/2)*sqrt(ld(0)),0,255));lte(ld(0),pow(W/2,2))*ld(1):g=st(0,pow(X-(W/2),2)+pow(Y-(H/2),2));st(1,clip(255-255/(W/2)*sqrt(ld(0)),0,255));lte(ld(0),pow(W/2,2))*ld(1):b=st(0,pow(X-(W/2),2)+pow(Y-(H/2),2));st(1,clip(255-255/(W/2)*sqrt(ld(0)),0,255));lte(ld(0),pow(W/2,2))*ld(1)'
255/hypot(W/2,H/2) で四隅が0になる
ffplay -f lavfi -i color,geq='r=st(0,pow(X-(W/2),2)+pow(Y-(H/2),2));st(1,clip(255-255/hypot(W/2,H/2)*sqrt(ld(0)),0,255));lte(ld(0),pow(hypot(W/2,H/2),2))*ld(1):g=st(0,pow(X-(W/2),2)+pow(Y-(H/2),2));st(1,clip(255-255/hypot(W/2,H/2)*sqrt(ld(0)),0,255));lte(ld(0),pow(hypot(W/2,H/2),2))*ld(1):b=st(0,pow(X-(W/2),2)+pow(Y-(H/2),2));st(1,clip(255-255/hypot(W/2,H/2)*sqrt(ld(0)),0,255));lte(ld(0),pow(hypot(W/2,H/2),2))*ld(1)'
円の半径を決めてグラデーション
lte(ld(0),pow()) で円の半径を決める。sqrt の倍数と併用する
ffplay -f lavfi -i color,geq='r=st(0,pow(X-(W/2),2)+pow(Y-(H/2),2));st(1,clip(255-1*sqrt(ld(0)),0,255));lte(ld(0),pow(100,2))*ld(1):g=st(0,pow(X-(W/2),2)+pow(Y-(H/2),2));st(1,clip(255-1*sqrt(ld(0)),0,255));lte(ld(0),pow(100,2))*ld(1):b=st(0,pow(X-(W/2),2)+pow(Y-(H/2),2));st(1,clip(255-1*sqrt(ld(0)),0,255));lte(ld(0),pow(100,2))*ld(1)'

幅の広いグラデーション

st() が分割数の指定

幅の広いグラデーション

左から黒から白へffplay -f lavfi -i color,geq=r='st(0,14);(255/(ld(0)-1))*trunc(X/(W/ld(0))):g=st(0,14);(255/(ld(0)-1))*trunc(X/(W/ld(0))):b=st(0,14);(255/(ld(0)-1))*trunc(X/(W/ld(0)))'
左から白から黒へ
ffplay -f lavfi -i color,geq=r='st(0,14);255-(255/(ld(0)-1))*trunc(X/(W/ld(0))):g=st(0,14);255-(255/(ld(0)-1))*trunc(X/(W/ld(0))):b=st(0,14);255-(255/(ld(0)-1))*trunc(X/(W/ld(0)))'
上から黒から白へ
ffplay -f lavfi -i color,geq=r='st(0,14);(255/(ld(0)-1))*trunc(Y/(H/ld(0))):g=st(0,14);(255/(ld(0)-1))*trunc(Y/(H/ld(0))):b=st(0,14);(255/(ld(0)-1))*trunc(Y/(H/ld(0)))'
上から白から黒へ
ffplay -f lavfi -i color,geq=r='st(0,14);255-(255/(ld(0)-1))*trunc(Y/(H/ld(0))):g=st(0,14);255-(255/(ld(0)-1))*trunc(Y/(H/ld(0))):b=st(0,14);255-(255/(ld(0)-1))*trunc(Y/(H/ld(0)))'

コメントを残す

メールアドレスが公開されることはありません。

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)