読者です 読者をやめる 読者になる 読者になる

Natural Software

KinectなどのDepthセンサーを中心に活動しています

KINECT SDK Beta2 で距離データを扱う( C# + WPF ) #kinectsdk_ac

Kinect

このエントリはKINECT SDK Advent Calendar 2011 : ATNDの12月5日分です!!
Advent Calendarでの、僕の全プロジェクトはこちらです


前回までで、RGBカメラのデータを扱ったので、今回はKINECTの特長の一つである距離カメラを使ってみます。
距離データからこんな画像を表示してみましょう。

見た目の解説

前回同様、Imageを一つはっつけてます

<Window x:Class="DepthWPF.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="521" Width="663">
    <Grid>
        <Image Height="480" HorizontalAlignment="Left" Name="image1" Stretch="Fill" VerticalAlignment="Top" Width="640
               " />
    </Grid>
</Window>

中身の解説

using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Microsoft.Research.Kinect.Nui;

namespace DepthWPF
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            // Depthのみを使う設定で初期化
            Runtime kinect = Runtime.Kinects[0];
            kinect.Initialize( RuntimeOptions.UseDepth );
            kinect.DepthStream.Open( ImageStreamType.Depth, 2,
                ImageResolution.Resolution640x480, ImageType.Depth );
            kinect.DepthFrameReady +=
                new EventHandler<ImageFrameReadyEventArgs>( kinect_DepthFrameReady );
        }

        void kinect_DepthFrameReady( object sender, ImageFrameReadyEventArgs e )
        {
            var source = e.ImageFrame.Image;
            image1.Source = BitmapSource.Create( source.Width, source.Height, 96, 96,
                    PixelFormats.Gray16, null, ConvertGrayScale( source ).Bits,
                    source.Width * source.BytesPerPixel );
        }

        // 距離データをグレースケールに変換する
        private static PlanarImage ConvertGrayScale( PlanarImage source )
        {
            // thanks : http://www.atmarkit.co.jp/fdotnet/kinectsdkbeta/kinectsdkbeta02/kinectsdkbeta02_02.html
            for ( int i = 0; i < source.Bits.Length; i += 2 ) {
                // Depthのみ(ユーザーデータなし)のデータ取得
                ushort depth = (ushort)(source.Bits[i] | (source.Bits[i+1] << 8));

                // 0x0fff(Depthの最大値)を0-0xffffの間に変換する
                depth = (ushort)(0xffff - (0xffff * depth / 0x0fff));

                // 戻す
                source.Bits[i] = (byte)(depth & 0xff);
                source.Bits[i+1] = (byte)((depth >> 8) & 0xff);
            }

            return source;
        }
    }
}
初期化

Depthのみを使う設定で初期化します。

  • Runtime.InitializeでRuntimeOptions.UseDepthを設定
  • Runtime.DepthStream.OpenでImageStreamType.Depthを設定。DepthのみなのでResolutionは640x480を指定(ユーザーデータが入ると320x240になる)
  • Runtime.DepthFrameReady でデータ更新イベントを設定
// Depthのみを使う設定で初期化
Runtime kinect = Runtime.Kinects[0];
kinect.Initialize( RuntimeOptions.UseDepth );
kinect.DepthStream.Open( ImageStreamType.Depth, 2,
    ImageResolution.Resolution640x480, ImageType.Depth );
kinect.DepthFrameReady +=
    new EventHandler<ImageFrameReadyEventArgs>( kinect_DepthFrameReady );
データの更新

RGBカメラと同様です。距離データをグレースケールに変換するために、ビット列をConvertGrayScaleメソッドに通しています。

void kinect_DepthFrameReady( object sender, ImageFrameReadyEventArgs e )
{
    var source = e.ImageFrame.Image;
    image1.Source = BitmapSource.Create( source.Width, source.Height, 96, 96,
            PixelFormats.Gray16, null, ConvertGrayScale( source ).Bits,
            source.Width * source.BytesPerPixel );
}
グレースケールへの変換

距離データを16bitのグレースケールに変換します。
変換はこちらを参考にしました。ただし、16bitの最大値なので、0-0xffff の間に設定しています

// 距離データをグレースケールに変換する
private static PlanarImage ConvertGrayScale( PlanarImage source )
{
    for ( int i = 0; i < source.Bits.Length; i += 2 ) {
        // Depthのみ(ユーザーデータなし)のデータ取得
        ushort depth = (ushort)(source.Bits[i] | (source.Bits[i+1] << 8));

        // 0x0fff(Depthの最大値)を0-0xffffの間に変換する
        depth = (ushort)(0xffff - (0xffff * depth / 0x0fff));

        // 戻す
        source.Bits[i] = (byte)(depth & 0xff);
        source.Bits[i+1] = (byte)((depth >> 8) & 0xff);
    }

    return source;
}
まとめ

このように簡単に距離データを扱うことができます。
距離そのものを扱う場合には

ushort depth = (ushort)(source.Bits[i] | (source.Bits[i+1] << 8));

のdepthを使えばOKです