OpenGLES滤镜开发汇总 —— 高斯模糊实现以及优化

高斯模糊

关于高斯模糊的基本概念,可以参考以下这篇文章内容:
https://blog.csdn.net/Serious_Tanx/article/details/53366438
这里就不多讲解了

OpenGLES实现高斯模糊

我们看看GPUImage中的高斯模糊是如何实现的:
GPUImage中的高斯模糊是分成横向和纵向进行低通滤波,然后再做混合得到的。
shader如下所示:
vertex shader:

uniform mat4 uMVPMatrix;
attribute vec4 aPosition;
attribute vec4 aTextureCoord;

// 高斯算子大小(3 x 3)
const int GAUSSIAN_SAMPLES = 9;

uniform float texelWidthOffset;
uniform float texelHeightOffset;

varying vec2 textureCoordinate;
varying vec2 blurCoordinates[GAUSSIAN_SAMPLES];

void main()
{
    gl_Position = uMVPMatrix * aPosition;
    textureCoordinate = aTextureCoord.xy;

    int multiplier = 0;
    vec2 blurStep;
    vec2 singleStepOffset = vec2(texelHeightOffset, texelWidthOffset);

    for (int i = 0; i < GAUSSIAN_SAMPLES; i++) {
        multiplier = (i - ((GAUSSIAN_SAMPLES - 1) / 2));
        blurStep = float(multiplier) * singleStepOffset;
        blurCoordinates[i] = aTextureCoord.xy + blurStep;
    }
}

fragment shader:

precision mediump float;
varying highp vec2 textureCoordinate;
uniform sampler2D inputTexture;
const lowp int GAUSSIAN_SAMPLES = 9;
varying highp vec2 blurCoordinates[GAUSSIAN_SAMPLES];

void main()
{
    lowp vec3 sum = vec3(0.0);
   lowp vec4 fragColor=texture2D(inputTexture,textureCoordinate);

    sum += texture2D(inputTexture, blurCoordinates[0]).rgb * 0.05;
    sum += texture2D(inputTexture, blurCoordinates[1]).rgb * 0.09;
    sum += texture2D(inputTexture, blurCoordinates[2]).rgb * 0.12;
    sum += texture2D(inputTexture, blurCoordinates[3]).rgb * 0.15;
    sum += texture2D(inputTexture, blurCoordinates[4]).rgb * 0.18;
    sum += texture2D(inputTexture, blurCoordinates[5]).rgb * 0.15;
    sum += texture2D(inputTexture, blurCoordinates[6]).rgb * 0.12;
    sum += texture2D(inputTexture, blurCoordinates[7]).rgb * 0.09;
    sum += texture2D(inputTexture, blurCoordinates[8]).rgb * 0.05;

    gl_FragColor = vec4(sum, fragColor.a);
}

我们可以看到计算量有点大。vertex shader中,做了9次的偏移坐标运算。这里是否可以节省效率?
答案是可以的,接下来我们讲解如何进行高斯模糊的优化。

高斯模糊的优化

首先我们来看下这两篇文章:
OpenGL(十六)通过 卷积 实现: 边缘混合 、 Blur 和 高斯模糊
iOS 图像处理系列 - 基于GPUImage的滤镜实现及优化

上面两篇文章都有讲到优化的一些思路。尤其是天天P图团队的文章中讲到了如何巧妙实现高斯算子的证明过程。本人思考了一下,在此基础上,进行了进一步的优化。
对于高斯算子来说,对于当前像素来说,一个5x5的高斯算子,左右的偏移值均为2。因此,我们可以得到偏移坐标,在VertexShader中,计算的过程如下:

// 优化后的高斯模糊
uniform mat4 uMVPMatrix;
attribute vec4 aPosition;
attribute vec4 aTextureCoord;

// 高斯算子左右偏移值,当偏移值为2时,高斯算子为5 x 5
const int SHIFT_SIZE = 2;

uniform highp float texelWidthOffset;
uniform highp float texelHeightOffset;

varying vec2 textureCoordinate;
varying vec4 blurShiftCoordinates[SHIFT_SIZE];

void main() {
    gl_Position = uMVPMatrix * aPosition;
    textureCoordinate = aTextureCoord.xy;
    // 偏移步距
    vec2 singleStepOffset = vec2(texelWidthOffset, texelHeightOffset);
    // 记录偏移坐标
    for (int i = 0; i < SHIFT_SIZE; i++) {
        blurShiftCoordinates[i] = vec4(textureCoordinate.xy - float(i + 1) * singleStepOffset,
                                       textureCoordinate.xy + float(i + 1) * singleStepOffset);
    }
}

在得到偏移坐标之后,我们可以就可以在fragment shader中计算高斯算子的像素总和以及平均值了,fragment shader如下:

precision mediump float;
varying vec2 textureCoordinate;
uniform sampler2D inputTexture;
// 高斯算子左右偏移值,当偏移值为2时,高斯算子为5 x 5
const int SHIFT_SIZE = 2;
varying vec4 blurShiftCoordinates[SHIFT_SIZE];
void main() {
    // 计算当前坐标的颜色值
    vec4 currentColor = texture2D(inputTexture, textureCoordinate);
    mediump vec3 sum = currentColor.rgb;
    // 计算偏移坐标的颜色值总和
    for (int i = 0; i < SHIFT_SIZE; i++) {
        sum += texture2D(inputTexture, blurShiftCoordinates[i].xy).rgb;
        sum += texture2D(inputTexture, blurShiftCoordinates[i].zw).rgb;
    }
    // 求出平均值
    gl_FragColor = vec4(sum * 1.0 / float(2 * SHIFT_SIZE + 1), currentColor.a);
}

我们将vertex shader 和 fragment shader 的内容替换掉原来的,可以看到,得到的效果几乎一致。而且计算量进一步减少了。vertex shader 中计算偏移量的过程少了几次,fragment shader 中并不需要每一步都乘上一个权重值,从而减少了乘法的运算次数,使得效率得到的进一步的提升。至于高斯算子的大小我们可以通过改变SHIFT_SIZE来实现,高斯算子的边长 = 2 * SHIFT_SIZE + 1。

上面是我们在高斯模糊算法进行优化的,另外一个问题则是,由于经过高斯模糊处理后,图片的细节已经丢失了,那么对于图像来说,我们可以先对图像缩放到一半之后,再做高斯模糊处理,这样需要处理的数据量就变成了原来的1/4左右。因此,在做高斯模糊的时候我们可以对纹理图像宽高均缩小一倍然后再做处理,此时的效率将会得到更进一步的提升。

详细实现过程,可以参考本人的开源相机项目:
CainCamera
CainCamera的FilterLibrary中有经过优化后的高斯模糊实现。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342