前言:Mat是OpenCV的最基本的類型,他有很多常見的屬性和方法,可以獲取這張圖片的基本信息,幫助我們更好地理解圖片,本文做了一個簡單的小結,並說明了一些常見的易錯點。
一、Mat對象常見的屬性以及方法一覽
cout << image.cols << endl; //相片的列數,一共有多少列,對應width
cout << image.rows << endl; //相片的行數,一共有多少行,對應height
cout << image.dims << endl; //相片的維度,這裏都是二維
cout << image.type() << endl; //16,其實是CV_8U3,即8位無符號三通道
cout << image.total() << endl; //156500 總共有多少個元素,即rows*cols
cout << image.channels() << endl; //相片的通道數,可能是1、3、4
//幾個需要特別注意的方法,在下面一個一個說明
cout << image.step << endl; //1500
cout << image.step1() << endl; //1500
cout << image.elemSize() << endl; //3
cout << image.elemSize1() << endl; //1
cout << typeid(image.data).name() << endl;
1、type()方法
該方法返回的是一個int類型的整數,表示的是每一個像素(i,j)的數據類型,常見的比如:
- CV_8U:8位單通道無符號,即灰度圖像,返回的是0
- CV_8UC3:8位三通道無符號,即常見的RGB圖像,返回的是16
- CV_32FC3:32位浮點數三通道,返回的是21
- 等等
2、elemSize1()方法
每一個像素位置(i,j)處單個通道所佔用的字節數,是以字節byte爲單位的。
- CV_8U:8位單通道無符號,即灰度圖像,只有一個通道,它爲1byte
- CV_8UC3:8位三通道無符號,即常見的RGB圖像,每一個通道是8bit,依然爲1byte
- CV_32FC3:32位浮點數三通道,每一個通道是32位,所以爲4byte
- 等等
3、elemSize()方法
每一個像素位置(i,j)處所有通道所佔用的字節數,是以字節byte爲單位的。
- CV_8U:8位單通道無符號,即灰度圖像,只有一個通道,它爲1byte
- CV_8UC3:8位三通道無符號,即常見的RGB圖像,每一個通道是8bit,有三個通道,所以爲3byte
- CV_32FC3:32位浮點數三通道,每一個通道是32位,即4byte,一共有三個通道所以是 12byte
- 等等
注意:elemSize() 和elemSize1()的區別。
4、step屬性
這個屬性表示的是圖片每一行的字節數,一字節byte爲單位,
- 單通道8位灰度圖:由於每一個像素只有一個通道,且爲8位,即1字節,所以 step=1*cols;
- 三通道8位RGB圖:由於每一個像素包含三個通道,每一個通道爲1字節,所以一個像素佔3字節,所以step=3*cols;
- 對於三通道32位浮點數:即上面的CV_32FC3,每一個像素有三個通道,每一個通道佔用32位即4字節,所以每一個像素佔用3*4字節,所以step=3*4*cols。
5、step1()方法
這個是最容易出錯的,step1()=step/elemSize1()
二、Mat類的元素高效遍歷方法
Mat類的元素便利有很多的方法,但是油的方法比較慢,這裏提供一種高校的元素遍歷方法,即通過image.data指針來實現。
2.1 data指針到底是什麼意思
需要特別注意的是,
- data指針指向的是Mat的首元素的指針,即位置(0,0)處的指針,它是將每一個像素點當成一個一維數組,然後指向這個數組的首元素,如果是單通道,則這個一位數組只有一個元素,如果是三通道,則這個一維數組是三個元素;
- 而且無論圖像是什麼類型,他總是返回的是unsigned char * 指針類型,即uchar類型,所以需要注意類型轉換。
(1)對於單通道灰度圖
//8位單通道,每個像素僅僅佔用 1 byte
for (int i = 0; i < rows; i++)
{
uchar * pixel = a.data + i * a.step; //將指針移動到每一行的開始
for (int j = 0; j < 5; j++)
{
cout << pixel[0] << endl; //單通道只有一個元素
pixel+=1; //將指針移動到下一列
}
cout << endl;
}
(2)對於三通道RGB圖像
//8位單通道,每個像素僅僅佔用 1 byte
for (int i = 0; i < rows; i++)
{
uchar * pixel = a.data + i * a.step; //將指針移動到每一行的開始
for (int j = 0; j < 5; j++)
{
cout << pixel[0] << endl; //三通道第一個元素
cout << pixel[1] << endl; //三通道第二個元素
cout << pixel[2] << endl; //三通道第三個元素
pixel+=3; //將指針移動到下一列的首元素
}
cout << endl;
}
(3)對於三通道32位浮點數,即CV_32FC3
//8位單通道,每個像素僅僅佔用 1 byte
for (int i = 0; i < rows; i++)
{
float * pixel = (float *)a.data + i * a.step/4; //將指針移動到每一行的開始需要轉換,爲什麼需要除以4,一定要弄清楚它的本質
for (int j = 0; j < 5; j++)
{
cout << pixel[0] << endl; //三通道第一個元素
cout << pixel[1] << endl; //三通道第二個元素
cout << pixel[2] << endl; //三通道第三個元素
pixel+=3; //將指針移動到下一列的首元素
}
cout << endl;
}
兩個需要注意的點:
(1)第一:什麼時候需要除以一個數,這取決於每一個像素的每一個通道佔用幾個字節
float * pixel = (float *)a.data + i * a.step/4;
(2)第二:什麼時候加1,什麼時候加3,這取決於相片的通道數目
pixel+=3;