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

Wdusitkul 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: 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:PublicSubNew()
IfNot CreateWordApp() Then
ThrowNew ApplicationException("Assembly di interoperabilità con Office non trovato!")
EndIf
EndSub
Private _wordApp As Word.ApplicationClass
ProtectedFunction CreateWordApp() AsBoolean
Dim retval = True
Try
_wordApp = New Word.ApplicationClass()
_wordApp.Visible = False
Catch ex As System.Exception
_wordApp = Nothing
retval = False
EndTry
Return retval
EndFunction

La conversione del file doc sarà effettuata aprendo il file stesso ed eseguendo un’operazione di SaveAs:

Pr…

Creare uno shortcut con VB.NET

Prendendo spunto da un post comparso sul forum MSDN vorrei proporvi un tip su come creare uno shortcut utilizzando VB.NET.Per poter creare uno shortcut possiamo procedere in due modi: o ci studiamo la struttura del file .lnk e scriviamo una classe che è in grado di ricreare tale struttura oppure utilizziamo Windows Scripting Host.La prima soluzione è percorribile ma laboriosa perchè la struttura di un file lnk non è banale. Chi fosse interessato a vedere come è composto, internamente, un file lnk può scaricare la seguente reference guide (link).Io vorrei proporvi la seconda strada e realizzerò una classe che incapsula l’utilizzo di Windows Scripting Host.L’object model di Windows Scripting Host è contenuto nella dll IWshRuntimeLibrary che può essere referenziata, nel nostro progetto, utilizzando il tab COM della finestra di aggiunta delle reference:Tra gli oggetti che troviamo all’interno della libreria utilizzeremo la classe WshShell e la classe WshShortcut.La prima delle due rappres…

VB.NET for Dummies: Gli Eventi

Vorrei iniziare con questo post una serie dedicata ad aspetti di VB.NET di base che possono essere utile a coloro che si avvicinano al mondo .NET e che, in genere, non vengono trattati a livello base.La serie di post non ha la pretesa di essere assolutamente esaustivi sugli argomenti che tratteròIn questo primo post parleremo degli eventi.Cosa sono e a cosa servonoUn evento è la “notifica” dell’accadimento di qualcosa.Quando, ad esempio, premiamo un bottone della finestra di un’applicazione, dietro le quinte, il bottone stesso “notifica” al mondo circostante che qualcuno, in quell’istante, lo ha premuto.Sta, poi, al mondo circostante preoccuparsi di “intercettare” l’evento stesso per gestirlo (tramite un gestore di evento).Attenzione a non confondere la pressione del tasto con la “notifica” della pressione del tasto: l’evento è la “notifica” dell’accadimento, non l’accadimento stesso.Ma a cosa serve tutto ciò?Utilizzare gli eventi è un modo per disaccoppiare due o più attori del nostr…