Conversion d'un point 2D en emplacement 3D

9

J'ai une caméra fixe avec connu cameraMatrixet distCoeffs. J'ai également un échiquier qui est fixe aussi, transformet le rotationvecteur est également calculé en utilisant solvePnP.

Je me demande comment est possible d'obtenir l'emplacement 3D d'un point 2D sur le même plan que l'échiquier, comme l'image ci-dessous:

entrez la description de l'image ici

Une chose est sûre, c'est que le Z de ce point est 0, mais comment obtenir X et Y de ce point.

EBAG
la source
avec vos vecteurs de transformation et de rotation, êtes-vous capable d'expliquer tous les coins de l'échiquier en 3D?
Micka
si vous dites que Z sera 0, est-ce que vous pouvez simplement obtenir les coordonnées planes de ce point? Comme "aller 10 cm dans la direction rouge et moins 15 cm dans la direction verte?
Micka
@Micka, cela ne fonctionnera pas, car les pixels plus proches de la caméra représentent une zone plus grande
EBAG
il est facile d'obtenir les coordonnées de l'avion avec une homographie d'animaux. Mais si vous avez besoin des points 3D dans votre espace 3D centré sur la caméra, vous devez ensuite transformer l'avion en fonction de vos vecteurs de rotation et de translation.
Micka
Pouvez-vous fournir le résultat attendu de ces coordonnées de point?
AbdelAziz AbdelLatef

Réponses:

6

Vous pouvez résoudre ce problème en 3 étapes simples:

Étape 1:

Calculez le vecteur de direction 3D, exprimé dans le cadre de coordonnées de la caméra, du rayon correspondant au point d'image 2D donné en inversant le modèle de projection de la caméra:

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)

Étape 2:

Calculez la direction 3D du vecteur de ce rayon dans le cadre de coordonnées attaché à l'échiquier, en utilisant la pose relative entre la caméra et l'échiquier:

// 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;

Étape 3:

Trouvez le point 3D souhaité en calculant l'intersection entre le rayon 3D et le plan de l'échiquier avec 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
la source
Votre méthode fonctionne parfaitement, merci :)
EBAG
1

Depuis votre cas limité aux plaines, le moyen le plus simple est d'utiliser l'homographie.

Déformez d' abord votre image. Utilisez ensuite findHomography pour calculer la matrice d'homographie qui transforme vos coordonnées de pixel (image) en coordonnées réelles (espace euclidien, par exemple en cm). Quelque chose de similaire à celui-ci:

#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
la source
J'ai fait ce que vous avez dit: vecteur <Point2f> coins, vecteur <Point2f> objectPoints2d; findChessboardCorners (img, patternSize, coins); calcChessboardCorners (patternSize, squareSize, objectPoints2d); chessboardHomography = findHomography (coins, objectPoints2d, RANSAC);
EBAG
cela ne fonctionne pas, et la coordonnée qu'il renvoie n'est pas correcte
EBAG
même si vous multipliez la matrice d'homographie par le pixel qui se trouve sur l'échiquier [0,0,0], elle renverra [-192, -129, 0,33]
EBAG
@EBAG est-ce que vous ne commencez pas par décomposer l'image? vérifiez que objectPoints2d soit correct. Imprimez l'événement et vérifiez-les manuellement.
ma.mehralian du