1. HSV色彩空间介绍
HSV(Hue, Saturation, Value)是根据颜色的直观特性由A. R. Smith在1978年创建的一种颜色空间, 也称六角锥体模型(Hexcone Model)。在HSV模型中,颜色是由色度(Hue),饱和度(Saturation),明度(Value)共同组成。
HSV 模型显示为圆柱形或圆锥形物体。当它表示为圆锥形物体时,色调由圆锥形的圆形部分表示。圆锥体通常以三维形式表示。饱和度是使用圆锥的半径计算的,值是圆锥的高度。六角锥也可以用来表示 HSV 模型。圆锥模型的优点是它能够在单个对象中表示 HSV 颜色空间。由于计算机接口的二维特性,HSV 的圆锥模型最适合为计算机图形选择颜色。
HSV色彩空间的圆柱模型的应用与圆锥模型类似。计算以类似的方式完成。从理论上讲,圆柱模型是 HSV 颜色空间计算的最准确形式。在实际使用中,降低值时无法区分饱和度和色调。圆柱模型因此失去了相关性,圆锥形状比它更受欢迎。
1.1 色度(Hue)
色度(Hue)即表示颜色,使用角度 ° \degree °度量的,范围为 [ 0 ° , 360 ° ] [0\degree, 360\degree] [0°,360°](逆时针旋转),色度范围如下图所示。
其中:
角度( ° \degree °) | 对应颜色 | 表示 |
---|---|---|
0-60 | 红色 | ■ |
60-120 | 黄色 | ■ |
120-180 | 绿色 | ■ |
180-240 | 青色 | ■ |
240-300 | 蓝色 | ■ |
300-360 | 品红 | ■ |
1.2 饱和度(Saturation)
饱和度(Saturation)表示颜色接近光谱色的程度。一种颜色,可以看成是某种光谱色与白色混合的结果。其中光谱色所占的比例愈大,颜色接近光谱色的程度就愈高,颜色的饱和度也就愈高。其范围是 [ 0 , 1 ] [0, 1] [0,1] (越大色彩越饱和)。
1.3 明度(Value)
明度(Value)表示颜色明亮的程度,对于光源色,明度值与发光体的光亮度有关。其范围是 [ 0 , 1 ] [0, 1] [0,1] (越大色彩越亮)。
1.4 HSV的应用
HSV 颜色空间被广泛用于生成高质量的计算机图形。简单来说,就是用来选择特定图片所需的各种不同颜色。用户可以从色轮中选择图片所需的特定颜色。它根据人类的感知给出颜色。
1.5 HSV的优势
HSV 颜色空间与人类感知颜色的方式非常相似。除 HSL 外,其他模型定义与原色相关的颜色。 HSV 中使用的颜色可以通过人类感知清楚地定义,而 RGB 或 CMYK 并非总是如此。
2. RGB色彩空间转为HSV色彩空间
2.1 OpenCV官方提供的公式
参考OpenCV官方文档:
首先将 R , G , B R, G, B R,G,B分量数值缩放到范围0到1之间(除以255)。 接下来按如下公式进行转换即可(RGB转HSV需要先计算V再计算S最后计算H😂)。
V
=
max
(
R
,
G
,
B
)
V = \max(R,G,B)
V=max(R,G,B)
S
=
{
V
−
min
(
R
,
G
,
B
)
V
if
V
≠
0
0
otherwise
S = \begin{cases} \frac{V - \min(R, G, B)}{V} & \text{if} \ \ \ V \ne 0 \\ 0 & \text{otherwise} \end{cases}
S={VV−min(R,G,B)0if V=0otherwise
H
=
{
60
(
G
−
B
)
/
(
V
−
min
(
R
,
G
,
B
)
)
if
V
=
R
120
+
60
(
B
−
R
)
/
(
V
−
min
(
R
,
G
,
B
)
)
if
V
=
G
240
+
60
(
R
−
B
)
/
(
V
−
min
(
R
,
G
,
B
)
)
if
V
=
B
0
if
R
=
G
=
B
H = \begin{cases} 60(G - B) / (V - \min(R, G, B)) & \text{if} \ \ \ V = R \\ 120 + 60(B - R) / (V - \min(R, G, B)) & \text{if} \ \ \ V = G \\ 240 + 60(R- B) / (V - \min(R, G, B)) & \text{if} \ \ \ V = B \\ 0 & \text{if} \ \ \ R= G = B \end{cases}
H=⎩⎪⎪⎪⎨⎪⎪⎪⎧60(G−B)/(V−min(R,G,B))120+60(B−R)/(V−min(R,G,B))240+60(R−B)/(V−min(R,G,B))0if V=Rif V=Gif V=Bif R=G=B
如果计算的结果<0,那么就加上360。其中:
0
≤
V
≤
1
0
≤
S
≤
1
0
≤
H
≤
360
\begin{aligned} & 0 \le V \le 1 \\ & 0 \le S \le 1 \\ & 0 \le H \le 360 \\ \end{aligned}
0≤V≤10≤S≤10≤H≤360
值得注意的是,上面的公式是针对某个像素点计算的
2.2 例子
假设一个像素点对应的RGB分量为(186, 179, 151),其在HSV色彩空间中对应的数值为:
R
,
G
,
B
=
(
186
,
179
,
151
)
/
255
=
(
0.7294
,
0.7020
,
0.5922
)
\begin{aligned} R,G,B & = (186, 179, 151) / 255 \\ & = (0.7294, 0.7020, 0.5922) \end{aligned}
R,G,B=(186,179,151)/255=(0.7294,0.7020,0.5922)
V
=
max
(
0.7294
,
0.7020
,
0.5922
)
=
0.7294
\begin{aligned} V & = \max(0.7294, 0.7020, 0.5922)\\ & = 0.7294 \end{aligned}
V=max(0.7294,0.7020,0.5922)=0.7294
S
=
{
V
−
min
(
R
,
G
,
B
)
V
if
V
≠
0
0
otherwise
=
0.7294
−
min
(
0.7294
,
0.7020
,
0.5922
)
0.7294
=
0.7294
−
0.5922
0.7294
=
0.1881
\begin{aligned} S & = \begin{cases} \frac{V - \min(R, G, B)}{V} & \text{if} \ \ \ V \ne 0 \\ 0 & \text{otherwise} \end{cases}\\ & =\frac{0.7294 - \min(0.7294, 0.7020, 0.5922)}{0.7294}\\ & =\frac{0.7294 - 0.5922}{0.7294}\\ & = 0.1881 \end{aligned}
S={VV−min(R,G,B)0if V=0otherwise=0.72940.7294−min(0.7294,0.7020,0.5922)=0.72940.7294−0.5922=0.1881
H
=
{
60
(
G
−
B
)
/
(
V
−
min
(
R
,
G
,
B
)
)
if
V
=
R
120
+
60
(
B
−
R
)
/
(
V
−
min
(
R
,
G
,
B
)
)
if
V
=
G
240
+
60
(
R
−
B
)
/
(
V
−
min
(
R
,
G
,
B
)
)
if
V
=
B
0
if
R
=
G
=
B
=
60
(
G
−
B
)
/
(
V
−
min
(
R
,
G
,
B
)
)
=
60
(
0.7020
−
0.5922
)
/
(
0.7294
−
0.5922
)
=
48.0175
\begin{aligned} H & = \begin{cases} 60(G - B) / (V - \min(R, G, B)) & \text{if} \ \ \ V = R \\ 120 + 60(B - R) / (V - \min(R, G, B)) & \text{if} \ \ \ V = G \\ 240 + 60(R- B) / (V - \min(R, G, B)) & \text{if} \ \ \ V = B \\ 0 & \text{if} \ \ \ R= G = B \end{cases}\\ & = 60(G - B) / (V - \min(R, G, B)) \\ & = 60(0.7020 - 0.5922) / (0.7294 - 0.5922) \\ & = 48.0175 \end{aligned}
H=⎩⎪⎪⎪⎨⎪⎪⎪⎧60(G−B)/(V−min(R,G,B))120+60(B−R)/(V−min(R,G,B))240+60(R−B)/(V−min(R,G,B))0if V=Rif V=Gif V=Bif R=G=B=60(G−B)/(V−min(R,G,B))=60(0.7020−0.5922)/(0.7294−0.5922)=48.0175
那么该RGB像素点转换为HSV色彩空间后的 V = 0.7294 , S = 0.1881 , H = 48.0175 V=0.7294,S=0.1881, H=48.0175 V=0.7294,S=0.1881,H=48.0175。
如果 H ≤ 0 H\le0 H≤0,那么 H = H + 360 H = H + 360 H=H+360
3. OpenCV实现RGB转HSV
3.1 函数调用及其注意事项
使用OpenCV将RGB模型图像转成HSV模型图像非常简单,直接使用cv2.cvtColor
函数,在ColorConversionCodes
参数中传入cv2.COLOR_RGB2HSV
参数即可。
需要注意的是,通过OpenCV转HSV后会根据传入的数据类型缩放到不同范围,如果输入的是Uint8
类型的数据(一般读入的图片数据类型都是Uint8
),默认缩放到0到255之间 。 那么对于饱和度和明度(默认0到1之间)而言直接乘以255然后取整即可。对于色度(默认是在0到360之间)由于超出了Uint8数据类型的范围,所以官方储存时是直接除以2即缩放到0到180之间。
- 8-bit图片 ->
Uint8
: V ′ = 255 × V , S ′ = 255 × S , H ′ = H 2 V' = 255\times V, S' = 255 \times S, H' = \frac{H}{2} V′=255×V,S′=255×S,H′=2H - 16-bit图片:当前并不支持😂
- 32-bit图片 ->
float32
:直接原值存储即可
那么对于刚刚讲的示例将(186, 179, 151)RGB模型空间转到HSV模型空间得到是(48.0175, 0.1881, 0.7294),按照刚刚讲的在OpenCV转换后应该是:
H ′ = H 2 → 24.0088 S ′ = 255 × S → 47.9655 V ′ = 255 × V → 185.9970 \begin{aligned} & H' = \frac{H}{2} \rightarrow 24.0088\\ & S' = 255 \times S \rightarrow 47.9655\\ & V' = 255\times V \rightarrow 185.9970 \end{aligned} H′=2H→24.0088S′=255×S→47.9655V′=255×V→185.9970
3.2 例子
https://blog.csdn.net/qq_37541097/article/details/119478023
import cv2
import numpy as np
rgb_point = np.array([186, 179, 151], dtype=np.uint8).reshape((1, 1, 3))
hsv_point = cv2.cvtColor(rgb_point, cv2.COLOR_RGB2HSV)
print(hsv_point) # [[[ 24 48 186]]]
和我们计算的是一样的。
3.3 HSV控制变量
接下来在使用OpenCV来固定色度(Hue),饱和度(Saturation),明度(Value)其中两个变量,渐变剩下一个变量来看看效果。
3.3.1 变量 —— Hue
需要注意的是,在HSV色彩空间中,因为保存方式的原因,所以 H ∈ [ 0 ° , 360 ° ] → [ 0 ° , 180 ° ] H \in [0\degree, 360\degree] \rightarrow [0\degree, 180\degree] H∈[0°,360°]→[0°,180°]。
"""
Hue
"""
import cv2
import numpy as np
# np.tile(a,(x,y,z))表示将数组a在行上重复x次,在列上重复y次,在第三维度重复z次(假设数组a是一维的)
hue = np.tile(np.arange(0, 180, dtype=np.uint8).reshape((1, 180, 1)), (50, 1, 1))
saturation = np.ones((50, 180, 1), dtype=np.uint8) * 255
value = np.ones((50, 180, 1), dtype=np.uint8) * 255
img_hsv = cv2.merge((hue, saturation, value))
img = cv2.cvtColor(img_hsv, cv2.COLOR_HSV2BGR)
img = cv2.resize(img, (720, 100))
cv2.imwrite("./Hue.jpg", img)
cv2.imshow("Hue", img)
cv2.waitKey(0)
3.3.2 变量 —— Saturation
"""
Saturation
"""
import cv2
import numpy as np
hue = np.zeros((100, 256, 1), dtype=np.uint8)
sat = np.tile(np.arange(0, 256, dtype=np.uint8).reshape((1, 256, 1)), (100, 1, 1))
val = np.ones((100, 256, 1), dtype=np.uint8) * 255
img_hsv = cv2.merge((hue, sat, val))
img = cv2.cvtColor(img_hsv, cv2.COLOR_HSV2BGR)
img = cv2.resize(img, (720, 100))
cv2.imwrite("./Saturation.jpg", img)
cv2.imshow("img", img)
cv2.waitKey(0)
3.3.3 变量 —— Value
"""
Value
"""
import cv2
import numpy as np
hue = np.zeros((100, 256, 1), dtype=np.uint8)
sat = np.ones((100, 256, 1), dtype=np.uint8) * 255
val = np.tile(np.arange(0, 256, dtype=np.uint8).reshape((1, 256, 1)), (100, 1, 1))
img_hsv = cv2.merge((hue, sat, val))
img = cv2.cvtColor(img_hsv, cv2.COLOR_HSV2BGR)
img = cv2.resize(img, (720, 100))
cv2.imwrite("./Value.jpg", img)
cv2.imshow("img", img)
cv2.waitKey(0)
4. HSV在数据增强中的应用
https://blog.csdn.net/qq_37541097/article/details/119478023
import cv2
import numpy as np
def augment_hsv(img, h_gain=0.5, s_gain=0.5, v_gain=0.5):
r = np.random.uniform(-1, 1, 3) * [h_gain, s_gain, v_gain] + 1 # random gains
hue, sat, val = cv2.split(cv2.cvtColor(img, cv2.COLOR_BGR2HSV))
dtype = img.dtype # uint8
x = np.arange(0, 256, dtype=np.int16)
lut_hue = ((x * r[0]) % 180).astype(dtype)
lut_sat = np.clip(x * r[1], 0, 255).astype(dtype)
lut_val = np.clip(x * r[2], 0, 255).astype(dtype)
img_hsv = cv2.merge((cv2.LUT(hue, lut_hue), cv2.LUT(sat, lut_sat), cv2.LUT(val, lut_val))).astype(dtype)
aug_img = cv2.cvtColor(img_hsv, cv2.COLOR_HSV2BGR)
return aug_img
- 首先传入图像
img
以及三个超参数h_gain, s_gain, v_gain
。 - 使用
np.random.uniform
对h, s, v
分别随机生成了一个 [ − 1 , 1 ] [-1, 1] [−1,1]之间的随机数,然后分别乘上三个超参数h_gain, s_gain, v_gain
,最后加上1
。
假设h_gain=0.5
那么会在[0.5, 1.5]
之间随机生成一个倍率因子,后面会将所有Hue数值乘上这个倍率。s_gain, v_gain
同理。 - 使用
cv2.cvtColor
函数将传入的图片由BGR格式(OpenCV读取图片的默认格式是BGR)转成HSV,在使用cv2.split
函数将HSV分量分开分别赋值给hue, sat, val
- 分别针对
hue, sat, val
生成对应的Look-Up Table(LUT)查找表(记录变换前后数值的对应表)。
就是将0-255范围内所有的数值都乘以刚刚生成的随机倍率因子构建LUT,后面针对每个元素直接查表无需再计算。
注意,hue范围是在0到180之间的,所以有个取余的操作(%180
),sat和val范围是0到255之间,所以使用np.clip
防止越界。 - 使用
cv2.LUT
方法利用刚刚针对hue, sat, val
生成的Look-Up Table进行变换。变换后使用cv2.merge
方法再将hue, sat, val
分量合并为HSV图像。 - 最后使用
cv2.cvtColor
再将HSV图像转换回BGR图像。
import cv2
import numpy as np
def augment_hsv(img, h_gain=0.5, s_gain=0.5, v_gain=0.5):
r = np.random.uniform(-1, 1, 3) * [h_gain, s_gain, v_gain] + 1 # 倍率因子
hue, sat, val = cv2.split(cv2.cvtColor(img, cv2.COLOR_BGR2HSV)) # 获取原图的hue, saturation, value
dtype = img.dtype # 一般为uint8
# 针对hue, saturation, value生成对应的LUT表(记录变换前后数值的对应表)
x = np.arange(0, 256, dtype=np.int16) # [0, 255]
lut_hue = ((x * r[0]) % 180).astype(dtype)
lut_sat = np.clip(x * r[1], 0, 255).astype(dtype)
lut_val = np.clip(x * r[2], 0, 255).astype(dtype)
# 将hue, saturation, value分量合并为hsv图像
img_hsv = cv2.merge((cv2.LUT(hue, lut_hue), cv2.LUT(sat, lut_sat), cv2.LUT(val, lut_val))).astype(dtype)
# 将HSV图像转换回BGR图像
aug_img = cv2.cvtColor(img_hsv, cv2.COLOR_HSV2BGR)
return aug_img
img = cv2.imread("./img_exp.jpg")
# 对该图片进行随机HSV数据增强
aug_img = augment_hsv(img=img, h_gain=0.5, s_gain=0.8, v_gain=0.9)
cv2.imshow("origin image", img)
cv2.imshow("Random HSV augmentation", aug_img)
cv2.waitKey(0)
参考
- https://blog.csdn.net/qq_37541097/article/details/119478023
- https://www.tech-faq.com/hsv.html
- https://docs.opencv.org/master/de/d25/imgproc_color_conversions.html#color_convert_rgb_hsv