Introduzione a Kinect
Scritto da
Massimo Bonanni
il
mercoledì 7 settembre 2011
Linguaggio:
•
Framework:
•
Livello: 100
Download pdf
Questo breve articolo nasce con lo scopo di fornire
un'introduzione al device Kinect e al suo SDK per permettere a
tutti coloro che hanno intenzione di avvicinarsi allo sviluppo di
applicazioni con questo dispositivo, di poter avere un punto di
partenza. L'articolo fa riferimento all'SDK Beta 1 nella versione
rilasciata il 29 Luglio 2011 (1.0.12)
Per cominciare
Il dispositivo Kinect può essere connesso ad un normale PC
tramite un apposito cavo (che troviamo incluso nella confezione che
vede il solo Kinect ma che non è compreso nel bundle con la Xbox
360).

Il cavo in questione, otre che fornire la connettività USB,
fonisce anche l'alimentazione al device.
Per poter sviluppare applicazioni che sfruttano le potenzialità
del Kinect è necessario scaricare ed installare l'SDK che possiamo
trovare all'indirizzo
http://research.microsoft.com/en-us/um/redmond/projects/kinectsdk/download.aspx.
Se abbiamo intenzione di sfruttare anche le capacità di voice
recognition del dispositivo ci servirà anche lo Speech SDK
scaricabile all'indirizzo
http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=14373.
I requisiti herdware da soddisfare sono i seguenti:
- Processore dual-core con cpu a 2.66GHz o superiore;
- Almeno 2GB di RAM;
- Scheda grafica compatibile con Windows 7 che supporti DirectX
9.0
- Kinect (ovviamente) e cavo USB
Dal punto di vista software, quello di cui abbiamo bisogno
è:
- Microsoft Visual Studio 2010 Express (o una qualsiasi altra
versione);
- NET Framework 4.0
Per la parte relativa al supporto grafico:
Per le funzionalità audio:
Una guida completa all'installazione di tutti i componenti è
presente all'indirizzo
http://research.microsoft.com/en-us/um/redmond/projects/kinectsdk/docs/readme.htm
che fornisce tutti i dettagli su cosa serve e come installare le
varie componenti.
Architettura
Prima di capire come è strutturata l'architettura del dispositivo,
cerchiamo di capire cosa ci mette a disposizione lo stesso in
termini di sensori:

- Video Camera: si tratta di una comune camera
in grado di fornire immagini con risoluzione 640x480 a
colori;
- Sensori di Profondità : rappresentano uno dei
punti di forza del dispositivo e sono in grado di recuperare un'
"immagine" in cui sono riportati i dati di distanza di ciò che
appare davanti al Kinect;
- Batteria di Microfoni: si tratta di 4
microfoni in grado di fornire funzionalità di pulitura del suono,
posizionamento della sorgente sonora, cancellazione dell'eco, etc.,
etc.
- Inclinazione motorizzata: il dispositivo può
essere brandeggiato (da codice) con un angolo verticale di ±23
gradi rispetto al piano orizzontale.
Il Software Development Kit fornisce una libreria in grado di
recuperare e gestire i flussi di dati provenienti dai sensori
appena visti e può essere utilizzata sia in C++ che in uno dei
linguaggi Managed del Framework .NET.

I componenti costituenti l'SDK sono mostrati nella seguente
figura:

1) Hardware : L'hardware comprende i sensori
visti in precedenza è l'hub USB che permette il loro collegamento
al pc.
2) Microsoft Kinect drivers: I driver di Windows 7
hanno le seguenti funzionalità:
- Permettono l'accesso all'array di microfoni con le API Audio
standard di Windows.
- Forniscono gli stream della video camera e dei sensori di
profondità.
- Forniscono la possibilità di utilizzare più device
contemporaneamente.
3) NUI API : Un insieme di API che
permettono di recuperare i dati dai sensori di immagine e di
controllare il device stesso (ad esempio brandeggiare il
dispositivo);
4) KinectAudio DMO : Estende le
funzionalità dell'array di microfoni supportato in Windows 7 per
fornire le funzionalità di Beamforming (mappatura sonora dell'area)
e localizzazione della sorgente sonora
5) Windows 7 standard APIs : Le API
audio, speech e media presenti in Windows 7 e Microsoft Speech.
La classe Runtime
Per poter utilizzare le classi messe a disposizione dall'SDK, è
necessario referenziare l'assembly Microsoft.Research.Kinect (menù
"Add Reference" che si ottiene con il tasto destro sul progetto in
cui vogliamo utilizzare l'SDK del Kinect):

Una volta referenziato l'assembly corretto possiamo procedere
all'utilizzo dei dati provenienti dai sensori video del nostro
device.
Gli stream video e di profondità vengono gestiti entrambi grazie
alla classe Runtime la quale permette anche di ottenere
informazioni riguardo lo Skeletal Tracking.

La classe Runtime mette a disposizione tre eventi che permettono
di recuperare i frame video, di profondità e di skeletal
tracking:
- DepthFrameReady : restituisce il frame di
profondità disponibile proveniente dai sensori;
- VideoFrameReady: restituisce il frame video
disponibile proveniente dalla camera;
- SkeletonFrameReady: restituisce il frame di
Skeletalk Tracking elaborato dalla libreria dell'SDK e contenente
le informazioni sugli "scheletri" dei player posti davanti al
dispositivo.
Per poter ricevere i dati provenienti dai sensori è necessario
istanziare la classe Runtime, inizializzarla scegliendo quali
sorgenti si vogliono gestire, agganciare gli opportuni gestori di
evento agli eventi della classe stessa e aprire gli stream di
input. Nel seguente esempio (il code behind di una window WPF), è
riportato il codice necessario per gestire tutti e tre gli stream
dati:
Private Nui As Runtime
Private Sub MainWindow_Loaded(sender As Object, e As System.Windows.RoutedEventArgs) Handles Me.Loaded
Try
Nui = New Runtime
Nui.Initialize(RuntimeOptions.UseColor Or RuntimeOptions.UseDepthAndPlayerIndex Or RuntimeOptions.UseSkeletalTracking)
AddHandler Nui.VideoFrameReady, AddressOf VideoFrameReadyHandler
AddHandler Nui.DepthFrameReady, AddressOf DepthFrameReadyHandler
AddHandler Nui.SkeletonFrameReady, AddressOf SkeletonFrameReadyHandler
Nui.VideoStream.Open(ImageStreamType.Video, 2, ImageResolution.Resolution640x480, ImageType.Color)
Nui.DepthStream.Open(ImageStreamType.Depth, 2, ImageResolution.Resolution320x240, ImageType.Depth)
Catch ex As Exception
End Try
End Sub
Private Sub VideoFrameReadyHandler(sender As Object, e As ImageFrameReadyEventArgs)
.
.
End Sub
Private Sub DepthFrameReadyHandler(sender As Object, e As ImageFrameReadyEventArgs)
.
.
End Sub
Private Sub SkeletonFrameReadyHandler(sender As Object, e As SkeletonFrameReadyEventArgs)
.
.
End Sub
Gli argomenti dell'evento VideoFrameReady e dell'evento
DepthFrameReady contengono entrambi un oggetto di tipo
ImageFrame:

La classe ImageFrame contiene l'immagine recuperata (proprietà
Image di tipo PlanarImage) e le informazioni riguardanti il
Timestamp , il tipo di immagine, le risoluzione, etc., etc.
I bytes costituenti l'immagine sono contenuti all'interno della
struttura PlanarImage nell'array Bits (array di bytes). La
struttura PlanarImage contiene altresì i dati relativi ad altezza,
larghezza e mnumero di bytes per ogni pixel.
Ogni tipo di immagine, infatti, ha un differente numero di bytes
per pixel:
- Immagine video di tipo Color: 4 bytes per pixel contenenti le
componenti ARGB (Alfa, Red, Green e Blue);
- Immagine di tipo ColorYUV: 4 bytes per pixel contenenti
le componenti YUV (ciano, magenta, giallo e nero);
- Immagine di tipo ColorYUV Raw: 2 bytes per pixel
contenenti le componenti YUV (ciano, magenta, giallo e nero);
- Dati di profondità senza indice del player: 2 bytes per pixel
(solo i primi 12 bit rappresentano la distanza, in millimetri del
punto dal device);
- Dati di profondità con indice del player: 2 bytes per pixel (i
3 bit meno significativi contengono l'indice del player, i
successivi 12 bit rappresentano la distanza, in millimetri, del
punto dal device).
Se vogliamo, ad esempio, convertire l'oggetto PlanarImage (di
tipo Color) in una BitmapSource da usare, ad esempio come source di
un controllo Image WPF, possiamo scrivere:
Dim imageData As PlanarImage = e.ImageFrame.Image
Dim bitmap = BitmapSource.Create(imageData.Width, imageData.Height, 96, 96,
PixelFormats.Bgr32, Nothing, imageData.Bits,
imageData.Width * imageData.BytesPerPixel)
Per quanto riguarda la parte video, c'è da dire che il
dispositivo riprende le immagini sempre con risoluzione 1280x1024,
comprime tali immagini (in modo che si possa avere un trasferimento
dati efficiente su bus USB), li trasferisce al pc dove la libreria
li decomprime nella risoluzione scelta.
L'algoritmo è, comunque, a perdita di informazione (minima).
L'argomento dell'evento SkeletonFrameReady, invece, contiene un
oggetto di classe SkeletonFrame.

Peculiarità della classe SkeletonFrame è quella di contenere,
tra le altre varie proprietà, una collezione di oggetti
SkeletonData (uno per ogni player identificato dal
dispositivo).
Gli SkeletonData altro non sono classi che modellano gli
"scheletri" dei player rilevati dal dispositivo:

Possiamo definire come "scheletro", l'insieme di un certo numero
di punti (20 per l'esattezza) caratteristici del corpo umano come
la testa, le mani, i polsi, i piedi, etc., etc.. Ogni punto (Joint)
è identificato da un id specifico (enumerazione JointID) ed espone
la posizione che questo assume nello spazio (in termini di
coordinate x, y e z) e lo stato di tracciamento del
punto(tracciato, dedotto dalla posizione degli altri punti - ad
esempio perchè nascosto da altri punti del corpo - o non
tracciato).

Lo stesso "scheletro" dispone dello
stato di tracciamento in modo che possiamo selezionare quale player
è realmente tracciato e quale no.
L'attuale versione della libreria del Kinect, infatti, riesce a
gestire fino a 6 player ma solo 2 sono effettivamente nello stato
Tracked (Active Tracking), mentre gli altri 4 sono nello stato
PositionOnly (Passive Tracking) nel quale sono alcune proprietà
sono disponibili.
Il seguente codice ci mostra come gestire l'evento
SkeletonFrameReady per recuperare la posizione delle due mani del
primo "scheletro" tracciato e verificare che la distanza sul piano
parallelo al device (quello x,y) sia minore di una certa
quantità:
Private Sub SkeletonFrameReadyHandler(sender As Object, e As SkeletonFrameReadyEventArgs)
Dim firstSkeleton = (From s In e.SkeletonFrame.Skeletons
Where s.TrackingState = SkeletonTrackingState.Tracked
Select s).FirstOrDefault()
If firstSkeleton IsNot Nothing Then
Dim handRightJoint = firstSkeleton.Joints(JointID.HandRight)
Dim handRightPosition = handRightJoint.Position
Dim handLeftJoint = firstSkeleton.Joints(JointID.HandLeft)
Dim handLeftPosition = handRightJoint.Position
If Calculate2DDistance(handRightPosition, handLeftPosition) < 0.05 Then
' posso considerare le mani vicine
End If
End If
End Sub
Private Function Calculate2DDistance(a As Vector, b As Vector) As Double
Dim dx = Math.Pow(a.X - b.X, 2)
Dim dy = Math.Pow(a.Y - b.Y, 2)
Dim distance = Math.Sqrt(dx + dy)
Return distance
End Function
I valori X e Y dell'oggetto Vector che descrive le posizioni
appartengono all'intervallo [-1,1] mentre il valore di Z
rappresenta la distanza (in metri) del punto dal sensore del
device.
Funzionalità Audio
Nella parte introduttiva dell'articolo abbiamo visto che
il device dispone di 4 microfoni disposti lungo la parte inferiore
del case.
Il Kinect è in grado di fornire le seguenti funzionalità
audio:
- eliminazione dell'eco (Multichannel Echo Cancellation
MEC);
- posizionamento della sorgente sonora (Beamforming);
- soppressione o riduzione del rumore.
In più, se utilizziamo le API fornite dall'SDK del Kinect in
cooperazione con lo Speech SDK, possiamo anche dotare le nostre
applicazioni del voice recognition.
In questo articolo non ci addentreremo in questa ultima
funzionalità ma accenneremo solo a come gestire il flusso audio
proveniente dal dispositivo.
La classe fondamentale per la gestione del flusso audio è la
KinectAudioSource:

Il metodo Start consente di avviare l'operazione di cattura
dell'audio da parte del dispositivo e restituisce lo stream dati
contenente l'audio.
Nell'utilizzo della classe KinectAudioSource è necessario fare
attenzione al fatto che tale classe incapsula il comportamento
della libreria (scritta in codice nativo) che accede alla parte
audio del device e deve lavorare in Multi Thread Apartment (MTA).
Poichè le applicazioni .NET sono Single Thread Apartment (STA) può
essere necessario relegare l'utilizzo della KinectAudioSource in un
opportuno thread avviato in modalità MTA:
Dim t = New Thread(New ThreadStart(AddressOf RecordAudio))
t.SetApartmentState(ApartmentState.MTA)
t.Start()
La classe KinectAudioSource fornisce anche informazioni sulla
localizzazione della sorgente sonora ed in particolare lo fa in due
modi distinti (ma che possono essere utilizzati anche in
contemporanea):
- Evento BeamChanged: sollevato dalla classe
ogni qualvolta si accorge che la sorgente sonora ha cambiato
posizione. L'argomento dell'evento contiene l'angolo orizzontale
dove è posizionata la sorgente (con 0 se la sorgente è
immediatamente di fronte al device);
- Proprietà SoundSourcePosition: la proprietà
contiene la stessa informazione restituita dall'argomento del
precedente evento.
La classe KinectAudioSource espone, infine, una serie di
proprietà che consentono di agire sui parametri dei microfoni per
modificare eco, soppressione del rumore, etc., etc..
Conclusioni
Questo articolo ha lo scopo di solleticare l'appetito nei confronti
di un device che promette potenzialità inimmaginabili considerato
che ha un SDK alla versione Beta. Tra i riferimenti potete trovare
approfondimenti su tutti gli argomenti trattati rapidamente in
questo articolo.
Riferimenti
[1] Kinect for Windows SDK Web Site:
http://research.microsoft.com/en-us/um/redmond/projects/kinectsdk/
[2] Channel9 Kinect for Windows SDK Quickstarts: http://dev9.channel9.msdn.com/Series/KinectSDKQuickstarts
[3] Channel9 Coding4Fun : http://dev9.channel9.msdn.com/coding4fun/kinect
Tags: Kinect