前言

  一般情况下,手势识别的第一步就是先手势定位,即手势所在部位的提取。本文是基于kinect来提取手势识别的,即 先通过kinect找出人体的轮廓,然后定位轮廓中与手部有关的点,在该点的周围提取出满足一定要求的区域,对该区域进行滤波后得到的区域就是手部了。然 后利用凸包和凹陷的数学几何方法,画出手指和手指凹陷处的点,以及手的轮廓线,并在图像中显示出来。文章所有代码都是网友Robert Walter提供的,它的代码下载网站为:http://dl.dropbox.com/u/5505209/FingertipTuio3d.zip

  本人因为要做这方面的研究,所有本文只是读了他的代码,并稍加分析了下。

  开发环境:OpenNI+OpenCV

 实验说明

  手势定位和提取功能的实现主要是依靠OpenNI和OpenCV的实现,定位部分依靠OpenNI的人体骨架跟踪功能,手部的提取依靠OpenCV中一些与轮廓有关的函数。整个实验的流程图如下:

  

  手部提取时用到的几个OpenCV函数解释如下:

  void convexHull(InputArray points, OutputArray hull, bool clockwise=false, bool returnPoints=true )

  该函数的作用是找到输入点集points的凸包集合,参数1为输入的点集;参数2为是与输出凸包集合有关的,它是一 个向量,如果向量元素的类型为整型,则表示其为凸包集合的点在原始输入集合点的下标索引,如果向量的数据类型为Point型,http://qun.81nanchang.cn 则表示其为凸包的点集;参数 3表示输出凸包集合的方向,为true时表示顺时针方向输出;参数4表示是否输出凸包的集合中的点坐标,这个只有在参数2的类型为Mat型的时候有效,如 果参数2为vector类型,则根据vector中元素类型来选择输出凸包到底是点的坐标还是原始输入点的索引,也就是说此时的参数4没有作用。

  void convexityDefects(InputArray contour, InputArray convexhull, OutputArray convexityDefects)

  该函数的作用是对输入的轮廓contour,凸包集合来检测其轮廓的凸型缺陷,一个凸型缺陷结构体包括4个元素,缺陷起点坐标,缺陷终点坐标,缺陷中离凸包线距离最远的点的坐标,以及此时最远的距离。参数3即其输出的凸型缺陷结构体向量。

  其凸型缺陷的示意图如下所示:

  

  void findContours(InputOutputArray image, OutputArrayOfArrays contours, OutputArray hierarchy, int mode, int method, Point offset=Point()

  该函数是找到输入图像image的轮廓,存储在contours中。输入的图像类型必须要求是8位单通道的二值图 像,只要是非0元素都被看成是1,只要是0元素就被看做是0;参数hierarchy存储的是每个轮廓的拓扑信息,参数method表示轮廓提取的模式;http://tx.qqlove789.com  参数4表示轮廓提取的近似方法,即怎么保存轮廓信息;参数5表示轮廓可移动的位移。

  void *memcpy(void *dest, const void *src, size_t n);

  该函数的作用是从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中,主要不要把源地址和目的地址的顺序搞反了。

  实验结果

  在进行手部的提取时,因为要先提取出人体全身的骨骼,再定位手的坐标,所以人必须先处于站立状态,一旦人体骨骼提取成功后,可以改为坐立姿态,其手部提取并显示的一张图如下所示:

  

  网友Robert Walter把它的演示视频放在这里: http://www.youtube.com/watch?v=lCuItHQEgEQ&feature=player_embedded

  实验主要部分代码和注释(参考资料中有工程代码下载地址):

main.cpp:

#include "SkeletonSensor.h"

// openCV

#include <opencv/highgui.h>

#include <opencv/cv.h>

using namespace cv;

#include <iostream>

using namespace std;

// globals

SkeletonSensor* sensor;

const unsigned int XRES = 640;

const unsigned int YRES = 480;

const float DEPTH_SCALE_FACTOR = 255./4096.;

// defines the value about which thresholding occurs

const unsigned int BIN_THRESH_OFFSET = 5;

// defines the value about witch the region of interest is extracted

const unsigned int ROI_OFFSET = 70;

// median blur factor

const unsigned int MEDIAN_BLUR_K = 5;

// grasping threshold

const double GRASPING_THRESH = 0.9;

// colors

const Scalar COLOR_BLUE        = Scalar(240,40,0);

const Scalar COLOR_DARK_GREEN  = Scalar(0, 128, 0);

const Scalar COLOR_LIGHT_GREEN = Scalar(0,255,0);

const Scalar COLOR_YELLOW      = Scalar(0,128,200);

const Scalar COLOR_RED         = Scalar(0,0,255);

// returns true if the hand is near the sensor area

bool handApproachingDisplayPerimeter(float x, float y)

{

return (x > (XRES - ROI_OFFSET)) || (x < (ROI_OFFSET)) ||

(y > (YRES - ROI_OFFSET)) || (y < (ROI_OFFSET));

}

// conversion from cvConvexityDefect

struct ConvexityDefect

{

Point start;

Point end;

Point depth_point;

float depth;

};

// Thanks to Jose Manuel Cabrera for part of this C++ wrapper function

//Convexity為凸的意思,Defect為缺陷的意思,hull為殼的意思

//貌似這個函數在opencv中已經被實現了

void findConvexityDefects(vector<Point>& contour, vector<int>& hull, vector<ConvexityDefect>& convexDefects)

{

if(hull.size() > 0 && contour.size() > 0)

{

CvSeq* contourPoints;

CvSeq* defects;

CvMemStorage* storage;

CvMemStorage* strDefects;

CvMemStorage* contourStr;

CvConvexityDefect *defectArray = 0;

strDefects = cvCreateMemStorage();

defects = cvCreateSeq( CV_SEQ_KIND_GENERIC|CV_32SC2, sizeof(CvSeq),sizeof(CvPoint), strDefects );

//We transform our vector<Point> into a CvSeq* object of CvPoint.

contourStr = cvCreateMemStorage();

contourPoints = cvCreateSeq(CV_SEQ_KIND_GENERIC|CV_32SC2, sizeof(CvSeq), sizeof(CvPoint), contourStr);

for(int i = 0; i < (int)contour.size(); i++) {

CvPoint cp = {contour[i].x,  contour[i].y};

cvSeqPush(contourPoints, &cp);

}

//Now, we do the same thing with the hull index

int count = (int) hull.size();

//int hullK[count];

int* hullK = (int*) malloc(count*sizeof(int));

for(int i = 0; i < count; i++) { hullK[i] = hull.at(i); }

CvMat hullMat = cvMat(1, count, CV_32SC1, hullK);

// calculate convexity defects

storage = cvCreateMemStorage(0);

defects = cvConvexityDefects(contourPoints, &hullMat, storage);

defectArray = (CvConvexityDefect*)malloc(sizeof(CvConvexityDefect)*defects->total);

cvCvtSeqToArray(defects, defectArray, CV_WHOLE_SEQ);

//printf("DefectArray %i %i\n",defectArray->end->x, defectArray->end->y);

//We store defects points in the convexDefects parameter.

for(int i = 0; i<defects->total; i++){

ConvexityDefect def;

def.start = Point(defectArray[i].start->x, defectArray[i].start->y);

def.end = Point(defectArray[i].end->x, defectArray[i].end->y);

def.depth_point = Point(defectArray[i].depth_point->x, defectArray[i].depth_point->y);

def.depth = defectArray[i].depth;

convexDefects.push_back(def);

}

// release memory

cvReleaseMemStorage(&contourStr);

cvReleaseMemStorage(&strDefects);

cvReleaseMemStorage(&storage);

}

}

int main(int argc, char** argv)

{

// initialize the kinect

sensor = new SkeletonSensor();

sensor->initialize();

sensor->setPointModeToProjective();

Mat depthRaw(YRES, XRES, CV_16UC1);

Mat depthShow(YRES, XRES, CV_8UC1);

Mat handDebug;

// this vector holds the displayed images of the hands

vector<Mat> debugFrames;

// rectangle used to extract hand regions from depth map

Rect roi;

roi.width = ROI_OFFSET*2;

roi.height = ROI_OFFSET*2;

namedWindow("depthFrame", CV_WINDOW_AUTOSIZE);

namedWindow("leftHandFrame", CV_WINDOW_AUTOSIZE);

namedWindow("rightHandFrame", CV_WINDOW_AUTOSIZE);

int key = 0;

while(key != 27 && key != 'q')

{

sensor->waitForDeviceUpdateOnUser();

// update 16 bit depth matrix

//參數3後面乘以2是因為kinect獲得的深度數據是1個像素2字節的

memcpy(depthRaw.data, sensor->getDepthData(), XRES*YRES*2);

//轉換成8位深度圖

depthRaw.convertTo(depthShow, CV_8U, DEPTH_SCALE_FACTOR);

for(int handI = 0; handI < 2; handI++)

{

int handDepth;

if(sensor->getNumTrackedUsers() > 0)

{

//Skeleton是包含15個人體骨骼節點的結構體

Skeleton skel =  sensor->getSkeleton(sensor->getUID(0));

//struct SkeletonPoint

//{

//   float x, y, z, confidence;

//};

SkeletonPoint hand;

if( handI == 0)

hand = skel.leftHand;//hand中保存左手點的座標

else

hand = skel.rightHand;//hand中保存有手點的座標

if(hand.confidence == 1.0)//手部的置信度为1

{

handDepth = hand.z * (DEPTH_SCALE_FACTOR);//轉換為8bit后的深度值

//handApproachingDisplayPerimeter返回為真是說明,手的位置已經越界

if(!handApproachingDisplayPerimeter(hand.x, hand.y))

{

roi.x = hand.x - ROI_OFFSET;    //截取出感興趣的區域roi,其區域大小由經驗值來設定

roi.y = hand.y - ROI_OFFSET;

}

}

}

else

handDepth = -1;

// extract hand from image

Mat handCpy(depthShow, roi);    //handCpy只是與depthShow共用數據內存而已

Mat handMat = handCpy.clone();    //真正的擁有自己的內存區域

// binary threshold

if(handDepth != -1)

//BIN_THRESH_OFFSET == 5;

//手部的閾值化,檢測到手部后,根據手部前後的深度信息來提取手的輪廓,此時,handMat就是0和1的矩陣了

handMat = (handMat > (handDepth - BIN_THRESH_OFFSET)) & (handMat < (handDepth + BIN_THRESH_OFFSET));

// last pre-filtering step, apply median blur

medianBlur(handMat, handMat, MEDIAN_BLUR_K);

// create debug image of thresholded hand and cvt to RGB so hints show in color

handDebug = handMat.clone();

debugFrames.push_back(handDebug);

//CV_GRAY2RGB表示3个通道的值是一样的

cvtColor(debugFrames[handI], debugFrames[handI], CV_GRAY2RGB);

std::vector< std::vector<Point> > contours;

//提取全部轮廓信息到contours,轮廓信息采用水平,垂直对角线的末断点存储。

findContours(handMat, contours, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);

//下面就是画多边形和点

if (contours.size()) {

for (int i = 0; i < contours.size(); i++) {

vector<Point> contour = contours[i];

//将vector转换成Mat型,此时的Mat还是列向量,只不过是2个通道的列向量而已

Mat contourMat = Mat(contour);

//返回轮廓的面积

double cArea = contourArea(contourMat);

if(cArea > 2000) // likely the hand

{

//找到轮廓的中心点

Scalar center = mean(contourMat);

Point centerPoint = Point(center.val[0], center.val[1]);

// approximate the contour by a simple curve

vector<Point> approxCurve;

//求出轮廓的封闭的曲线,保存在approxCurve,轮廓和封闭曲线直接的最大距离为10

approxPolyDP(contourMat, approxCurve, 10, true);

vector< vector<Point> > debugContourV;

debugContourV.push_back(approxCurve);

//在参数1中画出轮廓参数2,参数2必须是轮廓的集合,所以参数2是

//vector< vector<Point> >类型

//深绿色代表近似多边形

drawContours(debugFrames[handI], debugContourV, 0, COLOR_DARK_GREEN, 3);

vector<int> hull;

//找出近似曲线的凸包集合,集合hull中存储的是轮廓中凸包点的下标

convexHull(Mat(approxCurve), hull, false, false);

// draw the hull points

for(int j = 0; j < hull.size(); j++)

{

int index = hull[j];

//凸顶点用黄色表示

circle(debugFrames[handI], approxCurve[index], 3, COLOR_YELLOW, 2);

}

// find convexity defects

vector<ConvexityDefect> convexDefects;

findConvexityDefects(approxCurve, hull, convexDefects);

printf("Number of defects: %d.\n", (int) convexDefects.size());

for(int j = 0; j < convexDefects.size(); j++)

{

//缺陷点用蓝色表示

circle(debugFrames[handI], convexDefects[j].depth_point, 3, COLOR_BLUE, 2);

}

// assemble point set of convex hull

//将凸包集以点的坐标形式保存下来

vector<Point> hullPoints;

for(int k = 0; k < hull.size(); k++)

{

int curveIndex = hull[k];

Point p = approxCurve[curveIndex];

hullPoints.push_back(p);

}

// area of hull and curve

double hullArea  = contourArea(Mat(hullPoints));

double curveArea = contourArea(Mat(approxCurve));

double handRatio = curveArea/hullArea;

// hand is grasping

//GRASPING_THRESH == 0.9

if(handRatio > GRASPING_THRESH)

//握拳表示绿色

circle(debugFrames[handI], centerPoint, 5, COLOR_LIGHT_GREEN, 5);

else

//一般情况下手张开其中心点是显示红色

circle(debugFrames[handI], centerPoint, 5, COLOR_RED, 5);

}

} // contour conditional

} // hands loop

}

imshow("depthFrame", depthShow);

//debugFrames只保存2帧图像

if(debugFrames.size() >= 2 )

{

//长和宽的尺寸都扩大3倍

resize(debugFrames[0], debugFrames[0], Size(), 3, 3);

resize(debugFrames[1], debugFrames[1], Size(), 3, 3);

imshow("leftHandFrame",  debugFrames[0]);

imshow("rightHandFrame",  debugFrames[1]);

debugFrames.clear();

}

key = waitKey(10);

}

delete sensor;

return 0;

}

skeletonSensor.h:

#ifndef SKELETON_SENSOR_H

#define SKELETON_SENSOR_H

#include <XnCppWrapper.h>

#include <vector>

// A 3D point with the confidence of the point's location. confidence_ > 0.5 is good

struct SkeletonPoint

{

float x, y, z, confidence;

};

struct Skeleton

{

SkeletonPoint head;

SkeletonPoint neck;

SkeletonPoint rightShoulder;

SkeletonPoint leftShoulder;

SkeletonPoint rightElbow;

SkeletonPoint leftElbow;

SkeletonPoint rightHand;

SkeletonPoint leftHand;

SkeletonPoint rightHip;

SkeletonPoint leftHip;

SkeletonPoint rightKnee;

SkeletonPoint leftKnee;

SkeletonPoint rightFoot;

SkeletonPoint leftFoot;

SkeletonPoint torso;

};

// SkeletonSensor: A wrapper for OpenNI Skeleton tracking devices

//

// Requires the OpenNI + NITE framework installation and the device driver

// Tracks users within the device FOV, and assists in collection of user joints data

class SkeletonSensor

{

public:

SkeletonSensor();

~SkeletonSensor();

// set up the device resolution and data generators

int initialize();

// non-blocking wait for new data on the device

void waitForDeviceUpdateOnUser();

// update vector of tracked users

void updateTrackedUsers();

// return true if UID is among the tracked users

bool isTracking(const unsigned int uid);

// returns skeleton of specified user

Skeleton getSkeleton(const unsigned int uid);

// returns vector of skeletons for all users

std::vector<Skeleton> getSkeletons();

// get number of tracked users

unsigned int getNumTrackedUsers();

// map tracked user index to UID

unsigned int getUID(const unsigned int index);

// change point mode

void setPointModeToProjective();

void setPointModeToReal();

// get depth and image data

const XnDepthPixel* getDepthData();

const XnDepthPixel* getWritableDepthData(){};

const XnUInt8* getImageData();

const XnLabel*getLabels();

private:

xn::Context context_;

xn::DepthGenerator depthG_;

xn::UserGenerator userG_;

xn::ImageGenerator imageG_;

std::vector<unsigned int> trackedUsers_;

// current list of hands

//std::list<XnPoint3D> handCursors;

bool pointModeProjective_;

// on user detection and calibration, call specified functions

int setCalibrationPoseCallbacks();

// joint to point conversion, considers point mode

void convertXnJointsToPoints(XnSkeletonJointPosition* const j, SkeletonPoint* const p, unsigned int numPoints);

// callback functions for user and skeleton calibration events

static void XN_CALLBACK_TYPE newUserCallback(xn::UserGenerator& generator, XnUserID nId, void* pCookie);

static void XN_CALLBACK_TYPE lostUserCallback(xn::UserGenerator& generator, XnUserID nId, void* pCookie);

static void XN_CALLBACK_TYPE calibrationStartCallback(xn::SkeletonCapability& capability, XnUserID nId, void* pCookie);

static void XN_CALLBACK_TYPE calibrationCompleteCallback(xn::SkeletonCapability& capability, XnUserID nId, XnCalibrationStatus eStatus, void* pCookie);

static void XN_CALLBACK_TYPE poseDetectedCallback(xn::PoseDetectionCapability& capability, const XnChar* strPose, XnUserID nId, void* pCookie);

};

#endif

skeletonSensor.cpp:

#include "SkeletonSensor.h"

#include "log.h"

inline int CHECK_RC(const unsigned int rc, const char* const description)

{

if(rc != XN_STATUS_OK)

{

put_flog(LOG_ERROR, "%s failed: %s", description, xnGetStatusString(rc));

return -1;

}

return 0;

}

SkeletonSensor::SkeletonSensor()

{

pointModeProjective_ = false;

}

SkeletonSensor::~SkeletonSensor()

{

context_.Shutdown();

}

int SkeletonSensor::initialize()

{

context_.Init();

XnStatus rc = XN_STATUS_OK;

// create depth and user generators

rc = depthG_.Create(context_);

if(CHECK_RC(rc, "Create depth generator") == -1)

return -1;

rc = userG_.Create(context_);

if(CHECK_RC(rc, "Create user generator") == -1)

return -1;

rc = imageG_.Create(context_);

if(CHECK_RC(rc, "Create image generator") == -1)

return -1;

XnMapOutputMode mapMode;

depthG_.GetMapOutputMode(mapMode);

// for now, make output map VGA resolution at 30 FPS

mapMode.nXRes = XN_VGA_X_RES;

mapMode.nYRes = XN_VGA_Y_RES;

mapMode.nFPS = 30;

depthG_.SetMapOutputMode(mapMode);

imageG_.SetMapOutputMode(mapMode);

// turn on device mirroring

if(depthG_.IsCapabilitySupported("Mirror") == true)

{

rc = depthG_.GetMirrorCap().SetMirror(true);

CHECK_RC(rc, "Setting Image Mirroring on depthG");

}

// turn on device mirroring

if(imageG_.IsCapabilitySupported("Mirror") == true)

{

rc = imageG_.GetMirrorCap().SetMirror(true);

CHECK_RC(rc, "Setting Image Mirroring on imageG");

}

// make sure the user points are reported from the POV of the depth generator

userG_.GetAlternativeViewPointCap().SetViewPoint(depthG_);

depthG_.GetAlternativeViewPointCap().SetViewPoint(imageG_);

// set smoothing factor

userG_.GetSkeletonCap().SetSmoothing(0.9);

// start data streams

context_.StartGeneratingAll();

// setup callbacks

setCalibrationPoseCallbacks();

return 0;

}

void SkeletonSensor::waitForDeviceUpdateOnUser()

{

context_.WaitOneUpdateAll(userG_);

updateTrackedUsers();

}

void SkeletonSensor::updateTrackedUsers()

{

XnUserID users[64];

XnUInt16 nUsers = userG_.GetNumberOfUsers();

trackedUsers_.clear();

userG_.GetUsers(users, nUsers);

for(int i = 0; i < nUsers; i++)

{

if(userG_.GetSkeletonCap().IsTracking(users[i]))

{

trackedUsers_.push_back(users[i]);

}

}

}

bool SkeletonSensor::isTracking(const unsigned int uid)

{

return userG_.GetSkeletonCap().IsTracking(uid);

}

Skeleton SkeletonSensor::getSkeleton(const unsigned int uid)

{

Skeleton result;

// not tracking user

if(!userG_.GetSkeletonCap().IsTracking(uid))

return result;

// Array of available joints

const unsigned int nJoints = 15;

XnSkeletonJoint joints[nJoints] =

{   XN_SKEL_HEAD,

XN_SKEL_NECK,

XN_SKEL_RIGHT_SHOULDER,

XN_SKEL_LEFT_SHOULDER,

XN_SKEL_RIGHT_ELBOW,

XN_SKEL_LEFT_ELBOW,

XN_SKEL_RIGHT_HAND,

XN_SKEL_LEFT_HAND,

XN_SKEL_RIGHT_HIP,

XN_SKEL_LEFT_HIP,

XN_SKEL_RIGHT_KNEE,

XN_SKEL_LEFT_KNEE,

XN_SKEL_RIGHT_FOOT,

XN_SKEL_LEFT_FOOT,

XN_SKEL_TORSO

};

// holds the joint position components

XnSkeletonJointPosition positions[nJoints];

for (unsigned int i = 0; i < nJoints; i++)

{

userG_.GetSkeletonCap().GetSkeletonJointPosition(uid, joints[i], *(positions+i));

}

SkeletonPoint points[15];

convertXnJointsToPoints(positions, points, nJoints);

result.head = points[0];

result.neck = points[1];

result.rightShoulder = points[2];

result.leftShoulder = points[3];

result.rightElbow = points[4];

result.leftElbow = points[5];

result.rightHand = points[6];

result.leftHand = points[7];

result.rightHip = points[8];

result.leftHip= points[9];

result.rightKnee = points[10];

result.leftKnee = points[11];

result.rightFoot = points[12];

result.leftFoot = points[13];

result.torso = points[14];

return result;

}

std::vector<Skeleton> SkeletonSensor::getSkeletons()

{

std::vector<Skeleton> skeletons;

for(unsigned int i = 0; i < getNumTrackedUsers(); i++)

{

Skeleton s = getSkeleton(trackedUsers_[i]);

skeletons.push_back(s);

}

return skeletons;

}

unsigned int SkeletonSensor::getNumTrackedUsers()

{

return trackedUsers_.size();

}

unsigned int SkeletonSensor::getUID(const unsigned int index)

{

return trackedUsers_[index];

}

void SkeletonSensor::setPointModeToProjective()

{

pointModeProjective_ = true;

}

void SkeletonSensor::setPointModeToReal()

{

pointModeProjective_ = false;

}

const XnDepthPixel* SkeletonSensor::getDepthData()

{

return depthG_.GetDepthMap();

}

const XnUInt8* SkeletonSensor::getImageData()

{

return imageG_.GetImageMap();

}

const XnLabel* SkeletonSensor::getLabels()

{

xn::SceneMetaData sceneMD;

userG_.GetUserPixels(0, sceneMD);

return sceneMD.Data();

}

int SkeletonSensor::setCalibrationPoseCallbacks()

{

XnCallbackHandle hUserCallbacks, hCalibrationStart, hCalibrationComplete;

userG_.RegisterUserCallbacks(newUserCallback, lostUserCallback, this, hUserCallbacks);

userG_.GetSkeletonCap().RegisterToCalibrationStart(calibrationStartCallback, this, hCalibrationStart);

userG_.GetSkeletonCap().RegisterToCalibrationComplete(calibrationCompleteCallback, this, hCalibrationComplete);

// turn on tracking of all joints

userG_.GetSkeletonCap().SetSkeletonProfile(XN_SKEL_PROFILE_ALL);

return 0;

}

void SkeletonSensor::convertXnJointsToPoints(XnSkeletonJointPosition* const joints, SkeletonPoint* const points, unsigned int numPoints)

{

XnPoint3D xpt;

for(unsigned int i = 0; i < numPoints; i++)

{

xpt = joints[i].position;

if(pointModeProjective_)

depthG_.ConvertRealWorldToProjective(1, &xpt, &xpt);

points[i].confidence = joints[i].fConfidence;

points[i].x = xpt.X;

points[i].y = xpt.Y;

points[i].z = xpt.Z;

}

}

void XN_CALLBACK_TYPE SkeletonSensor::newUserCallback(xn::UserGenerator& generator, XnUserID nId, void* pCookie)

{

put_flog(LOG_DEBUG, "New user %d, auto-calibrating", nId);

SkeletonSensor* sensor = (SkeletonSensor*) pCookie;

sensor->userG_.GetSkeletonCap().RequestCalibration(nId, true);

}

void XN_CALLBACK_TYPE SkeletonSensor::lostUserCallback(xn::UserGenerator& generator, XnUserID nId, void* pCookie)

{

put_flog(LOG_DEBUG, "Lost user %d", nId);

}

void XN_CALLBACK_TYPE SkeletonSensor::calibrationStartCallback(xn::SkeletonCapability& capability, XnUserID nId, void* pCookie)

{

put_flog(LOG_DEBUG, "Calibration started for user %d", nId);

}

void XN_CALLBACK_TYPE SkeletonSensor::calibrationCompleteCallback(xn::SkeletonCapability& capability, XnUserID nId, XnCalibrationStatus eStatus, void* pCookie)

{

SkeletonSensor* sensor = (SkeletonSensor*) pCookie;

if(eStatus == XN_CALIBRATION_STATUS_OK)

{

put_flog(LOG_DEBUG, "Calibration completed: start tracking user %d", nId);

sensor->userG_.GetSkeletonCap().StartTracking(nId);

}

else

{

put_flog(LOG_DEBUG, "Calibration failed for user %d", nId);

sensor->userG_.GetSkeletonCap().RequestCalibration(nId, true);

}

}

void XN_CALLBACK_TYPE SkeletonSensor::poseDetectedCallback(xn::PoseDetectionCapability& capability, const XnChar* strPose, XnUserID nId, void* pCookie)

{

put_flog(LOG_DEBUG, "Pose detected for user %d", nId);

SkeletonSensor* sensor = (SkeletonSensor*) pCookie;

sensor->userG_.GetPoseDetectionCap().StopPoseDetection(nId);

sensor->userG_.GetSkeletonCap().RequestCalibration(nId, true);

}

  实验总结:通过本次实验,可以学会初步结合OpenCV和OpenNI来简单的提取手部所在的区域。

Kinect+OpenNI学习笔记之8(Robert-Walter手部提取代码的分析)相关推荐

  1. Kinect+OpenNI学习笔记之12(简单手势所表示的数字的识别)

    引自:http://www.cnblogs.com/tornadomeet/archive/2012/11/04/2753185.html 前言 这篇文章是本人玩kinect时做的一个小实验,即不采用 ...

  2. Kinect+OpenNI学习笔记之4(OpenNI获取的图像结合OpenCV显示)

    前言 本文来结合下opencv的highgui功能显示kinect采集得来的颜色图和深度图.本来在opencv中自带了VideoCapture类的,使用该类可以直接驱动kinect设备,具体的可以参考 ...

  3. Kinect+OpenNI学习笔记之13(Kinect驱动类,OpenCV显示类和手部预分割类的设计)

    前言 为了减小以后项目的开发效率,本次实验将OpenNI底层驱动Kinect,OpenCV初步处理OpenNI获得的原始数据,以及手势识别中的分割(因为本系统最后是开发手势识别的)这3个部分的功能单独 ...

  4. Kinect+OpenNI学习笔记之6(获取人体骨架并在Qt中显示)

    前言 MS的kinec SDK和OpenNI都提供了人体骨骼跟踪的算法,人体骨骼跟踪算法在kinect人体行为识别中非常重要,该识别过程通常被用来作为行为识别的第一步, 比如说,通过定位人体中的骨骼支 ...

  5. Kinect+OpenNI学习笔记之14(关于Kinect的深度信息)

    前言 由于最近要研究kinect采集到的深度信息的一些统计特征,所以必须先对kinect深度信息做进一步的了解.这些了解包括kinect的深 度值精度,深度值的具体代表的距离是指哪个距离以及kinec ...

  6. Kinect+OpenNI学习笔记之2(获取kinect的颜色图像和深度图像)

    前言 网上有不少使用Qt做界面,OpenNI为库来开发kinect.或许大家的第一个问题就是询问该怎样使用Kinect来获取颜色信息图和深度信息图呢?这一节就是简单来回答这个问题的. 开发环境:QtC ...

  7. Kinect+OpenNI学习笔记之11(OpenNI驱动kinect手势相关的类的设计)

    前言 本文所设计的类主要是和人体的手部打交道的,与人体的检测,姿势校正,骨架跟踪没有关系,所以本次类的设计中是在前面的OpenNI+Kinect系列博文基础上去掉那些与手势无关的驱动,较小代码量负担. ...

  8. Kinect开发学习笔记之(二)Kinect开发学习资源整理

    Kinect开发学习笔记之(二)Kinect开发学习资源整理 zouxy09@qq.com http://blog.csdn.net/zouxy09 刚刚接触Kinect,在网上狂搜资料,获得了很多有 ...

  9. java openni rgb显示_Kinect+OpenNI学习笔记之4(OpenNI获取的图像结合OpenCV显示)

    前言 本文来结合下opencv的highgui功能显示kinect采集得来的颜色图和深度图.本来在opencv中自带了VideoCapture类的,使用该类可以直接驱动kinect设备,具体的可以参考 ...

最新文章

  1. 如何用纯 CSS 创作气泡填色的按钮特效
  2. 释放数据价值,大数据分析如何助力电商获客又增收?
  3. 汇编语言直接偏移量操作数
  4. 白裤子变粉裤子怎么办_使用裤子构建构建数据科学的monorepo
  5. 【HDU - 5015 】233 Matrix (矩阵快速幂)
  6. macOS 10.15安装GDB
  7. [2019杭电多校第三场][hdu6606]Distribution of books(线段树dp)
  8. led led c语言程序设计,单片机C语言程序设计:8X8LED 点阵显示数字
  9. 190809每日一次
  10. php 判断是否在线,关于判断用户是否在线的问题!!!
  11. IP地址规划和设计方法
  12. word怎么显示左边目录?目录大纲(视图 --> 导航窗格)
  13. 体验更简单的 DM —— v1.0.2
  14. ElastiSearch与Solr和Lucene
  15. 【学生毕业设计】基于web学生信息管理系统网站的设计与实现(13个页面)
  16. 信息熵与信息增益的概念
  17. 南京邮电大学计算机学院讲师黄璞,我所与南京邮电大学建立“计算机技术联合实验室”...
  18. router-view添加key解决同一路由复用相同组件
  19. VHDL电话计费系统设计
  20. 机器视觉领域专业词汇中英对照

热门文章

  1. Windows Server 2016-Win Ser 2016新增功能
  2. Java中cvc是什么意思_什么是英语CVC?
  3. android同花顺布局,90%的Android手机已内置同花顺手机炒股
  4. V神参与的NFT动画系列Stoner Cats 40分钟售罄,致gas上涨至600 gwei以上
  5. 智慧宁夏大数据缘何“跑”不起来
  6. nextcloud和onlyoffice一键简单无脑部署
  7. 2021爱智先行者-可识别车牌号的智能摄像头
  8. 分布式系统(概念、时间、状态)
  9. BeeHive、CTMediator、MGJRouter
  10. SOC的第一个Hello_World实验