In opencv2, the mat class undoubtedly occupies the core position. Some time ago, when I first started opencv2, I had a preliminary understanding of the mat class. For details, see opencv2: mat beginner. In the past few days, we have tried to use opencv2 to implement two algorithms for image reduction: Image reduction based on equal-interval sampling and local mean, and found that we are still confused about the data layout and attributes in mat, this article summarizes some important attributes and Data Layout of mat.
Role of mat
The classMatRepresents an N-dimenspondense numerical single-channel or multi-channel array. it can be used to store real or complex-valued vectors and matrices, grayscale or color images, voxel volumes, Vector Fields, point clouds, tensors, histograms (though, very high-dimen1_histograms may be better stored inSparsemat).
The above section references the official document. The Mat class is used to represent a multi-dimensional single-channel or multi-channel dense array. It can be used to store real or complex vectors, matrices, gray-scale or color images, stereoscopic elements, point clouds, tensor, and histograms (high-dimensional histograms are well preserved using sparsemat ). In short, mat is used to store multi-dimensional matrices.
Common attributes of mat
- DataUchar pointer. The mat class consists of two parts: the matrix header and the pointer to the matrix data section. Data is the pointer to the matrix data.
- DimsDimension of the matrix. For example, if the 5*6 matrix is a two-dimensional matrix, dims = 2 and dims = 3.
- RowsNumber of rows in the Matrix
- ColsNumber of columns in the Matrix
- SizeThe size (cols, rows) of the matrix. If the dimension of the matrix is greater than 2, it is size (-1,-1)
- ChannelsNumber of channels owned by matrix elements, such as common color images. Each pixel is composed of three parts: RGB, then channels = 3
The following attributes are related to the Data Type of the elements in mat.
- Type
Indicates the type of elements in the matrix and the number of channels in the matrix. It is a series of predefined constants and Its naming rules are CV _ (number of digits) + (data type) + (number of channels ). The specific values are as follows:
Cv_8uc1 |
Cv_8uc2 |
Cv_8uc3 |
Cv_8uc4 |
Cv_8sc1 |
Cv_8sc2 |
Cv_8sc3 |
Cv_8sc4 |
Cv_16uc1 |
Cv_16uc2 |
Cv_16uc3 |
Cv_16uc4 |
Cv_16sc1 |
Cv_16sc2 |
Cv_16sc3 |
Cv_16sc4 |
Cv_32sc1 |
Cv_32sc2 |
Cv_32sc3 |
Cv_32sc4 |
Cv_32fc1 |
Cv_32fc2 |
Cv_32fc3 |
Cv_32fc4 |
Cv_64fc1 |
Cv_64fc2 |
Cv_64fc3 |
Cv_64fc4 |
Here, u (unsigned integer) indicates an unsigned integer, S (signed integer) indicates a signed integer, and F (float) indicates a floating point.
For example, cv_16uc2 indicates that the element type is a 16-bit unsigned integer and the channel is 2.
C1, C2, C3, C4 indicates the channel is 1, 2, 3, 4
Type is generally set when you create a mat object. If you want to obtain the mat element type, you do not need to use type. Use the following depth
-
- Depth
The data type of a channel of elements in the matrix. The value is related to the type. For example, type is cv_16sc2, a 16-bit signed integer of a 2-channel. Then, depth is cv_16s. Depth is also a series of predefined values,
Remove the pre-defined value of type from the channel information, that is, the depth value:
Cv_8u cv_8s cv_16u cv_16s cv_32s cv_32f cv_64f
-
- Elemsize
The number of bytes occupied by an element in the Matrix. For example, if type is cv_16sc3, elemsize = 3*16/8 = 6 bytes
-
- Elemsize1
The number of bytes occupied by a channel in the matrix element. For example, if type is cv_16cs3, elemsize1 = 16/8 = 2 bytes = elemsize/channels
The following is an example program that describes the attributes of MAT:
Mat img(3, 4, CV_16UC4, Scalar_<uchar>(1, 2, 3, 4)); cout << img << endl; cout << "dims:" << img.dims << endl; cout << "rows:" << img.rows << endl; cout << "cols:" << img.cols << endl; cout << "channels:" << img.channels() << endl; cout << "type:" << img.type() << endl; cout << "depth:" << img.depth() << endl; cout << "elemSize:" << img.elemSize() << endl; cout << "elemSize1:" << img.elemSize1() << endl;
First, a 3*4 matrix with four channels is created. Its element type is cv_16u. Scalar _ is a template Vector used to initialize each pixel of the matrix. Because the matrix has four channels, scalar _ has four values. The running result is as follows:
The running result first prints the matrix in mat, followed by Attributes of mat. Note that its type is 26, and depth is 2. This is because of the predefined types mentioned above
For example, cv_16uc4 and cv_8u are some predefined constants.
Step
The step in mat is an instance of an mstep. The statement is as follows:
struct CV_EXPORTS MStep { MStep(); MStep(size_t s); const size_t& operator[](int i) const; size_t& operator[](int i); operator size_t() const; MStep& operator = (size_t s); size_t* p; size_t buf[2]; protected: MStep& operator = (const MStep&); };
From its Declaration, we can see that mstep has a deep relationship with size_t. Using size_t as the parameter constructor and the overloaded value assignment operator
MStep(size_t s);MStep& operator = (size_t s);
Returns size_t to the size_t type conversion and the overloaded [] operator.
const size_t& operator[](int i) const; size_t& operator[](int i);
Array and pointer of size_t
size_t* p; size_t buf[2];
So what is size_t? Look at the code.
typedef unsigned int size_t;
Size_t is an unsigned integer.
Let's look at the mstep constructor to know What it actually stores.
inline Mat::MStep::MStep(size_t s) { p = buf; p[0] = s; p[1] = 0; }
From the definition of mstep, we can know that buff is a size_t [2], while P is size_t *, that is, we can regard mstep as a size_t [2]. So what is the relationship between the size_t [2] stored in step and the data in mat.
Step [0] is the number of bytes of a row of elements in the matrix.
Step [1] is the number of elements in the matrix, which is equal to the elemsize mentioned above.
As mentioned above, a uchar * Data in mat points to the first address of the matrix data, and now we know the data size of each row and each element, you can quickly access any element in the mat. The following formula:
Step 1
Normalized step. The value is step/elemsize1. Definition:
inline size_t Mat::step1(int i) const { return step.p[i]/elemSize1(); }
Taking the IMG defined in the above Code as an example, let's take a look at the specific values of step and step 1:
The type of IMG (3*4) is cv_16uc4, and step [0] is the number of data bytes occupied by a row 4*4*16/8 = 32.
Step [1] is the number of bytes occupied by an element. An IMG element has four channels, so: 4*16/8 = 2
Step 1 = Step/elemsize1. elemsize1 is the number of bytes occupied by each channel of the element.
N-dimensional step (n> 2)
The above analysis step is a size_t [2], which is not very correct. correctly, the step should be size_t [dims], and the dims is the mat dimension, so for the above two-dimensional mat, the step is size_t [2] is also correct.
Next we will deploy the three-dimensional mat data and the step (if the dimension is greater than 3 ).
Reference from the http://ggicci.blog.163.com/blog/static/210364096201261052543349/ to collect information found this figure, everything becomes simple thanks to the authorGgicci
3D data is stored by surface in mat, which is clearly described.
As mentioned above, step is a size_t [dims] And dims is a dimension. So, the three-dimensional step is size_t [3]. If you don't talk about the rest, you can see the picture. Create a three-dimensional mat.
int dims[3] = { 3, 3, 3 }; Mat src(3, dims, CV_16SC2, Scalar_<short>(1,2)); cout << "step[0]:" << src.step[0] << endl; cout << "step[1]:" << src.step[1] << endl; cout << "step[2]:" << src.step[2] << endl;
First, create a two-channel mat with 3*3*3 and depth as cv_16s.
Step [0] is the size of a data plane 3*3 * (16/8) * 2 = 36
Step [1] is the size of a row of Data 3 * (16/8) * 2 = 12
Step [2] is the size of an element 2 * (16/8) = 4
PS: 3D mat cannot be output using the <operator.
Over
Opencv2: mat attribute type, depth, step