このエントリはOpenNI Advent Calendar 2011 : ATNDの12月7日分です!!
Advent Calendarでの、僕の全プロジェクトはこちらです
OpenNI 1.4.0.2から骨格の追跡にポーズが不要になりました。
これを前提にすると、どこまでコードが短くなるのか試してみました。
比較対象のソースは、KINECTセンサープログラミングの骨格追跡にします
見た目の解説
特に変わらず、Imageを一つはっつけてます
<Window x:Class="SkeletonWPF.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.Diagnostics; using System.Runtime.InteropServices; using System.Threading; using System.Windows; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Threading; using OpenNI; namespace SkeletonWPF { /// <summary> /// MainWindow.xaml の相互作用ロジック /// </summary> public partial class MainWindow : Window { Context context; DepthGenerator depth; UserGenerator user; private Thread readerThread; private bool shouldRun; public MainWindow() { InitializeComponent(); try { // ContextとImageGeneratorの作成 ScriptNode node; context = Context.CreateFromXmlFile( "../../SamplesConfig.xml", out node ); context.GlobalMirror = false; depth = context.FindExistingNode( NodeType.Depth ) as DepthGenerator; user = new UserGenerator( context ); user.NewUser += new EventHandler<NewUserEventArgs>( user_NewUser ); if ( user.SkeletonCapability.DoesNeedPoseForCalibration ) { throw new Exception( "最新のOpenNIをインストールしてください" ); } user.SkeletonCapability.CalibrationStart += new EventHandler<CalibrationStartEventArgs>( SkeletonCapability_CalibrationStart ); user.SkeletonCapability.CalibrationComplete += new EventHandler<CalibrationProgressEventArgs>(SkeletonCapability_CalibrationComplete); user.SkeletonCapability.SetSkeletonProfile( SkeletonProfile.All ); context.StartGeneratingAll(); // 画像更新のためのスレッドを作成 shouldRun = true; readerThread = new Thread( new ThreadStart( ReaderThread ) ); readerThread.Start(); } catch ( Exception ex ) { MessageBox.Show( ex.Message ); } } void SkeletonCapability_CalibrationComplete( object sender, CalibrationProgressEventArgs e ) { // キャリブレーション成功 if ( e.Status == CalibrationStatus.OK ) { Trace.WriteLine( "Calibration Success:" + e.ID ); user.SkeletonCapability.StartTracking( e.ID ); } // キャリブレーション失敗 else { Trace.WriteLine( "Calibration Failed:" + e.ID ); } } void SkeletonCapability_CalibrationStart( object sender, CalibrationStartEventArgs e ) { Trace.WriteLine( "Calibration Start:" + e.ID ); } void user_NewUser( object sender, NewUserEventArgs e ) { Trace.WriteLine( "New User:" + e.ID ); user.SkeletonCapability.RequestCalibration( e.ID, true ); } private void Window_Closing( object sender, System.ComponentModel.CancelEventArgs e ) { shouldRun = false; } private void ReaderThread() { while ( shouldRun ) { context.WaitAndUpdateAll(); DepthMetaData depthMD = depth.GetMetaData(); this.Dispatcher.BeginInvoke( DispatcherPriority.Background, new Action( () => { // アンマネージド配列をマネージド配列に変換する Int16[] depthArray = new Int16[depthMD.XRes * depthMD.YRes]; Marshal.Copy( depthMD.DepthMapPtr, depthArray, 0, depthArray.Length ); for ( int i = 0; i < depthArray.Length; i++ ) { depthArray[i] = (Int16)(0xffff - (0xffff * depthArray[i] / depth.DeviceMaxDepth)); } // 描画可能なビットマップを作る // http://stackoverflow.com/questions/831860/generate-bitmapsource-from-uielement RenderTargetBitmap bitmap = new RenderTargetBitmap( depthMD.XRes, depthMD.YRes, 96, 96, PixelFormats.Default ); DrawingVisual drawingVisual = new DrawingVisual(); using ( DrawingContext drawingContext = drawingVisual.RenderOpen() ) { // グレースケールの描画 var xtion = BitmapSource.Create( depthMD.XRes, depthMD.YRes, 96, 96, PixelFormats.Gray16, null, depthArray, depthMD.XRes * depthMD.BytesPerPixel ); drawingContext.DrawImage( xtion, new Rect( 0, 0, depthMD.XRes, depthMD.YRes ) ); // 骨格の描画 var users = user.GetUsers(); foreach ( var u in users ) { if ( !user.SkeletonCapability.IsTracking( u ) ) { continue; } foreach ( SkeletonJoint s in Enum.GetValues( typeof( SkeletonJoint ) ) ) { if ( !user.SkeletonCapability.IsJointAvailable( s ) ) { continue; } var joint = user.SkeletonCapability.GetSkeletonJoint( u, s ); var point = depth.ConvertRealWorldToProjective( joint.Position.Position ); drawingContext.DrawEllipse( new SolidColorBrush( Colors.Red ), new Pen( Brushes.Red, 1 ), new Point( point.X, point.Y ), 5, 5 ); } } } bitmap.Render( drawingVisual ); image1.Source = bitmap; } ) ); } } } }
初期化
ポーズの検出がいらないので、ユーザーの検出と、キャリブレーションのイベントだけ実装します
// OpenNIの初期化 ScriptNode node; context = Context.CreateFromXmlFile( "../../SamplesConfig.xml", out node ); context.GlobalMirror = false; // depthの作成 depth = context.FindExistingNode( NodeType.Depth ) as DepthGenerator; // ユーザーの作成 user = new UserGenerator( context ); // ポーズが必要な場合は、最新版を入れてもらう if ( user.SkeletonCapability.DoesNeedPoseForCalibration ) { throw new Exception( "最新のOpenNIをインストールしてください" ); } // ユーザー検出、キャリブレーション完了のイベントを登録する user.NewUser += new EventHandler<NewUserEventArgs>( user_NewUser ); user.SkeletonCapability.CalibrationComplete += new EventHandler<CalibrationProgressEventArgs>(SkeletonCapability_CalibrationComplete); // すべての骨格を追跡する user.SkeletonCapability.SetSkeletonProfile( SkeletonProfile.All ); // 動作を開始する context.StartGeneratingAll();
ユーザーの検出
ユーザーを検出したら、即座にキャリブレーションを開始します
void user_NewUser( object sender, NewUserEventArgs e ) { Trace.WriteLine( "New User:" + e.ID ); user.SkeletonCapability.RequestCalibration( e.ID, true ); }
キャリブレーション完了
成否を見て、成功なら骨格の追跡を開始します
void SkeletonCapability_CalibrationComplete( object sender, CalibrationProgressEventArgs e ) { // キャリブレーション成功 if ( e.Status == CalibrationStatus.OK ) { Trace.WriteLine( "Calibration Success:" + e.ID ); user.SkeletonCapability.StartTracking( e.ID ); } // キャリブレーション失敗 else { Trace.WriteLine( "Calibration Failed:" + e.ID ); } }