OpenCV-Python教程:色彩空间变换(cvtColor)

原文链接:http://www.juzicode.com/opencv-python-cvtcolor

返回Opencv-Python教程

在数字图像中,最常见的彩色模型是RGB模型(红、绿、蓝,在OpenCV中彩色图像组织的顺序是B-G-R,仍然是RGB模型),这种模型是硬件处理的常用模型,比如采集图像的CCD传感器、显示图像的显示器等等,符合描述人类眼睛观察的则是HSV(色度、饱和度、亮度)模型。

1、色彩转换cvtColor()

OpenCV则提供了各种彩色模型(色彩空间)相互转换的接口,比如可以从BGR转换为HSV,HSV转换为BGR,也可以从BGR转换为灰度图。

色彩空间的转换函数cvtColor()的接口形式:

dst=cv2.cvtColor(src, code[, dst[, dstCn]])

src为源图像对象;code是OpenCV中色彩空间定义的宏常量,可以通过colors = [i for i in dir(cv) if i.startswith(‘COLOR_’)]的方法遍历出所有的色彩空间转换的名称,在4.5.2版本中转换方法有274种之多。比较常用的有COLOR_BGR2GRAY、COLOR_GRAY2BGR、COLOR_BGR2HSV、COLOR_BGR2RGB,这些名称都能见名知意。dstCn为目标图像的通道数,如果设置为0,会自动从源图像计算目标图像的通道数。

下面这个例子读取图像后图像默认是BGR色彩空间,分别转换为GRAY和HSV色彩空间:

import cv2
print('VX公众号: 桔子code / juzicode.com')
print('cv2.__version__:',cv2.__version__)

img1 = cv2.imread('..\\lena.jpg') 
img2 = cv2.imread('..\\opencv-logo.png') 

img_ret1 = cv2.cvtColor(img1,cv2.COLOR_BGR2GRAY)
print('img_ret1.shape:',img_ret1.shape)
print('img1[161,199]:    ',img1[161,199])
print('img_ret1[161,199]:',img_ret1[161,199])
cv2.imshow('lena-cvtColor',img_ret1)

img_ret2 = cv2.cvtColor(img2,cv2.COLOR_BGR2HSV)
print('img_ret2.shape:',img_ret2.shape)
print('img2[100,200]:    ',img2[100,200])
print('img_ret2[100,200]:',img_ret2[100,200])
cv2.imshow('logo-cvtColor',img_ret2)

cv2.waitKey(0)

运行结果:

VX公众号: 桔子code / juzicode.com
cv2.__version__: 4.5.2
img_ret1.shape: (512, 512)
img1[161,199]:     [109 105 201]
img_ret1[161,199]: 134
img_ret2.shape: (739, 600, 3)
img2[100,200]:     [  0   0 255]
img_ret2[100,200]: [  0 255 255]

从lena图像的运行结果看,转换为GRAY的图像变成了只有黑白2种颜色也就是灰度图像,从图像的shape属性看这时图像也是单通道的。opencv-log图像经过HSV转换后再显示,看起来“颜色”发生了变化,这是因为HSV变量用imshow显示的效果导致的,并不是说其颜色发生了变化,这点也可以通过再将HSV色彩转换回BGR色彩验证。

2、HSV色彩空间

HSV色彩空间通常用一个倒锥形表示:

在这个倒锥型中,小括号中的数值以OpenCV CV_8U类型的数据为例。从下往上的红色直线表示亮度V,越往上表示的亮度越大,OpenCV中的取值范围是0~255;从圆心往外的半径是饱和度V,表示的是颜色的纯度,越远离圆心值越大,取值范围是0~255;在水平截面上的圆的角度表示色度H,因为CV_8U类型的数值表示范围到不了360,在OpenCV中的取值范围实际为是0~180(等于圆的角度除以2),其中红色的色度为0,绿色的色度为120°,OpenCV中的数值为60,蓝色的色度为240°,OpenCV中的数值为120。

BGR转换为HSV的方法为:

V=max(R,G,B)
S=(max(R,G,B)-min(R,G,B))*255/max(R,G,B)
如果max(R,G,B)=0, S=0
如果R=max(R,G,B),H=(G-B)/(R-min(R,G,B))* 30
如果G=max(R,G,B),H=60+(B-R)/(G-min(R,G,B))* 30
如果B=max(R,G,B),H=120+(R-G)/(B-min(R,G,B))* 30
如果R=G=B,H=0; 如果H<0,H=H+360

下面我们以opencv-logo.png为例解释下HSV色彩空间和BGR色彩空间的对应关系:

import cv2
print('VX公众号: 桔子code / juzicode.com')
print('cv2.__version__:',cv2.__version__)

def show_img(win_name,img,wait_time=0,img_ratio=0.5,is_show=True):
    if is_show is not True:
        return 
    rows = img.shape[0]
    cols = img.shape[1]
    cv2.namedWindow(win_name, cv2.WINDOW_NORMAL )#cv2.WINDOW_AUTOSIZE)
    cv2.resizeWindow(win_name,(int(cols*img_ratio),int(rows*img_ratio)))
    cv2.imshow(win_name,img)
    #cv2.waitKey(wait_time)
    
img = cv2.imread('..\\opencv-logo.png') 
img_ret = cv2.cvtColor(img,cv2.COLOR_BGR2HSV)
show_img('logo',img)
h,s,v = cv2.split(img_ret)
show_img('h',h)
show_img('s',s)
show_img('v',v)

minVal, maxVal, minLoc, maxLoc = cv2.minMaxLoc(h) 
print('h[20,20]:',h[20,20]) #原图白色
print('h[600,20]:',h[600,20])#原图黑色
print('h[50,300]:',h[50,300])#原图红色
print('h[400,50]:',h[400,50])#原图绿色
print('h[500,500]:',h[500,500])#原图蓝色
print('h: minVal, maxVal=',minVal, maxVal)
minVal, maxVal, minLoc, maxLoc = cv2.minMaxLoc(s) 
print('s[20,20]:',s[20,20])
print('s[600,20]:',s[600,20])
print('s[50,300]:',s[50,300])
print('s[400,50]:',s[400,50])
print('s[500,500]:',s[500,500])
print('s: minVal, maxVal=',minVal, maxVal)
minVal, maxVal, minLoc, maxLoc = cv2.minMaxLoc(v) 
print('v[20,20]:',v[20,20])
print('v[600,20]:',v[600,20])
print('v[50,300]:',v[50,300])
print('v[400,50]:',v[400,50])
print('v[500,500]:',v[500,500])
print('v: minVal, maxVal=',minVal, maxVal)

cv2.waitKey(0)

运行结果:

cv2.__version__: 4.5.2
h[20,20]: 0
h[600,20]: 0
h[50,300]: 0
h[400,50]: 60
h[500,500]: 120
h: minVal, maxVal= 0.0 120.0
s[20,20]: 0
s[600,20]: 0
s[50,300]: 255
s[400,50]: 255
s[500,500]: 255
s: minVal, maxVal= 0.0 255.0
v[20,20]: 255
v[600,20]: 0
v[50,300]: 255
v[400,50]: 255
v[500,500]: 255
v: minVal, maxVal= 0.0 255.0

 

这个例子中用到了minMaxLoc()方法,可以计算出图像中的最小和最大值,这幅图像中色度H最大的是120对应的是蓝色。通过对比h分量和原图,原图的红色区域几乎是黑色,正好对应红色的色度值为0,原图的绿色区域稍暗,蓝色区域最亮,对应了绿色的色度值为60,蓝色的色度值为120,我们也可以在原图的红绿蓝3个区域中分别找3个像素点打印出H的值:

h[50,300]: 0    #红色像素
h[400,50]: 60   #绿色
h[500,500]: 120 #蓝色

对比s分量和原图,原图中红绿蓝三个区域对应的s分量都为最亮的白色,而其他区域都为最暗的黑色,因为红绿蓝3个区域对应的颜色非常“纯”,纯粹到只有三原色中的其中一种颜色,而白色区域BGR三色都为255,每种颜色都有,所以是最“不纯”的,这样其S值就为0,而文字部分的黑色区域其S值也为0。

对比v分量和原图,R、G、B三色中谁的值最大,表示的就是V的值,白色区域和红绿蓝区域都有一个分量的值为255,所以V的值就为255,而文字部分的黑色区域其V值也为0。

 

原文链接:http://www.juzicode.com/archives/5508

 

扩展阅读:

  1. Opencv-Python教程

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注