Android OpenGL ES - 反相、曝光、对比度、饱和度、色调滤镜

原文链接:https://segmentfault.com/a/1190000037668990


OpenGL ES - 简单滤镜

默认滤镜

vertex shader

attribute vec4 position;

attribute vec4 inputTextureCoordinate;

varying vec2 textureCoordinate;

void main() {

  gl_Position = position;

  textureCoordinate = inputTextureCoordinate.xy;

}

fragment shader

precision mediump float;

varying highp vec2 textureCoordinate;

uniform sampler2D inputImageTexture;

void main()

{

    gl_FragColor = texture2D(inputImageTexture, textureCoordinate);

};

默认的滤镜我们前面也说过了,使用texture2D函数对纹理进行取样

反相

关于图片反相的定义还是很容易理解的,我们已经知道在GL中颜色是用r,g,b,a表示的,r,g,b,a 的范围是0.0f~1.0f,若染色color = vec4(r,g,b,a),则反相的颜色

invert_color = vec4((1.0-color.rgb), color.a);

所以反相的fragment shader 自然而然就是下面了

fragment shader

precision mediump float;

varying highp vec2 textureCoordinate;

uniform sampler2D inputImageTexture;

void main()

{

    lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);

    // textureColor.w其实就是textureColor.a 透明度,这里不参与反相,因为我们透明度本来是1(完全不透明),参与反相的话就是0了,完全就是透明了,

    gl_FragColor = vec4((1.0 - textureColor.rgb), textureColor.w);

};


亮度(luminance)

亮度(luminance)是表示人眼对发光体或被照射物体表面的发光或反射光强度实际感受的物理量,亮度和光强这两个量在一般的日常用语中往往被混淆使用。简而言之,当任两个物体表面在照相时被拍摄出的最终结果是一样亮、或被眼睛看起来两个表面一样亮,它们就是亮度相同。

亮度(明度)反应了色彩的明暗程度,它和色相(H)、饱和度(S)共同构成HSL色彩空间。调整亮度只需要RGB色彩空间里面同时加上一个程度值。

fragment shader

varying highp vec2 textureCoordinate;

uniform sampler2D inputImageTexture;

uniform lowp float brightness;

void main()

{

    lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);

    gl_FragColor = vec4((textureColor.rgb + vec3(brightness)), textureColor.w);

};

这里我们定义了一个uniform lowp float brightness,我们通过界面Seekbar的拖动来动态修改这个值,还记得GLSL中uniform修饰的变量该如何赋值嘛,glUnifrom*系列函数


曝光度(Exposure)

PS

曝光度和亮度的原理基本上是一致的,亮度是全方位的线性增加色值,而曝光度是基于原色值的指数型叠加(红的会更红,绿的会更绿,蓝的会更蓝,白光的会更光)

fragment shader

varying highp vec2 textureCoordinate;

uniform sampler2D inputImageTexture;

uniform lowp float exposure;

void main()

{

    lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);

    gl_FragColor = vec4(textureColor.rgb * pow(2.0, exposure), textureColor.w);// rgb * 2^效果值

};

这里用到了GLSL的内置函数pow进行指数运算

对比度(Contrast)

接触了上面的亮度(luminance)曝光度(Exposure),我们来看一个稍微复杂点的对比度(Contrast)

PS

对比度是画面黑与白的比值,也就是从黑到白的渐变层次。比值越大,从黑到白的渐变层次就越多,从而色彩表现越丰富。

对比度对视觉效果的影响非常关键,一般来说对比度越大,图像越清晰醒目,色彩也越鲜明艳丽;而对比度小,则会让整个画面都灰蒙蒙的。

简单的说,对比度是像素颜色和某个中值的差,它可以让明亮的颜色更明亮,让灰暗的颜色更灰暗。

这里实现个简单的线性对比度算法:

结果=中值差*对比度+中值

fragment shader

varying highp vec2 textureCoordinate;

uniform sampler2D inputImageTexture;

uniform lowp float m_contrast;

void main()

{

    lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);

    gl_FragColor = vec4(((textureColor.rgb - vec3(0.5)) * m_contrast + vec3(0.5)), textureColor.w);

}

饱和度(Saturation)

PS

色彩饱和度:饱和度是指色彩的鲜艳程度,也称色彩的纯度。饱和度取决于该色中含色成分和消色成分(灰色)的比例。含色成分越大,饱和度越大;消色成分越大,饱和度越小。纯的颜色都是高度饱和的,如鲜红,鲜绿。混杂上白色,灰色或其他色调的颜色,是不饱和的颜色,如绛紫,粉红,黄褐等,完全不饱和的颜色根本没有色调,如黑白之间的各种灰色。

概念总结:饱和度 = X·原色 + Y·灰度值,其中(x+y=1)

其中使用Luma算法求算灰度:Gray = R0.2125 + G0.7154 + B*0.0721

fragment shader

varying highp vec2 textureCoordinate;

uniform sampler2D inputImageTexture;

const mediump vec3 luminanceWeighting = vec3(0.2125, 0.7154, 0.0721);

uniform lowp float saturation;

void main()

{

    lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);

    lowp float luminance = dot(textureColor.rgb, luminanceWeighting);

    lowp vec3 greyScaleColor = vec3(luminance);

    gl_FragColor = vec4(mix(greyScaleColor, textureColor.rgb, saturation), textureColor.w);

    // GLSL内置函数 mix(x,y,a) = x*(1-a)+y*a,刚好满足饱和度的公式定义。

}

色相(色调)

我们最后来解释调整图像色调,算是入门以上的图像处理手法。理论部分较多且深,要慢慢理解。先来了解什么是色调,如何定义色调。

PS

色调不是指颜色的性质,是对一幅绘画作品的整体评价。一幅绘画作品虽然可能用了多种颜色,但总体有一种色调,是偏蓝或偏红,是偏暖或偏冷等等。

色彩模型

在理解色调之前,我们先来解释一下色彩模型,数字图像处理当中的三大色彩模型:RGB、HSI、CMYK(注意!这里不是格式,是色彩模型)

(1)最常用的RGB色彩模型。

RGB是依据人眼识别的颜色定义出的空间,可表示大部分颜色。是图像处理中最基本、最常用、面向硬件的颜色空间,是一种光混合的体系。

可以看到RGB颜色模式用三维空间中的一个点表示一种颜色,每个点有三个分量,分别表示红、绿、蓝的亮度值,亮度值限定为【0,1】。在RGB模型的立方体中,原点对应的颜色为黑色,它的三个分量值都为0;距离原点最远的顶点对应的颜色为白色,它的三个分量值都为1。从黑色到白色的灰度值分布在这两个点的连线上,该虚线称为灰度线;立方体的其余各点对应不同的颜色,即三原色红、绿、蓝及其混合色黄、品红、青色。

(2)HSI色彩模型,视觉传输传播使用

HSI色彩空间是从人的视觉系统出发,用色调(Hue)、饱和度(Saturation或Chroma)和亮度 (Intensity或Brightness)来描述色彩。

H——表示颜色的相位角。红、绿、蓝分别相隔120度;互补色分别相差180度,即颜色的类别。

S——表示成所选颜色的纯度和该颜色最大的纯度之间的比率,范围:[0,  1],即颜色的深浅程度。

I——表示色彩的明亮程度,范围:[0, 1],人眼对亮度很敏感!

可以看到HSI色彩空间和RGB色彩空间只是同一物理量的不同表示法,因而它们之间存在着转换关系:HSI颜色模式中的色调使用颜色类别表示,饱和度与颜色的白光光亮亮度刚好成反比,代表灰色与色调的比例,亮度是颜色的相对明暗程度。

(3)CMYK模型,用于印刷品依靠反光的色彩模式

CMYK是一种依靠反光的色彩模式,我们是怎样阅读报纸的内容呢?是由阳光或灯光照射到报纸上,再反射到我们的眼中,才看到内容。它需要有外界光源,如果你在黑暗房间内是无法阅读报纸的。只要在屏幕上显示的图像,就是RGB模式表现的。只要是在印刷品上看到的图像,就是CMYK模式表现的。大多数在纸上沉积彩色颜料的设备,如彩色打印机和复印机,要求输入CMY数据,在内部进行RGB到CMY的转换。

青色Cyan、品红色Magenta、黄色Yellow是光的二次色,是颜料的颜色。而K取的是black最后一个字母,之所以不取首字母,是为了避免与蓝色(Blue)混淆。当红绿蓝三原色被混合时,会产生白色,当混合青色、品红色、黄色三原色时会产生黑色。从理论上来说,只需要CMY三种油墨就足够了,但是由于目前制造工艺还不能造出高纯度的油墨,CMY相加的结果实际是一种暗红色。

色调(Hue)描述的是整体的颜色效果,在三大色彩模型当中,HSI色彩模型是能较好接近直觉概念上的色调,饱和度,亮度的联系。在这方面rgb格式不方便计算,这里建议切换到YIQ色彩空间进行计算

fragment shader

precision highp float;

varying highp vec2 textureCoordinate;

uniform sampler2D inputImageTexture;

uniform mediump float hueAdjust;

const highp vec4 kRGBToYPrime = vec4 (0.299, 0.587, 0.114, 0.0);

const highp vec4 kRGBToI = vec4 (0.595716, -0.274453, -0.321263, 0.0);

const highp vec4 kRGBToQ = vec4 (0.211456, -0.522591, 0.31135, 0.0);

const highp vec4 kYIQToR = vec4 (1.0, 0.9563, 0.6210, 0.0);

const highp vec4 kYIQToG = vec4 (1.0, -0.2721, -0.6474, 0.0);

const highp vec4 kYIQToB = vec4 (1.0, -1.1070, 1.7046, 0.0);

void main ()

{

    // Sample the input pixel

    highp vec4 color = texture2D(inputImageTexture, textureCoordinate);

    // Convert to YIQ

    highp float YPrime = dot (color, kRGBToYPrime);

    highp float I = dot (color, kRGBToI);

    highp float Q = dot (color, kRGBToQ);

    // Calculate the hue and chroma

    highp float hue = atan (Q, I);

    highp float chroma = sqrt (I * I + Q * Q);

    // Make the user's adjustments

    hue += (-hueAdjust);

    // Convert back to YIQ Q = chroma * sin (hue);

    I = chroma * cos (hue);

    // Convert back to RGB

    highp vec4 yIQ = vec4 (YPrime, I, Q, 0.0);

    color.r = dot (yIQ, kYIQToR);

    color.g = dot (yIQ, kYIQToG);

    color.b = dot (yIQ, kYIQToB);

    // Save the result

    gl_FragColor = color;

};

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

推荐阅读更多精彩内容