カメラを少し横へ動かして同じ場面を撮ると、近い物体ほど背景に対して大きくずれる。この視差(parallax)を使えば、二次元画像から三次元の形やカメラの移動を復元できる。ただし、画像上で「同じ物理点」を対応付けるだけでは不十分である。誤対応、レンズ歪み、純回転、平面、動く物体が混じる現実の画像では、どの点の組が一つのカメラ運動と整合するかを判定しなければならない。そのための共通言語がEpipolar Geometry(エピポーラ幾何)である。

これはステレオ計測だけの話ではない。Structure from Motion(SfM)、Visual Odometry、Visual SLAM、ARの平面追跡、ロボットの自己位置推定、COLMAPの疎な再構成は、いずれも対応点と投影幾何を土台にする。本稿では座標系を曖昧にせず、行列が何を意味し、どの推定器を選び、どんなときに信じてはいけないかまでをつなげる。

ステレオカメラ式アイサイトを搭載したスバルWRX S4ステレオカメラ搭載車の実例

画像: Subaru WRX S4 2.0GT-S EyeSight(Tokumeigakarinoaoshima, CC BY-SA 4.0)、Wikimedia Commons。車両前部の外観写真で、カメラ内部のクローズアップではない。

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}]

で表せる。\tilde{\mathbf{x}}=(u,v,1)^\mathsf{T} は画像同次座標、K は内部パラメータ、R\in SO(3)\mathbf{t} は世界からカメラへの外部姿勢である。典型的には

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 はskewである。歪み補正済みなら正規化画像座標は \mathbf{x}=K^{-1}\tilde{\mathbf{x}} となる。以降、左カメラを基準に P_1=K[I\mid\mathbf{0}]、右カメラを P_2=K[R\mid\mathbf{t}] と置く。

エピポーラ平面と二つの画像面二つのカメラ中心CとC prime、三次元点X、二つの画像面、対応点xとx prime、および各画像のエピポーラ線を示す。 エピポーラ平面 C C′ X x x′ 基線 画像 1画像 2 l = Fᵀx′l′ = Fx

図の C,C' はカメラ中心、線分 CC' は基線である。点 X と二つの中心が定める一枚の平面は、左画像面をエピポーラ線 l、右画像面を l' として横切る。左で対応点 \mathbf{x} を見つけた時点で、右で探索すべき二次元領域は一本の直線へ落ちる。整列済みステレオならその線は水平で、対応探索は同じ走査線上の一次元探索になる。

2. Essential MatrixとFundamental Matrix

外部姿勢だけに注目するため、校正済みの正規化座標 (\mathbf{x},\mathbf{x}') を考える。左カメラから点への視線方向は \mathbf{x}、右の座標系では R\mathbf{x} である。並進ベクトル \mathbf{t} と二本の視線が同一平面にあることはスカラー三重積ゼロとして

\mathbf{x}'^\mathsf{T}[\mathbf{t}]_\times R\mathbf{x}=0

と書ける。ここで [\mathbf{t}]_\times は外積を行列化した歪対称行列である。

[\mathbf{t}]_\times= \begin{bmatrix}0&-t_z&t_y\\t_z&0&-t_x\\-t_y&t_x&0\end{bmatrix},\qquad [\mathbf{t}]_\times\mathbf{a}=\mathbf{t}\times\mathbf{a}

この E=[\mathbf{t}]_\times R をEssential Matrixと呼ぶ。E は任意の 3\times3 行列ではなく、階数2で、二つの非ゼロ特異値が等しいという制約を持つ。SVDで E=U\operatorname{diag}(s,s,0)V^\mathsf{T} の形に射影するのは、この物理制約を回復するためである。

画像画素座標を直接使う未校正の場合は

\tilde{\mathbf{x}}'^\mathsf{T}F\tilde{\mathbf{x}}=0,\qquad F=K_2^{-\mathsf{T}}EK_1^{-1}

となり、F がFundamental Matrixである。F\tilde{\mathbf{x}} は右画像のエピポーラ線 l'F^\mathsf{T}\tilde{\mathbf{x}}' は左画像の線 l を与える。F は内部パラメータを吸収するため画像対の幾何検証には便利だが、姿勢をメートル単位で解釈するには校正が必要である。

行列 座標 必要な既知量 形状制約 得られるもの 主な用途
F 生の画素同次座標 なし rank 2、自由度7 エピポーラ線 未校正SfM、対応検証
E K^{-1}\tilde{\mathbf{x}} 両カメラのK rank 2、特異値(s,s,0) R\mathbf{t}の方向 VO、SLAM、校正済みステレオ
H 平面上または純回転の画素 平面または回転モデル 一般に自由度8 平面ワープ AR平面、画像合成

エピポールは何を知らせるか

右カメラ中心を左画像へ投影した点が左のエピポール \mathbf{e} で、F\mathbf{e}=0 を満たす。同様に F^\mathsf{T}\mathbf{e}'=0 である。エピポールが画像内にあればエピポーラ線が放射状に集まり、カメラはおおむね前後方向へ動いたことを示す。無限遠にあれば線はほぼ平行で横移動に近い。これは有用な診断だが、推定が悪いだけでも不自然な位置になるため、単独で運動を断定してはならない。

3. 対応点から行列を推定する:8点法

対応 \tilde{\mathbf{x}}=(u,v,1)^\mathsf{T}\tilde{\mathbf{x}}'=(u',v',1)^\mathsf{T} 一組は、F の9要素に対して一次の制約を一つ与える。\mathbf{f}=\operatorname{vec}(F) とすると、例えば

[u'u,\ u'v,\ u',\ v'u,\ v'v,\ v',\ u,\ v,\ 1]\mathbf{f}=0

である。8組以上を積んだ行列 A に対し、A\mathbf{f}=0 の最小特異ベクトルを取るのが8点法である。名前は8対応で自由度を満たすことに由来するが、ノイズがある現実では多数点を最小二乗で使う。

素の画素座標で解くと、座標値の大きさが悪条件化を招く。Hartleyの正規化8点法では、各画像の点群を重心ゼロ・平均距離 \sqrt{2} になる相似変換 T,T' で正規化してから解き、最後に

F=T'^\mathsf{T}F_{\text{norm}}T

で戻す。さらに得られた F をSVDして最小特異値をゼロに置けば rank 2 を強制できる。これは小さな実装差に見えて、解の安定性を大きく左右する。

校正済みなら、正規化対応から同じ考えで E の初期値を作れる。ただし8点の線形解はEssential Matrixのより強い特異値制約を自動的には満たさない。E=U\operatorname{diag}(\sigma_1,\sigma_2,\sigma_3)V^\mathsf{T} を計算し、\operatorname{diag}((\sigma_1+\sigma_2)/2,(\sigma_1+\sigma_2)/2,0) に置換して射影する。

4. 5点法:校正済みなら最小サンプルを小さくする

Essential Matrixの自由度は5である。5点法は5対応から E の候補を有限個求める最小解法で、Nistérの手法は零空間を多項式制約へ代入して最大10個の実解候補を列挙する。導出も実装も8点法より複雑だが、RANSACの1仮説に必要な点が5個で済む利点は非常に大きい。

インライア率を w、一回の抽出で全点がインライアとなる確率を w^s、失敗確率を p、最小サンプル数を s とすると、必要反復数の目安は

N=\frac{\log p}{\log(1-w^s)}

となる。w=0.5,p=0.01 なら s=8 は約1177回、s=5 は約145回である。実際にはPROSACのようにマッチの品質順で標本を引き、適応的に打ち切るため単純比較ではない。それでも低インライア率の環境では5点法の価値が明確である。

OpenCVの findEssentialMat はRANSAC/LMEDSと5点法系の実装を提供し、recoverPose が候補分解とcheirality検査を行う。実装者は「5点法を呼んだ」ことより、入力が歪み補正済み・正規化済みか、しきい値がどの座標単位かを確認する方が重要である。

5. RANSAC:外れ値を前提に幾何を使う

SIFT、ORB、SuperPoint、LoFTRなどのマッチャは、同じ模様、反射、繰り返し格子、遮蔽で誤対応を出す。最小二乗で全対応へ F を当てはめれば、少数の誤りが行列全体を壊す。RANSACは次を繰り返す。

  1. 最小数の対応を無作為に選び、F または E の仮説を作る。
  2. 全対応について残差を計算し、閾値以内をインライアとする。
  3. 最も支持数が多い、またはロバストなスコアがよい仮説を保存する。
  4. 最終インライア全部で再推定し、必要なら非線形最適化で磨く。

エピポーラ拘束は代数誤差 \mathbf{x}'^\mathsf{T}F\mathbf{x} だけで閾値化してはいけない。F のスケールに依存するからである。実務ではSampson距離

d_S(\mathbf{x},\mathbf{x}',F)= \frac{(\mathbf{x}'^\mathsf{T}F\mathbf{x})^2} {(F\mathbf{x})_1^2+(F\mathbf{x})_2^2+(F^\mathsf{T}\mathbf{x}')_1^2+(F^\mathsf{T}\mathbf{x}')_2^2}

をよく用いる。これは幾何誤差の一次近似であり、対応点から各エピポーラ線への距離を正規化した量である。画素座標での閾値は画像解像度、特徴点の局在精度、歪み残差、ブラーに依存する。万能の「1 px」は存在しない。残差ヒストグラムとインライアの画像上の分布を可視化して調整する。

現在のOpenCVではUSAC系のロバスト推定も利用できる。品質順サンプリング、局所最適化、退化チェックを組み合わせるため、単純なRANSACより速く安定する場合がある。ただし、統計的な外れ値除去は「多数派が一つの静的剛体運動に従う」という前提を超えられない。画面の大半が走行車や人なら、セマンティックマスク、モーション分割、IMU、深度など別の情報を加える必要がある。

6. E を姿勢に分解し、正しい候補を選ぶ

補正済みの E=U\operatorname{diag}(s,s,0)V^\mathsf{T} に対し

W=\begin{bmatrix}0&-1&0\\1&0&0\\0&0&1\end{bmatrix}

を使うと、回転候補 R=UWV^\mathsf{T} または UW^\mathsf{T}V^\mathsf{T}、並進方向候補 \pm U_{:,3} が得られる。符号と回転の組合せは4通りある。ここで重要なのは、二視点拘束だけではどれも代数的には同じ E を作ることだ。

選択にはcheirality(正の深度)を用いる。各候補で少数のインライアを三角測量し、両カメラ座標系で Z>0 となる点が最も多い候補を採る。さらに、回転行列の行列式が +1 か、再投影誤差が小さいか、十分な視差があるかを検査する。特に覚えるべき限界は、\mathbf{t} は方向までしか復元できないことである。\mathbf{t} と全3D点を同じ係数で拡大しても投影は変わらない。既知のステレオ基線、車輪オドメトリ、IMU、既知サイズ物体、GNSSなどが尺度を与える。

7. 三角測量:二本の光線から3D点へ

投影式 \mathbf{x}\times(P\mathbf{X})=\mathbf{0} は各視点から独立な二式を作る。二視点分を積んだ線形系 A\mathbf{X}=0 をSVDで解くDLT三角測量は簡潔で、OpenCVの triangulatePoints もこの形式に近い。例えば P_i の第j行を \mathbf{p}_{ij}^\mathsf{T} とすれば、点 (u_i,v_i) から

\begin{bmatrix} u_i\mathbf{p}_{i3}^\mathsf{T}-\mathbf{p}_{i1}^\mathsf{T}\\ v_i\mathbf{p}_{i3}^\mathsf{T}-\mathbf{p}_{i2}^\mathsf{T} \end{bmatrix}\mathbf{X}=\mathbf{0}

を得る。最後に同次成分で割る前に、w が極端に小さくないかを検査する。

整列済みの水平ステレオならより直感的である。左・右の水平座標差を視差 d=u_L-u_R、焦点距離を f、基線を B とすると

Z=\frac{fB}{d},\qquad X=\frac{(u_L-c_x)Z}{f}

である。深度誤差は概ね \delta Z\simeq \frac{Z^2}{fB}\delta d となる。遠方ほど、焦点距離や基線が短いほど、同じ1画素の視差誤差が大きな深度誤差になる。したがって「マッチしたので点群に入れる」ではなく、三角測量角、視差、再投影誤差、正の深度を品質ゲートにする。

線形三角測量は初期値であって、画像ノイズを正しく最小化するものではない。カメラ姿勢 P_i と点 \mathbf{X}_j を同時に最適化するバンドル調整は

\min_{\{R_i,\mathbf{t}_i,\mathbf{X}_j\}} \sum_{(i,j)\in\mathcal{O}}\rho\left(\left\|\pi(K_i(R_i\mathbf{X}_j+\mathbf{t}_i))-\tilde{\mathbf{x}}_{ij}\right\|^2\right)

を解く。\rho はHuberやCauchyなどのロバスト損失、\pi は透視除算である。COLMAPやTheia、Ceres Solverを使う再構成で精度が上がる理由はここにある。ゲージ自由度を固定するため、最初のカメラを原点に置き、必要なら既知尺度を一つ固定する。

8. ホモグラフィとの使い分け

シーンの全点が一平面 \pi 上にある、またはカメラが純回転する場合、画像間の対応は \tilde{\mathbf{x}}'\sim H\tilde{\mathbf{x}} という3×3ホモグラフィで説明できる。校正済みで平面の法線を \mathbf{n}、距離を d とすれば

H=K\left(R+\frac{\mathbf{t}\mathbf{n}^\mathsf{T}}{d}\right)K^{-1}

である。純回転なら並進項が消え、H=KRK^{-1} になる。ポスター、机、建物正面、遠景をパンした映像では H が優れたモデルになり、ARの平面アンカーや画像スティッチングではむしろ第一選択である。

しかし、平面データだけから F/E を推定すると、見かけ上のインライアが多くても3D構造と並進を安定に分離できない。逆に一般の非平面シーンを一枚の H に押し込めると、手前と奥でワープが合わない。実装では F/EH を両方RANSACで推定し、残差、説明できた点数、点の分布、復元後の視差を比較する。モデルの採否をマッチ数だけで決めると、壁面や画面中央の大きな平面に引っ張られる。

状況 第一候補 得られること 注意点
校正済み、一般3D、並進あり E + 5点法 相対姿勢、疎な深度 尺度は未定、低視差で不安定
未校正の画像対 F + 正規化8点法 エピポーラ線、対応検証 Kなしに物理姿勢を解釈しない
ほぼ平面、ポスター、机 H + 4点法 平面ワープ、平面姿勢候補 平面外の奥行きは得ない
純回転・パノラマ H 画像整列、回転 並進と深度を観測できない
既知3D地図と2D観測 PnP + RANSAC 絶対姿勢 地図品質と尺度に依存

9. キャリブレーションは前処理ではなくモデルの一部

チェッカーボード、Charuco、AprilTagグリッドを複数の距離・傾き・画像位置で撮影し、K と歪み係数を推定する。Brown–Conrady型の放射歪みは、正規化半径 r^2=x^2+y^2 に対して概略

x_d=x(1+k_1r^2+k_2r^4+k_3r^6)+2p_1xy+p_2(r^2+2x^2)

のように表す。広角・魚眼では通常のピンホール歪みモデルを無理に当てず、OpenCVのfisheyeモデルや使用レンズに対応したモデルを選ぶ。校正の平均再投影誤差が小さくても、画像端、焦点距離、温度、フォーカス、解像度変更で誤差の構造が変わることがある。

二視点処理へ入る前に、同じ解像度・クロップ・デジタルズーム条件で取得した校正値かを確認する。undistortPoints で点を正規化して E を推定する方法と、歪み補正画像で F を推定する方法は混同しやすい。APIが焦点距離・主点・歪みを内部で使うか、既に補正された座標を期待するかを必ず読む。ステレオリグなら両カメラの内部値に加え、相対姿勢を stereoCalibrate で求め、stereoRectify でエピポーラ線を水平化する。

10. OpenCVでの最小パイプライン

次は校正済み単眼カメラの二枚から相対姿勢と、品質を通った疎な3D点を得る骨格である。特徴量にはORBを使うが、撮影条件に応じてSIFTや学習ベースのマッチャへ差し替えられる。実際には露出、動体、時刻同期もログへ残す。

import cv2 as cv
import numpy as np

# K, dist はこの撮影解像度・レンズ条件で校正した値
orb = cv.ORB_create(nfeatures=3000)
kp1, des1 = orb.detectAndCompute(img1, None)
kp2, des2 = orb.detectAndCompute(img2, None)
matches = cv.BFMatcher(cv.NORM_HAMMING).knnMatch(des1, des2, k=2)
good = [m for m, n in matches if m.distance < 0.75 * n.distance]

p1 = np.float32([kp1[m.queryIdx].pt for m in good])
p2 = np.float32([kp2[m.trainIdx].pt for m in good])
# threshold は画素単位。初期値ではなく残差分布で決める。
E, mask = cv.findEssentialMat(p1, p2, K, method=cv.USAC_MAGSAC,
                              prob=0.999, threshold=1.0)
in1, in2 = p1[mask.ravel() != 0], p2[mask.ravel() != 0]
count, R, t, pose_mask = cv.recoverPose(E, in1, in2, K)

# P1, P2 は正規化座標用。尺度は任意なので t の長さは物理単位ではない。
n1 = cv.undistortPoints(in1.reshape(-1, 1, 2), K, dist).reshape(-1, 2)
n2 = cv.undistortPoints(in2.reshape(-1, 1, 2), K, dist).reshape(-1, 2)
P1 = np.hstack([np.eye(3), np.zeros((3, 1))])
P2 = np.hstack([R, t])
X4 = cv.triangulatePoints(P1, P2, n1.T, n2.T)
X = (X4[:3] / X4[3]).T

# 両視点の正深度、再投影誤差、三角測量角でさらにフィルタする。
z1 = X[:, 2]
z2 = (R @ X.T + t).T[:, 2]
valid = (z1 > 0) & (z2 > 0) & np.isfinite(X).all(axis=1)

この例では findEssentialMat に生の画素と K を渡しているが、歪みが無視できないなら先に undistortPoints した正規化点を渡し、対応するAPI形へ変更する。recoverPose の返す \mathbf{t} を「移動距離」として使うのも誤りである。尺度を使うアプリケーションでは、既知基線、VIO、車輪、深度センサなどでスケールを拘束する。

COLMAPでは特徴抽出、マッチング、幾何検証、incremental mapping、bundle adjustmentが一連で実装されている。小規模なデータセットならGUIでカメラモデルと再構成を点検できる。コマンドラインではカメラモデルの指定、EXIF焦点距離の扱い、マッチング方式(exhaustive/sequential/vocab tree)、画像ペアの時間間隔が精度と計算量を左右する。再構成後は点の数ではなく、登録画像数、平均再投影誤差、各画像の観測数、点群の飛びを確認する。

11. よくある失敗条件と切り分け

視差が小さい・基線がない

前進運動、遠景、短いフレーム間隔では対応は得られても深度が出ない。エピポーラ線は妥当でも三角測量角が極小なら、深度を更新せず保留する。キーフレームを遠ざける、横方向に観測を取る、既知基線のステレオを使うことが根本策である。

純回転または平面縮退

パン撮影や壁面だけの視野では H が説明力を持つ。E のインライアが多いからといって並進が観測できたとは限らない。HE の競合を記録し、三角測量後の正深度率と視差の中央値をゲートにする。ARのポスター追跡では、これは失敗ではなく正しいモデル選択である。

誤対応・反復模様・反射

窓、タイル、書棚、液晶画面、水面では局所記述子が似通う。ratio test、相互最近傍、幾何RANSACを重ね、インライア点が画像全体に分散しているかを見る。鏡像や透明体は剛体・Lambert反射の仮定そのものを破るため、いくら閾値を調整しても救えないことがある。

動的物体と複数運動

RANSACは最大の一運動を選ぶだけである。背景が少数派なら車の運動を推定してしまう。人物・車両をセマンティックに除外する、光フローをクラスタリングする、複数モデル推定を行う、深度・IMUと整合させる、といった設計を用途に応じて選ぶ。

レンズ歪み、ローリングシャッター、非同期

広角の端部を未補正のまま使うと、エピポーラ線に系統的な曲がりが残る。高速移動中のローリングシャッターでは一枚の画像中で姿勢が変化し、単一の E は近似に過ぎない。ステレオの左右露光がずれても、動体では偽視差になる。グローバルシャッター、短露光、読出し時刻モデル、IMU補正、ハードウェア同期を検討する。

数値・座標系の事故

画素座標と正規化座標を混ぜる、R,\mathbf{t} の世界→カメラとカメラ→世界を取り違える、左・右の点順を逆にする、画像をリサイズ後に K を更新しない、といったミスは頻出する。推定値を鵜呑みにせず、対応点とエピポーラ線を重ね描きし、両カメラでの正深度、再投影誤差、\det R=1R^\mathsf{T}R\simeq I を自動テストにする。

12. 実務の評価指標と設計チェックリスト

二視点推定の成功を「行列が返った」で終わらせない。マッチ数はテクスチャ量に左右され、平均誤差だけは少数の良点に隠される。フレームごとに以下を保存すると、センサ、マッチャ、姿勢推定のどこが壊れたかを後から区別できる。

地上真値がある研究・製品評価では、姿勢の相対回転誤差、並進方向誤差、軌跡のATE/RPE、深度の絶対・相対誤差を分けて報告する。単眼二視点の並進はスケール曖昧なので、正規化後の誤差かSim(3)整列後かを明記する。失敗フレームを平均から除外するのではなく、どの縮退・視覚条件で失敗したかを併記する方が、システムの限界を正しく伝えられる。

13. 近年の発展:学習は幾何を置き換えたのか

学習ベースの特徴点・記述子(SuperPoint)、粗密マッチャ(LoFTR)、汎用対応推定(LightGlueなど)は、低テクスチャや視点変化で従来記述子より多くの候補を作れる場合がある。一方で、ネットワークが返す対応にも誤りはあり、カメラ運動、平面、ローリングシャッター、尺度の物理的曖昧さは消えない。実際のSfM/SLAMでは、学習マッチャの出力を E/F/H のロバスト推定とバンドル調整で検証するハイブリッド構成が実用的である。

より大きな流れとしては、NeRFや3D Gaussian Splattingのようなニューラル/明示的シーン表現も複数視点の整合を利用する。これらは見栄えのよい新規視点合成を可能にするが、カメラ姿勢と観測幾何の品質に敏感で、COLMAP由来の姿勢を初期値にする実装も多い。大規模・動的・反射的な環境では、対応、深度、セグメンテーション、慣性、時刻モデルを共同で推定する研究が続く。

したがって最新モデルを導入する判断は「ORBよりマッチ数が増えたか」だけでなく、推定後のインライア分布、姿勢誤差、計算遅延、GPU要件、撮影条件外での破綻、ライセンスまで含めて行うべきである。幾何は古い前処理ではなく、学習器の出力を現実の三次元と照合する検証器であり続ける。

14. まとめ

エピポーラ幾何は、二枚の画像の対応を「もっとも似ている点」から「同じカメラ運動で説明できる点」へ引き上げる枠組みである。内部パラメータが分かるなら E=[\mathbf{t}]_\times R を通して相対姿勢へ進み、分からなければ F でエピポーラ線と対応を検証する。8点法は理解と初期化の基礎、5点法はロバスト推定での効率的な最小解法、RANSACは外れ値を前提にする仕組み、三角測量とバンドル調整は三次元へ進む橋渡しである。

ただし、視差がない、平面しかない、純回転、動体多数、歪み・非同期が大きい状況では、行列が返っても物理的に意味のある深度や並進は保証されない。ホモグラフィとのモデル選択、校正条件の管理、再投影誤差と正深度の検査、外部尺度との融合までを一つのパイプラインとして設計することが、再現性のあるコンピュータビジョンにつながる。

参考資料(一次資料・公式ドキュメント)

#Computer Vision #Epipolar Geometry #Essential Matrix #Fundamental Matrix #RANSAC #Triangulation #SfM #Visual SLAM