2015. 5.21.
石立 喬

OpenCVとVisual C++による画像処理と認識(17)

----- 三次元再構成の準備として、カメラキャリブレーションを行う -----

 コンピュータ・ビジョンは、ロボットの目に応用例があるように、複数のカメラや複数の映像から、三次元物体を把握する技術である。OpenCVも、当然、この応用に向けた多数のクラスや関数がある。
 医用画像処理の分野では、CTやMRIで得た画像から、臓器の三次元再構成が行われており、最近では、外部からの撮像のみで、大腸を内視鏡で見たのと同様の像を得ることができる。
 これらは、すべて、多数の二次元データから、三次元としての画像を再構成する技術であり、ここでは、その前準備として、カメラのキャリブレーションを紹介する。

三次元再構成(3D Reconstruction)
 三次元の物体をカメラで撮影すると、二次元の画像になり、三次元としての情報が失われてしまう。ステレオ写真のように一定距離だけ離した二台のカメラを使用すると、その場所から見た場合のみの三次元再生が可能である。
 ステレオ写真の原理を拡張して、三次元物体を中心に、多数の方向から写真を撮ると、すべての方向から見た三次元物体が再生可能となる。

OpenCVで使用するカメラモデル
 三次元の物体が、カメラによって、二次元の画像に、どのようにして変換されるのかを、数式によって定義したものが、カメラモデルである。簡単化するために、光が一点を通って投影される、理想的なピンホールカメラを想定している。
 図1で、カメラマトリックスは、fxとfyの焦点距離(単位はピクセル)と、cxとcyの光学的中心オフセット(単位はピクセル)からなり、画像のサイズに応じて、スケーリングされる。歪係数ベクトルは、半径方向歪(radial distortion)係数k1、k2、k3と接線方向歪(tangential distortion)係数p1、p2から成る。歪係数自身はカメラ固有で一定であるが、カメラの位置や向きなど撮影条件の係数として働き、撮影ごとに異なる実際の歪マトリックスとなる。カメラマトリックスも歪係数ベクトルも、カメラ固有の値で、内部パラメータ(intrinsic parameter)と呼ばれる。
 回転・並進マトリックス(rotation and translation matrix)は、回転マトリックス(rotation matrix)(3 x 3)と並進ベクトル(translation vector)(3)から成り、全体として4 x 4のマトリックスにまとめられている。これらは、ワールド座標におけるカメラの向き(回転マトリックスが対応)と位置(並進ベクトルが対応)を示し、ビューマトリックスとも呼ばれる。これは、カメラ固有ではなく、外部パラメータ(extrinsic parameter)である。
 回転・並進マトリックスは、calibrateCamera関数により、rvecsとtvecsの二つのベクトル(正しくはベクトルのベクトル)として得られる。それぞれ三つの要素からなり、rvecsの場合は、ロドリゲスの回転公式(Rodrigues's rotation formula)により、3 x 3のマトリックスに変換して使用する。tvecsは、そのままで良い。


図1 カメラモデル


カメラキャリブレーション
 カメラは、三次元物体の測定器ではあるが、そのままでは、目盛がないのと同じである。そこで、寸法の分かっている、チェスボードのような三次元物体を撮影し、あらかじめ、カメラと被写体の関係を較正しておく方法がとられる。これがカメラキャリブレーションで、三次元再構成には不可欠である。
 カメラキャリブレーションとは、すでに述べた二つの内部パラメータを求めることである。これらはカメラに固有のもので、カメラの位置や方向に関係がないので、一度求めて保存しておけば、何回でも使用できる。
 精度良い結果を得るには、チェスボードの場合、最低でも10枚の写真を撮る必要がある。calibrateCamera関数は、誤差をピクセル値で戻り値として返す。今回、分かったことは、市販のデジカメのレンズは十分優れていて、広角の度合いが強くない限り、歪係数が比較的に小さいことである。そのため、チェスボードの精度に依存しやすく、チェスボードパターンの寸法の精度と平面性の良さが要求される。

calib3ライブラリを追加設定する
 すでに、あらかじめ設定してある場合は問題ないが、念のために、再度説明する。
 1)Visual Studioのウインドウで、「表示」→「その他のウインドウ」→「プロパティマネージャー」を選択する。
 2)右上方に現れたウインドウで、「OpenCVCameraCalibration」など、設定してあるプロジェクト名を展開する。
 3)「Debug | Win32」→「Microsoft.Cpp.Win32.userプロパティページ」と選択し、ダブルクリックする。
 4)開いたウインドウの左欄で、「共通プロパティ」→「リンカ―」→「入力」を選択する。
 5)右欄で、「追加の依存ファイル」を選択して、右側に現れた「V」→「<編集・・・>」をクリックする。
 6)図2のウインドウが開くので、「opencv_calib3d2410d.lib」を追加する(図は追加後の状態)。
 7)「OK」をクリックする。


図2 モジュールのcalib3を使用するので、依存ファイルを追加しておく


チェスボードを使って、カメラキャリブレーションを実行する
 プログラムは、下記から成っている。
 1)原画像として、チェスボードを色々な角度から撮影した画像を10枚読み込む(図3、画像は図13)。
 2)全ての原画像の個々に対して以下を実行する(C〜Eは、確認のためで、キャリブレーション結果には関係しない)(図4)。
   A.. コーナーimageCornersを検出する
   B. コーナーの位置精度を高めるcornerSubPix関数を実行する
   C. コーナーをdst_imageに描画する
   D. dst_imageを表示する(結果の一例は図9)
   E. コーナーの座標を確認する
   F. 個々のコーナーimageCornersを、全体的なimagePointsに順次格納する
 3)チェスボードの寸法、コーナーの数からobjectPointsを用意する(図5)。
 4)calibrateCamera関数を実行する(図5)。
 5)キャリブレーション結果として、誤差と二つの内部パラメータを表示する(図6、結果は図10)。
 6)キャリブレーション結果を、calibration.xmlとして格納する(図6、結果は図11)。
 7)参考までに、外部パラメータのrvecsとtvecsを表示する(図7、結果は図12)。
 8)キャリブレーション結果の確認として、原画像のサンプルを補正してみる(図8、結果は図14)。

使用したOpenCVクラス、構造体の説明
 以下は、使用したキャリブレーション関連の関数である。特に断らないものは、calib3d/Camera Calibration and 3D Reconstructionにある。
◎findChessboardCorners関数
 準備として、チェスボードからコーナーを探索する関数で、引数は、
  image --------- コーナーを探索したい対象画像
  patternSize ---- 探索するコーナーの数、Size(9, 6)など
  corners ------- 検出されたコーナーの二次元座標のベクトルを受け取る
  flags = CALIB_CB_ADAPTIVE_THRESH + CALIB_CB_NORMALIZE
        ---- 適応的二値化とヒストグラム平坦化をする、デフォルトで良い
であり、すべてのコーナーが順序正しく検出されれば、戻り値としてtrueを返す。
◎cornerSubPix関数
 imgproc/Image Processing/Feature Dtectionにある。
 コーナーの位置精度をさらに高める関数で、原則として使用する。引数は、
  image ------- ピクセル値の小数点以下を細かく求めたい対象画像
  corners ----- すでに求まっている粗い精度のコーナー座標を入れ、高精度のコーナー座標を受け取る
  winSize ----- 探索窓の一辺の長さの半分、Size(5, 5)なら、範囲が11 x 11、試行錯誤で決める
  zeroZone ---- 一般には、Size(-1, -1)で良い
  criteria ------ 繰り返し探索を打ち切る条件
であり、戻り値はない。
◎drawChessboardCorners関数
 確認のために、チェスボード上のコーナーを描画する関数で、引数は、
  image ------------- コーナーを描画する対象画像
  patternSize --------- 描画するコーナーの数、Size(9, 6)など
  corners ----------- findChessboardCorners関数で得たcornersを与える
  patternWasFound ---- findChessboardCorners関数の戻り値を与える
であり、戻り値はない。
◎calibrateCamera関数
 キャリブレーションのメインの関数で、引数は、
  objectPoints ----- チェスボードのコーナーの三次元座標を、Point3f型のベクトルのベクトル(撮影画像枚数分)で与える
  imagePoints ----- 各撮影画像のコーナーの二次元座標を、Point2f型のベクトルのベクトル(撮影画像ごと)で与える
  imageSize ------- 撮影画像のピクセル数、Size(512, 384)など
  cameraMatrix ---- 焦点距離、光学中心などのMat型の3 x 3マトリックスを受け取る
  distCoeffs ------ 半径方向と接線方向の歪係数を5要素のMat型ベクトルで受け取る
  rvecs ---------- 各撮影画像ごとの回転マトリックスをMat型のベクトル(撮影画像ごと)で受け取る
  tvecs ---------- 各撮影画像ごとの並進ベクトルをMat型のベクトル(撮影画像ごと)で受け取る
  flags = 0   一般的にはデフォルトで良い
  criteria = TermCriteria(TermCriteria::COUNT + TermCriteria::EPS, 30, DBL_EPSILON) デフォルトで良い
であり、再射影誤差(re-projection error)をdouble型のピクセル値で返す。
◎undistort関数
 imgproc/Image Processing/Geometric Image Transformにある。半径方向と接線方向のレンズ歪を補正する。
 引数は、
  src ------------- 補正の対象となる入力画像
  dst ------------- 補正後の画像をここで受け取る(対応点がない場合は、黒で表示)
  cameraMatrix ----- カメラの内部パラメータを与える
  distCoeffs -------- カメラの内部パラメータを与える
  newCameraMatrix = noArray() ----- カメラマトリックスとして、別の値を用いる場合に使う。一般的には、デフォルトで良い
であり、戻り値はない。


図3 最初の部分で仕様を決め、原画像を読み込む



図4 撮影画像各々についてコーナーを検出し、imagePointsを取得する



図5 objectPointsを設定し、キャリブレーションを実施する



図6 参考までに内部パラメータを表示し、将来の利用のために格納しておく



図7 参考のために、rvecsとtvecsを表示する



図8 キャリブレーション結果を確認するために、補正を試みた


プログラムの実行結果
 図9は、コーナーを検出した結果の一例を表示したもので、左上がコーナー番号0になっている(コーナーの座標を確認した結果として分かった)。全ての撮影原画像で、コーナーが検出されていることを確認しておく。
 図10はキャリブレーション結果で、calibrateCamera関数の戻り値として得られるrms(root mean square)を、まず、表示してある。これは、再射影誤差(Re-projection error)で、得られたパラメータから逆算した原画像のピクセル位置と、実際の位置との差の二乗和を平均して平方根を取ったもので、単位はピクセルである。当初は、簡素なチェスボードを用いたため、この値が0.6程度あったが、精度(平面性も)を向上させたら、良くなった。cameraMatrixのデータは、fxとfyが良く合っており、中心点cxとcyは5%程度の誤差がある。distCoeffsのデータは、精度(特に平面性)良いチェスボードにすると、半径方向歪係数が格段に小さくなった。
 図11は、キャリブレーション結果として、内部パラメータをXMLに書き出したものを、メモ帳で見たものである。
 図12は、rvecsとtvecsの内容を確認した結果で、このままでは分かりにくいが、図13のチェスボードの写真と比較すると、角度、向き、位置などが反映していることが分かる。
 図14は、図9の原画像を、キャリブレーションで得たカメラ内部パラメータで補正した結果で、半径方向に、若干伸びていることが分かる。


図9 cboard3についてコーナーを検出した例



図10 各データの出力結果



図11 XMLファイルとして格納されたキャリブレーション結果



図12 rotation vector と translation vectorを出力したところ



図13 使用したチェスボードの写真



図14 cboard3に対して、undistort関数で歪を補正した例


結 論
 カメラキャリブレーション(較正)とは、カメラ固有の二つの内部パラメータである、カメラマトリックス(焦点距離と光学中心)と、歪係数ベクトル(半径方向歪と接線方向歪)を求めることである。
 較正の基準となるチェスボードは、なるべく、精度よく、かつ平面性を良くしておく必要がある。うっかりすると、チェスボードの歪を測定していることになりかねない。
 レンズの歪係数は、広角になるほど、大きくなる。ここでは、焦点距離35mm(35mmフィルム換算)を使用したので、あまり顕著な歪はなかった。



参考文献
 Z.Zhang,"A Flexible New Technique for Camera Calibration"(2000)
 http://research.microsoft.com/en-us/um/people/zhang/Papers/TR98-71.pdf



「Visual C++の勉強部屋」(目次)へ