怎样用 rgb 三元组理解色相、亮度和饱和度?
11 个回答
HSL 空间是把 RGB 空间经过一个非线性变换得到的。在一个空间中改变一个参数,往往导致另一个空间中三个参数都改变,体现出「牵一发而动全身」的效果。
由 RGB 参数计算 HSL 参数的公式如下:
感性认识是这样的:
Hue 叫色相,表示了颜色在色环上的角度。纯红色位于 0 度,纯绿色位于 120 度,纯蓝色位于 240 度。具体计算时,角度主要由 RGB 中最大的那个决定,由次大的那个进行修正。
Lightness 叫亮度,具体计算公式是 RGB 中最大值与最小值的平均值。
Saturation 叫饱和度,表示的是 RGB 三个值的对比有多强烈。其算式中分子 C 是 RGB 中最大值与最小值的差值,但当 L 特别大或特别小的时候,C 的范围有限,为了把它归一化到 [0,1],又除了个与亮度有关的分母。
具体到题主的例子:现在有一个颜色 (0.1, 0.2, 0.5),这是一个比较暗的、略偏绿的蓝色。所以它的色相会略小于 240 度(具体值是 225 度),亮度 L = (0.1 + 0.5) / 2 = 0.3,饱和度分子 C = 0.5 - 0.1 = 0.4,分母为 0.6,所以饱和度为 0.4 / 0.6 ≈ 0.67。
调整色相: 由于亮度、饱和度都只与 RGB 中的最大、最小值相关,所以若要仅调整色相,就要调整绿色 G 的值。增大 G 的值会让色相更偏向绿色,即减小,G = B 时色相最小,为 180 度;反之,减小 G 则会让色相增大,G = R 时色相最大,为 240 度。
调整亮度: 要调整亮度,主要靠调整值最大和最小的 B 和 R,但要注意保持色相和饱和度不变。当 L < 0.5 时,饱和度 S 的公式可以简化为 (M - m) / (M + m)。为了保持饱和度不变,B 和 R 要同比例增减,增时亮度增加,减时亮度降低。为了保持色相不变,G 也要同比例增减。
调整饱和度: 同样,调整饱和度靠的也是 B 和 R。为了保持亮度不变,其中一个增加多少,另一个就要减小多少。增大 B、减小 R 可以让饱和度增加;减小 B、增大 R 可以让饱和度降低。为了保持色相不变,也要调整 G 使它与 B、R 的差值之比与原来相同。
上面的各种调整都要注意不能超过 0 <= R <= G <= B <= 1 的范围。一旦超过,调整方法也会改变。
本答案内容主要参考维基百科页面 HSL and HSV ,也推荐题主阅读一下。
需要注意的是,含有 H、S 两个参数的颜色空间有很多,但由于第三个参数的不同,S 的定义也会有不同。
知友 @王赟 Maigo 的答案很棒,不过,说明的方式更接近数学语言,理解起来相对较难。
下面我用更加「 通俗 」的语言来解释 色相 、 亮度 和 饱和度 ,作为一个补充。
打开 Windows 系统自带的画图软件,在上方的「工具」中选择「编辑颜色」,你就可以看到一个经典的色域图,如下图所示:
通过这个图,我们就可以直观上理解色相、亮度、饱和度了,如下图所示。(需要注意的是,在画图中,色相、饱和度和亮度的范围都是 0~240)
- 色相,简而言之,代表了一个颜色的 RGB 主色。 其中,红色为 0°,绿色为 120°,蓝色为 240°。如下图所示:
- 饱和度,简而言之,代表着一种颜色的「纯度」。饱和度越高,其混浊的部分,也就是偏灰色的成分就越少。
- 亮度,简而言之,代表的一种颜色与「白色」的距离。亮度越高,颜色越白,反之则越黑。
我们知道,通常来讲, RGB 的范围通常是 0~255,那么,我们应该怎样用 RGB 来描绘 色相、饱和度和亮度呢?
下面,我们以 RGB =(60,120,100) 为例,算出这个颜色的 色相、饱和度和亮度吧。
A 色相
在「编辑颜色」界面的首行,从左到右滑动光标,我们会发现,RGB 是以下面的方式变动的:
(255,0,0)(红,0°)—> (255,255,0)(黄,60°)—>(0,255,0)(绿,120°)—>
(0,255,255)(青,180°)—>(0,0,255)(蓝,240°)—>(255,0,255)(品红,300°)
可见:0°、120°、240° 对应光的三原色(红、绿、蓝);
60°、180°、300° 对应颜料三原色(黄、青、品红)。
反映到 RGB 上,我们先找到一个主色,也就是 RGB 中最大的数值。
对于 RGB =(60,120,100):
数值最大的是 G,也就是绿色,所以色相是在 120° 附近。
由于 R=60,B=100,颜色更偏蓝一点,所以色相的范围在 120°~180° 之间。
易见:120° 对应的是 (60,120,60),180° 对应的是 (60,120,120)
于是容易计算:
色相(E)=120°(绿)+60°\cdot\frac{mid(R,G,B)(蓝)-min(R,G,B)(红)}{max(R,G,B)(绿)-min(R,G,B)(红)}
=120°+60°×\frac{100-60}{120-60}=160°
* 如果将 160° 从 (0,360°)映射到 (0,240) 的范围内,那么色相大约是 107。
B 亮度
在讨论「饱和度」前,我们先讨论「亮度」。
上面所提到的 红、黄、绿、青、蓝、品红 六种颜色,它们的亮度都是 0.5,从它们的 RGB 中,我们能发现什么?显然: RGB 中的最大值都是 255,而最小值都是 0;
于是,可以猜到,亮度的定义也非常简洁:
亮度(L)=\frac{1}{2}\cdot\frac{max(R,G,B)+min(R,G,B)}{M}
其中 M 为 RGB 的理论最大值,一般为 255
对于 RGB =(60,120,100):
L=\frac{1}{2}\times\frac{120+60}{255}=0.353
* 如果将 0.353 从(0,1)映射到(0,240),大约为 85。
C 饱和度
相对来说,饱和度的计算会比较复杂,我试图用更直观的方式解释它。
直观感受 :RGB 中最大值和最小值越接近,饱和度越低,反之,饱和度越高。
另外,当我们在「编辑颜色」的顶行移动光标并且调节「亮度」时,会发现一个现象:
当饱和度为最高(240)时,RGB 总有一个为 255 或 0,反之亦然。并且,亮度大于 0.5 时,RGB 中必存在 255,亮度小于 0.5 时,RGB 中必存在 0。
于是,我们可以直观的感受到:「饱和度」公式中,分子应该是类似 max(R,G,B)-min(R,G,B) 之类的表达式,分母应该包括 max(R,G,B)+min(R,G,B) 以及 M。
由亮度的表达式可知,其为 0.5 时, max(R,G,B)+min(R,G,B) = M,为了让饱和度表达式分母的函数在亮度为 0.5 处出现拐点,表达式应该包含绝对值函数:
|max(R,G,B)+min(R,G,B) - M|
进一步地,数学直觉告诉我,分母的表达式就是:
M- |max(R,G,B)+min(R,G,B) - M|
于是:
饱和度(S)=\frac{max(R,G,B)-min(R,G,B)}{M-|max(R,G,B)+min(R,G,B)-M|}
对于 RGB=(60,120,100):
S=\frac{120-60}{255-|120+60-255|}=0.333
* 如果将 0.333 从(0,1)映射到(0,240),其值为 80。
综上所述,我们从直观上理解了 色相、亮度、和饱和度的含义,并且求得当 RGB =(60,120,100)时,我们算得:
- 色相为 160°(107/240);
- 亮度为 0.353(85/240);
- 饱和度为 0.333(80/240)。
而画图的「编辑颜色」告诉我,我的计算结果是 正确 的。
【总结】
当我们已知 R、G、B 的值时,我们可以将其映射到 色相E、亮度L 和饱和度S。
根据之前的分析,其映射公式如下:
- 色相 E (范围 [0°,360°))
分成 6 段讨论:
1) R≥G≥B 时,E =0°+60°·(G-B)/(R-B)
2) G≥R≥B 时, E =120°-60°·(R-B)/(G-B)
3) G≥B≥R 时, E =120°+60°·(B-R)/(G-R)
4) B≥G≥R 时, E =240°-60°·(G-R)/(B-R)
5) B≥R≥G 时, E =240°+60°·(R-G)/(B-G)
6) R≥B≥G 时, E =360°-60°·(B-G)/(R-G)
- 亮度 L (范围 [0,1])
L=\frac{1}{2}\cdot\frac{max(R,G,B)+min(R,G,B)}{M}
- 饱和度 S (范围 [0,1])
S=\frac{max(R,G,B)-min(R,G,B)}{M-|max(R,G,B)+min(R,G,B)-M|}