Kinect 开发 —— 近距离探测
如何将Kinect设备作为一个近距离探测传感器。为了演示这一点,我们处理的场景可能在以前看到过。就是某一个人是否站在Kinect前面,在Kinect前面移动的是人还是什么其他的物体。当我们设置的触发器超过一定的阈值,我们就发起另一个处理线程。类似的触发器如当用户走进房间时,我们打开房间里面的灯。对于商业广告系统,当展示牌前面没有人时,可以以“attract”模式展现内容,而当人靠近展示牌时,则展现一些更多的可供交互的内容。和仅仅编写交互性强的应用程序不同,我们可以编写一些能够感知周围环境的应用程序。
Kinect甚至可以被我们改造成一个安全的摄像头,当某些重要的特征发生时,我们可以记录下Kinect看到的景象。在晚上,我在门前放了一些食物,住在哪儿的猫可以吃到。最近我开始怀疑有其他的动物在偷吃我们家猫的食物。我将Kinect作为一个运动探测和视频录像机放在门口,这样就可以知道真实发生的情况了。如果你喜欢一些自然景像,你可以通过简单的设置来实现长时间的录像来获取其他动物的出现情况。虽然在探测到有动物靠近时开启视频影像录制可以节省磁盘空间,但是识别有动物靠近可能需要花费很长的时间。如果像我这样,你可以打开影像录制功能,这样你能够看到长时间的景象变化,比如风吹叶落的声音。Kinect作为一种显示增强的工具,其不仅仅可以作为应用程序的输入设备,一些新的Kinect的可能应用正在迅速发掘出来。
简单的近距离探测
建立一个近距离探测应用,当有人站在Kinect设备前面时,打开视频影像录制。自然,当有用户进入到Kinect的视野范围时需要触发一些列的操作。最简单的实现近距离探测的方法是使用KinectSDK中的骨骼探测功能。
首先创建一个名为KinectProximityDetectionUsingSkeleton的WPF应用程序,添加Microsoft.Kinect.dll和对System.Drawing命名空间的引用。将ImageExtensions.cs类文件拷贝到项目中,并添加对ImageManipulationExtensionMethods命名空间的引用。主界面元素非常简单,我们只是添加了一个名为rgbImage的Image对象来从Kinect影像数据中获取并显示数据。
<Grid >
<Image Name="rgbImage" Stretch="Fill"/>
</Grid>
下面的代码显示了一些初始化代码。大部分的代码都是在为Image提供数据源。在MainWindows的构造函数中,对_kinectSensor对象进行了初始化,并注册影像数据流和骨骼数据流响应事件。这部分代码和以前我们写的代码类似。所不同的是,我们添加了一个布尔型的_isTracking来表示是否我们的近距离探测算法识别到了有人进入视野。如果有,则更新影像数据流,更新image对象。如果没有,我们略过影像数据流,给Image控件的Source属性赋null值。
Microsoft.Kinect.KinectSensor _kinectSensor;
bool _isTracking = false; public MainWindow()
{
InitializeComponent(); this.Unloaded += delegate{
_kinectSensor.ColorStream.Disable();
_kinectSensor.SkeletonStream.Disable();
}; this.Loaded += delegate
{
_kinectSensor = Microsoft.Kinect.KinectSensor.KinectSensors[0];
_kinectSensor.ColorFrameReady += ColorFrameReady;
_kinectSensor.ColorStream.Enable(); _kinectSensor.Start();
};
} void ColorFrameReady(object sender, ColorImageFrameReadyEventArgs e)
{ if (_isTracking)
{
using (var frame = e.OpenColorImageFrame())
{
if (frame != null)
rgbImage.Source = frame.ToBitmapSource();
};
}
else
rgbImage.Source = null;
} private void OnDetection()
{
if (!_isTracking)
_isTracking = true;
} private void OnDetectionStopped()
{
_isTracking = false;
}
为了能够处理_isTracking标签,我们需要注册KienctSensor.SkeletonFrameReady事件。SkeletonFrameReady事件类似心脏跳动一样驱动程序的运行。只要有物体在Kinect前面,SkeletonFrameReady事件就会触发。在我们的代码中,我们需要做的是检查骨骼数据数组,判断数组中是否有骨骼数据处在追踪状态中。代码如下。
有时候我们不需要抛出事件。我们有一个内建的机制能够通知我们有人体进入到了Kinect视野范围内,但是,我们没有一个机制能够告诉在什么时候人走出了视野或者不在追踪状态。为了实现这一功能,不管是否探测到了用户,我们开启一个计时器,这个计时器的功能是存储最后一次追踪到的事件的时间,我们检查当前时间和这一时间的时间差,如果时差超过某一个阈值,就认为我们失去了对物体的追踪,我们应该结束当前的近距探测。
int _threshold = ;
DateTime _lastSkeletonTrackTime;
DispatcherTimer _timer = new DispatcherTimer();
public MainWindow()
{
InitializeComponent(); this.Unloaded += delegate{
_kinectSensor.ColorStream.Disable();
_kinectSensor.SkeletonStream.Disable();
}; this.Loaded += delegate
{
_kinectSensor = Microsoft.Kinect.KinectSensor.KinectSensors[];
_kinectSensor.ColorFrameReady += ColorFrameReady;
_kinectSensor.ColorStream.Enable(); _kinectSensor.SkeletonFrameReady += Pulse;
_kinectSensor.SkeletonStream.Enable();
_timer.Interval = new TimeSpan(, , );
_timer.Tick += new EventHandler(_timer_Tick); _kinectSensor.Start();
};
}
void _timer_Tick(object sender, EventArgs e)
{ if (DateTime.Now.Subtract(_lastSkeletonTrackTime).TotalMilliseconds > _threshold)
{
_timer.Stop();
OnDetectionStopped();
}
} private void Pulse(object sender, SkeletonFrameReadyEventArgs e)
{
using (var skeletonFrame = e.OpenSkeletonFrame())
{
if (skeletonFrame == null || skeletonFrame.SkeletonArrayLength == )
return; Skeleton[] skeletons = new Skeleton[skeletonFrame.SkeletonArrayLength];
skeletonFrame.CopySkeletonDataTo(skeletons); for (int s = ; s < skeletons.Length; s++)
{
if (skeletons[s].TrackingState == SkeletonTrackingState.Tracked)
{
OnDetection(); _lastSkeletonTrackTime = DateTime.Now; if (!_timer.IsEnabled)
{
_timer.Start();
}
break;
}
}
}
}
使用景深数据进行近距离探测
使用骨骼追踪进行近距离探测是近距离探测的基础,当没有一个人进入到视野中,并进行交互时,电子广告牌进入“StandBy”模式,在这种情况下,我们只是简单的播放一些视频。不幸的是,骨骼追踪不能很好的捕捉类似在我家后面的门廊上的偷食物的浣熊或者是在旷野中的大脚野人的图像。这是因为骨骼追踪的算法是针对人类的关键特征以及特定的人体类型进行设计的。超出人体的范围,在Kinect镜头前骨骼追踪会失败或者是追踪会变的时断时续。
为了处理这一情况,我们可以使用Kinect的深度影像数据,而不能依靠骨骼追踪。深度影像数据也是近距离探测的一种基本类型。如下代码所示,程序运行中必须配置或者获取彩色影像和深度影像数据流,而不是骨骼数据流。
相比骨骼追踪数据,使用景深数据作为近距探测算法的基础数据有一些优点。首先只要传感器在运行,那么深度影像数据就是连续的。这避免了需要另外设置一个计时器来监控在探测过程是否意外终止。另外,我们可以对我们要探测的对象离Kinect的距离设置一个最小和最大的距离阈值。当物体离Kinect的距离比这个最小的阈值还要小,或者超过最大阈值的范围时,将_isTracking设置为false。下面的代码中,我们探测距离Kinect 1米至1.2米的对象。通过分析深度影像数据的每一个像素来判断是否有像元落在该距离范围内。如果有一个像元落在该范围内,那么停止对影像的继续分析,将isTracking设置为true。ColorFrameReady事件处理探测到物体的事件,然后使用彩色影像数据来更新image对象。
void DepthFrameReady(object sender, DepthImageFrameReadyEventArgs e)
{
bool isInRange = false;
using (var imageData = e.OpenDepthImageFrame())
{
if (imageData == null || imageData.PixelDataLength == )
return;
short[] bits = new short[imageData.PixelDataLength];
imageData.CopyPixelDataTo(bits);
int minThreshold = ;
int maxThreshold = ; for (int i = ; i < bits.Length; i += imageData.BytesPerPixel)
{
var depth = bits[i] >> DepthImageFrame.PlayerIndexBitmaskWidth; if (depth > minThreshold && depth < maxThreshold)
{
isInRange = true;
OnDetection();
break;
}
}
} if (!isInRange)
OnDetectionStopped();
}
相比骨骼数据,使用深度影像数据进行近距离探测的最后一个好处是速度较快。即使在比我们对景深数据处理更低的级别上进行骨骼追踪,骨骼追踪仍需要有完整的人体出现在Kinect视野中。同时Kinect SDK需要利用决策树分析整个人体影像数据,并将识别出来的结果和骨骼识别预设的一些特征参数进行匹配,以判断是否是人体而不是其他物体。使用景深影像数据算法,我们只需要查找是否有一个像元点落在指定的深度值范围内,而不用分析整个人体。和之前的骨骼追中算法不同,代码中深度值探测算法只要有物体落在Kinect传感器的视野范围内,都会触发OnDetection方法。
对近距离探测的改进
当然,使用景深数据进行近距离探测也有一些缺点。最小和最大深度阈值必须明确定义,这样才能避免_isTracking永远为true的情况。深度影像允许我们放松只能对人体进行近距离探测的这一限制,但是这一放松有点过了,使得即使一些静止不动的物体可能也会触发近距离探测。在实现一个运动测试来解决这一问题之前,我们可以实现一个探测条件不紧也不松的近距离探测。
下面的代码展示了如何结合深度影像数据中的深度值数据和游戏者索引位数据来实现一个近距离探测器。如果骨骼追踪算法符合你的需求,同时你又想将探测的对象限定在距离传感器的最大最小距离阈值范围内,这种方法是最好的选择。这在露天的广告牌中也很有用,比如可以在广告牌前设置一个区域范围。当有人进入到这一范围时触发交互。当人进入到距离装有Kinect的广告牌1米至1.5时触发另一种交互,当人离广告牌够近以至于可以触摸到时,触发另外一种交互。要建立这种类型的近距离探测,需要在MainWindows的构造函数中开启骨骼追踪功能,使得能够使用景深影像的深度数据和游戏者索引位数据。这些都做好了之后,可以改写之前例子中的DepthFrameReady事件,来对距离阈值进行判断,并检查是否有游戏者索引位数据存在。代码如下:
void DepthFrameReady(object sender, DepthImageFrameReadyEventArgs e)
{
bool isInRange = false;
using (var imageData = e.OpenDepthImageFrame())
{
if (imageData == null || imageData.PixelDataLength == )
return;
short[] bits = new short[imageData.PixelDataLength];
imageData.CopyPixelDataTo(bits);
int minThreshold = ;
int maxThreshold = ; for (int i = ; i < bits.Length; i += imageData.BytesPerPixel)
{
var depth = bits[i] >> DepthImageFrame.PlayerIndexBitmaskWidth;
var player = bits[i] & DepthImageFrame.PlayerIndexBitmask; if (player > && depth > minThreshold && depth < maxThreshold)
{
isInRange = true;
OnDetection();
break;
}
}
} if(!isInRange)
OnDetectionStopped();
}
Kinect 开发 —— 近距离探测的更多相关文章
- Kinect 开发 —— ColorBasic
创建一个Kincet项目通常需要: 1. 创建一个VS项目,一般为了展示通常创建一个wpf项目. 2. 添加Microsoft.Kinect.dll引用,如果是早期版本的SDK,这个名称可能不同. 3 ...
- Kinect开发文章目录
整理了一下去年为止到现在写的和翻译的Kinect的相关文章,方便大家查看.另外,最近京东上微软在搞活动, 微软 Kinect for Windows 京东十周年专供礼包 ,如果您想从事Kinect开发 ...
- Kinect开发资源汇总
Kinect开发资源汇总 转自: http://www.sigvc.org/bbs/forum.php?mod=viewthread&tid=254&highlight=kinec ...
- Kinect开发学习笔记之(一)Kinect介绍和应用
Kinect开发学习笔记之(一)Kinect介绍和应用 zouxy09@qq.com http://blog.csdn.net/zouxy09 一.Kinect简单介绍 Kinectfor Xbox ...
- Kinect开发笔记之二Kinect for Windows 2.0新功能
这是本博客翻译文档的第一篇文章.笔者已经苦逼的竭尽全力的在翻译了.但无奈英语水平也是非常有限.不正确或者不妥当不准确的地方必定会有,还恳请大家留言或者邮件我以批评指正.我会虚心接受. 谢谢大家. ...
- Kinect 开发 —— 杂一
Kinect 提供了非托管(C++)和托管(.NET)两种开发方式的SDK,如果您用C++开发的话,需要安装Speech Runtime(V11),Kinect for Windows Runtime ...
- Kinect 开发 —— 控制PPT播放
实现Kinect控制幻灯片播放很简单,主要思路是:使用Kinect捕捉人体动作,然后根据识别出来的动作向系统发出点击向前,向后按键的事件,从而使得幻灯片能够切换. 这里的核心功能在于手势的识别,我们在 ...
- Kinect 开发 —— 全息图
Kinect的另一个有趣的应用是伪全息图(pseudo-hologram).3D图像可以根据人物在Kinect前面的各种位置进行倾斜和移动.如果方法够好,可以营造出3D控件中3D图像的效果,这样可以用 ...
- Kinect 开发 —— 进阶指引(上)
本文将会介绍一些第三方类库如何来帮助处理Kinect传感器提供的数据.使用不同的技术进行Kinect开发,可以发掘出Kinect应用的强大功能.另一方面如果不使用这些为了特定处理目的而开发的一些类库, ...
随机推荐
- SQL_触发器学习
--触发器学习-------------------------------------------------------------------------------after 触发器----- ...
- 函数式JS: 原来promise是这样的monad
转载请注明出处: http://hai.li/2017/03/27/prom... 背景 上篇文章 函数式JS: 一种continuation monad推导 得到了一个类似promise的链式调用, ...
- 2014 CodingTrip - 携程编程大赛 (预赛第一场)
1001: 可以证明(扩展欧几里得),只要卡片中有两个卡片互素,旁边点就是可达的. 因此只需要算出所有卡片不互素的情况有多少种,可用容斥原理. #include <cstdio> #inc ...
- 【Educational Codeforces Round 37 E】Connected Components?
[链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] bfs. 用一个链表来记录哪些点已经确定在某一个联通快里了. 一开始每个点都能用. 然后从第一个点开始进行bfs. 然后对于它的所有 ...
- 【Codeforces Round #421 (Div. 2) A】Mister B and Book Reading
[题目链接]:http://codeforces.com/contest/820/problem/A [题意] 每天看书能看v页; 且这个v每天能增加a; 但是v有上限v1; 然后每天还必须往回看t页 ...
- 一些牛人的IOS博客,mark下慢慢学习
http://blog.devtang.com/ 唐巧的个人blog http://gracelancy.com/ Lancy's blog http://b ...
- CCNP路由实验之十 组播(多播)
CCNP路由实验之十 组播(多播) 种方法: 在交换机上配置静态的多播MAC地址到用户接口的映射 使用CGMP.执行CGMP的多播路由器能够将用户发送给自己 ...
- 彻底解决lazarus安装组件后烦人的编译时单元找不到的问题!
以安装indy为例 1/下载组件包, http://www.indyproject.org/Sockets/fpc/indy-10.2.0.3.zip 2/爆开放于C:\lazarus\compone ...
- 在Unix上用 BIND建立名称服务器(naem server)
在Unix上用 BIND建立名称服务器(naem server) 安装 apt install -y bind9 yum install -y bind bind-utils 下载源码并解压缩,htt ...
- NuGet配置代理
https://stackoverflow.com/questions/9232160/nuget-behind-proxy nuget.exe config -set http_proxy=http ...