OpenCV旋转(Rodrigues)和转换向量,用于在Unity3D中定位3D对象

techguy18985

我正在使用“ OpenCV for Unity3d”资产(与Java的OpenCV包相同,但已转换为Unity3d的C#),以便为我的MSc论文(计算机科学)创建增强现实应用程序。

到目前为止,我已经能够使用ORB特征检测器从视频帧中检测到物体,并且还可以使用OpenCV的SolvePnP方法找到3D到2D关系(我也进行了摄像机校准)。通过这种方法,我得到了平移和旋转向量。问题发生在增强阶段,在此阶段,我必须将3d对象显示为虚拟对象,并在每个帧上更新其位置和旋转。OpenCV返回Rodrigues旋转矩阵,但是Unity3d使用四元数旋转,因此我错误地更新了对象的位置和旋转,因此我不知道如何实现转换论坛(从Rodrigues到四元数)。

获取rvec和tvec:

    Mat rvec = new Mat();
    Mat tvec = new Mat();
    Mat rotationMatrix = new Mat ();

    Calib3d.solvePnP (object_world_corners, scene_flat_corners, CalibrationMatrix, DistortionCoefficientsMatrix, rvec, tvec);
    Calib3d.Rodrigues (rvec, rotationMatrix);

更新虚拟对象的位置:

    Vector3 objPosition = new Vector3 (); 
    objPosition.x = (model.transform.position.x + (float)tvec.get (0, 0)[0]);
    objPosition.y = (model.transform.position.y + (float)tvec.get (1, 0)[0]);
    objPosition.z = (model.transform.position.z - (float)tvec.get (2, 0)[0]);
    model.transform.position = objPosition;

我的Z轴有一个负号,因为当您将OpenCV转换为Unty3d的系统坐标时,必须反转Z轴(我自己检查了系统坐标)。

Unity3d的坐标系(绿色是Y,红色是X,蓝色是Z):

在此处输入图片说明

OpenCV的坐标系:

在此处输入图片说明

另外,我对旋转矩阵做了同样的事情,并且更新了虚拟对象的旋转。

ps:我发现了一个类似的问题,但提出这个问题的那个人没有清楚地发布解决方案。

谢谢!

RCYR

在cv :: solvePnP之后,您有了3x3旋转矩阵。该矩阵由于是旋转,因此既正交归一化因此,该矩阵的列按从左到右的顺序排列:

  1. 右向量(在X轴上);
  2. 向上矢量(在Y轴上);
  3. 正向向量(在Z轴上)。

OpenCV使用右手坐标系。坐在相机上沿光轴方向看,X轴向右,Y轴向下,Z轴向前。

您将前向矢量F =(fx,fy,fz)和向上矢量U =(ux,uy,uz)传递给Unity。这些分别是第三和第二列。无需标准化;他们已经标准化。

在Unity中,您可以这样构建四元数:

Vector3 f; // from OpenCV
Vector3 u; // from OpenCV

// notice that Y coordinates here are inverted to pass from OpenCV right-handed coordinates system to Unity left-handed one
Quaternion rot = Quaternion.LookRotation(new Vector3(f.x, -f.y, f.z), new Vector3(u.x, -u.y, u.z));

就是这样。希望这可以帮助!

编辑的位置相关评论

注意:OpenCV中的Z轴位于相机的光轴上,该光轴穿过图像的中心附近,但通常不完全位于中心。在您的校准参数中,有Cx和Cy参数。这些组合是从中心到Z轴穿过图像的位置在图像空间中的2D偏移。必须考虑到这种偏移,才能在2D背景上精确地映射3D内容。

要在Unity中正确定位:

// STEP 1 : fetch position from OpenCV + basic transformation
Vector3 pos; // from OpenCV
pos = new Vector3(pos.x, -pos.y, pos.z); // right-handed coordinates system (OpenCV) to left-handed one (Unity)

// STEP 2 : set virtual camera's frustrum (Unity) to match physical camera's parameters
Vector2 fparams; // from OpenCV (calibration parameters Fx and Fy = focal lengths in pixels)
Vector2 resolution; // image resolution from OpenCV
float vfov =  2.0f * Mathf.Atan(0.5f * resolution.y / fparams.y) * Mathf.Rad2Deg; // virtual camera (pinhole type) vertical field of view
Camera cam; // TODO get reference one way or another
cam.fieldOfView = vfov;
cam.aspect = resolution.x / resolution.y; // you could set a viewport rect with proper aspect as well... I would prefer the viewport approach

// STEP 3 : shift position to compensate for physical camera's optical axis not going exactly through image center
Vector2 cparams; // from OpenCV (calibration parameters Cx and Cy = optical center shifts from image center in pixels)
Vector3 imageCenter = new Vector3(0.5f, 0.5f, pos.z); // in viewport coordinates
Vector3 opticalCenter = new Vector3(0.5f + cparams.x / resolution.x, 0.5f + cparams.y / resolution.y, pos.z); // in viewport coordinates
pos += cam.ViewportToWorldPoint(imageCenter) - cam.ViewportToWorldPoint(opticalCenter); // position is set as if physical camera's optical axis went exactly through image center

您将从物理摄像机检索到的图像放在虚拟摄像机的正前方,并以其前轴为中心(缩放以适合视锥),然后在2D背景上映射了正确的3D位置!

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章