如果我不需要,我宁愿不重新创建轮子,而且这之前肯定已经完成了。是否有使用 OpenGL ES 的 Sobel 过滤器的任何实现?

最佳答案

如果 Objective-C 是可以接受的,你可以看看我的 GPUImage 框架和它的 GPUImageSobelEdgeDetectionFilter。这应用了使用 OpenGL ES 2.0 片段着色器的 Sobel 边缘检测。您可以在 this answer 的“草图”示例中看到此输出。

如果您不想深入研究 Objective-C 代码,这里的关键工作是由两组着色器执行的。在第一遍中,我将图像降低到其亮度并将该值存储在红色、绿色和蓝色 channel 中。我使用以下顶点着色器执行此操作:

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

之后,我实际上使用此顶点着色器执行 Sobel 边缘检测(在这种情况下,较亮的像素是边缘):
 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);
 }
imageWidthFactorimageHeightFactor 只是输入图像大小(以像素为单位)的倒数。

您可能会注意到,这种两遍方法比上面链接的答案中的方法更复杂。这是因为在移动 GPU(至少是 iOS 设备中的 PowerVR 种类)上运行时,原始实现并不是最有效的。通过删除所有相关纹理读取并预先计算亮度,以便我只需要从最终着色器中的红色 channel 中进行采样,这种经过调整的边缘检测方法在我的基准测试中比一次完成所有这些操作的简单方法快 20 倍。

关于c++ - 使用 OpenGL ES 的 C/C++ 中的 Sobel 过滤器,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/9612153/

10-11 19:42