天天看點

OpenCV基礎---圖像存儲器(Mat類)一 圖像存儲容器

一 圖像存儲容器

        學習圖像處理首先要學會如何操作矩陣資訊,在OpenCV中提供了一個Mat類用于存儲矩陣資料。

       Mat類用來儲存矩陣類型的資料資訊,包括向量,矩陣,灰階或彩色圖像等資料。Mat類分為矩陣頭和指向存儲資料的矩陣指針兩部分。其中矩陣頭中包含矩陣的尺寸、存儲方法、位址和引用次數。矩陣頭的大小是一個常數,不會随着矩陣尺寸的大小二改變。在絕大多數情況下,矩陣頭大小遠小于矩陣中資料量的大小,是以圖像複制和傳遞過程中的主要開銷是存放矩陣資料。

1.建立Mat類

cv::Mat a;          //建立一個名為a的矩陣頭
a = cv::imread("test.jpg")   //向a中指派圖像資料,矩陣指針指向像素資料
cv::Mat b = a   //複制矩陣頭
           

        上述代碼,首先建立了名為a的矩陣頭,之後讀入一張圖像并将中的矩陣指針指向該圖的像素資料,最後将a矩陣頭中的内容複制到b矩陣頭中。雖然a、b有各自的矩陣頭,但是矩陣指針所指向的是同一矩陣資料,是以通過任意矩陣頭修改矩陣中的資料時,另一個矩陣頭指向的資料也會發生改變。但是當删除變量a時,b變量并不會指向一個空資料,隻用當兩個變量同時删除時,才會釋放矩陣資料。因為矩陣頭中引用次數标記了引用某個矩陣的次數,隻有當矩陣的引用次數為0的時候才會釋放矩陣資料。

1.1聲明一個指定類型的Mat類

cv::Mat A = Mat_<double>(3,3);   //建立一個3*3的矩陣用于存放double資料類型
           

        Mat可以存儲的資料類型包括double,float,uchar,unsigned char,以及自定義模闆。

1.2通過OpenCV資料類型建立Mat類

OpenCV中的資料類型

資料類型 具體類型 取值範圍
CV_8U 8位無符号整數 0~255
CV_8S 8位符号整數 -128~127
CV_16U 16位無符号整數 0~65535
CV_16S 16位符号整數 -32768~32767
CV_32S 32位符号整數 -2147483~2147483647
CV_32F 32位浮點小數
CV_64F 64位浮點小數
cv::Mat a(640,480,CV_8UC3)    //建立一個640*480 的三通道矩陣用于存放彩色圖像
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,CV_8U),就會提示建立錯誤。

2.Mat類構造與指派

2.1 Mat類的構造

(1)利用預設構造函數

cv::Mat::Mat();
           

        這種構造方式不需要輸入任何的參數,在後續給變量指派的時候會自動判斷矩陣的類型大小,實作靈活的存儲,常用于存儲讀取的圖像資料和某個函數的運算輸出結果。

(2)根據輸入矩陣尺寸和類型構造

cv::Mat::Mat( int rows,
            int cols,
            int type,
            )
//rows:構造矩陣的行數
//cols:構造矩陣的列數
//type:矩陣中存儲的資料類型
           

(3)用Size()結構構造Mat類

cv::Mat::Mat(Size size(),
            int type
            )
//size: 二維數組變量尺寸,通過Size(cols,rows)來進行指派,注意列在前,行在後
//type:略
           

(4)利用已有矩陣構造Mat執行個體

cv::Mat()::Mat(const Mat &m)
//m:以建構完成的Mat類矩陣變量
           

        希望複制兩個一模一樣的Mat類而批次之間不會影響,那麼可以使用m=a.clone()

(5)建立已有Mat類的子類

cv::Mat::Mat(const Mat &m,
            const Range &rowRange,
            const Range &colRange = Range::all()
            )
//m: 已經建構完成的Mat類矩陣資料
//rowRange:在以有矩陣中需要截取的行數範圍,是一個Range變量,例如Range(2,5)表示從第二行,    //到第五行
//colRange:截取列的範圍

//這種方式主要用于在原圖中截圖使用。不過需要注意的是,通過這種方式構造的Mat類與已有Mat類享有共同的資料。
           

2.Mat類的指派

(1)在構造時指派

cv::Mat::Mat(int rows,
            int cols,
            int type,
            const Scalar &s
            )
//rows:略
//cols:略
//type:略
//s: 給矩陣中沒個像素指派的參數變量

//執行個體: 
cv::Mat::Mat(2, 2, CV_8UC3, CV::Scalar(0,0,255))
           

(2)枚舉法指派

cv::Mat::Mat(cv::Mat_<int>(3,3)<<1, 2, 3, 4, 5, 6, 7, 8, 9);
           

(3)利用循環指派

cv::Mat c = cv::Mat_(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;
    {
}
           

(4)利用類方法指派

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),等主對角線出為1
//diag:建立對角矩陣,其參數必須是Mat類型的一維變量,用來存放對角元素的數值
//ones:建立一個全為1的矩陣
//zeros:建立一個全為0的矩陣
           

(5)利用數組進行指派

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);

//當矩陣中的元素數目大于數組中的資料時,将用-1.0737418e+08填充給矩陣;當矩陣中元素的數目小于數組中的資料時,将矩陣指派完成後,數組中剩餘資料将不再指派。
           

3.Mat類支援的運算

        Mat支援加減乘除的運算,在對圖像進行卷積運算時,需要兩個矩陣進行乘法運算,OpenCV提供了兩個Mat類矩陣的乘法運算,而且定義了兩個矩陣的内積和對應位的乘法運算。

cv::Mat() j,m;
double k;
j = c*d;
k = a.dot(b);
m = a.mul(b);
           

 4.Mat類元素的讀取

Mat類矩陣常用的屬性

屬性 作用
cols 矩陣的列數
rows 矩陣的行數
step 以位元組為機關的矩陣有效寬度
elemSize() 每個元素的位元組數
total() 矩陣中元素的個數
channels() 矩陣的通道數

(1)通過at方法讀取Mat類矩陣中的元素

//讀取單通道矩陣元素
cv::Mat a = (cv::Mat_<uchar>(3, 3) <<1, 2, 3, 4, 5, 6, 7, 8, 9);
int value = (int)a.at<uchar>(0, 0);

//讀取多通道矩陣元素
cv::Mat b(3, 4, CV_8UC3, CV::Scalar(0, 0, 1));
cv::Vec3b vc3 = b.at<cv::Vec3b>(0, 0);
int first = (int)vc3.val[0];
int second = (int)vc3.val[1];
int third = (int)vc3.val[2];

//在使用多通道變量類型是,同樣需要注意at方法中資料變量類型與矩陣的變量類型相對應,
//并且cv::Vec3b類型在輸入每個通道資料是需要将其變量類型強制轉化成int類型。
           

        針對三通道矩陣,定義了cv::Vec3b,cv::Vec3s,cv::Vec3w,cv::Vec3d,cv::Vec3f,cv::Vec3i。b是uchar類型的縮寫,s是short類型的縮寫,w是ushort類型的縮寫,d是double類型的縮寫,f是float類型的縮寫, i是int類型的縮寫。

(2)通過指針ptr讀取Mat類矩陣中的元素

        Mat類矩陣中每一行中的每個元素都是挨着存放的,是以給出了通道指針ptr讀取Mat類矩陣元素。

cv::Mat b(3, 4, CV_8UC3, cv::Scalar(0, 0, 1));
for (int i = 0; i < b.rows; i++)
{
    uchar* ptr = b.ptr<uchar>(i);
    for(int j = 0; j < b.cols*b.channels(); j++)
    {
        cout << (int)ptr[j] <<endl;
    }
}
           

(3)通過疊代器通路Mat類矩陣中的元素

cv::MatIterator_<uchar> it = a.begin<uchar>();
cv::MatIterator_<uchar> it_end = a.end<uchar>();
for(int i = 0; it ! = it_end; it++)
{
    cout << (int)(*it) <<"";
    if((++i%a.cols) == 0)
    {
        cout << endl;
    }

} 
           

        Mat類的疊代器變量類型是cv::MatIterator_<>,在定義時同樣需要在尖括号中聲明資料的變量類型。Mat類疊代器的起始是Mat.begin<>(),借宿是Mat.end<>(),與其他疊代器用法相同,通過“++”運算實作指針位置向下疊代,資料的讀取方式是先讀取第一個元素的每一個通道,之後再讀取第二個元素的每一個通道,直到最後一個元素的最後一個通道。

(4)通過矩陣元素的位址定位方式通路元素

(int)(*(b.data + b.step[0] * row + b.step[1] * col + channel));