Converting to and from RGB and HSV colour values
The short version: A few lines of Python code using the Matplotlib library will convert between HSV and RGB colour values. An online tool is not required.
For example, the open source keyboard firmware QMK tends to use HSV values instead of RGB. Some common values are in file color.h.
A trap for young players: Many online tools and code snippets don’t reveal what units they use (and thus expect) for HSV. For example, there is a difference between the formal HSV values and those scaled to fit the range for a byte (0-255). The latter is used in QMK. For RGB values, there isn’t any ambiguity (as far as I know).
Using Python
One option is using Python. The Python library Matplotlib is used here, but it may not be installed by default:
ImportError: No module named matplotlib
On Debian-like systems (for example, LMDE, Linux Mint, Ubuntu, Debian itself, Lubuntu, Kubuntu, Xubuntu, BunsenLabs, Linux Lite, WSL, etc.), install it by:
sudo apt update sudo pip install matplotlib
On a newer Linux system, you will probably get something like this error message:
This environment is externally managed
This cryptic error message (it does not even identify itself as an error message) means that it is too risky to change the global Python installation, as the operating system has become too dependent on Python (the operating system may become completely inoperable, requiring a reinstallation).
Instead, the change can be isolated by using a so-called virtual Python environment (so the system/global Python installation is not affected):
# Create the virtual Python environment python3 -m venv ~/.our_Matplotlib_environment # Enter the virtual Python environment source ~/.our_Matplotlib_environment/bin/activate # Install Matplotlib pip install matplotlib
Module ‘venv’ is probably installed by default. If it isn’t, it can be installed by:
sudo apt update sudo apt install python3-venv python3-pip
After use, exit the virtual Python environment:
# Exit the virtual Python environment deactivate
As an alternative method to the overhead and complexity of a virtual Python environment, including starting and exiting the virtual Python environment, a newfangled method is using ‘uv‘ (a terrible, terrible name is the search engine era). It hides all the virtual Python stuff and takes take of it automatically. Thus, it appears to install Matplotlib globally, but behind the scenes it doesn’t.
The actual conversion
With Matplotlib installed, to convert, for example, interactively, from the command line on, for Linux:
python
On some Linux systems, this is instead executable ‘python3’ (both newer and older Linux systems). In a virtual Python environment, ‘python’ may work fine, even if it doesn’t for the global Python installation.
After use, exit the interactive session:
quit()
Convert an HSV value to RGB (before the exit):
import matplotlib
import numpy as np # For set_printoptions()
np.set_printoptions(precision=4) # With byte values,
# 0-255, not more than 4 is warranted
# Quarter intensity for pure HSV RED
# (hue = 0 degrees, scaled to
# [0.0; 1.0] for 360 degrees)
256 * matplotlib.colors.hsv_to_rgb([[[0 / 256, 256 / 256, 256 / 256 / 4]]])
# Result: array([[[64., 0., 0.]]]),
# that is, RGB value 400000
#
Notes:
- hsv_to_rgb() uses the (floating point) range [0.0;1.0] for all three values for both RGB and HSV. Thus, both the input and output values must be scaled (as is done in this example).
- It isn’t 100% clear if 255 or 256 should be used. Or maybe a linear transformation (with an offset) instead of only a scaling factor?
- ‘import numpy as np’ works, because NumPy is a dependency of Matplotlib (and thus will be installed when Matplotlib is installed)
And for HSV_TURQUOISE (0x7B5A70) = RGB_TURQUOISE (0x476E6A) from the QMK project, converting from RGB to HSV:
import matplotlib
import numpy as np # For set_printoptions()
np.set_printoptions(precision=4) # With byte values,
# 0-255, not more than 4 is warranted
# RGB_TURQUOISE (0x476E6A, 71, 110, 106) =
# HSV_TURQUOISE (0x7B5A70, 123, 90, 112)
#
hsv2 = 255 * matplotlib.colors.rgb_to_hsv(
[[[0x47 / 255, 0x6E / 255, 0x6A / 255]]])
hsv2
# Result: array([[[123.141 , 90.4091, 110.]]])
# As hexadecimal HSV values rounded
# to the nearest integer:
print(f"{round(hsv2[0, 0, 0]):02X}")
print(f"{round(hsv2[0, 0, 1]):02X}")
print(f"{round(hsv2[0, 0, 2]):02X}")
# Or formatted as an HSV hexadecimal value,
# followed by the three decimal numbers,
# plus the angle (0-360) for hue:
#
print(f"HSV 0x{round(hsv2[0, 0, 0]):02X}{round(hsv2[0, 0, 1]):02X}{round(hsv2[0, 0, 2]):02X}, {round(hsv2[0, 0, 0])} (angle {round(hsv2[0, 0, 0]/255*360)}), {round(hsv2[0, 0, 1])}, {round(hsv2[0, 0, 2])}")
#
# Output:
#
# HSV 0x7B5A6E, 123 (angle 174), 90, 110
#
Note some advantages of this Python method:
- We have full control over the output, e.g., decimal or hexadecimal (or both), angle vs. byte value for the H (hue) value (or both), providing the exact kind of output we want, without having to do (manual) post converting and formatting
- It can easily be extended to handle a number of RGB values, instead of just one. With an online (or offline) tool, it would be by painstakingly converting one value at a time, using multiple steps (though it could be automated using a macro keyboard)
The discrepancy for value, V (brightness), 112 vs. 110 seems to be too high. Perhaps the QMK values are in error?
Bonus: Handling unbalanced RGB LEDs
For example, on Keychron keyboards, probably depending on the model, for the same current, the red LEDs are only about half as bright as the blue and green LEDs.
So the idea is to scale the RGB values recipocally, optionally rescaling the brightness to the maximum (convert to HSV, set the ‘V’ to 255 and convert back to RGB).
References
- Converting an image from RGB to HSV color space (Stack Overflow. For Python). Actual sample code.
- matplotlib.colors.rgb_to_hsv (a function in Python library Matplotlib)
- Convert color space from HSV to RGB and RGB to HSV (Python recipe). A code snippet for conversion to and from RGB and HSV values. But it may be flawed. Or at least it does not state the units of the input values
- Installing Matplotlib (Stack Overflow). At least on some versions of Ubuntu.
- A list of named colors
Leave a Reply