Operate image pixels and realize some functions -- image brightness, contrast adjustment, image inversion and fusion (openCV learning record -- 2)

Posted by echox on Mon, 20 Dec 2021 17:08:40 +0100

0. Preface

opencv has two operations on pixels, one is to operate on a single pixel (point operation), and the other is to operate on the pixels of a region.

They can be used separately to achieve different effects

Recently, I mainly learned some point operations and some effects that can be achieved through point operations

The first is to be able to take out all the pixel values of a picture

1. Extract the pixels of the picture

The operations of pictures in openCV are realized through mat type. Mat is essentially a matrix, and each pixel value of the image is a value in the matrix.

First, let's take a look at the implementation of a pixel to obtain a single channel image (gray image)

int num = img.at<uchar>(i,j);

Represents the pixel values of column i and row j of the gray image to be img

There is also the implementation of three channel map (the most common RGB map) to obtain a pixel of one point:

int b = img.at<Vec3b>(i,j)[0];
int g = img.at<Vec3b>(i,j)[1];
int r = img.at<Vec3b>(i,j)[2];

You can also get the float value of his pixels

Mat m1;
imgin.convertTo(m1, CV_32F);//First, type the img

float b = m1.at<Vec3f>(i, j)[0];
float g = m1.at<Vec3f>(i, j)[1];
float r = m1.at<Vec3f>(i, j)[2];

In other words, if you want to extract the pixel value of a picture, first, the number of channels and corresponding data types in the Mat variable will report an error if you make a mistake, and then the number of rows and columns at a certain point you need to extract.

Then we can do some operations on the picture

2. Adjustment of picture brightness and contrast

1. Relationship between brightness, contrast and pixel value

Reference link: Relationship between image RGB value, gray value and pixel value (360doc.com)

We can start with the grayscale image.

I think brightness is the relative amount of white color in an image. This feeling is particularly obvious in the gray image. When the whole image is white to the original image, we will feel that the brightness of the image increases. When the whole image is black (gray) to the original image, we will feel that the brightness of the image decreases.

The pixel value of the gray image represents the gray value of a certain point in the image. When the pixel value is 255, it is white, and when the pixel value is 0, it is black.

Therefore, when you want to adjust the brightness of the gray image, you can change the size of the pixels as a whole (let all pixels add or subtract a certain value).

Contrast, what's that? Encyclopedia's explanation: contrast refers to the measurement of different brightness levels between the brightest white and the darkest black in the light and dark area of an image. The larger the difference range, the greater the contrast, and the smaller the difference range, the smaller the contrast. A good contrast ratio of 120:1 can easily display vivid and rich colors. When the contrast ratio is as high as 300:1, all levels of colors can be supported.

My personal feeling is that black places are darker and white places are whiter. The effect of multiplication and division can make higher pixel values higher, and the change of relatively lower pixel values is not so obvious.

At that time, I thought that the effect of increasing the contrast when masking pixels was better than directly multiplying or dividing the pixel value by a certain value.

Therefore, the formula for the operation of brightness and contrast of the picture:
g ( x ) = α ∗ f ( x ) + β g(x) = \alpha * f(x) + \beta g(x)=α∗f(x)+β

α : yes than degree system number , β : bright degree system number \alpha: contrast coefficient, beta: brightness coefficient α: Contrast coefficient, β: Luminance coefficient

For three channel RGB pictures, the effective value range of R, G and B is also 0 ~ 255, so in fact, the above formula can be used, and the essence has not changed.

Some people don't understand why a specific value is directly added or multiplied, and the range box is set within 0 ~ 255. When the picture can still be seen, the tone of the original picture remains unchanged, but some other operations will become very strange

2. Code implementation

#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui/highgui_c.h>
#include <iostream>

using namespace cv;
using namespace std;

//Function operation of brightness and contrast
Mat Image_manipulation(Mat imgin,float alpha,float beta)//alpha contrast coefficient, beta luminance coefficient
{
	int height = imgin.rows;//Number of rows
	int width = imgin.cols;//Number of columns
	int dth = imgin.channels();//Number of channels
    //Create empty image
	Mat imgout;
	imgout = Mat::zeros(imgin.size(),imgin.type());
	//Convert to a data type that can use Vec3f
	Mat m1;
	imgin.convertTo(m1, CV_32F);
	//Carry out point operation
	for (int i = 0; i < height; i++)
	{
		for (int j = 0; j < width; j++)
		{
			if (dth == 3)
			{
				//Pay attention to the data type when operating
				float b = m1.at<Vec3f>(i, j)[0];
				float g = m1.at<Vec3f>(i, j)[1];
				float r = m1.at<Vec3f>(i, j)[2];
				
				imgout.at<Vec3b>(i, j)[0] = saturate_cast<uchar>(b * alpha + beta);//Make sure the value is in the range of 0 ~ 255
				imgout.at<Vec3b>(i, j)[1] = saturate_cast<uchar>(g * alpha + beta);
				imgout.at<Vec3b>(i, j)[2] = saturate_cast<uchar>(r * alpha + beta);
			}
				
			else if (dth == 1)
			{
				float gray_num = imgin.at<uchar>(i, j);
				imgout.at<uchar>(i, j) = saturate_cast<uchar>(gray_num * alpha + beta);
			}
		}
	}
	return imgout;
}

int main(int argc, char** argv)
{
	string path = "XXXXXX/XXXXXXX.jpg";
	Mat img = imread(path);
	Mat img_gray;
	cvtColor(img, img_gray, CV_RGB2GRAY);
    //Operation of brightness and contrast
	img = Image_manipulation(img, 2, 0);
	img_gray = Image_manipulation(img_gray, 1.2, -50);
	//imshow("img", img);
	imshow("img_gray", img_gray);
	waitKey(0);
	return 0;
}

Some functions that need attention

imgin.convertTo(m1, CV_32F);//Convert Mat type
saturate_cast<uchar>(b * alpha + beta);//Make sure the value is in the range of 0 ~ 255

3. Other operations

1. Picture inversion

I think this is a very simple understanding, that is, subtract each value of a pixel with 255

It is found that the direct negative pixel value can achieve a similar effect

Code (implementation only)

for (int i = 0; i < height; i++)
	{
		for (int j = 0; j < width; j++)
		{
			if (dth == 3)
			{
				//Pay attention to the data type when operating
				int b = m1.at<Vec3b>(i, j)[0];
				int g = m1.at<Vec3b>(i, j)[1];
				int r = m1.at<Vec3b>(i, j)[2];
				
				imgout.at<Vec3b>(i, j)[0] = 255-b;//Make sure the value is in the range of 0 ~ 255
				imgout.at<Vec3b>(i, j)[1] = 255-g;
				imgout.at<Vec3b>(i, j)[2] = 255-r;
			}
				
			else if (dth == 1)
			{
				int gray_num = imgin.at<uchar>(i, j);
				imgout.at<uchar>(i, j) = -gray_num;
			}
		}
	}

3. Picture mixing

Is to add up the two pictures with different weights to achieve the effect of two pictures together
y = α ∗ f ( x ) + ( 1 − α ) ∗ g ( x ) + β y = \alpha * f(x) + (1- \alpha)*g(x)+\beta y=α∗f(x)+(1−α)∗g(x)+β
It can also be realized directly here, which is relatively simple. Just pass two pictures in and set the value obtained by the formula to another picture directly.

openCV also provides an api:

float alpha = 0.5;
float beta = 0;
addWeighted(img1, alpha, img2, alpha, beta, dst);
//Calculate img1 and img2 with different weights to obtain different values, and then assign them to the Mat image of dst

Note: the size of img1 and img2 images should be the same

Topics: OpenCV Computer Vision image processing