Passa ai contenuti principali

Alla scoperta del Kinect: la classe Runtime

La classe Runtime (del namespace Microsoft.Research.KInect.Nui e contenuta nell’assemlby Microsoft.Research.Kinect) è per la nostra avventura come lo specchio per Alice: la porta peser entrare in un altro mondo……quello del Kinect!!!

A parte gli scherzi, la classe Runtime è fondamentale perché espone tutte le funzionalità che possiamo utilizzare del Kinect per quel che riguarda la parte delle gesture.

Di fatto un’istanza della classe Runtime gestisce un device connesso e la sua struttura è mostrata nella seguente immagine:

image

Per poter accedere alle funzionalità del sensore video, di profondità e allo skeleton engine (il “robo” che fa si che il Kinect individui la struttura di  un corpo umano che si agita come un forsennato davanti a lui) è sufficiente creare un’istanza della classe Runtime.

Ma procediamo con ordine, innanzitutto per avere visibilità sulla classe Runtime è necessario referenziare la libreria Microsoft.Research.Kinect e lo facciamo, come accade usualmente, utilizzando l’opzione di Visual Studio (menù Add Reference che si ottiene con il tasto destro):

SNAGHTML513572e

SNAGHTML515b803

A questo punto possiamo istanziare la classe:

  1. Dim nui = New Runtime

Istanziare la classe Runtime non è sufficiente per poter accedere ai sensori, è necessario inizializzare la classe con il metodo Initialize():

  1. Dim nui = New Runtime
  2. nui.Initialize(RuntimeOptions.UseColor or  RuntimeOptions.UseDepth)

In questo caso ho richiesto alla Runtime l’utilizzo dello stream video e di quello della profondità.

Prima di procedere analizziamo a fondo cosa ci mette a disposizione la Runtime a livello di proprietà, metodi ed eventi.

Le proprietà della Runtime

  • InstanceIndex : è un intero che identifica il numero di istanza della Runtime. Ogni device connesso al pc ha la sua Runtime e questo numero ci permette di identificare il singolo device;
  • NuiCamera : è la proprietà che ci consente di accedere all’oggetto Camera per poter, ad esempio, modificare l’inclinazione;
  • VideoStream : espone un’istanza di ImageStream che descive lo stream della video camera del Kinect. Vedremo in dettaglio in un altro post la classe ImageStream, per ora ci basti immaginarlo come uno stream che fornisce i fotogrammi rilevati dalla videocamera;
  • DepthStream : anche in questa proprietà espone un’istanza di ImageStream ma questa volta riguarda le immagini di profondità, ovvero le immagini in cui il Kinect traduce la distanza degli oggetti da se stesso;
  • SkeletonEngine : è la proprietà che espone il motore che gestisce il riconoscimento della figura umana (ce ne occuperemo in un altro post).

I metodi della Runtime

  • Initialize : abbiamo già visto questo metodo ed ha lo scopo di “avviare” l’aggetto richiedendo le funzionalità di cui abbiamo bisogno. Il parametro che accetta come argomento è del tipo RuntimeOptions, un’enumerazione flag con i seguenti valori:

image

  • Uninitialize : esegue lo shutdown dell’istanza di runtime. Una volta eseguito lo shutdown della Runtime, pur se l’oggetto managed non è Nothing, non è più possibile tentare di inizializzarlo in quanto viene rilasciata definitivamente tutta la parte unmanaged che c’è dietro. Se si prova si ottiene un’eccezione.

Gli eventi della Runtime

Gli eventi della classe Runtime sono i membri della classe che, di fatto, ci permettono un primo uso, anche se banale della stessa:

  • VideoFrameReady : viene sollevato quando un’immagine dello stream video è pronta per essere utilizzata;
  • DepthFrameReady : viene sollevato quando un’immagine dello stream di profondità è pronta per essere utilizzata;
  • SkeletonFrameReady : viene sollevato quando un’immagine del riconoscimento della figura umana è stata elaborata dallo skeleton engine ed è pronta per essere utilizzata.

I primi due eventi hanno per argomento la classe ImageFrameReadyEventArgs, mentre il terzo ha come argomento la classe SkeletonFrameReadyEventArgs.

Vedremo in dettaglio la struttura della SkeletonFrameReadyEventArgs in un prossimo post, mentre la ImageFrameReadyEventArgs ha la seguente struttura:

image

 

Di fatto l’argomento dell’evento ci fornisce l’immagine dello stream corrispondente che possiamo elaborare a nostro piacimento.

Quindi, istanziamo la Runtime, la inizializziamo, gestiamo gli eventi di cui sopra e siamo in grado, almeno per ora di riportare a video ciò che il Kinect sta inquadrando. In realtà, tra l’inizializzazione della Runtime e la fruizione delle immagini manca uno step fondamentale ovvero l’apertura degli stream VideoStream e DepthStream che se non aperti non comincerebbero a produrre immagini.

Il  metodo Open ha la seguente sintassi:

image

dove:

  • streamType : ci consente di definire la tipologia di stream che vogliamo ricevere;
  • poolSize : è il numero di frame che il runtime di NUI deve memorizzare. Il valore massimo è 4, nella maggior parte delle applicazioni è sufficiente 2;
  • resolution : dichiara la risoluzione che si vuole per lo stream. Sono disponibili svariate risoluzioni ma se si hanno più stream aperti (ad esempio video e depth) non tutte le combinazioni funzionano;
  • image : definisce la tipologia dell’immagine. Ad esempio DepthAndPlayerIndex cioè un’immagine che contiene al suo interno i dati di distanza e del numero del giocatore.

Andremo in dettaglio dello stream di distanza in un altro post prossimamente.

Nell’esempio allegato a questo post trovate una banale applicazione WPF, sviluppata con il pattern MVVM, che visualizza i due stream illustrati in precedenza.

La creazione del ViewModel relativo alla finestra principale è la seguente:

  1. Public Sub New()
  2.     If Not DesignerProperties.GetIsInDesignMode(New DependencyObject()) Then
  3.         Nui = New Runtime
  4.  
  5.         AddHandler Nui.DepthFrameReady, AddressOf DepthFrameHandler
  6.         AddHandler Nui.VideoFrameReady, AddressOf VideoFrameHandler
  7.  
  8.         Nui.Initialize(RuntimeOptions.UseColor Or RuntimeOptions.UseDepthAndPlayerIndex)
  9.  
  10.         Nui.DepthStream.Open(ImageStreamType.Depth, 2, ImageResolution.Resolution640x480, ImageType.DepthAndPlayerIndex)
  11.         Nui.VideoStream.Open(ImageStreamType.Video, 2, ImageResolution.Resolution640x480, ImageType.Color)
  12.     End If
  13. End Sub

dove Nui è un attributo privato del ViewModel di tipo Runtime.

Per la gestione delle immagini provenienti dai due stream mi sono affidato ad un toolkit sviluppato da Coding4Fun che fornisce degli extension methods per la conversione tra immagini Kinect e immagini WPF. Il toolkit si chiama “Kinect toolkit for WPF” (ne esiste una versione anche per Windows Form) e si può referenziare nel nostro progetto tramite NuGet:

SNAGHTML5752519

In questo caso il ViewModel espone le ultime due immagini dei due stream che possono essere messe in binding con un normale controllo Image dell’interfaccia:

  1. Imports System.ComponentModel
  2. Imports Microsoft.Research.Kinect.Nui
  3. Imports Coding4Fun.Kinect.Wpf
  4.  
  5. Public Class MainWindowViewModel
  6.     Implements INotifyPropertyChanged
  7.  
  8.     Private Nui As Runtime
  9.  
  10.     Public Sub New()
  11.         If Not DesignerProperties.GetIsInDesignMode(New DependencyObject()) Then
  12.             Nui = New Runtime
  13.  
  14.             AddHandler Nui.DepthFrameReady, AddressOf DepthFrameHandler
  15.             AddHandler Nui.VideoFrameReady, AddressOf VideoFrameHandler
  16.  
  17.             Nui.Initialize(RuntimeOptions.UseColor Or RuntimeOptions.UseDepth)
  18.  
  19.             Nui.VideoStream.Open(ImageStreamType.Video, 2, ImageResolution.Resolution640x480, ImageType.Color)
  20.             Nui.DepthStream.Open(ImageStreamType.Depth, 2, ImageResolution.Resolution320x240, ImageType.Depth)
  21.  
  22.         End If
  23.     End Sub
  24.  
  25.     Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
  26.  
  27.     Protected Sub OnNotifyPropertyChanged(propertyName As String)
  28.         RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
  29.     End Sub
  30.  
  31.     Private _VideoImage As ImageSource
  32.     Public Property VideoImage As ImageSource
  33.         Get
  34.             Return _VideoImage
  35.         End Get
  36.         Protected Set(value As ImageSource)
  37.             Me._VideoImage = value
  38.             OnNotifyPropertyChanged("VideoImage")
  39.         End Set
  40.     End Property
  41.  
  42.     Private _DepthImage As ImageSource
  43.     Public Property DepthImage As ImageSource
  44.         Get
  45.             Return _DepthImage
  46.         End Get
  47.         Protected Set(value As ImageSource)
  48.             Me._DepthImage = value
  49.             OnNotifyPropertyChanged("DepthImage")
  50.         End Set
  51.     End Property
  52.  
  53.     Private Sub DepthFrameHandler(sender As Object, e As ImageFrameReadyEventArgs)
  54.         Me.DepthImage = e.ImageFrame.ToBitmapSource()
  55.     End Sub
  56.  
  57.     Private Sub VideoFrameHandler(sender As Object, e As ImageFrameReadyEventArgs)
  58.         Me.VideoImage = e.ImageFrame.ToBitmapSource()
  59.     End Sub
  60.  
  61.  
  62.  
  63.     Protected Overrides Sub Finalize()
  64.         MyBase.Finalize()
  65.         Nui.Uninitialize()
  66.     End Sub
  67. End Class

Commenti

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