OpenCv2.X采用全新的图像数据结构Mat代替C接口cvMat和IplImage。全新Mat类不需要我们手动为其开辟空间,也不需要立即释放内存空间,它能够自动管理内存。Mat类由矩阵头和指向存储所有像素值的矩阵的指针构成。Mat类表示一个n维的密集数值单通道或多通道数组,它可用于存储实数或复数值的向量和矩阵、灰度或彩色图像等。
Mat类
class CV_EXPORTS Mat
{
public:
int flags; //标志位
int dims; //数组的维数
int rows, cols; //行和列的数量
uchar* data; //指向数据的指针
int* refcount; //指针的引用计数器,当阵列指向用户分配的数据时,指针为NULL
};
Mat类常用的构造方法
- 无参数构造方法
Mat::Mat()
- 指定类型的二维数组,创建行数为rows,列数为cols,类型为type的图像
Mat::Mat(int rows,int cols,int type);
- 创建大小为size,类型为type的图像
Mat::Mat(Size size,int type)
- 指定类型的二维数组,并指定初始化值。创建行数为rows,列数为cols,类型为type的图像,并将所有元素初始化为值s
Mat::Mat(int rows,int cols,int type,const Scalar&s);
- m和新对象共用图像数据,将m赋值给新创建的对象,此操作不会对图像数据进行复制,m和新对象共用图像数据。
Mat::Mat(const Mat& m)
对上面的几种构造函数应用举例
#include<opencv2/imgproc/imgproc.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int main()
{
Mat img1; //创建无初始化矩阵
Mat img2(6, 6, CV_8UC1); //创建6行6列类型为8位单通道矩阵
Mat img3(Size(7, 7), CV_8UC3); //创建大小为7*7类型为8位3通道矩阵
Mat img4(8, 8, CV_32FC2, Scalar(1, 3)); //创建一个用1+3j填充的8*8复矩阵
Mat img5(Size(9, 9), CV_8UC3, Scalar(1, 2, 3));//创建大小为9*9类型的8位3通道矩阵
Mat img6(img2);//将img2的值赋给img6,共用数据图像
cout << "img1: " << endl << img1 << endl << endl;
cout << "img2: " << endl << img2 << endl << endl;
cout << "img3: " << endl << img3 << endl << endl;
cout << "img4: " << endl << img4 << endl << endl;
cout << "img5: " << endl << img5 << endl << endl;
cout << "img6: " << endl << img6 << endl << endl;
}
运行结果如下:
上面列举了Mat的基本构造函数,主要分为几个类型:要求输入行数和列数来构造一个二维数组的、使用cv::Size对象来构造一个二维数组的、
Mat基本操作
Mat::row | 创建一个具有指定了矩阵头中行数的参数的矩阵 |
Mat::col | 创建一个具有指定了矩阵头中列数的参数的矩阵 |
Mat::rowRange | 为指定的行创建一个新的矩阵头,可取指定区间行元素 |
Mat::colRange | 为指定的列创建一个矩阵头,可取指定列区间元素 |
Mat::clone | 创建一个数组及其基础数据的完整副本 |
Mat::copyTo | 把矩阵复制到另一个矩阵中 |
Mat::convertTo | 在放缩或不放缩的情况下转换为另一种数据类型 |
Mat::zeros | 返回指定的大小和类型的零数组 |
Mat::ones | 返回一个指定的大小和类型全为1的数组 |
Mat::channels | 返回矩阵通道的数目 |
Mat::empty | 返回数组有没有元素,没有则返回true |
Mat::at | 返回对指定元素数组的引用 |
对上述函数应用举例:
#include<opencv2/imgproc/imgproc.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int main()
{
Mat img1(10, 8, CV_8UC1, Scalar(5));
//获取矩阵行列数
cout << "img1 row: " << img1.rows << endl; //10
cout << "img1 col: " << img1.cols << endl; //8
//获取指定行列元素
cout << img1.rowRange(1, 3) << endl;
cout << img1.colRange(2, 4) << endl; //区间范围是包括左边界,不包括右边界
//创建8*8复数矩阵1+5j
Mat img2(8, 8, CV_32FC2, Scalar(1, 5));
//利用create方法重新创建10*10的8位无符号3通道矩阵
img2.create(10, 10, CV_8UC3);
cout << "img2 channels: " << img2.channels() << endl;//3
//转换矩阵类型
img2.convertTo(img2, CV_32F);
cout << "img2 depth: " << img2.depth() << endl;//5
//depth是预定义值
//#define CV_8U 0
//#define CV_8S 1
//#define CV_16U 2
//#define CV_16S 3
//#define CV_32S 4
//#define CV_32F 5
//#define CV_64F 6
//zeros创建矩阵
Mat img3 = Mat::zeros(img2.rows, img2.cols, CV_8UC1);
cout << "img3" << endl << img3 << endl;
}
运行结果如下:
为了方便查看每个函数的作用,下面将每一步的结果单独显示出来。
将img1的第4行元素变换成img1的第5行元素乘2
img1.row(4) = img1.row(5) * 2;
cout << img1 << endl;
将img4矩阵赋值为img1的第4列
Mat img4 = img1.col(4);
cout << img4 << endl;
将img1矩阵的第1列复制到img4中
img1.col(1).copyTo(img4);
cout << img4<<endl;
独立获取数组元素
访问一个元素的两种主要方法是通过位置或者迭代器访问
直接访问是通过模板函数at<>()来实现的。这个函数的工作方式是先将at<>()特化到矩阵所包含的数据类型,然后使用你所想要的数据的行和列的位置访问该元素。
举一个简单的例子:
cv::Mat m = cv::Mat::eye(10,10,32FC1);
cout<<''Element(3,3) is << m.at<float>(3,3);
多通道数组的操作与单通道数组类似:
cv::Mat m = cv::Mat::eye(10,10,32FC2);
cout<<"Element(3,3) is "<<m.at<cv::Vec2f>(3,3)[0]<<m.at<cv::Vec2f>(3,3)[1];
at<>()访问器函数的变体
M.at<int>(i); //整型数组M中的元素i
M.at<float>(i,j); //浮点型数组M中的元素(i,j)
M.at<int>(pt); //整型矩阵M中处于(pt.x,pt.y)的元素
M.at<float>(i,j,k); //三维浮点型矩阵M中处于(i,j,k)位置的元素
M.at<uchar>(idx); //无符号字符数组M中位于idx[]所索引的n维位置的元素
通过块访问数组元素
Mat区块访问
m.row(i); //m中第i行数组
m.col(j); //m中第j列数组
m.rowRange(i0,i1); //m中第i0行到i1-1行所构成的数组
m.rowRange(cv::Range(i0,i1)); //m中第i0行到i1-1行所构成的数组
m.colRange(j0,j1); //m中第j0列到j1-1列所构成的数组
m.colRange(cv::Range(j0,j1)); //m中第j0列到j1-1列所构成的数组
m.diag(d); //m中偏移为d的对角线所组成的数组
m(cv::Range(i0,i1),cv::Range(j0,j1)); //m中从点(i0,j0)到(i1-1,j1-1)所包含数据组成的数组
m(cv::Rect(i0,j0,w,h)); //m中从点(i0,j0)到(i0+w-1,j0+h-1)所包含数据组成的数组
Mat的更多函数成员
m1=m0.clone(); //从m0进行完全复制,将复制所有的数据元素
m0.copyTo(m1); //将m0复制给m1,如果有必要,将给m1重分配内存空间(等同于m1=m0.clone())
m0.copyTo(m1,mask); //和m0.copyTo(m1)一样,但是只复制mask所指示的区域
m0.convertTo(m1,type,scale,offset);
//转换m0中元素的类型并且在尺度变换(默认为1)和增加偏置(默认为0)之后赋值给m1
m0.assignTo(m1,type); //也是将m0赋值给m1;
m0.setTo(s,mask); //设置m0所有元素为s,如果存在mask,则只对mask区域操作
m0.reshape(chan,rows); //改变二维数组的有效形状,chan和rows为0表示不做更改
m0.adjustROI(t,b,l,r); //通过四个值t(上),b(下),l(左),r(右)调整ROI范围
m0.locateROI(size,offset);
//将m0的全尺寸写入变量size,如果m0只是一个大矩阵的一小块区域,还会写入一个Point类型的offset
m0.type(); //返回m0元素的类型
m0.depth(); //返回m0通道中的元素类型
m0.channels(); //返回m0的通道数目
m0.size(); //以Size返回m0的大小