作者:马宁
Kinect SDK出来之后,不到24小时,很多Geek们已经将自己的示例发布到网上去了。可见,好东西肯定会被大家认可的,不好的东西投入再多的宣传也没用。
这一篇我们就要正式进入Kinect的编程世界了,介绍我们如何从Camera获取图像信息。先来介绍一下Kinect的整体结构,省得大家在后边的介绍中被某些名词弄晕。
Kinect一共有三个Camera,其中中间的一个是RGB Camera,用来获取640x480的彩色图像,每秒钟最多获取30帧图像;两侧是两个景深(3D Depth)传感器,用来检测玩家的相对位置,原理和人眼立体成像是一样的,不过这两个传感器使用的是红外线,所以说奥巴马玩不了Kinect的人一定是居心叵测。Kinect两侧是麦克风,下边还有一个可移动底座,用来调整Kinect的仰角。
Kinect开发环境
今天我们主要是操作RGB Camera和Depth Sensor,首先,我们要完成Kinect开发环境的配置:
第一步,创建WPF工程
打开Visual Studio 2010,创建一个WPF工程,名叫KinectWpfDemo:
当然,由于Kinect SDK中包含基于.NET的程序集,除了WPF外,我们使用.NET WinForm或XNA框架都可以,目前还没有人在Silverlight平台上实验成功。
第二步,添加Kinect程序集的引用
在Solution Explorer中,右键单击KinectWpfDemo,在右键菜单中选择“Add Reference…”。在弹出的对话框中,我们在.NET标签页里,选择“Microsoft.Research.Kinect”程序集。如下图所示:
第三步,添加Coding4Fun Kinect Toolkit
这是一个可选项,不过为了之后的编程方便,建议大家添加一个。Coding4Fun Kinect Toolkit的下载地址:
http://c4fkinect.codeplex.com/
解压缩后,一共有五个文件,针对WinForm、WPF平台,还有一个Microsoft.Expression.Drawing.dll。我们通过Add Reference,将Coding4Fun.Kinect.Wpf.dll添加进来。
获取RGB Camera数据
第四步,添加控件
双击打开MainWindow.xaml,在设计器中添加两个Image控件,一个用于显示RGB图像,另一个用于显示Depth信息。
第五步,引用命名空间
打开MainWindow.xaml.cs文件,在文件头部添加对于Kinect对象的引用:
using Microsoft.Research.Kinect.Nui; using Microsoft.Research.Kinect.Audio; using Coding4Fun.Kinect.Wpf;
回到MainWindow.xaml的设计器中,在属性窗口中选择Event,找到Loaded和Closed两个方法,分别双击,添加两个事件的处理函数:
在MainWindow.xaml.cs文件的MainWindow类中,声明Runtime的变量:
Runtime nui;
然后,在Loaded事件的处理函数中添加Runtime初始化的代码:
private void Window_Loaded(object sender, RoutedEventArgs e) { nui = new Runtime(); nui.Initialize(RuntimeOptions.UseColor| RuntimeOptions.UseDepth | RuntimeOptions.UseDepthAndPlayerIndex | RuntimeOptions.UseSkeletalTracking); }
接下来是Closed事件中关闭Runtime的代码:
private void Window_Closed(object sender, EventArgs e) { nui.Uninitialize(); }
Runtime对象是Kinect SDK中最主要的一个类,所有针对Kinect的操作都由Runtime类进行了封装。Runtime的构造函数没有接受任何参数,但有一个显式的初始化函数Initialize,接受RuntimeOptions参数,指定调用Kinect的哪些功能。其中RuntimeOptions.UseColor表示使用RGB Camera,而RuntimeOptions.UseDepth则表示使用Depth传感器。
初始化工作完成之后,我们要通过RGB Camera来获取实时的图像数据了。我们首先要声明一个事件处理方法,来接收视频数据的信息:
nui.VideoFrameReady += new EventHandler<ImageFrameReadyEventArgs>(nui_VideoFrameReady);
然后是事件处理函数:
void nui_VideoFrameReady(object sender, ImageFrameReadyEventArgs e) { PlanarImage imageData = e.ImageFrame.Image; image1.Source = BitmapSource.Create(imageData.Width, imageData.Height, 96, 96, PixelFormats.Bgr32, null, imageData.Bits, imageData.Width * imageData.BytesPerPixel); //image1.Source = e.ImageFrame.ToBitmapSource(); }
提示:Getting Started上提供的Sample Code有误,需要将最后一个参数中的data.Width改为imageData.Width才可以正常运行。
VideoFrameReady事件会传递一个ImageFrameReadyEventArgs参数给事件处理函数,其中的ImageFrame会包含关于图片的各种信息,比如Type变量指定了图像是来自RGB还是Depth,Resolution变量指定了分辨率,而Image中以byte[]数组的方式保存了图像的真实数据。
然后的工作就是根据PlanarImage中包括的数据来创建一个Bitmap对象,然后将其传递给Image控件,显示到WPF程序的界面上。
最后,我们还要在构造函数里打开视频流,来获取视频数据:
nui.VideoStream.Open(ImageStreamType.Video, 2, ImageResolution.Resolution640x480, ImageType.Color);
第一个参数是ImageStreamType,用来指定打开的设备流类型;第二个参数是PoolSize,指定缓冲区的数量,至少为2,保证一个Buffer进行绘制,另一个Buffer进行数据填充;第三个参数指定Camera的分辨率;第四个参数则是获取的图片类型。
显示效果如下图所示:
上面的示例代码,没有使用Coding4Fun的Helper类,如果使用的话,则代码如下:
void nui_VideoFrameReady(object sender, ImageFrameReadyEventArgs e) { image1.Source = e.ImageFrame.ToBitmapSource(); e.ImageFrame.ToBitmapSource().Save("catpure.jpg", ImageFormat.Jpeg); }
Helper类使用了C#的Extension Methods,为ImageFrame增加了一些转换方法。我们还可以将图像保存为文件,考虑到文件系统存储的效率文件,建议大家不用每张都存。
获取Depth信息
接下来我们要获取Depth信息了,过程与RGB Camera类似。首先要确保Runtime对象被初始化时,已经添加了RuntimeOptions.UseDepth的属性,否则设备无法正常打开。
然后,添加获取Depth数据的事件处理,并打开Depth的数据流,这次的分辨率是320x240:
nui.DepthFrameReady += new EventHandler<ImageFrameReadyEventArgs>(nui_DepthFrameReady);
nui.DepthStream.Open(ImageStreamType.Depth, 2, ImageResolution.Resolution320x240, ImageType.Depth);
下面是事件处理函数,在另外一个Image函数里,显示Depth图像:
void nui_DepthFrameReady(object sender, ImageFrameReadyEventArgs e) { image2.Source = e.ImageFrame.ToBitmapSource(); }
偷懒,所以使用了Coding4Fun的Helper类。程序运行的效果如下:
写到最后
这一篇中,我们完成了Kinect开发环境的配置、添加了Coding4Fun Kinect Toolkit、从RGB Camera和Depth Sensor中获取了图像信息。
接下来,我们就要进入Kinect动作捕捉部分了。