计算机视觉
图像处理

OpenCV学习笔记(15)使用OpenGL显示双目视觉三维重构效果

cmd_window

bm_3d_gl

sgbm_3d_gl

sgbm_fullDP_3d_gl

上一篇笔记中使用Matlab初步显示了双目视觉重构出的环境三维效果图,不过并没有加上纹理信息。在Opencv中文论坛里,大象的帖子给出了利用OpenGL显示视差数据的三维点云图,这是一个学习OpenGL和OpenCV混合编程的好帖子,里面的讨论跟帖也很有参考意义,我下面的代 码也是参考这个帖子的,感谢大象和论坛上的朋友们。在大象的帖子中,所显示的三维点云是基于视差图来绘制的,视差越大,点云就越靠近摄像机的近面,但要显 示环境的三维重构数据,则还需结合摄像机定标和双目校正(cvStereoRectify)获得的参数来计算出三维坐标 (cvReprojectImageTo3D);另一方面,要动态显示实时的三维重构数据,还需要用到一个 FreeGlut (http://freeglut.sourceforge.net/docs/api.php#WindowCallback)的函数库,因为原本的 glut 函数库的 glutMainLoop 在调用之后就不会返回、实现不了循环,而 FreeGlut 则有一个 glutMainLoopEvent 函数,每循环一次就会返回。下面结合着代码里分步讲述,主要参考来源包括:

[1] 大象帖子

[2] 李颖 等. OpenGL函数与范例解析手册. 国防工业出版社, 2002年1月.

[3] Edward Angel 著. 段菲 译. OpenGL编程基础(第3版). 清华大学出版社, 2008年3月.

[4] 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;

  1. #include “stdafx.h”
  2. #include “MemLeakDetect.h”    // 内存泄漏检测工具,下载地址:http://www.codeproject.com/KB/cpp/MemLeakDetect.aspx
  3. #include <vector>
  4. #include <string>
  5. #include <algorithm>
  6. #include <iostream>
  7. #include <ctype.h>
  8. #include <stdarg.h>
  9. #include <string.h>
  10. #include <stdio.h>
  11. #include <stdlib.h>
  12. #include <GL/freeglut.h>
  13. #include “cv.h”
  14. #include “highgui.h”
  15. #include “camerads.h”
  16. using namespace std;
  17. using namespace cv;
  18. // Detect Memory Leaks or comment    
  19. #ifdef _DEBUG   
  20. CMemLeakDetect memLeakDetect;
  21. #endif 
  22. enum { STEREO_BM=0, STEREO_SGBM=1 };
  23. int alg = STEREO_BM;
  24. int stereo_rectify = 1, adaptThresh = 1;
  25. int SADWindowSize = 15, numberOfDisparities = 64, SADWS_alpha = 8, MaxDisp_beta = 4,
  26.     uniqRatio = 25, thresRatio = 60;
  27. int saveFrames = 1;
  28. bool fullDP = false;
  29. double m_ObjectWidth[10] = {0.0};        // 目标宽度                    
  30. double m_ObjectHeight[10] = {0.0};    // 目标高度
  31. double m_ObjectDisparity[10] = {0.0};        // 视差
  32. double m_ObjectDistance[10] = {0.0};                    // 距离
  33. char img1name[100], img2name[100], dispImgName[100], dispDataName[100];
  34. //—OpenGL
  35. float imgdata[500][500][3];    // 存放三维坐标数据
  36. float texture[500][500][3]; // 存放纹理数据
  37. int width=0, height=0, rx = 0, ry = 0;
  38. int eyex = 115, eyez = 115, atx = 100, atz = 50;
  39. 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

  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. void renderScene(void) {
  31.     glClear (GL_COLOR_BUFFER_BIT);
  32.     glLoadIdentity();// Reset the coordinate system before modifying 
  33.     gluLookAt (eyex-100, 0.0, eyez-100.0, atx-100.0, 0.0, atz-100.0, 0.0, 1.0, 0.0);    // 根据滑动块位置变换OpenGL摄像机视角
  34.     glRotatef(ry, 0.0, 1.0, 0.0); //rotate about the z axis            // 根据键盘方向键按键消息变换摄像机视角
  35.     glRotatef(rx-180, 1.0, 0.0, 0.0); //rotate about the y axis
  36.     float x,y,z;
  37.     glPointSize(1.0);
  38.     glBegin(GL_POINTS);//GL_POINTS
  39.     for (int i=0;i<height;i++){
  40.         for (int j=0;j<width;j++){
  41.             glColor3f(texture[i][j][0]/255, texture[i][j][1]/255, texture[i][j][2]/255);    // 将图像纹理赋值到点云上
  42.             x=-imgdata[i][j][0]/scalar;        // 添加负号以获得正确的左右上下方位
  43.             y=-imgdata[i][j][1]/scalar;
  44.             z=imgdata[i][j][2]/scalar;
  45.             glVertex3f(x,y,z);
  46.         }
  47.     }
  48.     glEnd();
  49.     glFlush();
  50. }
  51. //////////////////////////////////////////////////////////////////////////
  52. // 窗口变化图像重构响应函数
  53. void reshape (int w, int h) {
  54.     glViewport (0, 0, (GLsizei)w, (GLsizei)h);
  55.     glMatrixMode (GL_PROJECTION);
  56.     glLoadIdentity ();
  57.     gluPerspective (60, (GLfloat)w / (GLfloat)h, 1.0, 500.0);    // 显示 1 – 500 距离单位(这里是 cm)内的点云
  58.     glMatrixMode (GL_MODELVIEW);
  59. }
  60. //////////////////////////////////////////////////////////////////////////
  61. // 载入三维坐标数据
  62. void load3dDataToGL(IplImage* img3d){
  63.     CvScalar s;
  64.     //accessing the image pixels
  65.     for (int i=0;i<height;i++){
  66.         for (int j=0;j<width;j++){
  67.             s=cvGet2D(img3d,i,j);            // s.val[0] = x, s.val[1] = y, s.val[2] = z
  68.             imgdata[i][j][0] = s.val[0];
  69.             imgdata[i][j][1] = s.val[1];
  70.             imgdata[i][j][2] = fabs(s.val[2]);
  71.         }
  72.     }
  73. }
  74. //////////////////////////////////////////////////////////////////////////
  75. // 载入左视图纹理数据
  76. void loadTextureToGL(IplImage* img){
  77.     //int ind=0;
  78.     CvScalar ss;
  79.     //accessing the image pixels
  80.     for (int i=0;i<height;i++){
  81.         for (int j=0;j<width;j++){
  82.             //OpenCV 是默认 BGR 格式存储彩色图像
  83.             ss=cvGet2D(img,i,j);            // ss.val[0] = blue, ss.val[1] = green, ss.val[2] = red
  84.             texture[i][j][2] = ss.val[0];    // OpenGL 则是 RGB 格式存储
  85.             texture[i][j][1] = ss.val[1];
  86.             texture[i][j][0] = ss.val[2];
  87.         }
  88.     }
  89. }

 

三、通过视差计算三维坐标数据

这部分主要以 OpenCV2.1版的 stereo_match 例程为基础,该例程包括三种双目匹配算法:STEREO_BM, STEREO_SGBM, STEREO_HH,其中 STEREO_HH 其实是 STEREO_SGBM 算法的状态参数中使能了 fullDP 。

  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. if( alg == STEREO_BM )
  8. {
  9.     bm(img1b, img2b, dispb);
  10.     // 截取与原始画面对应的视差区域(舍去加宽的部分)
  11.     displf = dispb.colRange(numberOfDisparities, img1b.cols);
  12. }
  13. else if(alg == STEREO_SGBM)
  14. {
  15.     sgbm(img1b, img2b, dispb);
  16.     displf = dispb.colRange(numberOfDisparities, img1b.cols);
  17. }

但是有两点需要注意:

(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. 00741         Mat disp_i = disp->rowRange(row0, row1);
  13. 00742         Mat cost_i = state->disp12MaxDiff >= 0 ? Mat(state->cost).rowRange(row0, row1) : Mat();
  14. 00751         if( state->disp12MaxDiff >= 0 )
  15. 00752             validateDisparity( disp_i, cost_i, state->minDisparity, state->numberOfDisparities, state->disp12MaxDiff );

这个 validateDisparity 函数是在 OpenCV2.1.0/src/cv/cvstereosgbm.cpp 中定义的,刚才说到的出错,源自以下代码的参数检查:

  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)用于检测离摄像头最近的物体:

  1. //////////////////////////////////////////////////////////////////////////
  2. // 双目匹配求解器状态初始化
  3. bm.state->roi1 = roi1;
  4. bm.state->roi2 = roi2;
  5. bm.state->preFilterCap = 31;
  6. bm.state->minDisparity = 0;
  7. bm.state->textureThreshold = 10;
  8. bm.state->speckleWindowSize = 100;
  9. bm.state->speckleRange = 32;
  10. bm.state->disp12MaxDiff = -1;
  11. sgbm.preFilterCap = 63;
  12. sgbm.minDisparity = 0;
  13. sgbm.speckleWindowSize = bm.state->speckleWindowSize;
  14. sgbm.speckleRange = bm.state->speckleRange;
  15. sgbm.disp12MaxDiff = -1;
  16. for(;;)
  17. {
  18.     //////////////////////////////////////////////////////////////////////////
  19.     // 求解器动态参数调整
  20.     bm.state->SADWindowSize = SADWindowSize;
  21.     bm.state->numberOfDisparities = numberOfDisparities;
  22.     bm.state->uniquenessRatio = uniqRatio;
  23.     sgbm.SADWindowSize = SADWindowSize;
  24.     sgbm.P1 = 8*cn*sgbm.SADWindowSize*sgbm.SADWindowSize;
  25.     sgbm.P2 = 32*cn*sgbm.SADWindowSize*sgbm.SADWindowSize;
  26.     sgbm.numberOfDisparities = numberOfDisparities;
  27.     sgbm.uniquenessRatio = uniqRatio;
  28.     sgbm.fullDP = fullDP;
  29.     …
  30.     …
  31.     //////////////////////////////////////////////////////////////////////////
  32.     // 检测离摄像头最近的物体
  33.     img1Ipl = img1c;
  34.     img2Ipl = img2c;
  35.     cvZero(bi_img);
  36.     if (stereo_rectify)
  37.     {
  38.         reprojectImageTo3D(displf, img3d, Q, true);
  39.         DoDetectNearObj( &img1Ipl, &img2Ipl, bi_img, img3d, displf, disp8, f1 );
  40.     }
  41.     …
  42.     …
  43. }

四、利用 OpenGL 和 OpenCV 来显示双目视觉三维重构效果

这里建立了两个 OpenCV 窗口来显示左右视图和视差数据、以及调整双目匹配参数和OpenGL视角参数的 TrackBar ,还有一个 OpenGL 窗口来显示三维重构的点云:

  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. //***OpenCV Window
  10. cvNamedWindow(“Stereo”);
  11. cvMoveWindow(“Stereo”, 470, 5);
  12. cvNamedWindow(“Parameters Adjustment”);
  13. cvMoveWindow(“Parameters Adjustment”, 10, 5);
  14. cvResizeWindow(“Parameters Adjustment”, 450, 350);
  15. cvCreateTrackbar( “Ndisp=n*16”“Parameters Adjustment”, &MaxDisp_beta, 15, onMaxdisp );
  16. cvCreateTrackbar( “Win=n*2-1”“Parameters Adjustment”, &SADWS_alpha, 11, onSADWinSiz );
  17. cvCreateTrackbar( “UniqRatio”“Parameters Adjustment”, &uniqRatio, 100, 0 );
  18. cvCreateTrackbar( “Threshold”“Parameters Adjustment”, &thresRatio, 100, 0 );
  19. cvCreateTrackbar( “EyeX=n-100”“Parameters Adjustment”, &eyex, 200, 0 );
  20. cvCreateTrackbar( “EyeZ=n-100”“Parameters Adjustment”, &eyez,200, 0 );
  21. cvCreateTrackbar( “AtX=n-100”“Parameters Adjustment”, &atx, 200, 0 );
  22. cvCreateTrackbar( “AtZ=n-100”“Parameters Adjustment”, &atz, 200, 0 );

 

OpenCV 窗口 “Stereo” 用于显示左右视图和视差数据,其中也包含了一些文字信息以显示所使用的算法和检测到的目标参数。

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

这里使用  FreeGlut 来显示 OpenGL 图像,有两点需要注意:

(1)在图像绘制的所有操作之后,要加入 glutPostRedisplay() 来重绘图像,否则在循环中图像只有响应鼠标或键盘消息时才会更新图像;

(2)由于大部分的按键和鼠标操作都来自 OpenCV 窗口,所以显示OpenGL图像的 glutMainLoopEvent() 函数应该放在 OpenCV 的 cvWaitKey 之后,否则 glutMainLoopEvent() 会影响 OpenCV 对按键、鼠标事件的响应。

  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. int c = cvWaitKey(10);
  13. if( (char) c == 27 )
  14.     break;
  15. switch( (char) c )
  16. {
  17. case ‘b’:
  18.     alg = STEREO_BM;
  19.     SADWindowSize = 15;
  20.     cvSetTrackbarPos(“Win=n*2-1”“Parameters Adjustment”, 8);
  21.     break;
  22. case ‘s’:
  23.     alg = STEREO_SGBM;
  24.     SADWindowSize = 7;
  25.     cvSetTrackbarPos(“Win=n*2-1”“Parameters Adjustment”, 4);
  26.     break;
  27. case ‘f’:
  28.     if (alg == STEREO_SGBM)
  29.         fullDP ^= 1;
  30.     break;
  31. case ‘r’:
  32.     stereo_rectify ^= 1;
  33.     break;
  34. case ‘a’:
  35.     adaptThresh ^= 1;
  36.     break;
  37. case ‘p’:
  38.     sprintf_s(img1name, “C://Stereo IO Data//lfFrame_%02d.jpg”, saveFrames);
  39.     sprintf_s(img2name, “C://Stereo IO Data//riFrame_%02d.jpg”, saveFrames);
  40.     sprintf_s(dispImgName, “C://Stereo IO Data//disparity_%02d.jpg”, saveFrames);
  41.     sprintf_s(dispDataName, “C://Stereo IO Data//disparity_%02d.txt”, saveFrames);
  42.     imwrite(img1name, img1r);
  43.     imwrite(img2name, img2r);
  44.     cvSaveImage(dispImgName, vdispRGB);
  45.     saveDisp(dispDataName, displf);
  46.     cout << “Save “ << saveFrames*2 << ” frames and “ << saveFrames << ” disparity image” << endl;
  47.     cout << endl;
  48.     saveFrames ++;
  49.     break;
  50. default:
  51.     ;
  52. }
  53. // OpenCV 处理键盘响应消息后,再显示 OpenGL 图像
  54. glutMainLoopEvent();

OK,本文到此就暂告一段落了,由于很多自己编写的功能函数还处于调试阶段,并且是属于实验室项目,就暂时不把所有代码发布出来了,以后会陆续把修改后的代码、以及有关摄像机标定、双目校正与匹配等方面的原理,以笔记的方式写到博客上和大家交流讨论。谢谢关注!

P.S. 有关双目视觉原理:

[1] 分享一些OpenCV实现立体视觉的经验(http://www.cvvision.cn/5366.html

[2] 双摄像头测距的OpenCV实现(http://www.cvvision.cn/2753.html

转载注明来源:CV视觉网 » OpenCV学习笔记(15)使用OpenGL显示双目视觉三维重构效果

分享到:更多 ()
扫描二维码,给作者 打赏
pay_weixinpay_weixin

请选择你看完该文章的感受:

0不错 0超赞 0无聊 0扯淡 0不解 0路过

评论 4

评论前必须登录!