はじめに
動画プレイヤーを作ったので、今度は HTML の Video をいじってみたい!
その中で、手頃で便利な処理といえば、アプコン (アップコンバート) ですね!
というわけで、アニメで顕著な効果が出るといわれている Anime4K を使ってみたい!
でも、探したはいいものの 4系 (4.0.1) の WebGL 移植が見当たらない!
3系の昔の実装はありましたが、4系 に関しては全然見当たりません...
じゃあ、やってしまえということで、Anime4K 4系 (4.0.1) を WebGL に移植した!
という記事になります。
成果物
Anime4K.js という形で作りました。
静止画、動画のアップコンバートをサポートしています。
読み込むと、こんな感じで window に Anime4KJS
が生えて、アップスケーラを作れます。
現時点では video の信号の 2倍 アップスケールができます。
const upscaler = new Anime4KJS.VideoUpscaler(30 /* TARGET FPS */, Anime4KJS.ANIME4KJS_SIMPLE_M_2X /* PROFILE */);
upscaler.attachVideo(videoElement, canvasElement);
upscaler.start(); // start upscaling
// ...
upscaler.stop(); // stop upscaling
この辺の敵
浮動小数点数テクスチャ
Anime4K は CNN の演算結果を、そのままテクスチャに入れ、次の層へ伝搬していきます。
このため、テクスチャが小数に対応していないと、切り捨てが発生し丸められます。
その結果として、整数で処理を行うと、層が進むたびに切り捨てられてしまいます。
なので、浮動小数点数テクスチャを使って、整数への丸めを回避する必要がありました。
WebGL1 の場合
WebGL1 で浮動小数点数テスクチャを扱う場合には、以下の拡張を使う必要があります。
-
OES_texture_float
: 32bit 精度の浮動小数点数テスクチャ -
OES_texture_half_float
: 16bit 精度の浮動小数点数テスクチャ
OES_texture_float
拡張を使う場合には、このようにデータを書き込みます。
gl.getExtension('OES_texture_float'); // 1度やればいい, get で有効化って微妙じゃない?
// 実際にデータを入れるときに gl.FLOAT を指定する
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.FLOAT, null);
OES_texture_half_float
の場合は、以下のようになります。
拡張のオブジェクトの中の HALF_FLOAT_OES
を使うと半精度 (16bit) になります。
const ext = gl.getExtension("OES_texture_half_float");
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, ext.HALF_FLOAT_OES, null);
WebGL2 の場合
WebGL2 では、浮動小数点テスクチャはサポートされています。
ただ描画する際には EXT_color_buffer_float
拡張が必要です。
gl.getExtension('EXT_color_buffer_float'); // 1度やればいい, get で有効化って微妙じゃない?
// 32bit float
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA32F, width, height, 0, gl.RGBA, gl.FLOAT, null);
// 16bit float
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA16F, width, height, 0, gl.RGBA, gl.HALF_FLOAT, null);
MPV のフック形式の自動変換
Anime4K は MPV のフック形式 でフラグメントシェーダーを記述しています。
これは、勝手に頂点シェーダーやシェーダーの変数定義を行ってくれる便利なものです。
また、フックタイミングを記載でき、様々な処理に割り込んでシェーダーを適用できます。
ただ、WebGL に移植する際には、この機能を自前で補完しなければなりません。
シェーダーの量が多いため、ここは自動で TypeScript + glsl に変換するコードを書きました。
基本的に Anime4K 4系 (4.0.1) は、以下のフックで構成されています。
(昔は YUV に対するフックなどがありましたが、4系では見当たりませんでした)
- MAIN フック: YUV から RGB に変換された後で、リサイズ前
- PREKERNEL フック 最終出力前
この部分を TypeScript + WebGL でフック処理するように変換しています。
課題
処理の最適化
オリジナルは 4ms でアップスケールできるようなのですが、14ms くらいかかってしまいます。
なぜか 1 テスクチャ書き込みに 1ms かかってしまうみたいです。
テスクチャを 12 個くらい書いてるので、14 ms かかってしまっているという感じです。
最適化が足りてないのだろうと思っているので、今後改善したいところです。
まとめ
Anime4K 4系 (4.0.1) を WebGL に移植して、ブラウザでアップコンバートしました。
浮動小数点数テクスチャ や MPV (libplacebo) のフックを勉強するいい機会になりました。
こんどは、アニメ向けではないアップスケーラも挑戦してみたいところです。