opencv里 函数参数类型: InputArray和OutputArray 解读
概述
InputArray和OutputArray两个类都是代理数据类型,用来接收Mat和Vector<>作为输入参数,OutputArray继承自InputArray。
InputArray作为输入参数的时候,传入的参数加了const限定符,即它只接收参数作为纯输入参数,无法更改输入参数的内容。而OutputArray则没有加入限定符,可以对参数的内容进行更改。
InputArray使用一系列的数据类型作为输入实例化自身,通过设定一系列的构造函数来实现。
_InputArray::_InputArray(constMat&m) :flags(MAT),obj((void*)&m) {}
_InputArray::_InputArray(constvector<Mat>&vec) : flags(STD_VECTOR_MAT),obj((void*)&vec) {}
_InputArray::_InputArray(constdouble&val) :flags(FIXED_TYPE +FIXED_SIZE +MATX +CV_64F),obj((void*)&val),sz(Size(1,1)) {}
.....
可以看到在构造的时候,同时指定了flags和obj,flags用于表明当前存储的数据类型,而obj存储的则是数据的内存地址。
除了这些基本的构造函数外,还有其他支持泛型的构造函数,如下
/// Input/Output Arrays /
template<typename_Tp>inline_InputArray::_InputArray(constvector<_Tp>&vec)
: flags(FIXED_TYPE +STD_VECTOR +DataType<_Tp>::type),obj((void*)&vec) {}
template<typename_Tp>inline_InputArray::_InputArray(constvector<vector<_Tp> >& vec)
: flags(FIXED_TYPE +STD_VECTOR_VECTOR +DataType<_Tp>::type),obj((void*)&vec) {}
template<typename_Tp>inline_InputArray::_InputArray(constvector<Mat_<_Tp> >& vec)
: flags(FIXED_TYPE +STD_VECTOR_MAT +DataType<_Tp>::type),obj((void*)&vec) {}
(这里只列出了部分,更详细的可以参见mat.hpp,或者全工程搜索_InputArray::_InputArray),对于泛型的构造函数,可以看出flags除了存放了传入的对象的类型,还存放了传入对象内部数据的类型,也就是里面的DataType<_Tp>::type。有了这个类型之后可以知道当前数据的类型,基于这个,可以实现将传入数据打包成不同的数据,例如将vector数据打包成Mat型数据。
后面可以使用CV_MAT_MASK获取数据元素的类型,在另一篇文章有解说opencv数据类型的位操作。总的来说,flags的低12位存放了数据元素的类型,包括通道channel和深度depth,而在本类里新定义的对象类型,则是从高16位开始的(从下面的enum定义可知),这样两者就互不干扰了。
类型标记
这两个类的作用是使Mat和Vector<>均可以作为统一的参数,将这些类型转换成InputArray的时候,会自动生成对应的类型标记,标记为类里的成员flag,然后在其他地方再根据这个标记解析出实际的类型。
支持这些类型的数据
enum {
KIND_SHIFT = 16,
FIXED_TYPE = 0x8000 <<KIND_SHIFT,
FIXED_SIZE = 0x4000 <<KIND_SHIFT,
KIND_MASK = ~(FIXED_TYPE|FIXED_SIZE) - (1 <<KIND_SHIFT) + 1,
NONE = 0 <<KIND_SHIFT,
MAT = 1 <<KIND_SHIFT,
MATX = 2 <<KIND_SHIFT,
STD_VECTOR = 3 <<KIND_SHIFT,
STD_VECTOR_VECTOR = 4 <<KIND_SHIFT,
STD_VECTOR_MAT = 5 <<KIND_SHIFT,
EXPR = 6 <<KIND_SHIFT,
OPENGL_BUFFER = 7 <<KIND_SHIFT,
OPENGL_TEXTURE = 8 <<KIND_SHIFT,
GPU_MAT = 9 <<KIND_SHIFT
};
获取类型使用
int_InputArray::kind()const
{
returnflags &KIND_MASK;
}
该函数返回的就是NONE = 0 <<KIND_SHIFT一下的类型,这些类型在指定给flags的时候是单独指定的,而其以上的FIXED_TYPE和FIXED_SIZE则可以组合使用。
数据转换
为了从InputArray取出具体类型的数据,该类提供了一系列的接口函数,如下:
virtualMatgetMat(inti=-1)const;
virtualvoidgetMatVector(vector<Mat>&mv)const;
virtualGlBuffergetGlBuffer()const;
virtualGlTexturegetGlTexture()const;
virtualgpu::GpuMatgetGpuMat()const;
可以看出,这些类型分别对应于flags里面的那些类型,具体的实现可以看里面的代码,但是这里面有比较有意思的地方,即传入的类型可以解压成其他的类型,就如开始时讲到的将vector打包成Mat类型。
要实现不同类型的打包操作需要用到如下几个函数
virtual Size size(int i=-1) const;
该函数返回当前类型的size。仅看该函数里面的vector部分:
Size_InputArray::size(inti)const
{
intk =kind();
if( k == STD_VECTOR )
{
CV_Assert( i < 0 );
constvector<uchar>&v = *(constvector<uchar>*)obj;
constvector<int>&iv = *(constvector<int>*)obj;
size_tszb =v.size(),szi = iv.size();
returnszb ==szi ?Size((int)szb, 1) :Size((int)(szb/CV_ELEM_SIZE(flags)), 1);
}
}
可以看出,首先将将原始的数据转换成最小单位uchar的vector,然后根据它的size就可以得到该数据所占的字节数,再除一个元素(旧事重提,opencv中一个元素对应于一个CV_MAKE_TYPE类型所指定的数据,例如三个通道的RGB类型是CV_MAKE_TYPE(CV_8U,3)即对应三个字节)大小所占的字节即可以得到元素的个数。并且可以看出这里的返回的size仅仅是指定了rows的,cols全部都设为1,也就是说,对于vector转换为Mat,返回的是仅仅有一列的矩阵。
virtual Mat getMat(int i=-1) const;
该函数将数据打包成Mat返回,仅仅vector转Mat的部分
Mat_InputArray::getMat(inti)const
{
intk =kind();
if( k == STD_VECTOR )
{
CV_Assert( i < 0 );
intt =CV_MAT_TYPE(flags);
constvector<uchar>&v = *(constvector<uchar>*)obj;
return !v.empty() ?Mat(size(),t, (void*)&v[0]) :Mat();
}
}
从这里代码可以看出,首先是从flags里面提取出元素类型编码,然后使用基本单位uchar(计算机中最小的存储单位是8位,即一个字节,这里uchar就是8字节)来将初始数据的内存进行解引用(关于对vector所对应的指针进行解引用的问题,和人讨论过应该是vector重载了解引用运算符*,但是不确定~~),然后就使用size、类型编码以及内存地址就可以构造一个Mat了。
更多推荐
所有评论(0)