我有一个带kinect的学期项目。我必须改进某个应用程序并为其添加新功能。问题出现的原因是应用程序使用过时的kinect sdk。我希望添加的一些额外功能(个人)需要使用新的Kinect SDK。有关从Kinect SDK Beta转移到最新SDK的快速指南吗? 除了大会参考文献之外还做了哪些改变?
答案 0 :(得分:3)
我在post上找到了以下信息:
此处及以后的所有信息都归于该文章的原始海报。我只是分享他的知识
如果您在2月1日之前使用过Kinect SDK的beta 2,那么您可能会对v1中引入的API更改数量感到沮丧。
为了获得右边和左边关节,你用来编写代码
Joint jointRight = sd.Joints[JointID.HandRight];
Joint jointLeft = sd.Joints[JointID.HandLeft];
首先你需要创建一个骨架
Skeleton[] skeletons = new Skeleton[0];
然后你必须越过骨架
foreach (Skeleton skel in skeletons)
然后使用
获得关节Joint rightHand = skeleton.Joints[JointType.HandRight];
Joint leftHand = skeleton.Joints[JointType.HandLeft];
用于编写此
的相机高程_nui.NuiCamera.ElevationAngle = 17;
现在您只需使用您创建的传感器(下面介绍它如何替换Runtime类)并编写
sensor.ElevationAngle = 17;
操纵彩色图像帧 这是必须在
之前编写的内容 rawImage.Source = e.ColorImageFrame.ToBitmapSource();
现在你必须打开colorimage框架并在执行上述操作之前检查是否有东西返回。转换为位图源也发生了变化。转型就像这样
using (var videoFrame = e.OpenColorImageFrame())
{
if (videoFrame != null)
{
var bits = new byte[videoFrame.PixelDataLength];
videoFrame.CopyPixelDataTo(bits);
}
}
然而,在将几个Kinect应用程序从beta 2移植到v1之后,我终于开始看到这些更改的模式。在大多数情况下,只需将一组样板代码替换为另一组样板代码即可。代码的任何独特部分在很大程度上都可以单独使用。
在这篇文章中,我想演示五个简单的代码转换,这些转换将简化从beta 2到Kinect SDK v1的方式。我将通过样板片段来做它的样板片段。
命名空间已被转移。 Microsoft.Research.Kinect.Nui现在只是Microsoft.Kinect。幸运的是,Visual Studio使得解析命名空间变得相对容易,因此我们可以继续前进。
Runtime类型是用于处理来自Kinect的数据流的控制器对象,现在称为KinectSensor类型。抓住它的一个实例也发生了变化。您曾经只是新建一个这样的实例:
Runtime nui = new Runtime();
现在,您可以从包含连接到PC的所有KinectSensors的静态数组中获取KinectSensor的实例。
KinectSensor sensor = KinectSensor.KinectSensors[0];
初始化KinectSensor对象以开始读取颜色流,深度流或骨架流也已更改。在beta 2中,初始化过程看起来并不像.NET-y。在v1中,这已经被大大清理干净了。用于初始化深度和骨架流的beta 2代码如下所示:
_nui.SkeletonFrameReady += new EventHandler( _nui_SkeletonFrameReady ); _nui.DepthFrameReady += new EventHandler( _nui_DepthFrameReady ); _nui.Initialize(RuntimeOptions.UseDepth, RuntimeOptions.UseSkeletalTracking); _nui.DepthStream.Open(ImageStreamType.Depth , 2 , ImageResolution.Resolution320x240 , ImageType.DepthAndPlayerIndex);
在v1中,此样板代码已被更改,因此Initialize方法消失,大致被Start方法取代。反过来,流上的Open方法已被Enable取代。只需启用骨架流即可使DepthAndPlayerIndex数据可用。另请注意,深度和颜色流的事件参数类型现在不同。这是v1中的相同代码:
sensor.SkeletonFrameReady +=
new EventHandler<SkeletonFrameReadyEventArgs>(
sensor_SkeletonFrameReady
);
sensor.DepthFrameReady +=
new EventHandler<DepthImageFrameReadyEventArgs>(
sensor_DepthFrameReady
);
sensor.SkeletonStream.Enable();
sensor.DepthStream.Enable(
DepthImageFormat.Resolution320x240Fps30
);
sensor.Start();
变换平滑:过去很容易在beta 2中平滑骨架流。你只需打开它。
nui.SkeletonStream.TransformSmooth = true;
在v1中,您必须创建一个新的TransformSmoothParameters对象并将其传递给骨架流的enable属性。与beta 2不同,您还必须自己初始化值,因为它们都默认为零。
sensor.SkeletonStream.Enable(
new TransformSmoothParameters()
{ Correction = 0.5f
, JitterRadius = 0.05f
, MaxDeviationRadius = 0.04f
, Smoothing = 0.5f });
流事件处理:处理来自深度流,视频流和骨架流的就绪事件也变得更加容易。以下是您在beta 2中处理DepthFrameReady事件的方式(骨架和视频遵循相同的模式):
void _nui_DepthFrameReady(object sender , ImageFrameReadyEventArgs e) { var frame = e.ImageFrame; var planarImage = frame.Image; var bits = planarImage.Bits; // your code goes here }
出于性能原因,较新的v1代码看起来非常不同,底层的C ++ API会漏掉一些。在v1中,我们需要打开图像框并检查以确保返回了某些内容。另外,我们创建自己的字节数组(对于深度流,这已成为一个短裤数组)并从帧对象填充它。在beta 2中你可能已经熟悉的PlanarImage类型完全消失了。还要注意使用using关键字来处理ImageFrame对象。上面代码的音译现在看起来像这样:
void sensor_DepthFrameReady(object sender
, DepthImageFrameReadyEventArgs e)
{
using (var depthFrame = e.OpenDepthImageFrame())
{
if (depthFrame != null)
{
var bits =
new short[depthFrame.PixelDataLength];
depthFrame.CopyPixelDataTo(bits);
// your code goes here
}
}
}
我注意到许多使用Kinect SDK beta 2的网站和库仍然没有移植到Kinect SDK v1。鉴于API似乎已经发生了多大变化,我当然理解犹豫不决。
但是,如果您遵循这五个简单的翻译规则,您将能够非常快速地转换大约80%的代码。
答案 1 :(得分:1)
使用最新的SDK,您的SkeletonFrameReady
回调应如下所示:
private Skeleton[] _skeletons = new Skeleton[0];
private void OnSkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e)
{
using (SkeletonFrame skeletonFrame = e.OpenSkeletonFrame())
{
if (skeletonFrame == null || skeletonFrame.SkeletonArrayLength == 0)
return;
// resize the skeletons array if needed
if (_skeletons.Length != skeletonFrame.SkeletonArrayLength)
_skeletons = new Skeleton[skeletonFrame.SkeletonArrayLength];
// get the skeleton data
skeletonFrame.CopySkeletonDataTo(_skeletons);
foreach (var skeleton in _skeletons)
{
// skip the skeleton if it is not being tracked
if (skeleton.TrackingState != SkeletonTrackingState.Tracked)
continue;
leftElbow = skeleton.Joints[JointType.ElbowLeft];
rightHand = skeleton.Joints[JointType.HandRight];
}
}
}
请注意SkeletonData
和JointID
不再存在。您将获得Skeleton
个对象的集合,每个对象都有一个Joints
数组。您可以使用JointType
枚举来拉出单个关节。
JointCollections
都会返回 Skeleton
,可以通过调用Skeleton.Joints
来访问。您可以为单个关节引用数组,或者将JointCollection
保存为其他处理。
缩放不是特定于SDK。缩放时,您将从Kinect获取实际坐标并将其编辑到屏幕上。如何获得这些真实世界的坐标可能会略有不同(即,您如何访问骨架),但缩放本身也不例外。没有内部函数来缩放像myJoint.ScaleTo()
这样的单个关节。
Coding4Fun Library具有缩放功能,可以将关节位置缩放到屏幕像素。或者,您可以自己编写以满足特定需求,例如:
private static double ScaleY(Joint joint)
{
double y = ((SystemParameters.PrimaryScreenHeight / 0.4) * -joint.Position.Y) + (SystemParameters.PrimaryScreenHeight / 2);
return y;
}
private static void ScaleXY(Joint shoulderCenter, bool rightHand, Joint joint, out int scaledX, out int scaledY)
{
double screenWidth = SystemParameters.PrimaryScreenWidth;
double x = 0;
double y = ScaleY(joint);
// if rightHand then place shouldCenter on left of screen
// else place shouldCenter on right of screen
if (rightHand)
{
x = (joint.Position.X - shoulderCenter.Position.X) * screenWidth * 2;
}
else
{
x = screenWidth - ((shoulderCenter.Position.X - joint.Position.X) * (screenWidth * 2));
}
if (x < 0)
{
x = 0;
}
else if (x > screenWidth - 5)
{
x = screenWidth - 5;
}
if (y < 0)
{
y = 0;
}
scaledX = (int)x;
scaledY = (int)y;
}
或类似的东西:
double xScaled = (rightHand.Position.X - leftShoulder.Position.X) / ((rightShoulder.Position.X - leftShoulder.Position.X) * 2) * SystemParameters.PrimaryScreenWidth;
double yScaled = (rightHand.Position.Y - head.Position.Y) / (rightHip.Position.Y - head.Position.Y) * SystemParameters.PrimaryScreenHeight;
对于缩放,您所做的只是定义实词中的位置(ala:Kinect坐标)等于屏幕的左侧,右侧,顶部和底部。您只是告诉应用程序“此Kinect坐标等于此屏幕像素”。
是否需要缩放?
为了与屏幕上的对象进行交互,需要进行某种缩放。 Kinect以米为单位返回相对于其视野的值。如果没有扩展,它将不是一个可用的系统。
请记住,缩放对于Kinect或旧版SDK而言都不是唯一的。您有一个正在使用的坐标系,以及需要转换为的另一个坐标系。发生在许多不同的情况。你在做什么是说一个坐标系中的“this”位置等于“那个”定位另一个坐标系。
有两种基本方法可以决定现实世界中的哪个位置等于像素。
一种是采用Kinect的坐标系,只需将其映射到屏幕即可。这意味着Kinect中的0,0等于屏幕上的0,0。然后,您可以获取Kinect系统的外部边界并将它们映射到屏幕分辨率。
我不推荐这个。它创造了一个非常大的工作空间,并会使用户感到沮丧。
另一种方法是创建一个“命中框”。看看我上面的两行翻译。这会在身体周围形成一个击球盒。使用右手,屏幕左侧等于左肩的X线;屏幕的右侧距右肩右侧很短(这是你的右肩的x-coord加上两肩之间的距离)。屏幕的垂直位置映射在头部和臀部之间。
此方法允许用户站在Kinect视野中的任何位置并以相同方式操纵对象。它创建的命中框对于普通用户来说也非常舒适。