opencv里 函数参数类型: InputArray和OutputArray 解读

来源:互联网 发布:额尔齐斯石价值 知乎 编辑:程序博客网 时间:2024/06/11 14:46

概述

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_TYPEFIXED_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 0
原创粉丝点击