學習mat矩陣,了解影象的儲存和mat矩陣的使用
2023年opencv剛出來的時候,是基於c語言介面而建。為了在記憶體(memory)中存放影象,當時採用名為 iplimage 的c語言結構體。其中最大的不足要數手動記憶體管理,使用者要為開闢和銷毀記憶體負責。一旦**開始變得越來越龐大,會越來越多地糾纏於這個問題。
c++出現了,並帶來類的概念,利用自動記憶體管理給出了解決問題的新方法。使用這個方法,不需要糾結在管理記憶體上,而且你的**會變得簡潔(少寫多得)。但c++介面唯一的不足是當前許多嵌入式開發系統只支援c語言。
mat 是乙個類,由兩個資料部分組成:矩陣頭(包含矩陣尺寸,儲存方法,儲存位址等資訊)和乙個指向儲存所有畫素值的矩陣(根據所選儲存方法的不同矩陣可以是不同的維數)的指標。矩陣頭的尺寸是常數值,但矩陣本身的尺寸會依影象的不同而不同,通常比矩陣頭的尺寸大多個數量級。因此,當在程式中傳遞影象並建立拷貝時,大的開銷是由矩陣造成的,而不是資訊頭。opencv是乙個影象處理庫,囊括了大量的影象處理函式,為了解決問題通常要使用庫中的多個函式,因此在函式中傳遞影象是家常便飯。同時不要忘了我們正在討論的是計算量很大的影象處理演算法,因此,除非萬不得已,我們不應該拷貝大的影象,因為這會降低程式速度。
為了搞定這個問題,opencv使用引用計數機制。其思路是讓每個 mat 物件有自己的資訊頭,但共享同乙個矩陣。這通過讓矩陣指標指向同一位址而實現。而拷貝建構函式則 只拷貝資訊頭和矩陣指標 ,而不拷貝矩陣。
mat a, c; // 只建立資訊頭部分
a = imread(argv[1], cv_load_image_color); // 這裡為矩陣開闢記憶體
mat b(a); // 使用拷貝建構函式
c = a; // 賦值運算子
以上**中的所有mat物件最終都指向同乙個也是唯一乙個資料矩陣。雖然它們的資訊頭不同,但通過任何乙個物件所做的改變也會影響其它物件。實際上,不同的物件只是訪問相同資料的不同途徑而已。這裡還要提及乙個比較棒的功能:你可以建立只引用部分資料的資訊頭。比如想要建立乙個感興趣區域( roi ),你只需要建立包含邊界資訊的資訊頭:
mat d (a, rect(10, 10, 100, 100) ); // using a rectangle
mat e = a(range:all(), range(1,3)); // using row and column boundaries
如果矩陣屬於多個 mat 物件,那麼當不再需要它時誰來負責清理?簡單的回答是:最後乙個使用它的物件。通過引用計數機制來實現。無論什麼時候有人拷貝了乙個 mat 物件的資訊頭,都會增加矩陣的引用次數;反之當乙個頭被釋放之後,這個計數被減一;當計數值為零,矩陣會被清理。但某些時候你仍會想拷貝矩陣本身(不只是資訊頭和矩陣指標),這時可以使用函式 clone() 或者 copyto() 。
mat f = a.clone();
mat g;
a.copyto(g);
現在改變 f 或者 g 就不會影響 mat 資訊頭所指向的矩陣。總結一下,你需要記住的是
這裡講述如何儲存畫素值。需要指定顏色空間和資料型別。
最簡單的顏色空間要屬灰度級空間,只處理黑色和白色,對它們進行組合可以產生不同程度的灰色。
對於彩色方式則有更多種類的顏色空間,但不論哪種方式都是把顏色分成三個或者四個基元素,通過組合基元素可以產生所有的顏色。rgb顏色空間是最常用的一種顏色空間,這歸功於它也是人眼內部構成顏色的方式。它的基色是紅色、綠色和藍色,有時為了表示透明顏色也會加入第四個元素 alpha (a)。
有很多的顏色系統,各有自身優勢:
每個組成元素都有其自己的定義域,取決於資料型別。如何儲存乙個元素決定了我們在其定義域上能夠控制的精度。最小的資料型別是char ,佔乙個位元組或者8位,可以是無符號型(0到255之間)或有符號型(-127到+127之間)。儘管使用三個 char 型元素已經可以表示1600萬種可能的顏色(使用rgb顏色空間),但若使用float(4位元組,32位)或double(8位元組,64位)則能給出更加精細的顏色分辨能力。但同時也要切記增加元素的尺寸也會增加了影象所佔的記憶體空間。
mat 不但是乙個很讚的影象容器類,它同時也是乙個通用的矩陣類,所以可以用來建立和操作多維矩陣。建立乙個mat物件有多種方法:
mat() 建構函式
mat m(2,2, cv_8uc3, scalar(0,0,255));
cout << "m = " << endl << " " << m << endl << endl;
比如 cv_8uc3 表示使用8位的 unsigned char 型,每個畫素由三個元素組成三通道。預先定義的通道數可以多達四個。 scalar 是個short型vector。指定這個能夠使用指定的定製化值來初始化矩陣。當然,如果你需要更多通道數,你可以使用大寫的巨集並把通道數放在小括號中,如下所示
int sz[3] = ;
mat l(3,sz, cv_8uc(1), scalar::all(0));
上面的例子演示了如何建立乙個超過兩維的矩陣:指定維數,然後傳遞乙個指向乙個陣列的指標,這個陣列包含每個維度的尺寸;其餘的相同
為已存在iplimage指標建立資訊頭:
create() 函式
m.create(4,4, cv_8uc(2));
cout << "m = "<< endl << " " << m << endl << endl;
這個建立方法不能為矩陣設初值,它只是在改變尺寸時重新為矩陣資料開闢記憶體。
zeros(), ones(), :eyes() 。使用以下方式指定尺寸和資料型別:
mat e = mat::eye(4, 4, cv_64f);
cout << "e = " << endl << " " << e << endl << endl;
mat o = mat::ones(2, 2, cv_32f);
cout << "o = " << endl << " " << o << endl << endl;
mat z = mat::zeros(3,3, cv_8uc1);
cout << "z = " << endl << " " << z << endl << endl;
OpenCV基礎類容器Mat類詳解
我記得開始接觸opencv就是因為乙個演算法裡面需要2維動態陣列,那時候看core這部分也算是走馬觀花吧,隨著使用的增多,對mat這個結構越來越喜愛,也覺得有必要溫故而知新,於是這次再看看mat。mat最大的優勢跟stl很相似,都是對記憶體進行動態的管理,不需要之前使用者手動的管理記憶體,對於一些大...
opencv之Mat類初始化
早期的 opencv 中,使用 iplimage 和 cvmat 資料結構來表示影象。iplimage 和 cvmat 都是 c 語言的結構。使用這兩個結構的問題是記憶體需要手動管理,開發者必須清楚的知道何時需要申請記憶體,何時需要釋放記憶體。這個開發者帶來了一定的負擔,開發者應該將更多精力用於演算...
opencv中的Mat詳解
1.初始化 mat the basic image container 最最基本的函式,所有在opencv中都是用mat形式來操作的。初始化乙個mat mat a,c a imread ar 1 imread color mat b a 複製a給b c a 賦值運算 mat d a,rect 10,...