|
|
|
@ -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
|
|
|
|
|
/// </summary>
|
|
|
|
|
public class InfraredImageStream : KinectStream
|
|
|
|
|
{
|
|
|
|
|
public override ImageSource Source => throw new NotImplementedException();
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Maximum value (as a float) that can be returned by the InfraredFrame
|
|
|
|
|
/// </summary>
|
|
|
|
|
private const float InfraredSourceValueMaximum = (float)ushort.MaxValue;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The value by which the infrared source data will be scaled
|
|
|
|
|
/// </summary>
|
|
|
|
|
private const float InfraredSourceScale = 0.75f;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Smallest value to display when the infrared data is normalized
|
|
|
|
|
/// </summary>
|
|
|
|
|
private const float InfraredOutputValueMinimum = 0.01f;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Largest value to display when the infrared data is normalized
|
|
|
|
|
/// </summary>
|
|
|
|
|
private const float InfraredOutputValueMaximum = 1.0f;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Active Kinect sensor
|
|
|
|
|
/// </summary>
|
|
|
|
|
private KinectSensor kinectSensor = null;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Reader for infrared frames
|
|
|
|
|
/// </summary>
|
|
|
|
|
private InfraredFrameReader infraredFrameReader = null;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Description (width, height, etc) of the infrared frame data
|
|
|
|
|
/// </summary>
|
|
|
|
|
private FrameDescription infraredFrameDescription = null;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Bitmap to display
|
|
|
|
|
/// </summary>
|
|
|
|
|
private WriteableBitmap infraredBitmap = null;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Current status text to display
|
|
|
|
|
/// </summary>
|
|
|
|
|
private string statusText = null;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the bitmap to display
|
|
|
|
|
/// </summary>
|
|
|
|
|
public override ImageSource Source
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return this.infraredBitmap;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Execute shutdown tasks
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="sender">object sending the event</param>
|
|
|
|
|
/// <param name="e">event arguments</param>
|
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Handles the infrared frame data arriving from the sensor
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="sender">object sending the event</param>
|
|
|
|
|
/// <param name="e">event arguments</param>
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 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.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="infraredFrameData">Pointer to the InfraredFrame image data</param>
|
|
|
|
|
/// <param name="infraredFrameDataSize">Size of the InfraredFrame image data</param>
|
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|