5

Attualmente sto cercando di implementare un metodo alternativo per l'AR basato su webcam utilizzando un sistema di tracciamento esterno. Ho tutto nel mio ambiente configurato salvo per la calibrazione estrinseca. Ho deciso di usare cv::solvePnP() come presumibilmente fa esattamente quello che voglio, ma dopo due settimane mi sto tirando fuori i capelli cercando di farlo funzionare. Un diagramma qui sotto mostra la mia configurazione. c1 è la mia macchina fotografica, c2 è il tracciatore ottico che sto usando, M è il marker tracciato collegato alla telecamera, e ch è la scacchiera.Calibrazione estrinseca con cv :: SolvePnP

Diagram of my configuration

Così com'è passo nella mia pixel dell'immagine coordinate acquisite con cv::findChessboardCorners(). I punti del mondo vengono acquisiti con riferimento al marker tracciato M apposto alla telecamera c1 (L'estrinseco è quindi la trasformazione dal frame di questo marker all'origine della telecamera). Ho testato questo con set di dati fino a 50 punti per mitigare la possibilità di minimi locali, ma per ora sto testando solo con quattro coppie di punti 2D/3D. Il risultato estrinseco che ottengo da rvec e tvec restituito da cv::solvePnP() è massicciamente off in termini sia di traslazione sia di rotazione relativi sia a una verità di base estrinseca che ho creato manualmente, sia a un'analisi visiva di base (la traduzione implica una distanza di 1100mm mentre la fotocamera è al massimo 10 mm di distanza).

Inizialmente ho pensato che il problema era che avevo alcune ambiguità nel modo in cui la posa della mia tavola era stata determinata, ma ora sono abbastanza certo che non è così. La matematica sembra piuttosto semplice e, dopo tutto il mio lavoro su come impostare il sistema, rimanere coinvolto in quello che è essenzialmente un one-liner è un'enorme frustrazione. Sono sinceramente a corto di opzioni, quindi se qualcuno può aiutare sarei enormemente in debito con te. Il mio codice di prova è pubblicato di seguito ed è lo stesso della mia implementazione meno alcune chiamate di rendering. L'estrinseca verità a terra ho che funziona con il mio programma è il seguente (in pratica una rotazione pura attorno un asse e una piccola traduzione):

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

Grazie!

#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: Ho provato normalizzando i valori dei pixel utilizzando il metodo di Xu Chi (xn = (x-cx)/f, yn = (y-cy)/f). Nessuna fortuna :(

EDIT 2: Come sembra che quasi tutti quelli che usano solvePnP utilizzino un metodo in cui contrassegnano gli angoli della scacchiera come i vettori per il loro mondo e origine, ho intenzione di provare a registrare il mio tracker sul scacchiera Se funziona come previsto, il primo segno I della coordinata sarà circa < 0,0>. La trasformata risultante da solvePnP sarà quindi moltiplicata per l'inverso della trasformazione del marker da fotocamera a macchina, risultando in tal modo (si spera) la matrice estrinseca

EDIT 3: Ho eseguito i passaggi descritti nel passaggio 2. Dopo aver stabilito un sistema di coordinate sulla scacchiera, ho calcolato la trasformazione cTw dallo spazio della scacchiera allo spazio mondiale e l'ho verificato nel mio ambiente di rendering Io t hen ha calcolato l'mTc estrinseco che rappresenta la trasformazione dallo spazio della telecamera allo spazio della scacchiera. La trasformazione dell'indicatore della telecamera wTr è stata acquisita dal sistema di tracciamento. Infine, ho preso tutte le trasformazioni e moltiplicato come segue per spostare la mia trasformazione tutta la strada da origine fotocamera a fotocamera marcatore:

mTc*cTw*wTr

Ed ecco ha dato esattamente lo stesso risultato il . Sto morendo qui per qualsiasi segno di ciò che sto facendo male. Se qualcuno ha qualche idea, ti prego di aiutarti.

EDIT 4: Oggi mi sono reso conto che avevo configurato i miei assi a scacchiera in modo tale che fossero in un sistema di coordinate mancino. Dato che OpenCV opera utilizzando la forma standard a destra, ho deciso di riprovare i test con gli assi del telaio a scacchiera configurati in modo corretto. Sebbene i risultati fossero all'incirca uguali, notai che la trasformazione da mondo a scacchiera era ora un formato di trasformazione non standard, cioè la matrice di rotazione 3x3 non era più approssimativamente simmetrica attorno alla diagonale come dovrebbe essere. Questo indica che il mio sistema di tracciamento non sta usando un sistema di coordinate a destra (sarebbe stato bello se lo avessero documentato, o qualsiasi altra cosa, per quello). Anche se non sono sicuro di come risolvere questo problema, sono certo che faccia parte del problema. Continuerò a martellarlo e spero che qualcuno qui sappia cosa fare. Ho anche aggiunto un diagramma fornito a me sulle schede OpenCV entro il Eduardo. Grazie Eduardo!

+0

* O qualsiasi altra cosa, per questo * +1 – null

risposta

3

Quindi ho capito il problema. Non sorprendentemente, è stato nell'implementazione, non nella teoria. Quando il progetto è stato inizialmente progettato, abbiamo utilizzato un tracker basato su webcam. Questo tracker aveva l'asse z che usciva dal piano del marker. Quando ci siamo spostati su un tracker ottico, il codice è stato portato per lo più così com'è. Sfortunatamente per noi (RIP 2 mesi della mia vita), non abbiamo mai controllato se l'asse z uscisse ancora dal piano del marker (non lo fece). Quindi alla pipeline di rendering è stata assegnata la vista errata e sono stati visualizzati i vettori sulla telecamera di scena. Funziona principalmente ora, tranne che le traduzioni sono disattivate per qualche motivo. Tuttavia, problema completamente diverso!

Problemi correlati