Wednesday, July 27, 2022
How to convert RGBA Amber to HSV
Some time ago, I obtained some LED par lamps that had amber LEDs in addition to the usual red, green and blue. This makes for generally warmer oranges and yellows as it increases the color gamut available. To control the colors in a natural way, I wrote a little routine to squeeze the orange hues into the usual color wheel (hue, saturation, value) representation. More details can be found in this blog post: /2015/Generating-RGBY-from-Hue/.
Fast-forward several years when someone asked me if the reverse transformation is possible. Turns out it is, but it's definitely more complicated and not at all perfect. (This is to be expected, as we are converting from a four-dimensional (R, G, B, A) space to the three dimensions of H, S, V so naturally the solution is only an approximation.)
To do this, I used the perceptually-based CIE
to model what color a mix of RGBA primaries will yield. Each primary
light source is represented as a point in
xy space, and they can be
interpolated to find the
xy coordinates of the resulting mixture.
Once this is determined, the
xy coordinates can be converted to HSV.
Here's an example of how that interpolation works: I've generated a ring of colors of different hues and similar saturation by appropriately mixing R, G, and B primaries. Mixing in a successively brighter amber LED shifts the colors towards the amber primary at 590nm.
Here's a library to do the color space conversions on Github: github.com/headrotor/colorxform/ Details on the various conversions follow.
HSV_to_RGBA_CIE() methods divide the hue
range into four sections corresponding to red, amber, green, and blue,
and crossfades between them to arrive at an approximately correct
HSV_to_RGBA() is the straightforward approach where the amber values are squeezed in between the red and green (further described in this blog post:
A more sophisticated approach
HSV_to_RGBA_CIE() gives colors that
are closer to equal angles in
xy space and should thus be a
perceptually smoother transition around the hue circle.
A benefit of the CIE approach is that a round-trip conversion
HSV will result in hues that are much closer to the
HSV_to_RGBA_CIE() method also has an optional parameter
warp that when set to true (the default) will prewarp the hue to
better match the
warp uses a 6th-order
polynomial fit to warp the input hue value; if round-trip hue matching
is of less importance then
warp mat be set to False to skip this
The following image shows
the locus in
xy space of hues equally spaced in 10 degree increments
HSV_to_RGBA() on the left and
HSV_to_RGBA_CIE() on the
right. Note that the CIE version is more regularly spaced and so
should be more perceptually equal as well.
RGBA_to_HSV_CIE() function is somewhat more complex and is
naturally an approximation as we are converting from a
four-dimensional space to three. Each light source (R, G, B, and A) is
converted into into the CIE
color coordinates, and interpolated to find the color of the resulting
mixture. Once this is calculated, the
xy coordinates can be
converted to HSV.
To convert from
xy coordinates to HSV, we need to determine the hue,
saturation, and value. The saturation can be obtained by the scaled
distance from the CIE white point (the ★ in the animation above) to
xy value, but in practice it is simpler to just use the minimum
of the RGBA values. The value, or brightness, is not specified in
space so the maximum of the RGBA values is used. Here is how this works for HSV-RGB, the extension to RGBA is straightforward.
To calculate the hue, we need to find the angular distance around the
hue circle. in
xy space, this is pretty well captured by the angle
around the white point. For example, given an arbitrary green color
g here), the hue angle is the angle between vector
(solid) and the red primary vector (dotted). So the hue angle for an
arbitrary color is calculated from the angle in
xy space by
reversing the direction (so increasing hue goes sequentially from red
to green to blue) and subtracting the red angle (so zero hue
corresponds to red).
Using the CIE conversion functions and warping reduce the round-trip
hue error to less than a few percent
RGBA_to_HSV_CIE()), as shown by
the green line in the following figure.
The warping is accomplished using a 6-degree polynomial fit to the difference between the input hue and output hue.
Note 1: color-space nomenclature is confusing!
Colorspace names are in uppercase except
xyY to disambiguate
y from CIE
xyY space. Four-color space is
A for Amber. (Though
Y is often used for for amber/Yellow to
disambiguate from alpha channel "A", we use
A here to disambiguate
XYZ color spaces.)