5

Obecnie próbuję wprowadzić alternatywną metodę do AR opartego na kamerach internetowych z wykorzystaniem zewnętrznego systemu śledzenia. Mam wszystko w moim środowisku skonfigurowane z zachowaniem kalibracji zewnętrznej. Postanowiłem użyć cv::solvePnP(), ponieważ podobno dokładnie to chcę, ale po dwóch tygodniach wyciągam włosy, próbując je uruchomić. Poniższy schemat pokazuje moją konfigurację. c1 to moja kamera, c2 to tracker optyczny, którego używam, M to znacznik śledzący podłączony do kamery, a ch to szachownica.Extrinsic Calibration With cv :: SolvePnP

Diagram of my configuration

W obecnej mijam na mój obraz piksel współrzędne nabyte z cv::findChessboardCorners(). Punkty światowe są pozyskiwane w odniesieniu do śledzonego znacznika M przymocowanego do kamery c1 (Zewnętrzny jest zatem przekształceniem z ramki tego znacznika do źródła kamery). Przetestowałem to z zestawami danych do 50 punktów, aby złagodzić możliwość lokalnych minimów, ale na razie testuję tylko z czterema parami punktów 2D/3D. Wynikowy wynik zewnętrzny uzyskany z rvec i tvec zwrócony z cv::solvePnP() jest masowo wyłączony pod względem zarówno translacji, jak i obrotu w stosunku zarówno do naziemnej prawdy zewnętrznej utworzonej ręcznie, jak i podstawowej analizy wizualnej (tłumaczenie implikuje odległość 1100 mm, podczas gdy kamera znajduje się co najwyżej 10 mm).

Początkowo myślałem, że problem polega na tym, że miałem pewne niejasności co do tego, w jaki sposób moja pozycja na desce została określona, ​​ale teraz jestem prawie pewien, że tak nie jest. Matematyka wydaje się całkiem prosta, a po moich wszystkich pracach nad ustawianiem systemu, złapanie na tym, co jest zasadniczo jednolinikiem, jest ogromną frustracją. Szczerze mówiąc, kończą mi się opcje, więc jeśli ktokolwiek może mi pomóc, będę ogromnie zadowolony z twojego długu. Mój kod testowy jest zamieszczony poniżej i jest taki sam jak moja implementacja minus niektóre wywołania renderujące. Grunt prawda zewnętrzna mam, która współpracuje z mojego programu jest następujący (Zasadniczo czysty obrót wokół jednej osi i mała tłumaczenie):

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

Dzięki!

#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: I próbuje normalizację wartości pikseli za pomocą metody Chi Xu (xn = (X-cx)/f in = (Y-CY)/F). Bez powodzenia :(

EDYCJA 2: Jak się wydaje, prawie każdy, kto używa solvePnP używa metody, w której zaznacza rogi szachownicy jako wektory dla ich ram i pochodzenia świata, spróbuję zarejestrować mój tracker do Jeśli działa to zgodnie z oczekiwaniami, pierwsza współrzędna, którą zaznaczę, będzie w przybliżeniu równa < 0,0>. Wynikowa transformacja z solvePnP będzie następnie pomnożona przez odwrotność transformacji świat-kamera-znacznik, w wyniku czego (miejmy nadzieję) Zewnętrzna matryca:

EDYCJA 3: Wykonałem kroki opisane w kroku 2. Po ustaleniu układu współrzędnych na szachownicy, obliczyłem transformację cTw z obszaru szachownicy na przestrzeń światową i zweryfikowałem ją w moim środowisku renderowania . To kura obliczyła zewnętrzne mTc reprezentujące transformację z przestrzeni kamery do przestrzeni szachownicy. Znacznik przekształcenia znacznika kamery wTr uzyskano z systemu śledzenia. Wreszcie wziąłem wszystkie transformacje i pomnożyć je w następujący sposób przenieść moja przemiana całą drogę od pochodzenia kamery do kamery markera:

mTc*cTw*wTr

I oto dał The dokładnie ten sam rezultat. Umieram tu z powodu jakichkolwiek oznak tego, co robię źle. Jeśli ktokolwiek ma jakąś wskazówkę, błagam cię o pomoc.

EDYCJA 4: Dziś zdałem sobie sprawę, że skonfigurowałem moje osie szachownicy w taki sposób, że znajdowały się one w leworęcznym układzie współrzędnych. Ponieważ OpenCV działa za pomocą standardowej, prawej ręki, postanowiłem spróbować ponownie z moimi osiami w szachownicy skonfigurowanymi w prawą stronę. Podczas gdy wyniki były mniej więcej takie same, zauważyłem, że transformacja między światem a szachownicą była teraz niestandardowym formatem przekształcania, tj. Macierz obrotu 3x3 nie była już z grubsza symetryczna wokół przekątnej, tak jak powinna. Oznacza to, że mój system monitorujący nie używa układu współrzędnych po prawej stronie (byłoby wspaniale, gdyby to udokumentowali, lub cokolwiek, jeśli o to chodzi). Chociaż nie jestem pewien, jak rozwiązać ten problem, jestem pewien, że jest to część problemu. Będę dalej walić i mam nadzieję, że ktoś tu wie, co robić. Dodałem także diagram dostarczony mi na tablicach OpenCV przez Eduardo. Dzięki Eduardo!

+0

* Lub cokolwiek, o to chodzi * +1 – null

Odpowiedz

3

Więc wymyśliłem problem. Nie było zaskakujące, że chodziło o implementację, a nie o teorię. Kiedy projekt został pierwotnie zaprojektowany, wykorzystaliśmy tracker z kamerą internetową. Ten tracker miał oś Z wychodzącą z płaszczyzny znacznika. Kiedy przenieśliśmy się do śledzenia optycznego, kod został przeniesiony w większości tak jak jest. Na nieszczęście dla nas (RIP 2 miesiące mojego życia), nigdy nie sprawdziliśmy, czy oś z nadal pochodzi z płaszczyzny znacznika (nie było). Tak więc rurociągowi renderowania przypisano niewłaściwy widok i wyświetlono wektory do kamery sceny. Działa to głównie teraz, z wyjątkiem tego, że tłumaczenia z jakiegoś powodu są wyłączone. Zupełnie inny problem jednak!