OpenCV4-image pixel operation processing

OpenCV4-image pixel operation processing

    • 1. Image pixel statistics
      • Maximum and minimum values
      • mean and standard deviation
    • 2. Pixel operations between two images
      • Comparison operation between two images
      • Logical operations on two images
    • 3. Image binarization
    • 4.LUT

1. Image pixel statistics

Find the maximum, minimum, average, mean square error and many other statistical functions of image pixels.

Maximum and minimum values

void minMaxLoc(InputArray src, // Must be a single-channel matrix
               CV_OUT double* minVal, // Output the maximum value
               CV_OUT double* maxVal = 0, // Output the minimum value
               CV_OUT Point* minLoc = 0, // Maximum coordinate
               CV_OUT Point* maxLoc = 0, // Minimum coordinate
               InputArray mask = noArray());

Coordinate type:

//Class template
template<typename _Tp> class Point_
{<!-- -->
public:
    // ...
    _Tp x; //!< x coordinate of the point
    _Tp y; //!< y coordinate of the point
};

//template instantiation
typedef Point_<int> Point2i;
typedef Point_<int64> Point2l;
typedef Point_<float> Point2f;
typedef Point_<double> Point2d;
//Integer coordinates
typedef Point2i Point;

Since the pixel coordinate axis of the image has the upper left corner as the coordinate origin, the horizontal direction is the x-axis, and the vertical direction is the y-axis, therefore Point(x, y) corresponds to the row and column representation of the image is Point(number of columns, number of rows).

The function of the minMaxLoc function is to find the maximum value in a specific area in the image. The first parameter of the function is a single-channel matrix. If it is a multi-channel matrix, you need to use the reshape function to convert the multi-channel into a single channel:

Mat cv::Mat::reshape(int cn, // Number of channels of the converted matrix
                     int rows=0) const; //The number of rows in the matrix after conversion. If it is 0, it means that the number of rows after conversion is the same as the number of rows before conversion.

If the minMaxLoc function does not find the maximum value, you can set the maxVal and maxLoc parameters to NULL.

The last parameter of the minMaxLoc function is the mask matrix for finding the maximum value, which is used to mark the range for finding the above four values. The default value of the parameter is noArray(), which means that the search range is all data in the matrix.

#include <opencv2/core/utils/logger.hpp>
#include <opencv2/opencv.hpp>

using namespace cv;
using namespace std;

int main()
{<!-- -->
cout << "OpenCV Version: " << CV_VERSION << endl;
utils::logging::setLogLevel(utils::logging::LOG_LEVEL_SILENT);

float a[12] = {<!-- --> 1, 2, 3, 4, 5, 10, 6, 7, 8, 9, 10, 0 };
Mat img = Mat(3, 4, CV_32FC1, a); //Single channel matrix
Mat imgs = Mat(2, 3, CV_32FC2, a); //Multi-channel matrix
double minVal, maxVal; //used to store the maximum and minimum values in the matrix
Point minIdx, maxIdx; //Used to store the position of the maximum and minimum values in the matrix in the matrix

/*Find the maximum value in the single-channel matrix*/
minMaxLoc(img, & amp;minVal, & amp;maxVal, & amp;minIdx, & amp;maxIdx);
cout << "The maximum value in img is:" << maxVal << " " << "The position in the matrix:" << maxIdx << endl;
cout << "The minimum value in img is:" << minVal << " " << "The position in the matrix:" << minIdx << endl;

/*Find the maximum value in the multi-channel matrix*/
Mat imgs_re = imgs.reshape(1, 4); //Convert multi-channel matrix into single-channel matrix
minMaxLoc(imgs_re, & amp;minVal, & amp;maxVal, & amp;minIdx, & amp;maxIdx);
cout << "The maximum value in imgs is:" << maxVal << " " << "Position in the matrix:" << maxIdx << endl;
cout << "The minimum value in imgs is:" << minVal << " " << "The position in the matrix:" << minIdx << endl;

int k = waitKey(0); // Wait for a keystroke in the window
return 0;
}
/*
The maximum value in img is: 10. The position in the matrix: [1, 1]
The minimum value in img is: 0. Position in the matrix: [3, 2]
The maximum value in imgs is: 10. The position in the matrix: [2, 1]
The minimum value in imgs is: 0. Position in the matrix: [2, 3]
*/

Note that the image matrix above can be used to view the matrix value in the Image Watch plug-in of VS2022. In addition, pay attention to the output results, with the number of columns first and the number of rows last.

Mean and standard deviation

The mean value of the image represents the overall brightness and darkness of the image. The greater the mean value of the image, the brighter the overall image. The standard deviation represents the contrast degree of light and dark changes in the image. The larger the standard deviation, the more obvious the light and dark changes in the image. OpenCV provides the mean() function to calculate the mean of the image, and the meanStdDev() function to simultaneously calculate the mean and standard deviation of the image.

Scalar cv::mean(InputArray src, // Image matrix to be averaged
                InputArray mask = noArray()); // The mask is used to mark which areas are averaged
/*
This function is used to find the average value of each channel of the image matrix. src can be channels 1-4. The return value is a variable of type cv::Scalar. The return value has 4 bits, which respectively represent the 4 channels of the input image. Average value, if the input image has only one channel, then the last 3 bits of the return value are all 0. Use cv::Scalar[n] to view the average value of the nth channel.
*/


void cv::meanStdDev(InputArray src, // Image matrix to be averaged
                    OutputArray mean, //Mean value of each channel, Mat type
                    OutputArray stddev, // standard deviation of each channel, Mat type
                    InputArray mask=noArray()); //Mask, used to mark which areas to obtain the average and standard deviation

View the mean return value: Scalar class

typedef Scalar_<double> Scalar;

// Class template, inherits Vec_
template<typename _Tp> class Scalar_ : public Vec<_Tp, 4>
{<!-- -->
public:
    // ...
};

// Class template, inherits Matx_, defaults to 1 column
template<typename _Tp, int cn> class Vec : public Matx<_Tp, cn, 1>
{<!-- -->
public:
// ...
}

// Class template, array of m rows and n columns
template<typename _Tp, int m, int n> class Matx
{<!-- -->
public:
    _Tp val[m*n]; //< matrix elements
};
#include <opencv2/core/utils/logger.hpp>
#include <opencv2/opencv.hpp>

using namespace cv;
using namespace std;

int main()
{<!-- -->
cout << "OpenCV Version: " << CV_VERSION << endl;
utils::logging::setLogLevel(utils::logging::LOG_LEVEL_SILENT);

float a[12] = {<!-- --> 1, 2, 3, 4, 5, 10, 6, 7, 8, 9, 10, 0 };
Mat img = Mat(3, 4, CV_32FC1, a); //Single channel matrix
Mat imgs = Mat(2, 3, CV_32FC2, a); //Multi-channel matrix

cout << "/* Use mean to find the mean of the image */" << endl;
Scalar myMean;
myMean = mean(imgs);
cout << "imgsmean=" << myMean << endl;
cout << "The mean value of the first channel of imgs=" << myMean[0] << " "
<< "The mean value of the second channel of imgs=" << myMean[1] << endl << endl;

cout << "/* Use meanStdDev to simultaneously find the mean and standard deviation of the image */" << endl;
Mat myMeanMat, myStddevMat;

meanStdDev(img, myMeanMat, myStddevMat);
cout << "imgmean=" << myMeanMat << endl;
cout << "img standard deviation=" << myStddevMat << endl << endl;
meanStdDev(imgs, myMeanMat, myStddevMat);
cout << "imgsmean=" << myMeanMat << endl;
cout << "imgs standard deviation=" << myStddevMat << endl;

int k = waitKey(0); // Wait for a keystroke in the window
return 0;
}
/*
/* Use mean to find the mean of the image */
imgs mean=[5.5, 5.33333, 0, 0]
The mean of the first channel of imgs=5.5 The mean of the second channel of imgs=5.33333

/* Use meanStdDev to simultaneously find the mean and standard deviation of the image */
imgmean=[5.416666666666666]
img standard deviation=[3.32812092461931]

imgsmean=[5.5;
 5.333333333333333]
imgs standard deviation=[2.986078811194819;
 3.636237371545238]
*/

2. Pixel operation between two images

The operations such as calculating the maximum value and average value introduced earlier are all processing one image. Next, we will introduce the related operations of pixels between two images.

Comparison operation between two images

OpenCV provides the max() and min() functions that calculate the larger or smaller grayscale value of each pixel in two images. These two functions compare the grayscale value of each element in the two images respectively, retaining the smaller grayscale value. Large (smaller) gray value.

void max(const Mat & amp; src1, // The first image matrix, any number of channels
         const Mat & amp; src2, // The second image matrix, the size, data type, and number of channels are the same as src1
         Mat & amp; dst); // retain the image matrix after the larger gray level of the corresponding position

void min(const Mat & src1,
         const Mat & src2,
         Mat & amp; dst); // Image matrix after retaining the smaller grayscale of the corresponding position
#include <opencv2/core/utils/logger.hpp>
#include <opencv2/opencv.hpp>

using namespace cv;
using namespace std;

int main()
{<!-- -->
cout << "OpenCV Version: " << CV_VERSION << endl;
    utils::logging::setLogLevel(utils::logging::LOG_LEVEL_SILENT);

float a[12] = {<!-- --> 1, 2, 3.3, 4, 5, 9, 5, 7, 8.2, 9, 10, 2 };
float b[12] = {<!-- --> 1, 2.2, 3, 1, 3, 10, 6, 7, 8, 9.3, 10, 1 };
Mat imga = Mat(3, 4, CV_32FC1, a);
Mat imgb = Mat(3, 4, CV_32FC1, b);
Mat imgas = Mat(2, 3, CV_32FC2, a);
Mat imgbs = Mat(2, 3, CV_32FC2, b);

//Compare two single-channel matrices
Mat myMax, myMin;
max(imga, imgb, myMax);
min(imga, imgb, myMin);

//Compare two multi-channel matrices
Mat myMaxs, myMins;
max(imgas, imgbs, myMaxs);
min(imgas, imgbs, myMins);

//Compare two color images
Mat img0 = imread("len.png");
Mat img1 = imread("noobcv.jpg");

if (img0.empty() || img1.empty())
{<!-- -->
cout << "Please confirm whether the image file name is correct" << endl;
return -1;
}
Mat comMin, comMax;
max(img0, img1, comMax);
min(img0, img1, comMin);
imshow("comMin", comMin);
imshow("comMax", comMax);

//Compare with mask
Mat src1 = Mat::zeros(Size(512, 512), CV_8UC3);
Rect rect(100, 100, 300, 300);
src1(rect) = Scalar(255, 255, 255); //Generate a low-pass 300*300 mask
Mat comsrc1, comsrc2;
min(img0, src1, comsrc1);
imshow("comsrc1", comsrc1);

Mat src2 = Mat(512, 512, CV_8UC3, Scalar(0, 0, 255)); //Generate a low-pass mask showing the red channel
min(img0, src2, comsrc2);
imshow("comsrc2", comsrc2);

//Compare two grayscale images
Mat img0G, img1G, comMinG, comMaxG;
cvtColor(img0, img0G, COLOR_BGR2GRAY);
cvtColor(img1, img1G, COLOR_BGR2GRAY);
max(img0G, img1G, comMaxG);
min(img0G, img1G, comMinG);
imshow("comMinG", comMinG);
imshow("comMaxG", comMaxG);

int k = waitKey(0); // Wait for a keystroke in the window
return 0;
}

It is worth mentioning the use of masks in the above code:

Mat src1 = Mat::zeros(Size(512, 512), CV_8UC3);
Rect rect(100, 100, 300, 300);
src1(rect) = Scalar(255, 255, 255); //Generate a low-pass 300*300 mask
Mat comsrc1, comsrc2;
min(img0, src1, comsrc1);
imshow("comsrc1", comsrc1);

Generate a 3-channel image of 512×512 size, in the square area from the upper left corner (100, 100) to the lower right corner (300, 300), set the value to Scalar (255, 255, 255) white. Then min takes the minimum value of the lena image and the mask image, and the lena image in the square area can be intercepted.

Logical operation of two images

OpenCV provides AND, OR, XOR operations between two image pixels and NOT operations on a single image pixel. If the pixel values are only 0 and 1, then the bit operation corresponds exactly. However, the pixel value of the CV_8U type image ranges from 0 to 255. The logical operation at this time needs to convert the pixel value into a binary number before performing it.

void bitwise_not(InputArray src,
                 OutputArray dst,
                 InputArray mask = noArray());

void bitwise_and(InputArray src1,
                 InputArray src2,
                 OutputArray dst,
                 InputArray mask = noArray());

void bitwise_or(InputArray src1,
                InputArray src2,
                OutputArray dst,
                InputArray mask = noArray());

void bitwise_xor(InputArray src1,
                 InputArray src2,
                 OutputArray dst,
                 InputArray mask = noArray());
/*
src1: The first image matrix, which can be multi-channel data
src2: The second image matrix, the size, number of channels, and data type are consistent with src1
dst: logical operation output result
mask: used to set the range of logical operations in an image or matrix
*/
#include <opencv2/core/utils/logger.hpp>
#include <opencv2/opencv.hpp>

using namespace cv;
using namespace std;

int main()
{<!-- -->
cout << "OpenCV Version: " << CV_VERSION << endl;
utils::logging::setLogLevel(utils::logging::LOG_LEVEL_SILENT);

Mat img = imread("lena.png");
if (img.empty())
{<!-- -->
cout << "Please confirm whether the image file name is correct" << endl;
return -1;
}
//Create two black and white images
Mat img0 = Mat::zeros(200, 200, CV_8UC1);
Mat img1 = Mat::zeros(200, 200, CV_8UC1);
Rect rect0(50, 50, 100, 100);
img0(rect0) = Scalar(255);
Rect rect1(100, 100, 100, 100);
img1(rect1) = Scalar(255);
imshow("img0", img0);
imshow("img1", img1);

//Perform logical operations
Mat myAnd, myOr, myXor, myNot, imgNot;
bitwise_not(img0, myNot);
bitwise_and(img0, img1, myAnd);
bitwise_or(img0, img1, myOr);
bitwise_xor(img0, img1, myXor);
bitwise_not(img, imgNot);
imshow("myAnd", myAnd);
imshow("myOr", myOr);
imshow("myXor", myXor);
imshow("myNot", myNot);
imshow("img", img);
imshow("imgNot", imgNot);

int k = waitKey(0); // Wait for a keystroke in the window
return 0;
}

3. Image binarization

The pixel gray value of a black and white image has only two values, the maximum value and the minimum value, no matter what data type it is, so it is called a binary image. Binary images with fewer color types can be highly compressed to save storage space. The process of converting non-binary images into binary images through calculation is called image binarization. OpenCV provides the threshold() and adaptiveThreshold() functions for binarizing images.

double threshold(InputArray src, // The image to be binarized can only be of two data types: CV_8U and CV_32F. The channel requirements are related to the type parameter.
                 OutputArray dst, // Binarized image
                 double thresh, // Binarization threshold
                 double maxval, // The maximum value of the binarization process, only used in THRESH_BINARY and THRESH_BINARY_INV
                 int type );

type:

Mark parameter Abbreviation Function
THRESH_BINARY 0 Grayscale The value greater than the threshold is the maximum value maxval, and other values are 0
THRESH_BINARY_INV 1 The gray value greater than the threshold is 0, and other values are the maximum value maxval
THRESH_TRUNC 2 Thresh is the grayscale value greater than the threshold, and other values remain unchanged
THRESH_TOZERO 3 The gray value greater than the threshold remains unchanged, other values are 0
THRESH_TOZERO_INV 4 The gray value greater than the threshold is 0, and other values remain unchanged
THRESH_OTSU 8 Otsu method automatically finds the global threshold
THRESH_TRIANGLE 16 The triangle method automatically finds the global threshold

The THRESH_OTSU and THRESH_TRIANGLE flags are methods for obtaining thresholds, not flags for threshold comparison methods. These two flags can be used together with the first 5 flags. The first five methods all need to set the threshold value. The Otsu method (OTSU) and the triangle method (TRIANGLE) combine the image gray value distribution characteristics to obtain the binarized threshold value, and the threshold value is given in the form of a function return value. These two flags only support images of type CV_8UC1.

The threshold function uses only one threshold globally. In actual situations, due to uneven lighting and the existence of shadows, having only one threshold globally will cause the white area in the shadow to be binarized into black by the function, so the adaptiveThreshold function provides Two binarization methods for local adaptive thresholds:

void adaptiveThreshold(InputArray src, // The image to be binarized can only be of CV_8UC1 type
                       OutputArray dst, // Binarized image
                       double maxValue, // The maximum value of binarization
                       int adaptiveMethod, // Adaptive threshold method, mean method ADAPTIVE_THRESH_MEAN_C, Gaussian method ADAPTIVE_THRESH_GAUSSIAN_C
                       int thresholdType, // Image binarization method, can only be THRESH_BINARY and THRESH_BINARY_INV
                       int blockSize, // Adaptively determine the pixel area size of the threshold, usually an odd number of 3, 5, 7
                       double C ); // constant subtracted from the average or weighted average

This function converts the grayscale image into a binary image, adaptively calculates the threshold within the blocksize * blocksize neighborhood through the mean method and Gaussian method, and then performs binarization.

#include <opencv2/core/utils/logger.hpp>
#include <opencv2/opencv.hpp>

using namespace cv;
using namespace std;

int main()
{<!-- -->
cout << "OpenCV Version: " << CV_VERSION << endl;
utils::logging::setLogLevel(utils::logging::LOG_LEVEL_SILENT);

Mat img = imread("lena.png");
if (img.empty())
{<!-- -->
cout << "Please confirm whether the image file name is correct" << endl;
return -1;
}
Mat gray;
cvtColor(img, gray, COLOR_BGR2GRAY);
Mat img_B, img_B_V, gray_B, gray_B_V, gray_T, gray_T_V, gray_TRUNC;

//Color image binarization
threshold(img, img_B, 125, 255, THRESH_BINARY);
threshold(img, img_B_V, 125, 255, THRESH_BINARY_INV);
imshow("img_B", img_B);
imshow("img_B_V", img_B_V);

//Greyscale image BINARY binarization
threshold(gray, gray_B, 125, 255, THRESH_BINARY);
threshold(gray, gray_B_V, 125, 255, THRESH_BINARY_INV);
imshow("gray_B", gray_B);
imshow("gray_B_V", gray_B_V);

//Grayscale image TOZERO transformation
threshold(gray, gray_T, 125, 255, THRESH_TOZERO);
threshold(gray, gray_T_V, 125, 255, THRESH_TOZERO_INV);
imshow("gray_T", gray_T);
imshow("gray_T_V", gray_T_V);

//Grayscale image TRUNC transformation
threshold(gray, gray_TRUNC, 125, 255, THRESH_TRUNC);
imshow("gray_TRUNC", gray_TRUNC);

//Grayscale image binarization using Otsu method and triangle method
Mat img_Thr = imread("threshold.png", IMREAD_GRAYSCALE);
Mat img_Thr_O, img_Thr_T;
threshold(img_Thr, img_Thr_O, 100, 255, THRESH_BINARY | THRESH_OTSU);
threshold(img_Thr, img_Thr_T, 125, 255, THRESH_BINARY | THRESH_TRIANGLE);
imshow("img_Thr", img_Thr);
imshow("img_Thr_O", img_Thr_O);
imshow("img_Thr_T", img_Thr_T);

//Adaptive binarization of grayscale images
Mat adaptive_mean, adaptive_gauss;
adaptiveThreshold(img_Thr, adaptive_mean, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 55, 0);
adaptiveThreshold(img_Thr, adaptive_gauss, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY, 55, 0);

imshow("adaptive_mean", adaptive_mean);
imshow("adaptive_gauss", adaptive_gauss);

int k = waitKey(0); // Wait for a keystroke in the window
return 0;
}

4.LUT

The threshold comparison methods introduced earlier only have one threshold. If you need to compare with multiple thresholds, you need to use a look-up table (LUT). LUT is a mapping table of pixel grayscale values.

void LUT(InputArray src, //Input image matrix, the data type can only be CV_8U
         InputArray lut, // Lookup table of 256 pixel grayscale values, single channel or the same number of src channels
         OutputArray dst); // Output image matrix

If the lut is a single channel, each channel in src is mapped according to the lut. If the lut is multi-channel, the i-th channel in src is mapped according to the i-th channel of the lut.

#include <opencv2/core/utils/logger.hpp>
#include <opencv2/opencv.hpp>

using namespace cv;
using namespace std;

int main()
{<!-- -->
cout << "OpenCV Version: " << CV_VERSION << endl;
utils::logging::setLogLevel(utils::logging::LOG_LEVEL_SILENT);

//LUT lookup table first layer
uchar lutFirst[256];
for (int i = 0; i < 256; i + + )
{<!-- -->
if (i <= 100)
lutFirst[i] = 0;
if (i > 100 & amp; & amp; i <= 200)
lutFirst[i] = 100;
if (i > 200)
lutFirst[i] = 255;
}
Mat lutOne(1, 256, CV_8UC1, lutFirst);

//LUT lookup table second layer
uchar lutSecond[256];
for (int i = 0; i < 256; i + + )
{<!-- -->
if (i <= 100)
lutSecond[i] = 0;
if (i > 100 & amp; & amp; i <= 150)
lutSecond[i] = 100;
if (i > 150 & amp; & amp; i <= 200)
lutSecond[i] = 150;
if (i > 200)
lutSecond[i] = 255;
}
Mat lutTwo(1, 256, CV_8UC1, lutSecond);

//LUT lookup table third layer
uchar lutThird[256];
for (int i = 0; i < 256; i + + )
{<!-- -->
if (i <= 100)
lutThird[i] = 100;
if (i > 100 & amp; & amp; i <= 200)
lutThird[i] = 200;
if (i > 200)
lutThird[i] = 255;
}
Mat lutThree(1, 256, CV_8UC1, lutThird);

//LUT lookup table matrix with three channels
vector<Mat> mergeMats;
mergeMats.push_back(lutOne);
mergeMats.push_back(lutTwo);
mergeMats.push_back(lutThree);
Mat LutTree;
merge(mergeMats, LutTree);

//Calculate lookup table for image
Mat img = imread("lena.png");
if (img.empty())
{<!-- -->
cout << "Please confirm whether the image file name is correct" << endl;
return -1;
}
Mat gray, out0, out1, out2;
cvtColor(img, gray, COLOR_BGR2GRAY);
LUT(gray, lutOne, out0);
LUT(img, lutOne, out1);
LUT(img, LutTree, out2);
imshow("out0", out0);
imshow("out1", out1);
imshow("out2", out2);

int k = waitKey(0); // Wait for a keystroke in the window
return 0;
}