使用 Python / PIL 检测 HSV 颜色空间中的阈值(来自 RGB)
Posted
技术标签:
【中文标题】使用 Python / PIL 检测 HSV 颜色空间中的阈值(来自 RGB)【英文标题】:Detecting thresholds in HSV color space (from RGB) using Python / PIL 【发布时间】:2011-06-20 21:28:30 【问题描述】:我想拍摄一张 RGB 图像并将其转换为黑白 RGB 图像,如果像素的 HSV 值在某个范围之间,则该像素为黑色,否则为白色。
目前我创建了一个新图像,然后通过迭代其数据创建一个新像素值列表,然后.putdata()
该列表形成新图像。
感觉应该有一种更快的方法来做到这一点,例如与.point()
,但似乎.point()
没有得到给定的像素,而是从0 到255 的值。是否有.point()
变换但在像素上?
【问题讨论】:
图片是否需要转为HSV?您可能会考虑在您的要求范围内进行转换以找到足够的 RGB 要求窗口(转换不是线性的,所以想知道近似值是否可以) 你使用 NumPy 吗?当事情不是“标准”图像调整时,我通常会避开大多数用于 numpy 数组操作的 PIL 函数。 我可以使用 numpy,虽然我不熟悉它。是的,转换必须是 hsv,但如果不是,我将如何使用 rgb 进行转换? 例如,如果您的 HSV 窗口很窄,并且您想从特定颜色的任何方向捕获一个具有 10 个 HSV 值的球体中的所有颜色,那么近似RGB 中的相同窗口,但它可能更像是一个椭圆形窗口,围绕着 RGB 空间中的相同颜色。 我使用scikits.image.color.rgb2hsv()
来获取 HSV 值,如果你能负担得起安装 numpy/scipy/et al 的费用,那就太好了。但是@paul 我敢打赌 HSV 函数不在 PIL 中,因为没有任何图像二进制格式将其数据存储为 HSV 值。此外,最近 PIL 中并没有添加太多东西...... 好的,这确实工作(修复了一些溢出错误):
import numpy, Image
i = Image.open(fp).convert('RGB')
a = numpy.asarray(i, int)
R, G, B = a.T
m = numpy.min(a,2).T
M = numpy.max(a,2).T
C = M-m #chroma
Cmsk = C!=0
# Hue
H = numpy.zeros(R.shape, int)
mask = (M==R)&Cmsk
H[mask] = numpy.mod(60*(G-B)/C, 360)[mask]
mask = (M==G)&Cmsk
H[mask] = (60*(B-R)/C + 120)[mask]
mask = (M==B)&Cmsk
H[mask] = (60*(R-G)/C + 240)[mask]
H *= 255
H /= 360 # if you prefer, leave as 0-360, but don't convert to uint8
# Value
V = M
# Saturation
S = numpy.zeros(R.shape, int)
S[Cmsk] = ((255*C)/V)[Cmsk]
# H, S, and V are now defined as integers 0-255
它基于***对HSV 的定义。当我有更多时间时,我会看看它。肯定有加速,也许还有错误。如果你找到任何东西,请告诉我。干杯。
结果:
从这个色轮开始:
我得到了这些结果:
色相:
价值:
饱和度:
【讨论】:
我从未见过这种使用面具的方式。我必须承认它比我的方法更容易遵循。 我会删除它们。您介意使用相同的色轮发布您自己的结果吗? .png 可以在这里找到:palette.com/hsvwheel.png 色度 (c
) 始终定义为 M-m
,无论型号如何。 Value v
的定义应在您的代码中更改为 M。值始终为M
,并且特定于六角锥模型。
使用您的代码会导致除以零,因为C
似乎为零(使用 hsvwheel.png)?!
我修复了上面的代码并添加了 RGB_TO_HSL。请参阅我在此线程中的帖子。【参考方案2】:
编辑 2:现在返回的结果与 Paul 的代码相同,因为它应该...
import numpy, scipy
image = scipy.misc.imread("test.png") / 255.0
r, g, b = image[:,:,0], image[:,:,1], image[:,:,2]
m, M = numpy.min(image[:,:,:3], 2), numpy.max(image[:,:,:3], 2)
d = M - m
# Chroma and Value
c = d
v = M
# Hue
h = numpy.select([c ==0, r == M, g == M, b == M], [0, ((g - b) / c) % 6, (2 + ((b - r) / c)), (4 + ((r - g) / c))], default=0) * 60
# Saturation
s = numpy.select([c == 0, c != 0], [0, c/v])
scipy.misc.imsave("h.png", h)
scipy.misc.imsave("s.png", s)
scipy.misc.imsave("v.png", v)
它给出了从 0 到 360 的色调,从 0 到 1 的饱和度和从 0 到 1 的值。我查看了图像格式的结果,它们看起来不错。
通过阅读您的问题,我不确定是否只是您感兴趣的 HSV 中的 V 中的“值”。如果是,那么您可以绕过大部分代码。
然后,您可以根据这些值选择像素,并使用以下方法将它们设置为 1(或白色/黑色):
newimage = (v > 0.3) * 1
【讨论】:
我收回了。这只是一个约定,灰度值定义为 0 的色调。只要您知道它们的存在,NAN 的工作就很好。 哇!几乎降到一个班轮!您缺少一个括号,并且未定义d
。色相测试中的条纹消失了,但还有其他问题。我想知道 scipy 的 misc.image 工具是否在做一些奇怪的事情?
我一定是在编辑过程中复制并粘贴了。顺便说一句,干得好。我一直希望在 numpy 中有一个 select
-type 函数。现在我知道有一个!
这是我使用你的算法得到的色调:i.imgur.com/JKUsa.jpg 你也得到同样的结果吗?你用的是什么版本的 scipy?
这是一张单色图像。圆圈中心的像素具有非零的色调,但您的算法说它们为零。我采取了额外的步骤,将您的数组转换为“uint8”数组并通过 PIL (Image.fromarray((255*h/360).astype('uint8'),'L').save('hsvwheel_Hb2.png')
) 输出,我得到了相同的结果。这意味着该错误与 scipy 的 misc.imsave()
函数无关。你的算法还是有问题。【参考方案3】:
此解决方案基于 Paul 的代码。我修复了 DivByZero 错误并将 RGB 实现为 HSL。还有HSL转RGB:
import numpy
def rgb_to_hsl_hsv(a, isHSV=True):
"""
Converts RGB image data to HSV or HSL.
:param a: 3D array. Retval of numpy.asarray(Image.open(...), int)
:param isHSV: True = HSV, False = HSL
:return: H,S,L or H,S,V array
"""
R, G, B = a.T
m = numpy.min(a, 2).T
M = numpy.max(a, 2).T
C = M - m #chroma
Cmsk = C != 0
# Hue
H = numpy.zeros(R.shape, int)
mask = (M == R) & Cmsk
H[mask] = numpy.mod(60 * (G[mask] - B[mask]) / C[mask], 360)
mask = (M == G) & Cmsk
H[mask] = (60 * (B[mask] - R[mask]) / C[mask] + 120)
mask = (M == B) & Cmsk
H[mask] = (60 * (R[mask] - G[mask]) / C[mask] + 240)
H *= 255
H /= 360 # if you prefer, leave as 0-360, but don't convert to uint8
# Saturation
S = numpy.zeros(R.shape, int)
if isHSV:
# This code is for HSV:
# Value
V = M
# Saturation
S[Cmsk] = ((255 * C[Cmsk]) / V[Cmsk])
# H, S, and V are now defined as integers 0-255
return H.swapaxes(0, 1), S.swapaxes(0, 1), V.swapaxes(0, 1)
else:
# This code is for HSL:
# Value
L = 0.5 * (M + m)
# Saturation
S[Cmsk] = ((C[Cmsk]) / (1 - numpy.absolute(2 * L[Cmsk]/255.0 - 1)))
# H, S, and L are now defined as integers 0-255
return H.swapaxes(0, 1), S.swapaxes(0, 1), L.swapaxes(0, 1)
def rgb_to_hsv(a):
return rgb_to_hsl_hsv(a, True)
def rgb_to_hsl(a):
return rgb_to_hsl_hsv(a, False)
def hsl_to_rgb(H, S, L):
"""
Converts HSL color array to RGB array
H = [0..360]
S = [0..1]
l = [0..1]
http://en.wikipedia.org/wiki/HSL_and_HSV#From_HSL
Returns R,G,B in [0..255]
"""
C = (1 - numpy.absolute(2 * L - 1)) * S
Hp = H / 60.0
X = C * (1 - numpy.absolute(numpy.mod(Hp, 2) - 1))
# initilize with zero
R = numpy.zeros(H.shape, float)
G = numpy.zeros(H.shape, float)
B = numpy.zeros(H.shape, float)
# handle each case:
mask = (Hp >= 0) == ( Hp < 1)
R[mask] = C[mask]
G[mask] = X[mask]
mask = (Hp >= 1) == ( Hp < 2)
R[mask] = X[mask]
G[mask] = C[mask]
mask = (Hp >= 2) == ( Hp < 3)
G[mask] = C[mask]
B[mask] = X[mask]
mask = (Hp >= 3) == ( Hp < 4)
G[mask] = X[mask]
B[mask] = C[mask]
mask = (Hp >= 4) == ( Hp < 5)
R[mask] = X[mask]
B[mask] = C[mask]
mask = (Hp >= 5) == ( Hp < 6)
R[mask] = C[mask]
B[mask] = X[mask]
m = L - 0.5*C
R += m
G += m
B += m
R *=255.0
G *=255.0
B *=255.0
return R.astype(int),G.astype(int),B.astype(int)
def combineRGB(r,g,b):
"""
Combines separated R G B arrays into one array = image.
scipy.misc.imsave("rgb.png", combineRGB(R,G,B))
"""
rgb = numpy.zeros((r.shape[0],r.shape[1],3), 'uint8')
rgb[..., 0] = r
rgb[..., 1] = g
rgb[..., 2] = b
return rgb
【讨论】:
【参考方案4】:我认为最快的结果是通过 numpy.该函数看起来类似于(更新,在示例中添加了更多细节):
limg = im.convert("L", ( 0.5, 0.5, 0.5, 0.5 ) )
na = numpy.array ( limg.getdata() )
na = numpy.piecewise(na, [ na > 128 ], [255, 0])
limg.pytdata(na)
limg.save("new.png")
理想情况下,您可以使用分段函数而无需先转换为黑白,这将更像原始示例。语法类似于:
na = numpy.piecewise(na, [ na[0] > 128 ], [255, 0])
但是,您必须小心,因为 RGB 图像的返回值是 3 或 4 元组。
【讨论】:
使用 numpy 的asarray()
函数是常用的转换技术,因为它节省了一步。我不清楚你要去哪里piecewise()
。你能详细说明一下吗?
请详细说明这里发生了什么。以上是关于使用 Python / PIL 检测 HSV 颜色空间中的阈值(来自 RGB)的主要内容,如果未能解决你的问题,请参考以下文章
使用`cv::inRange` (OpenCV) 为颜色检测选择正确的 HSV 上下边界
如何在OpenCV中为InRange阈值选择颜色的最佳HSV值
python使用openCV加载图像并将BGR格式转换成HSV格式定义HSV格式中需要分离颜色的掩码(掩模)区间(mask)并使用mask信息进行颜色分离BGR格式的图像转化为RGB并可视化