GLSL边缘检测着色器的最高效方法

5

我正在寻找一种快速而有效的边缘检测着色器或边缘网格,用于视频应用。由于这将在移动设备上完成,因此我需要优先考虑性能而不是准确性。无论如何,我都会模糊输出,并且我看到的所有边缘检测算法都倾向于通过比较原始图像和模糊图像来完成。我认为模糊往往会导致最大的性能问题。

我有一个像这样的函数正在工作:

vec4 edge()
{

    float K00 = -1.0;
    float K01 = -2.0;
    float K02 = -1.0;
    float K10 = 0.0;
    float K11 = 0.0;
    float K12 = 0.0;
    float K20 = 1.0;
    float K21 = 2.0;
    float K22 = 1.0;

    vec2 ox = vec2 (0.0,0.0);
    ox[0] = width;
    vec2 oy = vec2 (0.0,0.0);
    oy[1] = height;

    float g00, g01, g02;
    float g10, g11, g12;
    float g20, g21, g22;
    vec4 CC;

    vec2 PP = TextureCoord - oy;
    CC = texture2D(blurredFrame, vec2(PP-ox));
    g00 = getGrey(CC);
    CC = texture2D(blurredFrame, vec2(PP));
    g01 = getGrey(CC);
    CC = texture2D(blurredFrame, vec2(PP+ox));
    g02 = getGrey(CC);


    PP = TextureCoord;
    CC = texture2D(blurredFrame, vec2(PP-ox));
    g10 = getGrey(CC);
    CC = texture2D(blurredFrame, vec2(PP));
    g11 = getGrey(CC);
    CC = texture2D(blurredFrame, vec2(PP+ox));
    g12 = getGrey(CC);

    PP = TextureCoord + oy;
    CC = texture2D(blurredFrame, vec2(PP-ox));
    g20 = getGrey(CC);
    CC = texture2D(blurredFrame, vec2(PP));
    g21 = getGrey(CC);
    CC = texture2D(blurredFrame, vec2(PP+ox));
    g22 = getGrey(CC);

    float sx = 0.0, sy = 0.0;
    sx = sx + g00 * K00;
    sx = sx + g01 * K01;
    sx = sx + g02 * K02;
    sx = sx + g10 * K10;
    sx = sx + g11 * K11;
    sx = sx + g12 * K12;
    sx = sx + g20 * K20;
    sx = sx + g21 * K21;
    sx = sx + g22 * K22;
    sy = sy + g00 * K00;
    sy = sy + g01 * K10;
    sy = sy + g02 * K20;
    sy = sy + g10 * K01;
    sy = sy + g11 * K11;
    sy = sy + g12 * K21;
    sy = sy + g20 * K02;
    sy = sy + g21 * K12;
    sy = sy + g22 * K22;

    float dist = sqrt(sx * sx + sy * sy);

    return dist > threshold ? vec4 (0,0,0,1) : vec4 (1,1,1,1);
}

我看到的所有示例都是这样的,似乎都集中在桌面平台上——要在iPhone或Android设备上获得良好的帧速率太过复杂和昂贵。这将仅用于2D应用程序,速度至关重要。

有什么想法可以使其更加高效,或者可能有更好的替代方案吗?谢谢大家。

2个回答

4

我不确定是否知道其他算法。

但是,我有一些建议:

  1. 不要在比较距离之前进行sqrt()运算,而是与dist^2进行比较。
  2. 查看是否可以优化纹理加载的访问模式。纹理内存访问模式对性能有很大影响。您希望将内存访问尽可能连续(即0、1、2、3等),而不是随机的。
  3. 关闭mip映射,或者使用texture2DLod,在其中手动指定mip映射级别。

谢谢。我想到的另一种方法是从中删除“模糊”代码,并对图像进行缩小后进行模糊处理,然后将该纹理传递给此代码的边缘检测部分。我认为这将加速模糊处理,但使用多个通道会失去一些收益。 - akaru

1

我有几个优化纹理采样的想法:

  1. 在相应系数为零(K1*)的地方无需采样。
  2. 使用texture2DOffset而不是texture2D。它接受常量整数偏移量,使驱动程序能够更有效地预测您的访问模式。
  3. 您正在加权采样。您可以使用内置的线性过滤机制来完成此操作。例如,要在两个相邻的纹理单元中获取样本总和,您可以在它们之间进行线性采样(仅一次),然后将结果乘以2。这种变体排除了先前的建议。

1
谢谢,好的建议。我认为texture2DOffset在opengles上不可用,除非我实现有误。 - akaru
@akaru。看起来你是正确的 - 在GL ES中没有texture*Offset函数。 - kvark

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接