Cómo usar correctamente cv :: triangulatePoints ()

Estoy tratando de triangular algunos puntos con OpenCV y encontré esta función cv::triangulatePoints() . El problema es que casi no hay documentación ni ejemplos de ello.

Tengo algunas dudas al respecto.

  1. ¿Qué método usa? He hecho una pequeña investigación sobre triangulaciones y hay varios métodos (Linear, Linear LS, eigen, iterativo LS, iterativo eigen, …) pero no puedo encontrar cuál se está utilizando en OpenCV.

  2. ¿Cómo debería usarlo? Parece que como entrada necesita una matriz de proyección y puntos 2D homogéneos 3xN . Los tengo definidos como std::vector pnts , pero como salida necesita 4xN arrays y obviamente no puedo crear un std::vector porque no existe, Entonces, ¿cómo debería definir el vector de salida?

Para la segunda pregunta probé: cv::Mat pnts3D(4,N,CV_64F); y cv::Mat pnts3d; , ninguno parece funcionar (arroja una excepción).

1.- El método utilizado es mínimos cuadrados. Hay algoritmos más complejos que este. Todavía es el más común, ya que los otros métodos pueden fallar en algunos casos (es decir, algunos otros fallan si los puntos están en el plano o en el infinito).

El método se puede encontrar en Geometría de vista múltiple en Computer Vision por Richard Hartley y Andrew Zisserman (p312)

2.- El uso :

 cv::Mat pnts3D(1,N,CV_64FC4); cv::Mat cam0pnts(1,N,CV_64FC2); cv::Mat cam1pnts(1,N,CV_64FC2); 

Rellena las 2 matrices de puntos de chanel con los puntos en las imágenes.

cam0 y cam1 son matrices de cámara Mat3x4 (parámetros intrínsecos y extrínsecos). Puede construirlos multiplicando A * RT, donde A es la matriz de parámetros intrínsecos y RT la matriz de postura de traducción de rotación 3×4.

 cv::triangulatePoints(cam0,cam1,cam0pnts,cam1pnts,pnts3D); 

NOTA : pnts3D NECESITA ser un 4 canales 1xN cv::Mat cuando está definido, arroja una excepción si no es así, pero el resultado es una matriz cv::Mat(4,N,cv_64FC1) . Realmente confuso, pero es la única forma en que no obtuve una excepción.


ACTUALIZACIÓN : A partir de la versión 3.0 o posiblemente anterior, esto ya no es cierto, y pnts3D también puede ser del tipo Mat(4,N,CV_64FC1) o puede dejarse completamente vacío (como de costumbre, se crea dentro de la función).

Una pequeña adición a la respuesta de @Ander Biguri. Debes obtener tus puntos de imagen en una imagen no undistort e invocar undistortPoints() en las cam0pnts y cam1pnts , porque cv::triangulatePoints espera los puntos 2D en coordenadas normalizadas (independientes de la cámara) y cam0 y cam1 deben ser solo [R | t ^ T] matricias no es necesario que se multiplique con A.

Intenté cv :: triangulatePoints, pero de alguna manera calcula la basura. Me vi obligado a implementar manualmente un método de triangulación lineal, que devuelve una matriz 4×1 para el punto 3D triangulado:

 Mat triangulate_Linear_LS(Mat mat_P_l, Mat mat_P_r, Mat warped_back_l, Mat warped_back_r) { Mat A(4,3,CV_64FC1), b(4,1,CV_64FC1), X(3,1,CV_64FC1), X_homogeneous(4,1,CV_64FC1), W(1,1,CV_64FC1); W.at(0,0) = 1.0; A.at(0,0) = (warped_back_l.at(0,0)/warped_back_l.at(2,0))*mat_P_l.at(2,0) - mat_P_l.at(0,0); A.at(0,1) = (warped_back_l.at(0,0)/warped_back_l.at(2,0))*mat_P_l.at(2,1) - mat_P_l.at(0,1); A.at(0,2) = (warped_back_l.at(0,0)/warped_back_l.at(2,0))*mat_P_l.at(2,2) - mat_P_l.at(0,2); A.at(1,0) = (warped_back_l.at(1,0)/warped_back_l.at(2,0))*mat_P_l.at(2,0) - mat_P_l.at(1,0); A.at(1,1) = (warped_back_l.at(1,0)/warped_back_l.at(2,0))*mat_P_l.at(2,1) - mat_P_l.at(1,1); A.at(1,2) = (warped_back_l.at(1,0)/warped_back_l.at(2,0))*mat_P_l.at(2,2) - mat_P_l.at(1,2); A.at(2,0) = (warped_back_r.at(0,0)/warped_back_r.at(2,0))*mat_P_r.at(2,0) - mat_P_r.at(0,0); A.at(2,1) = (warped_back_r.at(0,0)/warped_back_r.at(2,0))*mat_P_r.at(2,1) - mat_P_r.at(0,1); A.at(2,2) = (warped_back_r.at(0,0)/warped_back_r.at(2,0))*mat_P_r.at(2,2) - mat_P_r.at(0,2); A.at(3,0) = (warped_back_r.at(1,0)/warped_back_r.at(2,0))*mat_P_r.at(2,0) - mat_P_r.at(1,0); A.at(3,1) = (warped_back_r.at(1,0)/warped_back_r.at(2,0))*mat_P_r.at(2,1) - mat_P_r.at(1,1); A.at(3,2) = (warped_back_r.at(1,0)/warped_back_r.at(2,0))*mat_P_r.at(2,2) - mat_P_r.at(1,2); b.at(0,0) = -((warped_back_l.at(0,0)/warped_back_l.at(2,0))*mat_P_l.at(2,3) - mat_P_l.at(0,3)); b.at(1,0) = -((warped_back_l.at(1,0)/warped_back_l.at(2,0))*mat_P_l.at(2,3) - mat_P_l.at(1,3)); b.at(2,0) = -((warped_back_r.at(0,0)/warped_back_r.at(2,0))*mat_P_r.at(2,3) - mat_P_r.at(0,3)); b.at(3,0) = -((warped_back_r.at(1,0)/warped_back_r.at(2,0))*mat_P_r.at(2,3) - mat_P_r.at(1,3)); solve(A,b,X,DECOMP_SVD); vconcat(X,W,X_homogeneous); return X_homogeneous; } 

los parámetros de entrada son dos matrices de proyección de cámara 3×4 y un par de píxeles izquierdo / derecho correspondiente (x, y, w).

¡Gracias a Ander Biguri! Su respuesta me ayudó mucho. Pero siempre prefiero la alternativa con std :: vector, edité su solución a esto:

 std::vector cam0pnts; std::vector cam1pnts; // You fill them, both with the same size... // You can pick any of the following 2 (your choice) // cv::Mat pnts3D(1,cam0pnts.size(),CV_64FC4); cv::Mat pnts3D(4,cam0pnts.size(),CV_64F); cv::triangulatePoints(cam0,cam1,cam0pnts,cam1pnts,pnts3D); 

Entonces solo necesitas hacer emplace_back en los puntos. Ventaja principal: no necesita saber el tamaño N antes de comenzar a llenarlos. Desafortunadamente, no hay cv :: Point4f, por lo que pnts3D debe ser un cv :: Mat …

Alternativamente, puede utilizar el método de Hartley & Zisserman implementado aquí: http://www.morethantechnical.com/2012/01/04/simple-triangulation-with-opencv-from-harley-zisserman-w-code/