Преобразование 2D-точки в 3D-местоположение

9

У меня есть фиксированная камера с известным cameraMatrixи distCoeffs. У меня также есть шахматная доска, которая тоже исправлена, transformи rotationвектор также рассчитывается с использованием solvePnP.

Мне интересно, как можно получить трехмерное местоположение 2D-точки на той же плоскости, что и шахматная доска, как на картинке ниже:

введите описание изображения здесь

Одно можно сказать наверняка, что Z этой точки равен 0, но как получить X и Y этой точки.

EBAG
источник
с вашими векторами преобразования и вращения, вы можете объяснить все углы шахматной доски в 3D?
Micka
если вы говорите, что Z будет 0, нормально ли вам просто получить координаты плоскости этой точки? Как «собирается 10 см в красной стороне и минус 15 см в зеленом направлении?
Micka
@ Микка, это не сработает, потому что пиксели, расположенные ближе к камере, представляют большую площадь
EBAG
легко получить координаты самолета с помощью домашней перспективы. Но если вам нужны трехмерные точки в трехмерном пространстве, центрированном вашей камерой, вы должны впоследствии преобразовать плоскость в соответствии с векторами вращения и перемещения.
Micka
Можете ли вы предоставить ожидаемый результат этой точки координат?
Абдель-Азиз Абдель-Латеф

Ответы:

6

Вы можете решить это за 3 простых шага:

Шаг 1:

Вычислить вектор направления 3d, выраженный в системе координат камеры, луча, соответствующего заданной точке 2d изображения, путем инвертирования модели проекции камеры:

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:

Вычислите трехмерное направление вектора этого луча в системе координат, прикрепленной к шахматной доске, используя относительную позу между камерой и шахматной доской:

// 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 точку, вычислив пересечение между 3d-лучом и плоскостью шахматной доски с Z = 0:

// 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;
BConic
источник
Ваш метод работает отлично, спасибо :)
EBAG
1

Поскольку ваш случай ограничен равнинами, простой способ заключается в использовании гомографии.

Сначала не искажай свое изображение. Затем используйте 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;
ma.mehralian
источник
Я сделал то, что вы сказали: vector <Point2f> corners, vector <Point2f> objectPoints2d; findChessboardCorners (img, patternSize, corners); calcChessboardCorners (patternSize, squareSize, objectPoints2d); chessboardHomography = findHomography (уголки, objectPoints2d, RANSAC);
EBAG
это не работает, и координата, которую это возвращает, не является правильной
EBAG
даже если вы умножите матрицу гомографии на пиксель, расположенный на шахматной доске [0,0,0], он вернет [-192, -129, 0,33]
EBAG
@EBAG сначала вы удаляете изображение? проверьте правильность objectPoints2d. Событие распечатайте и проверьте их вручную.
ma.mehralian