Cấu trúc dữ liệu Mat
Hình bên dưới mô phỏng cách mà con người và máy tính cùng "nhìn" 1 hình ảnh.
- Cách con người nhìn chính là hình ảnh thực của nó.
- Máy tính chỉ hiểu hình ảnh ở những giá trị có thể quy đổi ra số.
Hình ảnh là 1 hình chữ nhật nên thông thường được hình dung dưới 1 ma trận, chính vì vậy OpenCV xây dựng class Mat
để lưu trữ thông tin hình ảnh, nhờ vậy mà dễ thao tác và tối ưu hơn khi lập trình.

Dưới đây là hình mô phỏng ma trận lưu hình ảnh:
Column 0 | Column 1 | Column ... | Column m | |
Row 0 | 0, 0 | 0, 1 | ..., ... | 0, m |
Row 1 | 1, 0 | 1, 1 | ..., ... | 1, m |
Row ... | ..., 0 | ..., 1 | ..., ... | ..., m |
Row n | n, 0 | n, 1 | n, ... | n, m |
Ma trận sẽ gồm các dòng và các cột, hình dung như chiều cao của hình ảnh sẽ bằng đúng số dòng của matrix và chiều rộng của hình ảnh sẽ bằng đúng số cột của matrix. Và phần tử [row 0, colum 0] chính là đại diện cho 1 pixel của hình ảnh.
Dưới đây là hình ảnh thực tế hơn về cách lưu hình ảnh ứng với bảng màu BGR, tại phần tử thứ [row 0, column 0] là giá trị màu sắc của 1 pixel của hình ảnh. Nó được lưu với không gian màu BGR nên mỗi pixel sẽ có 3 kênh màu kế tiếp nhau là B Blue, G Green, R Red.
Đa số mỗi kênh màu sẽ được biểu diễn bằng 8-bit unsinged char
(uint8_t
), tương tự với các phần tử khác trong ma trận cũng có cấu trúc lưu trữ tương đương với phần tử [row 0, column 0].
Column 0 | Column 1 | Column ... | Column m | |||||||||
Row 0 | 0, 0 | 0, 0 | 0, 0 | 0, 1 | 0, 1 | 0, 1 | ..., ... | ..., ... | ..., ... | 0, m | 0, m | 0, m |
Row 1 | 1, 0 | 1, 0 | 1, 0 | 1, 1 | 1, 1 | 1, 1 | ..., ... | ..., ... | ..., ... | 1, m | 1, m | 1, m |
Row ... | ..., 0 | ..., 0 | ..., 0 | ..., 1 | ..., 1 | ..., 1 | ..., ... | ..., ... | ..., ... | ..., m | ..., m | ..., m |
Row n | n, 0 | n, 0 | n, 0 | n, 1 | n, 1 | n, 1 | n, ... | n, ... | n, ... | n, m | n, m | n, m |
Sử dụng Mat trong OpenCV
Constructor và ý nghĩa của chúng
Lớp Mat
trong OpenCV nằm trong module core của bộ thư viện OpenCV và nằm trong namespace cv
của bộ thư viện. Nếu không khai báo sử dụng namespace cv
ở đầu chương trình thì trong chương trình sử dụng bắt buộc có tiền tố cv
.
Ví dụ muốn sử dụng Mat
phải khai báo là cv::Mat mat;
Các contructor thường sử dụng với Mat
Mat(int rows, int cols, int type);
rows
: số dòng của ma trận hay nói cách khác là chiều cao của hình ảnh.cols
: số cột của ma trận hay nói cách khác là chiều rộng của hình ảnh.type
: có cấu trúc như dưới đây.- CV_[Số bit cho 1 channel][Kiểu có dấu, không dấu, số thực]C[Số channel]
- Ví dụ:
CV_8UC1
: mỗi pixel có 1 channel dùng 8-bit không dấu để biểu diễn.CV_8UC3
: mỗi pixel có 3 channel và ứng mới mỗi channel sẽ dùng 8-bit không dấu để biểu diễn (RGB
,BRG
, ...).CV_8UC4
: mỗi pixel có 4 channel và ứng mới mỗi channel sẽ dùng 8-bit không dấu để biểu diễn (ARGB
,BRGA
).
Mat(Size size, int type);
Tương tự như constructor trên nhưng thay vì truyền vào rows
và cols
thì truyền vào size
với với format Size(cols, rows)
Mat(int rows, int cols, int type, const Scalar& s);
Giống như contructor thứ nhất với đối số thứ ba là 1 option, giá trị s
có ý nghĩa là khởi tạo giá trị cho các phần tử trong Mat
.
Mat(Size size, int type, const Scalar& s);
Constructor này tương tự như constructor trên.
Các phương thức thường dùng với Mat
mat.clone();
Phương thức trả về đối tượng Mat
có dữ liệu giống với Mat
imageRed.copyTo(OutputArray m);
Giống như phương thức trên nhưng phương thức này phải truyền vào tham số là 1 OutputArray
Ví dụ:
Mat F = A.clone();
Mat G;
A.copyTo(G);
3 phương thức khởi tạo các giá trị đặc biệt - zeros, ones, eyes
Mat::zeros(int rows, int cols, int type);
Phương thức tạo 1 Mat
có các phần tử bằng giá trị 0, các tham số giống với tham số của constructor, phương thức này trả về 1 đối tượng Mat
.
Mat::zeros(Size size, int type);
Giống phương thức trên thay vì truyền vào rows
và cols
thì truyền vào size
có format là Size(cols, rows)
.
Ví dụ 1:
Mat zero_6_6 = Mat::zeros(6, 6, CV_8UC1); cout << zero_6_6 << endl;
Kết quả:
[ 0, 0, 0, 0, 0, 0; 0, 0, 0, 0, 0, 0; 0, 0, 0, 0, 0, 0; 0, 0, 0, 0, 0, 0; 0, 0, 0, 0, 0, 0; 0, 0, 0, 0, 0, 0 ]
Ví dụ 2:
Mat zero_6_18 = Mat::zeros(6, 6, CV_8UC3); cout << zero_6_18 << endl;
Kết quả:
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
Trong 2 trường hợp trên truyền vào rows
và cols
bằng nhau nhưng khác type
thì kết quả như sau:
type = CV_8UC1
: thì matrix sẽ là 6rows
và 6cols
.type = CV_8UC3
: thì matrix sẽ là 6rows
và 18cols
, hiểu ở mức độ liên quan đến hình ảnh thì mỗi pixel sẽ sử dụng 3 kênh màu, kích thước cột là 18 nhưng thực tế chiều rộng của hình ảnh vẫn là 6.
Mat::ones(int rows,int cols, int type);
Phương thức này trả về 1 Mat
có các phần tử mang giá trị 1.
Ví dụ:
Mat one_10_10 = Mat::ones(10, 10, CV_8UC1); cout << one_10_10 << endl;
Kết quả:
[ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1; 1, 1, 1, 1, 1, 1, 1, 1, 1, 1; 1, 1, 1, 1, 1, 1, 1, 1, 1, 1; 1, 1, 1, 1, 1, 1, 1, 1, 1, 1; 1, 1, 1, 1, 1, 1, 1, 1, 1, 1; 1, 1, 1, 1, 1, 1, 1, 1, 1, 1; 1, 1, 1, 1, 1, 1, 1, 1, 1, 1; 1, 1, 1, 1, 1, 1, 1, 1, 1, 1; 1, 1, 1, 1, 1, 1, 1, 1, 1, 1; 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ]
Mat::eye(int rows, int cols, int type);
Phương thức static
này khởi tạo 1 Mat
có các phần tử trên đường chéo chính có giá trị 1 và các phần tử còn lại mang giá trị 0.
Ví dụ 1:
Mat eye_10_10 = Mat::eye(10, 10, CV_8UC1); cout << eye_10_10 << endl;
Kết quả:
[ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0; 0, 1, 0, 0, 0, 0, 0, 0, 0, 0; 0, 0, 1, 0, 0, 0, 0, 0, 0, 0; 0, 0, 0, 1, 0, 0, 0, 0, 0, 0; 0, 0, 0, 0, 1, 0, 0, 0, 0, 0; 0, 0, 0, 0, 0, 1, 0, 0, 0, 0; 0, 0, 0, 0, 0, 0, 1, 0, 0, 0; 0, 0, 0, 0, 0, 0, 0, 1, 0, 0; 0, 0, 0, 0, 0, 0, 0, 0, 1, 0; 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 ]
Ví dụ 2:
Mat eye_10_15 = Mat::eye(10, 15, CV_8UC1); cout << eye_10_15 << endl;
Kết quả:
[ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0; 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0; 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0; 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0; 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 ]
Khi sử dụng phương thức eye
thì chỉ các phần thử có M[i, j]
với i = j
thì phần thử đó có giá trị bằng 1 còn lại các phần tử khác đều mang giá trị 0.
Tạo hình ảnh sử dụng Mat
// // main.cpp // OpenCVTest // // Created by NguyenNghia on 11/11/16. // Copyright © 2016 nguyennghia. All rights reserved. // #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp> using namespace std; using namespace cv; int main(int argc, const char * argv[]) { Mat imageRed(480,320, CV_8UC3, Scalar(0, 0, 255)); Mat imageGreen(480, 320, CV_8UC3, Scalar(0, 255, 0)); Mat imageBlue(480, 320, CV_8UC3, Scalar(255, 0, 0)); namedWindow("Image RED", WINDOW_AUTOSIZE); namedWindow("Image GREEN", WINDOW_AUTOSIZE); namedWindow("Image BLUE", WINDOW_AUTOSIZE); imshow("Image RED", imageRed); imshow("Image GREEN", imageGreen); imshow("Image BLUE", imageBlue); waitKey(); return 0; }
Kết quả:

Hoặc random các phần tử của matrix trong khoảng 0, 255
Mat R = Mat(480, 320, CV_8UC3); rand(R, Scalar::all(0), Scalar::all(255));
Kết quả khi show R:
namedWindow("Random Image", WINDOW_AUTOSIZE); imshow("Random Image", R);

Có thể chạy thử đoạn mã dưới đây để hiểu hơn về Mat
trong thư viện OpenCV
/* For description look into the help() function. */ #include <iostream> #include "opencv2/core/core.hpp" using namespace std; using namespace cv; static void help() { cout << "\n--------------------------------------------------------------------------" << endl << "This program shows how to create matrices(cv::Mat) in OpenCV and its serial" << " out capabilities" << endl << "That is, cv::Mat M(...); M.create and cout << M. " << endl << "Shows how output can be formated to OpenCV, python, numpy, csv and C styles." << endl << "Usage:" << endl << "./cvout_sample" << endl << "--------------------------------------------------------------------------" << endl << endl; } int main(int,char**) { help(); // Create by using the constructor Mat M(2,2, CV_8UC3, Scalar(0,0,255)); cout << "M = " << endl << " " << M << endl << endl; // Create by using the create function() M.create(4,4, CV_8UC(2)); cout << "M = "<< endl << " " << M << endl << endl; // Create multidimensional matrices int sz[3] = {2,2,2}; Mat L(3,sz, CV_8UC(1), Scalar::all(0)); // Cannot print via operator << // Create using MATLAB style eye, ones or zero matrix 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; // Create a 3x3 double-precision identity matrix Mat C = (Mat_(3,3) << 0, -1, 0, -1, 5, -1, 0, -1, 0); cout << "C = " << endl << " " << C << endl << endl; Mat RowClone = C.row(1).clone(); cout << "RowClone = " << endl << " " << RowClone << endl << endl; // Fill a matrix with random values Mat R = Mat(3, 2, CV_8UC3); randu(R, Scalar::all(0), Scalar::all(255)); // Demonstrate the output formating options cout << "R (default) = " << endl << R << endl << endl; cout << "R (python) = " << endl << format(R,"python") << endl << endl; cout << "R (numpy) = " << endl << format(R,"numpy" ) << endl << endl; cout << "R (csv) = " << endl << format(R,"csv" ) << endl << endl; cout << "R (c) = " << endl << format(R,"C" ) << endl << endl; Point2f P(5, 1); cout << "Point (2D) = " << P << endl << endl; Point3f P3f(2, 6, 7); cout << "Point (3D) = " << P3f << endl << endl; vector v; v.push_back( (float)CV_PI); v.push_back(2); v.push_back(3.01f); cout << "Vector of floats via Mat = " << Mat(v) << endl << endl; vector vPoints(20); for (size_t i = 0; i < vPoints.size(); ++i) vPoints[i] = Point2f((float)(i * 5), (float)(i % 7)); cout << "A vector of 2D Points = " << vPoints << endl << endl; return 0; }
Ngoài việc sử dụng cấu trúc dữ liệu này để lưu trữ dữ liệu hình ảnh thì cấu trúc dữ liệu này được sử dụng khá nhiều trong việc tính toán và sử dụng các bộ lọc (filter) trong OpenCV.