1.2 Mat的内存管理

图像数据量大,不妥善管理好内存会产生很大的问题。OpenCV1.X中多采用C的结构,需要用户自己管理内存,在图像不再使用时调用CvRelease。OpenCV2.X中采用C++面向对象的方式,内存可以由自动申请和释放。

 

1.2.1 图像头与图像内容

OpenCV中,图像的头与图像内容是分开的。如下面这段代码:

Mat A = Mat::zeros(800,600, CV_8UC3);
Mat B = A;




①Mat::zeros(800,600, CV_8UC3)是一个静态函数,用于返回一个全部是0的矩阵,该函数返回的MatExpr对象将是一个右值(有内存,但没有变量指向它),暂且我们叫他TMP。

opencv Mat索引 opencv mat setto_阅读

②Mat A = TMP,执行这一行代码,需要经历两个阶段:第一阶段,TMP的类型是MatExpr,此处会执行类型转换函数MatExpr:: operator Mat();第二阶段是Mat A的构造方法,Mat::Mat(constMat& m),从一个已有的Mat构造新的Mat。该代码完成后,TMP作为右值会被销毁掉。

opencv Mat索引 opencv mat setto_opencv_02

opencv Mat索引 opencv mat setto_阅读_03

③Mat B = A 也是执行B的构造方法Mat::Mat(const Mat& m) ,从一个已有的Mat构造新的Mat。

 

opencv Mat索引 opencv mat setto_阅读_04

TMP、A、B虽然三个Mat(MatExpr)对象,他们分别有自己的头,但他们却只有一个图像内容。图像自己带有一个计数器,当计数器为0时,数据才有可能销毁。

 

1.2.2 内存申请

在【core.hpp第1465行】定义了classCV_EXPORTS MatAllocator,他是个抽象类,供用户自定义申请内存的方法,如果用户没有指定内存申请器,那么Mat会使用默认的申请方法。

Mat::ones和Mat::zeros两个函数的内存申请在MatExpr转换为Mat的函数中,见【mat.hpp第1221行】operatorMat() const函数共有三条语句:

operator Mat() const
{
   Mat m;
   op->assign(*this, m);
   return m;
}




Op显得非常神秘,他是函数操作抽象类,他的子类在matop.cpp中,此处的op是MatOp_Initializer函数初始化操作,在MatOp_Initializer::assign函数中【matop.cpp第1554行】中调用了Mat::create函数,从而申请了内存。

imread函数则在【loadsave.cpp(sources\modules\highgui\src\loadsave.cpp)第235行】调用了mat::create函数。

顺带一提,OpenCV1.x中CvLoadImage中的内存申请在【array.cp(sources\modules\core\src\array.cpp)第830行】中调用cvAlloc函数。

无论是mat::create函数还是cvAlloc函数,他们都最终指向了同一个函数:【alloc.cp(sources\modules\core\src\alloc.cpp)第62行】fastMalloc函数。

 

tip:在看这一段代码时,看到一个有趣的小函数:

在core.hpp第343行

static inlinesize_t alignSize(size_t sz, int n)
{
    assert((n & (n - 1)) == 0); // n is apower of 2
    return (sz + n-1) & -n;
}




该函数的作用是求不比sz小的数中能被n整除的最小数。这句话比较拗口,举个例子,sz=23,n=16,那么23、24、25…中第一个16的倍数是32,那么答案就是32。再例如,sz=16,n=8,那么在16、17、18…中第一个8的倍数是16。

如果这个函数让我写的话,会写成下面这个摸样:

static inlinesize_t alignSize(size_t sz, int n)
{
    assert((n & (n - 1)) == 0); // n is apower of 2
    return ((sz/n)+(sz%n)>0?1:0)*n;
}




源代码中2个取反、2个加法、一个与运算,而我的代码中1个整除、1个取余、一个判断、1个乘法,确实我的代码远不如源代码高效。

解析这个算式:sz + n-1:sz到sz+n-1这n个数中,必有一个数是n的倍数,而这个数就是答案。那么sz + n-1对n整除就可以得到答案。n是2的整数次方(设K次方),-n在二进制上表现就是最后K位是0,其他都是1,如4是2的2次方,那么-4的二进制表达就是(1111 1111 , 1111 1111 , 1111 1111 , 1111 1100)2,与这个数做与运算,就是整除操作。

 

1.2.3 Mat操作中的内存

Mat在赋值运算,构造方法里,都没有新的内存申请。

只有调用Mat::copyTo函数,才会产生新的数据。

值得注意的是以下两种操作。

1)矩阵运算会产生新的内存。

Mat A;
Mat B;
…//给A、B创建数据
Mat C = A + B;




那么A + B是MatExpr。MatExpr转换成Mat往往是产生新的内存的。

2)POI操作是不产生新的内存的

======待续======