【问题标题】:How can I do these image processing tasks using OpenGL ES 2.0 shaders?如何使用 OpenGL ES 2.0 着色器完成这些图像处理任务?
【发布时间】:2011-08-15 08:48:37
【问题描述】:

如何使用 OpenGL ES 2.0 着色器执行以下图像处理任务?

  • 色彩空间变换(RGB/YUV/HSL/Lab)
  • 图像的漩涡
  • 转换为草图
  • 转换为油画

【问题讨论】:

    标签: image-processing opengl-es glsl opengl-es-2.0


    【解决方案1】:

    我刚刚在我的开源 GPUImage framework 中添加了过滤器,它执行您描述的四个处理任务中的三个(旋转、草图过滤和转换为油画)。虽然我还没有将色彩空间变换作为滤镜,但我确实能够应用矩阵来变换颜色。

    作为这些过滤器的实际应用示例,以下是棕褐色调颜色转换:

    漩涡扭曲:

    草图过滤器:

    最后,油画转换:

    请注意,所有这些过滤器都是在实时视频帧上完成的,除了最后一个过滤器之外,所有过滤器都可以在来自 iOS 设备摄像头的视频上实时运行。最后一个滤镜的计算量非常大,因此即使作为着色器,在 iPad 2 上渲染也需要大约 1 秒左右的时间。

    棕褐色调滤镜基于以下颜色矩阵片段着色器:

     varying highp vec2 textureCoordinate;
    
     uniform sampler2D inputImageTexture;
    
     uniform lowp mat4 colorMatrix;
     uniform lowp float intensity;
    
     void main()
     {
         lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
         lowp vec4 outputColor = textureColor * colorMatrix;
    
         gl_FragColor = (intensity * outputColor) + ((1.0 - intensity) * textureColor);
     }
    

    有一个矩阵

    self.colorMatrix = (GPUMatrix4x4){
            {0.3588, 0.7044, 0.1368, 0},
            {0.2990, 0.5870, 0.1140, 0},
            {0.2392, 0.4696, 0.0912 ,0},
            {0,0,0,0},
        };
    

    漩涡片段着色器基于this Geeks 3D example,代码如下:

     varying highp vec2 textureCoordinate;
    
     uniform sampler2D inputImageTexture;
    
     uniform highp vec2 center;
     uniform highp float radius;
     uniform highp float angle;
    
     void main()
     {
         highp vec2 textureCoordinateToUse = textureCoordinate;
         highp float dist = distance(center, textureCoordinate);
         textureCoordinateToUse -= center;
         if (dist < radius)
         {
             highp float percent = (radius - dist) / radius;
             highp float theta = percent * percent * angle * 8.0;
             highp float s = sin(theta);
             highp float c = cos(theta);
             textureCoordinateToUse = vec2(dot(textureCoordinateToUse, vec2(c, -s)), dot(textureCoordinateToUse, vec2(s, c)));
         }
         textureCoordinateToUse += center;
    
         gl_FragColor = texture2D(inputImageTexture, textureCoordinateToUse );
    
     }
    

    草图过滤器是使用 Sobel 边缘检测生成的,边缘以不同的灰色阴影显示。对此的着色器如下:

     varying highp vec2 textureCoordinate;
    
     uniform sampler2D inputImageTexture;
    
     uniform mediump float intensity;
     uniform mediump float imageWidthFactor; 
     uniform mediump float imageHeightFactor; 
    
     const mediump vec3 W = vec3(0.2125, 0.7154, 0.0721);
    
     void main()
     {
        mediump vec3 textureColor = texture2D(inputImageTexture, textureCoordinate).rgb;
    
        mediump vec2 stp0 = vec2(1.0 / imageWidthFactor, 0.0);
        mediump vec2 st0p = vec2(0.0, 1.0 / imageHeightFactor);
        mediump vec2 stpp = vec2(1.0 / imageWidthFactor, 1.0 / imageHeightFactor);
        mediump vec2 stpm = vec2(1.0 / imageWidthFactor, -1.0 / imageHeightFactor);
    
        mediump float i00   = dot( textureColor, W);
        mediump float im1m1 = dot( texture2D(inputImageTexture, textureCoordinate - stpp).rgb, W);
        mediump float ip1p1 = dot( texture2D(inputImageTexture, textureCoordinate + stpp).rgb, W);
        mediump float im1p1 = dot( texture2D(inputImageTexture, textureCoordinate - stpm).rgb, W);
        mediump float ip1m1 = dot( texture2D(inputImageTexture, textureCoordinate + stpm).rgb, W);
        mediump float im10 = dot( texture2D(inputImageTexture, textureCoordinate - stp0).rgb, W);
        mediump float ip10 = dot( texture2D(inputImageTexture, textureCoordinate + stp0).rgb, W);
        mediump float i0m1 = dot( texture2D(inputImageTexture, textureCoordinate - st0p).rgb, W);
        mediump float i0p1 = dot( texture2D(inputImageTexture, textureCoordinate + st0p).rgb, W);
        mediump float h = -im1p1 - 2.0 * i0p1 - ip1p1 + im1m1 + 2.0 * i0m1 + ip1m1;
        mediump float v = -im1m1 - 2.0 * im10 - im1p1 + ip1m1 + 2.0 * ip10 + ip1p1;
    
        mediump float mag = 1.0 - length(vec2(h, v));
        mediump vec3 target = vec3(mag);
    
        gl_FragColor = vec4(mix(textureColor, target, intensity), 1.0);
     }
    

    最后,使用 Kuwahara 滤镜生成油画外观。这个特殊的过滤器来自Jan Eric Kyprianidis 和他的同事研究人员的杰出工作,如GPU Pro book 中的文章“GPU 上的各向异性 Kuwahara 过滤”中所述。其中的着色器代码如下:

     varying highp vec2 textureCoordinate;
     uniform sampler2D inputImageTexture;
     uniform int radius;
    
     precision highp float;
    
     const vec2 src_size = vec2 (768.0, 1024.0);
    
     void main (void) 
     {
        vec2 uv = textureCoordinate;
        float n = float((radius + 1) * (radius + 1));
    
        vec3 m[4];
        vec3 s[4];
        for (int k = 0; k < 4; ++k) {
            m[k] = vec3(0.0);
            s[k] = vec3(0.0);
        }
    
        for (int j = -radius; j <= 0; ++j)  {
            for (int i = -radius; i <= 0; ++i)  {
                vec3 c = texture2D(inputImageTexture, uv + vec2(i,j) / src_size).rgb;
                m[0] += c;
                s[0] += c * c;
            }
        }
    
        for (int j = -radius; j <= 0; ++j)  {
            for (int i = 0; i <= radius; ++i)  {
                vec3 c = texture2D(inputImageTexture, uv + vec2(i,j) / src_size).rgb;
                m[1] += c;
                s[1] += c * c;
            }
        }
    
        for (int j = 0; j <= radius; ++j)  {
            for (int i = 0; i <= radius; ++i)  {
                vec3 c = texture2D(inputImageTexture, uv + vec2(i,j) / src_size).rgb;
                m[2] += c;
                s[2] += c * c;
            }
        }
    
        for (int j = 0; j <= radius; ++j)  {
            for (int i = -radius; i <= 0; ++i)  {
                vec3 c = texture2D(inputImageTexture, uv + vec2(i,j) / src_size).rgb;
                m[3] += c;
                s[3] += c * c;
            }
        }
    
    
        float min_sigma2 = 1e+2;
        for (int k = 0; k < 4; ++k) {
            m[k] /= n;
            s[k] = abs(s[k] / n - m[k] * m[k]);
    
            float sigma2 = s[k].r + s[k].g + s[k].b;
            if (sigma2 < min_sigma2) {
                min_sigma2 = sigma2;
                gl_FragColor = vec4(m[k], 1.0);
            }
        }
     }
    

    同样,这些都是GPUImage 中的内置过滤器,因此您只需将该框架放入您的应用程序中即可开始在图像、视频和电影上使用它们,而无需接触任何 OpenGL ES。该框架的所有代码都可以在 BSD 许可下使用,如果您想了解它的工作原理或对其进行调整。

    【讨论】:

    • 布拉德·拉尔森,你太棒了
    • @brad larson 如何在单个图像中添加超过 1 个曲线(漩涡),我可以将其设为半圆
    • @Johnykutty - 如果您想将多个漩涡效果应用于图像,您可以简单地将图像连续两次通过该过滤器。如果您想要不同的效果,请使用此代码作为模板创建自定义过滤器,并调整位移计算以匹配您想要的效果。
    • 非常好。就像fwiw一样,您似乎没有在草图着色器中使用 i00 值。 (当我将着色器移植到 Android 时,我将其取出并没有产生不良影响。)
    • @JonShemitz - 是的,我想我几个月前在我使用的实际着色器中把它拉出来了。当前的一个首先进行亮度转换或提取,并且仅在每次读取纹理时对红色通道进行采样,这确实加快了速度。不过,这个读起来更清楚一些。
    【解决方案2】:

    您可以先查看此着色器列表here。如果您想深入了解,我建议您查看here 找到的橙皮书。

    【讨论】:

    • 请注意,这些着色器和橙皮书适用于桌面 OpenGL,而不是 OpenGL ES 2.0。为了使这些着色器在 OpenGL ES 中工作,可能需要进行一些翻译(插入精度限定符、替换一些内置变量等)。
    • @Brad Larson:这是一个很好的说明。有一些很好的资源可以将 OpenGL 移植到 OpenGL ES 2.0,例如 the khronos difference specification
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多