2

私はkinectを使った学期プロジェクトを持っています。特定のアプリを改善し、新しい機能を追加する必要があります。この問題は、アプリが古い kinect SDK を使用しているために発生します。私が (個人的に) 追加したい追加機能のいくつかは、新しい Kinect SDK を使用する必要があります。Kinect SDK Beta から最新の SDK への移行に関するクイック ガイドはありますか? アセンブリ参照以外にどのような変更が加えられましたか?

4

2 に答える 2

3

この投稿から次の情報を見つけました。

ここ以降の情報のすべての功績は、その記事の元のポスターにあります. 私は単に彼の知識を共有しています

2 月 1 日より前に Kinect SDK のベータ 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 アプリケーションをベータ 2 から v1 に移植した後、最終的に変更のパターンが見え始めました。ほとんどの場合、定型コードのセットを別の定型コードのセットに置き換えるだけです。コードの固有部分は、ほとんどの場合、そのままにしておくことができます。

この投稿では、ベータ 2 から Kinect SDK v1 への移行を容易にする 5 つの単純なコード変換を紹介したいと思います。ボイラープレート フラグメントごとにボイラープレート フラグメントを実行します。

名前空間が移動されました。Microsoft.Research.Kinect.Nui は Microsoft.Kinect になりました。さいわい、Visual Studio では名前空間を比較的簡単に解決できるので、先に進むことができます。

Kinect からのデータ ストリームを操作するためのコントローラー オブジェクトである Runtime 型は、KinectSensor 型と呼ばれるようになりました。インスタンスの取得も変更されました。以前は、次のようにインスタンスを新しく作成していました。

Runtime nui = new Runtime();

代わりに、PC に接続されているすべての KinectSensor を含む静的配列から KinectSensor のインスタンスを取得します。

KinectSensor sensor = KinectSensor.KinectSensors[0];

カラー ストリーム、深度ストリーム、またはスケルトン ストリームの読み取りを開始するための KinectSensor オブジェクトの初期化も変更されました。Beta 2 では、初期化手順が .NET らしくありませんでした。v1 では、これが大幅にクリーンアップされました。深度とスケルトン ストリームを初期化するベータ 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();

変換スムージング: ベータ 2 では、スケルトン ストリームをスムーズにするのは非常に簡単でした。オンにするだけです。

nui.SkeletonStream.TransformSmooth = true;

v1 では、新しい TransformSmoothParameters オブジェクトを作成し、それをスケルトン ストリームの enable プロパティに渡す必要があります。beta 2 とは異なり、値はすべてデフォルトで 0 になるため、値を自分で初期化する必要があります。

sensor.SkeletonStream.Enable(
    new TransformSmoothParameters() 
    {   Correction = 0.5f
    , JitterRadius = 0.05f
    , MaxDeviationRadius = 0.04f
    , Smoothing = 0.5f });

ストリーム イベント処理: 深度ストリーム、ビデオ ストリーム、およびスケルトン ストリームからの準備完了イベントの処理も、以前ははるかに簡単でした。ベータ 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 では、画像フレームを開いて、何かが返されたことを確認する必要があります。さらに、独自のバイト配列を作成し (深度ストリームの場合、これは short の配列になっています)、フレーム オブジェクトから入力します。ベータ 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 ベータ 2 を使用していた多くのサイトとライブラリがまだ Kinect SDK v1 に移植されていないことに気付きました。API がどれだけ変更されたように見えるかを考えると、ためらいは確かに理解できます。

ただし、これらの 5 つの簡単な変換ルールに従えば、コードの約 80% を非常に迅速に変換できます。

于 2012-11-14T14:48:04.693 に答える
1

With the latest SDK your SkeletonFrameReady callback should look something like this:

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

Notice that SkeletonData and JointID no longer exists. You get a collection of Skeleton objects, each with a Joints array. You can pull individual joints out using the JointType enum.

JointCollections are returned for each Skeleton and can be accessed by calling Skeleton.Joints. You can reference the array for an individual joint, or save the JointCollection off for some other processing.

Scaling is not specific to the SDK. When scaling, you are taking a real-world coordinate from the Kinect and maping it onto the screen. How you get those real-world coordinates might be slightly different (i.e., how you access the skeletons) but the scaling itself is no different. There is no internal function to scale an individual joint like myJoint.ScaleTo().

The Coding4Fun Library has a scaling function that will allow you to scale joints positions to screen pixels. Alternatively you can write your own to match a specific need, such as:

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;
}

Or something like this:

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;

For scaling, all you are doing is defining where in the real-word (ala: the Kinect coordinates) equals the left, right, top and bottom of your screen. You are just telling your application that "this Kinect coordinate is equal to this screen pixel".

IS SCALING NEEDED?

Some sort of scaling is required in order to interact with objects on the screen. The Kinect returns values in meters, relative to its field of view. It would not be a usable system without scaling.

Remember that scaling is nothing unique to the Kinect or to the old vs. new SDK. You have one coordinate system that you are working in, and another coordinate system you need to translate to. Happens in lots of different situations. What you are doing is saying that "this" position in one coordinate system is equal to "that" position the other coordinate system.

There are two basic ways to decide what position in the real world is equal a pixel.

One is to take the Kinect's coordinate system and just map it to the screen. This means that 0,0 in the Kinect is equal to 0,0 on the screen. You then take the outer bounds of the Kinect's system and map them to the screens resolution.

I do not recommend this. It creates a very large space to work in and will frustrate users.

Another way is to create a "hit box". Have a look at the two line translation I do above. This creates a hit box around the body to work in. Using the right hand, the left side of the screen is equal to the x-cord of your left shoulder; the right side of the screen is a short distance to the right of your right shoulder (it is your right shoulder's x-coord plus the distance between your two shoulders). The vertical position of the screen is mapped between your head and hips.

This method allows the user to stand anywhere in the Kinect's field of view and manipulate objects in the same way. The hit box it creates is also very comfortable to work in for your average user.

于 2012-11-14T20:33:54.110 に答える