Exposure compensation vs. adjusting brightness

Yes. First contrast is added to the L channel (not relevant here), then brightness. Saturation is multiplied into the a and b channels.

The LUT for the brightness part is computed as follows (slightly simplified):

const float gamma = (brightness >= 0.0f) ? 1.0f / (1.0f + brightness)
                                         : (1.0f - brightness);

for(int k = 0; k < 0x10000; k++)
{
  ltable[k] = 100.0f * powf((float)k / 0x10000, gamma);
}

and it’s applied with

L_new = ltable[CLAMP((int)(L_old / 100.0f * 0x10000ul), 0, 0xffff)];

I removed all the parts that deal with extrapolation for L >= 100.0.

When looking at the two extreme values of L = 0 and L = 100 we get:

L_0_new = ltable[CLAMP((0.0 / 100.0 * 0x10000), 0, 0xffff)] = ltable[0]
L_100_new = ltable[CLAMP((100.0 / 100.0 * 0x10000), 0, 0xffff)] = ltable[0xffff]

with

ltable[0] = 100.0 * powf(0 / 0x10000, gamma)
          = 0
ltable[0xffff] = 100.0 * powf(0xffff / 0x10000, gamma)
               = 100.0 * powf(0.9999, gamma)
               = 100.0

The math isn’t 100% correct in the last step but it’s close enough and the differences are not visible, even for big brightness bossts, resulting in gamma quite different from 1.0.

I hope that explained your question. The real code can be found here for the processing and here for the LUT creation.

3 Likes