Фильтр Собеля в C / C ++ с использованием OpenGL ES - PullRequest
4 голосов
/ 08 марта 2012

Я бы предпочел не воссоздавать колесо, если мне не нужно, и это должно было быть сделано раньше.Существуют ли реализации фильтра Собела, использующие OpenGL ES?

1 Ответ

17 голосов
/ 09 марта 2012

Если Objective-C приемлем, вы можете взглянуть на мою GPUImage инфраструктуру и ее GPUImageSobelEdgeDetectionFilter. Это относится к обнаружению краев Собеля с использованием фрагментных шейдеров OpenGL ES 2.0. Вы можете увидеть результат этого в примере «набросок» в этот ответ .

Если вы не хотите копаться в коде Objective-C, критическая работа здесь выполняется двумя наборами шейдеров. На первом этапе я уменьшаю изображение до его яркости и сохраняю это значение в красном, зеленом и синем каналах. Я делаю это, используя следующий вершинный шейдер:

 attribute vec4 position;
 attribute vec4 inputTextureCoordinate;

 varying vec2 textureCoordinate;

 void main()
 {
    gl_Position = position;
    textureCoordinate = inputTextureCoordinate.xy;
 }

и фрагментный шейдер:

 precision highp float;

 varying vec2 textureCoordinate;

 uniform sampler2D inputImageTexture;

 const highp vec3 W = vec3(0.2125, 0.7154, 0.0721);

 void main()
 {
     float luminance = dot(texture2D(inputImageTexture, textureCoordinate).rgb, W);

     gl_FragColor = vec4(vec3(luminance), 1.0);
 }

После этого я фактически выполняю определение краев Собеля (с более светлыми пикселями в этом случае, являющимися краями), используя этот вершинный шейдер:

 attribute vec4 position;
 attribute vec4 inputTextureCoordinate;

 uniform highp float imageWidthFactor; 
 uniform highp float imageHeightFactor; 

 varying vec2 textureCoordinate;
 varying vec2 leftTextureCoordinate;
 varying vec2 rightTextureCoordinate;

 varying vec2 topTextureCoordinate;
 varying vec2 topLeftTextureCoordinate;
 varying vec2 topRightTextureCoordinate;

 varying vec2 bottomTextureCoordinate;
 varying vec2 bottomLeftTextureCoordinate;
 varying vec2 bottomRightTextureCoordinate;

 void main()
 {
     gl_Position = position;

     vec2 widthStep = vec2(imageWidthFactor, 0.0);
     vec2 heightStep = vec2(0.0, imageHeightFactor);
     vec2 widthHeightStep = vec2(imageWidthFactor, imageHeightFactor);
     vec2 widthNegativeHeightStep = vec2(imageWidthFactor, -imageHeightFactor);

     textureCoordinate = inputTextureCoordinate.xy;
     leftTextureCoordinate = inputTextureCoordinate.xy - widthStep;
     rightTextureCoordinate = inputTextureCoordinate.xy + widthStep;

     topTextureCoordinate = inputTextureCoordinate.xy + heightStep;
     topLeftTextureCoordinate = inputTextureCoordinate.xy - widthNegativeHeightStep;
     topRightTextureCoordinate = inputTextureCoordinate.xy + widthHeightStep;

     bottomTextureCoordinate = inputTextureCoordinate.xy - heightStep;
     bottomLeftTextureCoordinate = inputTextureCoordinate.xy - widthHeightStep;
     bottomRightTextureCoordinate = inputTextureCoordinate.xy + widthNegativeHeightStep;
 }

и этот фрагментный шейдер:

 precision highp float;

 varying vec2 textureCoordinate;
 varying vec2 leftTextureCoordinate;
 varying vec2 rightTextureCoordinate;

 varying vec2 topTextureCoordinate;
 varying vec2 topLeftTextureCoordinate;
 varying vec2 topRightTextureCoordinate;

 varying vec2 bottomTextureCoordinate;
 varying vec2 bottomLeftTextureCoordinate;
 varying vec2 bottomRightTextureCoordinate;

 uniform sampler2D inputImageTexture;

 void main()
 {
    float i00   = texture2D(inputImageTexture, textureCoordinate).r;
    float im1m1 = texture2D(inputImageTexture, bottomLeftTextureCoordinate).r;
    float ip1p1 = texture2D(inputImageTexture, topRightTextureCoordinate).r;
    float im1p1 = texture2D(inputImageTexture, topLeftTextureCoordinate).r;
    float ip1m1 = texture2D(inputImageTexture, bottomRightTextureCoordinate).r;
    float im10 = texture2D(inputImageTexture, leftTextureCoordinate).r;
    float ip10 = texture2D(inputImageTexture, rightTextureCoordinate).r;
    float i0m1 = texture2D(inputImageTexture, bottomTextureCoordinate).r;
    float i0p1 = texture2D(inputImageTexture, topTextureCoordinate).r;
    float h = -im1p1 - 2.0 * i0p1 - ip1p1 + im1m1 + 2.0 * i0m1 + ip1m1;
    float v = -im1m1 - 2.0 * im10 - im1p1 + ip1m1 + 2.0 * ip10 + ip1p1;

    float mag = length(vec2(h, v));

    gl_FragColor = vec4(vec3(mag), 1.0);
 }

imageWidthFactor и imageHeightFactor являются просто взаимными величинами входного размера изображения в пикселях.

Вы можете заметить, что этот двухпроходный подход является более сложным, чем в приведенном выше ответе. Это связано с тем, что первоначальная реализация была не самой эффективной при работе на мобильных графических процессорах (по крайней мере, в PowerVR на устройствах iOS). Удалив все зависимые чтения текстур и предварительно рассчитав яркость, чтобы мне пришлось производить выборку только из красного канала в конечном шейдере, этот метод обнаружения настроенных краев в моих тестах в 20 раз быстрее, чем наивный, который делает все это за один проход. 1023 *

...