引自:http://www.cnblogs.com/tornadomeet/archive/2012/11/04/2753185.html

前言

  这篇文章是本人玩kinect时做的一个小实验,即不采用机器学习等类似AI的方法来做简单的手势数字识别,当然了,该识别的前提是基于本人前面已提取出手部的博文Robert Walter手部提取代码的分析的基础上进行的。由于是纯数学形状上来判别手势,所以只是做了个简单的0~5的数字识别系统,其手势的分割部分效果还不错(因为其核心代码是由OpenNI提供的),手势数字识别时容易受干扰,效果一般般,毕竟这只是个简单的实验。

  实验基础

  首先来看下本系统的流程图,如下所示:

  

  其中轮廓的提取,多边形拟合曲线的求法,凸包集和凹陷集的求法都是采用opencv中自带的函数。手势数字的识别是利用凸包点以及凹陷点和手部中心点的几何关系,简单的做了下逻辑判别了(可以肯定的是这种方法很烂),具体的做法是先在手部定位出2个中心点坐标,这2个中心点坐标之间的距离阈值由程序设定,其中一个中心点就是利用OpenNI跟踪得到的手部位置。有了这2个中心点的坐标,在程序中就可以分别计算出在这2个中心点坐标上的凸凹点的个数。当然了,这样做的前提是用人在做手势表示数字的同时应该是将手指的方向朝上(因为没有像机器学习那样通过样本来训练,所以使用时条件要苛刻很多)。利用上面求出的4种点的个数(另外程序中还设置了2个辅助计算点的个数,具体见代码部分)和简单的逻辑判断就可以识别出数字0~5了。其它的数字可以依照具体的逻辑去设计(还可以设计出多位数字的识别),只是数字越多设计起来越复杂,因为要考虑到它们之间的干扰性,且这种不通用的设计方法也没有太多的实际意义。

  OpenCV知识点总结

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

  这个在函数在前面的博文Robert Walter手部提取代码的分析中已经介绍过,当时是这么解释的:

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

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

  

  不过这里需要重新对这3个参数做个详细的说明:

  第1个参数虽然写的是contour,字面意思是轮廓,但是本人实验过很多次,发现如果该参数为目标通过轮廓检测得到的原始轮廓的话,则程序运行到onvexityDefects()函数时会报内存错误。因此本程序中采用的不是物体原始的轮廓,而是经过多项式曲线拟合后的轮廓,即多项式曲线,这样程序就会顺利地运行得很好。另外由于在手势识别过程中可能某一帧检测出来的轮廓非常小(由于某种原因),以致于少到只有1个点,这时候如果程序运行到onvexityDefects()函数时就会报如下的错误:

  查看opencv源码中对应错误提示的位置,其源码如下:

  

  表示在1969行的位置出错,也就是CV_Assert( ptnum > 3 );出错,说明出错时此处的ptnum <=3;看上面一行代码ptnum = points.checkVector(2, CV_32S);所以我们需要了解checkVector()函数的功能,进入opencv中关于checkVector的源码,如下:

int Mat::checkVector(int _elemChannels, int _depth, bool _requireContinuous) const{return (depth() == _depth || _depth <= 0) &&(isContinuous() || !_requireContinuous) &&((dims == 2 && (((rows == 1 || cols == 1) && channels() == _elemChannels) || (cols == _elemChannels))) ||(dims == 3 && channels() == 1 && size.p[2] == _elemChannels && (size.p[0] == 1 || size.p[1] == 1) &&(isContinuous() || step.p[1] == step.p[2]*size.p[2])))? (int)(total()*channels()/_elemChannels) : -1;}

  该函数源码大概意思就是说对应的Mat矩阵如果其深度,连续性,通道数,行列式满足一定条件的话就返回Mat元素的个数和其通道数的乘积,否则返回-1;而本文是要求其返回值大于3,有得知此处输入多边形曲线(即参数1)的通道数为2,所以还需要求其元素的个数大于1.5,即大于2才满足ptnum > 3。简单的说就是用convexityDefects()函数来对多边形曲线进行凹陷检测时,必须要求参数1曲线本身至少有2个点(也不知道这样分析对不对)。因此本人在本次程序convexityDefects()函数前加入了if(Mat(approx_poly_curve).checkVector(2,  CV_32S) > 3)来判断,只有满足该if条件,才会进行后面的凹陷检测。这样程序就不会再出现类似的bug了。

  第2个参数一般是由opencv中的函数convexHull()获得的,一般情况下该参数里面存的是凸包集合中的点在多项式曲线点中的位置索引,且该参数以vector的形式存在,因此参数convexhull中其元素的类型为unsigned int。在本次凹陷点检测函数convexityDefects()里面根据文档,要求该参数为Mat型。因此在使用convexityDefects()的参数2时,一般将vector直接转换Mat型。

  参数3是一个含有4个元素的结构体的集合,如果在c++的版本中,该参数可以直接用vector<Vec4i>来代替,Vec4i中的4个元素分别表示凹陷曲线段的起始坐标索引,终点坐标索引,离凸包集曲线最远点的坐标索引以及此时的最远距离值,这4个值都是整数。在c版本的opencv中一般不是保存的索引,而是坐标值,如下所示:

struct CvConvexityDefect{CvPoint* start; // point of the contour where the defect begins
CvPoint* end; // point of the contour where the defect ends
CvPoint* depth_point; // the farthest from the convex hull point within the defectfloat depth; // distance between the farthest point and the convex hull

};

  C/c++知识点总结:

  std::abs()函数中的参数不能为整型,否则编译的时候会报错参数不匹配,因此一般情况下可以传入long型,这样就不会报错了。

  实验结果:

  这里显示的是手势分割后的效果以及其对应的数字识别结果。

  数字“0”的识别结果:

  

  数字“1”的识别结果:

  

  数字“2”的识别结果:

  

  数字“3”的识别结果:

  

  数字“4”的识别结果:

  

  数字“5”的识别结果:

  实验主要部分代码及注释:

  本次实验程序实现过程和上面的系统流程图类似,大概过程如下:

  1.  求出手部的掩膜

  2.  求出掩膜的轮廓

  3.  求出轮廓的多变形拟合曲线

  4.  求出多边形拟合曲线的凸包集,找出凸点

  5.  求出多变形拟合曲线的凹陷集,找出凹点

  6.  利用上面的凸凹点和手部中心点的几何关系来做简单的数字手势识别

  copenni类采用前面博文设计的,这里给出主函数代码部分。

  main.cpp:

#include <iostream>#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <opencv2/core/core.hpp>
#include "copenni.cpp"#include <iostream>#define DEPTH_SCALE_FACTOR 255./4096.
#define ROI_HAND_WIDTH 140
#define ROI_HAND_HEIGHT 140
#define MEDIAN_BLUR_K 5
#define XRES  640
#define YRES  480
#define DEPTH_SEGMENT_THRESH 5
#define MAX_HANDS_COLOR 10
#define MAX_HANDS_NUMBER  10
#define HAND_LIKELY_AREA 2000
#define DELTA_POINT_DISTENCE 25     //手部中心点1和中心点2距离的阈值
#define SEGMENT_POINT1_DISTANCE 27  //凸点与手部中心点1远近距离的阈值
#define SEGMENT_POINT2_DISTANCE 30  //凸点与手部中心点2远近距离的阈值using namespace cv;
using namespace xn;
using namespace std;int main (int argc, char **argv)
{unsigned int convex_number_above_point1 = 0;unsigned int concave_number_above_point1 = 0;unsigned int convex_number_above_point2 = 0;unsigned int concave_number_above_point2 = 0;unsigned int convex_assist_above_point1 = 0;unsigned int convex_assist_above_point2 = 0;unsigned int point_y1 = 0;unsigned int point_y2 = 0;int number_result = -1;bool recognition_flag = false;  //开始手部数字识别的标志
vector<Scalar> color_array;//采用默认的10种颜色
    {color_array.push_back(Scalar(255, 0, 0));color_array.push_back(Scalar(0, 255, 0));color_array.push_back(Scalar(0, 0, 255));color_array.push_back(Scalar(255, 0, 255));color_array.push_back(Scalar(255, 255, 0));color_array.push_back(Scalar(0, 255, 255));color_array.push_back(Scalar(128, 255, 0));color_array.push_back(Scalar(0, 128, 255));color_array.push_back(Scalar(255, 0, 128));color_array.push_back(Scalar(255, 128, 255));}vector<unsigned int> hand_depth(MAX_HANDS_NUMBER, 0);vector<Rect> hands_roi(MAX_HANDS_NUMBER, Rect(XRES/2, YRES/2, ROI_HAND_WIDTH, ROI_HAND_HEIGHT));namedWindow("color image", CV_WINDOW_AUTOSIZE);namedWindow("depth image", CV_WINDOW_AUTOSIZE);namedWindow("hand_segment", CV_WINDOW_AUTOSIZE);    //显示分割出来的手的区域namedWindow("handrecognition", CV_WINDOW_AUTOSIZE); //显示0~5数字识别的图像
COpenNI openni;if(!openni.Initial())return 1;if(!openni.Start())return 1;while(1) {if(!openni.UpdateData()) {return 1;}/*获取并显示色彩图像*/Mat color_image_src(openni.image_metadata_.YRes(), openni.image_metadata_.XRes(),CV_8UC3, (char *)openni.image_metadata_.Data());Mat color_image;cvtColor(color_image_src, color_image, CV_RGB2BGR);Mat hand_segment_mask(color_image.size(), CV_8UC1, Scalar::all(0));for(auto itUser = openni.hand_points_.cbegin(); itUser != openni.hand_points_.cend(); ++itUser) {point_y1 = itUser->second.Y;point_y2 = itUser->second.Y + DELTA_POINT_DISTENCE;circle(color_image, Point(itUser->second.X, itUser->second.Y),5, color_array.at(itUser->first % color_array.size()), 3, 8);/*设置不同手部的深度*/hand_depth.at(itUser->first % MAX_HANDS_COLOR) = (unsigned int)(itUser->second.Z* DEPTH_SCALE_FACTOR);//itUser->first会导致程序出现bug/*设置不同手部的不同感兴趣区域*/hands_roi.at(itUser->first % MAX_HANDS_NUMBER) = Rect(itUser->second.X - ROI_HAND_WIDTH/2, itUser->second.Y - ROI_HAND_HEIGHT/2,ROI_HAND_WIDTH, ROI_HAND_HEIGHT);hands_roi.at(itUser->first % MAX_HANDS_NUMBER).x =  itUser->second.X - ROI_HAND_WIDTH/2;hands_roi.at(itUser->first % MAX_HANDS_NUMBER).y =  itUser->second.Y - ROI_HAND_HEIGHT/2;hands_roi.at(itUser->first % MAX_HANDS_NUMBER).width = ROI_HAND_WIDTH;hands_roi.at(itUser->first % MAX_HANDS_NUMBER).height = ROI_HAND_HEIGHT;if(hands_roi.at(itUser->first % MAX_HANDS_NUMBER).x <= 0)hands_roi.at(itUser->first % MAX_HANDS_NUMBER).x  = 0;if(hands_roi.at(itUser->first % MAX_HANDS_NUMBER).x > XRES)hands_roi.at(itUser->first % MAX_HANDS_NUMBER).x =  XRES;if(hands_roi.at(itUser->first % MAX_HANDS_NUMBER).y <= 0)hands_roi.at(itUser->first % MAX_HANDS_NUMBER).y = 0;if(hands_roi.at(itUser->first % MAX_HANDS_NUMBER).y > YRES)hands_roi.at(itUser->first % MAX_HANDS_NUMBER).y =  YRES;}imshow("color image", color_image);/*获取并显示深度图像*/Mat depth_image_src(openni.depth_metadata_.YRes(), openni.depth_metadata_.XRes(),CV_16UC1, (char *)openni.depth_metadata_.Data());//因为kinect获取到的深度图像实际上是无符号的16位数据
        Mat depth_image;depth_image_src.convertTo(depth_image, CV_8U, DEPTH_SCALE_FACTOR);imshow("depth image", depth_image);//取出手的mask部分//不管原图像时多少通道的,mask矩阵声明为单通道就okfor(auto itUser = openni.hand_points_.cbegin(); itUser != openni.hand_points_.cend(); ++itUser) {for(int i = hands_roi.at(itUser->first % MAX_HANDS_NUMBER).x; i < std::min(hands_roi.at(itUser->first % MAX_HANDS_NUMBER).x+hands_roi.at(itUser->first % MAX_HANDS_NUMBER).width, XRES); i++)for(int j = hands_roi.at(itUser->first % MAX_HANDS_NUMBER).y; j < std::min(hands_roi.at(itUser->first % MAX_HANDS_NUMBER).y+hands_roi.at(itUser->first % MAX_HANDS_NUMBER).height, YRES); j++) {hand_segment_mask.at<unsigned char>(j, i) = ((hand_depth.at(itUser->first % MAX_HANDS_NUMBER)-DEPTH_SEGMENT_THRESH) < depth_image.at<unsigned char>(j, i))& ((hand_depth.at(itUser->first % MAX_HANDS_NUMBER)+DEPTH_SEGMENT_THRESH) > depth_image.at<unsigned char>(j,i));}}medianBlur(hand_segment_mask, hand_segment_mask, MEDIAN_BLUR_K);Mat hand_segment(color_image.size(), CV_8UC3);color_image.copyTo(hand_segment, hand_segment_mask);/*对mask图像进行轮廓提取,并在手势识别图像中画出来*/std::vector< std::vector<Point> > contours;findContours(hand_segment_mask, contours, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);//找出mask图像的轮廓Mat hand_recognition_image = Mat::zeros(color_image.rows, color_image.cols, CV_8UC3);for(int i = 0; i < contours.size(); i++) {  //只有在检测到轮廓时才会去求它的多边形,凸包集,凹陷集recognition_flag = true;/*找出轮廓图像多边形拟合曲线*/Mat contour_mat = Mat(contours[i]);if(contourArea(contour_mat) > HAND_LIKELY_AREA) {   //比较有可能像手的区域std::vector<Point> approx_poly_curve;approxPolyDP(contour_mat, approx_poly_curve, 10, true);//找出轮廓的多边形拟合曲线std::vector< std::vector<Point> > approx_poly_curve_debug;approx_poly_curve_debug.push_back(approx_poly_curve);drawContours(hand_recognition_image, contours, i, Scalar(255, 0, 0), 1, 8); //画出轮廓//            drawContours(hand_recognition_image, approx_poly_curve_debug, 0, Scalar(256, 128, 128), 1, 8); //画出多边形拟合曲线/*对求出的多边形拟合曲线求出其凸包集*/vector<int> hull;convexHull(Mat(approx_poly_curve), hull, true);for(int i = 0; i < hull.size(); i++) {circle(hand_recognition_image, approx_poly_curve[hull[i]], 2, Scalar(0, 255, 0), 2, 8);/*统计在中心点1以上凸点的个数*/if(approx_poly_curve[hull[i]].y <= point_y1) {/*统计凸点与中心点1的y轴距离*/long dis_point1 = abs(long(point_y1 - approx_poly_curve[hull[i]].y));int dis1 = point_y1 - approx_poly_curve[hull[i]].y;if(dis_point1 > SEGMENT_POINT1_DISTANCE && dis1 >= 0)  {convex_assist_above_point1++;}convex_number_above_point1++;}/*统计在中心点2以上凸点的个数*/if(approx_poly_curve[hull[i]].y <= point_y2)    {/*统计凸点与中心点1的y轴距离*/long dis_point2 = abs(long(point_y2 - approx_poly_curve[hull[i]].y));int dis2 = point_y2 - approx_poly_curve[hull[i]].y;if(dis_point2 > SEGMENT_POINT2_DISTANCE && dis2 >= 0)  {convex_assist_above_point2++;}convex_number_above_point2++;}}//            /*对求出的多边形拟合曲线求出凹陷集*/std::vector<Vec4i> convexity_defects;if(Mat(approx_poly_curve).checkVector(2, CV_32S) > 3)convexityDefects(approx_poly_curve, Mat(hull), convexity_defects);for(int i = 0; i < convexity_defects.size(); i++) {circle(hand_recognition_image, approx_poly_curve[convexity_defects[i][2]] , 2, Scalar(0, 0, 255), 2, 8);/*统计在中心点1以上凹陷点的个数*/if(approx_poly_curve[convexity_defects[i][2]].y <= point_y1)concave_number_above_point1++;/*统计在中心点2以上凹陷点的个数*/if(approx_poly_curve[convexity_defects[i][2]].y <= point_y2)concave_number_above_point2++;}}}/**画出手势的中心点**/for(auto itUser = openni.hand_points_.cbegin(); itUser != openni.hand_points_.cend(); ++itUser) {circle(hand_recognition_image, Point(itUser->second.X, itUser->second.Y), 3, Scalar(0, 255, 255), 3, 8);circle(hand_recognition_image, Point(itUser->second.X, itUser->second.Y + 25), 3, Scalar(255, 0, 255), 3, 8);}/*手势数字0~5的识别*///"0"的识别if((convex_assist_above_point1 ==0 && convex_number_above_point2 >= 2 && convex_number_above_point2 <= 3 &&concave_number_above_point2 <= 1 && concave_number_above_point1 <= 1) || (concave_number_above_point1 ==0|| concave_number_above_point2 == 0) && recognition_flag == true)number_result = 0;//"1"的识别if(convex_assist_above_point1 ==1 && convex_number_above_point1 >=1  && convex_number_above_point1 <=2 &&convex_number_above_point2 >=2 && convex_assist_above_point2 == 1)number_result = 1;//"2"的识别if(convex_number_above_point1 == 2 && concave_number_above_point1 == 1 && convex_assist_above_point2 == 2/*convex_assist_above_point1 <=1*/ && concave_number_above_point2 == 1)number_result = 2;//"3"的识别if(convex_number_above_point1 == 3 && concave_number_above_point1 <= 3 &&concave_number_above_point1 >=1 && convex_number_above_point2 >= 3 && convex_number_above_point2 <= 4 &&convex_assist_above_point2 == 3)number_result = 3;//"4"的识别if(convex_number_above_point1 == 4 && concave_number_above_point1 <=3 && concave_number_above_point1 >=2 &&convex_number_above_point2 == 4)number_result = 4;//"5"的识别if(convex_number_above_point1 >=4 && convex_number_above_point2 == 5 && concave_number_above_point2 >= 3 &&convex_number_above_point2 >= 4)number_result = 5;if(number_result !=0 && number_result != 1  && number_result != 2 && number_result != 3 && number_result != 4 && number_result != 5)number_result == -1;/*在手势识别图上显示匹配的数字*/std::stringstream number_str;number_str << number_result;putText(hand_recognition_image, "Match: ", Point(0, 60), 4, 1, Scalar(0, 255, 0), 2, 0 );if(number_result == -1)putText(hand_recognition_image, " ", Point(120, 60), 4, 2, Scalar(255, 0 ,0), 2, 0);elseputText(hand_recognition_image, number_str.str(), Point(150, 60), 4, 2, Scalar(255, 0 ,0), 2, 0);imshow("handrecognition", hand_recognition_image);imshow("hand_segment", hand_segment);/*一个循环中对有些变量进行初始化操作*/convex_number_above_point1 = 0;convex_number_above_point2 = 0;concave_number_above_point1 = 0;concave_number_above_point2 = 0;convex_assist_above_point1 = 0;convex_assist_above_point2 = 0;number_result = -1;recognition_flag = false;number_str.clear();waitKey(20);}}

  copenni.h:

#ifndef COPENNI_H
#define COPENNI_H#include <XnCppWrapper.h>
#include <XnCyclicStackT.h>
#include <XnHashT.h>
#include <XnListT.h>
#include <iostream>
#include <map>using namespace xn;
using namespace std;class COpenNI
{
public:COpenNI();~COpenNI();/*OpenNI的内部初始化,属性设置*/bool Initial();/*启动OpenNI读取Kinect数据*/bool Start();/*更新OpenNI读取到的数据*/bool UpdateData();/*得到色彩图像的node*/ImageGenerator& getImageGenerator();/*得到深度图像的node*/DepthGenerator& getDepthGenerator();/*得到人体的node*/UserGenerator& getUserGenerator();/*得到手势姿势的node*/GestureGenerator& getGestureGenerator();/*得到手部的node*/HandsGenerator& getHandGenerator();DepthMetaData depth_metadata_;   //返回深度图像数据ImageMetaData image_metadata_;   //返回彩色图像数据std::map<XnUserID, XnPoint3D> hand_points_;  //为了存储不同手的实时点而设置的std::map< XnUserID, vector<XnPoint3D> > hands_track_points_; //为了绘画后面不同手部的跟踪轨迹而设定的private:/*该函数返回真代表出现了错误,返回假代表正确*/bool CheckError(const char* error);/*表示有人体进入的回调函数*/static void XN_CALLBACK_TYPE CBNewUser(UserGenerator &generator, XnUserID user, void *p_cookie);/*表示骨骼校正完成的回调函数*/static void XN_CALLBACK_TYPE CBCalibrationComplete(SkeletonCapability &skeleton,XnUserID user, XnCalibrationStatus calibration_error, void *p_cookie);/*表示某个手势动作已经完成检测的回调函数*/static void XN_CALLBACK_TYPE  CBGestureRecognized(xn::GestureGenerator &generator, const XnChar *strGesture,const XnPoint3D *pIDPosition, const XnPoint3D *pEndPosition,void *pCookie);/*表示检测到某个手势开始的回调函数*/static void XN_CALLBACK_TYPE CBGestureProgress(xn::GestureGenerator &generator, const XnChar *strGesture,const XnPoint3D *pPosition, XnFloat fProgress, void *pCookie);/*手部开始建立的回调函数*/static void XN_CALLBACK_TYPE HandCreate(HandsGenerator& rHands, XnUserID xUID, const XnPoint3D* pPosition,XnFloat fTime, void* pCookie);/*手部开始更新的回调函数*/static void XN_CALLBACK_TYPE HandUpdate(HandsGenerator& rHands, XnUserID xUID, const XnPoint3D* pPosition, XnFloat fTime,void* pCookie);/*手部销毁的回调函数*/static void XN_CALLBACK_TYPE HandDestroy(HandsGenerator& rHands, XnUserID xUID, XnFloat fTime, void* pCookie);XnStatus status_;Context context_;XnMapOutputMode xmode_;UserGenerator user_generator_;ImageGenerator  image_generator_;DepthGenerator  depth_generator_;GestureGenerator gesture_generator_;HandsGenerator  hand_generator_;
};#endif // COPENNI_H

  copenni.cpp:

#include "copenni.h"
#include <XnCppWrapper.h>
#include <iostream>
#include <map>using namespace xn;
using namespace std;COpenNI::COpenNI()
{
}COpenNI::~COpenNI()
{
}bool COpenNI::Initial()
{status_ = context_.Init();if(CheckError("Context initial failed!")) {return false;}context_.SetGlobalMirror(true);//设置镜像xmode_.nXRes = 640;xmode_.nYRes = 480;xmode_.nFPS = 30;//产生颜色nodestatus_ = image_generator_.Create(context_);if(CheckError("Create image generator  error!")) {return false;}//设置颜色图片输出模式status_ = image_generator_.SetMapOutputMode(xmode_);if(CheckError("SetMapOutputMdoe error!")) {return false;}//产生深度nodestatus_ = depth_generator_.Create(context_);if(CheckError("Create depth generator  error!")) {return false;}//设置深度图片输出模式status_ = depth_generator_.SetMapOutputMode(xmode_);if(CheckError("SetMapOutputMdoe error!")) {return false;}//产生手势nodestatus_ = gesture_generator_.Create(context_);if(CheckError("Create gesture generator error!")) {return false;}/*添加手势识别的种类*/gesture_generator_.AddGesture("Wave", NULL);gesture_generator_.AddGesture("click", NULL);gesture_generator_.AddGesture("RaiseHand", NULL);gesture_generator_.AddGesture("MovingHand", NULL);//产生手部的nodestatus_ = hand_generator_.Create(context_);if(CheckError("Create hand generaotr error!")) {return false;}//产生人体nodestatus_ = user_generator_.Create(context_);if(CheckError("Create gesturen generator error!")) {return false;}//视角校正status_ = depth_generator_.GetAlternativeViewPointCap().SetViewPoint(image_generator_);if(CheckError("Can't set the alternative view point on depth generator!")) {return false;}//设置与手势有关的回调函数
    XnCallbackHandle gesture_cb;gesture_generator_.RegisterGestureCallbacks(CBGestureRecognized, CBGestureProgress, this, gesture_cb);//设置于手部有关的回调函数
    XnCallbackHandle hands_cb;hand_generator_.RegisterHandCallbacks(HandCreate, HandUpdate, HandDestroy, this, hands_cb);//设置有人进入视野的回调函数
    XnCallbackHandle new_user_handle;user_generator_.RegisterUserCallbacks(CBNewUser, NULL, NULL, new_user_handle);user_generator_.GetSkeletonCap().SetSkeletonProfile(XN_SKEL_PROFILE_ALL);//设定使用所有关节(共15个)//设置骨骼校正完成的回调函数
    XnCallbackHandle calibration_complete;user_generator_.GetSkeletonCap().RegisterToCalibrationComplete(CBCalibrationComplete, this, calibration_complete);return true;
}bool COpenNI::Start()
{status_ = context_.StartGeneratingAll();if(CheckError("Start generating error!")) {return false;}return true;
}bool COpenNI::UpdateData()
{status_ = context_.WaitNoneUpdateAll();if(CheckError("Update date error!")) {return false;}//获取数据
    image_generator_.GetMetaData(image_metadata_);depth_generator_.GetMetaData(depth_metadata_);return true;
}ImageGenerator &COpenNI::getImageGenerator()
{return image_generator_;
}DepthGenerator &COpenNI::getDepthGenerator()
{return depth_generator_;
}UserGenerator &COpenNI::getUserGenerator()
{return user_generator_;
}GestureGenerator &COpenNI::getGestureGenerator()
{return gesture_generator_;
}HandsGenerator &COpenNI::getHandGenerator()
{return hand_generator_;
}bool COpenNI::CheckError(const char *error)
{if(status_ != XN_STATUS_OK) {cerr << error << ": " << xnGetStatusString( status_ ) << endl;return true;}return false;
}void COpenNI::CBNewUser(UserGenerator &generator, XnUserID user, void *p_cookie)
{//得到skeleton的capability,并调用RequestCalibration函数设置对新检测到的人进行骨骼校正generator.GetSkeletonCap().RequestCalibration(user, true);
}void COpenNI::CBCalibrationComplete(SkeletonCapability &skeleton, XnUserID user, XnCalibrationStatus calibration_error, void *p_cookie)
{if(calibration_error == XN_CALIBRATION_STATUS_OK) {skeleton.StartTracking(user);//骨骼校正完成后就开始进行人体跟踪了
    }else {UserGenerator *p_user = (UserGenerator*)p_cookie;skeleton.RequestCalibration(user, true);//骨骼校正失败时重新设置对人体骨骼继续进行校正
    }
}void COpenNI::CBGestureRecognized(GestureGenerator &generator, const XnChar *strGesture, const XnPoint3D *pIDPosition, const XnPoint3D *pEndPosition, void *pCookie)
{COpenNI *openni = (COpenNI*)pCookie;openni->hand_generator_.StartTracking(*pEndPosition);
}void COpenNI::CBGestureProgress(GestureGenerator &generator, const XnChar *strGesture, const XnPoint3D *pPosition, XnFloat fProgress, void *pCookie)
{
}void COpenNI::HandCreate(HandsGenerator &rHands, XnUserID xUID, const XnPoint3D *pPosition, XnFloat fTime, void *pCookie)
{COpenNI *openni = (COpenNI*)pCookie;XnPoint3D project_pos;openni->depth_generator_.ConvertRealWorldToProjective(1, pPosition, &project_pos);pair<XnUserID, XnPoint3D> hand_point_pair(xUID, XnPoint3D());//在进行pair类型的定义时,可以将第2个设置为空hand_point_pair.second = project_pos;openni->hand_points_.insert(hand_point_pair);//将检测到的手部存入map类型的hand_points_中。
pair<XnUserID, vector<XnPoint3D>> hand_track_point(xUID, vector<XnPoint3D>());hand_track_point.second.push_back(project_pos);openni->hands_track_points_.insert(hand_track_point);
}void COpenNI::HandUpdate(HandsGenerator &rHands, XnUserID xUID, const XnPoint3D *pPosition, XnFloat fTime, void *pCookie)
{COpenNI *openni = (COpenNI*)pCookie;XnPoint3D project_pos;openni->depth_generator_.ConvertRealWorldToProjective(1, pPosition, &project_pos);openni->hand_points_.find(xUID)->second = project_pos;openni->hands_track_points_.find(xUID)->second.push_back(project_pos);
}void COpenNI::HandDestroy(HandsGenerator &rHands, XnUserID xUID, XnFloat fTime, void *pCookie)
{COpenNI *openni = (COpenNI*)pCookie;openni->hand_points_.erase(openni->hand_points_.find(xUID));openni->hands_track_points_.erase(openni->hands_track_points_.find(xUID ));
}

  实验总结:

  由本次实验的操作过程可以看出,识别效果抗干扰能力比较差差。因此后续的工作是建立一个手势识别的数据库,寻找一个好的手部特征向量,和一个好的分类器。这有可能将是本人研究生毕业论文的研究方向,加油!

  参考文献:

Robert Walter手部提取代码的分析

不需要骨骼跟踪的人体多个手部分割

OpenNI驱动kinect手势相关的类的设计

作者:tornadomeet出处:http://www.cnblogs.com/tornadomeet欢迎转载或分享,但请务必声明文章出处。

转载于:https://www.cnblogs.com/xjzhao-hhu/archive/2012/12/25/2833027.html

Kinect+OpenNI学习笔记之12(简单手势所表示的数字的识别)相关推荐

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

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

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

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

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

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

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

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

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

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

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

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

  7. Kinect+OpenNI学习笔记之8(Robert-Walter手部提取代码的分析)

    前言 一般情况下,手势识别的第一步就是先手势定位,即手势所在部位的提取.本文是基于kinect来提取手势识别的,即 先通过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. oracle 记录被另一个用户锁住
  2. IdentityHashMap
  3. Asp.NET中如何一次性下载多个文件
  4. 22504!Windows 11 新预览版发布
  5. 论文浅尝 | LGESQL: 结合线性有向图以及普通有向图的自然语言转换SQL语句模型...
  6. IoT -- (九) IoT通讯技术选型与模型设计
  7. HTML5跑酷网页游戏源码
  8. render注册一个链接组件_vue: 单文件组件 render函数
  9. 软考计算机网络初级试题答案,2015年下半年中级软考《计算机网络—网络工程师》试题及答案...
  10. Panel控件的使用
  11. html怎么设置展开li,html网页代码常用ul li列表布局标签。
  12. 给计算机专业的同学一些建议
  13. C++面向对象总结——数据抽象与数据封装
  14. v4l2架构专题模块handler分析 -- handler使能(1)cluster
  15. android系统中区分UDisk和SDCard
  16. 史上最完美的 Typora 教程
  17. C++primer plus学习笔记
  18. 《禅与计算机程序设计艺术》/ By 禅与计算机程序设计艺术GPT-4
  19. 基于C语言的网络编程笔记分享
  20. apache配置优化之伪静态设置

热门文章

  1. currentThread的一个复杂案例
  2. JDK源码解析-Runtime类
  3. Use Batch Apex
  4. Python 连接FTP服务器并实现文件夹下载实例演示,python区分ftp目录下文件和文件夹方法,ftp目录下包含中文名问题处理
  5. Docker selenium自动化 - 修改/dev/shm路径大小实例演示,“session deleted because of page crash“问题解决
  6. CTFshow php特性 web132
  7. [YTU]_2576( 虚函数练习:动物2)
  8. Reverse Linked List II
  9. 聚类之hierachical clustering算法
  10. Linux命令行下关机【Ubuntu】