版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、基于Kinect-OpenNI-OpenCV-OpenGL的环境三维重构Wikipedia,自由的百科全书项目源码详见: 前几天刚入手了期待已久的 Kinect ,用于实验室机器人项目的视觉导航与环境理解。 首先要做的是破解-连接PC-获取深度数据和图像数据-三维点云显示 这么几项基本工作。 开始仿照的是 饮水思源1 博客的方法(使用VS2008在windows平台上试用Kinect2 ),利用 CL-NUI-Platform 来破解,它的最新版是.1210,但我在XP上用会当机,后来换 121 版的就可以用了。CL NUI 提供了十分简便易用的接口,在OpenCV 上调用很简单
2、,另外它还提供了 Kinect 底座马达的控制接口和 LED 灯颜色的选择接口,其例程中可以操控 Kinect 上下摆动。如果只需要获取深度数据和图像数据,CL NUI 就够用了。不过要做深入的应用,比如人体姿态识别、骨架提取、深度数据与图像数据的合并等等,就该用到 OpenNI 了。 国内的 CNKINECT3 是个不错的 Kinect 开发论坛,版块丰富,有很多资料可供借鉴。我通过论坛介绍的 方法4 成功配置了 OpenNI + Kinect,先是用最新版的 OpenNI+SensorKinect+NITE ,但在 XP 下不能正常运行,可能跟 .net 平台有关,老实按上面论坛的方法装就
3、成功了。另外用 CMake + VS2008 装了最新的 OpenCV_SVN,开始试过在 CMake 里选择 With TBB,但诡异的是 TBB 似乎只适用于VS2005,在 VS2008 编译后试用里面的 samples 老是提示报错找不到msvcp80.dll,重新用 CMake 配置取消了 With TBB,就一切正常了。 编辑一、深度摄像机的视角调整 与 深度/彩色图像的合并通过研究 OpenCV_SVN 与 OpenNI 相关的代码(cap_openni.cpp)发现,OpenCV 目前只支持对Kinect的深度图、视差图和彩色/灰度图及相关属性的读取,更进一步的设置还没有实现。
4、参考台湾 Heresyspace5 的博客文章透过 OpneNI 合并 Kinect 深度以及彩色影像资料6,想把深度图和彩色图合并显示,但是由于 Kinect 的深度摄像机和彩色摄像机是在不同的位置,而且镜头本身的参数也不完全相同,所以两个摄像机所取得的画面会有些微的差异(如图1 左下角子图OpenGL三维点云显示窗口所示,天花板的两个日光灯对应深度图和彩色图的区域并没有重合,而是错位了)。图1 根据 Heresy 的分析,需要对深度摄像机的视角进行修正,在 OpenNI 下只需要一行代码就可以实现: / 6. correct view port mDepthGenerator.GetAlt
5、ernativeViewPointCap().SetViewPoint( mImageGenerator );不过在 OpenCV 中并没有提供这一项设置,其源代码 moduleshighguisrccap_openni.cpp 中 setDepthGeneratorProperty 并没有任何实质的属性设置。为此,需要改写该函数,并且要在相应的头文件 moduleshighguiincludeopencv2highguihighgui_c.h 中添加新的参数 id,具体如下: 1. 将 cap_openni.cpp 第 344 行的 setDepthGeneratorProperty 改写如
6、下: bool CvCapture_OpenNI:setDepthGeneratorProperty( int propIdx, double propValue ) bool res = false; CV_Assert( depthGenerator.IsValid() ); switch( propIdx ) case CV_CAP_PROP_OPENNI_VIEW_POINT : depthGenerator.GetAlternativeViewPointCap().SetViewPoint( imageGenerator ); res = true; break; default :
7、 CV_Error( CV_StsBadArg, Depth generator does not support such parameter for setting.n); res = false; return res; 1. 在 highgui_c.h 的第 348 行下添加改变视角的参数 ID 号: CV_CAP_PROP_OPENNI_VIEW_POINT = 24,然后在第 352 行下添加: CV_CAP_OPENNI_DEPTH_GENERATOR_VIEW_POINT = CV_CAP_OPENNI_DEPTH_GENERATOR + CV_CAP_PROP_OPENNI_
8、VIEW_POINT从而使得 OpenCV 的 VideoCapture 属性关于 OpenNI 的如下所示: / OpenNI map generators CV_CAP_OPENNI_DEPTH_GENERATOR = 0, CV_CAP_OPENNI_IMAGE_GENERATOR = 1 31, CV_CAP_OPENNI_GENERATORS_MASK = 1 31, / Properties of cameras avalible through OpenNI interfaces CV_CAP_PROP_OPENNI_OUTPUT_MODE = 20, CV_CAP_PROP_O
9、PENNI_FRAME_MAX_DEPTH = 21, / in mm CV_CAP_PROP_OPENNI_BASELINE = 22, / in mm CV_CAP_PROP_OPENNI_FOCAL_LENGTH = 23, / in pixels CV_CAP_PROP_OPENNI_VIEW_POINT = 24, CV_CAP_OPENNI_IMAGE_GENERATOR_OUTPUT_MODE = CV_CAP_OPENNI_IMAGE_GENERATOR + CV_CAP_PROP_OPENNI_OUTPUT_MODE, CV_CAP_OPENNI_DEPTH_GENERATO
10、R_BASELINE = CV_CAP_OPENNI_DEPTH_GENERATOR + CV_CAP_PROP_OPENNI_BASELINE, CV_CAP_OPENNI_DEPTH_GENERATOR_FOCAL_LENGTH = CV_CAP_OPENNI_DEPTH_GENERATOR + CV_CAP_PROP_OPENNI_FOCAL_LENGTH, CV_CAP_OPENNI_DEPTH_GENERATOR_VIEW_POINT = CV_CAP_OPENNI_DEPTH_GENERATOR + CV_CAP_PROP_OPENNI_VIEW_POINT改写完上述源代码后保存,
11、并且用 CMake 和 VS2008 重新编译一次 OpenCV,我们就可以用 OpenCV 的接口来控制 Kinect 深度摄像头的视角,使其深度数据和图像数据能够很好的重合起来,如图2所示,注意与图1相比,右上角的伪彩色视差图四周出现了黑边,这就是视角调整后的效果:图2 编辑二、三维点云坐标的计算在 OpenCV 中实际上已经提供了三维点云坐标计算的接口,通过 Kinect 的深度数据可以很快换算出三维点云坐标,cap_openni.cpp 中这部分代码具体如下: IplImage* CvCapture_OpenNI:retrievePointCloudMap() int cols = d
12、epthMetaData.XRes(), rows = depthMetaData.YRes(); if( cols = 0 | rows = 0 ) return 0; cv:Mat depth; getDepthMapFromMetaData( depthMetaData, depth, noSampleValue, shadowValue ); const float badPoint = 0; cv:Mat pointCloud_XYZ( rows, cols, CV_32FC3, cv:Scalar:all(badPoint) ); for( int y = 0; y rows; y
13、+ ) for( int x = 0; x cols; x+ ) unsigned short d = depth.at(y, x); / Check for invalid measurements if( d = CvCapture_OpenNI:INVALID_PIXEL_VAL ) / not valid continue; XnPoint3D proj, real; proj.X = x; proj.Y = y; proj.Z = d; depthGenerator.ConvertProjectiveToRealWorld(1, &proj, &real); pointCloud_X
14、YZ.at(y,x) = cv:Point3f( real.X*0.001f, real.Y*0.001f, real.Z*0.001f); / from mm to meters outputMapsCV_CAP_OPENNI_POINT_CLOUD_MAP.mat = pointCloud_XYZ; return outputMapsCV_CAP_OPENNI_POINT_CLOUD_MAP.getIplImagePtr(); 不过可以看到上面核心的代码就一行: depthGenerator.ConvertProjectiveToRealWorld(1, &proj, &real);具体是
15、怎样实现的呢?我们可以进一步找 OpenNI 的源代码来分析。不过这里我是从原来双目视觉的经验上自己编写代码来实现三维点云坐标的计算。实际上 Kinect 的深度摄像头成像也类似于普通的双目立体视觉,只要获取了两个摄像头之间的基线(baseline)和焦距(focal length)、以及视差数据,通过构造矩阵 Q,利用 OpenCV 的 reprojectimageTo3D 函数,也可以计算出三维坐标。 下面我们通过以下代码看看矩阵 Q 的构造过程: capture.set(CV_CAP_OPENNI_DEPTH_GENERATOR_VIEW_POINT, 1.0); / 调整深度摄像头视角
16、 double b = capture.get( CV_CAP_OPENNI_DEPTH_GENERATOR_BASELINE ); / mm double F = capture.get( CV_CAP_OPENNI_DEPTH_GENERATOR_FOCAL_LENGTH ); / pixels double q = 1, 0, 0, -320.0, 0, 1, 0, -240.0, 0, 0, 0, F, 0, 0, 1./b, 0 ; Mat matQ(4, 4, CV_64F, q);而三维点云坐标的计算,以及深度、视差、彩色图像的读取,则如下所示: / 抓取数据 if( !capt
17、ure.grab() ) cout Can not grab images. endl; return -1; / 读取深度数据 capture.retrieve( depthMap, CV_CAP_OPENNI_DEPTH_MAP ); minMaxLoc( depthMap, &mindepth, &maxdepth ); / 读取视差数据 capture.retrieve( disparityMap, CV_CAP_OPENNI_DISPARITY_MAP_32F ); colorizeDisparity( disparityMap, colorDisparityMap, maxDisp
18、arity ); colorDisparityMap.copyTo( validColorDisparityMap, disparityMap != 0 ); imshow( colorized disparity map, validColorDisparityMap ); / 读取彩色图像数据 capture.retrieve( bgrImage, CV_CAP_OPENNI_BGR_IMAGE ); imshow( Color image, bgrImage ); / 利用视差数据计算三维点云坐标 cv:reprojectImageTo3D(disparityMap, xyzMap, m
19、atQ, true);这里值得注意的是,在计算视差时,如果视差图是采用默认的 CV_8UC1 格式(参数ID是CV_CAP_OPENNI_DISPARITY_MAP),由于视差被限制在0-255整数范围内,造成一定的误差,所得到的三维坐标数据会出现分层,如图3和图4所示:图3 Matlab绘制的三维点云,不同深度上有明显分层 图4 左为直接得到的深度数据Mesh图,中为由8位视差数据计算得到的深度数据,右为对应的视差数据 而采用 32 位浮点格式来获取视差数据(参数ID:CV_CAP_OPENNI_DISPARITY_MAP_32F),则消除了这种因视差误差造成深度数值分层的现象,如图5、6所
20、示:图5 深度数值的分层现象基本消除 图6 两种方式得到的深度数据一致 编辑三、利用OpenGL显示三维点云数据这方面主要是基于 学习笔记(15) 7的内容,不过当时是通过另设 OpenCV 窗口设置 Trackbar 来调整 OpenGL 的视点位置和摄像机位置。在这里则主要参考了 OpenCV 论坛的帖子HQ OpenCV和OpenGL编程:关于显示点云数据-Stereo Vision源码分享8中 villager5 提供的方法来调整摄像机位置,做了一定的修改,使得鼠标左键拖曳能够实现对上下左右的视角变换,鼠标右键拖曳能够实现视距远近的变换,不再需要用 Trackbar 来调整。下面是相关
21、的代码: #define SIGN(x) ( (x)0?1:0 ) )/ /-OpenGL 全局变量float xyzdata4806403;float texture4806403;int glWinWidth = 640, glWinHeight = 480;int width=640, height=480;double eyex, eyey, eyez, atx, aty, atz; / eye* - 摄像机位置,at* - 注视点位置bool leftClickHold = false, rightClickHold = false;int mx,my; / 鼠标按键时在 OpenG
22、L 窗口的坐标int ry = 90, rx = 90; / 摄像机相对注视点的观察角度double mindepth, maxdepth;/ 深度数据的极值 double radius = 6000.0;/ 摄像机与注视点的距离/*/* OpenGL响应函数 */*/ 鼠标按键响应函数void mouse(int button, int state, int x, int y)if(button = GLUT_LEFT_BUTTON)if(state = GLUT_DOWN)leftClickHold=true;elseleftClickHold=false;if (button= GLUT
23、_RIGHT_BUTTON)if(state = GLUT_DOWN)rightClickHold=true;elserightClickHold=false;/ 鼠标运动响应函数void motion(int x, int y)int rstep = 5; if(leftClickHold=true)if( abs(x-mx) abs(y-my) )rx += SIGN(x-mx)*rstep; elsery -= SIGN(y-my)*rstep; mx=x;my=y;glutPostRedisplay();if(rightClickHold=true)radius += SIGN(y-m
24、y)*100.0;radius = std:max( radius, 100.0 );mx=x;my=y;glutPostRedisplay();/ 三维图像显示响应函数void renderScene(void) / clear screen and depth bufferglClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );/ Reset the coordinate system before modifying glLoadIdentity();/ set the camera positionatx = 0.0f;aty = 0
25、.0f;atz = ( mindepth - maxdepth ) / 2.0f;eyex = atx + radius * sin( CV_PI * ry / 180.0f ) * cos( CV_PI * rx/ 180.0f ); eyey = aty + radius * cos( CV_PI * ry/ 180.0f ); eyez = atz + radius * sin( CV_PI * ry / 180.0f ) * sin( CV_PI * rx/ 180.0f );gluLookAt (eyex, eyey, eyez, atx, aty, atz, 0.0, 1.0, 0
26、.0);glRotatef(0,0,1,0);glRotatef(-180,1,0,0);/ 对点云数据进行三角化/ we are going to loop through all of our terrains data points,/ but we only want to draw one triangle strip for each set along the x-axis.for (int i = 0; i height; i+)glBegin(GL_TRIANGLE_STRIP);for (int j = 0; j 1 / / / 2 - 3*/ draw vertex 0g
27、lTexCoord2f(0.0f, 0.0f);glColor3f(textureij0/255.0f, textureij1/255.0f, textureij2/255.0f);glVertex3f(xyzdataij0, xyzdataij1, xyzdataij2);/ draw vertex 1glTexCoord2f(1.0f, 0.0f);glColor3f(texturei+1j0/255.0f, texturei+1j1/255.0f, texturei+1j2/255.0f);glVertex3f(xyzdatai+1j0, xyzdatai+1j1, xyzdatai+1
28、j2);/ draw vertex 2glTexCoord2f(0.0f, 1.0f);glColor3f(textureij+10/255.0f, textureij+11/255.0f, textureij+12/255.0f);glVertex3f(xyzdataij+10, xyzdataij+11, xyzdataij+12);/ draw vertex 3glTexCoord2f(1.0f, 1.0f);glColor3f(texturei+1j+10/255.0f, texturei+1j+11/255.0f, texturei+1j+12/255.0f);glVertex3f(
29、xyzdatai+1j+10, xyzdatai+1j+11, xyzdatai+1j+12);glEnd();/ enable blendingglEnable(GL_BLEND);/ enable read-only depth bufferglDepthMask(GL_FALSE);/ set the blend function to what we use for transparencyglBlendFunc(GL_SRC_ALPHA, GL_ONE);/ set back to normal depth buffer mode (writable)glDepthMask(GL_TRUE);/ disable blendingglDisable(GL_BLEND);/* float x,y,z;/ 绘制图像点云glPointSize(1.0); glBegin(GL_POINTS);for (int i
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年中职(汽车维修技术)发动机维修试题及答案
- 2025年高职机械制造及自动化(数控加工工艺)试题及答案
- 2025年大学化学(有机化学)试题及答案
- 2025年中职(乐器修造)乐器维修基础试题及答案
- 2025年中职计算机与网络技术(网络故障排除)试题及答案
- 2025年中职安全(规避技巧)试题及答案
- 2026年棒球用品营销(营销规范)试题及答案
- 2025年中职畜牧兽医(常见疾病防治)试题及答案
- 2025年大学休闲体育服务与管理(健身课程设计)试题及答案
- 2025年中职(铁道运输服务)铁路货运组织试题及答案
- 2026云南省产品质量监督检验研究院招聘编制外人员2人考试参考试题及答案解析
- 泥浆护壁成孔灌注桩施工操作规程
- 舞台灯光效果课件
- 艺术史课件教学课件
- ARDS患者肺保护性机械通气方案
- 2025-2026学年北师大版二年级上册数学期末试卷及答案(三套)
- 2026年吉林工程职业学院单招职业技能考试必刷测试卷必考题
- 2025年中国泥炭生物肥项目创业投资方案
- 浙江省金华市2024-2025学年九年级上学期期末科学试题(学生版)
- 教育部人文社科一般课题申报书
- 串联谐振耐压试验原理讲解
评论
0/150
提交评论