在Python/PIL中检测HSV颜色空间的阈值(从RGB转换)
我想把一张RGB彩色图片转换成黑白RGB图片。这里的黑色像素是指它的HSV值在某个特定范围内,而其他的像素则是白色。
现在我先创建一张新图片,然后通过遍历原图片的数据来生成一个新的像素值列表,最后用.putdata()
把这个列表放到新图片里,形成最终的效果。
我觉得应该有更快的方法来做到这一点,比如使用.point()
,但是.point()
似乎是处理从0到255的值,而不是直接处理像素。有没有类似.point()
的操作可以直接对像素进行转换呢?
4 个回答
2
这个解决方案是基于保罗的代码。我修复了除零错误,并实现了从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
5
编辑 2:现在这个结果和保罗的代码是一样的,正如它应该的那样...
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)
这个代码会输出色相(hue)从0到360,饱和度(saturation)从0到1,亮度(value)从0到1。我查看了结果的图像格式,感觉效果不错。
我在读你的问题时不太确定你是否只对HSV中的“亮度”这个值感兴趣。如果是这样的话,你可以跳过大部分代码。
然后你可以根据这些值选择像素,并使用类似下面的代码将它们设置为1(或者白色/黑色):
newimage = (v > 0.3) * 1
21
好的,这个确实有效(修复了一些溢出错误):
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的定义。我会在有更多时间的时候再仔细看看。肯定还有可以加速的地方,也可能有一些bug。如果你发现了,请告诉我。谢谢!
结果:
从这个颜色轮开始:

我得到了这些结果:
色相:

明度:

饱和度:
