About BBS

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

作成者: 怡土順一, 最終変更者: 怡土順一, 最終変更リビジョン: 357, 最終変更日時: 2007-12-26 14:22:07 +0900 (水, 26 12月 2007)

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

カメラキャリブレーションとは,(ある時点において)カメラ固有の内部パラメータと, ワールド座標系における位置姿勢を意味する外部パラメータを求める処理である. カメラのキャリブレーションがなされると,ある3次元座標を持った点がカメラ画像のどこに投影されるか, あるいは複数のカメラに投影された点が3次元空間中のどこにあるか,などが計算できる. また,カメラ特有の(円周方向および半径方向)歪みの補正を行うこともでき る(が,これはキャリブレーション手法に依存し,歪みを持たないモデルを利用する単純な手法も存在する). 適応できるキャリブレーション手法は,カメラの台数や用意できる治具によって変化する. OpenCVのカメラキャリブレーションは,Z.Zhangの手法を基に実装されている (Zhangの手法については,"A flexible new technique for camera calibration". IEEE Transactions on Pattern Analysis and Machine Intelligence, 22(11):1330-1334, 2000.を参照されたい). Ver.2から(cvCalibrateCamera2など)は,Matlabのキャリブレーションエンジン(これもZhangの手法)を移植したものになった.

サンプル


カメラキャリブレーション cvCalibrateCamera2, cvFindExtrinsicCameraParams2

Zhangの手法を用いてカメラのキャリブレーションを行い,結果をファイルに保存する

サンプルコード

#include <stdio.h> #include <cv.h> #include <highgui.h> #define IMAGE_NUM (10) /* 画像数 */ #define PAT_ROW (6) /* パターンの行数 */ #define PAT_COL (8) /* パターンの列数 */ #define PAT_SIZE (PAT_ROW*PAT_COL) #define ALL_POINTS (IMAGE_NUM*PAT_SIZE) #define CHESS_SIZE (35.0) /* パターン1マスの1辺サイズ[mm] */ int main (int argc, char *argv[]) { int i, j, k; int corner_count, found; int p_count[IMAGE_NUM]; IplImage *src_img[IMAGE_NUM]; CvSize pattern_size = cvSize (PAT_ROW, PAT_COL); CvPoint3D32f objects[ALL_POINTS]; CvPoint2D32f *corners = (CvPoint2D32f *) cvAlloc (sizeof (CvPoint2D32f) * ALL_POINTS); CvMat object_points; CvMat image_points; CvMat point_counts; CvMat *intrinsic = cvCreateMat (3, 3, CV_32FC1); CvMat *rotation = cvCreateMat (1, 3, CV_32FC1); CvMat *translation = cvCreateMat (1, 3, CV_32FC1); CvMat *distortion = cvCreateMat (1, 4, CV_32FC1); // (1)キャリブレーション画像の読み込み for (i = 0; i < IMAGE_NUM; i++) { char buf[32]; sprintf (buf, "http://opencv.jp/sample/calib_img/%02d.png", i); src_img[i] = cvLoadImage (buf, CV_LOAD_IMAGE_COLOR); } // (2)3次元空間座標の設定 for (i = 0; i < IMAGE_NUM; i++) { for (j = 0; j < PAT_COL; j++) { for (k = 0; k < PAT_ROW; k++) { objects[i * PAT_SIZE + j * PAT_ROW + k].x = j * CHESS_SIZE; objects[i * PAT_SIZE + j * PAT_ROW + k].y = k * CHESS_SIZE; objects[i * PAT_SIZE + j * PAT_ROW + k].z = 0.0; } } } cvInitMatHeader (&object_points, ALL_POINTS, 3, CV_32FC1, objects); // (3)チェスボード(キャリブレーションパターン)のコーナー検出 int found_num = 0; cvNamedWindow ("Calibration", CV_WINDOW_AUTOSIZE); for (i = 0; i < IMAGE_NUM; i++) { found = cvFindChessboardCorners (src_img[i], pattern_size, &corners[i * PAT_SIZE], &corner_count); if (found) found_num++; // (4)コーナー位置をサブピクセル精度に修正,描画 IplImage *src_gray = cvCreateImage (cvGetSize (src_img[i]), IPL_DEPTH_8U, 1); cvCvtColor (src_img[i], src_gray, CV_BGR2GRAY); cvFindCornerSubPix (src_gray, &corners[i * PAT_SIZE], corner_count, cvSize (3, 3), cvSize (-1, -1), cvTermCriteria (CV_TERMCRIT_ITER | CV_TERMCRIT_EPS, 20, 0.03)); cvDrawChessboardCorners (src_img[i], pattern_size, &corners[i * PAT_SIZE], corner_count, found); p_count[i] = corner_count; cvShowImage ("Calibration", src_img[i]); cvWaitKey (0); } cvDestroyWindow ("Calibration"); if (found_num != IMAGE_NUM) return -1; cvInitMatHeader (&image_points, ALL_POINTS, 1, CV_32FC2, corners); cvInitMatHeader (&point_counts, IMAGE_NUM, 1, CV_32SC1, p_count); // (5)内部パラメータ,歪み係数の推定 cvCalibrateCamera2 (&object_points, &image_points, &point_counts, cvSize (640, 480), intrinsic, distortion); // (6)外部パラメータの推定 CvMat sub_image_points, sub_object_points; int base = 0; cvGetRows (&image_points, &sub_image_points, base, base + PAT_SIZE); cvGetRows (&object_points, &sub_object_points, base, base + PAT_SIZE); cvFindExtrinsicCameraParams2 (&sub_object_points, &sub_image_points, intrinsic, distortion, rotation, translation); // (7)XMLファイルへの書き出し CvFileStorage *fs; fs = cvOpenFileStorage ("http://opencv.jp/sample/camera.xml", 0, CV_STORAGE_WRITE); cvWrite (fs, "intrinsic", intrinsic); cvWrite (fs, "rotation", rotation); cvWrite (fs, "translation", translation); cvWrite (fs, "distortion", distortion); cvReleaseFileStorage (&fs); for (i = 0; i < IMAGE_NUM; i++) { cvReleaseImage (&src_img[i]); } return 0; }

// (1)キャリブレーション画像の読み込み
キャリブレーションに必要な画像列を読み込む.ここでは,calib_img 以下に あらかじめ用意してある,"00.png"〜"09.png"という名前の画像ファイルを利用した. zhangのキャリブレーションに必要な画像枚数(ビューの数)は決まっていないが, 一般的には,ぶれなどのない適切な画像を数枚程度用意すると十分な精度が得られる.

// (2)3次元空間座標の設定
今回は,1マスの長さが35[mm],内側のコーナー数が8×6のチェスボードパターンを利用した. xおよびyの値は,パターンのマスのサイズにあわせて値を代入する. ここで設定される軸方向は,カメラの外部パラメータに影響を与える. また,zはすべて0または1に設定する必要がある.

// (3)チェスボード(キャリブレーションパターン)のコーナー検出
関数cvFindChessboardCorners()により,チェスボードのコーナーを検出する. コーナーは左下から右上に向けて検出され,第2引数で設定されたサイズのコーナーが全て検出された場合には,0以外の値を返す. このサンプルコードでは,全ての画像(10枚)でコーナーが正しく検出されない場合は,プログラムが終了する.
ちなみに,本プログラムとはあまり関係ないが,2枚以上のキャリブレーションパターンを認識させたい場合は, (縦横の矩形数が)異なるタイプのキャリブレーションパターンを用意する, あるいは,一つのパターンを検出する度にそこを塗りつぶして再探索する,などの手法が考えられる

// (4)コーナー位置をサブピクセル精度に修正,描画
関数cvFindChessboardCorners()においても,0.5[pixel]単位でコーナーが検出されるが, さらに検出されたコーナーをサブピクセル精度で修正する. また,それらのコーナーを画像上に描画し,実際に表示する. すべてのコーナーが正しく検出された場合は内部で定義された7色で描画され,そうでない場合はすべてのコーナーが赤色で描画される.

// (5)内部パラメータ,歪み係数の推定
関数cvCalibrateCamera2()により,カメラの内部パラメータ,および歪み係数を推定する. 内部パラメータは,以下の式のAにあたる.

s\cdot{\bf m} = {\bf A}\cdot[{\bf R}|{\bf t}]\cdot{\bf M}

// (6)外部パラメータの推定
カメラの内部パラメータを推定する.これは,上述の式の[R|t]にあたる. 原点からの回転および並進を表す外部パラメータを決定するためには,基準となるワールド座標系原点が必要となる. ここでは,base=0として0番目の画像("00.png")を指定しており,この画像に写るパターンの3次元座標系((2)で与えられた)を基準にパラメータが推定される.

// (7)XMLファイルへの書き出し
求められた,内部パラメータ,外部パラメータ,歪み係数をファイルに書き出す. ここでは,拡張子によりXML形式を指定しているが,YAML形式で出力することも可能である.

実行結果例

カメラキャリブレーションに用いた画像の一部の認識結果. 実際のキャリブレーション結果ファイル(XML). このキャリブレーション結果は,VFW-VL500(sony)に広角レンズVCL-00637S(sony)を取り付けたもので行った.


歪み補正 cvUndistort2

キャリブレーションデータを利用して,歪みを補正する

サンプルコード

#include <cv.h> #include <highgui.h> int main (int argc, char *argv[]) { IplImage *src_img, *dst_img; CvMat *intrinsic, *distortion; CvFileStorage *fs; CvFileNode *param; // (1)補正対象となる画像の読み込み if (argc < 2 || (src_img = cvLoadImage (argv[1], CV_LOAD_IMAGE_COLOR)) == 0) return -1; dst_img = cvCloneImage (src_img); // (2)パラメータファイルの読み込み fs = cvOpenFileStorage ("http://opencv.jp/sample/camera.xml", 0, CV_STORAGE_READ); param = cvGetFileNodeByName (fs, NULL, "intrinsic"); intrinsic = (CvMat *) cvRead (fs, param); param = cvGetFileNodeByName (fs, NULL, "distortion"); distortion = (CvMat *) cvRead (fs, param); cvReleaseFileStorage (&fs); // (3)歪み補正 cvUndistort2 (src_img, dst_img, intrinsic, distortion); // (4)画像を表示,キーが押されたときに終了 cvNamedWindow ("Distortion", CV_WINDOW_AUTOSIZE); cvShowImage ("Distortion", src_img); cvNamedWindow ("UnDistortion", CV_WINDOW_AUTOSIZE); cvShowImage ("UnDistortion", dst_img); cvWaitKey (0); cvDestroyWindow ("Distortion"); cvDestroyWindow ("UnDistortion"); cvReleaseImage (&src_img); cvReleaseImage (&dst_img); cvReleaseMat (&intrinsic); cvReleaseMat (&distortion); return 0; }

// (1)補正対象となる画像の読み込み
補正を行う画像を読み込む.もちろん,補正対象となる画像を撮影したカメラは,キャリブレーション済みであるとする.

// (2)パラメータファイルの読み込み
キャリブレーション結果を格納したパラメータファイルを読み込む. 歪み補正に必要なパラメータは,内部パラメータ,および歪み係数だけなので,これをファイルから読み込む.

// (3)歪み補正
関数cvUndisotort2()により,入力画像の歪み補正をおこなう.

// (4)画像を表示,キーが押されたときに終了
歪み補正前画像と歪み補正後画像を表示し,なにかキーが押されるまで待つ.

実行結果例

[左]歪み補正前. [右]歪み補正後.


マップを利用した歪み補正 cvInitUndistortMap

キャリブレーションデータを利用してマップを作成し,歪みを補正する

サンプルコード

#include <cv.h> #include <highgui.h> int main (int argc, char *argv[]) { IplImage *src_img, *dst_img; CvMat *intrinsic, *distortion; CvFileStorage *fs; CvFileNode *param; IplImage *mapx = cvCreateImage (cvSize (640, 480), IPL_DEPTH_32F, 1); IplImage *mapy = cvCreateImage (cvSize (640, 480), IPL_DEPTH_32F, 1); if (argc < 2 || (src_img = cvLoadImage (argv[1], CV_LOAD_IMAGE_COLOR)) == 0) return -1; dst_img = cvCloneImage (src_img); fs = cvOpenFileStorage ("http://opencv.jp/sample/camera.xml", 0, CV_STORAGE_READ); param = cvGetFileNodeByName (fs, NULL, "intrinsic"); intrinsic = (CvMat *) cvRead (fs, param); param = cvGetFileNodeByName (fs, NULL, "distortion"); distortion = (CvMat *) cvRead (fs, param); cvReleaseFileStorage (&fs); // (1)歪み補正のためのマップ初期化 cvInitUndistortMap (intrinsic, distortion, mapx, mapy); // (2)歪み補正 cvRemap (src_img, dst_img, mapx, mapy); cvNamedWindow ("Distortion", CV_WINDOW_AUTOSIZE); cvShowImage ("Distortion", src_img); cvNamedWindow ("UnDistortion", CV_WINDOW_AUTOSIZE); cvShowImage ("UnDistortion", dst_img); cvWaitKey (0); cvDestroyWindow ("Distortion"); cvDestroyWindow ("UnDistortion"); cvReleaseImage (&src_img); cvReleaseImage (&dst_img); cvReleaseImage (&mapx); cvReleaseImage (&mapy); cvReleaseMat (&intrinsic); cvReleaseMat (&distortion); return 0; }

// (1)歪み補正のためのマップ初期化
関数cvInitUndistortMap()を用いて,歪みを補正するためのマップ mapx,mapy の初期化を行う.

// (2)歪み補正
作成されたマップを用いて,画像の幾何変換(cvRemap)を行う. 関数cvUndistort2()を利用する場合と比べて,計算速度が向上する. しかし,一般的な幾何変換を行う関数を利用しているためか,以前のバージョンで実装されていた歪み補正関数に比べてパフォーマンスが劣る. もちろん,以前の関数 cvUnDistortInit,cvUnDistort も cvcompat.h 内で宣言されているが, 将来的に廃止される予定なので利用は推奨されない (またコメントによれば,これらの関数は,quite hackerish implementations である). 単純に歪み補正の速度だけを求めるならば, Yahoo!Groupsの投稿(Speeding up undistort)にもあるように,自分でLUTを作成すれば良い. その場合は,画像の補完を別途行う必要がある.

実行結果例

[左]歪み補正前. [右]歪み補正後.

OpenCVリファレンス マニュアル
OpenCVリファレンス マニュアル(分割版)
OpenCVサンプルコード


ピクセルデータへの直接アクセス
部分画像のシャッフル
画像の連結
画像のコピー
画像形状の変形
タイリング
画像の反転
離散フーリエ変換
階層構造を持つ輪郭の座標取得
図形の描画
ポリゴンの描画
凸ポリゴンの描画
テキストの描画
IplImage構造体情報の保存
マップのシーケンスを保存
IplImage構造体情報の読み込み
マップのシーケンスを読み込む
K-means法によるクラスタリング
クラスタリングによる減色処理
エッジの検出
コーナーの検出
並進移動のためのピクセルサンプリング
回転移動のためのピクセルサンプリング
画像のサイズ変更
画像のアフィン変換(1)
画像のアフィン変換(2)
画像の透視投影変換
モルフォロジー変換
平滑化
ユーザ定義フィルタ
境界線の作成
画像の二値化
画像の二値化(大津の手法)
画像ピラミッドの作成
画像ピラミッドを用いた画像の領域分割
平均値シフト法による画像のセグメント化
Watershedアルゴリズムによる画像の領域分割
輪郭の検出と描画
画像のモーメントを計算
ハフ変換による直線検出
ハフ変換による円検出
距離変換とその可視化
不要オブジェクトの除去
ヒストグラムの描画
ヒストグラム間の距離
二次元のヒストグラム
バックプロジェクションパッチ
ヒストグラムの均一化
テンプレートマッチング
形状のマッチング
点列を包含する矩形
輪郭領域の面積と輪郭の長さ
二つの矩形を包含する矩形
楕円のフィッティング
点列を包含する図形
動的背景更新による物体検出
snakeによる輪郭追跡(静止画)
オプティカルフロー1
オプティカルフロー2
オプティカルフロー3
Condensation
顔の検出
カメラキャリブレーション
歪み補正
マップを利用した歪み補正
サポートベクターマシン
画像の各ピクセル値を特徴ベクトルとしたSVMの学習
画像の各ピクセル値を特徴ベクトルとしたSVMによる物体検出
マウスイベントの取得
トラックバーの利用
カメラからの画像キャプチャ
動画としてファイルへ書き出す
ラベリング