5

J'essaie actuellement d'implémenter une autre méthode de RA basé sur webcam en utilisant un système de suivi externe. J'ai tout dans mon environnement configuré pour l'étalonnage extrinsèque. J'ai décidé d'utiliser cv::solvePnP() comme il est censé faire exactement ce que je veux, mais après deux semaines, je suis en train de me tirer les cheveux en essayant de le faire fonctionner. Un diagramme ci-dessous montre ma configuration. c1 est ma caméra, c2 est le tracker optique que j'utilise, M est le marqueur suivi attaché à la caméra, et ch est le damier.Etalonnage extrinsèque avec cv :: SolvePnP

Diagram of my configuration

En l'état actuel, je passe dans mon pixel d'image des coordonnées acquises avec cv::findChessboardCorners(). Les points du monde sont acquis en référence au marqueur suivi M apposé sur la caméra c1 (L'extrinsèque est donc la transformation de la trame de ce marqueur à l'origine de la caméra). J'ai testé cela avec des jeux de données jusqu'à 50 points pour atténuer la possibilité de minima locaux, mais pour l'instant, je teste avec seulement quatre paires de points 2D/3D. La résultante extrinsèque que je reçois du rvec et tvec retourné de cv::solvePnP() est massivement éteinte en termes de translation et de rotation par rapport à une vérité de terrain extrinsèque que j'ai créée manuellement ainsi qu'une analyse visuelle de base (La traduction implique une distance de 1100mm tandis que la caméra est au plus à 10mm). Au départ, je pensais que le problème était que j'avais quelques ambiguïtés dans la façon dont la pose de mon tableau était déterminée, mais maintenant je suis assez certain que ce n'est pas le cas. Le calcul semble assez simple et après tout mon travail sur la mise en place du système, se laisser prendre à ce qui est essentiellement un one-liner est une énorme frustration. Je suis honnêtement à court d'options, donc si quelqu'un peut aider, je serais énormément dans votre dette. Mon code de test est posté ci-dessous et est le même que ma mise en œuvre moins quelques appels de rendu. La extrinsèque de vérité terrain je travaille avec mon que le programme est comme suit (en gros une rotation pure autour d'un axe et une petite traduction):

1  0  0  29 
0 .77 -.64 32.06 
0 .64 .77 -39.86 
0  0  0  1 

Merci!

#include <opencv2\opencv.hpp> 
#include <opencv2\highgui\highgui.hpp> 

int main() 
{ 
    int imageSize  = 4; 
    int markupsSize = 4; 
    std::vector<cv::Point2d> imagePoints; 
    std::vector<cv::Point3d> markupsPoints; 
    double tempImage[3], tempMarkups[3]; // Temp variables or iterative data construction 

    cv::Mat CamMat = (cv::Mat_<double>(3,3) << (566.07469648019332), 0, 318.20416967732666, 
     0, (565.68051204299513), -254.95231997403764, 0, 0, 1); 

    cv::Mat DistMat = (cv::Mat_<double>(5,1) << -1.1310542849120900e-001, 4.5797249991542077e-001, 
    7.8356355644908070e-003, 3.7617039978623504e-003, -1.2734302146228518e+000); 

    cv::Mat rvec = cv::Mat::zeros(3,1, cv::DataType<double>::type); 
    cv::Mat tvec = cv::Mat::zeros(3,1,cv::DataType<double>::type); 
    cv::Mat R; 
    cv::Mat extrinsic = cv::Mat::eye(4, 4, CV_64F); 

    // Escape if markup lists aren't equally sized 
    if(imageSize != markupsSize) 
    { 
    //TODO: Replace with try, throw error code, and catch in qSlicerLocalizationModuleWidget 
    return 0; 
    } 

    // Four principal chessboard corners only 
    imagePoints.push_back(cv::Point2d(368.906, 248.123)); 
    imagePoints.push_back(cv::Point2d(156.583, 252.414)); 
    imagePoints.push_back(cv::Point2d(364.808, 132.384)); 
    imagePoints.push_back(cv::Point2d(156.692, 128.289)); 

    markupsPoints.push_back(cv::Point3d(495.115, 39.106, 93.79)); 
    markupsPoints.push_back(cv::Point3d(463.143, -86.286, -51.178)); 
    markupsPoints.push_back(cv::Point3d(500.236, 121.988, 24.056)); 
    markupsPoints.push_back(cv::Point3d(471.276, -3.23, -127.809));  

    // Larger data set 
    /*imagePoints.push_back(cv::Point2d(482.066, 233.778)); 
    imagePoints.push_back(cv::Point2d(448.024, 232.038)); 
    imagePoints.push_back(cv::Point2d(413.895, 230.785)); 
    imagePoints.push_back(cv::Point2d(380.653, 229.242)); 
    imagePoints.push_back(cv::Point2d(347.983, 227.785)); 
    imagePoints.push_back(cv::Point2d(316.103, 225.977)); 
    imagePoints.push_back(cv::Point2d(284.02, 224.905)); 
    imagePoints.push_back(cv::Point2d(252.929, 223.611)); 
    imagePoints.push_back(cv::Point2d(483.41, 200.527)); 
    imagePoints.push_back(cv::Point2d(449.456, 199.406)); 
    imagePoints.push_back(cv::Point2d(415.843, 197.849)); 
    imagePoints.push_back(cv::Point2d(382.59, 196.763)); 
    imagePoints.push_back(cv::Point2d(350.094, 195.616)); 
    imagePoints.push_back(cv::Point2d(317.922, 194.027)); 
    imagePoints.push_back(cv::Point2d(286.922, 192.814)); 
    imagePoints.push_back(cv::Point2d(256.006, 192.022)); 
    imagePoints.push_back(cv::Point2d(484.292, 167.816)); 
    imagePoints.push_back(cv::Point2d(450.678, 166.982)); 
    imagePoints.push_back(cv::Point2d(417.377, 165.961)); 

    markupsPoints.push_back(cv::Point3d(457.132, 59.822, 89.247)); 
    markupsPoints.push_back(cv::Point3d(451.634, 42.015, 69.719)); 
    markupsPoints.push_back(cv::Point3d(447.06, 22.927, 48.635)); 
    markupsPoints.push_back(cv::Point3d(442.424, 4.454, 28.659)); 
    markupsPoints.push_back(cv::Point3d(437.621, -14.395, 7.495)); 
    markupsPoints.push_back(cv::Point3d(433.386, -33.034, -12.009)); 
    markupsPoints.push_back(cv::Point3d(429.227, -51.001, -32.269)); 
    markupsPoints.push_back(cv::Point3d(424.291, -70.266, -52.667)); 
    markupsPoints.push_back(cv::Point3d(460.300, 79.769, 69.948)); 
    markupsPoints.push_back(cv::Point3d(455.020, 61.379, 49.306)); 
    markupsPoints.push_back(cv::Point3d(450.501, 43.288, 30.250)); 
    markupsPoints.push_back(cv::Point3d(446.062, 24.572, 8.713)); 
    markupsPoints.push_back(cv::Point3d(441.346, 5.823, -10.997)); 
    markupsPoints.push_back(cv::Point3d(436.670, -13.135, -31.428)); 
    markupsPoints.push_back(cv::Point3d(432.367, -31.428, -51.785)); 
    markupsPoints.push_back(cv::Point3d(427.745, -50.016, -72.519)); 
    markupsPoints.push_back(cv::Point3d(464.824, 101.129, 52.251)); 
    markupsPoints.push_back(cv::Point3d(458.628, 81.864, 30.810)); 
    markupsPoints.push_back(cv::Point3d(454.120, 63.546, 10.458)); */ 


    // Calculate camera pose 
    cv::solvePnP(cv::Mat(markupsPoints), cv::Mat(imagePoints), CamMat, DistMat, rvec, tvec); 
    cv::Rodrigues(rvec, R); 

    // Invert results of Rodrigues by transposing rotation matrix and calculating inverted tvec 
    R = R.t(); 
    tvec = -R * tvec; // translation of inverse 

    std::cout << "Tvec = " << std::endl << tvec << std::endl; 

    // Copy R and tvec into the extrinsic matrix 
    extrinsic(cv::Range(0,3), cv::Range(0,3)) = R * 1; 
    extrinsic(cv::Range(0,3), cv::Range(3,4)) = tvec * 1; 

    // Fill last row of homogeneous transform (0,0,0,1) 
    double *p = extrinsic.ptr<double>(3); 
    p[0] = p[1] = p[2] = 0; p[3] = 1; 

    std::cout << "Extrinsic = " << std::endl << extrinsic << std::endl << std::endl; 
    std::cout << "Extrinsic (inv) = " << std::endl << extrinsic.inv() << std::endl; 
    std::cin >> tempImage[0]; 
    return 0; 
} 

EDIT 1: J'essayé de normaliser les valeurs de pixels en utilisant la méthode de Chi Xu (xn = (x-cx)/f, yn = (y-cy)/f). Pas de chance :(

EDIT 2: Comme il semble que presque tous ceux qui utilisent solvePnP utilisent une méthode où ils marquent les coins en damier comme les vecteurs de leur monde et origine, je vais essayer d'enregistrer mon tracker sur le Si cela fonctionne comme prévu, la première coordonnée I sera approximativement < 0,0> La transformation résultante de solvePnP sera alors multipliée par l'inverse de la transformation du monde en caméra-marqueur, ce qui entraînera (espérons-le Après avoir établi un système de coordonnées sur le damier, j'ai calculé la transformation cTw de l'espace en damier à l'espace du monde et vérifié dans mon environnement de rendu, la matrice extrinsèque

. Il Puis, on a calculé le mTc extrinsèque représentant la transformée de l'espace de la caméra à l'espace du damier. La transformée de marqueur de caméra wTr a été acquise à partir du système de suivi. Enfin, je pris toutes les transformations et les multipliai comme suit pour déplacer ma transformation tout le chemin d'origine de la caméra marqueur de la caméra:

mTc*cTw*wTr

Lo et voici, il a donné le la même résultat exact. Je meurs d'ici pour tout signe de ce que je fais mal. Si quelqu'un a la moindre idée, je vous prie de m'aider.

EDIT 4: Aujourd'hui, j'ai réalisé que j'avais configuré mes axes en damier de telle sorte qu'ils se trouvaient dans un système de coordonnées gaucher. Comme OpenCV fonctionne en utilisant la forme standard à droite, j'ai décidé de réessayer les tests avec mes axes de cadre en damier configurés de manière droite. Alors que les résultats étaient à peu près les mêmes, j'ai remarqué que la transformation monde-damier était maintenant un format de transformation non standard, c'est-à-dire que la matrice de rotation 3x3 n'était plus à peu près symétrique autour de la diagonale. Cela indique que mon système de suivi n'utilise pas de système de coordonnées de droite (cela aurait été génial s'ils l'avaient documenté ou quoi que ce soit, d'ailleurs). Bien que je ne sois pas sûr de la façon de résoudre ce problème, je suis certain que cela fait partie du problème. Je vais continuer à marteler et j'espère que quelqu'un ici sait quoi faire. J'ai également ajouté un diagramme qui m'a été fourni sur les cartes OpenCV par Eduardo. Merci Eduardo!

+0

* Ou n'importe quoi, d'ailleurs * +1 – null

Répondre

3

J'ai donc résolu le problème. Sans surprise, c'était dans la mise en œuvre, pas la théorie. Lorsque le projet a été initialement conçu, nous avons utilisé un tracker basé sur une webcam. Ce tracker avait l'axe z sortant du plan marqueur. Lorsque nous sommes passés à un tracker optique, le code a été porté principalement tel quel. Malheureusement pour nous (RIP 2 mois de ma vie), nous n'avons jamais vérifié si l'axe z sortait toujours du plan marqueur (ce n'était pas le cas). Ainsi, le pipeline de rendu a été affecté à la vue erronée et voir les vecteurs à la caméra de scène. Cela fonctionne principalement maintenant, sauf que les traductions sont désactivées pour une raison quelconque. Problème complètement différent cependant!