HSV to RGB Color Conversion

61,470

Solution 1

That function expects decimal for s (saturation) and v (value), not percent. Divide by 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)

If you would like the non-normalized RGB tuple, here is a function to wrap the colorsys function.

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

Example functionality

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

Solution 2

If you like performance, it's best to avoid imports and use your own optimized code

Here's the exact code from colorsys slightly modified to make the byte-code slightly faster:

    def hsv_to_rgb(h, s, v):
        if s == 0.0: return (v, v, v)
        i = int(h*6.) # XXX assume int() truncates!
        f = (h*6.)-i; p,q,t = v*(1.-s), v*(1.-s*f), v*(1.-s*(1.-f)); i%=6
        if i == 0: return (v, t, p)
        if i == 1: return (q, v, p)
        if i == 2: return (p, v, t)
        if i == 3: return (p, q, v)
        if i == 4: return (t, p, v)
        if i == 5: return (v, p, q)

output:

>>> hsv_to_rgb(359,1,1)
[1, 0.0, 0.0]

Using an if-chain like above is actually faster than using elif

Using a wrapper, like in Cyber's answer, takes a few extra steps for the interpreter to perform.
To add, the for loop in Cyber's example is a real performance killer when used like that

If you want slightly more performance, simply do this:
(I won't say this is the best possible performance, but it's certainly better)

    def hsv_to_rgb(h, s, v):
        if s == 0.0: v*=255; return (v, v, v)
        i = int(h*6.) # XXX assume int() truncates!
        f = (h*6.)-i; p,q,t = int(255*(v*(1.-s))), int(255*(v*(1.-s*f))), int(255*(v*(1.-s*(1.-f)))); v*=255; i%=6
        if i == 0: return (v, t, p)
        if i == 1: return (q, v, p)
        if i == 2: return (p, v, t)
        if i == 3: return (p, q, v)
        if i == 4: return (t, p, v)
        if i == 5: return (v, p, q)

^ this guarantees int() output with a range of 255 (the input is still the same)

>>> hsv_to_rgb(359./360.,1,1)
(255, 0, 0)

TIP: stay away from 3rd-party where possible, try the direct approach if you can.
exculusions: compiled C extensions such as PIL or NumPy, or ctypes wrappers such as PyOpenGL (uses the DLL)

Solution 3

The Hue argument should also vary from 0-1.

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

Solution 4

I have prepared a vectorized version, it is cca 10x faster

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

Solution 5

If you are working with Numpy arrays then matplotlib.colors.hsv_to_rgb is quite direct:

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)

Note that the input and output images have values in the range [0, 1].

Share:
61,470
AvZ
Author by

AvZ

Bash has all the answers avz@PC-Linux:~$ whatis love love: nothing appropriate. Mathematics and Physics are my favourite subjects. I also like to code a little here and then (In C++, Python, or Javascript usually). I log on to Mathematics SE whenever I can to find nice problems to solve. Got first rank change for 1st Quarter 2015 quarter for Mathematics SE! Can't believe it!

Updated on July 09, 2022

Comments

  • AvZ
    AvZ almost 2 years

    Is there a way to convert HSV color arguments to RGB type color arguments using pygame modules in python? I tried the following code, but it returns ridiculous values.

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

    and this code returns the following nonsense

    (100, -9900.0, -9900.0)
    

    This obviously isn't RGB. What am I doing wrong?

  • AvZ
    AvZ almost 10 years
    Thanks. Is there any built in function to "denormalise" the RGB function?
  • Cory Kramer
    Cory Kramer almost 10 years
    I don't know about an out-of-the-box function, but you could write one. Please see my latest edit for such a function.
  • Tcll
    Tcll over 9 years
    so... why am I down-rated?? why is there no comment telling me what I did wrong so I can better myself??
  • sfat
    sfat over 7 years
    because your solution takes hue values from 0 to 1, but in your examples you use values from 0 to 360.
  • Matteo Italia
    Matteo Italia almost 7 years
    If we are talking about performance, I would stay away from lists and return tuples instead.
  • Tcll
    Tcll almost 7 years
    +Matteo Italia: after growing up a bit, I actually agree with you, if anyone (I'm on a phone) would like to edit this answer to return a tuple, feel free.
  • Tcll
    Tcll almost 7 years
    NameError: name 'ravel' is not defined or I would upvote.
  • Tomáš Odstrčil
    Tomáš Odstrčil almost 7 years
    Ravel is a numpy function. Add from numpy import * before the code.
  • Anand C U
    Anand C U over 6 years
    The Hue argument should also vary from 0-1
  • aheigins
    aheigins about 6 years
    excellent comment, was wondering why i was constantly getting red!.
  • aheigins
    aheigins about 6 years
    Yep to echo what Anand C U and Paul Beloff have said you need to divide your Hue by 360 for example 125/360 (to get green). Otherwise you constantly get red.
  • Anshul Rai
    Anshul Rai about 6 years
    Thank you, this saved me a lot of time!
  • TimH
    TimH about 2 years
    As @sfat mentioned... note that the first function in this answer assumes all hsv and rgb values are floats from 0 to 1 (see colorsys docs). The example given of hsv_to_rgb(359,1,1) is misleading. I'm saying this here because I assumed this mistake had been fixed with the 2017 edit of this answer (and therefore disregarded sfat's comment), but the error remains.