OpenGL GLSL-Sobelエッジ検出フィルター


9

このトピックに関しては、GLSLにSobel Edge Detectionフィルターを正常に実装しました。フィルターのフラグメントシェーダーコードを次に示します。

#version 330 core
in vec2 TexCoords;
out vec4 color;

uniform sampler2D screenTexture;

mat3 sx = mat3( 
    1.0, 2.0, 1.0, 
    0.0, 0.0, 0.0, 
   -1.0, -2.0, -1.0 
);
mat3 sy = mat3( 
    1.0, 0.0, -1.0, 
    2.0, 0.0, -2.0, 
    1.0, 0.0, -1.0 
);

void main()
{
    vec3 diffuse = texture(screenTexture, TexCoords.st).rgb;
    mat3 I;
    for (int i=0; i<3; i++) {
        for (int j=0; j<3; j++) {
            vec3 sample  = texelFetch(screenTexture, ivec2(gl_FragCoord) + ivec2(i-1,j-1), 0 ).rgb;
            I[i][j] = length(sample); 
    }
}

float gx = dot(sx[0], I[0]) + dot(sx[1], I[1]) + dot(sx[2], I[2]); 
float gy = dot(sy[0], I[0]) + dot(sy[1], I[1]) + dot(sy[2], I[2]);

float g = sqrt(pow(gx, 2.0)+pow(gy, 2.0));
color = vec4(diffuse - vec3(g), 1.0);
} 

そして、Sobelエッジ検出を備えた立方体の結果は次のとおりです。

Sobelエッジ検出フィルター付きキューブ

画像を拡大すると、Sobelによって生成された多くの「ノイズ」があることがわかります。青/白のグラデーションのため、シーン全体に灰色の横縞があります。さらに、ライトコーンはキューブに不要なパターンを生成します。立方体の左半分にある光の円錐のため、立方体の左側の黒い縁も消えていくように見えます。

そこで、まず画像をグレースケールにし、エッジをよりはっきりさせるためにガウスぼかしフィルターを使用する必要があると述べたこの記事を読みました。記事の下部には、より良い結果をもたらすと思われるキャニーエッジ検出フィルターもあります。

今私は2つの質問があります:

  1. 次の手順は、最良のエッジ検出結果を生成するために正しいですか?

    • グレースケール
    • ガウスぼかし
    • Sobel / Cannyエッジ検出
  2. はいの場合、元の画像と処理された画像をどのようにマージしますか?つまり、上記の手順を実行した後、完全に黒いエッジと白いエッジまたはその逆のイメージを取得します。元の画像/テクスチャにエッジをどのように配置しますか?

ご協力いただきありがとうございます!


なんらかの方法でOpenGLのエッジフラグを利用することで、希望する結果が得られるかどうか疑問に思います。ラインモードでは、エッジフラグがオンになっている頂点間のエッジのみが描画されるように見えます。ここに説明があります。(私はそれを自分で使用したことはないか、例を投稿します。)
user1118321

回答:


9
  1. 最良の結果は、ユースケースに大きく依存します。また、達成したい効果によっても異なります。Sobelは単なるエッジ検出フィルターです。エッジは入力信号に依存するため、入力信号は自由に選択できます。

    ここでは、カラー画像を入力として使用しており、フィルターは青色のグラデーションでかすかなエッジを正しく検出しますが、立方体のエッジは、その色が背景色から近すぎると中断されます。

    あなたのプログラムは立方体の描画も担当していると思いますので、Sobelフィルターに入力できる他の情報にアクセスできます。たとえば、深度と法線はエッジ検出の良い候補です。照明前のアルベドが使えました。さまざまな入力でテストし、得られる結果に応じて使用する入力を決定します。

  2. エッジ情報を組み合わせる方法に関する質問については、g使用する前に少しフィルタリングすることをお勧めします。次に、それを使用して、元の色と必要なエッジ色の間を補間できます。

    たとえば、次のようなことを試すことができます。

    float g = sqrt(pow(gx, 2.0)+pow(gy, 2.0));

    // Try different values and see what happens
    g = smoothstep(0.4, 0.6, g);

    vec3 edgeColor = vec3(1., 0., 0.2);
    color = vec4(mix(diffuse, edgeColor, g), 1.);

補遺

深度または法線を使用するには、まだ行っていない場合は、テクスチャに保存する必要があります。通常のレンダリングパス用のフレームバッファーを作成するとき、さまざまなテクスチャーをそれにアタッチし(を参照glFramebufferTexture2D)、シーンの色だけでなく他の情報をそれらに書き込むことができます。

デプステクスチャを(をGL_DEPTH_ATTACHMENT使用して)アタッチすると、デプスに自動的に使用されます。1つまたは複数のカラーテクスチャを(を使用してGL_COLOR_ATTACHMENTi)アタッチする場合は、フラグメントシェーダーに複数の出力を宣言することによってそれらに書き込むことができます(これは以前はで行われていましたgl_FragData。どちらかの方法で、を参照してくださいglDrawBuffers)。

このトピックの詳細については、「マルチレンダーターゲット」(MRT)を参照してください。


素晴らしい情報、ジュリアンに感謝します。もう1つの質問:フラグメントシェーダーで深度または通常の情報をどのように使用できますか?私はまだGLSLにかなり慣れていないため、Sobelアルゴリズムに値を含める方法がわかりません。
enne87

2
レンダリングパスのフレームバッファを作成すると、それに複数のテクスチャをアタッチできます。深度テクスチャをアタッチすると、深度に自動的に使用されます。個別に書き込むことができる別のカラーテクスチャ(opengl.org/sdk/docs/man/html/glDrawBuffers.xhtmlを参照)をアタッチすると、「マルチレンダーターゲット」(MRT)と呼ばれる機能になります。
Julien Guertault 2016年

@ enne87:喜んでお手伝いします。:)
Julien Guertault 2016年

@trichoplax:提案ありがとうございます。完了しました。
Julien Guertault 2016年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.