Digital Reactor
画像・3D処理

カメラキャリブレーション - 内部パラメータ・外部パラメータから3次元復元まで

カメラキャリブレーション - 内部パラメータ・外部パラメータから3次元復元まで

はじめに

3次元空間をカメラで撮影して2次元画像にする過程では、レンズの歪みやカメラの位置・姿勢といった情報が失われます。これらを数値として正確に求めておかないと、画像から寸法を測ったり3次元形状を復元したりはできません。それを担うのがカメラキャリブレーションです。内部パラメータと外部パラメータの推定から、OpenCVでの実装、精度を出すための撮影のコツまでを順に見ていきます。

対象読者:

  • コンピュータビジョン初心者から中級者
  • カメラキャリブレーションの実践的な知識を学びたいエンジニア
  • 3次元復元、AR、ロボットビジョンなどの分野に関心のある方

記事のポイント:

  • カメラキャリブレーションで何が推定でき、なぜ必要になるのか
  • 内部パラメータと外部パラメータの意味と推定のしかた
  • OpenCVによるキャリブレーションと三角測量の実装
  • 精度を出すための撮影のしかたと、よくある問題への対処

カメラキャリブレーションとは

カメラキャリブレーションとは、カメラの特性や位置・姿勢に関するパラメータを推定する作業です。推定するパラメータは2種類あります。内部パラメータ(intrinsic parameters)は焦点距離やレンズ歪みといったカメラの光学的特性で、カメラごとに決まります。外部パラメータ(extrinsic parameters)はカメラの位置と姿勢(回転と並進)で、カメラ同士のペアに対して決まります。典型的にはステレオカメラのカメラ間の位置関係が該当し、1台のカメラしか使わない場合には計算しません。

なぜカメラキャリブレーションが必要か

キャリブレーションの結果は、以下のような応用の土台になります。

  • 拡張現実(AR)アプリケーション
  • 3次元再構成
  • ロボットビジョン
  • 産業用の計測・検査システム
  • マルチカメラシステムでの3D復元

現場では、キャリブレーションの精度がシステムの成否を直接左右します。例えば工場での製品検査で0.1mmの精度が要求される場合、適切なキャリブレーションなしでは全く使い物になりません。ここの詰めが甘いために、開発したシステムがまるごと使えなくなるケースもあります。

カメラの内部パラメータ(Intrinsic Parameters)

内部パラメータは、焦点距離やレンズ歪みなど、カメラ固有の光学的特性を表します。

カメラ行列

内部パラメータは通常、以下のような3×3のカメラ行列で表現されます。

| fx  0   cx |
| 0   fy  cy |
| 0   0   1  |

ここで、

  • fx, fy: x方向とy方向の焦点距離(ピクセル単位)
  • cx, cy: 画像中心(主点)の座標(ピクセル単位)

歪み係数

レンズの歪みは以下の係数で表現されます。

  • 放射歪み係数(k1, k2, k3)
  • 接線歪み係数(p1, p2)

最近の高品質なカメラレンズでは、k3やp1, p2の値がほぼゼロになることが多いです。特にスマートフォンのカメラは工場出荷時に高精度なキャリブレーションが行われているため、k1とk2だけで十分に補正できます。広角レンズや魚眼レンズを使う場合は、高次の歪み係数まで含めて推定します。

OpenCVでは、cv2.calibrateCamera()関数で以下のように歪み係数を推定します。

ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(
    objpoints, imgpoints, gray.shape[::-1], None, None
)

カメラの外部パラメータ(Extrinsic Parameters)

外部パラメータは、カメラの位置と姿勢を表します。

回転行列と並進ベクトル

外部パラメータは以下の2つの要素で構成されます。

  • 回転行列 R(3×3行列): カメラの姿勢を表す
  • 並進ベクトル t(3×1ベクトル): カメラの位置を表す

これらは以下の変換行列として表現されます。

| R11 R12 R13 t1 |
| R21 R22 R23 t2 |
| R31 R32 R33 t3 |
| 0   0   0   1  |

座標変換

ワールド座標系の点Pwからカメラ座標系の点Pcへの変換は以下の式で表されます。

Pc = R * Pw + t

ステレオカメラのキャリブレーション

ステレオカメラのキャリブレーションでは、2台のカメラ間の位置関係(外部パラメータ)を求めます。OpenCVではcv2.stereoCalibrate()関数を使います。

# ステレオキャリブレーションの実行
ret, mtx1, dist1, mtx2, dist2, R, t, E, F = cv2.stereoCalibrate(
    objpoints,          # 3D点群(チェッカーボードの世界座標)
    imgpoints1,         # カメラ1での2D点群
    imgpoints2,         # カメラ2での2D点群
    cameraMatrix1,      # カメラ1の内部パラメータ行列
    distCoeffs1,        # カメラ1の歪み係数
    cameraMatrix2,      # カメラ2の内部パラメータ行列
    distCoeffs2,        # カメラ2の歪み係数
    imageSize,          # 画像サイズ
    flags=cv2.CALIB_FIX_INTRINSIC  # 内部パラメータを固定
)

この関数は以下のパラメータを返します。

  • R: カメラ2からカメラ1への回転行列
  • t: カメラ2からカメラ1への並進ベクトル
  • E: Essential行列(画像のステレオ補正に使用します)
  • F: Fundamental行列(画像のステレオ補正に使用します)

実際の手順としては、まず各カメラを個別にキャリブレーションして内部パラメータを求め、次に両方のカメラで同時にチェッカーボードを撮影し、最後にcv2.stereoCalibrate()で外部パラメータを計算します。精度を出すには、チェッカーボードを両方のカメラから十分見える位置に置き、様々な角度・距離で最低15枚以上撮影してください。キャリブレーション時のRMS誤差(再投影誤差の目安)は1ピクセル以下に収まっているのが望ましいです。

# キャリブレーション結果の評価
print(f"キャリブレーションのRMS誤差: {ret}")

# ステレオ補正行列の計算
R1, R2, P1, P2, Q, roi1, roi2 = cv2.stereoRectify(
    mtx1, dist1,
    mtx2, dist2,
    imageSize, R, t
)

ステレオ補正(rectification)を行うと、エピポーラ線が画像の水平線と平行になるように画像を変換できます。対応点を水平方向に探すだけで済むようになり、ステレオマッチングが簡単になります。

三角測量による3次元復元

三角測量は、2つ以上のカメラで撮影された画像から3次元点の位置を復元する技術です。同じ点が各画像のどこに写っているかが分かれば、視点の違いから幾何学的に3次元位置を逆算できます。

OpenCVには三角測量に使える関数がいくつかありますが、ここでは2つのカメラの投影行列と対応点から3D点を復元するtriangulatePointsを使います。呼び出しは次の1行です。

points4D = cv2.triangulatePoints(projMatr1, projMatr2, points1, points2)

この関数は4次元の同次座標を出力します。ここから3次元座標への変換は以下のように行います。

# 同次座標から3次元座標への変換
points3D = points4D[:3] / points4D[3]

# より具体的な実装例
def homogeneous_to_3d(points4D):
    """
    同次座標系(4次元)から3次元ユークリッド座標系に変換

    Args:
        points4D: shape (4, N) の同次座標系の点群
    Returns:
        points3D: shape (3, N) の3次元座標系の点群
    """
    # 最後の要素(w)で他の要素を割る
    points3D = points4D[:3] / points4D[3]
    return points3D

# 使用例
points3D = homogeneous_to_3d(points4D)
print(f"3D座標: X={points3D[0]}, Y={points3D[1]}, Z={points3D[2]}")

同次座標の第4成分(w)がゼロに近い点は、無限遠にあることを意味します。キャリブレーションの誤差や対応点のミスマッチで生じることが多いので、wの絶対値が一定のしきい値(例:1e-8)を下回る点は除外してから使います。

パターンの準備とコーナー検出

キャリブレーションパターンの選び方

よく使われるのは次のパターンです。

  • チェッカーボードパターン
  • ChArUcoパターン
  • 円形グリッドパターン

チェッカーボードパターンを使う場合は、光沢のない用紙に印刷し、硬い板に貼り付けます。光沢紙は反射でコーナー検出の精度が大きく落ちます。また、パターンにわずかな歪みがあるだけでキャリブレーション結果に影響するため、完全な平面を保ってください。

ChArUcoパターンは照明条件の変化に強く、より安定した結果が得られます。産業用途ならChArUcoパターンを選ぶのが無難です。

コーナー検出

チェッカーボードのコーナー検出には以下の関数を使います。

ret, corners = cv2.findChessboardCorners(
    gray, (9,6), None
)

照明条件が安定しない場合は、コーナー検出の前にコントラスト改善とノイズ除去を入れます。

# コントラストの改善
gray = cv2.equalizeHist(gray)

# ノイズ除去
gray = cv2.GaussianBlur(gray, (5,5), 0)

精度を左右する撮影のしかた

キャリブレーションの精度を決めるのは、撮影枚数(最低15枚程度)、角度・距離のバリエーション、カメラ設定(フォーカス、露出など)の固定、そしてパターン自体の品質です。撮影は以下のような構成にすると良い結果が得られます。

  • パターンを画像中心に配置した正面からの撮影(3-4枚)
  • パターンを画像の四隅に寄せた撮影(各隅2-3枚)
  • パターンを30度以上傾けた撮影(4-5枚)
  • パターンをカメラに近づけた撮影(2-3枚)

オートフォーカスのままだと撮影のたびにフォーカスが変わってしまい、正確なキャリブレーションができません。マニュアルフォーカスに切り替えてから撮影してください。

よくある問題と解決策

問題考えられる原因解決策
再投影誤差が大きいキャリブレーション画像の品質が低い、画像枚数が少ないキャリブレーション画像の品質を確認、不適切な画像を除外、より多くのキャリブレーション画像を使用
歪み補正が不適切高次の歪み係数が考慮されていない高次の歪み係数を考慮、キャリブレーションパターンの位置を調整

まとめ

カメラキャリブレーションは地味な前処理に見えますが、ここでの精度がそのまま3次元復元や位置推定の精度を決めます。まずは手元のカメラをマニュアルフォーカスに固定し、チェッカーボードを角度と距離を変えて15枚ほど撮り、再投影RMS誤差を確認し、必要な寸法精度や撮影条件に照らして許容できるかを判断してください。1ピクセルという値だけで十分な校正精度を保証できるわけではありません。誤差が大きい場合は、パターンの平面性、フォーカスの固定、撮影角度のバリエーションのどれかが欠けていることがほとんどです。

関連記事

← 技術ブログ一覧へ