OpenCV basic tutorial - image digitization

Keywords: OpenCV Computer Vision image processing

1. Image digitization

1.1 Mat class in initial OpenCV

1.1.1 initial Mat

The constructor of Mat class is as follows:

Mat(int rows, int cols, int type)

Where, type represents the type, including the number of channels and their data types.

CV_8UC(n): uchar type occupying 1 byte

CV_8SC(n): char type occupying 1 byte

CV_16UC(n): ushort type occupying 2 bytes

CV_16SC(n): short type occupying 2 bytes

CV_32SC(n): int type occupying 4 bytes

CV_32FC(n): float type occupying 4 bytes

CV_64FC(n): double type occupying 8 bytes

S – signed int ------ signed integer

U – stands for – unsigned int ------ unsigned integer

F – stands for float ------ single precision floating point type

C(n) represents the number of channels, and the brackets can be omitted here.

n=1: single channel matrix / two-dimensional matrix / gray image

n=3: three channel matrix / three-dimensional matrix / RGB color picture

n=4: four channel matrix / three-dimensional matrix / RGB picture with Alph channel

The constructor of Mat can also take the following form:

Mat(Size(int cols, int rows), int type)

The Size class of OpenCV is used. Note that the order of Size is columns × that 's ok.

1.1.2 constructing a single channel Mat object

There are three methods to construct Mat objects:

//Construct a matrix with 2 rows and 3 columns
Mat m = Mat(2, 3, CV_32FC1);
//You can also use the Size object directly
Mat m = Mat(Size(3, 2), CV_32FC1);
//You can also use the member function create in Mat to complete the construction of Mat objects
Mat m;
m.create(2, 3, CV_32FC1);
m.create(Size(3, 2), CV_32FC1);

Construction of 0 matrix

Mat o = Mat::zeros(2, 3, CV_32FC1);
Mat o = Mat::zeros(Size(3, 2), CV_32FC1);

1. Construction of matrix

Mat m = Mat::ones(2, 3, CV_32FC1);
Mat m = Mat::ones(Size(3, 2), CV_32FC1);

How to quickly create a matrix

Mat m = (Mat_<int>(2,3) << 1,2,3,4,5,6);	//2 rows and 3 columns

1.1.3 obtain basic information of single channel Mat

Mat m = (Mat_<int>(2,3) << 1,2,3,4,5,6);
//Gets the number of rows of the matrix
cout << m.rows << endl;
//Gets the number of columns in the matrix
cout << m.cols << endl;
//Gets the size of the matrix
Size size = m.size();
cout << size << endl;	//[2×3]
//Gets the number of channels in the matrix
cout << m.channels() << endl;
//Get the area of the matrix (the number of rows of the matrix multiplied by the number of columns)
cout << m.total() << endl;
//Obtain the dimension of the matrix (the dimension of single channel matrix is 2 and the dimension of multi-channel matrix is 3)
cout << m.dims << endl;

1.1.4 accessing values in a single channel Mat object

1. Using member function at
//format
m.at<float>(r, c);	//Access the value of row r and column c
2. Using member function ptr

For the storage of the value in Mat in memory, the value of each row is stored in a continuous memory area, and the pointer to the first address of each row is obtained through the member function ptr.

You can use the pointer returned by the member function ptr to access the value in m.

for(int r = 0; r < m.rows; r++)
{
    //Get the address at the beginning of row r of matrix m
    const int * ptr = m.ptr<int>(r);
    //Print all values on line r
    for(int c = 0; c < m.cols; c++)
    {
        cout << ptr[c] << ",";
    }
    cout << endl;
}
3. Use the member functions isContinuous and ptr

For the storage of the values in Mat in memory, the values of each row are stored in a continuous memory area, but there may be an interval between rows.

If the isContinuous return value is true, it means that rows are also stored continuously, that is, all values are stored continuously. The usage is as follows:

if(m.isContinuous())
{
    //Get the address of the first value of matrix m
    int * ptr = m.ptr<int>(0);
    //Take value with operator []
    for(int n = 0; n < m.rows * m.cols; n++)
        cout << ptr[n] << ",";
}
4. Use the member variables step and data

step[0]: indicates the number of bytes occupied by each row (including the interval if there is an interval between rows)

step[1]: indicates the number of bytes occupied by each value

data: indicates the pointer to the first value

//Access the value of row r and column c of a single channel matrix of type int
*((int *)(m.data + m.step[0] * r + m.step[1] * c));
summary

In terms of value efficiency, the direct use of pointer is the fastest, and the use of at is the slowest;

In terms of readability, the use of at is the highest, and the direct use of pointer is the lowest.

1.1.5 vector Vec

The vector here can be understood as a column vector in the mathematical sense

//Construct a_ cn × Column vector of 1, data type is_ Tp
Vec<Typename _Tp, int _cn>;
//Construct a 3 × Column vector of 1, data type is int, initialization is 21, 32, 14
Vec<int, 3> vi(21, 32, 14);

The values in the vector can be accessed with [] or ()

cout << vi[0] << endl;
cout << vi(1) << endl;

OpenCV takes an alias for the declaration of vector class, for example:

typedef Vec<uchar, 3> Vec3b;
typedef Vec<int, 2> Vec2i;
typedef Vec<float, 4> Vec4f;
typedef Vec<double, 3> Vec3d;
...
//See opencv2/core/core.hpp for details

1.1.6 constructing multichannel Mat objects

//Construct a float type three channel matrix with 2 rows and 2 columns
Mat mm = (Mat_<Vec3f>(2,2)<<Vec3f(1,11,21), Vec3f(2,12,32), Vec3f(3,13,23), Vec3f(4,24,34));

1.1.7 accessing values in multichannel Mat objects

1. Using member function at
//Get the element value of row r and column c
cout << mm.at<Vec3f>(r,c) << ",";	//[1,11,21],[2,12,32],[3,13,23],[4,24,34]
2. Using member function ptr
for(int r = 0; r < mm.rows; r++)
{
    //Get the address at the beginning of row r of matrix mm
    Vec3f * ptr = m.ptr<Vec3f>(r);
    //Print all values on line r
    for(int c = 0; c < mm.cols; c++)
    {
        cout << ptr[c] << ",";	//The printing result is the same as using the member function at
    }
    cout << endl;
}
3. Use the member functions isContinuous and ptr
if(mm.isContinuous())
{
    //Get the address of the first value of matrix m
    Vec3f * ptr = mm.ptr<Vec3f>(0);
    //Take value with operator []
    for(int n = 0; n < mm.rows * mm.cols; n++)
        cout << ptr[n] << ",";
}
4. Use member variables data and step
//Access the value of row r and column c of a single channel matrix of type int
*((Vec3f *)(mm.data + mm.step[0] * r + mm.step[1] * c));
5. Separation channel
void cv::split(cv::InputArray m, cv::OutputArrayOfArrays mv)
Mat mm = (Mat_<Vec3f>(2,2)<<Vec3f(1,11,21),Vec3f(2,12,32),Vec3f(3,13,23),Vec3f(4,24,34));
vector<Mat> planes;
split(mm,planes);
cout<<planes[1].at<float>(0,0)<<endl;	//11

If the original matrix is a three channel matrix, such as mm. After separation:

planes[0]: represents the single channel matrix composed of the first number of each pixel

planes[1]: represents the single channel matrix composed of the second number of each pixel

planes[2]: represents the single channel matrix composed of the third number of each pixel

6. Merge channels
void cv::merge(const cv::Mat *mv, size_t count, cv::OutputArray dst)
//Three single channel matrices
Mat plane0 = (Mat_<int>(2,2) << 1, 2, 3, 4);
Mat plane1 = (Mat_<int>(2,2) << 5, 6, 7, 8);
Mat plane2 = (Mat_<int>(2,2) << 9, 10, 11, 12);
//Initializes an array with three single channel matrices
Mat plane[] = {plane0, plane1, plane2};
//Merge into a multichannel matrix
Mat mat;
merge(plane, 3, mat);

it's fine too:

//void merge(InputArrayOfArrays mv, OutputArray dst)
//Three single channel matrices
Mat plane0 = (Mat_<int>(2,2) << 1, 2, 3, 4);
Mat plane1 = (Mat_<int>(2,2) << 5, 6, 7, 8);
Mat plane2 = (Mat_<int>(2,2) << 9, 10, 11, 12);
//Put the three single channel matrices into the vector container in turn
vector<Mat> plane;
plane.push_back(plane0);
plane.push_back(plane1);
plane.push_back(plane2);
//Merge into a multichannel matrix
Mat mat;
merge(plane, mat);

1.1.8 obtain the value of an area in Mat

1. Use the member function row(i) or col(j) to get the i-th row or j-th column of the matrix
int r = 1;
int c = 0;
//Row r of the matrix
Mat mr = m.row(r);
//Column c of the matrix
Mat mc = m.col(c);

Note: the return value is still a single channel Mat type

2. Use the member function rowRange or colRange to obtain the continuous rows or columns of the matrix
Mat matrix = (Mat_<int>(5,5)<<1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25);
//The Range class of OpenCV is used to construct a continuous integer sequence of [_start,_end)
Mat r_range = matrix.rowRange(Range(2,4));	//Index starts at 0
//Range() can also be omitted
Mat r_range = matrix.rowRange(2,4);
Mat c_range = matrix.colRange(1,3);

Note: if you change a value of r_range or c_range, the original matrix will also change.

3. Use the member functions clone and copyTo

It can solve the problems mentioned in the previous point.

//clone
Mat r_range = matrix.rowRange(2, 4).clone();
//copyTo
Mat r_range;
matrix.rowRange(2, 4).copyTo(r_range);

Note: if you change a value of r_range or c_range, the original matrix will not change.

4. Use Rect class
Mat roi1 = matrix(Rect(Point(2,1),Point(3,2))).clone();	//The coordinates of the upper left corner and the coordinates of the lower right corner
Mat roi2 = matrix(Rect(2,1,2,2).clone();				//x. Y, width, height
Mat roi3 = matrix(Rect(Point(2,1),Size(2,2))).clone();	//Coordinates of upper left corner, dimension

1.2 matrix operation

1.2.1 addition operation

Mat src1 = (Mat_<uchar>(2,3) << 23, 123, 90, 100, 250, 0);
Mat src2 = (Mat_<uchar>(2,3) << 125, 150, 60, 100, 10, 40);
Mat dst = src1 + src2;

be careful:

  1. The data types of the two matrices must be the same, otherwise an error will be reported;
  2. A value can be added to a Mat object, but no matter what data type the value is, the data type of the returned Mat is the same as that of the input Mat;
  3. If the result of the addition exceeds the maximum value of a certain type, the result may be truncated to the maximum value.

To solve the problem of plus sign +, you can use the add function:

void cv::add(cv::InputArray src1, cv::InputArray src2, cv::OutputArray dst, cv::InputArray mask = noArray(), int dtype = -1)
Mat src1 = (Mat_<uchar>(2,3) << 23, 123, 90, 100, 250, 0);
Mat src2 = (Mat_<uchar>(2,3) << 125, 150, 60, 100, 10, 40);
Mat dst;
add(src1,src2,dst);
//The data types of the input matrix can be different
//The data type of the output matrix can be specified according to the situation
add(src1,src2,dst,Mat(),CV_64FC1);

Note: here, Mat() represents an empty matrix []. This parameter value will be used when mask operation is required, and Mat() can be used at other times.

Two vectors can also be added:

Vec3f v1 = Vec3f(1, 2, 3);
Vec3f v2 = Vec3f(10, 1, 12);
Vec3f v = v1 + v2;	//[11,3,15]

1.2.2 subtraction

Mat src1 = (Mat_<uchar>(2,3) << 23, 123, 90, 100, 250, 0);
Mat src2 = (Mat_<uchar>(2,3) << 125, 150, 60, 100, 10, 40);
Mat dst = src1 - src2;

be careful:

  1. The data types of the two matrices must be the same, otherwise an error will be reported;
  2. A Mat object can be subtracted from a value, but no matter what data type the value is, the data type of the returned Mat is the same as that of the input Mat;
  3. If the result of the addition exceeds the minimum value of a certain type, the result may be truncated to the minimum value.

To solve the minus sign problem, you can use the subtract function:

void cv::subtract(cv::InputArray src1, cv::InputArray src2, cv::OutputArray dst, cv::InputArray mask = noArray(), int dtype = -1)
Mat src1 = (Mat_<uchar>(2,3) << 23, 123, 90, 100, 250, 0);
Mat src2 = (Mat_<uchar>(2,3) << 125, 150, 60, 100, 10, 40);
Mat dst;
subtract(src1,src2,dst);
//The data types of the input matrix can be different
//The data type of the output matrix can be specified according to the situation
subtract(src1,src2,dst,Mat(),CV_64FC1);

Two vectors can also be subtracted:

Vec3f v1 = Vec3f(1, 2, 3);
Vec3f v2 = Vec3f(10, 1, 12);
Vec3f v = v1 - v2;	//[-9,1,-9]

1.2.3 point multiplication (multiplying the values of corresponding positions)

Mat src1 = (Mat_<uchar>(2,3) << 23, 123, 90, 100, 250, 0);
Mat src2 = (Mat_<uchar>(2,3) << 125, 150, 60, 100, 10, 40);
Mat dst = src1.mul(src2);

be careful:

  1. The data types of the two matrices must be the same, otherwise an error will be reported;
  2. If the result of the addition exceeds the maximum value of a certain type, the result may be truncated to the maximum value.

To solve the problem of. mul, you can use the multiply function:

void cv::multiply(cv::InputArray src1, cv::InputArray src2, cv::OutputArray dst, double scale = (1.0), int dtype = -1)
Mat src1 = (Mat_<uchar>(2,3) << 23, 123, 90, 100, 250, 0);
Mat src2 = (Mat_<uchar>(2,3) << 125, 150, 60, 100, 10, 40);
Mat dst;
multiply(src1,src2,dst);
//The data types of the input matrix can be different
//The data type of the output matrix can be specified according to the situation
subtract(src1,src2,dst,1.0,CV_64FC1);

Note: DST here = scale * SRC1 * src2, i.e. on the basis of the point multiplication result, it needs to be multiplied by the coefficient scale.

1.2.4 point division (division of values at corresponding positions)

Mat src1 = (Mat_<uchar>(2,3) << 23, 123, 90, 100, 250, 0);
Mat src2 = (Mat_<uchar>(2,3) << 125, 150, 60, 100, 10, 40);
Mat dst = src2 / src1;
/*
[  5,   1,   1;
   1,   0,   0]
*/

be careful:

  1. The data types of the two matrices must be the same, otherwise an error will be reported;
  2. If result of the division exceeds maximum or minimum value of the a certain type, result may be truncated to maximum or minimum value.
  3. If the denominator is 0, the result of division defaults to 0.

To solve the problem of the division operator /, you can use the divide function:

void cv::divide(cv::InputArray src1, cv::InputArray src2, cv::OutputArray dst, double scale = (1.0), int dtype = -1)
Mat src1 = (Mat_<uchar>(2,3) << 23, 123, 90, 100, 250, 0);
Mat src2 = (Mat_<uchar>(2,3) << 125, 150, 60, 100, 10, 40);
Mat dst;
divide(src2,src1,dst);
//The data types of the input matrix can be different
//The data type of the output matrix can be specified according to the situation
subtract(src2,src1,dst,1.0,CV_64FC1);	
/*
[  5,   1,   1;
   1,   0,   0]
*/

Note: if the denominator is 0, the division result defaults to 0.

1.2.5 multiplication (point multiplication in linear algebra)

Mat src1 = (Mat_<float>(2,3) << 1, 2, 3, 4, 5, 6);
Mat src2 = (Mat_<float>(3,2) << 6, 5, 4, 3, 2, 1);
Mat dst = src1 * src2;

Note: = = for the multiplication of Mat objects, two mats can only be of float type or double type at the same time== If you multiply matrices of other data types, an error will be reported.

For the multiplication of Mat, you can also use the gemm function provided by OpenCV.

void cv::gemm(cv::InputArray src1, cv::InputArray src2, double alpha, cv::InputArray src3, double beta, cv::OutputArray dst, int flags = 0)

The function controls whether src1, src2 and src3 are transposed through flags to realize different operations between matrices.

When flags is set to different parameters, the output matrix is:

dstflags
d s t = a l p h a ∗ s r c 1 ∗ s r c 2 + b e t a ∗ s r c 3 dst = alpha * src1 * src2 + beta * src3 dst=alpha∗src1∗src2+beta∗src3flags=0
d s t = a l p h a ∗ s r c 1 T ∗ s r c 2 + b e t a ∗ s r c 3 dst = alpha * src1^T * src2 + beta * src3 dst=alpha∗src1T∗src2+beta∗src3flags=GEMM_1_T
d s t = a l p h a ∗ s r c 1 ∗ s r c 2 T + b e t a ∗ s r c 3 dst = alpha * src1 * src2^T + beta * src3 dst=alpha∗src1∗src2T+beta∗src3flags=GEMM_2_T
d s t = a l p h a ∗ s r c 1 ∗ s r c 2 + b e t a ∗ s r c 3 T dst = alpha * src1 * src2 + beta * src3^T dst=alpha∗src1∗src2+beta∗src3Tflags=GEMM_3_T

Note: flags can be used in combination. If you need to transpose both src2 and src3, make flags=GEMM_2_T+GEMM_3_T.

Similarly, gemm can only accept mats of type float or double.

1.2.6 exponential operation

void cv::exp(cv::InputArray src, cv::OutputArray dst)

1.2.7 logarithmic operation

//Here, the log is based on e
void cv::log(cv::InputArray src, cv::OutputArray dst)

1.2.8 power exponent

void cv::pow(cv::InputArray src, double power, cv::OutputArray dst)

Note: the data type of dst after power exponent operation is the same as that of src, so the result may be truncated.

1.3 image digitization

1.3.1 image reading and display

cv::Mat cv::imread(const cv::String &filename, int flags = 1)

flags = IMREAD_COLOR: color image

flags = IMREAD_GRAYSCALE: grayscale image

flags = IMREAD_ANYCOLOR: any image

void cv::imshow(const cv::String &winname, cv::InputArray mat)

1.3.2 convert RGB color image to multi-channel Mat

Each square of the color image can be understood as a Vec3b.

Note: the vector of each pixel is not arranged with R, G and B components, but in the order of B, G and r. Therefore, after the channels are separated by the split function, the B, G and R channels are obtained successively.

Mat img = imread("../orange.jpg");
if(img.empty())
    return -1;
imshow("BGR", img);
vector<Mat> planes;
split(img, planes);		//Separation channel
imshow("B",planes[0]);	//Show channel B
imshow("G",planes[1]);	//Show G channel
imshow("R",planes[2]);	//Show R channel

Posted by mesz on Tue, 09 Nov 2021 13:47:13 -0800