将代码从kinect sdk beta转换为最新的kinect sdk的完整指南

时间:2012-11-14 14:23:26

标签: c# .net kinect kinect-sdk

我有一个带kinect的学期项目。我必须改进某个应用程序并为其添加新功能。问题出现的原因是应用程序使用过时的kinect sdk。我希望添加的一些额外功能(个人)需要使用新的Kinect SDK。有关从Kinect SDK Beta转移到最新SDK的快速指南吗? 除了大会参考文献之外还做了哪些改变?

2 个答案:

答案 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];
        }
    }
}

请注意SkeletonDataJointID不再存在。您将获得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视野中的任何位置并以相同方式操纵对象。它创建的命中框对于普通用户来说也非常舒适。