Passa ai contenuti principali

Kinect: gli scheletri e l’uomo vitruviano

Continuando la carrellata di ciò che è cambiato nel nuovo SDK del Kinect rispetto alla beta2, eccoci all’uomo vitruviano (vedi post).

E’ oramai noto che uno degli aspetti più interessanti del Kinect è il fatto che il suo SDK è in grado di fornirci la posizione di alcuni punti ben determinati dei player inquadrati e riconosciuti dal sensore.

L’insieme di questi punti (20 per la precisione) per ogni player è detto Skeleton e i punti sono mostrati in figura:

skeleton joints

L’SDK è in grado di ritornare una collection di oggetti di tipo Joint, ognuno dei quali rappresenta i dati spaziali del singolo punto.

Cominciamo dal vedere come accedere al sensore Kinect e attivare il riconoscimento dello scheletro (Skeletal Tracking).

Come già visto nei precedenti due post della serie, il meccanismo per accedere al Kinect è quello di passare dalla collezione KinectSensors della classe KinectSensor, registrare l’opportuno gestore di evento e abilitare lo stream che ci interessa:

  1. If KinectSensor.KinectSensors.Any() Then
  2.     Sensor = KinectSensor.KinectSensors(0)
  3.     If Sensor.Status = KinectStatus.Connected Then
  4.         AddHandler Sensor.SkeletonFrameReady, AddressOf SkeletonFrameHandler
  5.         Sensor.SkeletonStream.Enable()
  6.         Try
  7.             Sensor.Start()
  8.         Catch ex As Exception
  9.  
  10.         End Try
  11.     End If
  12. End If

L’evento SkeletonFrameReady viene sollevato dall’SDK del Kinect ogni volta che un nuovo frame contenente gli Skeleton è pronto.

La figura seguente mostra la struttura delle classi coinvolte nell’argomento dell’evento SkeletonFrameReady:

image

Come già visto per il video e la profondità, anche in questo caso, l’effettivo frame viene ottenuto utilizzando il metodo OpenSkeletonFrame.

Osserviamo che, a differenza della Beta2, la classe SkeletonFrame non ha più le proprietà NormalToGravity e Quality (poco significative) e la proprietà FloorClipPlane è di tipo Tuple(Of Single, Single, Single, Single) anziché Vector.

La proprietà FloorClipPlane fornisce i coefficienti dell’equazione del piano di terra (pavimento) stimata dall’SDK. L’equazione ha la forma:

Ax+By+Cz+D=0

dove:
    A = FloorClipPlane.Item1
    B = FloorClipPlane.Item2
    C = FloorClipPlane.Item3
    D = FloorClipPlane.Item4

L’equazione del piano è normalizzata, quindi D rappresenta, di fatto, l’altezza della camera rispetto al pavimento. Anche in questa versione dell’SDK, come nella versione Beta, il vettore è sempre nullo.

Una volta ottenuta l’istanza dello SkeletonFrame, possiamo utilizzare il metodo CopySkeletonDataTo  per recuperare gli effettivi dati degli scheletri:

  1. Private Skeletons() As Skeleton = New Skeleton(6) {}
  2.  
  3. Private Sub SkeletonFrameHandler(sender As Object, e As SkeletonFrameReadyEventArgs)
  4.     Dim skeletonFrame = e.OpenSkeletonFrame()
  5.     If skeletonFrame IsNot Nothing Then
  6.         skeletonFrame.CopySkeletonDataTo(Skeletons)
  7.         '
  8.         ' gestione degli skeleton recuperati
  9.         '
  10.     End If
  11. End Sub

La classe Skeleton (nella Beta2 si chiamava SkeletonData) contiene i dati essenziali degli scheletri e il diagramma delle classi coinvolte è il seguente:

ClassDiagram-SkeletonData

Ogni Skeleton ha un suo TrackingId che lo identifica univocamente, un TrackingState che ci dice se è tracciato o meno (ricordiamo che su 6 scheletri disponibili, solo fino a 2 sono tracciati contemporaneamente), una posizione (proprietà Position) che dovrebbe rappresentare il baricentro e una collezione di Joint (proprietà Joints).

Possiamo recuperare i singoli Joint a partire dal loro tipo (enumerazione JointType).

Questa enumerazione era già presente nella Beta2 ma il suo nome era JointId (ora è decisamente più chiara) così come la proprietà del Joint che la contiene si chiama JointType (si chiamava JointID nella Beta2).

Altri cambiamenti sono stati introdotti nelle Position (sia dello Skeleton che del Joint): nella Beta2 queste erano di tipo Vector, mentre ora sono di tipo SkeletonPoint. La SkeletonPoint non ha più la proprietà W (presente nel Vector) e non significativa nella Beta2.

Come abbiamo già visto quando abbiamo parlato del sensore di profondità, il Kinect for Windows (e, quindi, l’SDK) ha la modalità “Near Mode”.

Questa influenza non solo la minima distanza di riconoscimento di un player (come già visto nel precedente post) ma anche il numero di joint rilevati, come mostrato nella seguente tabella:

Near Mode vs Default Mode Funzionalità

La tabella va necessariamente spiegata perchè fuorviante: nel caso di Near Mode, la classe Skeleton valorizza la sola proprietà Position (e solo se l’intero scheletro è tracciato), quindi il punto denominato “Center Hip Joint” non è il Joint “HipCenter” (uno dei 20 a disposizione) anche se, in prima approssimazione, possiamo pensarli coincidenti.

Per poter passare nella modalità “Near Mode” si deve intervenire sulla proprietà Range del DepthStream esposto dal KinectSensor impostandola a KinectRange.NearMode.

Se si imposta la proprietà Range del DepthStream quando questo non è stato abilitato tramite il metodo Enable, non si ottiene alcun errore, ma il sensore non va il modalità near mode.

L’applicazione proposta in allegato mostra, istante per istante, le posizioni dei 20 punti sensibili dello skeletal tracking, come mostrato in figura:

15-03-2012 20-34-50

 

 

Commenti

Unknown ha detto…
How to use the SDK version ?
Massimo Bonanni ha detto…
This post is based on Kinect V1 and its SDK. You can use the code that you can find in this post with Kinect SDK 1.8.

Post popolari in questo blog

VB.NET : Aggregare stringhe con LINQ

Tip facile facile, ma a qualcuno potrebbe servire. Supponiamo di avere una lista di stringhe (magari come risultato di una query LINQ) e di voler ottenere una stringa con la concatenazione delle stesse: Dim list = CreateList() Dim concatStr = (From s In list _ Select s).Aggregate( Function (currentString, nextString) currentString + nextString) MessageBox.Show(concatStr) Il metodo CreateList non ci interessa, in questo momento, ma crea una lista di oggetti String. Protected Function CreateList() As IEnumerable( Of String ) Dim list As String () = {" stringa1 ", " stringa2 ", " stringa3 ", " stringa4 ", " stringa5 "} Return list.AsEnumerable() End Function Questo metodo potrebbe restituire una qualsiasi lista di oggetti di cui, nella select successiva recuperiamo solo stringhe. La stessa tecnica è utilizzabile per concatenare stringhe inserendovi un carattere separatore Dim list = CreateList() Dim

VB.NET: SplashScreen con effetto fade-in

In questo post vorrei proporvi un modo per realizzare una splash screen per le nostre applicazioni Windows Form che appare progressivamente con un effetto fade. Supponiamo di avere il nostro progetto VB.NET in una soluzione Visual Studio 2008 in cui abbiamo il sorgente della nostra applicazione Windows Form. Inseriamo una splash screen utilizzando il menù Progetto->Aggiungi Nuovo Elemento e selezionando il tipo di elemento “Schermata Iniziale” A questo punto Visual Studio creerà, automaticamente, la schermata iniziale che possiamo personalizzare graficamente come vogliamo. Per poter fare in modo che questa finestra appaia nel momento in cui avviamo l’applicazione, è necessario aprire le proprietà del progetto e impostare la maschera di avvio: In questo modo, all’avvio dell’applicazione, la schermata appare immediatamente e scompare un attimo prima della visualizzazione della finestra dell’applicazione. Possiamo far apparire la schermata iniziale con un ef

VB.NET: Convertire un file DOC in RTF e PDF con office interop

In questo post vorrei proporvi del codice per poter convertire un file .doc in un file .rtf oppure .pdf utilizzando le API di interoperabilità di Office. Creeremo una classe, DocConverter, che esporrà le due funzionalità sopra citate. Cominciamo con il prevedere un attributo privato della classe che rappresenterà l’applicazione Word che utilizzeremo per la conversione. Creeremo l’istanza dell’attributo privato all’interno del costruttore della classe: Public Sub New () If Not CreateWordApp() Then Throw New ApplicationException(" Assembly di interoperabilità con Office non trovato! ") End If End Sub Private _wordApp As Word.ApplicationClass Protected Function CreateWordApp() As Boolean Dim retval = True Try _wordApp = New Word.ApplicationClass() _wordApp.Visible = False Catch ex As System.Exception _wordApp = Nothing retval = False End Try Return retval End Function La conve