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

pwa*_*wan 2 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)
Run Code Online (Sandbox Code Playgroud)

我们如何实际可视化我在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]])
Run Code Online (Sandbox Code Playgroud)

alk*_*asm 16

什么是HSV颜色

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

圆柱形色彩空间

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

色调表示为从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_bound和之间查找图像的所有值upper_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
Run Code Online (Sandbox Code Playgroud)

如果我们想要选择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将包含所有值,不会影响我们的掩码.但是,仅S包括0到50之间的值(朝向圆柱体的中心),并且V将仅包括从200到255的值(朝向圆柱体的顶部).

可视化HSV的一系列颜色

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

例如,您可以为值范围从左到右创建值的渐变S,从顶部到底部为V值范围创建,然后循环遍历每个H值.整个程序看起来像这样:

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()
Run Code Online (Sandbox Code Playgroud)

范围值的Gif

现在这个gif H每帧显示一个新值.从左到右,我们有最小值到最大值S,从上到下我们有最小值到最大值V.此动画中显示的每种颜色都将从您的图像中选择,成为您的一部分mask.

制作自己的inRange()函数

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

功能背后的想法很简单:找到每个通道的值落在min和之间的所有通道max,然后找到&所有通道.

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
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

如何找到合适的颜色

找到用于特定图像的正确值可能有点棘手.不过,有一种简单的实验方法.您可以在OpenCV中创建跟踪栏,并使用它们来控制每个通道的最小值和最大值,并在每次更改值时让Python程序更新您的掩码.我为此制作了一个程序,你可以在这里抓住GitHub .这是一个.gif使用它的动画,用于演示:

cspaceThresh程序的Gif

  • 上帝,你的工具真是太棒了。 (2认同)