0%

opencv_4_色彩空间类型转换

色彩空间基础

  • 比较常见的色彩空间包括:

  • GRAY色彩空间

  • XYZ色彩空间

  • YCrCb色彩空间

  • HSV色彩空间

  • HLS色彩空间

  • CIELab色彩空间

  • CIELuv色彩空间

  • Bayer色彩空间等等

GRAY色彩空间——灰度

  • GRAY(灰度图像)通常指8位灰度图,其具有256个灰度级,像素值的范围是[0, 255]。当图像由RGB色彩空间转化位GRAY色彩空间时,其处理方式如下:
  • Gray=0.299R+0.587G+0.114*B
  • 上述是标准的转换方式,也是opencv中使用的转换方式。
  • 有时,也可以采用简化形式完成转换:
  • Gray=(R+G+B)/3
  • 当图像由GRAY色彩空间转化位RGB色彩空间时,最终所有通道的值都将是相同的,其处理方式如下:
  • R=Gray
  • G=Gray
  • B=Gray

XYZ色彩空间

  • XYZ色彩空间是由CIE定义的,是一种更便于计算的色彩空间,它可以与RGB色彩空间相互转换。
  1. RGB—>XYZ
    a9c7621989406af2b9a6833cc8ff725
  2. XYZ—>RGB
    dec1c26a90cfc8cb87a339c68d1abfa

YCrCb色彩空间

  • 人眼视觉系统(HVS)对颜色的敏感度要低于对亮度的敏感度。在传统的RGB色彩空间内,RGB三原色具有相同的重要性,但是忽略了亮度信息。
  • 在YCrCb色彩空间中,Y代表光源的亮度,色度信息保存在Cr和Cb中,其中,Cr表示红色分量信息,Cb表示蓝色分量信息。
  • 亮度给出了颜色亮或暗的程度信息,该信息可以通过照明中强度成分的加权和来计算。在RGB光源中,绿色分量的影响最大,蓝色分量的影响最小。
  • Y=0.299R+0.587G+0.114*B
  • Cr=(R-Y)*0.713+delta
  • Cb=(B-Y)*0.564+delta
  • 式中delta的值为:
    e9a284630772ff62d43fd50e6f767be
  • 从YCrCb色彩空间到RGB色彩空间的转换公式为:
  • R=Y+1.403*(Cr-delta)
  • G=Y-0.714(Cr-delta)-0.344(Cb-delta)
  • B=Y+1.773*(Cb-delta)
  • 式中,delta的值与上面公式中的delta值相同

HSV色彩空间和HLS色彩空间

  • RGB是从硬件的角度提出的颜色模型,在与人眼匹配的过程中可能存在一定的差异,HSV色彩空间是一种面向视觉感知的颜色模型。HSV色彩空间从心理学和视觉的角度出发,指出人眼的色彩知觉主要包含三要素:色调(Hue,也称为色相)、饱和度(Saturation)、亮度(Value),色调指光的颜色,饱和度是指色彩的深浅程度,亮度指人眼感受到的光的明暗程度。
  • 色调:色调与混合光谱中的主要光波长相关,例如“赤橙黄绿青蓝紫”分别表示不同的色调。如果从波长的角度考虑,不同波长的光表现为不同的颜色,实际上它们体现的是色调的差异。
  • 饱和度:指相对纯净度,或一种颜色混合白光的数量。纯谱色是全饱和的,像深红色(红加白)和淡紫色(紫加白)这样的彩色是欠饱和的,饱和度与所加白光的数量成反比。
  • 亮度:反映的是人眼感受到的光的明暗程度,该指标与物体的反射度有关。对于色彩来讲,如果在其中掺入的白色越多,则其亮度越高;如果在其中掺入的黑色越多,则其亮度越低。
  • 在具体实现上,我们将物理空间的颜色分布在圆周上,不同的角度代表不同的颜色。因此,通过调整色调值就能选取不同的颜色,色调的取值区间为[0,360]。色调取不同值时,所代表的颜色如表4-1所示,两个角度之间的角度对应两个颜色之间的过渡色。
    e73353e25a3cd79c1009ff44ff258d9
  • 饱和度为一比例值,范围是[0,1],具体为所选颜色的纯度值和该颜色最大纯度值之间的比值。饱和度的值为0时,只有灰度。亮度表示色彩的明亮程度,取值范围也是[0,1]。
  • 在HSV色彩模型中,取色变得更加直观。例如,取值“色调=0,饱和度=1,亮度=1”,则当前色彩为深红色,而且颜色较亮;取值“色调=120,饱和度=0.3,亮度=0.4”,则当前色彩为浅绿色,而且颜色较暗。
  • 在从RGB色彩空间转换到HSV色彩空间之前,需要先将RGB色彩空间的值转换到[0,1]之间,然后再进行处理。具体处理方法为:
    dd4cb7deeb1fe2e95cf2630aeedeb03
    4a2e0c364e7f0fcf42834cb25180dd0
    下载

CIELab色彩空间、CIELuv色彩空间、Bayer色彩空间

1
2
3
4

类型转换函数

  • 在opencv中我们使用cv2.cvtColor()函数实现色彩空间的变换。该函数能够实现多个色彩空间之间的转换。其语法格式为:
  • dst=cv2.cvtColor(src, code[,dstCn])
  • 式中:
  • dst:输出图像,与原始输入图像具有同样的数据类型和深度
  • src:表示原始的输入图像。可以是8位无符号图像、16位无符号图像,或者单精度浮点数等
  • code:是色彩空间转换码
  • dstCn:是目标图像的通道数。如果参数位默认的0,则通道数自动通过原始输入图像和code得到

510

  • 这里需要注意,BGR色彩空间与传统的RGB色彩空间不同。对于一个标准的24位位图,BGR色彩空间中第1个8位(第1个字节)存储的是蓝色组成信息(Blue component),第2个8位(第2个字节)存储的是绿色组成信息(Green component),第3个8位(第3个字节)存储的是红色组成信息(Red component)。同样,其第4个、第5个、第6个字节分别存储蓝色、绿色、红色组成信息,以此类推。
  • 颜色空间的转换都用到了如下约定:
  • 8位图像值的范围是[0,255]。
  • 16位图像值的范围是[0,65 535]。
  • 浮点数图像值的范围是[0.0~1.0]。
  • 对于线性转换来说,这些取值范围是无关紧要的。但是对于非线性转换来说,输入的RGB图像必须归一化到其对应的取值范围内,才能获取正确的转换结果。
  • 例如,对于8位图,其能够表示的灰度级有28=256个,也就是说,在8位图中,最多能表示256个状态,通常是[0,255]之间的值。但是,在很多色彩空间中,值的范围并不恰好在[0,255]范围内,这时,就需要将该值映射到[0,255]内。
  • 例如,在HSV或HLS色彩空间中,色调值通常在[0,360)范围内,在8位图中转换到上述色彩空间后,色调值要除以2,让其值范围变为[0,180),以满足存储范围,即让值的分布位于8位图能够表示的范围[0,255]内。又例如,在CIELab*色彩空间中,a通道和b通道的值范围是[−127,127],为了使其适应[0,255]的范围,每个值都要加上127。不过需要注意,由于计算过程存在四舍五入,所以转换过程并不是精准可逆的。

类型转换实例

通过数组观察转换效果

  • cv2.cvtColor()函数

案例1:将BGR图像转化位灰度图像

1
2
3
4
5
6
7
import cv2
import numpy as np

img = np.random.randint(0, 255, (5,5,3), dtype=np.uint8)
gray = cv2.cvtColor(img, code=cv2.COLOR_BGR2GRAY)
print('原始图像:\n', img.shape,'\n', img)
print('灰度图像:\n', gray.shape, '\n', gray)
原始图像:
 (5, 5, 3) 
 [[[244  90 138]
  [200  87 208]
  [ 19 178 133]
  [ 12 241  82]
  [244  82 135]]

 [[128 139 221]
  [113 103 113]
  [139 210 168]
  [ 67  41   2]
  [194  79 236]]

 [[121 144 166]
  [147 133 140]
  [182  48  25]
  [127 224 138]
  [115 205 145]]

 [[ 97 179 240]
  [ 73 113 227]
  [ 69 157 154]
  [ 44 109  55]
  [ 64  67 239]]

 [[ 74 113 216]
  [ 63  82  85]
  [155 220 149]
  [ 23  73 101]
  [137  84  56]]]
灰度图像:
 (5, 5) 
 [[122 136 146 167 116]
 [162 107 189  32 139]
 [148 137  56 187 177]
 [188 143 146  85 118]
 [139  81 191  76  82]]

案例2:将灰度图像转化位BGR图像

1
2
3
4
5
6
7
import cv2
import numpy as np

gray = np.random.randint(0, 255, size=(4,4), dtype=np.uint8)
bgr = cv2.cvtColor(gray, code=cv2.COLOR_GRAY2BGR)
print('灰度图像:\n', gray.shape, '\n', gray)
print('原始图像:\n', bgr.shape,'\n', bgr)
灰度图像:
 (4, 4) 
 [[155 250  53  39]
 [ 38  43  14  50]
 [242  89  92 150]
 [ 79 197   8 139]]
原始图像:
 (4, 4, 3) 
 [[[155 155 155]
  [250 250 250]
  [ 53  53  53]
  [ 39  39  39]]

 [[ 38  38  38]
  [ 43  43  43]
  [ 14  14  14]
  [ 50  50  50]]

 [[242 242 242]
  [ 89  89  89]
  [ 92  92  92]
  [150 150 150]]

 [[ 79  79  79]
  [197 197 197]
  [  8   8   8]
  [139 139 139]]]

案例3:将图像在BGR和RGB之间相互转化

1
2
3
4
5
6
7
8
9
10
import cv2
import numpy as np

img = np.random.randint(0, 255, size=(4,4,3), dtype=np.uint8)
bgr2rgb = cv2.cvtColor(img, code=cv2.COLOR_BGR2RGB)
rgb2bgr = cv2.cvtColor(bgr2rgb, code=cv2.COLOR_RGB2BGR)
print('原始图像:\n', img.shape,'\n', img)
print('BGR2RGB图像:\n', bgr2rgb.shape,'\n', bgr2rgb)
print('RGB2BGR原始图像:\n', rgb2bgr.shape,'\n', rgb2bgr)
print('再转化回来的图像和原来的图像一样吗:',(rgb2bgr==img).all())
原始图像:
 (4, 4, 3) 
 [[[147  19 239]
  [ 25  66 220]
  [ 50 241  99]
  [113 242 128]]

 [[154 102 243]
  [150 122 183]
  [ 85 107 204]
  [ 18 225 246]]

 [[ 42  71 251]
  [140 122 251]
  [104  84 195]
  [168 122 245]]

 [[168 127  46]
  [240  46  43]
  [252  75 114]
  [ 51 205  40]]]
BGR2RGB图像:
 (4, 4, 3) 
 [[[239  19 147]
  [220  66  25]
  [ 99 241  50]
  [128 242 113]]

 [[243 102 154]
  [183 122 150]
  [204 107  85]
  [246 225  18]]

 [[251  71  42]
  [251 122 140]
  [195  84 104]
  [245 122 168]]

 [[ 46 127 168]
  [ 43  46 240]
  [114  75 252]
  [ 40 205  51]]]
RGB2BGR原始图像:
 (4, 4, 3) 
 [[[147  19 239]
  [ 25  66 220]
  [ 50 241  99]
  [113 242 128]]

 [[154 102 243]
  [150 122 183]
  [ 85 107 204]
  [ 18 225 246]]

 [[ 42  71 251]
  [140 122 251]
  [104  84 195]
  [168 122 245]]

 [[168 127  46]
  [240  46  43]
  [252  75 114]
  [ 51 205  40]]]
再转化回来的图像和原来的图像一样吗: True

图像处理实例

1
from get_show_img import get_show
1
2
3
4
5
6
7
import cv2

img = cv2.imread('data/cat.jpg')
gray = cv2.cvtColor(img, code=cv2.COLOR_BGR2GRAY)
img_rgb = cv2.cvtColor(gray, code=cv2.COLOR_GRAY2RGB) # 此处的彩色图像是由灰度图像转化而来,所以三个通道的值都是一样的,故显示还是灰度图像
get_show(gray)
get_show(img,img_rgb)

png

png

1
2
3
4
5
import cv2

img = cv2.imread('data/dog.jpg')
img_rgb = cv2.cvtColor(img, code=cv2.COLOR_BGR2RGB)
get_show(img, img_rgb)

4output_20_0

HSV色彩空间讨论

  • HSV色彩空间从心理学和视觉的角度出发,提出人眼的色彩知觉主要包含三要素:
  • H:色调(Hue,也称为色相)——[0, 360]
  • S:饱和度(Saturation)——[0, 1]
  • V:亮度(Value)——[0, 1]

获取指定颜色

-在opencv中,测试RGB色彩空间中不同颜色的值转换到HSV色彩空间后的对应值

1
2
3
4
5
6
7
8
9
10
import cv2
import numpy as np
from get_show_img import get_show

imgBlue = np.zeros([1,1,3], dtype=np.uint8)
imgBlue[0,0,0] = 255
BlueHSV = cv2.cvtColor(imgBlue, cv2.COLOR_BGR2HSV)
print('BGR:', imgBlue)
print('HSV:', BlueHSV)
get_show(imgBlue, BlueHSV)
BGR: [[[255   0   0]]]
HSV: [[[120 255 255]]]

png

1
2
3
4
5
6
7
8
9
10
import cv2
import numpy as np
from get_show_img import get_show

imgGreen = np.zeros([1,1,3], dtype=np.uint8)
imgGreen[0,0,1] = 255
GreenHSV = cv2.cvtColor(imgGreen, cv2.COLOR_BGR2HSV)
print('BGR:', imgGreen)
print('HSV:', GreenHSV)
get_show(imgGreen, GreenHSV)
BGR: [[[  0 255   0]]]
HSV: [[[ 60 255 255]]]

4output_24_1

1
2
3
4
5
6
7
8
9
10
import cv2
import numpy as np
from get_show_img import get_show

imgRed = np.zeros([1,1,3], dtype=np.uint8)
imgRed[0,0,2] = 255
RedHSV = cv2.cvtColor(imgRed, cv2.COLOR_BGR2HSV)
print('BGR:', imgRed)
print('HSV:', RedHSV)
get_show(imgRed, RedHSV)
BGR: [[[  0   0 255]]]
HSV: [[[  0 255 255]]]

png

1
get_show(BlueHSV, GreenHSV, RedHSV)

png

标记指定颜色

  • 指将图像内的特定颜色标记出来,即将一幅图像内的其他颜色屏蔽,仅仅将特定颜色显示出来
  • opencv中通过函数cv2.Range()来判断图像内像素点的像素值是否在指定的范围内,其语法格式为:
  • dst=cv2.inRange(src, lowerb, upperb)
  • 式中:
  • dst表示输出结果,大小和src一致。
  • src表示要检查的数组或图像。
  • lowerb表示范围下界
  • upperb表示范围上界
  • 返回值dst与src等大小,其值取决于src中对应位置上的值是否取决于区间[lowerb, upperb]内:
  • 如果src值处于该指定区间内,则dst中对应位置上的值为255。
  • 如果src值不处于该指定区间内,则dst中对应位置上的值为0。

使用inRange函数锁定特定值

使用函数cv2.inRange()将某个图像内的在[100, 200]内的值标注出来。

1
2
3
4
5
6
7
8
9
import cv2
import numpy as np

img = 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=
 [[ 62 201 173 139 122]
 [148 141  91  98  18]
 [235  48 203  68   9]
 [ 95 132 171 104  54]
 [252  16 223 155  72]]
mask=
 [[  0   0 255 255 255]
 [255 255   0   0   0]
 [  0   0   0   0   0]
 [  0 255 255 255   0]
 [  0   0   0 255   0]]

通过掩码的按位与显示ROI

正常显示某个图像内的感兴趣区域(ROI),而将其余区域显示为黑色。

1
2
3
4
5
6
7
8
9
10
11
import cv2
import numpy as np

img = np.ones([5,5], dtype=np.uint8)*155
mask = np.zeros([5,5], dtype=np.uint8)
mask[0:3, 0] = 1
mask[2:5, 2:4] = 1
roi = cv2.bitwise_and(img, img, mask=mask)
print('img=\n', img)
print('mask=\n', mask)
print('roi=\n', roi)
img=
 [[155 155 155 155 155]
 [155 155 155 155 155]
 [155 155 155 155 155]
 [155 155 155 155 155]
 [155 155 155 155 155]]
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=
 [[155   0   0   0   0]
 [155   0   0   0   0]
 [155   0 155 155   0]
 [  0   0 155 155   0]
 [  0   0 155 155   0]]

显示特定颜色值

  • 分别提取opencv的logo图像内的红色,绿色,蓝色
  • 需要注意,在实际提取颜色时,往往不是提取一个特定的值,而是提取一个颜色区间。
  • 例如,在opencv中的HSV模式内,蓝色在H通道内的值是120。在提取蓝色时,通常将“蓝色值120”附近的一个区间的值作为提取范围。该区间的半径通常为10~20左右,例如通常提取[120-10, 120+10]范围内的值来指定蓝色。
  • 相比之下,HSV模式中S通道、V通道的值的取值范围一般是[100, 255]。这主要是因为,当饱和度和亮度太低时,计算出来的色调可能就不可靠了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import cv2

img = cv2.imread('data/opencv-logo-small.png')
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
minBlue = np.array([100, 100, 100])
maxBlue = np.array([140, 255, 255])
blue_ind = cv2.inRange(hsv, minBlue, maxBlue)
blue = cv2.bitwise_and(img, img, mask=blue_ind)

minGreen = np.array([50, 100, 100])
maxGreen = np.array([70, 255, 255])
green_ind = cv2.inRange(hsv, minGreen, maxGreen)
green = cv2.bitwise_and(img, img, mask=green_ind)

minRed = np.array([150, 100, 100])
maxRed = np.array([178, 255, 255])
red_ind = cv2.inRange(hsv, minRed, maxRed)
red = cv2.bitwise_and(img, img, mask=red_ind)

get_show(blue, green, red)

png

标记肤色

  • 在标记特点颜色的基础上,可以将标注范围进一步推广到特定的对象上。例如,通过分析可以估算出肤色在HSV色彩空间内的范围值。在HSV空间内筛选出肤色范围内的值,即可将图像内包含肤色的部分提出来。
  • 这里将肤色范围划定为:
  • 色调值在[5, 170]之间
  • 饱和度值在[25, 166]之间

提取一幅图像内的皮肤部分

1
2
3
4
5
6
7
8
9
10
import cv2
import numpy as np


hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
minFuSe = np.array([5, 25, 0])
maxFuSe = np.array([170, 166, 255])
img_pifu = cv2.inRange(hsv, minFuSe, maxFuSe)
pifu = cv2.bitwise_and(img, img, mask=img_pifu)
get_show(img, pifu)

4output_36_0

实现艺术效果

调整HSV色彩空间内V通道的值,观察其处理结果

1
2
3
4
5
6
7
import cv2

img = cv2.imread('data/dog.jpg')
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
hsv[:,:,2] = 255
art = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)
get_show(art)


png

alpha通道

  • 在RGB色彩空间三个通道的基础上,还可以加上一个A通道,也叫alpha通道,表示透明度。这种4个通道的色彩空间被称为RGBA色彩空间,PNG图像是一种典型的4通道图像。
  • alpha通道的赋值范围是[0, 1], 或者是[0, 255], 表示从透明到不透明
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import cv2
import numpy as np

img = cv2.imread('data/opencv-logo-small.png')
img_rgba = cv2.cvtColor(img, cv2.COLOR_BGR2RGBA)
b,g,r,a = cv2.split(img_rgba)
a[:,:] = 125
rgba125 = cv2.merge([b,g,r,a])
a[:,:] = 0
rgba0 = cv2.merge([b,g,r,a])
a[:,:] = 200
rgba200 = cv2.merge([b,g,r,a])
cv2.imshow('img', np.hstack([img_rgba, rgba0, rgba125, rgba200]))
cv2.waitKey()
cv2.destroyAllWindows()
cv2.imwrite('tmp/rgb0.png', rgba0)
cv2.imwrite('tmp/rgb125.png', rgba125)
cv2.imwrite('tmp/rgb200.png', rgba200)
True
-------------本文结束感谢您的阅读-------------