【问题标题】:Given camera matrices, how to find point correspondances using OpenCV?给定相机矩阵,如何使用 OpenCV 找到点对应?
【发布时间】:2016-03-17 08:51:52
【问题描述】:

我正在关注 this tutorial,它使用 Features2D + Homography。如果我知道每个图像的相机矩阵,我该如何优化结果?我尝试了一些图像,但效果不佳。

//编辑
在阅读了一些材料后,我认为我应该先纠正两个图像。但校正并不完美,因此图 1 上的垂直线一般对应于图 2 上的垂直带。有什么好的算法吗?

【问题讨论】:

  • 能否请您发布两张图片的相机矩阵?
  • 我没有计算这些相机矩阵,一个基本矩阵就足够了,对吧?虽然我也没有为它计算基本矩阵。@Micka
  • 我以为你确实有相机的内在和外在,并询问如何使用它们来改善你的结果。你是如何计算校正的?
  • 我有一个棋盘,所以我可以方便地使用findChessboardCornersstereoRectifyUncalibratedundistort。 @米卡
  • 所以相机系统是固定的(包括变焦/对焦)?你有没有把镜头变形?

标签: c++ opencv computer-vision surf


【解决方案1】:

我不确定我是否理解您的问题。您想找到图像之间的对应点,或者您想通过使用相机内在函数来提高匹配的正确性?

原则上,为了使用相机几何来查找匹配项,您需要基本矩阵或基本矩阵,具体取决于您是否了解相机内在函数(即校准的相机)。这意味着,您需要估计相机的相对旋转和平移。然后,通过计算与在一张图像中找到的特征相对应的核线,您需要沿着第二张图像中的这些线进行搜索以找到最佳匹配。但是,我认为简单地依靠自动特征匹配会更好。鉴于基本/基本矩阵,您可以使用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(imgpt@987654322@, 2, F, lines@987654323@); 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 = lines@987654324@[j][0], // epipolar line b2 = lines@987654325@[j]@987654326@, c2 = lines@987654327@[j][2]; float norm_factor2 = sqrt(pow(a2, 2) + pow(b2, 2)); float a1 = lines[0][j][0], b1 = lines[0][j]@987654328@, 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 使用 RANSAC 方法的结果有些相似。它返回一个掩码,其中对于每个匹配项都有一个1,这意味着它用于估计矩阵,或者一个0(如果它被抛出)。但是像这样估计基本矩阵可能不如使用棋盘准确。

【讨论】:

  • 我的最终目标是找到图像之间的对应点。而SurfFeatureDetector 在我的情况下效果不佳,所以我想通过使用基本矩阵来提高正确性。
  • @Zen 但我看不到如何在没有可靠对应关系的情况下获得基本矩阵。也许对于图像之间如此小的运动,您可以尝试光流。
  • 我可以在狗附近放一个棋盘,从中我可以得到一个基本矩阵。但是我不知道我可以用基本矩阵做什么来优化匹配算法,例如FlannBasedMatcher
  • @Zen 我用如何使用基本矩阵的想法更新了答案,但你的里程可能会有所不同。
【解决方案2】:

编辑:看起来 oarfish 打败了我,但我会把它留在这里。

假设完美校准,基本矩阵 (F) 定义了从左图像中的点到右图像中对应点必须位于的线的映射。这是极线,即通过左图像中的点和立体相机对的两个极线的线。如需参考,请参阅 HZ 书籍的 these lecture notesthis chapter

给定左右图像中的一组点对应关系:(p_L, p_R),来自 SURF(或任何其他特征匹配器),并给定 F,立体对的核几何约束表明 p_R 应该位于在 p_L 投影到右图的核线上,即

在实践中,来自噪声的校准误差以及错误的特征匹配会导致非零值。

但是,使用这个想法,您可以通过拒绝那些该方程大于某个阈值的特征匹配来执行异常值移除,即当且仅当:拒绝 (p_L, p_R):

在选择此阈值时,请记住,在图像空间中,您愿意容忍的点与极线的距离,在某种意义上就是您的极线误差容限。

退化情况:为了直观地想象这意味着什么,让我们假设立体对仅在纯 X 平移上有所不同。然后极线是水平的。这意味着您可以通过一条线连接特征匹配点对,并拒绝那些线斜率不接近于零的点对。上面的方程是这个想法对任意立体旋转和平移的推广,这是由矩阵 F 解释的。

您的特定图片:看起来您的特征匹配很少。我建议改为使用密集特征匹配方法,以便在去除异常值后,您仍然可以获得足够数量的优质匹配。我不确定 OpenCV 中已经实现了哪个密集特征匹配器,但我建议从 here 开始。

【讨论】:

    【解决方案3】:

    给出你的图片,你正在尝试做立体匹配。

    这个page 会很有帮助。您可以使用 stereoCalibrate 然后 stereoRectify 完成您想要的校正。

    结果(来自文档):

    【讨论】:

    • 我知道如何应用立体校正,但我不知道如何在图像校正后找到点对应。顺便说一句,您知道用于计算相机矩阵的 6 个 3d 点的要求是什么吗?没有4个点在同一个平面上?
    【解决方案4】:

    为了找到基本矩阵,您需要正确的对应关系,但为了获得良好的对应关系,您需要对基本矩阵进行良好的估计。这听起来像是一个不可能的先有鸡还是先有蛋的问题,但有成熟的方法可以做到这一点; 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 删除不良对应关系。它将输出所有内部对应关系的列表。

    这样做的要求是所有点都不在同一平面上。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-01-27
      • 2012-10-15
      • 1970-01-01
      • 1970-01-01
      • 2017-11-07
      • 2017-01-02
      • 1970-01-01
      • 2015-03-16
      相关资源
      最近更新 更多