Emscriptenでの画像フィルター:畳み込み操作とWebGLのアクセラレーション

Emscriptenでの画像フィルター:畳み込み操作とWebGLのアクセラレーション

Web開発におけるリアルタイム画像処理は、特に複雑なフィルター効果を扱う際にはパフォーマンス上の課題を抱えます。
EmscriptenはC/C++コードをWebAssemblyにコンパイルするツールチェーンです。WebGLを組み合わせることで、ブラウザ環境下でも高性能な画像フィルター処理を実現できます。

本記事では、畳み込み操作を核に、EmscriptenとWebGLを活用して画像フィルターの性能を向上させる方法を解説します。

畳み込み操作の基礎とEmscriptenでの実装

畳み込み(Convolution)は画像処理の基本アルゴリズムです。
畳み込みカーネルを使用し、画像の画素値を加重平均することで、ぼかし、鋭化、エッジ検出などの効果を実現します。

高斯ぼかしの典型的な3x3カーネルは以下のようになります。

float filter[9] = {
    1.0f / 16.0f, 2.0f / 16.0f, 1.0f / 16.0f,
    2.0f / 16.0f, 4.0f / 16.0f, 2.0f / 16.0f,
    1.0f / 16.0f, 2.0f / 16.0f, 1.0f / 16.0f
};

Emscriptenは効率的なメモリ操作APIを提供します。例えば、emscripten_set_main_loopModule._mallocを使用することで、画像データのバッファ管理を容易に行うことができます。

以下は、畳み込みを用いたシンプルなフィルター効果の実装例です。

// 簡易的な畳み込み実装(擬似コード)
for (int y = 1; y < height - 1; ++y) {
    for (int x = 1; x < width - 1; ++x) {
        float total = 0.0f;
        for (int ky = -1; ky <= 1; ++ky) {
            for (int kx = -1; kx <= 1; ++kx) {
                total += image[y + ky][x + kx] * filter[ky + 1][kx + 1];
            }
        }
        output[y][x] = total;
    }
}

しかし、高分解度画像の3x3畳み込みをCPUだけで行う場合、1920x1080画素の画像を処理するのに約80msを要し、60fpsのリアルタイム処理を満足するには十分ではありません。

WebGLアクセラレーションの仕組みとEmscriptenとの連携

WebGLはGPUの並列計算能力を活用し、畳み込み操作をCPUの10倍~100倍速く行うことができます。
EmscriptenはWebGL 1.0/2.0のAPIを完全に封装化しており、C/C++からWebGL関数を直接呼び出すことができます。

具体的には、以下のような手順で畳み込みをGPUで高速化します。

  1. テクスチャーロード
    画像データをGPUのテクスチャーにロードします。
  2. シェーダーでの畳み込み
    片元シェーダーで並列計算を行います。
  3. uniformの伝達
    卷積核のデータをシェーダーに伝達します。
  4. レンダリングと結果取得
    レンダリングを実行し、結果を取得します。

以下は、WebGLを用いた畳み込みの実装例です。

WebGLを用いた畳み込み加速の手順

  1. テクスチャーロード
    画像データをGPUにアップロードします。
  2. glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 幅, 高さ, 0, 
                     GL_RGBA, GL_UNSIGNED_BYTE, 画像データ);
        
  3. シェーダーでの畳み込み
    片元シェーダーで並列計算を行います。
  4. // 片元シェーダーでの畳み込み
    precision mediump float;
    uniform sampler2D u_image;
    uniform float u_filter[9];
    uniform vec2 u_texSize;
    
    void main() {
        vec2 coord = gl_FragCoord.xy / u_texSize;
        vec2 texel = 1.0 / u_texSize;
        vec4 sum = vec4(0.0);
        
        // 3x3フィルターを適用
        sum += texture2D(u_image, coord + vec2(-texel.x, -texel.y)) * u_filter[0];
        sum += texture2D(u_image, coord + vec2(0.0, -texel.y)) * u_filter[0];
        // ... 他の8方向のサンプリング
        
        gl_FragColor = sum;
    }
        
  5. uniformの伝達
    卷積核のデータをシェーダーに伝達します。
  6. glUniform1fv(glGetUniformLocation(プログラム, "u_filter"), 9, filter);
    glUniform2f(glGetUniformLocation(プログラム, "u_texSize"), 幅, 高さ);
        
  7. レンダリングと結果取得
    レンダリングを実行し、結果を取得します。
  8. glDrawArrays(GL_TRIANGLES, 0, 6); // 全画面の四角形を描画
    glReadPixels(0, 0, 幅, 高さ, GL_RGBA, GL_UNSIGNED_BYTE, 結果データ);
        

性能比較と最適化手法

GPUアクセラレーションの効果

処理方式 1920x1080画像の処理時間 性能向上
CPU単独 〜80ms 1x
WebGL 〜4ms 20x

上記のベンチマークテスト結果から、WebGLを活用することで畳み込み操作の処理時間を95%削減 possibleです。

最適化の提案

  1. テクスチャーコンプレッション
    メモリバンド幅を削減するためのテクスチャーコンプレッションを利用します。
  2. mipマップ
    大型画像に対して mipマップ技術を適用します。
  3. FBOでの離屏レンダリング
    複数回の画素読み取りを避け、GPU内ですべてのフィルターを適用します。
  4. フィルター分割
    3x3畳み込みを1x3と3x1の二段階に分割し、計算量を削減します。

実際の応用例

EmscriptenのWebGLアクセラレーションは、以下のような場面で活用されています。

  • ゲーム開発
    2Dゲームエンジンでのリアルタイムフィルター効果を実現します。
  • 医学画像
    CT画像のエッジ強調やノイズ除去を行います。
  • ARアプリケーション
    リアルタイムビデオストリームの処理を行います。

図1:左が元画像、右がEmscripten+WebGLを用いたエッジ検出結果です。

結論と今後の展望

EmscriptenとWebGLの組み合わせは、ブラウザ環境下での画像処理に高性能なソリューションを提供します。

  • 跨プラットフォーム一貫性
    1つのC/C++コードがすべてのWebGL対応ブラウザで動作します。
  • ネイティブ性能
    WebAssemblyとGPUアクセラレーションにより、ネイティブアプリに近い体験を提供します。
  • 豊富な生態系
    SDLやOpenGLなどの成熟したグラフィックライブラリを統合可能です。

今後は、WebGPUを活用することでさらなる性能向上が期待されます。

具体的な実装例は以下のリポジトリを参照してください。

  • 基本畳み込みの実装:test/gl_ps.c
  • WebGLの総合例:test/html5_webgl.c
  • ベンチマークツール:test/benchmark/run_benchmarks.py

Emscriptenプロジェクトのダウンロードはこちらから。

無料ダウンロードリンク:Emscriptenプロジェクトアドレス:https://gitcode.com/gh_mirrors/ems/emscripten

タグ: Emscripten WebGL 画像処理 畳み込み GPUアクセラレーション

7月26日 01:05 投稿