色彩平衡

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



其实我们通过三原色加色原理的图片就可以知道,红色的对比色是青色,蓝色的对比色是黄色,绿色的对比色是品红,这样说可能不太直观,其实我们只要试一下,把图片的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. Winform中怎样根据Name获取同窗体的控件

    场景 在同一个Winform窗体中,点击一个Button按钮时, 获取同窗体的其他控件的属性. 首先需要对要获取的控件赋予Name属性,然后就可以通过Name进行获取. 实现 在Button的点击事件 ...

  2. 前端之JavaScript篇

    一. 简介 javascript是一门动态弱类型的解释性编程语言, 增强页面动画效果,实现页面与用户之间实时动态的交互.  JavaScript有三部分组成: ECMAscript, DOM, BOM ...

  3. 03爬虫-requests模块基础(1)

    requests模块基础 什么是requests模块 requests模块是python中原生基于网络模拟浏览器发送请求模块.功能强大,用法简洁高效. 为什么要是用requests模块 用以前的url ...

  4. 通俗讲解 RESTful

    1 什么是 RESTful 百度一下 RESTful,查到的资料很多都讲得不清楚,看完了都不知道说的是啥,导致很多人对 RESTful 不甚了解.来看一下常见的解释: (1)神一样的描述REST 并不 ...

  5. 【iOS 】把一些不太重要的任务放在空时执行

    -(void)idleNotificationMethod { } -(void)registerForIdleNotification { [[NSNotificationCenter defaul ...

  6. Find the Multiple POJ-1426

    题目链接:Find the Multiple 题目大意 找出一个只由0和1组成的能整除n的数. 思路 所有由0和1组成的数可以看作是某个只由0.1组成的数a经过以下两种变化得到 1.a * 10 2. ...

  7. 【已解决】关于IDEA中 Tomcat 控制台打印日志中文乱码的解决

    在 Idea 上面使用 Tomcat 时,发现控制台打印信息的时候,出行中文乱码问题; 可以通过以下几种解决办法 1:在-Dfile.encoding=UTF-8 在vm中设置编码方式 2.然后从Fi ...

  8. 用Python怎么telnet到网络设备

    0.前言 Telnet协议属于TCP/IP协议族里的一种,对于我们这些网络攻城狮来说,再熟悉不过了,常用于远程登陆到网络设备进行操作,但是,它的缺陷太明显了,就是不安全,信息明文传送,极容易被攻击窃取 ...

  9. Spring 梳理 - javaConfig在App和webApp中的应用

    package com.dxz.demo.configuration; import org.springframework.context.annotation.Configuration; @Co ...

  10. Java8新特性——接口默认方法

    Java 8 新增了接口的默认方法. 简单说,默认方法就是接口可以有实现方法,而且不需要实现类去实现其方法. 我们只需在方法名前面加个default关键字即可实现默认方法. 为什么要有这个特性? 首先 ...