RGB 色彩空间是一种被广泛接受的色彩空间,但是该色彩空间过于抽象,我们不能够直接通过其值感知具体的色彩。
我们更习惯使用直观的方式来感知颜色,HSV 色彩空间提供了这样 的方式。
通过 HSV色彩空间,我们能够更加方便地通过色调、饱和度和亮度来感知颜色。 其实,除了 HSV 色彩空间,我们讨论的其他大多数色彩空间都不方便人们对颜色进行理解和解释。例如,现实中我们根本不可能用每种颜料的百分比(RGB
色彩空间)来形容一件衣服的颜色。
HSV 色彩空间从心理学和视觉的角度出发,提出人眼的色彩知觉主要包含三要素:
H:色调(Hue,也称为色相)。
S:饱和度(Saturation)。
V:亮度(Value)。
1.色调H
在 HSV 色彩空间中,色调 H 的取值范围是[0,360]。8 位图像内每个像素点所能表示的灰度级有 28=256 个,所以在 8 位图像内表示 HSV 图像时,要把色调的角度值映射到[0,255]范围内。
在 OpenCV 中,可以直接把色调的角度值除以 2,得到[0,180]之间的值,以适应 8 位二进制(256 个灰度级)的存储和表示范围。
在 HSV 空间中,色调值为 0 表示红色,色调值为 300 表示品红色,具体如表 4-3 所示。
根据上述分析可知,每个色调值对应一个指定的色彩,而与饱和度和亮度无关。
在 OpenCV中,将色调值除以 2 之后,会得到如表 4-4 所示的色调值与对应的颜色。
确定值范围后,就可以直接在图像的 H 通道内查找对应的值,从而找到特定的颜色。
例如,在 HSV 图像中,H 通道内值为 120 的像素点对应蓝色。查找 H 通道内值为 120 的像素点,找到的就是蓝色像素点。
在上述基础上,通过分析各种不同对象对应的 HSV 值,便可以查找不同的对象。
例如,通过分析得到肤色的 HSV 值,就可以直接在图像内根据肤色的 HSV 值来查找人脸(等皮肤)区域。
2.饱和度S
饱和度值的范围是[0,1],所以针对饱和度,需要说明以下问题:
灰度颜色所包含的 R、G、B 的成分是相等的,相当于一种极不饱和的颜色。所以,灰度颜色的饱和度值是 0。
作为灰度图像显示时,较亮区域对应的颜色具有较高的饱和度。
如果颜色的饱和度很低,那么它计算所得色调就不可靠。
19 节介绍 cv2.cvtColor()函数时,进行色彩空间转换后,为了适应 8 位图的256 个像素级,需要将新色彩空间内的数值映射到[0,255]范围内。所以,同样要将饱和度 S 的值从[0,1]范围映射到[0,255]范围内。
3亮度V
亮度的范围与饱和度的范围一致,都是[0,1]。同样,亮度值在
OpenCV 内也将值映射到[0,255]范围内。
亮度值越大,图像越亮;亮度值越低,图像越暗。当亮度值为 0 时,图像是纯黑色
获取指定颜色
可以通过多种方式获取 RGB 色彩空间的颜色值在 HSV 色彩空间内所对应的值。
例如,可以通过图像编辑软件或者在线网站获取 RGB 值所对应的 HSV 值。
需要注意,在从 RGB/BGR 色彩空间转换到 HSV 色彩空间时,OpenCV 为了满足 8 位图的要求,对 HSV 空间的值进行了映射处理。所以,通过软件或者网站获取的 HSV 值还需要被进一步映射,才能与 OpenCV 中的 HSV 值一致。
OpenCV 中,测试 RGB 色彩空间中不同颜色的值转换到 HSV 色彩空间后的对应值。
为了方便理解,这里分别对蓝色、绿色、红色的单个像素点进行测试。
首先,使用 np.zeros([1,1,3],dtype=np.uint8)
来生成一幅仅有一个像素点的图像(数组)。
接下来,通过对其中的通道分量赋值,将其设定为指定的颜色。例如:
语句 imgBlue[0,0,0]=255
可以将该像素点的第 0 个通道(即 B 通道)的值设置为 255,即将该点的颜色指定为蓝色。
语句 imgGreen[0,0,1]=255
可以将该像素点的第 1 个通道(即 G 通道)的值设置为 255,
即将该点的颜色指定为绿色。
语句 imgRed[0,0,2]=255
可以将该像素点的第 2 个通道(即 R 通道)的值设置为 255,
即将该点的颜色指定为红色。
然后,可以通过语句 cv2.cvtColor(Blue,cv2.COLOR_BGR2HSV)
将 Blue 从 BGR 色彩空间转换到 HSV 色彩空间。
最后,通过打印 HSV 色彩空间内的像素值,观察转换情况。
在本例中,对蓝色、绿色、红色三种不同的颜色分别进行转换,将它们从 BGR 色彩空间
转换到 HSV 色彩空间,并观察转换后所得到的 HSV 空间的对应值。
根据上面的流程用代码来验证下,代码如下:
import cv2
import numpy as np
#=========测试一下 OpenCV 中蓝色的 HSV 模式值=============
imgBlue=np.zeros([1,1,3],dtype=np.uint8)
#将 BGR 色彩空间中的蓝色的值赋值给 imgBlue
imgBlue[0,0,0]=255
Blue=imgBlue
#将 BGR 色彩空间中的蓝色的值转换为 HSV 色彩空间的值
BlueHSV=cv2.cvtColor(Blue,cv2.COLOR_BGR2HSV)
print("Blue=\n",Blue)
print("BlueHSV=\n",BlueHSV)
#=========测试一下 OpenCV 中绿色的 HSV 模式值=============
imgGreen=np.zeros([1,1,3],dtype=np.uint8)
imgGreen[0,0,1]=255
Green=imgGreen
GreenHSV=cv2.cvtColor(Green,cv2.COLOR_BGR2HSV)
print("Green=\n",Green)
print("GreenHSV=\n",GreenHSV)
#=========测试一下 OpenCV 中红色的 HSV 模式值=============
imgRed=np.zeros([1,1,3],dtype=np.uint8)
imgRed[0,0,2]=255
Red=imgRed
RedHSV=cv2.cvtColor(Red,cv2.COLOR_BGR2HSV)
print("Red=\n",Red)
print("RedHSV=\n",RedHSV)
运行结果
Blue=[[[255 0 0]]]
BlueHSV=[[[120 255 255]]]
Green=[[[ 0 255 0]]]
GreenHSV=[[[ 60 255 255]]]
Red=[[[ 0 0 255]]]
RedHSV=[[[ 0 255 255]]]
标记指定颜色
HSV 色彩空间中,H 通道(饱和度 Hue 通道)对应不同的颜色。或者换个角度理解,颜色的差异主要体现在 H 通道值的不同上。所以,通过对 H 通道值进行筛选,便能够筛选出特定的颜色。
例如,在一幅 HSV 图像中,如果通过控制仅仅将 H 通道内值为 240(在 OpenCV内被调整为 120)的像素显示出来,那么图像中就会仅仅显示蓝色部分。
本节将首先通过例题展示一些实现上的细节问题,然后通过具体例题展示如何将图像内的特定颜色标记出来,即将一幅图像内的其他颜色屏蔽,仅仅将特定颜色显示出来。
通过opencv.inRange()函数锁定特定值
OpenCV 中通过函数 cv2.inRange()来判断图像内像素点的像素值是否在指定的范围内,其语法格式为:
dst = cv2.inRange( src, lowerb, upperb )
式中:
dst 表示输出结果,大小和 src 一致。
src 表示要检查的数组或图像。
lowerb 表示范围下界。
upperb 表示范围上界。
返回值 dst 与 src 等大小,其值取决于 src 中对应位置上的值是否处于区间[lowerb,upperb]
内:
如果 src 值处于该指定区间内,则 dst 中对应位置上的值为 255。
如果 src 值不处于该指定区间内,则 dst 中对应位置上的值为 0。
实验验证:分别提取 OpenCV 的 logo 图像内的红色、绿色、蓝色。
在实际提取颜色时,往往不是提取一个特定的值,而是提取一个颜色区间。
例如,在 OpenCV 中的 HSV 模式内,蓝色在 H 通道内的值是 120。在提取蓝色时,通常将“蓝色值 120”附近的一个区间的值作为提取范围。该区间的半径通常为 10 左右,例如通常提取
[120−10,120+10]范围内的值来指定蓝色。
相比之下,HSV 模式中 S 通道、V 通道的值的取值范围一般是[100,255]。这主要是因为,当饱和度和亮度太低时,计算出来的色调可能就不可靠了。
根据上述分析,各种颜色的 HSV 区间值分布在[H−10,100,100]和[H+10,255,255]之间。因此,各种颜色值的范围为:
蓝色:值分布在[110,100,100]和[130,255,255]之间。
绿色:值分布在[50,100,100]和[70,255,255]之间。
红色:值分布在[0,100,100]和[10,255,255]之间。
根据前述例题的相关介绍,首先利用函数 cv2.inRange()查找指定颜色区域,然后利用基于掩码的按位与运算将指定颜色提取出来。
实验原图:
代码如下:
import cv2
import numpy as np
opencv=cv2.imread("opencv.jpg")
hsv = cv2.cvtColor(opencv, cv2.COLOR_BGR2HSV)
cv2.imshow('opencv',opencv)
#=============指定蓝色值的范围=============
minBlue = np.array([110,50,50])
maxBlue = np.array([130,255,255])
#确定蓝色区域
mask = cv2.inRange(hsv, minBlue, maxBlue)
#通过掩码控制的按位与运算,锁定蓝色区域
blue = cv2.bitwise_and(opencv,opencv, mask= mask)
cv2.imshow('blue',blue)
#=============指定绿色值的范围=============
minGreen = np.array([50,50,50])
maxGreen = np.array([70,255,255])
#确定绿色区域
mask = cv2.inRange(hsv, minGreen, maxGreen)
#通过掩码控制的按位与运算,锁定绿色区域
green = cv2.bitwise_and(opencv,opencv, mask= mask)
cv2.imshow('green',green)
#=============指定红色值的范围=============
minRed = np.array([0,50,50])
maxRed = np.array([30,255,255])
#确定红色区域
mask = cv2.inRange(hsv, minRed, maxRed)
#通过掩码控制的按位与运算,锁定红色区域
red= cv2.bitwise_and(opencv,opencv, mask= mask)
cv2.imshow('red',red)
cv2.waitKey()
cv2.destroyAllWindows()
运行结果如下,分别提取了不同的颜色出来