我記得開始接觸OpenCV就是因爲一個算法裏面需要2維動態數組,那時候看core這部分也算是走馬觀花吧,隨着使用的增多,對Mat這個結構越來越喜愛,也覺得有必要溫故而知新,於是這次再看看Mat。
Mat最大的優勢跟STL很相似,都是對內存進行動態的管理,不需要之前用戶手動的管理內存,對於一些大型的開發,有時候投入的lpImage內存管理的時間甚至比關注算法實現的時間還要多,這顯然是不合適的。除了有些嵌入式場合必須使用c語言,我任何時候都強烈像大家推薦Mat。
Mat這個類有兩部分數據。一個是matrix header,這部分的大小是固定的,包含矩陣的大小,存儲的方式,矩陣存儲的地址等等。另一個部分是一個指向矩陣包含像素值的指針。
- Mat A, C; // creates just the header parts
- A = imread(argv[1], CV_LOAD_IMAGE_COLOR); // here we’ll know the method used (allocate matrix)
- Mat B(A); // Use the copy constructor
- 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(行,列,類型(值))。例如:
- // make a 7x7 complex matrix filled with 1+3j.
- Mat M(7,7,CV_32FC2,Scalar(1,3));
- // and now turn M to a 100x60 15-channel 8-bit matrix.
- // The old content will be deallocated
- M.create(100,60,CV_8UC(15));
要是想創建更高維的矩陣,要寫成下面的方式
- // create a 100x100x100 8-bit array
- int sz[] = {100, 100, 100};
- Mat bigCube(3, sz, CV_8U, Scalar::all(0));
對於矩陣的行操作或者列操作,方式如下:(注意對列操作時要新建一個Mat,我想應該跟列地址不連續有關)
- // add the 5-th row, multiplied by 3 to the 3rd row
- M.row(3) = M.row(3) + M.row(5)*3;
- // now copy the 7-th column to the 1-st column
- // M.col(1) = M.col(7); // this will not work
- Mat M1 = M.col(1);
- M.col(7).copyTo(M1);
下面的東西就比較狂暴了,對於外來的數據,比如你從別的地方接受了一幅圖片,但可以不是Mat結構的,而只有一個數據的指針,看看接下來的代碼是如何應付的,重點哦,親
- void process_video_frame(const unsigned char* pixels,
- int width, int height, int step)
- {
- Mat img(height, width, CV_8UC3, pixels, step);
- GaussianBlur(img, img, Size(7,7), 1.5, 1.5);
- }
親,有木有很簡單!!!
還有一種快速初始化數據的辦法,如下:
- double m[3][3] = {{a, b, c}, {d, e, f}, {g, h, i}};
- Mat M = Mat(3, 3, CV_64F, m).inv();
也可以把原來的IplImage格式的圖片直接用Mat(IplImage)的方式轉成Mat結構,也可以像Matlab一樣調用zeros()、ones()、eye()這樣的函數進行初始化。
如果你需要提前釋放數據的指針和內存,可以調用release()。
對於數據的獲取,當然還是調用at<float>(3, 3)這樣的格式爲最佳。其他的方法我甚少嘗試,就不敢介紹了。
最後要提的一點是關於Mat的表達式,這個也非常多,加減乘除,轉置求逆,我怎麼記得我以前介紹過呢。那就不多說啦~