2012-04-25 134 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步驟。請讓我知道它是怎麼回事。