★★★ 本文源自AlStudio社区精品项目,【点击此处】查看更多精品内容 >>>

1. 项目背景

  • 双目深度估计重要性 : 双目深度估计是一个基本的视觉任务。其标准流程的流程,是需要我们提供了两个帧——一个左帧和一个右帧作为输入,任务是估计输入图像之间的像素位移图,即视差图。根据以下公式可以从已知的相机参数和和估计出的视差图恢复深度,得到点云等。双目深度估计可以直接应用于机器人增强现实摄影测量视频理解等领域

  • RAFT-Stereo优势 :早期的双目深度估计研究集中在特征匹配和正则化两个关键部分。在处理中,使用3D卷积神经网络计算代价体是主流方法,但代价较大,需要特殊方法才能操作高分辨率的图像。而光流问题则通常使用迭代精化的方法。光流和矫正立体镜头是紧密相关的问题,但两个任务的神经网络结构却有很大差异。RAFT-Stereo则是一种新的双视立体结构体系,利用了RAFT特有的3D体积和迭代细化法。RAFT-Stereo仅使用2D卷积和一个轻量级的成本体积,相比之前的立体网络,具有更好的泛化能力不需要复杂的损失项。RAFT-Stereo在KITTI、ETH3D和Middlebury等真实数据集上表现非常出色,且具有更好的跨数据集泛化能力。效果如下图所示:
  • 本项目简要介绍模型原理,并使用PaddlePaddle框架对RAFT-Stereo模型进行复现,提供训练、测试与评估的代码,不过训练模型建议使用多卡。可以不训练直接体验训练好的权重

2. RAFT-Stereo方法介绍

  • 双目深度估计是给定一对矫正后的图像( I L I_L IL, I R I_R IR),目标是估计一个视差场 d d d,使每个 I L I_L IL中的像素都有水平的位移。与RAFT类似,RAFT-Stereo的方法由三个主要组件组成:特征提取器、相关金字塔和基于GRU的更新运算符,如下图所示。更新运算符迭代地从相关金字塔中检索特征并对视差场进行更新。

2.1 特征提取

  • RAFT-Stereo使用了一种基于特征编码器上下文编码器特征提取方法。特征编码器应用于左右图像并生成密集的特征图,用于构造相关体积。上下文编码器与特征编码器结构相同,但采用批量归一化替代实例归一化,仅在左图像上应用。在作者的方法中,上下文特征用于初始化更新运算符的隐藏状态,并在每次迭代中注入GRU。

2.2 相关金字塔

  • 相关金字塔,用于提高矫正立体视觉的精度和鲁棒性。相关体积基于特征向量之间的点积,用于计算图像之间的相似性;相关金字塔通过对最后一个维度进行平均池化构建,它具有增加的接受野,但仅通过对最后一个维度进行池化,可以保留原始图像中的高分辨率信息,从而允许恢复非常细微的结构
  • 为了从相关金字塔中检索像素,作者使用相关查找算法,它利用一个类似于 RAFT 中定义的查找算子和线性插值来实现检索。该方法具有高效性和鲁棒性,并且易于实现

2.3 多级更新运算符

  • RAFT-Stereo从一个初始的起点 d 0 = 0 d_0 = 0 d0=0开始预测一系列视差场 d 1 、 … 、 d N {d_1、…、d_N} d1dN。在每次迭代中,作者使用目前的视差估计来索引相关体积,产生一组相关特征。这些特征经过了2个卷积层。同样地,目前的视差估计也经过了2个卷积层。然后将相关特征、视差和上下文特征连接起来并注入到GRU中。GRU更新隐藏状态,新的隐藏状态然后用于预测视差更新

  • 多个隐藏状态原始的RAFT完全在固定的高分辨率上进行更新。这种方法的一个问题是,随着GRU更新次数的增加,接受域的增加非常缓慢。这可能对纹理较大、局部信息较少的场景有影响。RAFT-Stereo通过一个同时在1/8、1/16和1/32分辨率上操作特征映射的多分辨率更新运算符来解决这个问题

  • 上采样:预测的视差场分辨率为输入图像的1/4或1/8。为了输出全分辨率的视差图,作者使用了和RAFT相同的凸上采样方法。RAFT-Stereo将全分辨率的视差值视为其粗略分辨率邻居3x3网格的凸组合。最高分辨率的GRU预测凸组合权重

2.4 递归神经网络模型

  • 针对隐藏状态的1/8分辨率进行GRU更新所需的FLOPs大约是更新1/16分辨率隐藏状态的4倍。为了加快推理速度,在RAFT-Stereo的一个版本中,每次更新1/8分辨率的隐藏状态时都会多次更新1/16分辨率和1/32分辨率的隐藏状态。这种修改将运行时间降低了52% ,但是不显著降低性能,使得RAFT-Stereo能够在实时运行中得到高效的计算结果

2.5 计算损失

  • 训练的过程中RAFT-Stereo预测一系列的视差图,整个预测序列中,作者监督预测和真值差距之间的距离 l 1 l_1 l1 d 1 , . . . , d N {d_1,..., d_N} d1,...,dN,权值呈指数增长。给定视差图真值 d g t d_gt dgt,损失定义为如下:

3. 预训练与finetune

  • 遵循以前的工作,使用合成Sceneflow数据集对模型进行预训练。但是由于Sceneflow数据集太大了,在AI Studio训练成本太高,所以直接将预训练权重转为paddle的,并放在RAFT-Stereo-Paddle/models/raftstereo-sceneflow.pdparams,并使用该权重进行Middlebury的零样本测试,得到结果如下表所示,误差是端点误差大于指定阈值的像素百分比指标(越小越好), Middlebury为2px,结果可以与论文对齐:

    modelmiddleburry Fmiddleburry Hmiddleburry Q
    paper18.3312.599.36
    paddle17.0011.6510.84
  • 作者在ETH3D、Middlebury 和KITTI-2015上评估了RAFT-Stereo的性能,目前使用Paddle框架复现的RAFT-Stereo能在KITTI-2015上与论文对齐 ,使用单机四卡的脚本任务复现KITTI-2015的结果,链接如下:RAFT-Stereo单机四卡复现 。训练得到的权重放在RAFT-Stereo-Paddle/models/kitti2015_raft-stereo-paddle.pdparams

  • 在Middlebury上使用作者提供的设置,多次训练均不能复现,已提issue:Question about fine-tuning on Middlebury2014

4. 测试

  • 本项目测试在合成数据集上预训练的权重在Middlebury2014数据集上零样本泛化的性能
# 配置环境
%cd /home/aistudio/RAFT-Stereo-Paddle/
!pip install -r requirements.txt
# 准备数据
!unzip -qo ../data/data189152/Middlebury.zip -d ./datasets/
# Middlebury full
!python evaluate_stereo.py --restore_ckpt models/raftstereo-sceneflow.pdparams --dataset middlebury_F --corr_implementation alt
# Middlebury half
!python evaluate_stereo.py --restore_ckpt models/raftstereo-sceneflow.pdparams --dataset middlebury_H --corr_implementation alt
# Middlebury quarter
!python evaluate_stereo.py --restore_ckpt models/raftstereo-sceneflow.pdparams --dataset middlebury_Q --corr_implementation alt

5. 预测

  • 使用raftstereo-middlebury.pdparams权重对Middlebury2014测试集中的图像进行深度估计,结果保存在work/middlebury_test文件夹下
!python demo.py --restore_ckpt models/raftstereo-middlebury.pdparams --corr_implementation alt --mixed_precision \
 -l=datasets/Middlebury/MiddEval3/testF/*/im0.png -r=datasets/Middlebury/MiddEval3/testF/*/im1.png \
--output_directory ../work/middlebury_test 
# 展示视差图预测的结果
from PIL import Image
import matplotlib.pyplot as plt
%matplotlib inline

image_file_l = r"./datasets/Middlebury/MiddEval3/testF/Newkuba/im0.png" # 左视图
pred_file = r"../work/middlebury_test/Newkuba.png" # 左图的视差图

img_l = Image.open(image_file_l)
disp = Image.open(pred_file)

plt.figure(figsize=(12, 6))
plt.subplot(1,2,1), plt.title('Left')
plt.imshow(img_l), plt.axis('off')
plt.subplot(1,2,2), plt.title('Pred_disp')
plt.imshow(disp), plt.axis('off') 
plt.show()

在这里插入图片描述

  • 也可以预测KITTI-2015数据集的结果,可以自行准备数据集测试,本项目仅提供预测代码
python demo.py --restore_ckpt ./models/kitti2015_raft-stereo-paddle.pdparams --corr_implementation reg --mixed_precision \
 -l=./datasets/KITTI/testing/image_2/*_10.png -r=./datasets/KITTI/testing/image_3/*_10.png
  • 同时,RAFT-Stereo的作者推荐使用raftstereo-middlebury.pdparams权重对自己拍摄的自然图像进行双目深度估计。下面以我宿舍拍摄的图片为例子,给出示例代码
!python demo.py --restore_ckpt models/raftstereo-middlebury.pdparams \
--corr_implementation alt --mixed_precision \
-l=../work/own_test/left.jpg \
-r=../work/own_test/right.jpg \
--output_directory ../work/own_test/disp 
W0523 15:00:22.128645 31409 gpu_resources.cc:61] Please NOTE: device: 0, GPU Compute Capability: 7.0, Driver API Version: 11.2, Runtime API Version: 11.2
W0523 15:00:22.133347 31409 gpu_resources.cc:91] device: 0, cuDNN Version: 8.2.
Found 1 images. Saving files to ../work/own_test/disp/
100%|█████████████████████████████████████████████| 1/1 [00:15<00:00, 15.69s/it]

it]

# 展示视差图预测的结果
from PIL import Image
import matplotlib.pyplot as plt
%matplotlib inline

image_file_l = r"../work/own_test/left.jpg" # 左视图
pred_file = r"../work/own_test/disp/own_test.png" # 左图的视差图

img_l = Image.open(image_file_l)
disp = Image.open(pred_file)

plt.figure(figsize=(12, 6))
plt.subplot(1,2,1), plt.title('Left')
plt.imshow(img_l), plt.axis('off')
plt.subplot(1,2,2), plt.title('Pred_disp')
plt.imshow(disp), plt.axis('off') 
plt.show()

在这里插入图片描述

6. 生成点云

  • 已知相机内参,则可以通过双目深度估计得到的视差图恢复场景点云。如以下代码,将middlebury2014测试集中bicycle图像生成点云
!python demo.py --restore_ckpt models/raftstereo-middlebury.pdparams \
--corr_implementation alt --mixed_precision \
-l=datasets/Middlebury/MiddEval3/testF/Bicycle2/im0.png \
-r=datasets/Middlebury/MiddEval3/testF/Bicycle2/im1.png \
--save_numpy

# Calibration
fx, fy, cx1, cy = 3997.684, 3997.684, 1176.728, 1011.728
cx2 = 1307.839
baseline=193.001 # in millimeters
import numpy as np
from pathlib import Path
from imageio import imread

testF_folder = Path("datasets/Middlebury/MiddEval3/testF/Bicycle2")

disp_path = f"demo_output/{testF_folder.name}.npy"
disp = np.load(disp_path)
image = imread(testF_folder / "im0.png")

# inverse-project
depth = (fx * baseline) / (-disp + (cx2 - cx1))
H, W = depth.shape
xx, yy = np.meshgrid(np.arange(W), np.arange(H))
points_grid = np.stack(((xx-cx1)/fx, (yy-cy)/fy, np.ones_like(xx)), axis=0) * depth

mask = np.ones((H, W), dtype=bool)

# Remove flying points
mask[1:][np.abs(depth[1:] - depth[:-1]) > 1] = False
mask[:,1:][np.abs(depth[:,1:] - depth[:,:-1]) > 1] = False

points = points_grid.transpose(1,2,0)[mask]
colors = image[mask].astype(np.float64) / 255
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import axes3d

%matplotlib inline

NUM_POINTS_TO_DRAW = 100000

subset = np.random.choice(points.shape[0], size=(NUM_POINTS_TO_DRAW,), replace=False)
points_subset = points[subset]
colors_subset = colors[subset]

x, y, z = points_subset.T

fig=plt.figure(figsize=(8, 8))

ax = axes3d.Axes3D(fig)
ax.view_init(elev=12., azim=72)
ax.scatter(x, -z, -y,
           cmap='spectral',
           c=colors_subset,
           s=0.5,
           linewidth=0,
           alpha=1,
           marker=".")

plt.title('Point Cloud')
ax.axis('scaled')  # {equal, scaled}
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
plt.show()

在这里插入图片描述

此文章为搬运
原项目链接

Logo

学大模型,用大模型上飞桨星河社区!每天8点V100G算力免费领!免费领取ERNIE 4.0 100w Token >>>

更多推荐