Mat类是OpenCV里广泛使用的一个类,主要用来作为存储图像的数据结构。
首先我们需要知道,OpenCV最早出现时其库是围绕C语言接口构建的。然后从OpenCV 2.0 开始引入c + + 接口。由于C++对C语言的兼容性,故而后面的版本可以兼容之前的版本,但反之则不行。
Mat类就是在2.0之后才有的,而在此之前,用来存储图像的是IplImage*和CvMat,由于C语言的限制,这两者都是struct,里面只定义了一些变量而没有函数(虽然C语言在struct使用函数指针可以达到类似class的效果,可以实现在C语言的struct中加成员函数,但在C语言中这样做的情况很少)。这样如果要释放IplImage和CvMat变量中的内存也就需要调用相关的函数,而不能让其自动处理,如果程序员没有释放其内存就会造成内存泄漏。相比之下,Mat就不需要程序员手动去管理内存。
Mat本质上是由两个数据部分组成: 一个矩阵头和一个指针,该指针指向包含了像素值的矩阵。矩阵头的大小是恒定的,用来存放矩阵的尺寸、存储方法、存储地址等相关信息,其内存开销并不大。关键在于Mat中数据指针所指向的那个矩阵,该矩阵用来保存图片中所有的像素值,开销相对而言大非常多。所以Mat会尽量不去重复存储同样的图片数据矩阵。Mat采取的方法是:当新建一个Mat用于存储一个图片的时候,(在矩阵头放完图片的相关信息后)会新建一个矩阵的空间用来存放这些像素数据,然后Mat只需要将其数据指针指向这个矩阵即可。如果我们想要进行赋值或者初始化构造的操作的时候,只是让Mat2也指向了Mat1数据指针所指向的那个矩阵而已(这里可以看出是浅拷贝),此时存放具体像素数据的矩阵依旧只有一份。如果我们想要进行深拷贝则需要Mat2=Mat1.clone();
Mat的内存自动清理采用引用计数的机制来实现(其类似于STL中的智能指针share_ptr):即有一个计数器会标记指向该矩阵的Mat数据指针有多少个,如果是最后一个指向该矩阵的指针,那么当该指针离开该矩阵的时候,会释放该矩阵所占用的空间,避免内存泄漏。
重要的成员变量:
data
data是一个uchar型的指针,这就是那个传说中的指向数据矩阵的指针。
dims
Mat矩阵的维度。若其指向的数据矩阵是一个二维矩阵,则dims=2,三维则dims=3。
rows
Mat矩阵的行数。
cols
Mat矩阵的列数。
以上这种类似于存储图像的矩阵的系统信息(列数、行数、维度等等)都归于之前所述的矩阵头部信息之列。
常用的构造方法:
Mat::Mat()
无参构造方法
Mat::Mat(int rows, int cols, int type)
创建行数为rows,列为cols,类型为type的Mat型变量(type如CV_8UC1等)。
Mat::Mat(Size size, int type)
创建大小为size,类型为type的图像,这里Size类可以通过cv::Size sz( w, h );方式构造。
Mat::Mat(int rows, int cols, int type, const Scalar& s)
创建行数为 rows,列数为 cols,类型为 type 的图像,并将所有元素初始值赋为s 。Scalar则根据图像类型来选择变量个数,比如RGB图像则用Scalar(1,2,3)赋三个值使得B=1,G=2,R=3。
Mat::Mat(Size size, int type, const Scalar& s)
创建大小为 size,类型为 type 的图像,并将所有元素初始值赋为 s 。
Mat::Mat(const Mat& m)
利用另一个Mat对象对其初始化,但是两个变量共用一个图像的矩阵数据。
Mat::Mat(int rows, int cols, int type, void* data, size_t step=AUTO_STEP)
创建行数为 rows,列数为 cols,类型为 type 的图像,此构造函数不创建图像数据所需内存,而是直接使用 data 所指内存,图像的行步长由 step 指定
Mat::Mat(Size size, int type, void* data, size_t step=AUTO_STEP)
创建大小为 size,类型为 type 的图像,此构造函数不创建图像数据所需 内存,而是直接使用 data 所指内存,图像的行步长由 step 指定
Mat::Mat(const Mat& m, const Range& rowRange, const Range& colRange)
创建的新图像为 m 的一部分,具体的范围由 rowRange 和 colRange 指 定,此构造函数也不进行图像数据的复制操作,新图像与m共用图像数据
Mat::Mat(const Mat& m, const Rect& roi)
创建的新图像为m的一部分,具体的范围roi指定,此构造函数也不进行图像数据的复制操作,新图像与m共用图像数据。
Mat常用的单个像素的读取方法:
1.动态地址计算
如果是灰度图可以
uchar value = grayim.at<uchar>(i, j);
grayim.at<uchar>(i, j) = 128;
RGB图像则可以
Vec3b pixel;
pixel[0] = 18;
pixel[1] = 28;
pixel[2] = 38;
colorim.at<Vec3b>(i, j) = pixel;
2.迭代器操作方式
这里只放一下彩色图像的方式了
MatIterator_<Vec3b> it, itend;
for (it = outputImage.begin<Vec3b>(), itend = outputImage.end<Vec3b>(); 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;
}
3.指针访问方式
for (int i = 0; i < rowNumber; i++) //行循环
{
uchar* data = outputImage.ptr<uchar>(i);
//ptr函数可以获取第i行首地址
for (int j = 0; j < colNumber; j++)//列循环
{
data[j] = data[j] / div*div + div / 2;
}
}
转载请标明出处,原文地址:https://blog.csdn.net/come_from_pluto