Introduzione a Kinect

Scritto da  Massimo Bonanni il mercoledì 7 settembre 2011
Linguaggio: VB   •  Framework: 4.0   •  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).

figura1

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:

figura2

  • 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.

figura3

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

figura4

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):

figura5

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. 

figura6

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: 

figura7

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.

figura8

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:

figura9

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).

figura10

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: 

figura11

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

 
x