python + opencv - 如何绘制hsv范围?

时间:2017-07-13 02:54:17

标签: python opencv

要提取颜色,我们有这个功能

# define range of blue color in HSV
lower_blue = np.array([110,50,50])
upper_blue = np.array([130,255,255])

# Threshold the HSV image to get only blue colors
mask = cv2.inRange(hsv, lower_blue, upper_blue)

我们如何实际可视化我在hsv空间定义的范围(lower_blue,upper_blue)? 另外我如何实际绘制hsv颜色,但它不起作用......? 我有这段代码:

upper = np.array([60, 255, 255])
upper = cv2.cvtColor(upper, cv2.COLOR_HSV2BGR)


upper = totuple(upper/-255)
print(upper)
plt.imshow([[upper]])

1 个答案:

答案 0 :(得分:14)

什么是HSV颜色

HSV与HSL(或OpenCV,HLS)一样,是圆柱形颜色空间之一。

cylindrical colorspaces

该名称在某种程度上描述了它们的值是如何被引用的。

色调表示为从0到360的度数(在OpenCV中,为了适应8位无符号整数格式,它们的度数除以2得到0到179之间的数字;因此OpenCV中的110为220度)。如果你要采用“范围”的色调值,就像从蛋糕上切下一片。你只是占了一小块蛋糕。

饱和度通道距离您的中心有多远---您所在的半径。中心绝对没有饱和度 - 只有从黑色到白色的灰色。如果您采用了这些值的范围,则类似于从圆柱体的外部剃掉,或从中心切出一个圆圈。例如,如果范围是0到255,则范围0到127将是仅延伸到半径的圆柱体;范围127到255将切割半径为半径的内圆柱。

价值渠道是一个有点混乱的名称;它不是完全黑暗的亮度,因为最高值代表直接颜色,而最低值代表黑色。这是圆柱体的高度。不难想象垂直切割圆柱片。

HSV值的范围

函数cv2.inRange(image, lower_bound, upper_bound)查找lower_boundupper_bound之间的所有图片值。例如,如果您的图像是3个通道的3x3图像(仅用于简单的演示目的),它可能看起来像这样:

# h channel    # s channel    # v channel
100 150 250    150 150 100    50  75  225
50  100 125    75  25  50     255 100 50
0   255 125    100 200 250    50  75  100

如果我们想要选择100到200之间的色调,那么我们的lower_b应该是[100, 0, 0]upper_b应该是[200, 255, 255]。这样我们的掩码只会考虑色调通道中的值,而不受饱和度和值的影响。这就是HSV如此受欢迎的原因---无论亮度或暗度如何,你都可以通过色调选择颜色,因此只需指定色调通道的最小值和最大值就可以选择暗红色和亮红色。

但是我们只想选择亮白色。回顾一下圆柱体模型---我们看到在圆柱体的顶部中心给出了白色,因此s值较低,v值较高,颜色较浅角度没关系。因此,lower_b看起来像[0, 0, 200]upper_b看起来像[255, 50, 255]。这意味着将包含所有H值,并且不会影响我们的掩码。但是,只包含0到50之间的S值(朝向圆柱体的中心),并且将仅包括从200到255的V值(朝向圆柱的顶部)。

可视化HSV

中的一系列颜色

可视化范围内所有颜色的一种方法是为两个通道中的每个通道创建两个方向的长度渐变,然后在更改的第三个通道上设置动画。

例如,您可以为S值的范围从左到右创建一个值的渐变,从上到下为V值的范围创建,然后循环遍历每个{{ 1}}价值。整个程序看起来像这样:

H

Gif of range values

现在这个gif每帧显示一个新的import numpy as np import cv2 lower_b = np.array([110,50,50]) upper_b = np.array([130,255,255]) s_gradient = np.ones((500,1), dtype=np.uint8)*np.linspace(lower_b[1], upper_b[1], 500, dtype=np.uint8) v_gradient = np.rot90(np.ones((500,1), dtype=np.uint8)*np.linspace(lower_b[1], upper_b[1], 500, dtype=np.uint8)) h_array = np.arange(lower_b[0], upper_b[0]+1) for hue in h_array: h = hue*np.ones((500,500), dtype=np.uint8) hsv_color = cv2.merge((h, s_gradient, v_gradient)) rgb_color = cv2.cvtColor(hsv_color, cv2.COLOR_HSV2BGR) cv2.imshow('', rgb_color) cv2.waitKey(250) cv2.destroyAllWindows() 值。从左到右,我们有最小到H个值,从上到下我们有最小到S个值。此动画中显示的每种颜色都将从您的图片中选出,成为V的一部分。

创建自己的inRange()函数

要完全理解OpenCV函数,最简单的方法就是使自己的函数完成任务。这根本不难,也不是很多代码。

该功能背后的想法很简单:找到每个通道的值落在maskmin之间的位置,然后找到所有通道的max

&

您可以阅读OpenCV docs,看看这确实是使用的公式。我们也可以验证它。

def inRange(img, lower_b, upper_b):
    ch1, ch2, ch3 = cv2.split(img)
    ch1m = (lower_b[0] <= ch1) & (ch1 <= upper_b[0])
    ch2m = (lower_b[1] <= ch2) & (ch2 <= upper_b[1])
    ch3m = (lower_b[2] <= ch3) & (ch3 <= upper_b[2])
    mask = ch1m & ch2m & ch3m
    return mask.astype(np.uint8)*255

如何找到合适的颜色

找到用于特定图像的正确的值可能有点棘手。不过,有一种简单的实验方法。您可以在OpenCV中创建跟踪栏,并使用它们来控制每个通道的最小值和最大值,并在每次更改值时让Python程序更新您的掩码。我为此制作了一个程序,你可以在GitHub here上获取。以下是使用它的动画lower_b = np.array([200,200,200]) upper_b = np.array([255,255,255]) mask = cv2.inRange(img, lower_b, upper_b) # OpenCV function mask2 = inRange(img, lower_b, upper_b) # above defined function print((mask==mask2).all()) # checks that the masks agree on all values # True 来演示:

Gif of cspaceThresh program