OpenCV 3.0: le calibrage ne convient pas comme prévu

J’obtiens des résultats auxquels je ne m’attends pas lorsque j’utilise OpenCV 3.0 calibrateCamera. Voici mon algorithme:

  1. Charger dans 30 points d’image
  2. Charge dans 30 points mondiaux correspondants (coplanaires dans ce cas)
  3. Utilisez des points pour calibrer la caméra, juste pour déformer
  4. Déformez les points de l’image, mais n’utilisez pas les éléments insortingnsèques (les points du monde coplanaire, donc les insortingnsèques sont douteux)
  5. Utilisez les points non déformés pour trouver une homographie, transformant en points du monde (peut le faire parce qu’ils sont tous coplanaires)
  6. Utiliser l’homographie et la transformation de perspective pour mapper les points non déformés sur l’espace mondial
  7. Comparer les points du monde d’origine aux points mappés

Les points que j’ai sont bruyants et seulement une petite partie de l’image. Il y a 30 points coplanaires à partir d’une même vue, donc je ne peux pas obtenir d’éléments insortingnsèques à la caméra, mais je devrais pouvoir obtenir des coefficients de distorsion et une homographie pour créer une vue fronto-parallèle.

Comme prévu, l’erreur varie en fonction des indicateurs d’étalonnage. Cependant, cela varie en face de ce que j’attendais. Si j’autorise l’ajustement de toutes les variables, je m’attendrais à une erreur. Je ne dis pas que je m’attends à un meilleur modèle; Je m’attends en fait à un ajustement excessif, mais cela devrait encore réduire les erreurs. Ce que je vois cependant, c’est que moins j’utilise de variables, plus mon erreur est faible. Le meilleur résultat est une homographie directe.

J’ai deux causes présumées, mais elles semblent improbables et j’aimerais entendre une réponse sans mélange avant de les diffuser. J’ai sorti le code pour faire juste ce dont je parle. C’est un peu long, mais cela inclut le chargement des points.

Le code ne semble pas avoir de bogues; J’ai utilisé de “meilleurs” points et cela fonctionne parfaitement. Je tiens à souligner que la solution ici ne peut être d’utiliser de meilleurs points ou d’effectuer un meilleur étalonnage; Le but de l’exercice est de voir comment les différents modèles d’étalonnage répondent aux différentes qualités des données d’étalonnage.

Des idées?

Ajoutée

Pour être clair, je sais que les résultats seront mauvais et je l’attends. Je comprends également que je peux apprendre de mauvais parameters de distorsion, ce qui conduit à de plus mauvais résultats lors des tests de points qui n’ont pas été utilisés pour entraîner le modèle. Ce que je ne comprends pas, c’est comment le modèle de distorsion présente plus d’erreurs lors de l’utilisation de l’ensemble d’apprentissage comme jeu de test. En d’autres termes, si cv :: calibrateCamera est supposé choisir des parameters pour réduire les erreurs sur l’ensemble de points d’entraînement fourni, il produit cependant plus d’erreur que s’il venait de sélectionner des 0 pour K !, K2, … K6, P1 , P2. Mauvaises données ou non, il devrait au moins faire mieux sur l’ensemble d’entraînement. Avant de pouvoir dire que les données ne sont pas appropriées pour ce modèle, je dois être sûr que je fais de mon mieux avec les données disponibles et je ne peux pas le dire à ce stade.

Voici un exemple d’image

Les points avec les épingles vertes sont marqués. Ce n’est évidemment qu’une image de test. Exemple d'image

Voici d’autres exemples de choses

Dans ce qui suit l’image est recadrée à partir du grand ci-dessus. Le centre n’a pas changé. C’est ce qui se passe quand je ne me déforme pas avec juste les points marqués manuellement à partir des broches vertes et en permettant à K1 (seulement K1) de varier de 0:

Avant Set A image, 1920 par 1080, déformé

Après Set A image, 1920 par 1080, sans distorsion

Je le mets à un bogue, mais quand j’utilise un plus grand nombre de points couvrant davantage l’écran, même à partir d’un seul plan, cela fonctionne raisonnablement bien. Cela semble terrible. Cependant, l’erreur n’est pas aussi grave que vous pourriez le penser en regardant la photo.

// Load image points std::vector 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 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(0)[0] = 1; insortingnsic_masortingx.ptr(1)[1] = 1; cv::Mat distortion_coeffs = cv::Mat::zeros(5, 1, CV_64F); // Rotation and translation vectors std::vector undistort_rvecs; std::vector undistort_tvecs; // Wrap in an outer vector for calibration std::vector<std::vector>im_points_v(1, im_points); std::vector<std::vector>w_points_v(1, world_points); // Calibrate; only 1 plane, so insortingnsics can't be trusted cv::Size image_size(4000, 3000); calibrateCamera(w_points_v, im_points_v, image_size, insortingnsic_masortingx, distortion_coeffs, undistort_rvecs, undistort_tvecs, calibration_flags); // Undistort im_points std::vector ud_points; cv::undistortPoints(im_points, ud_points, insortingnsic_masortingx, distortion_coeffs); // ud_points have been "uninsortingnsiced", but we don't know the insortingnsics, so reverse that double fx = insortingnsic_masortingx.at(0, 0); double fy = insortingnsic_masortingx.at(1, 1); double cx = insortingnsic_masortingx.at(0, 2); double cy = insortingnsic_masortingx.at(1, 2); for (std::vector::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 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; 

Je voudrais prendre des échantillons aléatoires des 30 points d’entrée et calculer l’homographie dans chaque cas avec les erreurs sous les homographies estimées, un schéma RANSAC, et vérifier le consensus entre les niveaux d’erreur et les parameters d’homographie, cela peut être juste une vérification de l’optimisation globale processus. Je sais que cela peut sembler inutile, mais c’est juste un test de santé pour savoir si la procédure est sensible à l’entrée (niveaux de bruit, emplacement)

En outre, il semble logique que la plupart des variables vous apportent le moins d’erreurs, car les degrés de liberté dans le processus de minimisation sont moindres. Je voudrais essayer de réparer les différents pour établir un autre consensus. Au moins, cela vous permettrait de savoir quelles variables sont les plus sensibles aux niveaux de bruit de l’entrée.

Heureusement, une si petite partie de l’image serait proche du centre de l’image car elle entraînerait le moins de distorsion possible. Est-ce que l’utilisation d’un modèle de distorsion différent est possible dans votre cas? Un moyen plus viable consiste à adapter le nombre de parameters de distorsion en fonction de la position du motif par rapport au centre de l’image.

Sans connaître les contraintes de l’algorithme, j’aurais peut-être mal compris la question, c’est aussi une option, dans ce cas, je peux revenir en arrière.

Je voudrais plutôt avoir ceci comme un commentaire, mais je n’ai pas assez de points.

OpenCV exécute l’algorithme de Levenberg-Marquardt à l’intérieur de la caméra de calibrage.

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

Cet algortihm fonctionne bien dans les problèmes avec un minimum. En cas d’image unique, les points situés à proximité les uns des autres et l’algorithme à problèmes dimensionnels (n = nombre de coefficients) peuvent être instables (en particulier avec une estimation initiale erronée de la masortingce. La convergence de l’algorithme est bien décrite ici:

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

Comme vous l’avez écrit, l’erreur dépend des drapeaux d’étalonnage – le nombre de drapeaux modifie la dimension d’un problème à optimiser.

Le calibrage de la caméra calcule également la pose de la caméra, ce qui sera mauvais dans les modèles avec une masortingce de calibrage incorrecte.

En tant que solution, je suggère de changer d’approche. Vous n’avez pas besoin de calculer la masortingce de l’appareil photo et de poser dans cette étape. Étant donné que vous savez que les points sont situés sur un plan, vous pouvez utiliser l’équation de projection 3D-2D pour déterminer le type de dissortingbution des points. Par dissortingbution, je veux dire que tous les points seront situés également sur une sorte de trapèze.

Ensuite, vous pouvez utiliser cv :: undistort avec différents distCoeffs sur votre image de test et calculer la dissortingbution des points d’image et les erreurs de dissortingbution.

La dernière étape sera d’effectuer ces étapes en tant que fonction cible pour un algorithme d’optimisation avec des coefficients de distorsion optimisés.

Ce n’est pas la solution la plus facile, mais j’espère que cela vous aidera.