RGB和HSL
RGB是我们熟悉的颜色表示方法,或许是最常见的表示方式。它由Red,Green,Blue三个颜色的按比例混合来表示我们能够感知的颜色。实际上这也是人眼感知颜色的模式,人眼有三种感光元素,分别能够感知三种不同的颜色(Red,Green,Blue),然后在视网膜叠加形成最终的颜色。

RGB的表示方式虽然模拟了人对色彩的感受形式,但是这种加性的颜色模型却在做图像处理的时候不是特别方便。举例来说我们有个桔色,它的值分别为217,118,33,当我们想得到这个颜色一半的鲜艳度时,我们需要RGB的值分别为186,132,92。这从它原本颜色并不能通过简单的加减来实现,非常的不直观,如下图所示。


HUE
Hue通常就是我们所说的颜色,区分某个颜色和另外一个颜色不同,取值是0到360。可以想象我们把所有的颜色围城一圈,首先是红色,然后依次是黄色,橙色,绿色,蓝色,最后又回到红色,所以称之为色度。

Saturation
Saturation是饱和度的意思,它代表的是这个颜色的纯度是多少,按照百分比取值从0%到100%。其中0%的饱和度定义为黑色。

Lightness
Lightness表示的是亮度,它表示这个颜色的亮度值是多少,取值从0~100%。

RGB和HSL互相转换
HSL是RGB的另一种颜色表示形式,而且他们可以进行互相转换,也就是可以从一个RGB得到它对于的HSL,反之亦然。
RGB转换为HSL
首先对于一个取值为0~255的RGB的值,我们归一化到0~1,并且计算它最大值和最小值的差:
R' = R/255
G' = G/255
B' = B/255
Cmax = max(R', G', B')
Cmin = min(R', G', B')
Δ = Cmax - Cmin
然后可以得到,Hue的计算方式为


L = (Cmax + Cmin) / 2
HSL转换为RGB
对于HSL的取值H为0~360,S和L取值为0~1.
C = (1 - |2L - 1|) × S
X = C × (1 - |(H / 60°) mod 2 - 1|)
m = L - C/2

(R,G,B) = ((R'+m)×255, (G'+m)×255,(B'+m)×255)
饱和度调整OpenGL实现
根据上面想要单独调整饱和度的算法,比如把整副图片的饱和度减半,实际上的思路就是先根据RGB计算HSL的值,然后调整HSL值中的Saturation,在把HSL还原为RGB。整个代码如下。
rgb2hsl
vec3 rgb2hsl(vec3 color) {
float h, s, l;
float r = color.r;
float g = color.g;
float b = color.b;
float Cmax = max(max(r, g), b);
float Cmin = min(min(r, g), b);
l = (Cmax + Cmin) / 2.0;
if (Cmax == Cmin) {
h = 0.0;
s = 0.0;
} else {
float delta = Cmax - Cmin;
s = (l > 0.5) ? delta / (2.0 - l * 2.0) : delta / (l * 2.0);
if (r > g && r > b) {
h = (g - b) / delta + ((g < b) ? 6.0 : 0.0);
} else if (g > b) {
h = (b - r) / delta + 2.0;
} else {
h = (r - g) / delta + 4.0;
}
h = h / 6.0;
}
return vec3(h, s, l);
}
hsl2rgb
float hue2rgb(float M1, float M2, float hue) {
float c;
if (hue < 0.0) {
hue += 1.0;
} else if (hue > 1.0) {
hue -= 1.0;
}
if ((6.0 * hue) < 1.0) {
c = (M1 + (M2 - M1) * hue * 6.0);
} else if ((2.0 * hue) < 1.0) {
c = M2;
} else if ((3.0 * hue) < 2.0) {
c = (M1 + (M2 - M1) * ((2.0/3.0) - hue) * 6.0);
} else {
c = M1;
}
return c;
}
vec3 hsl2rgb(vec3 hsl) {
float M1, M2;
float hue = hsl.x;
float saturation = hsl.y;
float lightness = hsl.z;
vec3 color;
if (saturation == 0.0) {
color.r = lightness;
color.g = lightness;
color.b = lightness;
} else {
if (lightness < 0.5) {
M2 = lightness * (1.0 + saturation);
} else {
M2 = lightness + saturation - lightness * saturation;
}
M1 = (2.0 * lightness - M2);
color.r = hue2rgb(M1, M2, hue + (1.0/3.0));
color.g = hue2rgb(M1, M2, hue);
color.b = hue2rgb(M1, M2, hue - (1.0/3.0));
}
return color;
}
单独调整Saturation
void main() {
vec4 textureColor = texture(u_texture, TexCoord);
vec3 hsl = rgb2hsl(textureColor.rgb);
hsl.y *= 0.5;
vec3 color = hsl2rgb(hsl);
FragColor = vec4(color, 1.0);
}
另外一种饱和度调整思路
实际上根据饱和度的定义,饱和度就算描述的颜色的纯度值,所以这里还有另外一种非常简单的调整图像饱和度的方式。首先根据每个像素的RGB值计算出它对于的灰度值,也就是Y的值,可以得到这个像素对应的一个灰色vec3(y)。然后饱和度增加和减少就是相应的减去或者增加对应比例的灰色值,实现算法如下。
const mediump vec3 L = vec3(0.2125, 0.7154, 0.0721);
void main() {
vec4 textureColor = texture(u_texture, TexCoord);
float luminance = dot(textureColor.rgb, L);
FragColor = vec4(mix(vec3(luminance), textureColor.rgb, saturation), textureColor.w);
}
下面分别是原图和上述两种方式饱和度降低一半的效果图。



参考
en.wikipedia.org/wiki/RGB_co… en.wikipedia.org/wiki/HSL_an… purple11.com/basics/hue-… www.rapidtables.com/convert/col… www.rapidtables.com/convert/col… github.com/jseidelin/w…