【问题标题】:Converting 2D point to 3D location将 2D 点转换为 3D 位置
【发布时间】:2024-04-26 00:50:02
【问题描述】:

我有一个固定摄像头,已知 cameraMatrixdistCoeffs。我也有一个固定的棋盘,transformrotation 向量也是使用solvePnP 计算的。

我想知道如何在棋盘所在的同一平面上获得 2D 点的 3D 位置,如下图所示:

有一点是肯定的,那个点的Z是0,但是如何得到那个点的XY

【问题讨论】:

  • 用你的变换和旋转向量,你能用 3D 解释所有的棋盘角吗?
  • 如果您说 Z 将为 0,您可以只获取该点的平面坐标吗?比如“红色方向10厘米,绿色方向负15厘米?
  • @Micka 这行不通,因为靠近相机的像素代表更大的区域
  • 用petspective单应性很容易得到平面坐标。但是,如果您需要以相机为中心的 3d 空间中的 3d 点,则必须在之后根据您的旋转和平移向量来变换平面。
  • 你能提供这个点坐标的预期结果吗?

标签: opencv 2d-3d-conversion


【解决方案1】:

您可以通过 3 个简单的步骤解决此问题:

第 1 步:

通过反转相机投影模型,计算对应于给定 2d 图像点的射线的 3d 方向矢量,以相机的坐标系表示:

std::vector<cv::Point2f> imgPt = {{u,v}}; // Input image point
std::vector<cv::Point2f> normPt;
cv::undistortPoints     (imgPt, normPt, cameraMatrix, distCoeffs);
cv::Matx31f ray_dir_cam(normPt[0].x, normPt[0].y, 1);
// 'ray_dir_cam' is the 3d direction of the ray in camera coordinate frame
// In camera coordinate frame, this ray originates from the camera center at (0,0,0)

第 2 步:

使用相机和棋盘之间的相对位姿,计算此射线在附加到棋盘的坐标系中的向量的 3d 方向:

// solvePnP typically gives you 'rvec_cam_chessboard' and 'tvec_cam_chessboard'
// Inverse this pose to get the pose mapping camera coordinates to chessboard coordinates
cv::Matx33f R_cam_chessboard;
cv::Rodrigues(rvec_cam_chessboard, R_cam_chessboard);
cv::Matx33f R_chessboard_cam = R_cam_chessboard.t();
cv::Matx31f t_cam_chessboard = tvec_cam_chessboard;
cv::Matx31f pos_cam_wrt_chessboard = -R_chessboard_cam*t_cam_chessboard;
// Map the ray direction vector from camera coordinates to chessboard coordinates
cv::Matx31f ray_dir_chessboard = R_chessboard_cam * ray_dir_cam;

第 3 步:

通过计算 3d 射线与 Z=0 的棋盘平面之间的交点来找到所需的 3d 点:

// Expressed in the coordinate frame of the chessboard, the ray originates from the
// 3d position of the camera center, i.e. 'pos_cam_wrt_chessboard', and its 3d
// direction vector is 'ray_dir_chessboard'
// Any point on this ray can be expressed parametrically using its depth 'd':
// P(d) = pos_cam_wrt_chessboard + d * ray_dir_chessboard
// To find the intersection between the ray and the plane of the chessboard, we
// compute the depth 'd' for which the Z coordinate of P(d) is equal to zero
float d_intersection = -pos_cam_wrt_chessboard.val[2]/ray_dir_chessboard.val[2];
cv::Matx31f intersection_point = pos_cam_wrt_chessboard + d_intersection * ray_dir_chessboard;

【讨论】:

    【解决方案2】:

    由于您的情况仅限于平原,简单的方法是使用 Homography。

    首先undistort您的图像。然后使用findHomography 计算将像素坐标(图像)转换为真实坐标(欧几里得空间,例如以厘米为单位)的单应矩阵。类似于这个的东西:

    #include <opencv2/calib3d.hpp>
    //...
    
    //points on undistorted image (in pixel). more is better
    vector<Point2f>  src_points = { Point2f(123,321), Point2f(456,654), Point2f(789,987), Point2f(123,321) };
    //points on chessboard (e.g. in cm)
    vector<Point2f>  dst_points = { Point2f(0, 0), Point2f(12.5, 0), Point2f(0, 16.5), Point2f(12.5, 16.5) }; 
    Mat H = findHomography(src_points, dst_points, RANSAC);
    
    //print euclidean coordinate of new point on undistorted image (in pixel)
    cout << H * Mat(Point3d(125, 521, 0)) << endl;
    

    【讨论】:

    • 我按照你说的做了:vector corners, vector objectPoints2d; findChessboardCorners(img, patternSize, 角); calcChessboardCorners(patternSize, squareSize, objectPoints2d); chessboardHomography = findHomography(corners, objectPoints2d, RANSAC);
    • 不行,返回的坐标不正确
    • 即使将单应矩阵与位于棋盘 [0,0,0] 上的像素相乘,它也会返回 [-192, -129, 0.33]
    • @EBAG 你先解散图像吗?检查 objectPoints2d 是否正确。事件打印并手动检查。