首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >将代码从kinect sdk测试版转换为最新版本kinect sdk的完整指南

问将代码从kinect sdk测试版转换为最新版本kinect sdk的完整指南
EN

Stack Overflow用户
提问于 2012-11-14 22:23:26
回答 2查看 3.2K关注 0票数 2

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

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2012-11-14 22:48:05

我在这个post上找到了以下信息:

这里和以后的所有信息都归功于那篇文章的原始海报。我只是在分享他的知识

如果你在2月1日之前已经在使用Kinect SDK的Beta2,你可能会对v1中引入的大量API更改感到沮丧。

用于获取您以前编写的代码的左右关节

代码语言:javascript
复制
Joint jointRight = sd.Joints[JointID.HandRight];
Joint jointLeft = sd.Joints[JointID.HandLeft];

首先,您需要创建一个骨架,以便

Skeleton[] skeletons = new Skeleton[0];

然后你必须仔细检查一下骨架

代码语言:javascript
复制
 foreach (Skeleton skel in skeletons)

然后您可以使用以下命令获取关节

代码语言:javascript
复制
Joint rightHand = skeleton.Joints[JointType.HandRight];
Joint leftHand = skeleton.Joints[JointType.HandLeft];

对于相机提升,你通常会这样写

代码语言:javascript
复制
_nui.NuiCamera.ElevationAngle = 17;

现在,您只需使用您创建的传感器(下面将解释它是如何替换Runtime类的),并编写

代码语言:javascript
复制
sensor.ElevationAngle = 17;

操纵彩色图像帧这是以前要写的

代码语言:javascript
复制
    rawImage.Source = e.ColorImageFrame.ToBitmapSource();

现在,在执行上述操作之前,您必须打开彩色图像帧并检查是否返回了某些内容。转换为位图源也发生了变化。变换是这样的

代码语言:javascript
复制
 using (var videoFrame = e.OpenColorImageFrame())
            {
                if (videoFrame != null)
                {
                    var bits = new byte[videoFrame.PixelDataLength];
                    videoFrame.CopyPixelDataTo(bits);
                }
            }

然而,在将几个Kinect应用程序从Beta2移植到v1之后,我终于开始看到这些变化的模式。在大多数情况下,这只是一个用一组样板代码替换另一组样板代码的问题。代码的任何独特部分在很大程度上都可以保持原样。

在这篇文章中,我想演示五个简单的代码转换,它们将使你从Beta2到Kinect SDK v1的过程变得更容易。我将一个接一个地做样板片段。

名称空间已经被转移了。Microsoft.Research.Kinect.Nui现在只是Microsoft.Kinect。幸运的是,Visual Studio使得解析名称空间变得相对容易,所以我们可以继续前进。

Runtime类型是用于处理来自Kinect的数据流的控制器对象,现在称为KinectSensor类型。抓取它的实例也发生了变化。您过去只需要像这样新建一个实例:

代码语言:javascript
复制
Runtime nui = new Runtime();

现在,您可以从一个静态数组中获取KinectSensor的一个实例,该数组包含连接到PC上的所有KinectSensors。

代码语言:javascript
复制
KinectSensor sensor = KinectSensor.KinectSensors[0];

初始化KinectSensor对象以开始读取颜色流、深度流或骨架流也已更改。在beta 2中,初始化过程看起来不太像.NET-y。在v1中,这一点已经得到了戏剧性的清理。用于初始化深度和骨架流的beta 2代码如下所示:

代码语言:javascript
复制
_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中的相同代码:

代码语言:javascript
复制
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中平滑骨架流非常容易,只需打开它即可。

代码语言:javascript
复制
nui.SkeletonStream.TransformSmooth = true;

在v1中,您必须创建一个新的TransformSmoothParameters对象,并将其传递给骨架流的enable属性。与beta 2不同的是,您还必须自己初始化值,因为它们都默认为零。

代码语言:javascript
复制
sensor.SkeletonStream.Enable(
    new TransformSmoothParameters() 
    {   Correction = 0.5f
    , JitterRadius = 0.05f
    , MaxDeviationRadius = 0.04f
    , Smoothing = 0.5f });

流事件处理:处理来自深度流、视频流和骨架流的就绪事件也变得容易得多。以下是您在beta 2中处理DepthFrameReady事件的方式(骨架和视频遵循相同的模式):

代码语言:javascript
复制
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++应用程序接口有一点泄漏。在v1中,我们需要打开图像框并进行检查,以确保返回了某些内容。此外,我们创建自己的字节数组(对于深度流,这已经变成了一个短整型数组),并从frame对象填充它。你在beta 2中熟悉的PlanarImage类型已经完全消失了。还要注意using关键字来处理ImageFrame对象。上面代码的音译现在看起来像这样:

代码语言:javascript
复制
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 beta2的网站和库还没有移植到Kinect SDK v1。考虑到API似乎已经发生了很大的变化,我当然理解这种犹豫。

然而,如果你遵循这五个简单的翻译规则,你将能够非常快地转换大约80%的代码。

票数 3
EN

Stack Overflow用户

发布于 2012-11-15 04:33:54

使用最新的SDK,您的SkeletonFrameReady回调应该如下所示:

代码语言:javascript
复制
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枚举将单个关节拉出。

每个Skeleton都会返回JointCollections,可以通过调用Skeleton.Joints来访问。您可以参考单个关节的阵列,或将JointCollection保存为关闭以进行某些其他处理。

伸缩不是SDK特有的。缩放时,您将从Kinect获取真实世界坐标并将其映射到屏幕上。如何获得这些真实世界的坐标可能略有不同(即,如何访问骨架),但缩放本身没有什么不同。没有像myJoint.ScaleTo()这样用于缩放单个关节的内部函数。

Coding4Fun Library具有缩放功能,允许您将关节位置缩放到屏幕像素。或者,您可以编写自己的代码来满足特定需求,例如:

代码语言:javascript
复制
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;
}

或者类似这样的东西:

代码语言:javascript
复制
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视野中的任何位置,并以相同的方式操作对象。它创建的点击框对于普通用户来说也是非常舒适的。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/13380619

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档