Mat的基本介绍
基本上讲 Mat 是一个类,由两个数据部分组成:
- 矩阵头(包含矩阵尺寸,存储方法,存储地址等信息)
- 一个指向存储所有像素值的矩阵(根据所选存储方法的不同矩阵可以是不同的维数)的指针。
矩阵头的尺寸是常数值,包含几个固定的图像属性,但矩阵本身的尺寸会依图像的大小和存储类型的不同而不同。
Mat 的传递
Mat中的矩阵头相比于存储像素值的矩阵而言非常小,所以在Mat的传递过程中通常传递的是Mat的矩阵头。每一个Mat拥有自己的矩阵头,但是不同的Mat共享一个像素值矩阵,矩阵维护一个引用次数,每拷贝一次Mat对象,矩阵的引用次数就会加一,反之矩阵头被释放时计数减一,最后一个引用的矩阵头负责释放掉矩阵。
Mat A, C; // 只创建矩阵头
A = imread(argv[1], CV_LOAD_IMAGE_COLOR); // 这里为矩阵开辟内存
Mat B(A); // 拷贝A的矩阵头
C = A; // 拷贝A的矩阵头
Mat D (A, Rect(10, 10, 100, 100) ); // 创建一个新的矩阵头,并指向A的矩阵
Mat E = A(Range:all(), Range(1,3)); // 创建一个新的矩阵头,并指向A的矩阵s
上述方法都是通过拷贝矩阵头的方法来实现Mat的传递,当用户想要拷贝整个矩阵的时候可以通过copyto()和clone()来实现。
Mat image = imread("2.jpg");
resize(image, image, Size(), 0.5, 0.5);
Mat image1(image) ;//仅是创建了Mat的头部分,image1与image共享数据区
Mat image2 = image ;//仅是创建了Mat的头部分,image1与image共享数据区
Mat image3 = image.clone() ;//完全拷贝,把image中的所有信息拷贝到image3中
Mat image4;
image.copyTo(image4);//根据image信息创建新的矩阵头,并拷贝image的数据区到image1中
for (int i = 0; i < image.rows; ++i)
{
uchar* ptr = image.ptr(i);
for (int j = 0; j < image.cols; ++j)
{
ptr[j] = 0;
}
}
imshow("image", image);
imshow("image1", image1);
imshow("image2", image2);
imshow("image3", image3);
imshow("image4", image4);
waitKey();
return 0;
上述代码运行后结果如下图,可见修改了image的数据区之后iamge1和image2都随着发生变化了,而image3和image4仍保持着原图的样子。
Mat 的常用属性
Mat有很多属性,有一些我经常用的如rows、cols、dims和channels等,也有一些调试的时候经常遇到的,这里放在一起统一整理一下。
data 指向矩阵数据部分的指针
rows 矩阵的行数
cols 矩阵的列数
dims 矩阵的维度,二维矩阵dims=2,三维矩阵dims=3.
size 矩阵的大小,size(cols,rows),如果矩阵的维数大于2,则是size(-1,-1)
channels 矩阵元素拥有的通道数,例如常见的彩色图像,每一个像素由RGB三部分组成,则channels = 3
type 用于表示矩阵中元素存储类型的预定义的常量,其本质是特殊命名规则下的int类型常量,其命名规则为CV_(位数)+(数据类型)+(通道数)。
数据类型包括这里U(unsigned integer)表示的是无符号整数,S(signed integer)是有符号整数,F(float)是浮点数。
例如:CV_16UC2,表示的是元素类型是一个16位的无符号整数,通道为2。
C1,C2,C3,C4则表示通道是1,2,3,4depth
矩阵中元素的一个通道的数据类型,这个值和type是相关的。例如 type为 CV_16SC2,一个2通道的16位的有符号整数。那么,depth则是CV_16S。depth也是一系列的预定义值,
将type的预定义值去掉通道信息就是depth值:
CV_8U CV_8S CV_16U CV_16S CV_32S CV_32F CV_64FelemSize
矩阵一个元素占用的字节数,例如:type是CV_16SC3,那么elemSize = 3 * 16 / 8 = 6 byteselemSize1
矩阵元素一个通道占用的字节数,例如:type是CV_16CS3,那么elemSize1 = 16 / 8 = 2 bytes = elemSize / channels-
step
step[0]是矩阵中一行元素的字节数。
step[1]是矩阵中一个元素的字节数,也就是和上面所说的elemSize相等。上面说到,Mat中一个uchar* data指向矩阵数据的首地址,而现在又知道了每一行和每一个元素的数据大小,就可以快速的访问Mat中的任意元素了。下面公式:
- step1
规整化的step,值为step / elemSize1。 定义如下:
inline size_t Mat::step1(int i) const { return step.p[i]/elemSize1(); }
以CV16UC4的img为例,来看下step,step1具体的值:
img的type是CV_16UC4, step[0]是其一行所占的数据字节数4 *4 * 16 / 8 = 32
step[1] 是一个元素所占的字节数,img的一个元素具有4个通道,故:4 * 16 / 8 = 8
step1 = step / elemSize1,elemSize1是元素的每个通道所占的字节数。