5点アルゴリズムによるカメラ位置・姿勢の推定
2枚の画像間のカメラ位置・姿勢を推定する際に、それぞれの画像を撮影したカメラの内部パラメータが既知の場合には5点アルゴリズムが利用できる。OpenCVにも5点アルゴリズムでカメラ位置・姿勢を推定するための関数cv::findEssentialMatが用意されている。
使い方は、対応点の座標を以下のように指定するだけ。
ただし、デフォルトでは焦点距離1.0、画像中心座標(0, 0)で計算するようになっているので、下記のコードのように内部パラメータ行列を用いて座標変換を行うか焦点距離、画像中心座標を指定する必要がある。座標変換を行わずに利用したい場合は、焦点距離、画像中心座標を第3,4引数に設定すれば良い。
推定したEssential Matrixからカメラの並進と回転を求めるためには、cv::recoverPoseで並進ベクトルと回転ベクトルに分解することができる。
以下のように、2枚の画像から対応点を求めて、5点アルゴリズムでカメラ位置・姿勢を推定することができる。
使い方は、対応点の座標を以下のように指定するだけ。
cv::Mat essentialMat = cv::findEssentialMat(p1, p2);
ただし、デフォルトでは焦点距離1.0、画像中心座標(0, 0)で計算するようになっているので、下記のコードのように内部パラメータ行列を用いて座標変換を行うか焦点距離、画像中心座標を指定する必要がある。座標変換を行わずに利用したい場合は、焦点距離、画像中心座標を第3,4引数に設定すれば良い。
推定したEssential Matrixからカメラの並進と回転を求めるためには、cv::recoverPoseで並進ベクトルと回転ベクトルに分解することができる。
以下のように、2枚の画像から対応点を求めて、5点アルゴリズムでカメラ位置・姿勢を推定することができる。
#include <iostream>
#include <vector>
#include <opencv2/opencv.hpp>
#include <opencv2/nonfree.hpp>
int main(int argc, char** argv)
{
cv::initModule_nonfree(); //モジュールの初期化
//カメラパラメータの読み込みとレンズ歪の除去
cv::Mat img1; //入力画像1
cv::Mat img2; //入力画像2
cv::Mat K;
cv::Mat distCoeffs;
cv::FileStorage fs("camera.xml", CV_STORAGE_READ);
fs["intrinsicMat"] >> K;
fs["distCoeffs"] >> distCoeffs;
cv::undistort(cv::imread("image1.jpg"), img1, K, distCoeffs);
cv::undistort(cv::imread("image2.jpg"), img2, K, distCoeffs);
//特徴抽出
cv::Ptr<cv::FeatureDetector> detector = cv::FeatureDetector::create( "SURF" ); //検出器
cv::Ptr<cv::DescriptorExtractor> descriptorExtractor = cv::DescriptorExtractor::create( "SURF" ); //特徴量
cv::Ptr<cv::DescriptorMatcher> matcher = cv::DescriptorMatcher::create("BruteForce"); //対応点探索方法の設定
std::vector<cv::KeyPoint> keypoints1, keypoints2;
cv::Mat descriptor1, descriptor2;
detector->detect(img1, keypoints1);
descriptorExtractor->compute(img1, keypoints1, descriptor1);
detector->detect(img2, keypoints2);
descriptorExtractor->compute(img2, keypoints2, descriptor2);
//対応点の探索
std::vector<cv::DMatch> dmatch;
std::vector<cv::DMatch> dmatch12, dmatch21;
matcher->match(descriptor1, descriptor2, dmatch12); //img1 -> img2
matcher->match(descriptor2, descriptor1, dmatch21); //img2 -> img1
for (size_t i = 0; i < dmatch12.size(); ++i)
{
//img1 -> img2 と img2 -> img1の結果が一致しているか検証
cv::DMatch m12 = dmatch12[i];
cv::DMatch m21 = dmatch21[m12.trainIdx];
if (m21.trainIdx == m12.queryIdx)
dmatch.push_back( m12 );
}
//十分な数の対応点があれば基礎行列を推定
if (dmatch.size() > 5)
{
std::vector<cv::Point2d> p1;
std::vector<cv::Point2d> p2;
//対応付いた特徴点の取り出しと焦点距離1.0のときの座標に変換
for (size_t i = 0; i < dmatch.size(); ++i)
{
cv::Mat ip(3, 1, CV_64FC1);
cv::Point2d p;
ip.at<double>(0) = keypoints1[dmatch[i].queryIdx].pt.x;
ip.at<double>(1) = keypoints1[dmatch[i].queryIdx].pt.y;
ip.at<double>(2) = 1.0;
ip = K.inv()*ip;
p.x = ip.at<double>(0);
p.y = ip.at<double>(1);
p1.push_back( p );
ip.at<double>(0) = keypoints2[dmatch[i].trainIdx].pt.x;
ip.at<double>(1) = keypoints2[dmatch[i].trainIdx].pt.y;
ip.at<double>(2) = 1.0;
ip = K.inv()*ip;
p.x = ip.at<double>(0);
p.y = ip.at<double>(1);
p2.push_back( p );
}
cv::Mat essentialMat = cv::findEssentialMat(p1, p2);
std::cout << "Essential Matrix\n" << essentialMat << std::endl;
cv::Mat r, t;
cv::recoverPose(essentialMat, p1, p2, r, t);
std::cout << "R:\n" << r << std::endl;
std::cout << "t:\n" << t << std::endl;
}
return 0;
}
コメント
コメントを投稿