OpenCV CvMat 初窥

小小研究了一下CvMat里面的宏,发现设计人员对位运算的使用 "别具匠心",值得我们学习!

研究结果:

/****************************************************************************************\
*                                  Matrix type (CvMat)                                   *
\****************************************************************************************/

#define CV_CN_MAX     512
#define CV_CN_SHIFT   3
#define CV_DEPTH_MAX  (1 << CV_CN_SHIFT)

#define CV_8U   0
#define CV_8S   1
#define CV_16U  2
#define CV_16S  3
#define CV_32S  4
#define CV_32F  5
#define CV_64F  6
#define CV_USRTYPE1 7

#define CV_MAT_DEPTH_MASK       (CV_DEPTH_MAX - 1)
#define CV_MAT_DEPTH(flags)     ((flags) & CV_MAT_DEPTH_MASK)

#define CV_MAKETYPE(depth,cn) (CV_MAT_DEPTH(depth) + (((cn)-1) << CV_CN_SHIFT))
#define CV_MAKE_TYPE CV_MAKETYPE

#define CV_8UC1 CV_MAKETYPE(CV_8U,1)
#define CV_8UC2 CV_MAKETYPE(CV_8U,2)
#define CV_8UC3 CV_MAKETYPE(CV_8U,3)
#define CV_8UC4 CV_MAKETYPE(CV_8U,4)
#define CV_8UC(n) CV_MAKETYPE(CV_8U,(n))

#define CV_8SC1 CV_MAKETYPE(CV_8S,1)
#define CV_8SC2 CV_MAKETYPE(CV_8S,2)
#define CV_8SC3 CV_MAKETYPE(CV_8S,3)
#define CV_8SC4 CV_MAKETYPE(CV_8S,4)
#define CV_8SC(n) CV_MAKETYPE(CV_8S,(n))

#define CV_16UC1 CV_MAKETYPE(CV_16U,1)
#define CV_16UC2 CV_MAKETYPE(CV_16U,2)
#define CV_16UC3 CV_MAKETYPE(CV_16U,3)
#define CV_16UC4 CV_MAKETYPE(CV_16U,4)
#define CV_16UC(n) CV_MAKETYPE(CV_16U,(n))

#define CV_16SC1 CV_MAKETYPE(CV_16S,1)
#define CV_16SC2 CV_MAKETYPE(CV_16S,2)
#define CV_16SC3 CV_MAKETYPE(CV_16S,3)
#define CV_16SC4 CV_MAKETYPE(CV_16S,4)
#define CV_16SC(n) CV_MAKETYPE(CV_16S,(n))

#define CV_32SC1 CV_MAKETYPE(CV_32S,1)
#define CV_32SC2 CV_MAKETYPE(CV_32S,2)
#define CV_32SC3 CV_MAKETYPE(CV_32S,3)
#define CV_32SC4 CV_MAKETYPE(CV_32S,4)
#define CV_32SC(n) CV_MAKETYPE(CV_32S,(n))

#define CV_32FC1 CV_MAKETYPE(CV_32F,1)
#define CV_32FC2 CV_MAKETYPE(CV_32F,2)
#define CV_32FC3 CV_MAKETYPE(CV_32F,3)
#define CV_32FC4 CV_MAKETYPE(CV_32F,4)
#define CV_32FC(n) CV_MAKETYPE(CV_32F,(n))

#define CV_64FC1 CV_MAKETYPE(CV_64F,1)
#define CV_64FC2 CV_MAKETYPE(CV_64F,2)
#define CV_64FC3 CV_MAKETYPE(CV_64F,3)
#define CV_64FC4 CV_MAKETYPE(CV_64F,4)
#define CV_64FC(n) CV_MAKETYPE(CV_64F,(n))

#define CV_AUTO_STEP  0x7fffffff
#define CV_WHOLE_ARR  cvSlice( 0, 0x3fffffff )

#define CV_MAT_CN_MASK          ((CV_CN_MAX - 1) << CV_CN_SHIFT)
#define CV_MAT_CN(flags)        ((((flags) & CV_MAT_CN_MASK) >> CV_CN_SHIFT) + 1)
#define CV_MAT_TYPE_MASK        (CV_DEPTH_MAX*CV_CN_MAX - 1)
#define CV_MAT_TYPE(flags)      ((flags) & CV_MAT_TYPE_MASK)
#define CV_MAT_CONT_FLAG_SHIFT  14
#define CV_MAT_CONT_FLAG        (1 << CV_MAT_CONT_FLAG_SHIFT)
#define CV_IS_MAT_CONT(flags)   ((flags) & CV_MAT_CONT_FLAG)
#define CV_IS_CONT_MAT          CV_IS_MAT_CONT
#define CV_SUBMAT_FLAG_SHIFT    15
#define CV_SUBMAT_FLAG          (1 << CV_SUBMAT_FLAG_SHIFT)
#define CV_IS_SUBMAT(flags)     ((flags) & CV_MAT_SUBMAT_FLAG)

#define CV_MAGIC_MASK       0xFFFF0000
#define CV_MAT_MAGIC_VAL    0x42420000
#define CV_TYPE_NAME_MAT    "opencv-matrix"

/************** 我的研究结果 *****************/

/*** 宏嵌套 ***/
#define C A
#define A B
#define B 1
/*** A=B=C=1 ***/


#define CV_CN_MAX     512   //最大通道
#define CV_CN_SHIFT   3    //偏移
#define CV_DEPTH_MAX  8    //最大深度8位(单个字节)

//DEPTH的取值类型,占3位
#define CV_8U   0   //unsigned char 
#define CV_8S   1   //char
#define CV_16U  2   //unsigned short
#define CV_16S  3   //short
#define CV_32S  4   //int
#define CV_32F  5   //float
#define CV_64F  6   //double
#define CV_USRTYPE1 7


#define CV_MAT_DEPTH_MASK       7 // 0000 0000 0111
#define CV_MAT_DEPTH(flags)     ((flags) & 7)

/*** depth + cn 编码 ***/
//depth占低3位,cn-1占高位
#define CV_MAKETYPE(depth,cn) ((depth & 7) + ((cn - 1) << 3))
#define CV_MAKE_TYPE CV_MAKETYPE

#define CV_MAT_CN_MASK          4088  // 1111 1111 1000

// (((flags) & CV_MAT_CN_MASK) >> CV_CN_SHIFT) ==>  0 <= flags < 4096时,等价于右移三位
// 后面可以发现 CV_MAT_CN_MASK = 1111 1111 1000, flags确实在这个范围里,所以其实也可以写成 ((flags) >> CV_CN_SHIFT)
#define CV_MAT_CN(flags)        ((((flags) & CV_MAT_CN_MASK) >> CV_CN_SHIFT) + 1)

#define CV_MAT_TYPE_MASK        4095  // 1111 1111 1111
#define CV_MAT_TYPE(flags)      ((flags) & 4095)

/******************* 规律 *********************/
 *      TYPE_MASK = DEPTH_MASK | CN_MASK      *                    
 *      CV_MAT_TYPE_MASK   1111 1111 1111     *
 *      CV_MAT_DEPTH_MASK  0000 0000 0111     *
 *      CV_MAT_CN_MASK     1111 1111 1000     *
/**********************************************/

#define CV_MAT_CONT_FLAG_SHIFT  14
#define CV_MAT_CONT_FLAG        (1 << 14)  //16384
#define CV_IS_MAT_CONT(flags)   ((flags) & 16384)  //二进制表示,右数第15位是否为1 
#define CV_IS_CONT_MAT          CV_IS_MAT_CONT
#define CV_SUBMAT_FLAG_SHIFT    15
#define CV_SUBMAT_FLAG          (1 << 15) //32768
#define CV_IS_SUBMAT(flags)     ((flags) & 32768) //二进制表示,右数第16位是否为1


#define CV_MAGIC_MASK       0xFFFF0000 // 1111 1111 1111 1111 0000 0000 0000 0000
#define CV_MAT_MAGIC_VAL    0x42420000 // 0100 0010 0100 0010 0000 0000 0000 0000

// 0100 0010 0100 0010 0100 低12位由type决定
// 其中type低3位由DEPTH决定,高9位由(CN-1)<<3决定,显然CN最大为4088,即CV_MAT_CN_MASK
cvMat.type = CV_MAT_MAGIC_VAL | CV_MAT_CONT_FLAG | type;


然后我们写一段程序来验证一下:

#include<iostream>
#include<opencv2/opencv.hpp>
using namespace std;

int main()
{
    int matrix[] = {1,2,3,4,5,6};
    CvMat mat;
    cvInitMatHeader(&mat,2,3,CV_32SC1,matrix);
    int x = mat.type;
    cout<< x <<endl;
    //0100 0010 0100 0010 0100 0000 0000 0100
    for(int i = 31,j = 0;i >= 0;i--){
        if(j%4 == 0) putchar(' ');
        cout<<((x & 1<<i) > 0);
        j++;
    }
    return 0;
}


由于CV_32SC1,是单通道,所以低12位的前9位为(1-1)<<3 = 0,这样CV_32SC1 = CV_32S = 4,所以对于单通道图像,其实type = depth,所以我们可以直接写 cvInitMatHeader(&mat,2,3,CV_32S,matrix); 效果是一样的,然后根据上面的分析:cvMat.type = CV_MAT_MAGIC_VAL | CV_MAT_CONT_FLAG | type;

所以前20位为0100 0010 0100 0010 0100,这样我们得到32位数0100 0010 0100 0010 0100 0000 0000 0100,最后我们取mat.type并将其转成二进制,可以发现输出的结果就是我们算出来的这32位,因此验证了我们上面的分析。

接下来我们自己来实现库的部分函数:

#include<iostream>
#include<opencv2/opencv.hpp>
using namespace std;

int main()
{
    int matrix[] = {1,2,3,4,5,6};
    CvMat mat;
    cvInitMatHeader(&mat,1,3,CV_32SC2,matrix);
    //0100 0010 0100 0010 0100 0000 0000 1100
    //取type
    cout<< cvGetElemType(&mat) <<endl;
    cout<< (mat.type & ((1 << 12) - 1))<<endl;
    cout<< (mat.type & CV_MAT_TYPE_MASK)<<endl;
    //取depth
    cout<< (mat.type & CV_MAT_DEPTH_MASK)<<endl;
    //取cn
    cout<< ((mat.type & CV_MAT_CN_MASK) >> 3) + 1<<endl;
    return 0;
}


这次是二通道的,所以(2-1)<<3,这样得到CN的贡献度是0000 0000 1000,加上DEPTH的贡献度0000 0000 0100, 我们确定32位数是0100 0010 0100 0010 0100 0000 0000 1100,那么如何获得type呢?就是取后12位啊,所以直接将mat.type & ((1<<12)-1)即可,这个((1<<12)-1)其实就是CV_MAT_TYPE_MASK啊。
同理我们可以取depth,也就是末三位,CV_32S=4,还有cn是右数4~12位,实际上是&4088,就是CV_MAT_CN_MASK,但是注意这不是cn,要知道cn是做了(cn-1)<<3运算的,所以我们做逆运算,将这个值右移3位,再加1,这样就恢复到了cn=2.



上面玩了一点trick,接下来还是要研究一下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;

基础知识:
union联合,关于union和struct的最明显的区别就是,union分配空间是分配所有成员占的空间的最大值(注意:不是累加),比如上面的data,uchar*指针占1个字节,short*占2个字节,int*和float*都占4字节,double*占8字节,所以union分配空间是8字节,而不是累加1+2+4+4+8=19字节,从另一个角度说明union的所有成员共用一片内存,并且起始地址都是一样的。关于union的详细资料,自行百度!
在扯一句uchar*是什么鬼?c++有这种数据类型吗?当然没有了,这个是opencv自己定义的一个宏,其实就是c++中的unsigned char...  类似还有很多,uint,int64,uint64什么的...
知道字面意思就可以了...

还有:
#ifdef __cplusplus
...A...
#else
...B...
#endif
这个是条件宏了,表示如果定义了宏名__cplusplus,那么就编译A部分,否则编译B部分,这个是给编译器看的...就这样,详细资料自行百度!
__cplusplus是什么鬼?其实看名字啊,就是cpp的全称啊,所以这玩意表示你的编译环境,可以看到如果是c++的话,条件宏会编译A部分,否则编译B部分。

显然博主用的是c++了,可以看到B部分就非常普通了,rows 和 cols,表示矩阵的行和列,但c++呢?A部分了,当然c++要兼容c,明显要更强大,是两个联合,用于联合的成员是共用的,所以可以发现rows等价于height,cols等价于width,这样你就可以喜欢写rows就写rows,喜欢写width就width,甚至1个rows,1个width交替写都可以,反正编译器认为他们是等价的。

还有几个成员,type开篇就讲了,其实包含了depth和cn两部分,step是什么鬼?官方说行的字节数,也就是存矩阵一行元素所需要的空间,以字节为单位。
下面两个refcount,hdr_refcount,这个是内部使用,换句话说这是写OpenCV库的人自己用的,我们用不着,不必关心,除非要仔细看源码...本博主能力有限还没看呢...

好了,差不多讲完了...
最后补充一点,OpenCV还将CvMat分成了两部分,一部分是data,就是存矩阵数据的,另一部分就是剩余的成员了,描述了矩阵的信息,这部分叫header,所以你可能会看到有一些什么create...Header之类的函数,就大概知道,这个函数只创建header,data域并没有分配空间。





你可能感兴趣的:(OpenCV CvMat 初窥)