基於RGB-D相機的視覺里程計實現(一)(圖像坐標轉換及圖像畸變)


此博客記錄我的slam學習之旅,近幾天目標為實現基於RGB-D SLAM的視覺里程計主要參考資料為高博的《視覺SLAM十四講:從理論到實踐》及高博的博客https://www.cnblogs.com/gaoxiang12/

首先實現一個不考慮任何實際問題的視覺里程計,根據RGB圖像及對應的深度圖像恢復出相鄰兩幀圖像間的R,T(也就是運動相機的位置姿態),幀與幀之間的運動關系最后構成相機的運動軌跡。大致思路分為如下幾個步驟:

(1)在圖像上選取特征點,進行圖像間的特征點匹配。

(2)根據特征匹配的結果(3D-3D),利用ICP算法恢復出相機的位置姿態(以第一幀的相機坐標系為參考坐標系)

(3)兩兩幀間的運動估計完成,再進行幀與幀之間的連接,最后連接成整體的軌跡了。

(ps:把視覺里程計寫的這么簡單也是沒誰了。。。。。實際的情況比這復雜很多)

補充一點基本數學知識:(這里只考慮小孔成像模型,實際還有幾種其他攝像機模型)

(本人使用opencv 3.1,ubuntu 16.04(在虛擬機上運行))

相機與圖像的坐標系

(1)相機坐標系

(2)圖像坐標系

(3)像素坐標系

 

由圖可知,設空間點P在相機坐標系的坐標為P[x,y,z],通過相機光心投影到圖像坐標系中P/(x/,y/),根據相似三角形原理(如上圖所示)可知,

                                                               (ps:由於第一次寫博客,不會使用公式編輯插件,所以在草稿本上寫放的照片)

 

  為了簡化模型,將成像平面放到相機前方,可以得到對應公式,不過在相機圖像中,我們得到的是像素,我們設物理成像平面坐標系上固定着一個像素坐標系,p/對應像素坐標p(u,v)

圖像成像坐標系與像素坐標系之間相差了一個縮放和平移,設在u軸上被縮放了a倍,按原點平移了cx,在v軸縮放了B倍,按原點平移了cy,即可得上述公式

為了易於表達,將左側公式寫成齊次坐標形式,其中K為相機內參矩陣,一般工廠會標定好相機內參,但很多時候需要我們進行標定,標定工具主要有基於Opencv和matlab的,目前已經很成熟了

 

有相機內參自然有相機外參,上面假定的空間點p的坐標是在相機坐標系下的,那么如何根據世界坐標系下的p點計算得到像素坐標下對應的點?

 

 

                                                                                                                                                                                                             因為都是齊次坐標,所以可以寫成上述公式。         

下面貼一段代碼表達像素坐標與相機坐標之間的關系:

以下是2d-3d的轉換代碼:

添加CMakeLists.txt

#設定最小版本

CMAKE_MINIMUM_REQUIRED(VERSION 2.8)

#設定工程名

PROJECT(2D)

# 添加c++ 11標准支持

set( CMAKE_CXX_FLAGS "-std=c++11" )

 

#找到opencv庫

FIND_PACKAGE(OpenCV REQUIRED)

 

# 添加頭文件

include_directories( ${OpenCV_INCLUDE_DIRS} )

 

ADD_EXECUTABLE(2D 2d.cpp)

# 鏈接OpenCV庫

target_link_libraries( 2D ${OpenCV_LIBS}

 

 

以下是2d轉換為3d坐標的代碼

//

// Created by xl on 18-5-4.

//此程序為將圖像像素坐標轉為相機坐標系,原圖像為一張RGB圖像和一張對應的深度圖

//opencv庫

#include <opencv2/core/core.hpp>

#include <opencv2/highgui/highgui.hpp>

//c++庫

#include <iostream>

using namespace std;

using namespace cv;

 

int main(int argc,char **argv)

{

    Mat rgb=imread("rgb.png",0);

    Mat depth=imread("depth.png",-1);//-1表示對原數據不做任何修改

    double scale=1000;//scale表示深度圖的縮放因子,一般為1000

    /*Mat K;

    K=(Mat_<double>(3,3)<<518.0,0,325.5,0,518.0,253.5,0,0,1);*///定義相機內參K,也可以采用這種方式

    // 相機內參

    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;

    vector<Point3f> pts_3d;//定義三維點

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

    {

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

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

            if(d==0)

                continue;//如果d=0,則被認為是壞點

            double dd=d/scale;

            pts_3d.push_back(Point3f((m-camera_cx)*dd/camera_fx,(n-camera_cy)*dd/camera_fy,dd));

        }

          

    }

}

 

既然講到圖像了,那就順便提一下圖像畸變吧。

為了呈現更好的成像效果,我們會在相機前方加上透鏡,透鏡的加入會對成像過程中光線的傳播產生新的影響。

畸變的形式一般為兩種:

(1)徑向畸變:由透鏡形狀引起的畸變,徑向畸變又可以分為枕形畸變和桶形畸變。

(2)切向畸變:在組裝過程中,透鏡和成像平面不能嚴格平行產生切向畸變

 

對於切向畸變,無論哪個形式,都可以用一個多項式函數來描述畸變前后的變化,可以用與距中心的距離有關的二次及高次多項式函數進行糾正。

對於切向畸變,也可以使用兩個參數來糾正。(具體公式見下圖)

 

                                                                                                                         注意:x,y為歸一化平面的坐標,r也是距歸一化中心點的距離。

所以給出一個空間點位置P(X,Y,Z),我們可以求出其在帶有畸變的相機上像素坐標。

 

                                                                                     (ps:對了,之前沒有講到歸一化坐標,這里的歸一化坐標其實就是相機坐標系下的坐標值(x,y,z)轉換到z=1的平面上,原來的坐標轉為(x/z,y/z,1))

接下來到了我們的實踐環節了,給你一張帶有畸變的圖像,如何把它糾正轉為去畸變的照片??

問題描述:

input:畸變后的圖像(因為相機原因,可能拍出來的照片里面的物體是斜的),上面提到的畸變參數

output:畸變前的圖像。

 

(ps:最開始我接觸這個問題的時候,當時也不明白該怎么求,后來看了網上的博客,才弄懂了。)

 

其實這個問題的核心是畸變后的坐標與畸變前的坐標,它們兩處坐標的灰度值是相同的,這里要求出畸變前的圖像,其實就是求出畸變前圖像的一個點坐標p,對應的畸變圖像的坐標是多少,找到p點對應到畸變圖像上的坐標值,再把畸變圖像的坐標值的灰度值賦給畸變前的坐標p,所有的圖像是一個個像素組成的,遍歷所有圖像這就可以了。。。所以這個問題的求解可以分為以下步驟

(注意:這里還有一個隱藏問題,像素點(u,v)經過畸變后在畸變圖像的坐標值不一定為整數,但是在像素坐標里一定要是整數的啊,比如在原始圖像(1,1)畸變后就可能變成了(1.2,1.3),這樣顯然是不行的,這就需要對畸變坐標作插值處理,我們這里先不討論插值的問題,對畸變坐標取整進行(ps:這樣真的是太暴力了啊 。。。。))

(還有個問題:我們這里討論的是灰度圖像,當然也可以考慮RGB圖像,其實原理是一樣的,只要每個坐標計算三個RGB值就行了,這里以灰度圖為例)

 

下面就是代碼示例了:

#include <iostream>

#include <opencv2/core/core.hpp>

#include <opencv2/features2d/features2d.hpp>

#include <opencv2/highgui/highgui.hpp>

#include <opencv2/calib3d/calib3d.hpp>

 

usingnamespace std;

usingnamespace cv;

int main(int argc, char **argv) {

 

    // 畸變參數,這里只考慮k1,k2,這里需要你知道參數。

    double k1,k2,p1,p2;
   //相機內參矩陣
  double fx,fy,cx,cy;
   Mat K;
  K=(Mat_double<3,3><<fx,0,cx,0,fy,cy,0,0,1);


 

    Mat image = imread(“test.png”,0);   // 這里填寫你的路徑,0表示將圖像轉換為灰度圖

 

    Mat image1 = Mat(image.rows, image.cols, CV_8UC1);   // 設置去畸變以后的圖

 

    // u,v為畸變前圖像的像素坐標,v為列,u為行

    for (int v= 0; v < image.rows; v++)

        for (int u = 0; u < image.cols; u++) {

 

            double x=(u-cx)/fx;

            double y=(v-cy)/fy;//這里的x,y為歸一化后的坐標

            double r=pow(x,2)+pow(y,2);//r為距中心點的距離的平方,之所以是平方,是因為方便,沒有調用開根號,注意也是歸一化平面上的

            double x1=x*(1+k1*r+k2*pow(r,2))+2*p1*x*y+p2*(r+2*x*x);

            double y1=y*(1+k1*r+k2*pow(r,2))+p1*(r+2*y*y)+2*p2*x*y;//x1,y1為畸變圖像上的歸一化坐標

           double u1=fx*x1+cx;

            double v1=fy*y1+cy;//u1,v1為畸變圖像上的像素坐標

          

            // 最后一步灰度值相等,而且需要對畸變圖像的像素坐標取整,這里的方法很粗暴,直接int

            if (u1= 0 && v1>=0 && u1< image.cols && v1<image.rows) {

                image1.at<uchar>(v, u) = image.at<uchar>((int) v1, (int) u1);

            } else {

                image1.at<uchar>(v, u) = 255;//對於超出坐標范圍,賦灰度值為255

            }

        }

 

    // 畫圖去畸變后圖像

    imshow("image1", image1);

    imshow("image ", image);

    cv::waitKey();

 

    return0;

}

 

 

                                                                                                                     (ps:代碼寫得可能不夠規范,請原諒啊,哈哈,因為中英文字符問題調了半天)

 

好了,這次主要介紹相機圖像的問題了,下一節將記錄特征點檢測匹配的問題了。。

 

 

 

 

 

 

如果有人看到我的博客,歡迎大家一起交流啊,,,,(寫的這么爛估計沒有人看吧。。。。。)

我的郵箱:1439741774@qq.com18:21:24

 

 

 

 


免責聲明!

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



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