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

Soy*_*ham 6 opengl opencv camera-calibration extrinsic-parameters

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

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

2D输入坐标

我已经将这些平面图的视图位置映射到标准化视图空间中的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
    );
Run Code Online (Sandbox Code Playgroud)

我将1,1作为ImageSize传递给校准相机.标志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;
Run Code Online (Sandbox Code Playgroud)

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

//  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 ); 
Run Code Online (Sandbox Code Playgroud)

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

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

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

//  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;
Run Code Online (Sandbox Code Playgroud)

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

ModelView = ModelView.getInverse();
Run Code Online (Sandbox Code Playgroud)

输出3D视图

这导致相机处于错误的位置,并且旋转错误.这不是太离谱,相机是Y平面的右侧,翻译不是大错特错了,我认为这完全取决于正确的方式....只是不正确呢.绘制的蓝色圆圈是我期待相机的所在.

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

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

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

Soy*_*ham 0

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

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