OpenCV源码详解之InputArray, OutputArray

在OpenCV中,有两个代理类是经常出现的:InputArray和OutputArray,它巧妙地利用了C++的定义转换,辅助完成对矩阵的管理。

定义

typedef const _InputArray& InputArray;
typedef InputArray InputArrayOfArrays;
typedef const _OutputArray& OutputArray;
typedef OutputArray OutputArrayOfArrays;
typedef const _InputOutputArray& InputOutputArray;
typedef InputOutputArray InputOutputArrayOfArrays;

说明

InputArray是一个代理类,用于将只读输入数组传递到OpenCV函数中。

其中,输入数组是可以`Mat`, `Mat_<T>`, `Matx<T, m, n>`, `std::vector<T>`, `std::vector<std::vector<T> >`, `std::vector<Mat>`, `std::vector<Mat_<T> >`,`UMat`, `std::vector<UMat>` or `double`。它也可以由矩阵表达式构造。

关于这个类,有一些关键点要注意:

(1)作为可选的输入参数,当需要输入数组或矩阵为空时,传递cv::noArray()即可,或者简单的使用cv::Mat() ,就像我们常做过的那样;

(2)该类仅用于传递参数。也就是说,我们通常不应该声明此类类型的本地成员和本地变量或全局变量;

(3)在函数内部, 我们可以通过_InputArray::getMat()方法构造一个数组(矩阵)的信息头(这不会拷贝数据);

(4)可以使用_InputArray::kind() 来对数据进行类别区分,比如到底是矩阵Mat 还是向量`vector<>`等.

同样,OutputArray有类似InputArray的性质,所以也不应该单独定义此类的成员,如果不需要计算某些输出数组,传入cv::noArray()即可,在应用上,可以用_OutputArray::needed()检查某些数组是否需要计算并输出。

下面是一个OpneCV源码注释中使用InputArray, OutputArray的例子,

void myAffineTransform(InputArray _src, OutputArray _dst, InputArray _m)
{
	// 从输入数组(矩阵)中得到 Mat headers 
	Mat src = _src.getMat(), m = _m.getMat();
	// CV_Assert(src.type() == CV_32FC2 && m.type() == CV_32F && m.size() == Size(3, 2));
	CV_Assert(src.type() == CV_32FC2);
	CV_Assert(m.type() == CV_32F);
	CV_Assert(m.size() == Size(3, 2));

	// 重新创建 一 个输出数组(矩阵).
	_dst.create(src.size(), src.type());
	Mat dst = _dst.getMat();

	for (int i = 0; i < src.rows; i++)
		for (int j = 0; j < src.cols; j++)
		{
			Point2f pt = src.at<Point2f>(i, j);
			dst.at<Point2f>(i, j) = Point2f(m.at<float>(0, 0)*pt.x +
				m.at<float>(0, 1)*pt.y +
				m.at<float>(0, 2),
				m.at<float>(1, 0)*pt.x +
				m.at<float>(1, 1)*pt.y +
				m.at<float>(1, 2));
		}
}

int main()
{
	float m[3][2] = {
		1,0,0,
		0,1,0,
	};
	Mat mt(2, 3, CV_32F);
	for (int i = 0; i<mt.rows; i++)
	{
		for (int j = 0; j<mt.cols; j++)
		{
			mt.at<float>(i, j) = m[i][j];
		}
	}

	std::vector<Point2f> vec;
	// 圆周上的点
	for (int i = 0; i < 30; i++)
		vec.push_back(Point2f((float)(100 + 30 * cos(i*CV_PI * 2 / 5)),
		(float)(100 - 30 * sin(i*CV_PI * 2 / 5))));
	cv::transform(vec, vec, cv::Matx23f(0.707, -0.707, 10, 0.707, 0.707, 20));

	Mat dst;
	myAffineTransform(vec, dst, mt);
}

在myAffineTransform(InputArray _src, OutputArray _dst, InputArray _m)接受_src, _dst这两个参数的时候,由于定义的转换,代理类被实例化,这些代理类有很多的构造函数,分别针对不同的输入类型,在这里,InputArray的构造函数是

inline _InputArray::_InputArray(const Mat& m) { init(MAT+ACCESS_READ, &m); }
inline void _InputArray::init(int _flags, const void* _obj){ flags = _flags; obj = (void*)_obj; }

我们看到,这里限定了Mat的类型为MAT,并且为只读权限ACCESS_READ,这两个参数被保存到参数flags中,flags相当于当前存储的数据类型和读/写方式,而obj存储的则是数据的内存地址。

OutputArray的构造函数是

inline _OutputArray::_OutputArray(Mat& m) { init(MAT+ACCESS_WRITE, &m); }

因为OutputArray继承了InputArray,所以会和前面的代码一样,MAT+ACCESS_WRITE保存参数flags中。

posted @ 2018-12-12 20:06  SpaceVision  阅读(252)  评论(0编辑  收藏  举报