In questo post continuiamo l’analisi delle informazioni forniteci dallo stream body del Kinect V2 prendendo in esame la funzionalità di tracking delle mani dei player.
Le funzionalità di tracking delle mani dei player sono accessibili grazie alla classe Body vista nel precedente post.
In particolare la classe Body espone due proprietà per ogni mano:
- HandRighState, HandLeftState: permette di conoscere lo stato della mano ovvero se la mano è non tracciata, aperta, chiusa o con le dita a V oppure se l’SDK non è riuscito a riconoscerla;
- HandRghtConfience, HandeLeftConfidence: indica la confidence (low o high) dello stato della mano.
Possiamo, quindi, utilizzare queste proprietà per prendere decisioni in base allo stato della mano o delle mani del player.
A livello di codice abbiamo la necessità di inizializzare il device e gestire la sorgente Body in maniera analoga a quanto visto nel precedente post:
If Sensor IsNot Nothing Then
Sensor.Open()
BodyData = New Body(CInt(Sensor.BodyFrameSource.BodyCount - 1)) {}
BodyReader = Sensor.BodyFrameSource.OpenReader()
End If
dove
Private Property BodyReader As BodyFrameReader
Private Property BodyData As Body()
Per ricevere i frame riguardanti i Body rilevati dal device dobbiamo gestire l’evento FrameArrived del BodyReader:
AddHandler BodyReader.FrameArrived, AddressOf BodyFrameArrivedHandler
End If
E, quindi, possiamo recuperare la collezione di Body:
Dim frameReference = e.FrameReference
Dim frame As BodyFrame = Nothing
Try
frame = frameReference.AcquireFrame()
If frame IsNot Nothing Then
frame.GetAndRefreshBodyData(BodyData)
OnPropertyChanged("Player0HandRightState")
OnPropertyChanged("Player0HandLeftState")
OnPropertyChanged("Player0HandRightConfidence")
OnPropertyChanged("Player0HandLeftConfidence")
End If
Catch ex As Exception
Finally
If frame IsNot Nothing Then
frame.Dispose()
End If
End Try
End Sub
Supponiamo di avere un’applicazione WPF e di voler semplicemente visualizzare lo stato delle mani con immagini differenti e la confidence con un differente valore di opacità (senza necessariamente dover visualizzare la mano nella posizione spaziale in cui si trova).
Per fare questo, esponeniamo le 4 proprietà riguardanti lo stato e la confidence delle mani del player con indice 0:
Get
Return GetHandStateForPlayer(0, Hand.Right)
End Get
End Property
Public ReadOnly Property Player0HandLeftState As HandState
Get
Return GetHandStateForPlayer(0, Hand.Left)
End Get
End Property
Public ReadOnly Property Player0HandRightConfidence As TrackingConfidence
Get
Return GetHandConfidenceForPlayer(0, Hand.Right)
End Get
End Property
Public ReadOnly Property Player0HandLeftConfidence As TrackingConfidence
Get
Return GetHandConfidenceForPlayer(0, Hand.Left)
End Get
End Property
If playerIndex < 0 Or playerIndex > 5 Then Throw New ArgumentOutOfRangeException
If BodyData(playerIndex) Is Nothing Then Return HandState.NotTracked
Select Case hand
Case MainWindowViewModel.Hand.Left
Return BodyData(playerIndex).HandLeftState
Case MainWindowViewModel.Hand.Right
Return BodyData(playerIndex).HandRightState
End Select
Throw New NotImplementedException()
End Function
Private Function GetHandConfidenceForPlayer(playerIndex As Int16, hand As Hand) As TrackingConfidence
If playerIndex < 0 Or playerIndex > 5 Then Throw New ArgumentOutOfRangeException
If BodyData(playerIndex) Is Nothing Then Return TrackingConfidence.Low
Select Case hand
Case MainWindowViewModel.Hand.Left
Return BodyData(playerIndex).HandLeftConfidence
Case MainWindowViewModel.Hand.Right
Return BodyData(playerIndex).HandRightConfidence
End Select
Throw New NotImplementedException()
End Function
e le mettiamo in binding con la nostra interfaccia:
<Image Grid.Row="1" Grid.Column="1" Width="64" Height="64"
Source="{Binding Path=Player0HandRightState, Converter={StaticResource ResourceKey=HandStateConverter}, ConverterParameter=Right}"
Opacity="{Binding Path=Player0HandRightConfidence, Converter={StaticResource ResourceKey=HandConfidenceConverter}}"
Margin="20,10,20,10"/>
<Image Grid.Row="1" Grid.Column="2" Width="64" Height="64"
Source="{Binding Path=Player0HandLeftState, Converter={StaticResource ResourceKey=HandStateConverter}, ConverterParameter=Left}"
Opacity="{Binding Path=Player0HandLeftConfidence, Converter={StaticResource ResourceKey=HandConfidenceConverter}}"
Margin="20,10,20,10"/>
Nel precedente XAML sfruttiamo due converter che, rispetivamente, selezionano l’immagine da visualizzare e decidono l’opacità della stessa:
Implements IValueConverter
Public Function Convert(value As Object, targetType As Type, parameter As Object, culture As Globalization.CultureInfo) As Object Implements IValueConverter.Convert
If TypeOf value Is HandState And TypeOf parameter Is String Then
Dim handString = parameter.ToString()
Return GetImageForHandState(CType(value, HandState), handString)
End If
Return Nothing
End Function
Public Function ConvertBack(value As Object, targetType As Type, parameter As Object, culture As Globalization.CultureInfo) As Object Implements IValueConverter.ConvertBack
Throw New NotImplementedException()
End Function
Private Function GetImageForHandState(state As HandState, hand As String) As ImageSource
Dim uriString As String = Nothing
Select Case state
Case HandState.Closed
uriString = String.Format("./Assets/{0}HandClose.png", hand)
Case HandState.Lasso
uriString = String.Format("./Assets/{0}HandLasso.png", hand)
Case HandState.Open
uriString = String.Format("./Assets/{0}HandOpen.png", hand)
Case HandState.Unknown
uriString = "./Assets/UnknowState.png"
End Select
If uriString IsNot Nothing Then
Dim bitmap = New BitmapImage(New Uri(uriString, UriKind.RelativeOrAbsolute))
Return bitmap
Else
Return Nothing
End If
End Function
End Class
Implements IValueConverter
Public Function Convert(value As Object, targetType As Type, parameter As Object, culture As Globalization.CultureInfo) As Object Implements IValueConverter.Convert
If TypeOf value Is TrackingConfidence Then
Dim confidence = CType(value, TrackingConfidence)
Select Case confidence
Case TrackingConfidence.High
Return 1
Case TrackingConfidence.Low
Return 0.5
Case Else
Return 0
End Select
End If
Return Nothing
End Function
Public Function ConvertBack(value As Object, targetType As Type, parameter As Object, culture As Globalization.CultureInfo) As Object Implements IValueConverter.ConvertBack
Throw New NotImplementedException()
End Function
End Class
Il primo converter agisce su un oggetto di tipo HandState e si aspetta un parametro stringa che indica di quale mano si tratta, mentre il secondo agisce su un oggetto di tipo TrackingConfidence.
Come possiamo vedere, una volta a disposizione l’istanza di Body, è relativamente semplice capire quale è lo stato delle mani dei singoli player.
Attualmente, l’SDK del Kinect è in grado di tracciare, per quanto riguarda le mani, solo due player (di default il player 0 ed il player 1). Possiamo modificare qualli player vengono tracciati grazie al metodo OverrideHandTracking() esposto dalla sorgente Body.
Disclaimer: “This is preliminary software and/or hardware and APIs are preliminary and subject to change.”
Commenti