原文链接:http://www.juzicode.com/archives/5508
在数字图像中,最常见的彩色模型是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