C++學習筆記——Mat類詳解及元素的遍歷方法

全文部分引自: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;  
}  

因爲這種方法是爲隨機訪問設計的,所以真的是奇慢無比。。。

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