官方对mat介绍的原话:
Mat represents an n-dimensional dense numerical single-channel or multi-channel array. It can be used to store real or complex-valued vectors and matrices, grayscale or color images, voxel volumes, vector fields, point clouds, tensors, histograms (though, very high-dimensional histograms may be better stored in a SparseMat 译文:Mat类用于表示一个多维的单通道或者多通道的稠密数组。能够用来保存实数或复数的向量、矩阵,灰度或彩色图像,立体元素,点云,张量以及直方图(高维的直方图使用SparseMat保存比较好)。简而言之,Mat就是用来保存多维的矩阵的.
opencv2的Mat类带来的改变:
1不必手动开辟空间 2不必再在不需要的时候立即将空间释放
Mat由两个数据部分组成:矩阵头(包含矩阵尺寸、存储方法、存储地址等信息)和一个指向存储所有像素值的矩阵的指针。
问题:关于图像拷贝问题?
OpenCV使用引用计数机制来解决复制图像的问题。其思路是让每个 Mat 对象有自己的信息头,但共享同一个矩阵。这通过让矩阵指针指向同一地址而实现。而拷贝构造函数则只拷贝信息头和矩阵指针 ,而不拷贝矩阵。
Mat A, C; // 只创建信息头部分
A = imread(argv[1], CV_LOAD_IMAGE_COLOR); // 这里为矩阵开辟内存
Mat B(A); // 使用拷贝构造函数
C = A; // 赋值运算符
以上代码中的所有Mat对象最终都指向同一个也是唯一一个数据矩阵。
如果想让各自对象都有一个信息头和矩阵,这时候可以使用cone()和copyTo()两个成员函数。
Mat F = A.clone();
Mat G;
A.copyTo(G);
问题:如何创建ROI?
你可以创建只引用部分数据的信息头。比如想要创建一个感兴趣区域( ROI ),你只需要创建包含边界信息的信息头。
Mat D (A, Rect(10, 10, 100, 100) ); // using a rectangle
Mat E = A(Range:all(), Range(1,3)); // using row and column boundariesq
其中Range()指定感兴趣行或列的范围,Range指从起始索引到终止索引(不包括终止索引)的一连续序列。
问题:图像的存储方式
存储像素值需要指定颜色空间和数据类型。
常用的颜色空间:RGB HSV HLS 灰度级空间 等。
<uchar>类型;如果是RGB彩色图,存放<Vec3b>类型。
单通道灰度图数据存放格式:
多通道的图像中,每列并列存放通道数量的子列,如RGB三通道彩色图:
isContinuous()函数来判断图像数组是否为连续的。
问题:如何访问图像中的像素
访问图像中像素的三个方法:
1.指针访问:c操作符[]
2.迭代器iterator
3.动态地址计算
三种方法在访问速度上有差异
第一种:用指针访问 访问速度最快
void colorreduce(Mat& inputImage,Mat& outputImage,int div)
{
outputImage=inputImage.clone();
int rowNumber=outputImage.rows;
int colNumber=outputImage.cols*outputImage.channels();//每一行元素的个数
for(int i=0;i<rowNumber;i++)
{
uchar* data=outputImage.ptr<uchar>(i);
//uchar* data=outputImage.ptr<uchar>(i)[j];//获取第i行第j列元素的值
for(int j=0;j<colNumber;j++)
{ data[j] = data[j] / div*div + div / 2;
} //*data++ = *data / div*div + div / 2; //所有方法中最快的
}
第二种:用迭代器操作像素
这里,我们只需要获取图像矩阵的begin和end,然后增加迭代直至从begin到end。将*操作符添加到迭代指针前,即可访问当前指向的内容。相比指针容易产生越界的情况,迭代器是绝对安全的
void colorReduce(Mat& inputImage,Mat& outputImage,int div)
{
outputImage = inputImage.clone(); Mat_<Vec3b>::iterator it = outputImage.begin<Vec3b>(); Mat_<Vec3b>::iterator itend = outputImage.end<Vec3b>(); for (; it != itend; ++it) { (*it)[0] = (*it)[0] / div*div + div / 2; (*it)[1] = (*it)[1] / div*div + div / 2; (*it)[2] = (*it)[2] / div*div + div / 2; }
第三种:动态地址计算
void colorReduce(Mat& inputImage,Mat& outputImage,int div)
{
outputImage = inputImage.clone(); int rowNumber = outputImage.rows; int colNumber = outputImage.cols; for (int i = 0; i < rowNumber; i++) { for (int j = 0; j < colNumber; j++) { outputImage.at<Vec3b>(i,j)[0]=
outputImage.at<Vec3b>(i,j)[0]/div*div+div/2; outputImage.at<Vec3b>(i,j)[1]=
outputImage.at<Vec3b>(i,j)[1]/div*div+div/2; outputImage.at<Vec3b>(i,j)[2]=
outputImage.at<Vec3b>(i,j)[2]/div*div+div/2;
}
}
}
问题:如何显式地创建一个Mat对象
Mat 不但是一个很赞的图像容器类,它同时也是一个通用的矩阵类,所以可以用来创建和操作多维矩阵。创建一个Mat对象有多种方法:
一:Mat() 构造函数
Mat M(2,2, CV_8UC3, Scalar(0,0,255));
cout << "M = " << endl << " " << M << endl << endl;
显示结果:
Scalar 是个short型vector。指定这个能够使用指定的定制化值来初始化矩阵.
二:Create()
M.create(4,4, CV_8UC(2));
cout << "M = "<< endl << " " << M << endl << endl;
结果:
这个创建方法不能为矩阵设初值,它只是在改变尺寸时重新为矩阵数据开辟内存
MATLAB形式的初始化方式: zeros(), ones(), :eyes() 。使用以下方式指定尺寸和数据类型:
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;
结果:
mat的常见属性
data uchar型的指针。Mat类分为了两个部分:矩阵头和指向矩阵数据部分的指针,data就是指向矩阵数据的指针
dims
rows
cols
size 矩阵的大小,size(cols,rows),如果矩阵的维数大于2,则是size(-1,-1)
channels
7:type 表示了矩阵中元素的类型以及矩阵的通道个数,它是一系列的预定义的常量,其命名规则为CV_(位数)+(数据类型)+(通道数)。具体的有以下值:
CV_8UC1 | CV_8UC2 | CV_8UC3 | CV_8UC4 |
CV_8SC1 | CV_8SC2 | CV_8SC3 | CV_8SC4 |
CV_16UC1 | CV_16UC2 | CV_16UC3 | CV_16UC4 |
CV_16SC1 | CV_16SC2 | CV_16SC3 | CV_16SC4 |
CV_32SC1 | CV_32SC2 | CV_32SC3 | CV_32SC4 |
CV_32FC1 | CV_32FC2 | CV_32FC3 | CV_32FC4 |
CV_64FC1 | CV_64FC2 | CV_64FC3 | CV_64FC4 |
8:depth 矩阵中元素的一个通道的数据类型,这个值和type是相关的。例如 type为 CV_16SC2,一个2通道的16位的有符号整数。那么,depth则是CV_16S。depth也是一系列的预定义值,