【從零學習OpenCV 4】Mat類介紹
- 2019 年 10 月 28 日
- 筆記
經過幾個月的努力,小白終於完成了市面上第一本OpenCV 4入門書籍《從零學習OpenCV 4》。為了更讓小夥伴更早的了解最新版的OpenCV 4,小白與出版社溝通,提前在公眾號上連載部分內容,請持續關注小白。
其實在最早的OpenCV 1.0版本中,影像使用名為IplImage的C語言結構體進行存儲的,所以在很多比較老的OpenCV版本教程中常會看到其身影。但是使用IplImage類型存在需要用戶手動釋放記憶體的缺點,如果程式結束後存在沒有釋放記憶體的IplImage變數,就會造成記憶體泄漏的問題。值得慶幸的是,隨著OpenCV版本的更新,OpenCV引入C++介面,提供Mat類用於存儲數據,利用自動記憶體管理技術很好的解決了記憶體自動釋放的問題,當變數不再需要時立即釋放記憶體。
Mat類用來保存矩陣類型的數據資訊,包括向量、矩陣、灰度或彩色影像等數據。Mat類分為矩陣頭和指向存儲數據的矩陣指針兩部分。矩陣頭中包含矩陣的尺寸、存儲方法、地址和引用次數等。矩陣頭的大小是一個常數,不會隨著矩陣尺寸大小而改變。在絕大多數情況下矩陣頭大小遠小於矩陣中數據量的大小,因此影像複製和傳遞過程中主要的開銷是存放矩陣數據。為了解決這個問題,在OpenCV中複製和傳遞影像時,只是複製了矩陣頭和指向存儲數據的指針,因此在創建Mat類時可以先創建矩陣頭後賦值數據,其方法如程式碼清單2-1所示。
程式碼清單2-1 創建Mat類 cv::Mat a; //創建一個名為a的矩陣頭 a = cv::imread(「test.jpg」); //向a中賦值影像數據,矩陣指針指向像素數據 cv::Mat b=a; //複製矩陣頭,並命名為b
上面這段程式碼首先創建了一個名為a的矩陣頭,之後讀入一張影像並將a中的矩陣指針指向該影像的像素數據,最後將a矩陣頭中的內容複製到b矩陣頭中。雖然a、b有各自的矩陣頭,但是其矩陣指針指向的是同一個矩陣數據,通過任意一個矩陣頭修改矩陣中的數據,另一個矩陣頭指向的數據也會跟著發生改變。但是當刪除a變數時,b變數並不會指向一個空數據,只有當兩個變數都刪除後,才會釋放矩陣數據。因為矩陣頭中引用次數標記了引用某個矩陣數據的次數,只有當矩陣數據引用次數為0的時候才會釋放矩陣數據。
提示
採用引用次數來釋放存儲內容是C++中常見的方式,用這種方式可以避免仍有某個變數引用數據時將這個數據刪除造成程式崩潰的問題,同時極大的縮減了程式運行時所佔用的記憶體。
接下來我們來了解Mat類里可以存儲的數據類型,根據官方給出的Mat類繼承圖,如圖2-2所示,我們發現Mat類可以存儲的數據類型包含double、float、uchar、unsigned char以及自定義的模板等。

圖2-2 Mat類繼承關係圖
我們可以通過程式碼清單2-2的方式聲明一個存放指定類型的Mat類變數:
程式碼清單2-2 聲明一個指定類型的Mat類 cv::Mat A = Mat_<double>(3,3);//創建一個3*3的矩陣用於存放double類型數據
由於OpenCV提出Mat類主要用於存儲影像,而像素值的最大值又決定了影像的品質,如果用8位無符號整數去存儲16點陣圖像,會造成嚴重的影像顏色失真或造成數據錯誤。而由於不同位數的編譯器對數據長度定義不同,為了避免在不同環境下因變數位數長度不同而造成程式執行問題,OpenCV根據數值變數存儲位數長度定義了數據類型,表2-1中列出了OpenCV中的數據類型與取值範圍。
表2-1 OpenCV中的數據類型與取值範圍
數據類型 |
具體類型 |
取值範圍 |
---|---|---|
CV_8U |
8位無符號整數 |
0—255 |
CV_8S |
8位符號整數 |
-128—127 |
CV_16U |
16位無符號整數 |
0-65535 |
CV_16S |
16位符號整數 |
-32768—32767 |
CV_32S |
32位符號整數 |
-2147483648—2147483647 |
CV_32F |
32位浮點整數 |
-FLT_MAX—FLT_MAX, INF, NAN |
CV_64F |
64位浮點整數 |
-DBL_MAX—DBL_MAX, INF, NAN |
僅有數據類型是不夠的,還需要定義影像數據的通道(Channel)數,例如灰度影像數據是單通道數據,彩色影像數據是3通道或者4通道數據。因此針對這個情況,OpenCV還定義了通道數標識,C1、C2、C3、C4分別表示單通道、雙通道、3通道和4通道。每一種數據類型都存在多個通道的情況,所以將數據類型與通道數表示結合便得到了OpenCV中對影像數據類型的完整定義,例如CV_8UC1表示的就是8位單通道數據,用於表示8位灰度圖,而CV_8UC3表示的是8位3通道數據,用於表示8位彩色圖。我們可以通過程式碼清單2-3的方式創建一個聲明通道數和數據類型的Mat類:
程式碼清單2-3 通過OpenCV數據類型創建Mat類 cv::Mat a(640,480,CV_8UC3) //創建一個640*480的3通道矩陣用於存放彩色影像 cv::Mat a(3,3,CV_8UC1) //創建一個3*3的8位無符號整數的單通道矩陣 cv::Mat a(3,3,CV_8U) //創建單通道矩陣C1標識可以省略
注意
雖然在64位編輯器里,uchar和CV_8U都表示8位無符號整數,但是兩者有嚴格的定義,CV_8U只能用在Mat類內部的方法。例如用Mat_<CV_8U>(3,3)和Mat a(3,3,uchar)會提示創建錯誤。