2

我有一个关于 kinect 的学期项目。我必须改进某个应用程序并为其添加新功能。问题的出现是由于该应用程序使用了过时的 kinect sdk。我希望(个人)添加的一些额外功能需要使用新的 Kinect SDK。是否有关于从 Kinect SDK Beta 转移到最新 SDK 的快速指南?除了程序集引用之外,还做了哪些更改?

4

2 回答 2

3

我从这篇文章中找到了以下信息:

此处及以后信息的所有功劳归于该文章的原始海报。我只是分享他的知识

如果您在 2 月 1 日之前使用过 Kinect SDK 的 beta 2,您可能会对 v1.0 中引入的 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 的所有 KinectSensor 的静态数组中获取 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 类型已经完全消失了。还要注意处理 ImageFrame 对象的 using 关键字。上面代码的音译现在看起来是这样的:

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% 的代码。

于 2012-11-14T14:48:04.693 回答
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具有缩放功能,可让您将关节位置缩放到屏幕像素。或者,您可以自己编写以满足特定需求,例如:

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 独有的。您有一个正在工作的坐标系,以及需要转换到的另一个坐标系。发生在许多不同的情况下。您正在做的是说一个坐标系中的“这个”位置等于另一个坐标系中的“那个”位置。

有两种基本方法可以确定现实世界中的哪个位置等于一个像素。

一种是采用 Kinect 的坐标系并将其映射到屏幕上。这意味着 Kinect 中的 0,0 等于屏幕上的 0,0。然后,您获取 Kinect 系统的外部边界并将它们映射到屏幕分辨率。

我不推荐这个。它创造了一个非常大的工作空间,并且会让用户感到沮丧。

另一种方法是创建一个“命中框”。看看我上面做的两行翻译。这会在身体周围创建一个击球框以进行工作。使用右手,屏幕的左侧等于左肩的 x 线;屏幕右侧是您右肩右侧的一小段距离(它是您右肩的 x 坐标加上您两个肩膀之间的距离)。屏幕的垂直位置映射在您的头部和臀部之间。

此方法允许用户站在 Kinect 视野中的任何位置,并以相同的方式操作对象。它创建的命中框也非常适合普通用户使用。

于 2012-11-14T20:33:54.110 回答