UserTracker.net - sample program (C#)

    <b>Source file:</b> Click the following link to view the source code file:
        - UserTracker.net\\MainWindow.cs

    The User Tracker sample program demonstrates the OpenNI code for tracking the movement of a user through its skeleton capability. This sample program is contained in the UserTracker.net file.

    This major section describes the OpenNI program code of the User Tracker sample program written in the C# language for .NET.

    The documentation describes the program code from the top of the program file(s) to bottom, unless otherwise indicated.

    All the following sections document the OpenNI code in the <code>UserTracker.net </code> file.

    @section utcs_main MainWindow() method

        The <code>MainWindow()</code> routine is located in the <code>MainWindow.cs</code> file. It is shown in the following code block. This routine performs data declarations and initializations. At the end of this routine, it calls the <code>readerThread.Start()</code>  method, which manages updating the OpenNI generated data for user access.

    @section utcs_refto_glb_dcl_blk Global Declaration Block

        The global declaration block is located at the end of the program file. However it is presented and described here for the convenience of the reader.

        The following declarations define the OpenNI objects required for building the OpenNI production graph. The production graph is the main object model in OpenNI.
        @code
            private OutArg<ScriptNode> scriptNode;
            private Context context;
            private DepthGenerator depthGen;
            private UserGenerator userGen;
            private SkeletonCapability skeletonCap;
            private PoseDetectionCapability poseDetectionCap;
        @endcode

        Each of these declarations is described separately in the following paragraphs.

        The @ref xn::ScriptNode object loads an XML script from a file or string, and then runs the XML script to build a production graph. The ScriptNode object must be kept alive as long as the other nodes are needed.
        @code
            private OutArg<ScriptNode> scriptNode;
        @endcode

        The <i>production graph</i> is a network of software objects - called production nodes - that can identify blobs as hands or human users. In this sample program the production graph identifies blobs as human users, and tracks them as they move.

        A @ref xn::Context object is a workspace in which the application builds an OpenNI production graph.
        @code
            private Context context;
        @endcode

        a @ref xn::DepthGenerator node generates a depth map. Each map pixel value represents a distance from the sensor.
        @code
            DepthGenerator depthGen;
        @endcode

        A @ref xn::UserGenerator node generates data describing users that it recognizes in the scene, identifying each user individually and thus allowing actions to be done on specific users. The single UserGenerator node gets ata for all users appearing in the  scene.
        @code
            private UserGenerator userGen;
        @endcode

        The @ref xn::SkeletonCapability lets the node generate a skeleton representation for  each human user generated by the node. Each UserGenerator node can have exactly one skeleton representation.
        The skeleton data includes the location of the skeletal joints, the ability to track skeleton positions and the user calibration capabilities.
        To help track a user's skeleton, the @ref xn::SkeletonCapability can execute a calibration process to measure and record the lengths of the human user's limbs.
        @code
            private SkeletonCapability skeletonCap;
        @endcode

        The PoseDetectionCapability object lets a @ref xn::UserGenerator "UserGenerator" node recognize when the user is posed in a specific position.
        @code
            private PoseDetectionCapability poseDetectionCap;
        @endcode

    @section utcs_func_main Main Program - MainWindow()

        @subsection svcs_scrpt_sets_up_pg Uses a Script to Set up a Context and Production Graph
            The following code block uses a script to set up a context and a production graph. The @ref xn::Context::InitFromXmlFile() "createFromXmlFile()" method, which is a shorthand combination of two other initialization methods, initializes the context object and then creates a production graph from an XML file. The XML script file describes all the nodes you want to create. For each node description in the XML file, this method creates a node in the production graph. See <a href="bmrk_nodedescr"><b>OpenNI Nodes Declarations</b></a>    for explanations of the production graph and generator nodes.
            @code
                this.context = Context.CreateFromXmlFile(SAMPLE_XML_FILE, out scriptNode);
            @endcode

        @subsection svcs_get_nodes_from_prodgrph Gets Nodes from Production Graph

            In the following statement, the @ref xn::Context::FindExistingNode() "FindExistingNode()" call gets a reference to production nodes in the production graph. In this example, the application passes the NodeType.Depth parameter to get a reference to a @ref xn::DepthGenerator "DepthGenerator node" so that it can work with it.
            @code
                this.depth = context.FindExistingNode( ) as DepthGenerator;
            @endcode

            The following code block demonstrates verifying that the script file defined a @ref xn::DepthGenerator node.
            @code
                if (this.depth == null)
                {
                    throw new Exception("Viewer must have a depth node!");
                }
            @endcode

        @subsection utcs_create_ug_node Creates a UserGenerator Node

            The following program code creates a @ref xn::UserGenerator "UserGenerator" node and then gets two capabilities of the node: a @ref xn::SkeletonCapability "SkeletonCapability" object and a @ref xn::PoseDetectionCapability "PoseDetectionCapability" object. The code then assigns references to the two capabilities for easy access to them.
            @code
                this.userGenerator = new UserGenerator(this.context);
                this.skeletonCapbility = this.userGenerator.SkeletonCapability;
                this.poseDetectionCapability = this.userGenerator.PoseDetectionCapability;
                this.calibPose = this.skeletonCapbility.CalibrationPose;
            @endcode

            Each of these declarations is described separately in the following paragraphs.

            The following statement creates and returns a reference to a @ref xn::UserGenerator "UserGenerator" node. The <code>new() </code> constructor can return a reference to an existing UserGenerator node if one already exists in the production graph created from the XML. If no UserGenerator node already exists, the constructor creates a new UserGenerator node and returns a reference to the new node.
            @code
                this.userGenerator = new UserGenerator(this.context);
            @endcode

            The following two statements get a @ref xn::SkeletonCapability object for accessing Skeleton functionality and a PoseDetectionCapability for accessing Pose Detection functionality.
            @code
                this.skeletonCapbility = this.userGenerator.SkeletonCapability;
                this.poseDetectionCapability = this.userGenerator.PoseDetectionCapability;
            @endcode

            The following statement gets the name of the pose that is required for calibration. The pose and its name reside in the plug-in module that provides the @ref xn::SkeletonCapability. The pose's name and details are defined by the developer of the module. The pose name is only needed when online calibration is turned off. This is seen in the callbacks described later.
            @code
                this.calibPose = this.skeletonCapability.CalibrationPose;
            @endcode

        @subsection utcs_init_event_hndlrs Initializes Event Handlers

            The following code block registers two event handlers for the UserGenerator node, and event handlers for its two capabilities: the @ref xn::SkeletonCapability "SkeletonCapability" object and a @ref xn::PoseDetectionCapability "PoseDetectionCapability" object.
            @code
                this.userGenerator.NewUser += userGenerator_NewUser;
                this.userGenerator.LostUser += userGenerator_LostUser;
                this.poseDetectionCapability.PoseDetected += poseDetectionCapability_PoseDetected;
                this.skeletonCapbility.CalibrationComplete += skeletonCapbility_CalibrationComplete;
            @endcode

            See @ref utcs_event_handlers for the descriptions of these events and their usages.

        @subsection utcs_set_ske_prfl Sets the Skeleton Profile

            In the following statement, the @ref xn::SkeletonCapability::SetSkeletonProfile() "setSkeletonProfile()" sets the skeleton profile. The skeleton profile specifies which joints are to be active, and which to be inactive. The @ref xn::UserGenerator node generates output data for the active joints only. This profile applies to all skeletons that the @ref xn::UserGenerator node generates. In this case, the method sets all joints to be active.
            @code
                this.skeletonCapbility.SetSkeletonProfile(SkeletonProfile.All);
            @endcode

        @subsection utcs_init_joints_array Initializes the 'joints' Array
            The following statement initializes the 'joints' array. The 'joints' array maintains for each user a mapping of the current position of each joint, as follows:

            @verbatim
                (user ID->(SkeletonJoint->SkeletonJointPosition))*)
            @endverbatim

            Each entry maps a particular <code>@ref xn::XnSkeletonJoint</code> "skeleton joint" (an ID identifying a particular joint in the skeleton) to its <code>keletonJointPosition</code> "3D position".
            @code
                this.joints = new Dictionary<int,Dictionary<SkeletonJoint,SkeletonJointPosition>>();
            @endcode

        @subsection utcs_start_node_generating  Starts the Node Generating

            The following statement ensures that all created @ref dict_gen_node "generator nodes" are in Generating state. Each node can be in Generating state or Non-Generating state. When a node is in Generating state it generates data.
            @code
                context.startGeneratingAll();
            @endcode

        @subsection utcs_setup_hist_array Sets up the Histogram Array

            The following statement defines the histogram array, and dimensions it using the sensor device's maximum depth. (This code is not OpenNI specific.) This array is a key part of this sample program. the @ref xn::DepthGenerator::GetDeviceMaxDepth() "DeviceMaxDepth()" method gets the maximum depth value that the DepthGenerator node can generate.
            @code
                this.histogram = new int[this.depth.DeviceMaxDepth];
            @endcode

        @subsection utcs_setup_map_out_mode Sets up the MapOutputMode

            The following statement gets the Map Output mode of the @ref xn::DepthGenerator "DepthGenerator" node.  The map output mode is the combination of the node's scene resolution and frame rate that was set to generate the latest frame.
            @code
                MapOutputMode mapMode = this.depth.MapOutputMode;
            @endcode

            The following statement accesses the Map Output mode to get the DepthGenerator's map dimensions and pixel color format. @ref xn::DepthMap::XRes "XRes" and @ref xn::DepthMap::YRes "YRes" get the frame X an Y resolutions of the most recently generated data. X and is the number of columns and rows, respectively, in the frame after any required cropping has been applied. See @ref conc_map_wrapper_classes "Map Wrapper Classes" for more information.<br>

        @subsection utcs_setup_bitmap Sets up the Bitmap Class and Start Run

            <code>bitmap</code> is not itself OpenNI object. In brief, the <code>bitmap</code> object is an instance of the .NET Bitmap class for work with images defined by pixel data. It is defined with dimensions given by the values of the OpenNI @ref xn::DepthMap::XRes "XRes" and @ref xn::DepthMap::YRes "YRes", i.e, the x-y dimensions of the DepthGenerator node.
            @code
                this.bitmap = new Bitmap((int)mapMode.XRes, (int)mapMode.YRes)
            @endcode

            Finally, the following code block sets up the reader thread and starts it running.
            @code
                this.shouldRun = true;
                this.readerThread = new Thread(ReaderThread);
                this.readerThread.Start();
            @endcode

    @section utcs_event_handlers  Declarations of Event Handlers

        This section describes the event handlers this sample program requires, describing the nature of the events themselves and what is done inside the handlers.

        A typical order of invocation of the events used in this sample program would be:
        1. 'New User' event
        2. 'Pose Detected' event (optional)
        3. 'Calibration Complete' event
        4. 'Lost User' event

        The events are described below in order of their declaration in the source code.

        @subsection utcs_calibcmplt_ev_hndlr  'Calibration Complete' event handler

            The <b>'Calibration Complete' event</b> signals that a specific user's skeleton has now completed the calibration process, and provides a result status. The user is identified by the ID given by the <code>e.ID</code> parameter.

            The <b>'Calibration Complete' event handler</b> is as below. It's processing is as follows. The handler tests whether the calibration process was completed successfully. If yes, that means that a user has been detected and calibrated, and enough information has been obtained to create a skeleton to represent the user.

            The handler then advances the processing to the next stage, i.e., to call @ref xn::HandsGenerator::StartTracking() to start tracking the skeleton, which represents a human user body, within a real-life (3D) scene for analysis, interpretation, and use by the application.
            (Description continued after the code.)
            @code
                void skeletonCapbility_CalibrationComplete(object sender, CalibrationProgressEventArgs e)
                {
                    if (e.Status == CalibrationStatus.OK)
                    {
                        this.skeletonCapbility.xn::(e.ID);
                        this.joints.Add(e.ID, new Dictionary<SkeletonJoint, SkeletonJointPosition>());
                    }

                else if (e.Status != CalibrationStatus.ManualAbort)
                {
                    if (this.skeletonCapbility.DoesNeedPoseForCalibration)
                 {
                     this.poseDetectionCapability.StartPoseDetection(calibPose, e.ID);
                 }
                    else
                    {
                     this.skeletonCapbility.RequestCalibration(e.ID, true);
                }
            @endcode

            In the above, the handler then creates, for the new user, a new user entry in the @ref utcs_init_joints_array <code>joints</code> array. This is a database for users and skeletons. In this database, each user has a list of entries where each entry is a data pair:
            @verbatim
                <SkeletonJoint, SkeletonJointPosition>
            @endverbatim

            In the above handler, if the calibration process failed, the handler restarts the whole calibration sequence by calling xn::PoseDetectionCapability::StartPoseDetection() "StartPoseDetection()".

        @subsection utcs_posedetect_ev_hndlr  'Pose Detected' event handler

            The <b>'Pose Detected' event</b> signals that a human user made the pose named in the call to the StartPoseDetection() method. The user is designated with the ID given by the <code>e.ID</code> parameter.

            The <b>'Pose Detected' event handler</b> is as below. It's processing is as follows.
            Now that a pose has been detected, the handler calls @ref xn::PoseDetectionCapability::StopPoseDetection() "StopPoseDetection()" to stop pose detection. The handler then calls @ref xn::SkeletonCapability::RequestCalibration() to start calibration. The <code>true</code> disregards any previous calibration and forces a new calibration.
            @code
                void poseDetectionCapability_PoseDetected(object sender, PoseDetectedEventArgs e)
                {
                    this.poseDetectionCapability.StopPoseDetection(e.ID);
                    this.skeletonCapbility.RequestCalibration(e.ID, true);
                }
            @endcode

        @subsection utcs_newuser_ev_hndlr  'New User' event handler

            The <b>'New User' event</b> signals that a new user has now been recognized in the scene. A new user is a user that was not previously recognized in the scene, and is now recognized in the scene.

            The <b>'New User' event handler</b> is as below. It's processing is as follows.
            Now that a new user has been detected, if a pose is required the handler calls xn::PoseDetectionCapability::StartPoseDetection() "StartPoseDetection()" to start pose detection. If a pose is not required, the handler requests calibration immediately.
            @code
                void userGenerator_NewUser(object sender, NewUserEventArgs e)
                {
                    if (this.skeletonCapbility.DoesNeedPoseForCalibration)
                 {
                    this.poseDetectionCapability.StartPoseDetection(this.calibPose, e.ID);
                    }
                else
                {
                 this.skeletonCapbility.RequestCalibration(e.ID, true);
             }
            @endcode

        @subsection utcs_lostuser_ev_hndlr  'Lost User' event handler

            The <b>'Lost User' event</b> signals that a user has been lost from the list of previously recognized users in the scene. The exact meaning of a lost user is decided by the developer of the @ref xn::UserGenerator. However, a typical implementation would define that a lost user is a previously recognized user that then exits the scene and does not return, even after a 'Lost User' timeout has elapsed. Thus this event is raised only after some delay after the user actually exited the scene.

            The <b>'Lost User' event handler</b> is as below. It's processing is as follows.
            Now that an existing user has been lost, the handler deletes the user's entry from the @ref utcs_init_joints_array <code>joints</code> array.
            @code
                void userGenerator_LostUser(object sender, UserLostEventArgs e)
                {
                    this.joints.Remove(e.ID);
                }
            @endcode

    @section utcs_calcHist CalcHist() - Using the Depth Values to Build an Accumulative Histogram

        The program builds this Accumulative Histogram in order to process the maps to display the closer areas more brightly.

        The following sequence of code blocks build and process an accumulative histogram of the depth map
    to increase the contrast of areas of different depths so that closer areas are brighter.
        The accumulative histogram achieves this by separating out areas of different depth values.

        With regards to the actual values contained in the histogram on completion of its preparation for later use, each cell ends up holding a number between 0 and 255, representing the percentage of the pixels that are further away from the sensor than the distance that its index represents (in mm). (This is always 'greater than', and not 'greater than or equal to'). This means, for example, that the furthest distance will have 0, while the closest can have as high as 256.

The following code block uses the depth values to build an accumulative histogram of frequency of occurrence of each depth value. The `depthMD.DepthMapPtr()`method returns a pointer to the Depth Map to access each value in the depth buffer. The depth value is then used as an index into the histogram[] array.

        @code
            private unsafe void CalcHist(DepthMetaData depthMD)
            {
                // reset
                for (int i = 0; i < this.histogram.Length; ++i)
                    this.histogram[i] = 0;

                ushort* pDepth = (ushort*)depthMD.DepthMapPtr.ToPointer();

                int points = 0;
                for (int y = 0; y < depthMD.YRes; ++y)
                {
                    for (int x = 0; x < depthMD.XRes; ++x, ++pDepth)
                    {
                        ushort depthVal = *pDepth;
                        if (depthVal != 0)
                        {
                            this.histogram[depthVal]++;
                            points++;
                        }
                    }
                }
        @endcode

        The following processing loop converts the histogram into a cumulative histogram of frequency of
        occurrence of each depth value. The cumulative histogram is a histogram in which the vertical axis
        shows not just the counts for a single depth value, but instead -- for each depth value -- shows
        the counts for that depth value plus all counts for smaller depth values. The processing loop
        achieves this by making a running total of the counters of the depth values. Depth values
        whose counters reach relatively large numbers indicate blobs at
        those depths. The cumulative total always increases for all depth values each,
        faster when the depth values are encountered that represent the sides of a blob,
        and more slowly at a blob's peak. Thus, blobs at significantly different depths are
        separated out by the histogram to be at significantly different frequency levels.
        @code
                for (int i = 1; i < this.histogram.Length; i++)
                {
                    this.histogram[i] += this.histogram[i-1];
                }
        @endcode        Note that at this stage a larger depth value means a greater distance of a human user from the sensor;
        accordingly, the higher cumulative frequency levels also mean a greater distance. Since we want
        a greater distance to be presented by a lower brightness (darker color), and a smaller distance to be
                represented by a greater brightness, then later in the code this direction must be reversed.

        The following processing loop normalizes the cumulative histogram by dividing each counter by
        <code>points</code>, i.e., it converts every counter to a fraction of 1.

        This loop also reverses the direction of the histogram - as explained above. This is the term <code>(1.0f&nbsp-&nbsp)</code>

        So now we have got the brightness factor we need: a smaller distance (depth)
        is represented by a greater cumulative count for a greater brightness,
        and larger distance (depth) is represented by a smaller cumulative count for a lower brightness.

        The <code>(256&nbsp*&nbsp)</code> multiplier and <code>(int)</code> cast then convert the brightness from a
        fraction of 1 to an integer between 0 and 255, which is exactly what is needed to directly create
        an RGB color later in this routine.
        @code
                if (points > 0)
                {
                    for (int i = 1; i < this.histogram.Length; i++)
                    {
                        this.histogram[i] = (int)(256 * (1.0f - (this.histogram[i] / (float)points)));
                    }
                }
            }
        @endcode

        The histogram calculation has now been completed. We now have a <code> histogram[]</code>
        array that when indexing it with a depth value returns you a brightness value that is
        larger than the brightness value returned by indexing with a smaller depth value.

    @section utcs_drawing_the_ske Drawing the Complete Skeleton

        The following sections show how to get all the individual joints, and then use them to draw a complete skeleton.

    @section utcs_get_joint GetJoint() method

        The <code>GetJoint()</code> method is called multiple times by the <code>GetJoints()</code> method (see further below), where each call gets one of the joints of a skeleton and adds it into the easy-to-access <code>joints</code> Dictionary object.

        In OpenNI, some of these <i>joints</i> are  actual joints, in the conventional sense as termed by the English language, for example, SkeletonJoint.LeftElbow and XN_SKEL_LEFT_WRIST; and other joints are in fact <i>limbs</i>, for example, XN_SKEL_HEAD and XN_SKEL_LEFT_HAND. However, OpenNI defines all these as <i>joints</i>, each joint with a single position coordinate.
        @code
            private void GetJoint(int user, SkeletonJoint joint)
            {
                SkeletonJointPosition pos = this.skeletonCapbility.GetSkeletonJointPosition(user, joint);
                if (pos.Position.Z == 0)
                {
                    pos.Confidence = 0;
                }
                else
                {
                    pos.Position = this.depth.ConvertRealWorldToProjective(pos.Position);
                }
                this.joints[user][joint] = pos;
            }
        @endcode
        The above statements are explained separately, as follows.

        The @ref xn::SkeletonCapability::GetSkeletonJointPosition() "getSkeletonJointPosition()" method gets the <code>SkeletonJointPosition</code> position of one of the skeleton joints in the most recently generated data for a specified user. The user is specified by its interger ID number, which is of type @ref xn::XnUserID.
        @code
            SkeletonJointPosition pos = this.skeletonCapbility.GetSkeletonJointPosition(user, joint);
        @endcode

        A sanity check is then performed to check that the joint does not have zero depth, since translation between coordinate systems does not work with a depth zero.
        @code
            if (pos.Position.Z == 0)
        @endcode

        If the position is not zero depth, a new <code>SkeletonJointPosition</code> object is created for the joint and inserted into the <b>joints</b> mapping table. The position structure comprises a 3D position, @ref xn::XnVector3D "XnVector3D", and a confidence that the joint is in fact in that position. the @ref xn::XnVector3D "3D position struct" is a projective coordinate, so the @ref xn::DepthGenerator::ConvertRealWorldToProjective() "convertRealWorldToProjective()" is used to convert the real world coordinate to a projective coordinate.
        @code
            pos.Position = this.depth.ConvertRealWorldToProjective(pos.Position);
            this.joints[user][joint] = pos;
        @endcode

    @section utcs_get_joints GetJoints() method

        This method comprises successive calls to the GetJoint() method to get all the joints in a skeleton. The following code block shows the first few statements in this method, which get the Head and Neck joints. The subsequent statements get the rest of the joints.
        @code
            private void GetJoints(int user)
            {
                GetJoint(user, SkeletonJoint.Head);
                GetJoint(user, SkeletonJoint.Neck);
                 ...
            }
        @endcode

    @section utcs_draw_line drawLine() method

        This method draws a limb of the avatar representation of a human user by drawing a line between two adjacent OpenNI @ref xn::XnSkeletonJoint "joints" passed as parameters to this function. The two joints are points in the scene, thus this function draws a line between the two points, i.e., between the current positions of two joints. The two adjacent joints come from the global <code>joints</code> mapping table through the <i>dict</i> parameter.
        @code
            private void DrawLine(Graphics g, Color color, Dictionary<SkeletonJoint, SkeletonJointPosition> dict, SkeletonJoint j1, SkeletonJoint j2)
            {
                ...
            }
        @endcode

        In the above, the <code>dict</code> parameter passes in the mapping list of joint-to-position for all the joints of a apecified user. The <code>dict</code> parameter is of type <code>Dictionary<SkeletonJoint, SkeletonJointPosition> dict</code>. The two parameters <code>j1</code> and <code>j2</code> are both enum types, specifying a particular joint in the skeleton. <code>j1</code> and <code>j2</code> are used to index the <code>dict</code> list to get the corresponding positions of the joints.

        Statements of this function are explained below.

        First, the method checks confidence, which is the likelihood that a point is real, and if either of them have a zero confidence the method fails.
        @code
            Point3D pos1 = dict[j1].Position;
            Point3D pos2 = dict[j2].Position;

            if (dict[j1].Confidence == 0 || dict[j2].Confidence == 0)
                return;

        @endcode

        The following code block draws the avatar's limb by drawing a line between the two adjacent points. It uses the locations <code>pos1 </code> and <code>pos2</code> obtained above.
        @code
            g.DrawLine(new Pen(color),
                        new Point((int)pos1.X, (int)pos1.Y),
                        new Point((int)pos2.X, (int)pos2.Y));
        @endcode

    @section utcs_draw_skel DrawSkeleton() method

        This method draws the complete skeleton for a specified user. It draws the skeleton by callng the drawLine() method successive times to draw connecting lines between each adjacent pair of joints. The following code block shows some sample statements:
        @code
            private void DrawSkeleton(Graphics g, Color color, int user)
            {
                GetJoints(user);
                Dictionary<SkeletonJoint, SkeletonJointPosition> dict = this.joints[user];

                DrawLine(g, color, dict, SkeletonJoint.Head, SkeletonJoint.Neck);
                  ...
                DrawLine(g, color, dict, SkeletonJoint.Neck, SkeletonJoint.LeftShoulder);
            }
        @endcode

        The first few statements are explained in the following.

        The <code>GetJoints(user)</code> call builds the complete <code>joints</code> list of all of the specified user's joints, that is to say, for the whole skeleton.
        GetJoints(user);
        @code
            GetJoints(user);
        @endcode

        The <code>dict</code> parameter is then assigned with this list of joints.
        Dictionary<SkeletonJoint, SkeletonJointPosition> dict = this.joints[user];
        @code
            Dictionary<SkeletonJoint, SkeletonJointPosition> dict = this.joints[user];
        @endcode

        A call to the DrawLine() method then passes two adjacent joint specifiers as parameters, which are used to select two joint entries from the dict list of joints. The DrawLine() method uses these joints to then draw a line -- or wire -- between the positions of the two joints -- for example, Head and Neck, as shown below -- to make one wire of the skeleton wire frame.
        @code
            DrawLine(g, color, dict, SkeletonJoint.Head, SkeletonJoint.Neck);
        @endcode
        The method then draws a further wire, later on, to connect the Neck to a Shoulder, as shown below.
        @code
            DrawLine(g, color, dict, SkeletonJoint.Neck, SkeletonJoint.LeftShoulder);
        @endcode

    @section utcs_reader_thread ReaderThread() method

        ReaderThread() is the main run-time method. it manages getting the skeleton tracking data and then calling the DrawSkeleton() method to print the skeleton on the graphic display.

        @subsection utcs_getdata_from_dg_node Gets Data from the DepthGenerator Node

            The following declares a metadata object to provide a frame object for the @ref xn::DepthGenerator node. A @ref dict_gen_node "generator node's" @ref glos_frame_object "frame object" contains generated data frame and all its associated properties. This data frame and its properties are accessible through the node's metadata object.
            @code
                DepthMetaData depthMD = new DepthMetaData();
            @endcode

            Following is the program's main processing loop. All of the remaining code in the ReaderThread() method is located in this loop.
            @code
                while (this.shouldRun)
                {
                    try
                    {
                        this.context.WaitOneUpdateAll(this.depth);
                    }
                    ...
                    ...
                }
            @endcode

            The first statement in the loop, shown above, calls the @ref xn::Context::WaitOneUpdateAll() "WaitOneUpdateAll()" method in the following statement waits for a specified node to have generated a new data frame. The method then refreshes the data of all nodes in the entire production graph. The application can then get the data (for example, using a metadata GetData() method). This method has a timeout.
            @code
                this.context.WaitOneUpdateAll(this.depth);code

            The following statement places the latest data generated in an 'easy-to-access' buffer. In OpenNI terminology: "the node's getMetaData() method gets the node's data that is designated as 'metadata to be placed in the node's metadata object'". The code copies the node's frame data and configuration to a metadata object - (<code>depthMD</code>). This metadata object is then termed the 'frame object'.
            @code
                this.depth.GetMetaData(depthMD);
            @endcode

            The following statement calls CalcHist() to calculate the histogram. The histogram stores the frequency of occurence of all depth values.
            @code
                CalcHist(depthMD);
            @endcode

            The rest of the code is within the 'lock' block
            @code
            lock (this)
            {
                ...

                if (this.shouldDrawPixels)
                {
                 ...
                }
            }
            @endcode

            The following describes code inside the above <code>'if'</code> block.

            @subsection utcs_nestedforloop Nested For-loop for Getting Depth and Label Pixels and Displaying them as RGB pixels

            The following statement defines and sets a pointer to iterate through the  entire data frame got from the @ref xn::DepthGenerator "DepthGenerator" node. This is a pointer to a @ref conc_map_wrapper_classes "light wrapper object".  Note that this is not the depth metadata. {Why not use the depth metadata object?}
            @code
                ushort* pDepth = (ushort*)this.depth.DepthMapPtr.ToPointer();
            @endcode

            The following statement defines and sets a pointer to iterate through the  entire User Labels map got from the @ref xn::UserGenerator "UserGenerator" node. This is a pointer to a @ref conc_map_wrapper_classes "light wrapper object".
            @code
                ushort* pLabels = (ushort*)this.userGenerator.GetUserPixels(0).LabelMapPtr.ToPointer();
            @endcode

            The above statement sets up a pointer to the User Labels Map. This  parallels the Depth map (same x-y dimensions) and identifies which specific user is associated with each pixel. This is needed since each user blob (in the scene) contains an area of pixels, and so applications need to know which user each pixel is part of. Each pixel of the User Labels Map is an integer specifying a user ID. This user ID is the 'label'. Thus, the User Labels Map is a map of the pixels of the entire scene, where each pixel identifies the user it is owned by. If the value of a pixel (in the User Label map) is zero then that means that the pixel is not part of any user and so is part of the background.

            The following loops iterate both depth map and label map, both being the same size, the label determines the color and the depth (through the histogram) determines the brightness.

            @subsection utcs_nestedforloop_for_disp_userID Nested For-loop Displaying User IDs

                The application then prints a status report for each user in the scene at the position of the user.

                The following statement is part of the setup for the nested for-loop for displaying user IDs on the center of the display images. This makes an array of all the users currently in the scene.
                @code
                    int[] users = this.userGenerator.GetUsers();
                @endcode

                For each user in the array, the main routine loop is performed.

                The application  displays the status report at the user's position. To do this, the application must first get the position of the user's center of mass (CoM). This is the single point for representing the user. This is done by calling the @ref xn::UserGenerator node's xn::UserGenerator::getUserCoM() "getUserCoM()" method for each user. The CoM must then be converted to projective coordinates using the @ref xn::DepthGenerator::ConvertRealWorldToProjective() "convertRealWorldToProjective()" method provided by the @ref xn::DepthGenerator "DepthGenerator" node.
                @code
                    foreach (int user in users)
                    {
                        if (this.shouldPrintID)
                        {
                            Point3D com = this.userGenerator.GetCoM(user);
                            com = this.depth.ConvertRealWorldToProjective(com);
                            ...

                        }
                        ...
                    }
                @endcode

                The following statements access the status of each user to report them on top of  each coresponding user image that is displayed on the output display device.

                @code
                    string label = "";
                    if (!this.shouldPrintState)
                        label += user;
                    else if (this.skeletonCapbility.IsTracking(user))
                        label += user + " - Tracking";
                    else if (this.skeletonCapbility.IsCalibrating(user))
                        label += user + " - Calibrating...";
                    else
                        label += user + " - Looking for pose";

                    g.DrawString(label, new Font("Arial", 6), new SolidBrush(anticolors[user % ncolors]), com.X, com.Y);
                @endcode

                Each of the above cases is a different state, as described below. A label is set up depending on the state, and then displayed on the screen at the user position.

                the @ref xn::SkeletonCapability::IsTracking() "IsTracking()" method returns whether a user is currently being tracked. A calibrated user means that the human user's limbs have been measured and the calibration data is available.
                @code
                    else if (this.skeletonCapbility.IsTracking(user))
                @endcode

                the @ref xn::SkeletonCapability::IsCalibrating() "IsCalibrating ()" method returns whether a user is currently being calibrated.
                @code
                    else if (this.skeletonCapbility.IsCalibrating(user))
                @endcode

                If a skeleton is not being calibrated or tracked, then in this implementation, the @ref xn::SkeletonCapability is looking for a pose, which is the assumed meaning of the catch-all branch of the if-then-else, as follows.
                @code
                    else
                        label += user + " - Looking for pose";
                @endcode

                The application then displays the status starting at the CoM position of the user as follows.
                @code
                    g.DrawString(label, new Font("Arial", 6), new SolidBrush(anticolors[user % ncolors]), com.X, com.Y);
                @endcode

                Finally, if a user is being tracked, its skeleton is drawn. This is checked with the @ref xn::SkeletonCapability::IsTracking() method.
                @code
                    if (this.shouldDrawSkeleton && this.skeletonCapbility.IsTracking(user))
                        DrawSkeleton(g, anticolors[user % ncolors], user);
                @endcode