2021SC@SDUSC
TwoViewerReconstruction.cc
主要完成单目初始化环节
Reconstruct函数,完成位姿估计。是单目初始化的重要环节,先获得rt再通过三角化回复3D坐标
vkeys1:第一帧的关键点
vkeys2:第二帧的关键点
vMatches12 匹配关系,长度与vKeys1一样,对应位置存放vKeys2中关键点的下标
vP3D 恢复出的三维点
vbTriangulated 是否三角化成功,用于统计匹配点数量
bool TwoViewReconstruction::Reconstruct(const std::vector<cv::KeyPoint> &vKeys1, const std::vector<cv::KeyPoint> &vKeys2, const vector<int> &vMatches12,
cv::Mat &R21, cv::Mat &t21, vector<cv::Point3f> &vP3D, vector<bool> &vbTriangulated)
{
// 1. 准备工作,提取匹配关系及准备RANSAC
mvKeys1.clear();
mvKeys2.clear();
mvKeys1 = vKeys1;
mvKeys2 = vKeys2;
// Fill structures with current keypoints and matches with reference frame
// Reference Frame: 1, Current Frame: 2
// 填写mvMatches12,里面存放的是vKeys1,vKeys2匹配点的索引,
mvMatches12.clear(); // 存放匹配点的id
mvMatches12.reserve(mvKeys2.size());
mvbMatched1.resize(mvKeys1.size()); // 长度与vKeys1,表示对应位置的vKeys1中的点是否有匹配关系
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;
// 使用vAllIndices为了保证8个点选不到同一个点
for (int i = 0; i < N; i++)
{
vAllIndices.push_back(i);
}
// Generate sets of 8 points for each RANSAC iteration
// 默认200次
mvSets = vector<vector<size_t>>(mMaxIterations, vector<size_t>(8, 0));
DUtils::Random::SeedRandOnce(0);
// 2. 先遍历把200次先取好
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已选点的索引
vAvailableIndices[randi] = vAvailableIndices.back();
vAvailableIndices.pop_back();
}
}
// Launch threads to compute in parallel a fundamental matrix and a homography
vector<bool> vbMatchesInliersH, vbMatchesInliersF;
float SH, SF;
cv::Mat H, F;
// 3. 双线程分别计算
// 加ref为了提供引用
thread threadH(&TwoViewReconstruction::FindHomography, this, ref(vbMatchesInliersH), ref(SH), ref(H));
thread threadF(&TwoViewReconstruction::FindFundamental, this, ref(vbMatchesInliersF), ref(SF), ref(F));
// Wait until both threads have finished
threadH.join();
threadF.join();
// Compute ratio of scores
if (SH + SF == 0.f)
return false;
float RH = SH / (SH + SF);
float minParallax = 1.0;
// Try to reconstruct from homography or fundamental depending on the ratio (0.40-0.45)
// 4. 看哪个分高用哪个,分别是通过H重建与通过F重建
// ORBSLAM2这里的值是0.4, TOSEE
if (RH > 0.50) // if(RH>0.40)
{
//cout << "Initialization from Homography" << endl;
return ReconstructH(vbMatchesInliersH, H, mK, R21, t21, vP3D, vbTriangulated, minParallax, 50);
}
else //if(pF_HF>0.6)
{
//cout << "Initialization from Fundamental" << endl;
return ReconstructF(vbMatchesInliersF, F, mK, R21, t21, vP3D, vbTriangulated, minParallax, 50);
}
}
FindHomography函数,计算H矩阵,同时计算得分与内点。
vbMatchesInliers 经过H矩阵验证,是否为内点,大小为mvMatches12
score 得分
H21 1到2的H矩阵
void TwoViewReconstruction::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
// 计算归一化后的H矩阵 p2 = H21*p1
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);
// 恢复正常H
H21i = T2inv * Hn * T1;
H12i = H21i.inv();
currentScore = CheckHomography(H21i, H12i, vbCurrentInliers, mSigma); // mSigma默认为1
if (currentScore > score)
{
H21 = H21i.clone();
vbMatchesInliers = vbCurrentInliers;
score = currentScore;
}
}
}
FindFundamental函数,用于计算F矩阵,同时计算得分与内点。
vbMatchesInliers 经过F矩阵验证,是否为内点,大小为mvMatches12
score 得分
F21 1到2的F矩阵
void TwoViewReconstruction::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);
// FN得到的是基于归一化坐标的F矩阵,必须乘上归一化过程矩阵才是最后的基于像素坐标的F
F21i = T2t * Fn * T1;
currentScore = CheckFundamental(F21i, vbCurrentInliers, mSigma);
if (currentScore > score)
{
F21 = F21i.clone();
vbMatchesInliers = vbCurrentInliers;
score = currentScore;
}
}
}