Mat对象介绍
Mat对象是从OpenCV2.0引入的对象,处理的对象其实是二维像素数组。
就像OpenCV官方教程中说的,下图中的车在CCD中看到的其实是一个二维的数组。Mat这个对象集成了一些操作这个数组数据的方法。
最初的opencv版本中,和Mat功能相似的是数据结构 lIplImage。
lIplImage是从2001年OpenCV发布之后就一直存在,是C语言风格的数据结构, 需要开发者自己分配与管理内存, 对大的程序使用它容易导致内存泄漏问题
lMat对象OpenCV2.0之后引进的图像数据结构、自动分配内存、不存在内存泄漏的问题, 是面向对象的数据结构。 分了两个部分,头部与数据部分
Mat的构造函数,和常用方法如下:
Mat() Mat(int rows,int cols,int type) Mat(Size size,int type) Mat(int rows,int cols,int type,const Scalar &s) Mat(Size size,int type,const Scalar &s) Mat(int ndims,const int *sizes,int type) Mat(int ndims,const int *sizes,int type,const Scalar &s) void copyTo(Mat mat) void convertTo(Mat dst, int type) Mat clone() int channels() int depth() bool empty(); uchar* ptr(i=0)
下面用代码测试一下上面的函数。
#include <opencv2/opencv.hpp> #include <iostream> #include <math.h> using namespace cv; using namespace std; double alpha = 1; double beta = 50; void changeContrastAndBright(const Mat& src, Mat& dst) { for (int i = 0; i < src.rows; i++) { for (int j = 0; j < src.cols; j++) { for (int k = 0; k < 3; k++) { dst.at<Vec3b>(i, j)[k] = saturate_cast<uchar>(alpha * (src.at<Vec3b>(i, j)[k]) + beta); } } } } int main(int argc, char** argv) { Mat src; src = imread("e:/5.png"); if (src.empty()) { cout << "不能载入图片..." << endl; return -1; } namedWindow("input", CV_WINDOW_AUTOSIZE); imshow("input", src); Mat dst; //创建与src一样大小的,默认像素的颜色填充(205,205,205)图像 dst = Mat(src.size(), src.type()); //指定像素颜色,这里是红色,即(0,0,255) dst = Scalar(0, 0, 255); namedWindow("win1", CV_WINDOW_AUTOSIZE); imshow("win1", dst); //cout << "dst=" << endl << dst << endl; //创建一个4通道的,每个通道像素的值为(255,255,0,25) //深度为32,像素点有4*4 //的图像矩阵,效果见图2 Mat dst1(4, 4, CV_32FC4, Scalar(255, 255, 0,25)); namedWindow("win2", CV_WINDOW_AUTOSIZE); imshow("win2", dst1); cout << "dst1=" << endl << dst1 << endl; //复制图形矩阵 //copyTo是深度复制 Mat dst2(src.size(), src.type()); //这种方式是浅复制,只复制Mat的头部分 Mat dst3(src); src.copyTo(dst2); namedWindow("win3", CV_WINDOW_AUTOSIZE); imshow("win3", dst2); namedWindow("win4", CV_WINDOW_AUTOSIZE); imshow("win4", dst3); //把src的色彩空间转为CV_BGR2GRAY(灰度图),输出通道数量 //效果见图3 Mat dst4(src.size(), src.type()); cvtColor(src, dst4, CV_BGR2GRAY); printf("src的通道数:%d\n", src.channels()); printf("dst4的通道数:%d\n", dst4.channels()); namedWindow("win5", CV_WINDOW_AUTOSIZE); imshow("win5", dst4); //create方法,跟构造函数作用差不多 Mat dst5; dst5.create(src.size(), src.type()); dst5 = Scalar(0, 255, 0); namedWindow("win6", CV_WINDOW_AUTOSIZE); imshow("win6", dst5); //掩模,提升图片对比度 Mat csrc; Mat dst6 = (Mat_<char>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0); filter2D(src, csrc, -1, dst6); namedWindow("win7", CV_WINDOW_AUTOSIZE); imshow("win7", csrc); //提升图片对比度亮度,见图4 Mat dst7=Mat::zeros(src.size(),src.type()); double time0 = static_cast<double>(getTickCount()); changeContrastAndBright(src, dst7); time0 = ((double)getTickCount() - time0) / getTickFrequency(); cout << "直接遍历方法运行时间为:" << time0 << "秒" << endl; Mat dst8; time0 = static_cast<double>(getTickCount()); src.convertTo(dst8, src.type(), alpha, beta); time0 = ((double)getTickCount() - time0) / getTickFrequency(); cout << "convertTo方法运行时间为:" << time0 << "秒" << endl; namedWindow("win8", CV_WINDOW_AUTOSIZE); imshow("win8", dst8); waitKey(0); return 0; }
(图2)
(图3)
(图4)
代码解释:
(1)Mat对象的浅复制与深度复制
部分复制:一般情况下只会复制Mat对象的头和指针部分,不会复制数据部分 Mat A= imread(imgFilePath); Mat B(A) 完全复制:如果想把Mat对象的头部和数据部分一起复制, 可以通过如下两个API实现 Mat F = A.clone(); 或 Mat G; A.copyTo(G);
输出图像的内存是自动分配的
使用OpenCV的C++接口,不需要考虑内存分配问题
赋值操作和拷贝构造函数只会复制头部分
使用clone与copyTo两个函数实现数据完全复制
(2)Mat构造函数
Mat M(2,2,CV_8UC3, Scalar(0,0,255)) 其中前两个参数分别表示行(row)跟列(column)、 第三个CV_8UC3中的8表示每个通道占8位、U表示无符号、C表示Char类型、 3表示通道数目是3,第四个参数是向量表示初始化每个像素值是多少, 向量长度对应通道数目一致
(3)create方法
它可以创建多维数组,虽然2维以上数组我们不常用。
下面的代码创建了3维数组。
创建多维数组cv::Mat::create int sz[3] = {2,2,2}; Mat L(3,sz, CV_8UC1, Scalar::all(0));
Mat M; M.create(4, 3, CV_8UC2); M = Scalar(127,127); cout << "M = " << endl << " " << M << endl << endl; uchar* firstRow = M.ptr<uchar>(0); printf("%d", *firstRow
结果:
(4)convertTo方法
这个方法用于数据类型的转换,请看下面的示例代码。
//验证 convertTo 是否支持in place 和 改变通道数 #include <opencv2/opencv.hpp> //头文件 using namespace cv; //包含cv命名空间 using namespace std; void main( ) { // 【1】读入一张图片,载入图像 Mat ImgColor = imread("1.jpg", IMREAD_COLOR);// CV_8UC3 Mat ImgGray = imread("1.jpg", IMREAD_GRAYSCALE);// CV_8UC1 cout << "original" << endl; cout << "ImgColor.type()" << ImgColor.type() << endl; cout << "ImgGray.type()" << ImgGray.type() << endl << endl; // 能否转为16位深度3通道 Mat ImgColorC3; Mat ImgGrayC3; ImgColor.convertTo(ImgColorC3, CV_16UC3); ImgGray.convertTo(ImgGrayC3, CV_16UC3); cout << "convertTo CV_16UC3" << endl; cout << "ImgColorC3.type()" << ImgColorC3.type() << endl; cout << "ImgGrayC3.type()" << ImgGrayC3.type() << endl << endl; // 能否转为16位深度1通道 Mat ImgColorC1; Mat ImgGrayC1; ImgColor.convertTo(ImgColorC1, CV_16UC1); ImgGray.convertTo(ImgGrayC1, CV_16UC1); cout << "convertTo CV_16UC1" << endl; cout << "ImgColorC1.type()" << ImgColorC1.type() << endl; cout << "ImgGrayC1.type()" << ImgGrayC1.type() << endl << endl; // convertTo是否支持in-place 操作 ImgColor.convertTo(ImgColor, CV_16UC3); ImgGray.convertTo(ImgGray, CV_16UC3); cout << "in place" << endl; cout << "ImgColor.type()" << ImgColor.type() << endl; cout << "ImgGray.type()" << ImgGray.type() << endl << endl; }
结果如下:
convertTo可以改变Mat的深度,例如可以把Mat的type从CV_8UC3改为CV_16SC3
convertTo不可以改变Mat的通道数,例如不可以把Mat的type从CV_8UC3改为CV_8UC1,即使你填入的转换类型通道数不同,输出的通道数还是与输入的通道数相同。
convertTo支持就地(in-place)操作
在本文的演示中,我们使用convertTo对图像的对比度和亮度的调整,主要是根据公式dst = alpha * src + beta(alpha控制对比度,beta控制亮度)。
代码中,我们演示了使用循环遍历像素的方法 changeContrastAndBright()以及使用convertTo()的方法。
而convertTo方法速度要快得多,不是一个数量级。
---------------------
作者:hackpig
来源:www.skcircle.com
版权声明:本文章代码及资料部分或全部来自贾志刚老师的视频,勇哥只是在个人理解的基础上做学习笔记,转载请附上博文链接!

