我正在使用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();
这导致相机在错误的位置,并且旋转错误。距离不算太远,相机在 Y 平面的右侧,平移也不是很偏,而且我认为这是正确的方向……只是还不正确。油漆绘制的蓝色圆圈是我期望相机所在的位置。
我已经阅读了大量的 SO 答案,文档十几次,但没有找到任何正确的东西,我很确定我已经涵盖了空间转换所需的所有内容,但也许我错过了一些明显的东西? 还是以错误的顺序做某事?
更新 1 - 世界空间平面... 我已将世界空间平面更改为 XY(Z up) 以匹配 openCV 的输入。(gWorldToCalibration 现在是一个单位矩阵)。旋转仍然是错误的,投影输出是一样的,但我认为现在翻译是正确的(它肯定在标记的正确一侧)
Update2 - 真实图像尺寸 我正在使用进入相机校准的图像尺寸;看到我使用的是标准化的 1,1,但 imageSize 参数是整数,我认为这可能很重要......我猜它是(红色框是投影视图空间点与 z 相交的位置=0 平面)没有任何失真校正,结果如下(唯一改变的是图像大小从 1,1 到 640,480。我也将标准化的输入视图空间坐标乘以 640,480) 我将尝试添加失真更正,看看它是否完美排列......