Kinect

与 Kinect 的多模通信

Leland Holmquest

下载代码示例

在四月 (msdn.microsoft.com/magazine/hh882450) 我向您介绍了"百合花"(这是一个虚拟的助手,旨在帮助办公室工作人员在日常任务。 我演示了如何使用微软的 Windows SDK 的 Kinect 创建上下文感知的对话,使百合倾听和对用户的意图做出适当的响应。

现在,我将带你通过实现一个自然的用户界面通过利用 Kinect 设备的骨骼肌跟踪,以便通过手势的用户交互的下一步了。 然后我将这两个概念联系在一起,通过让莉莉的输出依赖于作出什么手势,不仅还发出什么口语的命令演示多式联运的通信。 通过结合这两种模式的通信,用户出来的具有更丰富的体验,并获取一步靠近无处不在计算。 百合的演示文稿是 Windows 的演示文稿的基础 (WPF) 应用程序的形式。

初始化 Kinect

使用 Kinect 设备的第一步是构建一个运行时,设置各项参数。 图 1 显示的配置,我选择了莉莉。

图 1 Kinect 运行库建设

// Initialize Kinect
nui = Runtime.Kinects[0];// new Runtime();
nui.Initialize(RuntimeOptions.UseDepthAndPlayerIndex |    
  RuntimeOptions.UseDepth | RuntimeOptions.UseColor |
  RuntimeOptions.UseSkeletalTracking);
nuiInitialized = true; nui.SkeletonEngine.TransformSmooth = true;
nui.SkeletonEngine.SmoothParameters = new TransformSmoothParameters
{
  Smoothing = 0.75f,
  Correction = 0.0f,
  Prediction = 0.0f,
  JitterRadius = 0.05f,
  MaxDeviationRadius = 0.04f
};
nui.VideoStream.Open(ImageStreamType.Video, 2, 
  ImageResolution.Resolution640x480, ImageType.Color);
nui.DepthStream.Open(ImageStreamType.Depth, 2,
  ImageResolution.Resolution320x240, ImageType.DepthAndPlayerIndex);

设置 Kinect 设备,提供了许多选项。 首先,请注意在代码的第一行图 1。 Kinect 的 Windows SDK beta 2 有不同的构造函数中,运行库。 通过引用索引 (Runtime.Kinects[0];),它是简单将附加到应用程序的多个 Kinect 单位。 此应用程序中我已经有限的 it Kinect 的单个设备,因此根据定义,运行库必须在位置 [0]。 您可以循环访问集合的 Runtime.Kinects 来处理多个 Kinect 单位如果可用。 下一步,我需要告诉哪些功能要使用的设备 Kinect。 这是通过传递到初始化方法所需的功能。 有四个可供选择的值:

  • UseColor 使应用程序能够处理图像的颜色信息。
  • UseDepth 使应用程序以使用深度图像信息。
  • UseDepthAndPlayerIndex 使应用程序以使用深度图像信息,以及由骨架跟踪引擎生成的索引。
  • UseSkeletalTracking 使应用程序使用跟踪数据的骨架。

在这些值传递告诉什么子系统 Kinect 设备中的去,所以可以开始运行库的多级管道的相应部分使用的 API。 很重要,请注意您不能访问后来在应用程序中不声明的初始化过程中的功能。 例如,如果选择的唯一选项是 RuntimeOptions.UseColor,并稍后使用深度信息是必需,就不可以使用它。 因此,我通过中所有的值,该值指示我打算使用 Kinect 设备的全部功能。

跟踪用户

在讨论之前在代码中的下一节,让我们看看 Kinect 设备真正的给我们。 当使用跟踪能力的骨架,Kinect 设备可以跟踪两个活跃的人类与系统交互。 它通过创建 20 接头的集合,并将与每个关联 ID 实现这一点。 图 2 显示哪些节点都被建模。

The 20 Joints that Are Modeled in Kinect
图 2 20 接头进行建模的 Kinect

图 3 是从两个单独的用户被捕获的节点的图像。

Two Active Skeletons
图 3 两个活动骨架

为了使骨骼变得活跃,Kinect 设备必须能够看到从头到脚的用户。 一旦骨架处于活动状态,一个联合视图中,转到 Kinect 设备将尝试内插的骨架部分在哪里。 如果你要构建 Kinect 启用的应用程序,我极力建议您创建一个简单的应用程序,只是要看骨架的溪流和与 Kinect 设备进行交互。 请确保您有多个用户参与并设置方案障碍物哪里来您的用户与 Kinect 设备之间 — — 模仿您的应用程序将体验一次部署的方案。 这会给你一个出色的认识,骨架跟踪是如何工作的它有能力,以及哪些限制你可能要地址。 很快,您将看到的技术是多么令人惊奇和插值可以是多么的富有创造力。

在某些情况下 (比如一个由项目百合代表) 的速度和此插值的音频波动可以分心和非生产性。 因此,API 公开控制一个平滑的水平的能力。 指的图 1 再,首先使用 SkeletonEngine 在运行库将 TransformSmooth 设置为 true。 这会告诉您要影响正在呈现的数据的平整度的 Kinect 设备。 然后,设置 SmoothParameters。 下面是每个 TransformSmoothParameters 的简短说明:

  • 校正控制校正的量值从 0 到 1.0 和.5 的默认值。
  • JitterRadius 控制的抖动减少半径。 中传递的值代表米中所需的半径。 默认值设置为 0.05,从而转化为 5 厘米。 任何超出此半径的抖动被夹住半径。
  • MaxDeviationRadius (在米) 更正职位的最大半径可背离原始数据的控件。 默认值是 0.04。
  • 预测控制预测的帧的数。
  • 平滑控制量的平滑 0 到 1.0 的范围。 文档使平滑延迟 ; 有影响的特定点 增加平滑增加延迟。 默认值是 0.5。 将值设置为 0,导致要返回的原始数据。

视频和深度流

你会尝试使用这些设置在您自己的应用程序中,取决于您正在履行的要求。 为此应用程序所需的最后一件事就是打开视频流和 DepthStream。 这便于查看来自彩色摄像机的视频图像和分别来自深度相机的深度图像。 稍后我来告诉你如何这获取连接到 WPF 应用程序。

Open 方法需要四个参数。 第一个是 streamType。 它表示 (例如,视频) 打开的流的类型。 第二个参数是 poolSize。 这代表的是运行时的帧数的缓冲区。 最大值为 4。 第三个参数是号决议表示所需图像的分辨率。 值包括 80 x 60、 640 x 480、 320 x 240 和 1280 x 1024,以满足您的需要。 最后一个参数指示所需的类型的图像 (例如,颜色)。

Kinect 事件

与运行库初始化成功,它是时间线可供从运行库应用程序的事件。 莉莉,只是为了给最终用户的彩色图像和深度图像的图形视图用于将处理的首两个事件。 首先,让我们看看的处理 Runtime.VideoFrameReady 事件的方法。 此事件作为其事件参数传递的 ImageFrameReadyEventArgs。 Nui_VideoFrameReady 方法是莉莉哪里处理事件,如下面的代码所示:

void nui_VideoFrameReady(object sender, ImageFrameReadyEventArgs e)
{
  // Pull out the video frame from the eventargs and
  // load it into our image object.
PlanarImage image = e.ImageFrame.Image;
  BitmapSource source =
    BitmapSource.Create(image.Width, image.Height, 96, 96,
    PixelFormats.Bgr32, null, image.Bits,
    image.Width * image.BytesPerPixel);
  colorImage.Source = source;
}

Windows API 的 Kinect 使这种方法更简单。 ImageFrameReadyEventArgs 包含 ImageFrame.Image。 我将,转换为 BitmapSource,然后将该 BitmapSource 传递给图像控件在 WPF 应用程序中。 像中看到的内容的应用程序,从而显示来自 Kinect 设备的彩色摄像机的帧图 3

DepthFrameReady 事件,其中 nui_DepthFrameReady 正在处理类似,但有点需要更多的工作,获取有用的演示文稿。 你可以看看这种方法中的代码下载,这是上个月文章相同 (archive.msdn.microsoft.com/mag201204Kinect)。 我不我自己,创建此方法,但发现一些在线的例子中使用它。

才真正开始变得有趣的事件处理程序是 nui_SkeletonFrameReady 的方法。 此方法处理 SkeletonFrameReady 事件,如中所示,在 SkeletonFrameReadyEventArgs,获取通过图 4

图 4 nui_SkeletonFrameReady

void nui_SkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e)
{
  renderSkeleton(sender, e);
  if (!trackHands)
    return;// If the user doesn't want to use the buttons, simply return.
if (e.SkeletonFrame.Skeletons.Count() == 0)
    return;// No skeletons, don't bother processing.
SkeletonFrame skeletonSet = e.SkeletonFrame;
  SkeletonData firstPerson = (from s in skeletonSet.Skeletons
                              where s.TrackingState ==
                              SkeletonTrackingState.Tracked
                              orderby s.UserIndex descending
                              select s).FirstOrDefault();
  if (firstPerson == null)
    return;// If no one is being tracked, no sense in continuing.
JointsCollection joints = firstPerson.Joints;
  Joint righthand = joints[JointID.HandRight];
  Joint lefthand = joints[JointID.HandLeft];
  // Use the height of the hand to figure out which is being used.
Joint joinCursorHand = (righthand.Position.Y > lefthand.Position.Y)
    ?
righthand
    : lefthand;
  float posX = joinCursorHand.ScaleTo((int)SystemParameters.PrimaryScreenWidth,
    (int)SystemParameters.PrimaryScreenHeight).Position.X;
  float posY = joinCursorHand.ScaleTo((int)SystemParameters.PrimaryScreenWidth,
    (int)SystemParameters.PrimaryScreenHeight).Position.Y;
  Joint scaledCursorJoint = new Joint
  {
    TrackingState = JointTrackingState.Tracked,
    Position = new Microsoft.Research.Kinect.Nui.Vector
    {
      X = posX,
      Y = posY,
      Z = joinCursorHand.Position.Z
    }
  };
  OnButtonLocationChanged(kinectButton, buttons, 
    (int)scaledCursorJoint.Position.X,
    (int)scaledCursorJoint.Position.Y);
}

我发现将放入此应用程序所需的一件事是,第一次的条件,在图 4。 当用户不希望应用程序跟踪她手的动作时,有设置 trackHands 变量,因而可以确定是否跟踪手的口头的命令。 如果 trackHands 设置为 false,出的这种方法只是返回代码。 如果莉莉跟踪用户的手,这并不是所需的行为时,我们很快就会乏味和累人。

同样,如果没有骨架正在跟踪 (没有用户,或它们的 Kinect 设备视图范围) 然后继续评估数据,因此代码返回的方法有没有意义。 但是,如果那里骨架和用户想要跟踪的手,然后继续评估的代码。 HoverButton 项目 (bit.ly/nUA2RC) 附带的示例代码。 这种方法的大部分来自于这些示例。 在此方法中有趣的事情之一是该代码检查看哪只手上,用户是物理上较高。 然后,它使得最高的手是一个被用于潜在选择一个按钮的假设。 该代码然后去上确定是否一个按钮正在盘旋,并呈现"手",是对用户的手的位置屏幕的代表,在屏幕上。 换句话说,当用户移动时他的手,像时装一样在屏幕周围移动图形的手。 这为用户提供了自然的界面,不再受鼠标的线。 用户是控制器。

下一个感兴趣的项目是系统确定单击之一的 HoverButtons。 莉莉在屏幕上共有八个按钮。 每个有有线 on_click 事件处理程序。 在这一点上,我需要涵盖三个特别类:ButtonActionEvaluator、 LilyContext 和 MultiModalReactions。

单击一个按钮的行动有相应的事件相关联,但百合采用此单个操作并检查是否它可以到相应的音频命令,来评估作为多式联运的通信将会在较高水平的意义上的耦合。 例如,单击其中一个 HoverButtons 表示打算选择一个项目。 有了这些信息,系统所需的唯一行动是注意上下文就目前正在努力,该项目已更改。 不需要任何进一步的行动。 但是,如果用户先前所做一个不满意的请求,在"打开项目计划",或随后提出相同的请求,应用程序必须把这些两的不同部分的数据一起以创建高阶的意义 (通信从两个独立的模式来使此多式联运的通信) 和采取相应措施。 要使此所有发生在无缝的方式,实施以下设计的。

ButtonActionEvaluator 类单身人士作为实现和实现 INotifyPropertyChanged 接口。 此类还公开 PropertyChanged 处理的事件的由 LilyContext 类 (还单身人士)。 下面的代码可能需要一点解释,尽管看起来不够无害:

void buttonActionEvaluator_PropertyChanged(
  object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
  if (MultiModalReactions.ActOnMultiModalInput(
    buttonActionEvaluator.EvaluateCriteria()) == 
    PendingActionResult.ClearPendingAction)
    buttonActionEvaluator.ActionPending = 
      PendingAction.NoneOutstanding;
}

莉莉的状态评价

第一,前面的代码 buttonActionEvaluator 类上调用 EvaluateCriteria 方法。 此方法只是返回状态的数值表示形式,由 ActionPending 和 SelectedButton 属性定义。 这是如何应用程序是可以推断通过多式联运通信的使用意义的核心。 在传统的应用程序中,通过查看单个事件或属性 (例如,button1.clicked) 的状态计算所需的操作。 但百合,正在计算 (从多式联运的角度) 的状态是另有单独的两个属性的组合。 换言之,每个属性具有的意义和需要操作的独立,但计算时一起,采取新的更高水平的意义。

对多式联运然后传递到 ActOnMultiModalInput 方法的组合状态的数字表示­类反应。 此方法实现处理所有可能的排列的庞大 switch 语句。 (这是最基本的实现,用来说明这一点。 莉莉的未来小版本将替换此实现更高级的技术,如状态机和机器学习提高的整体经验和可用性。)如果此方法会导致用户满意的意图 (例如,用户想要打开的项目计划,项目莉莉系统),返回类型是 PendingActionResult.ClearPendingAction。 这将使系统的上下文仍处于参考框架的项目莉莉,但没有等待队列中执行的操作。 如果用户的意图是还是不满意,则返回 PendingActionResult.LeavePendingActionInPlace、 尚未满足用户的意图,并告知系统采取了任何行动并没有因此不清楚操作挂起。

第一篇文章展示了如何创建特定于给定的域或上下文的语法。 Kinect 单位,利用语音识别引擎,使用这些语法,装载和卸载它们能够满足用户的需求。 这创建的应用程序不要求坚持以脚本化的交互用户。 用户可以去什么方向,她的欲望,改变方向,而无需重新配置应用程序。 这创造了人类的用户和计算机应用程序之间进行对话自然的方式。

更高层次的意义

在这篇文章中,我演示了如何夫妇操作导致的上下文感知的语法形式的选择按钮悬停的一个用户的物理手势的手在按钮上。 每个事件 (speechDetected 和 buttonClicked),可以分别、 独立地进行处理。 但是,此外,两个事件可以将关联起来的系统,带来了更高水平的意义的事件,并采取相应行动。

我希望你一样兴奋的 Kinect 将放入我们的手,对我的能力。 我认为微软带我们到边缘人类计算界面可以向前采取跨越式发展。 作为对此的证词,我开发了莉莉,有不的倍时测试不同的组件和代码节。 当应用成熟,终于实际上"对话",百合,我会找错、 切换到我的第二个监视器,开始查找文档中或其它地方的东西。 但我将继续与莉莉,问她来执行任务,或甚至关闭各自以口头方式进行交互。 我发现百合不可用时,我成为摄动因为的启用莉莉代表量显著 — — 以关闭我的手,通过简单的口头通讯的小任务。

并将"小动作"纳入对话机制 (例如,随机但一些地域性和语法上正确响应) 的应用程序通过直观和令人满意。 Kinect 真正使你的身体,控制器。 你同去哪里它只受你的想象力的限制。 什么将你 Kinect?

Leland Holmquest 是企业战略咨询顾问,在 Microsoft。之前,他曾为海军表面战中心格伦。他正在研究博士学位 乔治 · 梅森大学信息技术。

多亏了以下的技术专家审查这篇文章: Mark Schwesinger