1. 程式人生 > >基於標記的AR的OpenCV實現:動態視訊輸入

基於標記的AR的OpenCV實現:動態視訊輸入

我的上篇部落格《基於標記的AR的OpenCV實現》實現的是單幅圖片的標記檢測和增強現實,稍微改動了程式實現了攝像頭視訊流影象的動態檢測和實時增強,經測試,

實時性不錯,在標記不被遮擋的情況下,繪製的虛擬模型和實時與檢測標記結合,但標記只要有被遮擋一少部分,標記就檢測失敗。

以下講下程式修改部分。

1:show函式並沒有修改,但還是講一下程式流程,首先在main()函式中呼叫show()函式後,在show()函式中,首先完成一些顯示引數的初始化,這部分只需執行一次即可,然後將影象資料讀入pixeldata中,之後通過glutDisplayFunc(&display)呼叫display()函式進行顯示但請注意,

display()函式只會執行一次。glutMainLoop進入GLUT事件處理迴圈。在一個GLUT程式中,這個例程被呼叫一次 。一旦被呼叫,這個程式將永遠不會返回 。它將呼叫必要的任何已註冊的回撥

int show(const char* filename,int argc, char** argv,Mat_<float>& cameraMatrix, vector<Marker>& detectedMarkers)  
{  
    //開啟檔案  
    FILE* pfile=fopen(filename,"rb");  
    if(pfile == 0) exit(0);  
    //讀取影象大小  
    fseek(pfile,0x0012,SEEK_SET);  
    fread(&imagewidth,sizeof(imagewidth),1,pfile);  
    fread(&imageheight,sizeof(imageheight),1,pfile);  
    //計算畫素資料長度  
    pixellength=imagewidth*3;  
    while(pixellength%4 != 0)pixellength++;  
    pixellength *= imageheight;  
    //讀取畫素資料  
    pixeldata = (GLubyte*)malloc(pixellength);  
    if(pixeldata == 0) exit(0);  
    fseek(pfile,54,SEEK_SET);  
    fread(pixeldata,pixellength,1,pfile);  
    //以上是讀取一個bmp影象寬高和影象資料的操作
    //關閉檔案  
    fclose(pfile);  
  
    build_projection(cameraMatrix);  //這是建立攝像機內參數矩陣,就是相機矩陣,display函式開始匯入的模型就是相機矩陣
    setMarker(detectedMarkers);  //匯入找到的標識
    //初始化glut執行  
    glutInit(&argc,argv);  
    glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA);  
    glutInitWindowPosition(100,100);  
    glutInitWindowSize(imagewidth,imageheight);  
    glutCreateWindow(filename);  
    glutDisplayFunc(&display);  
    glutMainLoop();  
    //-------------------------------------  
    free(pixeldata);  
    return 0;  
}

2:為了實現display()函式的迴圈呼叫,執行glutPostRedisplay();語句會觸發一次display()的執行,有兩種思路:1:設定一個定時器,定時執行glutPostRedisplay();從而週期性

呼叫display()函式;2:在display()函式體最後執行glutPostRedisplay();從而完成自我不斷觸發呼叫。我採用了思路2的做法成功實現,思路1原理上也可實現。

主要修改display()函式。#if 1---#endif為新增部分,主要是更新影象資料,並執行markerDetector.processFrame(src,camMatrix, distCoeff,markers);從而得到更新後標記。

pixeldata的更新還是採用show()函式中讀影象檔案的方法,因此先將從攝像頭讀入影象儲存為一個臨時圖片檔案"aa.bmp",在用fread()讀入的方式,這種方法確實比較low,
但比較容易實現了想要達到的效果。
void display(void)  
{  
#if 1
    Mat src;
    camera >> src;
    imwrite("aa.bmp",src);
    //開啟檔案  
    FILE* pfile=fopen("aa.bmp","rb");  
    if(pfile == 0) exit(0);  
    fseek(pfile,54,SEEK_SET);  
    fread(pixeldata,pixellength,1,pfile);  
    //以上是讀取一個bmp影象寬高和影象資料的操作
    //關閉檔案  
    fclose(pfile);  

  double t = (double)getTickCount();
      markerDetector.processFrame(src,camMatrix, distCoeff,markers);
  t = ((double)getTickCount() - t)/getTickFrequency(); 
  cout<<"t"<<t<<endl;
    setMarker(markers);  //匯入找到的標識
#endif 
    cout<<"display"<<endl;
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);  
    //繪製圖片,第一、二、三、四個引數表示圖象寬度、圖象高度、畫素資料內容、畫素資料在記憶體中的格式,最後一個引數表示用於繪製的畫素資料在記憶體中的位置  
    glDrawPixels(imagewidth,imageheight,GL_BGR_EXT,GL_UNSIGNED_BYTE,pixeldata);  
    
    /* 
     glMatrixMode - 指定哪一個矩陣是當前矩陣 
      
     glMatrixMode設定當前矩陣模式: 
     GL_MODELVIEW,對模型視景矩陣堆疊應用隨後的矩陣操作. 
     GL_PROJECTION,對投影矩陣應用隨後的矩陣操作. 
     GL_TEXTURE,對紋理矩陣堆疊應用隨後的矩陣操作. 
     與glLoadIdentity()一同使用 
     glLoadIdentity():該函式的功能是重置當前指定的矩陣為單位矩陣。 
     在glLoadIdentity()之後我們為場景設定了透檢視。glMatrixMode(GL_MODELVIEW)設定當前矩陣為模型檢視矩陣,模型檢視矩陣儲存了有關物體的資訊。 
     */  
    //繪製座標  ,匯入相機內參數矩陣模型
    glMatrixMode(GL_PROJECTION);  
    glLoadMatrixf(projectionMatrix.data);  
    glMatrixMode(GL_MODELVIEW);  
    glLoadIdentity();  
    
    glEnableClientState(GL_VERTEX_ARRAY);  //啟用客戶端的某項功能
    glEnableClientState(GL_NORMAL_ARRAY);  
  
    glPushMatrix();  
    glLineWidth(3.0f);  
  
    float lineX[] = {0,0,0,1,0,0};  
    float lineY[] = {0,0,0,0,1,0};  
    float lineZ[] = {0,0,0,0,0,1};  
  
    const GLfloat squareVertices[] = {  
        -0.5f, -0.5f,  
        0.5f,  -0.5f,  
        -0.5f,  0.5f,  
        0.5f,   0.5f,  
    };  
    const GLubyte squareColors[] = {  
        255, 255,   0, 255,  
        0,   255, 255, 255,  
        0,     0,   0,   0,  
        255,   0, 255, 255,  
    };  
  
    for (size_t transformationIndex=0; transformationIndex<m_detectedMarkers.size(); transformationIndex++)  
    {  
        const Transformation& transformation = m_detectedMarkers[transformationIndex].transformation;  
        Matrix44 glMatrix = transformation.getMat44();  
        
        //匯入相機外引數矩陣模型
        glLoadMatrixf(reinterpret_cast<const GLfloat*>(&glMatrix.data[0]));  //reinterpret_cast:任何型別的指標之間都可以互相轉換,修改了運算元型別,僅僅是重新解釋了給出的物件的位元模型而沒有進行二進位制轉換
  
        glVertexPointer(2, GL_FLOAT, 0, squareVertices);  //指定頂點陣列的位置,2表示每個頂點由三個量構成(x, y),GL_FLOAT表示每個量都是一個GLfloat型別的值。第三個引數0。最後的squareVertices指明瞭陣列實際的位置。這個squareVertices是由第一個引數和要畫的圖形有幾個頂點決定大小,理解。
        glEnableClientState(GL_VERTEX_ARRAY);  //表示啟用頂點陣列
        glColorPointer(4, GL_UNSIGNED_BYTE, 0, squareColors);  //RGBA顏色,四個頂點
        glEnableClientState(GL_COLOR_ARRAY);  //啟用顏色陣列
  
        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);  
        glDisableClientState(GL_COLOR_ARRAY);  
  
        float scale = 0.5;  
        glScalef(scale, scale, scale);  
  
        glColor4f(1.0f, 0.0f, 0.0f, 1.0f);  
        glVertexPointer(3, GL_FLOAT, 0, lineX);  
        glDrawArrays(GL_LINES, 0, 2);  
  
        glColor4f(0.0f, 1.0f, 0.0f, 1.0f);  
        glVertexPointer(3, GL_FLOAT, 0, lineY);  
        glDrawArrays(GL_LINES, 0, 2);  
  
        glColor4f(0.0f, 0.0f, 1.0f, 1.0f);  
        glVertexPointer(3, GL_FLOAT, 0, lineZ);  
        glDrawArrays(GL_LINES, 0, 2);  
    }     
    glFlush();  
    glPopMatrix();  
  
    glDisableClientState(GL_VERTEX_ARRAY);    
    
    glutSwapBuffers();  
    glutPostRedisplay();
} 
本部落格所改程式碼為mastering opencv with practical computer vision projects 第二章書中程式碼。

我實現的程式碼已上傳到,需要的自行下載。