User Tools

Brightness

In the simplest terms the “brightness” of a pixel is its luminosity value as perceived by a human eye (making infrared and gamma rays \(0\) “bright”). There are quite a few competing standards and definitions of what the means exactly, but for our purposes we will use the \(L\) component of the HSL color space as our “brightness” value of a pixel.

Again, in the simplest terms, increasing or reducing pixel's brightness can be interpreted as adding or subtracting a fixed “gain” value (others may argue that a more natural approach would be to use a multiplier, while others still may bring the “vibrancy” approach into picture).

In RGB color space

The mathematically correct way to do the brightness is to first convert into HSL color space, adjust the L (luma) component, and then convert back into RGB. That gives the correct result but costs way too many transistors and cycles. So we've got to improvise!

Algorithm

Here's the algorithm to follow, assuming \(R, G, B \in [0..4095]\) and \(br \in [-1024..+1023]\):

  1. calculate luminosity
  2. adjust the brightness additively and clamp the value to range \([0..4095]\)
  3. figure out the slope for each components based on whether luma is below or above 50% and set chroma components to values that correspond to that 50% luma
  4. figure out if the new luma is going to cross the 50% boundary and if so - “flip the slopes”
  5. recalculate RGB components

Calculation reference

To calculate luminosity we just find the max and min of the triplet and get a simple average: \[L = \frac{min(R, G, B)+max(R, G, B)}{2}\]

Brightness adjustment is a trivial addition, clamping the value to its proper limits:

\[ L` = L + br \\ L` \in [0..4095] \]

The slope \(k_R\) for the red component calculation depends on whether \(L\) is above or below the middle: \[ k_{R} = \begin{cases} R / L & \text{if} \; L \leq 2047 \\ \frac{R - 4095}{L - 4095} & \text{if} \; L > 2047 \end{cases} \]

Finding the “middle point” value also depends on whether the \(L\) is above or below the middle: \[ R = \begin{cases} k_R * 2047 & \text{if} \; L \leq 2047 \\ 4095 - k_R * 2047 & \text{if} \; L > 2047 \end{cases} \]

If we are crossing the middle luma boundary as the result of this adjustment - flip the slope: \[ k_R = 2 - k_R \]

Applying the new \(L`\) to R component and clamping the result is trivial:

\[ R` = k_R * (L` - 2047) + R \\ R` \in [0..4095] \]

\(G\) and \(B\) calculations are similar to \(R\).

In HSL color space

When using the HSL color space the adjustment is as simple as elementary school's arithmetic operation. Namely - just a simple addition.

With that the implementation of the Brightness adjustment can be as simple as the following (where the “luma”, or brightness component \(\in[0..100]%%\)):

// pseudo-code
void brightness(/*array of pixels*/image, int _br){
  for(const & pixel: image){
    pixel.luma = std::clamp(pixel.luma + _br, 0, 100);
  }
}

This website uses cookies. By using the website, you agree with storing cookies on your computer. Also, you acknowledge that you have read and understand our Privacy Policy. If you do not agree, please leave the website.

More information