40

我试图用 OpenCV 对一些点进行三角测量,我发现了这个cv::triangulatePoints()函数。问题是几乎没有文档或示例。

我对此有些怀疑。

  1. 它使用什么方法? 我对三角测量进行了一项小型研究,有几种方法(线性、线性 LS、特征、迭代 LS、迭代特征,...),但我找不到它在 OpenCV 中使用的方法。

  2. 我应该如何使用它?似乎作为输入,它需要一个投影矩阵和3xN齐次2D点。我将它们定义为std::vector<cv::Point3d> pnts,但作为输出它需要4xN数组,显然我无法创建 astd::vector<cv::Point4d>因为它不存在,那么我应该如何定义输出向量?

对于我尝试的第二个问题:cv::Mat pnts3D(4,N,CV_64F);and cv::Mat pnts3d;,似乎都不起作用(它引发异常)。

4

6 回答 6

60

1.-使用的方法是最小二乘法。还有比这更复杂的算法。它仍然是最常见的一种,因为其他方法在某些情况下可能会失败(即,如果点在平面上或无限上,则其他方法会失败)。

该方法可以在Richard Hartley 和 Andrew Zisserman的计算机视觉中的多视图几何中找到(p312)

2.-用法

cv::Mat pnts3D(1,N,CV_64FC4);
cv::Mat cam0pnts(1,N,CV_64FC2);
cv::Mat cam1pnts(1,N,CV_64FC2);

用图像中的点填充 2 个 chanel 点矩阵。

cam0cam1是相机矩阵(内在和Mat3x4外在参数)。您可以通过乘以 A*RT 来构造它们,其中 A 是内在参数矩阵,RT 是旋转平移 3x4 姿势矩阵。

cv::triangulatePoints(cam0,cam1,cam0pnts,cam1pnts,pnts3D);

注意pnts3D定义时需要是 4 通道1xN cv::Mat,否则抛出异常,但结果是cv::Mat(4,N,cv_64FC1)矩阵。真的很混乱,但这是我没有例外的唯一方法。


更新:从版本 3.0 或可能更早的版本开始,这不再是正确的,并且pnts3D也可以是类型Mat(4,N,CV_64FC1)或可能完全为空(像往常一样,它是在函数内部创建的)。

于 2013-04-30T12:27:41.750 回答
16

@Ander Biguri 回答的一个小补充。您应该在非编辑图像上获取图像点,并在and上undistort调用,因为期望归一化坐标中的 2D 点(独立于相机)并且并且应该只是您不需要的[R|t^T] 矩阵将它与A 相乘undistortPoints()cam0pntscam1pntscv::triangulatePointscam0cam1

于 2015-04-23T10:05:03.213 回答
4

感谢安德·比古里!他的回答对我帮助很大。但我总是更喜欢 std::vector 的替代方案,我编辑了他的解决方案:

std::vector<cv::Point2d> cam0pnts;
std::vector<cv::Point2d> cam1pnts;
// You fill them, both with the same size...

// You can pick any of the following 2 (your choice)
// cv::Mat pnts3D(1,cam0pnts.size(),CV_64FC4);
cv::Mat pnts3D(4,cam0pnts.size(),CV_64F);

cv::triangulatePoints(cam0,cam1,cam0pnts,cam1pnts,pnts3D);

所以你只需要在积分中做 emplace_back 。N主要优点:在开始填充之前您不需要知道尺寸。可惜没有cv::Point4f,所以pnts3D一定是cv::Mat...

于 2017-05-08T02:00:08.943 回答
3

我尝试了 cv::triangulatePoints,但它以某种方式计算垃圾。我被迫手动实现一个线性三角剖分方法,它为三角剖分的 3D 点返回一个 4x1 矩阵:

Mat triangulate_Linear_LS(Mat mat_P_l, Mat mat_P_r, Mat warped_back_l, Mat warped_back_r)
{
    Mat A(4,3,CV_64FC1), b(4,1,CV_64FC1), X(3,1,CV_64FC1), X_homogeneous(4,1,CV_64FC1), W(1,1,CV_64FC1);
    W.at<double>(0,0) = 1.0;
    A.at<double>(0,0) = (warped_back_l.at<double>(0,0)/warped_back_l.at<double>(2,0))*mat_P_l.at<double>(2,0) - mat_P_l.at<double>(0,0);
    A.at<double>(0,1) = (warped_back_l.at<double>(0,0)/warped_back_l.at<double>(2,0))*mat_P_l.at<double>(2,1) - mat_P_l.at<double>(0,1);
    A.at<double>(0,2) = (warped_back_l.at<double>(0,0)/warped_back_l.at<double>(2,0))*mat_P_l.at<double>(2,2) - mat_P_l.at<double>(0,2);
    A.at<double>(1,0) = (warped_back_l.at<double>(1,0)/warped_back_l.at<double>(2,0))*mat_P_l.at<double>(2,0) - mat_P_l.at<double>(1,0);
    A.at<double>(1,1) = (warped_back_l.at<double>(1,0)/warped_back_l.at<double>(2,0))*mat_P_l.at<double>(2,1) - mat_P_l.at<double>(1,1);
    A.at<double>(1,2) = (warped_back_l.at<double>(1,0)/warped_back_l.at<double>(2,0))*mat_P_l.at<double>(2,2) - mat_P_l.at<double>(1,2);
    A.at<double>(2,0) = (warped_back_r.at<double>(0,0)/warped_back_r.at<double>(2,0))*mat_P_r.at<double>(2,0) - mat_P_r.at<double>(0,0);
    A.at<double>(2,1) = (warped_back_r.at<double>(0,0)/warped_back_r.at<double>(2,0))*mat_P_r.at<double>(2,1) - mat_P_r.at<double>(0,1);
    A.at<double>(2,2) = (warped_back_r.at<double>(0,0)/warped_back_r.at<double>(2,0))*mat_P_r.at<double>(2,2) - mat_P_r.at<double>(0,2);
    A.at<double>(3,0) = (warped_back_r.at<double>(1,0)/warped_back_r.at<double>(2,0))*mat_P_r.at<double>(2,0) - mat_P_r.at<double>(1,0);
    A.at<double>(3,1) = (warped_back_r.at<double>(1,0)/warped_back_r.at<double>(2,0))*mat_P_r.at<double>(2,1) - mat_P_r.at<double>(1,1);
    A.at<double>(3,2) = (warped_back_r.at<double>(1,0)/warped_back_r.at<double>(2,0))*mat_P_r.at<double>(2,2) - mat_P_r.at<double>(1,2);
    b.at<double>(0,0) = -((warped_back_l.at<double>(0,0)/warped_back_l.at<double>(2,0))*mat_P_l.at<double>(2,3) - mat_P_l.at<double>(0,3));
    b.at<double>(1,0) = -((warped_back_l.at<double>(1,0)/warped_back_l.at<double>(2,0))*mat_P_l.at<double>(2,3) - mat_P_l.at<double>(1,3));
    b.at<double>(2,0) = -((warped_back_r.at<double>(0,0)/warped_back_r.at<double>(2,0))*mat_P_r.at<double>(2,3) - mat_P_r.at<double>(0,3));
    b.at<double>(3,0) = -((warped_back_r.at<double>(1,0)/warped_back_r.at<double>(2,0))*mat_P_r.at<double>(2,3) - mat_P_r.at<double>(1,3));
    solve(A,b,X,DECOMP_SVD);
    vconcat(X,W,X_homogeneous);
    return X_homogeneous;
}

输入参数是两个 3x4 相机投影矩阵和对应的左/右像素对 (x,y,w)。

于 2014-09-12T15:09:12.587 回答
0

或者,您可以使用此处实施的 Hartley & Zisserman 的方法:http ://www.morethantechnical.com/2012/01/04/simple-triangulation-with-opencv-from-harley-zisserman-w-code/

于 2013-10-11T09:26:35.443 回答
0

除了 Ginés Hidalgo 的评论,

如果您进行了立体校准并且可以从那里准确估计基本矩阵,该矩阵是基于棋盘计算的。

使用correctMatches函数细化检测到的关键点

std::vector<cv::Point2f> pt_set1_pt_c, pt_set2_pt_c;
cv::correctMatches(F,pt_set1_pt,pt_set2_pt,pt_set1_pt_c,pt_set2_pt_c)
于 2021-12-21T14:41:34.137 回答