OpenCV快速入门五:色彩空间转换

一:相关api

1:imread

imread(filename, cv.IMREAD_GRAYSCALE)

具体cv.imread及其使用方法和读取有中文路径的图片参考博客OpenCV学习一:图片读取&保存

2:cvtColor

cvtColor(	src,           #输入
			code[,         #色彩空间转换的代码或表示.
			dst=None[,     #输出
			dstCn=None]]#目标图像通道数
)

3getTrackbarPos

getTrackbarPos(滑动条名字,窗口名,输出当前值)

该函数将输入图像从一种颜色空间转换为另一种颜色空间。在从 RGB 颜色空间转换的情况下,应明确指定通道的顺序(RGB 或 BGR)。请注意,OpenCV 中的默认颜色格式通常称为 RGB,但实际上是 BGR(字节反转)。因此,标准(24 位)彩色图像中的第一个字节将是 8 位蓝色分量,第二个字节将是绿色,第三个字节将是红色。然后第四、第五和第六个字节将是第二个像素(蓝色,然后是绿色,然后是红色),依此类推。
R、G 和 B 通道值的常规范围是:
CV_8U 图像为 0 到 255
0 到 65535 用于 CV_16U 图像
CV_32F 图像的 0 到 1

在线性变换的情况下,范围无关紧要。但是在非线性变换的情况下,输入 RGB 图像应该被归一化到适当的值范围以获得正确的结果。例如,如果您有一个 32 位浮点图像直接从 8 位图像转换而来,没有任何缩放,那么它将具有 0——255 的值范围,而不是函数假定的 0——1。因此,在调用cvtColor之前,需要先缩小图像:

img *= 1./255;
cvtColor(img,img,COLOR_BGR2Luv);
如果对 8 位图像使用cvtColor
转换会丢失一些信息。对于许多应用程序,这并不明显,但建议在需要全范围颜色或在操作前转换图像然后再转换回来的应用程序中使用 32 位图像。
如果转换添加 alpha 通道,其值将设置为相应通道范围的最大值:CV_8U 为 255,CV_16U 为 65535,CV_32F 为 1。

二:代码演示

import cv2
import numpy as np

def callback(userdata):
    pass

cv2.namedWindow('color', cv2.WINDOW_NORMAL)
img = cv2.imdecode(np.fromfile(r"C:\Users\DMr\Pictures\个人\xiaoqi.jpg",dtype=np.uint8),1)


colorspaces = [cv2.COLOR_BGR2RGBA, cv2.COLOR_BGR2BGRA,
               cv2.COLOR_BGR2GRAY, cv2.COLOR_BGR2HSV,
               cv2.COLOR_BGR2YUV]
cv2.createTrackbar('curcolor', 'color', 0, 4, callback)

while True:
    index = cv2.getTrackbarPos('curcolor', 'color')

    #颜色空间转换API
    cvt_img = cv2.cvtColor(img, colorspaces[index])

    cv2.imshow('color', cvt_img)
    key = cv2.waitKey(10)
    if key & 0xFF == ord('q'):
        break

cv2.destroyAllWindows()

输出
在这里插入图片描述

三:常见色彩类型

RGB:人眼的色彩空间
BGR:OpenCV,Windows
HSV/HSB-是什么颜色、颜色有多深、颜色有多亮
YUV色彩空间-视频
a
在这里aa插入图片描述
在这里插asf入图片描述

1:RGB 的局限性

RGB 是我们接触最多的颜色空间,由三个通道表示一幅图像,分别为红色®,绿色(G)和蓝色(B)。这三种颜色的不同组合可以形成几乎所有的其他颜色。

RGB 颜色空间是图像处理中最基本、最常用、面向硬件的颜色空间,比较容易理解。

RGB 颜色空间利用三个颜色分量的线性组合来表示颜色,任何颜色都与这三个分量有关,而且这三个分量是高度相关的,所以连续变换颜色时并不直观,想对图像的颜色进行调整需要更改这三个分量才行。

自然环境下获取的图像容易受自然光照、遮挡和阴影等情况的影响,即对亮度比较敏感。而 RGB 颜色空间的三个分量都与亮度密切相关,即只要亮度改变,三个分量都会随之相应地改变,而没有一种更直观的方式来表达。

但是人眼对于这三种颜色分量的敏感程度是不一样的,在单色中,人眼对红色最不敏感,蓝色最敏感,所以 RGB 颜色空间是一种均匀性较差的颜色空间。如果颜色的相似性直接用欧氏距离来度量,其结果与人眼视觉会有较大的偏差。对于某一种颜色,我们很难推测出较为精确的三个分量数值来表示。

所以,RGB 颜色空间适合于显示系统,却并不适合于图像处理。

2:HSV 颜色空间

基于上述理由,在图像处理中使用较多的是 HSV 颜色空间,它比 RGB 更接近人们对彩色的感知经验。非常直观地表达颜色的色调、鲜艳程度和明暗程度,方便进行颜色的对比。

在 HSV 颜色空间下,比 BGR 更容易跟踪某种颜色的物体,常用于分割指定颜色的物体。

HSV 表达彩色图像的方式由三个部分组成:

  • Hue(色调、色相)0-360就是我们平时所说的红、绿,如果你分的更细的话可能还会有洋红、草绿等等;在HSV模型中,用度数来描述色相,其中红色对应0度,绿色对应120度,蓝色对应240度。

  • Saturation(饱和度、色彩纯净度)0-255色彩的深浅度(0-100%) ,对于一种颜色比如红色,我们可以用浅红——大红——深红——红得发紫等等语言来描述它(请原谅一个纯理科生的匮乏的颜色系统),对应在画水彩的时候即一种颜料加上不同分量的水形成不同的饱和度。

  • Value(明度)0-255这个在调整屏幕亮度的时候比较常见。

用下面这个圆柱体来表示 HSV 颜色空间,圆柱体的横截面可以看做是一个极坐标系 ,H 用极坐标的极角表示,S 用极坐标的极轴长度表示,V 用圆柱中轴的高度表示。
在这里插入图片描述

Hue 用角度度量,取值范围为0~360°,表示色彩信息,即所处的光谱颜色的位置。表示如下:
在这里插入图片描述
颜色圆环上所有的颜色都是光谱上的颜色,从红色开始按逆时针方向旋转,Hue=0 表示红色,Hue=120 表示绿色,Hue=240 表示蓝色等等。

在 RGB中 颜色由三个值共同决定,比如黄色为即 (255,255,0);在HSV中,黄色只由一个值决定,Hue=60即可。

HSV 圆柱体的半边横截面(Hue=60):
在这里插入图片描述

其中水平方向表示饱和度,饱和度表示颜色接近光谱色的程度。饱和度越高,说明颜色越深,越接近光谱色;饱和度越低,说明颜色越浅,越接近白色。饱和度为0表示纯白色。取值范围为0~100%,值越大,颜色越饱和。

竖直方向表示明度,决定颜色空间中颜色的明暗程度,明度越高,表示颜色越明亮,范围是 0-100%。明度为0表示纯黑色(此时颜色最暗)。
当S=1 V=1时,H所代表的任何颜色被称为纯色;
当S=0时,即饱和度为0,颜色最浅,最浅被描述为灰色(灰色也有亮度,黑色和白色也属于灰色),灰色的亮度由V决定,此时H无意义;
当V=0时,颜色最暗,最暗被描述为黑色,因此此时H(无论什么颜色最暗都为黑色)和S(无论什么深浅的颜色最暗都为黑色)均无意义。

可以通俗理解为:

在Hue一定的情况下,饱和度减小,就是往光谱色中添加白色,光谱色所占的比例也在减小,饱和度减为0,表示光谱色所占的比例为零,导致整个颜色呈现白色。

明度减小,就是往光谱色中添加黑色,光谱色所占的比例也在减小,明度减为0,表示光谱色所占的比例为零,导致整个颜色呈现黑色。

HSV 对用户来说是一种比较直观的颜色模型。我们可以很轻松地得到单一颜色,即指定颜色角H,并让V=S=1,然后通过向其中加入黑色和白色来得到我们需要的颜色。增加黑色可以减小V而S不变,同样增加白色可以减小S而V不变。例如,要得到深蓝色,V=0.4 S=1 H=240度。要得到浅蓝色,V=1 S=0.4 H=240度。

HSV 的拉伸对比度增强就是对 S 和 V 两个分量进行归一化(min-max normalize)即可,H 保持不变。

RGB颜色空间更加面向于工业,而HSV更加面向于用户,大多数做图像识别这一块的都会运用HSV颜色空间,因为HSV颜色空间表达起来更加直观!

RGB➡HSV

  1. V = max(R, G, B)/255.0f——亮度V就是RGB值中最大的那个值进行归一化。
    推论:纯色的RGB中肯定有一个是255。同时RGB值也不可能有3个255,因为3个255为白色,白色为对于任何色彩H,V=1而S=0时的产物。而V=1 S=0并不是纯色。同时如果V=0,那么RGB三者中的最大值是0,即GRB都为0,也就是说该像素是黑色。
  2. S = (max(R, G, B) - min(R, G, B))/(float)max(R, G, B)——饱和度S是RGB中最大值和最小值的差值与最大值的比值。
    推论:
    纯色(S=1 V=1)的RGB值中必定有一个0,因为当S=1,max(R, G, B) - min(R, G, B) == m ax(R, G, B),即RGBMin =0。这也说明了白色(RGB(255,255,255)并不是纯色)。
    当S = 0时,RGBMax-RGBMin == 0,即RGB,此时颜色呈不同程度的灰色(由白到黑,亮度由V而定,因为V=RGBMax*100/255,V越高,RGBMaxRG==B就越高,灰色越亮))。这也可以从上面给出的矩形图看出。
    因此对于纯色来说,RGB中必有一个255和一个0。
    公式换算:在这里aa插入图片描述

HSV➡RGB

在这里插入图aa片描述

3:HSL

在这里插入图a片描述
HSL 和 HSV 比较类似,这里一起介绍。HLS 也有三个分量,hue(色相)、saturation(饱和度)、lightness(亮度)。

HSL 和 HSV 的区别就是最后一个分量不同,HLS 的是 light(亮度),HSV 的是 value(明度)。可以到这个 网页 尝试一下。

HSL 中的 L 分量为亮度,亮度为100,表示白色,亮度为0,表示黑色;HSV 中的 V 分量为明度,明度为100,表示光谱色,明度为0,表示黑色。

下面是 HSL 颜色空间圆柱体:
在这里插入图片描述
提取白色物体时,使用 HLS 更方便,因为 HSV 中的Hue里没有白色,白色需要由S和V共同决定(S=0, V=100)。而在 HLS 中,白色仅由亮度L一个分量决定。所以检测白色时使用 HSL 颜色空间更准确。

将上面这个 HSL 颜色空间图用来测试:

img = cv2.imread("hls.jpg")

# Convert BGR to HLS
imgHLS = cv2.cvtColor(img, cv2.COLOR_BGR2HLS)

# range of white color in L channel
# mask = cv2.inRange(imgHLS[:,:,1], lowerb=250, upperb=255)
mask = cv2.inRange(imgHLS, np.array([0,250,0]), np.array([255,255,255]))

# Apply Mask to original image
white_mask = cv2.bitwise_and(img, img, mask=mask)

输出
在这里插入图片描述

注意:在 OpenCV 中 HLS 三个分量的范围为:
H = [0,179]
L = [0,255]
S = [0,255]

注意:在 OpenCV 中 HSV 三个分量的范围为:
H = [0,180]
S = [0,255]
V = [0,255]

获取要跟踪物体颜色的HSV值:

对一个BGR值进行颜色空间转换,得到HSV值。

 blue = np.uint8([[[255,0,0]]])
 hsv_blue = cv2.cvtColor(blue, cv2.COLOR_BGR2HSV)
 print(hsv_blue)  #[[[120 255 255]]]

为了识别特定颜色的物体,获取到颜色所对应的HSV值很重要,这里说一下获取步骤:

1、在线取色器传图识色,可以在这里上传特定颜色的图片,获取这些颜色对应的RGB值。

2、假设获取到的是这样的数据:#869C90,#899F92,#8A9E92,#8A9F8E,下面将其进行转换得到HSV各通道的数值范围:

rgb = '#869C90,#899F92,#8A9E92,#8A9F8E'

rgb = rgb.split(',')

# 转换为BGR格式,并将16进制转换为10进制
bgr = [[int(r[5:7], 16), int(r[3:5], 16), int(r[1:3], 16)] for r in rgb]

# 转换为HSV格式
hsv = [list(cv2.cvtColor(np.uint8([[b]]), cv2.COLOR_BGR2HSV)[0][0]) for b in bgr]

hsv = np.array(hsv)
print('H:', min(hsv[:, 0]), max(hsv[:, 0]))
print('S:', min(hsv[:, 1]), max(hsv[:, 1]))
print('V:', min(hsv[:, 2]), max(hsv[:, 2]))

然后对其中的Hue值进行加10和减10(这里的10也可以为其他值,视具体情况而定),得到Hue的范围,还要指定S和V的范围:在这里插入图s片描述
最后整个HSV值的上限和下限为 [hue+10,100,100]和 [hue-10,255,255],S和V的下限值可以根据实际情况设置。

因为H=0和H=180都对应红色,所以对于红色的话,需要定义两个范围,并进行取或操作。

sensitivity = 10
lower_red_0 = np.array([0,100,100]) 
upper_red_0 = np.array([sensitivity,255,255])
lower_red_1 = np.array([180-sensitivity,100,100]) 
upper_red_1 = np.array([180,255,255])

mask_0 = cv2.inRange(hsv, lower_red_0, upper_red_0)
mask_1 = cv2.inRange(hsv, lower_red_1, upper_red_1)

mask = cv2.bitwise_or(mask_0, mask_1)

使用 cv2:inRange() 作为基于颜色的阈值。

然后就可以使用该 HSV 值范围进行目标物体的提取。

import cv2
import numpy as np
#cam = cv2.VideoCapture(0)
img = cv2.imread('C:\\Users\\DMr\\Pictures\\text\\phone.jpg', cv2.IMREAD_COLOR)
cv2.imshow("yuantu",img)
Obj_low = np.array([0,0,0])#找到对应对象的HSV颜色
Obj_high = np.array([179,157,79])
while True:
    #img = cam.read()[1]
    img = cv2.resize(img, (800,600) )
    blur_img = cv2.GaussianBlur(img,(23,23),0)#使用高斯滤波器对图像进行归一化
    cv2.imshow("ga", blur_img)
    HSV = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)#将图像转换为HSV颜色模型
    cv2.imshow("hsv", HSV)
    MASK1 = cv2.inRange(HSV, Obj_low, Obj_high)#在给定的HSV值范围内应用二进制阈值,黑白Obj_low和Obj_high。
    #MASK2 = cv2.inRange(HSV,Obj2_low,Obj2_high) #如果尝试跟踪两个不同的对象,则需要创建2个不同的蒙版,并最终在两个蒙版上使用“按位与”运算符。
    #mask = cv2.bitwise_and(mask1,mask2)
    MASK1 = cv2.erode(MASK1, None, iterations=2)#侵蚀和膨胀:侵蚀和膨胀填充阈值图像中的黑色和白色斑点。这样可使图像更清晰,平滑并突出主要对象
    MASK1 = cv2.dilate(MASK1, None, iterations =2)
    cv2.imshow("fushi",MASK1)
    cnts = cv2.findContours(MASK1.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2]
    center = None#轮廓:突出显示对象的图像片段。例如,如果将二进制阈值应用于具有(180,255)的图像,则大于180
              # 的像素将以白色突出显示,而其他则为黑色。白色部分称为轮廓。

    if len(cnts)>0 :
        c = max(cnts, key = cv2.contourArea)#在上面给定的图像中,整个白色边界区域是轮廓。轮廓可能不止一个,但主要对象的面积将最大。所以选择轮廓最大

        ((x,y), radius) = cv2.minEnclosingCircle(c)#得到主要物体的轮廓后,在轮廓上画一个圆
        M = cv2.moments(c)
        center = (int(M['m10']/ M['m00']), int(M['m01']/ M['m00']) )
        if radius>10:
            cv2.circle(img, center, 5, (0,0,255), -1)
            cv2.circle(img, center, int(radius), (0,0,255), 2)

    cv2.imshow("my window",img)
    k = cv2.waitKey(1)
    if k==27:
        break
img.release()
#cam.release()
cv2.destroyAllWindows()

HSL通道分离见上篇文章OpenCV学习四:TrackBar控件(滑动条)
好啦给你们补上了

每日“大饼”:
命运恰如潜流 动无常则 进止难期

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

氿 柒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值