Kinect 接待处






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

引言
构思: 迎宾区的电视屏幕,当无人时(没有人进入迎宾区)我们可以播放视频,但当有人进入画面时,显示 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 – 入门 – 变身绿巨人浩克)。
什么是关节?
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
,则该规则的分数是多少。
背景
自从微软发布了 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 个管理器控制
- Kinect 管理器
- Configuration Manager
- 视频管理器
- 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 图像。如果您请求进入视频但不存在,则返回随机视频。
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 允许您设置一个范围或规则来定义什么是“有趣”,规则基于关节到关节,每个规则定义了如果规则适用时的得分。RuleObject
包含源关节和目标关节,用于检查两者的向量,运算符(大于或小于)和得分。
那么配置管理器做什么呢?它保存规则。(使用 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 的核心,它将采用您定义的所有规则,并与骨骼关节进行比较。
下面的方法将提取未跟踪的关节,并确保关节的质量足以进行计算(我们不希望用户移出画面被视为有趣 )。
如果骨骼关节达到您定义的最高得分,则会触发一个事件,通知主窗口保存当前图像并显示给用户。
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 日:初版