Con questo post vorrei iniziare una serie dedicata alle novità e ai cambiamenti presenti nell’SDK rilasciato il 1 febbraio 2012.
Cominciamo con riportare il link del portale Kinect in cui trovare il pacchetto di installazione dell’SDK e le risorse necessarie per cominciare a lavorare con lo stesso.
In questi post prenderò spunto dagli esempi che avevo a suo tempo riportato in un’analoga serie di post (ai tempi della beta2) e proverò di farli funzionare nuovamente con il nuovo SDK analizzando ciò che è cambiato.
Installato l’SDK, abbiamo a disposizione un browser che ci permette di accedere facilmente alla documentazione e agli esempi.
Prima cosa che balza all’occhio quando tentiamo di utilizzare l’assembly contenente le classi di gestione del Kinect è che l’assembly da referenziare non è più Microsoft.Research.Kinect.dll ma Microsoft.Kinect.dll.
In maniera analoga, il namespace principale delle classi dell’SDK non è più Microsoft.Research.Kinect ma Microsoft.Kinect.
La classe principale utilizzata nella Beta2 per accedere alle funzionalità del Kinect era la classe Runtime, nella versione 1.0 questa è completamente sparita ed è sparito anche il modo con cui si accede alla classe di gestione del Kinect.
La classe Runtime è stata sostituita dalla classe KInectSensor contenuta nel namespace Microsoft.Kinect.
Il seguente disegno mostra la struttura della classe KinectSensor:
La classe KinectSensor non ha costruttori ma espone una proprietà Shared chiamata KinectSensors che ci consente di avere a disposizione l’elenco dei sensori Kinect attaccati al nostro PC (fino a 4) e rilevati dal sistema. Altra cosa importante (di cui ci occuperemo in un prossimo post) è l’esistenza dell’evento AllFrameReady il cui scopo è quello di fornire dei frame sincronizzati per le sorgenti abilitate.
Possiamo, quindi, accedere al nostro Kinect nel seguente modo:
- If KinectSensor.KinectSensors.Any() Then
- Sensor = KinectSensor.KinectSensors(0)
- End If
dove Sensor è la variabile che utilizzeremo per accedere al dispositivo.
Una volta recuperata l’istanza della classe per la gestione del Kinect, dobbiamo sottoscrivere gli eventi da gestire per i frame che arrivano dal dispositivo e aprire gli opportuni stream.
Anche per questo, ci sono stati dei cambiamenti. In particolare, l’evento VideoFrameReady della vecchia Runtime, è stato rinominato in ColorFrameReady e l’attivazione degli stream non avviene più con il metodo Initialize (che apriva gli stream e avviava la gestione degli stessi) ma richiamando il metodo Enable (o Disable per disabilitare) delle proprietà ColorStream, DepthStream o SkeletonStream (in base a quale stream gestire) seguito dal metodo Start.
Il metodo Enable prevede, nel caso del ColorStream, la possibilità di passare come argomento la risoluzione delle immagini da recuperare. La presenza dell’Enable e del Disable fa si che si possano attivare e disattivare i flussi provenienti dal Kinect “a caldo” senza dover fermare l’istanza del KinectSensor e riavviarla.
Ad esempio per gestire una risoluzione video di 640x480 a 30 fps, possiamo scrivere:
- If KinectSensor.KinectSensors.Any() Then
- Sensor = KinectSensor.KinectSensors(0)
- If Sensor.Status = KinectStatus.Connected Then
- AddHandler Sensor.ColorFrameReady, AddressOf ColorFrameReadyHandler
- Sensor.ColorStream.Enable(ColorImageFormat.RgbResolution640x480Fps30)
- Try
- Sensor.Start()
- Catch ex As Exception
- End Try
- End If
- End If
La proprietà Status della KinectSensor ci consente di sapere lo stato del sensore (nel nostro caso se è connesso) in modo da eseguire le operazioni di apertura degli stream e di aggancio degli eventi solo se effettivamente il sensore è connesso.
Anche la firma del gestore dell’evento ColorFrameReady è cambiata rispetto alla firma del precedente evento presente nella classe Runtime.
- Private Sub ColorFrameReadyHandler(sender As Object, e As ColorImageFrameReadyEventArgs)
- End Sub
L’argomento ColorImageFrameReadyArgs permette di accedere alle informazioni relative al frame recuperato dal Kinect
Da notare la presenza del metodo OpenColorImageFrame per recuperare l’istanza della classe ColorImageFrame che è il vero contenitore dei dati provenienti dalla video camera del Kinect.
Per ottenere l’array di byte costituenti l’immagine effettiva è necessario definire un array di byte della lunghezza pari a PixelDataLenght e copiarvi dentro i dati con il metodo CopyPixelDataTo().
- Using colorImageFrame = e.OpenColorImageFrame()
- If colorImageFrame IsNot Nothing Then
- Dim imageArray() = New Byte(colorImageFrame.PixelDataLength - 1) {}
- colorImageFrame.CopyPixelDataTo(imageArray)
- '
- ' Gestione dell'array dati proveniente dal Kinect
- '
- End If
- End Using
Dato l’array di byte possiamo creare una BitmapSource e possiamo visualizzare la stessa all’interno, ad esempio, di un controllo image WPF:
- Private Sub ColorFrameReadyHandler(sender As Object, e As ColorImageFrameReadyEventArgs)
- Dim colorImageFrame = e.OpenColorImageFrame()
- Dim imageArray(colorImageFrame.PixelDataLength) As Byte
- colorImageFrame.CopyPixelDataTo(imageArray)
- Dim stride = colorImageFrame.Width * colorImageFrame.BytesPerPixel
- Me.VideoImage.Source = BitmapSource.Create(colorImageFrame.Width, colorImageFrame.Height,
- 96, 96,
- PixelFormats.Bgr32, Nothing,
- imageArray, stride)
- End Sub
Ogni volta che l’SDK avrà ricevuto un frame dalla video camera del dispositivo, richiamerà il nostro gestore di evento e visualizzeremo l’immagine corrispondente.
Infine, nel momento in cui dobbiamo chiudere la nostra applicazione e rilasciare la classe di gestione del Kinect, possiamo utilizzare il metodo Stop (per fermare il flusso degli stream e il Dispose per rilasciare effettivamente l’istanza:
- If Sensor IsNot Nothing Then
- If Sensor.IsRunning Then
- Sensor.Stop()
- Sensor.Dispose()
- End If
- End If
Prima di concludere questo primo post, un accenno allo stride di una immagine per capire meglio il codice precedente.
Quando memorizziamo una immagine in memoria, in realtà, questa è memorizzata in un buffer che potrebbe contenere dei byte in più rispetto a quelli necessari a memorizzare l’immagine stessa (vedi figura). Questi byte in più non influenzano la visualizzazione ma solo lo spazio occupato.
Lo stride di un’immagine altro non è che la lunghezza di una riga della precedente matrice buffer. Nella fattispecie è data dalla larghezza del frame per il numero di byte per pixel.
In allegato trovate una solution di esempio:
Commenti