GLSL 参考GIMP源码实现色彩平衡调节
色彩平衡
修图工具中的色彩平衡一般用来根据亮度等级调整图片中颜色的偏色,调整偏色涉及到加色原理和减色原理

其实我们通过三原色加色原理的图片就可以知道,红色的对比色是青色,蓝色的对比色是黄色,绿色的对比色是品红,这样说可能不太直观,其实我们只要试一下,把图片的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源码实现色彩平衡调节的更多相关文章
- list源码4(参考STL源码--侯捷):transfer、splice、merge、reverse、sort
list源码1(参考STL源码--侯捷):list节点.迭代器.数据结构 list源码2(参考STL源码--侯捷):constructor.push_back.insert list源码3(参考STL ...
- list源码1(参考STL源码--侯捷):list节点、迭代器、数据结构
list源码1(参考STL源码--侯捷):list节点.迭代器.数据结构 list源码2(参考STL源码--侯捷):constructor.push_back.insert list源码3(参考STL ...
- list源码2(参考STL源码--侯捷):constructor、push_back、insert
list源码1(参考STL源码--侯捷):list节点.迭代器.数据结构 list源码2(参考STL源码--侯捷):constructor.push_back.insert list源码3(参考STL ...
- 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 ...
- vector源码3(参考STL源码--侯捷):pop_back、erase、clear、insert
vector源码1(参考STL源码--侯捷) vector源码2(参考STL源码--侯捷):空间分配.push_back vector源码(参考STL源码--侯捷)-----空间分配导致迭代器失效 v ...
- vector源码(参考STL源码--侯捷):空间分配导致迭代器失效
vector源码1(参考STL源码--侯捷) vector源码2(参考STL源码--侯捷) vector源码(参考STL源码--侯捷)-----空间分配导致迭代器失效 vector源码3(参考STL源 ...
- vector源码2(参考STL源码--侯捷):空间分配、push_back
vector源码1(参考STL源码--侯捷) vector源码2(参考STL源码--侯捷) vector源码(参考STL源码--侯捷)-----空间分配导致迭代器失效 vector源码3(参考STL源 ...
- vector源码1(参考STL源码--侯捷):源码
vector源码1(参考STL源码--侯捷) vector源码2(参考STL源码--侯捷) vector源码(参考STL源码--侯捷)-----空间分配导致迭代器失效 vector源码3(参考STL源 ...
- 转换器5:参考Python源码,实现Php代码转Ast并直接运行
前两个周末写了<手写PHP转Python编译器>的词法,语法分析部分,上个周末卡文了. 访问器部分写了两次都不满意,没办法,只好停下来,参考一下Python的实现.我实现的部分正好和Pyt ...
随机推荐
- 初玩Docker
Docker 和VM的区别 Docker就是类似于一个打包好的环境,相关的服务都安装在里面,可以直接使用的. VM就相当于另外一套独立的系统,独立的IP,虚拟硬件. 要使用就需要单独构建一套才可以. ...
- mysql之innodb存储引擎介绍
一.Innodb体系架构 1.1.后台线程 后台任务主要负责刷新内存中的数据,保证缓冲池的数据是最近的数据,此外还将修改的数据刷新到文件磁盘,保证在数据库发生异常的情况下Innodb能恢复到正常的运行 ...
- HTML连载37-边框属性(下)、边框练习
一.边框属性 1.连写(分别设置四条边的边框) border-width:上 右 下 左: border-style:上 右 下 左: border-color:上 右 下 左: 注意点: (1)这三 ...
- Django ORM 知识点总结
Query是如何工作的 Django QuerySet是懒执行的,只有访问到对应数据的时候,才会去访问数据库.另外如果你再次读取查询到的数据,将不会触发数据库的访问,而是直接从缓存获取. 比如 # 这 ...
- spring 事务配置方式以及事务的传播性、隔离级别
在前面的文章中总结了spring事务的5中配置方式,但是很多方式都不用而且当时的配置使用的所有参数都是默认的参数,这篇文章就看常用的两种事务配置方式并信息配置事务的传播性.隔离级别.以及超时等问题,废 ...
- git远程仓库常用命令
1. git add . 将工作区的文件推到暂存区: 2. git commit -m " 备注信息" 将暂存区内容提交 ...
- [VB.NET Tips]VB.NET专有的字符串处理函数
.NET Framework类库中含有专门为Visual Basic.NET程序员设计的函数和过程. 这些方法虽然是为VB.NET程序员设计的,但是也可以被.NET Framework上支持的任何语言 ...
- JAVA设计模式-动态代理(Proxy)示例及说明
在Mybatis源码解析,一步一步从浅入深(五):mapper节点的解析文章的最后部分,我们提到了动态代理的概念,下面我们就简单了解一下动态代理. 一,概念 代理设计模式的目的就是在不直接操作对象的前 ...
- Python学习笔记整理总结【Django】:模板语言、分页、Cookie、Session
一.模板语言 1.在前段展示:对象/字典/元组 class Business(models.Model): # id #用默认的自增id列 即:Business中有3列数据(id, caption, ...
- 使用ImageIO.write上传二维码文件时候,提示系统找不到指定路径
报错如图所示: java.io.FileNotFoundException: E:\SF\.metadata\.plugins\org.eclipse.wst.server.core\tmp1\wtp ...