android-camera2相机开发【8】-使用opengl实现滤镜效果(1)

前一篇文章中,实现了 opengles 进行相机预览的功能,基本的流程如下:

  1. 把相机的预览数据做成纹理,绑定到opengles对应的纹理单元上
  2. 然后通过opengles 的内置函数 texture(),在片段着色器中根据纹理和纹理坐标进行插值计算
  3. 直接将计算结果输出到颜色缓冲区,显示到屏幕的像素上。

给图像添加滤镜本质上就是图片处理,也就是对图片的像素进行计算,简单来说,图像处理的方法可以分为三类:

  1. 点算子:当前像素的处理只和自身的像素值有关,和其他像素无关,比如灰度处理。
  2. 邻域算子:当前像素的处理需要和相邻的一定范围内的像素有关,比如高斯模糊。
  3. 全局算子:在全局上对所有像素进行统一变换,比如几何变换。

实现滤镜的思路

滤镜本质上就是对每个位置的颜色值进行调整,比如灰度效果,就是将彩色图像的各个颜色分量的值变成一样的。

对颜色值进行调整的时机应该是再上面步骤的2、3之间,这个时候已经拿到了颜色值,还没有输出到颜色缓冲区,这个时候我们对颜色值进行处理就可以实现滤镜效果。

用上一篇的片段着色器代码做个说明:

#version 300 es
#extension GL_OES_EGL_image_external_essl3 : require
precision mediump float;

in vec2 v_texCoord;
out vec4 outColor;
uniform samplerExternalOES s_texture;

void main(){
    //拿到颜色值
    vec4 tmpColor = texture(s_texture, v_texCoord);
    //对颜色值进行处理
    process(tmpColor);
    //将处理后的颜色值输出到颜色缓冲区
    outColor = tmpColor;
}

点算子实现的滤镜

灰度滤镜

灰度滤镜通过图像的灰度化算法进行实现。

在 RBG颜色模型中,让 R=G=B=grey, 即可将彩色图像转为灰度图像,其中 grey 叫做灰度值。

grey的计算方法有四种:分量法,最大值法,平均值法、加权平均法。

分量法

使用彩色图像的某个颜色分量的值作为灰度值。

  • grey=R:R分量灰度图
  • grey=G:G分量灰度图
  • grey=B:B分量灰度图

最大值法

将彩色图像三个颜色分量中值最大的作为灰度值。

grey = max(R,G,B)

平均值法

将彩色图像的三个颜色分量值的平均值作为灰度值

grey = (R+G+B)/3

加权平均法

在 RGB 颜色模型中,人眼对G(绿色)的敏感度最高,对B(蓝色)的敏感的最低,所以对彩色图像的三个颜色分量做加权平均计算灰度值效果比较好。

grey = 0.3R + 0.59G + 0.11*B

下面在片段着色器中用加权平均法实现灰度滤镜。

#version 300 es
#extension GL_OES_EGL_image_external_essl3 : require
precision mediump float;

in vec2 v_texCoord;
out vec4 outColor;
uniform samplerExternalOES s_texture;

//灰度滤镜
void grey(inout vec4 color){
    float weightMean = color.r * 0.3 + color.g * 0.59 + color.b * 0.11;
    color.r = color.g = color.b = weightMean;
}

void main(){
    //拿到颜色值
    vec4 tmpColor = texture(s_texture, v_texCoord);
    //对颜色值进行处理
    grey(tmpColor);
    //将处理后的颜色值输出到颜色缓冲区
    outColor = tmpColor;
}

效果图

原图 灰度滤镜
image
image

黑白滤镜

黑白滤镜就是将图像进行二值化处理,彩色图像的颜色值经过处理之后,要么是 0(黑色),要么是255(白色)。

实际应用中使用的不多,从各大直播、美颜相机、短视频app上就能发现,基本上没有用黑白滤镜,因为不好看。

二值化方法主要有:全局二值化,局部二值化,局部自适应二值化。最影响效果的就是阈值的选取。

  • 全局二值化是选定一个阈值,然后将大于该阈值的颜色值置为255,小于该阈值的颜色置为0。因为使用的全局阈值,所以会丧失很多细节。
  • 局部二值化:为了弥补全局阈值化的缺陷,将图像分为N个窗口,每个窗口设定一个阈值,进行二值化操作,一般取该窗口颜色值的平均值。
  • 局部自适应二值化:局部二值化的阈值选取方法仍然不能很好的将对应窗口的图像进行二值化,在此基础上,通过窗口颜色的平均值E、像素之间的差平方P、像素之间的均方根Q等能够表示窗口内局部特征的参数,设定计算公式计算阈值。

这里使用最简单的全局二值化做个示例

//黑白滤镜
void blackAndWhite(inout vec4 color){
    float threshold = 0.5;
    float mean = (color.r + color.g + color.b) / 3.0;
    color.r = color.g = color.b = mean >= threshold ? 1.0 : 0.0;
}

效果图

原图 黑白滤镜
image
image

反色滤镜

RGB 颜色值的范围是 [0,255],反色滤镜的的原理就是将 255 与当前颜色的每个分量Rs,Gs,Bs值做差运算。

结果颜色为 (R,G,B) = (255 - Rs, 255 - Gs, 255 - Bs);

//反向滤镜
void reverse(inout vec4 color){
    color.r = 1.0 - color.r;
    color.g = 1.0 - color.g;
    color.b = 1.0 - color.b;
}

效果图

原图 反色滤镜
image
image

亮度滤镜

增加亮度有两种方法:

  1. 再 rgb 颜色空间下,将各个颜色分量都加上一个值,可以达到图像亮度增加的目的,但是这种方式会导致图像一定程度上偏白。
  2. 将颜色值从 rgb 颜色空间转换到 hsl 颜色空间上,因为 hsl 更适合视觉上的描述,色相、饱和度、亮度,调整 l(亮度分量),即可实现图像的亮度处理,然后将调整后的 hsl 值再转换到 rgb 颜色空间上进行输出。

各颜色空间转换方法

下面以第2中方式为例:

//rgb转hsl
vec3 rgb2hsl(vec3 color){
    vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
    vec4 p = mix(vec4(color.bg, K.wz), vec4(color.gb, K.xy), step(color.b, color.g));
    vec4 q = mix(vec4(p.xyw, color.r), vec4(color.r, p.yzx), step(p.x, color.r));

    float d = q.x - min(q.w, q.y);
    float e = 1.0e-10;
    return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
}

//hsla转rgb
vec3 hsl2rgb(vec3 color){
    vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
    vec3 p = abs(fract(color.xxx + K.xyz) * 6.0 - K.www);
    return color.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), color.y);
}

//亮度
void light(inout vec4 color){
    vec3 hslColor = vec3(rgb2hsl(color.rgb));
    hslColor.z += 0.15;
    color = vec4(hsl2rgb(hslColor), color.a);
}
原图 亮度滤镜
image
image

项目github地址

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

推荐阅读更多精彩内容