HSV转RGB颜色转换

47 投票
9 回答
86588 浏览
提问于 2025-04-18 14:02

有没有办法在Python中使用pygame模块把HSV颜色参数转换成RGB颜色参数?我试过下面的代码,但返回的值完全不对。

import colorsys
test_color = colorsys.hsv_to_rgb(359, 100, 100)
print(test_color)

而这段代码返回了以下荒谬的结果

(100, -9900.0, -9900.0)

这显然不是RGB颜色。我哪里出错了呢?

9 个回答

2

我准备了一个向量化的版本,它大约快了10倍。

def hsv_to_rgb(h, s, v):
    shape = h.shape
    i = int_(h*6.)
    f = h*6.-i

    q = f
    t = 1.-f
    i = ravel(i)
    f = ravel(f)
    i%=6

    t = ravel(t)
    q = ravel(q)

    clist = (1-s*vstack([zeros_like(f),ones_like(f),q,t]))*v

    #0:v 1:p 2:q 3:t
    order = array([[0,3,1],[2,0,1],[1,0,3],[1,2,0],[3,1,0],[0,1,2]])
    rgb = clist[order[i], arange(prod(shape))[:,None]]

    return rgb.reshape(shape+(3,))
5

如果你在使用Numpy数组,那么可以直接用matplotlib.colors.hsv_to_rgb这个工具。

import numpy as np
from matplotlib.colors import hsv_to_rgb
# This will create a nice image of varying hue and value
hsv = np.zeros((512, 512, 3))
hsv[..., 0] = np.linspace(0, 1, 512)
hsv[..., 1] = 1.
hsv[..., 2] = np.linspace(0, 1, 512)[:, np.newaxis]
rgb = hsv_to_rgb(hsv)

请注意,输入和输出的图像值都是在[0, 1]这个范围内。

10

Hue参数的值应该在0到1之间变化。

import colorsys
test_color = colorsys.hsv_to_rgb(359/360.0, 1, 1)
26

如果你想要更好的性能,最好避免使用导入的库,自己写优化过的代码。

这里是GIMP的代码转成Python的版本,经过测试准确率达到99%,并且在性能上有所提升:
(这段代码的效果几乎和colorsys完全一样)

scalar = float # a scale value (0.0 to 1.0)
def hsv_to_rgb( h:scalar, s:scalar, v:scalar, a:scalar ) -> tuple:
    if s:
        if h == 1.0: h = 0.0
        i = int(h*6.0); f = h*6.0 - i
        
        w = v * (1.0 - s)
        q = v * (1.0 - s * f)
        t = v * (1.0 - s * (1.0 - f))
        
        if i==0: return (v, t, w, a)
        if i==1: return (q, v, w, a)
        if i==2: return (w, v, t, a)
        if i==3: return (w, q, v, a)
        if i==4: return (t, w, v, a)
        if i==5: return (v, w, q, a)
    else: return (v, v, v, a)

输出结果:

>>> hsv_to_rgb( 359/360.0, 1.0, 1.0, 1.0 )
(1.0, 0.0, 0.016666666666666607, 1.0)

像上面那样使用if链比用elif要快。

使用像Cyber的回答中提到的包装器,会让解释器多做几步操作。
另外,Cyber示例中的for循环用得不当会严重影响性能。

如果你想要稍微提高一点性能,可以这样做:
(我不会说这是性能最好的方法,但肯定会更好一些)

scalar = float # a scale value (0.0 to 1.0)
def hsv_to_rgb( h:scalar, s:scalar, v:scalar, a:scalar ) -> tuple:
    a = int(255*a)
    if s:
        if h == 1.0: h = 0.0
        i = int(h*6.0); f = h*6.0 - i
        
        w = int(255*( v * (1.0 - s) ))
        q = int(255*( v * (1.0 - s * f) ))
        t = int(255*( v * (1.0 - s * (1.0 - f)) ))
        v = int(255*v)
        
        if i==0: return (v, t, w, a)
        if i==1: return (q, v, w, a)
        if i==2: return (w, v, t, a)
        if i==3: return (w, q, v, a)
        if i==4: return (t, w, v, a)
        if i==5: return (v, w, q, a)
    else: v = int(255*v); return (v, v, v, a)

^ 这样可以确保int()的输出范围在255以内(输入还是一样的)。

>>> hsv_to_rgb( 359/360.0, 1.0, 1.0, 1.0 )
(255, 0, 4, 255)

注意:这段代码的测试准确率和GIMP的colorsys一样高(99%正确):

小贴士:尽量避免使用第三方库,如果可以的话,尝试直接的方法。
例外情况:像PIL或NumPy这样的编译C扩展,或者像PyOpenGL这样的ctypes包装器(使用DLL)。

60

这个函数需要的是小数形式的 s(饱和度)和 v(亮度),而不是百分比。你需要把它们除以100。

>>> import colorsys

# Using percent, incorrect
>>> test_color = colorsys.hsv_to_rgb(359,100,100)
>>> test_color
(100, -9900.0, -9900.0)

# Using decimal, correct
>>> test_color = colorsys.hsv_to_rgb(1,1,1)
>>> test_color
(1, 0.0, 0.0)

如果你想要不经过标准化的RGB元组,这里有一个函数可以用来封装 colorsys 函数。

def hsv2rgb(h,s,v):
    return tuple(round(i * 255) for i in colorsys.hsv_to_rgb(h,s,v))

功能示例

>>> hsv2rgb(0.5,0.5,0.5)
(64, 128, 128)

撰写回答