OpenCV中用于3D旋转的参考向量是什么?

ttp71kqs  于 2023-10-24  发布在  其他
关注(0)|答案(1)|浏览(120)

我正在使用OpenCV的Calib3D模块进行场景的3D重建,我试图了解OpenCV如何处理相机的外部校准,特别是旋转。我知道OpenCV使用Rodrigues矢量,即,通过其所指向的方向定义旋转轴的矢量以及矢量将围绕其旋转多少的旋转Angular 通过罗德里格斯矢量长度的轴(参见Wikipedia)。
为了说明这一点,以下是我对这种情况的设想:

换句话说:有一些参考向量v_ref在这个例子中,两者之间有一个75.96°的Angular 。因此,罗德里格斯矢量v_Rodigues的计算方法是首先计算v_Ref和v_cam之间的归一化叉积,然后将其乘以Angular 以获得适当的长度。
由此,我有两个问题:
1.我的理解正确吗?

  1. OpenCV使用什么作为v_Ref?

**编辑:**由于问题似乎引起了一些误解:我有效地有以下情况:我使用全站仪测量了相机的位置。这意味着我对相机的位置和相机的前向矢量v_Cam有了很好的估计。然而,OpenCV需要Rodrigues矢量(图中的v_Rodrigues),而不是相机的前向矢量v_Cam。因此,我试图从我的v_Cam计算v_Rodrigues,正如评论中指出的那样,只有当我知道v_Ref时才有效。这就是为什么我试图找出OpenCV使用什么作为v_Ref的定义。

我想我已经很好地理解了OpenCV的相机和世界坐标系是如何定义的,我只是缺少了v_Ref的定义。

ddhy6vgd

ddhy6vgd1#

好了,我想我终于找到了一些似乎是正确的东西。我将尝试在这里总结我的发现作为答案,但我仍然在整理我的想法,因为我写这篇文章,所以请原谅我,如果这个答案仍然有点不清楚。
正如@fana和@ChristophRackwitz所指出的,有一个物体坐标系,也有一个相机坐标系。物体坐标系是一个规则的右手坐标系,因此X和Y定义了水平面,Z指向上方。正如@ChristophRackwitz所描述的,相机坐标系与相机的投影平面相连。当通过相机的透镜观看时,X指向右,Y指向下,Z指向外。所以,用我自己的话来说,v_Cam等于相机坐标系中的Z轴。
This website很好地说明了坐标系。
此外,如@fana所述,如果不应用平移和旋转(即,旋转矩阵是单位矩阵或Rodrigues向量全为零),则对象坐标系和相机坐标系的轴是相同的。
因此,如果我的理解是正确的,以下情况适用:当没有旋转时,坐标轴是相同的。因此,v_cam等于对象坐标系的Z轴,这也意味着v_Ref = (0, 0, 1)(与对象坐标系的Z轴对齐)。
考虑到这一点,我开始做实验:我自己生成了一个11 × 11的平面对象点网格:

val objectPoints = List(11) { rowIndex ->
    List(11) { columnIndex ->
        Point3(columnIndex.toDouble() - 5.0, rowIndex.toDouble() - 5.0, 0.0)
    }
}.flatten()

(This代码生成从(-5.0, -5.0, 0.0)(5.0, 5.0, 0.0)的点的规则网格

然后,我希望我的相机位于这些平面物点上方,指向下方(参见上图中的v_cam),并相应地生成投影图像点:

val imagePoints = List(11) { rowIndex ->
    List(11) { columnIndex ->
        Point((columnIndex.toDouble() - 5.0) * imageScaleFactor, (rowIndex.toDouble() - 5.0) * imageScaleFactor)
    }
}.flatten()

然后我继续调用Calib3d.calibrateCamera(),并将这些点作为输入:

val cameraMatrix = Mat()
val distortionCoefficients = Mat()
val rotationVectors = mutableListOf<Mat>()
val translationVectors = mutableListOf<Mat>()

val overallRms = Calib3d.calibrateCamera(
    // The map and toMat-functions just convert the data into a form that OpenCV accepts
    listOf(objectPoints.map { it.toOpenCVPoint() }.toMat3f()),
    listOf(imagePoints.toMat2f()),
    imageSize,
    cameraMatrix,
    distortionCoefficients,
    rotationVectors,
    translationVectors
)

这给了我以下的intrinsic:

CameraIntrinsics(
    cameraMatrix = CameraMatrix(
        focalLength = Point(x = 1.350310986449413E17, y = 1.3503109864494126E17),
        opticalCenter = Point(x = 9.500000000000115, y = 9.500000000000115)
    ),
    distortionCoefficients = DistortionCoefficients(
        k1 = -1.2551867374857695E-26,
        k2 = -3.1640690574535313E-41,
        p1 = 1.4867968141433821E-6,
        p2 = 1.4867967882740299E-6,
        k3 = -9.212409017493725E-56,
    ),
    overallRms = 3.7999673703527265E9
)

和extrinsics:

CameraExtrinsics(
    worldLocation = Point3(x = -7.905761924365692E-9, y = -7.905762033403558E-9, z = 1.123709191949999E8),
    rotation = RodriguesVector(-1.110720731360427, 1.1107207365421794, -3.6640522551246774E-9)
)

起初,罗德里格斯向量看起来像我所期望的:摄像机是垂直向下的,所以摄像机的z轴(或v_cam)与对象坐标系的z轴方向完全相反(或者我认为是v_Ref)。因此,我希望v_Rodrigues在X-Y平面上,因为它必须垂直于两者,v_Camv_Ref。然而,我期望罗德里格斯向量的长度是3.1415 [rad] = 180°,因为v_cam指向下,而我假设v_Ref等于z轴,因此指向上,导致两者之间的Angular 为180°。
然而,事实并非如此:如果你计算上述罗德里格斯向量的长度,结果是pi/2 [rad] = 90°,而不是180°
由于OpenCV计算了一个合理的平移向量,我假设罗德里格斯向量也是正确的,只是不是我所期望的。然而,考虑到我知道相机是向下的,我可以反向应用罗德里格斯公式(即将罗德里格斯向量乘以-1,然后应用维基百科上提到的公式),得到真正的v_Ref
结果是,v_Ref似乎是(1.0, 1.0, 0.0)
我还没有一个合乎逻辑的解释,为什么会这样,但测试一些其他的相机位置似乎也证实了这一点。我现在将继续我的项目的假设下,v_Ref = (1.0, 1.0, 0.0)和更新这个答案,因为我发现更多。
感谢@fana和@ChristophRackwitz对我的宽容:)

相关问题