对于我的增强现实项目,我使用VTK相机观看3D模型,并使用真实相机观看模型的真实对象。
我使用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模型似乎也不能与真实的相机视频完美对齐。有人可以指导我一步一步调试问题吗?
答案 0 :(得分:12)
将这些参数应用到vtk相机时,事情变得复杂了。 这就是我做的方式(只是重要的代码段落的摘录,整个代码在这里粘贴太多了,无论如何都对你没用)。其他要考虑的要点:
首先,我使用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应用于相机)具有一些与坐标系方向相关的含义。但这对我有用。
最佳迈克尔