在OpenCV + Python中使用色彩空间进行图像分割

六眼飞鱼酱① 提交于 2020-02-04 02:33:50

在OpenCV + Python中使用色彩空间进行图像分割

什么是色彩空间?

在最常见的颜色空间RGB(红色绿色蓝色)中,颜色以红色,绿色和蓝色分量表示。用更专业的术语来说,RGB将颜色描述为三个组成部分的元组。每个分量可以取0到255之间的值,其中元组(0, 0, 0)代表黑色,(255, 255, 255)代表白色。

RGB被认为是三原色“加法”颜色空间,可以想象颜色是由大量红色,蓝色和绿色的光照射到黑色背景上产生的。

颜色 RGB值
红色 255,0,0
橙色 255,128,0
粉色 25,153,255

RGB是五个主要色彩空间模型之一,每个模型都有许多分支。颜色空间太多,因为不同的颜色空间可用于不同的目的。

在印刷领域,CMYK很有用,因为它描述了从白色背景产生颜色所需的颜色组合。RGB中的0元组是黑色,而CMYK中的0元组是白色。我们的打印机包含青色,品红色,黄色和黑色的墨水罐。

HSV和HSL是色相,饱和度和亮度/亮度的描述,对识别图像的对比度特别有用。这些色彩空间常用于软件的选色工具和网页设计中。

实际上,颜色是一种连续现象,意味着存在无限数量的颜色。但是,色彩空间通过离散结构(固定数量的整数整数值)表示颜色,这是可以接受的,因为人眼和感知也受到限制。颜色空间完全能够代表我们能够区分的所有颜色。

OpenCV中的色彩空间和读取图像

首先,您需要设置您的环境。本文将假定您在系统上安装了Python3.x。请注意,尽管当前的OpenCV版本是3.x,但是要导入的软件包的名称仍然是cv2:

import cv2

导入OpenCV之后,您可以查看OpenCV提供的所有色彩空间转换,并将它们全部保存到变量中:

flags = [i for i in dir(cv2) if i.startswith("COLOR_")]
# 查看OpenCV提供的所有色彩空间转换的数量
len(flags)
# 查找COLOR_RGB2HSV所在的索引
flags.index('COLOR_RGB2HSV')

您将需要matplotlib.pyplot查看图像,并需要NumPy进行一些图像处理。如果您还没有Matplotlib或NumPy的安装,您将需要pip3 install matplotlib和pip3 install numpy进行安装:

import cv2
import matplotlib.pyplot as plt
import numpy as np

img = cv2.imread('nemo.jpg')
# cv中默认BGR格式,在matplotlib中需要转换为RGB进行显示图像
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
plt.imshow(img)
plt.show()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7B5X2hGj-1580710370547)(C:\Users\67231\Desktop\ImageSegmentation\nemo.jpg)]

在RGB颜色空间中可视化Nemo

HSV是用于按颜色分割的颜色空间的不错选择,但是要了解为什么,让我们通过可视化其像素的颜色分布来比较RGB和HSV颜色空间中的图像。一个3D图很好地显示了这一点,每个轴代表色彩空间中的一个通道。

#%% 绘制彩色三维散点图
import cv2
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
# from matplotlib import cm
from matplotlib import colors

img = cv2.imread('nemo.jpg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
r,g,b = cv2.split(img)
fig = plt.figure(figsize=(8,6),dpi=80)
axis = fig.add_subplot(1,1,1,projection="3d")

# 像素颜色设置
pixel_colors = img.reshape((np.shape(img)[0]*np.shape(img)[1],3))
# print(pixel_colors)
# 归一化
norm = colors.Normalize(vmin=-1.,vmax=1.)
norm.autoscale(pixel_colors)
# 转换成list
pixel_colors = norm(pixel_colors).tolist()
# print('*'*20)
# print(pixel_colors)

# 显示三维散点图
axis.scatter(r.flatten(),g.flatten(),b.flatten(),facecolors=pixel_colors,marker='.')
axis.set_xlabel("Red")
axis.set_ylabel("Green")
axis.set_zlabel("Bule")
plt.show()

结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nSbsCCr8-1580710370548)(C:\Users\67231\Desktop\ImageSegmentation\三维rgb散点图.png)]

从该图可以看到,图像的橙色部分几乎覆盖了红色,绿色和蓝色值的整个范围。由于Nemo的各个部分在整个图上伸展,因此根据RGB值的范围在RGB空间中分割Nemo并不容易。

在HSV颜色空间中可视化Nemo

我们在RGB空间中看到了Nemo,所以现在让我们在HSV空间中查看他并进行比较。

如上所述,HSV代表色相,饱和度和值(或亮度),并且是圆柱形的色彩空间。颜色或色调建模为围绕中心垂直轴旋转的角度尺寸,该轴代表值通道。值从暗(底部为0)到顶部亮。第三个轴是饱和度,它定义了从垂直轴上的最小饱和度到离中心最远的最大饱和度的色调阴影:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5lXiJRfD-1580710370549)(C:\Users\67231\Desktop\ImageSegmentation\hsv.webp)]

#%% 在HSV中的图像生成彩色3D散点图
import cv2
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
# from matplotlib import cm
from matplotlib import colors

img = cv2.imread('nemo.jpg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
hsv_img = cv2.cvtColor(img, cv2.COLOR_RGB2HSV)
h,s,v = cv2.split(hsv_img)
fig = plt.figure(figsize=(8,6),dpi=80)
axis = fig.add_subplot(1,1,1,projection="3d")

# 像素颜色设置
pixel_colors = img.reshape((np.shape(img)[0]*np.shape(img)[1],3))
# print(pixel_colors)
# 归一化
norm = colors.Normalize(vmin=-1.,vmax=1.)
norm.autoscale(pixel_colors)
# 转换成list
pixel_colors = norm(pixel_colors).tolist()
# print('*'*20)
# print(pixel_colors)

# 显示三维散点图
axis.scatter(h.flatten(),s.flatten(),v.flatten(),facecolors=pixel_colors,marker='.')
axis.set_xlabel("hue")
axis.set_ylabel("saturation")
axis.set_zlabel("value")
plt.show()

结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rgMVvejq-1580710370549)(C:\Users\67231\Desktop\ImageSegmentation\hsv三维散点图.png)]

在HSV空间中,Nemo的橙色更加集中化并且在视觉上可分离。橙色的饱和度和值确实会有所不同,但是它们大多位于沿色相轴的较小范围内。

选择颜色范围

让我们根据一个简单的橙色阈值Nemo。您可以通过查看上面的图或在线使用颜色选择应用程序(例如RGB to HSV工具)来选择范围。此处选择的色板是浅橙色和几乎是红色的深橙色:

import matplotlib.pyplot as plt
from matplotlib.colors import hsv_to_rgb
import numpy as np

light_orange = (1,190,200)
dark_orange = (18,255,255)
lo_square = np.full((10,10,3),light_orange,dtype=np.uint8)/255.
do_square = np.full((10,10,3),dark_orange,dtype=np.uint8)/255.

plt.subplot(121)
plt.imshow(hsv_to_rgb(do_square))
plt.subplot(122)
plt.imshow(hsv_to_rgb(lo_square))
plt.show()

结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KAnED2NJ-1580710370549)(C:\Users\67231\Desktop\ImageSegmentation\oarnge.png)]

利用同样的方法选出白色的阈值

light_white = (0,0,200)
dark_white = (145,60,255)
利用彩色图像进行图像分割

一旦获得合适的色彩范围,您就可以cv2.inRange()尝试设定Nemo阈值。inRange()接受三个参数:图像,较低范围和较高范围。它返回ndarray图像大小的二进制mask(1和0),其中的值1表示范围内的值,0值表示范围外的值。

import cv2
import matplotlib.pyplot as plt
import numpy as np


img = cv2.imread('nemo.jpg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
hsv_img = cv2.cvtColor(img, cv2.COLOR_RGB2HSV)

light_orange = (1,190,200)
dark_orange = (18,255,255)

light_white = (0,0,200)
dark_white = (145,60,255)

orange_mask = cv2.inRange(hsv_img,light_orange,dark_orange)
white_mask = cv2.inRange(hsv_img,light_white,dark_white)
mask = orange_mask+white_mask

result = cv2.bitwise_and(img, img,mask=mask)

plt.subplot(121)
plt.imshow(mask,cmap="gray")
plt.subplot(122)
plt.imshow(result)
plt.show()

结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wYk0W3U2-1580710370550)(C:\Users\67231\Desktop\ImageSegmentation\1111111111111111111111111.png)]

结论

在本教程中,您已经了解了几种不同的色彩空间,如何在RGB和HSV色彩空间中分布图像,以及如何使用OpenCV在色彩空间之间进行转换并划分出范围。

总体而言,您已经了解了如何使用OpenCV中的色彩空间在图像中执行对象分割,并希望看到它也可以执行其他任务。这种分割技术简单,快速且可靠。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!