ORB-SLAM2的源码阅读(九):Initializer类
Rap_GodORBSLAM2源码学习(7) Initializer类
单目相机标定
通过奇异值分解(SVD)求解透视变换单应性矩阵
RANSAC算法详解
在orbslam2中,第一次新建Initializer对象实在Tracking的构造函数里。
class Initializer
{
typedef pair<int,int> Match;
public:
/**
* Initializer构造函数
* @param ReferenceFrame 输入Initializer的参考帧
* @param sigma //计算单应矩阵H和基础矩阵得分F时候一个参数
* @param iterations RANSAC迭代次数
*/
// Fix the reference frame
Initializer(const Frame &ReferenceFrame, float sigma = 1.0, int iterations = 200);
/**
* 开启初始化
* @param CurrentFrame 当前帧
* @param vMatches12 orbmatcher计算的初匹配
* @param R21
* @param t21
* @param vP3D 其大小为vKeys1大小,表示三角化重投影成功的匹配点的3d点在相机1下的坐标
* @param vbTriangulated 其大小为vKeys1大小,表示初始化成功后,特征点中三角化投影成功的情况
*/
// Computes in parallel a fundamental matrix and a homography
// Selects a model and tries to recover the motion and the structure from motion
bool Initialize(const Frame &CurrentFrame, const vector<int> &vMatches12,
cv::Mat &R21, cv::Mat &t21, vector<cv::Point3f> &vP3D, vector<bool> &vbTriangulated);
private:
/**
* 计算单应矩阵及其得分
* @param vbMatchesInliers 匹配点中哪些可以通过H21重投影成功
* @param score 输出H21得分
* @param H21 输出H21
*/
void FindHomography(vector<bool> &vbMatchesInliers, float &score, cv::Mat &H21);
/**
* 计算基础矩阵及其得分
* @param vbMatchesInliers 匹配点中哪些可以通过H21重投影成功
* @param score 输出F21得分
* @param H21 输出F21
*/
void FindFundamental(vector<bool> &vbInliers, float &score, cv::Mat &F21);
//视觉slam十四讲P147,7.3.3.单应矩阵
//通过vP1,vP2求得单应矩阵并返回
cv::Mat ComputeH21(const vector<cv::Point2f> &vP1, const vector<cv::Point2f> &vP2);
//通过vP1,vP2求得基础矩阵并返回
cv::Mat ComputeF21(const vector<cv::Point2f> &vP1, const vector<cv::Point2f> &vP2);
/**
* 计算单应矩阵得分,判断哪些点重投影成功
* @param vbMatchesInliers 通过H21,H12,匹配点重投影成功情况
* @param sigma 计算得分时需要的参数
* @return 单应矩阵得分
*/
float CheckHomography(const cv::Mat &H21, const cv::Mat &H12, vector<bool> &vbMatchesInliers, float sigma);
/**
* 计算基础得分,判断哪些匹配点重投影成功
* @param
* @param vbMatchesInliers 针对输入的单应矩阵F,匹配点重投影成功情况
* @param
* @return 基础矩阵得分
*/
float CheckFundamental(const cv::Mat &F21, vector<bool> &vbMatchesInliers, float sigma);
/**
* 通过输入的F21计算Rt
* @param vbMatchesInliers 匹配点中哪些可以通过F21重投影成功
* @param F21 基础
* @param K 内参
* @param R21 输出
* @param t21 输出
* @param vP3D 其大小为vKeys1大小,表示三角化重投影成功的匹配点的3d点在相机1下的坐标
* @param vbTriangulated 其大小为vKeys1大小,特征点中哪些可以通过F21重投影成功
* @param minParallax 设置的最小视差角余弦值参数,输出Rt模型的视差角小于此值则返回失败
* @param minTriangulated 匹配点中F21重投影成功的个数如果小于此值,返回失败
* @return 通过输入的F21计算Rt是否成功
*/
bool ReconstructF(vector<bool> &vbMatchesInliers, cv::Mat &F21, cv::Mat &K,
cv::Mat &R21, cv::Mat &t21, vector<cv::Point3f> &vP3D, vector<bool> &vbTriangulated, float minParallax, int minTriangulated);
/**
* 通过输入的H21计算Rt
* @param vbMatchesInliers 匹配点中哪些可以通过H21重投影成功
* @param H21 单应矩阵
* @param K 内参
* @param R21 输出
* @param t21 输出
* @param vP3D 其大小为vKeys1大小,表示三角化重投影成功的匹配点的3d点在相机1下的坐标
* @param vbTriangulated 其大小为vKeys1大小,特征点中哪些可以通过H21重投影成功
* @param minParallax 设置的最小视差角余弦值参数,输出Rt模型的视差角小于此值则返回失败
* @param minTriangulated 匹配点中H21重投影成功的个数如果小于此值,返回失败
* @return 通过输入的H21计算Rt是否成功
*/
bool ReconstructH(vector<bool> &vbMatchesInliers, cv::Mat &H21, cv::Mat &K,
cv::Mat &R21, cv::Mat &t21, vector<cv::Point3f> &vP3D, vector<bool> &vbTriangulated, float minParallax, int minTriangulated);
/**
* 计算kp1,kp2是匹配的关键点,它对应着世界坐标系中的一个点。
* P1,P2是F1,F2对应的投影矩阵。
* 输出综合考虑了P1,P2,kp1,kp2的在世界坐标系中的齐次坐标3D点坐标
* @param kp1
* @param kp2
* @param P1
* @param P2
* @param x3D 输出综合考虑了P1,P2,kp1,kp2的在世界坐标系中的齐次坐标3D点坐标
*/
void Triangulate(const cv::KeyPoint &kp1, const cv::KeyPoint &kp2, const cv::Mat &P1, const cv::Mat &P2, cv::Mat &x3D);
/**
* 将一个特征点集合归一化到另一个坐标系,使得归一化后的坐标点集合均值为0,一阶绝对矩为1
* @param vKeys 输入待归一化特征点集合
* @param vNormalizedPoints 输出归一化后特征点集合
* @param T vNormalizedPoints=T*vKeys
*/
void Normalize(const vector<cv::KeyPoint> &vKeys, vector<cv::Point2f> &vNormalizedPoints, cv::Mat &T);
/**计算在输入Rt下,匹配点三角化重投影成功的数量
* @param vMatches12 orbmatcher计算的初匹配
* @param vbInliers 匹配点中哪些可以通过H或者F重投影成功
* @param vP3D 其大小为vKeys1大小,表示三角化重投影成功的匹配点的3d点在相机1下的坐标
* @param th2 根据三角化重投影误差判断匹配点是否重投影成功的阈值
* @param vbGood 输出:特征点哪些三角化重投影成功
* @param parallax 三角化重投影成功匹配点的视差角
* @return 匹配点三角化重投影成功的数量
*/
int CheckRT(const cv::Mat &R, const cv::Mat &t, const vector<cv::KeyPoint> &vKeys1, const vector<cv::KeyPoint> &vKeys2,
const vector<Match> &vMatches12, vector<bool> &vbInliers,
const cv::Mat &K, vector<cv::Point3f> &vP3D, float th2, vector<bool> &vbGood, float ¶llax);
/**将本质矩阵分解成Rt,有四种模型
* @param E
* @param R1 输出其中一种R
* @param R2 输出其中一种R
* @param t 输出t
*/
void DecomposeE(const cv::Mat &E, cv::Mat &R1, cv::Mat &R2, cv::Mat &t);
// Keypoints from Reference Frame (Frame 1)
vector<cv::KeyPoint> mvKeys1;
// Keypoints from Current Frame (Frame 2)
vector<cv::KeyPoint> mvKeys2;
// Current Matches from Reference to Current
//储存着匹配点对在参考帧F1和当前帧F2中的序号
vector<Match> mvMatches12;
//描述参考帧F1中特征点匹配情况
vector<bool> mvbMatched1;
// Calibration
//内参
cv::Mat mK;
// Standard Deviation and Variance
//mSigma在CheckFundamental和CheckHomography计算F和H得分的时候有用到的参数,以及判断匹配点通过H或者F重投影是否成功的参数
//mSigma2在checkRt中判断匹配点三角重投影是否成功的一个参数
float mSigma, mSigma2;
// Ransac max iterations
//Ransac算法的最大迭代次数
int mMaxIterations;
// Ransac sets
//使用8点法计算F或者H时的RANSAC点对
vector<vector<size_t> > mvSets;
};
void Initializer::FindHomography(vector<bool> &vbMatchesInliers, float &score, cv::Mat &H21)
{
// Number of putative matches
//假定匹配的数量
const int N = mvMatches12.size();
// Normalize coordinates
vector<cv::Point2f> vPn1, vPn2;
cv::Mat T1, T2;
Normalize(mvKeys1,vPn1, T1);
Normalize(mvKeys2,vPn2, T2);
cv::Mat T2inv = T2.inv();
// Best Results variables
score = 0.0;
vbMatchesInliers = vector<bool>(N,false);
// Iteration variables
vector<cv::Point2f> vPn1i(8);
vector<cv::Point2f> vPn2i(8);
cv::Mat H21i, H12i;
vector<bool> vbCurrentInliers(N,false);
float currentScore;
// Perform all RANSAC iterations and save the solution with highest score
for(int it=0; it<mMaxIterations; it++)
{
// Select a minimum set
for(size_t j=0; j<8; j++)
{
int idx = mvSets[it][j];
vPn1i[j] = vPn1[mvMatches12[idx].first];
vPn2i[j] = vPn2[mvMatches12[idx].second];
}
//计算H
cv::Mat Hn = ComputeH21(vPn1i,vPn2i);
H21i = T2inv*Hn*T1;
H12i = H21i.inv();
//在参数 mSigma下,能够通过H21,H12重投影成功的点有哪些,并返回分数
currentScore = CheckHomography(H21i, H12i, vbCurrentInliers, mSigma);
if(currentScore>score)
{
H21 = H21i.clone();
vbMatchesInliers = vbCurrentInliers;
score = currentScore;
}
}
}
/**
* 将一个特征点集合归一化到另一个坐标系,使得归一化后的坐标点集合均值为0,一阶绝对矩为1
* @param vKeys 输入待归一化特征点集合
* @param vNormalizedPoints 输出归一化后特征点集合
* @param T vNormalizedPoints=T*vKeys
*/
void Initializer::Normalize(const vector<cv::KeyPoint> &vKeys, vector<cv::Point2f> &vNormalizedPoints, cv::Mat &T)
{
float meanX = 0;
float meanY = 0;
const int N = vKeys.size();
vNormalizedPoints.resize(N);
for(int i=0; i<N; i++)
{
meanX += vKeys[i].pt.x;
meanY += vKeys[i].pt.y;
}
meanX = meanX/N;
meanY = meanY/N;
float meanDevX = 0;
float meanDevY = 0;
for(int i=0; i<N; i++)
{
vNormalizedPoints[i].x = vKeys[i].pt.x - meanX;
vNormalizedPoints[i].y = vKeys[i].pt.y - meanY;
meanDevX += fabs(vNormalizedPoints[i].x);
meanDevY += fabs(vNormalizedPoints[i].y);
}
meanDevX = meanDevX/N;
meanDevY = meanDevY/N;
float sX = 1.0/meanDevX;
float sY = 1.0/meanDevY;
for(int i=0; i<N; i++)
{
vNormalizedPoints[i].x = vNormalizedPoints[i].x * sX;
vNormalizedPoints[i].y = vNormalizedPoints[i].y * sY;
}
T = cv::Mat::eye(3,3,CV_32F);
T.at<float>(0,0) = sX;
T.at<float>(1,1) = sY;
T.at<float>(0,2) = -meanX*sX;
T.at<float>(1,2) = -meanY*sY;
}
假设我们有一待归一化的特征点集合 ( u i , v i ) (u_i,v_i) (ui,vi), i ∈ [ 0 , N − 1 ] i \in[0,N-1] i∈[0,N−1],如果有另一个点集合 ( m i , n i ) (m_i,n_i) (mi,ni), i ∈ [ 0 , N − 1 ] i \in[0,N-1] i∈[0,N−1]满足下面条件
{ u i = ( m e a n d e v x ) ∗ m i + m e a n x v i = ( m e a n d e v y ) ∗ n i + m e a n y m e a n x = 1 N ∑ i = 0 N u i m e a n y = 1 N ∑ i = 0 N v i m e a n x = 1 N ∑ i = 0 N ∣ u i − m e a n x ∣ m e a n y = 1 N ∑ i = 0 N ∣ v i − m e a n v ∣ \begin{cases} u_i=(meandevx) *m_i+meanx \\ v_i= (meandevy) *n_i+meany \\ meanx=\frac{1}{N}\sum^N_{i=0}u_i \\ meany=\frac{1}{N}\sum^N_{i=0}v_i \\ meanx=\frac{1}{N}\sum^N_{i=0}|u_i-meanx| \\ meany=\frac{1}{N}\sum^N_{i=0}|v_i-meanv| \\ \end{cases} ⎩⎪⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎪⎧ui=(meandevx)∗mi+meanxvi=(meandevy)∗ni+meanymeanx=N1∑i=0Nuimeany=N1∑i=0Nvimeanx=N1∑i=0N∣ui−meanx∣meany=N1∑i=0N∣vi−meanv∣
那么此时点集合 ( m i , n i ) (m_i,n_i) (mi,ni)的坐标均值为0,一阶绝对矩为1。
将上面前两个式子转化一下得
{ m i = 1 m e a n d e v x ∗ u i − 1 m e a n d e v x ∗ m e a n x n i = 1 m e a n d e v y ∗ v i − 1 m e a n d e v y ∗ m e a n y \begin{cases} m_i=\frac{1}{meandevx}*u_i-\frac{1}{meandevx}*meanx \\ n_i=\frac{1}{meandevy}*v_i-\frac{1}{meandevy}*meany \end{cases} {mi=meandevx1∗ui−meandevx1∗meanxni=meandevy1∗vi−meandevy1∗meany
然后再将上面两个式子向量化得
[ m i n i 1 ] = [ 1 m e a n d e v x 0 − 1 m e a n d e v x ∗ m e a n x 0 1 m e a n d e v y − 1 m e a n d e v y ∗ m e a n y 0 0 1 ] [ u i v i 1 ] \begin{bmatrix} m_i \\ n_i \\ 1 \end{bmatrix}=\begin{bmatrix} \frac{1}{meandevx} &0&-\frac{1}{meandevx}*meanx \\ 0&\frac{1}{meandevy}&-\frac{1}{meandevy}*meany \\ 0&0&1 \end{bmatrix}\begin{bmatrix} u_i \\ v_i \\ 1 \end{bmatrix} ⎣⎡mini1⎦⎤=⎣⎡meandevx1000meandevy10−meandevx1∗meanx−meandevy1∗meany1⎦⎤⎣⎡uivi1⎦⎤
令
v N o r m a l i z e d P o i n t s = [ m i n i 1 ] T = [ 1 m e a n d e v x 0 − 1 m e a n d e v x ∗ m e a n x 0 1 m e a n d e v y − 1 m e a n d e v y ∗ m e a n y 0 0 1 ] v K e y s = [ u i v i 1 ] vNormalizedPoints=\begin{bmatrix} m_i \\ n_i \\ 1 \end{bmatrix}\quad T=\begin{bmatrix} \frac{1}{meandevx} &0&-\frac{1}{meandevx}*meanx \\ 0&\frac{1}{meandevy}&-\frac{1}{meandevy}*meany \\ 0&0&1 \end{bmatrix}\quad vKeys=\begin{bmatrix} u_i \\ v_i \\ 1 \end{bmatrix} vNormalizedPoints=⎣⎡mini1⎦⎤T=⎣⎡meandevx1000meandevy10−meandevx1∗meanx−meandevy1∗meany1⎦⎤vKeys=⎣⎡uivi1⎦⎤
则有
v N o r m a l i z e d P o i n t s = T ∗ v K e y s vNormalizedPoints=T*vKeys vNormalizedPoints=T∗vKeys
vKeys为归一化前的特征点
vNormalizedPoints为归一化后的特征点
计算单应矩阵将使用vNormalizedPoints,这样误差更小。
/**
* 开启初始化
* @param CurrentFrame 当前帧
* @param vMatches12 orbmatcher计算的初匹配
* @param R21
* @param t21
* @param vP3D 其大小为vKeys1大小,表示三角化重投影成功的匹配点的3d点在相机1下的坐标
* @param vbTriangulated 其大小为vKeys1大小,表示初始化成功后,特征点中三角化投影成功的情况
*/
bool Initializer::Initialize(const Frame &CurrentFrame, const vector<int> &vMatches12, cv::Mat &R21, cv::Mat &t21,
vector<cv::Point3f> &vP3D, vector<bool> &vbTriangulated)
{
// Fill structures with current keypoints and matches with reference frame
// Reference Frame: 1, Current Frame: 2
mvKeys2 = CurrentFrame.mvKeysUn;
//mvMatches12储存着匹配点对在参考帧F1和当前帧F2中的序号
mvMatches12.clear();
mvMatches12.reserve(mvKeys2.size());
//描述参考帧F1中特征点匹配情况
mvbMatched1.resize(mvKeys1.size());
for(size_t i=0, iend=vMatches12.size();i<iend; i++)
{
if(vMatches12[i]>=0)
{
mvMatches12.push_back(make_pair(i,vMatches12[i]));
mvbMatched1[i]=true;
}
else
mvbMatched1[i]=false;
}
//匹配点数
const int N = mvMatches12.size();
// Indices for minimum set selection
vector<size_t> vAllIndices;
vAllIndices.reserve(N);
vector<size_t> vAvailableIndices;
for(int i=0; i<N; i++)
{
vAllIndices.push_back(i);
}
// Generate sets of 8 points for each RANSAC iteration
// 在所有匹配特征点对中随机选择8对匹配特征点为一组,共选择mMaxIterations组
// 用于FindHomography和FindFundamental求解
// mMaxIterations:200
mvSets = vector< vector<size_t> >(mMaxIterations,vector<size_t>(8,0));
DUtils::Random::SeedRandOnce(0);
//RANSAC循环mMaxIterations次
for(int it=0; it<mMaxIterations; it++)
{
vAvailableIndices = vAllIndices;
// Select a minimum set
for(size_t j=0; j<8; j++)
{
int randi = DUtils::Random::RandomInt(0,vAvailableIndices.size()-1);
int idx = vAvailableIndices[randi];
mvSets[it][j] = idx;
vAvailableIndices[randi] = vAvailableIndices.back();
vAvailableIndices.pop_back();
}
}
// Launch threads to compute in parallel a fundamental matrix and a homography
//vbMatchesInliersH,ORBmatcher计算出的匹配中,哪些能够通过H重投影成功
vector<bool> vbMatchesInliersH, vbMatchesInliersF;
//SH计算单应矩阵的得分,SF计算基础矩阵得分
float SH, SF;
cv::Mat H, F;
//启动线程,第一个参数是启用函数的指针,后面是调用这个函数所需的参数
//由于FindHomography第二三个参数是引用,所以需要用ref()包裹
// 计算homograpy和得分
thread threadH(&Initializer::FindHomography,this,ref(vbMatchesInliersH), ref(SH), ref(H));
// 计算fundamental和得分
thread threadF(&Initializer::FindFundamental,this,ref(vbMatchesInliersF), ref(SF), ref(F));
// Wait until both threads have finished
//在这里等待线程threadH,threadF结束才往下继续执行
//也就是等待SH,SF的结果
threadH.join();
threadF.join();
// Compute ratio of scores
float RH = SH/(SH+SF);
// Try to reconstruct from homography or fundamental depending on the ratio (0.40-0.45)
// 从H矩阵或F矩阵中恢复R,t
if(RH>0.40)
return ReconstructH(vbMatchesInliersH,H,mK,R21,t21,vP3D,vbTriangulated,1.0,50);
else //if(pF_HF>0.6)
return ReconstructF(vbMatchesInliersF,F,mK,R21,t21,vP3D,vbTriangulated,1.0,50);
return false;
}
/**
* 计算基础矩阵及其得分
* @param vbMatchesInliers 匹配点中哪些可以通过H21重投影成功
* @param score 输出F21得分
* @param H21 输出F21
*/
void Initializer::FindFundamental(vector<bool> &vbMatchesInliers, float &score, cv::Mat &F21)
{
// Number of putative matches
const int N = vbMatchesInliers.size();
// Normalize coordinates
vector<cv::Point2f> vPn1, vPn2;
cv::Mat T1, T2;
Normalize(mvKeys1,vPn1, T1);
Normalize(mvKeys2,vPn2, T2);
cv::Mat T2t = T2.t();
// Best Results variables
score = 0.0;
vbMatchesInliers = vector<bool>(N,false);
// Iteration variables
vector<cv::Point2f> vPn1i(8);
vector<cv::Point2f> vPn2i(8);
cv::Mat F21i;
vector<bool> vbCurrentInliers(N,false);
float currentScore;
// Perform all RANSAC iterations and save the solution with highest score
for(int it=0; it<mMaxIterations; it++)
{
// Select a minimum set
for(int j=0; j<8; j++)
{
int idx = mvSets[it][j];
vPn1i[j] = vPn1[mvMatches12[idx].first];
vPn2i[j] = vPn2[mvMatches12[idx].second];
}
//计算出归一化特征点对应的基础矩阵
cv::Mat Fn = ComputeF21(vPn1i,vPn2i);
//转换成归一化前特征点对应的基础矩阵
F21i = T2t*Fn*T1;
//在参数 mSigma下,能够通过F21li,
//重投影成功的点有哪些,并返回分数
currentScore = CheckFundamental(F21i, vbCurrentInliers, mSigma);
if(currentScore>score)
{
F21 = F21i.clone();
vbMatchesInliers = vbCurrentInliers;
score = currentScore;
}
}
}
通过vP1,vP2求得单应矩阵并返回
cv::Mat Initializer::ComputeH21(const vector<cv::Point2f> &vP1, const vector<cv::Point2f> &vP2)
{
const int N = vP1.size();
cv::Mat A(2*N,9,CV_32F);
//虽然书上说最少用4个点对就可以解出单应矩阵,但是这里依然用的是8个点对
for(int i=0; i<N; i++)
{
const float u1 = vP1[i].x;
const float v1 = vP1[i].y;
const float u2 = vP2[i].x;
const float v2 = vP2[i].y;
A.at<float>(2*i,0) = 0.0;
A.at<float>(2*i,1) = 0.0;
A.at<float>(2*i,2) = 0.0;
A.at<float>(2*i,3) = -u1;
A.at<float>(2*i,4) = -v1;
A.at<float>(2*i,5) = -1;
A.at<float>(2*i,6) = v2*u1;
A.at<float>(2*i,7) = v2*v1;
A.at<float>(2*i,8) = v2;
A.at<float>(2*i+1,0) = u1;
A.at<float>(2*i+1,1) = v1;
A.at<float>(2*i+1,2) = 1;
A.at<float>(2*i+1,3) = 0.0;
A.at<float>(2*i+1,4) = 0.0;
A.at<float>(2*i+1,5) = 0.0;
A.at<float>(2*i+1,6) = -u2*u1;
A.at<float>(2*i+1,7) = -u2*v1;
A.at<float>(2*i+1,8) = -u2;
}
cv::Mat u,w,vt;
//SVD分解A=u*w*vt
cv::SVDecomp(A,w,u,vt,cv::SVD::MODIFY_A | cv::SVD::FULL_UV);
//返回了vt最后一个行向量
return vt.row(8).reshape(0, 3);
}
通过vP1,vP2求得基础矩阵并返回
cv::Mat Initializer::ComputeF21(const vector<cv::Point2f> &vP1,const vector<cv::Point2f> &vP2)
{
const int N = vP1.size();
cv::Mat A(N,9,CV_32F);
//八点法计算F
for(int i=0; i<N; i++)
{
const float u1 = vP1[i].x;
const float v1 = vP1[i].y;
const float u2 = vP2[i].x;
const float v2 = vP2[i].y;
A.at<float>(i,0) = u2*u1;
A.at<float>(i,1) = u2*v1;
A.at<float>(i,2) = u2;
A.at<float>(i,3) = v2*u1;
A.at<float>(i,4) = v2*v1;
A.at<float>(i,5) = v2;
A.at<float>(i,6) = u1;
A.at<float>(i,7) = v1;
A.at<float>(i,8) = 1;
}
cv::Mat u,w,vt;
cv::SVDecomp(A,w,u,vt,cv::SVD::MODIFY_A | cv::SVD::FULL_UV);
//用SVD算出基础矩阵
cv::Mat Fpre = vt.row(8).reshape(0, 3);
//将基础矩阵svd分解
cv::SVDecomp(Fpre,w,u,vt,cv::SVD::MODIFY_A | cv::SVD::FULL_UV);
//根据基础矩阵的性质分解出来的w第三个元素应该为0
w.at<float>(2)=0;
//返回复合要求的基础矩阵
return u*cv::Mat::diag(w)*vt;
}
/**
* 计算单应矩阵得分,判断哪些点重投影成功
* @param vbMatchesInliers 通过H21,H12,匹配点重投影成功情况
* @param sigma 计算得分时需要的参数
* @return 单应矩阵得分
*/
float Initializer::CheckHomography(const cv::Mat &H21, const cv::Mat &H12, vector<bool> &vbMatchesInliers, float sigma)
{
const int N = mvMatches12.size();
const float h11 = H21.at<float>(0,0);
const float h12 = H21.at<float>(0,1);
const float h13 = H21.at<float>(0,2);
const float h21 = H21.at<float>(1,0);
const float h22 = H21.at<float>(1,1);
const float h23 = H21.at<float>(1,2);
const float h31 = H21.at<float>(2,0);
const float h32 = H21.at<float>(2,1);
const float h33 = H21.at<float>(2,2);
const float h11inv = H12.at<float>(0,0);
const float h12inv = H12.at<float>(0,1);
const float h13inv = H12.at<float>(0,2);
const float h21inv = H12.at<float>(1,0);
const float h22inv = H12.at<float>(1,1);
const float h23inv = H12.at<float>(1,2);
const float h31inv = H12.at<float>(2,0);
const float h32inv = H12.at<float>(2,1);
const float h33inv = H12.at<float>(2,2);
vbMatchesInliers.resize(N);
float score = 0;
//判断通过单应矩阵重投影是否成功的阈值
const float th = 5.991;
const float invSigmaSquare = 1.0/(sigma*sigma);
//遍历所有的匹配点
for(int i=0; i<N; i++)
{
bool bIn = true;
const cv::KeyPoint &kp1 = mvKeys1[mvMatches12[i].first];
const cv::KeyPoint &kp2 = mvKeys2[mvMatches12[i].second];
const float u1 = kp1.pt.x;
const float v1 = kp1.pt.y;
const float u2 = kp2.pt.x;
const float v2 = kp2.pt.y;
// Reprojection error in first image
// x2in1 = H12*x2
const float w2in1inv = 1.0/(h31inv*u2+h32inv*v2+h33inv);
const float u2in1 = (h11inv*u2+h12inv*v2+h13inv)*w2in1inv;
const float v2in1 = (h21inv*u2+h22inv*v2+h23inv)*w2in1inv;
//计算u2,v2投影到F1后与u1,v1的距离的平方,也就是重投影误差
const float squareDist1 = (u1-u2in1)*(u1-u2in1)+(v1-v2in1)*(v1-v2in1);
const float chiSquare1 = squareDist1*invSigmaSquare;
//chiSquare1>th说明匹配的点对F1投影到F2,重投影失败
if(chiSquare1>th)
bIn = false;
else
score += th - chiSquare1;
// Reprojection error in second image
// x1in2 = H21*x1
const float w1in2inv = 1.0/(h31*u1+h32*v1+h33);
const float u1in2 = (h11*u1+h12*v1+h13)*w1in2inv;
const float v1in2 = (h21*u1+h22*v1+h23)*w1in2inv;
const float squareDist2 = (u2-u1in2)*(u2-u1in2)+(v2-v1in2)*(v2-v1in2);
const float chiSquare2 = squareDist2*invSigmaSquare;
if(chiSquare2>th)
bIn = false;
else
score += th - chiSquare2;
//bIn标志着此对匹配点是否重投影成功
if(bIn)
vbMatchesInliers[i]=true;
else
vbMatchesInliers[i]=false;
}
return score;
}
/**
* 计算基础得分,判断哪些匹配点重投影成功
* @param
* @param vbMatchesInliers 针对输入的单应矩阵F,匹配点重投影成功情况
* @param
* @return 基础矩阵得分
*/
float Initializer::CheckFundamental(const cv::Mat &F21, vector<bool> &vbMatchesInliers, float sigma)
{
const int N = mvMatches12.size();
const float f11 = F21.at<float>(0,0);
const float f12 = F21.at<float>(0,1);
const float f13 = F21.at<float>(0,2);
const float f21 = F21.at<float>(1,0);
const float f22 = F21.at<float>(1,1);
const float f23 = F21.at<float>(1,2);
const float f31 = F21.at<float>(2,0);
const float f32 = F21.at<float>(2,1);
const float f33 = F21.at<float>(2,2);
vbMatchesInliers.resize(N);
float score = 0;
const float th = 3.841;
const float thScore = 5.991;
const float invSigmaSquare = 1.0/(sigma*sigma);
for(int i=0; i<N; i++)
{
bool bIn = true;
const cv::KeyPoint &kp1 = mvKeys1[mvMatches12[i].first];
const cv::KeyPoint &kp2 = mvKeys2[mvMatches12[i].second];
const float u1 = kp1.pt.x;
const float v1 = kp1.pt.y;
const float u2 = kp2.pt.x;
const float v2 = kp2.pt.y;
// Reprojection error in second image
// l2=F21x1=(a2,b2,c2)
const float a2 = f11*u1+f12*v1+f13;
const float b2 = f21*u1+f22*v1+f23;
const float c2 = f31*u1+f32*v1+f33;
// num2=x2*F21*x1
const float num2 = a2*u2+b2*v2+c2;
const float squareDist1 = num2*num2/(a2*a2+b2*b2);
const float chiSquare1 = squareDist1*invSigmaSquare;
if(chiSquare1>th)
bIn = false;
else
score += thScore - chiSquare1;
// Reprojection error in second image
// l1 =x2tF21=(a1,b1,c1)
const float a1 = f11*u2+f21*v2+f31;
const float b1 = f12*u2+f22*v2+f32;
const float c1 = f13*u2+f23*v2+f33;
const float num1 = a1*u1+b1*v1+c1;
const float squareDist2 = num1*num1/(a1*a1+b1*b1);
const float chiSquare2 = squareDist2*invSigmaSquare;
if(chiSquare2>th)
bIn = false;
else
score += thScore - chiSquare2;
if(bIn)
vbMatchesInliers[i]=true;
else
vbMatchesInliers[i]=false;
}
return score;
}
/**
* 通过输入的F21计算Rt
* @param vbMatchesInliers 匹配点中哪些可以通过F21重投影成功
* @param F21 基础
* @param K 内参
* @param R21 输出
* @param t21 输出
* @param vP3D 其大小为vKeys1大小,表示三角化重投影成功的匹配点的3d点在相机1下的坐标
* @param vbTriangulated 其大小为vKeys1大小,特征点中哪些可以通过F21重投影成功
* @param minParallax 设置的最小视差角余弦值参数,输出Rt模型的视差角小于此值则返回失败
* @param minTriangulated 匹配点中F21重投影成功的个数如果小于此值,返回失败
* @return 通过输入的F21计算Rt是否成功
*/
bool Initializer::ReconstructF(vector<bool> &vbMatchesInliers, cv::Mat &F21, cv::Mat &K,
cv::Mat &R21, cv::Mat &t21, vector<cv::Point3f> &vP3D, vector<bool> &vbTriangulated, float minParallax, int minTriangulated)
{
int N=0;
//匹配点中
for(size_t i=0, iend = vbMatchesInliers.size() ; i<iend; i++)
if(vbMatchesInliers[i])
N++;
// Compute Essential Matrix from Fundamental Matrix
cv::Mat E21 = K.t()*F21*K;
cv::Mat R1, R2, t;
// Recover the 4 motion hypotheses
DecomposeE(E21,R1,R2,t);
cv::Mat t1=t;
cv::Mat t2=-t;
// Reconstruct with the 4 hyphoteses and check
vector<cv::Point3f> vP3D1, vP3D2, vP3D3, vP3D4;
vector<bool> vbTriangulated1,vbTriangulated2,vbTriangulated3, vbTriangulated4;
float parallax1,parallax2, parallax3, parallax4;
int nGood1 = CheckRT(R1,t1,mvKeys1,mvKeys2,mvMatches12,vbMatchesInliers,K, vP3D1, 4.0*mSigma2, vbTriangulated1, parallax1);
int nGood2 = CheckRT(R2,t1,mvKeys1,mvKeys2,mvMatches12,vbMatchesInliers,K, vP3D2, 4.0*mSigma2, vbTriangulated2, parallax2);
int nGood3 = CheckRT(R1,t2,mvKeys1,mvKeys2,mvMatches12,vbMatchesInliers,K, vP3D3, 4.0*mSigma2, vbTriangulated3, parallax3);
int nGood4 = CheckRT(R2,t2,mvKeys1,mvKeys2,mvMatches12,vbMatchesInliers,K, vP3D4, 4.0*mSigma2, vbTriangulated4, parallax4);
int maxGood = max(nGood1,max(nGood2,max(nGood3,nGood4)));
R21 = cv::Mat();
t21 = cv::Mat();
int nMinGood = max(static_cast<int>(0.9*N),minTriangulated);
int nsimilar = 0;
if(nGood1>0.7*maxGood)
nsimilar++;
if(nGood2>0.7*maxGood)
nsimilar++;
if(nGood3>0.7*maxGood)
nsimilar++;
if(nGood4>0.7*maxGood)
nsimilar++;
// If there is not a clear winner or not enough triangulated points reject initialization
//nsimilar>1表明没有哪个模型明显胜出
//匹配点三角化重投影成功数过少
if(maxGood<nMinGood || nsimilar>1)
{
return false;
}
// If best reconstruction has enough parallax initialize
if(maxGood==nGood1)
{
//如果模型一对应的视差角大于最小值
if(parallax1>minParallax)
{
vP3D = vP3D1;
vbTriangulated = vbTriangulated1;
R1.copyTo(R21);
t1.copyTo(t21);
return true;
}
}else if(maxGood==nGood2)
{
if(parallax2>minParallax)
{
vP3D = vP3D2;
vbTriangulated = vbTriangulated2;
R2.copyTo(R21);
t1.copyTo(t21);
return true;
}
}else if(maxGood==nGood3)
{
if(parallax3>minParallax)
{
vP3D = vP3D3;
vbTriangulated = vbTriangulated3;
R1.copyTo(R21);
t2.copyTo(t21);
return true;
}
}else if(maxGood==nGood4)
{
if(parallax4>minParallax)
{
vP3D = vP3D4;
vbTriangulated = vbTriangulated4;
R2.copyTo(R21);
t2.copyTo(t21);
return true;
}
}
return false;
}
/**
* 通过输入的H21计算Rt
* @param vbMatchesInliers 匹配点中哪些可以通过H21重投影成功
* @param H21 单应矩阵
* @param K 内参
* @param R21 输出
* @param t21 输出
* @param vP3D 其大小为vKeys1大小,表示三角化重投影成功的匹配点的3d点在相机1下的坐标
* @param vbTriangulated 其大小为vKeys1大小,特征点中哪些可以通过H21重投影成功
* @param minParallax 设置的最小视差角余弦值参数,输出Rt模型的视差角小于此值则返回失败
* @param minTriangulated 匹配点中H21重投影成功的个数如果小于此值,返回失败
* @return 通过输入的H21计算Rt是否成功
*/
bool Initializer::ReconstructH(vector<bool> &vbMatchesInliers, cv::Mat &H21, cv::Mat &K,
cv::Mat &R21, cv::Mat &t21, vector<cv::Point3f> &vP3D, vector<bool> &vbTriangulated, float minParallax, int minTriangulated)
{
//N,通过H重投影成功的数量
int N=0;
for(size_t i=0, iend = vbMatchesInliers.size() ; i<iend; i++)
if(vbMatchesInliers[i])
N++;
// We recover 8 motion hypotheses using the method of Faugeras et al.
// Motion and structure from motion in a piecewise planar environment.
// International Journal of Pattern Recognition and Artificial Intelligence, 1988
// 将H矩阵由图像坐标系变换到相机坐标系
cv::Mat invK = K.inv();
cv::Mat A = invK*H21*K;
cv::Mat U,w,Vt,V;
cv::SVD::compute(A,w,U,Vt,cv::SVD::FULL_UV);
//vt转置
V=Vt.t();
//cv::determinant(U)为U的行列式
float s = cv::determinant(U)*cv::determinant(Vt);
float d1 = w.at<float>(0);
float d2 = w.at<float>(1);
float d3 = w.at<float>(2);
//注意d1>d2>d3
//看吴博讲解的ppt19页,只考虑d1!=d2!=d3的情况,其他情况返回失败
if(d1/d2<1.00001 || d2/d3<1.00001)
{
return false;
}
vector<cv::Mat> vR, vt, vn;
vR.reserve(8);
vt.reserve(8);
vn.reserve(8);
//n'=[x1 0 x3] 4 posibilities e1=e3=1, e1=1 e3=-1, e1=-1 e3=1, e1=e3=-1
float aux1 = sqrt((d1*d1-d2*d2)/(d1*d1-d3*d3));
float aux3 = sqrt((d2*d2-d3*d3)/(d1*d1-d3*d3));
float x1[] = {aux1,aux1,-aux1,-aux1};
float x3[] = {aux3,-aux3,aux3,-aux3};
//case d'=d2
float aux_stheta = sqrt((d1*d1-d2*d2)*(d2*d2-d3*d3))/((d1+d3)*d2);
float ctheta = (d2*d2+d1*d3)/((d1+d3)*d2);
float stheta[] = {aux_stheta, -aux_stheta, -aux_stheta, aux_stheta};
for(int i=0; i<4; i++)
{
cv::Mat Rp=cv::Mat::eye(3,3,CV_32F);
Rp.at<float>(0,0)=ctheta;
Rp.at<float>(0,2)=-stheta[i];
Rp.at<float>(2,0)=stheta[i];
Rp.at<float>(2,2)=ctheta;
cv::Mat R = s*U*Rp*Vt;
vR.push_back(R);
cv::Mat tp(3,1,CV_32F);
tp.at<float>(0)=x1[i];
tp.at<float>(1)=0;
tp.at<float>(2)=-x3[i];
tp*=d1-d3;
cv::Mat t = U*tp;
vt.push_back(t/cv::norm(t));
cv::Mat np(3,1,CV_32F);
np.at<float>(0)=x1[i];
np.at<float>(1)=0;
np.at<float>(2)=x3[i];
cv::Mat n = V*np;
if(n.at<float>(2)<0)
n=-n;
vn.push_back(n);
}
//case d'=-d2
float aux_sphi = sqrt((d1*d1-d2*d2)*(d2*d2-d3*d3))/((d1-d3)*d2);
float cphi = (d1*d3-d2*d2)/((d1-d3)*d2);
float sphi[] = {aux_sphi, -aux_sphi, -aux_sphi, aux_sphi};
for(int i=0; i<4; i++)
{
cv::Mat Rp=cv::Mat::eye(3,3,CV_32F);
Rp.at<float>(0,0)=cphi;
Rp.at<float>(0,2)=sphi[i];
Rp.at<float>(1,1)=-1;
Rp.at<float>(2,0)=sphi[i];
Rp.at<float>(2,2)=-cphi;
cv::Mat R = s*U*Rp*Vt;
vR.push_back(R);
cv::Mat tp(3,1,CV_32F);
tp.at<float>(0)=x1[i];
tp.at<float>(1)=0;
tp.at<float>(2)=x3[i];
tp*=d1+d3;
cv::Mat t = U*tp;
vt.push_back(t/cv::norm(t));
cv::Mat np(3,1,CV_32F);
np.at<float>(0)=x1[i];
np.at<float>(1)=0;
np.at<float>(2)=x3[i];
cv::Mat n = V*np;
if(n.at<float>(2)<0)
n=-n;
vn.push_back(n);
}
int bestGood = 0;
int secondBestGood = 0;
int bestSolutionIdx = -1;
float bestParallax = -1;
vector<cv::Point3f> bestP3D;
vector<bool> bestTriangulated;
// Instead of applying the visibility constraints proposed in the Faugeras' paper (which could fail for points seen with low parallax)
// We reconstruct all hypotheses and check in terms of triangulated points and parallax
//经过上面的计算,公共有8种R,t计算结果,遍历这8种可能模型
//通过计算出匹配点的三角化重投影成功的数量,来找出最好模型和次好模型
for(size_t i=0; i<8; i++)
{
float parallaxi;
vector<cv::Point3f> vP3Di;
vector<bool> vbTriangulatedi;
//计算在输入Rt下,匹配点三角化重投影成功的数量
int nGood = CheckRT(vR[i],vt[i],mvKeys1,mvKeys2,mvMatches12,vbMatchesInliers,K,vP3Di, 4.0*mSigma2, vbTriangulatedi, parallaxi);
if(nGood>bestGood)
{
secondBestGood = bestGood;
bestGood = nGood;
bestSolutionIdx = i;
bestParallax = parallaxi;
bestP3D = vP3Di;
bestTriangulated = vbTriangulatedi;
}
else if(nGood>secondBestGood)
{
secondBestGood = nGood;
}
}
//secondBestGood<0.75*bestGood 如果最好模型与次好模型差距足够大
//bestParallax>=minParallax 最好模型对应的视差角大于此值
//bestGood>minTriangulated 最好模型对应的匹配点三角化重投影成功数量大于此阈值
//bestGood>0.9*N 匹配点三角化重投影成功数量占通过H重投影成功数量的比例需要大于0.9
if(secondBestGood<0.75*bestGood && bestParallax>=minParallax && bestGood>minTriangulated && bestGood>0.9*N)
{
vR[bestSolutionIdx].copyTo(R21);
vt[bestSolutionIdx].copyTo(t21);
vP3D = bestP3D;
vbTriangulated = bestTriangulated;
return true;
}
return false;
}
/**
* 计算kp1,kp2是匹配的关键点,它对应着世界坐标系中的一个点。
* P1,P2是F1,F2对应的投影矩阵。
* 输出综合考虑了P1,P2,kp1,kp2的在世界坐标系中的齐次坐标3D点坐标
* @param kp1
* @param kp2
* @param P1
* @param P2
* @param x3D 输出综合考虑了P1,P2,kp1,kp2的在世界坐标系中的齐次坐标3D点坐标
*/
void Initializer::Triangulate(const cv::KeyPoint &kp1, const cv::KeyPoint &kp2, const cv::Mat &P1, const cv::Mat &P2, cv::Mat &x3D)
{
cv::Mat A(4,4,CV_32F);
A.row(0) = kp1.pt.x*P1.row(2)-P1.row(0);
A.row(1) = kp1.pt.y*P1.row(2)-P1.row(1);
A.row(2) = kp2.pt.x*P2.row(2)-P2.row(0);
A.row(3) = kp2.pt.y*P2.row(2)-P2.row(1);
cv::Mat u,w,vt;
cv::SVD::compute(A,w,u,vt,cv::SVD::MODIFY_A| cv::SVD::FULL_UV);
//取最后一个行向量
x3D = vt.row(3).t();
//转换为齐次坐标
x3D = x3D.rowRange(0,3)/x3D.at<float>(3);
}
/**计算在输入Rt下,匹配点三角化重投影成功的数量
* @param vMatches12 orbmatcher计算的初匹配
* @param vbInliers 匹配点中哪些可以通过H或者F重投影成功
* @param vP3D 其大小为vKeys1大小,表示三角化重投影成功的匹配点的3d点在相机1下的坐标
* @param th2 根据三角化重投影误差判断匹配点是否重投影成功的阈值
* @param vbGood 输出:特征点哪些三角化重投影成功
* @param parallax 三角化重投影成功匹配点的视差角
* @return 匹配点三角化重投影成功的数量
*/
int Initializer::CheckRT(const cv::Mat &R, const cv::Mat &t, const vector<cv::KeyPoint> &vKeys1, const vector<cv::KeyPoint> &vKeys2,
const vector<Match> &vMatches12, vector<bool> &vbMatchesInliers,
const cv::Mat &K, vector<cv::Point3f> &vP3D, float th2, vector<bool> &vbGood, float ¶llax)
{
// Calibration parameters
const float fx = K.at<float>(0,0);
const float fy = K.at<float>(1,1);
const float cx = K.at<float>(0,2);
const float cy = K.at<float>(1,2);
vbGood = vector<bool>(vKeys1.size(),false);
vP3D.resize(vKeys1.size());
vector<float> vCosParallax;
vCosParallax.reserve(vKeys1.size());
// Camera 1 Projection Matrix K[I|0]
//相机1的投影矩阵K[I|0],世界坐标系和相机1坐标系相同
cv::Mat P1(3,4,CV_32F,cv::Scalar(0));
K.copyTo(P1.rowRange(0,3).colRange(0,3));
// 相机1的光心在世界坐标系坐标
cv::Mat O1 = cv::Mat::zeros(3,1,CV_32F);
// Camera 2 Projection Matrix K[R|t]
//相机2的投影矩阵
cv::Mat P2(3,4,CV_32F);
R.copyTo(P2.rowRange(0,3).colRange(0,3));
t.copyTo(P2.rowRange(0,3).col(3));
P2 = K*P2;
// 相机2的光心在世界坐标系坐标
cv::Mat O2 = -R.t()*t;
int nGood=0;
//遍历所有的匹配点
for(size_t i=0, iend=vMatches12.size();i<iend;i++)
{
//如果在
if(!vbMatchesInliers[i])
continue;
const cv::KeyPoint &kp1 = vKeys1[vMatches12[i].first];
const cv::KeyPoint &kp2 = vKeys2[vMatches12[i].second];
//3d点在相机1和世界坐标系下的坐标
cv::Mat p3dC1;
//输出的p3dC1是综合考虑了P1,P2的kp1,kp2匹配点在世界坐标系中的齐次坐标
//由于世界坐标系和相机1坐标系重合,所以p3dC1同时也是匹配点对应的空间点在相机1坐标系中的坐标
Triangulate(kp1,kp2,P1,P2,p3dC1);
//isfinite()判断一个浮点数是否是一个有限值
//相当于是确定p3dC1前三位数值正常
if(!isfinite(p3dC1.at<float>(0)) || !isfinite(p3dC1.at<float>(1)) || !isfinite(p3dC1.at<float>(2)))
{
vbGood[vMatches12[i].first]=false;
continue;
}
// Check parallax
//normal1是相机1到3d点的向量
cv::Mat normal1 = p3dC1 - O1;
float dist1 = cv::norm(normal1);
//normal2是相机2到3d点的向量
cv::Mat normal2 = p3dC1 - O2;
float dist2 = cv::norm(normal2);
//cosParallax为视差角的余弦,也就是normal1与normal2的余弦
float cosParallax = normal1.dot(normal2)/(dist1*dist2);
// Check depth in front of first camera (only if enough parallax, as "infinite" points can easily go to negative depth)
//p3dC1.at(2)<=0说明3d点在光心后面,深度为负
//p3dC1视差角较大,且深度为负则淘汰
if(p3dC1.at<float>(2)<=0 && cosParallax<0.99998)
continue;
// Check depth in front of second camera (only if enough parallax, as "infinite" points can easily go to negative depth)
cv::Mat p3dC2 = R*p3dC1+t;
//p3dC2视差角较大,且深度为负则淘汰
if(p3dC2.at<float>(2)<=0 && cosParallax<0.99998)
continue;
// Check reprojection error in first image
// 计算3D点在第一个图像上的投影误差
float im1x, im1y;
float invZ1 = 1.0/p3dC1.at<float>(2);
im1x = fx*p3dC1.at<float>(0)*invZ1+cx;
im1y = fy*p3dC1.at<float>(1)*invZ1+cy;
float squareError1 = (im1x-kp1.pt.x)*(im1x-kp1.pt.x)+(im1y-kp1.pt.y)*(im1y-kp1.pt.y);
if(squareError1>th2)
continue;
// Check reprojection error in second image
float im2x, im2y;
float invZ2 = 1.0/p3dC2.at<float>(2);
im2x = fx*p3dC2.at<float>(0)*invZ2+cx;
im2y = fy*p3dC2.at<float>(1)*invZ2+cy;
float squareError2 = (im2x-kp2.pt.x)*(im2x-kp2.pt.x)+(im2y-kp2.pt.y)*(im2y-kp2.pt.y);
// 重投影误差太大,淘汰
if(squareError2>th2)
continue;
//到这里说明这对匹配点三角化重投影成功了
vCosParallax.push_back(cosParallax);
vP3D[vMatches12[i].first] = cv::Point3f(p3dC1.at<float>(0),p3dC1.at<float>(1),p3dC1.at<float>(2));
nGood++;
//确认视差角最够大
if(cosParallax<0.99998)
vbGood[vMatches12[i].first]=true;
}
if(nGood>0)
{
//将视差角余弦有小到大排序
sort(vCosParallax.begin(),vCosParallax.end());
//取出第50个,或者最后那个也就是最大那个
size_t idx = min(50,int(vCosParallax.size()-1));
//计算出视差角
parallax = acos(vCosParallax[idx])*180/CV_PI;
}
else
parallax=0;
return nGood;
}
/**将本质矩阵分解成Rt,有四种模型
* @param E
* @param R1 输出其中一种R
* @param R2 输出其中一种R
* @param t 输出t
*/
void Initializer::DecomposeE(const cv::Mat &E, cv::Mat &R1, cv::Mat &R2, cv::Mat &t)
{
cv::Mat u,w,vt;
cv::SVD::compute(E,w,u,vt);
u.col(2).copyTo(t);
t=t/cv::norm(t);
cv::Mat W(3,3,CV_32F,cv::Scalar(0));
W.at<float>(0,1)=-1;
W.at<float>(1,0)=1;
W.at<float>(2,2)=1;
R1 = u*W*vt;
if(cv::determinant(R1)<0)
R1=-R1;
R2 = u*W.t()*vt;
if(cv::determinant(R2)<0)
R2=-R2;
}