【问题标题】:Gaussian filter with OpenGL Shaders带有 OpenGL 着色器的高斯滤波器
【发布时间】:2011-01-26 12:47:30
【问题描述】:

我正在尝试学习着色器以在我的 iPhone 应用程序中实现一些东西。到目前为止,我已经理解了简单的示例,例如将彩色图像制作为灰度、阈值等。大多数示例都涉及简单的操作,其中处理输入图像像素 I(x,y) 会导致对同一像素的颜色进行简单修改

但是,卷积呢?例如,最简单的例子是高斯滤波器,

其中输出图像像素O(x,y)不仅取决于I(x,y),还取决于周围的8个像素。

O(x,y) = (I(x,y)+ surrounding 8 pixels values)/9;

通常,这不能使用单个图像缓冲区来完成,否则输入像素会随着过滤器的执行而改变。我怎样才能用着色器做到这一点?另外,我应该自己处理边界吗?或者有一个内置函数或检查无效像素访问的东西,如I(-1,-1)

提前致谢

PS:我会很慷慨(阅读:给很多分);)

【问题讨论】:

    标签: iphone opengl-es shader


    【解决方案1】:

    一种高度优化的基于着色器的方法,用于执行九次高斯模糊was presented by Daniel Rákos。他的过程使用硬件中纹理过滤提供的底层插值来执行九次过滤,每次仅使用五个纹理读取。这也分为单独的水平和垂直通道,以进一步减少所需的纹理读取次数。

    我将针对 OpenGL ES 和 iOS GPU 调整的这个实现转入 my image processing framework(在 GPUImageFastBlurFilter 类下)。在我的测试中,它可以在 iPhone 4 上在 2.0 毫秒内执行 640x480 帧的单次模糊处理,这非常快。

    我使用了以下顶点着色器:

     attribute vec4 position;
     attribute vec2 inputTextureCoordinate;
    
     uniform mediump float texelWidthOffset; 
     uniform mediump float texelHeightOffset; 
    
     varying mediump vec2 centerTextureCoordinate;
     varying mediump vec2 oneStepLeftTextureCoordinate;
     varying mediump vec2 twoStepsLeftTextureCoordinate;
     varying mediump vec2 oneStepRightTextureCoordinate;
     varying mediump vec2 twoStepsRightTextureCoordinate;
    
     void main()
     {
         gl_Position = position;
    
         vec2 firstOffset = vec2(1.3846153846 * texelWidthOffset, 1.3846153846 * texelHeightOffset);
         vec2 secondOffset = vec2(3.2307692308 * texelWidthOffset, 3.2307692308 * texelHeightOffset);
    
         centerTextureCoordinate = inputTextureCoordinate;
         oneStepLeftTextureCoordinate = inputTextureCoordinate - firstOffset;
         twoStepsLeftTextureCoordinate = inputTextureCoordinate - secondOffset;
         oneStepRightTextureCoordinate = inputTextureCoordinate + firstOffset;
         twoStepsRightTextureCoordinate = inputTextureCoordinate + secondOffset;
     }
    

    以及以下片段着色器:

     precision highp float;
    
     uniform sampler2D inputImageTexture;
    
     varying mediump vec2 centerTextureCoordinate;
     varying mediump vec2 oneStepLeftTextureCoordinate;
     varying mediump vec2 twoStepsLeftTextureCoordinate;
     varying mediump vec2 oneStepRightTextureCoordinate;
     varying mediump vec2 twoStepsRightTextureCoordinate;
    
    // const float weight[3] = float[]( 0.2270270270, 0.3162162162, 0.0702702703 );
    
     void main()
     {
         lowp vec3 fragmentColor = texture2D(inputImageTexture, centerTextureCoordinate).rgb * 0.2270270270;
         fragmentColor += texture2D(inputImageTexture, oneStepLeftTextureCoordinate).rgb * 0.3162162162;
         fragmentColor += texture2D(inputImageTexture, oneStepRightTextureCoordinate).rgb * 0.3162162162;
         fragmentColor += texture2D(inputImageTexture, twoStepsLeftTextureCoordinate).rgb * 0.0702702703;
         fragmentColor += texture2D(inputImageTexture, twoStepsRightTextureCoordinate).rgb * 0.0702702703;
    
         gl_FragColor = vec4(fragmentColor, 1.0);
     }
    

    执行此操作。这两个通道可以通过为texelWidthOffset(垂直通道)发送一个0值来实现,然后将该结果输入到一个运行中,您为texelHeightOffset(水平通道)提供一个0值。

    我在上面链接的框架中也有一些更高级的卷积示例,包括 Sobel 边缘检测。

    【讨论】:

    • 利用线性插值的优势并不是什么新鲜事。通常更简单的方法的好处在哪里?查找坐标 [-4, -2, 0, 2, 4] 以获得 10 像素范围,然后应用每两个纹素的权重?
    • @djmj - 我不确定我是否关注你。你的意思是什么更简单的方法?你指的两个纹素是什么?您是否有可以指出描述这种方法的参考资料?
    • 见:drilian.com/journal/images/TextureGrid.png。如果在 (2, 0.5) 处采样,您将得到像素 (1, 0) 和 (2, 0) 的双线性插值。在 (2, 1) 处采样,您将得到 4 个相邻像素的平均值!所以只需使用 2 像素偏移,然后应用高斯加权。
    • @djmj - 谢谢,我会再调查一下。
    • @GingerBreadMane - 对于 iOS 设备中基于 PowerVR tile 的延迟渲染器(这里是问题的主题),在顶点着色器中计算它可以避免依赖纹理读取。由于它们缓存纹理提取的方式,这为这些设备提供了巨大的性能提升。
    【解决方案2】:

    利用双线性插值的优势进行水平模糊。垂直模糊通道是模拟的。展开以进行优化。

    //5 offsets for 10 pixel sampling!
    float[5] offset = [-4.0f, -2.0f, 0.0f, 2.0f, 4.0f];
    //int[5] weight = [1, 4, 6, 4, 1]; //sum = 16
    float[5] weightInverse = [0.0625f, 0.25f, 0.375, 0.25f, 0.0625f];
    
    vec4 finalColor = vec4(0.0f);
    
    for(int i = 0; i < 5; i++)
        finalColor += texture2D(inputImage, vec2(offset[i], 0.5f)) * weightInverse[i];
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-10-03
      • 1970-01-01
      • 2013-03-29
      • 2018-01-26
      • 2013-02-01
      • 1970-01-01
      • 2014-10-02
      • 2011-02-15
      相关资源
      最近更新 更多