我正在使用“ 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:我发现了一个类似的问题,但提出这个问题的那个人没有清楚地发布解决方案。
谢谢!
在cv :: solvePnP之后,您有了3x3旋转矩阵。该矩阵由于是旋转,因此既正交又归一化。因此,该矩阵的列按从左到右的顺序排列:
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] 删除。
我来说两句