Kinect for Windows SDKで、アプリを挿抜に対応させる(F# + WPF)
KINECT SDK Advent Calendarのときに
KINECT SDK Beta2 で、アプリを挿抜に対応させる(F#+WPF+Rx) #kinectsdk_ac - pocketberserkerの爆走
という記事をかいたわけですが、Kinect for Windows SDKがリリースされたのにあわせて修正してみました。
よりF#っぽいコードにもしたつもりです。
XAML
<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" Stretch="Uniform" /> <Image Height="240" Margin="326,50,6,248" Name="image2" Width="320" Grid.RowSpan="2" Stretch="Uniform" /> <Image Height="240" Margin="0,149,332,0" Name="image3" Width="320" Grid.Row="1" Stretch="Uniform" /> <Image Height="240" Margin="326,149,6,0" Name="image4" Width="320" Grid.Row="1" Stretch="Uniform" /> </Grid> </Window>
前回からの変更点はありません。
コード
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.Kinect open Coding4Fun.Kinect.Wpf let window = Application.LoadComponent(new System.Uri("/FsSampleKinectApplication2;component/MainWindow.xaml", System.UriKind.Relative)) :?> Window let kinectCount = window.FindName "kinectCount" :?> TextBox let findImage name = name |> window.FindName :?> Image let image1 = "image1" |> findImage let image2 = "image2" |> findImage let image3 = "image3" |> findImage let image4 = "image4" |> findImage let images = [| image1;image2;image3;image4 |] let mutable eventDictionary : (KinectSensor * IDisposable) list = [] let syncContext = if SynchronizationContext.Current = null then SynchronizationContext.SetSynchronizationContext(new DispatcherSynchronizationContext()) SynchronizationContext.Current let showKinectCount () = kinectCount.Text <- (string KinectSensor.KinectSensors.Count) + "台のKINECTが有効です" let createColorFrameReady (sensor:KinectSensor) = sensor.ColorFrameReady |> Observable.subscribe begin fun args -> KinectSensor.KinectSensors |> Seq.tryFindIndex ((=) sensor) |> Option.iter (fun index -> images.[index].Source <- args.OpenColorImageFrame().ToBitmapSource() ) end let initSensor (sensor:KinectSensor) = sensor.ColorStream.Enable() sensor.Start() eventDictionary <- (sensor, (sensor |> createColorFrameReady)) :: eventDictionary let kinect_StatusChanged = syncContext |> KinectSensor.KinectSensors.StatusChanged.SubscribeOn do kinect_StatusChanged .Subscribe begin fun (e:StatusChangedEventArgs) -> showKinectCount () match e.Status with | KinectStatus.Connected -> e.Sensor |> initSensor | KinectStatus.Disconnected -> images |> Array.partition ( fun image -> images |> Array.findIndex ((=) image) >= KinectSensor.KinectSensors.Count) |> fst |> Array.iter (fun image -> image.Source <- null) eventDictionary |> List.find (fst >> ((=) e.Sensor)) |> (fun (sensor,event) -> event.Dispose(); eventDictionary <- eventDictionary |> List.filter (fst >> ((<>) sensor))) e.Sensor.Stop() | _ -> () end |> ignore do window.Loaded |> Observable.subscribe (fun _ -> KinectSensor.KinectSensors |> Seq.iter initSensor ) |> ignore do window.Unloaded |> Observable.subscribe begin fun _ -> eventDictionary |> List.iter (fun (s,e) -> e.Dispose(); s.Stop() ) eventDictionary <- [] end |> ignore [<STAThread>] window |> (new Application()).Run |> ignore
Beta2の時には存在していたインスタンスのインデックスを取得できるRuntime.InstanceIndex相当のものが存在しない気がします。なので、ここはF#らしくSeq.tryFindIndexを使っておきました。通常ならKinectSensor.KinectSensors.IndexOfを使うのですかね?
ちなみに、今回は可能な限りif式やfor文を排除してみました。