2012-02-01 111 views
18

轉換數據我想從動畫每一幀的OpenGL與glReadPixels()和數據轉換爲的OpenCV ::墊。我知道glReadPixels()按從左到右的順序從低位到高位獲取數據。另一方面,OpenCV以不同的方式存儲數據。從glReadPixels()來的OpenCV ::墊

有誰知道任何圖書館或任何教程/例子,可以幫助我把數據從glReadPixels轉換爲OpenCV的:在C++墊

摘要

OpenGL frame  ----------------------->  CV::Mat 

Data from left to right,     Data from left to right, 
bottom to top.        top to bottom. 

回答

42

首先,我們爲我們的數據空(或unititialized)cv::Mat讀入直接。這可以在啓動時完成一次,但另一方面cv::Mat::create並沒有真正花費太多時,圖像已經具有匹配的大小和類型。類型取決於您的需求,通常它是類似於CV_8UC3的24位彩色圖像。

cv::Mat img(height, width, CV_8UC3); 

img.create(height, width, CV_8UC3); 

那麼你必須考慮cv::Mat不neccessarily存儲圖像行連續。在每行末尾可能會有一個小的填充值,以使行4字節對齊(或8?)。所以,你需要惹像素存儲模式:

//use fast 4-byte alignment (default anyway) if possible 
glPixelStorei(GL_PACK_ALIGNMENT, (img.step & 3) ? 1 : 4); 

//set length of one complete row in destination data (doesn't need to equal img.cols) 
glPixelStorei(GL_PACK_ROW_LENGTH, img.step/img.elemSize()); 

接下來,矩陣的類型影響的glReadPixels的格式和類型參數。如果你需要彩色圖像,你必須牢記OpenCV通常以BGR順序存儲顏色值,所以你需要使用GL_BGR(A)(這是OpenGL 1.2中添加的)而不是GL_RGB(A)。對於一個分量圖像,請使用GL_LUMINANCE(它將各個顏色分量相加)或GL_RED,GL_GREEN,...(以獲取單個分量)。所以對於我們的CV_8UC3圖像的最終調用直接讀入cv::Mat是:

glReadPixels(0, 0, img.cols, img.rows, GL_BGR, GL_UNSIGNED_BYTE, img.data); 

最後,OpenCV的商店圖片來自上下。因此,您可能需要在獲取它們後將它們翻轉或在OpenGL中翻轉它們(這可以通過調整投影矩陣來完成,但在這種情況下請注意三角形的方向)。要翻轉cv::Mat垂直,您可以使用cv::flip

cv::flip(img, flipped, 0); 

所以要記住OpenCV的:從上到下

  • 將影像存儲,從左至右
  • 專賣店在BGR順序彩色圖像
  • 可能無法存儲圖像行排列緊密
+0

真的很不錯的答案。我已經在研究翻轉,但我沒有注意到glPixelStorei。謝謝 – 2012-02-01 16:34:22

+0

我不能調用GL_BGR,我不知道爲什麼連接器找不到它,因爲我有最新版本 – 2012-02-01 16:37:10

+1

@Jav_Rock它是在OpenGL 1.2中引入的。因此,如果你的硬件和驅動程序支持它(這很有可能,除非你在20年前買了你的顯卡),你只需要定義這個符號,包括一個合理的新['glext.h'](http:// www.opengl.org/registry/api/glext.h),或者首先使用擴展管理庫(如GLEW),它應該爲自己的頭文件定義常量。無論如何,一旦你想使用高於1.1的任何功能(如PBO,這可能會加快你正常使用時的讀取性能),這將是必要的。 – 2012-02-01 16:39:56

1
unsigned char* getPixelData(int x1, int y1, int x2, int y2) 
{ 
    int y_low, y_hi; 
    int x_low, x_hi; 

    if (y1 < y2) 
    { 
     y_low = y1; 
     y_hi = y2; 
    } 
    else 
    { 
     y_low = y2; 
     y_hi = y1; 
    } 

    if (x1 < x2) 
    { 
     x_low = x1; 
     x_hi = x2; 
    } 
    else 
    { 
     x_low = x2; 
     x_hi = x1; 
    } 

    while (glGetError() != GL_NO_ERROR) 
    { 
     ; 
    } 

    glReadBuffer(GL_BACK_LEFT); 

    glDisable(GL_TEXTURE_2D); 

    glPixelStorei(GL_PACK_ALIGNMENT, 1); 

    unsigned char *data = new unsigned char[ (x_hi - x_low + 1) * (y_hi - y_low + 1) * 3 ]; 

    glReadPixels(x_low, y_low, x_hi-x_low+1, y_hi-y_low+1, GL_RGB, GL_UNSIGNED_BYTE, data); 

    if (glGetError() != GL_NO_ERROR) 
    { 
     delete[] data; 
     return 0; 
    } 
    else 
    { 
     return data; 
    } 
} 

使用:

CvSize size = cvSize(320, 240); 

unsigned char *pixel_buf = getPixelData(0, 0, size.width - 1, size.height - 1); 

if (pixel_buf == 0) 
    return 0; 

IplImage *result = cvCreateImage(size, IPL_DEPTH_8U, 3); 
memcpy(result->imageData, pixel_buf, size.width * size.height * 3); 
delete[] pixel_buf;