はじめに
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ビデオのみから深度推定する先駆け的なモデル
- シーン内の物体は時間変化がなく、カメラのみ動くと仮定し、深度とカメラの動きを推定する
- 推定したカメラの動きと深度マップから前後のシーンを計算し、損失関数を求める
- 教師ありの手法と比肩する精度を達成
ネットワークのアーキテクチャ
(1)モデル全体
以下、モデルの全体像。
動画の連続したフレームの中から特定のフレーム $I_t$ に対して Depth CNN で深度マップを推定する。
一方で $I_t$ からその近傍フレーム $I_{t+1}$ 、$I_{t-1}$ に対して Pose CNN でカメラ位置の変化などを推定する。
推定したカメラ位置変化に基づいて深度マップを遷移させる。
実際の位置との間で loss を計算する。
(2)アーキテクチャの詳細
まず、Depth CNN は以下のようなU-Net系。
次に Pose CNN は以下のようなencoder-decoder。
encodeした最後でカメラの回転行列や平行移動ベクトルを推定させる。
さらに decode した最後で移動物体を排除するためのマスクを推定させる(後述)
深度マップを用いたレンダリング
このへん、考え方間違ってたら連絡下さい。
まず t 時の画像座標を $p_t$ とする。
p_t = (u,v,1)^T
その近傍フレームにおける同一物体の画像座標を $p_s$ とする。
p_s = (u', v', 1)^T
カメラの内部行列を $K$ とする。
\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$ :1画素の横方向の物理的距離
$\delta_v$ :1画素の縦方向の物理的距離
$c_u$ :画像座標系における光軸と画像面との交点の横方向
$c_v$ :画像座標系における光軸と画像面との交点の縦方向
またAからBへの perspective projection matrix を $\bf{T}_{A \to B}$ とする。
{\bf{T}\rm}_{A \to B} = (\bf{R} | t)
$\bf{R}$ :回転ベクトル
$t$:は平行移動ベクトル。
depthマップを $\hat{D}_t(p_t)$ とする。
t時フレームの画像座標と対応するワールド座標 $W_t$ との関係は
\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}
一方でワールド座標上の同一点と近傍フレームにおける画像座標との対応関係は
p_s \sim \bf{K} \bf{T}_{\rm w \to s} \bf{W}_{\rm t} \tag{2}
(1)式と(2)式から
\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)式に対応。
損失関数
損失関数(1)view synthesis loss
lossのメインは上記で求めたレンダリングを利用したもの。
手順は以下。
-
まず、t 時の画像 $I_t$ の各点に対して上記(3)式で project を行う。
-
これにより隣接するフレーム $I_s$ における対応点 $p_s$ の座標が求まる。
-
しかしこれは連続値なので、上図中央のように4近傍点による内挿で画素値を求める。
-
この画素値を元の $p_t$ に移動(warp)させ、warp画像 $\hat{I}_s$ を求める。
lossは
\mathcal{L}_{vs} = \sum_s \sum_p | I_t(p) - \hat{I}_s(p) |
として元画像とwarp画像間で L1 を計算する。
損失関数(1)view synthesis loss の改良版
一般的なデータセット(KITTI等)では、カメラ以外にも対象物が動いている。なのでカメラ側の移動のみを考慮している本しくみでは、学習がうまくいかない可能性がある。
そこで、動く対象物に対してマスクをかけて排除する。
そのマスク自体も推定させる。
マスクも考慮すると view synthesis loss は以下。
\mathcal{L}_{vs} = \sum_s \sum_p \hat{E}_s (p) | I_t(p) - \hat{I}_s(p) |
損失関数(2)マスクの正則化
ただし、このまま使うとマスク $\hat{E}_s (p)$ が 0 になるというバカな学習をするだろう。
そこで値 1 との交差エントロピーを計算する正則化項 $\mathcal{L}_{reg} ( \hat{E}_s(p) )$ を加える。
損失関数(3)
ここ怪しいので、間違ってたら連絡ください。
view synthesis loss はproject and warpした画像と元画像とのRGB値を比較しているだけなので、よく知られた問題が生じる。
例えば、depth マップが滑らかでなければ、元画像の近い2点はprojectした際に大きく異なる2点となるだろう。
結果、warpさせた1点におけるlossが大きくなるが、これを修正するためにdepthマップをどのように修正させるかは難しい。
そこでdepthマップは滑らかにしたい。
本論文ではdepthマップの2階微分に対してL1をとり、最小化させることで、「depthマップの勾配が滑らかに変化する」ことを目指す。
\mathcal{L}_{smooth} = | \ \nabla^2_x \hat{D}(p) + \nabla^2_y \hat{D}(p) \ |_1
って感じ??
loss の全体
loss の全体は以下
\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 による他モデルとの比較
教師ありな手法と比較しても負けてない。
定性的評価
以下はKITTI dataset に対するdepth mapの推論例。
Ours(CS) はCityScape datasetで学習させたもの、Ours(CS + KITTI)はCityScapeとKITTIで学習させたもの。