opencv学习笔记(七)-CvMat矩阵结构以及矩阵数据访问

通道和维度

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
首先说一下对矩阵维度和通道的理解:
维:体现为坐标。
通道:
对于这样一个数组矩阵:

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

单通道显示的话,就是这样:
opencv学习笔记(七)-CvMat矩阵结构以及矩阵数据访问_第1张图片
所以,单通道:可以理解为:一个元素中含有1个数字。

双通道就是把数组中每两个数(如30和60)结合在一起,如:
opencv学习笔记(七)-CvMat矩阵结构以及矩阵数据访问_第2张图片
双通道可以
所以,双通道:可以理解为:一个元素中含有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;
}

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

实例解释:
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;
}
//二通道矩阵数据读取
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;
}

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

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;

此类信息通常被称作为矩阵头。很多程序是区分矩阵头和数据体的,后者是各个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
       );

初始化一个矩阵,但是不分配空间:

CvMat cvMat
(
    int rows,
    int cols,
    int type,
    void* data = NULL
);

用一个矩阵初始化另外一个矩阵,也就是将一个矩阵的复制到另外一个矩阵上去:
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;
}

利用宏写矩阵的值:

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维的数组,下面列举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
  );

实例:

//指针访问矩阵结构

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系列函数返回的是

建立自己的指针访问(恰当的方法):

#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;
}

你可能感兴趣的:(opencv)