小小研究了一下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域并没有分配空间。
以上是opencv1.0的内容,属于C部分的,接下来我打算主要研究C++部分,即opencv2.0以上,在opencv2.0,CvMat被Mat替代,不过仔细观察,本质没有变化,特别是宏部分基本没有变,还有设计思想也没变...这个以后再讲...
我觉得应该从最简单的数据类型研究起,这里所说的最简单的数据类型是指opencv中的,当然要比C++自带的基本类型复杂一下,比如Point_,Rect_,Scalar_,Mat_....
这篇就到这里了 ...
从下一篇起我们来看看opencv里的DataType
补充:
/*!
A helper class for cv::DataType
The class is specialized for each fundamental numerical data type supported by OpenCV.
It provides DataDepth<T>::value constant.
*/
template<typename _Tp> class CV_EXPORTS DataDepth {};
template<> class DataDepth<bool> { public: enum { value = CV_8U, fmt=(int)'u' }; };
template<> class DataDepth<uchar> { public: enum { value = CV_8U, fmt=(int)'u' }; };
template<> class DataDepth<schar> { public: enum { value = CV_8S, fmt=(int)'c' }; };
template<> class DataDepth<char> { public: enum { value = CV_8S, fmt=(int)'c' }; };
template<> class DataDepth<ushort> { public: enum { value = CV_16U, fmt=(int)'w' }; };
template<> class DataDepth<short> { public: enum { value = CV_16S, fmt=(int)'s' }; };
template<> class DataDepth<int> { public: enum { value = CV_32S, fmt=(int)'i' }; };
// this is temporary solution to support 32-bit unsigned integers
template<> class DataDepth<unsigned> { public: enum { value = CV_32S, fmt=(int)'i' }; };
template<> class DataDepth<float> { public: enum { value = CV_32F, fmt=(int)'f' }; };
template<> class DataDepth<double> { public: enum { value = CV_64F, fmt=(int)'d' }; };
template<typename _Tp> class DataDepth<_Tp*> { public: enum { value = CV_USRTYPE1, fmt=(int)'r' }; };