J'ai pour tâche de localiser un objet dans le 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 avec une coordonnée Z connue qui sera placée sur le dessus de l'objet en mouvement, comme la boule orange sur cette image:
Tout d'abord, j'ai fait le calibrage de la caméra pour obtenir les paramètres intrinsèques et ensuite j'ai utilisé cv :: resolPnP pour obtenir le vecteur de rotation et de translation comme dans ce 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 toutes les matrices, cette équation qui peut m'aider à transformer le point de l'image en coordonnées mondiales:
où M est cameraMatrix, R - rotationMatrix, t - tvec et s est une inconnue. Zconst représente la hauteur où se trouve la boule orange, dans cet exemple elle est de 285 mm. Donc, je dois d'abord résoudre l'équation précédente, obtenir "s", et ensuite je peux trouver les coordonnées X et Y en sélectionnant le point d'image:
Pour résoudre ce problème, je peux trouver la variable "s", en utilisant la dernière ligne des matrices, car Zconst est connu, voici donc le code suivant:
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: Preal = [-2629.6, 1269.5, 285.]
l'erreur est très petite, ce qui est très bien, mais lorsque je déplace cette boîte sur les bords de cette pièce, les erreurs sont peut-être de 20 à 40 mm et j'aimerais améliorer cela. Quelqu'un peut-il m'aider avec cela, avez-vous des suggestions?
Compte tenu de votre configuration, les erreurs de 20 à 40 mm sur les bords sont moyennes. Il semble que vous ayez tout bien fait.
Sans modifier la configuration de la caméra/du système, faire mieux sera difficile. Vous pouvez essayer de refaire l'étalonnage de la caméra et espérer de meilleurs résultats, mais cela ne les améliorera pas beaucoup (et vous risquez éventuellement d'obtenir de moins bons résultats, donc n'effacez pas les paramètres réels).
Comme l'a dit count0, si vous avez besoin de plus de précision, vous devez opter pour plusieurs mesures.
Obtenez-vous les points verts (imagePoints) de l'image déformée ou non déformée? Parce que la fonction resolPnP déforme déjà les imagePoints (sauf si vous ne transmettez pas les coefficients de distorsion, ou si vous les transmettez comme nuls). Vous pouvez déformer deux fois ces points image 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