色彩平衡

修图工具中的色彩平衡一般用来根据亮度等级调整图片中颜色的偏色,调整偏色涉及到加色原理和减色原理



其实我们通过三原色加色原理的图片就可以知道,红色的对比色是青色,蓝色的对比色是黄色,绿色的对比色是品红,这样说可能不太直观,其实我们只要试一下,把图片的r值整体提高,图片会偏红(废话),降低r值,图片会偏青色,g通道和b通道同理,或者,我们用白色(255, 255, 255)减去红色(255, 0, 0),,得到了(0, 255, 255)青色,实际上红色就是青色的反相,在色相环也是正对面的颜色。

为了对三个亮度进行不同程度的偏色调整,我们需要拿到一个颜色每个通道的值对应的三个亮度的权重,举个例子,一个r值为127,那么这个值的阴影调整的权重就应该比较低,中亮调整的权重应该比较高,高亮调整的权重应该比较低,那么我调整中亮的红黄拉杆的时候,对这个颜色的影响是比较大的,如果这个r值是高亮部分的(比如250),那么调整中亮的红黄拉杆对这个颜色的影响就很小(实际上是0),调整高亮的红黄拉杆影响就比较大。





可以看到,调节高亮拉杆对图片影响是比较大的,说明这幅图中r值比较高的像素较多,但是有的区域无论是调整高亮的拉杆还是调整中亮的拉杆都会被影响,这是因为三个亮度的权重并不是你一我零,色彩平衡的调整并不是把像素的值严格按三个等级区分开进行调整,只是说调整的权重有高有低



这个是接下来会讲到的权重的映射,x轴为某个通道的值,红绿蓝三种颜色分别代表阴影,中亮,高亮的权重的变化曲线

GIMP计算权重的代码

static void
color_balance_transfer_init (void)
{
gint i; for (i = 0; i < 256; i++)
{
static const gdouble a = 64, b = 85, scale = 1.785;
gdouble low = CLAMP ((i - b) / -a + .5, 0, 1) * scale;
gdouble mid = CLAMP ((i - b) / a + .5, 0, 1) *
CLAMP ((i + b - 255) / -a + .5, 0, 1) * scale; shadows[i] = low;
midtones[i] = mid;
highlights[255 - i] = low;
}
}

这段代码是GIMP里生成三种亮度等级的权重映射,实际上映射出来的权重我们也可以用GIMP的公式单独计算出来

GLSL计算权重的代码

vec3 transfer(float value)
{
const float a = 64.0, b = 85.0, scale = 1.785;
vec3 result;
float i = value * 255.0;
float shadows = clamp ((i - b) / -a + 0.5, 0.0, 1.0) * scale;
float midtones = clamp ((i - b) / a + 0.5, 0.0, 1.0) * clamp ((i + b - 255.0) / -a + .5, 0.0, 1.0) * scale;
float highlights = clamp (((255.0 - i) - b) / -a + 0.5, 0.0, 1.0) * scale;
result.r = shadows;
result.g = midtones;
result.b = highlights;
return result;
}

该映射的曲线



红色曲线为阴影的权重映射

绿色曲线为中亮的权重映射

蓝色曲线为高亮的权重映射

计算的时候将每个通道的值加上这个通道的三种亮度的权重乘上对应的拉杆值就行了

最后如果要保持亮度的话就调用一下rgb与hsl的转换函数就好,拉杆的取值范围是[-100, 100]

代码:

precision mediump float;
varying mediump vec2 textureCoordinate;
uniform sampler2D inputImageTexture;
uniform float cyan_red_shadow;
uniform float cyan_red_midtones;
uniform float cyan_red_highlights; uniform float magenta_green_shadow;
uniform float magenta_green_midtones;
uniform float magenta_green_highlights; uniform float yellow_blue_shadow;
uniform float yellow_blue_midtones;
uniform float yellow_blue_highlights; vec3 transfer(float value)
{
const float a = 64.0, b = 85.0, scale = 1.785;
vec3 result;
float i = value * 255.0;
float shadows = clamp ((i - b) / -a + 0.5, 0.0, 1.0) * scale;
float midtones = clamp ((i - b) / a + 0.5, 0.0, 1.0) * clamp ((i + b - 255.0) / -a + .5, 0.0, 1.0) * scale;
float highlights = clamp (((255.0 - i) - b) / -a + 0.5, 0.0, 1.0) * scale;
result.r = shadows;
result.g = midtones;
result.b = highlights;
return result;
} 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 main()
{
vec4 base = texture2D(inputImageTexture, textureCoordinate);
vec3 hsl = rgb2hsl(base.rgb);
vec3 weight_r = transfer(base.r);
vec3 weight_g = transfer(base.g);
vec3 weight_b = transfer(base.b);
vec3 color = vec3(base.rgb * 255.0);
color.r += cyan_red_shadow * weight_r.r;
color.r += cyan_red_midtones * weight_r.g;
color.r += cyan_red_highlights * weight_r.b; color.g += magenta_green_shadow * weight_g.r;
color.g += magenta_green_midtones * weight_g.g;
color.g += magenta_green_highlights * weight_g.b; color.b += yellow_blue_shadow * weight_b.r;
color.b += yellow_blue_midtones * weight_b.g;
color.b += yellow_blue_highlights * weight_b.b; color.r = clamp(color.r, 0.0, 255.0);
color.g = clamp(color.g, 0.0, 255.0);
color.b = clamp(color.b, 0.0, 255.0); vec3 hsl2 = rgb2hsl(color / 255.0);
hsl2.z = hsl.z; gl_FragColor=vec4(hsl2rgb(hsl2), base.a);
}

GLSL 参考GIMP源码实现色彩平衡调节的更多相关文章

  1. list源码4(参考STL源码--侯捷):transfer、splice、merge、reverse、sort

    list源码1(参考STL源码--侯捷):list节点.迭代器.数据结构 list源码2(参考STL源码--侯捷):constructor.push_back.insert list源码3(参考STL ...

  2. list源码1(参考STL源码--侯捷):list节点、迭代器、数据结构

    list源码1(参考STL源码--侯捷):list节点.迭代器.数据结构 list源码2(参考STL源码--侯捷):constructor.push_back.insert list源码3(参考STL ...

  3. list源码2(参考STL源码--侯捷):constructor、push_back、insert

    list源码1(参考STL源码--侯捷):list节点.迭代器.数据结构 list源码2(参考STL源码--侯捷):constructor.push_back.insert list源码3(参考STL ...

  4. list源码3(参考STL源码--侯捷):push_front、push_back、erase、pop_front、pop_back、clear、remove、unique

    list源码1(参考STL源码--侯捷):list节点.迭代器.数据结构 list源码2(参考STL源码--侯捷):constructor.push_back.insert list源码3(参考STL ...

  5. vector源码3(参考STL源码--侯捷):pop_back、erase、clear、insert

    vector源码1(参考STL源码--侯捷) vector源码2(参考STL源码--侯捷):空间分配.push_back vector源码(参考STL源码--侯捷)-----空间分配导致迭代器失效 v ...

  6. vector源码(参考STL源码--侯捷):空间分配导致迭代器失效

    vector源码1(参考STL源码--侯捷) vector源码2(参考STL源码--侯捷) vector源码(参考STL源码--侯捷)-----空间分配导致迭代器失效 vector源码3(参考STL源 ...

  7. vector源码2(参考STL源码--侯捷):空间分配、push_back

    vector源码1(参考STL源码--侯捷) vector源码2(参考STL源码--侯捷) vector源码(参考STL源码--侯捷)-----空间分配导致迭代器失效 vector源码3(参考STL源 ...

  8. vector源码1(参考STL源码--侯捷):源码

    vector源码1(参考STL源码--侯捷) vector源码2(参考STL源码--侯捷) vector源码(参考STL源码--侯捷)-----空间分配导致迭代器失效 vector源码3(参考STL源 ...

  9. 转换器5:参考Python源码,实现Php代码转Ast并直接运行

    前两个周末写了<手写PHP转Python编译器>的词法,语法分析部分,上个周末卡文了. 访问器部分写了两次都不满意,没办法,只好停下来,参考一下Python的实现.我实现的部分正好和Pyt ...

随机推荐

  1. android 之图片异步加载

    一.概述 本文来自"慕课网" 的学习,只是对代码做一下分析 图片异步加载有2种方式:  (多线程/线程池) 或者 用其实AsyncTask , 其实AsyncTask底层也是用的多 ...

  2. 泛型接口、JAVA API、包装类

    泛型接口就是拥有一个或多个类型参数的接口 语法: public interface 接口名<类型形参>{ 方法名(类型形参 类型形参实例); } 示例: public interface ...

  3. flex布局笔记整理

    flex布局笔记整理 了解-webkit-box 利用postcss进行css代码的向后兼容时,display:flex兼容后的代码常会带有display:-webkit-box. 部分移动端内核较低 ...

  4. 基于STC89C52的oled红外遥控闹钟

    这个红外遥控主要是程序通过对按下的键的键码进行解析,并运行相应的功能代码 一次按键动作的遥控编码信息为 32 位串行二进制码.对于二进制信号“0”,一个脉冲占 1.2ms:对于二进制信号“1”,一个脉 ...

  5. Android Studio 3.1.3填坑之路

      昨天编写程序的时候,遇到了一个非常令人头疼的BUG,如下图:   标题栏和里面的内容都消失了,这对于一个非常在乎排版的软件来说简直就是晴空霹雳,搞了好长时间,终于在今天找到解决方法,原来是升级搞的 ...

  6. Python3 爬虫之 Scrapy 框架安装配置(一)

    博客地址:http://www.moonxy.com 基于 Python 3.6.2 的 Scrapy 爬虫框架使用,Scrapy 的爬虫实现过程请参照本人的另一篇博客:Python3 爬虫之 Scr ...

  7. HttpClient远程接口调用-实名认证

    1.HttpClient远程接口调用 1)用户注册 注册按钮button提交表单时,要return false form表单 <!-- action="http://localhost ...

  8. [MySQL] 02- Optimisation solutions

    前言 一.资源 MySQL 对于千万级的大表要怎么优化? - MySQL - 知乎[方法论] MySQL大表优化方案[一些优化的细节操作] MySQL大表优化方案[一些优化的细节操作] 分布式数据库下 ...

  9. 新手学习FFmpeg - 调用API编写实现多次淡入淡出效果的滤镜

    前面几篇文章聊了聊FFmpeg的基础知识,我也是接触FFmpeg不久,除了时间处理之外,很多高深(滤镜)操作都没接触到.在学习时间处理的时候,都是通过在ffmpeg目前提供的avfilter基础上面修 ...

  10. 公开的免费WebService接口分享,用于做接口练习

    本文转载于 https://cloud.tencent.com/developer/article/1349603 天气预报Web服务,数据来源于中国气象局 Endpoint http://www.w ...