利用kinect2结构光相机进行运动动作识别和运动计数,不仅可以测量运动人员的卡路里,也可以测出运动速度和做功,并对于运动的动作做出科学规范的指导。
这里我们选用kinect2和windows系统作为开发工具来进行开发。如果要进行运动动作的识别,第一步需要对于采集运动的信息,比如人体的骨骼关键点信息,这里我们以手为采集对象,同时判断手是否有抓握杆的行为来进行运动的信息采集。同时我们这里以深蹲作为示范,举例如何进行运动动作的判定和计数。
人体关键点的效果如图所示
通过kinect2提供的接口,我们可以获取到人体各个骨骼关节点的三维信息
这里我把windows下获取人体骨骼关节点的信息的代码放在这里
//这里是.h文件
#ifndef __APP__
#define __APP__
#include <Windows.h>
#include <Kinect.h>
#include <opencv2/opencv.hpp>
#include <vector>
#include <array>
#include <wrl/client.h>
using namespace Microsoft::WRL;
class Kinect
{
private:
// Sensor
ComPtr<IKinectSensor> kinect;
// Coordinate Mapper
ComPtr<ICoordinateMapper> coordinateMapper;
// Reader
ComPtr<IColorFrameReader> colorFrameReader;
ComPtr<IBodyFrameReader> bodyFrameReader;
// Color Buffer
std::vector<BYTE> colorBuffer;
int colorWidth;
int colorHeight;
unsigned int colorBytesPerPixel;
cv::Mat colorMat;
// Body Buffer
std::array<IBody*, BODY_COUNT> bodies = { nullptr };
std::array<cv::Vec3b, BODY_COUNT> colors;
public:
// Constructor
Kinect();
// Destructor
~Kinect();
// Processing
void run();
private:
// Initialize
void initialize();
// Initialize Sensor
inline void initializeSensor();
// Initialize Color
inline void initializeColor();
// Initialize Body
inline void initializeBody();
// Finalize
void finalize();
// Update Data
void update();
// Update Color
inline void updateColor();
// Update Body
inline void updateBody();
// Draw Data
void draw();
// Draw Color
inline void drawColor();
// Draw Body
inline void drawBody();
// Draw Circle
inline void drawEllipse( cv::Mat& image, const Joint& joint, const int radius, const cv::Vec3b& color, const int thickness = -1 );
// Draw Hand State
inline void drawHandState( cv::Mat& image, const Joint& joint, HandState handState, TrackingConfidence handConfidence );
// Show Data
void show();
// Show Body
inline void showBody();
};
#endif // __APP__
//这里是cpp文件
#include "app.h"
#include "util.h"
#include <thread>
#include <chrono>
#include <ppl.h>
// Constructor
Kinect::Kinect()
{
// Initialize
initialize();
}
// Destructor
Kinect::~Kinect()
{
// Finalize
finalize();
}
// Processing
void Kinect::run()
{
// Main Loop
while( true ){
// Update Data
update();
// Draw Data
draw();
// Show Data
show();
// Key Check
const int key = cv::waitKey( 10 );
if( key == VK_ESCAPE ){
break;
}
}
}
// Initialize
void Kinect::initialize()
{
cv::setUseOptimized( true );
// Initialize Sensor
initializeSensor();
// Initialize Color
initializeColor();
// Initialize Body
initializeBody();
// Wait a Few Seconds until begins to Retrieve Data from Sensor ( about 2000-[ms] )
std::this_thread::sleep_for( std::chrono::seconds( 2 ) );
}
// Initialize Sensor
inline void Kinect::initializeSensor()
{
// Open Sensor
ERROR_CHECK( GetDefaultKinectSensor( &kinect ) );
ERROR_CHECK( kinect->Open() );
// Check Open
BOOLEAN isOpen = FALSE;
ERROR_CHECK( kinect->get_IsOpen( &isOpen ) );
if( !isOpen ){
throw std::runtime_error( "failed IKinectSensor::get_IsOpen( &isOpen )" );
}
// Retrieve Coordinate Mapper
ERROR_CHECK( kinect->get_CoordinateMapper( &coordinateMapper ) );
}
// Initialize Color
inline void Kinect::initializeColor()
{
// Open Color Reader
ComPtr<IColorFrameSource> colorFrameSource;
ERROR_CHECK( kinect->get_ColorFrameSource( &colorFrameSource ) );
ERROR_CHECK( colorFrameSource->OpenReader( &colorFrameReader ) );
// Retrieve Color Description
ComPtr<IFrameDescription> colorFrameDescription;
ERROR_CHECK( colorFrameSource->CreateFrameDescription( ColorImageFormat::ColorImageFormat_Bgra, &colorFrameDescription ) );
ERROR_CHECK( colorFrameDescription->get_Width( &colorWidth ) ); // 1920
ERROR_CHECK( colorFrameDescription->get_Height( &colorHeight ) ); // 1080
ERROR_CHECK( colorFrameDescription->get_BytesPerPixel( &colorBytesPerPixel ) ); // 4
// Allocation Color Buffer
colorBuffer.resize( colorWidth * colorHeight * colorBytesPerPixel );
}
// Initialize Body
inline void Kinect::initializeBody()
{
// Open Body Reader
ComPtr<IBodyFrameSource> bodyFrameSource;
ERROR_CHECK( kinect->get_BodyFrameSource( &bodyFrameSource ) );
ERROR_CHECK( bodyFrameSource->OpenReader( &bodyFrameReader ) );
// Initialize Body Buffer
Concurrency::parallel_for_each( bodies.begin(), bodies.end(), []( IBody*& body ){
SafeRelease( body );
} );
// Color Table for Visualization
colors[0] = cv::Vec3b( 255, 0, 0 ); // Blue
colors[1] = cv::Vec3b( 0, 255, 0 ); // Green
colors[2] = cv::Vec3b( 0, 0, 255 ); // Red
colors[3] = cv::Vec3b( 255, 255, 0 ); // Cyan
colors[4] = cv::Vec3b( 255, 0, 255 ); // Magenta
colors[5] = cv::Vec3b( 0, 255, 255 ); // Yellow
}
// Finalize
void Kinect::finalize()
{
cv::destroyAllWindows();
// Release Body Buffer
Concurrency::parallel_for_each( bodies.begin(), bodies.end(), []( IBody*& body ){
SafeRelease( body );
} );
// Close Sensor
if( kinect != nullptr ){
kinect->Close();
}
}
// Update Data
void Kinect::update()
{
// Update Color
updateColor();
// Update Body
updateBody();
}
// Update Color
inline void Kinect::updateColor()
{
// Retrieve Color Frame
ComPtr<IColorFrame> colorFrame;
const HRESULT ret = colorFrameReader->AcquireLatestFrame( &colorFrame );
if( FAILED( ret ) ){
return;
}
// Convert Format ( YUY2 -> BGRA )
ERROR_CHECK( colorFrame->CopyConvertedFrameDataToArray( static_cast<UINT>( colorBuffer.size() ), &colorBuffer[0], ColorImageFormat::ColorImageFormat_Bgra ) );
}
// Update Body
inline void Kinect::updateBody()
{
// Retrieve Body Frame
ComPtr<IBodyFrame> bodyFrame;
const HRESULT ret = bodyFrameReader->AcquireLatestFrame( &bodyFrame );
if( FAILED( ret ) ){
return;
}
// Release Previous Bodies
Concurrency::parallel_for_each( bodies.begin(), bodies.end(), []( IBody*& body ){
SafeRelease( body );
} );
// Retrieve Body Data
ERROR_CHECK( bodyFrame->GetAndRefreshBodyData( static_cast<UINT>( bodies.size() ), &bodies[0] ) );
}
// Draw Datax
void Kinect::draw()
{
// Draw Color
drawColor();
// Draw Body
drawBody();
}
// Draw Color
inline void Kinect::drawColor()
{
// Create cv::Mat from Color Buffer
colorMat = cv::Mat( colorHeight, colorWidth, CV_8UC4, &colorBuffer[0] );
}
// Draw Body
inline void Kinect::drawBody()
{
// Draw Body Data to Color Data
Concurrency::parallel_for( 0, BODY_COUNT, [&]( const int count ){
const ComPtr<IBody> body = bodies[count];
if( body == nullptr ){
return;
}
// Check Body Tracked
BOOLEAN tracked = FALSE;
ERROR_CHECK( body->get_IsTracked( &tracked ) );
if( !tracked ){
return;
}
// Retrieve Joints
std::array<Joint, JointType::JointType_Count> joints;
ERROR_CHECK( body->GetJoints( static_cast<UINT>( joints.size() ), &joints[0] ) );
Concurrency::parallel_for_each( joints.begin(), joints.end(), [&]( const Joint& joint ){
// Check Joint Tracked
if( joint.TrackingState == TrackingState::TrackingState_NotTracked ){
return;
}
// Draw Joint Position
drawEllipse( colorMat, joint, 5, colors[count] );
// Draw Left Hand State
if( joint.JointType == JointType::JointType_HandLeft ){
HandState handState;
TrackingConfidence handConfidence;
ERROR_CHECK( body->get_HandLeftState( &handState ) );
ERROR_CHECK( body->get_HandLeftConfidence( &handConfidence ) );
drawHandState( colorMat, joint, handState, handConfidence );
}
// Draw Right Hand State
if( joint.JointType == JointType::JointType_HandRight ){
HandState handState;
TrackingConfidence handConfidence;
ERROR_CHECK( body->get_HandRightState( &handState ) );
ERROR_CHECK( body->get_HandRightConfidence( &handConfidence ) );
drawHandState( colorMat, joint, handState, handConfidence );
}
} );
/*
// Retrieve Joint Orientations
std::array<JointOrientation, JointType::JointType_Count> orientations;
ERROR_CHECK( body->GetJointOrientations( JointType::JointType_Count, &orientations[0] ) );
*/
/*
// Retrieve Amount of Body Lean
PointF amount;
ERROR_CHECK( body->get_Lean( &amount ) );
*/
} );
}
// Draw Ellipse
inline void Kinect::drawEllipse( cv::Mat& image, const Joint& joint, const int radius, const cv::Vec3b& color, const int thickness )
{
if( image.empty() ){
return;
}
// Convert Coordinate System and Draw Joint
ColorSpacePoint colorSpacePoint;
ERROR_CHECK( coordinateMapper->MapCameraPointToColorSpace( joint.Position, &colorSpacePoint ) );
const int x = static_cast<int>( colorSpacePoint.X + 0.5f );
const int y = static_cast<int>( colorSpacePoint.Y + 0.5f );
if( ( 0 <= x ) && ( x < image.cols ) && ( 0 <= y ) && ( y < image.rows ) ){
cv::circle( image, cv::Point( x, y ), radius, static_cast<cv::Scalar>( color ), thickness, cv::LINE_AA );
}
}
// Draw Hand State
inline void Kinect::drawHandState( cv::Mat& image, const Joint& joint, HandState handState, TrackingConfidence handConfidence )
{
if( image.empty() ){
return;
}
// Check Tracking Confidence
if( handConfidence != TrackingConfidence::TrackingConfidence_High ){
return;
}
// Draw Hand State
const int radius = 75;
const cv::Vec3b blue = cv::Vec3b( 128, 0, 0 ), green = cv::Vec3b( 0, 128, 0 ), red = cv::Vec3b( 0, 0, 128 );
switch( handState ){
// Open
case HandState::HandState_Open:
drawEllipse( image, joint, radius, green, 5 );
break;
// Close
case HandState::HandState_Closed:
drawEllipse( image, joint, radius, red, 5 );
break;
// Lasso
case HandState::HandState_Lasso:
drawEllipse( image, joint, radius, blue, 5 );
break;
default:
break;
}
}
// Show Data
void Kinect::show()
{
// Show Body
showBody();
}
// Show Body
inline void Kinect::showBody()
{
if( colorMat.empty() ){
return;
}
// Resize Image
cv::Mat resizeMat;
const double scale = 0.5;
cv::resize( colorMat, resizeMat, cv::Size(), scale, scale );
// Show Image
cv::imshow( "Body", resizeMat );
}
//main函数
#include <iostream>
#include <sstream>
#include "app.h"
int main( int argc, char* argv[] )
{
try{
Kinect kinect;
kinect.run();
} catch( std::exception& ex ){
std::cout << ex.what() << std::endl;
}
return 0;
}
//util.h文件
#ifndef __UTIL__
#define __UTIL__
#include <sstream>
#include <stdexcept>
// Error Check Macro
#define ERROR_CHECK( ret ) \
if( FAILED( ret ) ){ \
std::stringstream ss; \
ss << "failed " #ret " " << std::hex << ret << std::endl; \
throw std::runtime_error( ss.str().c_str() ); \
}
// Safe Release
template<class T>
inline void SafeRelease( T*& rel )
{
if( rel != NULL ){
rel->Release();
rel = NULL;
}
}
// C++ Style Line Types For OpenCV 2.x
#if ( CV_MAJOR_VERSION < 3 )
namespace cv{
enum LineTypes{
FILLED = -1,
LINE_4 = 4,
LINE_8 = 8,
LINE_AA = 16
};
}
#endif
#endif // __UTIL__
下面我们就以这两份开源代码作为基础的开发版本,演示如何开发智能运动计数和动作识别的代码
首先,我们需要训练一个极快速度的识别模型,用于识别手是否有握杆的行为,这里我们选用caffe版本的squeezeNet作为训练的模型,具体教程网上很多,我们这里就用猫狗分类的模型,具体的地址在这里
超轻量级模型地址
使用这个模型在鉴黄的图片上速度能够达到200fps以上。利用kinect2的手部信息,对于彩色图像进行一定范围的截图,自己采集一些握杆和不握杆的图片,数量大概在6000张左右,正负样本各3000张。整合完成后识别率大概是98%左右。
通过采集kinect2的手部骨骼关键点信息,同时利用手是否握杆来判定,就可以进行数据的处理了。
需要对于采集到数据进行深度值回归和综合滤波以后,我们把处理过的数据放在json文件中留用,再利用python代码将数据展示出来,如图所示
这里是我们采集到的20组深蹲的手部骨骼关键点的坐标信息
接下来就是如何计数的问题了,这里我们采用一阶导数和二阶导数相结合的方式,同时利用三次样条插值补足缺失的移动轨迹来进行计数。具体效果如图所示。
在这里插入图片描述
这样子我们就能对于运动动作进行捕捉了。具体效果可以参考eliteform的使用效果
视频点击
相关产品见舒华8902I速度与力量反馈系统