HSV转RGB颜色转换

48

有没有一种方法可以使用Python的pygame模块将HSV颜色参数转换为RGB类型的颜色参数?我尝试了以下代码,但返回的值荒谬无比。

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

而这段代码返回了以下无意义的内容

(100, -9900.0, -9900.0)
这显然不是RGB。 我做错了什么?
10个回答

61

该函数期望使用十进制表示饱和度 (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)

谢谢。有没有内置函数可以“去规范化”RGB函数? - AvZ
我不知道是否有现成的函数可用,但你可以自己编写一个。请查看我的最新编辑以获取这样一个函数。 - Cory Kramer
4
色调参数也应该在0到1之间变化。 - Anand C U
是的,为了回应Anand C U和Paul Beloff所说的,你需要将你的色调值除以360,例如125/360(得到绿色)。否则你会一直得到红色。 - aheigins
对于hue,最大输入是多少? - Blue Robin

24

如果你追求性能,最好避免使用导入功能,并使用自己优化的代码。

这是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的答案中使用包装器会使解释器执行几个额外步骤。
此外,当像那样使用for循环时,Cyber示例是真正的性能杀手。

如果您想要稍微提高性能,只需这样做:
(我不会说这是最佳性能,但肯定更好)

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)

注意:此代码的测试结果与colorsys到GIMP一样准确(99%正确):

提示:尽可能避免使用第三方库,如果可以,请采用直接的方法。
例外情况:编译的C扩展库,如PIL或NumPy,或ctypes包装器,如PyOpenGL(使用DLL)。

那么...为什么我被降级了??为什么没有评论告诉我我做错了什么,以便我能更好地提高自己?? - Tcll
4
因为你的解决方案使用了从0到1的色调值,而在你的例子中使用了从0到360的值。 - sfat
3
如果我们谈论性能,我会避免使用列表,而是返回元组。 - Matteo Italia
+Matteo Italia:长大后,我实际上同意你的观点,如果有人(我在用手机)想要编辑这个答案并返回一个元组,请随意。 - Tcll
正如@sfat所提到的...请注意,此答案中的第一个函数假定所有hsvrgb值都是从0到1的浮点数(请参见colorsys文档)。给出的hsv_to_rgb(359,1,1)示例是误导性的。我在这里说这个是因为我认为这个错误已经在2017年的编辑中得到了修复(因此忽略了sfat的评论),但错误仍然存在。 - TimH
显示剩余3条评论

10
色调参数(Hue argument)也应该在0-1之间变化。
import colorsys
test_color = colorsys.hsv_to_rgb(359/360.0, 1, 1)

1
很好的评论,我一直想知道为什么我的代码总是出错! - aheigins

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]范围内。

谢谢,这节省了我很多时间! - Anshul Rai

2
由于问题标记为 ,我想提到 PyGame 可以在不同颜色方案之间进行转换。pygame.Color 对象可用于在 RGB 和 HSL/HSV 颜色方案之间进行转换。 hsva 属性:

获取或设置颜色的 HSVA 表示。HSVA 组件的范围为 H = [0, 360]、S = [0, 100]、V = [0, 100]、A = [0, 100]。

hsva = pygame.Color((red, green, blue, alpha)).hsva

color = pygame.Color(0)
color.hsva = (hue, saturation, value, alpha)
rgba = (color.r, color.g, color.b, color.a)

hsla属性:

获取或设置颜色的HSLA表示。 HSLA组件的范围为H = [0,360],S = [0,100],V = [0,100],A = [0,100]。

hsla = pygame.Color((red, green, blue, alpha)).hsla

color = pygame.Color(0)
color.hsla = (hue, saturation, lightness, alpha)
rgba = (color.r, color.g, color.b, color.a)

最简示例:

import pygame

pygame.init()

window = pygame.display.set_mode((450, 300))
clock = pygame.time.Clock()

run = True
while run:
    clock.tick(60)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False

    window.fill((255, 255, 255))
    w, h = window.get_size()
    for i in range(6):
        color = pygame.Color(0)
        color.hsla = (i * 60, 100, 50, 100)
        pygame.draw.circle(window, color, 
            (w//6 + w//3 * (i%3), h//4 + h//2 * (i//3)), 
            round(min(window.get_width() * 0.16, window.get_height() * 0.2)))

    pygame.display.flip()

pygame.quit()
exit()

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,))

1
NameError: 名称“ravel”未定义 - Tcll
Ravel是一个numpy函数。在代码之前添加from numpy import *。 - Tomáš Odstrčil

0
我找到了以下代码,可以处理表示为numpy ndarrays的图像:
from skimage.io import imread
import matplotlib.colors as mcolors
img = imread( 'my_image.png' )
img_hsv = mcolors.rgb_to_hsv( img )
img_hsv = img_hsv / (1.0, 1.0, 255.0)

最后一步的目的是将原本范围在0到255之间的最后一个分量转换为0.0到1.0之间的浮点表示形式,原因不明。

0

如果您需要更多控制颜色转换的格式(例如,希望RGB最大值为255而不是1),我建议使用colorir

from colorir import HSV, sRGB
rgb = HSV(359, 100, 100, max_sva=100).rgb()
... your code

colorir还被设计为与pygame和其他图形库完全兼容,因此您不必浪费时间进行这种预期输入和输出最大颜色分量值的操作...


0

OpenCV也提供了这种可能性。请注意,R和B通道是反转的,即BGR。因此,请使用最适合您需求的函数:

import cv2

rgbimg = cv2.cvtColor(hsvimg, cv2.COLOR_HSV2RGB)
bgrimg = cv2.cvtColor(hsvimg, cv2.COLOR_HSV2BGR)

0
在OpenCV中,可以使用它们的任何颜色转换。请注意,在cv2中,BGR和RGB是相反的。
def convertColor(hsv, conversion):
    return tuple(int(i) for i in cv2.cvtColor(np.uint8([[hsv]]), conversion).flatten())

使用方法:

hsvColor = (0,255,255) #bright red
bgrColor = convertColor(hsvColor, cv2.COLOR_HSV2BGR)

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