Skip to main content

UPDATE (Dec 26, 2013): The following techniques are part of Vitruvius, one of the most powerful Kinect utilities out there. Vitruvius is open-source and free for personal or commercial purposes.

Some people ask me if there is a way to determine the height of a user standing in front of a Kinect device. The answer is definitely positive: We can detect the user’s height regardless of her distance from the sensor! We only need some very basic Maths knowledge. Let’s find out more…

Prerequisites

The algorithm of finding the user’s height

Kinect provides you with the coordinates (X, Y and Z) of 20 skeleton joints. Someone might think that a person’s height is the distance from the head joint to a foot joint, right? Wrong! Your users might stand in bended knees, they might lean a bit to the left or to the right. If you try to calculate the distance defined from the head joint to one of the foot joints, you’ll get a far from accurate result.

Another point of interest is that Kinect gives you the center of the head joint. This means that you’ll need to add 9 – 12 centimetres to the calculated height. You won’t be able to calculate the total height with 100% accuracy. If you need more accuracy, you’ll need to detect the end of the head using the depth stream. Not that difficult, but let’s focus on the skeleton stream right now.

If you examine the human skeleton joints a little more carefully, you’ll notice that the height is the sum of the lengths of the following line segments:

  • Head – ShoulderCenter
  • ShoulderCenter – Spine
  • Spine – HipCenter
  • HipCenter – KneeLeft or KneeRight
  • KneeLeft / KneeRight – AnkleLeft / AnkleRight
  • AnkleLeft / AnkleRight – FootLeft / FootRight

Here is a comprehensive picture illustrating the above segments:
Kinect user height

First of all, we need to calculate the length of the line defined by two joints in the 3D space. This is quite easy Mathematics. Simply find the square root of the sum of squared differences of the coordinates:


public static double Length(Joint p1, Joint p2)
{
    return Math.Sqrt(
        Math.Pow(p1.Position.X - p2.Position.X, 2) +
        Math.Pow(p1.Position.Y - p2.Position.Y, 2) +
        Math.Pow(p1.Position.Z - p2.Position.Z, 2));
}

And here is a way to find the length of more than two joints:


public static double Length(params Joint[] joints)
{
    double length = 0;
    for (int index = 0; index < joints.Length - 1; index++)
    {
        length += Length(joints[index], joints[index + 1]);
    }
    return length;
}

Pretty straightforward till here. But, wait a second! How will you determine which leg corresponds to the most accurate user height? Since we suppose that both of the user’s legs have the same length, we need to choose the one that is tracked better! This means that no joint position is hypothesized. Here is how to find out the number of tracked joints within a joint collection:


public static int NumberOfTrackedJoints(params Joint[] joints)
{
    int trackedJoints = 0;
    foreach (var joint in joints)
    {
        if (joint.TrackingState == JointTrackingState.Tracked)
        {
            trackedJoints++;
        }
    }
    return trackedJoints;
}

We can now safely decide on which leg to use:


// Find which leg is tracked more accurately.
int legLeftTrackedJoints = NumberOfTrackedJoints(hipLeft, 
                                                 kneeLeft, 
                                                 ankleLeft,  
                                                 footLeft);
int legRightTrackedJoints = NumberOfTrackedJoints(hipRight, 
                                                  kneeRight, 
                                                  ankleRight, 
                                                  footRight);
double legLength = legLeftTrackedJoints > legRightTrackedJoints ? 
                   Length(hipLeft, kneeLeft, ankleLeft, footLeft) : 
                   Length(hipRight, kneeRight, ankleRight, footRight); 

And here is the final method:

We can now safely decide on which leg to use:


public static double Height(this Skeleton skeleton)
{
    const double HEAD_DIVERGENCE = 0.1;
    var head = skeleton.Joints[JointType.Head];
    var neck = skeleton.Joints[JointType.ShoulderCenter];
    var spine = skeleton.Joints[JointType.Spine];
    var waist = skeleton.Joints[JointType.HipCenter];
    var hipLeft = skeleton.Joints[JointType.HipLeft];
    var hipRight = skeleton.Joints[JointType.HipRight];
    var kneeLeft = skeleton.Joints[JointType.KneeLeft];
    var kneeRight = skeleton.Joints[JointType.KneeRight];
    var ankleLeft = skeleton.Joints[JointType.AnkleLeft];
    var ankleRight = skeleton.Joints[JointType.AnkleRight];
    var footLeft = skeleton.Joints[JointType.FootLeft];
    var footRight = skeleton.Joints[JointType.FootRight];
    // Find which leg is tracked more accurately.
    int legLeftTrackedJoints = NumberOfTrackedJoints(hipLeft, 
                                                     kneeLeft, 
                                                     ankleLeft,  
                                                     footLeft);
    int legRightTrackedJoints = NumberOfTrackedJoints(hipRight, 
                                                      kneeRight, 
                                                      ankleRight, 
                                                      footRight);
    double legLength = legLeftTrackedJoints > legRightTrackedJoints ? 
                       Length(hipLeft, kneeLeft, ankleLeft, footLeft) : 
                       Length(hipRight, kneeRight, ankleRight, footRight);            
    return Length(head, neck, spine, waist) + legLength + HEAD_DIVERGENCE;
}

Finally, you can use this extension method within a custom application easily:

We can now safely decide on which leg to use:


void Sensor_SkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e)
{
    using (var frame = e.OpenSkeletonFrame())
    {
        if (frame != null)
        {
            Skeleton[] skeletons = new Skeleton[frame.SkeletonArrayLength];
            frame.CopySkeletonDataTo(skeletons);
            var skeleton = skeletons.Where
                           (
                               s => s.TrackingState == SkeletonTrackingState.Tracked
                           ).FirstOrDefault();
            if (skeleton != null)
            {
                double height = skeleton.Height();
            }
        }
    }
}

You can now integrate this code to your own applications and find out the user’s height!

Consider downloading Vitruvius for source code and complete examples.

Enjoy kinecting! More posts coming soon 😉

Vangos Pterneas

Vangos Pterneas is a software engineer, book author, and award-winning Microsoft Most Valuable Professional (2014-2019). Since 2012, Vangos has been helping Fortune-500 companies and ambitious startups create demanding motion-tracking applications. He's obsessed with analyzing and modeling every aspect of human motion using AI and Maths. Vangos shares his passion by regularly publishing articles and open-source projects to help and inspire fellow developers.

12 Comments

  • Anonymous says:

    I am 1,97m, but the sample program tells me I am 1,72m. Any idea what could be wrong?

    Thanks!

    • This is a common problem tall people face. I think you are not totally visible from the depth cameras. Try to move further from the Kinect sensor and ensure that your head and feet state is “tracked” (not “hypothsized”).

  • Erik Lundqvist says:

    Hi!
    I’m a bit curious about the accuracy when adding the depth stream to sense the end of the head. Could you get as accurate as to let’s say 1/2 or even 1/10 cm? Have you tried this out? How accurate is the method as it is right now in terms of real height of the user?

    Thx!

  • Hi! Good example for juniors !

    • Thank you Vardan. This is an introductory post which uses very basic mathematical calculations. I just want to scratch the surface of what you can do with Kinect. In the next series of blog posts, I’ll publish some concepts, which integrate more advanced Algebra and 3D functionality.

  • cesar lopez says:

    Thank you for these great tutorials !!! . Would like to know can we detect the end of the head using the depth stream ?. im trying to do that following all your tutorials cant achieve that , so i can include that lenght to the joints lenght. Thank You Sir !

  • David Zhang says:

    Hi! I met a problem when i changed the code accustomed to kinectV2.
    In the method of Height(), part of the relevant code is:
    public static double Height(this Body body)
    {
    var head = body.Joints[JointType.Head];
    var neck = body.Joints[JointType.Neck];
    var spine = body.Joints[JointType.SpineMid];
    ……….
    }
    Error:Cannot apply indexing with[] to an expression of type ‘System.Collections.Generic.IReadOnlyDictionary’
    I’m a beginner and don’t have any idea about it. Could you tell me that’s why?Thanks!!!

  • Andre Carneiro says:

    Hi!

    First of all, congratulations for your posts! It´s the best for math’s ignorants like me.

    Did you post that ‘Distance’ function/method anywhere or this is a ‘core’ function? Could you please explain how this function/method works? Which axes are considerated to calculate the distances?

    Thanks!

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.