Android OpenGL ES(四)-为平面图添加滤镜

上文Android OpenGL ES(三)-平面图形的最后,我们通过渲染纹理,终于将我们的2D图片渲染到了OpenGL中。这章,我们再接再厉,为我们的纹理添加单独的滤镜效果

上一章加载图片的过程,在这里就不做赘述。

黑白效果

基础分析

之前我们通过YUV数据格式的处理知道,只要保留Y的数据,就是灰度的图片。但是OpenGL中处理的是RGB格式的数据,我们要如何去取得灰度图呢?
我们可以通过公式,计算出新的RGB值,就是灰度的图片了。

浮点算法:Gray=R0.3+G0.59+B*0.11

代码实现

我们的目标已经确定。下面我们需要将片段着色器上的每个像素的RGB值,通过上面的公式计算,装换成我们的灰度值。

更新着色器代码

根据上面的思路,我们需要去改片元着色器。texture_fragment_shader.glsl

precision mediump float;

//在片元着色器这里添加这个 sampler2D 表示我们要添加2D贴图
uniform sampler2D u_TextureUnit;
//定义一个u_ChangeColor,因为颜色的变量是RGB,所以使用vec3
uniform vec3 u_ChangeColor;

varying vec2 v_TextureCoordinates;

void main(){
    //得到2d color
    vec4 nColor=texture2D(u_TextureUnit,v_TextureCoordinates);
   //黑白图片
    float c= nColor.r*u_ChangeColor.r+nColor.g*u_ChangeColor.g+nColor.b*u_ChangeColor.b;
    gl_FragColor = vec4(c,c,c,nColor.a);
}

对比之前的,需要是有如下的修改点:

  • 在GLSL中,颜色是用包含四个浮点的向量vec4表示,四个浮点分别表示RGBA四个通道,取值范围为0.0-1.0。
  • 我们添加了一个uniform的属性u_ChangeColor,这样我们可以传递我们自己的系数给OpenGL
  • 着色器中取样的其实是小单元的RGB色值(图片每个像素的色彩值),我们可以通过计算操作,色彩值进行调整,得到我们想要的想过,最后传递给gl_FragColor就就可以完成对图片的色彩处理了。

更新代码

按照之前的想法,我们需要将我们的公式中的系数传递进入,就可以完成我们的操作了。基于之前的认识,我们知道传递我们的属性uniform给OpenGL的都是通过创建数组,绑定属性,这一套流程。

//0 创建数组
//黑白图片的公式:RGB 按照 0.2989 R,0.5870 G 和 0.1140 B 的比例构成像素灰度值。
float[] grayFilterColorData = {0.299f, 0.587f, 0.114f};

//1 .得到属性的location
uChangeColor = GLES20.glGetUniformLocation(mProgramObjectId, U_CHANGE_COLOR);

//2. 将数组传入
GLES20.glUniform3fv(uChangeColor, 1, grayFilterColorData, 0);

结果

灰度图.png

冷暖色调的处理

与上面的黑白色的处理相似,冷色调的处理就是单一增加蓝色通道的值,暖色调的处理可以增加红绿通道的值。

着色器代码更新

 precision mediump float;

//在片元着色器这里添加这个 sampler2D 表示我们要添加2D贴图
uniform sampler2D u_TextureUnit;
//定义一个u_ChangeColor,因为颜色的变量是RGB,所以使用vec3
uniform vec3 u_ChangeColor;
varying vec2 v_TextureCoordinates;

//modifyColor.将color限制在rgb
void modifyColor(vec4 color){
    color.r=max(min(color.r,1.0),0.0);
    color.g=max(min(color.g,1.0),0.0);
    color.b=max(min(color.b,1.0),0.0);
    color.a=max(min(color.a,1.0),0.0);
}

void main(){
    //得到2d color
    vec4 nColor=texture2D(u_TextureUnit,v_TextureCoordinates);
    //简单色彩处理,冷暖色调、增加亮度、降低亮度等
        vec4 deltaColor=nColor+vec4(u_ChangeColor,0.0);
        modifyColor(deltaColor);
        gl_FragColor=deltaColor;
}

不管是冷色还是暖色。每个像素的颜色都和我们传入的色值相加,产生偏置之后的颜色。同时还要确保颜色的值合法。如果超过最大,或者小于最小,就用极限值表示。

更新代码

还是之前的套路。

  //0.添加数组
  //暖色的颜色。是加强R/G来完成。这里注意的是颜色值在[0,1]之间
  float[] warmFilterColorData = {0.1f, 0.1f, 0.0f};
  //冷色系的调整。简单的就是增加B的分量
  float[] coolFilterColorData = {0.0f, 0.0f, 0.1f};

  //1. 和上面一样,得到属性的位置
          uChangeColor = GLES20.glGetUniformLocation(mProgramObjectId, U_CHANGE_COLOR);

  //2.传递值
//暖色调
            GLES20.glUniform3fv(uChangeColor, 1, warmFilterColorData, 0);
//或者。冷色调
            GLES20.glUniform3fv(uChangeColor, 1, coolFilterColorData, 0);

结果

暖色调

红黄通道增加的结果

暖色调滤镜结果.png

冷色调

蓝色通道增加的结果


冷色调滤镜.png

图片模糊处理

图片模糊处理相对上面的色调处理稍微复杂一点,通常图片模糊处理是采集周边多个点,
然后利用这些点的色彩和这个点自身的色彩进行计算,得到一个新的色彩值作为目标色彩。
模糊处理有很多算法,类似高斯模糊、径向模糊等等。

高斯模糊


最常用的还是高斯模糊。先看一下高斯模糊的原理。

正态分布(又名"高斯分布")用于图像处理。
本质上,它是一种数据平滑技术(data smoothing),适用于多个场合,图像处理恰好提供了一个直观的应用实例。

原理

使用正态分布作为权重分配模式,对周围像素取平均值的方式,就是高斯模糊。

一维正态分布图

在图形上,正态分布是一种钟形曲线,越接近中心,取值越大,越远离中心,取值越小。
计算平均值的时候,我们只需要将"中心点"作为原点,其他点按照其在正态曲线上的位置,分配权重,就可以得到一个加权平均值。

二维的正态分布

上面的正态分布是一维的,图像都是二维的,所以我们需要二维的正态分布。

二维正态分布
计算公式

二维高斯函数:

二维高斯函数.png

有了这个函数 ,就可以计算每个点的权重了。

权重矩阵的计算结果

为了计算权重矩阵,需要设定σ的值。假定σ=1.5,则模糊半径为1的权重矩阵,权重之和等于1,得到最终的权重矩阵。

权重和为1的结果.png
计算高斯模糊

对所有点重复这个过程,就得到了高斯模糊后的图像。如果原图是彩色图片,可以对RGB三个通道分别做高斯模糊。
如果一个点处于边界,周边没有足够的点,怎么办?
一个变通方法,就是把已有的点拷贝到另一面的对应位置,模拟出完整的矩阵。

代码实现
  • 更新着色器。
precision mediump float;

//在片元着色器这里添加这个 sampler2D 表示我们要添加2D贴图
uniform sampler2D u_TextureUnit;

varying vec2 v_TextureCoordinates;

void main(){
    vec4 color = vec4(0.0);
    int coreSize=3;
    int halfSize=coreSize/2;
    float texelOffset = 0.01;
    //创建我们计算好的卷积核
    float kernel[9];
    kernel[6] = 1.0; kernel[7] = 2.0; kernel[8] = 1.0;
    kernel[3] = 2.0; kernel[4] = 4.0; kernel[5] = 2.0;
    kernel[0] = 1.0; kernel[1] = 2.0; kernel[2] = 1.0;
    int index = 0;
    //每一块都进行图像卷积。
    for(int y=0;y<coreSize;y++)
    {
        for(int x = 0;x<coreSize;x++)
        {
            vec4 currentColor = texture2D(u_TextureUnit,v_TextureCoordinates+vec2(float((-1+x))*texelOffset,float((-1+y))*texelOffset));
            color += currentColor*kernel[index];
            index++;
        }
    }
    //归一处理
    color/=16.0;

    gl_FragColor=color;
}

上面着色器。我们是计算好了卷积核,直接在shader内写死应用的。

结果
高斯模糊lena.png

总结

这一小节的内容耗时比较长。其实就是利用OpenGL的shader对图像进行简单的滤镜处理。
从这节我们学习到

  • 图像的颜色的简单处理
  • 图像的高斯模糊。图像卷积。图像滤波等简单的处理

下一章,会回到Android的内容。将OpenGl和Camera结合在一起。通过OpenGl来显示一个预览的画面。

参考

系列文章地址
Android OpenGL ES(一)-开始描绘一个平面三角形
Android OpenGL ES(二)-正交投影
Android OpenGL ES(三)-平面图形
Android OpenGL ES(四)-为平面图添加滤镜
Android OpenGL ES(五)-结合相机进行预览/录制及添加滤镜
Android OpenGL ES(六) - 将输入源换成视频
Android OpenGL ES(七) - 生成抖音照片电影

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念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

推荐阅读更多精彩内容