http://blog.csdn.net/chenyusiyuan/article/details/5691481
上一篇筆記中使用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;
- #include "stdafx.h"
- #include "MemLeakDetect.h" // 內存洩漏檢測工具,下載地址:http://www.codeproject.com/KB/cpp/MemLeakDetect.aspx
- #include <vector>
- #include <string>
- #include <algorithm>
- #include <iostream>
- #include <ctype.h>
- #include <stdarg.h>
- #include <string.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <GL/freeglut.h>
- #include "cv.h"
- #include "highgui.h"
- #include "camerads.h"
- using namespace std;
- using namespace cv;
- // Detect Memory Leaks or comment
- #ifdef _DEBUG
- CMemLeakDetect memLeakDetect;
- #endif
- enum { STEREO_BM=0, STEREO_SGBM=1 };
- int alg = STEREO_BM;
- int stereo_rectify = 1, adaptThresh = 1;
- int SADWindowSize = 15, numberOfDisparities = 64, SADWS_alpha = 8, MaxDisp_beta = 4,
- uniqRatio = 25, thresRatio = 60;
- int saveFrames = 1;
- bool fullDP = false;
- double m_ObjectWidth[10] = {0.0}; // 目標寬度
- double m_ObjectHeight[10] = {0.0}; // 目標高度
- double m_ObjectDisparity[10] = {0.0}; // 視差
- double m_ObjectDistance[10] = {0.0}; // 距離
- char img1name[100], img2name[100], dispImgName[100], dispDataName[100];
- //---OpenGL
- float imgdata[500][500][3]; // 存放三維坐標數據
- float texture[500][500][3]; // 存放紋理數據
- int width=0, height=0, rx = 0, ry = 0;
- int eyex = 115, eyez = 115, atx = 100, atz = 50;
- float scalar=1; //scalar of converting pixel color to float coordinates
二、OpenGL 響應函數
在大象帖子的跟帖中,villager5 綜合了一小段代碼,隨著鼠標移動,可以從多個視角觀看生成的三維點雲圖,我在其基礎上做了修改。為了與OpenCV循環同步,去掉了鼠標移動的響應函數(villager5 的代碼裡用了定時器),改為使用 OpenCV 的 TrackBar 來調整 OpenGL 函數 glLookAt 的視角。
另外,對於紋理映射,我暫時用一種簡化的方式來實現,即直接把幀畫面的紋理數據(RGB值)賦值到點雲的顏色中 glColor3f ,這樣的做法缺點是顯示的三維點雲是分塊、不連續的,前方的點雲塊後面是黑色空洞。接下來會繼續嘗試按正常的紋理映射方法來實現,最終實現的效果應該是類似大象帖子中提到的 Structure From Motion軟件所實現的效果:
- /************************************************************************/
- /* OpenGL響應函數 */
- /************************************************************************/
- //
- // 功能鍵(方向鍵)響應函數
- void special(int key, int x, int y)
- {
- switch(key)
- {
- case GLUT_KEY_LEFT:
- ry-=5;
- glutPostRedisplay();
- break;
- case GLUT_KEY_RIGHT:
- ry+=5;
- glutPostRedisplay();
- break;
- case GLUT_KEY_UP:
- rx+=5;
- glutPostRedisplay();
- break;
- case GLUT_KEY_DOWN:
- rx-=5;
- glutPostRedisplay();
- break;
- }
- }
- //
- // 三維圖像顯示響應函數
- void renderScene(void) {
- glClear (GL_COLOR_BUFFER_BIT);
- glLoadIdentity();// Reset the coordinate system before modifying
- gluLookAt (eyex-100, 0.0, eyez-100.0, atx-100.0, 0.0, atz-100.0, 0.0, 1.0, 0.0); // 根據滑動塊位置變換OpenGL攝像機視角
- glRotatef(ry, 0.0, 1.0, 0.0); //rotate about the z axis // 根據鍵盤方向鍵按鍵消息變換攝像機視角
- glRotatef(rx-180, 1.0, 0.0, 0.0); //rotate about the y axis
- float x,y,z;
- glPointSize(1.0);
- glBegin(GL_POINTS);//GL_POINTS
- for (int i=0;i<height;i++){
- for (int j=0;j<width;j++){
- glColor3f(texture[i][j][0]/255, texture[i][j][1]/255, texture[i][j][2]/255); // 將圖像紋理賦值到點雲上
- x=-imgdata[i][j][0]/scalar; // 添加負號以獲得正確的左右上下方位
- y=-imgdata[i][j][1]/scalar;
- z=imgdata[i][j][2]/scalar;
- glVertex3f(x,y,z);
- }
- }
- glEnd();
- glFlush();
- }
- //
- // 窗口變化圖像重構響應函數
- void reshape (int w, int h) {
- glViewport (0, 0, (GLsizei)w, (GLsizei)h);
- glMatrixMode (GL_PROJECTION);
- glLoadIdentity ();
- gluPerspective (60, (GLfloat)w / (GLfloat)h, 1.0, 500.0); // 顯示 1 - 500 距離單位(這裡是 cm)內的點雲
- glMatrixMode (GL_MODELVIEW);
- }
- //
- // 載入三維坐標數據
- void load3dDataToGL(IplImage* img3d){
- CvScalar s;
- //accessing the image pixels
- for (int i=0;i<height;i++){
- for (int j=0;j<width;j++){
- s=cvGet2D(img3d,i,j); // s.val[0] = x, s.val[1] = y, s.val[2] = z
- imgdata[i][j][0] = s.val[0];
- imgdata[i][j][1] = s.val[1];
- imgdata[i][j][2] = fabs(s.val[2]);
- }
- }
- }
- //
- // 載入左視圖紋理數據
- void loadTextureToGL(IplImage* img){
- //int ind=0;
- CvScalar ss;
- //accessing the image pixels
- for (int i=0;i<height;i++){
- for (int j=0;j<width;j++){
- //OpenCV 是默認 BGR 格式存儲彩色圖像
- ss=cvGet2D(img,i,j); // ss.val[0] = blue, ss.val[1] = green, ss.val[2] = red
- texture[i][j][2] = ss.val[0]; // OpenGL 則是 RGB 格式存儲
- texture[i][j][1] = ss.val[1];
- texture[i][j][0] = ss.val[2];
- }
- }
- }
三、通過視差計算三維坐標數據
這部分主要以 OpenCV2.1版的 stereo_match 例程為基礎,該例程包括三種雙目匹配算法:STEREO_BM, STEREO_SGBM, STEREO_HH,其中 STEREO_HH 其實是 STEREO_SGBM 算法的狀態參數中使能了 fullDP 。
- /* SGBM 算法與原論文所提算法的差異
- by default the algorithm is single-pass, i.e. instead of 8 directions we only consider 5. Set
- fullDP=true to run the full variant of the algorithm (which could consume a lot of memory)
- the algorithm matches blocks, not individual pixels (though, by setting SADWindowSize=1
- the blocks are reduced to single pixels)
- mutual information cost function is not implemented. Instead, we use a simpler Birchfield-
- Tomasi sub-pixel metric from [22], though the color images are supported as well.
- we include some pre- and post- processing steps from K. Konolige algorithm cv::, such as
- pre-filtering (CV STEREO BM XSOBEL type) and post-filtering (uniqueness check, quadratic
- interpolation and speckle filtering)
- */
另外,該例程還對視差的計算做了改進,存儲視差的矩陣首先按照設定的 numberOfDisparity 進行 左側邊界延拓,計算得到視差後再截取出有效區域,這樣無論 numberOfDisparity 怎樣變化,我們都能夠得到與幀畫面視圖相同大小的視差圖,而不是像以前的例程那樣 numberOfDisparity 越大,視差圖左側空白區域就越大。
- //
- // 對左右視圖的左邊進行邊界延拓,以獲取與原始視圖相同大小的有效視差區域
- copyMakeBorder(img1r, img1b, 0, 0, numberOfDisparities, 0, IPL_BORDER_REPLICATE);
- copyMakeBorder(img2r, img2b, 0, 0, numberOfDisparities, 0, IPL_BORDER_REPLICATE);
- //
- // 計算視差
- if( alg == STEREO_BM )
- {
- bm(img1b, img2b, dispb);
- // 截取與原始畫面對應的視差區域(舍去加寬的部分)
- displf = dispb.colRange(numberOfDisparities, img1b.cols);
- }
- else if(alg == STEREO_SGBM)
- {
- sgbm(img1b, img2b, dispb);
- displf = dispb.colRange(numberOfDisparities, img1b.cols);
- }
但是有兩點需要注意:
(1)numberOfDisparity 太大的話,可能會增加誤匹配,因為搜索匹配點的范圍擴大後,有可能得到多個匹配對;
(2)BMState 和 SGBMState 的 disp12MaxDiff 都要設置為 -1,使左右視圖視差檢測功能失效,才能保證順利得到邊界延拓後的視差圖。否則在程序運行過程中,若增大 numberOfDisparity 後又減少其值,就會提示出錯。在 OpenCV2.1.0/src/cv/cvstereobm.cpp 的 findStereoCorrespondenceBM 中,有:
- 00715 int cols = left->cols, rows = left->rows;
- 00716 int _row0 = min(cvRound(range.begin() * rows / nstripes), rows);
- 00717 int _row1 = min(cvRound(range.end() * rows / nstripes), rows);
- 00718 uchar *ptr = state->slidingSumBuf->data.ptr + range.begin() * stripeBufSize;
- 00719 int FILTERED = (state->minDisparity - 1)*16;
- 00720
- 00721 Rect roi = validDisparityRect & Rect(0, _row0, cols, _row1);
- 00722 if( roi.height == 0 )
- 00723 return;
- 00724 int row0 = roi.y;
- 00725 int row1 = roi.y + roi.height;
- ...
- 00741 Mat disp_i = disp->rowRange(row0, row1);
- 00742 Mat cost_i = state->disp12MaxDiff >= 0 ? Mat(state->cost).rowRange(row0, row1) : Mat();
- ...
- 00751 if( state->disp12MaxDiff >= 0 )
- 00752 validateDisparity( disp_i, cost_i, state->minDisparity, state->numberOfDisparities, state->disp12MaxDiff );
這個 validateDisparity 函數是在 OpenCV2.1.0/src/cv/cvstereosgbm.cpp 中定義的,剛才說到的出錯,源自以下代碼的參數檢查:
- 00969 CV_Assert( numberOfDisparities > 0 && disp.type() == CV_16S &&
- 00970 (costType == CV_16S || costType == CV_32S) &&
- 00971 disp.size() == cost.size() );
增大 numberOfDisparity 時是正常的,滿足 disp.size() == cost.size() ;但一旦減少 numberOfDisparity ,條件 disp.size() == cost.size() 就不能滿足,從而提示出錯。至於為什麼不能滿足該條件,我還沒分析出來,調試經驗不足,這個Assert錯誤需要在程序運行遇到減少 numberOfDisparity 的情況才會出錯,不知道如何設置 breakpoint 使其只有 numberOfDisparity 減少時才生效。麻煩大家幫忙分析下啦O(∩_∩)O~
在獲取視差數據後,就可以利用 (cv)reprojectImageTo3D 來計算三維坐標數據,另外我還編寫了一個子程序(DoDetectNearObj)用於檢測離攝像頭最近的物體:
- //
- // 雙目匹配求解器狀態初始化
- bm.state->roi1 = roi1;
- bm.state->roi2 = roi2;
- bm.state->preFilterCap = 31;
- bm.state->minDisparity = 0;
- bm.state->textureThreshold = 10;
- bm.state->speckleWindowSize = 100;
- bm.state->speckleRange = 32;
- bm.state->disp12MaxDiff = -1;
- sgbm.preFilterCap = 63;
- sgbm.minDisparity = 0;
- sgbm.speckleWindowSize = bm.state->speckleWindowSize;
- sgbm.speckleRange = bm.state->speckleRange;
- sgbm.disp12MaxDiff = -1;
- for(;;)
- {
- //
- // 求解器動態參數調整
- bm.state->SADWindowSize = SADWindowSize;
- bm.state->numberOfDisparities = numberOfDisparities;
- bm.state->uniquenessRatio = uniqRatio;
- sgbm.SADWindowSize = SADWindowSize;
- sgbm.P1 = 8*cn*sgbm.SADWindowSize*sgbm.SADWindowSize;
- sgbm.P2 = 32*cn*sgbm.SADWindowSize*sgbm.SADWindowSize;
- sgbm.numberOfDisparities = numberOfDisparities;
- sgbm.uniquenessRatio = uniqRatio;
- sgbm.fullDP = fullDP;
- ...
- ...
- //
- // 檢測離攝像頭最近的物體
- img1Ipl = img1c;
- img2Ipl = img2c;
- cvZero(bi_img);
- if (stereo_rectify)
- {
- reprojectImageTo3D(displf, img3d, Q, true);
- DoDetectNearObj( &img1Ipl, &img2Ipl, bi_img, img3d, displf, disp8, f1 );
- }
- ...
- ...
- }
四、利用 OpenGL 和 OpenCV 來顯示雙目視覺三維重構效果
這裡建立了兩個 OpenCV 窗口來顯示左右視圖和視差數據、以及調整雙目匹配參數和OpenGL視角參數的 TrackBar ,還有一個 OpenGL 窗口來顯示三維重構的點雲:
- //
- // 創建顯示窗口
- //***OpenGL Window
- glutInit(&argc, argv);
- glutInitDisplayMode(GLUT_DEPTH | GLUT_SINGLE | GLUT_RGBA);
- glutInitWindowPosition(10,390);
- glutInitWindowSize(450,390);
- glutCreateWindow("3D disparity image");
- //***OpenCV Window
- cvNamedWindow("Stereo");
- cvMoveWindow("Stereo", 470, 5);
- cvNamedWindow("Parameters Adjustment");
- cvMoveWindow("Parameters Adjustment", 10, 5);
- cvResizeWindow("Parameters Adjustment", 450, 350);
- cvCreateTrackbar( "Ndisp=n*16", "Parameters Adjustment", &MaxDisp_beta, 15, onMaxdisp );
- cvCreateTrackbar( "Win=n*2-1", "Parameters Adjustment", &SADWS_alpha, 11, onSADWinSiz );
- cvCreateTrackbar( "UniqRatio", "Parameters Adjustment", &uniqRatio, 100, 0 );
- cvCreateTrackbar( "Threshold", "Parameters Adjustment", &thresRatio, 100, 0 );
- cvCreateTrackbar( "EyeX=n-100", "Parameters Adjustment", &eyex, 200, 0 );
- cvCreateTrackbar( "EyeZ=n-100", "Parameters Adjustment", &eyez,200, 0 );
- cvCreateTrackbar( "AtX=n-100", "Parameters Adjustment", &atx, 200, 0 );
- cvCreateTrackbar( "AtZ=n-100", "Parameters Adjustment", &atz, 200, 0 );
OpenCV 窗口 「Stereo」 用於顯示左右視圖和視差數據,其中也包含了一些文字信息以顯示所使用的算法和檢測到的目標參數。
- //
- // 轉換為 CV_8U 格式,彩色顯示
- displf.convertTo(disp8, CV_8U, 255/(numberOfDisparities*16.));
- CvMat disp8cv = disp8;
- F_Gray2Color(&disp8cv, vdispRGB);
- tmp_img1 = cvGetImage(vdispRGB, &tmp_img_hd1);
- //
- // 傳送界面顯示
- cvShowMultiImages("Stereo", &img1Ipl, &img2Ipl, tmp_img1, bi_img);
- /************************************************************************/
- /* cvShowMultiImages */
- /* 單窗口顯示多幅圖像的函數 */
- /************************************************************************/
- void cvShowMultiImages(char* title, IplImage* img1, IplImage* img2,
- IplImage* img3, IplImage* img4)
- {
- // DispImage - the image in which all the input images are to be copied
- IplImage *DispImage, *img;
- CvRect rect;
- int ind; // ind - the index of the image shown in the window
- int x, y; // x,y - the coordinate of top left coner of input images
- int w, h; // w,h - the width and height of the image
- float scale; // scale - How much we have to resize the image
- int max; // max - Max value of the width and height of the image
- // r - Maximum number of images in a column
- // c - Maximum number of images in a row
- int r = 2, c = 2;
- // size - the size of the images in the window
- int size = 352;
- // space - the spacing between images
- int space = 30;
- // Font Settings
- CvFont titleFont, infoFont;
- float fscale = 0.5f;
- cvInitFont(&titleFont, CV_FONT_HERSHEY_TRIPLEX, fscale, fscale, 0, 1, 8);
- cvInitFont(&infoFont, CV_FONT_HERSHEY_TRIPLEX, fscale, fscale, 0, 1, 8);
- // titleStr - Title of each images
- char *titleStr[] = {"Left Frame", "Right Frame", "Pseudo-color Disparity", "Threshold Disparity"};
- // infoStr - Information of the detected object
- char infoStr1[64], infoStr2[64];
- sprintf( infoStr1, "Object Width = %6.2f cm, Object Height = %6.2f cm",
- m_ObjectWidth[0], m_ObjectHeight[0] );
- sprintf( infoStr2, "Object Distance = %6.2f cm, Object Disparity = %6.2f pixels",
- m_ObjectDistance[0], m_ObjectDisparity[0] );
- // rectifyStr -- Currently use stereo rectification or not
- char* rectifyStr[] = { "-- Original Frame --", "-- Rectified Frame --" };
- // algStr -- Current algorithm
- char* algStr[] = {"STEREO_BM -- Left Broadened",
- "STEREO_SGBM -- Left Broadened", "STEREO_SGBM_fullDP -- Left Broadened"};
- // threshStr -- Current threshold method
- char* threshStr[] = { "Fix Max Value (255)", "Adaptive Max Value" };
- // Create a new 3 channel image to show all the input images
- DispImage = cvCreateImage( cvSize(90 + size*r, 70 + size*c), IPL_DEPTH_8U, 3 );
- cvZero(DispImage);
- // Loop for nArgs number of arguments
- for (ind = 0, x = space, y = space; ind < 4; ind++, x += (space + size)) {
- // Get the Pointer to the IplImage
- img = ind == 0 ? img1 :
- ind == 1 ? img2 :
- ind == 2 ? img3 :
- img4;
- // Find the width and height of the image
- w = img->width;
- h = img->height;
- // Find whether height or width is greater in order to resize the image
- max = (w > h)? w: h;
- // Find the scaling factor to resize the image
- scale = (float) ( (float) max / size );
- if(scale<1) scale = 1;
- // Used to Align the images
- // i.e. Align the image to next row
- if( ind % r == 0 && x!= space) {
- x = space;
- y += space*2 + size;
- }
- // Set the image ROI to display the current image
- rect = cvRect(x, y, (int)( w/scale ), (int)( h/scale ));
- cvSetImageROI(DispImage, rect);
- // Resize the input image and copy the it to the Single Big Image
- cvResize(img, DispImage);
- // Reset the ROI in order to display the next image
- cvResetImageROI(DispImage);
- // Add a green rectangle at the border of the image
- cvRectangleR(DispImage, rect, cvScalar(0, 255, 0), 2);
- // Add image title
- cvPutText(DispImage, titleStr[ind], cvPoint( x + 10, y - 10), &titleFont, CV_RGB(0,255,0));
- }
- // Add object information
- cvPutText(DispImage, infoStr1, cvPoint( 50, 360), &infoFont, CV_RGB(255,0,0) );
- cvPutText(DispImage, infoStr2, cvPoint( 50, 390), &infoFont, CV_RGB(255,0,0) );
- // Add algorithm information
- cvPutText(DispImage, rectifyStr[stereo_rectify], cvPoint( 180, 20), &infoFont, CV_RGB(255,0,0) );
- int p = alg;
- if(fullDP) p += 1;
- cvPutText(DispImage, algStr[p], cvPoint( 50, 750), &infoFont, CV_RGB(255,0,0) );
- // Add broaden information
- cvPutText(DispImage, threshStr[adaptThresh], cvPoint( 430, 750), &infoFont, CV_RGB(255,0,0) );
- // Create a new window, and show the Single Big Image
- //cvNamedWindow( title, 1 );
- cvShowImage( title, DispImage);
- // Release the Image Memory
- cvReleaseImage(&DispImage);
- }
這裡使用 FreeGlut 來顯示 OpenGL 圖像,有兩點需要注意:
(1)在圖像繪制的所有操作之後,要加入 glutPostRedisplay() 來重繪圖像,否則在循環中圖像只有響應鼠標或鍵盤消息時才會更新圖像;
(2)由於大部分的按鍵和鼠標操作都來自 OpenCV 窗口,所以顯示OpenGL圖像的 glutMainLoopEvent() 函數應該放在 OpenCV 的 cvWaitKey 之後,否則 glutMainLoopEvent() 會影響 OpenCV 對按鍵、鼠標事件的響應。
- //
- // OpenGL顯示
- img3dIpl = img3d;
- load3dDataToGL(&img3dIpl); // 載入三維坐標數據
- loadTextureToGL(&img1roi); // 載入紋理數據
- glutReshapeFunc (reshape); // 窗口變化時重構圖像
- glutDisplayFunc(renderScene); // 顯示三維圖像
- glutSpecialFunc(special); // 響應方向鍵按鍵消息
- glutPostRedisplay(); // 刷新畫面(不用此語句則不能動態更新圖像)
- //
- // 按鍵消息響應
- int c = cvWaitKey(10);
- if( (char) c == 27 )
- break;
- switch( (char) c )
- {
- case 'b':
- alg = STEREO_BM;
- SADWindowSize = 15;
- cvSetTrackbarPos("Win=n*2-1", "Parameters Adjustment", 8);
- break;
- case 's':
- alg = STEREO_SGBM;
- SADWindowSize = 7;
- cvSetTrackbarPos("Win=n*2-1", "Parameters Adjustment", 4);
- break;
- case 'f':
- if (alg == STEREO_SGBM)
- fullDP ^= 1;
- break;
- case 'r':
- stereo_rectify ^= 1;
- break;
- case 'a':
- adaptThresh ^= 1;
- break;
- case 'p':
- sprintf_s(img1name, "C://Stereo IO Data//lfFrame_%02d.jpg", saveFrames);
- sprintf_s(img2name, "C://Stereo IO Data//riFrame_%02d.jpg", saveFrames);
- sprintf_s(dispImgName, "C://Stereo IO Data//disparity_%02d.jpg", saveFrames);
- sprintf_s(dispDataName, "C://Stereo IO Data//disparity_%02d.txt", saveFrames);
- imwrite(img1name, img1r);
- imwrite(img2name, img2r);
- cvSaveImage(dispImgName, vdispRGB);
- saveDisp(dispDataName, displf);
- cout << "Save " << saveFrames*2 << " frames and " << saveFrames << " disparity image" << endl;
- cout << endl;
- saveFrames ++;
- break;
- default:
- ;
- }
- // OpenCV 處理鍵盤響應消息後,再顯示 OpenGL 圖像
- glutMainLoopEvent();
OK,本文到此就暫告一段落了,由於很多自己編寫的功能函數還處於調試階段,並且是屬於實驗室項目,就暫時不把所有代碼發布出來了,以後會陸續把修改後的代碼、以及有關攝像機標定、雙目校正與匹配等方面的原理,以筆記的方式寫到博客上和大家交流討論。謝謝關注!
P.S. 有關雙目視覺原理,推薦一個很好的博客,下面三篇文章和後面的跟帖討論都很有參考意義,大家不要錯過:
(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)