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

What is the reference vector in OpenCV for 3D rotation?

提问人:vatbub 提问时间:9/21/2023 最后编辑:vatbub 更新时间:10/6/2023 访问量:95

问:

我正在使用 OpenCV 的模块对场景进行 3D 重建,我试图了解 OpenCV 如何处理相机的外部校准,尤其是旋转。我知道 OpenCV 使用罗德里格斯向量,即定义旋转轴通过其指向的方向的向量和旋转角度的向量通过罗德里格斯向量的长度绕轴旋转的程度(参见维基百科)。Calib3D

为了说明这一点,以下是我对这种情况的想象:

Visualization of the geometric situation

换句话说:有一些参考向量v_ref(图像中的红色)和向量v_cam(蓝色)指向相机指向的方向。在此示例中,两者之间的角度为 75.96°。因此,罗德里格斯向量v_Rodigues的计算方法是首先计算 v_Ref 和 v_cam 之间的归一化叉积,然后将其乘以角度以使其达到适当的长度。

由此,我有两个问题:

  1. 我的理解正确吗?
  2. OpenCV 用作v_Ref吗?

编辑:由于这个问题似乎引起了一些误解: 我实际上有以下情况: 我使用全站仪测量了相机的位置。 这意味着我对相机的位置和相机的前向矢量有一个很好的估计。 但是,OpenCV 需要罗德里格斯向量(如图中),而不是相机的前向向量。 因此,我试图从我的 计算,正如评论中指出的那样,只有在我知道的情况下才有效。 这就是为什么我试图找出 OpenCV 使用什么作为 .v_Camv_Rodriguesv_Camv_Rodriguesv_Camv_Refv_Ref

我认为我对 OpenCV 的相机和世界坐标系是如何定义的有很好的理解,我只是错过了 .v_Ref

OpenCV 数学 几何 图形相机校准

评论

1赞 Christoph Rackwitz 9/22/2023
是的。旋转轴垂直于两个向量。交叉产品是合适的。角度可以用叉积或点积计算,然后用一些三角函数来计算。只要有一点代数和明智的应用或归一化(或缺乏归一化),整个表达式可能会大大简化。我不够数学,不敢写答案。
0赞 fana 10/3/2023
什么是取决于上下文,你已经知道这里代表什么。所以,想想没有轮换的情况。等于这种情况。这可能是您的问题 2 的答案。v_Refv_Rodriguesv_Refv_Cam
0赞 fana 10/3/2023
换句话说,考虑何时以另一种方式表示旋转。例如,当使用 3x3 矩阵时,旋转可以描述为 。看起来你在问“什么是?如果是这样,这个问题似乎有点荒谬,因为它应该已经在实际问题/上下文中定义。Rv_result = R * v_srcv_src
0赞 vatbub 10/4/2023
嗯,是武断的。它可以是 OR 或任何其他值,只要相应地调整,数学仍然有效。因此,我的问题是 OpenCV 中专门使用的价值是什么,因为我甚至试图深入研究 OpenCV 的源代码,但一无所获。v_Ref(0, 0, 1)(1, 0, 0)v_Rodriguesv_Ref
0赞 vatbub 10/4/2023
此外,虽然我确实或多或少地测量了(我用全站仪测量了相机),但我知道我的值是垃圾,这就是为什么我试图找到这样一种我可以从我的v_Camv_Rodriguesv_Refv_Rodriguesv_Cam

答:

0赞 vatbub 10/6/2023 #1

好吧,我想我终于有了一些看起来正确的东西。 我将尝试在这里总结我的发现作为答案,但在我写这篇文章时,我仍然在整理我的想法,所以如果这个答案仍然有点不清楚,请原谅我。

正如 @fana 和 @ChristophRackwitz 所指出的,有一个对象坐标系和一个相机坐标系。 对象坐标系是一个规则的右手笛卡尔坐标系,因此 X 和 Y 定义水平面,Z 指向上方。 如@ChristophRackwitz所述,相机坐标系连接到相机的投影平面。 通过相机的镜头观察时,X 指向右侧,Y 指向下方,Z 指向外部。 所以,用我自己的话来说,等于相机坐标系中的Z轴。v_Cam

这个网站有一个很好的坐标系说明。

此外,@fana如前所述,如果不应用平移和旋转(即旋转矩阵是单位矩阵或罗德里格斯向量全为零),则对象坐标系和相机坐标系的轴是相同的。

因此,如果我的理解是正确的,那么以下情况适用: 当未应用旋转时,坐标轴相同。 因此,在这种情况下,等于对象坐标系的 Z 轴,这也意味着(与对象坐标系的 Z 轴对齐)。v_camv_Ref = (0, 0, 1)

考虑到这一点,我开始尝试: 我为自己生成了一个 11×11 的平面对象点网格:

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

(此代码生成一个规则的点网格,从 到(-5.0, -5.0, 0.0)(5.0, 5.0, 0.0)

Visualization of the situation in object space

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

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

Points projected into the image plane

然后,我继续调用这些点作为输入: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
)

这给了我以下内在因素:

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
)

和外在因素:

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

起初,罗德里格斯向量看起来像我所期望的: 相机笔直地指向下方,因此相机的 z 轴(或 )与对象坐标系的 z 轴(或我认为的方向)完全相反。 因此,我希望在 X-Y 平面中,因为它必须垂直于两者,并且 .但是,我预计罗德里格斯向量的长度是 since 指向下方,而我假设等于 z 轴,因此指向上方,导致两者之间成 180° 角。v_camv_Refv_Rodriguesv_Camv_Ref3.1415 [rad] = 180°v_camv_Ref

然而,事实并非如此: 如果计算上述罗德里格斯向量的长度,则结果是 ,而不是 。pi/2 [rad] = 90°180°

由于 OpenCV 计算了一个合理的平移向量,我假设罗德里格斯向量也是正确的,只是不是我所期望的。 但是,鉴于我知道相机朝下,我可以反向应用罗德里格斯公式(即将罗德里格斯向量乘以,然后应用维基百科上提到的公式),给我真正的 .-1v_Ref

事实证明,似乎是.v_Ref(1.0, 1.0, 0.0)

对于为什么会这样,我还没有一个合乎逻辑的解释,但测试其他一些相机位置似乎也证实了这一点。 我现在将继续我的项目,假设并在我发现更多时更新此答案。v_Ref = (1.0, 1.0, 0.0)

谢谢你@fana和@ChristophRackwitz对我的包容:)

评论

0赞 fana 10/6/2023
overallRms = 3.7999673703527265E9看起来很大。(它可能表示一些 plobles。
0赞 fana 10/6/2023
我不熟悉,但是......你看起来很奇怪。也就是说,你的腰带的中心点被投影到相机图像的左上角像素。即只有 1/4 的网格包含在相机 FOV 中。calibrateCamera()imagePoints(X,Y,Z)=(0,0,0)(x,y)=(0,0)
0赞 fana 10/6/2023
因此,对于此输入,OpenCV 应该决定图案板的一些合理移动。当我非常非常简单地思考时,它可能会变成.(你的不是这么远吗?对于旋转,我无法估计为什么会计算出如此大的结果......但如前所述,从较大的 RMS 值来看,我可以估计该过程可能找不到好的答案。t = (-6, -6, some plus value)worldLocation
0赞 fana 10/6/2023
如果按原样显示输出外部参数值为“CameraExtrinsics”,则是错误的。它们是有模式的calibrateCamera()
0赞 vatbub 10/6/2023
哦,你对.我想这就是导致高 RMS 的原因。让我来解决这个问题......imagePoints being outsiode of the image