Quantcast
Channel: Опыт пользователя и дизайн
Viewing all articles
Browse latest Browse all 360

Intel RealSense SDK – Stream video

$
0
0

In questo articolo cominceremo ad analizzare le funzionalità esposte dall’SDK ed in particolare cominceremo a vedere la classe PXCMSenseManager e le funzionalità riguardanti gli stream video.
L’articolo si riferisce alla versione 6.0.x dell’SDK di Intel® RealSense™ attualmente scaricabile all’indirizzo https://software.intel.com/en-us/intel-realsense-sdk/download

La classe PXCMSenseManager

La classe PXCMSenseManager, come già visto in un precedente articolo, ci fornisce delle funzionalità di base da utilizzare negli scenari più comuni e accessibili in maniera più semplice ed immediata di quanto non si possa fare utilizzando la classe PXCMSession.
La classe PXCMSenseManager, di fatto, consente di utilizzare facilmente gli stream provenienti dalla camera o i moduli face tracking o di gesture recognition.
In questo articolo cominceremo a dare un’occhiata alla classe PXCMSenseManager e cercheremo di utilizzarla per recuperare gli stream video colore, profondità e infrarossi.
Nella figura seguente è mostrato lo schema della classe PXCMSenseManager:

La PXCMSenseManager espone anche un’istanza di PXCMSession e una istanza di PXCMCaptureManager.
La prima viene creata al momento della creazione dell’istanza di PXCMSenseManager, mentre la seconda viene creata solamente se si legge la proprietà di sola lettura captureManager.
La creazione di una nuova istanza di PXCMSenseManager avviene, come di consuetudine all’interno dell’SDK di RealSense™, utilizzando un factory method della classe PXCMSenseManager stessa:

SenseManager = PXCMSenseManager.CreateInstance()

Una volta istanziata la classe PXCMSenseManager, dobbiamo abilitare i moduli che ci interessano e lo possiamo fare utilizzando uno dei metodi EnableXXX. Nel caso degli stream video, il metodo che utilizzeremo sarà EnableStream o EnableStreams. 
Nell’esempio seguente vediamo come abilitare tutti gli stream disponibili nella camera F200 (colori, infrarossi e profondità) utilizzando il metodo EnableStream:

SenseManager.EnableStream(PXCMCapture.StreamType.STREAM_TYPE_COLOR, 1280, 720)
SenseManager.EnableStream(PXCMCapture.StreamType.STREAM_TYPE_DEPTH, 640, 480)
SenseManager.EnableStream(PXCMCapture.StreamType.STREAM_TYPE_IR, 640, 480)

La firma del metodo EnableStream prevede la tipologia dello stream, le dimensioni del frame che si desiderano e, eventualmente, il frame rate con cui si desidera lavorare.
Se non mettiamo il frame rate, sarà l’SDK a decidere quello migliore in base alle caratteristiche di impegno della macchina su cui sta girando l’applicazione.
L’elenco degli stream possibili si trova nell’enumerazione PXCMCapture.StreamType:
 

 
I valori STREAM_TYPE_LEFT e STREAM_TYPE_RIGHT rappresentano, rispettivamente, lo stream sinistro e destro della camera stereoscopica disponibile nel modello R200.  
In alternativa al metodo EnableStream possiamo utilizzare il metodo EnableStreams che accetta in input una struttura dati PXCMVideoModule.DataDesc al cui interno sono inserite tutte le informazioni necessarie all’abilitazione degli stream (di fatto le stesse che passeremmo ai singoli metodi EnabeStream).
Una volta abilitati gli stream, dobbiamo inizializzare la PXCMSenseManager utilizzando il metodo Init(). Questo metodo, come la stragrande maggioranza dei metodi dell’SDK, ritorna un valore di tipo pxcmStatus che identifica se si è verificato un errore o meno:

If SenseManager.Init() <> pxcmStatus.PXCM_STATUS_NO_ERROR Then
    MessageBox.Show("Errore nell'inizializzazione della camera")
    Close()
End If

Oppure, utilizzando un metodo di estensione messo a disposizione dal wrapper della libreria C++ dell’SDK verso il mondo .NET:

If SenseManager.Init().IsError() Then
    MessageBox.Show("Errore nell'inizializzazione della camera")
    Close()
End If

Se la chiamata al metodo Init() ha successo, la camera avvia i propri stream e la nostra applicazione ha la possibilità di ottenere i vari frame che li compongono.
Per fare ciò possiamo utilizzare due approcci:

  • Polling: implementiamo una sorta di loop (tipo il game loop che si utilizza nei giochi) all’interno del quale recuperiamo i frame, li elaboriamo (ad esempio creando l’immagine da visualizzare) e aggiorniamo l’interfaccia;
  • CallBack: utilizziamo l’infrastruttura basata su call-back messa disposizione dalla classe PCXMSenseManager per essere informati dall’SDK quando nuovi frame sono disponibili. A quel punto possiamo elaborarli e aggiornare la UI (una sorta di eventi). Vedremo che anche in questo caso abbiamo la necessità di avere un loop “infinito” ma l’onere di recuperare i frame e restituirceli è a carico dell’SDK (il loop ha lo scopo di far partire l’acquisizione dei frame.

L’approccio Polling ha senso nei giochi e in tutte quelle applicazioni in cui abbiamo necessità del pieno controllo sul recupero dei frame.
L’approccio “ad eventi”, invece, svincola lo sviluppatore dall’implementare la logica di recupero dei frame nel loop, lasciandogli solo l’incombenza di elaborarli all’interno della calback assegnata al SenseManager.
Polling
Come già detto in precedenza, in questo tipo di approccio, è nostro compito istruire un loop “infinito” che, ad ogni iterazione, recuperi i frame video che ci interessano, li elabori costruendo delle immagini (o qualsiasi altra elaborazione) e visualizzi queste immagini all’interno della UI.
Il loop, inoltre, deve “girare” in un thread separato rispetto all’interfaccia grafica per non bloccare la stessa.
Fortunatamente il framework .NET ci viene in aiuto fornendoci la classe Task e, quindi, realizzare un loop “infinito” in un thread separato si riduce a scrivere:

Private PollingTask As Task
Private PollingTaskCancellationToken As CancellationToken
Private TaskCancellationTokenSource As CancellationTokenSource

Private Sub ConfigurePollingTask()
    TaskCancellationTokenSource = New CancellationTokenSource()
    PollingTaskCancellationToken = TaskCancellationTokenSource.Token
    PollingTask = New Task(AddressOf PollingCode)
    PollingTask.Start()
End Sub

Private Sub PollingCode()
    While Not PollingTaskCancellationToken.IsCancellationRequested
        '' Aquisizione frame'' Elaborazione frame'' Visualizzazione frame
    End While
End Sub

PollingTask è l’istanza di Task che si occupa di “contenere” il thread del loop mentre PollingTaskCancellationToken è il cancellation token utilizzato dall’applicazione per comunicare al loop “infinito” (che, quindi, infinito non è) che deve chiudere (ad esempio quando l’applicazione viene chiusa).
Il loop che ci interessa e, in cui, viene interrogata la camera, si trova si trova nel metodo PollingCode.
Il primo step da eseguire è quello di richiedere all’SDK di poter accedere ai frame correnti e per fare ciò facciamo uso del metodo AcquireFrame() della classe PXCMSenseManager:

If SenseManager.AcquireFrame().IsSuccessful() Then''
    SenseManager.ReleaseFrame()
End If

Il metodo AcquireFrame() non restituisce alcun frame, semplicemente dice all’SDK di recuperare i frame che ci interessano. In questo modo l’SDK non alloca alcuna memoria fino a che non abbiamo veramente la necessità di recuperare i frame.
Il metodo ReleaseFrame() è fondamentale perché’ comunica all’SDK che abbiamo terminato il nostro lavoro e che può rilasciare gli eventuali frame da noi recuperati.
Per recuperare effettivamente i frame possiamo utilizzare il metodo QuerySample() della PXCMSenseManager:

Dim sample = SenseManager.QuerySample()

Il risultato di questa chiamata è un’istanza della classe PXCMCapture.Sample:

La classe Sample espone i 5 possibili stream già visti in precedenza in 5 differenti proprietà che possiamo interrogare per ottenere istanze della classe PXCMImage:


 
La classe PXCMImage astrae il concetto di immagine, espone dei metadati della stessa (nella proprietà info) e fornisce una serie di metodi utili per poterla effettivamente recuperare e gestire. 
Tra questi citiamo:

  • AcquireAccess: permette di ottenere l’istanza di PXCMImage.ImageData in cui sono effettivamente contenuti i byte dello stream


 

  • ReleaseAccess: permette di rilasciare, in maniera corretta, una istanza di ImageData precedentemente recuperata.

Grazie a questi due metodi possiamo recuperare effettivamente la bitmap da visualizzare:

Private Function GetImage(image As PXCMImage) As WriteableBitmap
    Dim imageData As PXCMImage.ImageData = Nothing
    Dim returnImage As WriteableBitmap = Nothing
    Dim width = 0
    Dim height = 0
    If image.AcquireAccess(PXCMImage.Access.ACCESS_READ,
                           PXCMImage.PixelFormat.PIXEL_FORMAT_RGB32,
                           imageData).IsSuccessful() Then
        width = Convert.ToInt32(imageData.pitches(0) \ 4)
        height = image.info.height
        returnImage = imageData.ToWritableBitmap(width, height, 96, 96)
        image.ReleaseAccess(imageData)
    End If
    Return returnImage
End Function

Nella funzione sfruttiamo il metodo ToWritableBitmap() per ottenere una istanza di WritableBitmap da poter visualizzare nella UI.
Questa funzione è utilizzabile per ogni stream della camera, in questo modo il nostro metodo che elabora il Sample ricavato dalla PXCMSenseManager diventa:

Private Sub ElaborateSample(ByVal sample As PXCMCapture.Sample)
    If sample Is Nothing Then Return

    Dim imageRGB As WriteableBitmap = Nothing
    Dim imageIR As WriteableBitmap = Nothing
    Dim imageDepth As WriteableBitmap = Nothing

    Dim pcxmImage As PXCMImage
    pcxmImage = sample.color

    If sample.color IsNot Nothing Then
        imageRGB = GetImage(sample.color)
        imageRGB.Freeze()
    End If
    If sample.ir IsNot Nothing Then
        imageIR = GetImage(sample.ir)
        imageIR.Freeze()
    End If
    If sample.depth IsNot Nothing Then
        imageDepth = GetImage(sample.depth)
        imageDepth.Freeze()
    End If

    Dispatcher.Invoke(Sub()
                          Me.ImageRGB = imageRGB
                          Me.ImageIR = imageIR
                          Me.ImageDepth = imageDepth
                      End Sub)
End Sub

Se i sample degli stream sono presenti (non Nothing), creiamo l’immagine e impostiamo delle proprietà che finiranno sull’interfaccia grazie al binding di WPF.
Osserviamo che la chiamata al metodo Freeze() e l’uso del Dispatcher è necessario in quanto stiamo girando in un thread che non è quello principale della UI.

Callback

La gestione con callback prevede la possibilità di impostare, in una proprietà della PXCMSenseManager, un handler ad una funzione che verrà richiamata tutte le volte che un frame o un insieme di frame è pronto.
Il procedimento si basa sul creare una istanza della classe PXCMSenseManager.Handler
 

Ed impostare l’opportuna proprietà con il delegate che verrà invocato nel momento in cui l’istanza di PXCMSenseManager ha la necessità di comunicare con l’esterno.
Nel nostro esempio imposteremo la proprietà onNewSample:

Dim handler = New PXCMSenseManager.Handler()
handler.onNewSample = AddressOf OnNewSample
dove il nostro delegate altro non è che la funzione:
Private Function OnNewSample(mid As Integer, sample As PXCMCapture.Sample) As pxcmStatus
    ElaborateSample(sample)
    Return pxcmStatus.PXCM_STATUS_NO_ERROR
End Function

Questa sfrutta di nuovo il metodo ElaborateSample() visto in precedenza e restituisce al chiamante che l’operazione è andata a buon fine.
Per poter inizializzare l’utilizzo dell’handler (o degli handler se ne utilizziamo più d’uno), è necessario invocare la funzione Init():

Dim handler = New PXCMSenseManager.Handler()
handler.onNewSample = AddressOf OnNewSample
If SenseManager.Init(handler).IsError() Then
    MessageBox.Show("Errore nell'inizializzazione della camera")
    Close()
End If

Come già accennato in precedenza, anche in questo caso è necessario un thread separato per poter dare il via al meccanismo di ricezione dei frame (che arriveranno con la chiamata al delegate). Possiamo decidere di utilizzare la coppia di chiamate AcquireFrame()/ReleaseFrame() o l’unica StreamFrames(). Le modalità sono esclusive, o si utilizza una o l’altra.

Chiudere il SenseManager

Quando la nostra applicazione viene chiusa, è importantissimo rilasciare correttamente l’istanza di PXCMSenseManager che si sta utilizzando.
Abbiamo già visto come rilasciare i frame ogni volta che ne richiediamo l’accesso. Per il SenseManager la situazione è analoga: nel momento in cui la nostra applicazione chiude, è necessario richiamare il metodo Close():

SenseManager.Close()
SenseManager.Dispose()

 

  • Разработчики
  • Партнеры
  • Профессорский состав
  • Студенты
  • Microsoft Windows* 10
  • Microsoft Windows* 8.x
  • Технология Intel® RealSense™
  • .NET*
  • Начинающий
  • Технология Intel® RealSense™
  • Опыт пользователя и дизайн
  • Лицевая видеокамера F200
  • Видеокамера R200
  • URL
  • Начало работы

  • Viewing all articles
    Browse latest Browse all 360

    Trending Articles



    <script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>