カメラはレンズを通して撮影するのですが、その場合どうしても映像がゆがんでしまいます。
直線を撮影しても少し歪むんです。
その歪みを治すためのプログラムが、元々OpenCVには入っています。
今回はそれを使ってみます。
歪みの直し方
歪はどのぐらい歪んでいるかを検出できれば直せます。上の画像のようにチェスボードの模様を歪を治したいカメラで撮影して、この直線がどのぐらい歪んでるかで補正することができるわけです。OpenCVにはチェスボード模様を使った補正関数が元々用意されています。
まずは複数枚撮影
まずは歪んでる写真を取りましょう。
チェスボード画像は
http://opencv.jp/sample/pics/chesspattern_7x10.pdf
からダウンロードできます。これを印刷しましょう。
そしてカメラで何度か撮影します。できればカメラは同じ位置の方が良いです。
その時に色々な位置・角度において、歪みをちゃんと検出できるようにします。
(ちなみにチェスボードを机から浮かせて撮影するのもありなようです)
さて、撮影したら(calibrationimage0.png calibrationimge1.png)といった連番で同じフォルダに保存しておきましょう。
取り込んでキャリブレーション
// デバッグウインドウを開く namedWindow("debugwindow", CV_WINDOW_AUTOSIZE); int horizonalCrossCount = 10; int verticalCrossCount = 7; // calibrate points vector<vector<Point3f>> object_points; vector<vector<Point2f>> image_points; vector<Point3f> obj; for(int j=0;j< horizonalCrossCount * verticalCrossCount;j++) { obj.push_back(Point3f(j/horizonalCrossCount, j%horizonalCrossCount, 0.0f)); } unsigned int numberOfImages = 7; for(unsigned int i=0; i<numberOfImages; i++) { char filename[32]; sprintf(filename, "/projects/desktop/Resources/calibration/debugimage%d.png", i); Mat frame = imread(filename); Mat gray; flip(frame, frame, -1); cvtColor(frame, gray, CV_BGR2GRAY); // 10-7 チェスを探す Size chessboardPatterns(horizonalCrossCount, verticalCrossCount); vector<Point2f> centers; bool found = findChessboardCorners(gray, chessboardPatterns, centers, CALIB_CB_ADAPTIVE_THRESH | CALIB_CB_NORMALIZE_IMAGE); if (found) { // 見つけたpointを高精度化する cornerSubPix(gray, centers, Size(11,11), Size(-1,-1), TermCriteria (TermCriteria::EPS+TermCriteria::COUNT, 30, 0.1)); object_points.push_back(obj); image_points.push_back(centers); // draw drawChessboardCorners(gray, chessboardPatterns, Mat(centers), true); imshow(_debugWindow, gray); } else { cout << "not found" << endl; } int key = waitKey(1); if (key == 'q') { break; } } // calibrate vector<Mat> rvecs, tvecs; Mat mtx(3,3, CV_64F); Mat dist(8,1, CV_64F); calibrateCamera(object_points, image_points, _captureSize, mtx, dist, rvecs, tvecs); unsigned int i=0; while(1) { Mat frame; _cap >> frame; flip(frame, frame, -1); if (++i > 10) { Mat undistorted; undistort(frame, undistorted, mtx, dist); imshow(_debugWindow, undistorted); if (i==20) { i=0; } } else { imshow(_debugWindow, frame); } int key = waitKey(1); if (key == 'q') { break; } }
実際の例を貼っておきます。
ファイルへのパスやなどは自分で修正してくださいね。
やっていることはさっき撮影した画像を1つずつとりだしては、
findChessboardCorners関数で7*10のチェスを探し出します。
見つけたらcornerSubPixで点の精度を上げてimage_points配列に保存しています。
この作業を全部の画像で出来たら
calibrateCamera
関数で補正値を取り出しています。
mtxとdistです。
ちなみにrevsとtvecsもこの関数から得られるものですが、mixとdistを計算するために内部で計算されたカメラの特性です。
この特性を元に「画像を実際に補正するための値」がmtxとdistになりますので、mtxとdistだけあれば良いわけです。
その下の無限ループでは実際にカメラの画像を取得しては表示しています。
ただし、10フレームごとに補正あり・なしを繰り返すので見てると補正されているのがわかると思います。
undistort関数にカメラの画像とmtxとdistを渡せば歪が補正された結果を取得できます。
mtxとdistの保存
一度計算したmtxとdistを保存したくなると思います。
mat構造体はymlファイルに書き出すことができます。
書き出しはこんな感じで。
FileStorage fs("/projects/calibration.yml", FileStorage::WRITE); fs << "mtx" << mtx; fs << "dist" << dist; fs.release();
読み出しはこんな感じで
FileStorage fs("/projects/calibration.yml", FileStorage::READ); Mat _camera_mtx, _camera_dist; fs["mtx"] >> _camera_mtx; fs["dist"] >> _camera_dist; fs.release();
この辺が参考になります
http://docs.opencv.org/3.1.0/dc/dbb/tutorial_py_calibration.html#gsc.tab=0