欢迎访问我的博客首页。


BTS

  • 1. 网络结构
  • 2. 两个 pytorch 函数
  • 3. 球坐标系和距离公式
    • 3.1 球坐标系
    • 3.2 距离公式
  • 4. LPG 层
    • 4.1 类 reduction_1x1
    • 4.2 类 local_planar_guidance
  • 5. 损失函数
  • 6. 参考

  本文讲解的是来自韩国汉阳大学研究团队提出的 BTS 算法,其想法来自 Make3D。Make3D 中有个概念 “superpixels”,指图像上颜色值相近的连通区域,这个区域内的颜色值相近,所以几乎没有纹理。BTS 把这样的区域叫做局部平面,通过预测局部平面的法向量来获取深度值。

1. 网络结构


  下面是来自 BTS 论文的网络结构图。其重要结构是 LPG 层,由类 reduction_1x1 和类 local_planar_guidance 实现,我们称之为 Reduce 层和 Guidance 层。 即 LPG 层由 Reduce 层和 Guidance 层串联而成。


图 1. B T S 网 络 结 构 图\ 1.\ BTS网络结构 图 1. BTS网络结构

  从图 1 可以看出,BTS 从 H/2、H/4、H/8 三个尺度的特征图预测深度信息。该网络的作用可以概况为:

  1. 把 stride=8 的特征经过 LPG 网络得到全尺寸深度图 c ~ 8 × 8 \tilde{c}^{8 \times 8} c~8×8。
  2. 把上一步得到的深度图经过下采样后与 stride=4 的特征叠加,叠加的结果经过 LPG 网络得到全尺寸深度图 c ~ 4 × 4 \tilde{c}^{4 \times 4} c~4×4。
  3. 把上一步得到的深度图经过下采样后与 stride=2 的特征叠加,叠加的结果经过 LPG 网络得到全尺寸深度图 c ~ 2 × 2 \tilde{c}^{2 \times 2} c~2×2。
  4. 把 stride=2 的特征图反卷积到全尺寸后经过 Reduce 层,得到全尺寸深度图 c ~ 1 × 1 \tilde{c}^{1 \times 1} c~1×1。
  5. 叠加前面的结果,再经过卷积得到这 4 个叠加分量的非线性组合,作为最终的全尺寸深度图 d ~ \tilde{d} d~。

  可以使用下面的代码调用 BTS 中创建的网络:

import argparse
import torch
import numpy as np
from pytorch.bts import BtsModelparser = argparse.ArgumentParser()
parser.add_argument('--encoder', type=str,help='desenet121_bts, densenet161_bts, resnet101_bts, resnet50_bts, resnext50_bts or resnext101_bts',default='densenet161_bts')
parser.add_argument('--dataset', type=str, help='kitti or nyu', default='nyu')
parser.add_argument('--max_depth', type=float, help='maximum depth in estimation', default=10)
parser.add_argument('--bts_size', type=int, help='initial num_filters in bts', default=512)
args = parser.parse_args()if __name__ == '__main__':net = BtsModel(args)input = torch.tensor(np.random.random(size=(1, 3, 256, 256)), dtype=torch.float32)output = net(input, 718.856)for out in output:print(out.shape)

2. 两个 pytorch 函数


1. tensor::repeat()

  假如 x . s i z e ( ) = [ d 0 , d 1 , . . . , d n ] x.size() = [d_0, d_1, ..., d_n] x.size()=[d0​,d1​,...,dn​], s i z e = [ a 0 , a 1 , . . . , a m , b 0 , b 1 , . . . , b n ] size = [a_0, a_1, ..., a_m, b_0, b_1, ..., b_n] size=[a0​,a1​,...,am​,b0​,b1​,...,bn​], y = x . r e p e a t ( s i z e ) y = x.repeat(size) y=x.repeat(size)。则 y . s i z e ( ) = [ a 0 , a 1 , . . . , a m , b 0 d 0 , b 1 d 1 , . . . , b n d n ] y.size() = [a_0, a_1, ..., a_m, b_0d_0, b_1d_1, ..., b_nd_n] y.size()=[a0​,a1​,...,am​,b0​d0​,b1​d1​,...,bn​dn​]。

2. torch.repeat_interleave()

  假如 x . s i z e ( ) = [ d 0 , d 1 , . . . , d n ] x.size() = [d_0, d_1, ..., d_n] x.size()=[d0​,d1​,...,dn​], y = t o r c h . r e p e a t _ i n t e r l e a v e ( x , r e p e a t s = r , d i m = k ) y = torch.repeat\_interleave(x, repeats=r, dim=k) y=torch.repeat_interleave(x,repeats=r,dim=k),则 y . s i z e ( ) = [ d 0 , d 1 , . . . , r d k , . . . , d n ] y.size() = [d_0, d_1, ..., rd_k, ..., d_n] y.size()=[d0​,d1​,...,rdk​,...,dn​]。

3. 球坐标系和距离公式


3.1 球坐标系


  下图是球坐标系示意图。

  原点到 P 点的距离为 r ,原点到点 P 的连线与正 z-轴之间的天顶角为 θ \theta θ。原点到点 P 的连线,在 xy-平面的投影线,与正 x-轴之间的方位角 ϕ \phi ϕ。则向量 r 的坐标在直角坐标系中是

{ x = r ⋅ s i n ( θ ) ⋅ c o s ( ϕ ) y = r ⋅ s i n ( θ ) ⋅ s i n ( ϕ ) z = r ⋅ c o s ( θ ) \begin{cases} x &= r \cdot sin(\theta) \cdot cos(\phi) \\ y &= r \cdot sin(\theta) \cdot sin(\phi) \\ z &= r \cdot cos(\theta) \end{cases} ⎩⎪⎨⎪⎧​xyz​=r⋅sin(θ)⋅cos(ϕ)=r⋅sin(θ)⋅sin(ϕ)=r⋅cos(θ)​

3.2 距离公式


  我们推导了 Make3D 和 BTS 中的距离公式,原点是相机光心。Make3D 求得的距离是局部平面上的某点 i i i 到相机光心的距离,BTS 求得的距离是局部平面上的某点 p i p_i pi​ 到相机成像平面的距离。

  BTS 的距离公式中, ( u , v ) (u, v) (u,v) 指的是点 p i p_i pi​ 的归一化坐标,即 p i ′ p_i' pi′​ 的坐标。

4. LPG 层


  LPG 即 local planar guidance,其结构如下图左侧黄色部分所示,它由两个类实现:红色虚线上方的部分是 Reduce 层,我们已经画出了 BTS 用到的 4 个 Reduce 层的实现;红色虚线下方的部分是 Guidance 层。


图 4. L P G 的 第 一 部 分 图\ 4.\ LPG 的第一部分 图 4. LPG的第一部分

  从图 1 和图 3 可以看出 BTS 共有 4 个 Reduce 层,3 个 Guidance 层。这是因为每个 LPG 层由一个 Reduce 层和一个 Guidance 层组成,多出的一个 Reduce 层单独使用,如图 1 所示。

4.1 类 reduction_1x1


  BTS 共有 4 个 reduction_1x1 对象。下表前 4 列是创建对象的参数,这些参数与输入的原图尺寸无关。第 5 列是对象处理的数据,L 代表原图边长。

num_in_filters num_out_filters max_depth is_final input output
128 128 10 False (b, 128, L/8, L/8) (b, 4, L/8, L/8)
128 64 10 False (b, 128, L/4, L/4) (b, 4, L/4, L/4)
64 32 10 False (b, 64, L/2, L/2) (b, 4, L/2, L/2)
32 16 10 True (b, 32, L, L) (b, 1, L, L)

  类 reduction_1x1 的实现如下。

class reduction_1x1(nn.Sequential):def __init__(self, num_in_filters, num_out_filters, max_depth, is_final=False):super(reduction_1x1, self).__init__()self.max_depth = max_depthself.is_final = is_finalself.sigmoid = nn.Sigmoid()self.reduc = torch.nn.Sequential()while num_out_filters >= 4:if num_out_filters < 8:if self.is_final:self.reduc.add_module('final', torch.nn.Sequential(nn.Conv2d(num_in_filters, out_channels=1, bias=False, kernel_size=1, stride=1, padding=0),nn.Sigmoid()))else:self.reduc.add_module('plane_params', torch.nn.Conv2d(num_in_filters, out_channels=3, bias=False, kernel_size=1, stride=1, padding=0))breakelse:self.reduc.add_module('inter_{}_{}'.format(num_in_filters, num_out_filters), torch.nn.Sequential(nn.Conv2d(in_channels=num_in_filters, out_channels=num_out_filters, bias=False, kernel_size=1,stride=1, padding=0),nn.ELU()))num_in_filters = num_out_filtersnum_out_filters = num_out_filters // 2def forward(self, net):net = self.reduc.forward(net)if not self.is_final:theta = self.sigmoid(net[:, 0, :, :]) * math.pi / 3phi = self.sigmoid(net[:, 1, :, :]) * math.pi * 2dist = self.sigmoid(net[:, 2, :, :]) * self.max_depthn1 = torch.mul(torch.sin(theta), torch.cos(phi)).unsqueeze(1)n2 = torch.mul(torch.sin(theta), torch.sin(phi)).unsqueeze(1)n3 = torch.cos(theta).unsqueeze(1)n4 = dist.unsqueeze(1)net = torch.cat([n1, n2, n3, n4], dim=1)return netif __name__ == '__main__':inputs = torch.tensor(np.random.random(size=(1, 128, 256, 256)))net = reduction_1x1(32, 16, 10, True)print(net)

4.2 类 local_planar_guidance


  类 local_planar_guidance 用于定义 Guidance 层,该层以 Reduce 层的输出为输入,并将其上采样到原图尺寸。所以创建它时需要给一个上采样参数 upratio。下图是 Reduce 层与 Guidance 层的衔接。

  下表是 Guidance 层的输入、输出维度。

input output
(b, 4, L/8, L/8) (b, L, L)
(b, 4, L/4, L/4) (b, L, L)
(b, 4, L/2, L/2) (b, L, L)

  在 BTS 中,LPG 层的输入维度是 (b, 4, L/upratio, L/upratio)。其中 b 指 batch size,4 指 XXX,L 指原图边长。BTS 共有 3 个 LPG 层,对应的 upratio 分别为 2、4、8,3 个 LPG 层的输出维度都是 ( b , L , L ) (b, L, L) (b,L,L)。

import torch
from torch import nn
import numpy as npclass local_planar_guidance(nn.Module):def __init__(self, upratio):super(local_planar_guidance, self).__init__()self.upratio = upratioself.u = torch.arange(self.upratio).reshape([1, 1, self.upratio]).float()self.v = torch.arange(self.upratio).reshape([1, self.upratio, 1]).float()self.upratio = float(upratio)def forward(self, plane_eq, focal):plane_eq_expanded = torch.repeat_interleave(plane_eq, int(self.upratio), 2)plane_eq_expanded = torch.repeat_interleave(plane_eq_expanded, int(self.upratio), 3)n1 = plane_eq_expanded[:, 0, :, :]n2 = plane_eq_expanded[:, 1, :, :]n3 = plane_eq_expanded[:, 2, :, :]n4 = plane_eq_expanded[:, 3, :, :]u = self.u.repeat(plane_eq.size(0), plane_eq.size(2) * int(self.upratio), plane_eq.size(3))u = (u - (self.upratio - 1) * 0.5) / self.upratiov = self.v.repeat(plane_eq.size(0), plane_eq.size(2), plane_eq.size(3) * int(self.upratio))v = (v - (self.upratio - 1) * 0.5) / self.upratioreturn n4 / (n1 * u + n2 * v + n3)if __name__ == '__main__':lpg = local_planar_guidance(4)input = torch.tensor(np.random.random(size=(1, 4, 32, 32)), dtype=torch.float32)output = lpg(input, 718.856)print(output.shape)

  LPG 的输入特征 s h a p e = [ b , 4 , L / u p r a t i o , L / u p r a t i o ] shape = [b, 4, L/upratio, L/upratio] shape=[b,4,L/upratio,L/upratio],有 4 个通道。n1、n2、n3、n4 分别是输入特征各通道每个像素扩展为 u p r a t i o × u p r a t i o upratio \times upratio upratio×upratio 的块(块内值相同)而成的 s h a p e = [ 1 , L , L ] shape = [1, L, L] shape=[1,L,L] 的单通道图像。
  u 是横向归一化得到的 s h a p e = [ 1 , L , L ] shape = [1, L, L] shape=[1,L,L] 的单通道图像,即 u [ 0 ] [ x ] [ : 4 ] = [ − 0.375 , − 0.125 , 0.125 , 0.375 ] u[0][x][:4] = [-0.375, -0.125, 0.125, 0.375] u[0][x][:4]=[−0.375,−0.125,0.125,0.375]。u 的每列由相同元素组成,每行有 i n p u t . s i z e ( 3 ) input.size(3) input.size(3) 个 ( − 0.375 , − 0.125 , 0.125 , 0.375 ) (-0.375, -0.125, 0.125, 0.375) (−0.375,−0.125,0.125,0.375) 组成。
  v 是竖向归一化得到的 s h a p e = [ 1 , L , L ] shape = [1, L, L] shape=[1,L,L] 的单通道图像,即 v [ 0 ] [ : , x ] [ : 4 ] = [ − 0.375 , − 0.125 , 0.125 , 0.375 ] v[0][:, x][:4] = [-0.375, -0.125, 0.125, 0.375] v[0][:,x][:4]=[−0.375,−0.125,0.125,0.375]。v 的每行由相同元素组成,每列有 i n p u t . s i z e ( 2 ) input.size(2) input.size(2) 个 ( − 0.375 , − 0.125 , 0.125 , 0.375 ) (-0.375, -0.125, 0.125, 0.375) (−0.375,−0.125,0.125,0.375) 组成。
  如果输入 plane_eq 如下:

plane_eq.shape = torch.Size([1, 4, 2, 2])
plane_eq = torch.tensor([[[[0.7799, 0.4785], [0.9060, 0.9121]],[[0.3925, 0.5553], [0.0565, 0.8174]],[[0.7666, 0.5016], [0.5544, 0.0807]],[[0.3296, 0.3307], [0.6265, 0.7735]]]])

则 LPG 中的主要中间变量如下:

5. 损失函数


  深度预测任务中,网络的输出一般是经过 sigmoid 函数变换后的结果,所以网络输出的值的范围是 [0, 1]。然后根据数据集乘以最大距离得到实际的深度值。比如 KITTI 的深度值范围是 [0, 80],BTS 给 sigmoid 的结果乘 80,而 ViP-DeepLab 给 sigmoid 的结果乘 88,这是因为 ViP-DeepLab 考虑到预测的结果与实际值有偏差,所以给个更大的区间以接受偏差。

  假设标注的深度值和预测的深度值分别是 d i d_i di​ 和 d ~ i \tilde{d}_i d~i​, g i = l n ( d ~ i ) − l n ( d i ) g_i = ln({\tilde{d}_i}) - ln({d_i}) gi​=ln(d~i​)−ln(di​), T T T 是所有有有效标注值的像素点, λ \lambda λ 是常数 0.5。Eigen 等人设计的损失函数是

D ( g ) = 1 T ∑ i g i 2 − λ T 2 ( ∑ i g i ) 2 . D(g) = \frac{1}{T}\sum_{i}g_i^2 - \frac{\lambda}{T^2}(\sum_{i}g_i)^2. D(g)=T1​i∑​gi2​−T2λ​(i∑​gi​)2.

当 λ = 1 \lambda = 1 λ=1 时,这是尺度不变对数误差,即 scale invariant logarithmic error (SILog),由 Eigen 等人于 2014 年提出。而 BTS 的损失函数是 L = α D ( g ) L = \alpha\sqrt{D(g)} L=αD(g) ​,其中 α = 10 \alpha = 10 α=10, λ \lambda λ = 0.85, D ( g ) D(g) D(g) 的定义如下

D ( g ) = 1 T ∑ i g i 2 − λ ( 1 T ∑ i g i ) 2 . D(g) = \frac{1}{T}\sum_{i}g_i^2 - \lambda (\frac{1}{T}\sum_{i}g_i)^2. D(g)=T1​i∑​gi2​−λ(T1​i∑​gi​)2.

  损失函数定义如下:

class silog_loss(nn.Module):def __init__(self, variance_focus):super(silog_loss, self).__init__()self.variance_focus = variance_focusdef forward(self, depth_est, depth_gt, mask):d = torch.log(depth_est[mask]) - torch.log(depth_gt[mask])return torch.sqrt((d ** 2).mean() - self.variance_focus * (d.mean() ** 2)) * 10.0

6. 参考


  1. 论文,arxiv
  2. 源码,Github

最新文章

  1. Android开发——布局性能优化的一些技巧(一)
  2. leetcode--删除排序数组中的重复项--python
  3. TensorFlow深度学习算法原理与编程实战 人工智能机器学习技术丛书
  4. java B2B2C springmvc mybatis多租户电子商城系统(三):服务提供与调用
  5. python的基础知识
  6. leetcode 779. K-th Symbol in Grammar | 779. 第K个语法符号(Java)
  7. 儿童手工制作日历_德莎胶带手工制作实用且美观的巨幅挂历,让你未雨绸缪
  8. 利用GAN原始框架生成手写数字
  9. 高精度矢量汉字的一种填充方法_惯导解算数学基础4(等效旋转矢量解,圆锥补偿解)...
  10. NUC1178 Kickdown【水题】
  11. 分区工具parted的详解及常用分区使用方法
  12. FOR XML PATH 应用及其反向分解
  13. RegExp(正则表达式对象)
  14. 【THUSC 2018】菜鸡互啄记
  15. 按搜索量排名前100位访问量最高的网站(截至2020年)
  16. Python3.1 使用卡通头像网络模型生成卡通头像(基于GAN)
  17. HR告诉你,怎么回答“为什么从上家公司离职?”
  18. Error: Rule can only have one resource source (provided resource and test + include + exclude)...
  19. Oracle官网下载JDK8需要注册怎么办
  20. 深度Linux的安装

热门文章

  1. android存储管理,Android存储设备管理
  2. 2021肥西实验高级中学高考成绩查询,高三年级召开2021年合肥市第二次教学质量检测成绩分析会...
  3. 如何使用电脑来程控数字示波器(一)USB接线程控
  4. 机器学习中的数学——学习曲线如何区别欠拟合与过拟合
  5. NUMERIC(10,4) 和DECIMAL(10, 4) 的区别和用法?
  6. eSIM时代,运营商的末日还是新生
  7. error while loading shared libraries: libnvinfer.so.8 |【TensorRT 依赖解决】
  8. RTX基于32位Windows实时操作系统
  9. Ubuntu16.04 RTX2060 安装CUDA10.2
  10. 怎么设置个人电脑做服务器(tomcat)