2011-01-26 22 views
10

私はiPhoneアプリケーションで何かを実装するためにシェイダーを習得しようとしています。これまでのところ、グレースケール、閾値処理などのカラー画像を作成するような簡単な例を理解していました。ほとんどの例では、入力画像ピクセルI(x,y)を処理すると、同じピクセルの色を単純に変更するという単純な操作が必要です。OpenGLシェーダを使用したガウスフィルタ

しかし、Convolutionsはどうですか?例えば、最も簡単な例は、ガウスフィルタ

ここで出力画像ピクセルはI(x,y)だけでなく、周囲の8ピクセルにも依存します。

O(x,y) = (I(x,y)+ surrounding 8 pixels values)/9; 

通常、これは単一のイメージバッファでは実行できません。また、入力ピクセルはフィルタが実行されると変更されます。どのようにシェーダでこれを行うことができますか?また、私は自分自身で国境を処理する必要がありますか?または、I(-1,-1)のような無効なピクセルアクセスをチェックする組み込み関数または何かがありますか?事前に

おかげ

PS:私は寛大になります(読み:ポイントの多くを与える);)

答えて

8

9-ヒットガウスぼかしwas presented by Daniel Rákosを実行するための高度に最適化されたシェーダベースのアプローチ。彼のプロセスでは、ハードウェアのテクスチャフィルタリングによって提供される基礎となる補間を使用して、1回のパスにつきわずか5回のテクスチャ読み取りを使用して9ヒットフィルタを実行します。これはまた、別々の水平パスと垂直パスに分割され、必要なテクスチャ読み取りの数をさらに減らします。

GPUImageFastBlurFilterクラスの下で、OpenGL ESとiOS GPU用に調整された実装をmy image processing frameworkにロールオーバーしました。私のテストでは、iPhone 4で2.0 msで640 x 480のフレームを1回ブラーすることができますが、これはかなり高速です。

Iは、次の頂点シェーダ使用:これを実行する

precision highp float; 

uniform sampler2D inputImageTexture; 

varying mediump vec2 centerTextureCoordinate; 
varying mediump vec2 oneStepLeftTextureCoordinate; 
varying mediump vec2 twoStepsLeftTextureCoordinate; 
varying mediump vec2 oneStepRightTextureCoordinate; 
varying mediump vec2 twoStepsRightTextureCoordinate; 

// const float weight[3] = float[](0.2270270270, 0.3162162162, 0.0702702703); 

void main() 
{ 
    lowp vec3 fragmentColor = texture2D(inputImageTexture, centerTextureCoordinate).rgb * 0.2270270270; 
    fragmentColor += texture2D(inputImageTexture, oneStepLeftTextureCoordinate).rgb * 0.3162162162; 
    fragmentColor += texture2D(inputImageTexture, oneStepRightTextureCoordinate).rgb * 0.3162162162; 
    fragmentColor += texture2D(inputImageTexture, twoStepsLeftTextureCoordinate).rgb * 0.0702702703; 
    fragmentColor += texture2D(inputImageTexture, twoStepsRightTextureCoordinate).rgb * 0.0702702703; 

    gl_FragColor = vec4(fragmentColor, 1.0); 
} 

attribute vec4 position; 
attribute vec2 inputTextureCoordinate; 

uniform mediump float texelWidthOffset; 
uniform mediump float texelHeightOffset; 

varying mediump vec2 centerTextureCoordinate; 
varying mediump vec2 oneStepLeftTextureCoordinate; 
varying mediump vec2 twoStepsLeftTextureCoordinate; 
varying mediump vec2 oneStepRightTextureCoordinate; 
varying mediump vec2 twoStepsRightTextureCoordinate; 

void main() 
{ 
    gl_Position = position; 

    vec2 firstOffset = vec2(1.3846153846 * texelWidthOffset, 1.3846153846 * texelHeightOffset); 
    vec2 secondOffset = vec2(3.2307692308 * texelWidthOffset, 3.2307692308 * texelHeightOffset); 

    centerTextureCoordinate = inputTextureCoordinate; 
    oneStepLeftTextureCoordinate = inputTextureCoordinate - firstOffset; 
    twoStepsLeftTextureCoordinate = inputTextureCoordinate - secondOffset; 
    oneStepRightTextureCoordinate = inputTextureCoordinate + firstOffset; 
    twoStepsRightTextureCoordinate = inputTextureCoordinate + secondOffset; 
} 

、次フラグメントシェーダ。 2つのパスは、texelWidthOffset(垂直パス用)に0の値を送信し、その結果をtexelHeightOffset(水平パス用)に0の値を与える実行に供給することで実現できます。

私はまた、上記リンクされたフレームワークに、Sobelエッジ検出を含むいくつかのより複雑なコンボリューションの例を持っています。

+0

線形補間の利点は、それほど新しいものではありません。いつもより簡単な方法の利点はどこですか?座標[-4、-2、0、2、4]を参照して10ピクセルの範囲を取得し、次に2つのテクセルのそれぞれの重み付けを適用しますか? – djmj

+0

@djmj - 私はあなたに従うことを確信していません。もっと簡単な方法は?あなたが指している2つのテクセルは何ですか?このアプローチについて説明している参考資料がありますか? –

+0

参照:http://drilian.com/journal/images/TextureGrid.png。 (2、0.5)でサンプリングすると、ピクセル(1,0)と(2,0)の双線形補間値が得られます。 (2、1)でサンプリングすると、4つの隣接画素の平均値が得られます!したがって、2ピクセルのオフセットを使用して、ガウス重みを適用してください。 – djmj

2

双線形補間を利用した水平ブラー。垂直ブラーパスはアナログです。展開して最適化します。

//5 offsets for 10 pixel sampling! 
float[5] offset = [-4.0f, -2.0f, 0.0f, 2.0f, 4.0f]; 
//int[5] weight = [1, 4, 6, 4, 1]; //sum = 16 
float[5] weightInverse = [0.0625f, 0.25f, 0.375, 0.25f, 0.0625f]; 

vec4 finalColor = vec4(0.0f); 

for(int i = 0; i < 5; i++) 
    finalColor += texture2D(inputImage, vec2(offset[i], 0.5f)) * weightInverse[i]; 
関連する問題