はじめに
本稿では、Visual C++を使ってStereo Camera Calibrationプログラムを作成した手法を Flow Chartで解説し、Source Code を開示しました。
このプログラムは2部で構成しています。第一部は校正に必要なサンプリング画像をリスト化して保存、第二部はその画像を使って校正します。
- 第一部:Chessboard Pattern の画像保存とリスト作成
- 第二部:画像リスト読み込みとステレオカメラ校正
第一部のサンプリングには ”Chessboard Pattern” を使っています。左右のカメラより、一対で2画像を撮影します。当然、画像には ”Chessboard Pattern” が切れることなく収まっていなければなりません。これは第一部実行中に確認します。これらの画像は、一度、ディスクに保存されて、第二部で使用されます。
第二部では、第一部で作成された画像リストを読み込み、Stereo Camera を校正します。このプログラムは「…//samples/cpp」フォルダー中の stereo_calib.cpp を流用する形で作成しています。特に第二部では「readStringList()」「StereoCalib()」等のファンクションを利用します。
第一部:Chessboard Patternの画像保存とリスト作成
第一部のフローは次の様になります。左右のカメラから画像を取得、”Chessboard Pattern” がその一対の画像に含まれているかを判断、含まれていれば、その画像を保存し、リストを作成します。
第二部:画像リスト読み込みとステレオカメラ校正
第二部は次の様になります。簡単に云うと、既存のファンクションを呼ぶだけです。第一部で作成された画像リストを読み込み、それより、Stereo Camera を校正します。
Stereo Camera Calibration の Source Code
以下は、Source Code の纏めです。ご参考にどうぞ。
void CMFC_OpenCV_StereoDoc::OnImageprocess2cameracalibration() { // TODO: ここにコマンド ハンドラー コードを追加します。 Size boardSize; bool showRectified = true; Settings s; const string inputSettingsFile = /*argc > 1 ? argv[1] :*/ "default.xml"; FileStorage fs(inputSettingsFile, FileStorage::READ); // Read the settings if (!fs.isOpened()) { MessageBoxW(0, _T("Could not open the configuration file"), _T("Error"), MB_OK); return /*-1*/; } fs["Settings"] >> s; if (!s.goodInput) { MessageBoxW(0, _T("Invalid input detected. Application stopping."), _T("Error"), MB_OK); return /*-1*/; } boardSize = s.boardSize; fs.release(); // close Settings file ////////////////////////////////////////////////////////////////////////// // // Start storing chessboard image files // /////////////////////////////////////////////////////////////////////////// /* Close all display windows */ cv::destroyAllWindows(); // In here, there must be routines to store 15 pairs of chessboard images. // These images must be in pairs. // These images must contain detected corners for calibration. bool found0; bool found1; vector<Point2f> corners0; vector<Point2f> corners1; cv::Mat cornerImage0; // Image frame cv::Mat cornerImage1; // Image frame // Input images from a camere VideoCapture cap0(0); VideoCapture cap1(1); // // The "for(;;)" loop searches for the chessboard corners. Exit from the loop when these are found or press "Esc" key. // int iw, jw; int nimagesw = 15; char c; for (iw = 0; iw < nimagesw; iw++) { string leftImage = "leftImage"; string rightImage = "rightImage"; char tempCount[15] = "0"; _itoa_s(iw, tempCount, 15, 10); for (jw = 0; jw < 200; jw++) { // Now "cap" is the image that shall be directed to "frame." cap0 >> frame0; cap1 >> frame1; cv::namedWindow("frame0", 1); // Name the "frame" window cv::namedWindow("frame1", 1); // Name the "frame" window cv::imshow("frame0", frame0); // Display the "frame" buffer. cv::imshow("frame1", frame1); // Display the "frame" buffer. cv::cvtColor(frame0, cornerImage0, COLOR_BGR2GRAY); // This line converts the color image "frame" to the grey scale image "frame0." cv::cvtColor(frame1, cornerImage1, COLOR_BGR2GRAY); // This line converts the color image "frame" to the grey scale image "frame1." // The command below finds the chess board corners. The numbers of corners (width and height in size) must be specified. Please refer to "OpenCV Reference Manual" or "Tutorial." found0 = findChessboardCorners(cornerImage0, boardSize, corners0, /*CALIB_CB_FAST_CHECK + */ CALIB_CB_NORMALIZE_IMAGE + CALIB_CB_ADAPTIVE_THRESH); found1 = findChessboardCorners(cornerImage1, boardSize, corners1, /*CALIB_CB_FAST_CHECK + */ CALIB_CB_NORMALIZE_IMAGE + CALIB_CB_ADAPTIVE_THRESH); /* Exit from loop if chessboard corners are found. */ if ((found0) && (found1)) { /* Close all display windows */ cv::destroyAllWindows(); // The function below draws the chessboard corners which are found on the "frame" image. Be careful this command does not display the "frame" image. // Draw and save the leftImage drawChessboardCorners(cornerImage0, boardSize, corners0, found0); leftImage.append(tempCount); leftImage.append(".jpg"); cv::namedWindow(leftImage, 1); // Name the "frame0" window. cv::imshow(leftImage, cornerImage0); // Now, I display "frame0" image. imwrite(leftImage, frame0); leftImage = "leftImage"; /* The line below is required for OpenCV camera image display. */ if (waitKey(30) >= 0) break; // Draw and save the rightImage drawChessboardCorners(cornerImage1, boardSize, corners1, found1); rightImage.append(tempCount); rightImage.append(".jpg"); cv::namedWindow(rightImage, 1); // Name the "frame1" window. cv::imshow(rightImage, cornerImage1); // Now, I display "frame1" image. imwrite(rightImage, frame1); rightImage = "rightImage"; /* The line below is required for OpenCV camera image display. */ if (waitKey(30) >= 0) break; c = (char)waitKey(1000); if (c == 27) break; break; } //End of "if ((found0) && (found1))" /* Press "Esc" key to exit from this loop. */ if (waitKey(27) >= 0) break; } // End of "for (jw = 0; jw < 200; jw++)" loop if (jw >= 100) MessageBoxW(0, _T("Corners could not detected"), _T("Error"), MB_OK); } // End of "for (iw = 0; iw < nimagesw; iw++)" /* Release image buffers */ cap0.release(); cap1.release(); ///////////////////////////////////////////////////////////////////////// // // End of storing chessboard image files // ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// // // Start Stereo Calibration // /////////////////////////////////////////////////////////////////////////// const string imagelistfn = "stereo_calib0.xml"; vector<string> imagelist; imagelist.clear(); fs.open(imagelistfn, FileStorage::READ); if (!fs.isOpened()){ MessageBox(0, _T("Can not open imageList.xml"), _T("Error"), MB_OK); return/* false*/; } ////////////////////////////////////////////////////////////////// // Please refer to "calibration.cpp" in sample directory. readStringList(imagelistfn, imagelist); // Though I listed above line, it did not work here. // The last resort I chose was that I copied above function's contents; and it worked perfectly. // (残念ですが「readStringList(imagelistfn, imagelist);」が正常に機能しなかったため、そのプログラム内容をここにコピーして使いました。申し訳ない。) //////////////////////////////////////////////////////////////////// MessageBox(0, _T("Opened imageList.xml"), _T("Working"), MB_OK); if (imagelist.empty()) { MessageBox(0, _T("can not open Image List or the string list is empty"), _T("Error"), MB_OK); return; } // Please refer to "calibration.cpp" in sample directory. StereoCalib(imagelist, boardSize, true, showRectified); return /*0*/; } ///////////////////////////////////////////////////////////////////////// // // End of Stereo Calibration // //////////////////////////////////////////////////////////////////////////
まとめ
如何でしたか。本稿ではVisual C++を使ってStereo Camera Calibrationプログラムを作成した手法を Flow Chart で解説し、Source Code を開示しています。最後までお読みいただき、ありがとうございました。