【OpenCV:從零到一】03:Mat對象的創建與使用

前言
這是我《OpenCV:從零到一》專欄的第三篇博客,想看跟多請戳
本文概要
void copyTo(Mat mat) Mat clone()
void convertTo(Mat dst, int type)
bool empty();
uchar* ptr(i=0)
create() Scalar();
zaros() ones()
案例代碼
大概內容:各種方式創建Mat以及Mat的使用。

#include <opencv2/core/core.hpp> 
#include <opencv2/imgcodecs.hpp> 
#include <opencv2/highgui/highgui.hpp>
#include<opencv2/highgui/highgui_c.h>
#include <iostream>

using namespace cv;
using std::cout;//引入cout和endl
using std::endl;

int main(int argc, char** args) {
	Mat image = imread("D:\\86186\\Documents\\opencv\\lena.jpg", IMREAD_GRAYSCALE);//讀進來的時候變成灰色
	if (image.empty()) {//Mat.empty() Returns true if the array has no elements.
		//The method returns true if Mat::total() is 0 or if Mat::data is NULL.
		cout << "could not find the image resource..." << endl;
		return -1;
	}
	namedWindow("My Image", CV_WINDOW_AUTOSIZE);
	imshow("My Image", image);

	Mat M;
	M.create(4, 3, CV_8UC2);//創建一個四行三列的矩陣,一個通道8bit無符號雙通道
	//CV_是前綴,8是bit數,U是類型(還有F,S上一篇有講過)而C就是通道的意思,後面的數字自然就代表通道數了
	
	M = Scalar(127, 127);//default constructor 用於給通道賦值,從上面知道M是雙通道的,scalar給每個通道都賦值爲127
	cout << "M = " << endl << " " << M << endl;//這裏輸出可以看見是四行六列的,可以驗證我們上一篇提到的模型
	uchar* firstRow = M.ptr<uchar>(0);//ptr重載多達20個,但是都是兩到三個參數,都是行列之類,返回值是指針,具體使用具體分析
	printf("%d\n", *firstRow);

	//Mat構造函數
	Mat C = (Mat_<double>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
	cout << "C = " << endl << " " << C << endl;
	//將 m 賦值給新創建的對象,此處不會對圖像數據進行復制,m 和新對象共用圖像數據(注意和後面copyTo和clone的區別)
	Mat D = Mat::Mat(C);//D複製C的頭

	Mat mat1 = Mat::ones(1, 5, CV_32F);   // [1,1,1,1,1]
	Mat mat2 = mat1;   // mat2與mat1指向同一內存地址
	Mat mat3 = Mat::zeros(1, 5, CV_32F);  // [0,0,0,0,0]

	mat1 = mat3.clone();   // mat1被重新分配內存,通過mat1不能改變mat2的內容
	cout << mat1 << endl;   // [0,0,0,0,0]
	cout << mat2 << endl;   // [1,1,1,1,1]

	mat3.copyTo(mat1); // mat1未被重新分配內存,通過mat1可以改變mat2的內容
	cout << mat1 << endl;   // [0,0,0,0,0] 
	cout << mat2 << endl;   // [0,0,0,0,0]
	//mat可以直接用cout輸出

	Mat dst;//destination 終點,目的地
	image.convertTo(dst, CV_8U);

	//zeros,創建全爲0的矩陣,ones和zeros如出一轍,只不過ones爲全爲1的矩陣,zeros有三個重載
	Mat A = Mat::zeros(3, 3, CV_32F);//(rows,cols,type)
	int a[] = { 3,4 };//const int* sz
	Mat B = Mat::zeros(2, a, CV_32F);//(int ndims,const int* sz ,int type)
	Mat E = Mat::zeros(Size(100, 70), CV_32F);//(Size size,int type)
	waitKey(0);
	return 0;
}

解析及注意事項

  • Mat對象創建方式有create和Mat構造函數,注意兩者區別
  • Mat對象的複製方式有clone、copyTo和Mat構造函數,注意三者區別(構造函數比較懶)
  • create被很多需要輸出矩陣的OpenCV函數調用
  • 矩陣類型就是上一篇提到的深度類型加上個通道,這些類型本質上來說都是int,但是爲了方便使用都進行了宏定義,把鼠標指向宏名可以看到原始值
  • 創建矩陣和複製矩陣無非就只用知道兩件事:1,要用多少空間 2,是矩陣什麼類型 各種重載都是幾十幾十的來,看似繁雜,其實就是圍繞以上兩個問題來。其中花樣比較多的是第一個問題。
  • Scalar是一個可以爲通道賦值的基礎結構體,而Size是參數形式和他差不多,但是範圍有很大區別,Size定義的是像素數量,圖片的長寬,每個像素有一一個對應的Scalar
  • zeros和ones是兩個很像的函數,也是用來創造Mat,但是他們每個像素的內容是確定的,即他們的Scalar是確定。
  • convertTo用於變換圖像的深度,也就是灰度範圍

全註釋代碼

#include <opencv2/core/core.hpp> 
#include <opencv2/imgcodecs.hpp> 
#include <opencv2/highgui/highgui.hpp>
#include<opencv2/highgui/highgui_c.h>
#include <iostream>

using namespace cv;
using std::cout;//引入cout和endl
using std::endl;

int main(int argc, char** args) {
	Mat image = imread("D:\\86186\\Documents\\opencv\\lena.jpg", IMREAD_GRAYSCALE);//讀進來的時候變成灰色
	if (image.empty()) {//Mat.empty() Returns true if the array has no elements.
		//The method returns true if Mat::total() is 0 or if Mat::data is NULL.
		cout << "could not find the image resource..." << endl;
		return -1;
	}
	namedWindow("My Image", CV_WINDOW_AUTOSIZE);
	imshow("My Image", image);

	Mat M;
	M.create(4, 3, CV_8UC2);//創建一個四行三列的矩陣,一個通道8bit無符號雙通道
	//CV_是前綴,8是bit數,U是類型(還有F,S上一篇有講過)而C就是通道的意思,後面的數字自然就代表通道數了
	/*
	Allocates new array data if needed.分配空間給數組
	Most new-style OpenCV functions and methods that produce arrays call this method for each output array.
	多數新式需要輸出的函數內部會調用create.也就是說:
	Mat color;
	...
	//Mat gray(color.rows, color.cols, color.depth());
	Mat gray;//上面一樣語句可以省略後面的堆參數,因爲cvtColor會幫你把color和gray規格一樣,即調用了create語句
	cvtColor(color, gray, COLOR_BGR2GRAY);
	參數
	int rows	New number of rows.
	int cols	New number of columns.
	int type	New matrix type.//爲了記憶方便一般用宏代替而不是直接寫數字
	create有三個重載,他們的描述都是This is an overloaded member function, provided for convenience. 
	It differs from the above function only in what argument(s) it accepts.也就是說他們只有參數不同
	三個重載type都是必須的而(rows + cols)可以替換爲一下幾組:
	Size 或 sizes 或 ndims  + sizes
	Size   size重載很多的類型,常見的是這樣的 Size(100,70) 注意和下面的區別
	const int * sizes	Array of integers specifying a new array shape.
	int ndims New array dimensionality.
	*/
	M = Scalar(127, 127);//default constructor 用於給通道賦值,從上面知道M是雙通道的,scalar給每個通道都賦值爲127
	/*
	typedef struct Scalar
	{
	    double val[4];
	}Scalar;
	上面是scalar原型,最大長度是四,不填默認0
	*/
	cout << "M = " << endl << " " << M << endl;//這裏輸出可以看見是四行六列的,可以驗證我們上一篇提到的模型
	uchar* firstRow = M.ptr<uchar>(0);//ptr重載多達20個,但是都是兩到三個參數,都是行列之類,返回值是指針,具體使用具體分析
	printf("%d\n", *firstRow);

	//Mat構造函數的29個重載(爺吐了)
	/*
	其中create有的參數形式Mat都有,下面列舉幾種其他常見Mat構造函數
	Mat::Mat(int rows, int cols, int type, void* data, size_t step=AUTO_STEP)
	創建行數爲 rows,列數爲 col,類型爲 type 的圖像,此構造函數不創建圖像數據所需內存,而是直接使用 data 所指內存,圖像的行步長由 step指定
	Mat::Mat(Size size, int type, void* data, size_t step=AUTO_STEP)
	創建大小爲 size,類型爲 type 的圖像,此構造函數不創建圖像數據所需內存,而是直接使用 data 所指內存,圖像的行步長由 step 指定
	Mat::Mat(const Mat& m, const Range& rowRange, const Range& colRange)
	創建的新圖像爲 m 的一部分,具體的範圍由 rowRange 和 colRange 指定,此構造函數也不進行圖像數據的複製操作,新圖像與 m 共用圖像數據
	Mat::Mat(const Mat& m, const Rect& roi)
	創建的新圖像爲 m 的一部分,具體的範圍 roi 指定,此構造函數也不進行圖像數據的複製操作,新圖像與 m 共用圖像數據
	*/
	Mat C = (Mat_<double>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
	cout << "C = " << endl << " " << C << endl;
	//將 m 賦值給新創建的對象,此處不會對圖像數據進行復制,m 和新對象共用圖像數據(注意和後面copyTo和clone的區別)
	Mat D=Mat::Mat(C);//D複製C的頭

	/*                                                copyTo和clone的區別
	Mat.copyTo(dst,[])
	The major difference is that when the destination matrix and the source matrix have the same type and size, 
	copyTo will not change the address of the destination matrix, while clone will always allocate a new address for the destination matrix.
	後面一個參數可以表示複製的部分,如src.copyTo( dst, detected_edges); 是將src中detected_edges矩陣對應的非零部分(即邊緣檢測結果)複製到dst中。
	Mat.clone();無參返回值爲Mat類型
	Creates a full copy of the array and the underlying data.
	The method creates a full copy of the array. The original step[] is not taken into account. 
	So, the array copy is a continuous array occupying total()*elemSize() bytes.
	*/
	Mat mat1 = Mat::ones(1, 5, CV_32F);   // [1,1,1,1,1]
	Mat mat2 = mat1;   // mat2與mat1指向同一內存地址
	Mat mat3 = Mat::zeros(1, 5, CV_32F);  // [0,0,0,0,0]

	mat1 = mat3.clone();   // mat1被重新分配內存,通過mat1不能改變mat2的內容
	cout << mat1 << endl;   // [0,0,0,0,0]
	cout << mat2 << endl;   // [1,1,1,1,1]

	mat3.copyTo(mat1); // mat1未被重新分配內存,通過mat1可以改變mat2的內容
	cout << mat1 << endl;   // [0,0,0,0,0] 
	cout << mat2 << endl;   // [0,0,0,0,0]
	//mat可以直接用cout輸出
	
	//convertTo
	/*
	convertTo(dst,rtype,scale,shift)用於圖片灰度範圍的縮放,後面兩個參數都是可選的
	dst(i)=src(i)xscale+shift
	Converts an array to another data type with optional scaling.The method converts source pixel values to the target data type. 
	saturate_cast<> is applied at the end to avoid possible overflows
	這個比例因子不是放大縮小的因子,實際上範圍的意思,比如你原來的圖是255的灰度圖,你現在轉成float型數據,並且歸一化到0-1,那麼就是scale=1.0/255,如果還是想0-255.0 那麼就直接1
	rtype:desired output matrix type or, rather, the depth since the number of channels are the same as the input has; 
	期望輸出矩陣的類型,確切的來說是深度(depth()),因爲輸入輸入通道數是一樣的
	if rtype is negative, the output matrix will have the same type as the input.如果值爲負則保持一致
	*/
	Mat dst;//destination 終點,目的地
	image.convertTo(dst, CV_8U);

	//zeros,創建全爲0的矩陣,ones和zeros如出一轍,只不過ones爲全爲1的矩陣,zeros有三個重載
	Mat A = Mat::zeros(3, 3, CV_32F);//(rows,cols,type)
	int a[] = { 3,4};//const int* sz
	Mat B = Mat::zeros(2, a, CV_32F);//(int ndims,const int* sz ,int type)
	Mat B = Mat::zeros(Size(100, 70), CV_32F);//(Size size,int type)
	waitKey(0);
	return 0;
}

翻譯筆記
destination n. 目的地,終點 =dst
dimensionality n. 維度;幅員;廣延 =dim
convert vt. 使轉變;轉換 =cvt
scalar adj. 標量的;數量的;梯狀的,分等級的
n. [數] 標量;[數] 數量
scale n. 規模;比例;

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