6

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

就opencv而言,“平面图”变成了世界中心为0,0,0的对象(即棋盘)。世界/地面位置是已知的,因此我需要获取投影信息(失真系数、fov 等)和相机的外部坐标。

二维输入坐标

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

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

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;

现在我有一个旋转和变换矩阵,但它是主要的列(我相信如果我不转置,对象会完全倾斜,并且上面的代码对我来说看起来主要是列)

//  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 向上)。

//  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();

输出 3D 视图

这导致相机在错误的位置,并且旋转错误。距离不算太远,相机在 Y 平面的右侧,平移也不是很偏,而且我认为这是正确的方向……只是还不正确。油漆绘制的蓝色圆圈是我期望相机所在的位置。

我已经阅读了大量的 SO 答案,文档十几次,但没有找到任何正确的东西,我很确定我已经涵盖了空间转换所需的所有内容,但也许我错过了一些明显的东西? 还是以错误的顺序做某事?

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

Update2 - 真实图像尺寸 我正在使用进入相机校准的图像尺寸;看到我使用的是标准化的 1,1,但 imageSize 参数是整数,我认为这可能很重要......我猜它是(红色框是投影视图空间点与 z 相交的位置=0 平面)没有任何失真校正,结果如下(唯一改变的是图像大小从 1,1 到 640,480。我也将标准化的输入视图空间坐标乘以 640,480) 在此处输入图像描述 我将尝试添加失真更正,看看它是否完美排列......

4

3 回答 3

0

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

于 2013-03-20T23:19:09.383 回答
0

至少现在我将我的 Edit 2(ImageSize 必须大于 1,1)视为修复,因为它产生的结果非常非常像我的预期。

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

于 2013-03-26T19:36:59.493 回答
0

我认为你不应该gWorldToCalibration 的倒数

ofMatrix4x4 gCalibrationToWorld = gWorldToCalibration.getInverse();

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

于 2013-12-04T12:19:45.463 回答