Search code examples
algorithmcolorsled

Generating pure colors of equal brightness (for display on LEDs)


I have some RGB LEDs, and I want to have them display random colors.

My first approach was to pick a random hue h from 0-360, create an HSV color of the form (h, 1, 1), and then convert from HSV to RGB using the algorithm given on Wikipedia. This almost does what I want, but not quite.

The problem is that the LED can appear to be brighter or dimmer, depending on the hue. I want it to always appear to be the same brightness, regardless of the hue.

So, for example, blue will be (0, 0, 1) in RGB space, but cyan will be (0, 1, 1). If we make the simplifying assumption that all three primaries are equally bright, then cyan is twice as bright as blue.

What I want is something where blue will be (0, 0, 1) in RGB space, and cyan will be (0, 0.5, 0.5), so that the sum of the primaries is always 1.0.

I can hack something together fairly easily with an algorithm like this:

  • Pick a value x between 0 and 1.
  • Pick one of the three primaries, and set it to x.
  • Pick one of the two remaining primaries, and set it to 1 - x.
  • Set the third primary to 0.

My question is: Is there a more principled way to do this? Is there some color space that behaves the way I want it to?

Conclusion: @Vincent van der Weele had the important insight that I needed to normalize. And @Stefan Haustein's luma is another way to say the same thing, while also adding in different weights for the different primaries. However, those particular weights didn't work in my situation. Experimentally, I picked weights of 0.3, 0.25, and 0.2 for R, G, and B. And also experimentally, I picked a gamma of 2.0 (as opposed to the 2.8 I had originally been using). This still isn't perfect, but it's a lot better than before. I guess this is just naturally messy.


Solution

  • I'd take the luma for the darkest color of your spectrum (= blue, 0.0722). Then scale the calculated RGB values so that the resulting luma matches this value. See https://en.wikipedia.org/wiki/Luma_(video)

    luma = 0.2126 * r + 0.7152 * g + 0.0722 * b;
    // factor * luma = 0.0722
    factor = 0.0722 / luma;
    r *= factor;
    g *= factor;
    b *= factor;
    

    P.S. With your simplifying assumption, this would be

    luma = (r + g + b) / 3.0;
    factor = 1.0 / (3.0 * luma);
    r *= factor;
    g *= factor;
    b *= factor;
    

    Normalizing the total brightness to the brightness of a single component (1/3)