3D Calibration

How it’s done: Stereo Camera Calibration.

はじめに

本稿では、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” がその一対の画像に含まれているかを判断、含まれていれば、その画像を保存し、リストを作成します。

第一部:Storing chessboard image files

第二部:画像リスト読み込みとステレオカメラ校正

第二部は次の様になります。簡単に云うと、既存のファンクションを呼ぶだけです。第一部で作成された画像リストを読み込み、それより、Stereo Camera を校正します。

第二部:Stereo Calibration routine

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 を開示しています。最後までお読みいただき、ありがとうございました。

返信を残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です