opencv學習_16(CvMat矩陣結構以及矩陣數據訪問)

通道和維度

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
首先說一下對矩陣維度和通道的理解: 
維:體現爲座標。 
通道: 
對於這樣一個數組矩陣:

float data[18] =
    {
        30, 60, 40, 60, 50, 40,
        67, 88, 55, 33, 22, 97,
        59, 69, 32, 46, 25, 45
    };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

單通道顯示的話,就是這樣: 
這裏寫圖片描述 
所以,單通道:可以理解爲:一個元素中含有1個數字。

雙通道就是把數組中每兩個數(如30和60)結合在一起,如: 
這裏寫圖片描述 
雙通道可以 
所以,雙通道:可以理解爲:一個元素中含有2個數字。

以此類推:三通道可以;理解爲一個元素中有3分數字。

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
打印雙通道矩陣實例:

#include "highgui.h"
#include "cv.h"

int main()
{
    float data[18] =
    {
        30, 60, 40, 60, 50, 40,
        67, 88, 55, 33, 22, 97,
        59, 69, 32, 46, 25, 45
    };

    CvMat mat;
    cvInitMatHeader(&mat, 3, 3, CV_32FC2, data);
    for (int y = 0; y < mat.rows; y++)
    {
        for (int x = 0; x < mat.cols; x++)
        {
            //CvScalar value = cvGetRealND(&mat, y, x,z);三維
            CvScalar value = cvGet2D(&mat, y, x);

            printf("(%f %f)", value.val[0], value.val[1]);
        }
        printf("\n");
    }

    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

實例解釋: 
cvInitMatHeader()函數:用來初始化Mat結構體; 
cvInitMatHeader( CvMat* arr, int rows, int cols,int type, void* data, int step ) 
參數解釋: 
arr:CvMat結構體; 
rows: 行數; 
cols:列數; 
type: 矩陣元素類型(可以是32位浮點型(CV_32FC1)或者是無符號的8位三元組的整形數據(CV_8UC3)或者其他類型); 
這個也是改通道數的參數:如單通道(CV_32FC1),雙通道(CV_32FC2),三通道(CV_32FC3); 
data:矩陣數組; 
step: 步長,默認是: 一行數據所佔的字節數(int min_step = arr->cols*pix_size)。

cvGet2D()函數:對多通道二維矩陣訪問。(多通道三維:cvGet3D(),多通道多維:cvGetND()等一系列:cvGet*D函數);

cvGetReal1D():對單通道一維矩陣訪問,(單通道三維:cvGetReal3D(),單通道多維:cvGetRealND()等一系列:cvGetReal*D函數);*

這種訪問方式比較慢因爲涉及到壓棧和出棧,如果矩陣較大的話耗費的時間也很多。 
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

下面介紹一種更有效率的訪問方式,用指針訪問矩陣元素:

//單通道形式訪問矩陣元素
int main()
{
    float data[18] =
    {
        30, 60, 40, 60, 50, 40,
        67, 88, 55, 33, 22, 97,
        59, 69, 32, 46, 25, 45
    };
    CvMat mat;
    cvInitMatHeader(&mat, 3, 6, CV_32FC1, data);
    int y = 2, x = 3;
    for (y = 0; y < mat.rows; y++)
    {
        float* p_float = (float*)(mat.data.ptr + y*mat.step);
        //mat.data.ptr:矩陣數組第一個數字。
        //mat.step:步長(矩陣一行數字所佔的字節數)。
        for (x = 0; x < mat.cols; x++)
        {
            float value = *(p_float + x);
            printf("(%f)", value);
        }
        printf("\n");
    }

    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
//二通道矩陣數據讀取
int main()
{
    float data[18] =
    {
        30, 60, 40, 60, 50, 40,
        67, 88, 55, 33, 22, 97,
        59, 69, 32, 46, 25, 45
    };
    CvMat mat;
    cvInitMatHeader(&mat, 3, 3, CV_32FC2, data);
    int y = 2, x = 3;
    int nchannel = 2;
    for (y = 0; y < mat.rows; y++)
    {
        float* p_float = (float*)(mat.data.ptr + y*mat.step);
        for (x = 0; x < mat.cols; x++)
        {
            float value[2];
            value[0] = *(p_float + x*nchannel);
            value[1] = *(p_float + x*nchannel + 1);
            //這裏加1是加一個float型的步長。
            printf("(%f %f)", value[0],value[1]);
        }
        printf("\n");
    }

    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

CvMat矩陣結構

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

typedef struct CvMat
{
    int type;
    int step;

    /* for internal use only */
    int* refcount;
    int hdr_refcount;

    union
    {
        uchar* ptr;
        short* s;
        int* i;
        float* fl;
        double* db;
    } data;

#ifdef __cplusplus
    union
    {
        int rows;
        int height;
    };

    union
    {
        int cols;
        int width;
    };
#else
    int rows;
    int cols;
#endif

}
CvMat;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

此類信息通常被稱作爲矩陣頭。很多程序是區分矩陣頭和數據體的,後者是各個data成員所指向的內存位置。 
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
矩陣的創建和釋放:

創建一個新的矩陣,分配矩陣空間:: 
CvMat* cvCreateMat(int rows, int cols,int type);

例程: 
CvMat* M = cvCreateMat(4,4,CV_32FC1);

創建一個矩陣頭,,不分配空間: 
CvMat* cvCreateMatHeader(int rows,int cols,int type);

初始化已存在的CvMat結構體:

CvMat* cvInitMatHeader
        (
            CvMat *mat,
            int row,
            int clos,
            int type;
            voud *data = NULL;
            int step = CV_AUTOSTEP
       );
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

初始化一個矩陣,但是不分配空間:

CvMat cvMat
(
    int rows,
    int cols,
    int type,
    void* data = NULL
);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

用一個矩陣初始化另外一個矩陣,也就是將一個矩陣的複製到另外一個矩陣上去: 
CvMat* cvCloneMat(const cvMat* mat);

例: 
CvMat* M1 = cvCreateMat(4,4,CV_32FC1); 
CvMat* M2; 
M2=cvCloneMat(M1);

釋放矩陣空間: 
例: 
CvMat* M = cvCreateMat(4,4,CV_32FC1); 
cvReleaseMat(&M); 
上面我們用介紹通道和維所用到的矩陣就是通過: 
用固定數據創建一個Opencv矩陣。

矩陣數據的存取

1,這是一個簡單的方法:利用宏CV_MAT_ELSEM(),這個宏(傳入矩陣,待提取的元素類型,行,列4個參數)(注意:行列是從0開始算的) 
利用宏讀取矩陣的值(簡單但慢):

int main()
{
    float data[18] =
    {
        30, 60, 40, 60, 50, 40,
        67, 88, 55, 33, 22, 97,
        59, 69, 32, 46, 25, 45
    };
    CvMat mat;

    cvInitMatHeader(&mat, 3, 6, CV_32FC1, data);

    float element_3_2 = CV_MAT_ELEM(mat, float, 0, 2);

    cout << element_3_2 << endl;

    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

利用宏寫矩陣的值:

int main()
{
    float data[18] =
    {
        30, 60, 40, 60, 50, 40,
        67, 88, 55, 33, 22, 97,
        59, 69, 32, 46, 25, 45
    };
    CvMat mat;

    cvInitMatHeader(&mat, 3, 6, CV_32FC1, data);
    float element = 7.7;

    *((float*)CV_MAT_ELEM_PTR(mat, 2, 4)) = element;

    float element_3_2 = CV_MAT_ELEM(mat, float, 2, 4);

    cout << element_3_2 << endl;

    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

但是這種使用宏的方法有個缺點,在每次調用宏的時候都要重新計算指針,即使宏使用起來容易,但是慢,所以不是存取矩陣的最好方法。

指針訪問矩陣結構(麻煩):

利用宏只能訪問1維和2維的數組,下面列舉cvPtr*D和cvGet*D等函數。 
cvPtr*D可以是:cvPtr1D(一維),cvPtr2D(二維),cvPtr3D(三維),cvPtrND(多維), 
cvGet*D也類似有這系列函數。

uchar* cvPtr1D(
    const CvArr* arr,           //訪問矩陣
    int          idx0,       //元素索引,也就是在數組中的座標
    int*            type = NULL//元素類型
  );
  uchar* cvPtr2D(
    const CvArr* arr,
    int          idx0,
    int          idx1,
    int*            type = NULL
  );
  uchar* cvPtr3D(
    const CvArr* arr,
    int          idx0,
    int          idx1,
    int          idx2,
    int*            type = NULL
  );
  uchar* cvPtrND(
    const CvArr* arr,
    int*            idx,
    int*            type                = NULL,
    int          create_node      = 1,
    unsigned*    precalc_hashval = NULL
  );
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

實例:

//指針訪問矩陣結構

int main()
{
    float vals[] = { 1, 2, 3, 4 };
    CvMat* rotmat = cvCreateMat(2, 2, CV_32FC1);
    cvInitMatHeader
        (
        rotmat,
        2,
        2,
        CV_32FC1,
        vals
        );
    float *p = (float*)cvPtr2D(rotmat, 1, 0);
    cout << *p << endl;
    return 0;
}

cvGet*D系列函數返回的是
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

建立自己的指針訪問(恰當的方法):

#include "highgui.h"
#include "cv.h"

int main()
{
    float data[18] =
    {
        30, 60, 40, 60, 50, 40,
        67, 88, 55, 33, 22, 97,
        59, 69, 32, 46, 25, 45
    };

    CvMat mat;
    cvInitMatHeader(&mat, 3, 3, CV_32FC2, data);
    for (int y = 0; y < mat.rows; y++)
    {
        for (int x = 0; x < mat.cols; x++)
        {
            //CvScalar value = cvGetRealND(&mat, y, x,z);三維
            CvScalar value = cvGet2D(&mat, y, x);

            printf("(%f %f)", value.val[0], value.val[1]);
        }
        printf("\n");
    }

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