Opencv 如何将使用EPnP计算的摄影机姿势变换应用于VTK摄影机?

Opencv 如何将使用EPnP计算的摄影机姿势变换应用于VTK摄影机?,opencv,computer-vision,augmented-reality,vtk,Opencv,Computer Vision,Augmented Reality,Vtk,对于我的增强现实项目,我有一个使用VTK摄像机查看的3D模型和一个使用真实摄像机查看的模型的真实对象 我使用EPnP来估计真实相机的外部矩阵(该相机之前已经校准过,因此我知道内部参数),方法是从VTK中给出3D点,从真实相机图像中给出相应的2D点,以及EPnP算法工作的真实相机的内部参数 之后,我得到了一个旋转和平移矩阵,其中包含元素->R1,R2,R3,…,R9和t1,t2和t3 所以我的真实相机的外在矩阵是这样的(我们称之为外在矩阵) 在此之后,我使用以下代码估计VTK相机的外部矩阵: vt

对于我的增强现实项目,我有一个使用VTK摄像机查看的3D模型和一个使用真实摄像机查看的模型的真实对象

我使用EPnP来估计真实相机的外部矩阵(该相机之前已经校准过,因此我知道内部参数),方法是从VTK中给出3D点,从真实相机图像中给出相应的2D点,以及EPnP算法工作的真实相机的内部参数

之后,我得到了一个旋转和平移矩阵,其中包含元素->R1,R2,R3,…,R9和t1,t2和t3

所以我的真实相机的外在矩阵是这样的(我们称之为外在矩阵)

在此之后,我使用以下代码估计VTK相机的外部矩阵:

vtkSmartPointer<vtkMatrix4x4> extrinsicVTK = vtkSmartPointer<vtkMatrix4x4>::New();
extrinsicVTK->DeepCopy(renderer->GetActiveCamera()->GetViewTransformMatrix());
vtkSmartPointer ExterinsicVTK=vtkSmartPointer::New();
ExterinsicVTK->DeepCopy(渲染器->GetActiveCamera()->GetViewTransformMatrix());
要将VTK摄像机3D模型与真实摄像机进行融合,应将VTK摄像机设置为与真实摄像机位置相同的位置,并且VTK摄像机的焦距应与真实摄像机的焦距相同。另一个重要步骤是将真实摄影机的相同外部矩阵应用于VTK摄影机。我该怎么做

我所做的是取外显式的倒数,并将其与外显式的CvTk相乘,得到一个新的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);
vtkSmartPointer newMatrix=vtkSmartPointer::New();
vtkMatrix4x4::Multiply4x4(ExterinsicRelinVert、ExterinsicVtk、newMatrix);
vtkSmartPointer transform=vtkSmartPointer::New();
变换->集合矩阵(NewM);
转换->更新();
渲染器->GetActiveCamera()->ApplyTransform(变换);
我不确定这是不是正确的方法。但是我检查了真实的相机位置(我在EPnP之后得到的)和VTK相机位置(在应用上面的变换之后),它们都是完全相同的。此外,真实相机的方向和VTK相机的投影方向也相同


问题是,即使在VTK和真实摄像头的上述参数都匹配之后,3D VTK模型似乎也无法与真实摄像头视频完美对齐。有人能指导我一步一步地调试这个问题吗

是的,将这些参数应用到vtk摄像机时,事情会变得复杂。 下面是我是如何做到的(只是重要代码片段的摘录,整个代码将太多,无法粘贴在这里,并且对您毫无用处)。其他需要考虑的问题:

  • 我正在vtkRenderWindow中将内窥镜图像渲染为背景纹理
  • 我混合使用了VTK、ITK(vnl)、OpenCV函数,但它们应该是可互换的(例如,cvRound也可以被vtkMath::Round()等替代)
  • 首先,我使用vtkrender中的活动摄像头:

    d->m_Renderer->GetActiveCamera()
    
    下一步是通过应用变换不断更新活动摄影机根据渲染窗口是否可调整大小,必须初始化或持续更新另外两个参数:1。视角,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);
    
      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);
    
    计算WindowCenter:

      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);
    
    将外部矩阵应用于相机

    // 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();
    
    希望这有帮助。我没有时间解释更多的细节。特别是最后一个代码(将外部对象应用于摄影机)具有一些与坐标系方向相关的含义。但这对我很有效


    贝斯特·迈克尔

    我只想说,这真是恰到好处。VTK文档是一场噩梦,所以在过去4个月我们的头撞在墙上之后,偶然发现这样的东西真是太棒了。我甚至从未考虑过SetWindowCenter函数!我们仍然会得到一个偏移量,这很明显是由于窗口大小(错误会随着大小的改变而改变),但其他一切都很好,因为我们采用了您的一些技术。我觉得你对VTK文档很满意。弄清楚到底发生了什么真是一场噩梦。需要有人来纠正这一点。我不理解应用外在因素的部分。带有两个负号的比例矩阵的作用是什么?如果R和t是OpenCV外部矩阵中的值,则相机位置应为
    -Rinv*t
    ,向上视图应为
    Rinv[:,1]
    ,焦点应为
    相机位置-Rinv[:,2]
    。当我应用这个时,我没有得到模型的正确叠加。即使假设m_CameraExterinsicMatrix具有OpenCV矩阵的格式,我也无法应用您的转换。在@Michael对下面“Exterinsic”部分(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]
    感谢您提供了这个很好的示例,有人能解释变量“d”是什么吗?您的问题对我帮助很大,非常感谢您在问题中分享您的代码。