前置内容
RGB色彩空间是常见的色彩的空间,其他还有GRAY色彩空间、HSV色彩空间等等,它们都可以从不同的角度进行理解颜色。
类比于数字10,它可以表示为二进制、八进制或者十六进制,以不同的规则来表示,都没有错误,但各个进制的计算必须按照各个进制的规则来执行,
色彩空间同样如此,各个色彩空间之间可以相互转换,类比不同进制之间也可以进行转换。
色彩空间基础
GRAY色彩空间
当图像从RGB色彩空间转变为GRAY色彩空间(灰度图)时,标准转换公式为: GRAY=0.299 * R + 0.587 * G + 0.114 * B
有时也可以简化计算转换: GRAY = ( R + G + B ) / 3
当GRAY色彩空间转换为BGR色彩空间时,所有通道将是一样的: B = G = R = GRAY
HSV色彩空间
RGB通过三种基色的不同比例混合成各种颜色,而在实际更习惯用直观的方式来感知颜色,这就是HSV色彩空间。
HSV色彩空间包含色调H、饱和度S、明度V
- 色调H(Hue)
色调即光的颜色。我们知道,阳光是混合光,中间包含赤橙黄绿青蓝紫等各种光,这些不同颜色的光因为光波波长的不一样表现为不同的颜色,这些不同的光即是不同的色调。
在HSV色彩空间中,色调H的取值范围为[0,360],而每个像素点的取值范围为[0.255],为了把色调在[0,360]映射到[0,255]中,OpenCV将色调除以2,得到的值为[0,180],以适应8位2进制数的存储和表示范围。
部分典型颜色对应的值如下:
颜色 | 色调 | OpenCV内的值 |
红色 | 0 | 0 |
黄色 | 60 | 30 |
绿色 | 120 | 60 |
青色 | 180 | 90 |
蓝色 | 240 | 120 |
品红 | 300 | 150 |
色调使用简单说明:在HSV图像中,H通道120的值对应的像素点是蓝色的。
- 饱和度S(Saturation)
饱和度是指色彩的鲜艳程度,表示为色彩的相对纯净度。饱和度取决于色彩中灰色的占比,灰色越多,色彩饱和度越低。当色彩不包含灰色,此时色彩表现为纯色。
当颜色的饱和度很低时(灰色太多了),计算的色调(光色)就不准确了,此时没有彩色信息只有灰色信息了。
饱和度取值范围为[0,1],饱和度为0时,只有灰度,饱和度为1时,此时为纯色。
为了在8位位图中显示,饱和度也需要将[0,1]映射到[0,255]。
- 明度V(Value)
明度指人眼感受到的色彩明亮程度,反映的是人眼感受到的光的明暗程度。
对于无彩色(黑、白、灰),白色的明度最高,黑色的明度最低,白色和黑色之间存在不同明度的灰色。
对于彩色图像来说,明度越高,图像越光亮,反之越暗淡。
明度的取值范围为[0,1],在OpenCV中也要映射到[0,255]上。
举例说明:
色调=0 饱和度=1 明度=1,人眼看到的色彩为红色,且颜色较亮。
色调=120 饱和度=0.3 明度=0.4,人眼看到的色彩为浅绿色,且颜色较暗。
色彩空间转换
OpenCV提供了方法实现不同色彩空间的转换
dst = cv2.cvtColor( src , code [,dstCn] )
dst : 转换后的输出图像,与原图具有相同的数据类型和深度。
src : 原始输入图像
code : 色彩空间转换码,此参数告诉OpenCV要从哪个色彩空间转换到哪个色彩空间,具体长的是cv2.COLOR_XXX2YYY
dstCn : 目标图像的通道数
各空间转换过程中不可避免存在映射操作造成的四舍五入情况,故来回转换色彩空间过程不是精准可逆。
举例-提取肤色
根据肤色在HSV色彩空间的范围可以将包含肤色的部分提取出来
- 色调值[0,33]
- 饱和度[10,255]
- 明度值[80,255]
具体实现如下:
import cv2 as cv
import numpy as np
img = cv.imread("x.png")
# BGR转为HSV
hsv = cv.cvtColor(img, cv.COLOR_BGR2HSV)
print(hsv) # HSV色彩空间的数据 二维数组的[H S V]或者三维数组
min_HSV = np.array([0, 10, 80], dtype="uint8") # [0 10 80]
max_HSV = np.array([33, 255, 255], dtype="uint8") # [33 255 255]
# cv2.inRange(src,min,max)低于min或高于max的值置为0 在min和max范围内的值置为255
mask = cv.inRange(hsv, min_HSV, max_HSV)
print(mask) # 掩模嘛,就是个二维数组
result = cv.bitwise_and(img, img, mask=mask) # 2个img这个我看的有点懵一开始,自己与自己,等于自己,就是图像在掩模作用范围内做算法
cv.imshow("img", img)
cv.imshow("mask", mask)
cv.imshow("result", result)
cv.waitKey()
cv.destroyAllWindows()
运行如下(本来想用lena的色彩图,但整张lena图对黄色比较广且杂,效果不大好):