Advertisement
Guest User

Saving Kinect Depth PNG in 16-Bit format

a guest
Jul 12th, 2017
355
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 12.55 KB | None | 0 0
  1. //------------------------------------------------------------------------------
  2. // <copyright file="MainWindow.xaml.cs" company="Microsoft">
  3. //     Copyright (c) Microsoft Corporation.  All rights reserved.
  4. // </copyright>
  5. //------------------------------------------------------------------------------
  6.  
  7. namespace Microsoft.Samples.Kinect.DepthBasics
  8. {
  9.     using System;
  10.     using System.ComponentModel;
  11.     using System.Diagnostics;
  12.     using System.Globalization;
  13.     using System.IO;
  14.     using System.Windows;
  15.     using System.Windows.Media;
  16.     using System.Windows.Media.Imaging;
  17.     using Microsoft.Kinect;
  18.  
  19.     /// <summary>
  20.     /// Interaction logic for MainWindow
  21.     /// </summary>
  22.     public partial class MainWindow : Window, INotifyPropertyChanged
  23.     {
  24.         /// <summary>
  25.         /// Map depth range to byte range
  26.         /// </summary>
  27.         private const int MapDepthToByte = 8000 / 256;
  28.        
  29.         /// <summary>
  30.         /// Active Kinect sensor
  31.         /// </summary>
  32.         private KinectSensor kinectSensor = null;
  33.  
  34.         /// <summary>
  35.         /// Reader for depth frames
  36.         /// </summary>
  37.         private DepthFrameReader depthFrameReader = null;
  38.  
  39.         /// <summary>
  40.         /// Description of the data contained in the depth frame
  41.         /// </summary>
  42.         private FrameDescription depthFrameDescription = null;
  43.            
  44.         /// <summary>
  45.         /// Bitmap to display
  46.         /// </summary>
  47.         private WriteableBitmap depthBitmap = null;
  48.  
  49.         /// <summary>
  50.         /// Intermediate storage for frame data converted to color
  51.         /// </summary>
  52.         private byte[] depthPixels = null;
  53.  
  54.         /// <summary>
  55.         /// Current status text to display
  56.         /// </summary>
  57.         private string statusText = null;
  58.  
  59.  
  60.         private ushort[] raw = null;
  61.         private WriteableBitmap rawPNG = null;
  62.  
  63.         /// <summary>
  64.         /// Initializes a new instance of the MainWindow class.
  65.         /// </summary>
  66.         public MainWindow()
  67.         {
  68.             // get the kinectSensor object
  69.             this.kinectSensor = KinectSensor.GetDefault();
  70.  
  71.             // open the reader for the depth frames
  72.             this.depthFrameReader = this.kinectSensor.DepthFrameSource.OpenReader();
  73.  
  74.             // wire handler for frame arrival
  75.             this.depthFrameReader.FrameArrived += this.Reader_FrameArrived;
  76.  
  77.             // get FrameDescription from DepthFrameSource
  78.             this.depthFrameDescription = this.kinectSensor.DepthFrameSource.FrameDescription;
  79.  
  80.             // allocate space to put the pixels being received and converted
  81.             this.depthPixels = new byte[this.depthFrameDescription.Width * this.depthFrameDescription.Height];
  82.             this.raw = new ushort[this.depthFrameDescription.Width * this.depthFrameDescription.Height];
  83.  
  84.             // create the bitmap to display
  85.             this.depthBitmap = new WriteableBitmap(this.depthFrameDescription.Width, this.depthFrameDescription.Height, 96.0, 96.0, PixelFormats.Gray8, null);
  86.             this.rawPNG= new WriteableBitmap( this.depthFrameDescription.Width, this.depthFrameDescription.Height, 96.0, 96.0, PixelFormats.Gray16, null );
  87.  
  88.             // set IsAvailableChanged event notifier
  89.             this.kinectSensor.IsAvailableChanged += this.Sensor_IsAvailableChanged;
  90.  
  91.             // open the sensor
  92.             this.kinectSensor.Open();
  93.  
  94.             // set the status text
  95.             this.StatusText = this.kinectSensor.IsAvailable ? Properties.Resources.RunningStatusText
  96.                                                             : Properties.Resources.NoSensorStatusText;
  97.  
  98.             // use the window object as the view model in this simple example
  99.             this.DataContext = this;
  100.  
  101.             // initialize the components (controls) of the window
  102.             this.InitializeComponent();
  103.         }
  104.  
  105.         /// <summary>
  106.         /// INotifyPropertyChangedPropertyChanged event to allow window controls to bind to changeable data
  107.         /// </summary>
  108.         public event PropertyChangedEventHandler PropertyChanged;
  109.  
  110.         /// <summary>
  111.         /// Gets the bitmap to display
  112.         /// </summary>
  113.         public ImageSource ImageSource
  114.         {
  115.             get
  116.             {
  117.                 return this.depthBitmap;
  118.             }
  119.         }
  120.  
  121.         /// <summary>
  122.         /// Gets or sets the current status text to display
  123.         /// </summary>
  124.         public string StatusText
  125.         {
  126.             get
  127.             {
  128.                 return this.statusText;
  129.             }
  130.  
  131.             set
  132.             {
  133.                 if (this.statusText != value)
  134.                 {
  135.                     this.statusText = value;
  136.  
  137.                     // notify any bound elements that the text has changed
  138.                     if (this.PropertyChanged != null)
  139.                     {
  140.                         this.PropertyChanged(this, new PropertyChangedEventArgs("StatusText"));
  141.                     }
  142.                 }
  143.             }
  144.         }
  145.  
  146.         /// <summary>
  147.         /// Execute shutdown tasks
  148.         /// </summary>
  149.         /// <param name="sender">object sending the event</param>
  150.         /// <param name="e">event arguments</param>
  151.         private void MainWindow_Closing(object sender, CancelEventArgs e)
  152.         {
  153.             if (this.depthFrameReader != null)
  154.             {
  155.                 // DepthFrameReader is IDisposable
  156.                 this.depthFrameReader.Dispose();
  157.                 this.depthFrameReader = null;
  158.             }
  159.  
  160.             if (this.kinectSensor != null)
  161.             {
  162.                 this.kinectSensor.Close();
  163.                 this.kinectSensor = null;
  164.             }
  165.         }
  166.  
  167.         /// <summary>
  168.         /// Handles the user clicking on the screenshot button
  169.         /// </summary>
  170.         /// <param name="sender">object sending the event</param>
  171.         /// <param name="e">event arguments</param>
  172.         private void ScreenshotButton_Click(object sender, RoutedEventArgs e)
  173.         {
  174.             if (this.depthBitmap != null)
  175.             {
  176.                 // create a png bitmap encoder which knows how to save a .png file
  177.                 BitmapEncoder encoder = new PngBitmapEncoder();
  178.  
  179.                 // create frame from the writable bitmap and add to encoder
  180.                
  181.                 // TO SAVE THE DISPLAYED 8-BIT PNG
  182.                 //encoder.Frames.Add( BitmapFrame.Create( this.depthBitmap) );
  183.  
  184.                 // TO SAVE THE RAW 16-BIT PNG
  185.                 encoder.Frames.Add( BitmapFrame.Create( this.rawPNG ) );
  186.  
  187.                 string time = System.DateTime.UtcNow.ToString("hh'-'mm'-'ss", CultureInfo.CurrentUICulture.DateTimeFormat);
  188.  
  189.                 string myPhotos = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures);
  190.  
  191.                 string path = Path.Combine(myPhotos, "KinectScreenshot-Depth-" + time + ".png");
  192.  
  193.                 // write the new file to disk
  194.                 try
  195.                 {
  196.                     // FileStream is IDisposable
  197.                     using (FileStream fs = new FileStream(path, FileMode.Create))
  198.                     {
  199.                         encoder.Save(fs);
  200.                     }
  201.  
  202.                     this.StatusText = string.Format(CultureInfo.CurrentCulture, Properties.Resources.SavedScreenshotStatusTextFormat, path);
  203.                 }
  204.                 catch (IOException)
  205.                 {
  206.                     this.StatusText = string.Format(CultureInfo.CurrentCulture, Properties.Resources.FailedScreenshotStatusTextFormat, path);
  207.                 }
  208.             }
  209.         }
  210.  
  211.         /// <summary>
  212.         /// Handles the depth frame data arriving from the sensor
  213.         /// </summary>
  214.         /// <param name="sender">object sending the event</param>
  215.         /// <param name="e">event arguments</param>
  216.         private void Reader_FrameArrived(object sender, DepthFrameArrivedEventArgs e)
  217.         {
  218.             bool depthFrameProcessed = false;
  219.  
  220.             using (DepthFrame depthFrame = e.FrameReference.AcquireFrame())
  221.             {
  222.                 if (depthFrame != null)
  223.                 {
  224.                     // the fastest way to process the body index data is to directly access
  225.                     // the underlying buffer
  226.                     using (Microsoft.Kinect.KinectBuffer depthBuffer = depthFrame.LockImageBuffer())
  227.                     {
  228.                         // verify data and write the color data to the display bitmap
  229.                         if (((this.depthFrameDescription.Width * this.depthFrameDescription.Height) == (depthBuffer.Size / this.depthFrameDescription.BytesPerPixel)) &&
  230.                             (this.depthFrameDescription.Width == this.depthBitmap.PixelWidth) && (this.depthFrameDescription.Height == this.depthBitmap.PixelHeight))
  231.                         {
  232.                             // Note: In order to see the full range of depth (including the less reliable far field depth)
  233.                             // we are setting maxDepth to the extreme potential depth threshold
  234.                             ushort maxDepth = ushort.MaxValue;
  235.  
  236.                             // If you wish to filter by reliable depth distance, uncomment the following line:
  237.                             //// maxDepth = depthFrame.DepthMaxReliableDistance
  238.                            
  239.                             this.ProcessDepthFrameData(depthBuffer.UnderlyingBuffer, depthBuffer.Size, depthFrame.DepthMinReliableDistance, maxDepth);
  240.                             depthFrameProcessed = true;
  241.                         }
  242.                     }
  243.                 }
  244.             }
  245.  
  246.             if (depthFrameProcessed)
  247.             {
  248.                 this.RenderDepthPixels();
  249.             }
  250.         }
  251.  
  252.         /// <summary>
  253.         /// Directly accesses the underlying image buffer of the DepthFrame to
  254.         /// create a displayable bitmap.
  255.         /// This function requires the /unsafe compiler option as we make use of direct
  256.         /// access to the native memory pointed to by the depthFrameData pointer.
  257.         /// </summary>
  258.         /// <param name="depthFrameData">Pointer to the DepthFrame image data</param>
  259.         /// <param name="depthFrameDataSize">Size of the DepthFrame image data</param>
  260.         /// <param name="minDepth">The minimum reliable depth value for the frame</param>
  261.         /// <param name="maxDepth">The maximum reliable depth value for the frame</param>
  262.         private unsafe void ProcessDepthFrameData(IntPtr depthFrameData, uint depthFrameDataSize, ushort minDepth, ushort maxDepth)
  263.         {
  264.             // depth frame data is a 16 bit value
  265.             ushort* frameData = (ushort*)depthFrameData;
  266.  
  267.             // convert depth to a visual representation
  268.             for (int i = 0; i < (int)(depthFrameDataSize / this.depthFrameDescription.BytesPerPixel); ++i)
  269.             {
  270.                 // Get the depth for this pixel
  271.                 ushort depth = frameData[i];
  272.                
  273.                 // FOR DISPLAYING ON SCREEN:
  274.                 // To convert to a byte, we're mapping the depth value to the byte range.
  275.                 // Values outside the reliable depth range are mapped to 0 (black).
  276.                 this.depthPixels[i] = (byte)(depth >= minDepth && depth <= maxDepth ? (depth / MapDepthToByte) : 0);
  277.  
  278.                 // FOR SAVING RAW PNG 16-BIT DATA
  279.                 // Keeping the raw depth values after clipping them to the max depth. The data is converted to ushort which is 16bit data
  280.                 raw[i] = (ushort)(depth >= minDepth && depth <= maxDepth ? (depth) : 0);
  281.             }
  282.         }
  283.  
  284.         /// <summary>
  285.         /// Renders color pixels into the writeableBitmap.
  286.         /// </summary>
  287.         private void RenderDepthPixels()
  288.         {
  289.             // Rendering frame for displaying on the screen, in 8-Bit grayscale format
  290.             depthBitmap.WritePixels(
  291.                 new Int32Rect( 0, 0, depthBitmap.PixelWidth, depthBitmap.PixelHeight ),
  292.                 depthPixels,
  293.                 depthBitmap.PixelWidth,
  294.                 0 );
  295.  
  296.             // Rendering frame for saving as raw-16-bit PNG file
  297.             rawPNG.WritePixels(
  298.                 new Int32Rect( 0, 0, depthBitmap.PixelWidth, depthBitmap.PixelHeight ),
  299.                 raw,
  300.                 depthBitmap.PixelWidth * 2,
  301.                 0 );
  302.         }
  303.  
  304.         /// <summary>
  305.         /// Handles the event which the sensor becomes unavailable (E.g. paused, closed, unplugged).
  306.         /// </summary>
  307.         /// <param name="sender">object sending the event</param>
  308.         /// <param name="e">event arguments</param>
  309.         private void Sensor_IsAvailableChanged(object sender, IsAvailableChangedEventArgs e)
  310.         {
  311.             // on failure, set the status text
  312.             this.StatusText = this.kinectSensor.IsAvailable ? Properties.Resources.RunningStatusText
  313.                                                             : Properties.Resources.SensorNotAvailableStatusText;
  314.         }
  315.     }
  316. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement