2021SC@SDUSC
TwoViewReconstruction.cc
ComputeH21函数,通过特征点匹配求homograpy
计算公式:
|x'| | h1 h2 h3 | |x|
|y'| = a | h4 h5 h6 | |y| 简写: x' = a H x, a为一个尺度因子
|1 | | h7 h8 h9 | |1|
使用DLT(direct linear tranform)求解该模型
x' = a H x
---> (x') 叉乘 (H x) = 0
---> Ah = 0
A = | 0 0 0 -x -y -1 xy' yy' y'| h = | h1 h2 h3 h4 h5 h6 h7 h8 h9 |
|-x -y -1 0 0 0 xx' yx' x'|
通过SVD求解Ah = 0,A'A最小特征值对应的特征向量即为解
vP1 归一化后的点, in reference frame
vP2 归一化后的点, in current frame
最终返回单应矩阵
cv::Mat TwoViewReconstruction::ComputeH21(const vector<cv::Point2f> &vP1, const vector<cv::Point2f> &vP2)
{
const int N = vP1.size();
cv::Mat A(2 * N, 9, CV_32F);
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;
cv::SVDecomp(A, w, u, vt, cv::SVD::MODIFY_A | cv::SVD::FULL_UV);
return vt.row(8).reshape(0, 3);
}
ComputeF21函数;通过8点法,从特征点匹配求fundamental matrix。
具体方法:
由x'Fx = 0 整理可得:Af = 0
A = | x'x x'y x' y'x y'y y' x y 1 |, f = | f1 f2 f3 f4 f5 f6 f7 f8 f9 |
通过SVD求解Af = 0,A'A最小特征值对应的特征向量即为解
vP1 归一化后的点, in reference frame
vP2 归一化后的点, in current frame
最终返回基础矩阵
cv::Mat TwoViewReconstruction::ComputeF21(const vector<cv::Point2f> &vP1, const vector<cv::Point2f> &vP2)
{
const int N = vP1.size();
cv::Mat A(N, 9, CV_32F);
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);
cv::Mat Fpre = vt.row(8).reshape(0, 3);
// 这里注意计算完要强制让第三个奇异值为0
cv::SVDecomp(Fpre, w, u, vt, cv::SVD::MODIFY_A | cv::SVD::FULL_UV);
w.at<float>(2) = 0;
return u * cv::Mat::diag(w) * vt;
}
CheckHomography函数,用来检查H矩阵结果。
vbMatchesInliers 匹配是否合法,大小为mvMatches12;sigma默认为1
float TwoViewReconstruction::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;
// 基于卡方检验计算出的阈值 自由度为2的卡方分布,显著性水平为0.05,对应的临界阈值
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
// 计算投影误差,2投1 1投2这么做,计算累计的卡方检验分数,分数越高证明内点与误差越优,这么做为了平衡误差与内点个数,不是说内点个数越高越好,也不是说误差越小越好
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;
const float squareDist1 = (u1 - u2in1) * (u1 - u2in1) + (v1 - v2in1) * (v1 - v2in1);
const float chiSquare1 = squareDist1 * invSigmaSquare;
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;
if (bIn)
vbMatchesInliers[i] = true;
else
vbMatchesInliers[i] = false;
}
return score;
}
CheckFundamental函数,用来检查F矩阵结果
float TwoViewReconstruction::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;
// 基于卡方检验计算出的阈值 自由度为1的卡方分布,显著性水平为0.05,对应的临界阈值
const float th = 3.841;
// 基于卡方检验计算出的阈值 自由度为2的卡方分布,显著性水平为0.05,对应的临界阈值
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)
// 计算 img1 上的点在 img2 上投影得到的极线 l2 = F21 * p1 = (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;
// 计算误差 e = (a * p2.x + b * p2.y + c) / sqrt(a * a + b * b)
const float num2 = a2 * u2 + b2 * v2 + c2;
const float squareDist1 = num2 * num2 / (a2 * a2 + b2 * b2);
const float chiSquare1 = squareDist1 * invSigmaSquare;
// 自由度为1是因为这里的计算是点到线的距离,判定分数自由度为2的原因可能是为了与H矩阵持平
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;
}