I’m currently sitting on the horns of a dilemma. This is pretty darned uncomfortable, let me tell you, so I thought I would ask for your sage advice.
Here’s the deal. As you may recall — and as you can see in this video — I’m in the process of playing around with a pair of pseudo robot heads that each boast a couple of SMADs (Steve and Max’s Awesome Displays) as eyes.
My first problem is that, when I try to take one of these videos, the LEDs are so bright that they overload my camera’s sensor and wash the image out. My second problem is that I’m about to start displaying a cornucopia of colors, for which I’m going to want to apply gamma correction.
Let’s start with brightness. In order to take my videos, I created a simple ModifyBrightness() function to reduce the brightness of my colors by a fixed percentage.
My 24-bit colors are composed of three 8-bit fields that store the red, green, and blue (RGB) channel values. These RGB values can each be 0 to 255 in decimal (0x00 to 0xFF in hexadecimal). The 24-bit values are stored in 32-bit words, with the most significant 8-bit byte remaining unused.
Say I wish to reduce the brightness of a 24-bit color to be 50% of its original value. I first split it into its RGB channels as illustrated by (a) in the figure below. Next, I modify the brightness of each channel. Finally, I reconstruct a new 24-bit color from the modified RGB values as illustrated by (b) in the figure below.
The next issue is that of gamma correction. My simplistic understanding of the way this works is as follows. If we use pulse-width modulation (PWM) to vary the brightness of our LEDs, the LEDs behave as we expect them to such that a 50% duty cycle does indeed result in 50% brightness as illustrated in (a) below.
The problem is that our eyes have a sort of built-in non-linearity as illustrated in (b), where the equation that specifies this non-linearity is a teasingly tricky power-law function. The solution is to modify the brightness of our LEDs using the inverse of this power-law function as illustrated in (c), with the result that our eyes end up seeing what we expected them to see in the first place.
“Gamma correction” is the name we give to the application of this inverse of this power-law function. The result is that the colors we see on our LEDs are more… I’m not sure what the right words is, but whatever it is, that’s what the colors are more of.
The clever guys and gals at Adafruit have written extensively on this topic (see LED Tricks: Gamma Correction). As part of this, they provided a handy-dandy look-up table as shown below.
In this case, the index into the table is the original 0 to 255 color value associated with one of the RGB channels, while the value returned from the table is the gamma corrected version.
This means that — as for modifying brightness — if I wish to apply gamma correction to a 24-bit color value, I first need to split it into its RGB channels, apply gamma correction to each channel, and then reconstruct a new 24-bit color from the modified RGB values.
Or course, I could simplify things and reduce the number of calculations by fragmenting the 24-bit color into its RGB channels, and then performing both actions — modifying the brightness and applying gamma correction — before constructing the new 24-bit color value.
All of which returns us to my horny dilemma (which is the worst sort): should I first modify the brightness and then apply the gamma correction, or should I apply the gamma correction first and then modify the brightness? What say you? Do you have any arcane and esoteric knowledge in this area that you would care to share with the rest of us?