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:
- The data types of the two matrices must be the same, otherwise an error will be reported;
- 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;
- 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:
- The data types of the two matrices must be the same, otherwise an error will be reported;
- 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;
- 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:
- The data types of the two matrices must be the same, otherwise an error will be reported;
- 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:
- The data types of the two matrices must be the same, otherwise an error will be reported;
- If result of the division exceeds maximum or minimum value of the a certain type, result may be truncated to maximum or minimum value.
- 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:
dst | flags |
---|---|
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∗src3 | flags=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∗src3 | flags=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∗src3 | flags=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∗src3T | flags=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