如何使用OpenCV通过3D到2D点对应来估计相机姿态

8

你好,我的目标是开发头部追踪功能,应用于飞机(模拟器)驾驶舱,以便在恶劣的视觉环境下为民用飞行员提供增强现实支持。

我的方法是检测特征点(在黑暗的模拟器LED中),我知道这些点的3D坐标,然后计算估计的(佩戴式摄像头的)姿态[R | t](旋转拼接平移)。

我遇到的问题是估计的姿态似乎总是错误的,而我的3D点的投影(我也用来估计姿态)与2D图像点不重叠(或不可见)。

LED detection works but pose estimation and 3D projection not

我的问题是:
如何估计在给定的二维和三维点对应的情况下相机的姿态。
我尝试时为什么不起作用,可能会有哪些错误来源?
为了在实际环境中使理论解决方案起作用,测量(3D 和 2D 点以及相机矩阵)必须有多精确?
在理论上,这种方法是否适用于共面点(x,y 轴改变)?
我使用的硬件是爱普生BT-200。
在飞机上,我定义了一个固定的坐标系,期望程序的相对平移和旋转结果。该程序检测(唯一)LED 的图像坐标,并将其与其相应的 3D 坐标匹配。使用通过 open-cv 示例 android 代码(https://github.com/Itseez/opencv/tree/master/samples/android/camera-calibration)获得的相机矩阵,我尝试使用 solvePnP 估计姿态。
我的相机矩阵和畸变系数略有不同。以下是我从程序中获得的一些值。我确保我的印刷圆形图案的圆距与源代码中写下的相同(以米为单位测量)。
以下是一些示例以及我如何创建其对应的OpenCV Mat。
//  protected final double[] DISTORTION_MATRIX_VALUES = new double[]{
//          /*This matrix should have 5 values*/
//          0.04569467373955304,
//          0.1402980385369059,
//          0,
//          0,
//          -0.2982135315849994
//  };

//  protected final double[] DISTORTION_MATRIX_VALUES = new double[]{
//          /*This matrix should have 5 values*/
//          0.08245931646421553,
//          -0.9893762277047577,
//          0,
//          0,
//          3.23553287438898
//  };

//  protected final double[] DISTORTION_MATRIX_VALUES = new double[]{
//          /*This matrix should have 5 values*/
//          0.07444480392067945,
//          -0.7817175834131075,
//          0,
//          0,
//          2.65433773093283
//  };
    protected final double[] DISTORTION_MATRIX_VALUES = new double[]{
            /*This matrix should have 5 values*/
            0.08909941096327206,
            -0.9537960457721699,
            0,
            0,
            3.449728790843752
    };

    protected final double[][] CAMERA_MATRIX_VALUES = new double[][]{
            /*This matrix should have 3x3 values*/
//          {748.6595405553738, 0, 319.5},
//          {0, 748.6595405553738, 239.5},
//          {0, 0, 1}
//          {698.1744297982436, 0, 320},
//          {0, 698.1744297982436, 240},
//          {0, 0, 1}
//          {707.1226937511951, 0, 319.5},
//          {0, 707.1226937511951, 239.5},
//          {0, 0, 1}
            {702.1458656346429, 0, 319.5},
            {0, 702.1458656346429, 239.5},
            {0, 0, 1}
    };

    private void initDestortionMatrix(){
        distortionMatrix = new MatOfDouble();
        distortionMatrix.fromArray(DISTORTION_MATRIX_VALUES);
    }

    private void initCameraMatrix(){
        cameraMatrix = new Mat(new Size(3,3), CvType.CV_64F);
        for(int i=0;i<CAMERA_MATRIX_VALUES.length; i++){
            cameraMatrix.put(i, 0, CAMERA_MATRIX_VALUES[i]);
        }
    }

为了估计相机的姿态,我使用了solvePnP(以及solvePnPRansac),如几个地方所述(1,2,3,4)。我将solvePnP的结果用作投影(Calib3d.projectPoints)的输入。连接结果[R|t]的逆被我用作估计的姿态。

因为我的生产环境的结果太差了,所以我创建了一个测试环境。在这个环境中,我将相机(由于其3D形状(它是玻璃)略微向下旋转)放置在桌子边缘。我使用这个边缘作为世界坐标系的纵坐标。我搜索了open-cv坐标系可能定向的方式,并找到了不同的答案(一个在stackoverflow上,一个在关于opencv的官方youtube讲座上)。无论如何,我通过在图像上投影描述在该坐标系中的3D点并检查给定的世界形状是否保持不变来测试是否正确获取了坐标系。

所以我想出了z指向前方,y向下,x向右的方向。 图片显示3D模式被正确投影。只有姿态没有估计,所以点不重合

为了更接近我的解决方案,我在测试环境中估计了姿态。翻译向量输出和欧拉角输出是[R | t]的逆。欧拉角可能显示不正确(如果考虑顺序,则可能被交换或错误),因为我使用传统的公式(我假设参考飞机坐标系),使用open-cv坐标系进行计算(计算发生在我将附上的Pose类中)。但无论如何,即使是逆的平移向量也出现错误(在我的简单测试中)。
在一次测试中,我拍摄了这张图片,我有一个30°的横滚角(可能是飞机坐标系中的俯仰角)和一个向上50厘米的平移。这似乎更合理。所以我认为因为我的点是共面的,我可能会得到模棱两可的结果。所以我意识到需要进行另一个测试,该测试中点在Z轴上发生变化。但是,这个测试甚至投影也失败了。

对于solvePnP,我尝试了所有不同的解算法标志和ransac algorithm的不同参数。

也许你可以帮我找到我的错误,或者向我展示解决我的初始问题的好方法。我将附上我的调试源代码,其中包含许多println语句和调试图像。这段代码包含我的点测量数据

提前感谢您的帮助。

Main.java Pose.java 0.png enter image description here

1.png enter image description here

编辑于22.03.2015: 最终我发现了自己的错误。

  1. 我在for循环中修改了一个Mat对象,因为OpenCV使用了大量的引用调用,而我没有足够小心。所以重投影的tvec和rvec是错误的。
  2. 我的测试环境中有一个点(在图像坐标系中),由于轴向混淆被错误标记。

所以我的方法总体来说是正确的。在我的测试数据集中,我至少收到了(通常是)有效的重投影。

很不幸,OpenCV的PnP算法:“ITERATIVE,P3P,EPNP”返回不同的结果,即使使用非常不准确但接近内部猜测,结果有时也只是正确的。P3P算法应该提供3个解决方案,但OpenCV只提供一个。EPNP应该返回良好的结果,但使用EPNP,OpenCV返回最差的结果,从我的人类观察中评估出来。
现在的问题是如何过滤不准确的值或确保OpenCV函数返回有效值。(也许我应该修改本地代码以接收PnP的3个解决方案)。
这里的压缩图像(37MB)展示了我的当前结果(使用迭代PnP-Solver),初始猜测是零旋转和向上75厘米。打印出来的图像中,x轴向前,y轴向左,z轴向下,并对应滚动、俯仰和偏航角度。

我现在发现我的图像点按y坐标排序是错误的(我的图像坐标系中有一个指向上方的y轴)。我的值变得更接近了,但仍然不够好。 - Jakob Alexander Eichler
1
我找到了这些文件,其中EPNP适用于共面点: P3P:http://www.mmrc.iss.ac.cn/~xgao/paper/ieee.pdf EPNP:http://cvlabwww.epfl.ch/~lepetit/papers/lepetit_ijcv08.pdf - Jakob Alexander Eichler
我在我的校准示例中将正方形大小的参数从米改为厘米,但是收到的矩阵几乎相等! - Jakob Alexander Eichler
问题尚未解决。我在这里创建了一个问题:http://answers.opencv.org/question/58634/what-are-the-requirements-for-solvepnp-and-how-to-improve-my-results/ - Jakob Alexander Eichler
1个回答

4

在尝试实现我的头部跟踪系统时,我学到的一件事情是,你应该从简单的问题开始,然后再逐步转向更加复杂的问题。由于你的问题比较长,我很遗憾没有时间分析它并在你的代码中寻找错误或逻辑错误,所以至少我将尝试给你一些提示和可行的示例。

这里有一个关于寻找物体平移和旋转的OpenCV教程。它是用Python编写的,如果有问题,在这里可以找到我的旧c++项目的一部分。
我的项目使用solvePnP或solvePnPRansac函数执行相同的任务(您可以更改模式)。请注意,我的代码是一些旧的“游乐场”项目的一部分,即使是在我进行了清理之后,它仍然相当凌乱。运行时,请将印刷的棋盘展示给摄像头,并按“p”键开始位置和旋转估计,“m”键更改模式(0-ransac,1-pnp,2-posit似乎不起作用...)或“d”键打开/关闭使用畸变系数。
这两个项目都依赖于找到棋盘格图案,但很容易修改它们以使用其他对象。

相机校准 - 在我开发头部跟踪系统时,我从未成功地将相机校准两次得到相同的结果...因此,我决定使用在GitHub上找到的一些校准文件,它很有效 - 这里 你可以找到更多信息和链接到此文件。

编辑:

尝试从简单的解决方案开始,以在某些(甚至是简单的)情况下产生良好结果。我的建议是从教程的印刷棋盘(this one)替换您的测试环境中的一张纸开始,并使其工作。从这里开始解决您的问题比直接开始解决问题要容易得多。尝试使用任何编程语言制作任何可行的解决方案-考虑使用Python或C++版本的OpenCV-相比Java版本,有更多的教程/示例,将您的代码的结果与工作代码的结果进行比较将使任务变得更加容易。当您拥有一些可行的解决方案时,请尝试修改它以使其适用于您的测试环境。可能会有很多原因导致它现在不能正常工作-点数不足,在您的代码中或者甚至在OpenCV Java包装器中出现了错误,对结果的错误解释等...

编辑2:

使用您代码中的点,我已经成功地获得了以下结果:

rvec = [[-158.56293283], [ 1.46777938], [ -17.32569125]]
tvec = [[ -36.23910413], [ -82.83704819], [ 266.03157578]]

很遗憾,对我来说很难判断结果是否好...唯一可能有问题的是2个角度不同于0(或180)。但是,如果您将points2d的最后一行从(355,37), (353,72), (353,101)更改为

(355,37), (355,72), (355,101)

(我猜这是您的错误,而不是正确的结果),则会得到:

rvec = [[-159.34101842], [ 1.04951033], [ -11.43731376]]
tvec = [[ -25.74308282], [ -82.58461674], [ 268.12321097]]

这可能更接近正确的结果。改变相机矩阵会显著改变结果,因此考虑测试this post中的值。

请注意,所有rvec值都乘以180.0/3.14 - 在C++和Python中,solvePnPRansac返回的rvec向量包含弧度角度。


我现在尝试使用单应性矩阵来实现,但是我仍然收到了一些没有意义的值。https://dev59.com/DWox5IYBdhLWcg3w3X9S - Jakob Alexander Eichler
非常感谢您的努力。在我的生产环境中,我现在非常接近了。我在重新投影的部分犯了一个错误。如上面的评论所提到的,在测试环境和调试代码中,2D图像点的y坐标指向了错误的方向。我发现改变2个像素会导致结果的改变。提高准确性将是下一个要解决的问题。我将尽快发布当前状态的更新以及有用的信息。 - Jakob Alexander Eichler
两个像素真的会有如此大的差别吗?测量的精度必须有多高?我现在在问题中附上了我的当前状态。圆圈是反投影。还显示了我的探测器估计LED和世界以及2D坐标和角度和平移猜测的位置。 - Jakob Alexander Eichler

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接