diff --git a/KinectConnection/DepthImageStream.cs b/KinectConnection/DepthImageStream.cs
index 1e4d780..0429cff 100644
--- a/KinectConnection/DepthImageStream.cs
+++ b/KinectConnection/DepthImageStream.cs
@@ -1,5 +1,11 @@
-using System;
+using Microsoft.Kinect;
+using System;
+using System.ComponentModel;
+using System.Globalization;
+using System.IO;
+using System.Windows;
using System.Windows.Media;
+using System.Windows.Media.Imaging;
namespace KinectConnection
{
@@ -8,16 +14,154 @@ namespace KinectConnection
///
public class DepthImageStream : KinectStream
{
- public override ImageSource Source => throw new NotImplementedException();
+ ///
+ /// Map depth range to byte range
+ ///
+ private const int MapDepthToByte = 8000 / 256;
+
+ ///
+ /// Active Kinect sensor
+ ///
+ private KinectSensor kinectSensor = null;
+
+ ///
+ /// Reader for depth frames
+ ///
+ private DepthFrameReader depthFrameReader = null;
+
+ ///
+ /// Description of the data contained in the depth frame
+ ///
+ private FrameDescription depthFrameDescription = null;
+
+ ///
+ /// Bitmap to display
+ ///
+ private WriteableBitmap depthBitmap = null;
+
+ ///
+ /// Intermediate storage for frame data converted to color
+ ///
+ private byte[] depthPixels = null;
+
+ ///
+ /// Gets the bitmap to display
+ ///
+ public override ImageSource Source
+ {
+ get
+ {
+ return this.depthBitmap;
+ }
+ }
public override void Start()
{
- throw new NotImplementedException();
+ // get FrameDescription from DepthFrameSource
+ this.depthFrameDescription = this.KinectSensor.DepthFrameSource.FrameDescription;
+
+ // allocate space to put the pixels being received and converted
+ this.depthPixels = new byte[this.depthFrameDescription.Width * this.depthFrameDescription.Height];
+
+ // create the bitmap to display
+ this.depthBitmap = new WriteableBitmap(this.depthFrameDescription.Width, this.depthFrameDescription.Height, 96.0, 96.0, PixelFormats.Gray8, null);
+
+ if (this.KinectSensor != null)
+ {
+ // open the reader for the depth frames
+ this.depthFrameReader = this.KinectSensor.DepthFrameSource.OpenReader();
+
+ if (this.depthFrameReader != null)
+ {
+ // wire handler for frame arrival
+ this.depthFrameReader.FrameArrived += this.Reader_FrameArrived;
+ }
+ }
}
public override void Stop()
{
throw new NotImplementedException();
}
+
+ ///
+ /// Handles the depth frame data arriving from the sensor
+ ///
+ /// object sending the event
+ /// event arguments
+ private void Reader_FrameArrived(object sender, DepthFrameArrivedEventArgs e)
+ {
+ bool depthFrameProcessed = false;
+
+ using (DepthFrame depthFrame = e.FrameReference.AcquireFrame())
+ {
+ if (depthFrame != null)
+ {
+ // the fastest way to process the body index data is to directly access
+ // the underlying buffer
+ using (Microsoft.Kinect.KinectBuffer depthBuffer = depthFrame.LockImageBuffer())
+ {
+ // verify data and write the color data to the display bitmap
+ if (((this.depthFrameDescription.Width * this.depthFrameDescription.Height) == (depthBuffer.Size / this.depthFrameDescription.BytesPerPixel)) &&
+ (this.depthFrameDescription.Width == this.depthBitmap.PixelWidth) && (this.depthFrameDescription.Height == this.depthBitmap.PixelHeight))
+ {
+ // Note: In order to see the full range of depth (including the less reliable far field depth)
+ // we are setting maxDepth to the extreme potential depth threshold
+ ushort maxDepth = ushort.MaxValue;
+
+ // If you wish to filter by reliable depth distance, uncomment the following line:
+ //// maxDepth = depthFrame.DepthMaxReliableDistance
+
+ this.ProcessDepthFrameData(depthBuffer.UnderlyingBuffer, depthBuffer.Size, depthFrame.DepthMinReliableDistance, maxDepth);
+ depthFrameProcessed = true;
+ }
+ }
+ }
+ }
+
+ if (depthFrameProcessed)
+ {
+ this.RenderDepthPixels();
+ }
+ }
+
+ ///
+ /// Directly accesses the underlying image buffer of the DepthFrame to
+ /// create a displayable bitmap.
+ /// This function requires the /unsafe compiler option as we make use of direct
+ /// access to the native memory pointed to by the depthFrameData pointer.
+ ///
+ /// Pointer to the DepthFrame image data
+ /// Size of the DepthFrame image data
+ /// The minimum reliable depth value for the frame
+ /// The maximum reliable depth value for the frame
+ private unsafe void ProcessDepthFrameData(IntPtr depthFrameData, uint depthFrameDataSize, ushort minDepth, ushort maxDepth)
+ {
+ // depth frame data is a 16 bit value
+ ushort* frameData = (ushort*)depthFrameData;
+
+ // convert depth to a visual representation
+ for (int i = 0; i < (int)(depthFrameDataSize / this.depthFrameDescription.BytesPerPixel); ++i)
+ {
+ // Get the depth for this pixel
+ ushort depth = frameData[i];
+
+ // To convert to a byte, we're mapping the depth value to the byte range.
+ // Values outside the reliable depth range are mapped to 0 (black).
+ this.depthPixels[i] = (byte)(depth >= minDepth && depth <= maxDepth ? (depth / MapDepthToByte) : 0);
+ }
+ }
+
+ ///
+ /// Renders color pixels into the writeableBitmap.
+ ///
+ private void RenderDepthPixels()
+ {
+ this.depthBitmap.WritePixels(
+ new Int32Rect(0, 0, this.depthBitmap.PixelWidth, this.depthBitmap.PixelHeight),
+ this.depthPixels,
+ this.depthBitmap.PixelWidth,
+ 0);
+ }
}
}
diff --git a/KinectConnection/InfraredImageStream.cs b/KinectConnection/InfraredImageStream.cs
index 6bfed0c..a4a17c8 100644
--- a/KinectConnection/InfraredImageStream.cs
+++ b/KinectConnection/InfraredImageStream.cs
@@ -1,5 +1,11 @@
-using System;
+using Microsoft.Kinect;
+using System;
+using System.ComponentModel;
+using System.Globalization;
+using System.IO;
+using System.Windows;
using System.Windows.Media;
+using System.Windows.Media.Imaging;
namespace KinectConnection
{
@@ -8,16 +14,171 @@ namespace KinectConnection
///
public class InfraredImageStream : KinectStream
{
- public override ImageSource Source => throw new NotImplementedException();
+ ///
+ /// Maximum value (as a float) that can be returned by the InfraredFrame
+ ///
+ private const float InfraredSourceValueMaximum = (float)ushort.MaxValue;
+
+ ///
+ /// The value by which the infrared source data will be scaled
+ ///
+ private const float InfraredSourceScale = 0.75f;
+
+ ///
+ /// Smallest value to display when the infrared data is normalized
+ ///
+ private const float InfraredOutputValueMinimum = 0.01f;
+
+ ///
+ /// Largest value to display when the infrared data is normalized
+ ///
+ private const float InfraredOutputValueMaximum = 1.0f;
+
+ ///
+ /// Active Kinect sensor
+ ///
+ private KinectSensor kinectSensor = null;
+
+ ///
+ /// Reader for infrared frames
+ ///
+ private InfraredFrameReader infraredFrameReader = null;
+
+ ///
+ /// Description (width, height, etc) of the infrared frame data
+ ///
+ private FrameDescription infraredFrameDescription = null;
+
+ ///
+ /// Bitmap to display
+ ///
+ private WriteableBitmap infraredBitmap = null;
+
+ ///
+ /// Current status text to display
+ ///
+ private string statusText = null;
+
+ ///
+ /// Gets the bitmap to display
+ ///
+ public override ImageSource Source
+ {
+ get
+ {
+ return this.infraredBitmap;
+ }
+ }
+
+ ///
+ /// Execute shutdown tasks
+ ///
+ /// object sending the event
+ /// event arguments
+ private void MainWindow_Closing(object sender, CancelEventArgs e)
+ {
+ if (this.infraredFrameReader != null)
+ {
+ // InfraredFrameReader is IDisposable
+ this.infraredFrameReader.Dispose();
+ this.infraredFrameReader = null;
+ }
+
+ if (this.kinectSensor != null)
+ {
+ this.kinectSensor.Close();
+ this.kinectSensor = null;
+ }
+ }
+
+ public InfraredImageStream()
+ {
+ // get FrameDescription from InfraredFrameSource
+ this.infraredFrameDescription = this.KinectSensor.InfraredFrameSource.FrameDescription;
+ }
public override void Start()
{
- throw new NotImplementedException();
+ // create the bitmap to display
+ this.infraredBitmap = new WriteableBitmap(this.infraredFrameDescription.Width, this.infraredFrameDescription.Height, 96.0, 96.0, PixelFormats.Gray32Float, null);
+
+ if (this.KinectSensor != null)
+ {
+ // open the reader for the depth frames
+ this.infraredFrameReader = this.KinectSensor.InfraredFrameSource.OpenReader();
+
+ if (this.infraredFrameReader != null)
+ {
+ // wire handler for frame arrival
+ this.infraredFrameReader.FrameArrived += this.Reader_InfraredFrameArrived;
+ }
+ }
}
public override void Stop()
{
throw new NotImplementedException();
}
+
+ ///
+ /// Handles the infrared frame data arriving from the sensor
+ ///
+ /// object sending the event
+ /// event arguments
+ private void Reader_InfraredFrameArrived(object sender, InfraredFrameArrivedEventArgs e)
+ {
+ // InfraredFrame is IDisposable
+ using (InfraredFrame infraredFrame = e.FrameReference.AcquireFrame())
+ {
+ if (infraredFrame != null)
+ {
+ // the fastest way to process the infrared frame data is to directly access
+ // the underlying buffer
+ using (Microsoft.Kinect.KinectBuffer infraredBuffer = infraredFrame.LockImageBuffer())
+ {
+ // verify data and write the new infrared frame data to the display bitmap
+ if (((this.infraredFrameDescription.Width * this.infraredFrameDescription.Height) == (infraredBuffer.Size / this.infraredFrameDescription.BytesPerPixel)) &&
+ (this.infraredFrameDescription.Width == this.infraredBitmap.PixelWidth) && (this.infraredFrameDescription.Height == this.infraredBitmap.PixelHeight))
+ {
+ this.ProcessInfraredFrameData(infraredBuffer.UnderlyingBuffer, infraredBuffer.Size);
+ }
+ }
+ }
+ }
+ }
+
+ ///
+ /// Directly accesses the underlying image buffer of the InfraredFrame to
+ /// create a displayable bitmap.
+ /// This function requires the /unsafe compiler option as we make use of direct
+ /// access to the native memory pointed to by the infraredFrameData pointer.
+ ///
+ /// Pointer to the InfraredFrame image data
+ /// Size of the InfraredFrame image data
+ private unsafe void ProcessInfraredFrameData(IntPtr infraredFrameData, uint infraredFrameDataSize)
+ {
+ // infrared frame data is a 16 bit value
+ ushort* frameData = (ushort*)infraredFrameData;
+
+ // lock the target bitmap
+ this.infraredBitmap.Lock();
+
+ // get the pointer to the bitmap's back buffer
+ float* backBuffer = (float*)this.infraredBitmap.BackBuffer;
+
+ // process the infrared data
+ for (int i = 0; i < (int)(infraredFrameDataSize / this.infraredFrameDescription.BytesPerPixel); ++i)
+ {
+ // since we are displaying the image as a normalized grey scale image, we need to convert from
+ // the ushort data (as provided by the InfraredFrame) to a value from [InfraredOutputValueMinimum, InfraredOutputValueMaximum]
+ backBuffer[i] = Math.Min(InfraredOutputValueMaximum, (((float)frameData[i] / InfraredSourceValueMaximum * InfraredSourceScale) * (1.0f - InfraredOutputValueMinimum)) + InfraredOutputValueMinimum);
+ }
+
+ // mark the entire bitmap as needing to be drawn
+ this.infraredBitmap.AddDirtyRect(new Int32Rect(0, 0, this.infraredBitmap.PixelWidth, this.infraredBitmap.PixelHeight));
+
+ // unlock the bitmap
+ this.infraredBitmap.Unlock();
+ }
}
}
diff --git a/KinectConnection/KinectConnection.csproj b/KinectConnection/KinectConnection.csproj
index 85e64d6..3fdbff9 100644
--- a/KinectConnection/KinectConnection.csproj
+++ b/KinectConnection/KinectConnection.csproj
@@ -23,6 +23,7 @@
DEBUG;TRACE
prompt
4
+ true
pdbonly
@@ -31,6 +32,7 @@
TRACE
prompt
4
+ true
diff --git a/KinectConnection/KinectStreamsFactory.cs b/KinectConnection/KinectStreamsFactory.cs
index 63b45b0..d06b010 100644
--- a/KinectConnection/KinectStreamsFactory.cs
+++ b/KinectConnection/KinectStreamsFactory.cs
@@ -21,6 +21,8 @@ namespace KinectConnection
{
{ KinectStreams.Color, () => new ColorImageStream() },
{ KinectStreams.Body, () => new BodyImageStream() },
+ { KinectStreams.IR, () => new InfraredImageStream() },
+ { KinectStreams.Depth, () => new DepthImageStream() },
// Other streams ...
};
}
diff --git a/KinectSensorStreams/ViewModel/MainWindowVM.cs b/KinectSensorStreams/ViewModel/MainWindowVM.cs
index 024d111..1890e4a 100644
--- a/KinectSensorStreams/ViewModel/MainWindowVM.cs
+++ b/KinectSensorStreams/ViewModel/MainWindowVM.cs
@@ -56,7 +56,7 @@ namespace KinectSensorStreams.ViewModel
// factory
KinectStreamsFactory = new KinectStreamsFactory(new KinectManager());
// kinect stream => color stream for now
- KinectStream = KinectStreamsFactory[KinectStreams.Body];
+ KinectStream = KinectStreamsFactory[KinectStreams.Depth];
StartCommand = new RelayCommand(Start);
// [Question] : StartCommand ici peut être mieux que BeginInit() dans MainWindow.xaml.cs ?