
深度学习:无监督深度测距:《Unsupervised Monocular Depth Estimation With Left-Right Consistency》论文笔记,视频笔记monodepth
2017 年的CVPRfrom youtubeLink是啥东西然后我们的工作能: 给出 一个 深度 图like this:为啥 depth 有用导航,机器人拿东西,虚拟现实,桌子有多远,多大为啥用 monocular:单目相机?贫穷和普适monocular 能行吗?既然 探测器 和 海盗头子 都能 ,我们也能以前的方法ground truth 值很难获得kitti 数据集 本身 也有误差或者说 遗
总揽:论文干了啥:
- 备注,这部分参考了: 读论文1
一句话:采用Left-Right Consitency来优化。
也就是以左视图为输入,以右视图为training中的监督真值(gruond truth),生成右侧对应的视图;然后又以左视图为监督真值,根据右视图生成左视图。最小化这两个过程的联合loss则可以一个很好的左右视图对应关系。
最终网络得到一个四个scale大小的输出(disp1-disp4)
论文的代码
如何做的,提出了什么新的想法
利用极坐标几何约束,我们可以用图像重建损失训练我们的神经网络,从而生成视差图。
我们还发现, 如果只用图像重建求深度图,会导致其质量较差。(之前的一些 DCNN方式 就是 用了 只用图像 的方式,直接求深度 图的方式,这种方式,在本文看来就是垃圾。)
为了解决这一问题,本文提出了一种新的损失函数,它加强了 左右视差图的一致性,与现有的方法相比,它提高了性能和鲁棒性。
1)你知道视差 和 深度 是有关系的
视差 和 深度 是有关系的;可以互相推算
disparity ~ depth
为什么 他们 可以互相推算: 一个 图解
假设 我们拍摄了两张图片, L, R
也就是 下面 图 里面 的 左右 两个 绿色框
现在 ,在 L 上 有 橙色 的一点,这个点, 可以来自于 其 射线 上 的:
- 紫色
- 绿色
- 蓝色
三个 可能 的位置;
当然实际上 有 无数可能性,而不仅仅是 这三个点; 我只是 用这三个 进行举例
假定 绿色 是真实 的位置
而 紫色 是 离镜头近 的一个 可能性 位置
蓝色 是 远一点的 可能性位置。
我们的模型,应该有能力 从 紫色 ,蓝色, 绿色 的可能性中,准确地知道,真实 的值 应该是 绿色 那个
如果 我们的模型 能知道 是绿色 那个,也就是说,我们的模型 具有 深度/距离 预测的功能了。
好的,如何 让模型 知道 是 绿色 呢?
我们从右图 出发,放大观察 一下 右图 的特点
(备注: 在上述两个图示中,我们有这样一个预设: 就是,左右两个相机是水平的,也就是说,它们拍摄得到的图像,在垂直的上下方向上没有什么错位,而仅仅在水平的, 左右的方向上存在错位,也就是,只在左右的 方向上,存在视差,而垂直的方向上,没有什么 视差)
退一步说,如果我们的模型 能 预测 绿色点 往左 多少 能 生成 一个 「紫色点 在 右图 的位置」, 能 预测 绿色点 能 往右多少,生成 的 是 「蓝色点 在 右图 的位置」。
这里的往左,往右,就是 一个 标量,用于和 左图 的 绿点 坐标进行 「某种计算」,从而产生位移,让位移的 绿点 变成 紫色点,or 蓝色点。
对于左图 的每个 像素(每个绿点) G r e e n P o i n t I m a g e L GreenPoint_{Image_L} GreenPointImageL,都有一个 对应的 d r d_r dr 与之匹配,能够 发生 这么一件事:
G r e e n P o i n t I m a g e R = G r e e n P o i n t I m a g e L ∗ d r GreenPoint_{Image_R} = GreenPoint_{Image_L} * d_r GreenPointImageR=GreenPointImageL∗dr // 这个公式的意思是: 右图上的绿点 来自 左图上的绿点 与 右向视差图 运算所得。
(想一想,这里真的是简单的乘法运算吗?还是带有加法的复杂矩阵运算呢?)
那么 这个预测右图每一个绿点的能力,就是完成了这件事: 变向地预测了 绿点 离镜头 的距离
也就是说, 只要模型能预测 d r d_r dr ,就能 预测 d e p t h depth depth
记住这句话:
视差是按比例缩小的逆深度,disparity=fb/depth
其中 的 fb 是 我们 规定/从经验 ,或者 计算 中得到的。
fb 分别是:
- f: 相机焦距(我们俩眼睛 的 晶状体 的 厚度导致的光线折射??)
- b: baseline distance(我们俩眼睛的距离??)
2)你知道我们如何用深度网络估计【视差/距离】
论文给了三个 架构,用来强调第三个(论文提出的这个)是最牛逼的, 分别是
- naive
- NoLR
- Ours (with LR)
从左到右,进行 解释:
naive的网络:
直接从Left image 生成 d r d_r dr , 然后 对 Left image 进行 采样 ,与 d r d_r dr 进行运算,得到 输出 :
估计的: I ~ r \widetilde{I}^r I r
然后 跟真实 的 I r I^r Ir 进行比对
结果不好
NoLR 的网络:
应该是没有 加上 left right consistency 的意思
这里的思路是:
直接从Left image 生成 d l d_l dl , 然后 对 Right image 进行 采样 ,与 d l d_l dl 进行运算, 得到 输出:
估计的: I ~ l \widetilde{I}^l I l
然后 跟真实 的 I l I^l Il 进行比对
结果是: 存在 很多 ‘贴图复制’ 的人工痕迹,并且深度不连续
见图片:
我们的方法:带有 left right consistency
仍然是从 图片 生成 disparity map, 但是这次,是从 left image 生成 d r d_r dr, 以及 d l d_l dl
然后 用相反 的 图片 进行采样
说清楚一点,就是, 和
d
r
d_r
dr 做运算的 ,其采样来自 left image 也就是 左侧的蓝线(连接了
I
l
I^l
Il 和
d
r
d^r
dr 后的sampler)
和
d
l
d_l
dl 做运算的,其 采样来自 right image 也就是 右侧的蓝线 (连接了
I
r
I^r
Ir 和
d
l
d^l
dl 后的 sampler)
然后生成 对应的 I ~ r \widetilde{I}^r I r , I ~ l \widetilde{I}^l I l , 跟真实 I r I^r Ir, I l I^l Il 对比
作者说的 4 个 scale 到底是什么意思?
下面 这个 图 就是在 讲 他们的 模型架构,写了每次层的参数,每一层的输入 是谁,
也就知道了 网络 的前后关系,各个层组件是什么了;
只不过,通过表格的方式,没有 通过 一个 流程图的方式 更好,所以希望 后期 我可以 把 一个 流程图 添加在这里(todo 2021-09-08 15:01:27)
注意 图中 的解释:
k: 就是 kernel size ,一般 就是 设定 为 3
s: 就是 stride 就是步长,一帮就是 1 或者 2
需要进一步 理解的东西:
channels
本身图片 就只有三个 通道,分别 是 rgb ,我们人类认识物体的颜色,就是这 三个 通道;
但是神经网络 可以 认识一些 更多 通道的信息,通道 的增加来自于 卷积 ,(aka 来自于 filter,来自于 kernel)
downscale factor
猜一下: 向下缩放的 因数,
单不知道是在缩放啥 todo 2021-09-08 14:44:50
upsampling
上采样,我猜测 就是 卷积 这个动作
根据维基百科 的解释: Upsampling
这是从低 波特率 的 序列中,进行 扩张,插值;以至于 最后得到的输出,很像 一个 从更高频率 下 采样得到的序列;
downsampling(就是卷积)
根据维基百科 的解释: Downsampling(signal_processing)
向下采样,类似于 音频里面 的 降低 波特率,压缩, 得到一个 品质稍微又些次的复制品,近似品。
它的同义词 叫做 decimation : 大规模歼杀;
我不是很理解的 loss function
我们拆开看:
第一个: appearance matching loss: C a p l C_{ap}^l Capl
这里 涉及 到了: SSIM , L1 正则化
L1 正则化
是为了 避免 模型 过于 复杂,避免 过 拟合
SSIM
来自一篇 2004 论文: Image quality assessment: from error visibility to structural similarity.
看 论文 的名字,大概 可以 知道 SSIM 是一种 评估 图像质量 的 指标,从 error 的可见性,到 结构 上 的相似度。
第二个: disparity smoothness loss
留待之后解释 todo
第三个: left right consistency loss
这篇论文的代码:
模型的代码:链接
有关 loss 的代码
这里参考了 这个博客
Training Loss
成本有如下三个部分ap,ds,lr,Cap是根据训练输入与它的重建图像相近的程度,
Cds限制了视差预测的平滑性,
Clr倾向于左右视差一致性。
只有左图经过卷积神经网络的洗礼
虽然每个主要的步骤当中都包含着左右两图。
1. Appearance Matching Loss
图像是通过空间转化网络(STN)与视差图从对应的立体匹配图片中
获取相应的像素值。
STN利用双线性采样,
这样输出像素是四个输入像素的加权之和。
2. Disparity Smooth Loss
这个项就是使得预测出来的视差变得非常的平滑,
所以就在视差梯度上面加了一个惩罚,
这个成本的权重是与图像梯度有关,就是边缘项
3. Left-right disparity consistency loss
没什么好讲的,直接上代码
至此,写完了 loss ,论文 就开始 实验,这个 上述 博客 也 结尾了。
所以,对 loss 的理解 还是 非常重要的。
你要理解下面 这几个 名词: todo: 2021-09-08 15:25:54
- 空间转化网络(STN)
- 双线性采样
- 四个输入像素的加权之和
- 视差梯度上面加了一个惩罚
- 成本的权重是与图像梯度有关
一些 可以 继续探索 的文章 和 博客:
- 被本文 参考 的: mayer: DispNet: 很像之前的: end-to-end deep optical flow network
2017 年的 CVPR 视频 观看笔记;趣味向
2017 年的 CVPR
from youtubeLink
是啥东西
然后
我们的工作能: 给出 一个 深度 图
like this:
为啥 depth 有用
导航,机器人拿东西,虚拟现实,桌子有多远,多大
为啥用 monocular:单目相机?
贫穷和普适
monocular 能行吗?
既然 探测器 和 海盗头子 都能 ,我们也能
以前的方法
- ground truth 值很难获得
kitti 数据集 本身 也有误差
或者说 遗漏: 见图片:
那咱么 可以用 双目 立体 几何 的手段
去训练
数学原理是:
原理就是上面这个原理,意思就是: L 和 R 之间的 差异 就是 跟 depth / 距离有关
现在 有很多 双目 设备:
我们不是第一个 用 双目 去做测距的:
多说一句:
这个文章 可能 就是 比较 早期 的 使用 双目 的 无监督 的 方法;
再补充一下:
这个文章 则是 使用 了 监督 的;
后记;视频里面的一些 细节
似乎上面 是在 讲 一个 baseline 的 模型,
下面 才是 我们的 的模型
我们 的 模型,似乎 是 可以 通过 生成 左右 两个 图片 进行 balalba。。。
一个 重要 组件
differentiable image smapler
他可以 把 left input image 转成 targe 的 input right image
看看这个图:
注意 线 1 和 线 2 ,这些 线 把 原始i 的 input 数据 ,打入 内部 走线 的输出
内部 走线 的输出 ,就是 一个 disparity 图片??
我的 理解 是 :
- 上面 的 R disparity 图像 ,是 left image 通过 线 1 , 进行 变化,从而 达到 逼近 right image 的 一个 转换器/convert
- 下面 的 L disparity 图像,虽然 也是 left image 输入 而 产生的,但是 是 用做 给 right image 用的,right iamge 通过 线 2 经历 了 L disparity 所代表的 convert 过程,就能 把 right image 自己 给 convert 成 一个 逼近 的 left image
有关于 架构
你可以 使用 任何 的 encoder: VGG ,ResNet
有关 结果
RTFSC (Read the f*cking source code )
读论文是RTM,Read the manual;接下来就是 RTFSC
但是 RTFSC 的过程中 也离不开 「STFW (Search The F*cking Web)」
所以,这一部分 记录 一下 这两个 步骤的 内容;
为了看懂代码:在网络上搜索到的tf相关知识
在monodepth_model.py 这个文件里
有这么一个类
有个 build_model() 函数:
def build_model(self):
# 使用 arg_scope 函数 (见 tt3 ) 为 conv2d, conv2d_transpose 都设定 同一个 elu 激励函数
with slim.arg_scope([slim.conv2d, slim.conv2d_transpose], activation_fn=tf.nn.elu):
with tf.variable_scope('model', reuse=self.reuse_variables):
self.left_pyramid = self.scale_pyramid(self.left, 4)
# 如果 是训练 阶段,那么 就 需要 右边的 缩放图 来帮忙
if self.mode == 'train':
self.right_pyramid = self.scale_pyramid(self.right, 4)
# 如果 开启了 立体的选项,那么 就把模型的input 制作为 左右图的 一起连接
if self.params.do_stereo:
self.model_input = tf.concat([self.left, self.right], 3)
else:
# 如果没有开启 立体 选项,那么 就是 仅仅 用 左图;
self.model_input = self.left
#build model
# 如果 使用 vgg 的编码器
if self.params.encoder == 'vgg':
self.build_vgg()
# 如果使用 resnet 的编码器
elif self.params.encoder == 'resnet50':
self.build_resnet50()
else:
return None
里面用到的 scale_pyramid 函数 ,是如下定义:
这个函数 整体 的流程 ,就是 确定输入;不过是加上了 scale_pyramid 这种方式 来重制了 输入;
好了,如果我们有了输入,剩下的,就是把输入打到模型里,
它给了两种 model 的可选
- 一个是 vgg
- 一个是 resnet50
我们看一看 vgg 这种模型;
build_vgg()
todo 2021-09-09 12:03:06 variable_scope https://blog.csdn.net/UESTC_C2_403/article/details/72328815
函数如下,有点长,但是有重复部分,不虚;
def build_vgg(self):
#set convenience functions
# 这里 使用 了 卷积;见 tt1
conv = self.conv
# 这里 分为 deconv 和 upconv ;见 tt2
if self.params.use_deconv:
upconv = self.deconv
else:
upconv = self.upconv
with tf.variable_scope('encoder'):
conv1 = self.conv_block(self.model_input, 32, 7) # H/2
conv2 = self.conv_block(conv1, 64, 5) # H/4
conv3 = self.conv_block(conv2, 128, 3) # H/8
conv4 = self.conv_block(conv3, 256, 3) # H/16
conv5 = self.conv_block(conv4, 512, 3) # H/32
conv6 = self.conv_block(conv5, 512, 3) # H/64
conv7 = self.conv_block(conv6, 512, 3) # H/128
with tf.variable_scope('skips'):
skip1 = conv1
skip2 = conv2
skip3 = conv3
skip4 = conv4
skip5 = conv5
skip6 = conv6
with tf.variable_scope('decoder'):
upconv7 = upconv(conv7, 512, 3, 2) #H/64
concat7 = tf.concat([upconv7, skip6], 3)
iconv7 = conv(concat7, 512, 3, 1)
upconv6 = upconv(iconv7, 512, 3, 2) #H/32
concat6 = tf.concat([upconv6, skip5], 3)
iconv6 = conv(concat6, 512, 3, 1)
upconv5 = upconv(iconv6, 256, 3, 2) #H/16
concat5 = tf.concat([upconv5, skip4], 3)
iconv5 = conv(concat5, 256, 3, 1)
upconv4 = upconv(iconv5, 128, 3, 2) #H/8
concat4 = tf.concat([upconv4, skip3], 3)
iconv4 = conv(concat4, 128, 3, 1)
self.disp4 = self.get_disp(iconv4)
udisp4 = self.upsample_nn(self.disp4, 2)
upconv3 = upconv(iconv4, 64, 3, 2) #H/4
concat3 = tf.concat([upconv3, skip2, udisp4], 3)
iconv3 = conv(concat3, 64, 3, 1)
self.disp3 = self.get_disp(iconv3)
udisp3 = self.upsample_nn(self.disp3, 2)
upconv2 = upconv(iconv3, 32, 3, 2) #H/2
concat2 = tf.concat([upconv2, skip1, udisp3], 3)
iconv2 = conv(concat2, 32, 3, 1)
self.disp2 = self.get_disp(iconv2)
udisp2 = self.upsample_nn(self.disp2, 2)
upconv1 = upconv(iconv2, 16, 3, 2) #H
concat1 = tf.concat([upconv1, udisp2], 3)
iconv1 = conv(concat1, 16, 3, 1)
self.disp1 = self.get_disp(iconv1)
tt1:
tt2: 参考 : 理解deconvolution(反卷积、转置卷积)概念原理和计算公式、up-sampling(上采样)的几种方式、dilated convolution(空洞卷积)的原理理解和公式计算
deconv和up-sampling是FCN图像分割网络中用到的,两个扩大feature map尺寸的重要操作。
这里的扩大,就是我们通信工程里面的,上采样,把原来低频的信号,插值了, 变成更加高频的信号。
有的人 将deconv反卷积称作转置卷积,我们暂且先认为它俩的概念一致
deconv 的动画解释:
(deconv: 把不丰富的图像,变得丰富,把小尺寸的 图片,变为 大尺寸的)
conv 就是 deconv 的反向:是把大尺寸的 图片,变为 小尺寸的; 是在做特征提取;
tt3:
tensorflow.contrib.slim
其主要目的是来做所谓的“代码瘦身”。
- 消除原生tensorflow里面很多重复的模板性的代码,
- 让代码更紧凑,更具备可读性。
- 提供了很多计算机视觉方面的著名模型(VGG, AlexNet等)
- 我们不仅可以直接使用,甚至能以各种方式进行扩展
tensorflow.contrib这个库:此目录中的任何代码未经官方支持,可能会随时更改或删除。每个目录下都有指定的所有者。它旨在包含额外功能和贡献,最终会合并到核心TensorFlow中,但其接口可能仍然会发生变化,或者需要进行一些测试,看是否可以获得更广泛的接受。所以slim依然不属于原生tensorflow。
更多推荐
所有评论(0)