细心的朋友肯定已经发现Kinect上长了三只眼睛,其中一个是彩色摄像头,另外两个深度摄像头,一个负责发射红外光,一个负责接收,这样,我们便能通过Kinect得到一幅彩色图像和一幅深度图像。如果大家对Kniect眼睛的技术细节感兴趣,请点击这里。
小斤的第一个范例,便是通过OpenNI得到彩色和深度图像咯,代码不长,其中一部分参考了Heresky童鞋的文章《透過 OpneNI 合併 Kinect 深度以及彩色影像資料》,此外,小斤补充了OpenCV的显示部分,使范例更为直观。
- #include <stdlib.h>
- #include <iostream>
- #include <string>
- //【1】
- #include <XnCppWrapper.h>
- #include "opencv/cv.h"
- #include "opencv/highgui.h"
- using namespace std;
- using namespace cv;
- void CheckOpenNIError( XnStatus result, string status )
- {
- if( result != XN_STATUS_OK )
- cerr << status << " Error: " << xnGetStatusString( result ) << endl;
- }
- int main( int argc, char** argv )
- {
- XnStatus result = XN_STATUS_OK;
- xn::DepthMetaData depthMD;
- xn::ImageMetaData imageMD;
- //OpenCV
- IplImage* imgDepth16u=cvCreateImage(cvSize(640,480),IPL_DEPTH_16U,1);
- IplImage* imgRGB8u=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,3);
- IplImage* depthShow=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,1);
- IplImage* imageShow=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,3);
- cvNamedWindow("depth",1);
- cvNamedWindow("image",1);
- char key=0;
- //【2】
- // context
- xn::Context context;
- result = context.Init();
- CheckOpenNIError( result, "initialize context" );
- // creategenerator
- xn::DepthGenerator depthGenerator;
- result = depthGenerator.Create( context );
- CheckOpenNIError( result, "Create depth generator" );
- xn::ImageGenerator imageGenerator;
- result = imageGenerator.Create( context );
- CheckOpenNIError( result, "Create image generator" );
- //【3】
- //map mode
- XnMapOutputMode mapMode;
- mapMode.nXRes = 640;
- mapMode.nYRes = 480;
- mapMode.nFPS = 30;
- result = depthGenerator.SetMapOutputMode( mapMode );
- result = imageGenerator.SetMapOutputMode( mapMode );
- //【4】
- // correct view port
- depthGenerator.GetAlternativeViewPointCap().SetViewPoint( imageGenerator );
- //【5】
- //read data
- result = context.StartGeneratingAll();
- //【6】
- result = context.WaitNoneUpdateAll();
- while( (key!=27) && !(result = context.WaitNoneUpdateAll( )) )
- {
- //get meta data
- depthGenerator.GetMetaData(depthMD);
- imageGenerator.GetMetaData(imageMD);
- //【7】
- //OpenCV output
- memcpy(imgDepth16u->imageData,depthMD.Data(),640*480*2);
- cvConvertScale(imgDepth16u,depthShow,255/4096.0,0);
- memcpy(imgRGB8u->imageData,imageMD.Data(),640*480*3);
- cvCvtColor(imgRGB8u,imageShow,CV_RGB2BGR);
- cvShowImage("depth", depthShow);
- cvShowImage("image",imageShow);
- key=cvWaitKey(20);
- }
- //destroy
- cvDestroyWindow("depth");
- cvDestroyWindow("image");
- cvReleaseImage(&imgDepth16u);
- cvReleaseImage(&imgRGB8u);
- cvReleaseImage(&depthShow);
- cvReleaseImage(&imageShow);
- context.StopGeneratingAll();
- context.Shutdown();
- return 0;
- }
这边小斤由上到下解释一把:
【1】<XnCppWrapper.h>便是OpenNI的文件头了,使用OpenNI的话,目前只要include这个就行。
【2】DepthGenerator和ImageGenerator,小斤称之为图像生成器,前者负责深度图像,后者负责彩色图像。创建一个生成器非常简单,首先我们要初始化一个Context上下文,然后把Context作为Create函数的参数,便可以创建生成器了。
【3】XnMapOutputMode是用来设定生成器的参数的,这边小斤设定了分辨率为640*480(标准),30fps采样。
【4】depthGenerator.GetAlternativeViewPointCap().SetViewPoint( imageGenerator)这句话也许会让大家疑惑,它是用来调整视角的。为什么要调整呢?因为Kinect的三只眼长在不同的地方,所以画幅一致的深度摄像头和彩色摄像头,它们看出来的景物是有偏差的,这里OpenNI提供了函数进行对齐。这里,小斤把深度生成器的视角,设定为彩色生成器的视角。
【5】调用StartGeneratingAll()后,生成器们便开始上班了,如果要结束,就StopGeneratingAll()函数。
【6】尽管生成器们在工作了,但他们一直忙着各读各的,没有人协调,自己不会乖乖把最新的资料给我们。我们调用getMetaData()方法前,需要使用WaitAnyUpdateAll()、WaitOneUpdateAll()、WaitNoneUpdateAll()和WiatAndUpdateAll()中的一种。功能如其名,这边小斤使用的是WaitNoneUpdateAll()函数,它比较暴力,不管生成器有没有读到新数据,我这边先更新了再说。大家可以试试其它三个,看看效果。
【7】这边使用OpenNI获得图像MetaData数据后,小斤通过一系列函数,转换为OpenCV的IplImage图像类型,然后输出。主要参考了这篇文章。
对于深度MetaData,这边使用cvConvertScale转换尺度,成为灰度值[0,255]的灰度图。对于彩色MetaData,使用cvCvtColor转换色彩空间即可。按ESC键可以退出循环,结束程序。
最终效果如下:
在灰度图中灰度值0显示为黑色,255为白色,所以,离Kinect近的地方(桌面)显示为黑色,中间为不同程度的灰色,远处(天花板)显示为白色。