Mat类
基本介绍
Mat类是OpenCV中使用最频繁的类之一,用于储存矩阵数据及相关操作,也是程序中图像的主要形式。
Mat类主要由两部分组成:一个描述头(matrix header)及一个指向矩阵数据的指针。其中,描述头包含了矩阵的一些基本信息,如矩阵的尺寸,所占空间大小等。
Mat类有以下几个特点:
- Mat类会自动分配内存,使用者无需考虑内存的管理。(老版的OpenCV中使用IPIImage类,需要用户自己管理内存)
- Mat类使用了引用计数系统(Reference Counting System),不同的Mat对象可共享同一个矩阵的数据。
- 赋值号和拷贝构造只会拷贝描述头,不会开辟新的空间保存矩阵数据。在下面的例子中,如果B,C的值发生变化,那么A的值也会发生变化。
Mat A,C;
Mat B(A);
C = A;
- 如果想要矩阵数据一同被拷贝,则可用cv::Mat::clone()命令。
Mat D = A.clone();
声明与初始化
1. cv::Mat A(2, 2, cv::CV_8UC3, cv::Scalar(0,0,255));
2. cv::Mat B = cv::Mat::eye(4, 4, cv::CV_64F); // 单位矩阵
3. cv::Mat C = cv::Mat::ones(2, 2, cv::CV_32F); // 元素均为1的矩阵
4. cv::Mat D = cv::Mat::zeros(3, 3, cv::CV_8UC1); // 元素均为0的矩阵
其中,CV_8UC3中的’8’表示8个比特,'U’表示无符号(unsigned),'C3’表示通道有三个通道。即,A中像素值都为无符号类型,有8个比特长,并且有3个通道。
存储形式
Mat 类本质上是一个通用的矩阵类型,可存储不同类型的图像。灰度图和RGB图的存储形式有所不同,具体看下图。
除此之外,一般而言,每一行的数据在内存中的地址都是连续的,但不同的行之间不一定。可以用isContinuous()成员函数判断行与行之间地址是否连续。
访问Mat中的任意元素
可使用at()函数来访问任意元素。at()是一个函数模板,需要指定元素类型,一般而言,灰度图的元素类型为uchar,RGB图的元素类型为Vec3b,是一个三维的向量。具体使用方式如下 :
uchar gray = img_gray.at<uchar>(i,j); // 灰度图
auto r = img_rgb.at<cv::Vec3b>(i,j)[0]; // RGB图
auto g = img_rgb.at<cv::Vec3b>(i,j)[1];
auto b = img_rgb.at<cv::Vec3b>(i,j)[2];
img_rgb.at<cv::Vec3b>(i,j) = cv::Vec3b(0,0,0); // 将某一点赋值为0
获得 Mat 的维度
cv::Mat m;
// do something to m
int channels = m.channels(); // 通道数
int rows = m.rows; // 行数
int cols = m.cols; // 列数
扫描整副图像
扫描图像上的每个元素,本质上就是要访问Mat矩阵中的每个元素。主要有三种方式扫描图像:
- 用指针扫描:
用成员函数ptr< type >()获取矩阵某一行首元素的指针,达到访问元素的目的。由于每一行的内存是连续的,因此能够按顺序扫描矩阵,效率较高。
// 用C语言的[]运算符访问
Mat &ScanImageAndReduceC(Mat& I, const uchar* const table) {
CV_Assert(I.depth() == CV_8U);
int channels = I.channels();
int nRows = I.rows;
int nCols = I.cols*channels;
// 一般而言,每一行的数据在内存中都是连续的;但不同行之间可能会有所不同
if (I.isContinuous()) {
nCols *= nRows;
nRows = 1;
}
uchar *p;
for (int i = 0; i < nRows; ++i) {
p = I.ptr<uchar>(i);//获得第i行的首指针
for (int j = 0; j < nCols; ++j) {
p[j] = table[p[j]];
}
}
return I;
}
- 用迭代器扫描
成员函数begin< type >()和end< type >()可获取Mat类的迭代器,进行数据元素的遍历。这种方法相比第一种方法更安全,不会有下标溢出的问题,但效率更低。
// 用迭代器迭代
Mat& ScanImageAndReduceIterator (Mat &I, const uchar* const table) {
CV_Assert(I.depth() == CV_8U);
const int channels = I.channels();
switch (channels)
{
case 1:
{
MatIterator_<uchar> it, end;
for (it = I.begin<uchar>(), end = I.end<uchar>(); it != end; ++it) {
*it = table[*it];
}
break;
}
case 3:
{
MatIterator_<Vec3b> it, end;
for (it = I.begin<Vec3b>(), end = I.end<Vec3b>(); it != end; ++it) {
(*it)[0] = table[(*it)[0]];
(*it)[1] = table[(*it)[1]];
(*it)[2] = table[(*it)[2]];
}
break;
}
}
return I;
}
- 通过at()函数扫描
该方法本质上是依次任意访问矩阵中的每一个元素,在遍历时,每一个循环都要重新计算指针的地址,因此效率更低。但这种方式最直观,可读性高。
// 利用at()函数随机访问元素,达到扫描的效果
Mat& ScanImageAndReduceRandomAccess (Mat &I, const uchar* const table) {
CV_Assert(I.depth() == CV_8U);
const int channels = I.channels();
switch (channels)
{
case 1:
{
for (int i = 0; i < I.rows; ++i) {
for (int j = 0; j < I.cols; ++j) {
I.at<uchar>(i,j) = table[I.at<uchar>(i,j)];
}
}
break;
}
case 3:
{
for (int i = 0; i < I.rows; ++i) {
for (int j = 0; j < I.cols; ++j) {
I.at<Vec3b>(i,j)[0] = table[I.at<Vec3b>(i,j)[0]];
I.at<Vec3b>(i,j)[1] = table[I.at<Vec3b>(i,j)[1]];
I.at<Vec3b>(i,j)[2] = table[I.at<Vec3b>(i,j)[2]];
}
}
break;
}
}
return I;
}
图像操作
Input/Output/Visualizing
Mat img = imread(filename, IMREAD_GRAYSCALE);
// Mat img = imread(filename, IMREAD_COLOR);
if (img.empty()) {
cout << "something wrong\n";
}
imwrite(filename, img);
namedWiodow("demo", WINDOW_AUTOSIZE);
imshow("image", img);
waitKey(0);
访问某一像素值
- 灰度图:
Scalar intensity = img.at<uchar>(y,x);
- RGB图:
Vec3b intensity = img.at<Vec3b>(y,x);
uchar b = intensity.val[0];
uchar g = intensity.val[1];
uchar b = intensity.val[2];
选择某一子区域
Rect r(top_x, top_y, width, height);
Mat subMatrix = img(r);
颜色空间转换
Mat img = imread(filename, IMREAD_COLOR);
Mat grey;
cvColor(img, grey, COLOR_BGR2GRAY);
线性加权
可使用addWeighted函数进行线性加权:
addWeighted(src1, alpha, src2, beta, 0.0, dst);
其中,参数含义如下:
模板运算
可使用filter2D()函数对图片进行模板运算。
Mat kernel = (Mat_<char>(3,3) << 0, -1, 0,
-1, 5, -1,
0, -1, 0);
filter2D(src, dst, src.depth(), kernel);
其他常用函数
- saturat_cast< T >(): 防止数值溢出
- CV_Assert(): 相当于Python中的assert