先来看看一般高斯模糊的原理,二维高斯公式是
使用该公式计算出的高斯核看起来是这样的
假设我们计算kernel大小3x3的高斯模糊,取sigma = 1.5,根据二维高斯函数,就可以计算出这个3x3的权重矩阵。
现在计算某个点的像素值,需要取得它周围其它8个点的像素值,
每个点乘以对应的权重,就得到
将这9个点的值加起来,就是中心点的高斯模糊值了。
但是原始的高斯模糊每个像素都需要做rxr(r是kernel的大小)次乘法再做rxr-1次加法才能得到该像素点的值,不仅是计算量大,而且读取内存的次数也非常大。
其实,二维的高斯模糊可以等价于两个一维高斯的乘积,
GPUImage的Gaussian Blur计算9x9大小的高斯模糊,就采用了这样一种快速的方法,分别计算水平方向和垂直方向的高斯模糊。
先计算水平方向,在vertex shader中获得中心点和中心点左右各4个点的坐标,假设中心点坐标是(x0,y0),blurStep =(0,1),那其他8个点的坐标分别是(x0-4,y0),(x0-3,y0),(x0-2,y0),(x0-1,y0),(x0+1,y0),(x0+2,y0),(x0+3,y0),(x0+4,y0),
void main()
{
gl_Position = position;
textureCoordinate = inputTextureCoordinate.xy;
// Calculate the positions for the blur
int multiplier = 0;
vec2 blurStep;
vec2 singleStepOffset = vec2(texelHeightOffset, texelWidthOffset);
for (int i = 0; i < GAUSSIAN_SAMPLES; i++)
{
multiplier = (i - ((GAUSSIAN_SAMPLES - 1) / 2));
// Blur in x (horizontal)
blurStep = float(multiplier) * singleStepOffset;
blurCoordinates[i] = inputTextureCoordinate.xy + blurStep;
}
}
然后再fragment shader中计算这9个点的加权和,
void main(){
lowp vec3 sum = vec3(0.0);
lowp vec4 fragColor=texture2D(inputImageTexture,textureCoordinate);
sum += texture2D(inputImageTexture, blurCoordinates[0]).rgb * 0.05;
sum += texture2D(inputImageTexture, blurCoordinates[1]).rgb * 0.09;
sum += texture2D(inputImageTexture, blurCoordinates[2]).rgb * 0.12;
sum += texture2D(inputImageTexture, blurCoordinates[3]).rgb * 0.15;
sum += texture2D(inputImageTexture, blurCoordinates[4]).rgb * 0.18;
sum += texture2D(inputImageTexture, blurCoordinates[5]).rgb * 0.15;
sum += texture2D(inputImageTexture, blurCoordinates[6]).rgb * 0.12;
sum += texture2D(inputImageTexture, blurCoordinates[7]).rgb * 0.09;
sum += texture2D(inputImageTexture, blurCoordinates[8]).rgb * 0.05;
gl_FragColor = vec4(sum,fragColor.a);}
计算垂直方向的高斯模糊也是使用这两个shader,不同的是计算水平方向时,texelHeightOffset = 0, texelWidthOffset = 1,计算垂直方向时则正好相反。
在实际取点的时候,并不一定是取相邻点,有一个mBlursize参数可以控制取点的间隔,mBlursize = 1就是取相邻点,mBlursize = 2就是每隔一点取,依次类推。
下面来分析一下这个快速高斯的计算量优势,
假设我们需要计算一个MxM大小图像的高斯模糊,高斯模糊的大小是rxr,那么每个像素点需要进行r^2次乘法和(r^2-1)次加法操作。
而这种快速方法仅需要2*r次乘法和2*(r-1)次加法操作。
一个像素点就可以减少r^2-*r次乘法和r^2-2*r+1次加法,对于整幅图像这个数据再乘以M^2,如果是彩色图像,再乘以3,这样减少的计算量是非常可观的,并且如果需要计算的高斯模糊半径越来,减少的计算量越多。