图像识别与处理之Opencv——Mat_类与Mat 类的内存管理
2017-11-01 11:34
921 查看
Mat_类
Mat_类是对 Mat 类的一个包装,其定义如下:
这是一个非常轻量级的包装,既然已经有 Mat 类,为何还要定义一个 Mat_类?
下面我们看这段代码:
在读取矩阵元素时,以及获取矩阵某行的地址时,需要指定数据类型。这样首先需要不停地写“”,让人感觉很繁琐,在繁琐和烦躁中容易犯错,如上面代码中的错误,用 at()获取矩阵元素时错误的使用了 double 类型。这种错误不是语法错误,因此在编译时编译器不会提醒。在程序运行时, at()函数获取到的不是期望的(i,j)位置处的元素,数据已经越界,但是运行时也未必会报错。这样
的错误使得你的程序忽而看上去正常,忽而弹出“段错误”,特别是在代码规模很大时,难以查错。
如果使用 Mat_类,那么就可以在变量声明时确定元素的类型, 访问元素时不再需要指定元素类型,即使得代码简洁,又减少了出错的可能性。上面代码可以用 Mat_实现,实现代码如下面例程里的第二个双重 for 循环。
Mat 类的内存管理
使用 Mat 类,内存管理变得简单,不再像使用 IplImage 那样需要自己申请和释放内存。虽然不了解 Mat 的内存管理机制,也无碍于 Mat 类的使用,但是如果清楚了解 Mat 的内存管理,会更清楚一些函数到底操作了哪些数据。
Mat 是一个类,由两个数据部分组成:矩阵头(包含矩阵尺寸,存储方法,存储地址等信息)和一个指向存储所有像素值的矩阵的指针,如下图所示。矩阵头的尺寸是常数值,但矩阵本身的尺寸会依图像的不同而不同,通常比矩阵头的尺寸大数个数量级。 复制矩阵数据往往花费较多时间,因此除非有必要,不要复制大的矩阵。
为了解决矩阵数据的传递, OpenCV 使用了引用计数机制。其思路是让每个Mat 对象有自己的矩阵头信息,但多个 Mat 对象可以共享同一个矩阵数据。让矩阵指针指向同一地址而实现这一目的。很多函数以及很多操作(如函数参数传值)只复制矩阵头信息,而不复制矩阵数据。
前面文提到过,有很多中方法创建 Mat 类。 如果 Mat 类自己申请数据空间,那么该类会多申请 4 个字节,多出的 4 个字节存储数据被引用的次数。引用次数存储于数据空间的后面, refcount 指向这个位置,如下图所示。当计数等于 0时,则释放该空间。
关于多个矩阵对象共享同一矩阵数据,我们可以看这个例子:
上面代码中有三个 Mat 对象,分别是 A, B 和 C。这三者共有同一矩阵数据,其示意图如下图所示。
Mat_类是对 Mat 类的一个包装,其定义如下:
template<typename _Tp> class Mat_ : public Mat { public: //只定义了几个方法 //没有定义新的属性 };
这是一个非常轻量级的包装,既然已经有 Mat 类,为何还要定义一个 Mat_类?
下面我们看这段代码:
Mat M(600, 800, CV_8UC1); for( int i = 0; i < M.rows; ++i) { uchar * p = M.ptr<uchar>(i); for( int j = 0; j < M.cols; ++j ) { double d1 = (double) ((i+j)%255); M.at<uchar>(i,j) = d1; double d2 = M.at<double>(i,j);//此行有错 } }
在读取矩阵元素时,以及获取矩阵某行的地址时,需要指定数据类型。这样首先需要不停地写“”,让人感觉很繁琐,在繁琐和烦躁中容易犯错,如上面代码中的错误,用 at()获取矩阵元素时错误的使用了 double 类型。这种错误不是语法错误,因此在编译时编译器不会提醒。在程序运行时, at()函数获取到的不是期望的(i,j)位置处的元素,数据已经越界,但是运行时也未必会报错。这样
的错误使得你的程序忽而看上去正常,忽而弹出“段错误”,特别是在代码规模很大时,难以查错。
如果使用 Mat_类,那么就可以在变量声明时确定元素的类型, 访问元素时不再需要指定元素类型,即使得代码简洁,又减少了出错的可能性。上面代码可以用 Mat_实现,实现代码如下面例程里的第二个双重 for 循环。
#include <iostream> #include "opencv2/opencv.hpp" #include <stdio.h> using namespace std; using namespace cv; int main(int argc, char* argv[]) { Mat M(600, 800, CV_8UC1); for( int i = 0; i < M.rows; ++i) { //获取指针时需要指定类型 uchar * p = M.ptr<uchar>(i); for( int j = 0; j < M.cols; ++j ) { double d1 = (double) ((i+j)%255); //用 at()读写像素时,需要指定类型 M.at<uchar>(i,j) = d1; //下面代码错误,应该使用 at<uchar>() //但编译时不会提醒错误 //运行结果不正确, d2 不等于 d1 double d2 = M.at<double>(i,j); } } //在变量声明时指定矩阵元素类型 Mat_<uchar> M1 = (Mat_<uchar>&)M; for( int i = 0; i < M1.rows; ++i) { //不需指定元素类型,语句简洁 uchar * p = M1.ptr(i); for( int j = 0; j < M1.cols; ++j ) { double d1 = (double) ((i+j)%255); //直接使用 Matlab 风格的矩阵元素读写,简洁 M1(i,j) = d1; double d2 = M1(i,j); } } return 0; }
Mat 类的内存管理
使用 Mat 类,内存管理变得简单,不再像使用 IplImage 那样需要自己申请和释放内存。虽然不了解 Mat 的内存管理机制,也无碍于 Mat 类的使用,但是如果清楚了解 Mat 的内存管理,会更清楚一些函数到底操作了哪些数据。
Mat 是一个类,由两个数据部分组成:矩阵头(包含矩阵尺寸,存储方法,存储地址等信息)和一个指向存储所有像素值的矩阵的指针,如下图所示。矩阵头的尺寸是常数值,但矩阵本身的尺寸会依图像的不同而不同,通常比矩阵头的尺寸大数个数量级。 复制矩阵数据往往花费较多时间,因此除非有必要,不要复制大的矩阵。
为了解决矩阵数据的传递, OpenCV 使用了引用计数机制。其思路是让每个Mat 对象有自己的矩阵头信息,但多个 Mat 对象可以共享同一个矩阵数据。让矩阵指针指向同一地址而实现这一目的。很多函数以及很多操作(如函数参数传值)只复制矩阵头信息,而不复制矩阵数据。
前面文提到过,有很多中方法创建 Mat 类。 如果 Mat 类自己申请数据空间,那么该类会多申请 4 个字节,多出的 4 个字节存储数据被引用的次数。引用次数存储于数据空间的后面, refcount 指向这个位置,如下图所示。当计数等于 0时,则释放该空间。
关于多个矩阵对象共享同一矩阵数据,我们可以看这个例子:
Mat A(100,100, CV_8UC1); Mat B = A; Mat C = A(Rect(50,50,30,30));
上面代码中有三个 Mat 对象,分别是 A, B 和 C。这三者共有同一矩阵数据,其示意图如下图所示。
相关文章推荐
- 图像识别与处理之Opencv——Mat表达式的运算(矩阵的一些运算)
- OpenCV+OCR 图像处理字符识别原理及代码
- javaCV图像处理之Frame、Mat和IplImage三者相互转换(使用openCV进行Mat和IplImage转换)
- Opencv中做图像处理与识别中常用的函数
- 图像识别与处理之Opencv——高斯滤波GaussianBlur() 11月2日暂存
- 图像处理和图像识别中常用的OpenCV函数
- Python-OpenCV 处理图像(六)(七)(八):对象识别 图像灰度化处理 图像二值化处理
- 图像识别与处理之Opencv——Mat类
- OpenCV作图像处理和识别方面的编程时常用的基础语句积累!
- javaCV图像处理之Frame、Mat和IplImage三者相互转换(使用openCV进行Mat和IplImage转换)
- 使用谷歌开源组件tesseract-OCR识别身份证,通过opencv处理图像后再进行识别(windows版本)
- OpenCV(一) Mat数据结构--最基本的图像处理容器
- 图像处理及图像识别中常用的 Opencv 函数
- OpenCV - 图形图像处理 - 形态学处理 1931 1 Opencv图像识别从零到精通(17)----开运算、闭运算、顶帽、黑帽、形态学梯度、形态学角点、细化、填充 作者:qq_208236
- opencv学习篇(1) 图像处理和图像识别中常用的OpenCV函数
- 图像识别与处理之Opencv——像素值的读写
- 图像处理之opencv---mat、cvmat、IplImage之间的转换
- opencv-python人眼识别图像处理基础
- 图像识别与处理之Opencv——高斯滤波及GaussianBlur()函数
- 图像识别与处理之Opencv——基本数据结构及示例