参数与Matrix一致,这里不多做介绍,详细可看官方文档。Array也定义了许多conventional typedef,如
Array的赋值、加法、减法也与Matrix类似,需要说明的是Array相乘,这个操作类似于Matlab中的".*",对应元素做计算,所以两个Array相乘,只能是大小相同的Array。
Array提供了一些操作函数,abs(),求每个元素的绝对值;sqrt(),求每个元素的算术平方根;a.min(b),求a和b中每个位置较小的元素。
Matrix和Array可以进行互换,利用matrix中的Array()函数和Array中的Matrix()函数,下面给出实例
需要说明的是,将array表达式赋值给Matrix变量是允许的;cwiseProduct()函数允许Matrix直接进行点对点乘法,而不用转换至Array。
Block Operations
利用block()函数,可以从Matrix中取出一个小矩阵来进行处理,使用的语法为matrix.block(i, j, p, q); 和 matrix.block(i, j)。其中i,j是block左上角元素位于矩阵中的位置,p、q是block的大小,如果是小块的话,那么采用第二种方法,在编译时就固定p、q,速度会比第一种方法快一些。
需要特别注意的是,block利用非常灵活,原因在于它不仅仅可以是右值表达式,它也可以作为左值对matrix进行修改。同样Array也有block函数,如下面实例
Block函数可以从矩阵中提取一块,特殊的如提取一整行或者一整列,如想达到如此操作,可以使用row()函数和col()函数,已达到更高效率。这两个函数很类似与Matlab中":"操作符。
如果想在矩阵中操作角落的block,Eigen还提供了一些特殊的函数:
最后一列如果在操作大小固定的小block时,在编译时提供size,可以达到更快的效果。同样对于vector,也有类似的操作函数:
Advanced Initialization
简单初始一个矩阵的方法是利用逗号表达式,初始顺序是从左到右、从上到下。可以一个元素一个元素的初始矩阵,也可以一次用一个矩阵去初始大矩阵的一部分,如
同样利用上面讲的block去初始矩阵的一部分:
Eigen提供了一些函数来初始特殊的矩阵,如Zero()用以初始全领的矩阵,其size可以通过参数或者命名空间设置,如Array33f::Zero(), ArrayXf::Zero(3), ArrayXXf::Zero(3, 4);Constant(rows, cols, value),用以初始元素相等的矩阵;Random(),用以初始一个随机矩阵;Identity(),用以初始一个单位阵,但这个函数只针对Matrix类型,不针对Array类型。LinSpaced(size, low, high)是一个特殊的函数,功能类似Matlab中的冒号用法,如下面实例:
Eigen也提供了一个函数来进行初始设置,如setZero(), MatrixBase::setIdentity(), DenseBase::setLinSpaced()等,如我们要生成一个矩阵,可以有下面几种方法:
上面介绍的例子中,一些方法如Zero(), Constant()等用以初始一个矩阵,我们可以认为这都是生成一个临时矩阵用以初始化,但事实是他们返回了一个所谓expression objects,而这不会引起多余的开销。例如我们想令一个的矩阵行互换,即左乘一个
。
上述的finished()是必要的,因为要获取一个实际的matrix来进行乘法计算,而不是一个用于初始某个矩阵的临时矩阵,它表明要在这个矩阵形成之后在用以做乘法计算。
Reductions,Visitors and Broadcasting
Reductions可以看做一个作用在matrix或者vector上的函数,返回结果是一个值,比如之前介绍的sum(), prod(), trace(), minCoeff()等,这里再介绍一类Reductions—Norm computations。Eigen提供了范数类函数,norm(),如范数,squaredNorm(), 范数,lpNnorm(),这里的p可以是1或者无穷。
Reductions还有一类波尔类型,即一个矩阵可以对其中每一个元素进行大小比较。all(),如果每个元素都为真,则为真;any(),如果有一个元素为真,返回真;count(),为真元素的个数,如下面的例子所述:
在使用maxCoeff()和minCoeff()函数是,可以寻求最大元素和最小元素,但如果我们想返回其位置,则需要给定相关参数,而参数的类型我们要使用Index类型,例如:
Reductions还可以部分使用,即返回的不是一个值,而是一组值,可以看做是一种降维操作,所使用的函数为colwise(), rowwise()。例如
Mat.colwise()理解为分别去看矩阵的每一列,然后再作用maxCoeff()函数,即求每一列的最大值。
需要注意的是,colwise返回的是一个行向量(列方向降维),rowwise返回的是一个列向量(行方向降维)。
下面说一下Broadcasting,他与Reductions的部分使用类似,但是他作用的类型是vector,相当于把vector按照某种方式进行扩充,再与矩阵进行计算。如
Mat.colwise() += v;是核心语句,相当于
经过上面介绍,我们来一个实例:
m.colwise() – v得到的是 ,再将得到的矩阵取列,再求范数的平方,即(m.colwise()-v).colwise().squaredNorm()得到的是,最后再求这一列的最小值,并记录位置。输出时m.col(index)便将最小的那一列输出。
Interfacing with Raw Buffers: the Map class
Map类型,给用户一种选择:在已经存在的矩阵或者向量中,不必拷贝对象,而是直接在该对象的内存上进行操作。创建语句如下:
Map>;
由此可见Map需要一个模板参数,还要确保给予一个指向该类型的指针,如
Map mf(pf, rows, cols);
其中pf是有一个float指针。
Map还可以接受两个参量,即Map,MapOptions可以有Aligned和Unaligned两个选择;StrideType是对如何具体选择数据的一个选择,看下面两个实例
上面实例解释了在创建Map的时候,如何决定内存的存储顺序;下面的实例将重点解释StrideType
class Eigen::Stride< _OuterStrideAtCompileTime, _InnerStrideAtCompileTime >,这是stride的声明语法,有两个int类型的参数:OuterStride是按行存储矩阵每行之间的增量或者按列存储矩阵每列之间的增量,InnerStreide是按行存储矩阵每列之间的增量或者按列存储矩阵每行之间的增量。具体增量可以编译时给定,也可以运行时给定,即设置为Dynamic。上面的例子中,MatrixXi是按列存储,运行时stride的第一个参数是8,即每列之间相差8个位置;第二个参数是2,即每行直接相差2个位置;所以该矩阵存储为
下面给一个使用Map变量的实例
需要注意的是,这里m2,m2map和m2mapconst指向的是同一块内存。
同样,Map也可以改变指向的内存,利用New语句,方法如下:
这里v改变的只是指向内存的位置,并没有为此新开辟内存,而且在声明v的时候,应该已经有存在的内存区域被指向,即不会使用如下语句:
Map A(NULL);
Aliasing
混淆往往出现在赋值号左右都用同一变量的时候,如mat = mat.transpose();解决方法之一是使用eval()函数,看下面两个实例
如果有xxxInplace()函数,用这个函数可达到最高效的效果,如transposeInPlace()
通常情况下,如果元素的表达只与该元素自己有关,那么一般不会引起混淆,如语句:mat = (2 * mat – MatrixXf::Identity(2, 2)).array().square();
矩阵乘法是个特殊的操作,编译器会默认为其做避免混淆的处理,尽管这会引起额外的开销。如果你确定不会引起混淆,可以使用noalias()函数来避免额外开销。
总结一下:
- 对于单个元素操作,如单个元素成绩或者加减操作一般不会引起混淆。
- 两个矩阵相乘时会做避免混淆的处理,如果你能确定不产生混淆,可以使用noalias()函数来避免额外开销。
- 其他情况下,都认为没有混淆存在,为了避免混淆产生,使用eval()函数或者xxxInPlace()函数,后者效率更高。
Storage orders
Matrix 创建的矩阵默认是按列存储,如果想修改可以在创建矩阵的时候加入参数,如Matrix Acolmajor; 或者 Matrix Arowmajor;
在选择存储顺序的时候,要有如下考虑:
- 你的使用者希望你用哪种方式存储,这可能跟其他处理库所要求的存储顺序有关。
- 与你所处理的实际算法有关。
- Eigen库在处理按列存储的矩阵时会更加高效。