opencv - tridimensionales - ubicacion de puntos en el espacio ejercicios
Cálculo de coordenadas x, y(3D) desde el punto de la imagen (2)
Tengo una tarea para localizar un objeto en el sistema de coordenadas 3D. Como tengo que obtener una coordenada X e Y casi exacta, decidí rastrear un marcador de color con una coordenada Z conocida que se colocará en la parte superior del objeto en movimiento, como la bola naranja en esta imagen:
Primero, hice la calibración de la cámara para obtener parámetros intrínsecos y luego usé cv :: solvePnP para obtener la rotación y el vector de traducción como en el siguiente código:
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);
Después de tener todas las matrices, esta ecuación que puede ayudarme a transformar el punto de la imagen a coordenadas wolrd:
donde M es cameraMatrix, R - rotationMatrix, t - tvec ys es un desconocido. Zconst representa la altura donde está la bola naranja, en este ejemplo es de 285 mm. Entonces, primero necesito resolver la ecuación anterior, obtener "s", y después puedo encontrar la coordenada X e Y seleccionando el punto de la imagen:
Resolviendo esto, puedo encontrar la variable "s", usando la última fila en matrices, porque Zconst es conocido, así que aquí está el siguiente código para eso:
cv::Mat uvPoint = cv::Mat::ones(3,1,cv::DataType<double>::type); //u,v,1
uvPoint.at<double>(0,0) = 363.; //got this point using mouse callback
uvPoint.at<double>(1,0) = 222.;
cv::Mat tempMat, tempMat2;
double s;
tempMat = rotationMatrix.inv() * cameraMatrix.inv() * uvPoint;
tempMat2 = rotationMatrix.inv() * tvec;
s = 285 + tempMat2.at<double>(2,0); //285 represents the height Zconst
s /= tempMat.at<double>(2,0);
std::cout << "P = " << rotationMatrix.inv() * (s * cameraMatrix.inv() * uvPoint - tvec) << std::endl;
Después de esto, obtuve el resultado: P = [-2629.5, 1272.6, 285.]
y cuando lo comparo con la medición, que es: Preal = [-2629.6, 1269.5, 285.]
el error es muy pequeño, lo cual es muy bueno, pero cuando muevo esta caja a los bordes de esta habitación, los errores son quizás de 20-40 mm y me gustaría mejorar eso. ¿Alguien puede ayudarme con eso? ¿Tiene alguna sugerencia?
¿Obtiene los puntos verdes (puntos de imagen) de la imagen distorsionada o sin distorsión? Debido a que la función solvePnP ya no distorsiona los puntos de imagen (a menos que no pase los coeficientes de distorsión, o los pase como nulos). Puede estar distorsionando esos imagePoints dos veces si los obtiene de la imagen sin distorsión, y esto terminaría causando un aumento en el error en las esquinas.
https://github.com/Itseez/opencv/blob/master/modules/calib3d/src/solvepnp.cpp
Dada su configuración, los errores de 20-40 mm en los bordes son promedio. Parece que has hecho todo bien.
Sin modificar la configuración de la cámara / sistema, hacerlo mejor será difícil. Puede intentar rehacer la calibración de la cámara y esperar mejores resultados, pero esto no los mejorará mucho (y es posible que obtenga resultados peores, así que no borre los parámetros intrínsecos).
Como dice count0, si necesita más precisión, debe realizar múltiples mediciones.