Convertendo ponto 2D em localização 3D


9

Eu tenho uma câmera fixa com conhecidos cameraMatrixe distCoeffs. Eu também tenho um tabuleiro de xadrez que também é fixo transforme o rotationvetor e também são calculados usando solvePnP.

Gostaria de saber como é possível obter a localização 3D de um ponto 2D no mesmo plano em que o tabuleiro de xadrez está localizado, como na figura abaixo:

insira a descrição da imagem aqui

Uma coisa é certa: o Z desse ponto é 0, mas como obter X e Y desse ponto.


com seus vetores de transformação e rotação, você consegue explicar todos os cantos do tabuleiro de xadrez em 3D?
Micka

se você disser que Z será 0, está correto obter as coordenadas planas desse ponto? Como "indo 10 cm na direção vermelha e menos 15 cm na direção verde?"
Micka

@Micka isso não vai funcionar, porque os pixels mais próximos da câmera representam área maior
EBAG

é fácil obter as coordenadas do avião com uma homografia em perspectiva. Mas se você precisar dos pontos 3d no espaço 3d centralizado em sua câmera, precisará transformar o plano de acordo com seus vetores de rotação e conversão posteriormente.
Micka

Você pode fornecer o resultado esperado dessas coordenadas de ponto?
AbdelAziz AbdelLatef 24/10/19

Respostas:


6

Você pode resolver isso com 3 etapas simples:

Passo 1:

Calcule o vetor de direção 3d, expresso no quadro de coordenadas da câmera, do raio correspondente ao ponto de imagem 2D especificado, invertendo o modelo de projeção da câmera:

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)

Passo 2:

Calcule a direção 3d do vetor desse raio no quadro de coordenadas anexado ao tabuleiro de xadrez, usando a pose relativa entre a câmera e o tabuleiro de xadrez:

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

Etapa 3:

Encontre o ponto 3d desejado calculando a interseção entre o raio 3d e o plano do tabuleiro de xadrez com 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;

Seu método funciona perfeitamente, graças :)
EBAG

1

Como o seu caso é limitado às planícies, a maneira mais simples é usar a homografia.

Primeiro, não distorça sua imagem. Em seguida, use findHomography para calcular a matriz de homografia que transforma sua coordenada de pixel (imagem) em coordenada real (espaço euclidiano, por exemplo, em cm). Algo semelhante a este:

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

Fiz o que você disse: vector <Point2f> cantos, vector <Point2f> objectPoints2d; findChessboardCorners (img, patternSize, cantos); calcChessboardCorners (patternSize, squareSize, objectPoints2d); chessboardHomography = findHomography (cantos, objectPoints2d, RANSAC);
EBAG 24/10/19

ele não funciona, e a coordenada ele retorna não é correto
EBAG

mesmo se você multiplicar a matriz de homografia pelo pixel que está localizado no tabuleiro de xadrez [0,0,0], ele retornará [-192, -129, 0,33]
EBAG 24/19

@EBAG você remove a imagem primeiro? verifique objectPoints2d estar correto. Evento imprima e verifique-os manualmente.
Ma.mehralian
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.