色彩平衡
修圖工具中的色彩平衡一般用來根據亮度等級調整圖片中顏色的偏色,調整偏色涉及到加色原理和減色原理
其實我們通過三原色加色原理的圖片就可以知道,紅色的對比色是青色,藍色的對比色是黃色,綠色的對比色是品紅,這樣說可能不太直觀,其實我們只要試一下,把圖片的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);
}