马宁的嵌入式开发研究

Windows Phone, XNA, Windows Embedded, Windows Mobile
posts - 80, comments - 676, trackbacks - 17, articles - 0

作者:马宁

我们的Kinect SDK开发开始渐入佳境了,Skeleton Tracking(骨骼追踪)是Kinect的核心技术,正因为有了这项技术,很多有趣的功能才得以实现。

首先,我们来看一下骨骼追踪的具体实现。Kinect最多可以追踪20个骨骼点,而且目前只能追踪人体,其他的物体或者动物就无能为力了。下图介绍了Kinect骨骼点的分布情况:

7

初始化代码

接下来,我们来看一下骨骼追踪的代码是如何编写的。首先,我们要创建一个新的Visual C#的工程,叫做“SkeletonTracking”,添加Kinect程序集和Coding4Fun程序集的工作,可以参考上一篇“Kinect for Windows SDK开发初体验(二)操作Camera”的内容,在这里不再重复。

首先我们还是创建Runtime对象,在初始化时,指定UseSkeletalTracking的RuntimeOptions,然后在SkeletonFrameReady事件中添加处理函数。

Runtime nui; 

private void Window_Loaded(object sender, RoutedEventArgs e)

{

nui = new Runtime();

nui.Initialize(RuntimeOptions.UseSkeletalTracking); 

nui.SkeletonFrameReady += new EventHandler<SkeletonFrameReadyEventArgs>(nui_SkeletonFrameReady);

}

接下来,窗体关闭时的事件处理函数:

private void Window_Closed(object sender, EventArgs e)

{

nui.Uninitialize();

}

如果这个时候,我们在空的nui_SkeletonFrameReady事件处理函数中,添加一个断点:

void nui_SkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e) 

{

}

正确连接Kinect设备,并且站在Kinect前,让Kinect能够正确识别人体时,SkeletonFrameReady事件将被触发。

我们可以通过下图看到返回的事件处理参数,其中比较重要的是SkeletonFrame和Skeletons两个对象。

image[11]

添加代码

接下来,我们要准备WPF的界面,在界面上添加五个小球,来跟踪头部、双手和膝盖的位置。在MainPage.xaml中,添加下列代码:

<Canvas Name="MainCanvas">
<Ellipse Canvas.Left="0" Canvas.Top="0" Height="50" Name="headEllipse" Stroke="Black" Width="50" Fill="Orange" />
<Ellipse Canvas.Left="50" Canvas.Top="0" Height="50" Name="rightEllipse" Stroke="Black" Width="50" Fill="SlateGray" />
<Ellipse Canvas.Left="100" Canvas.Top="0" Fill="SpringGreen" Height="50" Name="leftEllipse" Stroke="Black" Width="50" />
<Ellipse Canvas.Left="150" Canvas.Top="0" Height="50" Name="KneeRightEllipse" Stroke="Black" Width="50" Fill="Salmon" />
<Ellipse Canvas.Left="200" Canvas.Top="0" Fill="Navy" Height="50" Name="KneeLeftEllipse" Stroke="Black" Width="50" />
</Canvas>

然后,我们在SkeletonFrameReady事件处理函数中添加捕捉SkeletonData的方法:

void nui_SkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e) 

{

SkeletonFrame allSkeletons = e.SkeletonFrame;

//get the first tracked skeleton

SkeletonData skeleton = (from s in allSkeletons.Skeletons

where s.TrackingState == SkeletonTrackingState.Tracked

select s).FirstOrDefault();

}

我们使用了LINQ来获取TrackingState等于Tracked的SkeletonData数据。在SkeletonData对象的Joints属性集合中保存了所有骨骼点的信息。每个骨骼点的信息都是一个Joint对象,其中的Position的X、Y、Z表示了三维位置。其中X和Y的范围都是-1到1,而Z是Kinect到识别物体的距离。

我们可以用下面的代码,将Joint的位置缩放到合适的比例:

Joint j = skeleton.Joints[JointID.HandRight].ScaleTo(640, 480, .5f, .5f);

最后两个参数为原始大小的最大值和最小值,上面的语句相当于将-0.5到0.5的范围扩大为0到640的范围。

我们封装了一个函数,将获取到的SkeletonData数据,转换为屏幕上的某一个圆圈:

private void SetEllipsePosition(FrameworkElement ellipse, Joint joint) 

{ 

var scaledJoint = joint.ScaleTo(640, 480, .5f, .5f);

Canvas.SetLeft(ellipse, scaledJoint.Position.X);

Canvas.SetTop(ellipse, scaledJoint.Position.Y); 

}

最后,我们SkeletonFrameReady事件的处理方法会是这样的:

void nui_SkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e) 

{

SkeletonFrame allSkeletons = e.SkeletonFrame;

//get the first tracked skeleton

SkeletonData skeleton = (from s in allSkeletons.Skeletons

where s.TrackingState == SkeletonTrackingState.Tracked

select s).FirstOrDefault();

SetEllipsePosition(headEllipse, skeleton.Joints[JointID.Head]); 

SetEllipsePosition(leftEllipse, skeleton.Joints[JointID.HandLeft]); 

SetEllipsePosition(rightEllipse, skeleton.Joints[JointID.HandRight]);

SetEllipsePosition(KneeLeftEllipse, skeleton.Joints[JointID.KneeLeft]);

SetEllipsePosition(KneeRightEllipse, skeleton.Joints[JointID.KneeRight]);

}

最后,程序运行的效果如下,貌似膝盖的识别还是有些问题:

30

程序运行时,我们会发现小球运动时会有跳动的问题,为了减少这种情况,我们要设置SkeletonEngine引擎的TransformSmooth属性为true,并指定TransformSmoothParameters参数,根据应用的具体情况,该参数也应该被适当微调。

添加代码后的Load函数,代码如下:

private void Window_Loaded(object sender, RoutedEventArgs e)

{

nui = new Runtime();

nui.Initialize(RuntimeOptions.UseSkeletalTracking); 

//Must set to true and set after call to Initialize

nui.SkeletonEngine.TransformSmooth = true;

//Use to transform and reduce jitter

var parameters = new TransformSmoothParameters

{

Smoothing = 0.75f,

Correction = 0.0f,

Prediction = 0.0f,

JitterRadius = 0.05f,

MaxDeviationRadius = 0.04f

};

nui.SkeletonEngine.SmoothParameters = parameters;

nui.SkeletonFrameReady += new EventHandler<SkeletonFrameReadyEventArgs>(nui_SkeletonFrameReady);

}

写到最后

到这里, Kinect最精华的部分“骨骼追踪”已经介绍给大家了,大家可以去写一些有趣的应用了。接下来,我们会介绍另外一个Kinect的核心功能——Depth Data,景深数据。

 

OpenXLive杯Windows Phone游戏开发大赛

clip_image019

OpenXLive杯Windows Phone游戏开发大赛,是由OpenXLive联合国内知名的开发者社区:DevDiv、智机网、WPMind、Silverlight银光中国和XNA游戏世界,一起举办的针对Windows Phone游戏开发的比赛。

http://www.openxlive.net/posts/news/40

Feedback

#1楼  回复 引用 查看   

2011-06-21 16:44 by magicDict      
那个东西多少钱啊,对系统要求高吗?

#2楼  回复 引用 查看   

2011-06-21 16:58 by kklldog      
这个要顶

#3楼  回复 引用 查看   

2011-06-21 17:03 by gardensu      
马工,后续会有“景深图像”方面的讲座吗?

#4楼[楼主]  回复 引用 查看   

2011-06-21 17:07 by 马宁      
@magicDict
现在淘宝上好像是七八百,具体可以我上一篇文章的回复。Kinect的动作捕捉都是在硬件里做的,所以PC端只是一个接收的功能,不需要太高的系统配置。

#5楼  回复 引用 查看   

2011-06-21 17:15 by gardensu      
不好意思没有仔细阅读,下一讲就是”景深数据“。又,希望能用Kinect模拟触摸屏,鼠标,来操作Windows8。

#6楼  回复 引用 查看   

2011-06-21 17:17 by helloworld2      
调试比较麻烦,需人肉,我也在玩了

#7楼  回复 引用 查看   

2011-06-21 17:28 by aspnetx      
羡慕啊,我kinect都有了,就是本子Thinkpad R60E无法跑SDK.

#8楼  回复 引用 查看   

2011-06-21 18:41 by pandaren      
Windows Phone 系列看来改这个了... 0_0

#9楼  回复 引用 查看   

2011-06-21 20:52 by 新的开始      
Kinect同PC通过什么接口相连呢?

#10楼[楼主]  回复 引用 查看   

2011-06-21 21:04 by 马宁      
@helloworld2
人肉……俺们都是用自己来测的。

#11楼[楼主]  回复 引用 查看   

2011-06-21 21:05 by 马宁      
@pandaren
Windows Phone本周暂停,先玩Kinect,这个好玩。
反正Windows Phone一周以内也赶不上iPhone,不急。

#12楼  回复 引用 查看   

2011-06-22 00:16 by Tony Zhou      
引用马宁:
@helloworld2
人肉……俺们都是用自己来测的。

是啊,有人帮忙我做动作就好了。一个人太累了。

#13楼  回复 引用 查看   

2011-06-22 09:33 by Leepy      
调试需要“结对编程”吧……

#14楼  回复 引用 查看   

2011-06-22 10:54 by Rain.      
@aspnetx
引用aspnetx:羡慕啊,我kinect都有了,就是本子Thinkpad R60E无法跑SDK.

R60也太老了点吧。
大哥,你该换笔记本了。目前的T420貌似不错。

#15楼  回复 引用 查看   

2011-06-22 13:25 by debug_fan      
引用马宁:
@pandaren
Windows Phone本周暂停,先玩Kinect,这个好玩。
反正Windows Phone一周以内也赶不上iPhone,不急。


这个...呵呵

#16楼  回复 引用 查看   

2011-06-22 13:31 by 偶卖糕的      
能代替鼠标就好了,不过应该没那么精确

#17楼  回复 引用 查看   

2011-06-22 15:13 by YTYT2002YTYT      
你好,请问直接去购买XBOX+Kinect套装,把Kinect连到PC上就可以开发么? XBOX+Kinect一起买,Kinect折价900左右吧

#18楼  回复 引用 查看   

2011-06-22 15:34 by lovegq      
你好,请问kinect对手的精确度如何?能不能精确到每根手指?我希望能做到那种隔空双手打字的效果.
感觉 kinect 好像是针对全身骨骼的,不知道对手指头的灵敏度如何??毕竟1000块大洋,不敢随便试的,望楼主给说说。

#19楼  回复 引用 查看   

2011-06-22 16:42 by rickerliang      
我也曾经接触过Kinect和OpenNI,NI是未来的发展趋势,新的增长点,有兴趣的朋友真的可以关注一下NI,广州地铁5号线总站调度听说也是通过NI完成的,哈哈。

#20楼  回复 引用 查看   

2011-06-22 17:46 by Rain.      
楼主好像不在啊,我帮忙回答下吧。
楼主莫怪。。。

开发Kinect,不需要XBOX360,但是需要个AC适配器,2样加在一起,我在淘宝上买的 顺丰包邮价 762¥。

kinect对手的精确度如何?
很抱歉,目前的SDK不支持这么精确的定位。估计是不支持的。

代替鼠标,目前是不可能的,鼠标很精确的,就比如你在以前WM6.0时代用手指触摸那些键盘一样。虽然能做,但是很不方便。

#21楼[楼主]  回复 引用 查看   

2011-06-22 21:18 by 马宁      
@Rain.
谢了,刚回家。谢谢您帮忙回答。

#22楼  回复 引用 查看   

2011-06-22 23:10 by redmoon      
引用Tony Zhou:
引用马宁:
@helloworld2
人肉……俺们都是用自己来测的。

是啊,有人帮忙我做动作就好了。一个人太累了。


这样才好啊,又可以编程,又可以锻炼身体。自从有了Kinect,程序员第一次能在开发的过程中站起来活动筋骨了。

#23楼  回复 引用 查看   

2011-07-13 03:09 by 我要进Cisco      
你们谁有马宁老师Kinect for Windows SDK开发初体验(三)的源代码啊?请发到我QQ邮箱,让我学习一下,QQ:374574715.请加我QQ交个朋友吧,我准备搞kinect开发。以前搞c++,JAVA. 现在学c#。

#24楼  回复 引用 查看   

2011-08-03 14:34 by Sun_Flower      
你好!你写的非常棒!你的景深数据啥时候写呀?
我有两个问题:
1.SkeletonToDepthImage 方法的内部处理是怎样的。
2.GetColorPixelCoordinatesFromDepthPixel方法的内部处理是怎样的。

是不是就是很简单的函数调用?

#25楼  回复 引用 查看   

2011-08-20 15:17 by BinF      
如果我要精确到五个手指 能否给点指导呀

#26楼  回复 引用 查看   

2011-08-24 14:21 by 且听风吟~      
期待博主的景深数据分析,可别夭折了丫

#27楼  回复 引用 查看   

2011-08-24 22:38 by LULU0928      
我想請問一下, 程式運行時, 我的小球都沒有動作耶, 有人知道是哪裡出了問題嗎?謝謝!

#28楼  回复 引用 查看   

2011-10-04 18:33 by 即昵称      
@LULU0928
我也是啊,但都按步骤做了,也没报错啊,不知道为什么……

#29楼  回复 引用 查看   

2011-10-09 10:42 by aspnetx      
引用Rain.:
@aspnetx
引用aspnetx:羡慕啊,我kinect都有了,就是本子Thinkpad R60E无法跑SDK.

R60也太老了点吧。
大哥,你该换笔记本了。目前的T420貌似不错。


今天才看到这个回复,还真巧,我还真搞了一个呢。终于可以跑kinect的sdk了,不过马宁的后续教程没有了。。。

#30楼  回复 引用 查看   

2011-11-14 16:41 by OKYD      
SkeletonFrame allSkeletons = e.SkeletonFrame;
有时候allSkeletons里面的一个SkeletonsData都没有被追踪,会导致LINQ取得值为空,一直报空指针,刚刚发现了……
可以把skeleton是否为空做个判断

#31楼  回复 引用 查看   

2011-12-12 20:55 by 老四莱斯      
马老师开个QQ群吧,同意的顶一下