undistort findhomography c++ opencv camera-calibration

c++ - findhomography - opencv matrix calibration



OpenCV 3.0: la calibraciĆ³n no se ajusta a lo esperado (2)

Obtengo resultados que no espero cuando uso OpenCV 3.0 calibrateCamera. Aquí está mi algoritmo:

  1. Carga en 30 puntos de imagen
  2. Carga en 30 puntos mundiales correspondientes (coplanares en este caso)
  3. Use puntos para calibrar la cámara, solo para distorsionar
  4. Desinterte los puntos de la imagen, pero no use los intrínsecos (puntos del mundo coplanar, por lo que los intrínsecos son dudosos)
  5. Usa los puntos no distorsionados para encontrar una homografía, transformando los puntos del mundo (puedes hacer esto porque todos son coplanares)
  6. Utilice la transformación de homografía y perspectiva para asignar los puntos no distorsionados al espacio del mundo
  7. Compare los puntos del mundo original con los puntos mapeados

Los puntos que tengo son ruidosos y solo una pequeña parte de la imagen. Hay 30 puntos coplanarios en una sola vista, por lo que no puedo obtener los intrínsecos de la cámara, pero debería poder obtener coeficientes de distorsión y una homografía para crear una vista fronto-paralela.

Como se esperaba, el error varía según los indicadores de calibración. Sin embargo, varía de forma opuesta a lo que esperaba. Si permitiera que todas las variables se ajustaran, esperaría que el error baje. No estoy diciendo que espero un mejor modelo; De hecho, espero que se ajuste demasiado, pero eso aún debería reducir el error. Lo que sí veo es que cuantas menos variables uso, menor es mi error. El mejor resultado es con una homografía directa.

Tengo dos causas sospechosas, pero parecen poco probables y me gustaría escuchar una respuesta no adulterada antes de transmitirlas. He sacado el código para hacer lo que estoy diciendo. Es un poco largo, pero incluye cargar los puntos.

El código no parece tener errores; He usado puntos "mejores" y funciona perfectamente. Quiero enfatizar que la solución aquí no puede ser usar mejores puntos o realizar una mejor calibración; El objetivo del ejercicio es ver cómo los diversos modelos de calibración responden a las diferentes calidades de los datos de calibración.

¿Algunas ideas?

Adicional

Para ser claro, sé que los resultados serán malos y espero eso. También entiendo que puedo aprender los malos parámetros de distorsión que conducen a peores resultados cuando pruebo puntos que no han sido utilizados para entrenar el modelo. Lo que no entiendo es cómo el modelo de distorsión tiene más errores cuando se utiliza el conjunto de entrenamiento como el conjunto de prueba. Es decir, si el cv :: calibrateCamera se supone que debe elegir los parámetros para reducir el error sobre el conjunto de entrenamiento de los puntos proporcionados, aún está produciendo más errores que si hubiera seleccionado 0 para K !, K2, ... K6, P1 , P2. Datos erróneos o no, al menos debería ser mejor en el conjunto de entrenamiento. Antes de que pueda decir que los datos no son apropiados para este modelo, debo asegurarme de estar haciendo lo mejor que puedo con los datos disponibles, y no puedo decirlo en este momento.

Aquí una imagen de ejemplo

Los puntos con los pines verdes están marcados. Esto es obviamente solo una imagen de prueba.

Aquí hay más cosas de ejemplo

A continuación, la imagen está recortada de la grande anterior. El centro no ha cambiado. Esto es lo que sucede cuando distorsiono solo los puntos marcados manualmente desde los pines verdes y permitiendo que K1 (solo K1) varíe de 0:

antes de

Después

Lo atribuiría a un error, pero cuando uso un conjunto de puntos más grande que cubre más de la pantalla, incluso desde un solo plano, funciona razonablemente bien. Esto se ve terrible. Sin embargo, el error no es tan malo como podría pensar al mirar la imagen.

// Load image points std::vector<cv::Point2f> im_points; im_points.push_back(cv::Point2f(1206, 1454)); im_points.push_back(cv::Point2f(1245, 1443)); im_points.push_back(cv::Point2f(1284, 1429)); im_points.push_back(cv::Point2f(1315, 1456)); im_points.push_back(cv::Point2f(1352, 1443)); im_points.push_back(cv::Point2f(1383, 1431)); im_points.push_back(cv::Point2f(1431, 1458)); im_points.push_back(cv::Point2f(1463, 1445)); im_points.push_back(cv::Point2f(1489, 1432)); im_points.push_back(cv::Point2f(1550, 1461)); im_points.push_back(cv::Point2f(1574, 1447)); im_points.push_back(cv::Point2f(1597, 1434)); im_points.push_back(cv::Point2f(1673, 1463)); im_points.push_back(cv::Point2f(1691, 1449)); im_points.push_back(cv::Point2f(1708, 1436)); im_points.push_back(cv::Point2f(1798, 1464)); im_points.push_back(cv::Point2f(1809, 1451)); im_points.push_back(cv::Point2f(1819, 1438)); im_points.push_back(cv::Point2f(1925, 1467)); im_points.push_back(cv::Point2f(1929, 1454)); im_points.push_back(cv::Point2f(1935, 1440)); im_points.push_back(cv::Point2f(2054, 1470)); im_points.push_back(cv::Point2f(2052, 1456)); im_points.push_back(cv::Point2f(2051, 1443)); im_points.push_back(cv::Point2f(2182, 1474)); im_points.push_back(cv::Point2f(2171, 1459)); im_points.push_back(cv::Point2f(2164, 1446)); im_points.push_back(cv::Point2f(2306, 1474)); im_points.push_back(cv::Point2f(2292, 1462)); im_points.push_back(cv::Point2f(2278, 1449)); // Create corresponding world / object points std::vector<cv::Point3f> world_points; for (int i = 0; i < 30; i++) { world_points.push_back(cv::Point3f(5 * (i / 3), 4 * (i % 3), 0.0f)); } // Perform calibration // Flags are set out so they can be commented out and "freed" easily int calibration_flags = 0 | cv::CALIB_FIX_K1 | cv::CALIB_FIX_K2 | cv::CALIB_FIX_K3 | cv::CALIB_FIX_K4 | cv::CALIB_FIX_K5 | cv::CALIB_FIX_K6 | cv::CALIB_ZERO_TANGENT_DIST | 0; // Initialise matrix cv::Mat intrinsic_matrix = cv::Mat(3, 3, CV_64F); intrinsic_matrix.ptr<float>(0)[0] = 1; intrinsic_matrix.ptr<float>(1)[1] = 1; cv::Mat distortion_coeffs = cv::Mat::zeros(5, 1, CV_64F); // Rotation and translation vectors std::vector<cv::Mat> undistort_rvecs; std::vector<cv::Mat> undistort_tvecs; // Wrap in an outer vector for calibration std::vector<std::vector<cv::Point2f>>im_points_v(1, im_points); std::vector<std::vector<cv::Point3f>>w_points_v(1, world_points); // Calibrate; only 1 plane, so intrinsics can''t be trusted cv::Size image_size(4000, 3000); calibrateCamera(w_points_v, im_points_v, image_size, intrinsic_matrix, distortion_coeffs, undistort_rvecs, undistort_tvecs, calibration_flags); // Undistort im_points std::vector<cv::Point2f> ud_points; cv::undistortPoints(im_points, ud_points, intrinsic_matrix, distortion_coeffs); // ud_points have been "unintrinsiced", but we don''t know the intrinsics, so reverse that double fx = intrinsic_matrix.at<double>(0, 0); double fy = intrinsic_matrix.at<double>(1, 1); double cx = intrinsic_matrix.at<double>(0, 2); double cy = intrinsic_matrix.at<double>(1, 2); for (std::vector<cv::Point2f>::iterator iter = ud_points.begin(); iter != ud_points.end(); iter++) { iter->x = iter->x * fx + cx; iter->y = iter->y * fy + cy; } // Find a homography mapping the undistorted points to the known world points, ground plane cv::Mat homography = cv::findHomography(ud_points, world_points); // Transform the undistorted image points to the world points (2d only, but z is constant) std::vector<cv::Point2f> estimated_world_points; std::cout << "homography" << homography << std::endl; cv::perspectiveTransform(ud_points, estimated_world_points, homography); // Work out error double sum_sq_error = 0; for (int i = 0; i < 30; i++) { double err_x = estimated_world_points.at(i).x - world_points.at(i).x; double err_y = estimated_world_points.at(i).y - world_points.at(i).y; sum_sq_error += err_x*err_x + err_y*err_y; } std::cout << "Sum squared error is: " << sum_sq_error << std::endl;


OpenCV ejecuta el algoritmo Levenberg-Marquardt dentro de la cámara de calibración.

https://en.wikipedia.org/wiki/Levenberg%E2%80%93Marquardt_algorithm/

Este algortihm funciona bien en problemas con un mínimo. En el caso de una sola imagen, los puntos ubicados cerca uno del otro y el algoritmo de muchos problemas dimensionales (n = número de coeficientes) pueden ser inestables (especialmente con una conjetura inicial incorrecta de la matriz de la cámara. La convergencia del algoritmo está bien descrita aquí:

https://na.math.kit.edu/download/papers/levenberg.pdf/

Como escribió, el error depende de los indicadores de calibración: el número de indicadores cambia la dimensión de un problema que se debe optimizar.

La calibración de la cámara también calcula la posición de la cámara, lo que será malo en los modelos con matriz de calibración incorrecta.

Como solución, sugiero cambiar el enfoque. No es necesario calcular la matriz de la cámara y posar en este paso. Como ya sabe, los puntos se encuentran en un plano, puede usar una ecuación de proyección de plano 3d-2d para determinar el tipo de puntos de distribución. Por distribución quiero decir, que todos los puntos se ubicarán igualmente en algún tipo de trapezoide.

Luego puede usar cv :: undistort con diferentes distCoeffs en su imagen de prueba y calcular la distribución del punto de imagen y el error de distribución.

El último paso será realizar estos pasos como una función de destino para algún algoritmo de optimización con coeficientes de distorsión optimizados.

Esta no es la solución más fácil, pero espero que te ayude.


Tomaría muestras aleatorias de los 30 puntos de entrada y calcularía la homografía en cada caso junto con los errores en las homografías estimadas, un esquema RANSAC y verificaría el consenso entre los niveles de error y los parámetros de homografía, esto puede ser solo una verificación de la optimización global proceso. Sé que puede parecer innecesario, pero es solo una verificación de cordura de cuán sensible es el procedimiento a la entrada (niveles de ruido, ubicación)

Además, parece lógico que corregir la mayoría de las variables sea lo menos posible, ya que los grados de libertad en el proceso de minimización son menores. Trataría de arreglar otros para establecer otro consenso. Al menos esto le permitirá saber qué variables son las más sensibles a los niveles de ruido de la entrada.

Es de esperar que una sección tan pequeña de la imagen esté cerca del centro de la imagen, ya que tendrá la menor cantidad de distorsión de la lente. ¿Es posible utilizar un modelo de distorsión diferente en su caso? Una forma más viable es adaptar el número de parámetros de distorsión dada la posición del patrón con respecto al centro de la imagen.

Sin conocer las limitaciones del algoritmo, podría haber malinterpretado la pregunta, esa también es una opción, en tal caso puedo retroceder.

Me gustaría tener esto como un comentario, pero no tengo suficientes puntos.