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

ffmpeg で使える評価式

コマンド例

YUV、RGBどちらにも使える何も変化しないコマンド。ただし処理速度はかなり落ちる。
ffplay -i input -vf geq='p(X,Y):p(X,Y):p(X,Y)'

左右反転。hflipフィルタと同じ。
ffplay -f lavfi -i 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

もしくはxstackフィルタで一度に4マスを並べる。
ffplay -f lavfi -i "testsrc2,scale=iw/2:-1:flags=neighbor,split=4,xstack=4:0_0|w0_0|0_h0|w0_h0"

π/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 -i testsrc2,geq='(p(X,Y)+(256-p(X-4,Y-4)))/2'

RGB映像を座標によってRGBの値を変える。
ffplay -f lavfi -i 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

trim フィルタの使い方
ループ回数を指定できる loop, aloop

公式ドキュメント: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'

矩形の4隅を丸める

矩形の丸めた4隅を透過させて背景をオーバーレイすることで丸めた4隅が残る。透過のピクセル値は0が完全に透過している。
ffmpeg -f lavfi -i color=darkblue:size=800x600 -f lavfi -i color=gray:size=600x450 -filter_complex "[1:v]format=yuva420p,geq=lum='p(X,Y)':a='if(gt(abs(W/2-X),W/2-20)*gt(abs(H/2-Y),H/2-20),if(lte(hypot(20-(W/2-abs(W/2-X)),20-(H/2-abs(H/2-Y))),20),255,0),255)'[rounded];[0:v][rounded]overlay=x=(W-w)/2:y=(H-h)/2" -frames:v 50 example.mp4

Give a video rounded transparent edges so that it can be overlayed on another video using FFMPEG – Stack Overflow
How to draw text on a rectangle with rounded corners using ffmpeg? – Stack Overflow

drawbox

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*80pow(80,2)に変えてもよい。


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)'

fillbordersフィルタでもグラデーションを作れる。
上下左右を特定色で埋める fillborders

RGBのスペクトラム。
ffmpeg -f lavfi -i color=black:s=1280x256,geq=r='clip(512-X,0,255)+clip(X-1024,0,255)':g='lt(X,512)*clip(X,0,255)+gte(X,512)*clip(1024-X,0,255)':b='lt(X,1024)*clip(X-512,0,255)+gte(X,1024)*clip(1536-X,0,255)',oscilloscope=tw=1:s=1 -vframes 1 rgb-spectrum.png

http://www.ffmpeg-archive.org/Problem-with-colorhold-filter-td4692661.html:FFmpeg-users – Problem with colorhold filterより。

左から右に黒から白。
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

輝度プレーンを左右0から255までのフルレンジにしたグラデーション。
ffplay -f lavfi -i color=s=1920x1080:r=24000/1001,geq=lum='lerp(1\,0\,ceil(255*X/W))*255':cb=128:cr=128,oscilloscope=s=1:tw=1,trim=end_frame=1,loop=-1:1:0,setpts=N/FRAME_RATE/TB
ffplay -f lavfi -i color=s=1920x1080:r=24000/1001,geq=lum='lerp(0\,1\,ceil(255*X/W))*255':cb=128:cr=128,oscilloscope=s=1:tw=1,trim=end_frame=1,loop=-1:1:0,setpts=N/FRAME_RATE/TB

円形グラデーション

円形グラデーション

中心が左上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)))'

そのほか

twitter より
ffmpeg -f lavfi -i "nullsrc=32x24,geq=b='asin(sin( (N+(X+Y)*50)/20 ))/PI*255+127.5',scale=w=iw*20:h=ih*20:flags=neighbor" -pix_fmt yuv420p -vframes 126 output.mp4

ffmpegで簡単な映像制作 – Qiita(https://qiita.com/yukari120/items/f4c302f01f44a7aec1fe)より

ffplay -f lavfi -i color -vf "format=rgb24,scale=600:400,split[a][b];[a]geq=r='if( lt(mod(X+24+if(lt(mod(N/2,80),40),mod(N/2,80)),40),24) * lt(mod(Y+24+if(gt(mod(N/2,80),40),mod(N/2,80)),40),24),255)':g=0:b=0[c];[b]geq=200:30:230:'if( lt(mod(Y+N/2,40),16) * lt(mod(X+N/2,40),16),255)'[d];[c][d]overlay"
checkered finish line(いわゆるゴールの旗模様)

ffplay -f lavfi -i color=black:s=1280x720 -vf geq=lum='255*mod(floor(X/40)+floor(Y/40),2):cr=128'

黒をグレーにして透過部分に見られる白とグレーのパターン

ffplay -f lavfi -i color=black:s=1280x720 -vf geq=lum='if(mod(floor(X/40)+floor(Y/40),2),255,128):cr=128'

360度映像のようにゆがませる。
ffmpeg -f lavfi -i nullsrc=size=2016x2016 -vf "format=gray8,geq='clip(128-128/(6)*(180-192/(2016/2)*hypot(X-2016/2,Y-2016/2)),0,255)',v360=input=fisheye:output=e:ih_fov=192:iv_fov=192,format=rgb24" -frames:v 1 mergemap.png
ffmpeg -i input -i mergemap.png -lavfi "[0]format=rgb24,split[a][b];[a]crop=ih:iw/2:0:0, v360=input=fisheye:output=e:ih_fov=192:iv_fov=192:roll=90[c]; [b]crop=ih:iw/2:iw/2:0,v360=input=fisheye:output=e:yaw=180:ih_fov=192:iv_fov=192:roll=90[d];[c][d][1]maskedmerge,format=rgb24" -pix_fmt yuv420p output.mp4

FFmpeg preserving 360 video orientation : ffmpeg

動物を描く。
ffmpeg -f lavfi -i color=black:s=hd1080,format=gray -vf geq=lum='st(0,(X-W/3)/450);st(1,(H/2-Y)/450);255*gt(-7/20-1/PI*atan(175*pow((ld(0)-63/100),2)+3500*pow(abs(ld(1)-1/10),(26/10-6/10*ld(0)))+160*pow(sin(ld(0)+2/5),8)-50*pow(sin(ld(0)+4/5),100)-500)-exp(-2000*(pow((ld(0)-7/4),2)+pow((ld(1)-33/200),2)-1/1000))-exp(1000*(ld(0)-2/5*ld(1)-43/20))+10*(exp(-exp(1000*(-1/4+pow(ld(1)+1/2,2)))-300*(1/10+pow(ld(1),2))*pow((ld(0)-1/2+13/10+(9/100+23*-1/100)*(ld(1)+2/5)+1/10*pow(cos(ld(1)+7/10),20)),2))+exp(-exp(1000*(-1/4+pow(ld(1)+1/2,2)))-300*(1/10+pow(ld(1),2))*pow((ld(0)-2/2+13/10+(9/100+23*1/100)*(ld(1)+2/5)+1/10*pow(cos(ld(1)+7/10),20)),2))+exp(-exp(1000*(-1/4+pow(ld(1)+1/2,2)))-300*(1/10+pow(ld(1),2))*pow((ld(0)-3/2+13/10+(9/100+23*-1/100)*(ld(1)+2/5)+1/10*pow(cos(ld(1)+7/10),20)),2))+exp(-exp(1000*(-1/4+pow(ld(1)+1/2,2)))-300*(1/10+pow(ld(1),2))*pow((ld(0)-4/2+13/10+(9/100+23*1/100)*(ld(1)+2/5)+1/10*pow(cos(ld(1)+7/10),20)),2))),0)' -frames 1 -y out.png

geqフィルタで動物を描く

[FFmpeg-user] Mathematical Art

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

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