全文部分引自:http://blog.csdn.net/xiaowei_cqu/article/details/7771760
Mat的常見屬性
data uchar型的指針。Mat類分爲了兩個部分:矩陣頭和 指向矩陣部分的指針,data就是指向矩陣數據的指針
dims 矩陣的維度,例如5*6矩陣是二維矩陣,則dims=2,三維矩陣dims=3
rows 矩陣的行數
cols 矩陣的列數
size 矩陣的大小,size(cols,rows),如果矩陣的維數大於2,則是size(-1,-1)
channels 矩陣元素擁有的通道數,例如常見的彩色圖像,每一個像素由RGB三部分組成,則channels=3
和Mat相關的幾個數據類型
type
表示了矩陣中元素的類型以及矩陣的通道個數,它是一系列的預定義的變量,其命名規則爲CV_(位數)+(數據類型)+(通道數)。具體的有以下值:
這裏U(unsigned integer)表示的是無符號整數,S(signed integer),F(float)是浮點數。
例如:CV_16UC2,表示的是元素類型是一個16位的無符號整數,通道爲2. C1,C2,C3,C4則表示通道是1,2,3,4
type一般是在創建Mat對象時設定,如果要取得Mat的元素類型,則無需使用type,使用下面的depth
depth
矩陣中元素的一個通道的數據類型,這個值和type是相關的。例如 type爲 CV_16SC2,一個2通道的16位的有符號整數。那麼,depth則是CV_16S。depth也是一系列的預定義值,
將type的預定義值去掉通道信息就是depth值:
CV_8U CV_8S CV_16U CV_16S CV_32S CV_32F CV_64F
elemSize
矩陣一個元素佔用的字節數,例如:type是CV_16SC3,那麼elemSize = 3 * 16 / 8 = 6 bytes
elemSize1
矩陣元素一個通道佔用的字節數,例如:type是CV_16CS3,那麼elemSize1 = 16 / 8 = 2 bytes = elemSize / channels
訪問圖像中的像素
高效的方法:C操作符【】
最快的是直接用C風格的內存訪問操作符【】來訪問:
Mat& ScanImageAndReduceC(Mat& I, const uchar* const table)
{
// accept only char type matrices
CV_Assert(I.depth() != sizeof(uchar));
int channels = I.channels();
int nRows = I.rows ;
int nCols = I.cols* channels;
if (I.isContinuous())
{
nCols *= nRows;
nRows = 1;
}
int i,j;
uchar* p;
for( i = 0; i < nRows; ++i)
{
p = I.ptr<uchar>(i);
for ( j = 0; j < nCols; ++j)
{
p[j] = table[p[j]];
}
}
return I;
}
一般情況 isContinous爲true,運行不會出錯,但你可以註釋掉那個if,會有訪問越界的問題。
這種訪問形式就是在每行定義一個指針,然後在內存上直接連續訪問。如果整個數組在內存上都是連續存放的,那麼只需要定義一個指針就可以訪問所有的數據!如單通道的灰度圖訪問方式如下:
uchar* p = I.data;
for( unsigned int i =0; i < ncol*nrows; ++i)
*p++ = table[*p];
安全的方法:迭代器iterator
相比用指針直接訪問可能出現越界問題,迭代器絕對是非常安全的方法:
Mat& ScanImageAndReduceIterator(Mat& I, const uchar* const table)
{
// accept only char type matrices
CV_Assert(I.depth() != sizeof(uchar));
const int channels = I.channels();
switch(channels)
{
case 1:
{
MatIterator_<uchar> it, end;
for( it = I.begin<uchar>(), end = I.end<uchar>(); it != end; ++it)
*it = table[*it];
break;
}
case 3:
{
MatIterator_<Vec3b> it, end;
for( it = I.begin<Vec3b>(), end = I.end<Vec3b>(); it != end; ++it)
{
(*it)[0] = table[(*it)[0]];
(*it)[1] = table[(*it)[1]];
(*it)[2] = table[(*it)[2]];
}
}
}
return I;
}
這裏我們只定義了一個迭代器,用了一個for循環,這是因爲在OpenCV裏迭代器會訪問每一列然後自動跳到下一行,不用管在內存上是否isContinous。另外要注意的是在三通道圖像中我們定義的是 格式的迭代器,如果定義成uchar,則只能訪問到B即藍色通道的值。
這種方式雖然安全,但是挺慢的,一會兒就知道了。
更慢的方法:動態地址計算
這種方法在需要連續掃描所有點的應用時並不推薦,因爲它更實用與隨機訪問。這種方法最基本的用途是訪問任意的某一行某一列:
Mat& ScanImageAndReduceRandomAccess(Mat& I, const uchar* const table)
{
// accept only char type matrices
CV_Assert(I.depth() != sizeof(uchar));
const int channels = I.channels();
switch(channels)
{
case 1:
{
for( int i = 0; i < I.rows; ++i)
for( int j = 0; j < I.cols; ++j )
I.at<uchar>(i,j) = table[I.at<uchar>(i,j)];
break;
}
case 3:
{
Mat_<Vec3b> _I = I;
for( int i = 0; i < I.rows; ++i)
for( int j = 0; j < I.cols; ++j )
{
_I(i,j)[0] = table[_I(i,j)[0]];
_I(i,j)[1] = table[_I(i,j)[1]];
_I(i,j)[2] = table[_I(i,j)[2]];
}
I = _I;
break;
}
}
return I;
}
因爲這種方法是爲隨機訪問設計的,所以真的是奇慢無比。。。