public:
//... a lot of methods......
/ *! includes several bit-fields:
- the magic signature
- continuity flag
- depth
- number of channels
int flags;
//! the array dimensionality, >= 2
int dims;
//! the number of rows and columns or (-1, -1) when the array has more than 2 dimensions
int rows, cols;
//! pointer to the data
uchar* data;
//! pointer to the reference counter;
// when array points to user-allocated data, the pointer is NULL
int* refcount;
// other members...
};
The class Mat
represents an n-dimensional dense 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-dimensional histograms may be better stored in a SparseMat
). The data layout of the array
M is defined by the array M.step[]
, so that the address of element (i_0,...,i_(M.dims-1)), where 0 <= i_k<M.size[k], is computed as:
addr(M_(i_0,...,i_(M.dims-1))) = M.data + M.step[0]*i_0 + M.step[1]*i_1 +... + M.step[M.dims-1]*i_(M.dims-1)
In case of a 2-dimensional array, the above formula is reduced to:
addr(M_(i,j)) = M.data + M.step[0]*i + M.step[1]*j
Note that M.step[i] >= M.step[i+1]
(in fact, M.step[i] >= M.step[i+1]*M.size[i+1]
). This means that 2-dimensional matrices are stored row-by-row, 3-dimensional matrices are stored plane-by-plane, and so on. M.step[M.dims-1]
is minimal and always equal to the element size M.elemSize()
.
So, the data layout in Mat
is fully compatible with CvMat
, IplImage
, and CvMatND
types from OpenCV 1.x. It is also compatible with the majority of dense array types from the standard toolkits and SDKs, such as Numpy (ndarray), Win32 (independent device bitmaps), and others, that is, with any array that uses *steps* (or *strides*) to compute the position of a pixel. Due to this compatibility, it is possible to make a Mat
header for user-allocated data and process it in-place using OpenCV functions.
There are many different ways to create a Mat
object. The most popular options are listed below:
create(nrows, ncols, type)
method or the similar Mat(nrows, ncols, type[, fillValue])
constructor. A new array of the specified size and type is allocated. type
has the same meaning as in the cvCreateMat
method. For example, CV_8UC1
means a 8-bit single-channel array, CV_32FC2
means a 2-channel (complex) floating-point array, and so on.
// C++ code:
// make a 7x7 complex matrix filled with 1+3j.
Mat M(7,7,CV_32FC2,Scalar(1,3));
// and now turn M to a 100x60 15-channel 8-bit matrix.
// The old content will be deallocated
M.create(100,60,CV_8UC(15));
As noted in the introduction to this chapter, create()
allocates only a new array when the shape or type of the current array are different from the specified ones.
// C++ code:
// create a 100x100x100 8-bit array
int sz[] = {100, 100, 100};
Mat bigCube(3, sz, CV_8U, Scalar.all(0));
It passes the number of dimensions =1 to the Mat
constructor but the created array will be 2-dimensional with the number of columns set to 1. So, Mat.dims
is always >= 2 (can also be 0 when the array is empty).
Mat.clone()
method can be used to get a full (deep) copy of the array when you need it. // C++ code:
// add the 5-th row, multiplied by 3 to the 3rd row
M.row(3) = M.row(3) + M.row(5)*3;
// now copy the 7-th column to the 1-st column
// M.col(1) = M.col(7); // this will not work
Mat M1 = M.col(1);
M.col(7).copyTo(M1);
// create a new 320x240 image
Mat img(Size(320,240),CV_8UC3);
// select a ROI
Mat roi(img, Rect(10,10,100,100));
// fill the ROI with (0,255,0) (which is green in RGB space);
// the original 320x240 image will be modified
roi = Scalar(0,255,0);
Due to the additional datastart
and dataend
members, it is possible to compute a relative sub-array position in the main *container* array using locateROI()
:
// C++ code:
Mat A = Mat.eye(10, 10, CV_32S);
// extracts A columns, 1 (inclusive) to 3 (exclusive).
Mat B = A(Range.all(), Range(1, 3));
// extracts B rows, 5 (inclusive) to 9 (exclusive).
// that is, C ~ A(Range(5, 9), Range(1, 3))
Mat C = B(Range(5, 9), Range.all());
Size size; Point ofs;
C.locateROI(size, ofs);
// size will be (width=10,height=10) and the ofs will be (x=1, y=5)
As in case of whole matrices, if you need a deep copy, use the clone()
method of the extracted sub-matrices.
gstreamer
, and so on). For example: // C++ code:
void process_video_frame(const unsigned char* pixels,
int width, int height, int step)
Mat img(height, width, CV_8UC3, pixels, step);
GaussianBlur(img, img, Size(7,7), 1.5, 1.5);
// C++ code:
double m[3][3] = {{a, b, c}, {d, e, f}, {g, h, i}};
Mat M = Mat(3, 3, CV_64F, m).inv();
Partial yet very common cases of this *user-allocated data* case are conversions from CvMat
and IplImage
to Mat
. For this purpose, there are special constructors taking pointers to CvMat
or IplImage
and the optional flag indicating whether to copy the data or not.
Backward conversion from Mat
to CvMat
or IplImage
is provided via cast operators Mat.operator CvMat() const
and Mat.operator IplImage()
. The operators do NOT copy the data.
// C++ code:
IplImage* img = cvLoadImage("greatwave.jpg", 1);
Mat mtx(img); // convert IplImage* -> Mat
CvMat oldmat=mtx; // convert Mat -> CvMat
CV_Assert(oldmat.cols == img->width && oldmat.rows == img->height &&
oldmat.data.ptr == (uchar*)img->imageData && oldmat.step == img->widthStep);
zeros(), ones(), eye()
, for example: // C++ code:
// create a double-precision identity martix and add it to M.
M += Mat.eye(M.rows, M.cols, CV_64F);
// C++ code:
// create a 3x3 double-precision identity matrix
Mat M = (Mat_
With this approach, you first call a constructor of the "Mat_" class with the proper parameters, and then you just put <<
operator followed by comma-separated values that can be constants, variables, expressions, and so on. Also, note the extra parentheses required to avoid compilation errors.
Once the array is created, it is automatically managed via a reference-counting mechanism. If the array header is built on top of user-allocated data, you should handle the data by yourself. The array data is deallocated when no one points to it. If you want to release the data pointed by a array header before the array destructor is called, use Mat.release()
.
The next important thing to learn about the array class is element access. This manual already described how to compute an address of each array element. Normally, you are not required to use the formula directly in the code. If you know the array element type (which can be retrieved using the method Mat.type()
), you can access the elementM_(ij) of a 2-dimensional array as:
// C++ code:
M.at
assuming that M is a double-precision floating-point array. There are several variants of the method at
for a different number of dimensions.
If you need to process a whole row of a 2D array, the most efficient way is to get the pointer to the row first, and then just use the plain C operator []
:
// C++ code:
// compute sum of positive matrix elements
// (assuming that M isa double-precision matrix)
double sum=0;
for(int i = 0; i < M.rows; i++)
const double* Mi = M.ptr
for(int j = 0; j < M.cols; j++)
sum += std.max(Mi[j], 0.);
Some operations, like the one above, do not actually depend on the array shape. They just process elements of an array one by one (or elements from multiple arrays that have the same coordinates, for example, array addition). Such operations are called *element-wise*. It makes sense to check whether all the input/output arrays are continuous, namely, have no gaps at the end of each row. If yes, process them as a long single row:
// compute the sum of positive matrix elements, optimized variant
double sum=0;
int cols = M.cols, rows = M.rows;
if(M.isContinuous())
cols *= rows;
rows = 1;
for(int i = 0; i < rows; i++)
const double* Mi = M.ptr
for(int j = 0; j < cols; j++)
sum += std.max(Mi[j], 0.);
In case of the continuous matrix, the outer loop body is executed just once. So, the overhead is smaller, which is especially noticeable in case of small matrices.
Finally, there are STL-style iterators that are smart enough to skip gaps between successive rows:
// C++ code:
// compute sum of positive matrix elements, iterator-based variant
double sum=0;
MatConstIterator_
for(; it != it_end; ++it)
sum += std.max(*it, 0.);
The matrix iterators are random-access iterators, so they can be passed to any STL algorithm, including std.sort()
.
Note:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|