lunedì 24 marzo 2014

Kinect V2 : body stream – hand tracking

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.

image

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:

Sensor = KinectSensor.Default
If Sensor IsNot Nothing Then
    Sensor.Open()

    BodyData = New Body(CInt(Sensor.BodyFrameSource.BodyCount - 1)) {}
    
    BodyReader = Sensor.BodyFrameSource.OpenReader()
End If

dove

Private Property Sensor As KinectSensor
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:

If BodyReader IsNot Nothing Then
    AddHandler BodyReader.FrameArrived, AddressOf BodyFrameArrivedHandler
End If

E, quindi, possiamo recuperare la collezione di Body:

Private Sub BodyFrameArrivedHandler(sender As Object, e As BodyFrameArrivedEventArgs)
    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:

Public ReadOnly Property Player0HandRightState As HandState
    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

 

Private Function GetHandStateForPlayer(playerIndex As Int16, hand As Hand) As HandState
    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:

<TextBlock Grid.Row="1" Grid.Column="0" HorizontalAlignment="Right" VerticalAlignment="center" FontWeight="Bold" FontSize="24">Player 0</TextBlock>
<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:

Public Class HandStateToImageConverter
    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

Public Class HandConfidenceToOpacityConverter
    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.”

 

Technorati Tags: ,,

Nessun commento: