2014-08-28 76 views
7

對於我的增強現實項目,我使用VTK相機查看3D模型,並使用真實相機查看模型的實際對象。如何將使用EPnP計算出的相機姿態變換應用於VTK相機?

我用EPNP通過從實際照相機圖像和從VTK給3D點和它的對應的2D點來估計真實攝像機的非本徵矩陣(在此照相機已經被前手校準,所以我知道內部參數)真實攝像頭的內部參數爲EPnP算法工作。然後,我得到了元素 - > R1,R2,R3,......,R9和t1,t2和t3的旋轉和平移矩陣。

所以我的真實攝像機的外在矩陣如下所示(我們稱之爲extrinsicReal)

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

在此之後,我用下面的代碼估計我VTK相機的外在矩陣:

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

要將VTK相機3D模型與真實相機融合,應將VTK相機設置爲與真實相機位置相同的位置,並且VTK相機的焦距應與真實相機的焦距相同。另一個重要步驟是將真實相機的相同外部矩陣應用於VTK相機。我該怎麼做?

我所做的是我把extrinsicReal的反函數乘以extrinsicVTK得到一個新的4 * 4矩陣(我們稱它爲newMatrix)。我將這個矩陣用於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); 

我不確定這是否是正確的方法。但是我檢查了真實的攝像頭位置(我在EPnP之後獲得的)和VTK攝像頭的位置(在應用上面的轉換之後),它們都完全相同。此外,真實相機的方向和VTK相機的投影方向也是相同的。

問題是,即使上述參數與VTK和真實相機匹配後,3D VTK模型似乎也沒有完全與真實相機視頻對齊。有人可以指導我一步一步來調試問題嗎?

回答

10

是的,當將這些參數應用到vtk相機時,情況變得複雜。 下面是我如何做到的(只是重要的代碼片段的摘錄,整個代碼將太多粘貼在這裏,無論如何將無用)。其他要考慮的問題:

  1. 我在我的vtkRenderWindow中將內窺鏡圖像渲染爲背景紋理。
  2. 我使用VTK,ITK(VNL),OpenCV函數的組合,但他們應該是可互換的

首先(如cvRound也可以通過vtkMath :: ROUND()等取代),我用的是有效的照相機從我vtkRenderer:

d->m_Renderer->GetActiveCamera() 

下一步是通過應用的變換汽車無更新活動的攝像頭。 根據渲染窗口是否可調整大小,您必須初始化或不斷更新兩個其他參數:1. ViewAngle,2。WindowCenter(非常重要,完全沒有vtk記錄,但最後你必須在校準中找到你的校長點,或者你的曲面會用一個偏移量來渲染,花了3個月找到這兩條線解)。視角的

計算:

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; 

應用視角:

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

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

設置窗口中心:

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

外在矩陣應用於相機

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

最後搞得剪輯範圍重置

d->m_Renderer->ResetCameraClippingRange(); 

希望這有助於。我沒有時間來解釋更多細節。特別是最後一個代碼(將extrinsics應用於相機)具有一些與座標系方向連接的含義。但這對我有效。

最好的邁克爾

+3

只是想說這是真正的現貨。 VTK的文檔是一場噩夢,所以在過去4個月裏,我們頭撞牆壁之後偶然發現這樣的事情真是太棒了。我從來沒有考慮過SetWindowCenter函數!由於窗口大小(錯誤隨着調整大小而改變),我們仍然得到了一個很清晰的偏移量,但是現在我們改進了一些技巧後,其他所有東西都很好用。 我感覺你在VTK文檔。弄清楚發生了什麼實在是一場噩夢。有人需要糾正這一點。 – SwarthyMantooth 2015-01-20 22:52:41

+0

我不明白應用外在部分。具有兩個負號的尺度矩陣的作用是什麼?如果R和t是來自OpenCV外部矩陣的值,則相機位置應該是'-Rinv * t',並且查看應該是'Rinv [:,1]',焦點應該是'camera_pos - Rinv [:,2 ]'。當我應用這個時,我沒有得到正確的模型疊加。即使我應用了你的變換,假設m_CameraExtrinsicMatrix具有OpenCV矩陣的格式,我也沒有得到正確的渲染。 – krips89 2017-01-23 17:48:50

+0

在@邁克爾的'外在'部分下面(在Python中)的優秀答案指出在同一方法的清潔版本: (1)camera.SetPosition(* eye)(2)camera.SetFocalPoint(* center)( 3)camera.SetViewUp(* up),其中''eye =(-R'T),center = eye + R'(0,0,1)',up = -R [0] [1] ' – squid 2017-02-17 08:12:32