Gamma Correction for LED Lighting

Gamma correction, as applied to LED lighting, is a confusing and commonly-misunderstood topic. Proper application of gamma correction is important in achieving smooth brightness and color fades. Although there are a least a couple of articles out there that explain this concept and its application to LEDs (see links at the end of this post), I thought I would also take a swing at it. Please let me know if this is helpful.

In most LED lighting applications, the pulse width modulation (PWM) duty cycle of a digital signal is used to control the average current flowing through each color channel of an RGB or RGBW LED. The duty cycle ranges from 0% to 100%, where 100% duty cycle results in the maximum current that the LED driver can provide (and that the LED is rated to handle). The total amount of visible light power emitted by the LED (technically, the luminous flux) is directly proportional to the LED average current. So that means that PWM duty cycle provides linear control of the LED’s light output.

Luminous flux is a measure of the visible light power emitted in all directions within the two-dimensional beam angle of the LED, so it’s more common to describe LED “brightness” in terms of the luminous intensity, a measure of the light power emitted in a specific direction (i.e., toward the viewer’s eye). Although the total luminous flux isn’t spread evenly across the beam angle of an LED, it’s still accurate to say that luminous intensity (brightness) of a single LED color is proportional to the current being driven in that color channel.

But the human eye isn’t a calibrated piece of lab equipment; our eyes perceive light in somewhat unexpected ways. For example, our eyes have different levels of sensitivity to different color wavelengths in the visible spectrum, and the measurement standards for both luminous flux and luminous intensity take that into account. That’s important when striving for accurate composite color reproduction, since, for example, we perceive green light wavelengths as being much brighter than other wavelengths in a composite color.

But that’s a bit off-topic. There’s another human vision anomaly that affects our perception of individual color wavelengths. Our eyes are much more sensitive to variations in low brightness levels than they are to equivalent changes at high brightness levels. For example, we can easily detect a change in the brightness of a red LED from 5% to 10% of its maximum brightness. But it’s almost impossible to detect a change from 95% to 100%. There’s a physiological reason for that, having to do with the internal structure of the human eye, but I’m really not qualified to delve into that.

Another way of describing this difference in brightness sensitivity is to say that our eyes have a non-linear response to luminous intensity: a steady (linear) increase in luminous intensity is not perceived as a linear increase in what we think of as “brightness”. The degree of this nonlinearity is specified by a parameter called gamma, which varies depending on the specific type of light source. For LEDs, the typical values of gamma range between 1.5 and 3. The graph below illustrates relationship between PWM duty cycle (D) and perceived brightness (Bp) for some example values of gamma (g). The yellow curve (g=1.0) shows the ideal situation where the PWM duty cycle linearly translates to perceived brightness.

In the example above for g=2.2, as the PWM duty cycle is linearly increased from 0% to 22% (0.22 on the horizontal axis), the perceived brightness increases from off/dark to 50% of the maximum brightness. That means that a full-range brightness fade from 0% to 100% duty cycle will not appear smooth, since the perceived brightness is already at 50% after only 22% of the fade duration.

This non-linearity also affects color (hue) accuracy. Consider the example of a slightly orange-ish red hue with intended color components (R,G,B) = (1.0, 0.1, 0.0). If g=2.2 and a 10% duty cycle is applied to the green channel, the perceived brightness of the green component will actually be 35% (0.35) instead of 10% (0.1). That will cause the hue to be significantly more orange than intended.

The human-eye perceived brightness (Bp) of a light source can be described as an nth root function. This function can be expressed two different ways as shown below:

This is illustrated by the red curve below, where gamma=2.2.

So how can the non-linearity of the red curve be “flattened out” to achieve the ideal, linear response represented by the blue curve above? This is done by applying a correcting function that is the opposite of an nth root function, an exponential function. Without gamma correction, the PWM duty cycle is directly used to adjust the brightness of each LED color channel. With gamma correction, the desired brightness (Bd), on a scale from 0 to 1, is used to generate a gamma-corrected PWM duty cycle (Dc) represented by the green curve. Specifically:

Then, when the LED is illuminated using the gamma-corrected PWM duty cycle, the exponential function exactly cancels out the nth root human-eye nonlinearity, so that the desired brightness is correctly perceived:

Here’s a really simple example, where gamma=2 and the desired brightness is 50%. If the LED gamma=2, the duty cycle is generated by simply squaring the desired brightness, and the perceived-brightness function is a square root, so:

For all of my projects so far, I’ve used a gamma value of 2.2, and that seems to work reasonably well. But I haven’t yet come across a technique to actually measure the gamma value, or even to qualitatively confirm the effectiveness of gamma correction. I’ve concluded that it’s not a great idea to stare at bright LEDs for long periods while trying to gauge relative brightness. So I’ll have to do some more research and report back in a future post.

I’m also using the same gamma value for all three (RGB) or four (RGBW) color channels, but I suspect a case could be made for using different gamma values per color channel. Again, more research needed…

Implementation

For the past several years, all of the hardware for my lighting projects has been based on an a powerful (but still fairly inexpensive) MCU that incorporates a floating-point execution unit (FPU). That allows me to represent all color-related quantities, including the per-channel desired brightness, as floating point values in the range 0.0 to 1.0. The exponential function XY is implemented in hardware and incurs a very small performance penalty. So implementing the gamma correction function is pretty trivial.

For projects using a lower-performance MCU without an FPU, it probably makes the most sense to implement the gamma exponential function using a lookup table-based approach. This is discussed in the articles below.

Other Articles to Read

Here are two articles that do a decent job of explaining gamma correction, including implementation approaches:

  1. LED Tricks: Gamma Correction, Phillip Burgess
  2. RGB LEDs: How to Master Gamma and Hue for Perfect Brightness, Elliot Williams

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: