我正在使用opencv和openframeworks(即opengl)从图像(以及后来的多个图像用于三角测量)计算相机(世界变换和投影矩阵)。
对于opencv而言,“平面图”成为对象(即棋盘),其中0,0,0是世界的中心。已知世界/地板位置,因此需要获取投影信息(畸变系数、视场等)和相机的外部坐标。
我已将这些平面图点的视角位置映射到我的2D图像中的规范化视图空间中([0,0]为左上角。[1,1]为右下角)。
对象(平面图/世界点)位于xz平面,向-y方向,因此我将其转换为xy平面(在这里不确定z方向是向上还是向下...)以适应opencv,因为它需要是平面的。
ofMatrix4x4 gWorldToCalibration(
1, 0, 0, 0,
0, 0, 1, 0,
0, 1, 0, 0,
0, 0, 0, 1
);
我将1,1作为ImageSize传递给calibrateCamera函数。
flags参数为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向上),以匹配openCV的输入。(gWorldToCalibration现在是一个单位矩阵)。 旋转仍然不正确,投影输出也相同,但我认为平移现在是正确的(它肯定在标记的正确侧面)
更新2 - 真实图像尺寸 我正在调整进入相机校准的图像大小;由于我使用的是归一化的1,1,但imageSize参数是整数,所以我认为这可能是重要的......我猜想这确实重要(红框是投影视图空间点与z = 0地面平面相交的位置) 没有任何畸变校正,这就是结果(唯一改变的是图像尺寸从1,1到640,480。我也将我的归一化输入视图空间坐标乘以640,480) 我要尝试添加扭曲校正来看是否完全对齐...