I have managed to create a kinect application, that reads X,Y,Z positions of a certain skeleton joints and subsequently classify pose (standing, sitting, ...). The application is outputting the classification result also to a txt file.
In my main window I have skeleton display, buttons to save current position to training dataset, button to classify current pose and a textbox, where should classification result appear.
Application runs without errors. It also saves current positions to training set on button clicks. However when I click my classify button, the app freezes and in Visual Studio I get error:
Object reference not set to an instance of an object.
Here is piece of code, which includes the lines where I get the error (it is in the foreach
loop - I added a comment line there):
public partial class MainWindow : Window
{
KinectSensor sensor = KinectSensor.KinectSensors[0];
private double shoulderRightY;
private double shoulderLeftY;
private double headY;
private double hipY;
public MainWindow()
{
InitializeComponent();
//After Initialization subscribe to the loaded event of the form
Loaded += MainWindow_Loaded;
//After Initialization subscribe to the unloaded event of the form
//We use this event to stop the sensor when the application is being closed.
Unloaded += MainWindow_Unloaded;
}
void MainWindow_Unloaded(object sender, RoutedEventArgs e)
{
//stop the Sestor
sensor.Stop();
}
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
//Create a Drawing Group that will be used for Drawing
this.drawingGroup = new DrawingGroup();
//Create an image Source that will display our skeleton
this.imageSource = new DrawingImage(this.drawingGroup);
//Display the Image in our Image control
Image.Source = imageSource;
try
{
//Check if the Sensor is Connected
if (sensor.Status == KinectStatus.Connected)
{
//Start the Sensor
sensor.Start();
//Tell Kinect Sensor to use the Default Mode(Human Skeleton Standing) || Seated(Human Skeleton Sitting Down)
sensor.SkeletonStream.TrackingMode = SkeletonTrackingMode.Default;
//Subscribe to te Sensor's SkeletonFrameready event to track the joins and create the joins to display on our image control
sensor.SkeletonFrameReady += sensor_SkeletonFrameReady;
//nice message with Colors to alert you if your sensor is working or not
Message.Text = "Kinect Ready";
Message.Background = new SolidColorBrush(Colors.Green);
Message.Foreground = new SolidColorBrush(Colors.White);
// Turn on the skeleton stream to receive skeleton frames
this.sensor.SkeletonStream.Enable();
}
else if (sensor.Status == KinectStatus.Disconnected)
{
//nice message with Colors to alert you if your sensor is working or not
Message.Text = "Kinect Sensor is not Connected";
Message.Background = new SolidColorBrush(Colors.Orange);
Message.Foreground = new SolidColorBrush(Colors.Black);
}
else if (sensor.Status == KinectStatus.NotPowered)
{//nice message with Colors to alert you if your sensor is working or not
Message.Text = "Kinect Sensor is not Powered";
Message.Background = new SolidColorBrush(Colors.Red);
Message.Foreground = new SolidColorBrush(Colors.Black);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
/// <summary>
//When the Skeleton is Ready it must draw the Skeleton
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void sensor_SkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e)
{
//declare an array of Skeletons
Skeleton[] skeletons = new Skeleton[1];
//Opens a SkeletonFrame object, which contains one frame of skeleton data.
using (SkeletonFrame skeletonframe = e.OpenSkeletonFrame())
{
//Check if the Frame is Indeed open
if (skeletonframe != null)
{
skeletons = new Skeleton[skeletonframe.SkeletonArrayLength];
// Copies skeleton data to an array of Skeletons, where each Skeleton contains a collection of the joints.
skeletonframe.CopySkeletonDataTo(skeletons);
//draw the Skeleton based on the Default Mode(Standing), "Seated"
if (sensor.SkeletonStream.TrackingMode == SkeletonTrackingMode.Default)
{
//Draw standing Skeleton
DrawStandingSkeletons(skeletons);
}
else if (sensor.SkeletonStream.TrackingMode == SkeletonTrackingMode.Seated)
{
//Draw a Seated Skeleton with 10 joints
DrawSeatedSkeletons(skeletons);
}
}
}
foreach (Skeleton skeleton in skeletons)
{
//HERE IS THE ERROR
Joint rightShoulder = skeleton.Joints[JointType.ShoulderRight];
Joint leftShoulder = skeleton.Joints[JointType.ShoulderLeft];
Joint head = skeleton.Joints[JointType.Head];
Joint hip = skeleton.Joints[JointType.HipCenter];
shoulderRightY += rightShoulder.Position.Y;
shoulderLeftY += leftShoulder.Position.Y;
headY += head.Position.Y;
hipY += hip.Position.Y;
}
}
Interesting is, that it saves those values correctly - without error, into dataset (it uses also that foreach loop). Here is that dataset button:
// button click method
private void stoji_Click(object sender, RoutedEventArgs e)
{
File.AppendAllText(@"E:\KINECT\inputs.txt", shoulderRightY + " " + shoulderLeftY + " " + headY + " " + hipY + Environment.NewLine);
File.AppendAllText(@"E:\KINECT\outputs.txt", "1" + Environment.NewLine);
}
And here is my classify button. The Program class is in a separate .cs file and there is a SVM that is performing multiclass classification. It classify the case correctly, because it writes the correct result in my txt file.
private void classify_Click(object sender, RoutedEventArgs e)
{
if (File.Exists(@"E:\KINECT\test.txt"))
{
File.Delete(@"E:\KINECT\test.txt");
}
File.AppendAllText(@"E:\KINECT\test.txt", shoulderRightY + " " + shoulderLeftY + " " + headY + " " + hipY + Environment.NewLine);
double detect = Program.siet();
vysledok.Text = detect.ToString();
}
EDIT:
Here is what my "Program.cs" does. As O.R. Mapper said: "I suspect that sensor_SkeletonFrameReady is called somewhere down the road from Program.siet()". I do not see it anywhere here.
using System ;
using System . Collections.Generic ;
using System . Linq ;
using System . Text ;
using Encog . Neural.Networks ;
using Encog . Neural.Networks.Layers ;
using Encog . Engine.Network.Activation ;
using Encog .ML.Data;
using Encog . Neural.Networks.Training.Propagation.Resilient ;
using Encog .ML.Train;
using Encog .ML.Data.Basic ;
using System.IO;
using System.Collections;
using Encog.ML.SVM;
using Encog.ML.SVM.Training;
public class Program
{
public static double siet()
{
string cestain = @"E:\KINECT\inputs.txt";
double[][] innput = Load.FromFile(cestain);
string cestaout = @"E:\KINECT\outputs.txt";
double[][] ooutput = Load.FromFile(cestaout);
double[] skuska1 = File.ReadAllText(@"E:\KINECT\test.txt").Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries).Select(double.Parse).ToArray();
// c r e a t e a neural network , wi thout us ing a f a c t o r y
var svm = new SupportVectorMachine(2, false);
// c r e a t e t r a i n i n g data
IMLDataSet trainingSet = new BasicMLDataSet(innput, ooutput);
// t r a i n the neural network
/*IMLTrain train = new ResilientPropagation(network, trainingSet);*/
IMLTrain train = new SVMSearchTrain(svm, trainingSet);
int epoch = 1;
do
{
train.Iteration();
Console.WriteLine(@"Epoch #" + epoch + @" Error:" + train.Error);
epoch++;
} while (train.Error > 0.01);
// t e s t the neural network
Console.WriteLine(@"SVM Results:");
IMLData output = svm.Compute(new BasicMLData(skuska1));
Console.WriteLine(skuska1
+ @", actual=" + output[0]);
File.AppendAllText(@"E:\KINECT\testout.txt", output[0].ToString());
return output[0];
}
}
And here is Load.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Collections;
public class Load
{
public Load()
{
}
public static double[][] FromFile(string path)
{
var rows = new List<double[]>();
foreach (var line in File.ReadAllLines(path))
{
rows.Add(line.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries).Select(double.Parse).ToArray());
}
return rows.ToArray();
}
}