OpenCV Tutorials

Mat - The Basic Image Container

The first thing you need to be familiar with is how OpenCV stores and handles images.

Mat is basically a class with two data parts: the matrix header (containing information such as the size of the matrix, the method used for storing, at which address is the matrix stored, and so on) and a pointer to the matrix containing the pixel values (taking any dimensionality depending on the method chosen for storing) . The matrix header size is constant, however the size of the matrix itself may vary from image to image and usually is larger by orders of magnitude.

What you need to remember from all this is that: * Output image allocation for OpenCV functions is automatic (unless specified otherwise). * You do not need to think about memory management with OpenCVs C++ interface. * The assignment operator and the copy constructor only copies the header. * The underlying matrix of an image may be copied using the cv::Mat::clone() and cv::Mat::copyTo() functions.

The real interesting part is that you can create headers which refer to only a subsection of the full data. For example, to create a region of interest (ROI) in an image you just create a new header with the new boundaries:

Mat D (A, Rect(10, 10, 100, 100) ); // using a rectangle
Mat E = A(Range::all(), Range(1,3)); // using row and column boundaries

Storing methods

This is about how you store the pixel values. You can select the color space and the data type used.

There are, however, many other color systems each with their own advantages:

  • RGB is the most common as our eyes use something similar, however keep in mind that OpenCV standard display system composes colors using the BGR color space (a switch of the red and blue channel).
  • The HSV and HLS decompose colors into their hue, saturation and value/luminance components, which is a more natural way for us to describe colors. You might, for example, dismiss the last component, making your algorithm less sensible to the light conditions of the input image.
  • YCrCb is used by the popular JPEG image format.
  • CIE L*a*b* is a perceptually uniform color space, which comes handy if you need to measure the distance of a given color to another color.

Creating a Mat object explicitly

Although Mat works really well as an image container, it is also a general matrix class. Therefore, it is possible to create and manipulate multidimensional matrices.

  • cv::Mat::Mat Constructor
Mat M(2,2, CV_8UC3, Scalar(0,0,255)); // row, column
cout << "M = " << endl << " " << M << endl << endl;
CV_8UC3
the data type to use for storing the elements
Scalar(0,0,255)
the number of channels per matrix point.

We have multiple definitions constructed according to the following convention:

CV_[The number of bits per item][Signed or Unsigned][Type Prefix]C[The channel number]

For instance, CV_8UC3 means we use unsigned char types that are 8 bit long and each pixel has three of these to form the three channels. This are predefined for up to four channel numbers. The cv::Scalar is four element short vector. Specify this and you can initialize all matrix points with a custom value. If you need more you can create the type with the upper macro, setting the channel number in parenthesis as you can see below.

  • Use C/C++ arrays and initialize via constructor
int sz[3] = {2,2,2};
Mat L(3,sz, CV_8UC(1), Scalar::all(0));

The upper example shows how to create a matrix with more than two dimensions. Specify its dimension, then pass a pointer containing the size for each dimension and the rest remains the same.

Note

CV_8UC(1) 中 (1) 是什么意思?

  • cv::Mat::create function:
::
M.create(4,4, CV_8UC(2)); cout << “M = “<< endl << ” ” << M << endl << endl;

You cannot initialize the matrix values with this construction. It will only reallocate its matrix data memory if the new size will not fit into the old one.

  • MATLAB style initializer: cv::Mat::zeros , cv::Mat::ones , cv::Mat::eye . Specify size and data type to use:

    Mat E = Mat::eye(4, 4, CV_64F);
    cout << "E = " << endl << " " << E << endl << endl;
    Mat O = Mat::ones(2, 2, CV_32F);
    cout << "O = " << endl << " " << O << endl << endl;
    Mat Z = Mat::zeros(3,3, CV_8UC1);
    cout << "Z = " << endl << " " << Z << endl << endl;
    
  • For small matrices you may use comma separated initializers or initializer lists (C++11 support is required in the last case):

Mat C = (Mat_<double>(3,3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
cout << "C = " << endl << " " << C << endl << endl;
C = (Mat_<double>({0, -1, 0, -1, 5, -1, 0, -1, 0})).reshape(3);
cout << "C = " << endl << " " << C << endl << endl;

Note

Mat_<double>() 是 C++ 的语法吗?

  • Create a new header for an existing Mat object and cv::Mat::clone or cv::Mat::copyTo it.
Mat RowClone = C.row(1).clone();
cout << "RowClone = " << endl << " " << RowClone << endl << endl;

Output formatting

cout << "R (default) = " << endl <<        R           << endl << endl;
cout << "R (python)  = " << endl << format(R, Formatter::FMT_PYTHON) << endl << endl;
cout << "R (csv)     = " << endl << format(R, Formatter::FMT_CSV   ) << endl << endl;
cout &l