I am basically trying to take input as RGB Stream from Kinect for Windows using the same code as given in the SDK (ColorBasics Example).
The code of the Example in SDK is as follow
public partial class MainWindow : Window
{
/// <summary>
/// Active Kinect sensor
/// </summary>
private KinectSensor sensor;
/// <summary>
/// Bitmap that will hold color information
/// </summary>
private WriteableBitmap colorBitmap;
/// <summary>
/// Intermediate storage for the color data received from the camera
/// </summary>
private byte[] colorPixels;
/// <summary>
/// Initializes a new instance of the MainWindow class.
/// </summary>
public MainWindow()
{
//InitializeComponent();
}
/// <summary>
/// Execute startup tasks
/// </summary>
/// <param name="sender">object sending the event</param>
/// <param name="e">event arguments</param>
private void WindowLoaded(object sender, RoutedEventArgs e)
{
// Look through all sensors and start the first connected one.
// This requires that a Kinect is connected at the time of app startup.
// To make your app robust against plug/unplug,
// it is recommended to use KinectSensorChooser provided in Microsoft.Kinect.Toolkit
foreach (var potentialSensor in KinectSensor.KinectSensors)
{
if (potentialSensor.Status == KinectStatus.Connected)
{
this.sensor = potentialSensor;
break;
}
}
if (null != this.sensor)
{
// Turn on the color stream to receive color frames
this.sensor.ColorStream.Enable(ColorImageFormat.RgbResolution640x480Fps30);
// Allocate space to put the pixels we'll receive
this.colorPixels = new byte[this.sensor.ColorStream.FramePixelDataLength];
// This is the bitmap we'll display on-screen
this.colorBitmap = new WriteableBitmap(this.sensor.ColorStream.FrameWidth, this.sensor.ColorStream.FrameHeight, 96.0, 96.0, PixelFormats.Bgr32, null);
// Set the image we display to point to the bitmap where we'll put the image data
this.Image.Source = this.colorBitmap;
// Add an event handler to be called whenever there is new color frame data
this.sensor.ColorFrameReady += this.SensorColorFrameReady;
// Start the sensor!
try
{
this.sensor.Start();
}
catch (IOException)
{
this.sensor = null;
}
}
if (null == this.sensor)
{
this.statusBarText.Text = Properties.Resources.NoKinectReady;
}
}
/// <summary>
/// Execute shutdown tasks
/// </summary>
/// <param name="sender">object sending the event</param>
/// <param name="e">event arguments</param>
private void WindowClosing(object sender, System.ComponentModel.CancelEventArgs e)
{
if (null != this.sensor)
{
this.sensor.Stop();
}
}
/// <summary>
/// Event handler for Kinect sensor's ColorFrameReady event
/// </summary>
/// <param name="sender">object sending the event</param>
/// <param name="e">event arguments</param>
private void SensorColorFrameReady(object sender, ColorImageFrameReadyEventArgs e)
{
using (ColorImageFrame colorFrame = e.OpenColorImageFrame())
{
if (colorFrame != null)
{
// Copy the pixel data from the image to a temporary array
colorFrame.CopyPixelDataTo(this.colorPixels);
// Write the pixel data into our bitmap
this.colorBitmap.WritePixels(
new Int32Rect(0, 0, this.colorBitmap.PixelWidth, this.colorBitmap.PixelHeight),
this.colorPixels,
this.colorBitmap.PixelWidth * sizeof(int),
0);
}
}
}
/// <summary>
/// Handles the user clicking on the screenshot button
/// </summary>
/// <param name="sender">object sending the event</param>
/// <param name="e">event arguments</param>
private void ButtonScreenshotClick(object sender, RoutedEventArgs e)
{
if (null == this.sensor)
{
this.statusBarText.Text = Properties.Resources.ConnectDeviceFirst;
return;
}
// create a png bitmap encoder which knows how to save a .png file
BitmapEncoder encoder = new PngBitmapEncoder();
// create frame from the writable bitmap and add to encoder
encoder.Frames.Add(BitmapFrame.Create(this.colorBitmap));
string time = System.DateTime.Now.ToString("hh'-'mm'-'ss", CultureInfo.CurrentUICulture.DateTimeFormat);
string myPhotos = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures);
string path = Path.Combine(myPhotos, "KinectSnapshot-" + time + ".png");
// write the new file to disk
try
{
using (FileStream fs = new FileStream(path, FileMode.Create))
{
encoder.Save(fs);
}
this.statusBarText.Text = string.Format("{0} {1}", Properties.Resources.ScreenshotWriteSuccess, path);
}
catch (IOException)
{
this.statusBarText.Text = string.Format("{0} {1}", Properties.Resources.ScreenshotWriteFailed, path);
}
}
}
}
And the code in my application is as follows
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Microsoft.Kinect;
namespace VideoKinect
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private KinectSensor sensor;
private WriteableBitmap colorBitmap;
private byte[] colorPixels;
public MainWindow()
{
//InitializeComponent();
}
private void WindowLoaded(object sender, RoutedEventArgs e)
{
foreach (var potentialSensor in KinectSensor.KinectSensors)
{
if (potentialSensor.Status == KinectStatus.Connected)
{
this.sensor = potentialSensor;
break;
}
}
if (null != this.sensor)
{
// Turn on the color stream to receive color frames
this.sensor.ColorStream.Enable(ColorImageFormat.RgbResolution640x480Fps30);
// Allocate space to put the pixels we'll receive
this.colorPixels = new byte[this.sensor.ColorStream.FramePixelDataLength];
// This is the bitmap we'll display on-screen
this.colorBitmap = new WriteableBitmap(this.sensor.ColorStream.FrameWidth, this.sensor.ColorStream.FrameHeight, 96.0, 96.0, PixelFormats.Bgr32, null);
// Set the image we display to point to the bitmap where we'll put the image data
this.ColorImage.Source = this.colorBitmap;
// Add an event handler to be called whenever there is new color frame data
this.sensor.ColorFrameReady += this.SensorColorFrameReady;
// Start the sensor!
//try
//{
this.sensor.Start();
// }
// catch (IOException)
//{
this.sensor = null;
// }
}
if (null == this.sensor)
{
MessageBox.Show("No Kinect Available");
}
}
private void SensorColorFrameReady(object sender, ColorImageFrameReadyEventArgs e)
{
using (ColorImageFrame colorFrame = e.OpenColorImageFrame())
{
if (colorFrame != null)
{
// Copy the pixel data from the image to a temporary array
colorFrame.CopyPixelDataTo(this.colorPixels);
// Write the pixel data into our bitmap
this.colorBitmap.WritePixels(
new Int32Rect(0, 0, this.colorBitmap.PixelWidth, this.colorBitmap.PixelHeight),
this.colorPixels,
this.colorBitmap.PixelWidth * sizeof(int),
0);
}
}
}
}
}
But I am getting the Error at "IOException" in my code and the Image "ColorImage" is not being detected even though I named the image in my xaml file as the same.
I was able to compile and execute your code above with the expected behaviour - the only change was uncommenting the InitializeComponent(); and adding <Image x:Name="ColorImage"/> to the MainWindow.xaml.
What exactly does the IOException say? Can you double check that you Kinect sensor is connected properly via at least a USB 2.0 bus - 1.1 has insufficient bandwidth? Are you also sure that the sensor has sufficient power as you will need the kinect power supply cable. You could always check to see if the SDK is installed properly, have a look at this post.
With regards to it not finding your ColorImage rebuilding the solution should resolve the issue, assuming there is not a spelling mistake on the x:Name of the Image in XAML.
You messed up when you commented out the try ... catch statement, you left sensor = null; in there, after you started it, meaning you had no sensor. You should have commented that out.
// What it should be:
// try
// {
this.sensor.Start();
// }
// catch (IOException)
// {
// this.sensor = null; <- What it should be
// }
// What it is:
// try
// {
this.sensor.Start();
// }
// catch (IOException)
// {
this.sensor = null; //turns sensor null, then SensorColorFrame
// never gets called, etc.
// }
That line will ultimately destroy any Kinect program you ever write. Hope this helps!
you left out a reference. you need to include:
using System.IO;
at the very top. Hope This helps!
Related
This question already has answers here:
Windows shell extension with C#
(3 answers)
Closed 6 years ago.
I have windows 10.
It take some time until i see a messageBox say it was registered not sure why it's taking time. And after it nothing happen i don't see the context menus when doing mouse right click in the file explorer. One of the menus is Resize and when i click on it select it, it should run a method in the program that will resize all the images in the current directory i'm in the file explorer.
But even if it was working on windows 8 almost a year ago the resize action was taking lot of time, not sure why.
This is the code of Program.cs:
using System;
using System.Windows.Forms;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
[assembly: CLSCompliant(true)]
namespace SimpleContextMenu
{
static class Program
{
// file type to register
const string FileType = "bitmapfile";//"jpegfile";
// context menu name in the registry
const string KeyName = "Simple Context Menu";
const string KeyName1 = "Simple Context Menu1";
// context menu text
const string MenuText = "Copy to Grayscale";
const string MenuText1 = "Resize all images";
[STAThread]
static void Main(string[] args)
{
System.Threading.Thread.Sleep(30000);
// process register or unregister commands
if (!ProcessCommand(args))
{
string action = args[0];
MessageBox.Show(action);
string fileName = args[1];
if (action == "Copy")
{
// invoked from shell, process the selected file
CopyGrayscaleImage(fileName);
}
else if (action == "Resize")
{
string FilePath = Path.Combine(
Path.GetDirectoryName(fileName),
string.Format("{0} (resized){1}",
Path.GetFileNameWithoutExtension(fileName),
Path.GetExtension(fileName)));
MessageBox.Show(FilePath);
Bitmap bmp1 = new Bitmap(ResizeImages(FilePath, 100, 100));
bmp1.Save(FilePath);
bmp1.Dispose();
}
}
}
/// <summary>
/// Process command line actions (register or unregister).
/// </summary>
/// <param name="args">Command line arguments.</param>
/// <returns>True if processed an action in the command line.</returns>
static bool ProcessCommand(string[] args)
{
// register
if (args.Length == 0 || string.Compare(args[0], "-register", true) == 0)
{
// full path to self, %L is placeholder for selected file
string menuCommand = string.Format("\"{0}\" Copy \"%L\"", Application.ExecutablePath);
// register the context menu
FileShellExtension.Register(Program.FileType,
Program.KeyName, Program.MenuText,
menuCommand);
string menuCommand1 = string.Format("\"{0}\" Resize \"%L\"", Application.ExecutablePath);
FileShellExtension.Register(Program.FileType,
Program.KeyName1, Program.MenuText1,
menuCommand1);
MessageBox.Show(string.Format(
"The {0} shell extension was registered.",
Program.KeyName), Program.KeyName);
return true;
}
// unregister
if (string.Compare(args[0], "-unregister", true) == 0)
{
// unregister the context menu
FileShellExtension.Unregister(Program.FileType, Program.KeyName);
MessageBox.Show(string.Format(
"The {0} shell extension was unregistered.",
Program.KeyName), Program.KeyName);
return true;
}
// command line did not contain an action
return false;
}
/// <summary>
/// Make a grayscale copy of the image.
/// </summary>
/// <param name="filePath">Full path to the image to copy.</param>
static void CopyGrayscaleImage(string filePath)
{
try
{
// full path to the grayscale copy
string grayFilePath = Path.Combine(
Path.GetDirectoryName(filePath),
string.Format("{0} (grayscale){1}",
Path.GetFileNameWithoutExtension(filePath),
Path.GetExtension(filePath)));
// using calls Dispose on the objects, important
// so the file is not locked when the app terminates
using (Image image = new Bitmap(filePath))
using (Bitmap grayImage = new Bitmap(image.Width, image.Height))
using (Graphics g = Graphics.FromImage(grayImage))
{
// setup grayscale matrix
ImageAttributes attr = new ImageAttributes();
attr.SetColorMatrix(new ColorMatrix(new float[][]{
new float[]{0.3086F,0.3086F,0.3086F,0,0},
new float[]{0.6094F,0.6094F,0.6094F,0,0},
new float[]{0.082F,0.082F,0.082F,0,0},
new float[]{0,0,0,1,0,0},
new float[]{0,0,0,0,1,0},
new float[]{0,0,0,0,0,1}}));
// create the grayscale image
g.DrawImage(image, new Rectangle(0, 0, image.Width, image.Height),
0, 0, image.Width, image.Height, GraphicsUnit.Pixel, attr);
// save to the file system
grayImage.Save(grayFilePath, ImageFormat.Jpeg);
// success
MessageBox.Show(string.Format("Copied grayscale image {0}", grayFilePath), Program.KeyName);
}
}
catch (Exception ex)
{
MessageBox.Show(string.Format("An error occurred: {0}", ex.Message), Program.KeyName);
return;
}
}
private static Bitmap ResizeImages(String filename, int maxWidth, int maxHeight)
{
using (Image originalImage = Image.FromFile(filename))
{
//Caluate new Size
int newWidth = originalImage.Width;
int newHeight = originalImage.Height;
double aspectRatio = (double)originalImage.Width / (double)originalImage.Height;
if (aspectRatio <= 1 && originalImage.Width > maxWidth)
{
newWidth = maxWidth;
newHeight = (int)Math.Round(newWidth / aspectRatio);
}
else if (aspectRatio > 1 && originalImage.Height > maxHeight)
{
newHeight = maxHeight;
newWidth = (int)Math.Round(newHeight * aspectRatio);
}
if (newWidth >= 0 && newHeight >= 0)
{
Bitmap newImage = new Bitmap(newWidth, newHeight);
using (Graphics g = Graphics.FromImage(newImage))
{
//--Quality Settings Adjust to fit your application
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBilinear;
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
g.DrawImage(originalImage, 0, 0, newImage.Width, newImage.Height);
return newImage;
}
}
return null;
}
}
}
}
And this is the FileShellExtension.cs:
using System;
using System.Diagnostics;
using Microsoft.Win32;
namespace SimpleContextMenu
{
/// <summary>
/// Register and unregister simple shell context menus.
/// </summary>
static class FileShellExtension
{
/// <summary>
/// Register a simple shell context menu.
/// </summary>
/// <param name="fileType">The file type to register.</param>
/// <param name="shellKeyName">Name that appears in the registry.</param>
/// <param name="menuText">Text that appears in the context menu.</param>
/// <param name="menuCommand">Command line that is executed.</param>
public static void Register(
string fileType, string shellKeyName,
string menuText, string menuCommand)
{
Debug.Assert(!string.IsNullOrEmpty(fileType) &&
!string.IsNullOrEmpty(shellKeyName) &&
!string.IsNullOrEmpty(menuText) &&
!string.IsNullOrEmpty(menuCommand));
// create full path to registry location
string regPath = string.Format(#"{0}\shell\{1}", fileType, shellKeyName);
// add context menu to the registry
using (RegistryKey key = Registry.ClassesRoot.CreateSubKey(regPath))
{
key.SetValue(null, menuText);
}
// add command that is invoked to the registry
using (RegistryKey key = Registry.ClassesRoot.CreateSubKey(
string.Format(#"{0}\command", regPath)))
{
key.SetValue(null, menuCommand);
}
}
/// <summary>
/// Unregister a simple shell context menu.
/// </summary>
/// <param name="fileType">The file type to unregister.</param>
/// <param name="shellKeyName">Name that was registered in the registry.</param>
public static void Unregister(string fileType, string shellKeyName)
{
Debug.Assert(!string.IsNullOrEmpty(fileType) &&
!string.IsNullOrEmpty(shellKeyName));
// full path to the registry location
string regPath = string.Format(#"{0}\shell\{1}", fileType, shellKeyName);
// remove context menu from the registry
Registry.ClassesRoot.DeleteSubKeyTree(regPath);
}
}
}
I know that the siggestion is not to use/touch the registry but i'm trying to find a easy way to resize images in a directory something easy interface to the user and the only way to add a context menu to the file explorer already existing menu is by register to the registry.
I'm trying to use sharpshel library now but not sure how to work with it:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using SharpShell.Attributes;
using SharpShellContextMenu;
using SharpShell.SharpContextMenu;
namespace SharpShellContextMenu
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
[ComVisible(true)]
[COMServerAssociation(AssociationType.ClassOfExtension, ".jpg")]
public class CountLinesExtension : SharpContextMenu
{
protected override bool CanShowMenu()
{
return true;
}
protected override ContextMenuStrip CreateMenu()
{
// Create the menu strip.
var menu = new ContextMenuStrip();
// Create a 'count lines' item.
var itemCountLines = new ToolStripMenuItem
{
Text = "Resize Images",
//Image = Properties.Resources.CountLines
};
// When we click, we'll count the lines.
itemCountLines.Click += (sender, args) => CountLines();
// Add the item to the context menu.
menu.Items.Add(itemCountLines);
// Return the menu.
return menu;
}
private void CountLines()
{
// do the work
}
}
}
}
How do i call it use it in the constructor ?
Writing extensions for the Windows Explorer is cumbersome and errorprone work. The project SharpShell makes it easy for .net developers to integrate into the Explorer. Have a look at https://github.com/dwmkerr/sharpshell.
Enabling an action for the context menu is done in this manner (extract from the tutorial):
[ComVisible(true)]
[COMServerAssociation(AssociationType.ClassOfExtension, ".txt")]
public class CountLinesExtension : SharpContextMenu
{
protected override bool CanShowMenu()
{
return true;
}
protected override ContextMenuStrip CreateMenu()
{
// Create the menu strip.
var menu = new ContextMenuStrip();
// Create a 'count lines' item.
var itemCountLines = new ToolStripMenuItem
{
Text = "Count Lines...",
Image = Properties.Resources.CountLines
};
// When we click, we'll count the lines.
itemCountLines.Click += (sender, args) => CountLines();
// Add the item to the context menu.
menu.Items.Add(itemCountLines);
// Return the menu.
return menu;
}
private void CountLines()
{
// do the work
}
}
I am trying to write an application that saves depth and color streams of Kinect for Windows v2 as image files (like png or jpg). So, I used Kinect SDK v2 examples(since I have no prior experience with C# or Kinect API). I modified the ColorBasics-WPF sample code to achieve my goal. Here is the code that only convert color stream to png files(the only part I modified is Reader_ColorFrameArrived function):
//------------------------------------------------------------------------------
// <copyright file="MainWindow.xaml.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
namespace Microsoft.Samples.Kinect.ColorBasics
{
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Microsoft.Kinect;
using System.Collections.Generic;
/// <summary>
/// Interaction logic for MainWindow
/// </summary>
public partial class MainWindow : Window, INotifyPropertyChanged
{
static int count = 0;
/// <summary>
/// Active Kinect sensor
/// </summary>
private KinectSensor kinectSensor = null;
/// <summary>
/// Reader for color frames
/// </summary>
private ColorFrameReader colorFrameReader = null;
/// <summary>
/// Bitmap to display
/// </summary>
private WriteableBitmap colorBitmap = null;
/// <summary>
/// Current status text to display
/// </summary>
private string statusText = null;
/// <summary>
/// Initializes a new instance of the MainWindow class.
/// </summary>
public MainWindow()
{
// get the kinectSensor object
this.kinectSensor = KinectSensor.GetDefault();
// open the reader for the color frames
this.colorFrameReader = this.kinectSensor.ColorFrameSource.OpenReader();
// wire handler for frame arrival
this.colorFrameReader.FrameArrived += this.Reader_ColorFrameArrived;
// create the colorFrameDescription from the ColorFrameSource using Bgra format
FrameDescription colorFrameDescription = this.kinectSensor.ColorFrameSource.CreateFrameDescription(ColorImageFormat.Bgra);
// create the bitmap to display
this.colorBitmap = new WriteableBitmap(colorFrameDescription.Width, colorFrameDescription.Height, 96.0, 96.0, PixelFormats.Bgr32, null);
// 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 ? Properties.Resources.RunningStatusText
: Properties.Resources.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();
}
/// <summary>
/// INotifyPropertyChangedPropertyChanged event to allow window controls to bind to changeable data
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Gets the bitmap to display
/// </summary>
public ImageSource ImageSource
{
get
{
return this.colorBitmap;
}
}
/// <summary>
/// Gets or sets the current status text to display
/// </summary>
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"));
}
}
}
}
/// <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.colorFrameReader != null)
{
// ColorFrameReder is IDisposable
this.colorFrameReader.Dispose();
this.colorFrameReader = null;
}
if (this.kinectSensor != null)
{
this.kinectSensor.Close();
this.kinectSensor = null;
}
}
/// <summary>
/// Handles the user clicking on the screenshot button
/// </summary>
/// <param name="sender">object sending the event</param>
/// <param name="e">event arguments</param>
private void ScreenshotButton_Click(object sender, RoutedEventArgs e)
{
if (this.colorBitmap != null)
{
// create a png bitmap encoder which knows how to save a .png file
BitmapEncoder encoder = new PngBitmapEncoder();
// create frame from the writable bitmap and add to encoder
encoder.Frames.Add(BitmapFrame.Create(this.colorBitmap));
//bitmaps.Add(BitmapFrame.Create(this.colorBitmap.Clone()));
string time = count.ToString();
//string time = System.DateTime.Now.ToString("hh'-'mm'-'ss", CultureInfo.CurrentUICulture.DateTimeFormat);
string myPhotos = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures);
string path = Path.Combine(myPhotos, "KinectScreenshot-Color-" + time + ".png");
count++;
// write the new file to disk
try
{
// FileStream is IDisposable
using (FileStream fs = new FileStream(path, FileMode.Create))
{
encoder.Save(fs);
}
this.StatusText = string.Format(Properties.Resources.SavedScreenshotStatusTextFormat, path);
}
catch (IOException)
{
this.StatusText = string.Format(Properties.Resources.FailedScreenshotStatusTextFormat, path);
}
}
}
/// <summary>
/// Handles the color frame data arriving from the sensor
/// </summary>
/// <param name="sender">object sending the event</param>
/// <param name="e">event arguments</param>
private void Reader_ColorFrameArrived(object sender, ColorFrameArrivedEventArgs e)
{
// ColorFrame is IDisposable
using (ColorFrame colorFrame = e.FrameReference.AcquireFrame())
{
if (colorFrame != null)
{
FrameDescription colorFrameDescription = colorFrame.FrameDescription;
using (KinectBuffer colorBuffer = colorFrame.LockRawImageBuffer())
{
this.colorBitmap.Lock();
// verify data and write the new color frame data to the display bitmap
if ((colorFrameDescription.Width == this.colorBitmap.PixelWidth) && (colorFrameDescription.Height == this.colorBitmap.PixelHeight))
{
colorFrame.CopyConvertedFrameDataToIntPtr(
this.colorBitmap.BackBuffer,
(uint)(colorFrameDescription.Width * colorFrameDescription.Height * 4),
ColorImageFormat.Bgra);
this.colorBitmap.AddDirtyRect(new Int32Rect(0, 0, this.colorBitmap.PixelWidth, this.colorBitmap.PixelHeight));
}
this.colorBitmap.Unlock();
}
}
// my modification : save current frame as png file.
if (this.colorBitmap != null)
{
// create a png bitmap encoder which knows how to save a .png file
BitmapEncoder encoder = new PngBitmapEncoder();
// create frame from the writable bitmap and add to encoder
encoder.Frames.Add(BitmapFrame.Create(this.colorBitmap));
string time = count.ToString();
//string time = System.DateTime.Now.ToString("hh'-'mm'-'ss", CultureInfo.CurrentUICulture.DateTimeFormat);
string myPhotos = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures);
string path = Path.Combine(myPhotos, "KinectScreenshot-Color-" + time + ".png");
count++;
// write the new file to disk
try
{
// FileStream is IDisposable
using (FileStream fs = new FileStream(path, FileMode.Create))
{
encoder.Save(fs);
}
this.StatusText = string.Format(Properties.Resources.SavedScreenshotStatusTextFormat, path);
}
catch (IOException)
{
this.StatusText = string.Format(Properties.Resources.FailedScreenshotStatusTextFormat, path);
}
}
}
}
/// <summary>
/// Handles the event which the sensor becomes unavailable (E.g. paused, closed, unplugged).
/// </summary>
/// <param name="sender">object sending the event</param>
/// <param name="e">event arguments</param>
private void Sensor_IsAvailableChanged(object sender, IsAvailableChangedEventArgs e)
{
// on failure, set the status text
this.StatusText = this.kinectSensor.IsAvailable ? Properties.Resources.RunningStatusText
: Properties.Resources.SensorNotAvailableStatusText;
}
}
}
The problem with this code is that it generates say 200 png files during the recording time but starting from say frame 90 to the end, all the frames are identical to each other(It starts recording as soon as I run it and it stops whenever I close it).
1) Would you help me understand why this happens? Why it doesn't record the rest of the frames and repeats a frame again and again?
2) Do you have any advice or pointer about how I can record depth and color streams as image files simultaneously in an efficient way and with a good frame rate(say 20-30 fps) using Kinect for Windows v2?
Writing simultaneously color and depth streams with the same fps without loosing frames is a little tricky. In order to check your problem with the identical frames I suggest you try to write color and depth images to separate buffers (you can keep its frame timestamp in a buffer too) and write them to the disk after the recording is over. I'll give you an example and you can ajust it to your problem.
Saving color frames to a buffer:
private void myKinectSensor_ColorFrameReady(object sender, ColorImageFrameReadyEventArgs e)
{
using (ColorImageFrame color = e.OpenColorImageFrame())
{
if (color != null)
{
colorbits = new byte[color.PixelDataLength];
color.CopyPixelDataTo(colorbits);
image1.Source = BitmapSource.Create(color.Width, color.Height, 96, 96, PixelFormats.Bgr32, null, colorbits, color.Width * color.BytesPerPixel);
if (StartSavingFrames)
{
SaveColorTimestamps.AddLast(DateTime.Now.ToString("hhmmssfff"));
SaveColorFrames.AddLast(colorbits);
}
}
}
}
Saving depth frames to another buffer:
private void myKinectSensor_DepthFrameReady(object sender, DepthImageFrameReadyEventArgs e)
{
using (DepthImageFrame depth = e.OpenDepthImageFrame())
{
depthPixels = new DepthImagePixel[myKinectSensor.DepthStream.FramePixelDataLength];
if (depth != null)
{
frame = new short[depth.PixelDataLength];
depth.CopyPixelDataTo(frame);
for (int i = 0; i < frame.Length; i++)
{
frame[i] = (short)(((ushort)frame[i]) >> 3);
}
image3.Source = BitmapSource.Create(depth.Width, depth.Height, 96, 96, PixelFormats.Gray16, null, frame, depth.Width * depth.BytesPerPixel);
if (StartSavingFrames)
{
SaveDepthTimestamps.AddLast(DateTime.Now.ToString("hhmmssfff"));
SaveDepthFrames.AddLast(frame);
}
}
}
}
Finally, loop in each buffer and write frames to disk:
e = SaveColorTimestamps.GetEnumerator();
foreach (byte[] node in SaveColorFrames)
{
e.MoveNext();
PngBitmapEncoder enc = new PngBitmapEncoder();
enc.Frames.Add(BitmapFrame.Create(BitmapSource.Create(640, 480, 96, 96, PixelFormats.Bgr32, null, node, 640*4)));
string temppath = System.IO.Path.Combine(#"../output/kinect1/color/", e.Current + ".png");
using (FileStream fs = new FileStream(temppath, FileMode.Create))
{
enc.Save(fs);
fs.Close();
}
}
SaveColorTimestamps.Clear();
SaveColorFrames.Clear();
e.Dispose();
e = SaveDepthTimestamps.GetEnumerator();
foreach (short[] node in SaveDepthFrames)
{
e.MoveNext();
PngBitmapEncoder enc = new PngBitmapEncoder();
enc.Frames.Add(BitmapFrame.Create(BitmapSource.Create(640, 480, 96, 96, PixelFormats.Gray16, null, node, 640 * 2)));
string temppath = System.IO.Path.Combine(#"../output/kinect1/depth/", e.Current + ".png");
using (FileStream fs = new FileStream(temppath, FileMode.Create))
{
enc.Save(fs);
fs.Close();
}
}
It is not the optimal way to do it, but it will help you understand how the frame-writing works and you will not have any drop-frames.
Currently I can record the microphone input using the Naudio .dll with this:
public void recordInput()
{
Console.WriteLine("Now recording...");
waveSource = new WaveIn();
waveSource.WaveFormat = new WaveFormat(16000, 1);
waveSource.DataAvailable += new EventHandler<WaveInEventArgs>(waveSource_DataAvailable);
waveSource.RecordingStopped += new EventHandler<StoppedEventArgs>(waveSource_RecordingStopped);
//string tempFile = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString() + ".wav");
string tempFile = Path.Combine(#"C:\Users\Nick\Desktop", "test.wav");
waveFile = new WaveFileWriter(tempFile, waveSource.WaveFormat);
waveSource.StartRecording();
myTimer.Interval = 5000;
myTimer.Tick += new EventHandler(myTimer_Tick);
myTimer.Start();
}
Currently I used an eventhandler to wait for 5 seconds and then onTick I stop recording. However this is causing problems because I am having trouble waiting for the recording task to finish in the main part of the code where I call:
public string voiceToText()
{
recordInput();
//Somehow wait here until record is done then proceed.
convertToFlac();
return "done";
}
I've tried putting the Naudio in a thread and using a waitOne(); but waveSource.StartRecording(); is put in an eventhandler and thread an expection. I've also tried using a Thread.Sleep(5000) and stop recording after that thread finishes but the audio only records the first 500mS of audio for some reason.
I'm pretty new to c# and don't fully understand threading so any help or seperate approach is welcome.
I know this is an old topic, but here is some code that I made for one of my projects and that records audio for x seconds (it uses NAudio.Lame to convert the wave file to mp3):
public class Recorder
{
/// <summary>
/// Timer used to start/stop recording
/// </summary>
private Timer _timer;
private WaveInEvent _waveSource;
private WaveFileWriter _waveWriter;
private string _filename;
private string _tempFilename;
public event EventHandler RecordingFinished;
/// <summary>
/// Record from the mic
/// </summary>
/// <param name="seconds">Duration in seconds</param>
/// <param name="filename">Output file name</param>
public void RecordAudio(int seconds, string filename)
{
/*if the filename is empty, throw an exception*/
if (string.IsNullOrEmpty(filename))
throw new ArgumentNullException("The file name cannot be empty.");
/*if the recording duration is not > 0, throw an exception*/
if (seconds <= 0)
throw new ArgumentNullException("The recording duration must be a positive integer.");
_filename = filename;
_tempFilename = $"{Path.GetFileNameWithoutExtension(filename)}.wav";
_waveSource = new WaveInEvent
{
WaveFormat = new WaveFormat(44100, 1)
};
_waveSource.DataAvailable += DataAvailable;
_waveSource.RecordingStopped += RecordingStopped;
_waveWriter = new WaveFileWriter(_tempFilename, _waveSource.WaveFormat);
/*Start the timer that will mark the recording end*/
/*We multiply by 1000 because the Timer object works with milliseconds*/
_timer = new Timer(seconds * 1000);
/*if the timer elapses don't reset it, stop it instead*/
_timer.AutoReset = false;
/*Callback that will be executed once the recording duration has elapsed*/
_timer.Elapsed += StopRecording;
/*Start recording the audio*/
_waveSource.StartRecording();
/*Start the timer*/
_timer.Start();
}
/// <summary>
/// Callback that will be executed once the recording duration has elapsed
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void StopRecording(object sender, ElapsedEventArgs e)
{
/*Stop the timer*/
_timer?.Stop();
/*Destroy/Dispose of the timer to free memory*/
_timer?.Dispose();
/*Stop the audio recording*/
_waveSource.StopRecording();
}
/// <summary>
/// Callback executed when the recording is stopped
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void RecordingStopped(object sender, StoppedEventArgs e)
{
_waveSource.DataAvailable -= DataAvailable;
_waveSource.RecordingStopped -= RecordingStopped;
_waveSource?.Dispose();
_waveWriter?.Dispose();
/*Convert the recorded file to MP3*/
ConvertWaveToMp3(_tempFilename, _filename);
/*Send notification that the recording is complete*/
RecordingFinished?.Invoke(this, null);
}
/// <summary>
/// Callback executed when new data is available
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void DataAvailable(object sender, WaveInEventArgs e)
{
if (_waveWriter != null)
{
_waveWriter.Write(e.Buffer, 0, e.BytesRecorded);
_waveWriter.Flush();
}
}
/// <summary>
/// Converts the recorded WAV file to MP3
/// </summary>
private void ConvertWaveToMp3(string source, string destination)
{
using (var waveStream = new WaveFileReader(source))
using(var fileWriter = new LameMP3FileWriter(destination, waveStream.WaveFormat, 128))
{
waveStream.CopyTo(fileWriter);
waveStream.Flush();
}
/*Delete the temporary WAV file*/
File.Delete(source);
}
}
And here is how to use it in your code:
var rec = new Recorder();
/*This line allows us to be notified when the recording is complete and the callback 'OnRecordingFinished' will be executed*/
rec.RecordingFinished += OnRecordingFinished;
rec.RecordAudio(seconds, recPath);
private void OnRecordingFinished(object sender, RecordingContentArgs e)
{
//Put your code here to process the audio file
}
If you aren't running in a Windows Forms or WPF application, then you should use WaveInEvent instead, so that a background thread is set up to handle the callbacks. The default constructor for WaveIn uses Windows messages.
I am able to capture system audio which is generated by speaker with the help of WasapiLoopbackCapture (naudio). but the problem is it capture wav file and size of wav file very large (almost 10 to 15 MB/Min). I have to capture 2-3 hour audio and this will be too high.
I m looking for a solution that will convert wav stream which is capture by WasapiLoopbackCapture convert to MP3 and then save this to disk. I try a loat with LAME.exe or other solution but not get success. Any working code.
Here is My Code:
private void button1_Click(object sender, EventArgs e){
LoopbackRecorder obj = new LoopbackRecorder();
string a = textBox1.Text;
obj.StartRecording(#"e:\aman.mp3");
}
public class LoopbackRecorder
{
private IWaveIn _waveIn;
private Mp3WaveFormat _mp3format;
private WaveFormat _wavFormat;
private WaveFileWriter _writer;
private bool _isRecording = false;
/// <summary>
/// Constructor
/// </summary>
public LoopbackRecorder()
{
}
/// <summary>
/// Starts the recording.
/// </summary>
/// <param name="fileName"></param>
public void StartRecording(string fileName)
{
// If we are currently record then go ahead and exit out.
if (_isRecording == true)
{
return;
}
_fileName = fileName;
_waveIn = new WasapiLoopbackCapture();
// _waveIn.WaveFormat = new WaveFormat(16000, 16 , 2);
_writer = new WaveFileWriter(fileName, _waveIn.WaveFormat);
_waveIn.DataAvailable += OnDataAvailable;
// _waveIn.RecordingStopped += OnRecordingStopped;
_waveIn.StartRecording();
_isRecording = true;
}
private void OnDataAvailable(object sender, WaveInEventArgs waveInEventArgs)
{
if (_writer == null)
{
_writer = new WaveFileWriter(#"e:\aman.mp3", _waveIn.WaveFormat);
}
_writer.Write(waveInEventArgs.Buffer, 0, waveInEventArgs.BytesRecorded);
byte[] by= Float32toInt16(waveInEventArgs.Buffer, 0, waveInEventArgs.BytesRecorded);
}
private string _fileName = "";
/// <summary>
/// The name of the file that was set when StartRecording was called. E.g. the current file being written to.
/// </summary>
public string FileName
{
get
{
return _fileName;
}
}
}
Here's an example of using NAudio.Lame (in a console application) to capture data from sound card loopback and write direct to an MP3 file:
using System;
using NAudio.Lame;
using NAudio.Wave;
namespace MP3Rec
{
class Program
{
static LameMP3FileWriter wri;
static bool stopped = false;
static void Main(string[] args)
{
// Start recording from loopback
IWaveIn waveIn = new WasapiLoopbackCapture();
waveIn.DataAvailable += waveIn_DataAvailable;
waveIn.RecordingStopped += waveIn_RecordingStopped;
// Setup MP3 writer to output at 32kbit/sec (~2 minutes per MB)
wri = new LameMP3FileWriter( #"C:\temp\test_output.mp3", waveIn.WaveFormat, 32 );
waveIn.StartRecording();
stopped = false;
// Keep recording until Escape key pressed
while (!stopped)
{
if (Console.KeyAvailable)
{
var key = Console.ReadKey(true);
if (key != null && key.Key == ConsoleKey.Escape)
waveIn.StopRecording();
}
else
System.Threading.Thread.Sleep(50);
}
// flush output to finish MP3 file correctly
wri.Flush();
// Dispose of objects
waveIn.Dispose();
wri.Dispose();
}
static void waveIn_RecordingStopped(object sender, StoppedEventArgs e)
{
// signal that recording has finished
stopped = true;
}
static void waveIn_DataAvailable(object sender, WaveInEventArgs e)
{
// write recorded data to MP3 writer
if (wri != null)
wri.Write(e.Buffer, 0, e.BytesRecorded);
}
}
}
At the moment the NAudio.Lame package on NuGet is compiled for x86 only, so make sure your application is set to target that platform.
To convert the audio to MP3 on the fly, one of the easiest ways is to use the command line options that let you pass audio into LAME.exe via stdin. I describe how to do that in this article.
You may also be interested that Corey Murtagh has created a LAME package for NAudio. I haven't tried it myself, but it looks like it should also do the job. Documentation is here.
This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 8 years ago.
The problem is: I get a NullReferenceException but I can't seem to find out why. (I just start with C#) and have read the C# for kids from Microsoft. It explained a lot but this I do not understand.
But I don't get the piece of code which throw the exception to my head.
Code is:
using System;
using System.Collections.Generic;
using System.Linq; using System.Text;
using System.IO.Ports;
using PlugwiseLib.BLL.BC;
using plugwiseLib.BLL.BPC;
using System.Text.RegularExpressions;
using System.Threading;
using System.IO;
using PlugwiseLib.BLL.BPC;
using PlugwiseLib.UTIL;
namespace PlugwiseLib {
public class plugwiseControl
{
private SerialPort port;
private PlugwiseActions currentAction;
public delegate void PlugwiseDataReceivedEvent(object
sender, System.EventArgs e,
List<PlugwiseMessage> data);
public event PlugwiseDataReceivedEvent
DataReceived;
private PlugwiseReader reader;
/// <summary>
/// Constructor for the Plugwise Control class
/// </summary>
/// <param name="serialPort">The serial port name that the plugwise stick takes</param>
public plugwiseControl(string serialPort)
{
try
{
port = new SerialPort(serialPort);
port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived);
port.BaudRate = 115200;
currentAction = PlugwiseActions.None;
reader = new PlugwiseReader();
}
catch (Exception e)
{
throw new Exception("Could not connect to plug.");
}
}
/// <summary>
/// This is the method that sends a command to the plugwise plugs.
/// </summary>
/// <param name="mac">The mac adress of the plug that needs to perform the action</param>
/// <param name="action">The action that has to be performed</param>
public void Action(string mac,PlugwiseActions action)
{
try
{
string message = "";
switch (action)
{
case PlugwiseActions.On:
currentAction = PlugwiseActions.On;
message = PlugwiseSerialPortFactory.Create(PlugwiseSerialPortRequest.on,mac);
break;
case PlugwiseActions.Off:
currentAction = PlugwiseActions.Off;
message = PlugwiseSerialPortFactory.Create(PlugwiseSerialPortRequest.off, mac);
break;
case PlugwiseActions.Status:
currentAction = PlugwiseActions.Status;
message = PlugwiseSerialPortFactory.Create(PlugwiseSerialPortRequest.status, mac);
break;
case PlugwiseActions.Calibration:
currentAction = PlugwiseActions.Calibration;
message = PlugwiseSerialPortFactory.Create(PlugwiseSerialPortRequest.calibration, mac);
break;
case PlugwiseActions.powerinfo:
currentAction = PlugwiseActions.powerinfo;
message = PlugwiseSerialPortFactory.Create(PlugwiseSerialPortRequest.powerinfo,mac);
break;
case PlugwiseActions.history:
message = "";
break;
}
if (message.Length > 0)
{
port.WriteLine(message);
Thread.Sleep(10);
}
}
catch (Exception e)
{
throw e;
}
}
/// <summary>
/// This is the method that sends a command to the plugwise plugs that retrieves the history power information
/// </summary>
/// <param name="mac">The mac adress of the plug that needs to perform the action</param>
/// <param name="logId">The id of the history message that has to be retrieved</param>
/// <param name="action">The action that has to be performed this MUST be history</param>
public void Action(string mac,int logId,PlugwiseActions action)
{
string message = "";
switch(action)
{
case PlugwiseActions.history:
currentAction = PlugwiseActions.history;
message = PlugwiseSerialPortFactory.Create(PlugwiseSerialPortRequest.history, MessageHelper.ConvertIntToPlugwiseLogHex(logId), mac);
break;
}
if (message.Length > 0)
{
port.WriteLine(message);
Thread.Sleep(10);
}
}
private void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{// Event for receiving data
string txt = port.ReadExisting();
List<PlugwiseMessage> msg = reader.Read(Regex.Split(txt, "\r\n"));
DataReceived(sender, new System.EventArgs(), msg);
}
/// <summary>
/// This method Opens the connection to the serial port
/// </summary>
public void Open()
{
try
{
if (!port.IsOpen)
{
port.Open();
}
}
catch (System.IO.IOException ex)
{
throw ex;
}
}
/// <summary>
/// This method closes the connection to the serial port
/// </summary>
public void Close()
{
try
{
if (port.IsOpen)
{
port.Close();
}
Thread.Sleep(5);
}
catch (IOException ex)
{
throw ex;
}
}
}
}
I get the exception at this piece (in Visual C# 2008 express)
private void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{// Event for receiving data
string txt = port.ReadExisting();
List<PlugwiseMessage> msg = reader.Read(Regex.Split(txt, "\r\n"));
DataReceived(sender, new System.EventArgs(), msg);
I checked 'msg' but this is filled with so far i can see valid data.
So I don't know why I get the exception.
Do you have any subscribers for plugwiseControl.DataReceived event? A common way to raise event is
var handler = DataReceived;
if(handler != null) handler(sender, EventArgs.Empty, msg);
I think DataReceived event is not initialized, by the calling method.
In .NET, if you raise an event and there are no registered listeners for that event (nothing has done to your event what you do to your port's DataReceived event in the second line of your try {} block in your constructor), it shows up as null, and raises that exception. In your Watch list screenshot, DataReceived is null, which makes me think this is the problem. So:
if (DataReceived != null)
DataReceived(...arguments here...);