c++ - 给定相机矩阵,如何使用 OpenCV 找到点对应关系?

标签 c++ opencv computer-vision surf

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

//编辑
看了一些资料后,我想我应该先纠正两个图像。但校正并不完美,因此图像 1 上的垂直线通常对应于图像 2 上的垂直带。有什么好的算法吗?

first image second image rectification image

最佳答案

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

原则上,为了使用相机几何形状来查找匹配,您将需要基本或基本矩阵,具体取决于您是否了解相机内在特性(即校准相机)。这意味着,您需要估计相机的相对旋转和平移。然后,通过计算与一张图像中找到的特征相对应的极线,您需要沿着第二张图像中的这些线进行搜索以找到最佳匹配。不过,我认为简单地依靠自动特征匹配会更好。给定基本/本质矩阵,您可以尝试一下 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]);
    }
    
  • 更复杂的过滤方法是计算第一帧中每个关键点的重投影误差。这意味着计算第二个图像中相应的极线,然后检查其假定的匹配点距该线有多远。丢弃距离超过某个阈值的那些点将删除与外极几何形状不兼容的匹配(我认为这是已知的)。计算错误可以这样完成(老实说,我不记得从哪里获取此代码,我可能对其进行了一些修改,当代码位于列表内时,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]; <p></p>
    
    <p>// 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++;
      } 
    }</p>
    
    <p>Mat imgpt[2] = { Mat(imgpts1_copy), Mat(imgpts2_copy) };
    computeCorrespondEpilines(imgpt[0], 1, F, lines[0]);
    computeCorrespondEpilines(imgpt<a href="http://docs.opencv.org/2.4/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html#correctmatches" rel="noreferrer noopener nofollow">1</a>, 2, F, lines<a href="http://docs.opencv.org/2.4/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html#correctmatches" rel="noreferrer noopener nofollow">1</a>);
    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<a href="http://docs.opencv.org/2.4/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html#correctmatches" rel="noreferrer noopener nofollow">1</a>[j][0], // epipolar line
            b2 = lines<a href="http://docs.opencv.org/2.4/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html#correctmatches" rel="noreferrer noopener nofollow">1</a>[j]<a href="http://docs.opencv.org/2.4/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html#correctmatches" rel="noreferrer noopener nofollow">1</a>,
            c2 = lines<a href="http://docs.opencv.org/2.4/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html#correctmatches" rel="noreferrer noopener nofollow">1</a>[j][2];
      float norm_factor2 = sqrt(pow(a2, 2) + pow(b2, 2));
      float a1 = lines[0][j][0],
            b1 = lines[0][j]<a href="http://docs.opencv.org/2.4/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html#correctmatches" rel="noreferrer noopener nofollow">1</a>,
            c1 = lines[0][j][2];
      float norm_factor1 = sqrt(pow(a1, 2) + pow(b1, 2));</p>
    
    <p>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
    }</p>
    
    <p>return err / npt;
    }
    </p>
    关键是,获取基本矩阵,用它来计算所有点的线,然后计算距离(线以参数形式给出,因此您需要做一些代数来获得距离)。
    这与使用 RANSAC 方法的 findFundamentalMat 的结果有些相似。它返回一个掩码,其中对于每个匹配,都有一个1,这意味着它用于估计矩阵,或者如果它被丢弃,则有一个0。但像这样估计基本矩阵可能不如使用棋盘准确。

关于c++ - 给定相机矩阵,如何使用 OpenCV 找到点对应关系?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36055581/

相关文章:

python - 将全局特征添加到 SIFT 特征以查找图像相似性

tensorflow - 值错误: Didn't find op for builtin opcode 'RESIZE_BILINEAR' version '3' Registration failed

opencv - 绘制 calcOpticalFlow 的输出特征向量时跟踪不准确

c++ - 如何在 Opencv 中显示垫子

python - 角点检测算法为倾斜边缘提供非常高的值(value)?

machine-learning - 更改 TensorFlow Cifar10 示例后精度较低

java - 在哪里可以找到 Java 和/或 C++ 的快速回顾?

c++ - 如何在 C++ 中实现一个函数作为一些可选参数?

c++ - Linux 上的 SymbolicC++ 错误

c++ - O3是固定的优化序列吗?如何更改 LLVM IR 中的帧指针值?