GLSL 参考GIMP源码实现色彩平衡调节
- 2019 年 10 月 18 日
- 笔记
色彩平衡
修图工具中的色彩平衡一般用来根据亮度等级调整图片中颜色的偏色,调整偏色涉及到加色原理和减色原理
其实我们通过三原色加色原理的图片就可以知道,红色的对比色是青色,蓝色的对比色是黄色,绿色的对比色是品红,这样说可能不太直观,其实我们只要试一下,把图片的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); }