MMA Advent Calendar 2016/12/21
これはMMA Advent Calendar 2016の21日目の記事です
ffmpeg filterとは
https://trac.ffmpeg.org/wiki/FancyFilteringExamples
標準でffmpegにはマンデルブロ集合とかライフゲームなどの面白いfilter機能がついています。今回はそのfilterの作り方を説明します。Libavfilterから直接作ることもできるのですが今回はFrei0rで作る方法を記述します。実際の動画編集はノンリニア編集するほうが便利ですが生配信などにはもしかして自作フィルターが使えるかもしれません(?)
Frei0r
Frei0rというのはクロスプラットフォームのvideo effect pluginです。最近の新しいffmpegはfrei0rに対応しています。ffmpeg -versionして--enable-frei0rと表示されていれば対応しています。
#include "frei0r.hpp"
#include "frei0r_math.h"
#include <stdlib.h>
// frei0r::filterを継承して作っていきます
class Mat33 : public frei0r::filter
{
public:
Mat33(unsigned int width, unsigned int height)
{
// register_paramで引数を登録することができます
register_param(m11, "m11", "matrix element m11");
register_param(m12, "m12", "matrix element m12");
register_param(m13, "m13", "matrix element m13");
register_param(m21, "m21", "matrix element m21");
register_param(m22, "m22", "matrix element m22");
register_param(m23, "m23", "matrix element m23");
register_param(m31, "m31", "matrix element m31");
register_param(m32, "m32", "matrix element m32");
register_param(m33, "m33", "matrix element m33");
}
// updateで一フレームごとの処理を書いていきます
// inに編集前ピクセルデータがあり、outに編集後のピクセルデータを入れます
virtual void update(double time,
uint32_t* out,
const uint32_t* in)
{
std::copy(in, in + width*height, out);
for (unsigned int y=1; y<height-1; y++){
for (unsigned int x=1; x<width-1; x++){
unsigned char *p1 = (unsigned char *)&in[(y-1)*width+(x-1)];
unsigned char *p2 = (unsigned char *)&in[(y-1)*width+x];
unsigned char *p3 = (unsigned char *)&in[(y-1)*width+(x+1)];
unsigned char *p4 = (unsigned char *)&in[y*width+(x-1)];
unsigned char *p5 = (unsigned char *)&in[y*width+x];
unsigned char *p6 = (unsigned char *)&in[y*width+(x+1)];
unsigned char *p7 = (unsigned char *)&in[(y+1)*width+(x-1)];
unsigned char *p8 = (unsigned char *)&in[(y+1)*width+x];
unsigned char *p9 = (unsigned char *)&in[(y+1)*width+(x+1)];
unsigned char *o = (unsigned char *)&out[y*width+x];
for (int i=0; i<3; ++i){
o[i] = CLAMP0255(abs(p1[i]*m11 + p2[i]*m12 + p3[i]*m13 + p4[i]*m21 + p5[i]*m22 + p6[i]*m23 + p7[i]*m31 + p8[i]*m32 + p9[i]*m33));
}
o[3] = ((unsigned char*)&in[y*width+x])[3];
}
}
}
private:
f0r_param_double m11;
f0r_param_double m12;
f0r_param_double m13;
f0r_param_double m21;
f0r_param_double m22;
f0r_param_double m23;
f0r_param_double m31;
f0r_param_double m32;
f0r_param_double m33;
};
// filterの登録処理です
frei0r::construct<Mat33> plugin("Mat33",
"3*3 Matrix filter",
"su_zu",
0,1,
F0R_COLOR_MODEL_RGBA8888);
上の例は3×3行列のフィルター処理を行えるフィルターを書いてみました。なんかフィルターフィルター言っててわかりづらい。。。
How to Use
# buildしたfilter DLLのmat33.dllをC:\Program Files (x86)\frei0r\lib\frei0r-1に置いたとしましょう
#FREI0R_PATH環境変数を設定
$ export FREI0R_PATH="C:\Program Files (x86)\frei0r\lib\frei0r-1"
$ ffmpeg -i INPUT.mp4 -vf "frei0r=hogefugafilter" OUTPUT.mp4
ちなみにdllは https://www.mma.club.uec.ac.jp/~su_zu/public/mat33.dll に置いておきました。信用できない人はソースはgithubに上げたので自分でビルドしてみてください。 https://github.com/suzusuzu/frei0r/blob/mat33/src/filter/mat33/mat33.cpp
sobel filter
sobel filterとは空間1次微分を計算してedgeを抽出するfilterです。
上から下へのsobel filter
左から右へのsobel filter
ちなみにfilterは","で処理を繋げることができる。今回はsobelフィルターをかけるまえにグレースケールを行う
元動画 http://download.blender.org/peach/bigbuckbunny_movies/big_buck_bunny_480p_surround-fix.avi
$ ffmpeg -t 30 -i http://download.blender.org/peach/bigbuckbunny_movies/big_buck_bunny_480p_surround-fix.avi -vf "format=gray,frei0r=mat33:1|2|1|0|0|0|-1|-2|-1" -pix_fmt yuv420p sobel.mp4
平滑化 filter
平均化 filterとも言います。
名前の通り平らに滑らかにするfilterです。
$ ffmpeg -t 30 -i http://download.blender.org/peach/bigbuckbunny_movies/big_buck_bunny_480p_surround-fix.avi -vf "frei0r=mat33:0.111|0.111|0.111|0.111|0.111|0.111|0.111|0.111|0.111" -pix_fmt yuv420p ave.mp4