用opencv确定外部相机,用世界空间物体打开opengl

时间:2013-03-20 17:07:39

标签: opengl opencv camera-calibration extrinsic-parameters

我正在使用opencv和openframeworks(即.opengl)来计算图像中的相机(世界变换和投影矩阵)(以及后来的几个图像进行三角测量)。

出于opencv的目的,“平面图”成为世界中心0,0,0的对象(即棋盘)。 世界/楼层位置是已知的,所以我需要获得投影信息(失真系数,fov等)和相机的外部坐标。

2D input coordinates

我已将这些平面图的视图位置映射到标准化视图空间中的2D图像上([0,0]在左上角。[1,1]在右下角。)

对象(平面图/世界点)在xz平面上,-y向上,所以我转换为xy平面(不确定这里z-up是负还是正...)对于opencv,因为它需要是平面的

ofMatrix4x4 gWorldToCalibration(
    1, 0, 0, 0,
    0, 0, 1, 0,
    0, 1, 0, 0,
    0, 0, 0, 1
    );

我将1,1作为ImageSize传递给calibrateCamera。 标志是CV_CALIB_FIX_ASPECT_RATIO|V_CALIB_FIX_K4|CV_CALIB_FIX_K5 calibrateCamera成功运行,给我一个低误差(通常在0.003左右)。

使用calibrationMatrixValues我得到一个合理的FOV,通常在50度左右,所以我很确定内在属性是正确的。

现在计算相机的外在世界空间变换......我不相信我需要使用solvePnP,因为我只有一个对象(虽然我之前尝试了所有这些并且回来了结果相同)

//  rot and trans output...
cv::Mat& RotationVector = ObjectRotations[0];
cv::Mat& TranslationVector = ObjectTranslations[0];

//  convert rotation to matrix
cv::Mat expandedRotationVector;
cv::Rodrigues(RotationVector, expandedRotationVector);

//  merge translation and rotation into a model-view matrix
cv::Mat Rt = cv::Mat::zeros(4, 4, CV_64FC1);
for (int y = 0; y < 3; y++)
   for (int x = 0; x < 3; x++) 
        Rt.at<double>(y, x) = expandedRotationVector.at<double>(y, x);
Rt.at<double>(0, 3) = TranslationVector.at<double>(0, 0);
Rt.at<double>(1, 3) = TranslationVector.at<double>(1, 0);
Rt.at<double>(2, 3) = TranslationVector.at<double>(2, 0);
Rt.at<double>(3, 3) = 1.0;

现在我有一个轮换&amp;变换矩阵,但它是专栏(我相信,如果我不进行转置,对象完全倾斜,上面的代码看起来对我来说很重要)

//  convert to openframeworks matrix AND transpose at the same time
ofMatrix4x4 ModelView;
for ( int r=0;  r<4;    r++ )
    for ( int c=0;  c<4;    c++ )
        ModelView(r,c) = Rt.at<double>( c, r ); 

使用之前矩阵的倒数将我的飞机交换回我的坐标空间(y up)。

//  swap y & z planes so y is up
ofMatrix4x4 gCalibrationToWorld = gWorldToCalibration.getInverse();
ModelView *= gCalibrationToWorld;

不确定我是否需要这样做......当我把它们放入校准时,我没有否定飞机......

//  invert y and z planes for -/+ differences between opencv and opengl
ofMatrix4x4 InvertHandednessMatrix(
    1,  0,  0, 0,
    0,  -1, 0, 0,
    0,  0,  -1, 0,
    0,  0,  0,  1
    );
ModelView *= InvertHandednessMatrix;

最后,模型视图是对象相对于相机,我想将其反转为相机相对于对象(0,0,0)

ModelView = ModelView.getInverse();

output 3D view

这导致相机处于错误的位置,并且旋转错误。它不是远,相机是Y平面的右侧,翻译并没有大幅度关闭,我认为这是正确的方式....只是不正确。 绘制的蓝色圆圈是我期待相机的位置。

我已经经历了大量的SO答案,文档已经十几次了,但是找不到任何正确的东西,我很确定我已经涵盖了我需要的所有空间转换方式,但也许我已经错过了明显的东西 或者以错误的顺序做某事?

更新1 - 世界空间飞机...... 我已将世界空间地板平面更改为XY(Z向上)以匹配openCV的输入。 (gWorldToCalibration现在是一个单位矩阵)。 旋转仍然是错误的,投影输出是相同的,但我认为现在的翻译是正确的(它肯定在标记的正确一侧) 3D view on XY plane

Update2 - 实际图片尺寸 我正在玩相机校准的图像尺寸;因为我正在使用1,1这是规范化的,但是imageSize参数是整数,我认为这可能是重要的......我猜它是(红色框是投影的视图空间点与z相交的地方) = 0楼层) 没有任何失真校正,这里是结果(唯一改变的是从1,1到640,480的图像大小。我将我的标准化输入视图空间坐标也乘以640,480) enter image description here 我将尝试添加失真校正,以确定它是否完美排列 ...

3 个答案:

答案 0 :(得分:0)

要检查的第一件事是验证在给定估计的内在和外在相机矩阵的图像上正确地重新投影标记。然后,您可以在全局框架中找到相机位置,并查看它是否与标记位置一致。 (在OpenCV中使用坐标系。)一旦完成,没有太多可能出错的事情。由于您希望点位于xz平面上,因此您只需要一次坐标转换。如我所见,您可以使用gWorldToCalibration矩阵进行操作。然后将转换应用于标记和相机位置,并验证标记是否在正确的位置。然后摄像机位置也是正确的(除非坐标系的手性出现问题,但可以很容易地纠正)。

答案 1 :(得分:0)

至少现在我正在处理我的Edit 2(ImageSize必须大于1,1)作为修复,因为它产生了很多结果,更像是我期待的结果。

我现在可能会把事情颠倒过来,但这会产生相当不错的结果。

答案 2 :(得分:0)

我认为你应该采取 gWorldToCalibration 的反向

ofMatrix4x4 gCalibrationToWorld = gWorldToCalibration.getInverse();

在这里,我发布了代码,它或多或少地执行了您想要的OpenCV- to OpenGL COS。它在C中,但在C ++中应该类似。