論文まとめ：Unsupervised Learning of Depth and Ego-Motion from Video

# はじめに
CVPR2017から以下の論文
[1] T. Zhou, et. al. "Unsupervised Learning of Depth and Ego-Motion from Video", CVPR2017
のまとめ

公式のコード：
https://github.com/tinghuiz/SfMLearner

arXiv：
https://arxiv.org/abs/1704.07813

既に存在するまとめ記事：
takoroy氏の記事
https://qiita.com/takoroy/items/191ec46211838daa47e1

既にまとめ記事があるので、ここでは論文中の式の導出等、ロジック重視でまとめる。

# 概要
- 単眼のRGBビデオのみから深度推定する先駆け的なモデル
- シーン内の物体は時間変化がなく、カメラのみ動くと仮定し、深度とカメラの動きを推定する
- 推定したカメラの動きと深度マップから前後のシーンを計算し、損失関数を求める
- 教師ありの手法と比肩する精度を達成


# ネットワークのアーキテクチャ

## （１）モデル全体

以下、モデルの全体像。

![SfMLearner_img01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116706/e0bf59fc-4fdf-df29-33f5-de0071cea41e.png)


動画の連続したフレームの中から特定のフレーム $I_t$ に対して Depth CNN で深度マップを推定する。

一方で $I\_t$ からその近傍フレーム $I\_{t+1}$ 、$I\_{t-1}$ に対して Pose CNN でカメラ位置の変化などを推定する。

推定したカメラ位置変化に基づいて深度マップを遷移させる。

実際の位置との間で loss を計算する。

## （２）アーキテクチャの詳細

まず、Depth CNN は以下のようなU-Net系。

![SfMLearner_img03 _1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116706/db92bc4a-5e6b-4f4d-2e61-dfb5d5c4ee81.png)

次に Pose CNN は以下のようなencoder-decoder。

![SfMLearner_img03 _2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116706/9b0dd04a-e3ae-4348-d265-7d9673959a45.png)

encodeした最後でカメラの回転行列や平行移動ベクトルを推定させる。

さらに decode した最後で移動物体を排除するためのマスクを推定させる（後述）

# 深度マップを用いたレンダリング
<font color="blue">このへん、考え方間違ってたら連絡下さい。</font>

まず t 時の画像座標を $p_t$ とする。

```math
p_t = (u,v,1)^T
```

その近傍フレームにおける同一物体の画像座標を $p_s$ とする。

```math
p_s = (u', v', 1)^T
```

カメラの内部行列を $K$ とする。

```math
\bf{K} = \left(
    \begin{array}{ccc}
      \frac{f}{\delta_v} & 0 & c_u \\
      0 & \frac{f}{\delta_u} & c_v \\
      0 & 0 & 1
    \end{array}
  \right)
```
ここで、
$f$ ：焦点距離
$\delta_u$ ：１画素の横方向の物理的距離
$\delta_v$ ：１画素の縦方向の物理的距離
$c_u$ ：画像座標系における光軸と画像面との交点の横方向
$c_v$ ：画像座標系における光軸と画像面との交点の縦方向

またAからBへの perspective projection matrix を $\bf{T}\_{A \to B}$ とする。

```math
{\bf{T}\rm}_{A \to B} = (\bf{R} | t)
```

$\bf{R}$ ：回転ベクトル
$t$：は平行移動ベクトル。

depthマップを $\hat{D}\_t(p\_t)$ とする。

t時フレームの画像座標と対応するワールド座標 $W_t$ との関係は
 
```math
\begin{eqnarray}
\hat{\bf{D}\rm}_t (p_t) p_t &=& {\bf{K}\rm} {\bf{T}\rm}_{w \to t} {\bf{W}\rm}_t \tag{1}\\
{\bf{W}\rm}_t &=& {\bf{T}\rm}_{t \to w} {\bf{K}\rm}^{-1} \hat{\bf{D}\rm}_t (p_t) p_t
\end{eqnarray}
```

一方でワールド座標上の同一点と近傍フレームにおける画像座標との対応関係は

```math
p_s \sim \bf{K} \bf{T}_{\rm w \to s} \bf{W}_{\rm t} \tag{2}
```

(1)式と(2)式から

```math
\begin{eqnarray}
p_s &\sim& {\bf{K}\rm} {\bf{T}\rm}_{w \to s} {\bf{W} \rm}_{t} \\
p_s &\sim& {\bf{K} \rm} {\bf{T} \rm}_{w \to s} {\bf{T} \rm}_{t \to w} {\bf{K} \rm}^{-1} \hat{\bf{D}}_t (p_t) p_t \\
p_s &\sim& {\bf{K} \rm} {\bf{T} \rm}_{t \to s} {\bf{K} \rm}^{-1} \hat{\bf{D}}_t (p_t) p_t  \tag{3}\\

\end{eqnarray}
```

(3)式は論文中の(2)式に対応。

# 損失関数

## 損失関数（１）view synthesis loss

lossのメインは上記で求めたレンダリングを利用したもの。

手順は以下。

![SfMLearner_img02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116706/b02ceb6b-ef89-8a3e-7fac-9c393b35297b.png)

1. まず、t 時の画像 $I\_t$ の各点に対して上記（３）式で project を行う。

2. これにより隣接するフレーム $I\_s$ における対応点 $p\_s$ の座標が求まる。

3. しかしこれは連続値なので、上図中央のように４近傍点による内挿で画素値を求める。

4. この画素値を元の $p\_t$ に移動（warp）させ、warp画像 $\hat{I}\_s$ を求める。

lossは

```math
\mathcal{L}_{vs} = \sum_s \sum_p | I_t(p) - \hat{I}_s(p) |
```

として元画像とwarp画像間で L1 を計算する。

## 損失関数（１）view synthesis loss の改良版
一般的なデータセット（KITTI等）では、カメラ以外にも対象物が動いている。なのでカメラ側の移動のみを考慮している本しくみでは、学習がうまくいかない可能性がある。

そこで、動く対象物に対してマスクをかけて排除する。

そのマスク自体も推定させる。

マスクも考慮すると view synthesis loss は以下。

```math
\mathcal{L}_{vs} = \sum_s \sum_p \hat{E}_s (p) | I_t(p) - \hat{I}_s(p) |
```

## 損失関数（２）マスクの正則化

ただし、このまま使うとマスク $\hat{E}_s (p)$ が 0 になるというバカな学習をするだろう。

そこで値 1 との交差エントロピーを計算する正則化項 $\mathcal{L}\_{reg} ( \hat{E}\_s(p) )$ を加える。

## 損失関数（３）
<font color="blue">ここ怪しいので、間違ってたら連絡ください。</font>

view synthesis loss はproject and warpした画像と元画像とのRGB値を比較しているだけなので、よく知られた問題が生じる。

例えば、depth マップが滑らかでなければ、元画像の近い２点はprojectした際に大きく異なる２点となるだろう。

結果、warpさせた１点におけるlossが大きくなるが、これを修正するためにdepthマップをどのように修正させるかは難しい。

そこでdepthマップは滑らかにしたい。

本論文ではdepthマップの２階微分に対してL1をとり、最小化させることで、「depthマップの勾配が滑らかに変化する」ことを目指す。

```math
\mathcal{L}_{smooth} = | \ \nabla^2_x \hat{D}(p)  +  \nabla^2_y \hat{D}(p) \ |_1 
```
って感じ？？

## loss の全体

loss の全体は以下

```math
\mathcal{L}_{final} = \sum_l \mathcal{L}^l_{vs} + \lambda_s \mathcal{L}^l_{smooth} + \lambda_e \sum_s \mathcal{L}_{reg} (\hat{E}_s^l)
```

ラムダは掛け率、$l$ は異なるスケールの画像。

# 実験と結果

## KITTI dataset による他モデルとの比較

![SfMLearner_img06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116706/09d89eaf-c442-3e2f-99c8-7e75018e8e5d.png)

教師ありな手法と比較しても負けてない。

## 定性的評価
以下はKITTI dataset に対するdepth mapの推論例。

![SfMLearner_img07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116706/217a4968-9f57-9aad-7522-407f43352ace.png)

Ours(CS) はCityScape datasetで学習させたもの、Ours(CS + KITTI)はCityScapeとKITTIで学習させたもの。