上次介绍了双目摄像头如何估计深度的方案。老板表示两个摄像头还是有点贵呀,只用一个能不能做?嗯,没问题!
作者:Mankaran Singh 编译:McGL
激光雷达(LiDAR),或称光探测和测距(light detection and ranging),是一种流行的用于测量物体精确距离的遥感方法。他们能够生成物体周围的精确三维信息。
正如你所看到的,激光雷达生成了一个非常精确的周围世界的三维地图。这张地图是以点云的形式可视化的。点云由三维空间中沿 X、 Y 和 Z 轴的点组成。这些点在三维空间中绘制时构成了如上图所示的场景。这个场景可以用于自动驾驶车辆的路径规划、环境建图、 AR 应用,也可以用于需要“深度信息”的任何其他应用。
激光雷达的问题
在计算深度信息时,激光雷达是非常精确的。深度信息是自主车辆进行路径规划、与目标保持安全距离等工作的重要依据之一。这使得激光雷达成为集成到自动驾驶汽车的一个极佳选项。但问题是,它们太贵了!!
激光雷达价格昂贵
早些时候,远程激光雷达费用约为75,000美元。为了降低激光雷达的成本,广泛的研究一直在进行。Waymo,Alphabet 公司的子公司,通过他们的研究,可以将成本降低90% !
但尽管如此,迄今为止,用于自动驾驶汽车的激光雷达比一些低端汽车本身的成本还要高。激光雷达的维护和处理输出仍然是一项昂贵和令人头疼的任务。所以这使得它们不是商业化生产自动驾驶汽车的很合适选择。
其次,激光雷达在恶劣的天气条件下不能很好的工作,它们会产生膨胀的点云,这可能会使激光雷达点云的输出不准确。
但是公司仍然应该投资使用激光雷达 和点云处理的方法来实现自动驾驶,因为谁知道呢,也许有一天激光雷达也会变得非常便宜呢?
摄像头的问题
摄像头擅长捕捉高分辨率的场景细节。但问题是,它们不像激光雷达那样为我们提供“深度信息”。该相机的输出是一个高分辨率,但平坦的二维图像。而且几乎不可能从单张图像中获得“深度信息”。目前有一些利用立体视觉从图像中获得深度的方法。
通过立体图像对计算深度图
给定两个摄像头在同一水平位置拍摄的两幅图像,利用计算机视觉算法估计深度信息。
在计算机视觉文献中有很多立体深度估计算法,但据我所知,它们都不能同时实现:
- 实时处理
- 高精度
- 全自动
神经网络是如何发挥作用的
但是等等,人类使用立体视觉(眼睛) ,并且在估算深度方面非常出色,即使它是一张单一的图像。你甚至可以闭上一只眼睛,仍然可以合理地估计深度!
哇!这里发生了什么?人类真的学会了如何感知深度吗?我们不能真正回答这个问题,因为我们不能回忆起我们刚出生时的世界是什么样子的。
但是,深度是否可以作为一个学习问题来处理,这样它就足够好来解决自动驾驶问题?
Elon musk 讨厌激光雷达。特斯拉目前是最成功的自动驾驶公司之一,商业化生产自动驾驶汽车供消费者使用,他们的技术栈主要包括摄像头。
目前已有多篇论文将视觉深度估计视为一个学习问题。
有监督的深度估计
监督式深度学习背后的概念很简单,收集 RGB 图像和相应的深度图,训练一个类似于自动编码器(autoencoder)的结构来进行深度估计。(虽然训练起来不是那么简单,如果不在训练过程中集成一些特殊的技巧,FCN 永远不会真正起作用: p)。还有其他一些监督式学习的方法,
虽然这种方法比较容易掌握,但是在现实生活中收集深度图是一项昂贵的任务。激光雷达的数据可以用来训练这些类型的网络,所以如果我们用激光雷达收集的数据进行训练,神经网络的性能会比激光雷达差很多,当然仍然是可用的,因为我们不需要那么高的精度来驾驶一辆汽车,比如知道树上树叶的精确距离。
我们将在这篇博文中使用这种方法来训练网络。
无监督深度估计
仅仅在一系列环境中记录高质量的深度数据是一个具有挑战性的问题。无监督的方法可以在无ground truth深度图的情况下学习深度!
“这种方法只是观察未标记的视频,并找到一种方法来创建深度图,不是试图预测对,而是试图做到一致。”
欲了解更多信息,你可以参考论文《Unsupervised Monocular Depth Estimation with Left-Right Consistency》
从那时起,许多能够产生更好结果的非监督式学习方法被引入。
在这篇文章中,我们将会训练一个有监督的网络。无监督的方法我们将在以后的博文中讨论。
实现细节
我们将训练一个基于 CARLA 数据的神经网络,CARLA 是一个自动驾驶汽车模拟器。实现将基于论文《High Quality Monocular Depth Estimation via Transfer Learning》
简单的说,本文利用在 ImageNet 上训练的 DenseNet 模型作为编码器,并定义了一种基于双线性上采样(Bilinear Upsampling)的解码器。(将在下面讨论)。
使用的损失函数(将在下面详细讨论)是 MAE、 SSIM 和图像梯度差异(image gradients difference)的组合。
数据收集
首先,我们需要从 CALRA 模拟器收集训练数据。CARLA使深度图和相应的 RGB 图像的收集非常容易。但是,对于 CARLA 的初学者来说,这种数据收集可能非常棘手。
深度图长什么样
深度图看起来像这样,离摄像头近的东西比离摄像头远的东西更暗。
深度图存储为“ float64”或“ float32”对象类型数组。保存这些类型的深度图像的问题在于,图像通常以 uint8的形式存储。uint8的数据范围是从0到255,太离散而无法存储深度。我们需要连续和精确的深度测量,如0.254588, 5.56314, 100.25656等。为了解决这个问题,CARLA 有自己的原始深度图编码,RGB 图像看起来像这样:
CARLA 用RGB的编码深度图
现在可以将其存储为 RGB 图像。
这个 RGB 编码的 CARLA 原始深度图可以通过以下公式转换为实际深度:
代码语言:javascript复制normalized = (R G * 256 B * 256 * 256) / (256 * 256 * 256 - 1)
in_meters = 1000 * normalized
从CARLA深度图编码到实际逐像素深度的转换
代码可以在这里找到:https://github.com/MankaranSingh/Auto-Depth/blob/master/utils.py#L23
在这个转换之后,我们终于得到了每个像素深度,单位为米。所以现在我们可以开始存储 RGB 图像和它们相应的“原始 RGB 深度图”。
为了使数据收集过程更加容易,我们将在 CARLA 中设置一个主角车(ego-vehicle),并将其设置为自动模式,这样它将在城市周围自动驾驶,不断收集数据并保存到自己的磁盘上!
但在最终开始采集深度数据之前,还有一个问题需要解决,那就是自主模式的车辆总是在红绿灯前停下来,造成大量的冗余数据。为了解决这个问题,当自我车辆到达红绿灯时,灯会自动变绿。但是要确保你不会在地图上产生超过50辆车,因为那样的话,把交通灯变成绿色会导致交通事故,而我们的主角车会卡在里面!
使交通灯变绿,以避免数据冗余
数据收集脚本是由 Raghav Prabhakar 编写的! 感谢他上传和开源数据。
Raghav Prabhakar 上传的13GB 数据在这里:https://drive.google.com/file/d/1i5Y7Nd-DaWGord9ai-ngT3cn5Pa9az6p/view?usp=sharing
最后,保存摄像头的视场(FOV),图像宽度,高度。这些将被用来构造摄像头的固有矩阵(intrinsic matrix),此矩阵使用深度信息将图像空间的像素投影到三维世界。(稍后会详细讲到)
神经网络结构
该网络有一个类似于U-Net的架构,编码器部分是一个在 ImageNet 数据集上训练的预训练DenseNet 模型。解码器部分采用双线性上采样,而不是简单的上采样。
简言之,我们使用双线性上采样,因为它使整体上采样后的图像“更平滑”。我们也可以使用先进的上采样技术,如双立方(Bi-Cubic )上采样,lanczos3, lanczos5等。但我注意到,这些方法往往训练后会产生更多的artifacts,而且他们训练需要更昂贵的计算资源和更多的时间。因此,双线性上采样是一个最佳点。
输出的深度图是图像大小的一半。这有助于网络更快学习。以后我们总是可以将输出上采样到原始图像大小。
损失函数
最初,一个模型通过简单的 MAE 或 MSE 损失函数训练,结果并不令人满意。
最初的结果(和最后的模型差距很大)
论文中的损失函数和本文中使用的损失函数分为三部分:
- MAE: 这是用来惩罚预测的深度值。这是一个独立于其他邻近像素的像素级损失。卷积神经网络工作良好,因为他们考虑到邻近像素和图像是高度相关的。在这种情况下,我们的神经网络具有“类图像”的输出,因此考虑图像级的损失比单独使用“逐像素”损失更有意义。一个好的候选是 SSIM
- SSIM (Structural Similarity Index): 这种损失实际上测量了相似图像之间的感知差异。结构信息是指像素具有强烈的相互依赖性,特别是当它们在空间上相近时。这些依赖关系在视觉场景中承载着关于对象结构的重要信息。SSIM被广泛用作深度学习图像重建任务的一种损失。SSIM 是一种结构损失,因此同时使用另一种损失如 MSE/MAE 很重要,所以现在我们可以既惩罚结构又惩罚像素深度。我早些时候在图像压缩上做深度学习,使用独立的 SSIM 损失,输出结果非常好,但颜色不对。SSIM不知道图像是否丢失了对比度或颜色。下面显示的图片来自另一个项目,这并不是显示颜色丢失的最好例子,但是我可以告诉你,它的输出结构上比 MAE 更相似,但是问题是颜色信息丢失。因此,采用 MSE 和 SSIM 的加权组合,可以解决这一问题。
- 图像梯度损失: 图像梯度是图像中灰度或颜色的方向变化。图像的梯度是图像处理的基础构件之一。例如,Canny边缘检测器使用图像梯度进行边缘检测。这种损失惩罚了深度输出中的边缘。
幸运的是,tensorflow 已经完成了实现这些损失函数的艰苦工作。
代码实现在这里:https://github.com/MankaranSingh/Auto-Depth/blob/master/loss_function.py
图像增强
对于图像增强,我们可以使用以下技术:
- 图像翻转
- 输入图像的颜色通道shuffling
- 向输入图像添加噪声
- 增加输入图像的对比度、亮度、温度等
这将确保模型在整个训练过程中不断看到新的数据,并更好地对未看到的数据进行泛化。
代码在这里:https://github.com/MankaranSingh/Auto-Depth/blob/master/augmentation.py
深度normalization
深度normalization是与深度图相反的概念,因为我们需要惩罚那些“更近”的东西,而不是远处的东西,因为对于路径规划来说,更近的物体更为重要。
代码语言:javascript复制depthNormalized = maxDepth / original_depth_map
其中 maxDepth 是整个数据集中的最大深度值,在我们的例子中是1000。
在执行深度normalization之前,确保裁剪深度图到最小深度0.1和最大深度1000之间,以防止在深度图中出现被零除的错误。这将导致错误 NaN,梯度爆炸并使整个网络变得无用。
训练网络
你必须非常小心使用超参数,一个错误的参数,损失就会直接变成 NaN。
该模型使用 Adam 优化器,学习率 = 0.0001,无amsgrad训练10个epoch。在 colab 的 P4 GPU 上一个epoch花费3.5个小时。
最终模型总计经过了35小时的训练。其他的变种也训练,所以需要很长时间才能得到结果。
这个网络由 Raghav Prabhakar,Chirag Goel,Mandeep 和我共同合作训练完成。
结果
结果
哇! 这看起来简直就是一幅完美的深度图!
我对三维重建的效果感到非常兴奋。
要做三维重建,你需要懂一些数学。
下面是对没看过的数据进行三维重建的结果:
有意思!结果看起来非常好,可以用于路径规划,我们将在其他的博客文章中讨论。
该模型可以在GTX 1060上实时运行 !
虽然,预测的深度图看起来清晰逼真,三维重建看起来仍然有点不稳定。这表明,即使是深度上的微小缺陷也可能导致三维重建点云中的大误差。这可以通过逆Huber损失来修复。
这种损失惩罚的是远处的物体,而不是近处的物体。但是请记住,如果我们使用深度Normalization,影响将是相反的。
这个模型没有经过充分的训练,因此还有很大的改进空间。其次,为了改进重建,你可以使用异常值去除,如下所述:http://www.open3d.org/docs/release/tutorial/Advanced/pointcloud_outlier_removal.html
进一步的改进
当前的模型接受单个图像的输入。从简单的图像中估计所有物体的正确深度实际上是不可能的。
为了解决这个问题,我们可以通过输入一系列的帧,或者一对立体图像来更好地估计那些不能通过一张图像映射到一对一解决方案的事物。
结语
完整项目的代码链接:https://github.com/MankaranSingh/Auto-Depth
这是一个非常有趣的项目。深度估计在 AR/VR 中也有很多应用。比如苹果就在他们的 iPad 中增加了一个低量程的迷你激光雷达。
原文:https://medium.com/swlh/making-a-pseudo-lidar-with-cameras-and-deep-learning-e8f03f939c5f