AR with OpenCV & OpenGL

AR with OpenCV & OpenGL

本文关键字:OpenGL OpenCV with AR      更新时间:2023-10-16

这是一个问题:我写了一个代码,在带有图纸的纸上显示OpenGL茶壶。为此,我跟踪纸张的四个角(使用冲浪检测及匹配,然后计算同型矩阵,然后移动拐角位置的平均位置以减少抖动)。角坐标用于计算固有的&相机的外部矩阵(分别使用calibrateCamera()solvePnP())。然后使用Rodrigues()计算旋转矩阵。之后,我使用decomposeProjectionMatrix()计算了旋转角度。这是代码的OPENCV部分:

...
objPoints.push_back(objCorners);
scenePoints.push_back(sceneCorners);
calibrateCamera(objPoints, scenePoints, Size(640,480), camMtx, distortCoeff, RVecs, tVecs);
solvePnP(objCorners, sceneCorners, camMtx, distortCoeff, RVec, tVec);
Rodrigues(RVec, rotMtx);
getAngles(rotMtx, rotAngles);

objCorners是模板图像中的角坐标([1 1],[img宽度1],[IMG宽度IMG高度],[1 IMG高度])。SceneCorners是使用同型矩阵计算的网络摄像头框架中的角落坐标。功能getAngles()如下:

void getAngles(Mat &rotCamMtx, Vec3d &angles)
{
    Mat camMtx, rotMtx, transVec, rotMtxX, rotMtxY, rotMtxZ;
    double  *r = rotCamMtx.ptr<double>();
    double projMtx[12] = {r[0], r[1], r[2], 0, 
                          r[3], r[4], r[5], 0, 
                          r[6], r[7], r[8], 0};
    decomposeProjectionMatrix(Mat(3,4,CV_64FC1,projMtx), camMtx, rotMtx, transVec, rotMtxX, rotMtxY, rotMtxZ, angles);
}

然后,我设置OpenGL模型视图矩阵的元素如下:

modelViewMat[0]  = 1.0;
modelViewMat[1]  = 0.0;
modelViewMat[2]  = 0.0;
modelViewMat[3]  = 0.0;
modelViewMat[4]  = 0.0;
modelViewMat[5]  = 1.0;
modelViewMat[6]  = 0.0;
modelViewMat[7]  = 0.0;
modelViewMat[8]  = 0.0;
modelViewMat[9]  = 0.0;
modelViewMat[10] = 1.0;
modelViewMat[11] = 0.0;
modelViewMat[12] = 2*matCenter.x/639 - 641/639;
modelViewMat[13] = 481/479 - 2*matCenter.y/479;
modelViewMat[14] = -0.25;
modelViewMat[15] = 1.0;

matCenter是纸的中心坐标,通过取4个角的平均值获得。modelViewMat[12]modelViewMat[13]中的值是通过映射像素坐标([1 640],[1 480])到([-1 1],[1 -1])获得的。代码的OpenGL部分:

...
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadMatrixd(modelViewMat);
glRotated(-45, 1.0, 0.0, 0.0);
glRotated(rotAngles[2], 0.0, 1.0, 0.0);
glShadeModel(GL_SMOOTH);
glColor3f(1.0, 1.0, 1.0);
glutSolidTeapot(0.3);

我在X轴周围旋转-45度-45度,使其看起来"坐着"在纸上。结果是:如果我在桌子上翻译纸张,则茶壶在纸上的位置或多或少正确(在同一地点)。如果我旋转纸张,茶壶将正确遵循旋转(y轴周围),但是位置不再正确。问题是:如何始终在纸张的同一地点"固定"茶壶?我已经尝试直接在OpenGL模型视图矩阵中使用Rodrigues()solvePnP()的结果(如OpenCV OpenGl:使用SolvePNP的正确相机姿势),但结果不正确。

几天前,基于http://blog.yarrago.com/2011/08/introduction-to-aig-aigmented-reality.html的代码解决了此问题。要正确显示3D对象,首先设置OpenGL投影矩阵,然后是OpenGL模型视图矩阵。投影矩阵的元素是从摄像机的固有矩阵计算的,如下所示:

calibrateCamera(objPoints, scenePoints, Size(640,480), camMtx, distortCoeff, RVecs, tVecs);
...
projectionMat[0]  = 2*camMtx.at<double>(0,0)/frameW;
projectionMat[1]  = 0;
projectionMat[2]  = 0;
projectionMat[3]  = 0;
projectionMat[4]  = 0;
projectionMat[5]  = 2*camMtx.at<double>(1,1)/frameH;
projectionMat[6]  = 0;
projectionMat[7]  = 0;
projectionMat[8]  = 1 - 2*camMtx.at<double>(0,2)/frameW;
projectionMat[9]  = -1 + (2*camMtx.at<double>(1,2) + 2)/frameH;
projectionMat[10] = (zNear + zFar)/(zNear - zFar);
projectionMat[11] = -1;
projectionMat[12] = 0;
projectionMat[13] = 0;
projectionMat[14] = 2*zNear*zFar/(zNear - zFar);
projectionMat[15] = 0;

frameWframeH分别为640480zNear0.1zFar100

OpenGL模型视图矩阵的元素是从旋转矩阵和翻译向量计算的(从solvePnP()Rodrigues()获得)。为了获得3D对象的正确定位,在计算模型视图矩阵之前,需要对翻译向量进行转换。

// Offset value to move the translation vector
double offsetC[3][1] = {424, 600, 0};
Mat    offset(3, 1, CV_64F, offsetC);
...
solvePnP(objCorners, sceneCorners, camMtx, distortCoeff, RVec, tVec);
Rodrigues(RVec, rotMtx);
tVec = tVec + rotMtx*offset;    // Move tVec to refer to the center of the paper
tVec = tVec / 250.0;            // Converting pixel coordinates to OpenGL world coordinates
...
modelviewMat[0]  = rotMtx.at<double>(0,0);
modelviewMat[1]  = -rotMtx.at<double>(1,0);
modelviewMat[2]  = -rotMtx.at<double>(2,0);
modelviewMat[3]  = 0;
modelviewMat[4]  = rotMtx.at<double>(0,1);
modelviewMat[5]  = -rotMtx.at<double>(1,1);
modelviewMat[6]  = -rotMtx.at<double>(2,1);
modelviewMat[7]  = 0;
modelviewMat[8]  = rotMtx.at<double>(0,2);
modelviewMat[9]  = -rotMtx.at<double>(1,2);
modelviewMat[10] = -rotMtx.at<double>(2,2);
modelviewMat[11] = 0;
modelviewMat[12] = tVec.at<double>(0,0);
modelviewMat[13] = -tVec.at<double>(1,0);
modelviewMat[14] = -tVec.at<double>(2,0);
modelviewMat[15] = 1;

offsetC的数值是纸的中心的像素坐标。然后,代码的OpenGL部分是:

glMatrixMode(GL_PROJECTION);
glLoadMatrixf(projectionMat);
glMatrixMode(GL_MODELVIEW);
glLoadMatrixf(modelviewMat);
glRotatef(90, -1.0, 0.0, 0.0);  // Rotate the teapot first so that it will be displayed correctly on the paper
glutSolidTeapot(1.0);

茶壶正确定位的重要一件事是tVec的转换。