4、HSV色彩空间:色调(H)、饱和度(S)、明度(V)的含义与取值范围
讲完了RGB,咱们来聊聊HSV。说实话,RGB虽然直观,但在做图像处理时经常让人抓狂。你想想看,要调个颜色稍微亮一点,R、G、B三个值都得跟着变,特别麻烦。
HSV就是来解决这个问题的。它把颜色拆成了三个更符合人类直觉的维度:色调(Hue)、饱和度(Saturation)、明度(Value)。我个人习惯叫它「色相、纯度、亮度」,这样更好记。
4.1 色调(H)—— 颜色是什么
色调说白了就是「这是什么颜色」。红色、绿色、蓝色、紫色……这些都属于色调的范畴。
在OpenCV里,H的取值范围是 0 ~ 179。注意,不是0~360!这是很多新手踩的第一个坑。我记得第一次用OpenCV做颜色检测时,按网上教程设了H=350,结果啥也检测不到,折腾了半天才发现范围不对。
| 颜色 | H值(OpenCV) | H值(常规) |
|---|---|---|
| 红色 | 0 ~ 10 或 170 ~ 179 | 0° ~ 20° 或 340° ~ 360° |
| 橙色 | 11 ~ 25 | 21° ~ 50° |
| 黄色 | 26 ~ 34 | 51° ~ 68° |
| 绿色 | 35 ~ 77 | 69° ~ 154° |
| 青色 | 78 ~ 99 | 155° ~ 198° |
| 蓝色 | 100 ~ 124 | 199° ~ 248° |
| 紫色 | 125 ~ 155 | 249° ~ 310° |
| 粉红/品红 | 156 ~ 179 | 311° ~ 360° |
4.2 饱和度(S)—— 颜色有多纯
饱和度描述的是颜色的「鲜艳程度」。饱和度越高,颜色越纯、越浓烈;饱和度越低,颜色越灰、越接近黑白。
在OpenCV中,S的取值范围是 0 ~ 255。
- S = 0:纯灰色,完全失去色彩
- S = 255:最纯的颜色,比如纯红、纯蓝
- S 在中间值:比如S=128,就是半鲜艳的颜色
举个例子:同样是红色,H=0, S=255, V=255 是刺眼的大红色;而 H=0, S=50, V=255 就是那种发白的粉红色。嗯,这里要注意,饱和度低不等于颜色暗,它只是「不纯」。
4.3 明度(V)—— 颜色有多亮
明度就是颜色的「亮度」。V值越高,颜色越亮;V值越低,颜色越暗,最终变成黑色。
在OpenCV中,V的取值范围也是 0 ~ 255。
- V = 0:纯黑色,不管H和S是什么,看起来都是黑的
- V = 255:最亮的状态
- V 在中间值:比如V=128,就是中等亮度的颜色
这里有个容易混淆的点:明度(V)和亮度(Lightness)不是一回事。HSV的V是取RGB三个通道的最大值,而HSL的L是取平均值。所以HSV的V对高光更敏感,做高光检测时用HSV的V通道效果更好。
- H(0~179):决定颜色种类,红色在两端
- S(0~255):决定颜色纯度,0为灰色,255为纯色
- V(0~255):决定颜色明暗,0为黑色,255为最亮
4.4 为什么用HSV而不是RGB?
这个问题我经常被问到。说白了,RGB的三个通道都跟亮度强相关,你调任何一个值,颜色的「感觉」都会变。而HSV把「颜色是什么」「颜色有多纯」「颜色有多亮」分开了,互不干扰。
举个例子:你想把一张图片里所有红色物体找出来。
- 用RGB:你得判断R > G 且 R > B,还得考虑光照变化,阈值很难定
- 用HSV:直接限定H在0~10或170~179,S和V设个范围就行,光照变化主要影响V,不影响H
我在做交通标志识别项目时,就是用HSV做颜色分割的。红色禁令标志、蓝色指示标志,用H通道一区分,准确率直接拉满。
4.5 代码示例:查看任意像素的HSV值
光说不练假把式。咱们写段代码,看看实际图片里每个像素的HSV值长什么样。
import cv2
import numpy as np
# 创建一个纯红色的图片
img = np.zeros((100, 100, 3), dtype=np.uint8)
img[:] = [0, 0, 255] # BGR格式的纯红色
# 转成HSV
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# 查看第一个像素的值
print("BGR:", img[0, 0]) # 输出: [0 0 255]
print("HSV:", hsv[0, 0]) # 输出: [ 0 255 255]
# 再试试暗红色
img2 = np.zeros((100, 100, 3), dtype=np.uint8)
img2[:] = [0, 0, 128] # 暗红色
hsv2 = cv2.cvtColor(img2, cv2.COLOR_BGR2HSV)
print("暗红色 HSV:", hsv2[0, 0]) # H不变,V变小了
看到了吗?纯红色和暗红色,H值都是0,区别只在V值上。这就是HSV的威力——色调不受光照影响。
cv2.imshow() 分别显示H、S、V三个通道的灰度图,看看哪个通道的对比度最明显。这样能快速确定用哪个通道做阈值分割。
4.6 避坑指南
最后分享几个我踩过的坑:
- H范围别搞错:OpenCV的H是0~179,不是0~360。如果你从别的库迁移代码,记得除以2
- 红色要分段:检测红色时,H<10 和 H>170 都要考虑,别漏了
- S和V的阈值:S太低(<20)时颜色接近灰色,H值已经没意义了;V太低(<30)时接近黑色,H和S也基本失效
- 数据类型:HSV和RGB一样,都是uint8类型,范围0~255。别想着用float,OpenCV默认不认
嗯,掌握了这些,HSV这块基本就稳了。下一节咱们用HSV做个实战项目——颜色物体追踪,到时候你就知道这玩意儿有多好用了。