//------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // //------------------------------------------------------------------------------ using System; using System.ComponentModel; using System.IO; using System.Runtime.InteropServices.WindowsRuntime; using Windows.ApplicationModel.Resources; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Media.Imaging; using WindowsPreview.Kinect; namespace Microsoft.Samples.Kinect.InfraredBasics { /// /// Main page for sample /// public sealed partial class MainPage : Page, INotifyPropertyChanged { /// /// InfraredSourceValueMaximum is the highest value that can be returned in the InfraredFrame. /// It is cast to a float for readability in the visualization code. /// private const float InfraredSourceValueMaximum = (float)ushort.MaxValue; /// /// The InfraredOutputValueMinimum value is used to set the lower limit, post processing, of the /// infrared data that we will render. /// Increasing or decreasing this value sets a brightness "wall" either closer or further away. /// private const float InfraredOutputValueMinimum = 0.01f; /// /// The InfraredOutputValueMaximum value is the upper limit, post processing, of the /// infrared data that we will render. /// private const float InfraredOutputValueMaximum = 1.0f; /// /// The InfraredSceneValueAverage value specifies the average infrared value of the scene. /// This value was selected by analyzing the average pixel intensity for a given scene. /// Depending on the visualization requirements for a given application, this value can be /// hard coded, as was done here, or calculated by averaging the intensity for each pixel prior /// to rendering. /// private const float InfraredSceneValueAverage = 0.08f; /// /// The InfraredSceneStandardDeviations value specifies the number of standard deviations /// to apply to InfraredSceneValueAverage. This value was selected by analyzing data /// from a given scene. /// Depending on the visualization requirements for a given application, this value can be /// hard coded, as was done here, or calculated at runtime. /// private const float InfraredSceneStandardDeviations = 3.0f; /// /// Size of the RGB pixel in the bitmap /// private const int BytesPerPixel = 4; /// /// Resource loader for string resources /// #if WIN81ORLATER private ResourceLoader resourceLoader = ResourceLoader.GetForCurrentView("Resources"); #else private ResourceLoader resourceLoader = new ResourceLoader("Resources"); #endif /// /// Active Kinect sensor /// private KinectSensor kinectSensor = null; /// /// Reader for infrared frames /// private InfraredFrameReader infraredFrameReader = null; /// /// Bitmap to display /// private WriteableBitmap bitmap = null; /// /// Intermediate storage for receiving frame data from the sensor /// private ushort[] infraredFrameData = null; /// /// Intermediate storage for frame data converted to color /// private byte[] infraredPixels = null; /// /// Current status text to display /// private string statusText = null; /// /// Initializes a new instance of the MainPage class. /// public MainPage() { // get the kinectSensor object this.kinectSensor = KinectSensor.GetDefault(); // get the infraredFrameDescription from the InfraredFrameSource FrameDescription infraredFrameDescription = this.kinectSensor.InfraredFrameSource.FrameDescription; // open the reader for the infrared frames this.infraredFrameReader = this.kinectSensor.InfraredFrameSource.OpenReader(); // wire handler for frame arrival this.infraredFrameReader.FrameArrived += this.Reader_InfraredFrameArrived; // allocate space to put the pixels being received and converted this.infraredFrameData = new ushort[infraredFrameDescription.Width * infraredFrameDescription.Height]; this.infraredPixels = new byte[infraredFrameDescription.Width * infraredFrameDescription.Height * BytesPerPixel]; // create the bitmap to display this.bitmap = new WriteableBitmap(infraredFrameDescription.Width, infraredFrameDescription.Height); // set IsAvailableChanged event notifier this.kinectSensor.IsAvailableChanged += this.Sensor_IsAvailableChanged; // open the sensor this.kinectSensor.Open(); // set the status text this.StatusText = this.kinectSensor.IsAvailable ? resourceLoader.GetString("RunningStatusText") : resourceLoader.GetString("NoSensorStatusText"); // use the window object as the view model in this simple example this.DataContext = this; // initialize the components (controls) of the window this.InitializeComponent(); } /// /// INotifyPropertyChangedPropertyChanged event to allow window controls to bind to changeable data. /// public event PropertyChangedEventHandler PropertyChanged; /// /// Gets or sets the current status text to display /// public string StatusText { get { return this.statusText; } set { if (this.statusText != value) { this.statusText = value; // notify any bound elements that the text has changed if (this.PropertyChanged != null) { this.PropertyChanged(this, new PropertyChangedEventArgs("StatusText")); } } } } /// /// Execute shutdown tasks. /// /// object sending the event /// event arguments private void MainPage_Unloaded(object sender, RoutedEventArgs e) { if (this.infraredFrameReader != null) { // InfraredFrameReder is IDisposable this.infraredFrameReader.Dispose(); this.infraredFrameReader = null; } if (this.kinectSensor != null) { this.kinectSensor.Close(); this.kinectSensor = null; } } /// /// Handles the infrared frame data arriving from the sensor. /// /// object sending the event /// event arguments private void Reader_InfraredFrameArrived(object sender, InfraredFrameArrivedEventArgs e) { bool infraredFrameProcessed = false; // InfraredFrame is IDisposable using (InfraredFrame infraredFrame = e.FrameReference.AcquireFrame()) { if (infraredFrame != null) { FrameDescription infraredFrameDescription = infraredFrame.FrameDescription; // verify data and write the new infrared frame data to the display bitmap if (((infraredFrameDescription.Width * infraredFrameDescription.Height) == this.infraredFrameData.Length) && (infraredFrameDescription.Width == this.bitmap.PixelWidth) && (infraredFrameDescription.Height == this.bitmap.PixelHeight)) { // Copy the pixel data from the image to a temporary array infraredFrame.CopyFrameDataToArray(this.infraredFrameData); infraredFrameProcessed = true; } } } // we got a frame, convert and render if (infraredFrameProcessed) { this.ConvertInfraredData(); this.RenderInfraredPixels(this.infraredPixels); } } /// /// Convert infrared to RGB. /// private void ConvertInfraredData() { // Convert the infrared to RGB int colorPixelIndex = 0; for (int i = 0; i < this.infraredFrameData.Length; ++i) { // normalize the incoming infrared data (ushort) to a float ranging from // [InfraredOutputValueMinimum, InfraredOutputValueMaximum] by // 1. dividing the incoming value by the source maximum value float intensityRatio = (float)this.infraredFrameData[i] / InfraredSourceValueMaximum; // 2. dividing by the (average scene value * standard deviations) intensityRatio /= InfraredSceneValueAverage * InfraredSceneStandardDeviations; // 3. limiting the value to InfraredOutputValueMaximum intensityRatio = Math.Min(InfraredOutputValueMaximum, intensityRatio); // 4. limiting the lower value InfraredOutputValueMinimym intensityRatio = Math.Max(InfraredOutputValueMinimum, intensityRatio); // 5. converting the normalized value to a byte and using the result // as the RGB components required by the image byte intensity = (byte)(intensityRatio * 255.0f); this.infraredPixels[colorPixelIndex++] = intensity; this.infraredPixels[colorPixelIndex++] = intensity; this.infraredPixels[colorPixelIndex++] = intensity; this.infraredPixels[colorPixelIndex++] = 255; } } /// /// Renders color pixels into the writeableBitmap. /// /// pixel data private void RenderInfraredPixels(byte[] pixels) { pixels.CopyTo(this.bitmap.PixelBuffer); this.bitmap.Invalidate(); theImage.Source = this.bitmap; } /// /// Handles the event which the sensor becomes unavailable (E.g. paused, closed, unplugged). /// /// object sending the event /// event arguments private void Sensor_IsAvailableChanged(object sender, IsAvailableChangedEventArgs e) { // on failure, set the status text this.StatusText = this.kinectSensor.IsAvailable ? resourceLoader.GetString("RunningStatusText") : resourceLoader.GetString("SensorNotAvailableStatusText"); } } }