OpenCV 自学笔记21. RGB色彩空间和HSV色彩空间的理解

RGB色彩空间和HSV色彩空间的理解

本文的结构如下:
1、RGB色彩空间
2、HSV色彩空间(附HSV颜色分量范围表)
3、RGB到HSV的转换的Demo
  使用OpenCV实现RGB转HSV,并通过滑动条动态设定HSV阈值
  自己写程序,实现RGB转HSL


1、RGB色彩空间

RGB色彩空间源于使用阴极射线管的彩色电视,RGB分别代表三个基色(R-红色、G-绿色、B-蓝色),具体的色彩值由三个基色叠加而成。在图像处理中,我们往往使用向量表示色彩的值,如(0,0,0)表示黑色、(255, 255, 255)表示白色。其中,255表示色彩空间被量化成255个数,最高亮度值为255(255 = 2^8 - 1,即每个色彩通道用8位表示)。在这个色彩空间中,有256*256*256种颜色。RGB色彩空间如下图所示(图片来自百度百科)。是一个包含Red、Green、Blue的三维空间。

这里写图片描述

注:
1、在OpenCV中,RGB色彩空间的顺序是BGR,千万不要弄错了
2、在Java的Bitmap中,RGB色彩空间被表示为ARGB,其中A代表透明度

2、HSV色彩空间

HSV色彩空间(Hue-色调、Saturation-饱和度、Value-值)将亮度从色彩中分解出来,在图像增强算法中用途很广。在我本人接触的图像处理项目中,经常将图像从RGB色彩空间转换到了HSV色彩空间,以便更好地感知图像颜色,利用HSV分量从图像中提取感兴趣的区域。

HSV色彩空间也被称为HSB(色调、饱和度、亮度),在PS中常被用到。

HSV色彩空间如下图所示,用一个倒圆锥体表示整个色彩空间:

这里写图片描述

注:
1、H的范围是[0,360),S和V的范围是[0,1]。但是在OpenCV 中,HSV好像被规范化到了[0, 255],此处求大神指导。
2、另外,网上有人总结了HSV颜色对应RGB的分量范围,见下面的表格。参考自:
http://www.cnblogs.com/wangyblzu/p/5710715.html
http://blog.csdn.net/taily_duan/article/details/51506776

这里写图片描述

3、RGB到HSV的转换的Demo

(1) 使用OpenCV的cv2.cvtColor(src, cv2.COLOR_BGR2HSV)

OpenCV为我们提供了现成的函数cvtColor(),帮助我们将图像从BGR转换到HSV。

# -*- coding:utf-8 -*-

import cv2

"""
功能:读取一张图片,显示出来,并转化为HSV色彩空间
"""
image = cv2.imread('images/my_wife2.jpg') # 根据路径读取一张图片
cv2.imshow("BGR", image) # 显示图片


# 转化图片到HSV色彩空间
dst = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
cv2.imshow("HSV", dst) # 显示图片
cv2.waitKey(0) # 等待键盘触发事件,释放窗口

结果如下:

这里写图片描述

挺好看的美女,结果被弄成了这个样子。一些初学者包括我自己有时会问,为什么要把好端端的图片转成HSV色彩空间呢。其实这样做大有用处,比如我们要提取美女的头发区域,就可以通过设置HSV色彩空间的高低阈值来做。

# -*- coding:utf-8 -*-

import cv2
import numpy as np   # ------------------改变1

"""
功能:读取一张图片,显示出来,并转化为HSV色彩空间
"""
image = cv2.imread('images/my_wife2.jpg') # 根据路径读取一张图片
cv2.imshow("BGR", image) # 显示图片


# 转化图片到HSV色彩空间
dst = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
cv2.imshow("HSV", dst) # 显示图片


# 根据HSV提取头发 --------------------------------改变2
low_hsv = np.array([0, 0, 46])
high_hsv = np.array([200, 40, 220])
dst = cv2.inRange(dst, low_hsv, high_hsv)
cv2.imshow("result", dst) # 显示图片

cv2.waitKey(0) # 等待键盘触发事件,释放窗口

程序的运行效果如下。我们看到,头发区域显示为白色,这样我们就初步地提取出了头发区域。当然这个效果并不理想,是因为我们设定的阈值不好,如果能动态地设定阈值就好了。

这里写图片描述

我们可以利用滑块动态地设置阈值,一边拖动滑块,一边观察图像。OpenCV提供了createTrackbar(),用于创建滑块。代码如下:

# -*- coding:utf-8 -*-

import cv2
import numpy as np

"""
功能:读取一张图片,显示出来,转化为HSV色彩空间
     并通过滑块调节HSV阈值,实时显示
"""

image = cv2.imread('images/my_wife2.jpg') # 根据路径读取一张图片
cv2.imshow("BGR", image) # 显示图片

hsv_low = np.array([0, 0, 0])
hsv_high = np.array([0, 0, 0])

# 下面几个函数,写得有点冗余

def h_low(value):
    hsv_low[0] = value

def h_high(value):
    hsv_high[0] = value

def s_low(value):
    hsv_low[1] = value

def s_high(value):
    hsv_high[1] = value

def v_low(value):
    hsv_low[2] = value

def v_high(value):
    hsv_high[2] = value

cv2.namedWindow('image')
cv2.createTrackbar('H low', 'image', 0, 255, h_low) 
cv2.createTrackbar('H high', 'image', 0, 255, h_high)
cv2.createTrackbar('S low', 'image', 0, 255, s_low)
cv2.createTrackbar('S high', 'image', 0, 255, s_high)
cv2.createTrackbar('V low', 'image', 0, 255, v_low)
cv2.createTrackbar('V high', 'image', 0, 255, v_high)

while True:
    dst = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) # BGR转HSV
    dst = cv2.inRange(dst, hsv_low, hsv_high) # 通过HSV的高低阈值,提取图像部分区域
    cv2.imshow('dst', dst)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break
cv2.destroyAllWindows()

程序运行的效果如下,我们拉动滑块会自动改变HSV的高低阈值,进而根据高低阈值提取的图像区域也会改变:

这里写图片描述

(2) 自己编写程序实现RGB转HSL

根据维基百科,RGB转HSV的公式如下: 设 (r, g,b)分别是一个颜色的红、绿和蓝坐标,它们的值是在0到1之间的实数。设max等价于r,g和b中的最大者。设min等于这些值中的最小者。要找到在HSL空间中的 (h, s, l)值,这里的h ∈ [0,360)度是角度的色相角,而s, l ∈ [0,1]是饱和度和亮度,计算为:

这里写图片描述

如果要转化为HSV)h的值通常规范化到位于0到360°之间。而h = 0用于max = min的(就是灰色)时候而不是留下h未定义。HSL和HSV有同样的色相定义,但是其他分量不同。HSV颜色的s和v的值定义如下:

这里写图片描述

根据以上公式,我们可以写出RGB转HSV的代码:

def rgb2hsv(r, g, b):
    r, g, b = r / 255.0, g / 255.0, b / 255.0
    mx = max(r, g, b)
    mn = min(r, g, b)
    diff = mx - mn

    # 计算H
    if mx == mn:
        h = 0
    elif mx == r:
        if g >= b:
            h = 60 * ((g - b) / diff) + 0
        else:
            h = 60 * ((g - b) / diff) + 360
    elif mx == g:
        h = 60 * ((b - r) / diff) + 120
    elif mx == b:
        h = 60 * ((r - g) / diff) + 240

    # 先计算L
    l = (mx + mn) / 2.0

    # 再计算S
    if mx == min:
        s = 0
    elif l > 0 and l <= 0.5:
        s = (diff / l) / 2.0
    elif l > 0.5:
        s = (diff / (1 - l)) / 2.0

    return h, s, l

# 测试
h,s,l = rgb2hsv(200, 221, 221)
print('h=%f s=%f l=%f' % (h, s, l))

程序运行结果如下:
这里写图片描述

可以根据颜色代码表,判断我们的结果是否正确

这里写图片描述

附:

1、 HSV转RGB的公式

类似的,给定在HSV中 (h, s,
v)值定义的一个颜色,带有如上的h,和分别表示饱和度和明度的s和v变化于0到1之间,在RGB空间中对应的 (r, g,
b)三原色可以计算为(R,G,B变化于0到1之间):

这里写图片描述

对于每个颜色向量 (r, g, b),

这里写图片描述

(参考自维基百科)

2、RGB转GRAY的公式

请见:http://blog.csdn.net/xdrt81y/article/details/8289963

总结:
本文包括以下几个部分,
1、RGB和HSV色彩空间的理解
2、通过OpenCV实现了RGB到HSV的转换,包含一个使用滑动条的小例子
3、根据RGB转HSL的公式,自己编程实现了RGB转HSL的程序。

今天就到这里吧,拜拜~~

这里写图片描述

1. BGR_img = cv2.imread('fruit.jpg',1):读取名为'fruit.jpg'的图像,参数1表示以BGR格式读取,返回一个BGR格式的图像。 2. def bgr_to_hsv(BGR_img): 定义一个函数bgr_to_hsv,将输入的BGR格式的图像转换为HSV格式的图像。 3. rows, cols, _ = BGR_img.shape:获取BGR图像的行数、列数通道数(这里的通道数是3,即BGR)。 4. HSV_img = np.zeros_like(BGR_img):创建一个与BGR_img大小相同、所有值为0的HSV格式的图像。 5. for i in range(rows): for j in range(cols)::遍历BGR图像中的每个像素点。 6. b, g, r = BGR_img[i, j]:获取当前像素点的BGR值。 7. b, g, r = b / 255.0, g / 255.0, r / 255.0:将BGR值归一化到[0,1]之间。 8. cmax = max(b, g, r) cmin = min(b, g, r) delta = cmax - cmin:计算当前像素点的最大值、最小值差值。 9. if delta == 0: h = 0 elif cmax == r: h = 30 * (((g - b) / delta) % 6) elif cmax == g: h = 30 * (((b - r) / delta) + 2) else: h = 30 * (((r - g) / delta) + 4):根据不同情况计算当前像素点的色调值h。 10. if cmax == 0: s = 0 else: s = delta / cmax v = cmax:计算当前像素点的饱度值s亮度值v。 11. HSV_img[i, j] = [h, s * 255.0, v * 255.0]:将计算得到的HSV值赋给对应的像素点。 12. return HSV_img:返回转换后的HSV格式的图像。 13. HSV_img = bgr_to_hsv(BGR_img):调用bgr_to_hsv函数将BGR格式的图像转换为HSV格式的图像。 14. low = np.array([26,43,46]) high = np.array([34,255,255]):设置HSV阈值范围。 15. mask = np.zeros_like(HSV_img[:,:,0]):创建一个与HSV_img大小相同、所有值为0的二值化掩码图像。 16. b = HSV_img[:,:,0] g = HSV_img[:,:,1] r = HSV_img[:,:,2]:分别获取HSV图像中的色调、饱亮度通道。 17. mask[(b >= low[0]) & (b <= high[0]) & (g >= low[1]) & (g <= high[1]) & (r >= low[2]) & (r <= high[2])] = 255:根据阈值范围将符合条件的像素点的掩码值设为255,即白色,其余像素点为黑色。 18. height,width = mask.shape mask = np.reshape(mask,(height,width,1)) / 255:对掩码图像进行形状变换,将其转换为三通道的图像,并将其像素值归一化到[0,1]之间。 19. mask_BGR = mask * BGR_img:将掩码图像与原始BGR图像相乘得到掩码后的图像。 20. cv2.imwrite("mask_fruit.jpg", mask_BGR):将掩码后的图像保存到名为'mask_fruit.jpg'的文件中。 21. plt.show():显示掩码后的图像。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值