地平線雙目深度估計參考演算法 StereoNetPlus最佳化 思路解讀

地平线智能驾驶开发者發表於2024-10-12

【參考演算法】地平線雙目深度估計參考演算法 StereoNetPlus-v1.2.1

1.引言

本文將介紹地平線基於公版的雙目深度估計演算法 StereoNet 做的最佳化設計。首先介紹了雙目深度估計的原理以及雙目點雲和 Lidar 點雲的對比,然後由公版 StereoNet 的介紹切入到地平線參考演算法的針對性最佳化,最後對視覺化結果進行了解讀。

2.雙目深度估計原理

2.1 基本假設

假設雙目系統是標準形式,即:

  • 兩相機內引數相同,即焦距、解析度等引數一致;
  • 兩相機光軸平行;
  • 成像平面處於同一水平線;

假設以左相機座標系為主座標系,也就是說兩相機只存在 X 軸方向上的平移變換。

2.2 幾何法

  • P 是待測物體上的某一點,
  • O_R 與 O_T 分別是兩個相機的光心,
  • p、p':點 P 在兩個相機感光器上的成像點,相機的成像平面經過旋轉後放在了鏡頭前方
  • f 為相機焦距,B 為兩相機中心距,Z 為我們想求得的深度資訊。

公式中,焦距 f 和攝像頭中心距 B 可透過標定得到,因此,只要獲得了視差 d=X_R−X_T,就可以計算出深度 Z。

視差是同一個場景在兩個相機下成像的畫素的位置偏差。

2.3 相機引數推導法

由基本假設可以可知,左右相機內參相等,且左右相機只存在 X 軸方向的平移運動。那麼有:

相機模型和各種座標系介紹:https://blog.csdn.net/qq_40918859/article/details/122271381

2.4 雙目點雲和鐳射雷達點雲的比較

參考連結:https://www.zhihu.com/question/264726552

  • 感知距離

雙目模型在近處比較有優勢(幾十米),在遠處的時候類似於單目,而 Lidar 感知距離可以達到 200m+(210-250)。

  • 點雲****密度

雙目的點雲比 Lidar 要稠密。雙目模型估計出的深度是畫素級別的,camera 解析度越大,點雲就越稠密。而 Lidar 的取樣點覆蓋相對於場景的尺度來講,具有很強的稀疏性。

  • 精度

Lidar 是主動方法,雙目是被動方法,而且雙目是根據模型估計視差計算出的深度,存在一定的標定、安裝誤差以及深度失真問題,所以其輸出的深度資訊的精度是不及 Lidar 的,但是需要注意的是 Lidar 受天氣影響更大。

雙目測距在某些場景下,深度圖邊界容易失真,錯誤主要體現在以下三方面。

  • 缺失(Missing):邊界缺失是指高質量 RGB 影像中存在真實物件邊界,但在深度圖中這些邊界丟失了;
  • 虛假(Fake):虛假邊界是指在深度圖中存在物件邊界,但在 RGB 影像中不存在真實邊界的情況;
  • 錯位(Misaligned):RGB 影像和深度圖中均有真實邊界,但彼此沒有很好的對齊;
  • 黑夜不 work

3. StereoNet

3.1 基本情況

  • dataset: SceneFlow
  • Input shape: 540x960
  • 精度:

3.2 網路結構

3.2.1.特徵提取

採用共享權重的孿生神經網路提取雙目影像的特徵,使用 K 個下采樣 block 進行高層特徵提取;

3.2.2.Cost volume 構建

  • Cost volume 是在雙目匹配中用於衡量左右檢視的相似性的張量。
  • 特徵下采樣之後,在低解析度下計算 cost volume,輸出的 shape 是

  • Cost volume 的計算方式為 concat,即將左右檢視的特徵圖在通道維進行 concat:
def build_concat_volume(
        self,
        refimg_fea: Tensor,
        targetimg_fea: Tensor,
        maxdisp: int,
    ) -> Tensor:
        """
        Build the concat cost volume.

        Args:
            refimg_fea: Left image feature.
            targetimg_fea: Right image feature.
            maxdisp: Maximum disparity value.

        Returns:
            volume: Concatenated cost volume.
        """

        B, C, H, W = refimg_fea.shape
        C = 2 * C
        tmp_volume = []
        for i in range(maxdisp):
            if i > 0:
                tmp = self.c_cat[i].cat(
                    (refimg_fea[:, :, :, i:], targetimg_fea[:, :, :, :-i]),
                    dim=1,
                )
                tmp_volume.append(self.c_pad[i](tmp).view(-1, 1, H, W))
            else:
                tmp_volume.append(
                    self.c_cat[i]
                    .cat((refimg_fea, targetimg_fea), dim=1)
                    .view(-1, 1, H, W)
                )
        volume = self.c_cat_final.cat(tmp_volume, dim=1).view(
            B, C, maxdisp, H, W
        )
        return volume
  • 透過 concat 獲得的 cost volumes 不包含有關特徵相似性的資訊,因此在後續模組中需要更多引數來學習相似度函式。
  • maxdisp 是預先設定的最大視差,也就是模型能預測到的最大視差。

3.2.3.Cost volume 最佳化和計算視差

獲得 4D cost volume,使用 conv3d 進行最佳化。

然後基於最佳化後的 cost volume 獲得低解析度下的視差圖。cost volume 最佳化後得到 Nx1xDxHxW 大小的特徵圖,然後使用 softmax 得到在每個視差值下的機率。

相關程式碼:

        # Optimize cost volume to obtain low disparity
        for f in self.filter:
            cost0 = f(cost0)
        #最佳化視差
        cost0 = self.conv3d_alone(cost0)
        cost0 = cost0.squeeze(1)
        #轉化為機率
        pred0 = self.softmax(cost0)
        #乘以對應的視差值
        pred0 = self.dis_mul(pred0)
        pred0 = self.dis_sum(pred0)

3.2.4.refine 到原圖

利用層次化微調模組來逐漸對視差圖進行修復,補充高頻資訊以實現邊緣保留。具體做法是對粗糙的低解析度視差圖進行上取樣,並根據粗糙視差圖和原始左檢視 rgb 輸入預測出一個殘差圖,加到該視差圖中獲得一個細化視差圖。

具體操作:

首先將深度圖和 RGB 影像拼接(Concatenate),得到的拼合張量再經過一個 3x3 的卷積操作得到 32 通道的表示張量,之後再透過 6 個 殘差塊(Residual Block)的操作,每個殘差塊由於卷積、批正則化(Batch Normalization)、矯正線性單元(Leakey ReLU)等操作;為了擴大網路,在每個殘差塊中使用了擴張(Dilate)卷積的操作,最後經過一個 3x3 的卷積,得到最後的單通道深度圖。

SteroNet 由左右圖的特徵經過連線得到一個 4D cost volume, 之後利用 3D 卷積進行代價聚合得到最終的視差圖,這種方式導致其計算成本十分高昂。

4.StereoNetPlus

4.1 總體結構

4.2 模型最佳化點

4.2.1 特徵提取

使用 MixVarGENet+FPN 來提取雙目影像的多尺度特徵;

4.2.2 Cost Volume 構建和最佳化

採用 AANet 的思想,基於相關性構建多尺度 cost volume,並進行尺度內和跨尺度融合,最終輸出 1/8 原圖尺度下的 cost volume。

AANet 網路

作者透過設計兩個有效且高效的成本聚合模組:自適應同尺度聚合模組(Adaptive Intra-Scale Aggregation)

和自適應跨尺度聚合模組(Adaptive Cross-ScaleAggregation)來實現成本聚合。並且使用特徵相關性而不是 concat 的方式構造多尺度 Cost Volume。

cost volume 構建

使用 1/8,1/16, 1/32 原圖尺度下的特徵圖構造多尺度 cost volume;

   def build_aanet_volume(self, refimg_fea, maxdisp, offset, idx):
        B, C, H, W = refimg_fea.shape
        num_sample = B // 2
        tmp_volume = []
        #maxdisp:最大預測視差
        for i in range(maxdisp):
            if i > 0:
                #計算左右檢視特徵的相關性
                cost = self.gc_mul[i + offset].mul(
                    refimg_fea[:num_sample, :, :, i:],
                    refimg_fea[num_sample:, :, :, :-i],
                )
                #取均值
                tmp = self.gc_mean[i + offset].mean(cost, dim=1)
                #padding
                tmp_volume.append(self.gc_pad[i + offset](tmp))
            else:
                #計算左右檢視特徵的相關性
                cost = self.gc_mul[i + offset].mul(
                    refimg_fea[:num_sample, :, :, :],
                    refimg_fea[num_sample:, :, :, :],
                )
                #取均值
                tmp = self.gc_mean[i + offset].mean(cost, dim=1)
                tmp_volume.append(tmp)

        volume = (
            self.gc_cat_final[idx]
            .cat(tmp_volume, dim=1)
            .view(num_sample, maxdisp, H, W)
        )
        return volume

ISA

在視差非連續時,邊緣位置總會有一圈連續的錯誤匹配值,為了緩解這種 edge-fattening 問題,使用 3 個殘差模組 BasicResBlock 對每個尺度的 cost volume 進行聚合。

CSA

雙目影像進行下采樣後,在相同的 patch 尺寸下,紋理資訊將更具鑑別性,所以跨尺度成本聚合演算法中引入了多尺度互動。最終的 cost volume 是透過對不同尺度的成本聚合結果進行自適應組合得到的,公式如下:

下面將對公式中的 3 種情況進行說明:

最佳化效果

由於沒有使用 conv3d,所以相對於基礎版模型,效能有了一定的提升。

基礎版模型:720P 輸入下,單核 fps 18.22, latency:54.9ms, 960*540 輸入下精度 1.12(浮點)

最佳化版模型:54.9ms ->16.59ms epe: 1.12->0.948

4.2.3 計算粗略視差

使用融合後的 1/8 尺度下的 cost volume 計算小圖視差。

        # Fusion costvolume as AANet
        aanet_volumes = aanet_volumes[::-1]
        for i in range(len(self.fusions)):
            fusion = self.fusions[i]
            aanet_volumes = fusion(aanet_volumes)
        #1/8尺度:1X24x68x120(24=192/8)
        cost0 = self.final_conv(aanet_volumes[0])
        # Obtain low disparity and unfold it.
        #轉化為視差機率
        pred0 = self.softmax(cost0)
        #乘以對應的視差值範圍[0,23]
        #24=192/8
        pred0 = self.dis_mul(pred0)
        #sum求和,獲得1/8尺度下的粗略視差
        pred0 = self.dis_sum(pred0)
        #unfold操作:使用2x2conv替代
        #pred0_unfold shape:1x4x68x120
        pred0_unfold = self.unfold(pred0)

對粗略視差進行上取樣時,會用到鄰域點的特徵,所以用了一個 unfold 的一個操作,先把這個視差從那個小圖上面取出來,取出來過後,然後上取樣,然後再跟 featuremap 預測出來的權重去相乘,最後會得到一個最終的時差值。這種做法會減少 refine 的過程中的計算量。

這裡為了進一步節省耗時,使用 conv2x2 替換 unfold 操作:

class UnfoldConv(nn.Module):
    """
    A unfold module using conv.
    Args:
        in_channels: The channels of inputs.
        kernel_size: The kernel_size of unfold.
    """
    ...
    def forward(self, x: Tensor) -> Tensor:
        """Perform the forward pass of the model."""
        #將尺寸pad到(1,1,69,121)
        x = self.pad(x)
        #conv代替unfold
        x = self.conv(x)
        return x

4.2.4 refine 原圖

使用左圖特徵預測出權重,然後和上取樣到原圖尺寸的粗略視差相乘,這樣就得到了最終視差。

CoEx

  • 特徵提取:使用 MobileNetV2 作為主幹特徵提取器,因為它具有輕量級的特性,並構建了一個 U-Net 方式的上取樣模組,在每個尺度層次上都有長跳躍連線;
  • 構建 cost volume:在左右影像的 1/4 尺度上提取的特徵圖構建相關層,以輸出 D/4×H/4×W/4 cost volume;
  • cost volume 聚合:使用了 3D 卷積的沙漏結構,但減少了通道數量和網路深度以降低計算代價,在每一個模組之後加入引導代價體激勵;
  • Guided Cost volume Excitation (GCE):利用從影像中提取的特徵圖作為成本聚合的指導,以提高精度
GCE

CoEx 使用 DeconvResModule 融合 P1/2、P1/4、P1/8 的左圖特徵,從而獲得原始影像解析度下的視差權重。

將預測到的權重和上取樣到原圖大小的粗略視差相乘:

  1. 將雙線性插值最佳化為最近鄰插值;
  2. 此部分在模型的後處理部分進行。

4.3 計算深度並視覺化

4.3.1 計算深度

根據獲得的視差計算畫素深度:

#基於幾何法,根據模型預測的視差計算深度
def process_outputs(model_outs, viz_func, vis_inputs):
    preds = model_outs.squeeze(0).cpu().numpy()
    f = float(vis_inputs["f"])
    baseline = float(vis_inputs["baseline"])
    img = vis_inputs["img"]
    #f:焦距;baseline:相機平行光軸之間的距離
    #preds:預測視差
    depth = baseline * f / preds
    preds = viz_func(img, preds, depth)
    return None

4.3.2 視覺化

  1. 視覺化結果從左至右為:left 左圖、right 右圖、disparity 視差圖、根據視差計算的 depth 深度圖;
  2. 視差圖中的顏色代表:顏色越紅,視差值越小;顏色越藍,視差值越大;
  3. 深度圖中的顏色代表:顏色越紅,對應的畫素深度值則越小;顏色越藍,對應的畫素深度值則越大;
  4. 視差圖到深度圖的計算原理可參考【雙目深度估計】—原理理解中的幾何法,相應程式碼可以參考 config 檔案中的 process_outputs 函式。

參考連結

  • https://blog.csdn.net/sinat_29819401/article/details/129382207
  • https://blog.csdn.net/wjinjie/article/details/122303148
  • https://github.com/meteorshowers/X-StereoLab
  • https://zhuanlan.zhihu.com/p/302888864
  • https://blog.csdn.net/gy1153441419/article/details/126709760
  • https://zhuanlan.zhihu.com/p/58165275

相關文章