opencv⾥函数参数类型:InputArray和OutputArray解读
概述
InputArray和OutputArray两个类都是代理数据类型,⽤来接收Mat和Vector<>作为输⼊参数,OutputArray继承⾃InputArray。
InputArray作为输⼊参数的时候,传⼊的参数加了const限定符,即它只接收参数作为纯输⼊参数,⽆法更改输⼊参数的内容。⽽OutputArray则没有加⼊限定符,可以对参数的内容进⾏更改。
InputArray使⽤⼀系列的数据类型作为输⼊实例化⾃⾝,通过设定⼀系列的构造函数来实现。
_InputArray::_InputArray(const Mat&m) :flags(MAT),obj((void*)&m) {}
_InputArray::_InputArray(const vector<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(const vector<_Tp>&vec)
: flags(FIXED_TYPE +STD_VECTOR +DataType<_Tp>::type),obj((void*)&vec) {}
template<typename_Tp>inline_InputArray::_InputArray(const vector<vector<_Tp> >& vec)
: flags(FIXED_TYPE +STD_VECTOR_VECTOR +DataType<_Tp>::type),obj((void*)&vec) {}
template<typename_Tp>inline_InputArray::_InputArray(const vector<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
{
return flags &KIND_MASK;
}
该函数返回的就是NONE = 0 <<KIND_SHIFT⼀下的类型,这些类型在指定给flags的时候是单独指定的,⽽其以上的FIXED_TYPE和FIXED_SIZE则可以组合使⽤。
数据转换
为了从InputArray取出具体类型的数据,该类提供了⼀系列的接⼝函数,如下:
virtual MatgetMat(int i=-1)const;
virtualvoid getMatVector(vector<Mat>&mv)const;
virtual GlBuffergetGlBuffer()const;
virtual GlTexturegetGlTexture()const;
virtual gpu::GpuMatgetGpuMat()const;
可以看出,这些类型分别对应于flags⾥⾯的那些类型,具体的实现可以看⾥⾯的代码,但是这⾥⾯有⽐较有意思的地⽅,即传⼊的类型可以解压成其他的类型,就如开始时讲到的将vector打包成Mat类型。
要实现不同类型的打包操作需要⽤到如下⼏个函数
virtual Size size(int i=-1) const;
该函数返回当前类型的size。仅看该函数⾥⾯的vector部分:
Size_InputArray::size(int i)const
{
int k =kind();
if( k == STD_VECTOR )
{
CV_Assert( i < 0 );
const vector<uchar>&v = *(const vector<uchar>*)obj;
const vector<int>&iv = *(const vector<int>*)obj;
parameter数据类型size_tszb =v.size(),szi = iv.size();
return szb ==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(int i)const
{
int k =kind();
if( k == STD_VECTOR )
{
CV_Assert( i < 0 );
int t =CV_MAT_TYPE(flags);
const vector<uchar>&v = *(const vector<uchar>*)obj;
return !v.empty() ?Mat(size(),t, (void*)&v[0]) :Mat();
}
}
从这⾥代码可以看出,⾸先是从flags⾥⾯提取出元素类型编码,然后使⽤基本单位uchar(计算机中最⼩的存储单位是8位,即⼀个字节,这⾥uchar就是8字节)来将初始数据的内存进⾏解引⽤(关于对vector所对应的指针进⾏解引⽤的问题,和⼈讨论过应该是vector重载了解引⽤运算符*,但是不确定~~),然后就使⽤size、类型编码以及内存地址就可以构造⼀个Mat了。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。