Passa ai contenuti principali

Kinect V2: body source – joints

In questo post prenderemo in esame lo stream body source fornito dal Kinect V2 e, in particolare, ci occuperemo della funzionalità che sostituisce quella, già presente nella versione 1 dell’SDK e che prendeva il nome di Skeletal Tracking.

Come accadeva per la versione precedente, il device è in grado di fornirci la posizione nello spazio di un insieme di punti del corpo umano.

Nella precedente versione del Kinect avevamo a disposizione 20 punti (detti joints) mentre nella nuova versione sono 25. Sono stati aggiunti il collo (neck), il pollice (Thumb) di entrambe le mani e la punta dell’insieme delle altre dita (Hand_Tip).

image

Nella precedente versione dell’SDK, la struttura dati che conteneva le informazioni relative ai joints prendeva il nome di Skeleton, in questa versione la struttura dati che contiene i dati dei joints si chiama Body.
Questo perchè, come vedremo anche nei prossimi post, la classe Body contiene un numero di informazioni superiore rispetto alla vecchia skeleton: tra queste troviamo le espressioni, le attività, lo stato delle mani.

Inoltre, a differenza della vecchia versione, in questa abbiamo fino a 6 player tracciati contemporaneamente in modo completo (nella vecchia ne avevamo 2 completamente e 4 solo per il baricentro).

Occupiamoci, in questo post di analizzare solo la parte relativa allo scheletro (sia in termini di joints che di orientamento di questi).

Come già visto in precedenti post, abbiamo la necessità di recuperare il device e, quindi, il reader dello stream body:

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

   BodyReader = Sensor.BodyFrameSource.OpenReader()
End If

In maniera analoga a quanto visto in precedenza dobbiamo gestire l’evento FrameArrived:

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

La struttura delle classi in gioco per quanto riguarda reader ed evento è la seguente:

image

image

Il metodo AcquireFrame() della classe BodyFrameReference ci permette di recuperare l’istanza di BodyFrame:

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

            'Elabora il frame

        End If
    Catch ex As Exception

    Finally
        If frame IsNot Nothing Then
            frame.Dispose()
        End If
    End Try
End Sub

La classe BodyFrame ci mette a disposizione il metodo GetAndRefreshBodyData che ci permette di recuperare effettivamente le istanze dei Body tracciati.

In particolare, il metodo GetAndRefreshBodyData ha un comportamento del tutto particolare:

  • E’ nostro compito preparare e dimensionare opportunamente l’array di oggetti Body da passare al metodo. Il numero di body tracciabili dal Kinect è contenuto nella proprietà  BodyCount della classe BodyFrameSource;
  • Per ogni elemento dell’array da noi passato:
    • se l’elemento è Nothing, viene creata una nuova istanza di Body e memorizzata nell’array;
    • se l’elemento non è nullo, l’istanza viene riutilizzata e riempita con i nuovi dati ottenuti dal nuovo frame.

Questo meccanismo consente di ottimizzare la memoria, riutilizzando le strutture dati e allocando, quindi, meno memoria.

Se abbiamo la necessità di memorizzare una particolare istanza di Body in un determinato frame, è sufficiente salvare l’elemento dell’array desiderato in una variabile d’appoggio e impostare l’elemento al valore Nothing. L’SDK ne creerà uno nuovo quando recupereremo il frame e la vecchia istanza resterà a nostra disposizione )avendo un puntatore valido nella variabile d’appoggio).

La classe Body contiene moltissime informazioni (alcune delle quali verranno prese in esame nei prossimi post) tra le quali troviamo quelle relative ai joint e al loro orientamento:

image

Chi conosce la funzione di skeletal tracking del Kinect V1, potrà osservare che le classi in gioco sono praticamente le stesse.

Ogni joint, identificato da un valore dell’enumerazione JointType, espone una posizione nello spazio (CameraSpacePoint) e un TrackingState.

Ogni JointOrientation, anch’esso identificato dal valore di JointType, un vettore a 4 dimensioni il cui significato è mostrato nella seguente figura:

image

Da notare anche la proprietà ClippedEdges della classe Body che ci permette di sapere se lo scheletro corrispondente viene “tagliato” da uno o più dei bordi (cioè uno dei joints finisce fuori dallo schermo da uno dei lati).

Un esempio di elaborazione dei dati ottenuti dallo stream del body può essere il seguente:

Private Property DrawingGroup As DrawingGroup

Private Property DrawingImage As DrawingImage

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

            Using dc = DrawingGroup.Open()
                frame.GetAndRefreshBodyData(BodyData)
                dc.DrawRectangle(Brushes.Black, Nothing, New Rect(0.0, 0.0, DisplayWidth, DisplayHeight))
                For Each Body In BodyData

                    If Body.IsTracked Then
                        BodyGraphicHelper.DrawClippedEdges(Body, DisplayHeight, DisplayWidth, dc)
                        Dim joints = Body.Joints

                        Dim jointPoints = New Dictionary(Of JointType, Point)()
                        For Each jointType In joints.Keys
                            Dim depthSpacePoint = CoordinateMapper.MapCameraPointToDepthSpace(joints(jointType).Position)
                            jointPoints(jointType) = New Point(depthSpacePoint.X, depthSpacePoint.Y)
                        Next

                        BodyGraphicHelper.DrawBody(joints, jointPoints, dc)
                    End If
                Next
                DrawingGroup.ClipGeometry = New RectangleGeometry(New Rect(0.0, 0.0, DisplayWidth, DisplayHeight))
            End Using
        End If
    Catch ex As Exception

    Finally
        If frame IsNot Nothing Then
            frame.Dispose()
        End If
    End Try
End Sub

In questo caso, utilizziamo un’istanza della classe DrawingGroup come superfice di disegno e, per ogni body tracciato (proprietà IsTracked a true), disegnamo una riga rossa lungo gli eventuali bordi che “tagliano” lo scheletro e lo scheletro vero e proprio.
Per eseguire il disegno utilizziamo la seguente classe statica:

Imports Microsoft.Kinect

Public Class BodyGraphicHelper

    Private Shared HandSize As Double = 30

    Private Shared JointThickness As Double = 3

    Private Shared ClipBoundsThickness As Double = 10

    Private Shared ReadOnly HandClosedBrush As Brush = New SolidColorBrush(Color.FromArgb(128, 255, 0, 0))

    Private Shared ReadOnly HandOpenBrush As Brush = New SolidColorBrush(Color.FromArgb(128, 0, 255, 0))

    Private Shared ReadOnly HandLassoBrush As Brush = New SolidColorBrush(Color.FromArgb(128, 0, 0, 255))

    Private Shared ReadOnly TrackedJointBrush As Brush = New SolidColorBrush(Color.FromArgb(255, 68, 192, 68))

    Private Shared ReadOnly InferredJointBrush As Brush = Brushes.Yellow

    Private Shared ReadOnly TrackedBonePen As Pen = New Pen(Brushes.Green, 6)

    Private Shared ReadOnly InferredBonePen As Pen = New Pen(Brushes.Gray, 1)

    Public Shared Sub DrawBody(joints As IReadOnlyDictionary(Of JointType, Joint),
                                 jointPoints As IDictionary(Of JointType, Point),
                                 drawingContext As DrawingContext)
        ' Draw the bones

        ' Torso
        DrawBone(joints, jointPoints, JointType.Head, JointType.Neck, drawingContext)
        DrawBone(joints, jointPoints, JointType.Neck, JointType.SpineShoulder, drawingContext)
        DrawBone(joints, jointPoints, JointType.SpineShoulder, JointType.SpineMid, drawingContext)
        DrawBone(joints, jointPoints, JointType.SpineMid, JointType.SpineBase, drawingContext)
        DrawBone(joints, jointPoints, JointType.SpineShoulder, JointType.ShoulderRight, drawingContext)
        DrawBone(joints, jointPoints, JointType.SpineShoulder, JointType.ShoulderLeft, drawingContext)
        DrawBone(joints, jointPoints, JointType.SpineBase, JointType.HipRight, drawingContext)
        DrawBone(joints, jointPoints, JointType.SpineBase, JointType.HipLeft, drawingContext)

        ' Right Arm    
        DrawBone(joints, jointPoints, JointType.ShoulderRight, JointType.ElbowRight, drawingContext)
        DrawBone(joints, jointPoints, JointType.ElbowRight, JointType.WristRight, drawingContext)
        DrawBone(joints, jointPoints, JointType.WristRight, JointType.HandRight, drawingContext)
        DrawBone(joints, jointPoints, JointType.HandRight, JointType.HandTipRight, drawingContext)
        DrawBone(joints, jointPoints, JointType.WristRight, JointType.ThumbRight, drawingContext)

        ' Left Arm
        DrawBone(joints, jointPoints, JointType.ShoulderLeft, JointType.ElbowLeft, drawingContext)
        DrawBone(joints, jointPoints, JointType.ElbowLeft, JointType.WristLeft, drawingContext)
        DrawBone(joints, jointPoints, JointType.WristLeft, JointType.HandLeft, drawingContext)
        DrawBone(joints, jointPoints, JointType.HandLeft, JointType.HandTipLeft, drawingContext)
        DrawBone(joints, jointPoints, JointType.WristLeft, JointType.ThumbLeft, drawingContext)

        ' Right Leg
        DrawBone(joints, jointPoints, JointType.HipRight, JointType.KneeRight, drawingContext)
        DrawBone(joints, jointPoints, JointType.KneeRight, JointType.AnkleRight, drawingContext)
        DrawBone(joints, jointPoints, JointType.AnkleRight, JointType.FootRight, drawingContext)

        ' Left Leg
        DrawBone(joints, jointPoints, JointType.HipLeft, JointType.KneeLeft, drawingContext)
        DrawBone(joints, jointPoints, JointType.KneeLeft, JointType.AnkleLeft, drawingContext)
        DrawBone(joints, jointPoints, JointType.AnkleLeft, JointType.FootLeft, drawingContext)

        ' Draw the joints
        For Each jointType In joints.Keys
            Dim drawBrush As Brush = Nothing

            Dim trackingState = joints(jointType).TrackingState

            If trackingState = trackingState.Tracked Then

                drawBrush = TrackedJointBrush
            ElseIf trackingState = trackingState.Inferred Then
                drawBrush = InferredJointBrush
            End If

            If drawBrush IsNot Nothing Then drawingContext.DrawEllipse(drawBrush, Nothing, jointPoints(jointType), JointThickness, JointThickness)
        Next
    End Sub

    Public Shared Sub DrawBone(joints As IReadOnlyDictionary(Of JointType, Joint),
                                  jointPoints As IDictionary(Of JointType, Point),
                                   jointType0 As JointType, jointType1 As JointType,
                                    drawingContext As DrawingContext)

        Dim joint0 = joints(jointType0)
        Dim joint1 = joints(jointType1)

        If joint0.TrackingState = TrackingState.NotTracked Or joint1.TrackingState = TrackingState.NotTracked Then Exit Sub

        If joint0.TrackingState = TrackingState.Inferred And joint1.TrackingState = TrackingState.Inferred Then Exit Sub

        Dim drawPen = InferredBonePen
        If joint0.TrackingState = TrackingState.Tracked And joint1.TrackingState = TrackingState.Tracked Then drawPen = TrackedBonePen

        drawingContext.DrawLine(drawPen, jointPoints(jointType0), jointPoints(jointType1))
    End Sub

    Public Shared Sub DrawClippedEdges(body As Body, displayHeight As Double, displayWidth As Double, drawingContext As DrawingContext)
        Dim clippedEdges = body.ClippedEdges

        If clippedEdges.HasFlag(FrameEdges.Bottom) Then drawingContext.DrawRectangle(Brushes.Red, Nothing,
            New Rect(0, displayHeight - ClipBoundsThickness, displayWidth, ClipBoundsThickness))

        If clippedEdges.HasFlag(FrameEdges.Top) Then drawingContext.DrawRectangle(Brushes.Red, Nothing,
            New Rect(0, 0, displayWidth, ClipBoundsThickness))

        If clippedEdges.HasFlag(FrameEdges.Left) Then drawingContext.DrawRectangle(Brushes.Red, Nothing,
            New Rect(0, 0, ClipBoundsThickness, displayHeight))

        If clippedEdges.HasFlag(FrameEdges.Right) Then drawingContext.DrawRectangle(Brushes.Red, Nothing,
            New Rect(displayWidth - ClipBoundsThickness, 0, ClipBoundsThickness, displayHeight))
    End Sub
End Class

In sostanza andiamo a disegnare le linee che congiungono le parti del corpo (in pratica le ossa), disegnando lo scheletro del player.

Nei prossimi post andremo ad esaminare quali altre informazioni ci mette a disposizione la classe Body e come possiamo sfruttarle.

Disclaimer: “This is preliminary software and/or hardware and APIs are preliminary and subject to change.”

Technorati Tags: ,,,

Commenti

Post popolari in questo blog

MVP Reconnect …… ovvero quando entri nella “famigghia” resti sempre nella “famigghia”!!!

Ma di che “famigghia” stiamo parlando!!!!

Fermi tutti, non si tratta di robe strane o sette segrete o affari malavitosi….stiamo parlando della grande famiglia dei Microsoft MVP.

Per chi non sapesse cosa sono i Microsoft MVP, vi consiglio di fare un giro sul sito ufficiale del programma (link), ma, volendolo spiegare in pochisime parole, si tratta di un riconoscimento che Microsoft da a persone che si distinguono per il loro impegno, aiutando gli altri ad ottenere il massimo grazie alle tecnologie Microsoft. Si tratta di persone, non dipendenti Microsoft, che mettono la loro passione, il loro tempo, la loro buona volontà per la divulgazione e la condivisione della conoscenza. Non necessariamente (come qualcuno erroneamente sostiene, evidentemente non conoscendo le basi del programma) si tratta di professionisti nel termine letterale del termine ma si tratta comunque di un gruppo di persone che sacrifica un pò del suo tempo (e, a volte, vi assicuro neanche pò!!!) per la sua passione.

Pe…

Template di progetto per sviluppare applicazioni WPF con Intel® RealSense™

E’ disponibile, nella gallery di Visual Studio, la prima versione del mio template di progetto per applicazioni WPF scritte in C# che permette di realizzare applicazioni con l’SDK di Intel® RealSense™.Il template si può scaricare direttamente all’interno Visual Studio utilizzando il tool “Extensions and Updates”oppure all’indirizzo https://visualstudiogallery.msdn.microsoft.com/1c36ecfd-8c00-4aee-b20c-a1726ab6424dIl template esegue le seguenti operazioni per voi:Aggiunge la reference all’assembly libpxcclr.cs.dll (nelle due distinte versioni per x86 e x64);Aggiunge lo script di post build per copiare la libreria libpxccpp2c.dll dalla cartella dell’SDK alla cartella bin del vostro progetto.Una volta creato il progetto dovete rimuovere la configurazione di compilazione AnyCPU (che non ha più senso) dalla vostra solution e sarete pronti per sviluppare con Intel® RealSense™.Ovviamente dovete installare l’SDK che potete scaricare all’indirizzo https://software.intel.com/en-us/intel-realsen…

Nuova versione del Band SDK

E’ di ieri l’annuncio del rilascio della nuova versione dell’SDK per il Microsoft Band.
Si tratta della versione 1.3.10417 (la precedente e, prima della serie, era la 1.3.10219 preview).
Maggiori informazioni, download dell’SDK per le tre piattaforme Windows Phone, iOS e Android all’indirizzo http://developer.microsoftband.com/.
Allo stesso indirizzo potrete trovare anche la documentazione.
Nei mesi scorsi mi sono gia’ occupato della precedente versione e questi sono i post che ne parlano:
Microsoft Band SDK Preview - First LookMicrosoft Band SDK Preview - ”Hello Band”Microsoft Band SDK Preview - Accesso ai sensoriMicrosoft Band SDK Preview - TileMicrosoft Band SDK Preview - NotificheMicrosoft Band SDK Preview - Personalizzazione
Gli argomenti trattati e il codice proposto dovrebbe, ad una prima lettura delle nuove funzionalita’ inserite, essere ancora valido e funzionante ma nei prossimi giorni prendero’ in esame tutti gli argomenti dei precedenti post e vedremo cosa cambia e cosa e’ …