如何将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 开发 —— 近距离探测的更多相关文章

  1. Kinect 开发 —— ColorBasic

    创建一个Kincet项目通常需要: 1. 创建一个VS项目,一般为了展示通常创建一个wpf项目. 2. 添加Microsoft.Kinect.dll引用,如果是早期版本的SDK,这个名称可能不同. 3 ...

  2. Kinect开发文章目录

    整理了一下去年为止到现在写的和翻译的Kinect的相关文章,方便大家查看.另外,最近京东上微软在搞活动, 微软 Kinect for Windows 京东十周年专供礼包 ,如果您想从事Kinect开发 ...

  3. Kinect开发资源汇总

    Kinect开发资源汇总   转自: http://www.sigvc.org/bbs/forum.php?mod=viewthread&tid=254&highlight=kinec ...

  4. Kinect开发学习笔记之(一)Kinect介绍和应用

    Kinect开发学习笔记之(一)Kinect介绍和应用 zouxy09@qq.com http://blog.csdn.net/zouxy09 一.Kinect简单介绍 Kinectfor Xbox ...

  5. Kinect开发笔记之二Kinect for Windows 2.0新功能

    这是本博客翻译文档的第一篇文章.笔者已经苦逼的竭尽全力的在翻译了.但无奈英语水平也是非常有限.不正确或者不妥当不准确的地方必定会有,还恳请大家留言或者邮件我以批评指正.我会虚心接受. 谢谢大家.   ...

  6. Kinect 开发 —— 杂一

    Kinect 提供了非托管(C++)和托管(.NET)两种开发方式的SDK,如果您用C++开发的话,需要安装Speech Runtime(V11),Kinect for Windows Runtime ...

  7. Kinect 开发 —— 控制PPT播放

    实现Kinect控制幻灯片播放很简单,主要思路是:使用Kinect捕捉人体动作,然后根据识别出来的动作向系统发出点击向前,向后按键的事件,从而使得幻灯片能够切换. 这里的核心功能在于手势的识别,我们在 ...

  8. Kinect 开发 —— 全息图

    Kinect的另一个有趣的应用是伪全息图(pseudo-hologram).3D图像可以根据人物在Kinect前面的各种位置进行倾斜和移动.如果方法够好,可以营造出3D控件中3D图像的效果,这样可以用 ...

  9. Kinect 开发 —— 进阶指引(上)

    本文将会介绍一些第三方类库如何来帮助处理Kinect传感器提供的数据.使用不同的技术进行Kinect开发,可以发掘出Kinect应用的强大功能.另一方面如果不使用这些为了特定处理目的而开发的一些类库, ...

随机推荐

  1. GoldenGate 反向切换步骤

    1 事先配置好反向复制链路: 2 停止源端的应用程序; 3 确认源端Capture已捕获所有的Redo信息: GGSCI>info all GGSCI>info ext_app 4 确认源 ...

  2. CF37E Trial for Chief(最短路)

    题意 题意是给你一张 NMNMNM 的图,每个点有黑色和白色,初始全为白色,每次可以把一个相同颜色的连续区域染色,求最少的染色次数:(n,m<=50) 题解 转化为最短路.对于每一个点与它相邻的 ...

  3. Winscp远程连接Linux主机,上传和下载文件

    1.安装Winscp.这里不再赘述,网上搜索下载安装就可以 2.点击桌面Winscp快捷键,打开Winscp 3.在打开的页面上填写远程主机的IP,用户名和密码,点击保存,会在页面的左边出现一个站点, ...

  4. memset函数的用法

    重点需要注意一个细节:memset是以byte来填充的,也就是一个字节(8bit)一个单位来填充. 因此:如果int array[10]: memset(array, 1, 10):然后再把array ...

  5. Kubernetes本地私有仓库配置

    实验环境 master 10.6.191.181 node1 10.6.191.182 node2 10.6.191.183 本地私有仓库 10.6.191.184 一.安装本地私有仓库 1.安装do ...

  6. 杀死超过5min闲置的终端

    #!/bin/bash #杀死超过5min闲置的终端 while [ 1 -lt 2 ] do sleep 30 for i in `w -sh | grep ":" | awk ...

  7. windows server 打开 FTP 服务器上的文件夹时发生错误。请检查是否有权限访问该文件夹。

    解决方案1: 打开高级安全windows防火墙,设置出入站规则. 然后,再打开windows防火墙界面,点击左上角“允许程序或功能通过windows防火墙”,勾选上设置的出入站名称和FTP服务器. 如 ...

  8. 用typename和template消除歧义

  9. 【JavaScript】分秒倒计时器

    一.基本目标 在JavaScript设计一个分秒倒计时器,一旦时间完毕使button变成不可点击状态 详细效果例如以下图.为了说明问题.调成每50毫秒也就是每0.05跳一次表, 真正使用的时候,把wi ...

  10. Select 选择算法 - 编程珠玑(续) 笔记

    Select 算法 I 编程珠玑(续)介绍的 Quickselect 算法 选择 N 个元素中的第 K 小(大)值,是日常场景中常见的问题,也是经典的算法问题. 选取 N 个元素的数组的中的第 K 小 ...