2014-08-28 18 views
7

Per il mio progetto di realtà aumentata, ho un modello 3D visualizzato utilizzando la fotocamera VTK e un oggetto reale del modello visualizzato utilizzando una fotocamera reale.Come applicare la trasformazione della posa della telecamera calcolata usando EPnP alla videocamera VTK?

Ho utilizzato EPnP per stimare la matrice estrinseca della videocamera reale (questa fotocamera è già stata calibrata prima della mano, quindi conosco i parametri interni) fornendo punti 3D da VTK e i corrispondenti punti 2D dall'immagine della telecamera reale e parametri interni della camera reale per il funzionamento dell'algoritmo EPnP.

Successivamente, ho ottenuto una matrice di rotazione e traslazione con gli elementi -> R1, R2, R3, ....., R9 e t1, t2 e t3.

Quindi la mia matrice estrinseca della vera fotocamera si presenta così (chiamiamolo questo extrinsicReal)

R1 R2 R3 T1 
R4 R5 R6 T2 
R7 R8 R9 T3 
0 0 0 1 

Dopo questo, ho stima la matrice estrinseca della mia macchina fotografica VTK utilizzando il seguente codice:

vtkSmartPointer<vtkMatrix4x4> extrinsicVTK = vtkSmartPointer<vtkMatrix4x4>::New(); 
extrinsicVTK->DeepCopy(renderer->GetActiveCamera()->GetViewTransformMatrix()); 

Per fondere il modello 3D della videocamera VTK con la fotocamera reale, la videocamera VTK deve essere impostata su una posizione uguale a quella della posizione reale della videocamera e la lunghezza focale della videocamera VTK deve essere uguale a quella della fotocamera reale . Un altro passo importante è quello di applicare la stessa matrice estrinseca della videocamera reale alla videocamera VTK. Come lo faccio?

Quello che ho fatto è stato l'inverso di extrinsicReal e l'ho moltiplicato con l'extrinsicVTK per ottenere una nuova matrice 4 * 4 (chiamiamola newMatrix). Ho applicato questa matrice per la trasformazione della videocamera VTK.

vtkSmartPointer<vtkMatrix4x4> newMatrix = vtkSmartPointer<vtkMatrix4x4>::New(); 
vtkMatrix4x4::Multiply4x4(extrinsicRealInvert,extrinsicVTK,newMatrix); 

vtkSmartPointer<vtkTransform> transform = vtkSmartPointer<vtkTransform>::New(); 
transform->SetMatrix(NewM); 
transform->Update(); 

renderer->GetActiveCamera()->ApplyTransform(transform); 

Non sono sicuro se questo è il metodo corretto. Ma ho controllato la posizione reale della telecamera (che ho ottenuto dopo EPnP) e la posizione della videocamera VTK (dopo aver applicato la trasformazione sopra) e sono entrambi esattamente uguali. Inoltre, l'orientamento della camera reale e la direzione di proiezione della videocamera VTK sono uguali.

Il problema è che anche dopo aver abbinato i parametri di cui sopra sia al VTK che alla telecamera reale, il modello 3D VTK non sembra essere perfettamente allineato al video della telecamera reale. Qualcuno può guidarmi passo passo per eseguire il debug del problema?

risposta

10

Sì, le cose si complicano quando si applicano questi parametri alla videocamera VTK. Ecco come l'ho fatto (solo estratti degli importanti passaggi di codice, l'intero codice sarebbe troppo per incollarlo qui e sarebbe comunque inutile per te). Altri punti da considerare:

  1. Sto rendendo l'immagine dell'endoscopio come texture di sfondo nella mia vtkRenderWindow.
  2. Sto usando un mix di VTK, ITK (VNL), funzioni OpenCV ma dovrebbero essere intercambiabili (ad es cvRound potrebbe anche essere sostituito da vtkMath :: Round() ecc)

Prima di tutto, io uso la macchina fotografica attiva dal mio vtkRenderer:

d->m_Renderer->GetActiveCamera() 

il passo successivo è quello di aggiornare continuamente la fotocamera attiva applicando la vostra trasformazione. A seconda che la finestra di rendering sia ridimensionabile o meno, è necessario inizializzare o anche aggiornare continuamente due ulteriori parametri: 1. ViewAngle, 2.WindowCenter (ESTREMAMENTE importante, non documentato affatto da vtk. Ma alla fine devi applicare il tuo punto principale qui che hai trovato per calibrazione o avrai le superfici renderizzate con un offset. Ci sono voluti 3 mesi per trovare questa linea soluzione).

Calcolo dell'angolo vista:

double focalLengthY = _CameraIntrinsics->GetFocalLengthY(); 
    if(_WindowSize.height != _ImageSize.height) 
    { 
    double factor = static_cast<double>(_WindowSize.height)/static_cast<double>(_ImageSize.height); 
    focalLengthY = _CameraIntrinsics->GetFocalLengthY() * factor; 
    } 

    _ViewAngle = 2 * atan((_WindowSize.height/2)/focalLengthY) * 180/vnl_math::pi; 

Applicare l'angolo di visualizzazione:

d->m_Renderer->GetActiveCamera()->SetViewAngle(viewAngle); 

Calcolo del WindowCenter:

double px = 0; 
    double width = 0; 

    double py = 0; 
    double height = 0; 

    if(_ImageSize.width != _WindowSize.width || _ImageSize.height != _WindowSize.height) 
    { 
    double factor = static_cast<double>(_WindowSize.height)/static_cast<double>(_ImageSize.height); 

    px = factor * _CameraIntrinsics->GetPrincipalPointX(); 
    width = _WindowSize.width; 
    int expectedWindowSize = cvRound(factor * static_cast<double>(_ImageSize.width)); 
    if(expectedWindowSize != _WindowSize.width) 
    { 
     int diffX = (_WindowSize.width - expectedWindowSize)/2; 
     px = px + diffX; 
    } 

    py = factor * _CameraIntrinsics->GetPrincipalPointY(); 
    height = _WindowSize.height; 
    } 
    else 
    { 
    px = _CameraIntrinsics->GetPrincipalPointX(); 
    width = _ImageSize.width; 

    py = _CameraIntrinsics->GetPrincipalPointY(); 
    height = _ImageSize.height; 
    } 

    double cx = width - px; 
    double cy = py; 

    _WindowCenter.x = cx/((width-1)/2) - 1 ; 
    _WindowCenter.y = cy/((height-1)/2) - 1; 

Impostazione del Centro Window:

d->m_Renderer->GetActiveCamera()->SetWindowCenter(_WindowCenter.x, _WindowCenter.y); 

Applicando la matrice estrinseca alla telecamera:

// create a scaling matrix (THE CLASS TRANSFORM IS A WRAPPER FOR A 4x4 Matrix, methods should be self-documenting) 
d->m_ScaledTransform = Transform::New(); 
d->m_ScaleMat.set_identity(); 
d->m_ScaleMat(1,1) = -d->m_ScaleMat(1,1); 
d->m_ScaleMat(2,2) = -d->m_ScaleMat(2,2); 

// scale the matrix appropriately (m_VnlMat is a VNL 4x4 Matrix) 
d->m_VnlMat = d->m_CameraExtrinsicMatrix->GetMatrix(); 
d->m_VnlMat = d->m_ScaleMat * d->m_VnlMat; 
d->m_ScaledTransform->SetMatrix(d->m_VnlMat); 

d->m_VnlRotation = d->m_ScaledTransform->GetVnlRotationMatrix(); 
d->m_VnlRotation.normalize_rows(); 
d->m_VnlInverseRotation = vnl_matrix_inverse<mitk::ScalarType>(d->m_VnlRotation); 

// rotate translation vector by inverse rotation P = P' 
d->m_VnlTranslation = d->m_ScaledTransform->GetVnlTranslation(); 
d->m_VnlTranslation = d->m_VnlInverseRotation * d->m_VnlTranslation; 
d->m_VnlTranslation *= -1; // save -P' 

// from here proceed as normal 
// focalPoint = P-viewPlaneNormal, viewPlaneNormal is rotation[2] 
d->m_ViewPlaneNormal[0] = d->m_VnlRotation(2,0); 
d->m_ViewPlaneNormal[1] = d->m_VnlRotation(2,1); 
d->m_ViewPlaneNormal[2] = d->m_VnlRotation(2,2); 

d->m_vtkCamera->SetPosition(d->m_VnlTranslation[0], d->m_VnlTranslation[1], d->m_VnlTranslation[2]); 

d->m_vtkCamera->SetFocalPoint(d->m_VnlTranslation[0] - d->m_ViewPlaneNormal[0], 
           d->m_VnlTranslation[1] - d->m_ViewPlaneNormal[1], 
           d->m_VnlTranslation[2] - d->m_ViewPlaneNormal[2]); 
d->m_vtkCamera->SetViewUp(d->m_VnlRotation(1,0), d->m_VnlRotation(1,1), d->m_VnlRotation(1,2)); 

E finalmente fare un ritaglio di gamma resettare:

d->m_Renderer->ResetCameraClippingRange(); 

Speranza questo aiuta. Non ho il tempo di spiegare maggiori dettagli. Soprattutto l'ultimo codice (che applica gli estrinseci alla telecamera) ha alcune implicazioni che sono collegate all'orientamento del sistema di coordinate. Ma quello ha funzionato per me.

Best Michael

+3

Voglio solo dire che questo è davvero perfetto. La documentazione VTK è un incubo quindi è bello incappare in qualcosa di simile dopo aver sbattuto la testa contro un muro negli ultimi 4 mesi. Non ho mai considerato la funzione SetWindowCenter! Stiamo ancora ottenendo un offset che è chiaramente dovuto alla dimensione della finestra (modifiche all'errore con il ridimensionamento), ma tutto il resto funziona alla grande ora che abbiamo adattato alcune delle tue tecniche. Mi sento nella documentazione VTK. È davvero un incubo capire cosa sta succedendo. Qualcuno deve correggerlo. – SwarthyMantooth

+0

Non ho capito l'applicazione della parte estrinseca. Qual è il ruolo della matrice di scala con due segni negativi? Se R e t sono i valori della matrice estrinseca OpenCV, la posizione della telecamera dovrebbe essere '-Rinv * t', e la vista dovrebbe essere' Rinv [:, 1] 'e il punto focale dovrebbe essere' camera_pos - Rinv [:, 2 ] '. Quando lo applico, non ottengo una corretta sovrapposizione del modello. Anche quando applico la tua trasformazione assumendo che m_CameraExtrinsicMatrix abbia il formato di matrice OpenCV, non ottengo il rendering corretto. – krips89

+0

Una versione più pulita nello stesso approccio ha sottolineato nell'eccellente risposta di @ Michael per la parte "estrinseca" di seguito (in python): (1) camera.SetPosition (* eye) (2) camera.SetFocalPoint (* center) (3) camera.SetViewUp (* su), dove '' 'eye = (-R'T), center = eye + R '(0,0,1)', up = -R [0] [1]' ' ' – squid