2014-08-28 7 views
7

Für mein Augmented-Reality-Projekt habe ich ein 3D-Modell mit der VTK-Kamera und ein reales Objekt des Modells mit einer echten Kamera betrachtet.Wie wird die mit EPnP berechnete Transformation der Kamera-Pose auf die VTK-Kamera angewendet?

Ich habe EPnP verwendet, um die extrinsische Matrix der realen Kamera zu schätzen (diese Kamera wurde bereits vorher kalibriert, also kenne ich die internen Parameter), indem ich 3D - Punkte von VTK und die entsprechenden 2D - Punkte von echten Kamerabildern und interne Parameter der realen Kamera, damit der EPnP-Algorithmus funktioniert.

Danach habe ich eine Rotations-und Translationsmatrix mit den Elementen -> R1, R2, R3, ....., R9 und t1, t2 und t3 erhalten.

meine extrinsische Matrix der realen Kamera So sieht wie folgt aus (lassen Sie sich dieses extrinsicReal nennen)

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

Danach schätze ich die extrinsische Matrix meiner VTK Kamera mit dem folgenden Code:

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

Um das VTK-Kamera-3D-Modell mit der realen Kamera zu verschmelzen, sollte die VTK-Kamera auf eine Position eingestellt werden, die der tatsächlichen Kameraposition entspricht, und die Brennweite der VTK-Kamera sollte der der realen Kamera entsprechen . Ein weiterer wichtiger Schritt ist, die gleiche extrinsische Matrix der realen Kamera auf die VTK-Kamera anzuwenden. Wie mache ich es?

Was ich getan habe war, dass ich das Inverse der extrinsicReal genommen und multipliziert dies mit der extrinsicVTK, um eine neue 4 * 4-Matrix zu erhalten (nennen wir es newMatrix). Ich habe diese Matrix für die Transformation der VTK-Kamera angewendet.

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); 

Ich bin nicht wirklich sicher, ob dies die richtige Methode ist. Aber ich habe die reale Kameraposition (die ich nach EPnP bekommen habe) und die VTK-Kameraposition (nach Anwendung der Transformation oben) überprüft und sie sind beide genau gleich. Auch die Ausrichtung der realen Kamera und die Projektionsrichtung der VTK-Kamera sind identisch.

Das Problem ist, dass das 3D-VTK-Modell, auch nachdem die oben genannten Parameter für die VTK und die echte Kamera übereinstimmen, nicht perfekt auf das echte Kamera-Video ausgerichtet ist. Kann mir jemand Schritt für Schritt helfen, das Problem zu beheben?

Antwort

10

Ja Dinge werden kompliziert, wenn diese Parameter auf die VTK-Kamera angewendet werden. Hier ist, wie ich es gemacht habe (nur Auszüge der wichtigen Code-Passagen, der ganze Code wäre viel zu viel zum Einfügen hier und wäre sowieso nutzlos für Sie). Weitere zu berücksichtigende Punkte:

  1. Ich rende das Endoskopbild als Hintergrundtextur in meinem vtkRenderWindow.
  2. Ich bin eine Mischung aus VTK, ITK (VNL), OpenCV Funktionen, aber sie sollten austauschbar sein

Zu allererst (zB cvRound auch durch vtkMath :: Round() usw. ersetzt werden könnte), ich benutze die aktive Kamera aus meinem vtkRenderer:

d->m_Renderer->GetActiveCamera() 

Der nächste Schritt die aktive Kamera, indem Sie Ihre Transformation kontinuierlich zu aktualisieren ist. Je nachdem, ob Ihr Renderfenster skalierbar ist oder nicht, müssen Sie zwei weitere Parameter initialisieren oder auch kontinuierlich aktualisieren: 1. ViewAngle, 2.WindowCenter (EXTREM wichtig, von vtk überhaupt nicht dokumentiert. Aber am Ende muss man hier seinen Hauptpunkt anwenden, den man durch Kalibrierung gefunden hat oder man hat seine Flächen mit einem Offset gerendert. Hat mich drei Monate gebraucht, um diese zwei Linien zu finden Lösung).

Berechnung des Blickwinkels:

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; 

Wenden Sie den Blickwinkel:

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

Berechnung des 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; 

Einstellen der Fenstermitte:

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

die extrinsische Matrix auf die Kamera anwenden:

// 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)); 

Und schließlich ein Ausschnitt tun zurückgesetzt Bereich:

d->m_Renderer->ResetCameraClippingRange(); 

Hoffnung das hilft. Ich habe keine Zeit mehr Details zu erklären. Insbesondere der letzte Code (der die Extrinsik auf die Kamera anwendet) hat einige Auswirkungen, die mit der Ausrichtung des Koordinatensystems verbunden sind. Aber das hat für mich funktioniert.

Beste Michael

+3

Ich möchte nur sagen, dass dies wirklich genau richtig ist. Die VTK-Dokumentation ist ein Alptraum, also ist es toll, so etwas zu finden, nachdem wir in den letzten 4 Monaten unsere Köpfe gegen eine Wand gestoßen haben. Ich habe nie die SetWindowCenter-Funktion in Betracht gezogen! Wir bekommen immer noch einen Offset, der ziemlich eindeutig auf die Fenstergröße zurückzuführen ist (Fehler ändert sich mit der Größenänderung), aber alles andere funktioniert jetzt gut, nachdem wir einige Ihrer Techniken angepasst haben. Ich fühle dich auf der VTK-Dokumentation. Es ist wirklich ein Albtraum, herauszufinden, was vor sich geht. Jemand muss das korrigieren. – SwarthyMantooth

+0

Ich habe den applizierenden extrinsischen Teil nicht verstanden. Welche Rolle spielt die Skalenmatrix mit zwei negativen Vorzeichen? Wenn R und t die Werte aus der extrinsischen OpenCV-Matrix sind, sollte die Kameraposition "-Rinv * t" sein, und die Ansicht sollte "Rinv [:, 1]" sein und der Brennpunkt sollte "camera_pos - Rinv [:, 2 ] '. Wenn ich dies anwende, bekomme ich keine korrekte Überlagerung des Modells. Selbst wenn ich Ihre Transformation anwende, vorausgesetzt, dass m_CameraExtrinsicMatrix das Format der OpenCV-Matrix hat, bekomme ich kein korrektes Rendering. – krips89

+0

Eine sauberere Version in der gleichen Ansatz in @ Michael's ausgezeichnete Antwort für den 'extrinsischen' Teil unten (in Python) gezeigt: (1) camera.SetPosition (* Auge) (2) camera.SetFocalPoint (* center) (3) camera.SetViewUp (* up), wobei '' 'eye = (-R'T), center = Auge + R '(0,0,1)', up = -R [0] [1]' ' ' – squid

Verwandte Themen