当应用这些参数到vtk相机时,情况变得复杂起来。以下是我如何做的(只列出了重要的代码段,完整的代码会太长且对你也没有用处)。其他需要考虑的点:
- 我将内窥镜图像作为背景纹理呈现在我的vtkRenderWindow中。
- 我使用了VTK、ITK(vnl)、OpenCV函数的混合,但它们应该是可互换的(例如cvRound也可以被vtkMath::Round()等替换)。
首先,我使用来自我的vtkRenderer的活动相机:
d->m_Renderer->GetActiveCamera()
下一步是通过应用您的变换来持续更新活动相机。根据您的渲染窗口是否可调整大小,您必须初始化或持续更新两个进一步的参数:1.视角,2.窗口中心(非常重要,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)
窗口中心的计算:
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);
将外部矩阵应用于相机:
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);
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 );
d->m_VnlTranslation = d->m_ScaledTransform->GetVnlTranslation();
d->m_VnlTranslation = d->m_VnlInverseRotation * d->m_VnlTranslation;
d->m_VnlTranslation *= -1;
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();
希望这可以帮到你。我没有时间解释更多细节,特别是最后的代码(将外参应用于相机)有一些涉及坐标系方向的影响。但对我来说,那个方法可行。
祝好,
Michael