原理
HSV颜色空间
HSV(色相hue, 饱和度sarturation, 色明度value)颜色空间与RGB颜色空间相似。hue色相通道代表颜色类型;saturation饱和度通道代表颜色的饱和度,即颜色的浓淡或深浅、value通道代表颜色的亮度。下面的圆柱体展示了HSV颜色空间的模型:
RGB与HSV颜色空间之间的转换
RGB颜色空间是通过红、绿、蓝3种基本颜色的比例来确定颜色。RGB颜色空间模型如下:
OpenCV默认的是BGR颜色空间(相当于RGB),所以需要将RGB颜色空间转换成HSV颜色空间。在OpenCV中可以使用cv::cvtColor
函数来进行不同颜色空间之间的转换。每个通道之间的具体转换方法如下:
V
←
m
a
x
(
R
,
G
,
B
)
V \leftarrow max(R,G,B) \\
V←max(R,G,B)
S
←
{
V
−
m
i
n
(
R
,
G
,
B
)
V
if
V
≠
0
0
otherwise
S \leftarrow \begin{cases} \frac{V-min(R,G,B)}{V} & \text{if} \quad V \neq 0 \\ 0 & \text{otherwise} \end{cases} \\
S←{VV−min(R,G,B)0ifV=0otherwise
H
←
{
60
(
G
−
B
)
/
(
V
−
m
i
n
(
R
,
G
,
B
)
if
V
=
R
120
+
60
(
B
−
R
)
/
(
V
−
m
i
n
(
R
,
G
,
B
)
if
V
=
G
240
+
60
(
R
−
G
)
/
(
V
−
m
i
n
(
R
,
G
,
)
if
V
=
B
0
if
R
=
G
=
B
H \leftarrow \begin{cases} 60(G-B)/(V-min(R,G,B) & \text{if} \quad V=R \\ 120+60(B-R)/(V-min(R,G,B) & \text{if} \quad V=G \\ 240+60(R-G)/(V-min(R,G,) & \text{if} \quad V=B \\ 0 & \text{if} \quad 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−G)/(V−min(R,G,)0ifV=RifV=GifV=BifR=G=B
- 如果计算出的 H < 0 H<0 H<0,则 H ← H + 360 H \leftarrow H+360 H←H+360;
- 计算出的H, S, V的范围分别是: 0 ≤ H ≤ 360 0 \leq H \leq 360 0≤H≤360, 0 ≤ S ≤ 1 0 \leq S \leq 1 0≤S≤1, 0 ≤ V ≤ 255 0 \leq V \leq 255 0≤V≤255
因为转换后的3个通道的颜色值可能会超出数据类型的最大值,所以还需要进行值域的转换
- 如果数据是8位: S ← 255 S , H ← H / 2 S \leftarrow 255S, H \leftarrow H/2 S←255S,H←H/2,即最终H, S, V的范围分别是: 0 ≤ H ≤ 180 0 \leq H \leq 180 0≤H≤180, 0 ≤ S ≤ 255 0 \leq S \leq 255 0≤S≤255, 0 ≤ V ≤ 255 0 \leq V \leq 255 0≤V≤255
- 如果数据是16位:目前还不支持
- 如果数据是32位:不需要转换
用RGB中的白色来举例。在8位的RGB颜色中,3个通道分别是
R
=
255
R=255
R=255,
G
=
255
G=255
G=255,
B
=
255
B=255
B=255。根据上面的转换公式,转换成HSV颜色空间,则有:
V
←
m
a
x
(
R
,
G
,
B
)
=
255
V \leftarrow max(R, G, B) = 255
V←max(R,G,B)=255
V
≠
0
V \neq 0
V=0,所以有:
S
←
V
−
m
i
n
(
R
,
G
,
B
)
V
=
0
S \leftarrow \frac{V-min(R,G,B)}{V} = 0
S←VV−min(R,G,B)=0
因为
R
=
G
=
B
R = G = B
R=G=B,所以有:
H
←
0
H \leftarrow 0
H←0
最后进行值域转换得:
S
←
255
S
=
0
,
H
←
H
/
2
=
127
S \leftarrow 255S = 0, H \leftarrow H/2 = 127
S←255S=0,H←H/2=127
所以,
R
G
B
(
255
,
255
,
255
)
RGB(255, 255, 255)
RGB(255,255,255)转换成
H
S
V
为:
HSV为:
HSV为:HSV(0, 0, 127)$。
下面将展示一个用HSV颜色空间来分离图片中的对象的例子:
代码实现
这里使用的图片是来自下载的OpenCV中自带的示例图片:...\opencv\sources\samples\data\stuff.jpg
。原图如下:
将该图片导入为src:
Mat src{ imread("stuff.jpg") };
颜色空间的转换
由于导入的图片默认是BGR模式,所以要使用cv::cvtColor函数进行颜色空间的转换:
Mat src_HSV;
cvtColor(src, //原图
src_HSV, //结果图
COLOR_BGR2HSV); //颜色空间转换类型:BGR转为HSV
转换后的效果如下:
范围阈值操作
范围阈值操作由inRange()
函数完成,其函数原型如下:
void cv::inRange(InputArray src,
InputArray lowerb,
InputArray upperb,
OutputArray dst)
src
:原图lowerb
:数组或Scalar
类型,表示范围的最小边界upperb
:数组或Scalar
类型,表示范围的最大边界dst
:输出图(与原图尺寸相同,CV_8U类型)
这个函数检查图片中的每个值,看它们是否落在其定义的范围内。其检查方法具体如下:
- 对于单通道图片
d s t ( I ) = l o w e r b ( I ) 0 ≤ s r c ( I ) 0 ≤ u p p e r b ( I ) 0 dst(I)=lowerb(I)_0 \leq src(I)_0 \leq upperb(I)_0 dst(I)=lowerb(I)0≤src(I)0≤upperb(I)0 - 对于双通道图片:
d s t ( I ) = l o w e r b ( I ) 0 ≤ s r c ( I ) 0 ≤ u p p e r b ( I ) 0 ∧ l o w e r b ( I ) 1 ≤ s r c ( I ) 1 ≤ u p p e r b ( I ) 1 dst(I)=lowerb(I)_0 \leq src(I)_0 \leq upperb(I)_0 \land lowerb(I)_1 \leq src(I)_1 \leq upperb(I)_1 dst(I)=lowerb(I)0≤src(I)0≤upperb(I)0∧lowerb(I)1≤src(I)1≤upperb(I)1 - 对于3通道图片:由上类推
d s t ( I ) = l o w e r b ( I ) 0 ≤ s r c ( I ) 0 ≤ u p p e r b ( I ) 0 ∧ l o w e r b ( I ) 1 ≤ s r c ( I ) 1 ≤ u p p e r b ( I ) 1 ∧ l o w e r b ( I ) 2 ≤ s r c ( I ) 2 ≤ u p p e r b ( I ) 2 dst(I)=lowerb(I)_0 \leq src(I)_0 \leq upperb(I)_0 \land \\ \qquad lowerb(I)_1 \leq src(I)_1 \leq upperb(I)_1 \land \\ \qquad lowerb(I)_2 \leq src(I)_2 \leq upperb(I)_2 dst(I)=lowerb(I)0≤src(I)0≤upperb(I)0∧lowerb(I)1≤src(I)1≤upperb(I)1∧lowerb(I)2≤src(I)2≤upperb(I)2
所以,无论是多少通道的图片,指定的范围都是一个闭区间。如果图片中的某个值低于范围的下边界,那就会被赋予下边界值;同理,如果超过上边界,就会被赋予上边界的值。
当上下边界是用Scalar
类型定义的时候,上面几个公式就不用加小括号来表示数组的下标了。
注意,转换成HSV格式之后,颜色值的最大值变成了Scalar(180, 255, 255)
.
本例中使用最大值为上边界,最大值的1/6为下边界:
Mat dst;
inRange(src_HSV, //HSV图
Scalar(30, 37, 37), //下边界
Scalar(180, 255, 255), //上边界
dst); //输出图
最终效果如下:
可以看到3个物体被很好的识别出来了,但是另一个物体并没有,而且背景中的暗部也被识别了。
可以变换上下边界,查看不同的效果。