Nei precedenti post abbiamo visto che il Kinect ci mette a disposizione una serie di eventi che ci permettono di recuperare i frame video, di profondità o gli skeleton data.
Possiamo utilizzare le potenzialità del Kinect anche senza fare ricorso alla gestione di tali eventi ma utilizzando una tecnica di polling sugli streams.
Partiamo dall’inizio: la classe Runtime (già vista in precedenza) ci mette a disposizione 3 proprietà utilissime in questo contesto e che contengono proprio gli stream di cui sopra.
- VideoStream : è un’istanza di ImageStream che incapsula lo stream video;
- DepthStream : è un’istanza di ImageStream che incapsula lo stream dei dati di profondità;
- SkeletonEngine : è il motore di skeletal Tracking che permette l’accesso ai frem di skeleton data.
In particolare osserviamo la presenza, in tutte le classi in gioco, del metodo GetNextFrame il cui risultato è un oggetto di tipo ImageFrame per il VideoStream e per il DepthStream e SkeletonFrame per lo SkeletonEngine.
ImageFrame e SkeletonFrame sono esattamente le istanze che ci vengono restituite all’interno degli eventi della classe Runtime utilizzati in precedenza.
Per questo motivo possiamo utilizzare questo metodo per accedere allo stream e recuperare il frame che ci interessa.
L’applicazione allegata a questo post altro non fa che visualizzare lo stream video e depth utilizzando un polling.
In particolare vengono utilizzati due Task che si occupano di eseguire il “polling” accedendo ai differenti stream.
- Private Nui As Runtime
- Private VideoWorkTask As Task
- Private DepthWorkTask As Task
- Private WorkTaskTokenSource As CancellationTokenSource
- Private Sub MainWindow_Loaded(sender As Object, e As System.Windows.RoutedEventArgs) Handles Me.Loaded
- Try
- Nui = New Runtime
- Nui.Initialize(RuntimeOptions.UseColor Or RuntimeOptions.UseDepthAndPlayerIndex)
- Nui.VideoStream.Open(ImageStreamType.Video, 2, ImageResolution.Resolution640x480, ImageType.Color)
- Nui.DepthStream.Open(ImageStreamType.Depth, 2, ImageResolution.Resolution320x240, ImageType.DepthAndPlayerIndex)
- WorkTaskTokenSource = New CancellationTokenSource()
- Dim cancellationToken = WorkTaskTokenSource.Token
- VideoWorkTask = New Task(New Action(Of Object)(AddressOf VideoWorkTaskCode), Nui, cancellationToken)
- VideoWorkTask.Start()
- cancellationToken = WorkTaskTokenSource.Token
- DepthWorkTask = New Task(New Action(Of Object)(AddressOf DepthWorkTaskCode), Nui, cancellationToken)
- DepthWorkTask.Start()
- Catch ex As Exception
- End Try
- End Sub
Viene inizializzata l’istanza di Runtime e gli stream Video e Depth, non vengono agganciati i gestori di evento ma vengono avviati due task che si occupano di recuperare i frame:
- Private Sub VideoWorkTaskCode(obj As Object)
- Dim nui = CType(obj, Runtime)
- While Not WorkTaskTokenSource.IsCancellationRequested
- If nui IsNot Nothing AndAlso nui.VideoStream IsNot Nothing Then
- Dim frame = nui.VideoStream.GetNextFrame(0)
- If frame IsNot Nothing Then
- Dispatcher.BeginInvoke(Sub()
- Me.VideoImage.Source = frame.ToBitmapSource()
- End Sub)
- End If
- End If
- System.Threading.Thread.Sleep(100)
- End While
- End Sub
- Private Sub DepthWorkTaskCode(obj As Object)
- Dim nui = CType(obj, Runtime)
- While Not WorkTaskTokenSource.IsCancellationRequested
- If nui IsNot Nothing AndAlso nui.DepthStream IsNot Nothing Then
- Dim frame = nui.DepthStream.GetNextFrame(0)
- If frame IsNot Nothing Then
- Dispatcher.BeginInvoke(Sub()
- Me.DepthImage.Source = frame.ToBitmapSource()
- End Sub)
- End If
- End If
- System.Threading.Thread.Sleep(100)
- End While
- End Sub
A differenza della gestione degli eventi vista nei precedenti post, in questo caso i task vengono eseguiti in threads differenti dal principale ed è, quindi, necessario l’utilizzo del Dispatcher per aggiornare l’interfaccia.
Il metodo GetNextFrame prevede un parametro che indica quanti millisecondi attendere prima di restituire il frame.
Il valore di ritorno di GetFrame può anche essere nothing (nel caso che il frame non sia ancora pronto).
Questa tecnica è un “pelino” più complicata di quella Event-Driven e deve essere utilizzata quando l’applicazione lo richiede (ad esempio abbiamo la necessità di recuperare un frame ogni x secondi).
Commenti