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产生很多“噪音”:由于蓝/白渐变,整个场景中都有灰色水平条纹。此外,光锥在立方体上产生不需要的图案。由于立方体左半部的光锥,立方体左侧的黑色边缘似乎也逐渐消失。

因此,我阅读了这篇文章,指出应该先对图像进行灰度处理,然后使用高斯模糊滤镜使边缘更明显。在文章的底部,还有似乎可以产生更好结果的精巧边缘检测过滤器。

现在我有两个问题:

  1. 以下步骤是否正确以产生最佳的边缘检测结果:

    • 灰阶
    • 高斯模糊
    • Sobel / Canny边缘检测
  2. 如果是,我将如何合并原始图像和经过处理的图像?我的意思是,在完成上述步骤后,我得到的图像要么是完全黑的,带有白色边缘,要么是反之。如何将边缘放在原始图像/纹理上?

谢谢你的帮助!


我想知道您是否可以通过某种方式利用OpenGL的edge标志来获得所需的结果。在行模式下,看起来仅绘制了带有边标志的顶点之间的边。这里有描述。(我自己没有使用过它,或者我会举一个例子。)
user1118321 2016年

Answers:


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),它将自动用于深度。如果附加一个或多个颜色纹理(带有GL_COLOR_ATTACHMENTi),则可以通过将多个输出声明到片段着色器中来对其进行写入(此操作以前是用gl_FragData;两种方法都可以参见glDrawBuffers)。

有关该主题的更多信息,请查找“多渲染目标”(MRT)。


很好的信息,谢谢朱利安。另一个问题:如何在片段着色器中使用深度或常规信息?我对GLSL还是很陌生,因此不知道如何在Sobel算法中包括这些值。
enne87 '16

2
为渲染过程创建帧缓冲区时,可以在其上附加多个纹理。如果附加深度纹理,则它将自动用于深度。它附加了另一个可以分别写入其中的颜色纹理(请参见opengl.org/sdk/docs/man/html/glDrawBuffers.xhtml),此功能称为“多渲染目标”(MRT)。
Julien Guertault

@ enne87:乐于帮助。:)
Julien Guertault

@trichoplax:感谢您的建议;完成。
Julien Guertault
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.