1. 程式人生 > 其它 >C++版本OpenCv教程(二)Mat類構造與賦值

C++版本OpenCv教程(二)Mat類構造與賦值

技術標籤:OpenCV

目錄

Mat類的構造

1.利用預設建構函式

預設建構函式使用方式
cv::Mat::Mat();

通過程式碼清單2-4,利用預設建構函式構造了一個Mat類,這種構造方式不需要輸入任何的引數,在後續給變數賦值的時候會自動判斷矩陣的型別與大小,實現靈活的儲存,常用於儲存讀取的影象資料和某個函式運算輸出結果。

2.根據輸入矩陣尺寸和型別構造

利用矩陣尺寸和型別引數構造Mat類
cv::Mat::Mat(
int rows,int cols,int type)
  • rows:構造矩陣的行數
  • cols:矩陣的列數
  • type:矩陣中儲存的資料型別。此處除了CV_8UC1、CV_64FC4等從1到4通道以外,還提供了更多通道的引數,通過CV_8UC(n)中的n來構建多通道矩陣,其中n最大可以取到512.

這種構造方法我們前文也見過,通過輸入矩陣的行、列以及儲存資料型別實現構造。這種定義方式清晰、直觀、易於閱讀,常用在明確需要儲存資料尺寸和資料型別的情況下,例如相機的內參矩陣、物體的旋轉矩陣等。利用輸入矩陣尺寸和資料型別構造Mat類的方法存在一種變形,通過將行和列組成一個Size()結構進行賦值,程式碼清單2-6中給出了這種構造方法的原型。

用Size()結構構造Mat類
cv::Mat::Mat(Size size(),int  type)
  • size:2D陣列變數尺寸,通過Size(cols, rows)進行賦值。
  • type:與程式碼清單2-5中的引數一致

利用這種方式構造Mat類時要格外注意,在Size()結構裡矩陣的行和列的順序與程式碼清單2-5中的方法相反,使用Size()時,列在前、行在後。如果不注意同樣會構造成功Mat類,但是當我們需要檢視某個元素時,我們並不知道行與列顛倒,就會出現陣列越界的錯誤。使用該種方法建構函式如下:

用Size()結構構造Mat示例
cv::Mat a(Size(480, 640), CV_8UC1)
; //構造一個行為640,列為480的單通道矩陣 cv::Mat b(Size(480, 640), CV_32FC3); //構造一個行為640,列為480的3通道矩

3.利用已有矩陣構造

利用已有矩陣構造Mat類
cv::Mat::Mat( const Mat & m);
m:已經構建完成的Mat類矩陣資料。

這種構造方式非常簡單,可以構造出與已有的Mat類變數儲存內容一樣的變數。注意這種構造方式只是複製了Mat類的矩陣頭,矩陣指標指向的是同一個地址,因此如果通過某一個Mat類變數修改了矩陣中的資料,另一個變數中的資料也會發生改變。
【注】如果想複製兩個一模一樣的Mat類而彼此之間不會受影響,可以使用m=a.clone()實現。
如果需要構造的矩陣尺寸比已有矩陣小,並且儲存的是已有矩陣的子內容,那麼可以用程式碼清單2-9中的方法進行構建:

構造已有Mat類的子類

cv::Mat::Mat(const Mat & m, const Range & rowRange,const Range & colRange = Range::all())
  • m:已經構建完成的Mat類矩陣資料。
  • rowRange:在已有矩陣中需要擷取的行數範圍,是一個Range變數,例如從第2行到第5行可以表示為Range(2,5)。
  • colRange:在已有矩陣中需要擷取的列數範圍,是一個Range變數,例如從第2列到第5列可以表示為Range(2,5),當不輸入任何值時表示所有列都會被擷取。

這種方式主要用於在原圖中截圖使用,不過需要注意的是,通過這種方式構造的Mat類與已有Mat類享有共同的資料,即如果兩個Mat類中有一個數據發生更改,另一個也會隨之更改。

構造已有Mat類的子類

cv::Mat::Mat(const Mat & m,const Range & rowRange, const Range & colRange = Range::all())

Mat類的賦值

構建完成Mat類後,變數裡並沒有資料,需要將資料賦值給它。針對不同情況,OpenCV 4.1提供了多種賦值方式,接下來將介紹如何給Mat類變數進行賦值。

1.構造時賦值

在構造時賦值的方法

cv::Mat::Mat(int  rows,int  cols,int  type,const Scalar & s)
  • rows:矩陣的行數
  • cols:矩陣的列數
  • type:儲存資料的型別
  • s:給矩陣中每個畫素賦值的引數變數,例如Scalar(0, 0, 255)。

該種方式是在構造的同時進行賦值,將每個元素想要賦予的值放入Scalar結構中即可,這裡需要注意的是,用此方法會將影象中的每個元素賦值相同的數值,例如Scalar(0, 0, 255)會將每個畫素的三個通道值分別賦值0,0,255。我們可以使用如下的形式構造一個已賦值的Mat類

在構造時賦值示例
cv::Mat a(2, 2, CV_8UC3, cv::Scalar(0,0,255));//建立一個3通道矩陣,每個畫素都是0,0,255
cv::Mat b(2, 2, CV_8UC2, cv::Scalar(0,255));//建立一個2通道矩陣,每個畫素都是0,255
cv::Mat c(2, 2, CV_8UC1, cv::Scalar(255)); //建立一個單通道矩陣,每個畫素都是255

我們在程式return語句之前加上斷點進行除錯,用Image Watch檢視每一個Mat類變數裡的資料,結果如圖2-3所示,證明我們已成功構造矩陣並賦值。
使用Scalar結構給Mat類賦值結果

Scalar結構中變數的個數一定要與定義中的通道數相對應,如果Scalar結構中變數個數大於通道數,則位置大於通道數之後的數值將不會被讀取,例如執行a(2, 2, CV_8UC2, Scalar(0,0,255))後,每個畫素值都將是(0,0),而255不會被讀取。如果Scalar結構中變數數小於通道數,則會以0補充。

2.列舉賦值法

這種賦值方式是將矩陣中所有的元素都一一枚舉出,並用資料流的形式賦值給Mat類。具體賦值形式如程式碼清單2-13所示。

利用列舉法賦值示例
cv::Mat a = (cv::Mat_<int>(3, 3) << 1, 2, 3, 4, 5, 6, 7, 8, 9);
cv::Mat b = (cv::Mat_<double>(2, 3) << 1.0, 2.1, 3.2, 4.0, 5.1, 6.2);

上面第一行程式碼建立了一個3×3的矩陣,矩陣中存放的是從1-9的九個整數,先將矩陣中的第一行存滿,之後再存入第二行、第三行,即1、2、3存放在矩陣a的第一行,4、5、6存放在矩陣a的第二行,7,8,9存放在矩陣a的第三行。第二行程式碼建立了一個2×3的矩陣,其存放方式與矩陣a相同。
採用列舉法時,輸入的資料個數一定要與矩陣元素個數相同,例如程式碼清單2-13中第一行程式碼只輸入從1到8八個數,賦值過程會出現報錯,因此本方法常用在矩陣資料比較少的情況。

3.迴圈賦值

與通過列舉法賦值方法相類似,迴圈法賦值也是對矩陣中的每一位元素進行賦值,但是可以不在宣告變數的時候進行賦值,而且可以對矩陣中的任意部分進行賦值。具體賦值形式如程式碼清單2-14所示。

利用列舉法賦值示例
cv::Mat c = cv::Mat_<int>(3, 3); //定義一個3*3的矩陣
for (int i = 0; i < c.rows; i++) //矩陣行數迴圈
{
	for (int j = 0; j < c.cols; j++) //矩陣列數迴圈
	{
		c.at<int>(i, j) = i+j;
	}
}

上面程式碼同樣建立了一個3×3的矩陣,通過for迴圈的方式,對矩陣中的每一位元素進行賦值。需要注意的是,在給矩陣每個元素進行賦值的時候,賦值函式中宣告的變數型別要與矩陣定義時的變數型別相同,即上面程式碼中第1行和第6行中變數型別要相同,如果第6行程式碼改成c.at(i, j) ,程式就會報錯,無法賦值。

4.類方法賦值

在Mat類裡提供了可以快速賦值的方法,可以初始化指定的矩陣。例如生成單位矩陣、對角矩陣、所有元素都為0或者1的矩陣等。

利用類方法賦值示例
cv::Mat a = cv::Mat::eye(3, 3, CV_8UC1);
cv::Mat b = (cv::Mat_<int>(1, 3) << 1, 2, 3);
cv::Mat c = cv::Mat::diag(b);
cv::Mat d = cv::Mat::ones(3, 3, CV_8UC1);
cv::Mat e = cv::Mat::zeros(4, 2, CV_8UC3);

上面程式碼中,每個函式作用及引數含義分別如下:

  • eye():構建一個單位矩陣,前兩個引數為矩陣的行數和列數,第三個引數為矩陣存放的資料型別與通道數。如果行和列不相等,則在矩陣的 (1,1),(2,2),(3,3)等主對角位置處為1。
  • diag():構建對角矩陣,其引數必須是Mat型別的1維變數,用來存放對角元素的數值。
  • ones():構建一個全為1的矩陣,引數含義與eye()相同。
  • zeros():構建一個全為0的矩陣,引數含義與eye()相同。

5.利用陣列進行賦值

這種方法與列舉法相類似,但是該方法可以根據需求改變Mat類矩陣的通道數,可以看作列舉法的拓展,在程式碼清單2-16中給出了這種方法的賦值形式。

利用陣列賦值示例
float a[8] = { 5,6,7,8,1,2,3,4 };
cv::Mat b = cv::Mat(2, 2, CV_32FC2, a);
cv::Mat c = cv::Mat(2, 4, CV_32FC1, a);

這種賦值方式首先將需要存入到Mat類中的變數存入到一個數組中,之後通過設定Mat類矩陣的尺寸和通道數將陣列變數拆分成矩陣,這種拆分方式可以自由定義矩陣的通道數,當矩陣中的元素數目大於陣列中的資料時,將用-1.0737418e+08填充賦值給矩陣,如果矩陣中元素的數目小於陣列中的資料時,將矩陣賦值完成後,陣列中剩餘資料將不再賦值。由陣列賦值給矩陣的過程是首先將矩陣中第一個元素的所有通道依次賦值,之後再賦值下一個元素,為了更好的體會這個過程,我們將定義的b和c矩陣在圖2-4中給出。
矩陣b和c中儲存的資料