1.Mat简介
OpenCv提供了Mat类用来存储矩阵类型数据,包括向量、矩阵、图像等数据。Mat类分为分为矩阵头和指向存储数据的矩阵指针两部分。矩阵头包含矩阵的尺寸、存储方法、地址和引用次数。矩阵头是一个常数,绝大数情况下,矩阵头的大小远小于矩阵数据量的大小。
Mat {
public:
---
int dims; //维数
int rows,cols; //行列数
uchar *data; //存储数据的指针
int *refcount; //引用计数
}
1.1 Mat基本结构
2.Mat类的构造与赋值
2.1 Mat类的构造
2.1.1 默认构造函数
cv::Mat::Mat();
这种构造方式,不需要传递任何参数,在后续给变量赋值的时候会自动判断矩阵的类型与大小,实现灵活的存储,常用于存储读取的图像数据和某个函数运算的输出结果。
2.1.2 根据输入矩阵尺寸和类型构造
cv::Mat::Mat(int rows,int cols,int type)
其中rows:构造矩阵的行数;cols:矩阵的列数;type:矩阵存储的数据类型,例如CV_8UC1之类。
2.1.3 用Size()结构构造Mat类。
cv::Mat::Mat(Size size(), int type);
其中size:为二位数组变量尺寸,通过Size(cols,rows)进行赋值(列在前,行在后);type:矩阵存储的数据类型,例如CV_8UC1之类。
示例:
cv::Mat a(Size(480,580), CV_8UC1);//构造一个行580,列480的单通道矩阵
2.1.4 利用已有矩阵构造Mat类
cv::Mat::Mat(const Mat & a);
这种构造方式只是复制了Mat类的矩阵头,矩阵指针指向是同一个地址。(浅复制)
若要深复制,可用
b = a.clone();
a.copyto(b);
2.1.5 构造已有Mat的子类
cv::Mat::Mat(const Mat & a,const Range & rowRange, const Range & colRange=Range::all);
其中,m:已经构建完成的Mat;rowRange:在已有的矩阵中需要获取的行数范围,是一个Range变量,例如从第二行到第五行表示为Range(2,5);colRange也是一样,在已有的矩阵中需要获取的列数范围,是一个Range变量,例如从第二列到第五列表示为Range(2,5)省略时,所有的列都会被截取。(注意:这种方式构造的类与已有Mat类共享数据。)
例如:
cv::Mat b(a, Range(2, 5), Range(1, 4));//从a中截取第二行到第五行,第一列到第四列的内容
cv::Mat b(a, Range(2, 5));//从a中截取第二行到第五行的内容
2.2 Mat类的赋值
2.2.1 在构造时赋值
cv::Mat::Mat(int rows, int cols, int type, const Scalar & s)
其中rows:矩阵的行数; cols:矩阵的列数;type:存储数据的类型;s:给矩阵中每个橡树赋值的参数变量,例如Scalar(0,0,255)
例如:
cv::Mat a(2, 2, CV_8UC3, cv::Scalar(0, 0, 255));//创建一个3通道矩阵,每个像素都是0,0,255
cv::Mat a(2, 2, CV_8UC2, cv::Scalar(0, 255));//创建一个2通道矩阵,每个像素都是0,255
cv::Mat a(2, 2, CV_8UC1, cv::Scalar(255));//创建一个单通道矩阵,每个像素都是255
2.2.2 枚举法赋值:
cv::Mat a= (cv::Mat_<int>(3,3)<<1,2,3,4,5,6,7,8,9);
cv::Mat a = (cv::Mat_<double>(2, 3) << 1.0, 2.0, 3.0, 4.0, 5.0, 6.0);
第一行创建一个3*3的 矩阵,数据类型为int型,一行一行的存入。
2.2.3 循环法赋值:
cv::Mat a = (cv::Mat_<int>(3, 3));//定义一个3*3的矩阵
for(int i = 0; i < a.rows;i++) //矩阵行数循环
{
for (int j; j < a.cols; j++) //矩阵列数循环
{
a.at<int>(i, j) = i + j;
}
}
2.2.4 利用类方法赋值示例:
cv::Mat a = cv::Mat::eye(3, 3, CV_8UC1);//对角矩阵
cv::Mat b = (cv::Mat_<int>(1, 3) << 1, 2, 3);
cv::Mat c = cv::Mat::diag(b);//提取或创建矩阵对角线
cv::Mat d = cv::Mat::ones(3, 3, CV_8UC1);//每个像素的第一个通道为1,其余通道为0,相当于Scalar(1,0,0)
cv::Mat e = cv::Mat::zeros(3, 2, CV_8UC3);//每个像素的所有通道都为0,相当于Scalar(0,0,0)黑色图片
2.2.5 利用数组进行赋值
Mat(const std::vector<int>& sizes, int type);
# 3.Mat类支持的运算
## 3.1 Mat类的加减乘除运算
```cpp
cv::Mat a = (cv::Mat_<int>(3, 3) << 1, 2, 3, 4, 5, 6, 7, 8, 9);
cv::Mat b = (cv::Mat_<int>(3, 3) << 1, 2, 3, 4, 5, 6, 7, 8, 9);
cv::Mat c = (cv::Mat_<double>(3, 3) << 1.0, 2.0, 3.0, 4.0,5.0, 6.0, 7.0, 8.0, 9.0);
cv::Mat d = (cv::Mat_<double>(3, 3) << 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0);
cv::Mat e, f, g, h,i;
e = a + b;
f = c - d;
g = 2 * a;
h = d / 2.0;
i = a - 1;
当两个类进行加减运算时,需保证数据要一致,比如int和double类型数据的两个类不能进行加减运算。常数与Mat类变量运算,结果的数据类型保留Mat类变量的数据类型。
3.2 两个Mat类矩阵的乘法运算
cv::Mat a = (cv::Mat_<int>(3, 3) << 1, 2, 3, 4, 5, 6, 7, 8, 9);
cv::Mat b = (cv::Mat_<int>(3, 3) << 1, 2, 3, 4, 5, 6, 7, 8, 9);
cv::Mat c = (cv::Mat_<double>(3, 3) << 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0);
cv::Mat d = (cv::Mat_<double>(3, 3) << 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0);
cv::Mat e, f;
double k;
e = c*d;//数学乘积
k = a.dot(b);//内积
f=a.mul(b)//对应位乘积
4. Mat类元素的读取
4.1 多通道数据的存储
存储第一个元素的三个通道数据,再存储第二个元素三个通道数据。
4.2 Mat类常用属性和成员方法
属性 作用
cols 矩阵的列数
rows 矩阵的行数
steps 以字节为单位的有效宽度
dims 矩阵的维度
成员方法
elemSize() 每个元素的字节数
total() 矩阵中元素的个数
channels() 矩阵的通道数
4.3 通过at读取Mat类单通道矩阵的元素
cv::Mat a = (cv::Mat_<int>(3, 3) << 1, 2, 3, 4, 5, 6, 7, 8, 9);
int b = a.at<int>(0, 0);
通过at读取元素需要跟上“<数据类型>”,类型不匹配则会报错。该方法以坐标的形式给出需要读取的元素坐标(行数,列数)。
4.4 通过at读取Mat类多通道矩阵的元素
因为单通道图像是一个二维矩阵,所以 a t方法的最后给出二维坐标即可访问对应位置元素。在OpenCv中,针对三通道,定义了,cv::Vec3b、cv::Vec3s、cv::Vec3w、cv::Vec3d、cv::Vec3f、cv::Vec3i共6种类型,其中数字表示通道数,最后一位是类型的缩写,b是uchar的缩写,s是short类型的缩写,w是ushort类型的缩写,d是double类型的缩写,f是float类型的缩写,i是int类型的缩写。
举例说明:
cv::Mat a(3,4,CV_8UC3,Scalar(2,0,255));
cv::Vec3b b = a.at<Vec3b>(0, 0);
int first = b[0]; //值为2
int second = b.val[1];//值为0
int third = b.val[2];//值为255
4.5 通过指针ptr读取Mat矩阵中的元素
矩阵中每一行的每个元素都是挨着放的,如果能找到每一行元素的起始地址位置,那么读取矩阵中每一行不同位置的元素时将指针在起始位置向后移动若干位即可。
cv::Mat a(3,4,CV_8UC3,Scalar(2,0,255));
for (int i = 0; i < a.rows;i++)
{
uchar *ptr = a.ptr<uchar>(i);
for (int j = 0; j < a.cols*a.channels(); j++)
{
cout << (int)ptr[j] << endl;
}
}
当读取第2行数据中第3个数据时,可直接用a.ptr(1)[2]直接访问
4.6 通过迭代器访问Mat类矩阵中的元素
Mat类变量也是一个容器变量,故Mat类变量拥有迭代器,用于访问Mat变量中的数据,通过迭代器可以实现对矩阵中每一个元素的遍历。
cv::Mat a = (cv::Mat_<uchar>(3, 3) << 3, 4, 5, 1, 6, 7, 8, 0, 1);
cv::MatIterator_<uchar> it = a.begin < uchar > ();
cv::MatIterator_<uchar> it_end = a.end < uchar >();
for (int i = 0;it!=it_end;it++)
{
cout << (int)(*it) << " ";
if (++i%a.channels()==0)
{
cout << endl;
}
}
Mat类的迭代器变量类型是cv::MatIterator_< >,在定义时同样需要在括号中声明数据的变量类型。Mat类迭代器的起始是Mat.begin< >(),结束是Mat.end< >(),与其他迭代器用法相同,通过“++”运算实现指针位置向下迭代,数据的读取方式是先读取第一个元素的每一个通道,之后再读取第二个元素的每一个通道,直到最后一个元素的最后一个通道。
原文链接:https://blog.csdn.net/Xx_Studying/article/details/122209414