我正在关注使用Features2D + Homography的this tutorial。如果我知道每张图像的相机矩阵,我该如何优化结果?我尝试了一些图像,但效果不佳。
//编辑
在阅读了一些材料后,我想我应该先纠正两个图像。但是整流并不完美,因此图像1上的垂直线通常对应于图像2上的垂直带。有没有好的算法?
答案 0 :(得分:3)
我不确定我是否理解你的问题。您想在图像之间找到相应的点,或者您想通过使用相机内在函数来提高匹配的正确性?
原则上,为了使用相机几何来寻找匹配,您需要基本或必要的矩阵,这取决于您是否知道相机内在函数(即校准相机)。这意味着,您需要估计相机的相对旋转和平移。然后,通过计算与一个图像中找到的特征对应的对极线,您需要沿第二个图像中的那些线搜索以找到最佳匹配。但是,我认为仅仅依靠自动功能匹配会更好。给定基本/必要矩阵,您可以试试correctMatches
运气,这将移动对应关系,使重投影误差最小化。
更佳匹配的提示
为了提高自动匹配的稳定性和显着性,通常需要支付
执行比率测试以过滤掉那些具有非常相似的次优匹配且因此不稳定的关键点。这是这样做的:
Mat descriptors_1, descriptors_2; // obtained from feature detector
BFMatcher matcher;
vector<DMatch> matches;
matcher = BFMatcher(NORM_L2, false); // norm depends on feature detector
vector<vector<DMatch>> match_candidates;
const float ratio = 0.8; // or something
matcher.knnMatch(descriptors_1, descriptors_2, match_candidates, 2);
for (int i = 0; i < match_candidates.size(); i++)
{
if (match_candidates[i][0].distance < ratio * match_candidates[i][1].distance)
matches.push_back(match_candidates[i][0]);
}
更复杂的过滤方式是计算第一帧中每个关键点的重投影错误。这意味着在第二个图像中计算相应的极线,然后检查其假定的匹配点离该线的距离。抛弃那些距离超过某个阈值的点将删除与epiploar几何不相容的匹配(我假设这是已知的)。计算错误可以这样做(我老实说不记得我从哪里获取这些代码,我可能已经修改了一下,当代码在列表中时,SO编辑器也有错误,抱歉格式错误):
double computeReprojectionError(vector& imgpts1, vector& imgpts2, Mat& inlier_mask, const Mat& F)
{
double err = 0;
vector lines[2];
int npt = sum(inlier_mask)[0];
// strip outliers so validation is constrained to the correspondences
// which were used to estimate F
vector imgpts1_copy(npt),
imgpts2_copy(npt);
int c = 0;
for (int k = 0; k < inlier_mask.size().height; k++)
{
if (inlier_mask.at(0,k) == 1)
{
imgpts1_copy[c] = imgpts1[k];
imgpts2_copy[c] = imgpts2[k];
c++;
}
}
Mat imgpt[2] = { Mat(imgpts1_copy), Mat(imgpts2_copy) };
computeCorrespondEpilines(imgpt[0], 1, F, lines[0]);
computeCorrespondEpilines(imgpt1, 2, F, lines1);
for(int j = 0; j < npt; j++ )
{
// error is computed as the distance between a point u_l = (x,y) and the epipolar line of its corresponding point u_r in the second image plus the reverse, so errij = d(u_l, F^T * u_r) + d(u_r, F*u_l)
Point2f u_l = imgpts1_copy[j], // for the purpose of this function, we imagine imgpts1 to be the "left" image and imgpts2 the "right" one. Doesn't make a difference
u_r = imgpts2_copy[j];
float a2 = lines1[j][0], // epipolar line
b2 = lines1[j]1,
c2 = lines1[j][2];
float norm_factor2 = sqrt(pow(a2, 2) + pow(b2, 2));
float a1 = lines[0][j][0],
b1 = lines[0][j]1,
c1 = lines[0][j][2];
float norm_factor1 = sqrt(pow(a1, 2) + pow(b1, 2));
double errij =
fabs(u_l.x * a2 + u_l.y * b2 + c2) / norm_factor2 +
fabs(u_r.x * a1 + u_r.y * b1 + c1) / norm_factor1; // distance of (x,y) to line (a,b,c) = ax + by + c / (a^2 + b^2)
err += errij; // at this point, apply threshold and mark bad matches
}
return err / npt;
}
关键是,抓住基本矩阵,用它来计算所有点的epilines,然后计算距离(这些线以参数形式给出,所以你需要做一些代数来得到距离)。 findFundamentalMat
的结果有些相似。它返回一个掩码,其中对于每个匹配,有一个1
,意味着它用于估计矩阵,或者0
,如果它被抛出。但是像这样估计基本矩阵可能不如使用棋盘那么准确。
答案 1 :(得分:1)
编辑:看起来oarfish打败了我,但我会把它留在这里。
基本矩阵(F)定义了从左图像中的点到右图像中必须位于其上的线的映射,假设完美校准。这是极线,即通过左图像中的点和立体相机对的两个行星的线。有关参考资料,请参阅HZ书籍的these lecture notes和this chapter。
给定左图像和右图像中的一组点对应:(p_L,p_R),来自SURF(或任何其他特征匹配器),并且给定F,来自立体对的对极几何的约束表明p_R应该是谎言在p_L投射到右图像上的极线上,即
实际上,噪声校准误差以及错误的特征匹配会导致非零值。
然而,使用这个想法,您可以通过拒绝这个等式大于某个阈值的特征匹配来执行异常值删除,即拒绝(p_L,p_R)当且仅当:
选择此阈值时,请记住,它是您愿意容忍的极线之间的点的图像空间距离,这在某种意义上是您的极线误差容差。
退化情况:为了在视觉上想象这意味着什么,让我们假设立体声对仅在纯X平移中有所不同。然后,极线是水平的。这意味着您可以通过一条线连接特征匹配的点对,并拒绝那些线斜率不接近零的对。上面的等式是这个想法到任意立体旋转和平移的概括,它由矩阵F来解释。
您的特定图片:您的功能匹配似乎很稀疏。我建议改为使用密集的特征匹配方法,以便在删除离群值后,仍然可以保留足够数量的高质量匹配。我不确定OpenCV中已经实现了哪个密集功能匹配器,但我建议启动here。
答案 2 :(得分:0)
答案 3 :(得分:0)
为了找到基本矩阵,您需要正确的对应关系,但为了获得良好的对应关系,您需要对基本矩阵进行良好估计。这可能听起来像一个不可能的鸡蛋和鸡蛋问题,但有完善的方法来做到这一点; RANSAC。
随机选择一小组对应,使用它们计算基本矩阵(使用7或8点算法),然后测试符合此矩阵的其他对应数量(使用scribbleink描述的方法)测量点和极线之间的距离)。它不断测试一定数量迭代的新对应组合,并选择具有最多内点的那个。
这已经在OpenCV中实现为cv :: findFundamentalMat(http://docs.opencv.org/2.4/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html#findfundamentalmat)。选择方法CV_FM_RANSAC以使用ransac删除不良对应关系。它将输出所有内部对应的列表。
对此的要求是所有点都不在同一平面上。