vSLAM重讀(5): vSLAM中對雙目相機的數據處理及與單目相對比

1. 雙目相機概述

  • 雙目立體視覺模型

    雙目模型求取深度

    • 雙目立體相機分別校準可參考 ROS_單目相機_分別校準
  • 雙目立體匹配算法案例

    https://www.cnblogs.com/riddick/p/8486223.html
    https://www.cnblogs.com/zyly/p/9373991.html

  • 雙目相機的特性

    1.雙目相機有左右兩個視野圖,所以有了參數基線

    • 基線的特性

      1.當系統的硬件結構固定不變,則通過外參校準的T中的位移量可對比參考基線長;且工作距離越大,測量精度越低。

      2.當基線增大時,FOV中水平角在增大,其對精度的影響是非線性的。

  • 雙目相機的矯正

    • 相機內參標定

      相機內參標定,獲取相機座標系與圖像座標系之間的投影關係,如(fx, fy, cx, cy, k1, k2, k3, 等)

    • 相機外參標定

      相機外參反應的是攝像機座標系和世界座標系之間的旋轉R+T關係,雙目相機外參主要獲取兩個相機之間“基線”

  • 雙目相機的立體匹配算法

    在雙目相機中特徵點的匹配與單目類似,用光流法跟蹤特徵,選取匹配點。

    然後對匹配的特徵點對計算本質矩陣,再分解矩陣獲得旋轉矩陣R和平移矩陣t;

    • 基於局部的塊匹配 - BM(Block Matching StereoBM)

    • 半全局塊匹配 - SGBM(Semi-Global-Block-Matching)

    SGBM算法要遠遠優於BM算法。

2. 單目相機+雙目相機+深度相機(結構光)+TOF對比

  • 單目無法確認深度

單目SLAM不受環境大小的影響,因此既可以用於室內,又可以用於室外。

  • 雙目相機

雙目相機標定配置較爲複雜(why?)(好像還有一個對準,有點不太記得…歡迎補充):

1. 雙目相機的各個單獨的鏡頭需要內參校準;
2. 雙目相機的各個單獨的鏡頭需要外參校準;
3. 雙目相機兩個相機之間需要校準;
4. 雙目相機的兩個相機校準之後還要與出廠的基線對齊,若誤差較大則校準失敗。

雙目相機的優勢: 它不管在靜止和運動下都可以直接估計場景的深度值。其深度量程受雙目的基線和分辨率限制。

另外,使用雙目相機去計算視差,還原深度值,比較耗時(如SGBM立體匹配)。

  • 結構光測量 (主動式測量:可以直接獲取距離值)

3D結構光投射的是散斑或編碼圖案,接收模組需要拍攝到清晰的圖案才能計算出深度。

而隨着距離的增加,投出的圖案或出現模糊,或出現亮度能量上的衰減,導致深度圖不完整,出現破洞,甚至於失效,所以3D結構光並不適用於遠距離深度信息採集。

結構光在室外容易受到強光的影響,效果很差。因爲光斑成像很容易受到環境的干擾。
結構光的測量距離一般較近(0.1~10m)如realsense D435
  • TOF(飛行時間測距法) (主動式測量:可以直接獲取距離值)

TOF是通過紅外光發射器發射調製後的紅外光脈衝,不停地打在物體表面,經反射後被接收器接收,通過相位的變化來計算時間差,進而結合光速計算出物體深度信息

不受環境光照的影響,室內室外都可行。TOF測量的距離一般較遠(0.4~130m) 如VLP16

3. 視差圖和深度圖的填充方法

以視差圖dispImg爲例。計算圖像的積分圖integral,並保存對應積分圖中每個積分值處所有累加的像素點個數n(空洞處的像素點不計入n中,因爲空洞處像素值爲0,對積分值沒有任何作用,反而會平滑圖像)。

採用多層次均值濾波。首先以一個較大的初始窗口去做均值濾波(積分圖實現均值濾波就不多做介紹了,可以參考我之前的一篇博客),將大區域的空洞賦值。然後下次濾波時,將窗口尺寸縮小爲原來的一半,利用原來的積分圖再次濾波,給較小的空洞賦值(覆蓋原來的值);依次類推,直至窗口大小變爲3x3,此時停止濾波,得到最終結果。

多層次濾波考慮的是對於初始較大的空洞區域,需要參考更多的鄰域值,如果採用較小的濾波窗口,不能夠完全填充,而如果全部採用較大的窗口,則圖像會被嚴重平滑。因此根據空洞的大小,不斷調整濾波窗口。先用大窗口給所有空洞賦值,然後利用逐漸變成小窗口濾波覆蓋原來的值,這樣既能保證空洞能被填充上,也能保證圖像不會被過度平滑

void insertDepth32f(cv::Mat& depth)
{
    const int width = depth.cols;
    const int height = depth.rows;
    float* data = (float*)depth.data;
    cv::Mat integralMap = cv::Mat::zeros(height, width, CV_64F);
    cv::Mat ptsMap = cv::Mat::zeros(height, width, CV_32S);
    double* integral = (double*)integralMap.data;
    int* ptsIntegral = (int*)ptsMap.data;
    memset(integral, 0, sizeof(double) * width * height);
    memset(ptsIntegral, 0, sizeof(int) * width * height);
    for (int i = 0; i < height; ++i)
    {
        int id1 = i * width;
        for (int j = 0; j < width; ++j)
        {
            int id2 = id1 + j;
            if (data[id2] > 1e-3)
            {
                integral[id2] = data[id2];
                ptsIntegral[id2] = 1;
            }
        }
    }
    // 積分區間
    for (int i = 0; i < height; ++i)
    {
        int id1 = i * width;
        for (int j = 1; j < width; ++j)
        {
            int id2 = id1 + j;
            integral[id2] += integral[id2 - 1];
            ptsIntegral[id2] += ptsIntegral[id2 - 1];
        }
    }
    for (int i = 1; i < height; ++i)
    {
        int id1 = i * width;
        for (int j = 0; j < width; ++j)
        {
            int id2 = id1 + j;
            integral[id2] += integral[id2 - width];
            ptsIntegral[id2] += ptsIntegral[id2 - width];
        }
    }
    int wnd;
    double dWnd = 2;
    while (dWnd > 1)
    {
        wnd = int(dWnd);
        dWnd /= 2;
        for (int i = 0; i < height; ++i)
        {
            int id1 = i * width;
            for (int j = 0; j < width; ++j)
            {
                int id2 = id1 + j;
                int left = j - wnd - 1;
                int right = j + wnd;
                int top = i - wnd - 1;
                int bot = i + wnd;
                left = max(0, left);
                right = min(right, width - 1);
                top = max(0, top);
                bot = min(bot, height - 1);
                int dx = right - left;
                int dy = (bot - top) * width;
                int idLeftTop = top * width + left;
                int idRightTop = idLeftTop + dx;
                int idLeftBot = idLeftTop + dy;
                int idRightBot = idLeftBot + dx;
                int ptsCnt = ptsIntegral[idRightBot] + ptsIntegral[idLeftTop] - (ptsIntegral[idLeftBot] + ptsIntegral[idRightTop]);
                double sumGray = integral[idRightBot] + integral[idLeftTop] - (integral[idLeftBot] + integral[idRightTop]);
                if (ptsCnt <= 0)
                {
                    continue;
                }
                data[id2] = float(sumGray / ptsCnt);
            }
        }
        int s = wnd / 2 * 2 + 1;
        if (s > 201)
        {
            s = 201;
        }
        cv::GaussianBlur(depth, depth, cv::Size(s, s), s, s);
    }
}

4. 相對於單目, 雙目相機的劣勢?

1.配置及標定比較複雜,兩個鏡頭都需要單獨標定內參和外參矩陣,然後將兩個相機聯合校準計算基線,並與出廠參數進行對照,
    若相差較大,則需要重新校準;

2.雙目(900--1600)成本相對於單目相機(50--200)貴;

3.雙目相機是被動式數據獲取,單目相機是主動式數據獲取; 雙目通過視差可以獲取深度值,而單目需要運動才能獲取深度信息;

4.雙目在計算立體配準獲取視差時(像素級計算,若想圖像塊計算則會模糊深度值),需要消耗較大的計算量。而單目不需要大量或者GPU計算;

5.所以可能就是因爲從成本、計算量、實時性等方面考慮, 目前單目落地應用相比於雙目要多一些。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章