Opencv C++ 基本数据结构 Mat

Mat

Mat代表矩阵,该类声明在头文件opencv2/core/core.hpp中
其构造函数为:

Mat(int rows, int cols, int type)

rows代表行数,cols代表列数
type可以设置为 CV_8UC(n)CV_8SC(n)CV_16SC(n)、、CV_16UC(n)CV_32FC(n)、*、CV_32SC(n)CV_64FC(n)
其中8U、8S、16S、16U、32S、32F、64F前的数字代表Mat中每一个数值的位数
U代表uchar类型、S代表int类型、F代表浮点型(32F为float、64F为double)其他类似。


构造单通道Mat对象

如代码所示:

# include <opencv2corecore.hpp>
using namespace cv;
int main() {
	//构造两行三列的float型矩阵
	Mat m = Mat(2, 3, CV_32FC(1));
	//利用Size对象构造两行3列矩阵,Size(列,行)
	Mat m1 = Mat(Size(3, 2), CV_32FC(1));
	//使用Mat类中的成员函数create完成Mat对象构造
	Mat m2;
	m2.create(Size(2, 3), CV_32FC1);
	//构造零矩阵和1矩阵
	Mat o = Mat::ones(2, 3, CV_32FC1);
	Mat z = Mat::zeros(Size(3,2), CV_32FC1);
	//初始化小型矩阵
	Mat m = (Mat_<int>(2, 3) << 1, 2, 3, 4, 5, 6;
	return 0;
}

获取单通道Mat的基本信息

注:单通道与ndarray中的二维对应,多通道与ndarray的三维对应

以三行两列的矩阵为例

m = [ 11 12 33 43 51 16 ] m=left[ egin{matrix} 11 & 12 \ 33 & 43 \ 51& 16 end{matrix} ight] m=113351124316

1、获取行数和列数

# include <opencv2corecore.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main() {
	//快速构造矩阵
	Mat m = (Mat_<int>(3, 2) << 11, 12, 33, 43, 51, 16);
	//矩阵的行数
	cout << "行数:" << m.rows << endl;
	//矩阵的列数
	cout << "行数:" << m.cols << endl;
	return 0;
}

输出:
这里写图片描述


2、使用成员函数size()获取矩阵的尺寸

成员函数**size()**返回的是Size对象

//获取尺寸
	Size size = m.size();
	cout << "尺寸:" << size << endl;

这里写图片描述


3、使用成员函数channels()获取矩阵的通道数

cout << "通道数:" << m.channels() << endl;

输出:
在这里插入图片描述


4、使用成员函数total获得面积(行数乘列数)

	cout << "面积:" << m.total() << endl;

输出
在这里插入图片描述


5、成员变量dims(维数)

cout << "维数:" << m.dims << endl;

输出:
在这里插入图片描述


访问单通道对象中的值

1、使用成员函数at

例如访问单通道数据类型为CV_32F的对象m,访问其第r行c列的值:
格式为:m.at<类型>(行,列)

m.at<float>(r,c)

遍历上文中矩阵m中的值并输出:

# include <opencv2corecore.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main() {
	//快速构造矩阵
	Mat m = (Mat_<int>(3, 2) << 11, 12, 33, 43, 51, 16);
	//for循环打印m中的每一个值
	for (int r = 0; r < m.rows; r++)
	{
		for (int c = 0; c < m.cols; c++)
		{
			cout << m.at<int>(r, c) << ",";
		}
		cout << endl;
	}
	//return 0;
}

还可以结合opencv中的Point类和at成员函数来实现:
m.at(r, c)可用m.at(Point(r,c))代替


2、利用成员函数ptr

矩阵每一行的值值是连续存储在内存中的,行与行在内存中也可能存在间隔
通过ptr成员函数可以获得执行每一行地址的指针
格式为:m.ptr<类型>(第几行)
遍历上文中矩阵m中的值并输出:

for (int r = 0; r < m.rows; r++)
	{
		const int* ptr = m.ptr<int>(r);
		//打印第r行所有值
		for (int c = 0; c < m.cols; c++)
		{
			cout << ptr[c] << ",";
			//cout << *(ptr+c) << ",";
		}
		cout << endl;
	}

3、使用成员函数isContinuous 和 ptr

矩阵在存储时如果每一行在内存中没有间隔则isContinuous返回true
遍历上文中矩阵m中的值并输出:

if (m.isContinuous())
	{
		//获取指向第一行的指针
		int* ptr = m.ptr<int>(0);
		for (int i = 0; i < m.total(); i++)
		{
			cout << ptr[i] << ",";
			if (i % m.cols)
			{
				cout << endl;
			}
		}
	}

4、使用成员变量step和data

对于单通道矩阵来说,step[0]代表每一行所占的字节数,如果有间隔则间隔也作为字节数被计算在内;step[1]代表每一个数值所占的字节数,data是一个指针类型为uchar,它指向第一个数值。
因此无论矩阵行与行之间在内存中是否有间隔,都可以使用以下代码来访问第r行c列:

*((int *)(m.data+m.step[0]*r+c*m.step[1]))

向量类Vec(构建多通道Mat的基础)

可以把这里的向量理解为列向量,构造一个 _cn行x1列的数据类型为_Tp的列向量格式:Vec<Typename _Tp, int _cn>
例如构造一个长度为3类型为int,且初始化为11,87,37的列向量:

Vec<int, 3> vi(11, 87, 37);

这样构建的向量默认为列向量,可以通过输出行数和列数查看

	cout << vi.rows << endl;
	cout << vi.cols << endl;

在这里插入图片描述
可以使用 “[]” 或"()"操作符f访问向量中的值

//访问向量中的值
	cout << "访问第0个元素" << vi(0) << endl;
	cout << "访问第1个元素" << vi[1] << endl;

在这里插入图片描述
opencv 为向量类取了别名:

typedef Vec<uchar ,3> Vec3b
typedef Vec<int ,2> Vec2i
typedef Vec<float,4> Vec4f
typedef Vec<double ,3> Vec3d

单通道矩阵每个元素都是一个数值,多通道矩阵每个元素可以看作一个向量。


构造多通道Mat对象

构造一个由n个rows*cols二维浮点型矩阵组成的三维矩阵的格式如下:

Mat(int rows,int cols,CV_32FC(n))

例如构造一个2行2列的float类型的三通道矩阵

Mat mm = (Mat_<Vec3f>(2, 2) << Vec3f(1, 11, 21), Vec3f(2, 12, 32),
		Vec3f(3, 13, 23), Vec3f(4, 24, 34));

访问多通道对象中的值

1、使用成员函数at

可以将多通道Mat看作一个特殊的二维数组,数组的每个元素不是数值而是一个向量
以上文 创建的mm多通道对象为例遍历输出多通道矩阵mm的每个元素

	for (int r = 0; r < mm.rows; r++)
	{
		for (int c = 0; c < mm.cols; c++)
		{
			cout << mm.at<Vec3f>(r, c);
		}
		cout << endl;
	}

输出:
在这里插入图片描述


2、利用成员函数ptr

多通道Mat的元素在内存中也是按行存储,每一个行存储在连续内存区域中,如果行与行之间由间隔,间隔相等。成员函数ptr返回指向指定行的第一个元素的指针。
使用ptr访问mm对象的每个元素。

for (int r = 0; r < mm.rows; r++)
	{
		Vec3f* ptr = mm.ptr<Vec3f>(r);
		for (int c = 0; c < mm.cols; c++)
		{
			cout << ptr[c] << ",";
			if (c % mm.cols)
			{
				cout << endl;
			}
		}
	}

输出与使用at相同


3、使用成员函数isContinuous 和 ptr

与单通道Mat对象一样,可通过isContinuous判断整个Mat对象中的元素是否存储在连续内存中,返回true即表示连续存储;如果是连续存储,就可以少一个循环用ptr遍历

	if (mm.isContinuous())
	{
		//获取指向第一行的指针
		Vec3f* ptr = mm.ptr<Vec3f>(0);
		for (int i = 0; i < mm.total(); i++)
		{
			cout << ptr[i] << ",";
			if (i % mm.cols)
			{
				cout << endl;
			}
		}
	}

4、使用成员变量step和data

与单通道Mat类似,step[0]代表每一行所占的字节数,如果有间隔则间隔计算在内,step[1]代表每一个个元素所占的字节数,step[1] = elemSize1()*channels()
elemSize1()代表一个元素中一个数值所占的字节数;data代表首个数值的地址
下面使用step和data遍历输出mm中的元素:

	for (int r = 0; r < mm.rows; r++)
	{
		for (int c = 0; c < mm.cols; c++)
		{
			cout <<*(Vec3f*) (mm.data + r*mm.step[0]+c*mm.step[1]);
		}
		cout << endl;
	}

输出与1,2,3相同


5、分离通道

以上述讨论的mm多通道对象为例,将所有向量第一个数值组成的二维矩阵作为第一通道,将所有向量第二个数值组成的单通道矩阵作为第二通道以此类推;
所以mm分离后的三个通道为
第一通道:2x2的矩阵,元素为 1、2、3、4
第二通道:2x2的矩阵,元素为 11、12、13、24
第三通道:2x2的矩阵,元素为 21、32、23、34
使用split可以将mm分离为多个单通道:

vector<Mat>planes;
split(mm, planes);

5、合并通道

使用merge函数可以将多个单通道矩阵合并为一个三维矩阵
该函数声明为:

void merge (const Mat *mv, size_t count, OutputArray dst)
//其重载函数
void merge (InputArrayofArrays mv, OutputArray dst)

例如将三个2*2int型单通道合并:

	Mat plane0 = (Mat_<int>(2, 2) << 1, 2, 3, 4);
	Mat plane1 = (Mat_<int>(2, 2) << 5, 6, 7, 8);
	Mat plane2 = (Mat_<int>(2, 2) << 9, 10, 11, 12);
	//初始化Mat数组也
	Mat plane[]{ plane0, plane1, plane2 };
	//声明一个mat对象用来保存合并后的多通道对象
	Mat mat;
	merge(plane, 3, mat);
	//将三个单通道矩阵放在vector容器中
	vector<Mat>pl;
	pl.push_back(plane0);
	pl.push_back(plane1);
	pl.push_back(plane2);
	Mat mat1;
	merge(pl, mat);

获得Mat中某一个区域的值

1、使用row(i)或col(j)得到矩阵的第i行j列

Mat mr = m.row(1);
Mat mc = m.col(1);

2、使用rowRange或colRange得到矩阵的连续行或连续列

首先知道Opencv中的Range类,该类用于构造连续整数序列,左闭右开。
Range(2,5)产生2、3、4序列。以5*5矩阵为例
m a t r i x = [ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 ] matrix = left[ egin{matrix} 1 & 2 & 3 & 4 & 5\ 6 & 7 & 8 & 9 & 10 \ 11 & 12 & 13 & 14 & 15 \ 16 & 17 & 18 & 19 & 20 \ 21 & 22 & 23 & 24 & 25 end{matrix} ight] matrix=16111621271217223813182349141924510152025

//构造一个5*5int型矩阵
	Mat matrix = (Mat_<int>(5, 5) << 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25);
	//访问第2、3两行
	Mat r_range = matrix.rowRange(Range(2, 4));//可以写为matrix.rowRange(2,4)
	for (int r = 0; r < r_range.rows; r++)
	{
		for (int c = 0; c < r_range.cols; c++)
		{
			cout << r_range.at<int>(r, c) << ",";
		}
		cout << endl;
	}

输出:
在这里插入图片描述
colRange与之类似

Mat c_range = matrix.colRange(Range(2, 4));
	for (int r = 0; r < c_range.rows; r++)
	{
		for (int c = 0; c < c_range.cols; c++)
		{
			cout << c_range.at<int>(r, c) << ",";
		}
		cout << endl;
	}

输出:
在这里插入图片描述
注意:成员函数row、 col 、 rowRange 、colRange 返回的矩阵是指向原矩阵的,例如改变c_range第1行1列的值,原矩阵也会改变:

c_range.at<int>(1, 1) = 1000;//改为1000
	cout <<  "输出c_range" <<endl;
	for (int r = 0; r < c_range.rows; r++)
	{
		for (int c = 0; c < c_range.cols; c++)
		{
			cout << c_range.at<int>(r, c) << ",";
		}
		cout << endl;
	}
	cout << "输出原矩阵" << endl;
	for (int r = 0; r < matrix.rows; r++)
	{
		for (int c = 0; c < matrix.cols; c++)
		{
			cout << matrix.at<int>(r, c) << ",";
		}
		cout << endl;
	}

输出:
在这里插入图片描述
使用成员函数clone和copyTo可以解决这个问题


3、使用rowRange或colRange得到矩阵的连续行或连续列

使用这两个成员函数会将矩阵克隆一份,以上述r_range为例

	Mat r_range = matrix.rowRange(Range(2, 4)).clone();
	r_range.at<int>(0, 0) = 1000;//修改值为1000
	cout << "输出r_range" << endl;
	for (int r = 0; r < r_range.rows; r++)
	{
		for (int c = 0; c < r_range.cols; c++)
		{
			cout << r_range.at<int>(r, c) << ",";
		}
		cout << endl;
	}
	cout << "输出原矩阵" << endl;
	for (int r = 0; r < matrix.rows; r++)
	{
		for (int c = 0; c < matrix.cols; c++)
		{
			cout << matrix.at<int>(r, c) << ",";
		}
		cout << endl;
	}

输出:
在这里插入图片描述
copyTo的用法为:

matrix.rowRange(2,4).copyTo(r_range);

4、使用Rect类

如果我们需要矩阵中一块矩形区域,我们可以使用rowRange和colRange来定位,但是Opencv提供了Rect类来简化操作。知道矩形的左上角坐标,和矩形的宽高就可以确定一个矩形,所以其构造函数为:

Rect(int _x,int _y, int _width, int _height);

也可以将 _width 和 _height保存在一个Size中

Rect(int _x,int _y, Size size);

如果知道左上角和右下角的坐标也可以确定一个矩形,所以构造函数为:

Rect(Point2i &pt1,Point2i &pt2);

以之前叙述的5*5矩阵matrix为例,要获得使矩形框中的值为8,9,13,14
可以按如下方式构建矩形框

	Mat roi1 = matrix(Rect(Point(2, 1), Point(4, 3)));//左上角和右下角坐标,"左开右闭"
	Mat roi2 = matrix(Rect(2, 1, 2, 2)) ;//x,y,宽,高
	Mat roi3 = matrix(Rect(Point(2, 1), Size(2,2)));//左上角坐标,尺寸

使用for循环输出:
在这里插入图片描述
这样得到的矩形区域仍然是指向原矩阵的所以,仍然可以使用clone和copyTo

Mat roi1 = matrix(Rect(Point(2, 1), Point(4, 3))).clone;
原文地址:https://www.cnblogs.com/PythonFCG/p/13860134.html