一文學(xué)會(huì)Eigen庫
添加微信:CV3d007,備注:SLAM,拉你入群。文末附行業(yè)細(xì)分群。
00 Eigen簡介
Eigen:基于線性代數(shù)的C ++模板庫,主要用于矩陣,向量,數(shù)值求解器和相關(guān)算法。SLAM中常用的Ceres、G2O等項(xiàng)目均是基于Eigen庫。
Eigen庫的優(yōu)點(diǎn):
支持整數(shù)、浮點(diǎn)數(shù)、復(fù)數(shù),使用模板編程,可以為特殊的數(shù)據(jù)結(jié)構(gòu)提供矩陣操作。
OpenCV自帶到Eigen的接口。
支持逐元素、分塊、和整體的矩陣操作。
支持使用Intel MKL加速部分功能。
支持多線程,對稀疏矩陣支持良好。
支持常用幾何運(yùn)算,包括旋轉(zhuǎn)矩陣、四元數(shù)、矩陣變換、角軸等等。
即使不做SLAM,在3D視覺中,當(dāng)處理大量數(shù)學(xué)運(yùn)算時(shí),我們也會(huì)用到Eigen庫,它幫我們優(yōu)化了性能。在安裝完成Eigen庫后,開始接下來的學(xué)習(xí)。
01 數(shù)據(jù)類型
Eigen庫的核心類是 Matrix,由6個(gè)參數(shù)構(gòu)成:
Matrix<????????typename?Scalar,????????int?RowsAtCompileTime,????????int?ColsAtCompileTime,????????int?Options?=?0,???????????????????????????????//?默認(rèn)(無需更改)????????int?MaxRowsAtCompileTime?=?RowsAtCompileTime,??//?默認(rèn)(最大行數(shù),提前知道極限)????????int?MaxColsAtCompileTime?=?ColsAtCompileTime???//?默認(rèn)(最大列數(shù),提前知道極限)>
其中:
前三個(gè)參數(shù):需要我們指定
后三個(gè)參數(shù):默認(rèn)即可,無需指定
因?yàn)榻?jīng)常需要實(shí)例化一些方陣、向量,因此Eigen庫也提供了很多直接使用的模板(利用C++的關(guān)鍵字:typedef),例如 Matrix4f 是 的float型矩陣:
typedef?Matrix<float,?4,?4>?Matrix4f;
還有例如列向量:Vector3f ,其本質(zhì)也是 Matrix 類:
typedef?Matrix<?float,?3,?1?>?Vector3f;
行向量RowVector:
typedef?Matrix<int,?1,?2>?RowVector2i;
靜態(tài)-動(dòng)態(tài)-矩陣
靜態(tài)矩陣:矩陣是靜態(tài)的,即編譯時(shí)候就知道運(yùn)行結(jié)果,例如Matrix3d:表示元素類型為double大小為3*3的矩陣變量,其大小在編譯時(shí)就知道。
動(dòng)態(tài)矩陣:有時(shí)候運(yùn)行完之后,才可以知道,這里使用MatrixXd:表示任意大小的元素類型為double的矩陣變量,其大小只有在運(yùn)行被賦值之后才能知道;
數(shù)據(jù)類型
Eigen中的矩陣類型一般都是用類似MatrixNX來表示,可以根據(jù)該名字來判斷其大?。?,3,4,或X,意思Dynamic)和數(shù)據(jù)類型,比如:
d:表示double類型
f:表示float類型
i:表示整數(shù)
c:表示復(fù)數(shù);
舉例:Matrix2f,表示的是一個(gè)維的,其每個(gè)元素都是float類型。
02 新建矩陣
矩陣構(gòu)造
默認(rèn)構(gòu)造,分配了大小和內(nèi)存空間,但沒有初始化矩陣元素(里面的數(shù)值是隨機(jī)的,不能使用):
Matrix3f?a;?//?3*3的元素,其中還有一個(gè)float[9]數(shù)組,其中的元素沒有初始化;MatrixXf?b;?//?動(dòng)態(tài)大小的矩陣,目前的大小是0*0,它的元素?cái)?shù)組完全沒有分配。
對于動(dòng)態(tài)數(shù)組,你也可以直接分配大?。ㄊプ饔昧耍瑯記]有初始化矩陣元素:
MatrixXf?a(10,?15);//?10x15動(dòng)態(tài)矩陣,數(shù)組內(nèi)存已經(jīng)分配,但是沒有初始化;VectorXf?b(30);???//?大小為30的向量,數(shù)組內(nèi)存已經(jīng)分配,但是元素沒有初始化。
或者更通用的:
Matrix<?float,?3,?1?>?Vector3f_def;
矩陣初始化
在構(gòu)造完后,我們需要對元素進(jìn)行初始化,常用的是直接賦值:
Eigen::Matrix3f?m;?m?<<?1,?2,?3,?????4,?5,?6,?????7,?8,?9;
它是逐行寫入的,這只適用于較小的矩陣:
Eigen::MatrixXd?m(3,3);m?<<1,2,3,?????4,5,6,?????7,8,9;
對于向量,還可以在構(gòu)造的時(shí)候初始化:
Vector3d?v(1,?2,?3);Vector3d?w(1,?0,?0);
還有一些特殊函數(shù),函數(shù):
MatrixXf::Zero(3,4);?????//?將矩陣3行4列初始化為0?MatrixXf::Ones(3,3);?????//?將矩陣3行3列初始化為1?Vector3f::Ones();????????//?將3行的縱向量初始化為1?MatrixXi::Identity(3,3);?//?單位矩陣?Matrix3d::Random();??????//?隨機(jī)矩陣
03 矩陣索引
當(dāng)前矩陣的行數(shù)、列數(shù)、大小可以通過rows()、cols()和size()來獲取。遍歷Eigen矩陣時(shí)最好通過rows和cols來限制訪問范圍,索引的方法如下:
1、矩陣訪問按照先行索引、后列索引方式進(jìn)行,索引下標(biāo)從0開始(與Matlab不同);
2、矩陣元素的訪問可以通過**”( )”操作符完成。例如m(2, 3)**,矩陣m的第2行第3列元素;
3、針對向量還提供”**[ ]”操作符,注意矩陣則不可**如此使用。
resize:不同于matlab、Python,對于動(dòng)態(tài)矩陣雖然可以通過resize()函數(shù)來動(dòng)態(tài)修改矩陣的大小,但是需要說明的是,在Eigen中:
不能用:固定大小的矩陣是不能使用resize()來修改矩陣的大??;
數(shù)據(jù)會(huì)變:resize()函數(shù)會(huì)析構(gòu)掉原來的數(shù)據(jù),變?yōu)?.,因此最好使用:conservativeResize()函數(shù)
大小修改:使用”=”操作符操作動(dòng)態(tài)矩陣時(shí),如果左右兩邊的矩陣大小不等,則左邊的動(dòng)態(tài)矩陣的大小會(huì)被修改為右邊的大小。
利用block()函數(shù),可以從Matrix中取出一個(gè)小矩陣來進(jìn)行處理,使用的語法為:
matrix.block<p,q>(i,j);
例如:
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;}//?Output?is://?Block?in?the?middle//??6??7//?10?11//?Block?of?size?1x1//?1//?Block?of?size?2x2//?1?2//?5?6//?Block?of?size?3x3//??1??2??3//??5??6??7//??9?10?11
單獨(dú)的列和行是塊的特殊情況。Eigen提供了可以輕松解決它們的方法:.col()和.row():
Eigen::MatrixXi?m(2,?2);m?<<?1,?2,?3,?4;cout?<<?m.col(0)?<<?endl;//??1?3
04 數(shù)學(xué)運(yùn)算
Eigen幫我們重載了,直接運(yùn)算:
Vector3d?v(1,?2,?3);Vector3d?w(1,?0,?0);cout?<<?v?+?w?<<?endl;
除法:通常我們是除以標(biāo)量。對于矩陣除法,我們是求它的逆,再轉(zhuǎn)換為矩陣乘法。因此較為簡單:
Vector3d?v(1,?2,?3);Vector3d?r?=?v?/?3;cout?<<?r?<<?endl;
矩陣乘法:*
乘法,標(biāo)量非常簡單:
cout?<<?v?*?2?<<?endl;v?*=?2;??//?原地操作
Matrix2d?mat;mat?<<?1,?2,????3,?4;Vector2d?u(-1,?1),?v(2,?0);//?矩陣乘法?乘以矩陣std::cout?<<?"Here?is?mat*mat: "??????????<<?mat?*?mat?<<?std::endl;//?矩陣乘法?乘以向量std::cout?<<?"Here?is?mat*u: "??????????<<?mat?*?u?<<?std::endl;//?轉(zhuǎn)置之后,再矩陣乘法std::cout?<<?"Here?is?u^T*mat: "??????????<<?u.transpose()?*?mat?<<?std::endl;//?轉(zhuǎn)置之后,向量的矩陣乘法std::cout?<<?"Here?is?u^T*v: "??????????<<?u.transpose()?*?v?<<?std::endl;std::cout?<<?"Here?is?u*v^T: "??????????<<?u?*?v.transpose()?<<?std::endl;//?矩陣乘法std::cout?<<?"Let's?multiply?mat?by?itself"?<<?std::endl;mat?=?mat?*?mat;std::cout?<<?"Now?mat?is?mat: "??????????<<?mat?<<?std::endl;//Output?is://?Here?is?mat*mat://??7?10//?15?22//?Here?is?mat*u://?1//?1//?Here?is?u^T*mat://?2?2//?Here?is?u^T*v://?-2//?Here?is?u*v^T://?-2?-0//??2??0//?Let's?multiply?mat?by?itself//?Now?mat?is?mat://??7?10//?15?22
補(bǔ)充:轉(zhuǎn)置
向量、矩陣的乘法,因?yàn)樾枰?strong>size一致,因此需要用到轉(zhuǎn)置:
MatrixXcf?a?=?MatrixXcf::Random(2,?2);?//MatrixXcf?為復(fù)數(shù)矩陣cout?<<?"Here?is?the?matrix?a "?<<?a?<<?endl;//?矩陣轉(zhuǎn)置cout?<<?"Here?is?the?matrix?a^T "?<<?a.transpose()?<<?endl;//?共軛矩陣cout?<<?"Here?is?the?conjugate?of?a "?<<?a.conjugate()?<<?endl;//?共軛轉(zhuǎn)置矩陣cout?<<?"Here?is?the?matrix?a^* "?<<?a.adjoint()?<<?endl;
需要說明的是,在Eigen中,對于自身的操作,都有專門的函數(shù),例如對自身的轉(zhuǎn)置:
?a.transposeInPlace();?//?直接在a上操作
點(diǎn)乘和叉乘
Vector3d?v(1,?2,?3);Vector3d?w(0,?1,?2);//?點(diǎn)乘cout?<<?"Dot?product:?"?<<?v.dot(w)?<<?endl;//?叉乘cout?<<?"Cross?product: "?<<?v.cross(w)?<<?endl;//?點(diǎn)成結(jié)果Dot?product:?8??//?1?*?0?+?2?*?1?+?3?*?2=8????Cross?product:??1??//?2?*?2?-?1?*?3?=??1-2??//?3?*?0?-?1?*?2?=?-2?1??//?1?*?1?-?0?*?2?=??1
在Eigen中,向量的叉乘只支持三維的向量,這是因?yàn)椴娉送ǔS糜谟?jì)算方向、夾角等,它的計(jì)算規(guī)則如下:
//?Eigen?also?provides?some?reduction?operations?to?reduce?a?given?matrix?or?vector?to?a?single?value//?such?as?the?sum?(computed?by?sum()),?product?(prod()),?or?the?maximum?(maxCoeff())?and?minimum?(minCoeff())?of?all?its?coefficients.Eigen::Matrix2d?mat;mat?<<?1,?2,???????3,?4;//元素和,元素乘積,元素均值,最小系數(shù),最大系數(shù),蹤cout?<<?"Here?is?mat.sum():???????"?<<?mat.sum()?<<?endl;cout?<<?"Here?is?mat.prod():??????"?<<?mat.prod()?<<?endl;cout?<<?"Here?is?mat.mean():??????"?<<?mat.mean()?<<?endl;cout?<<?"Here?is?mat.minCoeff():??"?<<?mat.minCoeff()?<<?endl;cout?<<?"Here?is?mat.maxCoeff():??"?<<?mat.maxCoeff()?<<?endl;cout?<<?"Here?is?mat.trace():?????"?<<?mat.trace()?<<?endl;//?可以返回元素位置Matrix3f?m?=?Matrix3f::Random();std::ptrdiff_t?i,?j;??//?std::ptrdiff_t?是二個(gè)指針相減結(jié)果的有符號整數(shù)類型float?minOfM?=?m.minCoeff(&i,?&j);cout?<<?"Here?is?the?matrix?m: "?????<<?m?<<?endl;cout?<<?"Its?minimum?coefficient?("?<<?minOfM?????<<?")?is?at?position?("?<<?i?<<?","?<<?j?<<?") ";RowVector4i?v?=?RowVector4i::Random();int?maxOfV?=?v.maxCoeff(&i);cout?<<?"Here?is?the?vector?v:?"?<<?v?<<?endl;cout?<<?"Its?maximum?coefficient?("?<<?maxOfV?????<<?")?is?at?position?"?<<?i?<<?endl;//?Output?is://?Here?is?mat.sum():???????10//?Here?is?mat.prod():??????24//?Here?is?mat.mean():??????2.5//?Here?is?mat.minCoeff():??1//?Here?is?mat.maxCoeff():??4//?Here?is?mat.trace():?????5//?Here?is?the?matrix?m://??-0.444451???0.257742???0.904459//????0.10794??-0.270431????0.83239//?-0.0452059??0.0268018???0.271423//?Its?minimum?coefficient?(-0.444451)?is?at?position?(0,0)
05 通用數(shù)組
Array類提供了通用數(shù)組。此外,Array類提供了一種執(zhí)行逐系數(shù)運(yùn)算的簡便方法,該運(yùn)算可能沒有線性代數(shù)含義,例如將常數(shù)添加到數(shù)組中的每個(gè)系數(shù)或按系數(shù)乘兩個(gè)數(shù)組。
注:Eigen計(jì)算三角函數(shù)等,Matrix并不支持,需要通過.array() 轉(zhuǎn)換到Array類,再計(jì)算!
m1.array().atan();
常見數(shù)據(jù)類型
Array<float,Dynamic,1>???????????????????ArrayXfArray<float,3,1>?????????????????????????Array3fArray<double,Dynamic,Dynamic>????????????ArrayXXdArray<double,3,3>????????????????????????Array
常見操作:
//?逐元素操作Vectorized?operations?on?each?element?independently??//?Eigen???????????????????????//?Matlab????????//注釋??R?=?P.cwiseProduct(Q);?????????//?R?=?P?.*?Q????//逐元素乘法??R?=?P.array()?*?s.array();?????//?R?=?P?.*?s????//逐元素乘法(s為標(biāo)量)??R?=?P.cwiseQuotient(Q);????????//?R?=?P?./?Q????//逐元素除法??R?=?P.array()?/?Q.array();?????//?R?=?P?./?Q????//逐元素除法??R?=?P.array()?+?s.array();?????//?R?=?P?+?s?????//逐元素加法(s為標(biāo)量)??R?=?P.array()?-?s.array();?????//?R?=?P?-?s?????//逐元素減法(s為標(biāo)量)??R.array()?+=?s;????????????????//?R?=?R?+?s?????//逐元素加法(s為標(biāo)量)??R.array()?-=?s;????????????????//?R?=?R?-?s?????//逐元素減法(s為標(biāo)量)??R.array()?<?Q.array();?????????//?R?<?Q?????????//逐元素比較運(yùn)算??R.array()?<=?Q.array();????????//?R?<=?Q????????//逐元素比較運(yùn)算??R.cwiseInverse();??????????????//?1?./?P????????//逐元素取倒數(shù)??R.array().inverse();???????????//?1?./?P????????//逐元素取倒數(shù)??R.array().sin()????????????????//?sin(P)????????//逐元素計(jì)算正弦函數(shù)??R.array().cos()????????????????//?cos(P)????????//逐元素計(jì)算余弦函數(shù)??R.array().pow(s)???????????????//?P?.^?s????????//逐元素計(jì)算冪函數(shù)??R.array().square()?????????????//?P?.^?2????????//逐元素計(jì)算平方??R.array().cube()???????????????//?P?.^?3????????//逐元素計(jì)算立方??R.cwiseSqrt()??????????????????//?sqrt(P)???????//逐元素計(jì)算平方根??R.array().sqrt()???????????????//?sqrt(P)???????//逐元素計(jì)算平方根??R.array().exp()????????????????//?exp(P)????????//逐元素計(jì)算指數(shù)函數(shù)??R.array().log()????????????????//?log(P)????????//逐元素計(jì)算對數(shù)函數(shù)??R.cwiseMax(P)??????????????????//?max(R,?P)?????//逐元素計(jì)算R和P的最大值??R.array().max(P.array())???????//?max(R,?P)?????//逐元素計(jì)算R和P的最大值??R.cwiseMin(P)??????????????????//?min(R,?P)?????//逐元素計(jì)算R和P的最小值??R.array().min(P.array())???????//?min(R,?P)?????//逐元素計(jì)算R和P的最小值??R.cwiseAbs(P)???????????????????//?abs(P)????????//逐元素計(jì)算R和P的絕對值??R.array().abs()????????????????//?abs(P)????????//逐元素計(jì)算絕對值??R.cwiseAbs2()??????????????????//?abs(P.^2)?????//逐元素計(jì)算平方??R.array().abs2()???????????????//?abs(P.^2)?????//逐元素計(jì)算平方??(R.array()?<?s).select(P,Q);??//?(R?<?s???P?:?Q)?????????//根據(jù)R的元素值是否小于s,選擇P和Q的對應(yīng)元素??R?=?(Q.array()==0).select(P,A)?//?R(Q==0)?=?P(Q==0)?R(Q!=0)?=?P(Q!=0)??????//根據(jù)Q中元素等于零的位置選擇P中元素??R?=?P.unaryExpr(ptr_fun(func))?//?R?=?arrayfun(func,?P)?????//?對P中的每個(gè)元素應(yīng)用func函數(shù)
06 更多操作
對于Eigen,它適合一個(gè)簡單的數(shù)值計(jì)算庫,并沒有什么實(shí)用技巧。其實(shí)大多數(shù)時(shí)候,你只需要利用Google和百度去查詢你需要的操作即可!對于更多的操作,可以參考:Eigen 常用函數(shù)查詢,對比MatLab操作 。
—END—
目前工坊已經(jīng)建立了3D視覺方向多個(gè)社群,包括SLAM、工業(yè)3D視覺、自動(dòng)駕駛方向,細(xì)分群包括:[工業(yè)方向]三維點(diǎn)云、結(jié)構(gòu)光、機(jī)械臂、缺陷檢測、三維測量、TOF、相機(jī)標(biāo)定、綜合群;[SLAM方向]多傳感器融合、ORB-SLAM、激光SLAM、機(jī)器人導(dǎo)航、RTK|GPS|UWB等傳感器交流群、SLAM綜合討論群;[自動(dòng)駕駛方向]深度估計(jì)、Transformer、毫米波|激光雷達(dá)|視覺攝像頭傳感器討論群、多傳感器標(biāo)定、自動(dòng)駕駛綜合群等。[三維重建方向]NeRF、colmap、OpenMVS等。除了這些,還有求職、硬件選型、視覺產(chǎn)品落地等交流群。大家可以添加小助理微信: CV3d007,備注:加群+方向+學(xué)校|公司, 小助理會(huì)拉你入群。