| カメラキャリブレーション (camera calibration) | 
    
            
        
            | 
                2017-03-04 - 2018-05-30 (update)             | 
              | 
            
                                             | 
        
    
 
                 
    
        
    
        
            
            
            
         
        
*カメラキャリブレーションとは
 カメラの内部パラメータと,場合によってはさらに外部パラメータを求める処理を指します.内部パラメータとは,カメラの個体ごとに異なる焦点距離や画像中心のずれ,レンズ歪みの具合などを表すパラメータです.外部パラメータとは,カメラの位置姿勢を表すパラメータです.これらのパラメータを事前に求めておくことで,画像から幾何的な情報を正確に計算できるようになります.
{{small:カメラの内部パラメータについては,[link:カメラモデル]を参照してください.}} 
{{small:外部パラメータについては,複数のカメラを想定するユースケースでカメラの相対的な位置姿勢を知るために推定する場合が多いです.}} 
{{small:カメラキャリブレーションは,色の感度について推定するカラーキャリブレーションを含める場合もあります.}} 
 Zhang[1]では,平面ボードを使ったカメラキャリブレーションの方法が解説されています.平面ボードを使ったキャリブレーションは,(恐らくボードを用意しやすい都合から)一般的には最も良く利用されている方法だと思います.
 図1は,キャリブレーションに利用するボードの例です.カメラキャリブレーションは,このボード上の特徴(図1の例では円)の間隔や歪み具合を手掛かりにして計算します.
[img:xzc7]
{{small:図1 キャリブレーションボードの例}}
{{small:[1] Zhang.Z, “A Flexible New Technique for Camera Calibration”, IEEE Transactions on Pattern Analysis and Machine Intelligence. Vol. 22, No. 11, 2000, pp. 1330–1334. 
[link:http://citeseerx.ist.psu.edu/viewdoc/download?rep=rep1&type=pdf&doi=10.1.1.220.534] }} 
*キャリブレーション用の画像撮影のポイント
・ボードの見え方を変えて複数撮影する
{{small:同じ見え方の画像を何枚も撮影してもほとんど意味がありません.角度や位置のバリエーションを変えて撮影してください.}} 
・ピンボケやブラーはできるだけ抑える
{{small:円模様の検出精度が落ちるような条件では,キャリブレーションの精度も悪くなります.}} 
*サンプルコード①
OpenCVを利用したサンプルコードです.
図1に示す円模様のボードをUSBカメラで何枚か撮影し,キャリブレーションを行います.
下記コード中のcamMatとdistはカメラの内部パラメータと歪みの結果を格納する行列です.
結果を保存したい場合は,cv::format関数等を利用してCSVファイルなどで出力してください.{{small:(参考:[link:OpenCV Mat メモ])}}
{#
#include <vector>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main() {
    // マーカーの設計値 円の個数5x5 円の間隔30.0[mm]
    const Size bsize(5, 5);
    const double bdistance = 30.0;
    // マーカーの点の座標を設定
    vector<Point3f> grid;
    for (int y = 0; y < bsize.height; y++) {
        for (int x = 0; x < bsize.width; x++) {
            grid.push_back(Point3f(x, y, 0.0f) * bdistance);
        }
    }
    VideoCapture cap(0);
    if (cap.isOpened() == false) {
        printf("could not find USB camera\n");
        exit(0);
    }
    printf("'a' key : add detected points for calibration\n");
    printf("'c' key : execute calibration (i >= 3) \n");
    int key = 0;
    // 27 = 'ESC' key
    while ((key = waitKey(1)) != 27) {
        // capture
        Mat img;
        cap >> img;
        // 画像上からボードの円模様を検出する
        vector<Point2f> pointbuf;
        const bool found = findCirclesGrid(img, bsize, pointbuf);
        if (found == true) {
            // 検出した位置を画像上に描画する
            drawChessboardCorners(img, bsize, pointbuf, found);
            static int cnt = 0;
            static vector<vector<Point2f> > pixsList;
            static vector<vector<Point3f> > objsList;
            // 検出した点をキャリブレーション用のデータに追加する
            if (key == 'a') {
                printf("add detected points (i = %d)\n", cnt++);
                pixsList.push_back(pointbuf);
                objsList.push_back(grid);
            }
            // これまで追加したデータを使って,キャリブレーションを行う
            if (key == 'c' && cnt >= 3) {
                Mat camMat, dist;
                vector<Mat> rvecs, tvecs;
                const double rms = calibrateCamera(objsList, pixsList, img.size(), camMat, dist, rvecs, tvecs);
                printf("opencv rms error: %g\n", rms);
                cout << "camMat = " << endl << camMat << endl;
                cout << "dist = " << endl << dist << endl;
            }
        }
        imshow("calibration", img);
    }
    destroyAllWindows();
    return 0;
}
#}
*サンプルコード② (C++)
ライブラリ:[link:simplesp]
サンプルコード:simplesp/sample/cv/dotmarker
図1に示すドット模様のボードをWebカメラで何枚か撮影することで,カメラの内部パラメータを推定します.
ドット模様の画像はsimplesp/data/markerにあります.印刷するかモニタに表示した状態で撮影してください.
        
 
        
>> ご意見・ご質問など お気軽にご連絡ください.info