Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Most efficient method for GLSL edge detection shader

I'm looking for a quick and dirty, very efficient edge detection shader or edge mesh for a video application. Since this will be done on a mobile device, I need something that places performance over accuracy. I'll be blurring the output anyway, and all edge detection algorithms I've seen tend to be done by comparing against a certain threshold an original image and a blurred one. I think it's the blur that tends to cause the most performance issues.

I have a function like this working:

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);
}

All examples I have seen are like this and seem to focus on a desktop platform--too involved and costly to get a decent framerate on an iPhone or Android device. This will be for a 2d application only, and speed is key.

Any ideas to make this more efficient, or perhaps a better alternative? Thanks everyone.

like image 800
akaru Avatar asked Apr 30 '11 01:04

akaru


2 Answers

Not sure if I know of a different algorithm.

But, some suggestions come to mind:

  1. Don't take the sqrt() before doing the compare to dist, instead compare to dist^2
  2. See if you can optimize the access pattern of texture loads. The texture memory access pattern can have a big impact on the performance. You want to keep your memory access as contigious as possible (i.e. 0,1,2,3,...), instead of random.
  3. Turn mip mapping off, or use texture2DLod where you manually specify the mip map level.
like image 131
Himadri Choudhury Avatar answered Nov 19 '22 15:11

Himadri Choudhury


I have a couple of ideas about optimizing the texture samples:

  1. No need to sample where the corresponding coefficient is zero (K1*).
  2. Use texture2DOffset instead of texture2D. It accepts constant integer offsets, allowing the driver to predict your access pattern more effectively.
  3. You are weighting the samples. You can use built-in linear filtering mechanics to do that. For example, to get a sum of samples in two neighbour texels you can sample linearly (only once) between them and multiply the result by 2. This variant excludes previous suggestion.
like image 31
kvark Avatar answered Nov 19 '22 16:11

kvark