当前位置:文档之家› 基于Kinect的三维重建

基于Kinect的三维重建

项目源码详见:/forum/viewtopic.php?f=1&t=13042前几天刚入手了期待已久的Kinect ,用于实验室机器人项目的视觉导航与环境理解。

首先要做的是破解-->连接PC-->获取深度数据和图像数据-->三维点云显示这么几项基本工作。

开始仿照的是饮水思源[1]博客的方法(使用VS2008在windows平台上试用Kinect[2]),利用CL-NUI-Platform 来破解,它的最新版是1.0.0.1210,但我在XP上用会当机,后来换 1.0.0.1121 版的就可以用了。

CL NUI 提供了十分简便易用的接口,在OpenCV 上调用很简单,另外它还提供了Kinect 底座马达的控制接口和LED 灯颜色的选择接口,其例程中可以操控Kinect 上下摆动。

如果只需要获取深度数据和图像数据,CL NUI 就够用了。

不过要做深入的应用,比如人体姿态识别、骨架提取、深度数据与图像数据的合并等等,就该用到OpenNI 了。

国内的CNKINECT[3]是个不错的Kinect 开发论坛,版块丰富,有很多资料可供借鉴。

我通过论坛介绍的方法[4]成功配置了OpenNI + Kinect,先是用最新版的OpenNI+SensorKinect+NITE ,但在XP 下不能正常运行,可能跟 .net 平台有关,老实按上面论坛的方法装就成功了。

另外用CMake + VS2008 装了最新的OpenCV_SVN,开始试过在CMake 里选择With TBB,但诡异的是TBB 似乎只适用于VS2005,在VS2008 编译后试用里面的samples 老是提示报错找不到msvcp80.dll,重新用CMake 配置取消了With TBB,就一切正常了。

[编辑]一、深度摄像机的视角调整与深度/彩色图像的合并通过研究OpenCV_SVN 与OpenNI 相关的代码(cap_openni.cpp)发现,OpenCV 目前只支持对Kinect的深度图、视差图和彩色/灰度图及相关属性的读取,更进一步的设置还没有实现。

参考台湾Heresy’space[5]的博客文章《透过OpneNI 合并Kinect 深度以及彩色影像资料》[6],想把深度图和彩色图合并显示,但是由于Kinect 的深度摄像机和彩色摄像机是在不同的位置,而且镜头本身的参数也不完全相同,所以两个摄像机所取得的画面会有些微的差异(如图1 左下角子图OpenGL三维点云显示窗口所示,天花板的两个日光灯对应深度图和彩色图的区域并没有重合,而是错位了)。

图1根据Heresy 的分析,需要对深度摄像机的视角进行修正,在OpenNI 下只需要一行代码就可以实现:// 6. correct view portmDepthGenerator.GetAlternativeViewPointCap().SetViewPoint ( mImageGenerator );不过在OpenCV 中并没有提供这一项设置,其源代码modules\highgui\src\cap_openni.cpp 中setDepthGeneratorProperty 并没有任何实质的属性设置。

为此,需要改写该函数,并且要在相应的头文件modules\highgui\include\opencv2\highgui\highgui_c.h 中添加新的参数id,具体如下:1.将cap_openni.cpp 第344 行的setDepthGeneratorProperty 改写如下: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: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_VIEW_POINT从而使得OpenCV 的VideoCapture 属性关于OpenNI 的如下所示:// OpenNI map generatorsCV_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 interfacesCV_CAP_PROP_OPENNI_OUTPUT_MODE =20,CV_CAP_PROP_OPENNI_FRAME_MAX_DEPTH =21,// in mmCV_CAP_PROP_OPENNI_BASELINE =22,// in mmCV_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_GENERATOR_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'''改写完上述源代码后保存,并且用CMake 和VS2008 重新编译一次OpenCV,我们就可以用OpenCV 的接口来控制Kinect 深度摄像头的视角,使其深度数据和图像数据能够很好的重合起来,如图2所示,注意与图1相比,右上角的伪彩色视差图四周出现了黑边,这就是视角调整后的效果:图2[编辑]二、三维点云坐标的计算在OpenCV 中实际上已经提供了三维点云坐标计算的接口,通过Kinect 的深度数据可以很快换算出三维点云坐标,cap_openni.cpp 中这部分代码具体如下:IplImage* CvCapture_OpenNI::retrievePointCloudMap(){int cols = depthMetaData.XRes(), rows =depthMetaData.YRes();if( cols <=0|| rows <=0)return0;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++){for(int x =0; x < cols; x++){unsigned short d = depth.at(y, x);// Check for invalid measurementsif(d ==CvCapture_OpenNI::INVALID_PIXEL_VAL)// not validcontinue;XnPoint3D proj, real;proj.X= x;proj.Y= y;proj.Z= d;depthGenerator.ConvertProjectiveToRealWorld(1,&proj,&real);pointCloud_XYZ.at(y,x)=cv::Point3f( real.X*0.001f, real.Y*0.001f, real.Z*0.001f); // from mm to meters}}outputMaps[CV_CAP_OPENNI_POINT_CLOUD_MAP].mat= pointCloud_XYZ;returnoutputMaps[CV_CAP_OPENNI_POINT_CLOUD_MAP].getIplImagePtr( );}不过可以看到上面核心的代码就一行:depthGenerator.ConvertProjectiveToRealWorld(1,&proj,&real);具体是怎样实现的呢?我们可以进一步找OpenNI 的源代码来分析。

不过这里我是从原来双目视觉的经验上自己编写代码来实现三维点云坐标的计算。

实际上Kinect 的深度摄像头成像也类似于普通的双目立体视觉,只要获取了两个摄像头之间的基线(baseline)和焦距(focal length)、以及视差数据,通过构造矩阵Q,利用OpenCV 的reprojectimageTo3D 函数,也可以计算出三维坐标。

相关主题