Eigen C++开源矩阵计算工具——Eigen的简单用法
Eigen非常方便矩陣操作,當(dāng)然它的功能不止如此,由于本人只用到了它的矩陣相關(guān)操作,所以這里只給出了它的一些矩陣相關(guān)的簡單用法,以方便快速入門。矩陣操作在算法研究過程中,非常重要,例如在圖像處理中二維高斯擬合求取光斑中心時(shí)使用Eigen提供的矩陣算法,差不多十來行代碼即可實(shí)現(xiàn),具體可見:http://blog.csdn.net/hjx_1000/article/details/8490653
Eigen的下載與安裝,可參考下面兩個(gè)博客:
http://blog.csdn.net/hjx_1000/article/details/8477522
或者:http://blog.csdn.net/abcjennifer/article/details/7781936;
Eigen幫助文檔的地址:http://eigen.tuxfamily.org/dox/pages.html,本文中很多例子也是直接摘自這些幫助文檔,
另外關(guān)于Eigen的論壇可以訪問http://forum.kde.org/viewforum.php?f=74
Eigen用源碼的方式提供給用戶使用,在使用時(shí)只需要包含Eigen的頭文件即可進(jìn)行使用。
之所以采用這種方式,是因?yàn)镋igen采用模板方式實(shí)現(xiàn),由于模板函數(shù)不支持分離編譯,所以只能提供源碼而不是動(dòng)態(tài)庫的方式供用戶使用,不過這也也更方面用戶使用和研究。關(guān)于模板的不支持分離編譯的更多內(nèi)容,請參考:http://blog.csdn.net/hjx_1000/article/details/8093701
1、 ?矩陣的定義
Eigen中關(guān)于矩陣類的模板函數(shù)中,共有6個(gè)模板參數(shù),但是目前常用的只有前三個(gè),如下所示:
template<typename _Scalar, int _Rows, int _Cols, int _Options, int _MaxRows, int _MaxCols>?struct traits<Matrix<_Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols> >.......其前三個(gè)參數(shù)分別表示矩陣元素的類型,行數(shù)和列數(shù)。矩陣定義時(shí)可以使用Dynamic來表示矩陣的行列數(shù)為未知,例如:| typedef?Matrix<double,Dynamic, Dynamic>?MatrixXd; |
| typedef Matrix< double , 3 , 1> Vector3d |
注意:
(1)Eigen中無論是矩陣還是數(shù)組、向量,無論是靜態(tài)矩陣還是動(dòng)態(tài)矩陣都提供默認(rèn)構(gòu)造函數(shù),也就是你定義這些數(shù)據(jù)結(jié)構(gòu)時(shí)都可以不用提供任何參數(shù),其大小均由運(yùn)行時(shí)來確定。
(2)矩陣的構(gòu)造函數(shù)中只提供行列數(shù)、元素類型的構(gòu)造參數(shù),而不提供元素值的構(gòu)造,對于比較小的、固定長度的向量提供初始化元素的定義,例如:
Vector2d a(5.0, 6.0);Vector3d b(5.0, 6.0, 7.0);Vector4d c(5.0, 6.0, 7.0, 8.0);2、動(dòng)態(tài)矩陣和靜態(tài)矩陣
動(dòng)態(tài)矩陣是指其大小在運(yùn)行時(shí)確定,靜態(tài)矩陣是指其大小在編譯時(shí)確定,在Eigen中并未這樣稱呼矩陣。具體可見如下兩段代碼:
代碼段1:
using namespace Eigen;using namespace std;int main(){MatrixXd m = MatrixXd::Random(3,3);m = (m + MatrixXd::Constant(3,3,1.2)) * 50;cout << "m =" << endl << m << endl;VectorXd v(3);v << 1, 2, 3;cout << "m * v =" << endl << m * v << endl;}代碼段2:using namespace Eigen;using namespace std;int main(){Matrix3d m = Matrix3d::Random();m = (m + Matrix3d::Constant(1.2)) * 50;cout << "m =" << endl << m << endl;Vector3d v(1,2,3);cout << "m * v =" << endl << m * v << endl;}說明:1)代碼段1中MatrixXd表示任意大小的元素類型為double的矩陣變量,其大小只有在運(yùn)行時(shí)被賦值之后才能知道;?MatrixXd::Random(3,3)表示產(chǎn)生一個(gè)元素類型為double的3*3的臨時(shí)矩陣對象。
?2) 代碼段2中Matrix3d表示元素類型為double大小為3*3的矩陣變量,其大小在編譯時(shí)就知道;
3)上例中向量的定義也是類似,不過這里的向量時(shí)列優(yōu)先,在Eigen中行優(yōu)先的矩陣會(huì)在其名字中包含有row,否則就是列優(yōu)先。
4)向量只是一個(gè)特殊的矩陣,其一個(gè)維度為1而已,如:typedef Matrix< double , 3 , 1> Vector3d
3、矩陣元素的訪問
在矩陣的訪問中,行索引總是作為第一個(gè)參數(shù),需注意Eigen中遵循大家的習(xí)慣讓矩陣、數(shù)組、向量的下標(biāo)都是從0開始。矩陣元素的訪問可以通過()操作符完成,例如m(2,3)即是獲取矩陣m的第2行第3列元素(注意行列數(shù)從0開始)。可參看如下代碼:
using namespace Eigen;int main(){MatrixXd m(2,2);m(0,0) = 3;m(1,0) = 2.5;m(0,1) = -1;m(1,1) = m(1,0) + m(0,1);std::cout << "Here is the matrix m:\n" << m << std::endl;VectorXd v(2);v(0) = 4;v(1) = v(0) - 1;std::cout << "Here is the vector v:\n" << v << std::endl;}其輸出結(jié)果為:Here is the matrix m:? 3? -12.5 1.5Here is the vector v:43
針對向量還提供[]操作符,注意矩陣則不可如此使用,原因?yàn)?#xff1a;在C++中m[i, j]中逗號(hào)表達(dá)式 “i, j”的值始終都是“j”的值,即m[i, j]對于C++來講就是m[j];
4、設(shè)置矩陣的元素
在Eigen中重載了"<<"操作符,通過該操作符即可以一個(gè)一個(gè)元素的進(jìn)行賦值,也可以一塊一塊的賦值。另外也可以使用下標(biāo)進(jìn)行復(fù)制,例如下面兩段代碼:
代碼段1
Matrix3f m;m << 1, 2, 3,4, 5, 6,7, 8, 9;std::cout << m;輸出結(jié)果為:1 2 34 5 67 8 9代碼段二(使用下標(biāo)進(jìn)行復(fù)制)VectorXf m_Vector_A;MatrixXf m_matrix_B;int m_iN =-1;bool InitData(int pSrc[100][100], int iWidth, int iHeight){?if (NULL == pSrc || iWidth <=0 || iHeight <= 0)??return false;?m_iN = iWidth*iHeight;?VectorXf tmp_A(m_iN);?MatrixXf tmp_B(m_iN, 5);?int i =0, j=0, iPos =0;?while(i<iWidth)?{?? j=0;??while(j<iHeight)??{???tmp_A(iPos) = pSrc[i][j] * log((float)pSrc[i][j]);???tmp_B(iPos,0) = pSrc[i][j] ;???tmp_B(iPos,1) = pSrc[i][j] * i;???tmp_B(iPos,2) = pSrc[i][j] * j;???tmp_B(iPos,3) = pSrc[i][j] * i * i;???tmp_B(iPos,4) = pSrc[i][j] * j * j;???++iPos;???++j;??}??++i;?}?m_Vector_A = tmp_A;?m_matrix_B = tmp_B;}5、重置矩陣大小當(dāng)前矩陣的行數(shù)、列數(shù)、大小可以通過rows(),cols()和size()來獲取,對于動(dòng)態(tài)矩陣可以通過resize()函數(shù)來動(dòng)態(tài)修改矩陣的大小.需注意:(1)?固定大小的矩陣是不能使用resize()來修改矩陣的大小;(2)?resize()函數(shù)會(huì)析構(gòu)掉原來的數(shù)據(jù),因此調(diào)用resize()函數(shù)之后將不能保證元素的值不改變。
(3) 使用“=”操作符操作動(dòng)態(tài)矩陣時(shí),如果左右邊的矩陣大小不等,則左邊的動(dòng)態(tài)矩陣的大小會(huì)被修改為右邊的大小。例如下面的代碼段:MatrixXf a(2,2);std::cout << "a is of size " << a.rows() << "x" << a.cols() << std::endl;MatrixXf b(3,3);a = b;std::cout << "a is now of size " << a.rows() << "x" << a.cols() << std::endl;輸出結(jié)果為:a is of size 2x2a is now of size 3x3
6、如何選擇動(dòng)態(tài)矩陣和靜態(tài)矩陣?Eigen對于這問題的答案是:對于小矩陣(一般大小小于16)的使用固定大小的靜態(tài)矩陣,它可以帶來比較高的效率,對于大矩陣(一般大小大于32)建議使用動(dòng)態(tài)矩陣。
還需特別注意的是:如果特別大的矩陣使用了固定大小的靜態(tài)矩陣則可能造成棧溢出的問題
---------------------------------------------------------------------------------------------本文主要是Eigen中矩陣和向量的算術(shù)運(yùn)算,在Eigen中的這些算術(shù)運(yùn)算重載了C++的+,-,*,所以使用起來非常方便。
1、矩陣的運(yùn)算
Eigen提供+、-、一元操作符“-”、+=、-=,例如:
二元操作符+/-表示兩矩陣相加(矩陣中對應(yīng)元素相加/減,返回一個(gè)臨時(shí)矩陣): B+C 或?B-C;
一元操作符-表示對矩陣取負(fù)(矩陣中對應(yīng)元素取負(fù),返回一個(gè)臨時(shí)矩陣):?-C;?
組合操作法+=或者-=表示(對應(yīng)每隔元素都做相應(yīng)操作):A += B 或者 A-=B
代碼段1為矩陣的加減操作,代碼如下:
using namespace Eigen;int main(){Matrix2d a;a << 1, 2,3, 4;MatrixXd b(2,2);b << 2, 3,1, 4;std::cout << "a + b =\n" << a + b << std::endl;std::cout << "a - b =\n" << a - b << std::endl;std::cout << "Doing a += b;" << std::endl;a += b;std::cout << "Now a =\n" << a << std::endl;Vector3d v(1,2,3);Vector3d w(1,0,0);std::cout << "-v + w - v =\n" << -v + w - v << std::endl;}輸出結(jié)果為:a + b =3 54 8a - b =-1 -1?2? 0Doing a += b;Now a =3 54 8-v + w - v =-1-4-6另外,矩陣還提供與標(biāo)量(單一個(gè)數(shù)字)的乘除操作,表示每個(gè)元素都與該標(biāo)量進(jìn)行乘除操作。例如:
二元操作符*在:A*a中表示矩陣A中的每隔元素都與數(shù)字a相乘,結(jié)果放在一個(gè)臨時(shí)矩陣中,矩陣的值不會(huì)改變。
對于a*A、A/a、A*=a、A /=a也是一樣,例如下面的代碼:
using namespace Eigen;int main(){Matrix2d a;a << 1, 2,3, 4;Vector3d v(1,2,3);std::cout << "a * 2.5 =\n" << a * 2.5 << std::endl;std::cout << "0.1 * v =\n" << 0.1 * v << std::endl;std::cout << "Doing v *= 2;" << std::endl;v *= 2;std::cout << "Now v =\n" << v << std::endl;}輸出結(jié)果為:a * 2.5 =2.5? 57.5 100.1 * v =0.10.20.3Doing v *= 2;Now v =246需要注意:
在Eigen中,算術(shù)操作例如 “操作符+”并不會(huì)自己執(zhí)行計(jì)算操作,他們只是返回一個(gè)“算術(shù)表達(dá)式對象”,而實(shí)際的計(jì)算則會(huì)延遲到后面的賦值時(shí)才進(jìn)行。這些不影響你的使用,它只是為了方便Eigen的優(yōu)化。
2、求矩陣的轉(zhuǎn)秩、共軛矩陣、伴隨矩陣。
可以通過?成員函數(shù)transpose(),?conjugate(),和?adjoint()來完成,注意這些函數(shù)返回操作后的結(jié)果,而不會(huì)對原矩陣的元素進(jìn)行直接操作,如果要讓原矩陣的進(jìn)行轉(zhuǎn)換,則需要使用響應(yīng)的InPlace函數(shù),例如:transposeInPlace()?、?adjointInPlace()?之類。
例如下面的代碼所示:
MatrixXcf a = MatrixXcf::Random(2,2);cout << "Here is the matrix a\n" << a << endl;cout << "Here is the matrix a^T\n" << a.transpose() << endl;cout << "Here is the conjugate of a\n" << a.conjugate() << endl;cout << "Here is the matrix a^*\n" << a.adjoint() << endl;輸出結(jié)果為:Here is the matrix a?(-0.211,0.68) (-0.605,0.823)?(0.597,0.566)? (0.536,-0.33)Here is the matrix a^T(-0.211,0.68) (0.597,0.566)(-0.605,0.823) (0.536,-0.33)Here is the conjugate of a?(-0.211,-0.68) (-0.605,-0.823)?(0.597,-0.566)??? (0.536,0.33)Here is the matrix a^*(-0.211,-0.68) (0.597,-0.566)(-0.605,-0.823)?? (0.536,0.33)矩陣的相乘,矩陣與向量的相乘也是使用操作符*,共有*和*=兩種操作符,其用法可以參考如下代碼:
using namespace Eigen;int main(){Matrix2d mat;mat << 1, 2,3, 4;Vector2d u(-1,1), v(2,0);std::cout << "Here is mat*mat:\n" << mat*mat << std::endl;std::cout << "Here is mat*u:\n" << mat*u << std::endl;std::cout << "Here is u^T*mat:\n" << u.transpose()*mat << std::endl;std::cout << "Here is u^T*v:\n" << u.transpose()*v << std::endl;std::cout << "Here is u*v^T:\n" << u*v.transpose() << std::endl;std::cout << "Let's multiply mat by itself" << std::endl;mat = mat*mat;std::cout << "Now mat is mat:\n" << mat << std::endl;}輸出結(jié)果為:Here is mat*mat:?7 1015 22Here is mat*u:11Here is u^T*mat:2 2Here is u^T*v:-2Here is u*v^T:-2 -0?2? 0Let's multiply mat by itselfNow mat is mat:?7 1015 22--------------------------------------------------------------------------------------------本節(jié)主要涉及Eigen的塊操作以及QR分解,Eigen的QR分解非常繞人,搞了很久才搞明白是怎么回事,最后是一個(gè)使用Eigen的矩陣操作完成二維高斯擬合求取光點(diǎn)的代碼例子,關(guān)于二維高斯擬合求取光點(diǎn)的詳細(xì)內(nèi)容可參考:http://blog.csdn.net/hjx_1000/article/details/8490653
1、矩陣的塊操作
? ? ? ? 1)矩陣的塊操作有兩種使用方法,其定義形式為:
matrix.block(i,j,p,q);????? (1)matrix.block<p,q>(i,j);??? (2)定義(1)表示返回從矩陣的(i, j)開始,每行取p個(gè)元素,每列取q個(gè)元素所組成的臨時(shí)新矩陣對象,原矩陣的元素不變。定義(2)中block(p, q)可理解為一個(gè)p行q列的子矩陣,該定義表示從原矩陣中第(i, j)開始,獲取一個(gè)p行q列的子矩陣,返回該子矩陣組成的臨時(shí) 矩陣對象,原矩陣的元素不變。
詳細(xì)使用情況,可參考下面的代碼段:
using namespace std;int main(){Eigen::MatrixXf m(4,4);m << 1, 2, 3, 4,5, 6, 7, 8,9,10,11,12,13,14,15,16;cout << "Block in the middle" << endl;cout << m.block<2,2>(1,1) << endl << endl;for (int i = 1; i <= 3; ++i){cout << "Block of size " << i << "x" << i << endl;cout << m.block(0,0,i,i) << endl << endl;}}輸出的結(jié)果為:Block in the middle?6? 710 11Block of size 1x11Block of size 2x21 25 6Block of size 3x3?1? 2? 3?5? 6? 7?9 10 11通過上述方式獲取的子矩陣即可以作為左值也可以作為右值,也就是即可以用這個(gè)子矩陣給其他矩陣賦值,也可以給這個(gè)子矩陣對象賦值。2)矩陣也提供了獲取其指定行/列的函數(shù),其實(shí)獲取某行/列也是一種特殊的獲取子塊。可以通過 .col()和 .row()來完成獲取指定列/行的操作,參數(shù)為列/行的索引。
注意:
(1)需與獲取矩陣的行數(shù)/列數(shù)的函數(shù)( rows(), cols() )的進(jìn)行區(qū)別,不要弄混淆。
(2)函數(shù)參數(shù)為響應(yīng)行/列的索引,需注意矩陣的行列均以0開始。
下面的代碼段用于演示獲取矩陣的指定行列:using namespace std;int main(){Eigen::MatrixXf m(3,3);m << 1,2,3,4,5,6,7,8,9;cout << "Here is the matrix m:" << endl << m << endl;cout << "2nd Row: " << m.row(1) << endl;m.col(2) += 3 * m.col(0);cout << "After adding 3 times the first column into the third column, the matrix m is:\n";cout << m << endl;}輸出結(jié)果為:
Here is the matrix m:1 2 34 5 67 8 92nd Row: 4 5 6After adding 3 times the first column into the third column, the matrix m is:?1? 2? 6?4? 5 18?7? 8 303)向量的塊操作,其實(shí)向量只是一個(gè)特殊的矩陣,但是Eigen也為它單獨(dú)提供了一些簡化的塊操作,如下三種形式:
獲取向量的前n個(gè)元素:vector.head(n);?
獲取向量尾部的n個(gè)元素:vector.tail(n);
獲取從向量的第i個(gè)元素開始的n個(gè)元素:vector.segment(i,n);
其用法可參考如下代碼段:using namespace std;int main(){Eigen::ArrayXf v(6);v << 1, 2, 3, 4, 5, 6;cout << "v.head(3) =" << endl << v.head(3) << endl << endl;cout << "v.tail<3>() = " << endl << v.tail<3>() << endl << endl;v.segment(1,4) *= 2;cout << "after 'v.segment(1,4) *= 2', v =" << endl << v << endl;}輸出結(jié)果為:
v.head(3) =123v.tail<3>() = 456after 'v.segment(1,4) *= 2', v =1468106
2、QR分解
? ? ? ? Eigen的QR分解非常繞人,它總共提供了下面這些矩陣的分解方式:
| PartialPivLU | partialPivLu() | Invertible | ++ | + |
| FullPivLU | fullPivLu() | None | - | +++ |
| HouseholderQR | householderQr() | None | ++ | + |
| ColPivHouseholderQR | colPivHouseholderQr() | None | + | ++ |
| FullPivHouseholderQR | fullPivHouseholderQr() | None | - | +++ |
| LLT | llt() | Positive definite | +++ | + |
| LDLT | ldlt() | Positive or negative semidefinite | +++ | ++ |
3、一個(gè)矩陣使用的例子:用矩陣操作完成二維高斯擬合,并求取光斑中心
下面的代碼段是一個(gè)使用Eigen的矩陣操作完成二維高斯擬合求取光點(diǎn)的代碼例子,關(guān)于二維高斯擬合求取光點(diǎn)的詳細(xì)內(nèi)容可參考:http://blog.csdn.net/hjx_1000/article/details/8490653
http://blog.csdn.net/houjixin/article/details/8490941
http://blog.csdn.net/houjixin/article/details/8492841
http://blog.csdn.net/houjixin/article/details/8494582
再分享一下我老師大神的人工智能教程吧。零基礎(chǔ)!通俗易懂!風(fēng)趣幽默!還帶黃段子!希望你也加入到我們?nèi)斯ぶ悄艿年?duì)伍中來!https://blog.csdn.net/jiangjunshow
總結(jié)
以上是生活随笔為你收集整理的Eigen C++开源矩阵计算工具——Eigen的简单用法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 医药行业的契约锁电子签章应用场景:印章统
- 下一篇: 会员连锁配置以及金额走向