cv::Mat 类
Mat类是n维紧致数组类(n-dimensional dense array class)。Mat类表示一个n维紧致数字型单通道或多通道数组。可用于存储实/复值矢量和矩阵,灰度或彩色图像,立方体数据,矢量场,点云,张量,直方图(即使是高维直方图,也可以有效地存储在SparseMat中(稀疏矩阵))。数组m的数据定义为m.step[],所以,元素(i0,...,iM.dims−1)的地址可如下计算:
addr(Mi0,...,iM.dims−1)=M.data+M.step[0]∗i0+M.step[1]∗i1+...+M.step[M.dims−1]∗iM.dims−1
其中 0≤ik<M.size[k]。
在2维情况下,上述公式可简化为:
addr(Mi,j)=M.data+M.step[0]∗i+M.step[1]∗j
注意, M.step[i] >= M.step[i+1] (事实是,M.step[i] >= M.step[i+1]*M.size[i+1])。即2维矩阵逐行存储,3维矩阵逐面存储等等。M.step[M.dims-1]是最小矩阵单位,总是等于矩阵的元素尺寸M.elemSize()。
所以,Mat的数据存储形式兼容于主要的标准工具和SDF的紧致数组类型,如Numpy (ndarray), Win32 (设备无关的bitmaps)等,也就是说,可以用于任何使用steps(或strides)计算像素位置的数组。正是这种兼容性,使得Mat Header在OpenCV函数中用于分配和处理数据成为可能。
有许多建立Mat对象的方法,最流行的选择如下:
- 使用create(nrows, ncols, type) 方法或类似的Mat(nrows, ncols, type[, fillValue]) 构造函数。
这个方法分配指定尺寸和类型的新数组,其中类型与cvCreateMat方法有相同的意义。如CV_8UC1意指8-bit单通道数组,CV_32FC2意指2-channel(复数)浮点数组,等等。
// 建立一个7x7复数矩阵,使用1+3j作为充填值。
Mat M(7,7,CV_32FC2,Scalar(1,3));
//现在转换M到100x60 15-通道 8-bit 矩阵。旧内容将被释放
M.create(100,60,CV_8UC(15));
就像注释中解释的,create()函数仅在当前指定形状和类型不同于原来数组的时候才分配一个新数组。
- 建立多维数组:
// 建立一个100x100x100 8-位数组
int sz[] = {100, 100, 100};
Mat bigCube(3, sz, CV_8U, Scalar::all(0));
这个函数在传给Mat构造函数的维数=1,而Mat构造函数确按照2维方式建立数组,此时的列数为1,所以Mat::dims总是>= 2 (在数组为空时也可以等于0)。
- 使用拷贝构造函数或当表达式右端是一个数组或数组表达式时使用赋值操作符,就像注释中说的那样,数组赋值是一个O(1)操作,它仅仅复制数组头数据和增加引用计数。Mat::clone()方法可用于获得完整的数组复制。
- 用另一个数组的一部分构建数组头,可以是单一的行,列或几行,几列,数组的矩形区域(这称之为代数缩小)或对角矩阵。这样的操作也是O(1)的,因为新头引用了相同的数据,你所修改的实际上只是数组的属性部分。例如:
// 把第五行乘以3,加到第3行
M.row(3) = M.row(3) + M.row(5)*3;
// 拷贝第7列到第1列
M.col(1) = M.col(7); // 这样不对
//应该如下操作
Mat M1 = M.col(1);
M.col(7).copyTo(M1);
// 建立一个新的320x240图像
Mat img(Size(320,240),CV_8UC3);
//选择一个ROI
Mat roi(img, Rect(10,10,100,100));
// 用(0,255,0)充填这个ROI(这是RGB色彩空间上的绿色); 原始的320x240图像将被修改
roi = Scalar(0,255,0);
由于附加了datastart和dataend两个成员,可能需要在主容器数组中使用locateROI()来计算子数组的相对位置:
Mat A = Mat::eye(10, 10, CV_32S);
// 抽取A的列1(含有)到3(不含有)。
Mat B = A(Range::all(), Range(1, 3));
// 抽取B行5(含有)到9(不含有)。即 C \~ A(Range(5, 9), Range(1, 3))
Mat C = B(Range(5, 9), Range::all());
Size size; Point ofs;
C.locateROI(size, ofs);
// A的尺寸是(width=10,height=10)而ofs为(x=1, y=5),其中2X4的数据来自源数组。
就像整个矩阵一样,如果需要完全拷贝,只需使用clone()方法来抽取子矩阵。
- 生成一个矩阵头用于存储用户数据,可以使用如下步骤:
- 使用OpenCV处理外部数据(如,使用DirectShow滤波器或处理gstreamer模块时)。例如:
void process_video_frame(const unsigned char* pixels, int width, int height, int step)
{
Mat img(height, width, CV_8UC3, pixels, step);
GaussianBlur(img, img, Size(7,7), 1.5, 1.5);
}
2. 快速初始化一个小矩阵并获得超快的元素存取速度。
double m[3][3] = {{a, b, c}, {d, e, f}, {g, h, i}};
Mat M = Mat(3, 3, CV_64F, m).inv();
- 使用MATLAB风格的数组初始化方式,zeros(), ones(), eye(),例如:
// 建立双精度单位矩阵并把它加到M上。
M += Mat::eye(M.rows, M.cols, CV_64F);
- 使用逗号分隔的初始化:
// 建立3x3双精度单位矩阵
Mat M = (Mat_<double>(3,3) << 1, 0, 0, 0, 1, 0, 0, 0, 1);
在使用这个方法时,首先使用适当的参数调用Mat类的构造函数然后使用操作符 << 将后面的逗号分隔数值以常量,变量,表达式等形式放入矩阵。需要注意的是要求有一个括号 来避免编译错误。
一旦建立了数组,就可以使用引用计数器机理对它进行自动管理了。如果数组头是顶部用户分配数据建立的,则应该由用户自己处理数据的释放。数组数据在没有指针指向它的时候被释放。如果想要在数组对象析构之前释放数组头指向的数据,需要先使用Mat::release()。
学习数组类的另一个重点是对元素的访问。这里的说明已经描述了怎样计算每一个数组元素的地址。正常情况下,并不需要直接使用代码中的公式。如果已知数组元素的类型(可以使用Mat::type()函数获得类型),就可以用at访问二维数组的元素Mij:
M.at<double>(i,j) += 1.f;
假设M是双精度浮点数组。对于不同的维数,有几个重载的at方法。
如果需要处理2D数组的一整行,最有效的方法是获得指向行开头的指针,然后使用普通的C操作符[]:
// 计算矩阵正元素的和(假设M是双精度矩阵)
double sum=0;
for(int i = 0; i < M.rows; i++)
{
const double* Mi = M.ptr<double>(i);
for(int j = 0; j < M.cols; j++)
sum += std::max(Mi[j], 0.);
}
某些操作,如上所述,实际上并不依赖于数组的形状,而是依据数组元素(或是具有相同坐标的多个数组元素)逐个进行处理(例如数组的加操作)。这样的操作称之为元素层的操作。一般在检查整个数组是否连续的操作中使用这种元素层的操作,即检查每一行的末端是否有缝隙。如果没有,则可以把数组处理成长单行形式。
//计算正矩阵元素的和,优化方式
double sum=0;
int cols = M.cols, rows = M.rows;
if(M.isContinuous())
{
cols *= rows;
rows = 1;
}
for(int i = 0; i < rows; i++)
{
const double* Mi = M.ptr<double>(i);
for(int j = 0; j < cols; j++)
sum += std::max(Mi[j], 0.);
}
在连续的矩阵中,外层循环仅执行一次,所以开销较小,特别是在小矩阵的情况下,尤为显著。
最后,有一种STL风格的迭代器能够智能的跳过行间的缝隙:
// 计算矩阵正元素的和,基于迭代器的方法
double sum=0;
MatConstIterator_<double> it = M.begin<double>();
MatConstIterator_<double> it_end = M.end<double>();
for(; it != it_end; ++it)
sum += std::max(*it, 0.);
矩阵迭代器是随机访问迭代器,因此它可以传送给任何STL算法,包括std::sort()。
注:矩阵表达式和算法参见MatExpr
MAT类定义:
class CV_EXPORTS Mat
{
public:
//! 默认构造函数
Mat();
//! 构造特定尺寸和类型的2D矩阵(_type 为 CV_8UC1, CV_64FC3, CV_32SC(12) 等。)
Mat(int rows, int cols, int type);
Mat(Size size, int type);
//! 构造2D矩阵并且使用指定的_s填充之
Mat(int rows, int cols, int type, const Scalar& s);
Mat(Size size, int type, const Scalar& s);
//! 构造n维矩阵
Mat(int ndims, const int* sizes, int type);
Mat(int ndims, const int* sizes, int type, const Scalar& s);
//! 拷贝式构造函数
Mat(const Mat& m);
//! 矩阵头指向用户分配数据的构造函数
Mat(int rows, int cols, int type, void* data, size_t step=AUTO_STEP);
Mat(Size size, int type, void* data, size_t step=AUTO_STEP);
Mat(int ndims, const int* sizes, int type, void* data, const size_t* steps=0);
//! 为大较矩阵的一部分建立矩阵头
Mat(const Mat& m, const Range& rowRange, const Range& colRange=Range::all());
Mat(const Mat& m, const Rect& roi);
Mat(const Mat& m, const Range* ranges);
//! 转换旧风格的CvMat到新的矩阵;默认是不拷贝数据到新矩阵
Mat(const CvMat* m, bool copyData=false);
//! 转换旧风格的CvMatND到新矩阵;默认是不拷贝数据到新矩阵
Mat(const CvMatND* m, bool copyData=false);
//! 转换旧风格的IplImage到新矩阵;默认是不拷贝数据到新矩阵
Mat(const IplImage* img, bool copyData=false);
//! 从std::vector构建矩阵;数据可以拷贝或不拷贝
template<typename _Tp> explicit Mat(const vector<_Tp>& vec, bool copyData=false);
//! 从cv::Vec构建矩阵;默认是数据拷贝到矩阵
template<typename _Tp, int n> explicit Mat(const Vec<_Tp, n>& vec, bool copyData=true);
//! cv::Matx构建矩阵;默认是数据拷贝到矩阵
template<typename _Tp, int m, int n> explicit Mat(const Matx<_Tp, m, n>& mtx, bool copyData=true);
//! 从2D点构建矩阵
template<typename _Tp> explicit Mat(const Point_<_Tp>& pt, bool copyData=true);
//! 从3D点构建矩阵
template<typename _Tp> explicit Mat(const Point3_<_Tp>& pt, bool copyData=true);
//! 从逗号初始化器构建矩阵
template<typename _Tp> explicit Mat(const MatCommaInitializer_<_Tp>& commaInitializer);
//! 从GpuMat下载数据
explicit Mat(const gpu::GpuMat& m);
//! 析构函数 – 调用release()
~Mat();
//! 赋值操作符
Mat& operator = (const Mat& m);
Mat& operator = (const MatExpr& expr);
//! 为指定的行返回一个新矩阵的头
Mat row(int y) const;
//! 为指定的列返回一个新矩阵的头
Mat col(int x) const;
//! ... 为指定的行范围
Mat rowRange(int startrow, int endrow) const;
Mat rowRange(const Range& r) const;
//! ... 为指定的列范围
Mat colRange(int startcol, int endcol) const;
Mat colRange(const Range& r) const;
//! ... 为指定的对角
// (d=0 – 主对角,
// >0 – 低半矩阵对角,
// <0 – 高半矩阵对角)
Mat diag(int d=0) const;
//! 构造函数,构造一个主对角线为矢量d的方形矩阵
static Mat diag(const Mat& d);
//! 返回一个矩阵的全拷贝(克隆),即全数据的拷贝
Mat clone() const;
//! 拷贝矩阵内容到"m"。调用m.create(this->size(), this->type())。
void copyTo( OutputArray m ) const;
//! 拷贝矩阵的一些元素到"m",这些元素是由mask标记为非零的元素。
void copyTo( OutputArray m, InputArray mask ) const;
//! 转换矩阵元素到另一种选择的数据类型。参见cvConvertScale。
void convertTo( OutputArray m, int rtype, double alpha=1, double beta=0 ) const;
//! 实际convertTo函数的操作函数,由MatrixExpressions函数内部使用
void assignTo( Mat& m, int type=-1 ) const;
//! 设置s到矩阵的每一个元素(操作符)
Mat& operator = (const Scalar& s);
//! 依据mask屏蔽指示设置某些矩阵的元素到s
Mat& setTo(InputArray value, InputArray mask=noArray());
//! 使用不同数量的通道或行,建立一个矩阵的头。参见cvReshape。
Mat reshape(int cn, int rows=0) const;
Mat reshape(int cn, int newndims, const int* newsz) const;
//! 矩阵表达式意义上的矩阵转置
MatExpr t() const;
//! 矩阵表达式意义上的矩阵求逆
MatExpr inv(int method=DECOMP_LU) const;
//! 矩阵表达式意义上的矩阵相乘(每元素相乘)
MatExpr mul(InputArray m, double scale=1) const;
//! 计算2 3D矢量的叉积(叉积只能在2 3D矢量上进行,有数学意义,法矢量)
Mat cross(InputArray m) const;
//! 计算矢量点积
double dot(InputArray m) const;
//! Matlab风格的矩阵初始化
static MatExpr zeros(int rows, int cols, int type);
static MatExpr zeros(Size size, int type);
static MatExpr zeros(int ndims, const int* sz, int type);
static MatExpr ones(int rows, int cols, int type);
static MatExpr ones(Size size, int type);
static MatExpr ones(int ndims, const int* sz, int type);
static MatExpr eye(int rows, int cols, int type);
static MatExpr eye(Size size, int type);
//! 分配新的矩阵数据,除非矩阵已经有了相同的尺寸和类型。并消除对旧数据的引用。
void create(int rows, int cols, int type);
void create(Size size, int type);
void create(int ndims, const int* sizes, int type);
//! 增加引用计数,用于避免内存泄漏。
void addref();
//! 减少引用计数;当引用计数减到0时,释放引用数据内存。
void release();
//! 释放矩阵数据
void deallocate();
//! 内部使用函数;适当的重分配数组的尺寸和行长度
void copySize(const Mat& m);
//! 保留一定的行空间
void reserve(size_t sz);
//! 改变矩阵的行数,并重新分配空间
void resize(size_t sz);
//!改变矩阵的行数;并用s初始化新增加的元素
void resize(size_t sz, const Scalar& s);
//! 内部函数
void push_back_(const void* elem);
//! 添加元素到1d矩阵的尾部(可能是多个元素,如果_Tp=Mat)
template<typename _Tp> void push_back(const _Tp& elem);
template<typename _Tp> void push_back(const Mat_<_Tp>& elem);
void push_back(const Mat& m);
//! 从矩阵底部删除几个超位面
void pop_back(size_t nelems=1);
//! 在父矩阵内定位矩阵的头。参见adjustROI
void locateROI( Size& wholeSize, Point& ofs ) const;
//! 在父矩阵内移动/重计当前矩阵的ROI
Mat& adjustROI( int dtop, int dbottom, int dleft, int dright );
//! 抽取一个矩形的子矩阵(这是一个行的普化形式,rowRange 。)
Mat operator()( Range rowRange, Range colRange ) const;
Mat operator()( const Rect& roi ) const;
Mat operator()( const Range* ranges ) const;
//! 转换矩阵头到CvMat;没有数据被拷贝
operator CvMat() const;
//! 转换矩阵头到CvMatND;没有数据被拷贝
operator CvMatND() const;
//! 转换矩阵头到IplImage;没有数据被拷贝
operator IplImage() const;
template<typename _Tp> operator vector<_Tp>() const;
template<typename _Tp, int n> operator Vec<_Tp, n>() const;
template<typename _Tp, int m, int n> operator Matx<_Tp, m, n>() const;
//! 返回真,当且仅当矩阵数据是连续的(即,后续行之间没有空隙)。
//类似于CV_IS_MAT_CONT(cvmat->type)
bool isContinuous() const;
//! 返回真,如果矩阵是另一个矩阵的子矩阵
bool isSubmatrix() const;
//! 以字节单位返回元素的尺寸,类似于CV_ELEM_SIZE(cvmat->type)
size_t elemSize() const;
//! 以字节单位返回元素通道的尺寸。
size_t elemSize1() const;
//! 返回元素数据类型,类似于CV_MAT_TYPE(cvmat->type)
int type() const;
//! 返回元素深度类型,类似于CV_MAT_DEPTH(cvmat->type)
int depth() const;
//! 返回元素通道类型,类似于CV_MAT_CN(cvmat->type)
int channels() const;
//! 返回行步长/elemSize1()
size_t step1(int i=0) const;
//! 返回真,如果矩阵数据为NULL
bool empty() const;
//! 返回矩阵元素的总数
size_t total() const;
//!返回 N 如果矩阵是1通道(N x ptdim) 或 ptdim-channel(1xN) 或 (Nx1);否则返回负数
int checkVector(int elemChannels, int depth=-1, bool requireContinuous=true) const;
//! 返回属于维数#0的指向第i0个子矩阵的指针
uchar* ptr(int i0=0);
const uchar* ptr(int i0=0) const;
//! 返回属于#0和#1维数的指向(i0,i1)子矩阵的指针
uchar* ptr(int i0, int i1);
const uchar* ptr(int i0, int i1) const;
//! 返回属于#0,#1,#2维的指向(i0,i1,i3)子矩阵的指针
uchar* ptr(int i0, int i1, int i2);
const uchar* ptr(int i0, int i1, int i2) const;
//! 返回指向矩阵元素的指针
uchar* ptr(const int* idx);
//! 返回指向矩阵元素的只读指针
const uchar* ptr(const int* idx) const;
template<int n> uchar* ptr(const Vec<int, n>& idx);
template<int n> const uchar* ptr(const Vec<int, n>& idx) const;
//! 上述方法的模板版本
template<typename _Tp> _Tp* ptr(int i0=0);
template<typename _Tp> const _Tp* ptr(int i0=0) const;
template<typename _Tp> _Tp* ptr(int i0, int i1);
template<typename _Tp> const _Tp* ptr(int i0, int i1) const;
template<typename _Tp> _Tp* ptr(int i0, int i1, int i2);
template<typename _Tp> const _Tp* ptr(int i0, int i1, int i2) const;
template<typename _Tp> _Tp* ptr(const int* idx);
template<typename _Tp> const _Tp* ptr(const int* idx) const;
template<typename _Tp, int n> _Tp* ptr(const Vec<int, n>& idx);
template<typename _Tp, int n> const _Tp* ptr(const Vec<int, n>& idx) const;
//! 与上述模板方法相同,返回的是非引用指针
template<typename _Tp> _Tp& at(int i0=0);
template<typename _Tp> const _Tp& at(int i0=0) const;
template<typename _Tp> _Tp& at(int i0, int i1);
template<typename _Tp> const _Tp& at(int i0, int i1) const;
template<typename _Tp> _Tp& at(int i0, int i1, int i2);
template<typename _Tp> const _Tp& at(int i0, int i1, int i2) const;
template<typename _Tp> _Tp& at(const int* idx);
template<typename _Tp> const _Tp& at(const int* idx) const;
template<typename _Tp, int n> _Tp& at(const Vec<int, n>& idx);
template<typename _Tp, int n> const _Tp& at(const Vec<int, n>& idx) const;
//! 特殊版本的2D数组(特指习惯引的图像像素)
template<typename _Tp> _Tp& at(Point pt);
template<typename _Tp> const _Tp& at(Point pt) const;
//! 矩阵元素循环的模板方法,循环小心地跳过行尾的空隙(如果有的话)
template<typename _Tp> MatIterator_<_Tp> begin();
template<typename _Tp> MatIterator_<_Tp> end();
template<typename _Tp> MatConstIterator_<_Tp> begin() const;
template<typename _Tp> MatConstIterator_<_Tp> end() const;
enum { MAGIC_VAL=0x42FF0000,
AUTO_STEP=0,
CONTINUOUS_FLAG=CV_MAT_CONT_FLAG,
SUBMATRIX_FLAG=CV_SUBMAT_FLAG
};
/*! flag包含如下位字段(场):
- 特殊签注
- 连续标志
- 深度
- 通道数
*/
int flags;
//! 矩阵维数,>= 2
int dims;
//! 行数和列数,或(-1, -1),当矩阵大于2维时
int rows, cols;
//! 指向数据的指针
uchar* data;
//! 指向引用计数的指针,当矩阵指向用户分配数据时,这个指针为NULL
int* refcount;
//! 用于locateROI和adjustROI的辅助字段
uchar* datastart;
uchar* dataend;
uchar* datalimit;
//! 用户分配器
MatAllocator* allocator;
struct CV_EXPORTS MSize
{
MSize(int* _p);
Size operator()() const;
const int& operator[](int i) const;
int& operator[](int i);
operator const int*() const;
bool operator == (const MSize& sz) const;
bool operator != (const MSize& sz) const;
int* p;
};
struct CV_EXPORTS MStep
{
MStep();
MStep(size_t s);
const size_t& operator[](int i) const;
size_t& operator[](int i);
operator size_t() const;
MStep& operator = (size_t s);
size_t* p;
size_t buf[2];
protected:
MStep& operator = (const MStep&);
};
MSize size;
MStep step;
protected:
void initEmpty();
};
关于类方法的说明:
Mat row(int y) const;
为指定的矩阵行建立矩阵头。
这个方法为指定的矩阵行生成一个新的矩阵头并返回这个头。这是一个O(1)操作,与矩阵尺寸无关。新矩阵的数据与源数据共享。下面的例子就是典型的基本矩阵处理操作,axpy可用于LU和其它许多算法。
inline void matrix_axpy(Mat& A, int i, int j, double alpha)
{
A.row(i) += A.row(j)*alpha;
}
注意:在当前实现中下面的代码不能像期望的那样工作:
Mat A;
...
A.row(i) = A.row(j); //不能正确工作
这是因为A.row(i)是一个临时的头,这个头下一步是要赋值给另一个头的。要记住,这里的每一个操作都是O(1),即,没有数据要拷贝。因而,上面的赋值不是真赋值,如果你真实期望第j行拷贝到第i行,就应该转换这种赋值为表达式或使用 Mat::copyTo 方法:
Mat A;
...
// 能工作,但有点隐晦
A.row(i) = A.row(j) + 0;
// 这个有点长,但这是推荐使用的方法
A.row(j).copyTo(A.row(i));
参数
y | 基于0的行索引 |
Mat col(int x) const;
为指定的列建立矩阵头。
这个方法为指定的矩阵列生成一个新的矩阵头并返回这个头。这是一个O(1)操作,与矩阵尺寸无关。新矩阵下的数据与源数据共享。参见Mat::row的描述。
参数
x | 基于0的列索引。 |
Mat rowRange(int startrow, int endrow) const;
建立指定行范围的矩阵头。
这个方法为矩阵的指定行范围生成一个新的矩阵头。类似于Mat::row 和Mat::col,这是一个O(1)操作。
参数
startrow | 包含当前行在内的行范围开始索引,基于0的。 |
endrow | 不包含当前行在内的行范围终止索引,基于0的。 |
Mat rowRange(const Range& r) const;
这是一个重载的成员函数,提供了一个习惯上的操作。与上面函数的区别在于所接收的变量类型。
参数
r | Range 结构,开始和终止索引。 |
Mat colRange(int startcol, int endcol) const;
建立一个指定列范围的矩阵头。
这个方法为矩阵的指定列范围生成一个新的矩阵头。类似于Mat::row 和Mat::col,这是一个O(1)操作。
参数
startcol | 包含本列在内的列范围开始索引,基于0的。 |
endcol | 不包含本列在内的列范围终止索引,基于0的。 |
Mat colRange(const Range& r) const;
这是一个重载的成员函数,提供了一个习惯上的操作。与上面函数的区别在于所接收的变量类型。
Parameters
r | Range 结构,包含开始和终止索引。 |
Mat diag(int d=0) const;
从矩阵中抽取对角线元素。
这个方法生成一个新的矩阵头表示指定的对角线矩阵。新矩阵是一个单列矩阵。类似于Mat::row 和Mat::col,这是一个O(1)操作。
参数
d | 是对角线索引,取值如下:
例如: Mat m = (Mat_<int>(3,3) << 1,2,3,4,5,6,7,8,9); Mat d0 = m.diag(0); Mat d1 = m.diag(1); Mat d_1 = m.diag(-1); 结果矩阵为 d0 =[1;5;9] d1 =[2;6] d_1 =[4;8] |
|
|
static Mat diag(const Mat& d);
建立一个对角线矩阵
这个方法从指定矩阵的主对角线建立方形对角线矩阵。
参数
d | 表示主对角线的移位矩阵。 |
Mat clone() const;
建立数组和其数据的完全拷贝。
这个方法建立数组的完全拷贝。不考虑源step[]。因此,拷贝的数组是一个连续的数组拥有total() * elemSize()个字节。
void copyTo( OutputArray m ) const;
拷贝矩阵到另一个矩阵。
这个方法拷贝矩阵数据到另一个矩阵。在拷贝数据之前,这个方法调用:
m.create(this->size(), this->type());
所以目的矩阵在必要时会重新分配。然而m.copyTo(m),操作稍微有点不同,这个函数不会处理源目矩阵的部分重叠情况。
在指定了操作屏蔽时,如果Mat::create 调用再分配矩阵,新矩阵将在拷贝数据之前使用零来初始化。
参数
m | 目的矩阵。如果在这个操作之前没有适当的尺寸和类型,则被重新分配。 |
void copyTo( OutputArray m, InputArray mask ) const;
这是一个重载的成员函数,提供一种习惯上的方法。与上一函数的差别仅在于接受的变量不同。
参数
M | 目的矩阵。如果在这个操作之前没有适当的尺寸和类型,则被重新分配。 |
mask | 与矩阵同尺寸的操作屏蔽符矩阵。它的非零元素表示对应矩阵的元素需要被拷贝。这个屏蔽必须是CV_8U类型的并且可以有1或多个通道。 |
void convertTo( OutputArray m, int rtype, double alpha=1, double beta=0 ) const;
转换数组到另一个具有可选尺度数据类型。
这个方法转换源像素值到目标数据类型。最后使用saturate_cast<>来避免可能的溢出:
m(x,y)=saturate_cast<rType>(α(∗this)(x,y)+β)
参数
m | 输出矩阵;如果在这个操作之前没有适当的尺寸和类型,则被重新分配。 |
rtype | 期望的输出矩阵类型或,在通道数与输入相同时,表示深度;如果rtype是负值,则输出矩阵与输入有相同的类型。 |
alpha | 可选的尺度因子。 |
beta | 可选的delta尺度因子附加值。 |
void assignTo( Mat& m, int type=-1 ) const;
是convertTo 函数的另一种形式。
这是一个由MatrixExpressions引擎内部使用的方法。
参数
M | 目的数组。 |
Type | 所期望的目的数组的深度(或-1,则与源类型相同)。 |
Mat& operator = (const Mat& m);
赋值操作符(多重重载)
这些都是可用的赋值操作符。因为它们有较多的差别,一定要仔细地阅读这些操作符的参数描述。
参数
m | 赋值的矩阵,是操作符右边的矩阵。矩阵赋值是O(1)操作。即,不拷贝数据,它与源共享数据,引用计数器(如果有)增加。在赋值新数据之前,旧数据通过Mat::release断开数据的引用。 |
Mat& operator = (const MatExpr& expr);
这是一个重载的成员函数,提供习惯上的操作。其与上面函数的差别仅在于接收的变量。
参数
expr | 赋值的矩阵表达式对象. 与上一种赋值形式相反,这个赋值形式可以重用已经分配的矩阵,只要它有与矩阵表达式结果相适合的尺寸和类型。这些都由矩阵表达式扩展中的函数自动进行处理。例如,C=A+B被扩展成add(A, B, C),而且加操作自动执行C的再分配。 |
Mat& operator = (const Scalar& s);
设置所有或某些数组元素的值到指定的值s。
参数
s | 赋值的数值,转换到实际的数组类型。 |
Mat& setTo(InputArray value, InputArray mask=noArray());
设置全部或某些数组元素到指定的值。
这是赋值操作符Mat::operator=(const Scalar& s)的另一种形式。
参数
value | 赋给数组元素的值,被转换为数组的实际类型。 |
mask | 某些元素的操作屏蔽,类似于* 。其非零元素表示矩阵元素需要被复制。屏蔽有CV_8U类型,和1或多个通道。 |
Mat reshape(int cn, int rows=0) const;
改变2D矩阵的形状和通道数,不拷贝数据。
这个方法为指定的那些元素产生一个新的矩阵头。新矩阵可以有与源矩阵不同的尺寸和通道数。任何尺寸和通道数的组合都是可能的,如果:
- 新矩阵中不包含额外元素,也没有排除源矩阵元素。在转换之后,产生的rows * cols * channels() 等同于源矩阵。
- 不拷贝数据。即,这是一个O(1)操作。因此,如果改变了行数,或这个操作以某种方法改变了行索引,这个矩阵就必须是连续的。参见Mat::isContinuous。
例如,如果有一个3D点集储存为STL矢量,并且想要表述其为3XN矩阵点,如下操作即可:
std::vector<Point3f> vec;
...
Mat pointMat = Mat(vec). // 转换矢量到矩阵O(1)操作
reshape(1). //在Nx1的三通道之外,生成Nx3的1通道矩阵,也是O(1)操作
t(); //最后转置这个Nx3矩阵,这个调用拷贝所有元素
Mat pointMat = Mat(vec). reshape(1). t();
参数
Cn | 新通道数。如果为0,则通道数保持不变。 |
rows | 新行数。如果为0,则行数保持不变。 |
Mat reshape(int cn, int newndims, const int* newsz) const;
这是一个重载的成员函数,提供了上函数的习惯性用法。区别仅在于接收的变量。
MatExpr t() const;
转置矩阵函数。
这个方法执行矩阵表达式意义上的转置操作。它并不执行实际意义上的转置,但是返回一个临时的矩阵转置对象,这个临时对象可以进一步用于更复杂矩阵表达式或赋值给一个矩阵:
Mat A1 = A + Mat::eye(A.size(), A.type())*lambda;
Mat C = A1.t()*A1; // 计算(A + lambda*I)^t * (A + lamda*I)
MatExpr inv(int method=DECOMP_LU) const;
计算逆矩阵。
这个方法执行矩阵表达式意义上的矩阵求逆运算,也就是说返回的临时矩阵对象是原矩阵的逆矩阵,并可进一步用于复杂矩阵表达式或赋值到一个矩阵。
参数
method | 矩阵求你的方法。cv::DecompTypes之一。 |
MatExpr mul(InputArray m, double scale=1) const;
执行两个矩阵之间元素层的乘或除运算。
这个方法返回临时数组元素编码对象,表示数组元素使用所选的比例(scale,操作因子)与矩阵的乘积。注意,这并不是使用对应的操作符 * 的矩阵乘积。
例:
Mat C = A.mul(5/B); //等价于divide(A, B, C, 5)
参数
m | 与源矩阵有相同的类型和尺寸的数组,或矩阵表达式。 |
scale | 选择的标度因子。 |
Mat cross(InputArray m) const;
计算两个3元素矢量的叉积(三维空间中矢量的叉积,法矢量)。
这个方法计算两个3元素矢量的叉积。矢量必须是相同形状和尺寸的3元素浮点矢量。结果是与操作数类型相同的3元素矢量。
参数
m | 叉积操作数。 |
double dot(InputArray m) const;
计算两个矢量的点积。
这个方法计算两个矩阵的点积。如果矩阵不是单列或单行矢量,按从上到下和从左到右扫描顺序就象1D矢量那样处理。矢量必须有相同的尺寸和类型。如果矩阵有多个通道,点积将所有通道的总和在一起。
参数
m | 点积的操作数。 |
static MatExpr zeros(int rows, int cols, int type);
返回指定尺寸和类型的零数组。
这个方法返回Matlab风格的零元素初始化的数组。它可用于快速形成作为函数参数的矩阵表达式或矩阵初始化的常量数组:
Mat A;
A = Mat::zeros(3, 3, CV_32F);
在这个例子中,仅在A不是3x3浮点矩阵时才分配新矩阵,否则,矩阵A用零充填。
参数
rows | 行数 |
cols | 列数 |
type | 要建立的矩阵类型 |
static MatExpr zeros(Size size, int type);
这是一个重载的成员函数,提供一种习惯上的使用方法,与上函数的差别仅在于接收的参数。
参数
size | 另一种矩阵尺寸的指定方式Size(cols, rows) 。 |
type | 要建立矩阵的类型。 |
static MatExpr zeros(int ndims, const int* sz, int type);
这是一个重载的成员函数,提供一种习惯上的使用方法。其差别在于所接收的变量。
参数
ndims | 数组维数 |
Sz | 一个整数数组,指定要建立数组的形状。 |
Type | 要建立矩阵的类型. |
static MatExpr ones(int rows, int cols, int type);
返回一个元素全为1的指定尺寸和类型的数组。
这个方法返回Matlab风格的用1初始化的数组,类似于Mat::zeros。注意,使用这个方法可以用任意值初始化数组,例如下面的Matlab语式:
Mat A = Mat::ones(100, 100, CV_8U) * 3; //使用3,充填一个100x100的矩阵
上面的操作并不先生成用1充填的100x100矩阵然后再乘以3。而是,先记住了比例因子(此处为3)并在实际的矩阵初始化中使用它。
注意
在多通道类型中,仅仅第一个通道被初始化为1,其他通道被设置成0。
参数
rows | 行数。 |
cols | 列数。 |
type | 要建立矩阵的类型。 |
static MatExpr ones(Size size, int type);
这是一个重载的成员函数,提供一种习惯上的使用方法。其差别在于所接收的变量。
参数
size | 另一种矩阵尺寸的表示Size(cols, rows) 。 |
type | 要建立矩阵的类型。 |
static MatExpr ones(int ndims, const int* sz, int type);
这是一个重载的成员函数,提供一种习惯上的使用方法。其差别在于所接收的变量。
参数
ndims | 数组的维数。 |
sz | 一个整数组指定矩阵的形状。 |
type | 要建立矩阵的类型。 |
static MatExpr eye(int rows, int cols, int type);
返回一个指定尺寸和类型的单位矩阵。
这个方法返回一个Matlab风格的单位矩阵,类似于Mat::zeros ,Mat::ones,你可以使用比例操作来建立一个带系数的单位矩阵:
//生成4x4对角线矩阵,使用0.1值。
Mat A = Mat::eye(4, 4, CV_32F)*0.1;
注
在多通道类型中,单位矩阵仅初始化头一个通道,其他通道设置为0
参数
rows | 行数。 |
cols | 列数。 |
type | 要建立矩阵的类型。 |
static MatExpr eye(Size size, int type);
这是一个重载的成员函数,提供一种习惯上的使用方法。其差别在于所接收的变量。
参数
size | 另一种矩阵尺寸的表示Size(cols, rows) 。 |
type | 要建立矩阵的类型。 |
void create(int rows, int cols, int type);
建立数组,必要时分配新的数组数据。
这是关键的Mat 方法之一。最新风格下的OpenCV函数和方法在生成数组时都调用这个方法来输出数组。这个方法使用下面的算法路线:
- 如果当前数组的形状和类型与新数组匹配,立即返回。否则,调用Mat::release断开旧数据的引用。
- 初始化新的矩阵头。
- 分配新数据 total() * elemSize()个字节。
- 用关联的数据分配新的引用计数,并设置为1。(引用计数器是一个指针,外部数据集时为NULL)
这个方案使存储管理强健而有效,同时也有助于避免额外输入。也就是说,通常情况下不需要显式分配输出数组,例如:
Mat color;
...
Mat gray(color.rows, color.cols, color.depth());
cvtColor(color, gray, COLOR_BGR2GRAY);
可以简单滴写成:
Mat color;
...
Mat gray;
cvtColor(color, gray, COLOR_BGR2GRAY);
因为cvtColor,以及绝大多数OpenCV 函数,都内部调用 Mat::create() 来输出数组。
参数
rows | 新行数 |
cols | 新列数 |
type | 新矩阵类型。 |
void create(Size size, int type);
这是一个重载的成员函数,提供一种习惯上的使用方法。其差别在于所接收的变量。
参数
size | 另一种新矩阵尺寸表示:Size(cols, rows) |
type | 新矩阵类型。 |
void create(int ndims, const int* sizes, int type);
这是一个重载的成员函数,提供一种习惯上的使用方法。其差别在于所接收的变量。
Parameters
ndims | 新数组维数。 |
sizes | 整数组指定新数组的形状 |
type | 新矩阵类型。 |
void addref();
增加引用计数。
这个方法关联矩阵数据的引用计数。如果矩阵头指向外部数据集(见Mat::Mat ),引用计数为NULL,此时这个方法无效。正常情况,为了避免内存泄漏,一般不显式调用这个方法。在矩阵赋值操作中都隐含调用此方法。引用计数的增加是平台支持下的原子操作。因此在不同线程中的异步矩阵操作中也是安全的。
void release();
减少引用计数,并在必要时释放矩阵。
这个方法减少关联矩阵数据的引用计数。在引用计数达到0时,矩阵数据被释放,并且数据和引用计数指针设置为NULL。如果矩阵头指向外部数据集(见Mat::Mat ),引用计数为NULL,此方法无效。
这个方法可以手动调用,以强制释放矩阵数据。但是,由于这个方法在类的析构函数或有其他改变数据指针方法中自动调用,因此通常情况下不必调用此方法。引用计数减少和检查是否到0是一个平台支持的原子操作。因此,在矩阵的多线程异步情况下是安全的。
void deallocate();
内部使用函数,作为'release'方法的替代;释放矩阵数据。
void copySize(const Mat& m);
内部使用函数;适当地重新分配数组的_size,_step。
void reserve(size_t sz);
保留一定行数的存储空间。
这个方法保留 sz 行空间。如果矩阵已经有足够的空间储存 sz 行,这个函数不做任何事情。如果矩阵重分配(源数据空间小于sz行空间),则前Mat::rows行数据被保留。 这个方法仿真STL 矢量类中对应的方法。
参数
sz | 行数 |
void resize(size_t sz);
改变矩阵行数。
这个方法改变矩阵行数。如果矩阵重分配,则前min(Mat::rows, sz) 行被保留。这个方法仿真STL 矢量类中对应的方法。
参数
sz | 新行数 |
void resize(size_t sz, const Scalar& s);
这是一个重载的成员函数,提供一种习惯上的使用方法。其差别在于所接收的变量。
参数
sz | 新行数 |
s | 赋值给新附加行元素的值 |
void push_back_(const void* elem);
添加元素到矩阵的底部。
这个方法添加一个或多个元素到矩阵的底部。它们仿真对应STL矢量类的方法。当元素是Mat时,其类型和列数必需与容器矩阵相同。
参数
elem | 被添加的元素。 |
void push_back(const Mat& m);
这是一个重载的成员函数,提供一种习惯上的使用方法。其差别在于所接收的变量。
参数
m | 要添加的行。 |
void pop_back(size_t nelems=1);
从矩阵底部删除元素。
这个方法从矩阵底部删除一行或多行元素。
参数
nelems | 要删除的行数。如果大于总行数,则抛出异常。 |
void locateROI( Size& wholeSize, Point& ofs ) const;
在父矩阵内定位矩阵头。
在使用Mat::row, Mat::col, Mat::rowRange, Mat::colRange从矩阵中抽取子矩阵之后,导致子矩阵正好指向初始大矩阵的一部分。然而,每个子矩阵都包含有辅助于重构原始矩阵的尺寸和位置等信息(由datastart和dataend 字段标识) 。方法locateROI 则是实际获取这些信息的手段。
参数
wholeSize | 输出参数,包含整个矩阵尺寸信息 |
Ofs | 输出参数,包含子矩阵在整个矩阵内的偏移。 |
Mat& adjustROI( int dtop, int dbottom, int dleft, int dright );
调整子矩阵在父矩阵内的尺寸和位置。
这个方法是Mat::locateROI的补充。其典型应用就是调整子矩阵在父矩阵内的位置并在某种程度上移动这个位置。典型应用是在滤波操作中,当遇到像素超出了ROI的情况时,则要求这个操作。当所有参数均为正值时,ROI需要在各个方向上增长指定的量,例如:
A.adjustROI(2, 2, 2, 2);
矩阵尺寸在水平和垂直两个方向上各增加4个元素。矩阵向左和向上移动两个元素,这对于5x5核滤波是必须增加的像素。
adjustROI强制调节的ROI是在父矩阵内的,即,调节的ROI边界是有父矩阵边界所包含的边界。例如,如果子矩阵A 定位在父矩阵的第一行上并且调用A.adjustROI(2, 2, 2, 2),则A不能在向上的方向增加。
这个函数在OpenCV中由滤波函数内部使用,如filter2D,形态学操作等。
参数
dtop | 向上移动子矩阵边界。 |
dbottom | 向下移动子矩阵边界。 |
dleft | 向左移动子矩阵边界。 |
dright | 向右移动子矩阵边界。 |
bool isContinuous() const;
检查矩阵是否为连续的。
如果矩阵元素连续存储,在每一行的末端没有缝隙,这个方法返回真(true)。否则返回假(false)。显然,1x1 或1xN 矩阵总是连续的。用Mat::create 建立的矩阵总是连续的。但是,如果使用Mat::col, Mat::diag等抽取部分矩阵或使用外部数据构造矩阵的头,这样的矩阵可能不再有连续的属性。
连续标志作为位标志存储在于Mat::flags 中,并在构造矩阵头时自动计算。因此,连续性检查是非常快的操作,理论上能够像下述操作一样:
// Mat::isContinuous()另一种实现
bool myCheckMatContinuity(const Mat& m)
{
//返回(m.flags & Mat::CONTINUOUS_FLAG) != 0;
return m.rows == 1 || m.step == m.cols*m.elemSize();
}
这个方法在相当多的OpenCV函数中使用。特别是元素层的操作(如代数和逻辑操作,数学函数,alpha混合,颜色空间变换等),不依赖于图像几何的函数。因而,如果所有输入和输出数组都是连续的,这些函数就可以把它们作为非常长的单行矢量来处理。下面的例子说明一个alpha混合函数的实现:
template<typename T>
void alphaBlendRGBA(const Mat& src1, const Mat& src2, Mat& dst)
{
const float alpha_scale = (float)std::numeric_limits<T>::max(),
inv_scale = 1.f/alpha_scale;
CV_Assert( src1.type() == src2.type() &&
src1.type() == CV_MAKETYPE(traits::Depth<T>::value, 4) &&
src1.size() == src2.size());
Size size = src1.size();
dst.create(size, src1.type());
// 这里的语句是:检查数组的连续性,如果是,则把数组当做1D矢量处理
If (src1.isContinuous()&&src2.isContinuous()&&dst.isContinuous()){
size.width *= size.height;
size.height = 1;
}
size.width *= 4;
for ( int i = 0; i < size.height; i++ ){
// 在数组连续时,外部循环仅执行一次
const T* ptr1 = src1.ptr<T>(i);
const T* ptr2 = src2.ptr<T>(i);
T* dptr = dst.ptr<T>(i);
for( int j = 0; j < size.width; j += 4 ){
float alpha = ptr1[j+3]*inv_scale, beta = ptr2[j+3]*inv_scale;
dptr[j]=saturate_cast<T>(ptr1[j]*alpha + ptr2[j]*beta);
dptr[j+1]=saturate_cast<T>(ptr1[j+1]*alpha+ptr2[j+1]*beta);
dptr[j+2] = saturate_cast<T>(ptr1[j+2]*alpha + ptr2[j+2]*beta);
dptr[j+3]=saturate_cast<T>((1-(1-alpha)*(1-beta))*alpha_scale);
}
}
}
这个方法是非常简单的,可以提高简单元素操作性能的10-20个百分点,尤其是对小图像,操作相当简单。
这个函数的另一个OpenCV要点是调用 Mat::create 建立目标数组,除非目标数组已经有了适当的尺寸和类型,否则它分配空间。而且新分配的数组总是连续的,由于Mat::create 并不总是分配新矩阵,所以仍然要检查目标数组的连续性。
bool isSubmatrix() const;
如果矩阵是另一个矩阵的子矩阵,返回真(true)。
size_t elemSize() const;
返回矩阵元素尺寸的字节数。
这个方法返回矩阵元素尺寸的字节数。例如,如果矩阵类型是CV_16SC3,这个方法返回3*sizeof(short) 或 6。
size_t elemSize1() const;
返回矩阵元素单通道尺寸的字节数。
这个方法返回矩阵单通道下元素尺寸的字节数,即,不考虑通道数。 例如,如果矩阵类型为CV_16SC3,这个方法返回sizeof(short) 或2。
int type() const;
返回矩阵元素的类型。
这个方法返回矩阵元素的类型。这是一个与CvMat类型系统兼容的标识符,如CV_16SC3 为16位有符号的3通道数组等。
int depth() const;
返回矩阵元素的深度。
这个方法返回矩阵元素深度标识符(通道类型)。例如,对于16位有符号元素数组,这个方法返回CV_16S。下面列出全部矩阵类型列表值:
- CV_8U – 8位无符号整数( 0..255 )
- CV_8S – 8位有符号整数( -128..127 )
- CV_16U – 16位无符号整数( 0..65535 )
- CV_16S – 16位有符号整数( -32768..32767 )
- CV_32S – 32位有符号整数( -2147483648..2147483647 )
- CV_32F – 32位浮点数( -FLT_MAX..FLT_MAX, INF, NAN )
- CV_64F – 64位浮点数( -DBL_MAX..DBL_MAX, INF, NAN )
int channels() const;
返回矩阵通道数。
这个方法返回矩阵的通道数。
size_t step1(int i=0) const;
返回正则化的步长(行长)。
这个方法返回由Mat::elemSize1()划分的矩阵步长。可用于快速访问任意矩阵的元素。
bool empty() const;
如果矩阵没有元素,则返回真(true)。
如果 Mat::total() 为 0 或 Mat::data 是NULL,这个方法返回真。注意pop_back() 和 resize()方法的M.total() == 0
并不隐含 M.data == NULL
。
size_t total() const;
返回数组元素的总数。
这个方法返回数组元素数(如果数组表示图像,则是像素数)。
uchar* ptr(int i0=0);
返回指向指定矩阵行的指针。
这个方法返回uchar*
或类型指针,指向指定的矩阵行。见 Mat::isContinuous 中的示例就知道这个方法的使用。
参数
i0 | 0基的行索引. |
InputArray----------------------------------------------------------------------------------------
cv::_InputArray类
这是传递只读输入数组到OpenCV函数的代理类。
其定义如下:
typedef const _InputArray& InputArray;
此处_InputArray 是一个可以从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>
或双精度浮点数构造的类。它也可以从矩阵表达式构造。
由于这是一个最主要的实现层的类,并且其接口在未来版本中可能有变化,我们并不对这个类进行详细描述。尽管如此,我们还是要记住几个关键点:
- 当你在引用说明或OpenCV源码的函数中看到使用InputArray时,说明可以在实际参数中传递 Mat
,
Matx,vector<T>
等类型变量。 (参见上面的完整列表)。 - 有选择的输入变量:如果某些输入数组可以为空,传递cv::noArray() (或简单地在此之前使用cv::Mat())。
- 这个类仅仅是为了传递参数而设计。也就是说,正常情况,不应该声明这种类型的类成员的本地和全局变量。
- 如果你想要在设计自己的函数或类方法中操作多重类型的数组,也可以使用InputArray (或OutputArray)作为传递的参数类型。在函数内,应该使用_InputArray::getMat() 方法来构造数组的矩阵头(不拷贝数据)。_InputArray::kind() 可用于区分Mat 和
vector<>
等类型
,正常情况不需要这样做。
下面是怎样使用具有InputArray类型参数的函数:
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))));
//vec是数组类型,transform函数使用了InputArray参数类型。
cv::transform(vec, vec, cv::Matx23f(0.707, -0.707, 10, 0.707, 0.707, 20));
此处,我们生成了一个包含点的STL矢量,并且直接进行仿射变换到使用Matx<float, 2, 3>
建立的
2x3矩阵实例中。
这里展示了函数是怎样实现的 ( 简单地讲,我们仅根据语句内的要求实现了一种非常特殊的情况 ) :
void myAffineTransform(InputArray _src, OutputArray _dst, InputArray _m)
{
// 取输入数组的矩阵头这是O(1)操作,除非_src 或_m 是矩阵表达式。
Mat src = _src.getMat(), m = _m.getMat();
CV_Assert( src.type() == CV_32FC2 && m.type() == CV_32F && m.size() == Size(3, 2) );
// 建立输出数组,使之有一个适当的尺寸和类型。在Mat::create被调用时,在STL矢量情况下,它调用vector::resize。
_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));
}
}
还有另一个相关的类型InputArrayOfArrays当前也被定义为同义的InputArray:
typedef InputArray InputArrayOfArrays;
这表明函数变量可以是矢量的矢量(矢量的每个元素都是矢量)或者是矩阵矢量(矢量的每个元素都是矩阵)。Python/Java等也需要生成其独特的同义类型。在函数的实现层它们的使用都是相似的,但_InputArray::getMat(idx) 应该用来获取外部矢量dx-th元素的头,并且 _InputArray::size().area() 应该用来查询外部矢量的元素数(矢量数/矩阵数)。
一般情况,所支持的类型受限于cv::Mat 类型。其它类型是被禁止的。但,有的时候我们需要支持传递用户非一般情况下 Mat 的类型, 例如 cv::KeyPoint,cv::DMatch,数组等。这种数据不一定解释为图像数据,或处理成某种程度上的规则的cv::Mat。要传递这种用户类型,需使用 rawIn() / rawOut() / rawInOut() 进行包装。用户类型需要包装成Mat兼容的 CV_8UC<N>
值(N = sizeof(T),N <= CV_CN_MAX)。
cv::_InputArray类定义
//代理数据类型,用于传递Mat类型和vector<>类型的输入参数
class CV_EXPORTS _InputArray
{
public:
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,
OCL_MAT =10 << KIND_SHIFT
};
_InputArray();
_InputArray(const Mat& m);
_InputArray(const MatExpr& expr);
template<typename _Tp> _InputArray(const _Tp* vec, int n);
template<typename _Tp> _InputArray(const vector<_Tp>& vec);
template<typename _Tp> _InputArray(const vector<vector<_Tp> >& vec);
_InputArray(const vector<Mat>& vec);
template<typename _Tp> _InputArray(const vector<Mat_<_Tp> >& vec);
template<typename _Tp> _InputArray(const Mat_<_Tp>& m);
template<typename _Tp, int m, int n> _InputArray(const Matx<_Tp, m, n>& matx);
_InputArray(const Scalar& s);
_InputArray(const double& val);
// < 已弃用
_InputArray(const GlBuffer& buf);
_InputArray(const GlTexture& tex);
// >
_InputArray(const gpu::GpuMat& d_mat);
_InputArray(const ogl::Buffer& buf);
_InputArray(const ogl::Texture2D& tex);
virtual Mat getMat(int i=-1) const;
virtual void getMatVector(vector<Mat>& mv) const;
// <已弃用
virtual GlBuffer getGlBuffer() const;
virtual GlTexture getGlTexture() const;
// >
virtual gpu::GpuMat getGpuMat() const;
/*虚拟*/ ogl::Buffer getOGlBuffer() const;
/*虚拟*/ ogl::Texture2D getOGlTexture2D() const;
virtual int kind() const;
virtual Size size(int i=-1) const;
virtual size_t total(int i=-1) const;
virtual int type(int i=-1) const;
virtual int depth(int i=-1) const;
virtual int channels(int i=-1) const;
virtual bool empty() const;
#ifdef OPENCV_CAN_BREAK_BINARY_COMPATIBILITY
virtual ~_InputArray();
#endif
int flags;
void* obj;
Size sz;
};
cv::_OutputArray-------------------------------------------------------------------------------
cv::_OutputArray 类
这个类型与InputArray 非常类似,它用于输入/输出和输出函数参数。
就像InputArray一样,OpenCV用户不应该关心OutputArray,它们只是用于传递Mat,vector<T>
等类型的参数到函数。与InputArray
一样有同样的限制:在使用中不要显式建立OutputArray实例 。
如果想要函数多态性(即,可以接受不同类型的数组作为输出参数),这也不是很困难。只要采用上面的示例作为引用即可。注意,_OutputArray::create() 必须在_OutputArray::getMat()之前调用。这个方法能够保证适当地分配输出数组。
可选输出参数。如果不需要计算和返回一定的输出数组,传递cv::noArray(),就像在可选输入数组那样。在实现层,使用_OutputArray::needed() 来检查是否确定需要计算输出数组。
OutputArray 有几个同义的类型,用于协助Python/Java/... 包装生成器:
typedef OutputArray OutputArrayOfArrays;
typedef OutputArray InputOutputArray;
typedef OutputArray InputOutputArrayOfArrays;
--------------------------------------------------------------------------------------------
注释:以上是在使用openCV开源代码中对其中的帮助文档进行的文字翻译和一些编程注释,对于使用openCV的程序员可能会有所帮助。
在使用openCV开源代码中,后续将继续发布相关的函数和说明,其中绝大部分是对OpenCV帮助文档的文字翻译和解释,其中包括:
2、 Imgproc模块
3、 features2d模块
4、 objdetect模块
5、 ml模块
6、 flann模块
7、 stitching模块
对各模块中的函数,主要是外部使用的函数,以及使用说明,实际效果等进行解释。