OpenCV 3.0: Calibration non ajustée 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 de l'image
  2. Charger dans 30 correspondant du monde de points coplanaires dans ce cas)
  3. utilisez des points pour calibrer la caméra, juste pour ne pas déformer
  4. dés-déformer les points d'image, mais n'utilisez pas les intrinsèques (les points du monde coplanaire, donc les intrinsèques sont douteux)
  5. utilisez les points non déformés pour trouver une homographie, la transformation du monde des points (peut le faire, car ils sont tous coplanaires)
  6. utilisez l'homographie et la transformation de perspective pour cartographier les points non déformés de l'espace du monde
  7. comparez les points originaux du monde aux points cartographiés

Les points que j'ai sont bruyants et seule une petite partie de l'image. Il ya 30 points coplanaires à partir d'une seule vue donc je ne peux pas obtenir caméra intrinsèques, mais devrait être en mesure d'obtenir des coefficients de distorsion et une homographie pour créer une fronto-parallèle.

comme prévu, l'erreur varie selon les indicateurs d'étalonnage. Cependant, cela varie à l'opposé de ce à quoi je m'attendais. Si je laisse toutes les variables s'ajuster, Je m'attendrais à ce que l'erreur descende. Je suis dire que je m'attends à un meilleur modèle; je m'attends en fait à un ajustement excessif, mais cela devrait tout de même réduire les erreurs. Ce que je vois, c'est que moins j'utilise de variables, moins j'ai d'erreur. Le meilleur résultat est avec une homographie droite.

je j'ai deux causes suspectes, mais elles semblent invraisemblables et j'aimerais entendre une réponse franche avant de les diffuser. J'ai sorti le code pour faire ce dont je parle. C'est un peu long, mais cela inclut le chargement des points.

le code ne semble pas avoir de bugs; j'ai utilisé des" meilleurs " points et il fonctionne parfaitement. Je tiens à souligner que la solution ici ne peut pas être d'utiliser de meilleurs points ou d'effectuer un meilleur calibrage; tout le but de l'exercice est de voir comment les différents les modèles d'étalonnage répondent à différentes qualités de données d'étalonnage.

des idées?

Ajouté

pour être clair, je les résultats seront mauvais et je pense que. Je comprends aussi que je puisse apprendre de mauvais paramètres de distorsion, ce qui conduit à des résultats plus mauvais lorsque je teste des points qui n'ont pas été utilisés pour former le modèle. Ce que je ne comprends pas c'est comment le modèle de distorsion a plus d'erreur quand on utilise le jeu de formation comme ensemble de test. C'est-à-dire, si le cv::calibrateCamera est censé choisir des paramètres pour réduire l'erreur sur le jeu de points d'entraînement fourni, mais il produit plus d'erreur que s'il venait de sélectionner 0s pour K!, K2, ... K6, P1, P2. Mauvaises données ou pas, il devrait au moins faire mieux sur le jeu de formation. Avant de pouvoir dire que les données ne sont pas appropriées pour ce modèle, je dois m'assurer que je fais de mon mieux avec les données disponibles, et je ne peux pas dire cela à ce stade.

voici un exemple d'image

les points avec les pins verts sont marqués. C'est évidemment juste une image de test. Example image

Voici d'autres exemples

dans ce qui suit l'image est recadrée de la grande ci-dessus. Le centre n'a pas changé. C'est ce qui se produit lorsque je m'efface avec juste les points marqués manuellement à partir des pins verts et permettant à K1 (seulement K1) de varier de 0:

Avant de Set A image, 1920 by 1080, distorted

Après Set A image, 1920 by 1080, undistorted

je le mettrais sur un bug, mais quand j'utilise un plus grand ensemble de points qui couvre plus de l'écran, même à partir d'un seul plan, cela fonctionne assez bien. Cela a l'air terrible. Cependant, l'erreur n'est pas aussi mauvais que vous le pensez en regardant la photo.

// 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;
42
demandé sur timbo 2015-07-08 15:10:41

2 réponses

je prendrais des échantillons aléatoires des 30 points d'entrée et calculerais l'homographie dans chaque cas avec les erreurs sous les homographies estimées, un schéma RANSAC, et vérifierais le consensus entre les niveaux d'erreur et les paramètres d'homographie, cela peut être juste une vérification du processus d'optimisation globale. Je sais que cela peut sembler inutile, mais ce n'est qu'une vérification de la sensibilité de la procédure à l'entrée (niveau de bruit, emplacement)

en outre, il semble logique que la correction de la plupart des variables vous donne le moins d'erreurs, comme les degrés de liberté dans le processus de minimisation sont moins. J'essaierai d'en fixer d'autres 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.

Espérons-le, une petite section de l'image est proche du centre de l'image qu'il va engager le moins de distorsion de l'objectif. Un modèle de distorsion possible dans votre cas? Plus moyen viable est d'adapter le nombre de paramètres de distorsion étant donné la position du motif par rapport au centre de l'image.

sans connaître les contraintes de l'algorithme, je pourrais avoir mal compris la question, c'est aussi une option, dans ce cas je peux revenir en arrière.

je voudrais avoir cela comme un commentaire plutôt, mais je n'ai pas assez de points.

1
répondu trox 2016-03-16 14:25:35

OpenCV exécute l'algorithme de Levenberg-Marquardt à l'intérieur de la caméra calibrée.

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

C'algortihm fonctionne bien dans les problèmes avec un minimum. Dans le cas d'une image simple, les points situés à proximité l'un de l'autre et de nombreux problèmes dimensionnels (n= nombre de coefficients) algorithme peut être instable (en particulier avec une mauvaise estimation initiale de la matrice de la caméra. 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 options d'étalonnage - le nombre de Options change la dimension d'un problème à optimiser.

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

comme solution, je suggère de changer d'approche. Vous n'avez pas besoin de calculer la matrice de la caméra et de poser dans cette étape. Puisque vous le savez, que les points sont situés sur un plan que vous pouvez utiliser 3D-2D plan équation de projection pour déterminer le type de distribution des points. Par répartition, je veux dire que tous les points seront situés également sur une sorte de trapèze.

vous pouvez alors utiliser cv:: undistort avec différents distCoeffs sur votre image de test et calculer la distribution de points d'image et l'erreur de distribution.

la dernière étape sera d'effectuer ces étapes comme une fonction cible pour un algorithme d'optimisation avec distorsion coefficents être optimisé.

ce n'est pas la solution la plus facile, mais j'espère qu'elle vous aidera.

1
répondu Kamil Szelag 2017-09-27 12:28:17