将2D点转换为3D位置


9

我有一台固定相机,已知cameraMatrixdistCoeffs。我也有一个固定的棋盘,transform并且rotation矢量也使用来计算solvePnP

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

在此处输入图片说明

可以肯定的是,该点的Z为0,但如何获得该点的XY。


借助变换和旋转矢量,您是否能够解释3D中的所有棋盘角?
米卡,

如果您说Z将为0,那么可以确定该点的平面坐标吗?就像“红色方向10厘米,绿色方向15厘米?”
Micka,

@Micka无法正常工作,因为距离相机更近的像素代表更大的区域
EBAG

用透视单应性很容易获得平面坐标。但是,如果您需要以相机为中心的3d空间中的3d点,则必须随后根据旋转和平移矢量来变换平面。
米卡,

您可以提供该点坐标的预期结果吗?
阿卜杜勒·阿齐兹·阿卜杜勒·拉特夫

Answers:


6

您可以通过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;

第三步:

通过计算Z = 0的3d射线与棋盘平面之间的交点找到所需的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;

您的方法效果很好,谢谢:)
EBAG

1

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

首先,不扭曲您的图像。然后使用findHomography计算Homography矩阵,该矩阵将您的像素坐标(图像)转换为真实坐标(欧氏空间,例如cm)。与此类似:

#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 <Point2f>角,vector <Point2f> objectPoints2d; findChessboardCorners(img,patternSize,corners); calcChessboardCorners(patternSize,squareSize,objectPoints2d); ChessboardHomography = findHomography(corners,objectPoints2d,RANSAC);
EBAG

它不起作用,并且返回的坐标不正确
EBAG

即使您将单应性矩阵与位于棋盘[0,0,0]上的像素相乘,它也将返回[-192,
-129,0.33

@EBAG您首先取消存储图像吗?检查objectPoints2d是否正确。事件打印并手动检查。
ma.mehralian
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.