OpenCV數據結構之Mat

在講解Mat之前,先來介紹一些基礎知識。

陣列的數據類型

陣列的數據類型定義了爲陣列的每個元素(圖片中的像素)分配的比特數以及如何使用這些比特數表示元素的值。任何陣列的元素都應該有下面數據類型的一種:

單通道陣列

  • CV_8U (8 bit 無符號整數)
  • CV_8S (8 bit 有符號整數)
  • CV_16U(16 bit 無符號整數)
  • CV_16S (16 bit 有符號整數)
  • CV_32S (32 bit 有符號整數)
  • CV_32F (32 bit 浮點數)
  • CV_64F (64 bit 浮點數)

舉例來說:下圖展示了一個使用8 bit無符號整數的單通道陣列。因爲數據類型是8 bit無符號整數,因此這個陣列的每個元素爲0-255的值。

single channel arrays

多通道陣列

我們可以爲多通道陣列定義上面的所有的數據類型(最多支持512個通道)。這裏只演示爲多通道定義CV_8U數據類型:

  • CV_8UC1 (單通道陣列,8 bit 無符號整數)
  • CV_8UC2 (2通道陣列,8 bit 無符號整數)
  • CV_8UC3 (3通道陣列,8 bit 無符號整數)
  • CV_8UC4 (4通道陣列,8 bit 無符號整數)
  • CV_8UC(n) (n通道陣列,8 bit 無符號整數 (n 可以從 1 到 512) )

例1:下圖展示了一個使用8 bit 無符號整數的3通道陣列。因爲數據類型是8 bit無符號整數,因此這個陣列的每個元素爲0-255的值。由於是3通道陣列,所以陣列由帶有3個元素的元組組成,第一個元組是{54, 0, 34},第二個元組是 {58, 78, 185} ,以此類推。multi channel arrays例2:下圖展示了一個使用8 bit有符號整數的2通道陣列。因爲數據類型是8 bit有符號整數,故陣列的每個元素值從-128到127。因爲這是一個2通道陣列,所以陣列由有2個元素的元組組成。第一個元組是{-85,-127},第二個是{25,23},以此類推。multi channel arrays_2注意: CV_8U = CV_8UC1 = CV_8UC(1)

使用示例

  • Mat img1(3, 5, CV_32F ); //寬3x高5 使用32 bit浮點數的單通道陣列
  • Mat img2(23, 53, CV_64FC(5) ); //23 x 53 使用64 bit浮點數的5通道陣列
  • Mat img3(Size(100, 200), CV_16UC2 ); //100 x 200 使用16 bit無符號整數的2通道陣列

記住:一些OpenCV函數不支持上面全部的數據類型,所以在使用時注意一些。

IplImage的位深(C style)

  • IPL_DEPTH_<bit_depth>(S|U|F)
    • <bit_depth> 可能的值爲 1,8,16,32 and 64
    • S = Signed
    • U = Unsigned
    • F = Float
    • 位深爲1的圖片應該爲unsigned
    • 位深爲8的圖片應該爲signed或unsigned
    • 位深爲16的圖片應該爲signed或unsigned
    • 位深爲32位的圖片應該爲signed或float
    • 位深爲64的圖片應該爲float
  • 例子:
    • IPL_DEPTH_1U (位深爲1,unsigned)
    • IPL_DEPTH_8U (位深爲8,unsigned)
    • IPL_DEPTH_16U
    • IPL_DEPTH_32F (位深爲32,float)
    • IPL_DEPTH_8S
    • IPL_DEPTH_16S (位深爲16,signed)
    • IPL_DEPTH_32S
    • IPL_DEPTH_64F

位深表示爲每個像素分配的比特數。比如,使用IPL_DEPTH_8U的IplImage每個像素使用8 bit無符號整數,這表示每個像素的值是從0-255的整數。 當前IplImage數據結構支持IPL_DEPTH_8U, IPL_DEPTH_8S, IPL_DEPTH_16S,  IPL_DEPTH_32S, IPL_DEPTH_32F and IPL_DEPTH_64F。

Mat類

Mat是基本的圖像容器,它由兩個數據部分組成:矩陣頭(包含矩陣尺寸,存儲方法,存儲地址等信息)和一個指向存儲所有像素值的矩陣(根據所選存儲方法的不同矩陣可以是不同的維數)的指針。Mat使用了引用技術機制,這樣在拷貝Mat對象的時候,只拷貝信息頭,所有的對象都共享一個矩陣。

1
2
3
4
5
6
Mat A, C;                                 // 只創建信息頭部分
A = imread(argv[1], CV_LOAD_IMAGE_COLOR); // 這裏爲矩陣開闢內存
 
Mat B(A);                                 // 使用拷貝構造函數
 
C = A;                                    // 賦值運算符

以上代碼中的所有Mat對象最終都指向同一個也是唯一一個數據矩陣。雖然它們的信息頭不同,但通過任何一個對象所做的改變也會影響其它對象。實際上,不同的對象只是訪問相同數據的不同途徑而已。這裏還要提及一個比較棒的功能:你可以創建只引用部分數據的信息頭。比如想要創建一個感興趣區域( ROI ),你只需要創建包含邊界信息的信息頭:

1
2
Mat D (A, Rect(10, 10, 100, 100) ); // using a rectangle
Mat E = A(Range:all(), Range(1,3)); // using row and column boundaries

如果真的需要拷貝矩陣(不只是信息頭和矩陣指針),可以使用函數 clone() 或者 copyTo()

1
2
3
Mat F = A.clone();
Mat G;
A.copyTo(G);

現在改變 F 或者 G 就不會影響 Mat 信息頭所指向的矩陣。 Mat 不但是一個很讚的圖像容器類,它同時也是一個通用的矩陣類,所以可以用來創建和操作多維矩陣。創建一個Mat對象有多種方法:

Mat() 構造函數

1
2
Mat M(2, 2, CV_8UC3, Scalar(0, 0, 255));
cout << "M = " << endl << " " << M << endl << endl;

20140325220024對於二維多通道圖像,首先要定義其尺寸,即行數和列數。CV_8UC3在前面已經詳細解釋了,這裏不再贅述。Scalar是個short型vector。指定這個能夠使用指定的定製化值來初始化矩陣。

在 C\C++ 中通過構造函數進行初始化

1
2
int sz[3] = { 2, 2, 2 };
Mat L(3, sz, CV_8UC(1), Scalar::all(0));

上面的例子演示瞭如何創建一個超過兩維的矩陣:指定維數,然後傳遞一個指向一個數組的指針,這個數組包含每個維度的尺寸;其餘的相同

爲已存在IplImage指針創建信息頭

1
2
IplImage* img = cvLoadImage("greatwave.png", 1);
Mat mtx(img); // convert IplImage* -> Mat

Create() 函數

1
2
3
Mat M;
M.create(4, 4, CV_8UC(2));
cout << "M = " << endl << " " << M << endl << endl;

20140325222254

這個創建方法不能爲矩陣設初值,它只是在改變尺寸時重新爲矩陣數據開闢內存。

MATLAB形式的初始化方式: zeros(), ones(), :eyes()

使用以下方式指定尺寸和數據類型:

1
2
3
4
5
6
7
8
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;

20140325222535

對於小矩陣你可以用逗號分隔的初始化函數

1
2
Mat C = (Mat_<double>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
cout << "C = " << endl << " " << C << endl << endl;

20140325222821

使用 clone() 或者 copyTo() 爲一個存在的 Mat 對象創建一個新的信息頭

1
2
3
Mat C(3, 1, CV_8UC1, Scalar(1));
Mat RowClone = C.row(1).clone();
cout << "RowClone = " << endl << " " << RowClone << endl << endl;

總結

本篇文章介紹了陣列的數據類型和位深的內容,講解了Mat類的一些基礎知識以及創建Mat對象的一些方法。

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