《視覺SLAM十四講》(第二版)學習筆記


CH1:

clion的使用

斷點設置之后,逐行執行 (Step Into) ( F7 ),逐函數執行 (Step Over) ( F8 ),注意0和O在clion中的顯示區別,0是中間有個點的,O看起來像0,中間沒有原點

Run->Edit Configurations-> Program arugements可直接加入程序所需的文件等等,便於debug的使用 如ch5的圖片

Run->Edit Configurations-> Working Directory 可直接加入fstream所需的工作目錄,便於debug或者run的使用 如ch4的軌跡文件

TODO 類似與帶書簽屬性的注釋

在Clion中打開程序: Open File or Project ->選中父目錄,直接搜索待打開的目錄名字即可,會自動進入查找模式,選中之后,Tab+Enter即可

 

CH2:

文件夾的命名和作用:

library是庫文件 include是頭文件 src是源文件(該源文件不一定是可執行程序的所需的源文件,也可以是生成所需的內部庫的源文件)

CMakeLists.txt:

library是庫的意思 include是包含 directory是目錄  

include_directories() 添加目錄

一般find_package()下緊挨着include_directories() 找到外部庫之后添加目錄到工程中

find_package(OpenCV 3 REQUIRED)    include_directories(${OpenCV_INCLUDE_DIRS})

注意:OpenCV大小寫和${NAME_INCLUDE_DIRS}  在 target_link_libraries( ... ${NAME_LIBS}) DIRS和LIBS

在ORB_SLAM的CMakeLists中 使用${PROJECT_NAME} 變量作為中間量,把ORB_SLAM中所需的內部庫和外部庫一切連接起來

set(CMAKE_CXX_STANDARD 11)是高版本 

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")是低版本 

在set(CMAKE_BUILD_TYPE "Release")如果要是用斷點 修改"Release"為"Debug"。

同時,如果有set(CMAKE_CXX_FLAGS "-O3")也要去掉"-O3"或者改成"-O0" "-O3"表示優化程度最高 "-O0"表示不需要優化

find_package具體用法https://www.jianshu.com/p/39fc5e548310

有主工程下的CMakeLists.txt直接編譯此文件即可,無需編譯子目錄的CMakeLists.txt


CH3:

sudo apt-get install libeigen3-dev :

libeigen3-dev lib表示庫 eigen3就是軟件名稱和版本 dev表示開發包 安裝到/usr/include/目錄中

eigen求矩陣的特征值和特征向量的時候,得是實對稱矩陣,才能保證對角化的成功。

.cast<double> 是顯式轉換的意思

對於eigen的出錯,因為是模板類,所以錯誤不是很好讀,但是可以在錯誤中找到 required from here 有對應的錯誤代碼行提示的。

CH3/examples/plotTrajectory.cpp:

按照git下clone的文件 由於在ch3/下有CMakeLists.txt 直接在ch3/目錄下編譯,並且不切換到其他目錄,直接在該目錄下執行: cmake-build-debug/examples/plotTrajectory 就會成功編譯。是因為trajectory_file的路徑顯示examples/trajectory.txt,而在編譯的時候(無論是build還是cmake-build-debug)都是默認在當前目錄下尋找./examples/trajectory.txt。所以無論是在example/目錄下還是正確編譯之后(cmake-build-debug/examples/plotTrajectory),只要運行可執行程序時,對應不到./examples/trajectory.txt都會報錯。當然,也可以更改trajectory_file的路徑。

如果STL容器中的元素是Eigen庫數據結構,例如這里定義一個vector容器,元素是Matrix4d 則vector<Eigen::Matrix4d>這個錯誤也是和上述一樣的提示,編譯不會出錯,只有在運行的時候出錯。解決的方法很簡單,定義改成下面的方式:vector<Eigen::Matrix4d, Eigen::aligned_allocator<Eigen::Matrix4d>>;其實上述的這段代碼才是標准的定義容器方法,只是我們一般情況下定義容器的元素都是C++中的類型,所以可以省略,這是因為在C++11標准中,aligned_allocator管理C++中的各種數據類型的內存方法是一樣的,可以不需要着重寫出來。但是在Eigen管理內存和C++11中的方法是不一樣的,所以需要單獨強調元素的內存分配和管理。

pangolin::CreatWindowAndBind創建3D窗口,()內含windows_title,width,height

glEnable:啟用或禁用服務器端GL功能 GL_DEPTH_TEST: GL_BLEND顏色的blend 

glBlendFunc:定義啟用所有繪制緩沖區的混合操作。總之就是為了畫圖的

 

CH4:

SLAM十四講第一版用的sophus是非模板,第二版用的是模板類。模板類是基於eigen3.2.92版本,非模板是基於eigen3.3.7。雖然書上說Sophus不用安裝,但是有時程序會找不到頭文件,還是需要sudo make install

sophus是一個很小的庫,不用安裝,完全可以集成在工程中,find_package 是尋找SophusConfig.cmake文件,這里面說明了你的Sophus庫安裝在哪里,如果只是make了,沒有make install,那把Sophus的目錄就放在當前項目根目錄中,這個工程中的find_package會在當前目錄中找這個配置文件

由於系統既安裝了模板類,也安裝了非模板類的sophus 因此在使用模板類時,正常使用 而使用非模板類時,需將target_link_libraries中的${Sophus_LIBRARIES}改為具體路徑,如/usr/local/lib/libSophus.so 等(在sophus非模板的源文件中,找到SophusConfig.cmake中,對應的${Sophus_LIBRARIES}變量即可)

因為sophus是基於eigen的,所以R或者T也可以用SO3和SE3表示更優。

ch4/trajectoryError.cpp: 

typedef:

1)定義一種類型的別名:char* pa, pb; // 這多數不符合我們的意圖,它只聲明了一個指向字符變量的指針和一個字符變量;

 typedef char* P; P pa,pb;//即可

2)struct tag{int x;int y;}; struct tag p1;可以改為 typedef struct tag{int x; int y;}P; P p1;

詳情見https://blog.csdn.net/andrewniu/article/details/80566324#commentBox

assert:預處理宏(所謂預處理宏就是一個預處理變量,行為有點類似與內聯函數)用法assert(expr); 如果expr的值為0/假,assert輸出信息並終止程序的執行。如果expr的值為1/真,assert什么也不做。

 

CH5:
CH5/imageBasics.cpp:

int main(int argc,char ** argv) //函數體內使用或不使用argc和argv都可

帶形參的main函數,如 main( int argc, char* argv[], char **env ) ,是UNIX、Linux以及Mac OS操作系統中C/C++的main函數標准寫法,並且是血統最純正的main函數寫法。 

第一個參數,int型的argc,為整型,用來統計程序運行時發送給main函數的命令行參數的個數,在VS中默認值為1。        

第二個參數,char*型的argv[],為字符串數組,用來存放指向的字符串參數的指針數組,每一個元素指向一個參數。各成員含義如下:        

argv[0]指向程序運行的全路徑名        

argv[1]指向在DOS命令行中執行程序名后的第一個字符串        

argv[2]指向執行程序名后的第二個字符串        

argv[3]指向執行程序名后的第三個字符串        

argv[argc]為NULL        

第三個參數,char**型的env,為字符串數組。env[]的每一個元素都包含ENVVAR=value形式的字符串,其中ENVVAR為環境變量,value為其對應的值。平時使用到的比較少。

詳細解釋內容見https://blog.csdn.net/dgreh/article/details/80985928


CV_8UC1:一般的圖像文件格式使用的是 Unsigned 8bits吧,CvMat矩陣對應的參數類型就是CV_8UC1,CV_8UC2,CV_8UC3。(最后的1、2、3表示通道數,譬如RGB3通道就用CV_8UC3)

而float 是32位的,對應CvMat數據結構參數就是:CV_32FC1,CV_32FC2,CV_32FC3...double是64bits,對應CvMat數據結構參數:CV_64FC1,CV_64FC2,CV_64FC3等。

https://blog.csdn.net/qq_29540745/article/details/52487832


size_t:size_t在C語言中就有了。它是一種"整型"類型,里面保存的是一個整數,就像int, long那樣。這種整數用來記錄一個大小(size)。size_t的全稱應該是size type,就是說"一種用來記錄大小的

數據類型",便於程序的移植。 

遍歷圖像:有兩層循環,第一層是循環圖像的行row,第二層是圖像的列column。該遍歷使用的是指針循環首先定義指針 指針類型為unsigned char 是屬於圖像指針image.ptr的一種,image.ptr的類型也為unsigned char, (y)表示第y行,因為是要跟着循環走。第二層循環定義了數據指針data_ptr 要跟着第一層循環,所以是&row_ptr[x* image.channels()]可能圖片是灰度圖、深度圖、或者彩色圖。對於灰色圖只有一個通道(其實通道這個概念是針對彩色圖而提出的),而對於彩色圖來說,有三或四個通道。進一步,還能輸出每個像素對應的多個各個通道的值。


在cv::Mat拷貝中加入cv::imshow("image_another",image_another);與cv::imshow("image",image);

對比可得知:淺拷貝也就是這段code 在修改image_another時,原image也相應的改變。而下一段code,就只改變image_clone圖片,原image不改變,就是所謂的深拷貝。


單目成像的過程:第一步,在世界坐標系下有一固定的點$P_W$
;第二步,由於相機在運動,$P_W$轉換成$P_W^\prime=T_{CW}P_W$(因為相機的位置就是機器人的位置,而對於成的像來說,就相當於路標,所以要切換到相機坐標系下);第三步,將$P_C^\prime$投影到歸一化平面上($Z=1$)變成 $P_C$,$\left [ X,Y,Z \right ] \to \left [ X/Z,Y/Z,1\right] $ $P_C=\left [ X/Z,Y/Z \right ]$;第四步,將$P_C$ 畸化 $P_{cdistorted}$,因為相機產生畸變(注意,這里所說的是畸變,看下角標distorted,去畸化是undistorted);第五步,$P_{Cdistorted}$左乘內參矩陣,得出像素矩陣。(內參矩陣包含坐標的放縮和像素原點的平移)。


ch5/undistort_image:

要求去畸化。計算去畸變后的圖像的內容:循環給出的最后一部分是將image(v_distorted,u_distorted)=image_undistorted(v,u),就是求得的distorted視為undistorted。像素坐標與歸一化坐標沒有問題。問題就是畸變和去畸變之間的轉變問題。


通常來說,假設v是一個vector對象,則v.at(n)和v[n]是一樣的,只不過前者會檢查是否越界(因此花費的時間稍多),而后者不會(后者越界會導致未定義行為)。


最近鄰插值
越是簡單的模型越適合用來舉例子,我們就舉個簡單的圖像:3X3 的256級灰度圖,也就是高為3個象素,寬也是3個象素的圖像,每個象素的取值可以是 0-255,代表該像素的亮度,255代表最亮,也就是白色,0代表最暗,即黑色。假如圖像的象素矩陣如下圖所示(這個原始圖把它叫做源圖,Source):

234   38    22

67     44    12

89     65    63

這個矩陣中,元素坐標(x,y)是這樣確定的,x從左到右,從0開始,y從上到下,也是從零開始,這是圖象處理中最常用的坐標系,就是這樣一個坐標: 

----------------------> X 

|

|

∨ Y

如果想把這副圖放大為 4X4大小的圖像,那么該怎么做呢?那么第一步肯定想到的是先把4X4的矩陣先畫出來再說,好了矩陣畫出來了,如下所示,當然,矩陣的每個像素都是未知數,等待着我們去填充(這個將要被填充的圖的叫做目標圖,Destination):

?        ?        ?       ?

?        ?        ?       ?

?        ?        ?       ?

?        ?        ?       ?                

然后要往這個空的矩陣里面填值了,要填的值從哪里來來呢?是從源圖中來,先填寫目標圖最左上角的象素,坐標為(0,0),那么該坐標對應源圖中的坐標可以由如下公式得出:    

$$X_{src}=X_{dst}*\left({Width}_{src}/{Width}_{dst}\right)\\Y_{src}=Y_{dst}*\left({Width}_{src}/{Width}_{dst}\right)$$

好了,套用公式,就可以找到對應的原圖的坐標了(0*(3/4),0*(3/4))=>(0*0.75,0*0.75)=>(0,0),找到了源圖的對應坐標,就可以把源圖中坐標為(0,0)處的234象素值填進去目標圖的(0,0)這個位置了。接下來,如法炮制,尋找目標圖中坐標為(1,0)的象素對應源圖中的坐標,套用公式:(1*0.75,0*0.75)=>(0.75,0)結果發現,得到的坐標里面竟然有小數,這可怎么辦?計算機里的圖像可是數字圖像,象素就是最小單位了,象素的坐標都是整數,從來沒有小數坐標。這時候采用的一種策略就是采用四舍五入的方法(也可以采用直接舍掉小數位的方法),把非整數坐標轉換成整數,那么按照四舍五入的方法就得到坐標(1,0),完整的運算過程就是這樣的:(1*0.75,0*0.75)=>(0.75,0)=>(1,0)那么就可以再填一個象素到目標矩陣中了,同樣是把源圖中坐標為(1,0)處的像素值38填入目標圖中的坐標。  

依次填完每個象素,一幅放大后的圖像就誕生了,像素矩陣如下所示:

234    38     22     22

67      44     12     12

89      65     63     63

89      65     63     63

這種放大圖像的方法叫做最臨近插值算法,這是一種最基本、最簡單的圖像縮放算法,效果也是最不好的,放大后的圖像有很嚴重的馬賽克,縮小后的圖像有很嚴重的失真;效果不好的根源就是其簡單的最臨近插值方法引入了嚴重的圖像失真,比如,當由目標圖的坐標反推得到的源圖的的坐標是一個浮點數的時候,采用了四舍五入的方法,直接采用了和這個浮點數最接近的象素的值,這種方法是很不科學的,當推得坐標值為 0.75的時候,不應該就簡單的取為1,既然是0.75,比1要小0.25 ,比0要大0.75 ,那么目標象素值其實應該根據這個源圖中虛擬的點四周的四個真實的點來按照一定的規律計算出來的,這樣才能達到更好的縮放效果。雙線型內插值算法就是一種比較好的圖像縮放算法,它充分的利用了源圖中虛擬點四周的四個真實存在的像素值來共同決定目標圖中的一個像素值,因此縮放效果比簡單的最鄰近插值要好很多。原文鏈接:https://blog.csdn.net/caomin1hao/article/details/81092134

 CH6:

牛頓法的函數是目標函數(Cost function etc.)$\Delta x^*$用函數的泰勒展開表示。

高斯牛頓法的函數是原誤差函數。$\Delta x^*$用函數的最小二乘法(最小平方法)表示。

ch6/ceresCurveFitting:

struct默認為public class默認為private struct詳見 http://c.biancheng.net/view/1407.html

operator()為重載操作符 詳見https://blog.csdn.net/liitdar/article/details/80654324

 https://blog.csdn.net/xgf415/article/details/52966475


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM