使用OpenCV确定外部相机参数,将其转换为OpenGL中的世界坐标系对象。

6

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

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

2D input coordinates

我已将这些平面图点的视角位置映射到我的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();

output 3D view

这导致相机位置错误,旋转也不正确。它并没有离谱,相机在Y平面的正确侧面,平移也没有太大偏差,我认为它是朝上的...只是还不正确。 我期望相机在绘制的蓝色圆圈内。

我查看了很多SO答案,文档也看了十几遍,但还没有找到正确的方法,我相信我已经涵盖了所有需要进行空间转换的内容,但也许我错过了一些显而易见的东西? 或者按照错误的顺序执行了某些操作?

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

更新2 - 真实图像尺寸 我正在调整进入相机校准的图像大小;由于我使用的是归一化的1,1,但imageSize参数是整数,所以我认为这可能是重要的......我猜想这确实重要(红框是投影视图空间点与z = 0地面平面相交的位置) 没有任何畸变校正,这就是结果(唯一改变的是图像尺寸从1,1到640,480。我也将我的归一化输入视图空间坐标乘以640,480) enter image description here 我要尝试添加扭曲校正来看是否完全对齐...


你确定你可视化了相机的位置,而不是外部相机矩阵的平移分量? - fdermishin
我不确定是否误读了您的评论,但最终的模型视图矩阵(我认为应该是相机位置)被设置为ofCamera全局变换。可视化如下:绿色+Y,蓝色+z,红色+x。 - Soylent Graham
网格以0、0、0为中心。我可能把外部和相机变换搞错了?但这就是反演的作用。 - Soylent Graham
抱歉,我错过了反转步骤。然后这个看起来就没问题了。 - fdermishin
3个回答

0

我认为你不应该对 gWorldToCalibration 取反

ofMatrix4x4 gCalibrationToWorld = gWorldToCalibration.getInverse();

这里我发布了一段代码,它基本上做了你想要的OpenCV- to OpenGL COS。它是用C编写的,但在C++中应该类似。


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

2
标记被从相机重新投影成射线(蓝色线条),显然位置不正确。绿色多边形是到N米的射线投影,红色是它击中y=0平面的位置(在这种情况下是无意义的)。但我想你的意思是重新投影到视图/图像空间,我明天会做的!也许将所有内容转换为z=up以消除一步操作... - Soylent Graham
是的,我指的是在校准后立即对图像进行重新投影。这可以通过将标记物的坐标(在用于校准的坐标系中)获得的内部和外部矩阵相乘来完成。 - fdermishin
我的结果不好...我制作了一个4x4的内在矩阵(附加列/行为0,0,0,1),并从calibrateCamera中复制其余部分的cameraMatrix。我使用“rT”作为外在矩阵(这样做对吗?)并乘以我的归一化输入视图坐标。每个重新投影的世界位置都有相似的偏差...(4.0,1.7,5.5, 4.3,1.6,6.0, 4.7, 1.5, 6.0, 4.8,1.6,5.5)。我的外在平移是1.4, 4.3, 6.3。我不禁想这些值是否有关联....我是否正确进行了重新投影测试? - Soylent Graham

0

目前至少我把我的Edit 2(ImageSize必须大于1,1)视为修复,因为它产生了更接近我预期的结果。

也许我现在有些混淆,但这产生了相当不错的结果。


你好!我有一个类似的问题,但是从编辑中不太清楚。最终你的步骤是什么? - nkint
问题的解决方案是不要对图像坐标进行归一化处理,因此我使用了像素坐标和1920,1080而不是浮点数和图像大小1,1。我认为我不需要改变其他任何东西。 - Soylent Graham

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接