从GLSL纹理中平滑轮廓检测

时间:2017-11-03 10:33:19

标签: opengl glsl contour smoothing sobel

我想从纹理生成平滑(宽松和连续)轮廓,如下所示:

enter image description here

我目前运行的是一个执行非常基本边缘检测的着色器,最终得到以下结果:

void main(void) {

  vec2 texCoord = vec2(((vProjectedCoords.x / vProjectedCoords.w) + 1.0 ) / 2.0,
            ((vProjectedCoords.y / vProjectedCoords.w) + 1.0 ) / 2.0 );

  float borderWidth = uWidth; // in px
  float step_u = borderWidth * 1.0 / uCanvasWidth;
  float step_v = borderWidth * 1.0 / uCanvasHeight;

  vec4 centerPixel = texture2D(uTextureFilled, texCoord);
  vec4 rightPixel  = texture2D(uTextureFilled, texCoord + vec2(step_u, 0.0));
  vec4 bottomPixel = texture2D(uTextureFilled, texCoord + vec2(0.0, step_v));
  // now manually compute the derivatives
  float _dFdX = length(rightPixel - centerPixel) / step_u;
  float _dFdY = length(bottomPixel - centerPixel) / step_v;

  gl_FragColor.r = max(max(centerPixel.r, rightPixel.r), bottomPixel.r);
  gl_FragColor.g = max(max(centerPixel.g, rightPixel.g), bottomPixel.g);
  gl_FragColor.b = max(max(centerPixel.b, rightPixel.b), bottomPixel.b);
  gl_FragColor.a = max(_dFdX, _dFdY);

  return;

enter image description here

显然生成的轮廓不干净,但即使采用适当的索贝尔过滤也没有提高结果。

我希望获得几乎像素完美的轮廓,如果可能的话,理想情况下也会忽略输入纹理中的噪点。

谢谢!

编辑:可能需要添加一些平滑的步骤,例如: https://www.shadertoy.com/view/4ssSRl

1 个答案:

答案 0 :(得分:2)

根据您的示例图像,您似乎正在处理具有固定背景颜色的图像。这可以简化工作。我不确定第二张图像与第一张图像的关系。它是结果的放大部分吗?

如果我理解正确,你所描述的可以通过“渗出”纹理来实现。以下是步骤:

去除噪音

添加一个预过滤器,用于检测小于N像素的非背景色区域,并将其从源图像中删除。每个纹素应该:

  1. 示例其邻居。如果它们都是背景,请设置为背景颜色并退出。如果N-1个邻居是非背景的,请保留texel并退出。
  2. 对邻居的下一个“响铃”进行抽样并重复此过程,直到满足N-1个非背景邻居或达到“步骤”阈值。
  3. 这种方法和其他方法可能有不同的方法来消除可能适用于您的情况的噪音。另见https://computergraphics.stackexchange.com/questions/3904/is-it-better-to-blur-the-input-or-output-of-an-edge-detection-shader-for-noise-r

    您从ShaderToy引用的平滑步骤直接使用不适用于您的用例的程序线方程。

    轮廓线

    该想法是生成“边缘距离”的地图,其包含对于每个纹素,到最近的非背景纹理像素的距离。应用以下过滤器以从源图像生成另一个纹理。对于每个像素:

    1. 如果相应的纹理元素为非背景,则输出黑色并退出。
    2. 否则,根据所需的边框大小,在适当大小的区域中对输入纹理元素周围的纹素进行采样。将距离/ 255输出到最近的非背景纹理像素。如果未找到,则输出白色。
    3. 最终处理步骤将适用于两种纹理以添加轮廓线。对于每个像素:

      1. 获取(或使用最近纹理过滤的样本)相应的“边缘距离”图像纹理。
      2. 如果“边缘距离”== 0,则按原样从源图像中获取并输出相应的纹理元素。
      3. 如果“边距”== 1.0,则输出背景颜色(或源图像中的相应纹理元素)。
      4. 否则,这是一个边界!乘以255可得到距离最近的非背景纹理像素的距离。如果距离小于所需的边框大小,则输出边框颜色。为获得最佳效果,请使用smoothstep生成平滑轮廓,而不是使用硬边框切断。
      5. 优化性能

        整个过程有许多可能的优化领域。其中,计算着色器和共享内存的智能使用可以显着减少内存带宽的使用。

相关问题