2012-04-25 174 views
6

我正在尝试使用OpenCV来做一些基本的增强现实。我正在使用的方式是使用findChessboardCorners从相机图像获取一组点。然后,我沿着z = 0平面创建一个3D四边形,并使用solvePnP获得成像点与平面点之间的单应性。从那里,我想我应该能够建立一个模型视图矩阵,这将允许我在图像顶部渲染一个具有正确姿势的立方体。OpenCV:旋转/平移向量到OpenGL模型视图矩阵

documentation对于solvePnP表示它输出一个旋转矢量“(与[平移矢量]一起)将模型坐标系中的点带到相机坐标系中。”我认为这与我想要的是相反的;因为我的四边形是在z = 0的平面上的,所以我想要一个模型视图矩阵,它将把这个四边形转换为合适的3D平面。

我认为通过以相反的顺序执行相反的旋转和平移,我可以计算出正确的模型视图矩阵,但似乎不起作用。虽然渲染对象(立方体)确实随着相机图像移动,并且似乎大致正确地平移,但旋转根本不起作用;它在多个轴上,只能在一个轴上旋转,有时在错误的方向旋转。下面是我在做什么至今:

std::vector<Point2f> corners; 
bool found = findChessboardCorners(*_imageBuffer, cv::Size(5,4), corners, 
             CV_CALIB_CB_FILTER_QUADS | 
             CV_CALIB_CB_FAST_CHECK); 
if(found) 
{ 
    drawChessboardCorners(*_imageBuffer, cv::Size(6, 5), corners, found); 

    std::vector<double> distortionCoefficients(5); // camera distortion 
    distortionCoefficients[0] = 0.070969; 
    distortionCoefficients[1] = 0.777647; 
    distortionCoefficients[2] = -0.009131; 
    distortionCoefficients[3] = -0.013867; 
    distortionCoefficients[4] = -5.141519; 

    // Since the image was resized, we need to scale the found corner points 
    float sw = _width/SMALL_WIDTH; 
    float sh = _height/SMALL_HEIGHT; 
    std::vector<Point2f> board_verts; 
    board_verts.push_back(Point2f(corners[0].x * sw, corners[0].y * sh)); 
    board_verts.push_back(Point2f(corners[15].x * sw, corners[15].y * sh)); 
    board_verts.push_back(Point2f(corners[19].x * sw, corners[19].y * sh)); 
    board_verts.push_back(Point2f(corners[4].x * sw, corners[4].y * sh)); 
    Mat boardMat(board_verts); 

    std::vector<Point3f> square_verts; 
    square_verts.push_back(Point3f(-1, 1, 0));        
    square_verts.push_back(Point3f(-1, -1, 0)); 
    square_verts.push_back(Point3f(1, -1, 0)); 
    square_verts.push_back(Point3f(1, 1, 0)); 
    Mat squareMat(square_verts); 

    // Transform the camera's intrinsic parameters into an OpenGL camera matrix 
    glMatrixMode(GL_PROJECTION); 
    glLoadIdentity(); 

    // Camera parameters 
    double f_x = 786.42938232; // Focal length in x axis 
    double f_y = 786.42938232; // Focal length in y axis (usually the same?) 
    double c_x = 217.01358032; // Camera primary point x 
    double c_y = 311.25384521; // Camera primary point y 


    cv::Mat cameraMatrix(3,3,CV_32FC1); 
    cameraMatrix.at<float>(0,0) = f_x; 
    cameraMatrix.at<float>(0,1) = 0.0; 
    cameraMatrix.at<float>(0,2) = c_x; 
    cameraMatrix.at<float>(1,0) = 0.0; 
    cameraMatrix.at<float>(1,1) = f_y; 
    cameraMatrix.at<float>(1,2) = c_y; 
    cameraMatrix.at<float>(2,0) = 0.0; 
    cameraMatrix.at<float>(2,1) = 0.0; 
    cameraMatrix.at<float>(2,2) = 1.0; 

    Mat rvec(3, 1, CV_32F), tvec(3, 1, CV_32F); 
    solvePnP(squareMat, boardMat, cameraMatrix, distortionCoefficients, 
       rvec, tvec); 

    _rv[0] = rvec.at<double>(0, 0); 
    _rv[1] = rvec.at<double>(1, 0); 
    _rv[2] = rvec.at<double>(2, 0); 
    _tv[0] = tvec.at<double>(0, 0); 
    _tv[1] = tvec.at<double>(1, 0); 
    _tv[2] = tvec.at<double>(2, 0); 
} 

然后在绘制代码...

GLKMatrix4 modelViewMatrix = GLKMatrix4MakeTranslation(0.0f, 0.0f, 0.0f); 
modelViewMatrix = GLKMatrix4Translate(modelViewMatrix, -tv[1], -tv[0], -tv[2]); 
modelViewMatrix = GLKMatrix4Rotate(modelViewMatrix, -rv[0], 1.0f, 0.0f, 0.0f); 
modelViewMatrix = GLKMatrix4Rotate(modelViewMatrix, -rv[1], 0.0f, 1.0f, 0.0f); 
modelViewMatrix = GLKMatrix4Rotate(modelViewMatrix, -rv[2], 0.0f, 0.0f, 1.0f); 

我从渲染创建原点周围的单位长度的立方体(即顶点 - )我知道OpenGL翻译函数以“逆序”执行转换,所以上面应该沿着z,y和x轴旋转立方体,然后翻译它。但是,它似乎是先翻译后旋转的,所以也许苹果的GLKMatrix4的工作方式不同?

This question与我的似乎非常相似,特别是coder9的答案似乎可能是或多或少我正在寻找。然而,我尝试了它并将结果与​​我的方法进行了比较,并且我在两种情况下得到的矩阵都是相同的。我觉得答案是对的,但我错过了一些关键的细节。

+1

试着如何自己做到这一点。看到这篇文章的坐标系差异的话题。 http://stackoverflow.com/questions/9081900/reference-coordinate-system-changes-between-opencv-opengl-and-android-sensor – Koobz 2012-04-26 00:15:16

回答

2

您必须确保轴正对着正确的方向。特别是,在OpenGL和OpenCV中,y和z轴面向不同的方向,以确保x-y-z基础是直接的。您可以在blog post中找到一些信息和代码(使用iPad相机)。

- 编辑 - 好的。不幸的是,我使用这些资源来反过来(opengl ---> opencv)来测试一些算法。我的主要问题是图像的行顺序在OpenGL和OpenCV之间反转(这可能有帮助)。

当模拟相机时,我遇到了相同的投影矩阵,可以找到heregeneralized projection matrix paper。在博客文章的评论中引用的This paper也显示了计算机视觉和OpenGL投影之间的一些联系。

+0

感谢您的链接。不幸的是我之前看过那篇文章。我试着或多或少逐字地使用他的代码,并得到基本相同的结果。我认为那篇文章也缺少关键细节,因为它没有显示他是如何计算单应性的,这可能很重要。我也知道y/z轴的差异,但我已经多次检查了我的代码,并且据我所知我正在处理该问题。 – 2012-04-28 05:32:15

0

我不是IOS程序员,所以这个答案可能会引起误解! 如果问题不是按照应用旋转和平移的顺序,则建议使用更简单,更常用的坐标系。

角向量中的点在图像的左上角有原点(0,0),y轴朝向图像的底部。通常从数学中我们习惯于考虑坐标系,原点在中心,y轴朝向图像顶部。根据你推入board_verts的坐标,我猜你正在犯同样的错误。如果是这样的情况下,很容易通过这样的改造角落的位置:

for (i=0;i<corners.size();i++) { 
    corners[i].x -= width/2; 
    corners[i].y = -corners[i].y + height/2; 
} 

然后调用solvePnP()。调试这并不困难,只需打印四个角的位置和估计的R和T,并查看它们是否有意义。然后,您可以继续进行OpenGL步骤。请让我知道它是怎么回事。