Skip to content
No results
Purple hexagonal logo with white diamond-shaped center featuring geometric star pattern design suitable for corporate branding and digital applications
LIPS®Developer
Documentation
Back To LIPS
Purple hexagonal logo with white diamond-shaped center featuring geometric star pattern design suitable for corporate branding and digital applications
LIPS®Developer
Documentation
Back To LIPS

WELCOME

7
  • LIPS® Documentation Overview
  • About 3D Depth Cameras
  • Depth Image
  • Understanding Depth
  • An Ideal Depth Camera
  • LIPSedge™ AE Series Camera Selection Guide
  • Frequently Asked Questions

LIPSedge™ SDK 1.x

30
  • Introduction to LIPSedge™ SDK 1.x
    • LIPSedge™ SDK 1.x
    • Benefits from LIPSedge™ SDK implementation
    • LIPSedge™ SDK 1.x Installation Architecture
    • How to identify each camera SDK
  • New Features for v1.02
    • New Features for v1.02
    • Installing and identifying packages on Windows
    • Saving Captures and Videos with the DepthViewer
    • Saving Point Cloud with PointCloudViewer
    • Live On-Screen displaying Depth, FPS
    • Live On-Screen displaying XYZ parameters in RawfileViewer
    • Distance measurement on-screen from point-to-point
    • Mouse Pointer Change
    • LIPSedge™ AE and S series Colormap new feature addition
    • Simple naming for LIPSedge™ SDK Tools
    • Importing parameters from .json files for LIPSedge™ AE and S series
  • LIPSedge™ SDK v1.02 Tools
    • LIPSedge™ SDK v1.02 Tools
    • DepthViewer
    • PointCloudViewer
    • CameraParameterViewer
    • CameraCenterViewer
    • CameraEventListener
    • CameraPowerTest
    • LensModeSelector
    • LIPSImuReader
    • CameraSimpleViewer
    • RawFileViewer
  • Tutorial and deployment
    • Tutorials
    • Development and Deployment on arm64
  • Features Articles
    • Features Articles
    • LIPSedge™ SDK Persistent Connection for PoE and USB

DOCUMENTS, INSTALLATION & SETUP

121
  • LIPSedge™ AE400 / AE450
    • LIPSedge™ AE400 / AE450 User Guide
      • LIPSedge™ AE400 / AE450
      • User Guide
      • Previous Releases (Archive)
    • LIPSedge™ AE400 / AE450 SDK Release
      • SDK Release
      • Previous Releases (Archive)
    • LIPSedge™ AE400 / AE450 Installation
      • Installation
      • Linux
      • Windows
      • ROS Wrapper
      • NVIDIA ISAAC Wrapper
      • Persistent Connection
    • LIPSedge™ AE400 / AE450 Complementary Documents
      • Certifications Documents
      • STEP files for CAD Use
      • Firmware
  • LIPSedge™ AE430 / AE470
    • LIPSedge™ AE430 / AE470 User Guide
      • LIPSedge™ AE430 / AE470
      • User Guide
      • Previous Releases (Archive)
    • LIPSedge™ AE430 / AE470 SDK Release
      • SDK Release
      • Previous Releases (Archive)
    • LIPSedge™ AE430 / AE470 Supplementary Documents
      • Firmware
      • STEP files for CAD Use
  • LIPSedge™ AE430-DK / AE470-DK
    • LIPSedge™ AE430-DK / AE470-DK
    • User Guide
  • LIPSedge™ DL & M3 Series
    • LIPSedge™ DL & M3 Series User Guide
      • LIPSedge™ DL & M3 Series
      • User Guide
      • Previous Releases (Archive)
    • LIPSedge™ DL & M3 Series SDK Release
      • SDK Release
      • Changelog
      • Previous Releases (Archive)
    • LIPSedge™ DL & M3 Series Installation
      • Installation
      • Ubuntu
      • Windows
    • LIPSedge™ DL & M3 Supplementary Documents
      • STEP files for CAD Use
  • LIPSedge™ L215u / L210u
    • LIPSedge™ L215u / L210u User Guide
      • LIPSedge™ L215u / L210u
      • User Guide
      • Previous Releases (Archive)
    • LIPSedge™ L215u / L210u SDK Release
      • SDK Release
      • Changelog
      • Previous Releases (Archive)
    • LIPSedge™ L215u / L210u Installation
      • Installation
      • Linux
      • Windows
    • LIPSedge™ L215u / L210u Supplementary Documents
      • STEP files for CAD Use
  • LIPSFace™ HW110/115 On-Device 3D Facial Recognition
    • LIPSFace™ HW110/115 On-Device 3D Facial Recognition
    • User Guide & SDK Download
    • STEP files for CAD Use
  • LIPSFace™ HW120/125 On-Device 3D Facial Recognition
    • LIPSFace™ HW120/125 On-Device 3D Facial Recognition
    • User Guide
    • SDK Download
    • STEP files for CAD Use
  • LIPScan 3D ™ Middleware
    • LIPScan 3D ™ Middleware
    • LIPScan 3D™ SDK
      • LIPScan 3D™ SDK
      • User Guide
      • SDK Download
      • Release Notes
      • Previous Releases
    • LIPScan 3D™ Desktop
      • LIPScan 3D™ Desktop
      • User Guide
      • Application Download
      • Release Notes
      • Previous Releases
  • LIPSense™ 3D Body Pose Middleware
    • LIPSense™ 3D Body Pose Middleware
    • User Manual & SDK Download
    • Changelog
    • Previous Releases (Archive)
    • Examples
      • C++
        • C++
        • skeleton_example
        • skeleton_opencv
  • LIPSMetric™ HA110 Handheld Dimensioner
    • LIPSMetric™ HA110 Handheld Dimensioner
    • User Guide
  • LIPSMetric™ ST115 Static Dimensioner
    • LIPSMetric™ ST115 Static Dimensioner
    • User Guide
  • LIPSMetric™ ST130 Pallet Dimensioner
    • LIPSMetric™ ST130 Pallet Dimensioner
    • User Guide
  • LIPSedge™ SDK Languages & Libraries
    • LIPSedge™ SDK Languages & Libraries
    • OpenCV
    • C++
      • C++
      • environment-setup
      • hello-lipsedge-sdk
      • opencv-viewer
      • roi
      • depth-data
      • align-depth-color
      • range-filter
      • remove-background
      • record
      • pointcloud
      • config-resolution
      • camera-parameter
    • Python
      • Python
      • environment-setup
      • hello-lipsedge-sdk
      • opencv-viewer
      • roi
      • depth-data
      • align-depth-color
      • range-filter
      • remove-background
      • record
    • Java
      • Java
      • ni-hello
    • C#
      • C#
      • ni-hello
      • simple-read
  • LIPSedge™ SDK Frameworks
    • LIPSedge™ SDK Frameworks
    • ROS
    • ROS2
    • NVIDIA Isaac Wrapper
    • GenICam (for Halcon / Aurora Vision)
      • GenICam (for Halcon / Aurora Vision)
      • User Manual
      • Driver(.cti) and Nodemap (.xml)
      • Supported LIPSedge™ Camera SDK
      • Installation Example
  • LIPSedge™ SDK Sample Codes
    • LIPSedge™ SDK Sample Codes
    • Sample Applications & Viewer & Utilities
      • Sample Applications & Viewer & Utilities
      • ni-viewer
      • ni-pointcloud-gl
      • ni-camera-matrix
  • LIPSFace™ SDK
    • LIPSFace™ SDK
    • LIPSFace Overview
    • Changelog
View Categories
  • Home
  • LIPS® Developer Documentation
  • DOCUMENTS, INSTALLATION & SETUP
  • LIPSense™ 3D Body Pose Middleware
  • Examples
  • C++
  • skeleton_example

skeleton_example

Overview #

This example shows how to use LIPSBodypose API to connect the camera, get and draw the skeletons on RGB images and 3D coordination.

Tutorial #

FileFunction
main.cppThe main process. It initializes the whole process, opens an OpenGL window and refreshes the whole process when necessary.
GLUTWindow.cppBuild and refresh the windows.
PoseResult.cppStores skeleton data (image / depth / skeleton)
renderColor.cppRenders color images (2D Overlay)
rednder3D.cppRenders 3D spaces

[main.cpp] – Tutorial #

1. Include header files to access the functions the header files provide.

#include <iostream>

// OpenGL headers
#include <GL/freeglut.h>

// LIPSBodyPose headers
#include <LIPSBodyPose.h>

// Headers for this example
#include "GLUTWindow.h"
#include "renderColor.h"
#include "render3D.h"
#include "PoseResult.h"

2. Create an instance of the body pose detection engine.

// LIPSBodyPose engine
lips::LIPSBodyPose lipsbodypose;

3. Create windows for display.

// Windows for display
std::shared_ptr<GLUTWindow> window_color;
std::shared_ptr<GLUTWindow> window_3d;

4. Create Timer callback function for continuous get skeleton result and update windows.

oid timer( int id )
{
    try
    {
        lips::Frame frame;                                          //1
        lipsbodypose.readFrame( frame );                            //2
        PoseResult& pose_result = PoseResult::Instance();           //3

        //4
        pose_result.setBGR( frame.color_image_bgr );
        pose_result.setDepth( frame.depth_image_mm );
        pose_result.setSkeletons( frame.skeletons );
        pose_result.setProfileInfo( frame.profile_info );

        //5
        window_color->update();
        window_3d->update();
    }
    catch( std::exception& e )
    {
        std::cout << "catch unexpected exception: " << e.what() << std::endl;
        exit( -1 );
    }
    // Reset timer
    glutTimerFunc( 0, timer, 0 );   //6
}
  • 1 – A structure that holds one frame’s value of: RGB image, Depth image, Detected skeletons.
  • 2 – Reads the latest available frame from the LIPS SDK
  • 3 – Get PoseResult single instance
  • 4 – Set the results to PoseResult
  • 5 – Update windows
  • 6 – Reset timer for 0 seconds

[GLUTWindow.cpp] – Tutorial #

1. Include header files to access the functions the header files provide.

#include "GLUTWindow.h"

#include <GL/freeglut.h>
#include <iostream>

2. Set up a default constructor.

LUTWindow::GLUTWindow()
{
    window = -1;
    window_name = default_name;
    window_width = default_width;
    window_height = default_height;
    init_func = NULL;
    display_func = NULL;
    reshape_func = NULL;
}

3. Allows setting the title, width, and height during object creation.

GLUTWindow::GLUTWindow( const std::string name, const int width, const int height )
{
    window = -1;
    window_name = name;
    window_width = width;
    window_height = height;
    init_func = NULL;
    display_func = NULL;
    reshape_func = NULL;
}

4. Set up a destructor.

LUTWindow::~GLUTWindow()
{
}

5. Set a callback function.

oid GLUTWindow::setInitFunc( void ( *func )( void ) )          //1
{
    init_func = func;
}

void GLUTWindow::setDisplayFunc( void ( *func )( void ) )       //2
{
    display_func = func;
}

void GLUTWindow::setReshapeFunc( void ( *func )( int, int ) )   //3
{
    reshape_func = func;
}
SectionFunction
1Stores a pointer to the function that initializes OpenGL state, buffers, etc
2Function that is called in every frame to draw the scene.
3Function called whenever window is resized.

6. Sets window size, creates a new GLUT window and stores its ID, enables double buffering (for smooth rendering), and ensures glutMainLoop() will return when the window closes.

oid GLUTWindow::create()
{
    glutInitWindowSize( window_width, window_height ); // create window
    window = glutCreateWindow( window_name.c_str() );
    glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB );   // double buffering and RGB
    glutSetOption( GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_GLUTMAINLOOP_RETURNS );
    if( init_func != NULL )
        init_func();
    if( display_func != NULL )
        glutDisplayFunc( display_func );
    if( reshape_func != NULL )
        glutReshapeFunc( reshape_func );
}

7. Refresh the wndow.

oid GLUTWindow::update()
{
    if( window >= 0 )
    {
        glutSetWindow( window );
        glutPostRedisplay();
    }
    else
    {
        std::cerr << "ERROR: Window not be created" << std::endl;
    }
}

[PoseResults.cpp] – Tutorial #

1. Include header files to access the functions the header files provide.

#include "PoseResult.h"
#include <LIPSBodyPose.h>

2. Defines the singleton object.

PoseResult PoseResult::object;

3. This is the public interface for accessing the shared PoseResult instance. Other parts of the program call PoseResult::Instance() to read or update data.

PoseResult& PoseResult::Instance()
{
    return object;
}

4. Getters (Accessor Functions).

std::vector<lips::Skeleton>& PoseResult::getSkeletons() const
{
    return object.skeletons;
}

lips::Image<unsigned char[3]>& PoseResult::getBGR() const
{
    return object.image_bgr;
}

lips::Image<unsigned short>& PoseResult::getDepth() const
{
    return object.image_depth;
}

lips::ProfileInfo& PoseResult::getProfileInfo() const
{
    return object.profile_info;
}

5. Setters (Mutator Functions).

oid PoseResult::setSkeletons( const std::vector<lips::Skeleton>& skeletons )
{
    object.skeletons = skeletons;
}

void PoseResult::setBGR( const lips::Image<unsigned char[3]>& image_bgr )
{
    object.image_bgr = image_bgr;
}

void PoseResult::setDepth( const lips::Image<unsigned short>& image_depth )
{
    object.image_depth = image_depth;
}

void PoseResult::setProfileInfo( const lips::ProfileInfo& profile_info )
{
    object.profile_info = profile_info;
}

[RenderColor.cpp] – Tutorial #

1. Include header files to access the functions the header files provide.

#if (defined _WIN64 || defined _WIN32)
#include <windows.h> // must be included before other header
#define _USE_MATH_DEFINES
#endif
#include <cmath>

#include "PoseResult.h"
#include "renderColor.h"
#include "intToColor.h"

#include <GL/glew.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/freeglut.h>
#include <sstream>
#include <array>
#include <iomanip>

2. .Create a namespace block called renderColor.

namespace renderColor
{

3. Define the keypoints (joints) that should be connected with the lines to form the stick-like skeletal figure.

const std::vector<std::array<enum lips::Skeleton::Part, 2>> connection_pairs =
{
    // Face
    { lips::Skeleton::R_EYE, lips::Skeleton::R_EAR },
    { lips::Skeleton::L_EYE, lips::Skeleton::L_EAR },
    { lips::Skeleton::R_EYE, lips::Skeleton::NOSE },
    { lips::Skeleton::L_EYE, lips::Skeleton::NOSE },
    { lips::Skeleton::R_EYE, lips::Skeleton::HEAD },
    { lips::Skeleton::L_EYE, lips::Skeleton::HEAD },
    // Body
    { lips::Skeleton::NOSE, lips::Skeleton::NECK },
    { lips::Skeleton::NECK, lips::Skeleton::R_CLAVICLE },
    { lips::Skeleton::R_CLAVICLE, lips::Skeleton::R_SHOULDER },
    { lips::Skeleton::NECK, lips::Skeleton::L_CLAVICLE },
    { lips::Skeleton::L_CLAVICLE, lips::Skeleton::L_SHOULDER },
    { lips::Skeleton::R_SHOULDER, lips::Skeleton::R_HIP },
    { lips::Skeleton::L_SHOULDER, lips::Skeleton::L_HIP },
    { lips::Skeleton::L_HIP, lips::Skeleton::PELVIS },
    { lips::Skeleton::PELVIS, lips::Skeleton::R_HIP },
    { lips::Skeleton::NECK, lips::Skeleton::CHEST_SPINE },
    { lips::Skeleton::CHEST_SPINE, lips::Skeleton::NAVEL_SPINE },
    { lips::Skeleton::NAVEL_SPINE, lips::Skeleton::PELVIS },

    // Right arm
    { lips::Skeleton::R_SHOULDER, lips::Skeleton::R_ELBOW },
    { lips::Skeleton::R_ELBOW, lips::Skeleton::R_WRIST },
    { lips::Skeleton::R_WRIST, lips::Skeleton::R_HAND },
    { lips::Skeleton::R_HAND, lips::Skeleton::R_THUMB_TIP },
    { lips::Skeleton::R_HAND, lips::Skeleton::R_HAND_TIP },
    // Left arm
    { lips::Skeleton::L_SHOULDER, lips::Skeleton::L_ELBOW },
    { lips::Skeleton::L_ELBOW, lips::Skeleton::L_WRIST },
    { lips::Skeleton::L_WRIST, lips::Skeleton::L_HAND },
    { lips::Skeleton::L_HAND, lips::Skeleton::L_THUMB_TIP },
    { lips::Skeleton::L_HAND, lips::Skeleton::L_HAND_TIP },
    // Right leg
    { lips::Skeleton::R_HIP, lips::Skeleton::R_KNEE },
    { lips::Skeleton::R_KNEE, lips::Skeleton::R_ANKLE },
    { lips::Skeleton::R_ANKLE, lips::Skeleton::R_FOOT },
    // Left leg
    { lips::Skeleton::L_HIP, lips::Skeleton::L_KNEE },
    { lips::Skeleton::L_KNEE, lips::Skeleton::L_ANKLE },
    { lips::Skeleton::L_ANKLE, lips::Skeleton::L_FOOT }
};

4. Declare a global variable named id_pbo of type GLuint.

GLuint id_pbo;

5. Set up an OpenGL state for rendering textures and pixel buffer usage.

void init()
{
    glewInit();
    glGenBuffersARB( 1, &id_pbo );
    glActiveTexture( GL_TEXTURE0 );
    glBindTexture( GL_TEXTURE_2D, 0 );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
    glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );
}

6. The main rendering function that is called whenever the window needs to be redrawn.

void display()
{
    static bool inited = false;

    glClearColor( 0, 0, 0, 1 ); //1 
    glClear( GL_COLOR_BUFFER_BIT ); //2 

    PoseResult& pose_result = PoseResult::Instance(); //3 
    lips::Image<unsigned char[3]> image_bgr = pose_result.getBGR(); //4 

    if( image_bgr.empty() )
        return;

    //5 
    glBindBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB, id_pbo );
    glBufferDataARB( GL_PIXEL_UNPACK_BUFFER_ARB,
                     image_bgr.width() * image_bgr.height() * image_bgr.bytesPerPixel(),
                     image_bgr.data(), GL_STREAM_DRAW_ARB );
    if( !inited )   //6
    {
        glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, image_bgr.width(), image_bgr.height(), 0, GL_BGR, GL_UNSIGNED_BYTE, nullptr );
        inited = true;
    }
    else
    {
        glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, image_bgr.width(), image_bgr.height(), GL_BGR, GL_UNSIGNED_BYTE, nullptr );
    }
    glBindBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB, 0 );

    renderTex();        //7
    renderPose();       //8

    glutSwapBuffers(); //9 
}
SegmentFunction
1Background is black.
2Clear the window.
3get PoseResult single instance
4get color image.
5Set image to OpenGL texture.
6Create or update.
7
8Draws the skeleton lines and keypoints.
9Swap buffers.

8. This function is responsible for rendering a 2D texture (the color image from the camera) onto a quad (rectangle) that spans the whole OpenGL viewport.

void renderTex()
{
    glEnable( GL_TEXTURE_2D );

    glColor4f( 1, 1, 1, 1 );
    glBegin( GL_QUADS );
    {
        glTexCoord2f( 0, 0 );
        glVertex2f( 0, 0 );
        glTexCoord2f( 0, 1 );
        glVertex2f( 0, 1 );
        glTexCoord2f( 1, 1 );
        glVertex2f( 1, 1 );
        glTexCoord2f( 1, 0 );
        glVertex2f( 1, 0 );
    }
    glEnd();

    glDisable( GL_TEXTURE_2D ); // disable texture
}

9. This function overlays detected skeleton data (like body joints and bones) over the camera image using OpenGL

oid renderPose()
{
    PoseResult& pose_result = PoseResult::Instance(); //1 
    std::vector<lips::Skeleton> skeletons = pose_result.getSkeletons(); //2 
    lips::Image<unsigned short> image_depth = pose_result.getDepth(); //3 

    if( skeletons.size() == 0 )
        return;

    for( auto& skeleton : skeletons )
    {
        //4 Render lines //
        glLineWidth( 3 ); // seting pair line width
        std::array<float, 3> color = intToColor( skeleton.id() );
        glColor3f( color[0], color[1], color[2] );
        for( auto& pair : connection_pairs )
        {
            lips::Keypoint keypoint_a = skeleton[pair[0]];
            lips::Keypoint keypoint_b = skeleton[pair[1]];

            if( keypoint_a.is_valid && keypoint_b.is_valid ) //5
            {
                const float x_a = keypoint_a.point2d.x / image_depth.width();
                const float y_a = keypoint_a.point2d.y / image_depth.height();

                const float x_b = keypoint_b.point2d.x / image_depth.width();
                const float y_b = keypoint_b.point2d.y / image_depth.height();

                glBegin( GL_LINES );
                {
                    glVertex2f( x_a, y_a );
                    glVertex2f( x_b, y_b );
                }
                glEnd();
            }
        }

        //6 
        const float joint_size = 0.007;
        glColor3f( color[0] + 0.5, color[1] + 0.5, color[2] + 0.5 );
        for( auto& keypoint : skeleton )
        {
            if( keypoint.is_valid ) //7 
            {
                const float x = keypoint.point2d.x / image_depth.width(); // 0~1
                const float y = keypoint.point2d.y / image_depth.height(); // 0~1

                drawFilledCircle( x, y, joint_size );
            }
        }

        //8 
        if( !image_depth.empty() )
        {
            lips::Keypoint keypoint = skeleton[lips::Skeleton::NECK];

            if( keypoint.is_valid )
            {
                const int pixel_x = static_cast<int>( keypoint.point2d.x + 0.5 );
                const int pixel_y = static_cast<int>( keypoint.point2d.y + 0.5 );

                const float norm_x = keypoint.point2d.x / image_depth.width();
                const float norm_y = keypoint.point2d.y / image_depth.height();

                const float distance_meter = static_cast<float>( image_depth.at( pixel_x, pixel_y ) ) / 1000;

                std::stringstream text;
                text << std::setprecision( 2 ) << distance_meter << "m";

                glPushMatrix();
                {
                    glLineWidth( 3 );
                    glColor3f( color[0] + 0.8, color[1] + 0.8, color[2] + 0.8 );
                    glTranslatef( norm_x - 0.05, norm_y + 0.1, 0 );
                    glScalef( 0.0002, 0.0002, 1 );

                    gluOrtho2D( 0, 1, 1, 0 ); // flip vertically
                    glutStrokeString( GLUT_STROKE_ROMAN, reinterpret_cast<const unsigned char*>( text.str().c_str() ) );
                }
                glPopMatrix();
            }
        }
    }
}
SegmentsFunctions
1get PoseResult single instance.
2get 3D skeleton data.
3get depth map.
4Render lines.
5Verify if these keypoints are both available.
6Render parts.
7Verify if this particular keypoint is available.
8Render neck distance.

10. Draws a filled circle at position (x, y) with the specified radius.

void drawFilledCircle( const float x, const float y, const float radius )
{
    const int triangle_amount = 10; //# of triangles used to draw circle

    const float twice_pi = static_cast<float>( M_PI * 2 );
    glBegin( GL_TRIANGLE_FAN );
    glVertex2f( x, y ); // center of circle
    for( int i = 0; i <= triangle_amount; i++ )
    {
        glVertex2f(
            x + ( radius * cos( i * twice_pi / triangle_amount ) ),
            y + ( radius * sin( i * twice_pi / triangle_amount ) )
        );
    }
    glEnd();
}

11. End namespace renderColor.

} // namespace renderColor

[render3D.cpp] – Turorial #

1. Include header files to access the functions the header files provide.

#if (defined _WIN64 || defined _WIN32)
#include <windows.h> // must be included b4 other header
#endif

#include "PoseResult.h"
#include "render3D.h"
#include "intToColor.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/freeglut.h>
#include <sstream>
#include <iomanip>
#include <array>

2. Create a namespace block called render3D.

namespace render3D
{

3. Define the keypoints (joints) that should be connected with the lines to form the stick-like skeletal figure.

const std::vector<std::array<enum lips::Skeleton::Part, 2>> connection_pairs =
{
    // Face
    { lips::Skeleton::R_EYE, lips::Skeleton::R_EAR },
    { lips::Skeleton::L_EYE, lips::Skeleton::L_EAR },
    { lips::Skeleton::R_EYE, lips::Skeleton::NOSE },
    { lips::Skeleton::L_EYE, lips::Skeleton::NOSE },
    { lips::Skeleton::R_EYE, lips::Skeleton::HEAD },
    { lips::Skeleton::L_EYE, lips::Skeleton::HEAD },
    // Body
    { lips::Skeleton::NOSE, lips::Skeleton::NECK },
    { lips::Skeleton::NECK, lips::Skeleton::R_CLAVICLE },
    { lips::Skeleton::R_CLAVICLE, lips::Skeleton::R_SHOULDER },
    { lips::Skeleton::NECK, lips::Skeleton::L_CLAVICLE },
    { lips::Skeleton::L_CLAVICLE, lips::Skeleton::L_SHOULDER },
    { lips::Skeleton::R_SHOULDER, lips::Skeleton::R_HIP },
    { lips::Skeleton::L_SHOULDER, lips::Skeleton::L_HIP },
    { lips::Skeleton::L_HIP, lips::Skeleton::PELVIS },
    { lips::Skeleton::PELVIS, lips::Skeleton::R_HIP },
    { lips::Skeleton::NECK, lips::Skeleton::CHEST_SPINE },
    { lips::Skeleton::CHEST_SPINE, lips::Skeleton::NAVEL_SPINE },
    { lips::Skeleton::NAVEL_SPINE, lips::Skeleton::PELVIS },

    // Right arm
    { lips::Skeleton::R_SHOULDER, lips::Skeleton::R_ELBOW },
    { lips::Skeleton::R_ELBOW, lips::Skeleton::R_WRIST },
    { lips::Skeleton::R_WRIST, lips::Skeleton::R_HAND },
    { lips::Skeleton::R_HAND, lips::Skeleton::R_THUMB_TIP },
    { lips::Skeleton::R_HAND, lips::Skeleton::R_HAND_TIP },
    // Left arm
    { lips::Skeleton::L_SHOULDER, lips::Skeleton::L_ELBOW },
    { lips::Skeleton::L_ELBOW, lips::Skeleton::L_WRIST },
    { lips::Skeleton::L_WRIST, lips::Skeleton::L_HAND },
    { lips::Skeleton::L_HAND, lips::Skeleton::L_THUMB_TIP },
    { lips::Skeleton::L_HAND, lips::Skeleton::L_HAND_TIP },
    // Right leg
    { lips::Skeleton::R_HIP, lips::Skeleton::R_KNEE },
    { lips::Skeleton::R_KNEE, lips::Skeleton::R_ANKLE },
    { lips::Skeleton::R_ANKLE, lips::Skeleton::R_FOOT },
    // Left leg
    { lips::Skeleton::L_HIP, lips::Skeleton::L_KNEE },
    { lips::Skeleton::L_KNEE, lips::Skeleton::L_ANKLE },
    { lips::Skeleton::L_ANKLE, lips::Skeleton::L_FOOT }
};

4.Set the position of the camera (i.e. the eye point) in 3D space.

const float eye[]  = { 0,  5.0, -10 }; // eye position
const float look[] = { 0,  2.0,  10 }; // look at this position
const float up[]   = { 0, -1.0,   0 }; // camera up vecter
const float frustum[] = { -0.5,  // left,
                          0.5,   // right,
                          0.5,   // bottom,
                          -0.5,  // top,
                          1.0,   // near,
                          1000 // far
                        };

5.This function is part of an OpenGL rendering pipeline, responsible for drawing the current frame on the screen.

void display()
{
    glClearColor( 0, 0, 0, 1 ); // background is black
    glClear( GL_COLOR_BUFFER_BIT ); // clear the window

    glLoadIdentity();
    gluLookAt( eye[0],  eye[1],  eye[2],
               look[0], look[1], look[2],
               up[0],   up[1],   up[2] );

    renderFloor();
    renderPose();
    renderAxis();
    renderProfileInfo();

    glutSwapBuffers(); // swap buffers
}

6.The reshape() function is a callback triggered when the OpenGL window is resized. It redefines how the scene is projected onto the screen.

void reshape( int width, int height )
{
    glViewport( 0, 0, width, height );
    glMatrixMode( GL_PROJECTION );
    glLoadIdentity();
    glFrustum( frustum[0], // left,
               frustum[1], // right,
               frustum[2], // bottom,
               frustum[3], // top,
               frustum[4], // near,
               frustum[5]  // far
             );
    glMatrixMode( GL_MODELVIEW );
}

7.This function visually represents the 3D world axes (X, Y, Z) using colored lines. This helps users understand orientation and direction in 3D space.

oid renderAxis()
{
    glPushMatrix();
    {
        glLineWidth( 3 );

        // X Axis, red
        glColor3f( 1, 0, 0 );
        glBegin( GL_LINES );
        {
            glVertex3f( 0, 0, 0 );
            glVertex3f( 1, 0, 0 );
        }
        glEnd();

        // Y Axis, green
        glColor3f( 0, 1, 0 );
        glBegin( GL_LINES );
        {
            glVertex3f( 0, 0, 0 );
            glVertex3f( 0, 1, 0 );
        }
        glEnd();

        // Z Axis, blue
        glColor3f( 0, 0, 1 );
        glBegin( GL_LINES );
        {
            glVertex3f( 0, 0, 0 );
            glVertex3f( 0, 0, 1 );
        }
        glEnd();
    }
    glPopMatrix();
}

8. This function visually represents the virtual 3D spaces.

void renderFloor()
{
    glColor3f( 0.2, 0.2, 0.2 );
    glLineWidth( 1 );
    glBegin( GL_LINES );
    for( float x = -25; x <= 25; x++ )
    {
        // Vertical lines
        glVertex3f( x, 0,  25 );
        glVertex3f( x, 0, -25 );
        // Horizontal lines
        glVertex3f( 25, 0, x );
        glVertex3f( -25, 0, x );
    }
    glEnd();
}

9.This function renders a grid floor (like a ground plane) to give a spatial reference in the 3D scene. Think of it like graph paper stretched across the XZ-plane (horizontal ground).

void renderPose()
{
    PoseResult& pose_result = PoseResult::Instance(); // get PoseResult single instance
    std::vector<lips::Skeleton> skeletons = pose_result.getSkeletons(); // get 3D skeleton data

    if( skeletons.size() == 0 )
        return;

    const float coord_factor = 0.005;

    for( auto& skeleton : skeletons )
    {
        // Render lines
        glLineWidth( 8 ); // seting pair line width

        std::array<float, 3> color = intToColor( skeleton.id() );
        glColor3f( color[0], color[1], color[2] );
        for( auto& pair : connection_pairs )
        {
            lips::Keypoint keypoint_a = skeleton[pair[0]];
            lips::Keypoint keypoint_b = skeleton[pair[1]];

            if( keypoint_a.is_valid  && keypoint_b.is_valid ) // if these keypoints are both available
            {
                const float x_a = keypoint_a.point3d.x * coord_factor;
                const float y_a = keypoint_a.point3d.y * coord_factor;
                const float z_a = keypoint_a.point3d.z * coord_factor;

                const float x_b = keypoint_b.point3d.x * coord_factor;
                const float y_b = keypoint_b.point3d.y * coord_factor;
                const float z_b = keypoint_b.point3d.z * coord_factor;

                glBegin( GL_LINES );
                {
                    glVertex3f( x_a, y_a, z_a );
                    glVertex3f( x_b, y_b, z_b );
                }
                glEnd();
            }
        }

        // Render parts
        glColor3f( color[0] + 0.5, color[1] + 0.5, color[2] + 0.5 );
        for( auto& keypoint : skeleton )
        {
            float x = keypoint.point3d.x; // world coordinate: millimeter
            float y = keypoint.point3d.y; // world coordinate: millimeter
            float z = keypoint.point3d.z; // world coordinate: millimeter

            x *= coord_factor; // downscale for rendering
            y *= coord_factor; // downscale for rendering
            z *= coord_factor; // downscale for rendering

            if( keypoint.is_valid ) // if this keypoint is available
            {
                glMatrixMode( GL_MODELVIEW );
                glPushMatrix();
                {
                    glTranslatef( x, y, z );
                    glutSolidSphere( 0.15, 10, 10 ); // glutSolidSpere draw at (0,0,0), so translate to target position and draw
                }
                glPopMatrix();
            }
        }
    }
}

10.This function displays real-time performance info (like FPS and latency) as text on the OpenGL window — typically drawn in the bottom-left corner.

void renderProfileInfo()
{
    PoseResult& pose_result = PoseResult::Instance(); // get PoseResult single instance
    lips::ProfileInfo profile_info = pose_result.getProfileInfo(); // get statistic data

    std::stringstream text;
    text << "FPS: " << std::setprecision( 4 ) << profile_info.fps << " | ";
    text << "LATENCY: " << std::setprecision( 4 ) << profile_info.latency_ms << "ms";

    glMatrixMode( GL_MODELVIEW );
    glPushMatrix();
    glMatrixMode( GL_PROJECTION );
    glPushMatrix();
    {
        glMatrixMode( GL_PROJECTION );
        glLoadIdentity();
        gluOrtho2D( 0, 1, 0, 1 );

        glMatrixMode( GL_MODELVIEW );
        glLoadIdentity();
        glLineWidth( 2 );
        glColor3f( 1, 1, 1 );
        glTranslatef( 0.02, 0.02, 0 );
        glScalef( 0.00035, 0.00035, 1 );

        glutStrokeString( GLUT_STROKE_ROMAN, reinterpret_cast<const unsigned char*>( text.str().c_str() ) );
    }
    glMatrixMode( GL_PROJECTION );
    glPopMatrix();
    glMatrixMode( GL_MODELVIEW );
    glPopMatrix();
}

11. End namespace render3D.

} // namespace render3D

Expected output #

Expected output1 - LIPS® Developer Documentation

Updated on 2025 年 9 月 4 日
LIPSense Body Pose
C++skeleton_opencv
Table of Contents
  • Overview
  • Tutorial
    • [main.cpp] - Tutorial
    • [GLUTWindow.cpp] - Tutorial
    • [PoseResults.cpp] - Tutorial
    • [RenderColor.cpp] - Tutorial
    • [render3D.cpp] - Turorial
  • Expected output

#LIPS

About Us

Store

Blog

Terms of Use

DEVELOPER

Technical Support

SDK Download

Code Samples

Github

© 2025 LIPS Corporation