82 votes

Calcul des coordonnées x,y (3D) à partir d'un point de l'image

J'ai pour tâche de localiser un objet dans un système de coordonnées 3D. Comme je dois obtenir des coordonnées X et Y presque exactes, j'ai décidé de suivre un marqueur de couleur dont la coordonnée Z est connue et qui sera placé au sommet de l'objet en mouvement, comme la balle orange sur cette image : undistored

D'abord, j'ai fait le calibrage de la caméra pour obtenir les paramètres intrinsèques et ensuite j'ai utilisé cv::solvePnP pour obtenir les vecteurs de rotation et de translation comme dans le code suivant :

std::vector<cv::Point2f> imagePoints;
std::vector<cv::Point3f> objectPoints;
//img points are green dots in the picture
imagePoints.push_back(cv::Point2f(271.,109.));
imagePoints.push_back(cv::Point2f(65.,208.));
imagePoints.push_back(cv::Point2f(334.,459.));
imagePoints.push_back(cv::Point2f(600.,225.));

//object points are measured in millimeters because calibration is done in mm also
objectPoints.push_back(cv::Point3f(0., 0., 0.));
objectPoints.push_back(cv::Point3f(-511.,2181.,0.));
objectPoints.push_back(cv::Point3f(-3574.,2354.,0.));
objectPoints.push_back(cv::Point3f(-3400.,0.,0.));

cv::Mat rvec(1,3,cv::DataType<double>::type);
cv::Mat tvec(1,3,cv::DataType<double>::type);
cv::Mat rotationMatrix(3,3,cv::DataType<double>::type);

cv::solvePnP(objectPoints, imagePoints, cameraMatrix, distCoeffs, rvec, tvec);
cv::Rodrigues(rvec,rotationMatrix);

Après avoir eu toutes les matrices, voici l'équation qui peut m'aider à transformer le point d'image en coordonnées mondiales :

transform_equation

où M est cameraMatrix, R - rotationMatrix, t - tvec, et s est une inconnue. Zconst représente la hauteur où se trouve la balle orange, dans cet exemple elle est de 285 mm. Donc, d'abord je dois résoudre l'équation précédente, pour obtenir "s", et ensuite je peux trouver les coordonnées X et Y en sélectionnant le point d'image : equation2

En résolvant ceci, je peux trouver la variable "s", en utilisant la dernière ligne des matrices, parce que Zconst est connu, donc voici le code suivant pour cela :

cv::Mat uvPoint = (cv::Mat_<double>(3,1) << 363, 222, 1); // u = 363, v = 222, got this point using mouse callback

cv::Mat leftSideMat  = rotationMatrix.inv() * cameraMatrix.inv() * uvPoint;
cv::Mat rightSideMat = rotationMatrix.inv() * tvec;

double s = (285 + rightSideMat.at<double>(2,0))/leftSideMat.at<double>(2,0)); 
//285 represents the height Zconst

std::cout << "P = " << rotationMatrix.inv() * (s * cameraMatrix.inv() * uvPoint - tvec) << std::endl;

Après cela, j'ai obtenu le résultat : P = [-2629.5, 1272.6, 285.]

et quand je le compare à la mesure, qui est : Préal = [-2629.6, 1269.5, 285.]

l'erreur est très faible, ce qui est très bien, mais lorsque je déplace cette boîte vers les bords de cette pièce, les erreurs sont peut-être de 20-40 mm et je voudrais améliorer cela. Quelqu'un peut-il m'aider à ce sujet, avez-vous des suggestions ?

14voto

Pascal Lécuyot Points 506

Compte tenu de votre configuration, des erreurs de 20-40mm sur les bords sont moyennes. Il semble que vous ayez tout bien fait.

Sans modifier la configuration de la caméra/du système, il sera difficile de faire mieux. Vous pouvez essayer de refaire l'étalonnage de la caméra et espérer obtenir de meilleurs résultats, mais cela ne les améliorera pas beaucoup (et vous risquez d'obtenir de moins bons résultats, donc n'effacez pas les paramètres intrinsèques réels).

Comme l'a dit count0, si vous avez besoin de plus de précision, vous devez opter pour des mesures multiples.

9voto

Obtenez-vous les points verts (imagePoints) à partir de l'image déformée ou non déformée ? Parce que la fonction solvePnP déforme déjà les imagePoints (à moins que vous ne passiez pas les coefficients de distorsion, ou que vous les passiez comme nuls). Il se peut que vous déformiez ces imagePoints deux fois si vous les obtenez à partir de l'image non déformée, ce qui entraînerait une augmentation de l'erreur dans les coins.

https://github.com/Itseez/opencv/blob/master/modules/calib3d/src/solvepnp.cpp

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X