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?
RGB = hsv2rgb( HSV ) converts the hue, saturation, and value (HSV) values of an HSV image to red, green, and blue values of an RGB image. rgbmap = hsv2rgb( hsvmap ) converts an HSV colormap to an RGB colormap.
HSV = rgb2hsv( RGB ) converts the red, green, and blue values of an RGB image to hue, saturation, and value (HSV) values of an HSV image. hsvmap = rgb2hsv( rgbmap ) converts an RGB colormap to an HSV colormap.
To convert HSV color values to RGB color values in Python, import colorsys library, call hsv_to_rgb() function, and pass the Hue, Saturation, and Value values as arguments. hsv_to_rgb() function takes Hue, Saturation, and Value values as arguments, and returns a tuple containing Red, Blue, and Green values.
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)
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)
The Hue argument should also vary from 0-1.
import colorsys
test_color = colorsys.hsv_to_rgb(359/360.0, 1, 1)
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,))
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].
Since the question is tagged pygame, I want to mention that PyGame allows conversion between color schemes. The pygame.Color
object can be used to convert between the RGB and HSL/HSV color schemes.
The hsva
property:
Gets or sets the HSVA representation of the Color. The HSVA components are in the ranges 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)
The hsla
property:
Gets or sets the HSLA representation of the Color. The HSLA components are in the ranges 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)
Minimal example:
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()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With