Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Algorithm for adjustment of image levels

I need to implement in C++ algorithm for adjusting image levels that works similar to Levels function in Photoshop or GIMP. I.e. inputs are: color RGB image to be adjusted adjust, while point, black point, midtone point, output from/to values. But I didn't find yet any info on how to perform this adjustment. Probably someone recommend me algorithm description or materials to study.

To the moment I've came up with following code myself, but it doesn't give expected result, similar to what I can see, for example in the GIMP, image becomes too lightened. Below is my current fragment of the code:

const int normalBlackPoint = 0;
const int normalMidtonePoint = 127;
const int normalWhitePoint = 255;
const double normalLowRange = normalMidtonePoint - normalBlackPoint + 1;
const double normalHighRange = normalWhitePoint - normalMidtonePoint;

int blackPoint = 53;
int midtonePoint = 110;
int whitePoint = 168;
int outputFrom = 0;
int outputTo = 255;

double outputRange = outputTo - outputFrom + 1;
double lowRange = midtonePoint - blackPoint + 1;
double highRange = whitePoint - midtonePoint;
double fullRange = whitePoint - blackPoint + 1;
double lowPart = lowRange / fullRange; 
double highPart = highRange / fullRange; 

int dim(256);
cv::Mat lut(1, &dim, CV_8U);
for(int i = 0; i < 256; ++i)
{
    double p = i > normalMidtonePoint
        ? (static_cast<double>(i - normalMidtonePoint) / normalHighRange) * highRange * highPart + lowPart
        : (static_cast<double>(i + 1) / normalLowRange) * lowRange * lowPart;
    int v = static_cast<int>(outputRange * p ) + outputFrom - 1;
    if(v < 0) v = 0;
    else if(v > 255) v = 255;
    lut.at<uchar>(i) = v;
}


....

    cv::Mat sourceImage = cv::imread(inputFileName, CV_LOAD_IMAGE_COLOR);
    if(!sourceImage.data)
    {
        std::cerr << "Error: couldn't load image " << inputFileName << "." << std::endl;
        continue;
    }


#if 0       
    const int forwardConversion = CV_BGR2YUV;
    const int reverseConversion = CV_YUV2BGR;
#else
    const int forwardConversion = CV_BGR2Lab;
    const int reverseConversion = CV_Lab2BGR;
#endif

    cv::Mat convertedImage;
    cv::cvtColor(sourceImage, convertedImage, forwardConversion);

    // Extract the L channel
    std::vector<cv::Mat> convertedPlanes(3);
    cv::split(convertedImage, convertedPlanes);

    cv::LUT(convertedPlanes[0], lut, convertedPlanes[0]);

    //dst.copyTo(convertedPlanes[0]);
    cv::merge(convertedPlanes, convertedImage);

    cv::Mat resImage;
    cv::cvtColor(convertedImage, resImage, reverseConversion);
    cv::imwrite(outputFileName, resImage);
like image 230
ivan.ukr Avatar asked Dec 15 '22 03:12

ivan.ukr


1 Answers

Pseudocode for Photoshop's Levels Adjustment

First, calculate the gamma correction value to use for the midtone adjustment (if desired). The following roughly simulates Photoshop's technique, which applies gamma 9.99-1.00 for midtone values 0-128, and 1.00-0.01 for 128-255.

Apply gamma correction:

Gamma = 1
MidtoneNormal = Midtones / 255
If Midtones < 128 Then
    MidtoneNormal = MidtoneNormal * 2
    Gamma = 1 + ( 9 * ( 1 - MidtoneNormal ) )
    Gamma = Min( Gamma, 9.99 )
Else If Midtones > 128 Then
    MidtoneNormal = ( MidtoneNormal * 2 ) - 1
    Gamma = 1 - MidtoneNormal
    Gamma = Max( Gamma, 0.01 )
End If
GammaCorrection = 1 / Gamma

Then, for each channel value R, G, B (0-255) for each pixel, do the following in order.

Apply the input levels:

ChannelValue = 255 * ( ( ChannelValue - ShadowValue ) / 
    ( HighlightValue - ShadowValue ) )

Apply the midtones:

If Midtones <> 128 Then
    ChannelValue = 255 * ( Pow( ( ChannelValue / 255 ), GammaCorrection ) )
End If

Apply the output levels:

ChannelValue = ( ChannelValue / 255 ) *
    ( OutHighlightValue - OutShadowValue ) + OutShadowValue

Where:

  • All channel and adjustment parameter values are integers, 0-255 inclusive
  • Shadow/Midtone/HighlightValue are the input adjustment values (defaults 0, 128, 255)
  • OutShadow/HighlightValue are the output adjustment values (defaults 0, 255)
  • You should optimize things and make sure values are kept in bounds (like 0-255 for each channel)

For a more accurate simulation of Photoshop, you can use a non-linear interpolation curve if Midtones < 128. Photoshop also chops off the darkest and lightest 0.1% of the values by default.

like image 169
Beejor Avatar answered Dec 29 '22 16:12

Beejor