Pose de cámara computacional con matriz de homografía basada en 4 puntos coplanarios

Tengo 4 puntos coplanarios en un video (o imagen) que representa un cuadrángulo (no necesariamente un cuadrado o rectángulo) y me gustaría poder mostrar un cubo virtual encima de ellos, donde las esquinas del cubo están exactamente en las esquinas del video quad

Como los puntos son coplanarios, puedo calcular la homografía entre las esquinas de un cuadrado unitario (es decir, [0,0] [0,1] [1,0] [1,1]) y las coordenadas de video del cuadrángulo.

De esta homografía debería poder calcular una posición correcta de la cámara, es decir [R | t] donde R es una matriz de rotación de 3×3 yt es un vector de traducción de 3×1 para que el cubo virtual mienta en el video quad.

He leído muchas soluciones (algunas de ellas en SO) e intenté implementarlas, pero parecen funcionar solo en algunos casos “simples” (como cuando el cuadrante de video es cuadrado) pero no funcionan en la mayoría de los casos.

Estos son los métodos que probé (la mayoría de ellos se basan en los mismos principios, solo el cálculo de la traducción es ligeramente diferente). Deje que K sea la matriz intrínseca de la cámara y H sea la homografía. Calculamos:

A = K-1 * H 

Sean a1, a2, a3 los vectores de columna de A y r1, r2, r3 los vectores de columna de la matriz de rotación R.

 r1 = a1 / ||a1|| r2 = a2 / ||a2|| r3 = r1 x r2 t = a3 / sqrt(||a1||*||a2||) 

El problema es que esto no funciona en la mayoría de los casos. Para verificar mis resultados, comparé R yt con los obtenidos por el método solvePnP de OpenCV (usando los siguientes puntos 3D [0,0,0] [0,1,0] [1,0,0] [1,1 , 0]).

Dado que visualizo el cubo de la misma manera, noté que en todos los casos solvePnP proporciona resultados correctos, mientras que la pose obtenida de la homografía es mayormente incorrecta.

En teoría, dado que mis puntos son coplanares, es posible calcular la pose desde una homografía, pero no pude encontrar la forma correcta de calcular la pose de H.

¿Alguna idea de lo que estoy haciendo mal?

Editar luego de probar el método de @Jav_Rock

Hola Jav_Rock, muchas gracias por tu respuesta, probé tu enfoque (y muchos otros también) que parece ser más o menos correcto. Sin embargo, todavía tengo algunos problemas al calcular la pose basada en 4 puntos coplanarios. Para verificar los resultados, los comparo con los resultados de solvePnP (que será mucho mejor debido al enfoque de minimización de errores de reproyección iterativa).

Aquí hay un ejemplo:

cubo

  • Cubo amarillo: Resuelve PNP
  • Black Cube: técnica de Jav_Rock
  • Cubo (s) cian (y morado): algunas otras técnicas dan exactamente los mismos resultados

Como puede ver, el cubo negro está más o menos bien, pero no parece estar bien proporcionado, aunque los vectores parecen ortonormales.

EDIT2: normalicé v3 después de calcularlo (para aplicar ortonormalidad) y parece que también resuelve algunos problemas.

Si tiene su Homografía, puede calcular la pose de la cámara con algo como esto:

 void cameraPoseFromHomography(const Mat& H, Mat& pose) { pose = Mat::eye(3, 4, CV_32FC1); // 3x4 matrix, the camera pose float norm1 = (float)norm(H.col(0)); float norm2 = (float)norm(H.col(1)); float tnorm = (norm1 + norm2) / 2.0f; // Normalization value Mat p1 = H.col(0); // Pointer to first column of H Mat p2 = pose.col(0); // Pointer to first column of pose (empty) cv::normalize(p1, p2); // Normalize the rotation, and copies the column to pose p1 = H.col(1); // Pointer to second column of H p2 = pose.col(1); // Pointer to second column of pose (empty) cv::normalize(p1, p2); // Normalize the rotation and copies the column to pose p1 = pose.col(0); p2 = pose.col(1); Mat p3 = p1.cross(p2); // Computes the cross-product of p1 and p2 Mat c2 = pose.col(2); // Pointer to third column of pose p3.copyTo(c2); // Third column is the crossproduct of columns one and two pose.col(3) = H.col(2) / tnorm; //vector t [R|t] is the last column of pose } 

Este método funciona desde mi. Buena suerte.

La respuesta propuesta por Jav_Rock no proporciona una solución válida para poses de cámara en el espacio tridimensional.

Para estimar una transformación de árbol-dimensional y la rotación inducida por una homografía, existen múltiples enfoques. Uno de ellos proporciona fórmulas cerradas para descomponer la homografía, pero son muy complejos. Además, las soluciones nunca son únicas.

Afortunadamente, OpenCV 3 ya implementa esta descomposición ( decomposeHomographyMat ). Dada una homografía y una matriz intrínseca correctamente escalada, la función proporciona un conjunto de cuatro rotaciones y traducciones posibles.

La computación [R | T] de la matriz de homografía es un poco más complicada que la respuesta de Jav_Rock.

En OpenCV 3.0, hay un método llamado cv :: decomposeHomographyMat que devuelve cuatro posibles soluciones, una de ellas es la correcta. Sin embargo, OpenCV no proporcionó un método para elegir el correcto.

Ahora estoy trabajando en esto y quizás publique mis códigos aquí más adelante este mes.

Solo en caso de que alguien necesite portar python de la función escrita por @Jav_Rock:

 def cameraPoseFromHomography(H): H1 = H[:, 0] H2 = H[:, 1] H3 = np.cross(H1, H2) norm1 = np.linalg.norm(H1) norm2 = np.linalg.norm(H2) tnorm = (norm1 + norm2) / 2.0; T = H[:, 2] / tnorm return np.mat([H1, H2, H3, T]) 

Funciona bien en mis tareas.

El plano que contiene tu Cuadrado en la imagen tiene agentes de carril que desaparecen de tu cámara. La ecuación de esta línea es A x + B y + C = 0.

Normal de tu avión es (A, B, C)!

Permita que p00, p01, p10, p11 sean coordenadas de punto después de aplicar los parámetros intrínsecos de la cámara y en forma homogénea p. Ej., P00 = (x00, y00,1)

La línea de fuga se puede calcular como:

  • abajo = p00 cruz p01;
  • left = p00 cross p10;
  • derecha = p01 cruz p11;
  • up = p10 cross p11;
  • v1 = izquierda cruz derecha;
  • v2 = arriba hacia abajo;
  • vanish_line = v1 cross v2;

Donde cruz en producto cruzado de vector estándar

Aquí hay una versión de Python, basada en la presentada por Dmitriy Voloshyn que normaliza la matriz de rotación y transpone el resultado para que sea 3×4.

 def cameraPoseFromHomography(H): norm1 = np.linalg.norm(H[:, 0]) norm2 = np.linalg.norm(H[:, 1]) tnorm = (norm1 + norm2) / 2.0; H1 = H[:, 0] / norm1 H2 = H[:, 1] / norm2 H3 = np.cross(H1, H2) T = H[:, 2] / tnorm return np.array([H1, H2, H3, T]).transpose() 
Intereting Posts