1. 程式人生 > >OpenCV原始碼閱讀——1.2 Mat的記憶體管理

OpenCV原始碼閱讀——1.2 Mat的記憶體管理

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。


②Mat A = TMP,執行這一行程式碼,需要經歷兩個階段:第一階段,TMP的型別是MatExpr,此處會執行型別轉換函式MatExpr:: operator Mat();第二階段是Mat A的構造方法,Mat::Mat(constMat& m),從一個已有的Mat構造新的Mat。該程式碼完成後,TMP作為右值會被銷燬掉。



③Mat B = A 也是執行B的構造方法Mat::Mat(const Mat& m) ,從一個已有的Mat構造新的Mat。

 

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操作是不產生新的記憶體的

======待續======