OpenCV學習筆記(15)使用OpenGL顯示雙目視覺三維重構效果

http://blog.csdn.net/chenyusiyuan/article/details/5691481

cmd_window

bm_3d_gl

sgbm_3d_gl

sgbm_fullDP_3d_gl

上一篇筆記中使用Matlab初步顯示了雙目視覺重構出的環境三維效果圖,不過並沒有加上紋理信息。在OpenCV中文論壇裡,大象的帖子(http://www.opencv.org.cn/forum/viewtopic.php?f=1&t=8722&sid=02986dcffb5ebcedf299833e7cbf457c)給出了利用OpenGL顯示視差數據的三維點雲圖,這是一個學習OpenGL和OpenCV混合編程的好帖子,裡面的討論跟帖也很有參考意義,我下面的代碼也是參考這個帖子的,感謝大象和論壇上的朋友們。在大象的帖子中,所顯示的三維點雲是基於視差圖來繪制的,視差越大,點雲就越靠近攝像機的近面,但要顯示環境的三維重構數據,則還需結合攝像機定標和雙目校正(cvStereoRectify)獲得的參數來計算出三維坐標(cvReprojectImageTo3D);另一方面,要動態顯示實時的三維重構數據,還需要用到一個 FreeGlut (http://freeglut.sourceforge.net/docs/api.php#WindowCallback)的函數庫,因為原本的 glut 函數庫的 glutMainLoop 在調用之後就不會返回、實現不了循環,而 FreeGlut 則有一個 glutMainLoopEvent 函數,每循環一次就會返回。下面結合著代碼裡分步講述,主要參考來源包括:

[1] 大象帖子:http://www.opencv.org.cn/forum/viewtopic.php?f=1&t=8722&sid=02986dcffb5ebcedf299833e7cbf457c

[2] 李穎 等. OpenGL函數與范例解析手冊. 國防工業出版社, 2002年1月.

[3] Edward Angel 著. 段菲 譯. OpenGL編程基礎(第3版). 清華大學出版社, 2008年3月.

[4] Nehe 教程 Lesson 6:http://nehe.gamedev.net/data/lessons/lesson.asp?lesson=06

[5] 博客「守護地下鐵」:http://hi.baidu.com/shirdrn/blog/item/047ed30f94bbbc2d6059f318.html

[6] FreeGlut 主頁:http://freeglut.sourceforge.net/index.php#download

 

一、FreeGlut的安裝

(1)在 VC 的安裝目錄(例如 D:/Microsoft Visual Studio 9.0/VC)新建一個文件夾 freeglut;

(2)將下載的 FreeGlut (freeglut 2.6.0‑3 for MSVC)解壓後,把 include 和 lib 文件夾復制到文件夾 freeglut,把 freeglut.dll 復制到系統文件夾 system32;

(3)在 VS2008 的 Tools –> Options 的 VC++ Directories 中加入 freeglut 的 include 和 lib 路徑;

(4)在項目 Properties 的 Link –> input 中加入 opengl32.lib glu32.lib freeglut.lib;


  
  
[c-sharp] view plain copy
  1. #include "stdafx.h"  
  2. #include "MemLeakDetect.h"    // 內存洩漏檢測工具,下載地址:http://www.codeproject.com/KB/cpp/MemLeakDetect.aspx  
  3.  
  4. #include <vector>  
  5. #include <string>  
  6. #include <algorithm>  
  7. #include <iostream>  
  8. #include <ctype.h>  
  9. #include <stdarg.h>  
  10. #include <string.h>  
  11. #include <stdio.h>  
  12. #include <stdlib.h>  
  13.  
  14. #include <GL/freeglut.h>  
  15. #include "cv.h"  
  16. #include "highgui.h"  
  17. #include "camerads.h"  
  18.   
  19. using namespace std;  
  20. using namespace cv;  
  21.   
  22. // Detect Memory Leaks or comment      
  23. #ifdef _DEBUG     
  24. CMemLeakDetect memLeakDetect;     
  25. #endif   
  26.   
  27. enum { STEREO_BM=0, STEREO_SGBM=1 };  
  28. int alg = STEREO_BM;  
  29. int stereo_rectify = 1, adaptThresh = 1;      
  30. int SADWindowSize = 15, numberOfDisparities = 64, SADWS_alpha = 8, MaxDisp_beta = 4,  
  31.     uniqRatio = 25, thresRatio = 60;  
  32. int saveFrames = 1;  
  33. bool fullDP = false;  
  34. double m_ObjectWidth[10] = {0.0};        // 目標寬度                      
  35. double m_ObjectHeight[10] = {0.0};    // 目標高度  
  36. double m_ObjectDisparity[10] = {0.0};        // 視差  
  37. double m_ObjectDistance[10] = {0.0};                    // 距離  
  38. char img1name[100], img2name[100], dispImgName[100], dispDataName[100];  
  39. //---OpenGL  
  40. float imgdata[500][500][3];    // 存放三維坐標數據  
  41. float texture[500][500][3]; // 存放紋理數據  
  42. int width=0, height=0, rx = 0, ry = 0;  
  43. int eyex = 115, eyez = 115, atx = 100, atz = 50;  
  44. float scalar=1;        //scalar of converting pixel color to float coordinates  

二、OpenGL 響應函數

在大象帖子的跟帖中,villager5 綜合了一小段代碼,隨著鼠標移動,可以從多個視角觀看生成的三維點雲圖,我在其基礎上做了修改。為了與OpenCV循環同步,去掉了鼠標移動的響應函數(villager5 的代碼裡用了定時器),改為使用 OpenCV 的 TrackBar 來調整 OpenGL 函數 glLookAt 的視角。

另外,對於紋理映射,我暫時用一種簡化的方式來實現,即直接把幀畫面的紋理數據(RGB值)賦值到點雲的顏色中 glColor3f ,這樣的做法缺點是顯示的三維點雲是分塊、不連續的,前方的點雲塊後面是黑色空洞。接下來會繼續嘗試按正常的紋理映射方法來實現,最終實現的效果應該是類似大象帖子中提到的 Structure From Motion軟件所實現的效果:

StructureFromMotion

[cpp]  view plain copy
  1. /************************************************************************/  
  2. /*                                           OpenGL響應函數                                                 */  
  3. /************************************************************************/  
  4. //  
  5. // 功能鍵(方向鍵)響應函數  
  6. void special(int key, int x, int y)  
  7. {  
  8.     switch(key)  
  9.     {  
  10.     case GLUT_KEY_LEFT:  
  11.         ry-=5;  
  12.         glutPostRedisplay();  
  13.         break;  
  14.     case GLUT_KEY_RIGHT:  
  15.         ry+=5;  
  16.         glutPostRedisplay();  
  17.         break;  
  18.     case GLUT_KEY_UP:  
  19.         rx+=5;  
  20.         glutPostRedisplay();  
  21.         break;  
  22.     case GLUT_KEY_DOWN:  
  23.         rx-=5;  
  24.         glutPostRedisplay();  
  25.         break;  
  26.     }  
  27. }  
  28.   
  29. //  
  30. // 三維圖像顯示響應函數  
  31. void renderScene(void) {  
  32.   
  33.     glClear (GL_COLOR_BUFFER_BIT);  
  34.     glLoadIdentity();// Reset the coordinate system before modifying   
  35.     gluLookAt (eyex-100, 0.0, eyez-100.0, atx-100.0, 0.0, atz-100.0, 0.0, 1.0, 0.0);    // 根據滑動塊位置變換OpenGL攝像機視角  
  36.     glRotatef(ry, 0.0, 1.0, 0.0); //rotate about the z axis            // 根據鍵盤方向鍵按鍵消息變換攝像機視角  
  37.     glRotatef(rx-180, 1.0, 0.0, 0.0); //rotate about the y axis  
  38.   
  39.     float x,y,z;  
  40.   
  41.     glPointSize(1.0);   
  42.     glBegin(GL_POINTS);//GL_POINTS  
  43.     for (int i=0;i<height;i++){   
  44.         for (int j=0;j<width;j++){  
  45.             glColor3f(texture[i][j][0]/255, texture[i][j][1]/255, texture[i][j][2]/255);    // 將圖像紋理賦值到點雲上  
  46.             x=-imgdata[i][j][0]/scalar;        // 添加負號以獲得正確的左右上下方位  
  47.             y=-imgdata[i][j][1]/scalar;   
  48.             z=imgdata[i][j][2]/scalar;   
  49.             glVertex3f(x,y,z);   
  50.         }  
  51.     }  
  52.     glEnd();  
  53.     glFlush();  
  54. }  
  55.   
  56. //  
  57. // 窗口變化圖像重構響應函數  
  58. void reshape (int w, int h) {  
  59.     glViewport (0, 0, (GLsizei)w, (GLsizei)h);  
  60.     glMatrixMode (GL_PROJECTION);  
  61.     glLoadIdentity ();  
  62.     gluPerspective (60, (GLfloat)w / (GLfloat)h, 1.0, 500.0);    // 顯示 1 - 500 距離單位(這裡是 cm)內的點雲  
  63.     glMatrixMode (GL_MODELVIEW);  
  64. }  
  65.   
  66. //  
  67. // 載入三維坐標數據  
  68. void load3dDataToGL(IplImage* img3d){  
  69.     CvScalar s;  
  70.     //accessing the image pixels  
  71.     for (int i=0;i<height;i++){   
  72.         for (int j=0;j<width;j++){  
  73.             s=cvGet2D(img3d,i,j);            // s.val[0] = x, s.val[1] = y, s.val[2] = z  
  74.             imgdata[i][j][0] = s.val[0];  
  75.             imgdata[i][j][1] = s.val[1];  
  76.             imgdata[i][j][2] = fabs(s.val[2]);  
  77.         }  
  78.     }   
  79. }  
  80.   
  81. //  
  82. // 載入左視圖紋理數據  
  83. void loadTextureToGL(IplImage* img){  
  84.     //int ind=0;  
  85.     CvScalar ss;  
  86.     //accessing the image pixels  
  87.     for (int i=0;i<height;i++){   
  88.         for (int j=0;j<width;j++){  
  89.             //OpenCV 是默認 BGR 格式存儲彩色圖像  
  90.             ss=cvGet2D(img,i,j);            // ss.val[0] = blue, ss.val[1] = green, ss.val[2] = red  
  91.             texture[i][j][2] = ss.val[0];    // OpenGL 則是 RGB 格式存儲  
  92.             texture[i][j][1] = ss.val[1];  
  93.             texture[i][j][0] = ss.val[2];  
  94.         }  
  95.     }   
  96. }  
 

 

三、通過視差計算三維坐標數據

這部分主要以 OpenCV2.1版的 stereo_match 例程為基礎,該例程包括三種雙目匹配算法:STEREO_BM, STEREO_SGBM, STEREO_HH,其中 STEREO_HH 其實是 STEREO_SGBM 算法的狀態參數中使能了 fullDP 。


  
  
[c-sharp] view plain copy
  1. /* SGBM 算法與原論文所提算法的差異 
  2.  by default the algorithm is single-pass, i.e. instead of 8 directions we only consider 5. Set 
  3. fullDP=true to run the full variant of the algorithm (which could consume a lot of memory) 
  4.  the algorithm matches blocks, not individual pixels (though, by setting SADWindowSize=1 
  5. the blocks are reduced to single pixels) 
  6.  mutual information cost function is not implemented. Instead, we use a simpler Birchfield- 
  7. Tomasi sub-pixel metric from [22], though the color images are supported as well. 
  8.  we include some pre- and post- processing steps from K. Konolige algorithm cv::, such as 
  9. pre-filtering (CV STEREO BM XSOBEL type) and post-filtering (uniqueness check, quadratic 
  10. interpolation and speckle filtering) 
  11. */  

另外,該例程還對視差的計算做了改進,存儲視差的矩陣首先按照設定的 numberOfDisparity 進行 左側邊界延拓,計算得到視差後再截取出有效區域,這樣無論 numberOfDisparity 怎樣變化,我們都能夠得到與幀畫面視圖相同大小的視差圖,而不是像以前的例程那樣 numberOfDisparity 越大,視差圖左側空白區域就越大。


  
  
  1. //  
  2. // 對左右視圖的左邊進行邊界延拓,以獲取與原始視圖相同大小的有效視差區域  
  3. copyMakeBorder(img1r, img1b, 0, 0, numberOfDisparities, 0, IPL_BORDER_REPLICATE);  
  4. copyMakeBorder(img2r, img2b, 0, 0, numberOfDisparities, 0, IPL_BORDER_REPLICATE);  
  5.   
  6. //  
  7. // 計算視差  
  8. if( alg == STEREO_BM )  
  9. {  
  10.     bm(img1b, img2b, dispb);  
  11.     // 截取與原始畫面對應的視差區域(舍去加寬的部分)  
  12.     displf = dispb.colRange(numberOfDisparities, img1b.cols);      
  13. }  
  14. else if(alg == STEREO_SGBM)  
  15. {  
  16.     sgbm(img1b, img2b, dispb);  
  17.     displf = dispb.colRange(numberOfDisparities, img1b.cols);  
  18. }      

但是有兩點需要注意:

(1)numberOfDisparity 太大的話,可能會增加誤匹配,因為搜索匹配點的范圍擴大後,有可能得到多個匹配對;

(2)BMState 和 SGBMState 的 disp12MaxDiff  都要設置為 -1,使左右視圖視差檢測功能失效,才能保證順利得到邊界延拓後的視差圖。否則在程序運行過程中,若增大 numberOfDisparity 後又減少其值,就會提示出錯。在 OpenCV2.1.0/src/cv/cvstereobm.cpp 的 findStereoCorrespondenceBM 中,有:


  
  
  1. 00715         int cols = left->cols, rows = left->rows;  
  2. 00716         int _row0 = min(cvRound(range.begin() * rows / nstripes), rows);  
  3. 00717         int _row1 = min(cvRound(range.end() * rows / nstripes), rows);  
  4. 00718         uchar *ptr = state->slidingSumBuf->data.ptr + range.begin() * stripeBufSize;  
  5. 00719         int FILTERED = (state->minDisparity - 1)*16;  
  6. 00720           
  7. 00721         Rect roi = validDisparityRect & Rect(0, _row0, cols, _row1);  
  8. 00722         if( roi.height == 0 )  
  9. 00723             return;  
  10. 00724         int row0 = roi.y;  
  11. 00725         int row1 = roi.y + roi.height;  
  12. ...  
  13. 00741         Mat disp_i = disp->rowRange(row0, row1);  
  14. 00742         Mat cost_i = state->disp12MaxDiff >= 0 ? Mat(state->cost).rowRange(row0, row1) : Mat();  
  15. ...  
  16. 00751         if( state->disp12MaxDiff >= 0 )  
  17. 00752             validateDisparity( disp_i, cost_i, state->minDisparity, state->numberOfDisparities, state->disp12MaxDiff );  
  18.      

這個 validateDisparity 函數是在 OpenCV2.1.0/src/cv/cvstereosgbm.cpp 中定義的,剛才說到的出錯,源自以下代碼的參數檢查:


  
  
[c-sharp] view plain copy
  1. 00969     CV_Assert( numberOfDisparities > 0 && disp.type() == CV_16S &&  
  2. 00970               (costType == CV_16S || costType == CV_32S) &&  
  3. 00971               disp.size() == cost.size() );  

增大 numberOfDisparity 時是正常的,滿足 disp.size() == cost.size() ;但一旦減少 numberOfDisparity ,條件 disp.size() == cost.size() 就不能滿足,從而提示出錯。至於為什麼不能滿足該條件,我還沒分析出來,調試經驗不足,這個Assert錯誤需要在程序運行遇到減少 numberOfDisparity 的情況才會出錯,不知道如何設置 breakpoint 使其只有 numberOfDisparity 減少時才生效。麻煩大家幫忙分析下啦O(∩_∩)O~

在獲取視差數據後,就可以利用 (cv)reprojectImageTo3D 來計算三維坐標數據,另外我還編寫了一個子程序(DoDetectNearObj)用於檢測離攝像頭最近的物體:

[c-sharp]  view plain copy
  1. //  
  2. // 雙目匹配求解器狀態初始化  
  3.   
  4. bm.state->roi1 = roi1;  
  5. bm.state->roi2 = roi2;  
  6. bm.state->preFilterCap = 31;  
  7. bm.state->minDisparity = 0;  
  8. bm.state->textureThreshold = 10;  
  9. bm.state->speckleWindowSize = 100;  
  10. bm.state->speckleRange = 32;  
  11. bm.state->disp12MaxDiff = -1;  
  12.   
  13. sgbm.preFilterCap = 63;  
  14. sgbm.minDisparity = 0;  
  15. sgbm.speckleWindowSize = bm.state->speckleWindowSize;  
  16. sgbm.speckleRange = bm.state->speckleRange;  
  17. sgbm.disp12MaxDiff = -1;  
  18. for(;;)  
  19. {  
  20.     //  
  21.     // 求解器動態參數調整  
  22.     bm.state->SADWindowSize = SADWindowSize;  
  23.     bm.state->numberOfDisparities = numberOfDisparities;  
  24.     bm.state->uniquenessRatio = uniqRatio;  
  25.   
  26.     sgbm.SADWindowSize = SADWindowSize;  
  27.     sgbm.P1 = 8*cn*sgbm.SADWindowSize*sgbm.SADWindowSize;  
  28.     sgbm.P2 = 32*cn*sgbm.SADWindowSize*sgbm.SADWindowSize;  
  29.     sgbm.numberOfDisparities = numberOfDisparities;  
  30.     sgbm.uniquenessRatio = uniqRatio;  
  31.     sgbm.fullDP = fullDP;  
  32.       
  33.     ...  
  34.     ...  
  35.       
  36.     //  
  37.     // 檢測離攝像頭最近的物體  
  38.     img1Ipl = img1c;  
  39.     img2Ipl = img2c;  
  40.     cvZero(bi_img);  
  41.     if (stereo_rectify)  
  42.     {  
  43.         reprojectImageTo3D(displf, img3d, Q, true);  
  44.         DoDetectNearObj( &img1Ipl, &img2Ipl, bi_img, img3d, displf, disp8, f1 );  
  45.     }  
  46.     ...  
  47.     ...  
  48. }      

四、利用 OpenGL 和 OpenCV 來顯示雙目視覺三維重構效果

這裡建立了兩個 OpenCV 窗口來顯示左右視圖和視差數據、以及調整雙目匹配參數和OpenGL視角參數的 TrackBar ,還有一個 OpenGL 窗口來顯示三維重構的點雲:

 

[c-sharp]  view plain copy
  1. //  
  2. // 創建顯示窗口  
  3. //***OpenGL Window  
  4. glutInit(&argc, argv);  
  5. glutInitDisplayMode(GLUT_DEPTH | GLUT_SINGLE | GLUT_RGBA);  
  6. glutInitWindowPosition(10,390);  
  7. glutInitWindowSize(450,390);  
  8. glutCreateWindow("3D disparity image");  
  9.   
  10. //***OpenCV Window  
  11. cvNamedWindow("Stereo");  
  12. cvMoveWindow("Stereo", 470, 5);  
  13.   
  14. cvNamedWindow("Parameters Adjustment");  
  15. cvMoveWindow("Parameters Adjustment", 10, 5);  
  16. cvResizeWindow("Parameters Adjustment", 450, 350);  
  17.   
  18. cvCreateTrackbar( "Ndisp=n*16""Parameters Adjustment", &MaxDisp_beta, 15, onMaxdisp );  
  19. cvCreateTrackbar( "Win=n*2-1""Parameters Adjustment", &SADWS_alpha, 11, onSADWinSiz );  
  20. cvCreateTrackbar( "UniqRatio""Parameters Adjustment", &uniqRatio, 100, 0 );  
  21. cvCreateTrackbar( "Threshold""Parameters Adjustment", &thresRatio, 100, 0 );  
  22. cvCreateTrackbar( "EyeX=n-100""Parameters Adjustment", &eyex, 200, 0 );  
  23. cvCreateTrackbar( "EyeZ=n-100""Parameters Adjustment", &eyez,200, 0 );  
  24. cvCreateTrackbar( "AtX=n-100""Parameters Adjustment", &atx, 200, 0 );  
  25. cvCreateTrackbar( "AtZ=n-100""Parameters Adjustment", &atz, 200, 0 );  

 

OpenCV 窗口 「Stereo」 用於顯示左右視圖和視差數據,其中也包含了一些文字信息以顯示所使用的算法和檢測到的目標參數。


  
  
[c-sharp] view plain copy
  1.         //  
  2.         // 轉換為 CV_8U 格式,彩色顯示  
  3.         displf.convertTo(disp8, CV_8U, 255/(numberOfDisparities*16.));  
  4.         CvMat disp8cv = disp8;  
  5.         F_Gray2Color(&disp8cv, vdispRGB);  
  6.         tmp_img1 = cvGetImage(vdispRGB, &tmp_img_hd1);  
  7.   
  8.         //  
  9.         // 傳送界面顯示  
  10.         cvShowMultiImages("Stereo", &img1Ipl, &img2Ipl, tmp_img1, bi_img);  
  11.           
  12.           
  13. /************************************************************************/  
  14. /*                                        cvShowMultiImages                                              */  
  15. /*                                    單窗口顯示多幅圖像的函數                                         */  
  16. /************************************************************************/  
  17. void cvShowMultiImages(char* title, IplImage* img1, IplImage* img2,   
  18.                        IplImage* img3, IplImage* img4)   
  19. {  
  20.   
  21.     // DispImage - the image in which all the input images are to be copied  
  22.     IplImage *DispImage, *img;  
  23.     CvRect rect;  
  24.   
  25.     int ind;        // ind - the index of the image shown in the window  
  26.     int x, y;    // x,y - the coordinate of top left coner of input images  
  27.     int w, h;    // w,h - the width and height of the image  
  28.       
  29.     float scale;    // scale - How much we have to resize the image      
  30.     int max;    // max - Max value of the width and height of the image      
  31.   
  32.     // r - Maximum number of images in a column   
  33.     // c - Maximum number of images in a row   
  34.     int r = 2, c = 2;  
  35.     // size - the size of the images in the window  
  36.     int size = 352;      
  37.     // space - the spacing between images  
  38.     int space = 30;      
  39.   
  40.     // Font Settings  
  41.     CvFont titleFont, infoFont;  
  42.     float fscale = 0.5f;  
  43.     cvInitFont(&titleFont, CV_FONT_HERSHEY_TRIPLEX, fscale, fscale, 0, 1, 8);  
  44.     cvInitFont(&infoFont, CV_FONT_HERSHEY_TRIPLEX, fscale, fscale, 0, 1, 8);  
  45.   
  46.     // titleStr - Title of each images  
  47.     char *titleStr[] = {"Left Frame""Right Frame""Pseudo-color Disparity""Threshold Disparity"};  
  48.     // infoStr - Information of the detected object  
  49.     char infoStr1[64], infoStr2[64];  
  50.     sprintf( infoStr1, "Object Width = %6.2f cm, Object Height = %6.2f cm",   
  51.         m_ObjectWidth[0], m_ObjectHeight[0] );  
  52.     sprintf( infoStr2, "Object Distance = %6.2f cm, Object Disparity = %6.2f pixels",   
  53.         m_ObjectDistance[0], m_ObjectDisparity[0] );  
  54.     // rectifyStr -- Currently use stereo rectification or not  
  55.     char* rectifyStr[] = { "-- Original Frame --""-- Rectified Frame --" };  
  56.     // algStr -- Current algorithm  
  57.     char* algStr[] = {"STEREO_BM -- Left Broadened",   
  58.         "STEREO_SGBM -- Left Broadened""STEREO_SGBM_fullDP -- Left Broadened"};  
  59.     // threshStr -- Current threshold method  
  60.     char* threshStr[] = { "Fix Max Value (255)""Adaptive Max Value" };  
  61.   
  62.     // Create a new 3 channel image to show all the input images  
  63.     DispImage = cvCreateImage( cvSize(90 + size*r, 70 + size*c), IPL_DEPTH_8U, 3 );  
  64.     cvZero(DispImage);  
  65.   
  66.     // Loop for nArgs number of arguments  
  67.     for (ind = 0, x = space, y = space; ind < 4; ind++, x += (space + size)) {  
  68.   
  69.         // Get the Pointer to the IplImage  
  70.         img = ind == 0 ? img1 :       
  71.             ind == 1 ? img2 :       
  72.             ind == 2 ? img3 :       
  73.             img4;  
  74.   
  75.         // Find the width and height of the image  
  76.         w = img->width;  
  77.         h = img->height;  
  78.   
  79.         // Find whether height or width is greater in order to resize the image  
  80.         max = (w > h)? w: h;  
  81.   
  82.         // Find the scaling factor to resize the image  
  83.         scale = (float) ( (float) max / size );  
  84.         if(scale<1)        scale = 1;  
  85.   
  86.         // Used to Align the images  
  87.         // i.e. Align the image to next row  
  88.         if( ind % r == 0 && x!= space) {  
  89.             x  = space;  
  90.             y += space*2 + size;  
  91.         }  
  92.   
  93.         // Set the image ROI to display the current image  
  94.         rect = cvRect(x, y, (int)( w/scale ), (int)( h/scale ));  
  95.         cvSetImageROI(DispImage, rect);  
  96.   
  97.         // Resize the input image and copy the it to the Single Big Image  
  98.         cvResize(img, DispImage);  
  99.   
  100.         // Reset the ROI in order to display the next image  
  101.         cvResetImageROI(DispImage);  
  102.   
  103.         // Add a green rectangle at the border of the image  
  104.         cvRectangleR(DispImage, rect, cvScalar(0, 255, 0), 2);  
  105.         // Add image title  
  106.         cvPutText(DispImage, titleStr[ind], cvPoint( x + 10, y - 10), &titleFont, CV_RGB(0,255,0));  
  107.     }  
  108.     // Add object information  
  109.     cvPutText(DispImage, infoStr1, cvPoint( 50, 360), &infoFont, CV_RGB(255,0,0) );  
  110.     cvPutText(DispImage, infoStr2, cvPoint( 50, 390), &infoFont, CV_RGB(255,0,0) );  
  111.     // Add algorithm information  
  112.     cvPutText(DispImage, rectifyStr[stereo_rectify], cvPoint( 180, 20), &infoFont, CV_RGB(255,0,0) );  
  113.     int p = alg;  
  114.     if(fullDP) p += 1;  
  115.     cvPutText(DispImage, algStr[p], cvPoint( 50, 750), &infoFont, CV_RGB(255,0,0) );  
  116.     // Add broaden information  
  117.     cvPutText(DispImage, threshStr[adaptThresh], cvPoint( 430, 750), &infoFont, CV_RGB(255,0,0) );  
  118.   
  119.     // Create a new window, and show the Single Big Image  
  120.     //cvNamedWindow( title, 1 );  
  121.     cvShowImage( title, DispImage);  
  122.   
  123.     // Release the Image Memory  
  124.     cvReleaseImage(&DispImage);  
  125. }  

這裡使用  FreeGlut 來顯示 OpenGL 圖像,有兩點需要注意:

(1)在圖像繪制的所有操作之後,要加入 glutPostRedisplay() 來重繪圖像,否則在循環中圖像只有響應鼠標或鍵盤消息時才會更新圖像;

(2)由於大部分的按鍵和鼠標操作都來自 OpenCV 窗口,所以顯示OpenGL圖像的 glutMainLoopEvent() 函數應該放在 OpenCV 的 cvWaitKey 之後,否則 glutMainLoopEvent() 會影響 OpenCV 對按鍵、鼠標事件的響應。


  
  
[c-sharp] view plain copy
  1. //  
  2. // OpenGL顯示  
  3. img3dIpl = img3d;  
  4. load3dDataToGL(&img3dIpl);            // 載入三維坐標數據  
  5. loadTextureToGL(&img1roi);        // 載入紋理數據  
  6. glutReshapeFunc (reshape);            // 窗口變化時重構圖像  
  7. glutDisplayFunc(renderScene);        // 顯示三維圖像  
  8. glutSpecialFunc(special);                // 響應方向鍵按鍵消息  
  9. glutPostRedisplay();                        // 刷新畫面(不用此語句則不能動態更新圖像)  
  10.   
  11. //  
  12. // 按鍵消息響應  
  13. int c = cvWaitKey(10);  
  14. if( (char) c == 27 )  
  15.     break;  
  16. switch( (char) c )  
  17. {  
  18. case 'b':  
  19.     alg = STEREO_BM;  
  20.     SADWindowSize = 15;  
  21.     cvSetTrackbarPos("Win=n*2-1""Parameters Adjustment", 8);  
  22.     break;  
  23. case 's':  
  24.     alg = STEREO_SGBM;  
  25.     SADWindowSize = 7;  
  26.     cvSetTrackbarPos("Win=n*2-1""Parameters Adjustment", 4);  
  27.     break;  
  28. case 'f':  
  29.     if (alg == STEREO_SGBM)  
  30.         fullDP ^= 1;  
  31.     break;  
  32. case 'r':  
  33.     stereo_rectify ^= 1;  
  34.     break;  
  35. case 'a':  
  36.     adaptThresh ^= 1;  
  37.     break;  
  38. case 'p':  
  39.     sprintf_s(img1name, "C://Stereo IO Data//lfFrame_%02d.jpg", saveFrames);  
  40.     sprintf_s(img2name, "C://Stereo IO Data//riFrame_%02d.jpg", saveFrames);  
  41.     sprintf_s(dispImgName, "C://Stereo IO Data//disparity_%02d.jpg", saveFrames);  
  42.     sprintf_s(dispDataName, "C://Stereo IO Data//disparity_%02d.txt", saveFrames);  
  43.     imwrite(img1name, img1r);  
  44.     imwrite(img2name, img2r);  
  45.     cvSaveImage(dispImgName, vdispRGB);      
  46.     saveDisp(dispDataName, displf);  
  47.     cout << "Save " << saveFrames*2 << " frames and " << saveFrames << " disparity image" << endl;  
  48.     cout << endl;  
  49.     saveFrames ++;  
  50.     break;  
  51. default:  
  52.     ;  
  53. }  
  54. // OpenCV 處理鍵盤響應消息後,再顯示 OpenGL 圖像  
  55. glutMainLoopEvent();  

OK,本文到此就暫告一段落了,由於很多自己編寫的功能函數還處於調試階段,並且是屬於實驗室項目,就暫時不把所有代碼發布出來了,以後會陸續把修改後的代碼、以及有關攝像機標定、雙目校正與匹配等方面的原理,以筆記的方式寫到博客上和大家交流討論。謝謝關注!

P.S. 有關雙目視覺原理,推薦一個很好的博客,下面三篇文章和後面的跟帖討論都很有參考意義,大家不要錯過:

[1] 關於OpenCV立體匹配算法的一個試驗以及請教

http://www.opencv.org.cn/forum/viewtopic.php?f=1&t=9301

[2] 分享一些OpenCV實現立體視覺的經驗

http://blog.csdn.net/scyscyao/archive/2010/04/02/5443341.aspx

http://www.opencv.org.cn/forum/viewtopic.php?f=1&t=9771

[1] 雙攝像頭測距的OpenCV實現

http://blog.csdn.net/scyscyao/archive/2010/05/06/5562024.aspx


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值