基于opencv3的本质矩阵(essential matrix)估计算例
2016-08-18 14:12
274 查看
至今opencv3面世快两年了,五一期间抽空看了下新增的几个关于双目视觉标定算法的使用。
关于双目的标定有一类方法是基于本质矩阵(Essential matrix)的标定方法,本质方程与基础方程都是刻画双目视觉极几何关系的两个方程,只不过它们所使用的坐标系不一样而已,本质方程是在相机光心坐标系下描述的双目成像几何关系,而基础方程是本质方程在相机像素坐标系下的表示。
由于本质矩阵是平移量t叉乘旋转矩阵R的结果。所以通过本质方程估计出本质矩阵,然后再对本质矩阵进行运动分解,即可得到双目摄像头之间的相互位置关系,即双目的标定。值得一提的是,由于本质矩阵的秩为2,分解出来的平移向量t只能得到方向,其模需要使用其它方法来获取。
理论上可以直接使用估计基础矩阵(Fundamental matrix)的方法来估计本质矩阵,如:8点算法,7点算法,最小中值法,RANSAC法等。但是实际中使用这些方法估计出的本质矩阵的结果并不太令人满意,尤其当匹配点对趋于共面时,这些方法获得的结果完全就不能用。如果用于标定的匹配点对,是在一个平面上的话,这些方法将完全失效,因为平面是一种退化结构或者叫临界曲线(Critical surfaces)。而且在opencv3之前的opencv版本只提供了求取基础矩阵F的API,虽然cv::stereoCalibrate(...)可以得到双目标定结果以及本质矩阵E,但该方法是使用平面标定版辅助的plane2plane估计出各相机的外参数,然后由外参数直接计算出旋转矩阵R和平移量t,此时获取的刚体运动参数自由度为6。
此外双目标定还有一些方法并不是基于本质矩阵来完成的,如:光束法平差(Bundle adjustment)、结构重投影法等。在此就不进行讨论了。
由于基础矩阵的自由度为7,而本质矩阵的自由度为5。直接使用基础矩阵的估计方法来估计本质矩阵并不适合,而且本质矩阵还有一种叫做5点算法的方法可以估计本质矩阵,该算法比较复杂,涉及到高次多项式方程的求解。但是对于估计本质矩阵,5点算法会得到比7点或8点算法更好的结果,而且不受匹配点集共面的影响。该方法的具体原理可见论文David
Nistér. An efficient solution to the five-point relative pose problem. Pattern Analysis and Machine Intelligence, IEEE Transactions on,
26(6):756–770, 2004.和Henrik Stewenius. Calibrated fivepoint solver.
5点算法的大概思路就是把本质方程展开,再加上本质矩阵的充要条件。化简后得到一个高次多项式方程,其实数解中的一个确定出的本质矩阵是系统的真实本质矩阵,对伪解进行剔除。然后对获得的本质矩阵进行使用SVD分解而得到旋转矩阵R和单位平移量t,即完成双目的相互位置标定。
opencv3中的cv::findEssentialMat(...)即是基于该五点算法实现的本质矩阵的估计,附上使用代码
[cpp] view
plain copy
/**************************************************************
* an example to estimate essential matrix and relative pose of
* binocular vision sensor using the API function of opencv3.0.0
*
* by jah10527@126.com
* May, 1st, 2016
*
***************************************************************/
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
static void calcChessboardCorners(Size boardSize, float squareSize, vector<Point3f>& corners)
{
corners.resize(0);
for( int i = 0; i < boardSize.height; i++ )
for( int j = 0; j < boardSize.width; j++ )
corners.push_back(Point3f(float(j*squareSize),
float(i*squareSize), 0));
}
int main()
{
int i, point_count = 54;
vector<Point2f> rpoints(point_count);
vector<Point2f> lpoints(point_count);
Mat Ml(3,3,CV_64F);
Mat Mr(3,3,CV_64F);
FileStorage fs("camera_l.yml", FileStorage::READ);
if( !fs.isOpened() )
return -1;
fs["camera_matrix"] >> Ml;
fs.release();
fs.open("camera_r.yml", FileStorage::READ);
if( !fs.isOpened() )
return -1;
fs["camera_matrix"] >> Mr;
fs.release();
cout << "Mr:" << endl << Mr << endl;
cout << "Ml:" << endl << Ml << endl;
Mat rim=imread("fight01.jpg", IMREAD_GRAYSCALE);
Mat lim=imread("feft01.jpg", IMREAD_GRAYSCALE);
findChessboardCorners( rim, Size(6,9), rpoints,
CALIB_CB_ADAPTIVE_THRESH | CALIB_CB_FAST_CHECK | CALIB_CB_NORMALIZE_IMAGE);
findChessboardCorners( lim, Size(6,9), lpoints,
CALIB_CB_ADAPTIVE_THRESH | CALIB_CB_FAST_CHECK | CALIB_CB_NORMALIZE_IMAGE);
/* cout << "right:" << endl;
for (i=0;i<point_count;i++)
cout << rpoints[i] << endl;
cout << "left:" << endl;
for (i=0;i<point_count;i++)
cout << lpoints[i] << endl;*/
cornerSubPix( rim, rpoints, Size(8,8),
Size(-1,-1), TermCriteria( TermCriteria::EPS+TermCriteria::COUNT, 30, 0.1 ));
cornerSubPix( lim, lpoints, Size(8,8),
Size(-1,-1), TermCriteria( TermCriteria::EPS+TermCriteria::COUNT, 30, 0.1 ));
for (i=0;i<point_count;i++)
{
rpoints[i].x = (rpoints[i].x-*((double*)Mr.data+2))/ *((double*)Mr.data);
rpoints[i].y = (rpoints[i].y-*((double*)Mr.data+5))/ *((double*)Mr.data+4);
lpoints[i].x = (lpoints[i].x-*((double*)Ml.data+2))/ *((double*)Ml.data);
lpoints[i].y = (lpoints[i].y-*((double*)Ml.data+5))/ *((double*)Ml.data+4);
// cout << "r:" << rpoints[i] << endl;
// cout << "l:" << lpoints[i] << endl;
}
Mat E_mat = findEssentialMat(rpoints, lpoints, 1, Point2d(0, 0), RANSAC, 0.999, 1.f);
cout << "E:" << endl << E_mat << endl;
/* Mat rr(3,1,CV_64F);
Mat ll(1,3,CV_64F);
*((double*)rr.data+2)=1;
*((double*)ll.data+2)=1;
for (i=0;i<8;i++){
*((double*)rr.data) = rpoints[i].x;
*((double*)rr.data+1) = rpoints[i].y;
*((double*)ll.data) = lpoints[i].x;
*((double*)ll.data+1) = lpoints[i].y;
cout << ll*E_mat*rr << endl;
cout << rr.t()*E_mat*ll.t() << endl;
}
Mat R1, R2, t;
decomposeEssentialMat(E_mat, R1, R2, t);
cout << "R1:" << endl << R1 << endl;
cout << "R2:" << endl << R2 << endl;
cout << "t:" << endl << t << endl;*/
Mat R, t;
recoverPose(E_mat, rpoints, lpoints, R, t);
cout << "t:" << endl << t << endl;
cout << "R:" << endl << R << endl;
Mat om;
Rodrigues(R, om);
cout << "om:" << endl << om << endl;
vector<vector<Point3f> > objpt(1);
vector<vector<Point2f> > imgpt(1);
vector<vector<Point2f> > imgpt_right(1);
for (i=0;i<point_count;i++)
{
imgpt[0].push_back(rpoints[i]);
imgpt_right[0].push_back(lpoints[i]);
}
calcChessboardCorners(Size(6,9), 0.03, objpt[0]);
Mat RR, TT, E, F;
Mat eye3, zeros5;
eye3 = Mat::eye(3,3,CV_64F);
zeros5 = Mat::zeros(1,5,CV_64F);
double err = stereoCalibrate(objpt, imgpt, imgpt_right, eye3, zeros5, eye3, zeros5,
Size(640,480), RR, TT, E, F, CALIB_FIX_INTRINSIC,
TermCriteria( TermCriteria::EPS+TermCriteria::COUNT, 30, 0.1 ));
cout << "Pair calibration reprojection error = " << err << endl;
Rodrigues(RR, om);
cout << "TT:" << endl << TT << endl;
cout << "RR:" << endl << RR << endl;
cout << "om:" << endl << om <<endl;
cout << "E:" << endl << E << endl;
return 0;
}
可以看出两种标定方法获取的结果不太一样。
重构出的标定版的三维坐标如下图所示,红色为本质矩阵方法获得的标定版空间情况,蓝色为stereoCalibrate(...)的运动参数三维重构标定版角点坐标的结果。
源码及数据下载点击打开链接
关于双目的标定有一类方法是基于本质矩阵(Essential matrix)的标定方法,本质方程与基础方程都是刻画双目视觉极几何关系的两个方程,只不过它们所使用的坐标系不一样而已,本质方程是在相机光心坐标系下描述的双目成像几何关系,而基础方程是本质方程在相机像素坐标系下的表示。
由于本质矩阵是平移量t叉乘旋转矩阵R的结果。所以通过本质方程估计出本质矩阵,然后再对本质矩阵进行运动分解,即可得到双目摄像头之间的相互位置关系,即双目的标定。值得一提的是,由于本质矩阵的秩为2,分解出来的平移向量t只能得到方向,其模需要使用其它方法来获取。
理论上可以直接使用估计基础矩阵(Fundamental matrix)的方法来估计本质矩阵,如:8点算法,7点算法,最小中值法,RANSAC法等。但是实际中使用这些方法估计出的本质矩阵的结果并不太令人满意,尤其当匹配点对趋于共面时,这些方法获得的结果完全就不能用。如果用于标定的匹配点对,是在一个平面上的话,这些方法将完全失效,因为平面是一种退化结构或者叫临界曲线(Critical surfaces)。而且在opencv3之前的opencv版本只提供了求取基础矩阵F的API,虽然cv::stereoCalibrate(...)可以得到双目标定结果以及本质矩阵E,但该方法是使用平面标定版辅助的plane2plane估计出各相机的外参数,然后由外参数直接计算出旋转矩阵R和平移量t,此时获取的刚体运动参数自由度为6。
此外双目标定还有一些方法并不是基于本质矩阵来完成的,如:光束法平差(Bundle adjustment)、结构重投影法等。在此就不进行讨论了。
由于基础矩阵的自由度为7,而本质矩阵的自由度为5。直接使用基础矩阵的估计方法来估计本质矩阵并不适合,而且本质矩阵还有一种叫做5点算法的方法可以估计本质矩阵,该算法比较复杂,涉及到高次多项式方程的求解。但是对于估计本质矩阵,5点算法会得到比7点或8点算法更好的结果,而且不受匹配点集共面的影响。该方法的具体原理可见论文David
Nistér. An efficient solution to the five-point relative pose problem. Pattern Analysis and Machine Intelligence, IEEE Transactions on,
26(6):756–770, 2004.和Henrik Stewenius. Calibrated fivepoint solver.
5点算法的大概思路就是把本质方程展开,再加上本质矩阵的充要条件。化简后得到一个高次多项式方程,其实数解中的一个确定出的本质矩阵是系统的真实本质矩阵,对伪解进行剔除。然后对获得的本质矩阵进行使用SVD分解而得到旋转矩阵R和单位平移量t,即完成双目的相互位置标定。
opencv3中的cv::findEssentialMat(...)即是基于该五点算法实现的本质矩阵的估计,附上使用代码
[cpp] view
plain copy
/**************************************************************
* an example to estimate essential matrix and relative pose of
* binocular vision sensor using the API function of opencv3.0.0
*
* by jah10527@126.com
* May, 1st, 2016
*
***************************************************************/
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
static void calcChessboardCorners(Size boardSize, float squareSize, vector<Point3f>& corners)
{
corners.resize(0);
for( int i = 0; i < boardSize.height; i++ )
for( int j = 0; j < boardSize.width; j++ )
corners.push_back(Point3f(float(j*squareSize),
float(i*squareSize), 0));
}
int main()
{
int i, point_count = 54;
vector<Point2f> rpoints(point_count);
vector<Point2f> lpoints(point_count);
Mat Ml(3,3,CV_64F);
Mat Mr(3,3,CV_64F);
FileStorage fs("camera_l.yml", FileStorage::READ);
if( !fs.isOpened() )
return -1;
fs["camera_matrix"] >> Ml;
fs.release();
fs.open("camera_r.yml", FileStorage::READ);
if( !fs.isOpened() )
return -1;
fs["camera_matrix"] >> Mr;
fs.release();
cout << "Mr:" << endl << Mr << endl;
cout << "Ml:" << endl << Ml << endl;
Mat rim=imread("fight01.jpg", IMREAD_GRAYSCALE);
Mat lim=imread("feft01.jpg", IMREAD_GRAYSCALE);
findChessboardCorners( rim, Size(6,9), rpoints,
CALIB_CB_ADAPTIVE_THRESH | CALIB_CB_FAST_CHECK | CALIB_CB_NORMALIZE_IMAGE);
findChessboardCorners( lim, Size(6,9), lpoints,
CALIB_CB_ADAPTIVE_THRESH | CALIB_CB_FAST_CHECK | CALIB_CB_NORMALIZE_IMAGE);
/* cout << "right:" << endl;
for (i=0;i<point_count;i++)
cout << rpoints[i] << endl;
cout << "left:" << endl;
for (i=0;i<point_count;i++)
cout << lpoints[i] << endl;*/
cornerSubPix( rim, rpoints, Size(8,8),
Size(-1,-1), TermCriteria( TermCriteria::EPS+TermCriteria::COUNT, 30, 0.1 ));
cornerSubPix( lim, lpoints, Size(8,8),
Size(-1,-1), TermCriteria( TermCriteria::EPS+TermCriteria::COUNT, 30, 0.1 ));
for (i=0;i<point_count;i++)
{
rpoints[i].x = (rpoints[i].x-*((double*)Mr.data+2))/ *((double*)Mr.data);
rpoints[i].y = (rpoints[i].y-*((double*)Mr.data+5))/ *((double*)Mr.data+4);
lpoints[i].x = (lpoints[i].x-*((double*)Ml.data+2))/ *((double*)Ml.data);
lpoints[i].y = (lpoints[i].y-*((double*)Ml.data+5))/ *((double*)Ml.data+4);
// cout << "r:" << rpoints[i] << endl;
// cout << "l:" << lpoints[i] << endl;
}
Mat E_mat = findEssentialMat(rpoints, lpoints, 1, Point2d(0, 0), RANSAC, 0.999, 1.f);
cout << "E:" << endl << E_mat << endl;
/* Mat rr(3,1,CV_64F);
Mat ll(1,3,CV_64F);
*((double*)rr.data+2)=1;
*((double*)ll.data+2)=1;
for (i=0;i<8;i++){
*((double*)rr.data) = rpoints[i].x;
*((double*)rr.data+1) = rpoints[i].y;
*((double*)ll.data) = lpoints[i].x;
*((double*)ll.data+1) = lpoints[i].y;
cout << ll*E_mat*rr << endl;
cout << rr.t()*E_mat*ll.t() << endl;
}
Mat R1, R2, t;
decomposeEssentialMat(E_mat, R1, R2, t);
cout << "R1:" << endl << R1 << endl;
cout << "R2:" << endl << R2 << endl;
cout << "t:" << endl << t << endl;*/
Mat R, t;
recoverPose(E_mat, rpoints, lpoints, R, t);
cout << "t:" << endl << t << endl;
cout << "R:" << endl << R << endl;
Mat om;
Rodrigues(R, om);
cout << "om:" << endl << om << endl;
vector<vector<Point3f> > objpt(1);
vector<vector<Point2f> > imgpt(1);
vector<vector<Point2f> > imgpt_right(1);
for (i=0;i<point_count;i++)
{
imgpt[0].push_back(rpoints[i]);
imgpt_right[0].push_back(lpoints[i]);
}
calcChessboardCorners(Size(6,9), 0.03, objpt[0]);
Mat RR, TT, E, F;
Mat eye3, zeros5;
eye3 = Mat::eye(3,3,CV_64F);
zeros5 = Mat::zeros(1,5,CV_64F);
double err = stereoCalibrate(objpt, imgpt, imgpt_right, eye3, zeros5, eye3, zeros5,
Size(640,480), RR, TT, E, F, CALIB_FIX_INTRINSIC,
TermCriteria( TermCriteria::EPS+TermCriteria::COUNT, 30, 0.1 ));
cout << "Pair calibration reprojection error = " << err << endl;
Rodrigues(RR, om);
cout << "TT:" << endl << TT << endl;
cout << "RR:" << endl << RR << endl;
cout << "om:" << endl << om <<endl;
cout << "E:" << endl << E << endl;
return 0;
}
可以看出两种标定方法获取的结果不太一样。
重构出的标定版的三维坐标如下图所示,红色为本质矩阵方法获得的标定版空间情况,蓝色为stereoCalibrate(...)的运动参数三维重构标定版角点坐标的结果。
源码及数据下载点击打开链接
相关文章推荐
- Visual Studio 2010基于OpenCV3.0.0的本质矩阵估计
- 基于opencv3的本质矩阵(essential matrix)估计算例
- 基于Opencv的 矩阵点乘
- DCT变换及量化的c++实现(基于opencv矩阵运算)
- 基于Dlib和OpenCV的人脸姿态估计(HeadPoseEstimation)
- zz:基于 OpenCV 的矩阵运算 (CvMat)
- 讨论opencv位姿估计结果与实际运动轨迹的关系(涉及变换矩阵与其逆矩阵)
- OpenCV 根据对应的三维点估计刚体变换的旋转平移矩阵(RT矩阵)
- OpenCV 根据对应的三维点估计刚体变换的旋转平移矩阵(RT矩阵)
- 基于Opencv的矩阵三角函数封装 cvAtan2Mat cvSinMat cvAtan2Mat功能等于matlab里的atan2 sin cos
- 基于Opencv的MeanShift跟踪算法实现
- 基于MFC和OpenCV的图像标注工具
- eigen与opencv矩阵转换,eigen与matlab函数比照
- 转:基于opencv的小波变换
- opencv 求矩阵的特征向量矩阵和特征值
- 基于opencv的相机之物体跟踪/抖动检测/图库更新/模仿HDR/模仿ChromaFlash(三)
- Rob Hess的SIFT算法的C语言实现(基于OpenCV)(调通!!!)
- 基于OpenCV及Python的数独问题识别与求解(二)边框识别与图像矫正
- opencv基于混合高斯模型的图像分割
- 基于OpenCV的程序脱离动态链接库运行方法