1

Kinect を使用する XNA ゲームを開発しています。画面に映っているプレイヤーは、Kinect センサーの前でプレイしている人物の実像です。背景を削除してプレーヤーの画像のみを取得するために、次の操作を次のように行っていますkinect.AllFramesReady

using (ColorImageFrame colorVideoFrame = imageFrames.OpenColorImageFrame())
{
    if (colorVideoFrame != null)
    {
        //Getting the image of the colorVideoFrame to a Texture2D named colorVideo
    }
    //And setting its information on a Color array named colors with GetData
    colorVideo.GetData(colors); 
}

using (DepthImageFrame depthVideoFrame = imageFrames.OpenDepthImageFrame())
{
    if (depthVideoFrame != null){
        //Copying the the image to a DepthImagePixel array
        //Using only the pixels with PlayerIndex > 0 to create a Color array
        //And then setting the colors of this array from the 'colors' array by using MapDepthPointToColorPoint method, provided by Kinect SDK
        //Finally I use SetData function in order to set the colors to a Texture2D I created before
    }
}

しかし、パフォーマンスは驚くほど低いです。すべてのフレームGetDataで、長さ 640*480 = 307200 のカラー配列 ( のためColorImageFormat) とSetData長さ 320*480 = 76800 の別のカラー配列( のためDepthImageFormat)を使用する必要があるためです。

この問題に対する他の解決策SetDataGetDataおそらくの代替案があるのではないかと思います。GPU と CPU の間でデータを移動するこれらの関数は、ビッグ データにとってコストのかかる操作であることを知っているからです。助けてくれてありがとう。

4

1 に答える 1

1

Kinect for Windows Toolbox には「GreenScreen-WPF」の例が付属しており、情報の処理に関する洞察が得られるはずです。XNA で作業しているため、いくつかの違いがあるかもしれませんが、全体的な概念は 2 つの例の間で機能するはずです。

この例は、複数のプレーヤーを抽出することで機能します。処理関数のビジネス エンドは次のとおりです。

private void SensorAllFramesReady(object sender, AllFramesReadyEventArgs e)
{
    // in the middle of shutting down, so nothing to do
    if (null == this.sensor)
    {
        return;
    }

    bool depthReceived = false;
    bool colorReceived = false;

    using (DepthImageFrame depthFrame = e.OpenDepthImageFrame())
    {
        if (null != depthFrame)
        {
            // Copy the pixel data from the image to a temporary array
            depthFrame.CopyDepthImagePixelDataTo(this.depthPixels);

            depthReceived = true;
        }
    }

    using (ColorImageFrame colorFrame = e.OpenColorImageFrame())
    {
        if (null != colorFrame)
        {
            // Copy the pixel data from the image to a temporary array
            colorFrame.CopyPixelDataTo(this.colorPixels);

            colorReceived = true;
        }
    }

    // do our processing outside of the using block
    // so that we return resources to the kinect as soon as possible
    if (true == depthReceived)
    {
        this.sensor.CoordinateMapper.MapDepthFrameToColorFrame(
            DepthFormat,
            this.depthPixels,
            ColorFormat,
            this.colorCoordinates);

        Array.Clear(this.greenScreenPixelData, 0, this.greenScreenPixelData.Length);

        // loop over each row and column of the depth
        for (int y = 0; y < this.depthHeight; ++y)
        {
            for (int x = 0; x < this.depthWidth; ++x)
            {
                // calculate index into depth array
                int depthIndex = x + (y * this.depthWidth);

                DepthImagePixel depthPixel = this.depthPixels[depthIndex];

                int player = depthPixel.PlayerIndex;

                // if we're tracking a player for the current pixel, do green screen
                if (player > 0)
                {
                    // retrieve the depth to color mapping for the current depth pixel
                    ColorImagePoint colorImagePoint = this.colorCoordinates[depthIndex];

                    // scale color coordinates to depth resolution
                    int colorInDepthX = colorImagePoint.X / this.colorToDepthDivisor;
                    int colorInDepthY = colorImagePoint.Y / this.colorToDepthDivisor;

                    // make sure the depth pixel maps to a valid point in color space
                    // check y > 0 and y < depthHeight to make sure we don't write outside of the array
                    // check x > 0 instead of >= 0 since to fill gaps we set opaque current pixel plus the one to the left
                    // because of how the sensor works it is more correct to do it this way than to set to the right
                    if (colorInDepthX > 0 && colorInDepthX < this.depthWidth && colorInDepthY >= 0 && colorInDepthY < this.depthHeight)
                    {
                        // calculate index into the green screen pixel array
                        int greenScreenIndex = colorInDepthX + (colorInDepthY * this.depthWidth);

                        // set opaque
                        this.greenScreenPixelData[greenScreenIndex] = opaquePixelValue;

                        // compensate for depth/color not corresponding exactly by setting the pixel 
                        // to the left to opaque as well
                        this.greenScreenPixelData[greenScreenIndex - 1] = opaquePixelValue;
                    }
                }
            }
        }
    }

    // do our processing outside of the using block
    // so that we return resources to the kinect as soon as possible
    if (true == colorReceived)
    {
        // Write the pixel data into our bitmap
        this.colorBitmap.WritePixels(
            new Int32Rect(0, 0, this.colorBitmap.PixelWidth, this.colorBitmap.PixelHeight),
            this.colorPixels,
            this.colorBitmap.PixelWidth * sizeof(int),
            0);

        if (this.playerOpacityMaskImage == null)
        {
            this.playerOpacityMaskImage = new WriteableBitmap(
                this.depthWidth,
                this.depthHeight,
                96,
                96,
                PixelFormats.Bgra32,
                null);

            MaskedColor.OpacityMask = new ImageBrush { ImageSource = this.playerOpacityMaskImage };
        }

        this.playerOpacityMaskImage.WritePixels(
            new Int32Rect(0, 0, this.depthWidth, this.depthHeight),
            this.greenScreenPixelData,
            this.depthWidth * ((this.playerOpacityMaskImage.Format.BitsPerPixel + 7) / 8),
            0);
    }
}

1 人のプレーヤーのみに関心がある場合は、プレーヤー マスクを使用して、適切なピクセル セットをより迅速に抽出することを検討できます。あなたはFiだろう

using (SkeletonFrame skeletonFrame = e.OpenSkeletonFrame())
{
    if (skeletonFrame != null && skeletonFrame.SkeletonArrayLength > 0)
    {
        if (_skeletons == null || _skeletons.Length != skeletonFrame.SkeletonArrayLength)
        {
            _skeletons = new Skeleton[skeletonFrame.SkeletonArrayLength];
        }

        skeletonFrame.CopySkeletonDataTo(_skeletons);

        // grab the tracked skeleton and set the playerIndex for use pulling
        // the depth data out for the silhouette.
        this.playerIndex = -1;
        for (int i = 0; i < _skeletons.Length; i++)
        {
            if (_skeletons[i].TrackingState != SkeletonTrackingState.NotTracked)
            {
                this.playerIndex = i+1;
            }
        }
    }
}

その後、深度データをステップスルーして、適切なビットを抽出できます。

depthFrame.CopyPixelDataTo(this.pixelData);

for (int i16 = 0, i32 = 0; i16 < pixelData.Length && i32 < depthFrame32.Length; i16++, i32 += 4)
{
    int player = pixelData[i16] & DepthImageFrame.PlayerIndexBitmask;
    if (player == this.playerIndex)
    {
        // the player we are tracking
    }
    else if (player > 0)
    {
        // a player, but not the one we want.
    }
    else
    {
        // background or something else we don't care about
    }
}

このコードは、シルエットを作成するために使用するコントロールから取得しているため、カラー ストリームは処理されません。ただし、適切なタイミングで を呼び出すMapDepthFrameToColorFrameと、カラー ストリーム データを処理し、対応するピクセルをプレーヤーのマスクに抽出できるようになります。

于 2013-02-15T20:36:13.873 に答える