使用OpenGL着色器的高斯滤波器

时间:2011-01-26 12:47:30

标签: iphone opengl-es shader

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

但是,Convolutions怎么样?例如,最简单的例子是高斯滤波器,

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

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

通常情况下,使用单个图像缓冲区无法完成此操作,或者输入像素会随着滤镜的执行而改变。如何使用着色器执行此操作?另外,我应该自己处理边框吗?或者有一个内置函数或者像I(-1,-1)那样检查无效像素访问的东西?

提前致谢

PS:我会很慷慨(阅读:给出很多观点);)

2 个答案:

答案 0 :(得分:9)

一种高度优化的基于着色器的方法,用于执行九次高斯模糊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边缘检测。

答案 1 :(得分: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];