Python+OpenCV - 如何绘制HSV范围?

5
为提取颜色,我们有如下函数。
# 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]])

我想绘制范围内所有可能的值。这是可能的吗? - pwan
@AlexanderReynolds,你能教我如何绘制那个圆柱并可视化切割那块吗?我刚刚更新了我的问题。 - pwan
我认为现在可能更容易的做法是:创建一个渐变图像,从左到右从s_mins_max,从上到下从v_minv_max,然后循环遍历每个色调,以获得它看起来像什么的动画。我会为此做些准备,请给我一点时间。 - alkasm
1
我真的不明白这里的“range”是什么意思?在我的情况下,110〜130度都在范围内。但“H”和“V”呢? - pwan
回答你更新的帖子中问题的第二部分:upper = np.array([[[60, 255, 255]]], dtype=np.uint8); upper = cv2.cvtColor(upper, cv2.COLOR_HSV2BGR); upper_img = np.ones((500,500,3), dtype=np.uint8)*upper; cv2.imshow('', upper_img); cv2.waitKey(0) - alkasm
显示剩余2条评论
1个回答

27

什么是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之间的值。例如,如果您的图像是一个3x3图像(仅用于简单演示目的),具有3个通道,可能如下所示:

# 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值都将被包括在内,并且不会影响我们的掩码。但是,只有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()

范围值的动态图

这个gif图像展示了每一帧都有一个新的H值。从左到右是S值的最小到最大值,从上到下是V值的最小到最大值。在这个动画中出现的每个颜色都将从您的图像中选择为mask的一部分。

制作自己的inRange()函数

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

函数的思路很简单:找到每个通道的值介于minmax之间的位置,然后将所有通道&在一起。

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文档,以了解这确实是使用的公式。我们也可以进行验证。


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

如何找到合适的颜色

对于特定的图像来说,找到合适的颜色值可能有点棘手。不过有一种简单的方法可以进行实验。您可以在OpenCV中创建滑块,并使用它们控制每个通道的最小值和最大值,并让Python程序在更改值时更新您的遮罩。我为此编写了一个程序,您可以在GitHub上获取这里。这是一个动画.gif,演示了它的使用方法:

cspaceThresh程序的GIF


谢谢!这是一个非常深入的解释,非常有帮助。只有一个更愚蠢的问题,是否可能将其内联显示在IPython中?这样我就可以一次性地可视化多个范围进行比较。 - pwan
1
@AlexanderReynolds 圆柱模型在存储数据方面也更容易。但需要认识到,圆柱体中的所有黑色点实际上都是相同的颜色,尽管它们具有不同的H和S值。这可能会导致圆柱体中的颜色距离函数无法按预期工作。 - Rob Audenaerde
1
@RobAu 说得好,我接受了,谢谢你的意见。我知道对于 v=0hs 通道并不重要;现在我们正在讨论这个问题,我意识到这意味着在 RGB 中有更多的颜色(甚至没有考虑 OpenCV 的 HSV 模型中度数增量为 2 度的事实)。 - alkasm
2
神啊,你的工具真是太棒了。 - pwan
@KjMag 谢谢你的鼓励,我很高兴它对你有帮助! - alkasm
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接