このエントリはKINECT SDK Advent Calendar 2011 : ATNDの12月4日分です!!
Advent Calendarでの、僕の全プロジェクトはこちらです
KINECT SDK Beta2 に追加された、KINECTの挿抜状態を通知するイベントを試してみました。
複数KINECTや商用KINECTアプリを、実用的に利用するためには必須の機能だと思います。
動画も載せてみました。KINECTを抜いたり刺したりしています。
見た目の解説
一つのKINECTにつき一つのImageで、4つのKINECTまで表示できます。
<Window x:Class="CameraImageWPF.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="577" Width="669"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="149*" /> <RowDefinition Height="389*" /> </Grid.RowDefinitions> <TextBox Name="kinectCount" Margin="0,0,0,97" FontSize="18" Text="Text" TextAlignment="Center" FontWeight="Bold" FontStretch="Normal" TextWrapping="NoWrap" VerticalContentAlignment="Center" /> <Image Name="image1" Height="240" Width="320" Margin="0,50,332,248" Grid.RowSpan="2" /> <Image Height="240" Margin="326,50,6,248" Name="image2" Width="320" Grid.RowSpan="2" /> <Image Height="240" Margin="0,149,332,0" Name="image3" Width="320" Grid.Row="1" /> <Image Height="240" Margin="326,149,6,0" Name="image4" Width="320" Grid.Row="1" /> </Grid> </Window>
中身の解説
いろいろ試しながら、こんな感じになりました。
using System; using System.Windows; using System.Windows.Media; using System.Windows.Media.Imaging; using Microsoft.Research.Kinect.Nui; using System.Windows.Controls; namespace CameraImageWPF { /// <summary> /// MainWindow.xaml の相互作用ロジック /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); images = new Image[] { image1, image2, image3, image4 }; // 挿抜状態の変化を通知する Runtime.Kinects.StatusChanged += new EventHandler<StatusChangedEventArgs>( Kinects_StatusChanged ); ShowKinectCount(); // 最初に刺されている分を初期化する foreach ( Runtime kinect in Runtime.Kinects ) { InitKinect( kinect ); } } private void InitKinect( Runtime kinect ) { kinect.Initialize( RuntimeOptions.UseColor ); kinect.VideoStream.Open( ImageStreamType.Video, 2, ImageResolution.Resolution640x480, ImageType.Color ); kinect.VideoFrameReady += new EventHandler<ImageFrameReadyEventArgs>( kinect_VideoFrameReady ); } // KINECTの状態が変わった void Kinects_StatusChanged( object sender, StatusChangedEventArgs e ) { // KINECTの数を表示 ShowKinectCount(); // KINECTが刺された if ( e.Status == KinectStatus.Connected ) { InitKinect( e.KinectRuntime ); } // KINECTが抜かれた else if ( e.Status == KinectStatus.Disconnected ) { // インスタンス数が切りつめられるので、インスタンス数の最後のImageをnullにする // 一度に何個も抜かれるかもなので、最後までnullにしちゃう for ( int i = Runtime.Kinects.Count; i < images.Length; i++ ) { images[i].Source = null; } // 抜かれたKINECTのイベント削除と、インスタンスの破棄 e.KinectRuntime.VideoFrameReady -= new EventHandler<ImageFrameReadyEventArgs>( kinect_VideoFrameReady ); e.KinectRuntime.Uninitialize(); } } private void ShowKinectCount() { kinectCount.Text = Runtime.Kinects.Count + "台のKINECTが有効です"; } void kinect_VideoFrameReady( object sender, ImageFrameReadyEventArgs e ) { // 抜かれた瞬間のKINECTは、InstanceIndex が 0 になる Runtime kinect = sender as Runtime; if ( (kinect != null) && (kinect.InstanceIndex >= 0) ) { PlanarImage srouce = e.ImageFrame.Image; Image dest = images[kinect.InstanceIndex]; dest.Source = BitmapSource.Create( srouce.Width, srouce.Height, 96, 96, PixelFormats.Bgr32, null, srouce.Bits, srouce.Width * srouce.BytesPerPixel ); } } Image[] images; } }
挿抜状態のイベントを登録する
Runtime.Kinects.StatusChangedイベントで、KINECTの挿抜状態が変わったら、イベントが来るようになりました。
// 挿抜状態の変化を通知する Runtime.Kinects.StatusChanged += new EventHandler<StatusChangedEventArgs>( Kinects_StatusChanged );
起動時のKINECTの初期化
Runtime.KinectsがRuntimeのコレクションになっているので、最初に接続されているKINECTをすべて初期化します
// 最初に刺されている分を初期化する foreach ( Runtime kinect in Runtime.Kinects ) { InitKinect( kinect ); }
KINECTの挿抜状態の変更イベント
今回のキモです。
// KINECTの状態が変わった void Kinects_StatusChanged( object sender, StatusChangedEventArgs e ) { // KINECTの数を表示 ShowKinectCount(); // KINECTが刺された if ( e.Status == KinectStatus.Connected ) { InitKinect( e.KinectRuntime ); } // KINECTが抜かれた else if ( e.Status == KinectStatus.Disconnected ) { // インスタンス数が切りつめられるので、インスタンス数の最後のImageをnullにする // 一度に何個も抜かれるかもなので、最後までnullにしちゃう for ( int i = Runtime.Kinects.Count; i < images.Length; i++ ) { images[i].Source = null; } // 抜かれたKINECTのイベント削除と、インスタンスの破棄 e.KinectRuntime.VideoFrameReady -= new EventHandler<ImageFrameReadyEventArgs>( kinect_VideoFrameReady ); e.KinectRuntime.Uninitialize(); } }
カメラ画像の更新処理
少し変わっています。
ImageをInstanceIndexで取得しますが、KINECTが抜かれたのと、画像の更新が重なった場合、InstanceIndexが-1になります。そのため、今回は、インスタンスのnullとInstanceIndexの-1をチェックしています。
void kinect_VideoFrameReady( object sender, ImageFrameReadyEventArgs e ) { // 抜かれた瞬間のKINECTは、InstanceIndex が 0 になる Runtime kinect = sender as Runtime; if ( (kinect != null) && (kinect.InstanceIndex >= 0) ) { PlanarImage srouce = e.ImageFrame.Image; Image dest = images[kinect.InstanceIndex]; dest.Source = BitmapSource.Create( srouce.Width, srouce.Height, 96, 96, PixelFormats.Bgr32, null, srouce.Bits, srouce.Width * srouce.BytesPerPixel ); } }
以上で、複数KINECTや、KINECT状態による処理の変更ができるようになりました。
KINECTが刺されていなくても、アプリが動作可能になるので、OpenNIに対する優位点になりそうですね。