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 #
File | Function |
---|---|
main.cpp | The main process. It initializes the whole process, opens an OpenGL window and refreshes the whole process when necessary. |
GLUTWindow.cpp | Build and refresh the windows. |
PoseResult.cpp | Stores skeleton data (image / depth / skeleton) |
renderColor.cpp | Renders color images (2D Overlay) |
rednder3D.cpp | Renders 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;
}
Section | Function |
---|---|
1 | Stores a pointer to the function that initializes OpenGL state, buffers, etc |
2 | Function that is called in every frame to draw the scene. |
3 | Function 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
}
Segment | Function |
---|---|
1 | Background is black. |
2 | Clear the window. |
3 | get PoseResult single instance |
4 | get color image. |
5 | Set image to OpenGL texture. |
6 | Create or update. |
7 | |
8 | Draws the skeleton lines and keypoints. |
9 | Swap 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();
}
}
}
}
Segments | Functions |
---|---|
1 | get PoseResult single instance. |
2 | get 3D skeleton data. |
3 | get depth map. |
4 | Render lines. |
5 | Verify if these keypoints are both available. |
6 | Render parts. |
7 | Verify if this particular keypoint is available. |
8 | Render 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 #
