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°](逆时针旋转),色度范围如下图所示。
其中:
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,BR, G, BR,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)VifV≠00otherwiseS = \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)0ifV=0otherwise
H={60(G−B)/(V−min(R,G,B))ifV=R120+60(B−R)/(V−min(R,G,B))ifV=G240+60(R−B)/(V−min(R,G,B))ifV=B0ifR=G=BH = \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))0ifV=RifV=GifV=BifR=G=B
如果计算的结果<0,那么就加上360。其中:
0≤V≤10≤S≤10≤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)VifV≠00otherwise=0.7294−min(0.7294,0.7020,0.5922)0.7294=0.7294−0.59220.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)0ifV=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))ifV=R120+60(B−R)/(V−min(R,G,B))ifV=G240+60(R−B)/(V−min(R,G,B))ifV=B0ifR=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))0ifV=RifV=GifV=BifR=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.0175V=0.7294,S=0.1881, H=48.0175V=0.7294,S=0.1881,H=48.0175。
如果H≤0H\le0H≤0,那么H=H+360H = H + 360H=H+360
3. OpenCV实现RGB转HSV
3.1 函数调用及其注意事项
使用OpenCV将RGB模型图像转成HSV模型图像非常简单,直接使用cv2.cvtColor
函数,在ColorConversionCodes
参数中传入cv2.COLOR_RGB2HSV
参数即可。
cv2.cvtColor官方文档
ColorConversionCodes官方文档
需要注意的是,通过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′=H2V' = 255\times V, S' = 255 \times S, H' = \frac{H}{2}V′=255×V,S′=255×S,H′=2H16-bit图片:当前并不支持😂32-bit图片 ->float32
:直接原值存储即可
那么对于刚刚讲的示例将(186, 179, 151)RGB模型空间转到HSV模型空间得到是(48.0175, 0.1881, 0.7294),按照刚刚讲的在OpenCV转换后应该是:
H′=H2→24.0088S′=255×S→47.9655V′=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 例子
/qq_37541097/article/details/119478023
import cv2import numpy as nprgb_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 cv2import 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) * 255value = np.ones((50, 180, 1), dtype=np.uint8) * 255img_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 cv2import numpy as nphue = 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) * 255img_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 cv2import numpy as nphue = np.zeros((100, 256, 1), dtype=np.uint8)sat = np.ones((100, 256, 1), dtype=np.uint8) * 255val = 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在数据增强中的应用
/qq_37541097/article/details/119478023
import cv2import numpy as npdef 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 gainshue, sat, val = cv2.split(cv2.cvtColor(img, cv2.COLOR_BGR2HSV))dtype = img.dtype # uint8x = 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 cv2import numpy as npdef 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, valuedtype = 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_imgimg = 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)