Search code examples
c++imagebrightnessexposure

How to programatically adjust brightness of an image without saturating highlights?


I am developing an image processing algorithm to adjust brightness, contrast, shadows, highlights, levels and gamma.

I'm having trouble modeling brightness to behave mostly linear (like the exposure adjustment) but compressing the highlights instead of saturating it.

This is the curve that I want: enter image description here

And this is the curve that I have in my current implementation. You can notice that it shifts from the linear line too early. I'd prefer a curve where it would happen in the highlights area: enter image description here

This is the code that I have right now:

fCnt += 1.0f; // fCnt->[-1,1]
fBri += 1.0f; // fBri->[-1,1]
fShd = (fShd/2.0f)*-0.6876f; // fShd -> [0,1]
fHlt /= 2.0f; // [0,1]
const float fRange = 1/(fWhite-fBlack); // fWhite, fBlack -> [0,1]
const float K = 1.0f/255;

float lut[256];
for(int i=0; i<256; ++i)
{
    float x = i*K; // map [0-255] uchar to [0-1] float
    // brightness 
    x = (1-pow(1-x, fBri));
    // contrast
    x = x <= 0.5f ? 0.5f*pow(x*2.0f, fCnt) : 1.0f - (0.5f*pow((1.0f - x)*2.0f, fCnt));
    // shadow
    x *= (qLn(x+0.0001f) * fShd + 1.0f);
    // highlights
    const float x2 = x*x;
    x *= (x <= 0.4 ? 1 : 1 + fHlt*(1.9434*x*x2 - 3.65455*x2 + 1.98107*x - 0.333037));
    // levels
    x = (x - fBlack) * fRange;
    // gamma [0,4]
    x = pow(min(0.0001f, x), fGamma);

    lut[i] = x;
}

for(int i=0; i<size; ++i)
    img[i] = clamp(img[i]*255.0f);

Solution

  • You can use a Bezier curve to get any arbitrary shape you want.

    I obtained this picture from https://www.desmos.com/calculator/cahqdxeshd

    enter image description here