(轉)OpenCV數據結構Mat詳解

我記得開始接觸OpenCV就是因爲一個算法裏面需要2維動態數組,那時候看core這部分也算是走馬觀花吧,隨着使用的增多,對Mat這個結構越來越喜愛,也覺得有必要溫故而知新,於是這次再看看Mat。

Mat最大的優勢跟STL很相似,都是對內存進行動態的管理,不需要之前用戶手動的管理內存,對於一些大型的開發,有時候投入的lpImage內存管理的時間甚至比關注算法實現的時間還要多,這顯然是不合適的。除了有些嵌入式場合必須使用c語言,我任何時候都強烈像大家推薦Mat。

Mat這個類有兩部分數據。一個是matrix header,這部分的大小是固定的,包含矩陣的大小,存儲的方式,矩陣存儲的地址等等。另一個部分是一個指向矩陣包含像素值的指針

  1. Mat A, C; // creates just the header parts  
  2. A = imread(argv[1], CV_LOAD_IMAGE_COLOR); // here we’ll know the method used (allocate matrix)  
  3. Mat B(A); // Use the copy constructor  
  4. C = A; // Assignment operator  

需要注意的是,copy這樣的操作只是copy了矩陣的matrix header和那個指針,而不是矩陣的本身,也就意味着兩個矩陣的數據指針指向的是同一個地址,需要開發者格外注意。比如上面這段程序,A、B、C指向的是同一塊數據,他們的header不同,但對於A的操作同樣也影響着B、C的結果。剛剛提高了內存自動釋放的問題,那麼當我不再使用A的時候就把內存釋放了,那時候再操作B和C豈不是很危險。不用擔心,OpenCV的大神爲我們已經考慮了這個問題,是在最後一個Mat不再使用的時候纔會釋放內存,咱們就放心用就行了。

如果想建立互不影響的Mat,是真正的複製操作,需要使用函數clone()或者copyTo()

說到數據的存儲,這一直就是一個值得關注的問題,Mat_<uchar>對應的是CV_8U,Mat_<uchar>對應的是CV_8U,Mat_<char>對應的是CV_8S,Mat_<int>對應的是CV_32S,Mat_<float>對應的是CV_32F,Mat_<double>對應的是CV_64F,對應的數據深度如下:

• CV_8U - 8-bit unsigned integers ( 0..255 )

• CV_8S - 8-bit signed integers ( -128..127 )

• CV_16U - 16-bit unsigned integers ( 0..65535 )

• CV_16S - 16-bit signed integers ( -32768..32767 )

• CV_32S - 32-bit signed integers ( -2147483648..2147483647 )

• CV_32F - 32-bit floating-point numbers ( -FLT_MAX..FLT_MAX, INF, NAN )

• CV_64F - 64-bit floating-point numbers ( -DBL_MAX..DBL_MAX, INF, NAN )

這裏還需要注意一個問題,很多OpenCV的函數支持的數據深度只有8位和32位的,所以要少使用CV_64F,但是vs的編譯器又會把float數據自動變成double型,有些不太爽。

還有個需要注意的問題,就是流操作符<<對於Mat的操作,僅限於Mat是2維的情況。

還有必要說一下Mat的存儲是逐行的存儲的。

再說說Mat的創建,方式有兩種,羅列一下:1.調用create(行,列,類型)2.Mat(行,列,類型(值))。例如:

  1. // make a 7x7 complex matrix filled with 1+3j.  
  2. Mat M(7,7,CV_32FC2,Scalar(1,3));  
  3. // and now turn M to a 100x60 15-channel 8-bit matrix.  
  4. // The old content will be deallocated  
  5. M.create(100,60,CV_8UC(15));  

要是想創建更高維的矩陣,要寫成下面的方式

  1. // create a 100x100x100 8-bit array  
  2. int sz[] = {100, 100, 100};  
  3. Mat bigCube(3, sz, CV_8U, Scalar::all(0));  

對於矩陣的行操作或者列操作,方式如下:(注意對列操作時要新建一個Mat,我想應該跟列地址不連續有關
  1. // add the 5-th row, multiplied by 3 to the 3rd row  
  2. M.row(3) = M.row(3) + M.row(5)*3;  
  3. // now copy the 7-th column to the 1-st column  
  4. // M.col(1) = M.col(7); // this will not work  
  5. Mat M1 = M.col(1);  
  6. M.col(7).copyTo(M1);  

下面的東西就比較狂暴了,對於外來的數據,比如你從別的地方接受了一幅圖片,但可以不是Mat結構的,而只有一個數據的指針,看看接下來的代碼是如何應付的,重點哦,親

  1. void process_video_frame(const unsigned char* pixels,  
  2. int width, int height, int step)  
  3. {  
  4. Mat img(height, width, CV_8UC3, pixels, step);  
  5. GaussianBlur(img, img, Size(7,7), 1.5, 1.5);  
  6. }  

親,有木有很簡單!!!

還有一種快速初始化數據的辦法,如下:

  1. double m[3][3] = {{a, b, c}, {d, e, f}, {g, h, i}};  
  2. Mat M = Mat(3, 3, CV_64F, m).inv();  

也可以把原來的IplImage格式的圖片直接用Mat(IplImage)的方式轉成Mat結構,也可以像Matlab一樣調用zeros()、ones()、eye()這樣的函數進行初始化。

如果你需要提前釋放數據的指針和內存,可以調用release()。

對於數據的獲取,當然還是調用at<float>(3, 3)這樣的格式爲最佳。其他的方法我甚少嘗試,就不敢介紹了。

最後要提的一點是關於Mat的表達式,這個也非常多,加減乘除,轉置求逆,我怎麼記得我以前介紹過呢。那就不多說啦~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章