65.9K
CodeProject 正在变化。 阅读更多。
Home

Kinect 接待处

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.94/5 (34投票s)

2011 年 9 月 2 日

Ms-PL

5分钟阅读

viewsIcon

74931

downloadIcon

1977

迎宾区的电视屏幕,当无人时(没有人进入迎宾区)我们可以播放视频,但当有人进入画面时,显示 Kinect 图像,如果用户做了一些有趣的事情,就捕捉他的图像并保存。

1.png

引言

构思: 迎宾区的电视屏幕,当无人时(没有人进入迎宾区)我们可以播放视频,但当有人进入画面时,显示 Kinect 图像,如果用户做了一些有趣的事情,就捕捉他的图像并保存。

问题是,我如何知道一个人是否在做有趣的事情?

为此,我创建了一个名为 AuthenticManager 的类,其中包含一套规则,定义了哪些姿势或组合是有趣的。

例如: 如果右手位置高于头部位置,则加 2 分,如果左脚位置交叉右脚,则额外加 2 分,等等。
当用户达到目标分数时,我们决定拍照。

在开始编码之前,让我们先谈谈应用程序流程
主窗口由两个 Timer AuthenticManager 控制

SeriousTimer 设置为 10 秒,当 Kinect Skeleton 事件首次触发时(该事件仅在 Kinect 识别出完整人物骨骼时才有效),它就开始计时。
SkeletonFrameReady 中,我们还有一个名为 _fpsCount 的整数,它会在 SeriousTimer 启动后,每次调用 SkeletonFrameReady 时递增 1。这将帮助我们确保用户正站在 Kinect 前,而不是仅仅路过。

那么 _fbsCount 是如何告知我的呢? 我们只需要将 SeriousTimer 的秒间隔乘以最小帧率(例如 10),如果用户站在 Kinect 前,_fpsCount 应该更大。

在这种情况下,Timer 将停止视频播放,并显示 Kinect 图像。

IdleTimer 默认设置为 30 秒,每次 SkeletonFrameReady 方法被触发时,我们都会重新启动 IdleTimer

因此,如果 SkeletonFrameReady 在一段时间内没有事件,IdleTimer 将恢复视频播放。

JointID - AuthenticManager 使用 RuleObject ,其中包含 JointID Source JointID Target (更多关于关节的信息 - Kinect – 入门 – 变身绿巨人浩克)。

AuthenticManager 是 Kinect Reception 应用程序的核心,此类将根据您自己的规则检查用户姿势是否有趣。

11.png

什么是关节?

Kinect 应用程序代码提供的数据是一组点,称为骨骼位置,它们构成骨骼结构。

public enum JointID
  {
    HipCenter = 0,
    Spine = 1,
    ShoulderCenter = 2,
    Head = 3,
    ShoulderLeft = 4,
    ElbowLeft = 5,
    WristLeft = 6,
    HandLeft = 7,
    ShoulderRight = 8,
    ElbowRight = 9,
    WristRight = 10,
    HandRight = 11,
    HipLeft = 12,
    KneeLeft = 13,
    AnkleLeft = 14,
    FootLeft = 15,
    HipRight = 16,
    KneeRight = 17,
    AnkleRight = 18,
    FootRight = 19,
    Count = 20,
  } 
  • Vector - 对于 Source Target 关节,您还需要定义 Vector 来相互比较 X 或 Y。
  • Operator – 您是期望 Source 向量大于还是小于 Target 向量?
  • Score – 如果规则为 true ,则该规则的分数是多少。

3.png

背景

自从微软发布了 Kinect.NET SDK 以来,我写了很多关于这个主题的文章

我认为 Kinect 非常酷,我正在为 Kinect 寻找更多的项目和好的想法。几天前,我和我的朋友 Guy Burstein 聊过,他提出了一个 Kinect 应用的想法,他说如果人们进入微软以色列迎宾区,而不是播放商业广告的视频屏幕,让我们用 Kinect 添加一些有趣的东西。

Using the Code

利用 Kinect 事件,我可以检测到用户何时进入画面,使用两个计时器,我可以检查用户只是路过还是正对着摄像头。

下图描述了应用程序流程,开始时应用程序将播放随机视频,当 Kinect Skeleton 事件触发时,Serious Timer 将开始计时,每次滴答根据帧率累加到变量 NumTicks 中,当 Serious Timer 完成时,我们检查 NumTicks 是否足够大(基于帧率),如果是,那么我们将启动 Idle Timer 并切换到 Kinect 图像。

Idle Timer – 每次 Kinect Skeleton 事件触发时,Idle Timer 都会重新启动,因此如果没有人在 Kinect 摄像头前,Idle Timer 将切换回视频。

4.png

正如您从下面的图片(或 完整视频)中看到的,当我移动我的手或腿时,分数条根据您定义的规则发生了变化。

5.png

6.png

7.png

您已达到目标!!!

8.png

整个应用程序由 4 个管理器控制

  1. Kinect 管理器
  2. Configuration Manager
  3. 视频管理器
  4. Authentic Manager

Kinect 管理器

我在之前的帖子中已经多次演示过如何开始使用 Kinect,但对于这个应用程序,我创建了一个单例类来处理所有与 Kinect 设置相关的内容,从重启 Kinect RunTime 对象到更改摄像头角度。

public KinectManager()
{
    try
    {

    KinectNui = new Runtime();

    KinectNui.Initialize(RuntimeOptions.UseColor | RuntimeOptions.UseSkeletalTracking |
                            RuntimeOptions.UseColor);

    KinectNui.VideoStream.Open(ImageStreamType.Video, 2, 
    ImageResolution.Resolution640x480, 
            ImageType.ColorYuv);

    KinectNui.SkeletonEngine.TransformSmooth = true;
    var parameters = new TransformSmoothParameters
                            {
                            Smoothing = 1.0f,
                            Correction = 0.1f,
                            Prediction = 0.1f,
                            JitterRadius = 0.05f,
                            MaxDeviationRadius = 0.05f
                            };
    KinectNui.SkeletonEngine.SmoothParameters = parameters;

    _lastTime = DateTime.Now;
    Camera = KinectNui.NuiCamera;

    IsInitialize = true;
    StatusMessage = Properties.Resources.KinectReady;
    }
    catch (InvalidOperationException ex)
    {
    IsInitialize = false;
    StatusMessage = ex.Message;
    }
} 

KinectManager 拥有的另一个重要方法是 CameraAngle 控制。

public void ChangeCameraAngle(ChangeDirection dir)
{
    if (!IsInitialize) return;

    try
    {
    if (dir == ChangeDirection.Up)
        Camera.ElevationAngle = Camera.ElevationAngle +
        Properties.Settings.Default.ElevationAngleInterval;
    else
        Camera.ElevationAngle = Camera.ElevationAngle -
        Properties.Settings.Default.ElevationAngleInterval;

    StatusMessage = Properties.Resources.KinectReady;
    }
    catch (InvalidOperationException ex)
    {
    StatusMessage = ex.Message;
    }
    catch (ArgumentOutOfRangeException outOfRangeException)
    {
    StatusMessage = outOfRangeException.Message;
    }
}  

视频管理器

视频管理器有两种特定类型的视频要播放,以及主视频文件夹,可以每次随机选择一个视频。

特定?当用户进入 Kinect 范围时,您可以选择在 Kinect 图像出现之前播放特定视频,当用户离开 Kinect 范围时,您可以选择播放退出视频。

定义您要播放的视频类型。如果您请求退出视频但不存在,则返回 null - 停止视频并开始显示 Kinect 图像。如果您请求进入视频但不存在,则返回随机视频。

9.png

public static Uri GetVideo(VideoType type)
{
  if (string.IsNullOrEmpty(Properties.Settings.Default.VideosLibraryFolder) ||
      !Directory.Exists(Properties.Settings.Default.VideosLibraryFolder))
      return null;
  else
  {
      string value = null;
      switch (type)
      {
          case VideoType.In:
              value = Properties.Settings.Default.VideosLibraryInFile;
              return string.IsNullOrEmpty(value) || !File.Exists(value) ?
                  CollectRandomMovie() : new Uri(value);
          case VideoType.Out:
              value = Properties.Settings.Default.VideosLibraryOutFile;
              return string.IsNullOrEmpty(value) || !File.Exists(value) ?
                  null : new Uri(value);
          default:
              return CollectRandomMovie();
      }
  }
} 
 private static Uri CollectRandomMovie()
{
    var movies = new ArrayList();

    foreach (var filter in Properties.Settings.Default.VideoFilter)
        movies.AddRange(Directory.GetFiles(Properties.Settings.Default.VideosLibraryFolder,
            filter));

    if (movies.Count == 0) return null;

    var rnd = new Random();
    return new Uri(movies[rnd.Next(movies.Count)].ToString());
} 

Configuration Manager

Kinect Reception 允许您设置一个范围或规则来定义什么是“有趣”,规则基于关节到关节,每个规则定义了如果规则适用时的得分。

10.png

RuleObject 包含源关节和目标关节,用于检查两者的向量,运算符(大于或小于)和得分。

2.png

那么配置管理器做什么呢?它保存规则。(使用 MemoryStream Rule 转换为 string ,然后将其保存在应用程序资源中。)

public static ObservableCollection<RuleObject> Load()
{
    var deserializer = new XmlSerializer(typeof(ObservableCollection<RuleObject>));
    try
    {
        var xs = new XmlSerializer(typeof(ObservableCollection<RuleObject>));
        var memoryStream = 
    new MemoryStream(Encoding.UTF8.GetBytes(Properties.Settings.Default.Rules));
        var xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8);
        return (ObservableCollection<RuleObject>)xs.Deserialize(memoryStream);
    }
    catch (Exception)
    {
        return new ObservableCollection<RuleObject>();
    }
} 
public static void Save(ObservableCollection<RuleObject> items)
{
    try
    {
        var memoryStream = new MemoryStream();
        var xs = new XmlSerializer(typeof(ObservableCollection<RuleObject>));

        var xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8);
        xs.Serialize(xmlTextWriter, items);

        memoryStream = (MemoryStream)xmlTextWriter.BaseStream;
        var xmlizedString = Encoding.UTF8.GetString(memoryStream.ToArray());

        Properties.Settings.Default.Rules = xmlizedString;
    }
    catch (Exception ex)
    {
        throw new ArgumentException(ex.Message);
    }
} 

Authentic Manager

Authentic Manager 是 Kinect Reception 的核心,它将采用您定义的所有规则,并与骨骼关节进行比较。

下面的方法将提取未跟踪的关节,并确保关节的质量足以进行计算(我们不希望用户移出画面被视为有趣 Sad smile)。

如果骨骼关节达到您定义的最高得分,则会触发一个事件,通知主窗口保存当前图像并显示给用户。

public void ChecksForAuthentic(JointsCollection joints)
{
  if (_rules.Count == 0) return;

  var fixJoints =
      joints.Cast<Joint>().Where(
          joint => joint.Position.W >= 0.6f &&
              joint.TrackingState == JointTrackingState.Tracked).ToList();

  var sb = new StringBuilder();
  for (var index = 0; index < _rules.Count; index++)
  {
    var rule = _rules[index];
    var s = (from j in fixJoints.Where(joint => joint.ID == rule.Source) select j).
        DefaultIfEmpty(new Joint() { TrackingState = JointTrackingState.NotTracked }).
        Single();

    var t = (from j in fixJoints.Where(joint => joint.ID == rule.Target) select j).
        DefaultIfEmpty(new Joint() { TrackingState = JointTrackingState.NotTracked }).
        Single();

    if (s.TrackingState == JointTrackingState.NotTracked ||
        t.TrackingState == JointTrackingState.NotTracked) break;

    var sv = s.ToFloat(rule.SourceVector);
    var tv = t.ToFloat(rule.TargetVector);

    if (rule.Operator == Operators.Bigger && sv > tv)
    {
      Score = Score + rule.Score;
      sb.AppendLine(string.Format("Bigger -> Source: {0}, Target:{1} , Vector:{2}",
          rule.Source, rule.Target, rule.SourceVector));
    }
    else if (rule.Operator == Operators.Smaller && sv < tv)
    {
      Score = Score + rule.Score;
      sb.AppendLine(string.Format("Smaller -> Source: {0}, Target:{1} , Vector:{2}",
          rule.Source, rule.Target, rule.SourceVector));
    }
  }

  if (Score >= _goal)
    IsAuthentic(Score, sb.ToString());
} 

历史

  • 2011 年 9 月 2 日:初
© . All rights reserved.