Visual SLAMもVisual Odometryも、エピポーラ幾何PnPも、すべて「画素座標から3次元空間への正しい対応づけ」を前提にしている。しかしレンズを通った光は、教科書どおりの理想的なピンホールでは像を結ばない。焦点距離は画素単位でいくつなのか、主点は画像中心からどれだけずれているのか、直線は画像上でどれだけ曲がるのか——これらを数値として求める処理がカメラキャリブレーション(camera calibration)である。校正を軽視すると、その後どれほど洗練されたSLAMやSfMのアルゴリズムを積んでも、系統誤差が土台から全体に伝播する。

0. 30秒要約

1. ピンホールモデルを座標から再確認する

世界座標の点 \mathbf{X}=(X,Y,Z,1)^\mathsf{T}(同次座標)は、ピンホールモデルではスケールを除いて

\tilde{\mathbf{x}} \sim P\mathbf{X},\qquad P=K[R\mid\mathbf{t}]

として画像へ投影される。R\in SO(3)\mathbf{t} は世界座標からカメラ座標への回転・並進(外部パラメータ)、K は内部パラメータで

K=\begin{bmatrix}f_x&s&c_x\\0&f_y&c_y\\0&0&1\end{bmatrix}

と書ける。f_x,f_y は画素単位の焦点距離、(c_x,c_y) は主点(光軸が画像面と交わる点)、s はスキュー(多くの現代センサではほぼゼロ)である。校正とは、この K と歪み係数、そして各撮影の R,\mathbf{t} を、観測データだけから求める逆問題である。

複数姿勢のチェッカーボードから内部パラメータを求める 固定したカメラに対し、平面パターンを異なる角度・距離で複数回撮影し、それぞれのホモグラフィから内部パラメータKと歪み係数を共同で推定する様子を示す図。 カメラ(固定) 姿勢1 姿勢2 姿勢3 K, k1,k2,k3 p1,p2 各姿勢のR,t

図が示すように、1回の撮影だけでは内部パラメータと外部パラメータを分離できない。姿勢(傾き・距離)を変えた複数の観測を集めることで、初めて K を一意に近く求められる。これは次節のZhangの手法の核心でもある。

2. レンズは理想ピンホールではない:歪みモデル

現実のレンズは、放射状歪み(radial distortion)と接線方向歪み(tangential distortion)を持つ。正規化画像座標を (x,y)=(X_c/Z_c,\,Y_c/Z_c)、半径を r^2=x^2+y^2 とすると、Brown–Conrady型の歪みモデルは次のように書ける。

x_d = x\left(1+k_1r^2+k_2r^4+k_3r^6\right) + 2p_1xy+p_2\left(r^2+2x^2\right)
y_d = y\left(1+k_1r^2+k_2r^4+k_3r^6\right) + p_1\left(r^2+2y^2\right)+2p_2xy

k_1,k_2,k_3 が放射状歪み係数、p_1,p_2 が接線方向歪み係数である。放射状歪みはレンズ中心からの距離に応じて像が縮む・膨らむ現象で、広角レンズで顕著な「樽型(barrel)」歪みや、望遠レンズでの「糸巻き型(pincushion)」歪みとして現れる。接線方向歪みは、レンズ群と撮像素子が完全に平行でない実装誤差から生じる、より小さい非対称成分である。最終的な画素座標は \tilde{\mathbf{x}}_{px}=K(x_d,y_d,1)^\mathsf{T} として得る。

歪みなしの格子と樽型放射状歪みの比較 左側は直線のまま並ぶ理想的な格子、右側は同じ格子がレンズの放射状歪みによって外側ほど内側へ湾曲した樽型に見える様子を示す。 理想(歪みなし) 樽型放射状歪み

魚眼のように視野角が非常に広いレンズでは、Brown–Conrady型の多項式モデルは端で発散しやすく実用的でない。OpenCVのfisheyeモデルのような、角度に基づく等距離射影(equidistant projection)近似を使うのが一般的である。「歪みモデルを一つ覚える」のではなく、レンズの画角と光学設計に応じてモデルを選ぶという意識が重要になる。

3. Zhangの手法:平面パターンで校正する

現在もっとも広く実装されているのが、Zhengyou Zhangが2000年に発表した平面パターンによる校正法である。特別な3D校正治具を使わず、印刷したチェッカーボードのような平面パターンを、カメラまたはパターン自身を動かしながら複数の姿勢で撮影するだけでよい。

ある姿勢 i でのパターン平面(Z=0 とする)から画像への写像は、回転行列の第1・第2列 \mathbf{r}_1,\mathbf{r}_2 と並進 \mathbf{t} を使ったホモグラフィ

H_i \sim K\begin{bmatrix}\mathbf{r}_1&\mathbf{r}_2&\mathbf{t}\end{bmatrix}

として書ける。ここで各姿勢の H_i は、パターン上の既知の格子点とその画像上の対応から、ホモグラフィ入門で扱うDLT法で線形に推定できる。回転行列の列が正規直交、すなわち \mathbf{r}_1^\mathsf{T}\mathbf{r}_2=0,\ \|\mathbf{r}_1\|=\|\mathbf{r}_2\| という制約を使うと、B=K^{-\mathsf{T}}K^{-1} に関する線形方程式

\mathbf{h}_1^\mathsf{T}B\mathbf{h}_2=0,\qquad \mathbf{h}_1^\mathsf{T}B\mathbf{h}_1=\mathbf{h}_2^\mathsf{T}B\mathbf{h}_2

が姿勢1回につき2本ずつ得られる(\mathbf{h}_1,\mathbf{h}_2H_i の第1・第2列)。B は対称行列で自由度6なので、3姿勢以上あれば線形最小二乗で B、続いてコレスキー分解に相当する手順で K を閉形式で求められる。これがZhangの手法が「平面を数枚撮るだけでよい」理由である。ただし全ての姿勢がほぼ平行(フロントパラレル)だと式が退化するため、傾きの異なる複数姿勢が必要になる。

閉形式解は歪みを無視した初期値にすぎない。ここから歪み係数を含めた全パラメータを、次節の再投影誤差を目的関数とする非線形最適化(多くはLevenberg–Marquardt法)で精密化するのが、実務での標準的な二段階構成である。

4. 再投影誤差とパラメータ最適化

校正の目的関数は、すべての姿勢・すべての格子点について、観測画素と、推定パラメータで計算した投影位置の差を最小化することである。姿勢 i、点 j の観測を \mathbf{u}_{ij}、対応する既知の平面上の3D点を \mathbf{X}_j とすると

E(K,\boldsymbol{\kappa},\{R_i,\mathbf{t}_i\})= \sum_{i=1}^{n}\sum_{j=1}^{m} \left\|\mathbf{u}_{ij}-\pi_d\!\left(K,\boldsymbol{\kappa},R_i\mathbf{X}_j+\mathbf{t}_i\right)\right\|^2

を最小化する。\boldsymbol{\kappa}=(k_1,k_2,k_3,p_1,p_2) は歪み係数、\pi_d は歪みを含めた投影関数である。未知数は K(4〜5自由度)、\boldsymbol{\kappa}(3〜5自由度)、そして姿勢ごとの R_i,\mathbf{t}_i(6自由度×姿勢数)と大きいが、観測点数が十分多ければ十分に拘束される。この定式化は、バンドル調整基礎入門で扱う「カメラ姿勢と3D構造を同時最適化する」枠組みの特殊ケースと見ることができる。校正では3D点の座標が既知で固定されている分、通常のバンドル調整より簡単な部分問題になっている。

再投影誤差 \left\|\mathbf{u}_{ij}-\hat{\mathbf{u}}_{ij}\right\| の平均平方根(RMS)を「校正精度」として報告するのが一般的だが、平均値だけを見て校正完了とするのは危険である。以下を必ず確認する。

5. OpenCVでの実装骨格

典型的な単眼カメラの校正手順は次のようになる。撮影条件(解像度、ズーム、フォーカス)を固定し、パターンを画面の四隅・中央・複数の傾きで撮ることが、良い校正の9割を占める。

import cv2 as cv
import numpy as np

pattern_size = (9, 6)  # 内部コーナー数
objp = np.zeros((pattern_size[0]*pattern_size[1], 3), np.float32)
objp[:, :2] = np.mgrid[0:pattern_size[0], 0:pattern_size[1]].T.reshape(-1, 2)
objp *= square_size_m  # 実測した正方形1辺の長さ[m]

obj_points, img_points = [], []
for gray in calibration_images:  # 異なる姿勢で撮影した複数フレーム
    found, corners = cv.findChessboardCorners(gray, pattern_size)
    if found:
        corners = cv.cornerSubPix(gray, corners, (11, 11), (-1, -1),
                                   (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 30, 0.001))
        obj_points.append(objp)
        img_points.append(corners)

ret, K, dist, rvecs, tvecs = cv.calibrateCamera(
    obj_points, img_points, gray.shape[::-1], None, None)
# ret はRMS再投影誤差[px]。姿勢ごとの誤差も別途計算して確認する。

広角・魚眼レンズでは、通常のcalibrateCameraではなくcv.fisheye.calibrate系のAPIを使う。ステレオリグでは、両カメラの内部パラメータに加えて相対姿勢をcv.stereoCalibrateで求め、cv.stereoRectifyで左右画像を水平エピポーラ線に整列させる。この手順はステレオカメラの仕組みデプスカメラの仕組みで扱うベースライン設計と直接つながっている。

6. キャリブレーションドリフトはなぜ下流を壊すのか

校正値は撮影条件が変わらない限り一定という前提で、Visual SLAM・VO・PnPの多くの数式は書かれている。エピポーラ幾何入門のEssential Matrix推定は正規化座標 K^{-1}\tilde{\mathbf{x}} を前提にし、PnP入門の再投影誤差最小化も K を既知として扱う。K や歪み係数が実際の光学系とずれていると、これらの計算はすべて「間違った物差し」で行われることになる。

具体的には、次のような要因で校正値は静かに劣化する。

これらの誤差は、単発の姿勢推定では気づきにくい。しかしVOやSLAMのように姿勢を逐次積分する系では、系統的な再投影誤差がドリフトとして蓄積し、ループ閉合で修正しきれない歪んだ地図を作る原因になる。実運用のシステムでは、固定された既知の3D特徴(建物の直線、既知サイズの標識など)を使ってオンラインで校正のずれを監視する、あるいは定期的な再校正の運用を組み込むことが望ましい。

7. よくある失敗と対策

失敗パターン 何が起きるか 対策
姿勢の多様性不足(正面からのみ撮影) K と歪みが不定になりやすく、特にk_3や主点が不安定 画像の四隅・中央、複数の傾き角度で撮影する
パターンが平面から反っている 校正の前提そのものが崩れ、系統誤差が全体に乗る 硬い平板に貼る、印刷のスケール誤差を実測で補正する
コーナー検出精度が粗い モデルの表現力に関係なく再投影誤差の下限が上がる サブピクセル補正、十分な解像度、ピント・露出の管理
広角レンズに通常のピンホール歪みモデルを適用 画像端で発散的な誤差、最適化が不安定になる fisheyeモデルなど画角に合ったモデルを選ぶ
リサイズ・クロップ後にKを更新しない 主点・焦点距離がスケールとずれ、姿勢推定に系統誤差 画像変換のたびにKを対応するスケールへ変換する
ローリングシャッターを無視 同一フレーム内でも行ごとに実際の投影中心が異なる グローバルシャッター採用、または行時刻モデルで補正

8. まとめ

カメラキャリブレーションは、画像を「ただの2次元配列」から「幾何学的に解釈できる観測」へ変える最初の一歩である。ピンホールモデルの内部・外部パラメータと、放射状・接線方向の歪み係数を、Zhangの平面パターン法のように複数姿勢の観測から求め、再投影誤差の非線形最適化で仕上げる。この校正値の質は、単体のアルゴリズムの精度指標には現れないが、エピポーラ幾何、PnP、Visual SLAM、SfM、バンドル調整といった後段のすべての数式の前提になっている。校正は一度きりの儀式ではなく、撮影条件の変化とともに監視し続けるべきパイプラインの一部である。

参考資料

#Camera Calibration #カメラキャリブレーション #Zhang's Method #レンズ歪み #OpenCV #Computer Vision