つくるって楽しい

主にpythonとか。画像処理とか。

カメラの基本行列を理解する、エピポーラ線を描く

2つの画像における\boldsymbol{E}の投影点を\boldsymbol{x}_1,\boldsymbol{x}_2とし、それを対応付ける行列\boldsymbol{E}を求めます。\boldsymbol{E}を基本行列と呼び、以下が成り立ちます[1]。

{
\boldsymbol{x}_1^T  \boldsymbol{E}  \boldsymbol{x}_2 = 0
\tag{1}
}

\boldsymbol{E}の表すもの

X,\boldsymbol{x}_1,\boldsymbol{x}_2の位置関係を図示するとFig1のようになっています。
f:id:mikekochang:20190307115528j:plain
Fig1. \boldsymbol{X},\boldsymbol{x}_1,\boldsymbol{x}_2の位置関係

\boldsymbol{x}_1\boldsymbol{x}_2を回転、平行移動することで表すことができるので、回転を\boldsymbol{R}、平行移動を\boldsymbol{t}として

{
\boldsymbol{x}_1 =  \boldsymbol{R} \boldsymbol{x}_2 + \boldsymbol{t}
\tag{2}
}

と書けます。\boldsymbol{R}\boldsymbol{x}_2, \boldsymbol{t}は同一平面上にあるので、この外積\boldsymbol{t} \times  \boldsymbol{R} \boldsymbol{x}_2は平面上の垂線ベクトルを表します。
また、\boldsymbol{x}_1も同様に同一平面上にあるので、垂線ベクトルと直交する(=垂線ベクトルとの内積が0となる)ことから

{
\boldsymbol{x}_1 (\boldsymbol{t} \times  \boldsymbol{R} \boldsymbol{x}_2)= 0
\tag{3}
}

となります。ここで、外積を行列表現します。
\boldsymbol{a}=(a_{1}, a_{2}, a_{3})の時

{
\boldsymbol{a} \times \boldsymbol{b} 
=
\begin{pmatrix}
0 && -a_{3} && a_{2}  \\
a_{3}  && 0 && -a_{1}  \\
 -a_{2} && a_{1} && 0  \\
\end{pmatrix}
\boldsymbol{b}
\tag{4}
}
と表せるので、(3)は

{
\boldsymbol{x}_1^T  \boldsymbol{E}  \boldsymbol{x}_2 = 0
\tag{1}
}
ここで、
{
\boldsymbol{E} = \boldsymbol{TR}
\tag{5}
}

{
\boldsymbol{T} =
\begin{pmatrix}
0 && -t_{3} && t_{2}  \\
t_{3}  && 0 && -t_{1}  \\
 -t_{2} && t_{1} && 0  \\
\end{pmatrix}
\tag{6}
}

と表せます。\boldsymbol{E}\boldsymbol{TR}のことですね。
また、(1)の両辺の転置を取ると、
{
\boldsymbol{x}_2^T  \boldsymbol{E}^T  \boldsymbol{x}_1 = 0
\tag{7}
}
となるので、\boldsymbol{x}_1\boldsymbol{x}_2に対応付ける行列は\boldsymbol{E}^Tであると言えます。

 \boldsymbol{E}の性質

  • \boldsymbol{E}はカメラの内部パラメータで正規化済みの点\boldsymbol{x}_1,\boldsymbol{x}_2の対応を求めたものです。正規化をしていない点\boldsymbol{x}_1^{'},\boldsymbol{x}_2^{'}に関しては、\boldsymbol{K}で正規化を行うと\boldsymbol{x}_1 = \boldsymbol{K}^{-1} \boldsymbol{x}_1^{'}, \boldsymbol{x}_2 = \boldsymbol{K}^{-1} \boldsymbol{x}_2^{'}と表せるので、

{
\boldsymbol{x}_1^{T} E \boldsymbol{x}_2
= (\boldsymbol{K}^{-1} \boldsymbol{x}_1^{'} )^{T} E (\boldsymbol{K}^{-1} \boldsymbol{x}_2^{'} )
=  {\boldsymbol{x}_1^{'}}^{T} {\boldsymbol{K}^{-1}}^{T} E \boldsymbol{K}^{-1} \boldsymbol{x}_2
\tag{8}
}

と表せ、 F = {\boldsymbol{K}^{-1}}^{T} E \boldsymbol{K}^{-1}と置き、Fを基礎行列と呼びます。

  • \boldsymbol{E}のランクについて、rank(\boldsymbol{T})=2\boldsymbol{T}を変形してランクを調べる操作をすると分かる)、rank(\boldsymbol{R})=3から、rank(\boldsymbol{E})=rank(\boldsymbol{TR})=rank(\boldsymbol{T})=2である[2]と言えます。(\boldsymbol{F}についても同様です。)

エピ極、エピポーラ線の求め方

画像1の光学中心\boldsymbol{o}_1を画像2の光学中心に投影した点がエピ極です(Fig1.の\boldsymbol{e}_2)。また、\boldsymbol{e}_2\boldsymbol{x}_2を結んだ線をエピポーラ線と呼びます。すべてのエピポーラ線はエピ極を通過します。これは、

{
\boldsymbol{x}_1^{T} \boldsymbol{F} \boldsymbol{e}_2 \forall \boldsymbol{x}_1
\tag{9}
}

と表すことが出来ます。(どんな\boldsymbol{x}_1に対しても\boldsymbol{F}によって\boldsymbol{e}_2と対応づけることが出来る。)ここから、

{
\boldsymbol{F} \boldsymbol{e}_2 = \boldsymbol{0}
\tag{10}
}

ということが分かり、これを解くことで\boldsymbol{e}_2が求まります。(特異値分解で解ける)

\boldsymbol{x}_1を通るエピポーラ線は、
{
\boldsymbol{l}= \boldsymbol{F} \boldsymbol{x}_2 
\tag{11}
}

として求めることが出来ます。これは\boldsymbol{x}_1^{T} \boldsymbol{l}= \boldsymbol{0} という直線を表し、\boldsymbol{l}は直線の係数ということになります。

基礎行列の求め方

8点アルゴリズムというシンプルな方法で解くことを考えます。(1)を要素で表し、

{
(x_{1},  y_{1},  1)
\begin{pmatrix}
f_{11} && f_{12} && f_{13}  \\
f_{21} && f_{22} && f_{23}  \\
f_{31} && f_{32} && f_{33}  \\
\end{pmatrix}
\begin{pmatrix}
x_{2} \\
y_{2}  \\
1  \\
\end{pmatrix}
= \boldsymbol{0}
\tag{9}
}

とします。これを書き換えると、

{
\boldsymbol{b}_i \boldsymbol{f} = \boldsymbol{0}
\tag{12}
}

ここで、
{
\boldsymbol{b}_i = 
(x_{2i}x_{1i},y_{2i}x_{1i}, x_{1i}, x_{2i}y_{1i}, y_{2i}y_{1i}, y_{1i}, x_{2i}z_{1i}, y_{2i}z_{1i}, 1)
\tag{13}
}

{
\boldsymbol{f} = 
(f_{11}, f_{12}, f_{13}, f_{21}, f_{22}, f_{23}, f_{31}, f_{32}, f_{33})^{T}
\tag{14}
}

と表せます。対応点が8点ある場合、

{
\boldsymbol{Bf} = 0
\tag{15}
}

{
\boldsymbol{B}
=
\begin{pmatrix}
\boldsymbol{b}_{1} \\
\vdots  \\
\boldsymbol{b}_{8}  \\
\end{pmatrix}
\tag{16}
}

となり、これを解くことで\boldsymbol{f}が求まります。(\boldsymbol{B}特異値分解で解きます。)

\boldsymbol{F}の性質として、rank(\boldsymbol{F})=2となるはずですが、対応点データが正確でない限り求めた解はrank(\boldsymbol{F})=2になるとは限りません。よって、最終的な解のランクが2になるように事後調整など行います[3]。

実装

以下の実装に、エピポーラ線を描く部分を加えました。tukurutanoshi.hateblo.jp

f:id:mikekochang:20190307115542j:plain
3段目左はカメラ1から物体を見た画像(画像1)、右はカメラ2から物体を見た画像(画像2)です。
カメラ1はワールド座標系と同じ座標軸、カメラ2はz方向に4だけ移動したもので、画像1,2ともエピポーラ線は画像中心で交わっています。

コード

pyCodes/recon3d at master · misakikobayashi1984/pyCodes · GitHub

一部計算は[4]のサンプルを使用しています。