2、將圖像轉換為點雲


1、它是后面處理地圖的基礎,最簡單的點雲地圖就是把不同位置的點雲進行拼接得到的。

2、由於從RGB-D相機里可以采集到兩種形式的數據:彩色圖像和深度圖像。如果有kinect和ros,那么可以運行如下

roslaunch openni_launch openni.launch

來使Kinect工作。如果PC機連上Kinect,那么彩色圖像和深度圖像會發布在/camera/rgb/image_color 和 /camera/depth_registered/image_raw 中;可以通過運行如下

 rosrun image_view image_view image:=/camera/rgb/image_color

來顯示彩色圖像,或者也可以在Rviz里看到圖像與點雲的可視化數據。

3、rgb圖像與對應的深度圖像,如下

這兩張圖是來自於數據集nyuv2:http://cs.nyu.edu/~silberman/datasets/ 原圖格式是ppm和pgm的,目前格式是png。

在實際的Kinect里(或其他rgb-d相機里)直接采到的RGB圖和深度圖可能會有些小問題,比如

1)有一些時差(約幾到十幾個毫秒)。這個時差的存在,會產生“RGB圖已經向右轉了,深度圖還沒轉”的感覺。

2)光圈中心未對齊。因為深度畢竟是靠另一個相機獲取的,所以深度傳感器和彩色傳感器參數可能不一致。

3)深度圖里有很多“洞”。因為RGB-D相機不是萬能的,它有一個探測距離的限制啦!太遠或太近的東西都是看不見的呢。關於這些“洞”,我們暫時睜一只眼閉一只眼,不去理它。以后我們也可以靠雙邊bayes濾波器去填這些洞。但是!這是RGB-D相機本身的局限性。軟件算法頂多給它修修補補,並不能完全彌補它的缺陷。

在以上給出的圖當中,都進行了預處理,可以認為“深度圖就是彩色圖里每個像素距傳感器的距離“。

4、現在需要把這兩個圖轉成點雲,因為計算每個像素的空間點位置,是后面配准、拼圖等一系列事情的基礎,比如:在配准時,必須知道特征點的3D位置,這時候就需要用到這里講的東西。

5、從2D到3D(數學部分)

上面兩張圖像給出了機器人外部世界的一個局部信息,假設這個世界由一個點雲來描述:X={x1,x2,...xn}。其中每一個點由六個分量組成:r,g,b,x,y,z,分別表示該點的顏色與空間位置;顏色方面,主要由彩色圖像記錄;而空間位置,可由圖像和相機模型、姿態一起計算出來

對於常規相機,SLAM里使用針孔相機模型(圖來自http://www.comp.nus.edu.sg/~cs4243/lecture/camera.pdf ):

 

一個空間點[x,y,z]和它在圖像中的像素坐標[u,v,d](d指深度數據)的對應關系式這樣的:

其中,fx,fy指相機在x,y兩個軸上的焦距,cx,cy指相機的光圈中心,s指深度圖的縮放因子。

上面的公式是從(x,y,z)推導到(u,v,d),反之,也可以已知(u,v,d),推導到(x,y,z)的方式,推導如下

那么就可以根據上式構建點雲了。

通常把fx,fy,cx,cy這四個參數定義為相機的內參矩陣C,也就是相機做好之后就不會變得參數。相機的內參可以用很多方法來標定,詳細的步驟比較繁瑣;在給定內參之后,每個點的空間位置與像素坐標就可以用簡單的矩陣模型來表示了:

其中,R和t是相機的姿態。R代表旋轉矩陣,t代表位置矢量。目前我們做的是單幅圖像的點雲,故認為相機沒有旋轉和平移;所以把R射程單位矩陣I,把t設成了零。s是scaling factor,即深度圖里給的數據與實際距離的比例。由於深度圖給的都是short (mm單位),s通常為1000。

如果相機發生了位移和旋轉,那么只要對這些點進行位移和旋轉操作即可。

6、從2D到3D (編程部分)

完成從圖像到點雲的轉換,在上一節講到的代碼根目錄/src/ 文件夾中新建一個generatePointCloud.cpp文件:

touch src/generatePointCloud.cpp

在一個工程里可以有多個main函數,因為cmake允許你自己定義編譯的過程,我們會把這個cpp也編譯成一個可執行的二進制,只要在cmakelists.txt里作相應的更改便行了,更改如下

然后在新建的文件generatePointCloud.cpp里面編寫如下代碼

//c++標准庫 

#include<iostream>

#include<string>

using namespace std;

//opencv庫

#include<opencv2/core/core.cpp>

#include<opencv2/highgui/highgui.hpp>

//PCL庫

#include<pcl/io/pcd_io.h>

#include<pcl/point_types.h>

//定義點雲類型

typedef pcl::PointXYZRGBA PointT;

typedef pcl::PointCloud<PointT> PointCloud;

//相機內參

const double camera_factor=1000;

const double camera_cx=325.5;

const double camera_cy=253.5;

const double camera_fx=518.0;

const double camera_fy=519.0;

//主函數

int main(int argc,char** argv)

{

 //讀取./data/rgb.png和./data/depth.png,並轉化為點雲

//圖像矩陣

cv::Mat rgb,depth;

//使用cv::imread()來讀取圖像

//rgb圖像是8UC3的彩色圖像

rgb=cv::imread(./data/rgb.png);

//depth是16UC1的單通道圖像,注意flags設置為-1,表示讀取原始數據不做修改

depth=cv::imread("./data/depth.png",-1);

//點雲變量

//使用智能指針,創建一個空點雲。這種指針用完會自動釋放

PointCloud::Ptr cloud(new PointCloud);

//遍歷深度圖

for(int m=0;m<depth.rows;m++)

   for(int n=0;n<depth.cols;n++)

  {

   //獲取深度圖中(m,n)處的值

ushort d=depth.ptr<ushort>(m)[n];

//d可能沒有值,若如此,跳過此點

if(d==0)

   continue;

//d存在值,則向點雲增加一個點

PointT p;

//計算這個點的空間坐標

p.z=double(d)/camera_factor;

p.x=(n-camera_cx)*p.z/camera_fx;

p.y=(m-camera_cy)*p.z/camera_fy;

//從rgb圖像中獲取它的顏色

//rgb是三通道的BGR格式圖,所以按下面的順序獲取顏色

p.b=rgb.ptr<uchar>(m)[n*3];

p.g=rgb.ptr<uchar>(m)[n*3+1];

p.r=rgb.ptr<uchar>(m)[n*3+2];

//把p加入到點雲中

cloud->points.push_back(p);

 }

//設置並保存點雲

cloud->height=1;

cloud->width=cloud->points.size();

cout<<"point cloud size="<<cloud->points.size()<<endl;

cloud->is_dense=false;

pcl::io::savePCDFile("./pointcloud.pcd",*cloud);

//清楚數據並保存

cloud->points.clear();

cout<<"Point cloud saved."<<endl;

return 0;

}

分析:

1.我們使用OpenCV的imread函數讀取圖片。在OpenCV2里,圖像是以矩陣(cv::MAt)作為基本的數據結構。Mat結構既可以幫你管理內存、像素信息,還支持一些常見的矩陣運算,是非常方便的結構。彩色圖像含有R,G,B三個通道,每個通道占8個bit(也就是unsigned char),故稱為8UC3(8位unsigend char, 3通道)結構。而深度圖則是單通道的圖像,每個像素由16個bit組成(也就是C++里的unsigned short),像素的值代表該點離傳感器的距離。通常1000的值代表1米,所以我們把camera_factor設置成1000. 這樣,深度圖里每個像素點的讀數除以1000,就是它離你的真實距離了。

2.在遍歷深度圖的時候,是按照“先列后行”的順序,由於m指圖像的行,n指圖像的列,它和空間點的坐標關系是如下:

對於深度圖第m行,第n列的數據可以用depth.ptr<ushort>(m)[n]來獲取。其中,cv::Mat的ptr函數會返回指向該圖像第m行數據的頭指針。然后加上位移n后,這個指針指向的數據就是我們需要讀取的數據。

計算三維點坐標的公式我們已經給出過了,代碼里原封不動地實現了一遍。我們根據這個公式,新增了一個空間點,並放入了點雲中。最后,把整個點雲存儲為 ./data/pointcloud.pcd 文件。

7、編譯並運行

cd build

cmake ..

make

編譯通過后,就可以到bin目錄下運行generate_pointcloud;運行之后,就可以在data目錄下生成點雲文件。安裝了pcl的話,就可以通過如下命令來查看點雲文件

pcl_viewer pointcloud.pcd

TIPS:

  • 如果你打開點雲,只看到紅綠藍三個方塊,請按R重置視角。剛才你是站在原點盯着坐標軸看呢。
  • 如果點雲沒有顏色,請按5顯示顏色。
  • cmake過程可能有PCL的警告,如果你編譯成功了,無視它即可。這是程序員的本能。

 


免責聲明!

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



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