@ 《視覺SLAM十四講》知識點與習題
《視覺SLAM十四講》第五講知識點整理+習題
正在學習SLAM相關知識,将一些關鍵點及時記錄下來。
知識點整理
本講主要讨論了相機模型,描述投影關系,講解相機内參和外參
-
相機的針孔模型:需要了解焦距(實體成像平面到小孔的距離)、光心(針孔模型中的小孔)、實體成像平面和像素平面
像素坐标系與成像平面之間,相差了一個縮放和原點平移:x軸縮放α,y軸縮放β,原點平移[cx, cy]^T
- 相機的内參:K。常認為相機的内參在出場之後是固定的,不會在使用過程中發生變化
- 相機的外參:R, t。由于相機在運動,是以外參應該是他的世界坐标系根據相機的目前位姿變換到相機坐标系下的結果。外參會随着相機運動發生改變
- 透鏡的畸變模型
- 徑向畸變:由透鏡自身的形狀引起的畸變。越靠近圖像的邊緣,這種現象越明顯。徑向畸變包括桶形畸變和枕形畸變。在這兩種畸變中,穿過圖像中心和光軸有交點的直線還能保持形狀不變
- 桶形畸變是由于圖像放大率随着與光軸之間的距離的增加而減少
-
枕形畸變則恰好相反
用與距中心的距離有關的二次及高次多項式函數進行糾正。對于畸變較小的圖像中心區域,主要是k1起作用; 對于畸變較大的邊緣區域,主要是k2起作用;對于魚眼鏡頭等畸變很大的攝像頭,可以加入k3進行糾正
-
切向畸變:是由于在相機的組裝過程中由于不能使透鏡和成像面嚴格平行引起的。
在極坐标系中,徑向畸變認為是坐标點沿着長度方向發生了變化,切向畸變認為是水準夾角發生了變化。
用下圖中的公式,分别對x,y的畸變進行糾正。其中k1,k2,k3進行的是徑向畸變的糾正,p1, p2進行的是切向畸變的糾正
- 基線:左右相機之間的距離
-
雙目相機:通過同步采集左右相機的圖像,計算圖像間視差,來估計每一個像素的深度。其中,視差是指左右圖的橫坐标之差。視差越大,距離越近。同時,由于視差最小為1個像素,于是雙目的深度存在一個理論上的最大值,由fb确認。且,當基線越長時,雙目能測到的最大距離就會越遠。
但是視差d本身的計算比較困難,因為需要确切知道同一個點在左右圖中的對應關系。當想計算每個像素的深度時,其計算量與精度都稱為問題,而且隻有在圖像紋理變化豐富的地方才能計算視差
- RGB-D相機模型:按原理可以分為兩類。
- 第一類是通過紅外結構光(Structured Light)來測量像素距離
-
第二種是通過飛行時間法(ToF)原理測量像素距離,可以獲得整個圖像的像素深度
無論是哪一種RGB-D相機都需要向探測目标發射一束光線,相機根據傳回的結構光圖案,計算物體與自身之間的距離。
在測量深度之後,RGB-D相機通常按照生産時的各相機擺放位置,自己完成深度與彩色圖像素之間的配對,輸出一一對應的彩色圖和深度圖,是以可以在同一圖像位置,讀取到色彩和距離資訊,計算像素的3D相機坐标,生成點雲。
對于RGB-D資料,既可以在圖像層面進行處理,還可以在點雲層面處理
RGB-D相機能夠實時的測量每個像素點的距離,但是由于這種發射-接收的測量方式,其适用範圍比較受限。不能在室外使用,同時使用多個時也會互相幹擾。對于透射材質的物體,因為接收不到反射光,是以無法測量這些點的距離
- 圖像:這裡需要注意的點是:在圖像中,數組的行數對應圖像的高度,列數對應圖像的寬度。平時說的圖像的寬度或列數對應着X軸,而圖像的行數或高度對應着Y軸
unsigned char pixel = image[y][x];
- 一般,圖像灰階使用unsigned char表示8位整數記錄;各個像素與相機的距離使用unsigned short 0-65536表示
OpenCV實踐
- 圖檔基礎
- 注意對位于x,y處的像素的通路。是以可以得到,對于每一個像素點,它們的多通道值是連續存儲的
for ( size_t y=0; y<image.rows; y++ ) { // 用cv::Mat::ptr獲得圖像的行指針 unsigned char* row_ptr = image.ptr<unsigned char> ( y ); // row_ptr是第y行的頭指針 for ( size_t x=0; x<image.cols; x++ ) { // 通路位于 x,y 處的像素 unsigned char* data_ptr = &row_ptr[ x*image.channels() ]; // data_ptr 指向待通路的像素資料 // 輸出該像素的每個通道,如果是灰階圖就隻有一個通道 for ( int c = 0; c != image.channels(); c++ ) { unsigned char data = data_ptr[c]; // data為I(x,y)第c個通道的值 } } }
- 注意對位于x,y處的像素的通路。是以可以得到,對于每一個像素點,它們的多通道值是連續存儲的
PCL實踐
- 安裝PCL點雲庫
-
因為先前已經安裝了OpenCV和ROS,是以很多依賴不需要重複安裝了
安裝sudo apt-get install libopenni-dev
sudo apt-get install libopenni2-dev
-
- 從PCL的github上的release目錄下,網址為1。 通過git clone方式下載下傳下來
- mkdir build然後進入build目錄,進行cmake和make操作即可。注意,PCL庫的make過程比較漫長,如果卡了,可以嘗試關閉目前terminal,重新make一下
- 下面進入了編譯joinMap的工作。同3操作,不過最後運作時,需要在包含pose.txt的目錄下運作,是以,輸入指令為
此時會顯示下述文字,同時在目前目錄下生成一個map.pcd檔案 這是使用cloud_viewer顯示的map.pcd影像,是可以進行任意角度旋轉的./build/joinMap
- 源碼分析:從源碼中可以獲得對于深度圖、彩色圖等更多相關的知識點
-
for ( int i=0; i<5; i++ ) { boost::format fmt( "./%s/%d.%s" ); //圖像檔案格式 colorImgs.push_back( cv::imread( (fmt%"color"%(i+1)%"png").str() )); depthImgs.push_back( cv::imread( (fmt%"depth"%(i+1)%"pgm").str(), -1 )); // 使用-1讀取原始圖像 double data[7] = {0}; for ( auto& d:data ) fin>>d; Eigen::Quaterniond q( data[6], data[3], data[4], data[5] ); Eigen::Isometry3d T(q); T.pretranslate( Eigen::Vector3d( data[0], data[1], data[2] )); poses.push_back( T ); }
-
其中,固定了depthScale表明樣例中給定的圖檔的深度尺度已知// 計算點雲并拼接 // 相機内參 double cx = 325.5; double cy = 253.5; double fx = 518.0; double fy = 519.0; double depthScale = 1000.0;
-
cv::Mat color = colorImgs[i]; cv::Mat depth = depthImgs[i]; Eigen::Isometry3d T = poses[i]; for ( int v=0; v<color.rows; v++ ) for ( int u=0; u<color.cols; u++ ) { unsigned int d = depth.ptr<unsigned short> ( v )[u]; // 深度值 if ( d==0 ) continue; // 為0表示沒有測量到 Eigen::Vector3d point; point[2] = double(d)/depthScale; point[0] = (u-cx)*point[2]/fx; point[1] = (v-cy)*point[2]/fy; Eigen::Vector3d pointWorld = T*point; PointT p ; p.x = pointWorld[0]; p.y = pointWorld[1]; p.z = pointWorld[2]; p.b = color.data[ v*color.step+u*color.channels() ]; p.g = color.data[ v*color.step+u*color.channels()+1 ]; p.r = color.data[ v*color.step+u*color.channels()+2 ]; pointCloud->points.push_back( p ); }
其中,對color和depth的每一個元素同時進行周遊,就暗含了“相機已經将彩色圖和深度圖一一對應”
對于u,v是指圖檔上的像素位置,表示像素空間
Eigen::Vector3d point表示對應像素點在相機坐标系中的位置
根據書本P60的公式,**相機位姿T(即相機坐标系在世界坐标系中的表示)**左乘目前點在相機中的坐标,等于目前點在世界坐标系中的坐标。p_w=T_w_c p_c
-
習題
- 由于暫時使用的是開源資料集,後續才開始實時采集,暫時不進行相機标定,後續用到了再來補充