KINECT SDK Beta2 ã§ãã¢ããªãæ¿æã«å¯¾å¿ããã(F#+WPF+Rx) #kinectsdk_ac
ãã®ã¨ã³ããªã¯KINECT SDK Advent Calendar 2011 : ATNDã®12æ7æ¥åã§ãã
KINECT SDK Beta2 で、挿抜状態に応じたアプリの動作をする #kinectsdk_ac - かおるんダイアリーãF#ã§å®è£ ãã¦ã¿ã¾ããã
è¦ãç®ã¯åãåä½ãããã¯ãã§ã*1ã
è¦ãç®é¨å
<Window 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>
åºæ¬çã«ã¯ä¸ç·ã§ããã
ä¸èº«ã®è§£èª¬
ããã¸ã§ã¯ãåãXAMLãã¡ã¤ã«åã¯é©å®ç½®ãæãã¦ãã ããã
ã¾ããä»åã®ããã°ã©ã ã§ã¯Reactive Extensions(以éRx)ã¨ããã©ã¤ãã©ãªã使ç¨ãã¦ããã®ã§ãåããã¦ã¿ããæ¹ã¯Rxãã¤ã³ã¹ãã¼ã«ãã¦åç
§è¨å®ã«å ãã¦ãã ãããNuGetãå©ç¨ã§ããæ¹ã¯NuGetããã¤ã³ã¹ãã¼ã«ãã¾ãããã
module FsSampleKinectApplication2 open System open System.Threading open System.Reactive.Linq open System.Windows open System.Windows.Controls open System.Windows.Threading open System.Windows.Media.Imaging open Microsoft.Research.Kinect.Nui type MainWindow() = let window = Application.LoadComponent(new System.Uri("/FsSampleApplication2;component/MainWindow.xaml", System.UriKind.Relative)) :?> Window let kinectCount = window.FindName "kinectCount" :?> TextBox let image1 = window.FindName "image1" :?> Image let image2 = window.FindName "image2" :?> Image let image3 = window.FindName "image3" :?> Image let image4 = window.FindName "image4" :?> Image let images = [| image1;image2;image3;image4 |] let mutable eventDictionary : (Runtime * IDisposable) list = [] let syncContext = if SynchronizationContext.Current = null then SynchronizationContext.SetSynchronizationContext(new DispatcherSynchronizationContext()) SynchronizationContext.Current let showKinectCount () = kinectCount.Text <- (string Runtime.Kinects.Count) + "å°ã®KINECTãæå¹ã§ã" let createVideoFrameReady (runtime:Runtime) = runtime.VideoFrameReady |> Observable.subscribe begin fun args -> if runtime <> null && runtime.InstanceIndex >= 0 then let image = args.ImageFrame.Image let source = BitmapSource.Create(image.Width, image.Height, 96.0, 96.0, Media.PixelFormats.Bgr32, null, image.Bits, image.Width * image.BytesPerPixel) images.[runtime.InstanceIndex].Source <- source end let initKinect (kinect:Runtime) = kinect.Initialize( RuntimeOptions.UseColor ) kinect.VideoStream.Open(ImageStreamType.Video, 2, ImageResolution.Resolution640x480, ImageType.Color) eventDictionary <- (kinect, (kinect |> createVideoFrameReady)) :: eventDictionary do window.Loaded |> Observable.subscribe (fun _ -> for kinect in Runtime.Kinects do kinect |> initKinect ) |> ignore do window.Unloaded |> Observable.subscribe begin fun _ -> eventDictionary |> List.iter (fun (r,e) -> e.Dispose() ) eventDictionary <- [] for kinect in Runtime.Kinects do kinect.Uninitialize() end |> ignore let kinect_StatusChanged = syncContext |> Runtime.Kinects.StatusChanged.ObserveOn do kinect_StatusChanged .Subscribe begin fun (e:StatusChangedEventArgs) -> showKinectCount () match e.Status with | KinectStatus.Connected -> initKinect e.KinectRuntime | KinectStatus.Disconnected -> for i in Runtime.Kinects.Count .. images.Length - 1 do images.[i].Source <- null // æãããKINECTã®ã¤ãã³ãåé¤ã¨ãã¤ã³ã¹ã¿ã³ã¹ã®ç ´æ£ eventDictionary |> List.find (fun (runtime,_) -> runtime = e.KinectRuntime) |> (fun (runtime,event) -> event.Dispose(); eventDictionary <- eventDictionary |> List.filter (fun (r,_) -> r <> runtime)) e.KinectRuntime.Uninitialize() | _ -> () end |> ignore member this.Window = window [<STAThread>] (new MainWindow()).Window |> (new Application()).Run |> ignore
DispatcherSynchronizationContextãå©ç¨ãã
Runtime.Kinects.StatusChangedã¤ãã³ãã¯(Window.LoadedãUnloadedã®ãããª)UIã¤ãã³ãã§ã¯ãªãã®ã§ãStatusChangedã¤ãã³ãã«ç»é²ãããããªã²ã¼ãã¯UIã¤ãã³ãã«ç»é²ãããããªã²ã¼ãã¨åãåªå
度ã§å®è¡ãããããã§ã¯ããã¾ãããããããStatusChangedã¤ãã³ãã«ç»é²ããããªã²ã¼ãã¯UIã¤ãã³ãã«ç»é²ããã¦ããããªã²ã¼ãã¨åãåªå
度ã§å®è¡ããã¦ã»ããã®ã§ãDispatcherSynchronizationContextãéãã¦ããªã²ã¼ãããã¥ã¼ç»é²ãããã¨ã§ããã¥ã¼ã«ç»é²ãããé çªã«å¾ã£ã¦ä¸åº¦ã«1ã¤ãã¤å®è¡ãããããã«ãã¾ãã
DispatcherSynchronizationContextã®çæã¨åå¾ã®ã³ã¼ãã¯ä»¥ä¸ã®ããã«ãªãã¾ãããªããSynchronizationContextã¯ã©ã¹ã¯DispatcherSynchronizationContextã¯ã©ã¹ã®è¦ªã¯ã©ã¹ã§ãã
let syncContext = if SynchronizationContext.Current = null then SynchronizationContext.SetSynchronizationContext(new DispatcherSynchronizationContext()) SynchronizationContext.Current
ä¸è¬çã«ã¯SynchronizationContext.Postã¡ã½ããã使ã£ã¦ã³ã¼ã«ããã¯ãéåæã«å¼ã³åºãã¦ãããããã«ãã¾ãããã ã第2å¼æ°ã¨ãã¦ããªã²ã¼ãã«æ¸¡ããããªãã¸ã§ã¯ãã渡ããªãã¨ãããªãã£ãããStatusChangedã§Reactive Programmingã§ããªããªã©ã®åé¡ãããã¾ããããã§ãObservable.ObserveOnã¡ã½ããã®åºçªã§ãã
let kinect_StatusChanged = syncContext |> Runtime.Kinects.StatusChanged.ObserveOn
ObservrOnã¡ã½ããã¯Rxã§æä¾ããã¦ããæ¡å¼µã¡ã½ããã®1ã¤ã§ãSynchronizationContextãéãã¦ã¤ãã³ãããã¥ã¼ç»é²ãããã®ã§ããæ»ãåãIObservableãªã®ã§ãObserveOnã¡ã½ããã®æ»ãå¤ãObservable.subscribeã§é£ãããããã¨ãã§ãã¾ãã
ã¤ãã³ãã®ç»é²ã¨è¨æ¶
createVideoFrameReadyé¢æ°ã§KINECTã©ã³ã¿ã¤ã ãã¨ã«VideoFrameReadyã¤ãã³ããç»é²ãã¾ãããã®é¢æ°ã§ã¯IDisposableãè¿ãã¦ããã®ã§ããããã®ãã¼ã¿ã«å¯¾ãã¦Disposeã¡ã½ãããå®è¡ããã¨ãã¤ãã³ãã解é¤ãããã¨ãã§ãã¾ããããã¯KINECTãPCããæãããæã«å¿ è¦ãªã®ã§ãã©ããã«ä¿æãã¦ããããã¨ããã§ããã¾ããã©ã®ã©ã³ã¿ã¤ã ã®ã¤ãã³ããè¨æ¶ãã¦ããããã¨ããã§ããããã§ã¾ãããããã®ãã¼ã¿ãä¿æããå¤æ°ãç¨æãã¾ãã
let mutable eventDictionary : (Runtime * IDisposable) list = []
mutableãã¤ããã¨ããã®å¤æ°ã¸ã®å代å
¥ãå¯è½ã«ãªãã¾ãã:ã¨=ã®éã«æ¸ããã¦ããã®ã¯å¤æ°ã®åã§ãããã®å¤æ°ã¯Runtimeã¨IDisposableãããªãã¿ãã«ãè¦ç´ ã¨ããlistãä¿æãã¾ããæåã¯ç©ºãªã¹ãã代å
¥ããã¦ãã¾ãã
ã¤ãã³ãã®ä¿æã¯InitKinecté¢æ°ã®æå¾ã«è¡ã£ã¦ãã¾ãã
eventDictionary <- (kinect, (kinect |> createVideoFrameReady)) :: eventDictionary
(kinect,IDisposable)ã®å½¢å¼ã§ã¿ãã«åããçæãããã¿ãã«ã¨eventDictionaryã«æ ¼ç´ããã¦ãããªã¹ããã::ã使ã£ã¦æ°ãããªã¹ããä½æããæå¾ã«<-ã§eventDictionaryã«ä»£å ¥ãã¦ãã¾ããããã§ç¡äºãè¨æ¶ãã§ãã¾ããã
KINECTã®æ¿æç¶æ ã®å¤æ´ã¤ãã³ã
do kinect_StatusChanged .Subscribe begin fun (e:StatusChangedEventArgs) -> showKinectCount () match e.Status with | KinectStatus.Connected -> initKinect e.KinectRuntime | KinectStatus.Disconnected -> for i in Runtime.Kinects.Count .. images.Length - 1 do images.[i].Source <- null // æãããKINECTã®ã¤ãã³ãåé¤ã¨ãã¤ã³ã¹ã¿ã³ã¹ã®ç ´æ£ eventDictionary |> List.find (fun (runtime,_) -> runtime = e.KinectRuntime) |> (fun (runtime,event) -> event.Dispose(); eventDictionary <- eventDictionary |> List.filter (fun (r,_) -> r <> runtime)) e.KinectRuntime.Uninitialize() | _ -> () end |> ignore
ãã¡ãã®è¨äºã§ã¯Observable.subscribeé¢æ°ã使ç¨ãã¾ããããRxã§ãSubscribeã¡ã½ããã¯æä¾ããã¦ãããä»åã¯ObserveOnã¨å¯¾ã«ããç®çã§Rxå´ã®Subscribeã使ã£ã¦ãã¾ãã
å¦çèªä½ã§C#ã³ã¼ãã¨ç°ãªã£ã¦ããé¨åã¯ãC#ã§ifæã ã£ãæããã¿ã¼ã³ãããã«ç½®ãæãã£ã¦ãããã¨ãforæã®å½¢å¼ãã¤ãã³ãã®è§£é¤æ¹æ³ã§ãã
F#ã«ã¯C#ã³ã¼ãã«æ¸ããã¦ããå¾æ¥å½¢å¼ã®foræãåå¨ããªãã®ã§ãç¯å²å¼ã使ã£ã¦ä»£æ¿ãã¾ãããç¯å²å¼ã§ã¯ç¯å²æ¼ç®å..(ããã2ã¤)ã使ã£ã¦æå®ããæ°å¤ã®ç¯å²ãæå®ãã¾ããã¾ããåè¿°ããã¨ãããIDisposable.Disposeãå®è¡ãã¦ã¤ãã³ãã解é¤ãã¦ãã¾ããList.findé¨åã§åãã©ã³ã¿ã¤ã ã¤ã³ã¹ã¿ã³ã¹ã®ãã¼ã¿ãæ¢ããDisposeãå®è¡ããå¾ãList.filterã«ãã£ã¦å¾ããããªã¹ããå代å
¥ãã¦ãã¾ãã
å°ã
ç´°ãã話ãå¢ãããã®ã®ãF#ã«ããKINECTããã°ã©ãã³ã°ã®é°å²æ°ã¯ã¤ãããã®ã§ã¯ãªãã§ããããã
SynchronizationContextã«ãããã«ãã¹ã¬ããããã°ã©ãã³ã°ãRxã«ã¤ãã¦ã¯ããã§ã¯ããã£ã¨ãã説æãã¦ãã¾ãããã両è
ã¨ãã«ä¾¿å©ãªãã®ãªã®ã§ãä½è£ãããã°ä¸ç·ã«åå¼·ãã¦ã¿ããã¨ããå§ããã¾ãã