カメラモデル

提供: メモ帳@fmaj7b5.info
移動: 案内検索

目次

ピンホールカメラ

迷ったら、コレ。

画像座標を\boldsymbol{m}、3次元座標を\boldsymbol{X}とすると、

 s
  \begin{bmatrix}
   u \\ v \\ 1
  \end{bmatrix}
 = s \tilde{\boldsymbol{m}}
 = P \tilde{\boldsymbol{X}}
で表される。ただし、\tilde{\boldsymbol{m}}, \tilde{\boldsymbol{X}}はそれぞれ画像座標と3次元座標の同次座標Pは3x4行列、sは0でない任意の定数とする。

射影行列Pの例

Pは3x3内部パラメータ行列K、3x4外部パラメータ行列[R | \boldsymbol{t}]に(だいたい)分ける事ができる。

  P = K [R | \boldsymbol{t}]
なおKは上三角行列、R, \boldsymbol{t}はそれぞれカメラから見た世界座標系の軸の向き(回転行列)、原点の位置を表す。 で、Pには定数倍の不定性があるので、Pと任意の0でない定数aを掛けたaPは同じ変換を表す。

普通のカメラ


  P =
  \begin{bmatrix}
    \alpha & \gamma & u_0 \\
         0 & \beta  & v_0 \\
         0 &      0 & 1
  \end{bmatrix}
  \begin{bmatrix}
    R & \boldsymbol{t}
  \end{bmatrix}
自由度は内部パラメータ5、外部パラメータ6(回転3、並進3)の計11。 内部パラメータの(3, 3)成分を1しばりにすれば、定数倍の不定性が除去できる。

一般カメラ

要はパラメータが物理的なものと関係なくてもよいというだけ。12個の成分を持ったただの行列。

  P =
  \begin{bmatrix}
    p_{11} & \dots & p_{14} \\
    \vdots & \ddots & \vdots \\
    p_{31} & \dots & p_{34}
  \end{bmatrix}
ただし、定数倍の不定性があるので独立な成分は11個だけ。

レンズ歪み

カメラで直線を撮影しても、真っ直ぐに写らない事がある。 原因があるらしいけど(霊的な何か?)、よく分からない。 それはともかく、こういう歪みがあると画像処理が面倒になるので、頑張って補正する。

\boldsymbol{x} \in \mathcal{R}^3を正規化カメラ画像座標とする。つまり3次元点\tilde{\boldsymbol{X}}に外部パラメータを作用させて、第3成分で割ったものとする;


  \begin{array}{l}
   \boldsymbol{\xi} =
    \begin{bmatrix}
     R & \boldsymbol{t}
    \end{bmatrix}
    \tilde{\boldsymbol{X}}
\\
  \boldsymbol{x} =
   \begin{bmatrix}
    \xi_1 / \xi_3 \\ \xi_2 / \xi_3 \\ 1
   \end{bmatrix}
  \end{array}

放射方向の歪み


  \begin{array}{l}
   r^2 = x_1^2 + x_2^2 \\
   \begin{cases}
    \breve{x}_1 = (1 + \kappa_1 r^2 + \kappa_2 r^4 + \cdots) x_1 \\
    \breve{x}_2 = (1 + \kappa_1 r^2 + \kappa_2 r^4 + \cdots) x_2
   \end{cases}
  \end{array}

レンズが回転対称なので歪みはどうせ偶関数だろう、という式。{\kappa_i}が係数で、キャリブレーションする時に頑張って求める。 普通のレンズなら\kappa_2までで十分だと思うけど、不安ならもう少し増やすべし。 ただ、増やすだけ求めるべき係数も増えるのでキャリブレーション時の計算が不安定になるし、すぐに桁あふれするしで良い事は無い。

OpenCV 2.1まで


  \begin{cases}
   \breve{x}_1 = (1 + \kappa_1 r^2 + \kappa_2 r^4 + \kappa_3 r^6) x_1 + 2 p_1 x_1 x_2 + p_2 (r^2 + 2 x_1^2) \\
   \breve{x}_2 = (1 + \kappa_1 r^2 + \kappa_2 r^4 + \kappa_3 r^6) x_2 + p_1 (r^2 + 2 x_2^2) + 2 p_2 x_1 x_2
  \end{cases}

p_1, p_2は接線方向の歪み係数らしい。元ネタが分からないので、そんなもんかなーって感じ。

OpenCV 2.2以降


  \begin{cases}
   \breve{x}_1 = \frac{1 + \kappa_1 r^2 + \kappa_2 r^4 + \kappa_3 r^6}{1 + \kappa_4 r^2 + \kappa_5 r^4 + \kappa_6 r^6} x_1 + 2 p_1 x_1 x_2 + p_2 (r^2 + 2 x_1^2) \\
   \breve{x}_2 = \frac{1 + \kappa_1 r^2 + \kappa_2 r^4 + \kappa_3 r^6}{1 + \kappa_4 r^2 + \kappa_5 r^4 + \kappa_6 r^6} x_2 + p_1 (r^2 + 2 x_2^2) + 2 p_2 x_1 x_2
  \end{cases}

放射方向の歪みに分母が付いた。複雑化の一途だなあ。

この辺りの事情に疎いので、有名どころのOpenCVをパクってみた。

歪みの補正

画像を歪ませるのは上式の通りに計算すればできるけど、逆は非線形方程式を解かないといけないので少し面倒。 とはいえ、座標値が少し変化するぐらいなので、歪んだ座標値を初期値にして適当に数値計算すれば大丈夫。

繰り返し計算はそれなりに遅くなるし、(カメラを触らなければ)計算される値はいつも同じなので、1回だけ計算して歪んだ座標から補正後の座標を引ける表を作っておくのが便利。

普通のカメラ画像を補正する手順

1. 歪んだ正規化座標を求める

\breve{\boldsymbol{x}} = K^{-1} \tilde{\boldsymbol{m}}

2. 計算するか、表を引いて補正後の座標を求める

\boldsymbol{x} = \boldsymbol{x}(\breve{\boldsymbol{x}})

3. 画像面に投影する

\tilde{\boldsymbol{m}}^\prime = K^\prime \boldsymbol{x}

ここのポイントは最後の画像を作る時に任意の内部パラメータが使えるということ。 普通はK^\prime = Kとすればいいけど、隙間ができても歪み補正後の画像全体を納めたい場合とか、周りを切り落として隙間の無い矩形領域を取り出したい場合とかに、ちょちょっと計算して新しいK^\primeを使う。

蛇足だけど、歪み補正後の座標が画像の外に出る可能性があるから、メモリの変な所を触らないように注意してね。

ライトフィールドカメラ

カメラは、視点に向かって飛んでくる光を方向毎に記録する装置であると考えられる。 ・・・って、別に光線が視点を通らなくても良くね?ということで、「3次元空間に存在する光線を記録する装置」を考える。

普通の画像は、2次元の座標(x, y)と色\lambdaを指定すると観測値を返す関数f(x, y, \lambda)と見なせるので、普通じゃない画像は3次元の直線\boldsymbol{l}と色\lambdaを指定すると観測値を返す関数f(\boldsymbol{l}, \lambda)だろうと想像がつく。

3次元の光線は観測点(3自由度)と飛んでくる方向(2自由度)で表現できるから、普通の画像の3次元(= 2+1)に対して6次元(= 3+2+1)になるっぽい。 あ、あと時刻も入れとけば動画にできるなー。

ただ、次元を増やすと指数的にデータが大きくなっちゃうので(例えば1000x1000の画像を1000x1000x1000にしたら1000倍)、使えそうな範囲で仮定を付ける事にする。 窓から見える景色とか。

窓から見える景色は、窓ガラスを通る光線の集合として表される。もちろん、窓を開けてる場合は窓枠の内側を通る光線になるけど。 ここで重要なのは窓ガラスがあるかないかじゃなくて、そこを通る光だけを考えればいいって事。これらの光線は窓ガラスの外側の面と内側の面を通るから、通った2点の座標で1条の光線が表現できる。つまり、


  f(x_O, y_O, x_I, y_I, \lambda)

によって、(ある時点の)窓の外の景色が完全に記録できるはず。別に窓の外から内でも同じだけど、ここでは外を見る事にする。変な想像はしないように。

さて、データの表現方法が決まったところで、撮影の仕方を考える。 これは単純に、窓の内側にキャリブレーション済みのカメラ(互いの位置関係が完全に分かっている状態)を沢山並べれば良い。 後は各カメラで観測した画像の全ての画素に対して、光線が通過した窓ガラス内外の2点を計算して値を格納すればライトフィールドのできあがり。

適当な視点から見た窓の景色をレンダリングするには、視点の前にスクリーンを置いてスクリーン上の各点が窓ガラスのどこを通るか計算して、記録されている値を並べればできる。 まあ、実際にはそのものズバリな光線が記録されている事は中々無いから近くの値を取ってきて補間する必要があるけど、それはまた別の話。