このエントリはKINECT SDK Advent Calendar 2011 : ATNDの12月9日分です
Advent Calendarでの、僕の全プロジェクトはこちらです
KINECTの特長の一つであるユーザーの検出を、KINECT SDKでやってみます。
ユーザーをこんな感じに色付けします。
見た目の解説
いつも通り、Imageを一つはっつけてます
<Window x:Class="UserWPF.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="521" Width="661" Closing="Window_Closing"> <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; using System.Threading; using System.Windows.Threading; namespace UserWPF { /// <summary> /// MainWindow.xaml の相互作用ロジック /// </summary> public partial class MainWindow : Window { private Thread readerThread; private bool shouldRun; public MainWindow() { try { InitializeComponent(); Runtime kinect = Runtime.Kinects[0]; kinect.Initialize( RuntimeOptions.UseColor | RuntimeOptions.UseDepthAndPlayerIndex ); kinect.VideoStream.Open( ImageStreamType.Video, 2, ImageResolution.Resolution640x480, ImageType.Color ); kinect.DepthStream.Open( ImageStreamType.Depth, 2, ImageResolution.Resolution320x240, ImageType.DepthAndPlayerIndex ); shouldRun = true; readerThread = new Thread( new ThreadStart( RenderThread ) ); readerThread.Start(); } catch ( Exception ex ) { MessageBox.Show( ex.Message ); } } // ユーザーにつける色 static Color[] userColor= new Color[] { Color.FromRgb( 0, 0, 0 ), // ユーザーなし Colors.Red, Colors.Green, Colors.Blue, Colors.Yellow, Colors.Magenta }; void RenderThread() { while ( shouldRun ) { Runtime kinect = Runtime.Kinects[0]; var video = kinect.VideoStream.GetNextFrame( 100 ); var depth = kinect.DepthStream.GetNextFrame( 100 ); // ピクセルごとのユーザーIDを取得する for ( int y = 0; y < depth.Image.Height; y++ ) { for ( int x = 0; x < depth.Image.Width; x++ ) { int index = (x + (y * depth.Image.Width)) * 2; byte byte0 = depth.Image.Bits[index]; byte byte1 = depth.Image.Bits[index + 1]; // ユーザーIDと距離を取得する int playerIndex = byte0 & 0x7; int distance = byte1 << 5 | byte0 >> 3; if ( playerIndex != 0 ) { // 距離座標をカメラ座標に変換する int videoX = 0, videoY = 0; kinect.NuiCamera.GetColorPixelCoordinatesFromDepthPixel( ImageResolution.Resolution640x480, new ImageViewArea(), x, y, 0, out videoX, out videoY ); int videoIndex = (videoX + (videoY * video.Image.Width)) * video.Image.BytesPerPixel; videoIndex = Math.Min( videoIndex, video.Image.Bits.Length - video.Image.BytesPerPixel ); video.Image.Bits[videoIndex] = userColor[playerIndex].R; video.Image.Bits[videoIndex + 1] = userColor[playerIndex].G; video.Image.Bits[videoIndex + 2] = userColor[playerIndex].B; } } } this.Dispatcher.BeginInvoke( DispatcherPriority.Background, new Action( () => { var bitmap = video.Image; image1.Source = BitmapImage.Create( bitmap.Width, bitmap.Height, 96, 96, PixelFormats.Bgr32, null, bitmap.Bits, bitmap.Width * bitmap.BytesPerPixel ); } ) ); } } private void Window_Closing( object sender, System.ComponentModel.CancelEventArgs e ) { shouldRun = false; } } }
初期化
RGBカメラと距離カメラおよびユーザーインデックスを使用します。ユーザーインデックスを使用数場合は、Depthの解像度は320x240になります。また、ImageTypeもDepthAndPlayerIndex を指定します。
Runtime kinect = Runtime.Kinects[0]; kinect.Initialize( RuntimeOptions.UseColor | RuntimeOptions.UseDepthAndPlayerIndex ); kinect.VideoStream.Open( ImageStreamType.Video, 2, ImageResolution.Resolution640x480, ImageType.Color ); kinect.DepthStream.Open( ImageStreamType.Depth, 2, ImageResolution.Resolution320x240, ImageType.DepthAndPlayerIndex );
データの更新
RGBカメラと、距離カメラのデータを同期して取得したいので、イベントではなくポーリングで取得します。
Runtime kinect = Runtime.Kinects[0]; var video = kinect.VideoStream.GetNextFrame( 100 ); var depth = kinect.DepthStream.GetNextFrame( 100 );
ユーザーの認識
距離データのバイト列から、距離データそのものと、ユーザーインデックスを取り出します。
データはそれぞれ、このように取り出します。
int playerIndex = byte0 & 0x7; int distance = byte1 << 5 | byte0 >> 3;
次に、そのデータを使ってユーザーの座標を塗りつぶします。RGBカメラと距離カメラのズレを補正するために、GetColorPixelCoordinatesFromDepthPixelを使っています。
詳しくはこちら
// ピクセルごとのユーザーIDを取得する for ( int y = 0; y < depth.Image.Height; y++ ) { for ( int x = 0; x < depth.Image.Width; x++ ) { int index = (x + (y * depth.Image.Width)) * 2; byte byte0 = depth.Image.Bits[index]; byte byte1 = depth.Image.Bits[index + 1]; // ユーザーIDと距離を取得する int playerIndex = byte0 & 0x7; int distance = byte1 << 5 | byte0 >> 3; if ( playerIndex != 0 ) { // 距離座標をカメラ座標に変換する int videoX = 0, videoY = 0; kinect.NuiCamera.GetColorPixelCoordinatesFromDepthPixel( ImageResolution.Resolution640x480, new ImageViewArea(), x, y, 0, out videoX, out videoY ); int videoIndex = (videoX + (videoY * video.Image.Width)) * video.Image.BytesPerPixel; videoIndex = Math.Min( videoIndex, video.Image.Bits.Length - video.Image.BytesPerPixel ); video.Image.Bits[videoIndex] = userColor[playerIndex].R; video.Image.Bits[videoIndex + 1] = userColor[playerIndex].G; video.Image.Bits[videoIndex + 2] = userColor[playerIndex].B; } } }