3、RGB与HSV互转:色调、饱和度、明度的数学映射
好,咱们接着聊。上一节我们把RGB和HSV各自的特点捋清楚了,这一节就来点硬核的——怎么在这两个色彩空间之间来回切换。
说实话,我第一次看这个转换公式的时候,也觉得有点绕。但后来在项目中用多了,发现其实就那么几个关键步骤。你只要抓住「色调是角度、饱和度是纯度、明度是亮度」这个感觉,剩下的就是数学上的映射关系了。
3.1 从RGB到HSV:把立方体掰成圆锥
RGB是个立方体,HSV是个圆锥。转换的过程,说白了就是把立方体里的每个点,映射到圆锥里对应的位置。
我个人习惯先算明度V,再算饱和度S,最后算色调H。这个顺序最顺手。
第一步:求明度V
明度最简单,就是RGB三个通道的最大值:
V = max(R, G, B)
嗯,这里要注意,RGB的值通常归一化到[0, 1]区间。如果你拿到的数据是0-255,记得先除以255。
第二步:求饱和度S
饱和度反映的是「颜色有多纯」。公式是这样的:
S = (V - min(R, G, B)) / V (当V ≠ 0时)
S = 0 (当V = 0时)
为什么V=0时饱和度是0?你想想看,V=0意味着RGB全是0,那就是纯黑色。黑色没有颜色倾向,饱和度自然为0。
第三步:求色调H
色调是最麻烦的,因为它是个角度。公式分三种情况:
如果 V == R:
H = 60° × ((G - B) / (V - min(R,G,B)))
如果 V == G:
H = 60° × (2 + (B - R) / (V - min(R,G,B)))
如果 V == B:
H = 60° × (4 + (R - G) / (V - min(R,G,B)))
算完之后,如果H是负数,加360°让它落在[0°, 360°)区间。
你可能会问:为什么是60°?因为HSV色环上,红、黄、绿、青、蓝、品红这六个主色正好相隔60°。这个设计很巧妙,对吧?
3.2 完整代码示例:RGB转HSV
光讲公式不过瘾,咱们直接上代码。我习惯用Python写,因为NumPy处理图像太方便了:
import numpy as np
def rgb_to_hsv(rgb):
"""
RGB转HSV,输入rgb为[0,1]范围的数组
返回hsv,h在[0,360),s和v在[0,1]
"""
r, g, b = rgb[0], rgb[1], rgb[2]
# 求最大值和最小值
v = max(r, g, b)
min_val = min(r, g, b)
delta = v - min_val
# 计算饱和度
if v == 0:
s = 0
else:
s = delta / v
# 计算色调
if delta == 0:
h = 0 # 无色调
elif v == r:
h = 60 * ((g - b) / delta)
elif v == g:
h = 60 * (2 + (b - r) / delta)
elif v == b:
h = 60 * (4 + (r - g) / delta)
# 确保h在[0, 360)
if h < 0:
h += 360
return np.array([h, s, v])
3.3 从HSV回到RGB:逆映射
有去就有回。HSV转RGB稍微复杂一点,但核心思路是:根据H落在哪个60°区间,确定RGB三个通道的取值关系。
具体步骤是这样的:
- 先把H除以60,得到它落在哪个扇区(0-5)
- 计算中间变量:f = H/60 - floor(H/60)
- 计算三个临时值:p = V×(1-S), q = V×(1-S×f), t = V×(1-S×(1-f))
- 根据扇区查表,得到RGB
查表规则如下:
| 扇区 | H范围 | R | G | B |
|---|---|---|---|---|
| 0 | [0°, 60°) | V | t | p |
| 1 | [60°, 120°) | q | V | p |
| 2 | [120°, 180°) | p | V | t |
| 3 | [180°, 240°) | p | q | V |
| 4 | [240°, 300°) | t | p | V |
| 5 | [300°, 360°) | V | p | q |
你看这个表,其实有规律:每个扇区里,RGB三个值分别是V、某个中间值、p。只是排列顺序不同。记住这个规律,代码写起来就快了。
3.4 HSV转RGB的Python实现
def hsv_to_rgb(hsv):
"""
HSV转RGB,h在[0,360),s和v在[0,1]
返回rgb在[0,1]范围
"""
h, s, v = hsv[0], hsv[1], hsv[2]
if s == 0:
# 饱和度0,就是灰度
return np.array([v, v, v])
# 确定扇区
h_i = int(h / 60) % 6
f = h / 60 - h_i
p = v * (1 - s)
q = v * (1 - s * f)
t = v * (1 - s * (1 - f))
# 查表
if h_i == 0:
return np.array([v, t, p])
elif h_i == 1:
return np.array([q, v, p])
elif h_i == 2:
return np.array([p, v, t])
elif h_i == 3:
return np.array([p, q, v])
elif h_i == 4:
return np.array([t, p, v])
elif h_i == 5:
return np.array([v, p, q])
3.5 实际应用中的选择
讲完了互转,你可能会问:什么时候用RGB,什么时候用HSV?
我个人经验是这样的:
- 图像采集和显示:用RGB,因为硬件原生支持
- 颜色识别和分割:用HSV,因为色调H对光照变化不敏感
- 图像调色:用HSV,调整饱和度和明度比调整RGB三个通道直观得多
- 深度学习预处理:看任务。分类任务用RGB多,但如果是颜色相关的任务(比如车牌识别),HSV往往效果更好
我记得有一次做交通信号灯识别,用RGB死活分不清红灯和橙灯。后来换成HSV,只根据H值范围做判断,准确率直接从82%飙到了96%。这就是色彩空间选择的威力。
好,这一节的内容就到这。公式虽然多,但你只要理解了「色调是角度、饱和度是纯度、明度是亮度」这个本质,剩下的就是查表和套公式的事。下一节我们聊聊色彩空间在图像预处理中的实战技巧。