选择正确的基本矩阵

Dap*_*nee 5 matlab opencv computer-vision

我想使用 Matlab 自己编写 sfm 管道,因为我需要一些 opencv 函数不提供的输出。不过,我使用 opencv 进行比较。

Opencv 函数[E,mask] = cv.findEssentialMat(points1, points2, 'CameraMatrix',K, 'Method','Ransac');使用 Nister 的五点算法和 RANSAC 提供基本的矩阵解。

使用以下方法找到内点指数:InliersIndices=find(mask>0);

我使用了 Nister 算法的 Matlab 实现:

Fivepoint_算法_代码

该函数的调用如下:

[E_all, R_all, t_all, Eo_all] = five_point_algorithm( pts1, pts2, K, K);
Run Code Online (Sandbox Code Playgroud)

该算法最多输出 10 个基本矩阵的解。但是,我遇到了以下问题:

  1. 上述实现仅用于完美对应(没有 Ransac),我使用 向算法提供 5 个对应InliersIndices,输出的基本矩阵(最多 10 个)都与 Opencv 返回的矩阵不同。
  2. 所有返回的基本矩阵都应该是解,那么为什么当我使用以下函数对每个矩阵进行三角测量时,我没有获得相同的 3D 点?
  3. 如何选择正确的基本矩阵解决方案?

我使用matlab工具箱的功能进行三角测量

投影矩阵:

P1=K*[eye(3) [0;0;0]];
P2=K*[R_all{i} t_all{i}];

[pts3D,rep_error] = triangulate(pts1', pts2', P1',P2');
Run Code Online (Sandbox Code Playgroud)

编辑

返回的E来自[E,mask] = cv.findEssentialMat(points1, points2, 'CameraMatrix',K, 'Method','Ransac');

E =

    0.0052   -0.7068    0.0104
    0.7063    0.0050   -0.0305
   -0.0113    0.0168    0.0002
Run Code Online (Sandbox Code Playgroud)

对于 5 点 Matlab 实现,从内点中获取 5 个随机索引,如下所示:

pts1 =

  736.7744  740.2372  179.2428  610.5297  706.8776
  112.2673  109.9687   45.7010   91.4371   87.8194

pts2 =

  722.3037  725.3770  150.3997  595.3550  692.5383
  111.7898  108.6624   43.6847   90.6638   86.8139

K =

  723.3631    7.9120  601.7643
   -3.8553  719.6517  182.0588
    0.0075    0.0044    1.0000
Run Code Online (Sandbox Code Playgroud)

返回4个解决方案:

E1 =

   -0.2205    0.9436   -0.1835
    0.8612    0.2447   -0.1531
    0.4442   -0.0600   -0.0378

 E2 =

   -0.2153    0.9573    0.1626
    0.8948    0.2456   -0.3474
    0.1003    0.1348   -0.0306
E3 =

    0.0010   -0.9802   -0.0957
    0.9768    0.0026   -0.1912
    0.0960    0.1736   -0.0019
E4 =

   -0.0005   -0.9788   -0.1427
    0.9756    0.0021   -0.1658
    0.1436    0.1470   -0.0030
Run Code Online (Sandbox Code Playgroud)

编辑2:

使用基本矩阵 E、R 和 t 进行三角测量时返回的 pts1 和 pts2[R, t] = cv.recoverPose(E, p1, p2,'CameraMatrix',K);

X1 =

   -0.0940    0.0478   -0.4984
   -0.0963    0.0497   -0.4987
    0.3033    0.1009   -0.5202
   -0.0065    0.0636   -0.5053
   -0.0737    0.0653   -0.5011
Run Code Online (Sandbox Code Playgroud)

R =

   -0.9977   -0.0063    0.0670
    0.0084   -0.9995    0.0305
    0.0667    0.0310    0.9973
Run Code Online (Sandbox Code Playgroud)

t =

    0.0239
    0.0158
    0.9996
Run Code Online (Sandbox Code Playgroud)

当用 Matlab 代码进行三角测量时,选择的解决方案是E_all{2}

R_all{2}=

   -0.8559   -0.2677    0.4425
   -0.1505    0.9475    0.2821
   -0.4948    0.1748   -0.8512
Run Code Online (Sandbox Code Playgroud)

t_all{2}=

   -0.1040
   -0.1355
    0.9853

X2 =

    0.1087   -0.0552    0.5762
    0.1129   -0.0578    0.5836
    0.4782    0.1582   -0.8198
    0.0028   -0.0264    0.2099
    0.0716   -0.0633    0.4862
Run Code Online (Sandbox Code Playgroud)

做的时候

X1./X2

ans =

   -0.8644   -0.8667   -0.8650
   -0.8524   -0.8603   -0.8546
    0.6343    0.6376    0.6346
   -2.3703   -2.4065   -2.4073
   -1.0288   -1.0320   -1.0305
Run Code Online (Sandbox Code Playgroud)

三角测量的 3D 点之间存在几乎恒定的比例因子。然而,旋转矩阵不同,并且平移之间没有比例因子。

 t./t_all{2}=
       -0.2295
       -0.1167
        1.0145
Run Code Online (Sandbox Code Playgroud)

这使得绘制的轨迹错误

rfa*_*bri 2

回答您的编号问题:

  1. 请注意,Nister 的 5 点算法有很多实现,但大多数都效果不佳。个人经验和同事未发表的工作表明 OpenCV 没有很好的实现。Bundler和其他工作SfM 管道中的开放实现在实践中效果更好(但还有很大改进空间)。

  2. 这 10 个解只是某个多项式方程的零点。就多项式方程可以描述问题而言,这 10 个解都将方程归零。该方程并没有描述这 10 个点是真实的,或者与 5 个点对应关系对应的 3D 点对于每个解必须相同,而只是有一些 3D 点(对于每个解)投影到 5 个点。点,甚至不考虑 3D 点是否位于相应摄像机的前面。此外,很可能有两组 3D 点和摄像机碰巧生成 5 个点的相同图像,因此您必须使用其他一些过程(如下)将它们清除。

  3. 在 10 个复杂的解决方案中选择正确的解决方案通常通过多种技术来完成:

    • 放弃会导致纯复杂点或具有负深度的 3D 点的解决方案(当前 Bundler 不执行最后一项检查)
    • 由于其他原因放弃非物理解决方案(您可能必须为您的应用程序自行执行其中一些操作)
    • 更常见的过程:对于每个剩余的解决方案,检查哪一个与其他对应关系更一致。在真实的系统中,您不知道哪些附加对应关系是正确的,哪些是纯粹的垃圾。因此,对每个解决方案运行 RANSAC,并保留具有最多内点的解决方案。这计算量很大,因此应作为最后的手段使用。

您可以在 file 中看到 Bundler 如何执行此操作5point.c line 668

    generate_Ematrix_hypotheses(5, r_pts_inner, l_pts_inner, &num_hyp, E);

    for (i = 0; i < num_hyp; i++) {
        int best_inlier;
        double score = 0.0;

        double E2[9], tmp[9], F[9];
        memcpy(E2, E + 9 * i, 9 * sizeof(double));
        E2[0] = -E2[0];
        E2[1] = -E2[1];
        E2[3] = -E2[3];
        E2[4] = -E2[4];
        E2[8] = -E2[8];

        matrix_transpose_product(3, 3, 3, 3, K2_inv, E2, tmp);
        matrix_product(3, 3, 3, 3, tmp, K1_inv, F);

        inliers = evaluate_Ematrix(n, r_pts, l_pts, // r_pts_norm, l_pts_norm, 
                                   thresh_norm, F, // E + 9 * i, 
                                   &best_inlier, &score);

        if (inliers > max_inliers ||
            (inliers == max_inliers && score < min_score)) {
            best = 1;
            max_inliers = inliers;
            min_score = score;
            memcpy(E_best, E + 9 * i, sizeof(double) * 9);
            r_best = r_pts_norm[best_inlier];
            l_best = l_pts_norm[best_inlier];
        }

        inliers_hyp[i] = inliers;
    }
Run Code Online (Sandbox Code Playgroud)