In questo post prenderemo in esame lo stream di profondità fornito da Kinect V2.
Le API che utilizzeremo sono del tutto simili a quelle viste per la sorgente infrarossi nel precedente post.
La classe KinectSensor (già vista nei precedenti post della serie) espone la proprietà DepthFrameSource che ci consente di accedere al reader per lo stream di profondità (come già visto per lo stream dell’immagine a colori e del’infrarosso).
Possiamo, dunque, una volta ottenuta l’istanza della classe KinectSensor, ottenere il reader grazie al metodo OpenReader:
If Sensor IsNot Nothing Then
Sensor.Open()
Dim depthFrameDescription = Sensor.DepthFrameSource.FrameDescription
DepthData = New UShort(CInt(depthFrameDescription.LengthInPixels - 1)) {}
DepthPixels = New Byte(depthFrameDescription.Width * depthFrameDescription.Height * BytesPerPixel - 1) {}
DepthBitMap = New WriteableBitmap(depthFrameDescription.Width, depthFrameDescription.Height,
96.0, 96.0, PixelFormats.Bgr32, Nothing)
DepthReader = Sensor.DepthFrameSource.OpenReader()
End If
La variabile DepthReader contiene l’istanza della classe DepthFrameReader attraverso la quale possiamo gestire i frame di profondità provenienti dal device.
Come già visto in precedenza, abbiamo la necessità di gestire l’evento FrameArrived:
AddHandler DepthReader.FrameArrived, AddressOf DepthFrameArrivedHandler
End If
L’evento FrameArrived viene sollevato nel momento in cui è disponibile un frame, in questo caso di tipo profondità, da poter elaborare:
'
'
'
End Sub
L’argomento dell’evento, di tipo DepthFrameArrivedEventArgs, espone la proprietà FrameReference, di tipo DepthFramereference, che ci consente di ottenere il frame effettivo:
Il metodo AcquireFrame, della classe DepthFrameReference, consente di ottenere l’istanza della classe DepthFrame contenente gli effettivi dati del frame:
Osserviamo che il frame ci fornisce direttamente le distanze di lavoro del Kinect: DepthMaxReliableDistance e DepthMinReliableDistance contengono rispettivamente la distanza massima e minima di operatività del device.
Possiamo ottenere i dati di profondità dell’immagine ripresa dal Kinect utilizzando uno dei due metodi CopyFrameDataXXX:
- CopyFrameDataToArray : copia i dati di profondità in un’array di unsigned short (intero a 16 bit senza segno). La lunghezza dell’array è data dalla proprietà LengthInPixels esposta dalla classe FrameDescription esposta dall’omonima proprietà del frame;
- CopyFrameDataToBuffer : esegue la copia dei dati di profondità in una locazione di memoria.
Ogni elemento dell’array contiene la distanza del corrispondente punto di coordinate X,Y posto immediatamente di fronte al device. Dati, infatti, X e Y le due coordinate del punto di cui recuperare la distanza (in millimetri), e data la larghezza dell’immagine di profondità (contenuta nella proprietà Width della classe FrameDescription) otteniamo l’indice dell’elemento dell’array con la seguente funzione:
Dim arrayIndex = y * currentWidth + x
Possiamo sfruttare l’evento FrameArrived per renderizzare l’imagine di profondità in modo che sia comprensibile agli occhi umani.
Dim frameReference = e.FrameReference
Dim frame As DepthFrame = Nothing
Try
frame = frameReference.AcquireFrame()
If frame IsNot Nothing Then
DepthFrameDescription = frame.FrameDescription
frame.CopyFrameDataToArray(DepthData)
DepthMinReliableDistance = frame.DepthMinReliableDistance
DepthMaxReliableDistance = frame.DepthMaxReliableDistance
DepthData.CopyDepthDataToPixelsArray(DepthPixels, DepthMaxReliableDistance, DepthMinReliableDistance)
DepthBitMap.WritePixels(New Int32Rect(0, 0, DepthFrameDescription.Width, DepthFrameDescription.Height),
Me.DepthPixels, DepthFrameDescription.Width * BytesPerPixel, 0)
End If
Catch ex As Exception
Finally
If frame IsNot Nothing Then
frame.Dispose()
End If
End Try
End Sub
Il metodo di estensione CopyDepthDataToPIxelsArray permette di convertire la distanza pixel per pixel in un colore da visualizzare.
Public Sub CopyDepthDataToPixelsArray(depthData As UShort(), pixelsArray As Byte(),
maxDepth As UShort, minDepth As UShort)
Dim colorPixelIndex = 0
For i = 0 To depthData.Length - 1
Dim depth = depthData(i)
Dim intensity As Byte = CByte(If(depth >= minDepth And depth <= maxDepth, depth, 0) * 255 / maxDepth)
pixelsArray(colorPixelIndex) = intensity
colorPixelIndex += 1
pixelsArray(colorPixelIndex) = intensity
colorPixelIndex += 1
pixelsArray(colorPixelIndex) = intensity
colorPixelIndex += 2
Next
End Sub
Osserviamo che, a differenza di quanto accade nella versione 1 del Kinect e del suo SDK, i dati di profondità non contengono informazioni riguardo l’eventuale player presente davanti al device.
Vedremo in un post specifico che queste informazioni sono contenute nello stream BodyIndex (BodyIndexFrameSource).
Commenti