在Python中进行HSV到RGB(以及反向转换)的计算,而不使用浮点数运算

4

有没有一个好的Python算法可以将HSV颜色转换为RGB(反之亦然),而不依赖于任何外部模块? 我正在编写一些动画生成代码,并希望支持HSV颜色空间,但它在树莓派上运行,我正在尝试避免任何浮点数。


那么,当你说“外部模块”时,是否意味着 https://docs.python.org/2/library/colorsys.html(标准库)不是一个选项? - Dair
嗯...我对像那样的内置模块还算满意,但前提是它不能使用float,而colorsys却使用了。它使用0.0到1.0来表示所有内容,但我需要0到255。 - Adam Haile
无法真正辩论语言选择...这里没有选择。必须是可能的。 - Adam Haile
是的,那是我现在正在使用的,但它需要浮点数。 - Adam Haile
如果值在0到1之间,而不是0到255,则只需将它们乘以255...? - desired login
显示剩余3条评论
1个回答

5
这个网站在这里带你了解RGB到HSV的转换步骤,包括如何使用整数除法进行转换。这里提供了一个Python版本的RGB到HSV函数,其描述与那里类似。
def RGB_2_HSV(RGB):
    ''' Converts an integer RGB tuple (value range from 0 to 255) to an HSV tuple '''

    # Unpack the tuple for readability
    R, G, B = RGB

    # Compute the H value by finding the maximum of the RGB values
    RGB_Max = max(RGB)
    RGB_Min = min(RGB)

    # Compute the value
    V = RGB_Max;
    if V == 0:
        H = S = 0
        return (H,S,V)


    # Compute the saturation value
    S = 255 * (RGB_Max - RGB_Min) // V

    if S == 0:
        H = 0
        return (H, S, V)

    # Compute the Hue
    if RGB_Max == R:
        H = 0 + 43*(G - B)//(RGB_Max - RGB_Min)
    elif RGB_Max == G:
        H = 85 + 43*(B - R)//(RGB_Max - RGB_Min)
    else: # RGB_MAX == B
        H = 171 + 43*(R - G)//(RGB_Max - RGB_Min)

    return (H, S, V)

当与colorsys函数进行比较时,可以得到正确的结果。
import colorsys
RGB = (127, 127, 127)

Converted_2_HSV = RGB_2_HSV(RGB)
Verify_RGB_2_HSV = colorsys.rgb_to_hsv(RGB[0], RGB[1], RGB[2])

print Converted_2_HSV
>>> (0, 0, 127)

print Verify_RGB_2_HSV # multiplied by 255 to bring it into the same scale
>>> (0.0, 0.0, 127.5)

您可以检查输出仍然是一个整数

type(Converted_2_HSV[0])
>>> <type 'int'>

现在说说反向函数。原始代码可以在这里找到:这里,以下是 Python 版本。
def HSV_2_RGB(HSV):
    ''' Converts an integer HSV tuple (value range from 0 to 255) to an RGB tuple '''

    # Unpack the HSV tuple for readability
    H, S, V = HSV

    # Check if the color is Grayscale
    if S == 0:
        R = V
        G = V
        B = V
        return (R, G, B)

    # Make hue 0-5
    region = H // 43;

    # Find remainder part, make it from 0-255
    remainder = (H - (region * 43)) * 6; 

    # Calculate temp vars, doing integer multiplication
    P = (V * (255 - S)) >> 8;
    Q = (V * (255 - ((S * remainder) >> 8))) >> 8;
    T = (V * (255 - ((S * (255 - remainder)) >> 8))) >> 8;


    # Assign temp vars based on color cone region
    if region == 0:
        R = V
        G = T
        B = P
    
    elif region == 1:
        R = Q; 
        G = V; 
        B = P;

    elif region == 2:
        R = P; 
        G = V; 
        B = T;

    elif region == 3:
        R = P; 
        G = Q; 
        B = V;

    elif region == 4:
        R = T; 
        G = P; 
        B = V;

    else: 
        R = V; 
        G = P; 
        B = Q;


    return (R, G, B)

我们可以以与之前相同的方式验证结果。

interger_HSV = (127, 127, 127)
Converted_2_RGB = HSV_2_RGB(interger_HSV)
Verify_HSV_2_RGB = colorsys.hsv_to_rgb(0.5, 0.5, 0.5)

print Converted_2_RGB
>>> (63, 127, 124)

print type(Converted_2_RGB[0])
>>> <type 'int'>

print Verify_HSV_2_RGB # multiplied these by 255 so they are on the same scale
>>> (63.75, 127.5, 127.5)

整数算术确实会引入一些错误,但根据应用程序的不同,这些错误可能是可以接受的。


1
我非常确定 S = 255 * ((RGB_Max - RGB_Min) / V) 在大多数情况下都是错误的(在Python 2中),或者使用了浮点除法(在Python 3中)。在Python 2中,/ 执行整数除法,它可以正确得到饱和度为255和0,但是在两者之间则不行。尝试去掉最外层的括号,我认为它会起作用。此外,您可能还应该在想要整数除法的地方使用 // 运算符,以便向前兼容。 - Blckknght
谢谢你发现了这个问题,我已经更新了答案以包含你的建议,并更改了测试用例,使其不再处于边界上。 - Ashok Fernandez
我必须询问,在HSV转RGB函数中,为什么要使用(H - (region * 43)) * 6而不是简单地使用(H%43) * 6来找到余数? - MiffTheFox
仅供参考,我能够轻松地将HSV转换为RGB并在micropython中使用“@micropython.viper”代码发射器进行翻译。现有代码在ESP32上执行大约需要100微秒。当我将其更改为“@micropython.native”代码发射器时,时间降至50微秒。使用viper时,时间约为25微秒。当我将其更改为传递两个字节缓冲区以存储输入和输出,并在viper函数内运行循环时,每个HSV元组的时间降至5微秒,这足以以高帧率驱动许多LED! - Avi Cherry
链接失效:正确的链接是:https://literateprograms.org/rgb_to_hsv_color_space_conversion__c_.html - slashdottir

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