三维视觉论文阅读:Eigen2014单目深度估计

论文

Depth Map Prediction from a Single Image using a Multi-Scale Deep Network

摘要

直接从单张影像恢复深度是非常难搞定的,所以需要同时结合局部和全局信息来猜。在这篇论文里,作者攒了一个类似Encoder和Decoder网络,使用NYU Depth Dataset和KITTI Dataset训练,最终结果感觉还不错。

网络模型

以下展示了本篇论文中所用的网络结构,其包括两部分,分别是coarse-net和refine-net。早期的网络设计我感觉都非常粗糙,也没啥想说的,就随便贴贴。
在这里插入图片描述

1. coarse-net

coarse-net主要强调网络的全局视野,经过一系列卷积层从影像上提取特征,并最终生成一个粗略的深度图;并且其中有两个池化层,用于增强全局视野;由于2014年还没有出现UNet这种结构,所以coarse-net的最后两层是全连接,某种意义上也可以认为进一步增强了模型的全局视野。在github上找到一个对应的Pytorch代码,贴一下,加深印象。
其实看的时候感觉这个实现还是挺容易的,直接照着图片的参数填就行了。

# reference
# https://github.com/imran3180/depth-map-prediction/blob/master/model.py
class coarseNet(nn.Module):
    def __init__(self,init_weights=True):
        super(coarseNet, self).__init__()
        self.conv1 = nn.Conv2d(3, 96, kernel_size = 11, stride = 4, padding = 0)
        self.conv2 = nn.Conv2d(96, 256, kernel_size = 5, padding = 2)
        self.conv3 = nn.Conv2d(256, 384, kernel_size = 3, padding = 1)
        self.conv4 = nn.Conv2d(384, 384, kernel_size = 3, padding = 1)
        self.conv5 = nn.Conv2d(384, 256, kernel_size = 3, stride = 2)
        self.fc1 = nn.Linear(12288, 4096)
        self.fc2 = nn.Linear(4096, 4070)
        self.pool = nn.MaxPool2d(2)
        self.dropout = nn.Dropout2d()
        if init_weights:
            self._initialize_weights()


    def forward(self, x):
                                                # [n, c,  H,   W ]
                                                # [8, 3, 228, 304]
        x = self.conv1(x)                       # [8, 96, 55, 74]
        x = F.relu(x)
        x = self.pool(x)                        # [8, 96, 27, 37] -- 
        x = self.conv2(x)                       # [8, 256, 23, 33]
        x = F.relu(x)
        x = self.pool(x)                        # [8, 256, 11, 16] 18X13
        x = self.conv3(x)                       # [8, 384, 9, 14]
        x = F.relu(x)
        x = self.conv4(x)                       # [8, 384, 7, 12]
        x = F.relu(x)
        x = self.conv5(x)                       # [8, 256, 5, 10] 8X5
        x = F.relu(x)
        x = x.view(x.size(0), -1)               # [8, 12800]
        x = F.relu(self.fc1(x))                 # [8, 4096]
        x = self.dropout(x)
        x = self.fc2(x)                         # [8, 4070]     => 55x74 = 4070
        x = x.view(-1, 1, 55, 74)
        return x

2. refine-net

由于coarse-net获取的深度图分辨率较低,且质量一般;所以在本文里还添加了refine-net,用于提高深度图质量。虽然refine-net的参数非常少,但是其在2014年设计时还是非常有特色的,其将coarse-net的输出嵌入到refine-net的第二层,有点skip-connection和resnet的意味。网络也有人实现了这部分的代码,继续贴一下。

class fineNet(nn.Module):
    def __init__(self, init_weights=True):
        super(fineNet, self).__init__()
        self.conv1 = nn.Conv2d(3, 63, kernel_size = 9, stride = 2)
        self.conv2 = nn.Conv2d(64, 64, kernel_size = 5, padding = 2)
        self.conv3 = nn.Conv2d(64, 1, kernel_size = 5, padding = 2)
        self.pool = nn.MaxPool2d(2)
        if init_weights:
            self._initialize_weights()


    def forward(self, x, y):
                                                # [8, 3, 228, 304]
        x = F.relu(self.conv1(x))               # [8, 63, 110, 148]
        x = self.pool(x)                        # [8, 63, 55, 74]
        x = torch.cat((x,y),1)                  # x - [8, 63, 55, 74] y - [8, 1, 55, 74] => x = [8, 64, 55, 74]
        x = F.relu(self.conv2(x))               # [8, 64, 55, 74]
        x = self.conv3(x)                       # [8, 64, 55, 74]
        return x
损失函数 公式(1)=公式(3)=公式(3)

这篇文章最有意思就是这个损失函数,但是不得不说论文里的损失函数让人看得乱七八糟的,根本搞不懂。
在这里插入图片描述
在这里插入图片描述

经过我两个夜晚的思考,终于把公式看懂了公式(1)、公式(3)、公式(3)的等价性,真是难得啊,特此记录一下。
首先记 d i = l o g y i ∗ − l o g y i d_i=logy_i^*-logy_i di=logyilogyi d ‾ = α ( y , y ∗ ) = 1 n ∑ d i \overline{d}=\alpha(y,y^*)=\frac{1}{n}\sum{d_i} d=α(y,y)=n1di以方便公式推导。
以下在推导时还充分利用了两个定则,分别是,第一个显而易见,第二个基于求和公式的理解。
1 n ∑ d i 2 = 1 n ∑ d j 2 ∑ C = 1 n ∑ ∑ C \frac{1}{n}\sum{d_i^2}=\frac{1}{n}\sum{d_j^2} \\ \sum C=\frac{1}{n} \sum\sum{C} n1di2=n1dj2C=n1C

以下为完整的推导,证明了公式(1)、公式(3)、公式(3)的等价性,不难发现损失函数的两个视角其实是完全等价的(与文中差了一个2倍)
D ( y , y ∗ ) = 1 n ∑ ( l o g y i − l o g y i ∗ + α ( y , y ∗ ) ) 2 = 1 n ∑ ( d i − d ‾ ) 2 = 1 n ∑ ( d i 2 − 2 d ‾ d i + d ‾ 2 ) = 1 n ∑ d i 2 − 2 n d ‾ ∑ d i + 1 n ∑ d ‾ 2 = 1 n ∑ d i 2 − 2 d ‾ 2 + d ‾ 2 = 1 n ∑ d i 2 − d ‾ 2 = 1 2 ( 1 n ∑ d i 2 − 2 d ‾ 2 + 1 n ∑ d j 2 ) = 1 2 ( 1 n ∑ d i 2 − 2 1 n ∑ d i 1 n ∑ d j + 1 n ∑ d j 2 ) = 1 2 n ( ∑ d i 2 − 2 1 n ∑ ∑ d i d j + ∑ d j 2 ) = 1 2 n ( 1 n ∑ ∑ d i 2 − 2 1 n ∑ ∑ d i d j + 1 n ∑ ∑ d j 2 ) = 1 2 n 2 ( ∑ ∑ d i 2 − 2 ∑ ∑ d i d j + ∑ ∑ d j 2 ) = 1 2 n 2 ( ∑ ∑ ( d i 2 − 2 d i d j + d j 2 ) = 1 2 n 2 ∑ ∑ ( d i − d j ) 2 \begin{aligned} D(y,y^*) &=\frac{1}{n}\sum(logy_i-logy_i^*+\alpha(y,y^*))^2 \\ &=\frac{1}{n}\sum(d_i-\overline{d})^2 \\ &=\frac{1}{n}\sum(d_i^2-2\overline{d}d_i+\overline{d}^2) \\ &=\frac{1}{n}\sum{d_i^2}-\frac{2}{n}\overline{d}\sum{d_i}+\frac{1}{n}\sum{\overline{d}^2} \\ &=\frac{1}{n}\sum{d_i^2}-2\overline{d}^2+\overline{d}^2 \\ &=\frac{1}{n}\sum{d_i^2}-\overline{d}^2 \\ &=\frac{1}{2}(\frac{1}{n}\sum{d_i^2}-2\overline{d}^2+\frac{1}{n}\sum{d_j^2}) \\ &=\frac{1}{2}(\frac{1}{n}\sum{d_i^2}-2\frac{1}{n}\sum{d_i}\frac{1}{n}\sum{d_j}+\frac{1}{n}\sum{d_j^2}) \\ &=\frac{1}{2n}(\sum{d_i^2}-2\frac{1}{n}\sum{\sum{d_id_j}}+\sum{d_j^2}) \\ &=\frac{1}{2n}(\frac{1}{n}\sum\sum{d_i^2}-2\frac{1}{n}\sum{\sum{d_id_j}}+\frac{1}{n}\sum\sum{d_j^2}) \\ &=\frac{1}{2n^2}(\sum\sum{d_i^2}-2\sum{\sum{d_id_j}}+\sum\sum{d_j^2}) \\ &=\frac{1}{2n^2}(\sum\sum({d_i^2}-2d_id_j+d_j^2) \\ &=\frac{1}{2n^2}\sum\sum{(d_i-d_j)^2}\\ \end{aligned} D(y,y)=n1(logyilogyi+α(y,y))2=n1(did)2=n1(di22ddi+d2)=n1di2n2ddi+n1d2=n1di22d2+d2=n1di2d2=21(n1di22d2+n1dj2)=21(n1di22n1din1dj+n1dj2)=2n1(di22n1didj+dj2)=2n1(n1di22n1didj+n1dj2)=2n21(di22didj+dj2)=2n21((di22didj+dj2)=2n21(didj)2

最后再来说一下这个损失函数有什么意义吧。公式(1)想表达的是在单张影像深度估计下,深度的尺度其实是一个没有确定的问题,如果放任这个问题不管,会对深度估计产生负面影响;作者后续的实验也证明了这个问题。公式(3)则从另外一个角度说明,对于尺度未知的情况下,两个像素间的相对深度值更有价值。

训练

对于训练,有三点需要格外提醒一下:
1. 原文作者训练的时候,其实是先训练coarse-net;然后固定coarse-net,训练refine-net;最后再同时训练两个网络;
2. 训练时肯定要做数据增强啦,这个不用多说了
3. 训练时的损失函数其实稍微有一点变化,其公式如下,其中 α = 0.5 \alpha=0.5 α=0.5,保证网络还是有一定能力学习全局尺度的,反正作者这样说了。
D ( y , y ∗ ) = 1 n ∑ d i 2 − α d ‾ 2 D(y,y^*)=\frac{1}{n}\sum{d_i^2}-\alpha\overline{d}^2 \\ D(y,y)=n1di2αd2

结果

以下是最终的一些结果,论文里有,我也贴一下。
在这里插入图片描述

Eigen3Config.cmake和eigen3-config.cmake是用于配置Eigen3库的包配置文件。这些文件包含了关于Eigen3库的信息,如库的路径、头文件路径和编译选项等。这些文件通常由Eigen3库的安装程序生成,并用于帮助CMake在编译时找到并链接Eigen3库。引用 当你在编译使用Eigen3库的项目时,如果编译器无法找到这些文件,就会出现报错信息。这可能是由于Eigen3库的版本与你的项目所需的版本不兼容,或者是库的安装位置或配置有问题。引用 如果你想解决这个问题,可以首先确保你已经正确地安装了Eigen3库,并且安装路径是正确的。然后,你可以尝试在CMakeLists.txt文件中添加以下内容,以帮助CMake找到Eigen3库的位置: ``` find_package(Eigen3 3.1 REQUIRED) ``` 这将告诉CMake在编译时查找版本为3.1的Eigen3库。如果你的Eigen3库版本不是3.1,可以根据实际情况修改这个版本号。引用 如果你仍然遇到问题,你可以尝试手动设置Eigen3库的路径,方法是使用cmake-gui或在CMakeLists.txt中添加以下内容: ``` set(Eigen3_DIR /path/to/eigen3) ``` 将`/path/to/eigen3`替换为你Eigen3库的实际路径。引用 综上所述,Eigen3Config.cmake和eigen3-config.cmake是用于配置Eigen3库的包配置文件。如果编译时无法找到这些文件,你可以尝试检查库的版本兼容性、安装路径和配置,并根据需要进行相应的修改。<span class="em">1</span><span class="em">2</span><span class="em">3</span><span class="em">4</span>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值