2

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();
}
}
user2886091
  • 725
  • 7
  • 16
  • 29
  • I have red that topic, but It was not really helpful. Because as I said app is collecting those positions OK in that dataset button, wich also uses the foreach loop to update joint positions. How is possible that when dataset button is performing, the line is not null and when classification is performing, it is null. The Program class is not working with joints, but only with txt dataset files. – user2886091 Mar 02 '14 at 19:52
  • Even though you claim so, I do not see any `foreach` loop in your click event handler for the dataset button. Can you run your program with a debugger after setting a breakpoint on the line that produces the exception, and then check whether either `skeleton` or `skeleton.Joints` is `null`? – O. R. Mapper Mar 02 '14 at 21:13
  • May be a misunderstanding. I have a foreach loop in SkeletonFrameReady method, where the positions are updated. What is interesting for me, that the exception is thrown only on classification button click. The dataset button is OK and it also works with joints. – user2886091 Mar 02 '14 at 21:18
  • Can you have a look at your stacktrace? I don't know what `Program.siet()` does, but I *suspect* that `sensor_SkeletonFrameReady` is called somewhere down the road from `Program.siet()`. As such, it is hardly surprising that the problem occurs for the classify button, but not for the dataset button, as the dataset button does not do anything that could lead to `sensor_SkeletonFrameReady` getting called. Also note that the dataset button does `not` "work with joints"; in your `File.AppendAllText` invocations you merely write out some `double` values, which have nothing to do with your problem. – O. R. Mapper Mar 02 '14 at 21:34
  • Unrelated to this, please be aware that the way you write your file in the classify button is risky. Another process could in theory write a file with the same path after your call to `File.Delete` and before your call to `File.AppendAllText`, thus making the latter fail if the file is then locked and accordingly unaccessible for appending. Have a look at the [`StreamWriter` constructor that allows appending](http://msdn.microsoft.com/en-us/library/36b035cb%28v=vs.110%29.aspx). – O. R. Mapper Mar 02 '14 at 21:36
  • Hi, I edited my post, and here is everything, what I have got. Hopefully you will see where could be problem. – user2886091 Mar 03 '14 at 07:13
  • You have declared Skeleton[] skeletons = new Skeleton[1]; and in any case of the using loop is not entered, the foreach loop can throw object reference null error. Just initialize Skeleton[] skeletons = null and check for null before foreach. – ray Mar 03 '14 at 10:21

1 Answers1

0

This was the solution:

  foreach (Skeleton skeleton in skeletons)
            {
                if (skeleton != null)
                {
                    hip = skeleton.Joints[JointType.HipCenter];
                    rightShoulder = skeleton.Joints[JointType.ShoulderRight];
                    leftShoulder = skeleton.Joints[JointType.ShoulderLeft];
                    head = skeleton.Joints[JointType.Head];


                    shoulderRightY += rightShoulder.Position.Y;
                    shoulderLeftY += leftShoulder.Position.Y;
                    headY += head.Position.Y;
                    hipY += hip.Position.Y;
                }


        }

Can anyone verify it?

user2886091
  • 725
  • 7
  • 16
  • 29