范围阈值操作
原理
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:
cpp
Mat src{ imread("stuff.jpg") };
颜色空间的转换
由于导入的图片默认是BGR模式,所以要使用cv::cvtColor函数进行颜色空间的转换:
cpp
Mat src_HSV;
cvtColor(src, //原图
src_HSV, //结果图
COLOR_BGR2HSV); //颜色空间转换类型:BGR转为HSV
转换后的效果如下:
范围阈值操作
范围阈值操作由inRange()
函数完成,其函数原型如下:
cpp
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为下边界:
cpp
Mat dst;
inRange(src_HSV, //HSV图
Scalar(30, 37, 37), //下边界
Scalar(180, 255, 255), //上边界
dst); //输出图
最终效果如下:
可以看到3个物体被很好的识别出来了,但是另一个物体并没有,而且背景中的暗部也被识别了。
可以变换上下边界,查看不同的效果。