Android和OpenCV:考虑相机内参和反投影的单应性矩阵到相机位姿的转换

5

库:OpenCV 目标:Android(OpenCV4Android)

我尝试计算世界平面(例如监视器屏幕)的单应性,以获取相机姿态,对其进行转换并重新投影点以进行跟踪任务。 我使用OpenCV的findHomography() / getPerspectiveTransform()获取单应性。使用perspectiveTransform()重新投影点(如此处所述:http://docs.opencv.org/doc/tutorials/features2d/feature_homography/feature_homography.html),效果非常好。 "screenPoints"是监视器边缘的世界坐标(使用纵横比和z值为0),而“imagePoints”是图像中屏幕边缘的x / y坐标。

Mat homography = org.opencv.imgproc.Imgproc.getPerspectiveTransform(screenPoints, imagePoints);

我拥有相机标定矩阵(我使用了Matlab标定工具箱),我在评论中找到了一个提示(在https://dsp.stackexchange.com/questions/2736/step-by-step-camera-pose-estimation-for-visual-tracking-and-planar-markers)。建议在单应性中考虑相机参数。
H' = K^-1 * H
(H' - 考虑相机标定的单应性矩阵,H - 单应性矩阵,K^-1 - 相机标定矩阵的逆矩阵)。
Mat intrinsicInverse = new Mat(3, 3, CvType.CV_32FC1);
Core.invert(intrinsic, intrinsicInverse);
intrinsicInverse.convertTo(intrinsicInverse, CvType.CV_32FC1);          
homography.convertTo(homography, CvType.CV_32FC1);
// compute H respect the intrinsics
Core.gemm(intrinsicInverse, homography, 1, new Mat(), 0, homography);

我的下一步是从同态矩阵中计算相机姿态,具体描述可参考使用基于4个共面点的同态矩阵计算相机姿态

由于我试图在Android上完成这项任务,因此我需要将C++代码移植到Java:

private Mat cameraPoseFromHomography(Mat h) {
    Log.d("DEBUG", "cameraPoseFromHomography: homography " + matToString(h));

    Mat pose = Mat.eye(3, 4, CvType.CV_32FC1);  // 3x4 matrix, the camera pose
    float norm1 = (float) Core.norm(h.col(0));
    float norm2 = (float) Core.norm(h.col(1));
    float tnorm = (norm1 + norm2) / 2.0f;       // Normalization value

    Mat normalizedTemp = new Mat();
    Core.normalize(h.col(0), normalizedTemp);
    normalizedTemp.convertTo(normalizedTemp, CvType.CV_32FC1);
    normalizedTemp.copyTo(pose.col(0));

    Core.normalize(h.col(1), normalizedTemp);
    normalizedTemp.convertTo(normalizedTemp, CvType.CV_32FC1);    
    normalizedTemp.copyTo(pose.col(1));

    Mat p3 = pose.col(0).cross(pose.col(1));
    p3.copyTo(pose.col(2));

    Mat temp = h.col(2);
    double[] buffer = new double[3];
    h.col(2).get(0, 0, buffer);
    pose.put(0, 3, buffer[0] / tnorm);
    pose.put(1, 3, buffer[1] / tnorm);
    pose.put(2, 3, buffer[2] / tnorm);

    return pose;
}

虽然我无法检查代码是否正确,但它正在运行。此时我假设已经考虑了相机标定,获得完整的相机姿态。

根据这里的描述,一个三维点的投影只需按以下公式计算:

p = K * CP * P

(其中 p 为二维位置,K 为标定矩阵,CP 为相机姿态,P 为三维点)

    Core.gemm(intrinsic, cameraPosition, 1, new Mat(), 0, vec4t);
    Core.gemm(vec4t, point, 1, new Mat(), 0, result);

结果与屏幕边缘的源图像位置相差甚远。但我可以通过其相对差异识别所有三个边缘 - 因此可能只是某个因素出了问题。
这是我第一次做这样的计算机视觉任务,我有来自Zisserman的“多视角几何”书籍,并阅读了所有相关部分 - 但说实话 - 我没有理解大部分内容。
更新:在我的相机矩阵中发现了一个错误 - 上面的实施方法完全正常工作!
2个回答

1
将其用其他方法运行。而不是使用findHomography()/getPerspectiveTransform()函数,我找到了另一个称为solvePnP()的函数,它基于世界和图像点以及内部相机矩阵返回相机姿态。

使用该函数与projectPoints()方法相结合,我能够将3D点重新投影到图像中。

对于屏幕边缘,在图像中放置在正确的位置。

更新:

我发现我的实现中存在错误-我的相机内部矩阵是错误的。上面的基于单应性实现的相机姿态适用于我!


0

在已标定情况下,单应性矩阵(H)与未标定情况下的单应性矩阵(H')之间的关系为

H′=^(−1),其中K是相机的内部矩阵。


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