2013-04-30 15 views
25

Sto provando a triangolare alcuni punti con OpenCV e ho trovato questa funzione cv::triangulatePoints(). Il problema è che non c'è quasi nessuna documentazione o esempi di esso.Come utilizzare correttamente cv :: triangulatePoints()

Ho qualche dubbio a riguardo.

  1. Quale metodo usa? Ho fatto una piccola ricerca sulle triangolazioni e ci sono diversi metodi (Linear, Linear LS, eigen, iterative LS, iterative eigen, ...) ma non riesco a trovare quale sia in uso in OpenCV.

  2. Come devo usarlo? Sembra che come input abbia bisogno di una matrice di proiezione e 3xN omogeneo 2D punti. Li ho definiti come std::vector<cv::Point3d> pnts, ma come output sono necessari gli array 4xN e ovviamente non riesco a creare uno std::vector<cv::Point4d> perché non esiste, quindi come dovrei definire il vettore di output?

Per la seconda domanda ho provato: cv::Mat pnts3D(4,N,CV_64F); e cv::Mat pnts3d;, non sembra funzionare (viene generata un'eccezione).

+0

Hai cercato su [OpenCV documentation website] (http://docs.opencv.org/modules/calib3d/doc /camera_calibration_and_3d_reconstruction.html#triangulatepoints)? – sgarizvi

+0

@ sgar91 infatti l'ho fatto, ma quella documentazione non risolve nessuna delle mie domande! –

+0

controlla [questo] (https://github.com/MasteringOpenCV/code/blob/master/Chapter4_StructureFromMotion/Triangulation.cpp). – William

risposta

40

1.- Il metodo utilizzato è Minimi quadrati, il più semplice. Ci sono metodi molto migliori di questo. Ancora è il più comune, in quanto gli altri metodi potrebbero non riuscire in alcuni casi (cioè alcuni altri falliscono se i punti sono in piano o all'infinito).

Il metodo può essere trovato in multipla Geometry View in Computer Vision da Richard Hartley e Andrew Zisserman (P312)

2.- L'utilizzo:

cv::Mat pnts3D(1,N,CV_64FC4); 
cv::Mat cam0pnts(1,N,CV_64FC2); 
cv::Mat cam1pnts(1,N,CV_64FC2); 

Riempi le 2 matrici punto chanel con i punti nelle immagini.

cam0 e cam1 sono matrici di telecamera Mat3x4 (parametri intrinseci ed estrinseci). Puoi costruirli moltiplicando A * RT, dove A è la matrice dei parametri intrinseci e RT la traduzione di rotazione 3x4 pose matrix.

cv::triangulatePoints(cam0,cam1,cam0pnts,cam1pnts,pnts3D); 

NOTA: pnts3D deve essere un canale 4 1xNcv::Mat quando definito, getta un'eccezione se non, ma il risultato è una matrice cv::Mat(4,N,cv_64FC1). Davvero confuso, ma è l'unico modo in cui non ho ottenuto un'eccezione.


UPDATE: Dalla versione 3.0 o forse prima, ciò non è più vero, e pnts3D può anche essere di tipo Mat(4,N,CV_64FC1) oppure può essere lasciata completamente vuota (come al solito, è creato all'interno della funzione) .

+3

Grazie, un buon punto di partenza. Man, la documentazione di OpenCV è BAD. Voglio dire, è meglio di niente documentazione, ma comunque ... –

+0

@ dare2be Mi ci è voluto tempo per scoprire queste informazioni ... –

+3

Questa risposta è stata molto utile grazie ... la documentazione opencv manca di un esempio – mkuse

0

In alternativa è possibile utilizzare il metodo da Hartley & Zisserman implementato qui: http://www.morethantechnical.com/2012/01/04/simple-triangulation-with-opencv-from-harley-zisserman-w-code/

+0

Tuttavia non riesco a far funzionare il codice. –

+1

Questo codice è pieno di bug. – aledalgrande

+0

Il codice di Chris che collega a morethantechnical funziona abbastanza con alcune modifiche. Il tizio di morethantechnical è uno degli autori del libro Mastering OpenCV. Ma va detto che ci sono alcuni bug in esso. –

2

ho provato cv :: triangulatePoints, ma in qualche modo calcola spazzatura. Sono stato costretto ad implementare un metodo di triangolazione lineare manualmente, che restituisce una matrice 4x1 per il punto 3D triangolato:

Mat triangulate_Linear_LS(Mat mat_P_l, Mat mat_P_r, Mat warped_back_l, Mat warped_back_r) 
{ 
    Mat A(4,3,CV_64FC1), b(4,1,CV_64FC1), X(3,1,CV_64FC1), X_homogeneous(4,1,CV_64FC1), W(1,1,CV_64FC1); 
    W.at<double>(0,0) = 1.0; 
    A.at<double>(0,0) = (warped_back_l.at<double>(0,0)/warped_back_l.at<double>(2,0))*mat_P_l.at<double>(2,0) - mat_P_l.at<double>(0,0); 
    A.at<double>(0,1) = (warped_back_l.at<double>(0,0)/warped_back_l.at<double>(2,0))*mat_P_l.at<double>(2,1) - mat_P_l.at<double>(0,1); 
    A.at<double>(0,2) = (warped_back_l.at<double>(0,0)/warped_back_l.at<double>(2,0))*mat_P_l.at<double>(2,2) - mat_P_l.at<double>(0,2); 
    A.at<double>(1,0) = (warped_back_l.at<double>(1,0)/warped_back_l.at<double>(2,0))*mat_P_l.at<double>(2,0) - mat_P_l.at<double>(1,0); 
    A.at<double>(1,1) = (warped_back_l.at<double>(1,0)/warped_back_l.at<double>(2,0))*mat_P_l.at<double>(2,1) - mat_P_l.at<double>(1,1); 
    A.at<double>(1,2) = (warped_back_l.at<double>(1,0)/warped_back_l.at<double>(2,0))*mat_P_l.at<double>(2,2) - mat_P_l.at<double>(1,2); 
    A.at<double>(2,0) = (warped_back_r.at<double>(0,0)/warped_back_r.at<double>(2,0))*mat_P_r.at<double>(2,0) - mat_P_r.at<double>(0,0); 
    A.at<double>(2,1) = (warped_back_r.at<double>(0,0)/warped_back_r.at<double>(2,0))*mat_P_r.at<double>(2,1) - mat_P_r.at<double>(0,1); 
    A.at<double>(2,2) = (warped_back_r.at<double>(0,0)/warped_back_r.at<double>(2,0))*mat_P_r.at<double>(2,2) - mat_P_r.at<double>(0,2); 
    A.at<double>(3,0) = (warped_back_r.at<double>(1,0)/warped_back_r.at<double>(2,0))*mat_P_r.at<double>(2,0) - mat_P_r.at<double>(1,0); 
    A.at<double>(3,1) = (warped_back_r.at<double>(1,0)/warped_back_r.at<double>(2,0))*mat_P_r.at<double>(2,1) - mat_P_r.at<double>(1,1); 
    A.at<double>(3,2) = (warped_back_r.at<double>(1,0)/warped_back_r.at<double>(2,0))*mat_P_r.at<double>(2,2) - mat_P_r.at<double>(1,2); 
    b.at<double>(0,0) = -((warped_back_l.at<double>(0,0)/warped_back_l.at<double>(2,0))*mat_P_l.at<double>(2,3) - mat_P_l.at<double>(0,3)); 
    b.at<double>(1,0) = -((warped_back_l.at<double>(1,0)/warped_back_l.at<double>(2,0))*mat_P_l.at<double>(2,3) - mat_P_l.at<double>(1,3)); 
    b.at<double>(2,0) = -((warped_back_r.at<double>(0,0)/warped_back_r.at<double>(2,0))*mat_P_r.at<double>(2,3) - mat_P_r.at<double>(0,3)); 
    b.at<double>(3,0) = -((warped_back_r.at<double>(1,0)/warped_back_r.at<double>(2,0))*mat_P_r.at<double>(2,3) - mat_P_r.at<double>(1,3)); 
    solve(A,b,X,DECOMP_SVD); 
    vconcat(X,W,X_homogeneous); 
    return X_homogeneous; 
} 

i parametri di input sono due matrici di proiezione fotocamera 3x4 e una coppia corrispondente di pixel sinistra/destra (x, y , w).

+0

Proverò il metodo opencv un giorno. Il mio scopo è quello di fornire una funzione alternativa se hai problemi con il metodo opencv o semplicemente non vuoi usarlo. Per dimostrare che è preciso vedere risultato triangolato http://imagizer.imageshack.us/v2/150x100q90/674/3tYH9R.png dall'originale http://imagizer.imageshack.us/v2/150x100q90/540/wyg6bC.png – YuZ

+0

Sicuramente molto buono per avere un metodo alternativo qui intorno. –

7

Una piccola aggiunta alla risposta di @Ander Biguri. Si dovrebbe ottenere i punti immagine un'immagine non undistort Ed in poi, e invocare undistortPoints() sulla cam0pnts e cam1pnts, perché cv::triangulatePoints aspetta i punti 2D in coordinate normalizzate (indipendenti dalla fotocamera) e cam0 e cam1 dovrebbe essere solo [R | t^T] MATERIE Non è necessario moltiplicarlo con A.

+0

Non sono sicuro che abbiano cambiato il codice o meno, ma sono abbastanza sicuro che era necessario al momento della risposta. Un moderatore ha modificato la mia risposta aggiungendo un po 'più di spiegazioni, concordando con me. –

+0

Non so, ma non riuscivo a farlo funzionare come hai postato (comunque ha perfettamente senso), e volevo condividere il mio modo di farlo, forse qualcuno potrebbe incontrarlo di nuovo. ;) –

+0

Stranamente, l'ho fatto funzionare con una precisione abbastanza elevata nel mio sistema (0.02 pixel) quindi immagino che funzioni. Comunque è stato troppo tempo fa! –

2

Grazie ad Ander Biguri! La sua risposta mi ha aiutato molto. Ma io preferisco sempre l'alternativa con std :: vector, ho modificato la sua soluzione a questo:

std::vector<cv::Point2d> cam0pnts; 
std::vector<cv::Point2d> cam1pnts; 
// You fill them, both with the same size... 

// You can pick any of the following 2 (your choice) 
// cv::Mat pnts3D(1,cam0pnts.size(),CV_64FC4); 
cv::Mat pnts3D(4,cam0pnts.size(),CV_64F); 

cv::triangulatePoints(cam0,cam1,cam0pnts,cam1pnts,pnts3D); 

quindi basta fare emplace_back in zona punti. Vantaggio principale: non è necessario conoscere la dimensione N prima di iniziare a riempirli. Sfortunatamente non esiste cv :: Point4f, quindi pnts3D deve essere un cv :: Mat ...

+1

Fortunatamente questo fucntion sembrano accettare ingressi meglio ora (2017), quando ho scritto la domanda/risposta (2013) è stato un disastro! –

+1

Il documento è ancora un po 'disordinato, quindi la tua risposta mi ha aiutato a farlo! Grazie! –