How to adjust image brightness and contrast in opencv, and the principle of brightness and contrast adjustment

1. The principle of adjusting image brightness and contrast

The contrast and brightness adjustment can be completed by designing a mapping curve. The specific process is shown in the figure below,
a) is the original image;
b) increase the brightness, pixel intensity + fixed value;
c) decrease the brightness, pixel intensity - fixed d )
increase the contrast near pixel intensity 75; increase the slope near
e) increase the contrast near pixel intensity 150;
f) increase the contrast near pixel intensity 75 and 225.

The gray scale of the area with the slope of the curve greater than 45 degrees is stretched, the accuracy increases, and the contrast becomes higher; the gray
scale of the area with the slope of the curve less than 45 degrees is compressed, the accuracy decreases, and the contrast becomes lower.

The image below is 0-255 mapped to 0-255, a original image, b original image plus a number, c original image minus a number, d stretched at value=75, e stretched at value=150, f is stretched at 75 and 225. Stretching can increase the contrast in this area
insert image description here

Opencv's misunderstanding about contrast and brightness

2. A code implemented by python is as follows, which can be run directly

First subtract the mean, then adjust the contrast (multiply by a factor greater than 1 to stretch), then add the mean, and finally adjust the brightness.
aa is the stretch factor, bb is the brightness change
img_a = aa * (img - bri_mean) + bb + bri_mean

import cv2
import numpy as np
from matplotlib import pyplot as plt

if __name__ == "__main__":
    file = r'D:\dataset\data_gain1_2\cap_frame_0002_raw.png'
    img = cv2.imread(file).astype(np.float32)
    bri_mean = np.mean(img)

    a = np.arange(5, 16, 5) / 10
    b = np.arange(-30, 31, 30)

    a_len = len(a)
    b_len = len(b)
    print(a_len, b_len)
    plt.figure()

    for i in range(a_len):
        for j in range(b_len):
            aa = a[i]
            bb = b[j]
            img_a = aa * (img-bri_mean) + bb + bri_mean
            print(i, j, aa, bb)
            img_a = np.clip(img_a,0,255).astype(np.uint8)
            plt.subplot(a_len+1, b_len, (j + b_len * i + 1))
            plt.imshow(img_a, cmap='gray')
    plt.subplot(a_len + 1, b_len, a_len*b_len+1)
    plt.imshow(img.astype(np.uint8), cmap='gray')
    plt.show()

Get results with different brightness and contrast
insert image description here

3. Method of adjusting image and contrast in opencv

You can refer to the demo code linked below:
opencv demo code
to run the program, corresponding to the change of the histogram:
the original image
insert image description here
increases brightness, the histogram shifts to the right
insert image description here
to increase the contrast, and the histogram is more balanced
insert image description here

a. Implementation method in opencv:

First understand the convertTo function

image.convertTo(dst, CV_8U, a, b);

a and b in converTo are multiplication and addition coefficients

In opencv, insert image description here
how do you find alpha and beta when you get a and b coefficients in opencv? as follows:

if (contrast > 0)
{
    
    
    delta = 127. * contrast / 100;
    a = 255. / (255. - delta * 2);
    b = a * (brightness - delta);
}
else
{
    
    
    delta = -128. * contrast / 100;
    a = (256. - delta * 2) / 255.;
    b = a * brightness + delta;
}

b. According to our formula, we can also get our own a and b coefficients, and the realization results show that it has a similar effect to the opencv method

img_a = aa * (img - bri_mean) + bb + bri_mean
When bri_mean is anchored at 127,
img_a = aa * (img - 127) + bb + 127 = aa * img - 127*aa + bb + 127 = aa * img + (1-aa) * 127 + bb

The range of _brightness and _contrast in opencv is 0-200, no adjustment will be made when it is equal to 100, refer to and run the demo program below

a = _contrast / 100.0f;
b = (1 - a) * 127 + _brightness - 100;

By comparing the change effect of the histogram, it can be seen that the above two methods have similar effects.
I originally wanted to find the principle link corresponding to the opencv method, but the link has expired

c. The complete demo of the two schemes, C++ implementation:

#include "opencv2/core/utility.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"

#include <iostream>

using namespace cv;
using namespace std;

int _brightness = 100;
int _contrast = 100;

Mat image;

/* brightness/contrast callback function */
static void updateBrightnessContrast(int /*arg*/, void*)
{
    
    
    int histSize = 64;
    int brightness = _brightness - 100;
    int contrast = _contrast - 100;

    /*
     * The algorithm is by Werner D. Streidt
     * (http://visca.com/ffactory/archives/5-99/msg00021.html)
     */
    double a, b;
    
    double delta=0;
    //opencv中的方法
    if (contrast > 0)
    {
    
    
        delta = 127. * contrast / 100;
        a = 255. / (255. - delta * 2);
        b = a * (brightness - delta);
    }
    else
    {
    
    
        delta = -128. * contrast / 100;
        a = (256. - delta * 2) / 255.;
        b = a * brightness + delta;
    }
    /*method 2*/ 
    //a = _contrast / 100.0f;
    //b = (1 - a) * 127 + _brightness - 100;
    //printf("ab value :%.4lf, %.4lf, %.4lf\n",delta, a, b);
    Mat dst, hist;
    image.convertTo(dst, CV_8U, a, b);
    imshow("image", dst);

    calcHist(&dst, 1, 0, Mat(), hist, 1, &histSize, 0);
    Mat histImage = Mat::ones(200, 320, CV_8U) * 255;

    normalize(hist, hist, 0, histImage.rows, NORM_MINMAX, CV_32F);

    histImage = Scalar::all(255);
    int binW = cvRound((double)histImage.cols / histSize);

    for (int i = 0; i < histSize; i++)
        rectangle(histImage, Point(i * binW, histImage.rows),
            Point((i + 1) * binW, histImage.rows - cvRound(hist.at<float>(i))),
            Scalar::all(0), -1, 8, 0);
    imshow("histogram", histImage);
}

const char* keys =
{
    
    
    "{help h||}{@image|baboon.jpg|input image file}"
};

int main(int argc, const char** argv)
{
    
    
    CommandLineParser parser(argc, argv, keys);
    parser.about("\nThis program demonstrates the use of calcHist() -- histogram creation.\n");
    if (parser.has("help"))
    {
    
    
        parser.printMessage();
        return 0;
    }
    string inputImage = parser.get<string>(0);
    // Load the source image. HighGUI use.
    image = imread(samples::findFile(inputImage), IMREAD_GRAYSCALE);
    if (image.empty())
    {
    
    
        std::cerr << "Cannot read image file: " << inputImage << std::endl;
        return -1;
    }

    namedWindow("image", 0);
    namedWindow("histogram", 0);

    createTrackbar("brightness", "image", &_brightness, 200, updateBrightnessContrast);
    createTrackbar("contrast", "image", &_contrast, 200, updateBrightnessContrast);

    updateBrightnessContrast(0, 0);
    waitKey();

    return 0;
}

Guess you like

Origin blog.csdn.net/tywwwww/article/details/126626804