第四章:色彩空间类型转换
- one. 色彩空间基础知识:
- 1. GRAY色彩空间:
- 2. XYZ色彩空间
- 3. YCrCb色彩空间
- 3. HSV色彩空间
- 4. HLS 色彩空间
- 5. CIEL * a * b *色彩空间
- 6. CIEL * u * v *色彩空间
- 7. Bayer色彩空间
- two. 类型转换函数:
- three. 类型转换实例
- four. HSV色彩空间讨论:
- 1. 获取指定颜色:
- 2. 标记指定颜色:
- five. alpha通道:
色彩空间称为颜色空间、彩色空间、颜色模型、彩色系统、彩色模型、色彩模型等。
常见的色彩空间类型有:
- RGB色彩空间(最常见)
- GRAY色彩空间(灰度图像)
- XYZ色彩空间
- YCrCb色彩空间
- HSV色彩空间
- HLS色彩空间
- CIEL* a * b* 色彩空间
- CIEL * u * v* 色彩空间
- Bayer色彩空间
每个色彩空间都有处理问题的领域,因此当我们处理具体问题时,就需要用的色彩空间类型转换。
one. 色彩空间基础知识:
1. GRAY色彩空间:
GRAY(灰度图像)通常指8位灰度图,其具有256个灰度级,像素值的范围是[0, 255]。当图像由RGB色彩空间转换为GRAY色彩空间时,其处理方式如下:
- GRAY = 0.299 * R + 0.587 * G + 0.114 * B
上面的转换公式是标准转换公式,也是OpenCV中使用的转换方式。有时也可以简化成:
- $GRAY = \cfrac { R + G +B }{ 3 } $
当图像由GRAY色彩空间转换为RGB色彩空间时,最终所有的通道值都将是相同的, 其处理方式如下:
- R = GRAY
- G = GRAY
- B = GRAY
2. XYZ色彩空间
XYZ色彩空间是有CIE定义的,是一种更加便于计算的色彩空间,可以与RGB色彩空间进行相互转换。
- 将RGB色彩空间转换为XYZ色彩空间:
- 将XYZ色彩空间转换为RGB色彩空间
3. YCrCb色彩空间
人眼视觉系统(HSV)对颜色的敏感度要低于对亮度的敏感度。在传统RGB色彩空间中,RGB三原色具有相同的重要性,但是忽略了亮度信息。
在YCrCb色彩空间中,Y代表光源亮度,色彩信息保存在Cr和Cb中,其中,Cr表示红色分量信息,Cb表示蓝色分量信息。
亮度给出了颜色的亮暗程度信息,该信息通过照明强度的加权和来计算。在RGB光源中,绿色分量影响最大,蓝色分量影响最小。
-
RGB 色彩空间转换为YCrCb色彩空间:
- Y = 0.299 * R + 0.578 * G + 0.114 * B
- Cr = ( R - Y ) × 0.713 + delta
- Cb = (B - Y ) × 0.546 + delta
式中delta的值为:{128,8位图像32768,16位图像0.5,单精度图像\begin{cases} 128, \quad 8位图像 \\ 32768, \quad 16位图像 \\ 0.5, \quad 单精度图像 \end{cases}⎩⎪⎨⎪⎧128,8位图像32768,16位图像0.5,单精度图像
-
YCrCb色彩空间转为RGB色彩空间:
- R = Y + 1.403 * (Cr - delta)
- G = Y - 0.714 * (Cr - delta) - 0.344 * (Cb - delta)
- B = Y + 1.733 * (Cr - delta)
式中delta的值与上面公式中的delta的值相同。
3. HSV色彩空间
RGB是从硬件的角度提出颜色模型,在与人眼匹配的过程中可能存在一定的差异。但HSV色彩空间不同,是一种面向视觉感知的颜色模型。HSV色彩空间从心理学和视觉角度出发,指出了人眼的色彩知觉主要包含三要素:色调(Hue)、饱和度(Saturation)、亮度(Value),色调指光的颜色,饱和度指色彩深浅程度、亮度之人眼感受到的光的明暗程度。
- 色调:色调与混合光谱中主要光波长相关,例如"赤橙黄绿青蓝紫"分别表示不同的色调。如果从波长的角度考虑,不同的波长的光表现为不同的颜色,实际上它们体现的是色调的差异。
- 饱和度:指相对纯净度,或一种颜色混合白光的数量。纯普色是全饱和的,像深红色(红加白)和淡紫色(紫加白)这样的彩色是欠饱和的,饱和度与所加白光的数量成反比。
- 亮度:反映的是人眼感受到的光的明暗程度,该指标与物体的反射度相关。对于色彩来讲,如果期中参入的白色越多,则亮度越高;如果其中掺入的黑色越多,则其亮度越低。
在具体实现上,我们将物理空间的颜色分布在圆周上,不同的角度代表不同的颜色。因此,通过调整色调值就能选取不同的颜色,色调的取值区间是[0, 360]。色调取不同值时,所代表的的颜色不同,两个角度之间的角度对应两个颜色之间的过渡色。
饱和度为一比例值,范围是[0, 1],具体为所选颜色的纯度值和该颜色最大纯度值之间的比值。饱和度为0时,只有灰度。亮度表示色彩的明亮程度,取值范围也是[0, 1]。
在HSV色彩模型中,取色变得更加直观。例如,取值"色调=0, 饱和度=1, 亮度= 1",则当前颜色为深红色,而且颜色较亮。取值"色调=120,饱和度=0.3,亮度=0.4",则当前颜色为浅绿色,而且颜色较暗。
在从RGB色彩空间转换到HSV色彩空间之前,需要先将RGB色彩空间的值转换到[0, 1]之间,然后再进行处理。具体处理方法为:
计算结果可能存在H<0的情况,如果出现这种情况,则需要对H进行进一步计算,如下
H={H+360,H<360H,(其他情况)H = \begin{cases} H + 360, \quad H<360 \\ H, \quad (其他情况) \end{cases}H={H+360,H<360H,(其他情况)
由上述公式可知:
- S∈[0,1]S \in [0, 1]S∈[0,1]
- V∈[0,1]V \in [0, 1]V∈[0,1]
- H∈[0,360]H \in [0, 360]H∈[0,360]
4. HLS 色彩空间
HLS色彩空间包含三要素:色调H(Hue)、光亮度\明度L(Lightness)、饱和度S(Saturation)与HSV色彩空间类似,只是HLS色彩空间用“光亮度/明度L(lightness)”替换了“亮度(Value)”。
- 色调:表示人眼所能感知的颜色,在 HLS 模型中,所有的颜色分布在一个平面的色调环上,整个色调环为360度的圆心角,不同的角度代表不同的颜色。
- 光亮度/明度:用来控制色彩的明暗变化,它的取值范围也是[0,1]。我们通过光亮度/明度的大小来衡量有多少光线从物体表面反射出来。光亮度/明度对于眼睛感知颜色很重要,因为当一个具有色彩的物体处于光线太强或者光线太暗的地方时,眼睛是无法准确感知物体颜色的。
- 饱和度:使用[0,1]的值描述相同色调、相同光亮度/明度下的色彩纯度变化。饱和度的值越大,表示颜色的纯度越高,颜色越鲜艳;反之,饱和度的值越小,色彩的纯度越低,颜色越暗沉。通常用该属性表示颜色的深浅,比如深绿色、浅绿色
5. CIEL * a * b *色彩空间
CIEL * a * b 色彩空间是均匀色彩空间模型,它是面向视觉感知的颜色模型。从视觉感知均匀的角度来讲,人所感知到的两种颜色的区别程度,应该与这两种颜色在色彩空间中的距离成正比。在某个色彩空间中,如果人所观察到的两种颜色的区别程度,与这两种颜色在该色彩空间中对应的点之间的欧式距离成正比,则称该色彩空间为均匀色彩空间。
CIEL * a * b 色彩空间中的L分量用于表示像素的亮度,取值范围是[0,100],表示从纯黑到纯白;a * 分量表示从红色到绿色的范围,取值范围是[-127,127]; b * 分量表示从黄色到蓝色的范围,取值范围是[-127,127]。
在从RGB色彩空间转换到CIEL * a * b * 色彩空间之前,需要先将RGB色彩空间的值转换到[0,1]之间,然后再进行处理。
由于CIEL * a * b * 色彩空间是在CIE的XYZ色彩空间的基础上发展起来的,在具体处理时,需要先将RGB转换为XYZ色彩空间,再将其转换到CIELa * b *色彩空间。具体实现方法为:
式中:
f(t)={t13,t>0.00088567.787t+16116,其他情况f(t) = \begin{cases} t^\cfrac { 1 }{ 3 }, \quad t > 0.0008856 \\ 7.787t + \cfrac { 16 }{ 116 }, \quad 其他情况 \end{cases}f(t)=⎩⎪⎪⎨⎪⎪⎧t31,t>0.00088567.787t+11616,其他情况
delta={128,8位图像0,单精度图像delta = \begin{cases} 128, \quad 8位图像 \\ 0, \quad 单精度图像 \end{cases}delta={128,8位图像0,单精度图像
所得结果中各个值的取值范围为:
- L∈[0,100]L \in [0, 100]L∈[0,100]
- a∈[−127,127]a \in [-127, 127]a∈[−127,127]
- b∈[−127,127]b \in [-127, 127]b∈[−127,127]
6. CIEL * u * v *色彩空间
CIEL * u * v * 色彩空间同 CIEL * a * b * 色彩空间一样,都是均匀的颜色模型。CIEL * u * v * 色彩空间与设备无关,适用于显示器显示和根据加色原理进行组合的场合,该模型中比较强调对红色的表示,即对红色的变化比较敏感,但对蓝色的变化不太敏感。
下面的公式给出了从RGB色彩空间到CIEL * u * v *色彩空间的转换公式。
从RGB色彩空间到XYZ色彩空间的转换:
从XYZ色彩空间到CIEL * u * v *色彩空间的转换:
所得结果中各个值的取值范围为:
- L∈[0,100]L \in [0, 100]L∈[0,100]
- u∈[−127,127]u \in [-127, 127]u∈[−127,127]
- v∈[−127,127]v \in [-127, 127]v∈[−127,127]
7. Bayer色彩空间
Bayer色彩空间(Bayer模型)被广泛地应用在CCD和CMOS相机中。它能够从如图所示的单平面R、G、B交错表内获取彩色图像。
输出的RGB图像的像素点值,是根据当前点的1个、2个或4个邻域像素点的相同颜色的像素值获得的。上述模式能够通过移动一个左边的像素或者上方的像素来完成修改。在函数cv2.cvtColor()的色彩空间转换参数中,通常使用两个特定的参数x和y来表示特定的模式。该模式组成通过图第二行中的第2列与第3列的值来指定。图就是典型的“BG”模式。
常见的模式还有很多,例如 cv2.COLOR_BayerBG2BGR、cv2.COLOR_BayerGB2BGR、cv2.COLOR_BayerRG2BGR、cv2.COLOR_BayerGR2BGR、cv2.COLOR_BayerBG2RGB、cv2.COLOR_BayerGB2RGB、cv2.COLOR_BayerRG2RGB、cv2.COLOR_BayerGR2RGB等。
two. 类型转换函数:
在OpenCV中,我们使用cv2.cvtColor()函数进行色彩空间的转换。该函数可以实现多个色彩空间的转换。
-
dst = cv2.cvtColor(src, code [, dstCn ] )
dst:表示输出图像,与原始输入图像具有同样的数据类型和深度。
src:表示原始输入图像。可以是8位无符号图像、16位无符号图像,或者单精度浮点数等。
code:是色彩空间转换码。
dstCn:是目标图像的通道数。如果参数为默认的0,则通道数自动通过原始输入图像和code得到。
注意: 图像深度是指存储每个像素所用的位数,也用于量度图像的色彩分辨率。如8位无符号图像 指的是图像深度。https://blog.csdn.net/qq_40041064/article/details/102971585
注意:BGR色彩空间与传统的RGB色彩空间不同。对于一个标准的24位位图,BGR色彩空中第1个字节存放的是蓝色组成的信息,第2个字节存放的是绿色组成的信息,第3个字节存放的是红色组成的的信息。
颜色空间的转换都用到了如下约定:
- 8位图像值的范围是[0,255]。
- 16位图像值的范围是[0,65 535]。
- 浮点数图像值的范围是[0.0~1.0]。
对于线性转换来说,这些取值范围是无关紧要的。但是对于非线性转换来说,输入的RGB图像必须归一化到其对应的取值范围内,才能获取正确的转换结果。
例如,对于8位图,其能够表示的灰度级有282^828=256个,也就是说,在8位图中,最多能表示256个状态,通常是[0,255]之间的值。但是,在很多色彩空间中,值的范围并不恰好在[0,255]范围内,这时,就需要将该值映射到[0,255]内。
例如,在HSV或HLS色彩空间中,色调值通常在[0,360)范围内,在8位图中转换到上述色彩空间后,色调值要除以2,让其值范围变为[0,180),以满足存储范围,即让值的分布位于8位图能够表示的范围[0,255]内。又例如,在CIEL * a * b * 色彩空间中,a通道和b通道的值范围是[−127,127],为了使其适应[0,255]的范围,每个值都要加上127。不过需要注意,由于计算过程存在四舍五入,所以转换过程并不是精准可逆的。
three. 类型转换实例
1、将图像在BGR模式和灰度图像之间相互转换。
import cv2lena = cv2.imread('../lena512color.tiff')
gray = cv2.cvtColor(lena, cv2.COLOR_BGR2GRAY)
rgb = cv2.cvtColor(gray, cv2.COLOR_GRAY2RGB)
print(lena.shape)
print(gray.shape)
print(rgb.shape)
cv2.imshow('lena', lena)
cv2.imshow('gray', gray)
cv2.imshow('rgb', rgb)
cv2.waitKey()
cv2.destroyAllWindows()
注意:通过“rgb=cv2.cvtColor(gray,cv2.COLOR_GRAY2BGR)”得到的 RGB 图像中,B 通道、G 通道、R通道的值都是一样的,所以其看起来仍是灰度图像。
2、将图像从BGR模式转成RGB模式。
import cv2bgr = cv2.imread('../lena512color.tiff')
rgb = cv2.cvtColor(bgr, cv2.COLOR_BGR2RGB)
cv2.imshow('bgr', bgr)
cv2.imshow('rgb', rgb)
cv2.waitKey()
cv2.destroyAllWindows()
four. HSV色彩空间讨论:
RGB色彩空间是一种被广泛接受的色彩空间,但是该色彩空间过于抽象,我们不能够直接通过其值感知具体的色彩。我们更习惯使用直观的方式来感知颜色,HSV色彩空间提供了这样的方式。通过HSV色彩空间,我们能够更加方便地通过色调、饱和度和亮度来感知颜色。
HSV色彩空间从心理学和视觉的角度出发,提出人眼的色彩知觉主要包含三要素:
- H:色调(Hue,也称为色相)。
- S:饱和度(Saturation)。
- V:亮度(Value)。
**1. 色调: **
在HSV色彩空间中,色调H的取值范围是[0,360]。8位图像内每个像素点所能表示的灰度级有282^828=256个,所以在8位图像内表示HSV图像时,要把色调的角度值映射到[0,255]范围内。在OpenCV中,可以直接把色调的角度值除以2,得到[0,180]之间的值,以适应8位二进制(256个灰度级)的存储和表示范围。具体如表所示:
在OpenCV中,将色调值除以2之后,会得到如表所示的色调值与对应的颜色,以下是映射后的色调值。
确定值范围后,就可以直接在图像的H通道内查找对应的值,从而找到特定的颜色。例如,在HSV图像中,H通道内值为120的像素点对应蓝色。查找H通道内值为120的像素点,找到的就是蓝色像素点。
在上述基础上,通过分析各种不同对象对应的 HSV 值,便可以查找不同的对象。例如,通过分析得到肤色的HSV值,就可以直接在图像内根据肤色的HSV值来查找人脸(等皮肤)区域。
2. 饱和度:
饱和度值的范围是[0,1],所以针对饱和度,需要说明以下问题:
- 灰度颜色所包含的R、G、B的成分是相等的,相当于一种极不饱和的颜色。所以,灰度颜色的饱和度值是0。
- 作为灰度图像显示时,较亮区域对应的颜色具有较高的饱和度。
- 如果颜色的饱和度很低,那么它计算所得色调就不可靠。
- 同样要将饱和度S的值从[0,1]范围映射到[0,255]范围内
3. 亮度:
亮度的范围与饱和度的范围一致,都是[0,1]。同样,亮度值在OpenCV内也将值映射到[0,255]范围内。
亮度值越大,图像越亮;亮度值越低,图像越暗。当亮度值为0时,图像是纯黑色。
1. 获取指定颜色:
可以通过多种方式获取RGB色彩空间的颜色值在HSV色彩空间内所对应的值。例如,可以通过图像编辑软件或者在线网站获取RGB值所对应的HSV值。
需要注意,在从RGB/BGR色彩空间转换到HSV色彩空间时,OpenCV为了满足8位图的要求,对HSV空间的值进行了映射处理。所以,通过软件或者网站获取的HSV值还需要被进一步映射,才能与OpenCV中的HSV值一致。
例:在OpenCV中,测试RGB色彩空间中不同颜色的值转换到HSV色彩空间后的对应值。
import cv2
import numpy as npimg_blue = np.zeros([1, 1, 3], dtype=np.uint8)
img_blue[0, 0, 0] = 255
blue = img_blue
blue_hsv = cv2.cvtColor(blue, cv2.COLOR_BGR2HSV)
print('blue=\n', blue)
print('blue_hsv=\n', blue_hsv)img_green = np.zeros([1, 1, 3], dtype=np.uint8)
img_green[0, 0, 1] = 255
green = img_green
green_hsv = cv2.cvtColor(green, cv2.COLOR_BGR2HSV)
print('green=\n', green)
print('green_hsv=\n', green_hsv)img_red = np.zeros([1, 1, 3], dtype=np.uint8)
img_red[0, 0, 2] = 255
red = img_red
red_hsv = cv2.cvtColor(red, cv2.COLOR_BGR2HSV)
print('red=\n', red)
print('red_hsv=\n', red_hsv)# 输出结果
blue=[[[255 0 0]]]
blue_hsv=[[[120 255 255]]]
green=[[[ 0 255 0]]]
green_hsv=[[[ 60 255 255]]]
red=[[[ 0 0 255]]]
red_hsv=[[[ 0 255 255]]]
2. 标记指定颜色:
在HSV色彩空间中,H通道(饱和度Hue通道)对应不同的颜色。或者换个角度理解,颜色的差异主要体现在H通道值的不同上。所以,通过对H通道值进行筛选,便能够筛选出特定的颜色。例如,在一幅HSV图像中,如果通过控制仅仅将H通道内值为240(在OpenCV内被调整为120)的像素显示出来,那么图像中就会仅仅显示蓝色部分。
-
通过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。
例:使用函数cv2.inRange()将某个图像内的在[100,200]内的值标注出来。
import cv2 import numpy as npimg = np.random.randint(0, 256, size=[5, 5], dtype=np.uint8) min = 100 max = 200 mask = cv2.inRange(img, min, max) print('img=\n', img) print('mask=\n', mask)# 输出结果 img=[[170 181 245 13 255][ 79 72 133 113 138][112 72 169 87 47][142 210 139 94 165][ 32 16 9 89 95]] mask=[[255 255 0 0 0][ 0 0 255 255 255][255 0 255 0 0][255 0 255 0 255][ 0 0 0 0 0]]
-
-
通过基于掩码的按位与操作显示ROI:
例:正常显示某个图像内的感兴趣区域(ROI),而将其余区域显示为黑色。
import cv2 import numpy as npimg = np.ones([5, 5], dtype=np.uint8) * 9 mask = np.zeros([5, 5], dtype=np.uint8) mask[0:3, 0] = 1 mask[2:5, 2:4] = 1roi = cv2.bitwise_and(img, img, mask=mask) print('img=\n', img) print('mask=\n', mask) print('roi=\n', roi)# 输出结果 img=[[9 9 9 9 9][9 9 9 9 9][9 9 9 9 9][9 9 9 9 9][9 9 9 9 9]] mask=[[1 0 0 0 0][1 0 0 0 0][1 0 1 1 0][0 0 1 1 0][0 0 1 1 0]] roi=[[9 0 0 0 0][9 0 0 0 0][9 0 9 9 0][0 0 9 9 0][0 0 9 9 0]]
-
显示特定颜色值:
例:使用OpenCV,提取图片中不同颜色。
需要注意的是,在实际提取颜色时,往往不是提取一个特定的值,而是提取一个颜色区间。例如,在OpenCV中的HSV模式内,蓝色在H通道内的值是120。在提取蓝色时,通常将“蓝色值120”附近的一个区间的值作为提取范围。该区间的半径通常为10左右,例如通常提取[120−10,120+10]范围内的值来指定蓝色。
相比之下,HSV模式中S通道、V通道的值的取值范围一般是[100,255]。这主要是因为,当饱和度和亮度太低时,计算出来的色调可能就不可靠了。
import cv2 import numpy as npimg = cv2.imread('../pepper.tiff') cv2.imshow('img', img) print(img) img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) cv2.imshow('img_hsv', img_hsv)min_red = np.array([0, 50, 50]) max_red = np.array([20, 255, 255]) mask = cv2.inRange(img_hsv, min_red, max_red) img_red = cv2.bitwise_and(img_hsv, img_hsv, mask=mask) cv2.imshow('img_red', img_red)min_green = np.array([40, 50, 50]) max_green = np.array([70, 255, 255]) mask = cv2.inRange(img_hsv, min_green, max_green) img_green = cv2.bitwise_and(img_hsv, img_hsv, mask=mask) cv2.imshow('img_green', img_green)min_yellow = np.array([20, 50, 50]) max_yellow = np.array([40, 255, 255]) mask = cv2.inRange(img_hsv, min_yellow, max_yellow) img_yellow = cv2.bitwise_and(img_hsv, img_hsv, mask=mask) cv2.imshow('img_yellow', img_yellow)cv2.waitKey() cv2.destroyAllWindows()
five. alpha通道:
在RGB色彩空间三个通道的基础上,还可以加上一个A通道,也叫alpha通道,表示透明度。这种4个通道的色彩空间被称为RGBA色彩空间,PNG图像是一种典型的4通道图像。alpha通道的赋值范围是[0,1],或者[0,255],表示从透明到不透明。
例:编写一个程序,对图像的alpha通道进行处理。
import cv2img = cv2.imread('../lena512color.tiff')
bgra = cv2.cvtColor(img, cv2.COLOR_BGR2BGRA)
b, g, r, a = cv2.split(bgra)
a[:, :] = 125
bgra_125 = cv2.merge([b, g, r, a])
a[:, :] = 0
bgra_0 = cv2.merge([b, g, r, a])cv2.imshow('img', img)
cv2.imshow('bgra', bgra)
cv2.imshow('bgra_125', bgra_125)
cv2.imshow('bgra_0', bgra_0)cv2.imwrite('../bgra_125.png', bgra_125)
cv2.imwrite('../bgra_0.png', bgra_0)cv2.waitKey()
cv2.destroyAllWindows()
上面显示了bgra_125和bgra_0图片,注意:如果只用cv2.imshow显示图像,各个图像的alpha通道值虽然不同,但是在显示时是没有差别的。 如果用cv2.imwrite()将其保存到本地文件中看,是可以发现其不同的。