前言

  为了减小以后项目的开发效率,本次实验将OpenNI底层驱动Kinect,OpenCV初步处理OpenNI获得的原始数据,以及手势识别中的分割(因为本系统最后是开发手势识别的)这3个部分的功能单独做成类,以便以后移植和扩展。其实在前面已经有不少文章涉及到了这3部分的设计,比如说:Kinect+OpenNI学习笔记之3(获取kinect的数据并在Qt中显示的类的设计), Kinect+OpenNI学习笔记之11(OpenNI驱动kinect手势相关的类的设计), Kinect+OpenNI学习笔记之12(简单手势所表示的数字的识别) 。这次是综合前面几次的设计,优化了下这几个类。

  开发环境:开发环境:QtCreator2.5.1+OpenNI1.5.4.0+Qt4.8.2+OpenCV2.4.3

  实验基础

  OPenNI/OPenCV知识点总结:

  Kinect驱动类,OpenCV显示类和手部预分割类这3个类,单独来设计其实参考了前面的博文还是很简单的,但是由于这3个类之间有相互联系,设计不好就会出现图像显示非常卡。这时候,需要注意下面几点问题(在本程序代码中,Kinect驱动类为COpenniHand,OpenCV显示类为CKinectOpenCV, 手部预分割类为CKinectHandSegment):

  因为在kinect驱动类中有完整的kinect驱动程序(这个驱动会占用一部分时间的),而OpenCV显示类调用了Kinect驱动类中的内容,相当于完成了一次Kinect驱动完整过程,这时候,因为在手部预分割过程中,要获得手部的中心点,如果在该类中再次执行kinect的驱动来获得该中心点,那么整个系统中一个流程的时间其kinect需要驱动两次,这会浪费很多系统资源,导致图像显示不流畅等。因此我们应该在OpenCV显示类中就返回Kinect驱动类中能够返回的值,比如说手部中心点的位置

  在CKinectOpenCV类中由于要返回手部的中心点位置,本打算在类内部公共部分设置一个获取手部中心点位置的函数的,但是发现如果这个函数的返回值是map类型时,运行时老出错误(理论上应该是不会出错的),所以后面该为直接返回手部中心点的变量(map类型),但是在这个变量返回前要保证它的值是实时更新的,所以应该在返回前加入kinect驱动程序中的Updata函数,我这里将其设计成了一个开关函数,即如果允许获取手部中心点,就将开关函数中的参数设置为ture,具体参见代码部分。

  C/C++知识点总结:

  定义类的对象并使用该对象后,一般会先调用该类的初始化函数,该函数的作用一般是为类成员变量进行一些初始设置,但如果类中其它函数的调用前每次都初始化某些变量时,这些变量的初始化不宜放在类的初始化函数中,而应该单独给个私有函数,在那些需要调用它的函数前面进行被调用,达到初始化某些变量的目的。

  类的设计的目的一是为了方便,而是为了提高效率,有时候不能够光为了方便而去设计,比如说在本次类设计中要获得分割好了的图像,或者原始图像,或者深度图像等等,确实是可以直接使用一个函数每一幅图像,不过每次获得图像就要更新一个下kinect驱动中的数据,因此这样的效率就非常低了,在实际设计中,我把那些kinect驱动设备的程序写在了一个函数中,但是这个函数又不能被获取图像的每个函数去调用,否则还是相当于驱动了多次,因此只能由类所定义的对象来调用了。结果是每一个主函数循环中,我们在定义了类的对象后多调用一个函数,再去获得所需的图像,这样只是多了一句代码,却节省了不少时间消耗。

  实验结果

  本次实验完成的功能依旧是获取kinect的深度图,颜色图,手势分割图,手势轮廓图等。下面是手势分割图和轮廓处理图的结果:

  

  实验代码及注释:

  copennihand.h:

#ifndef COpenniHand_H
#define COpenniHand_H#include <XnCppWrapper.h>
#include <iostream>
#include <vector>
#include <map>using namespace xn;
using namespace std;class COpenniHand
{
public:COpenniHand();~COpenniHand();/*OpenNI的内部初始化,属性设置*/bool Initial();/*启动OpenNI读取Kinect数据*/bool Start();/*更新OpenNI读取到的数据*/bool UpdateData();/*得到色彩图像的node*/ImageGenerator& getImageGenerator();/*得到深度图像的node*/DepthGenerator& getDepthGenerator();/*得到手势姿势的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  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_;ImageGenerator  image_generator_;DepthGenerator  depth_generator_;GestureGenerator gesture_generator_;HandsGenerator  hand_generator_;
};#endif // COpenniHand_H

  copennihand.cpp:

#include "copennihand.h"
#include <XnCppWrapper.h>
#include <iostream>
#include <map>using namespace xn;
using namespace std;COpenniHand::COpenniHand()
{
}COpenniHand::~COpenniHand()
{
}bool COpenniHand::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;}//视角校正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);return true;
}bool COpenniHand::Start()
{status_ = context_.StartGeneratingAll();if(CheckError("Start generating error!")) {return false;}return true;
}bool COpenniHand::UpdateData()
{status_ = context_.WaitNoneUpdateAll();if(CheckError("Update date error!")) {return false;}//获取数据
    image_generator_.GetMetaData(image_metadata_);depth_generator_.GetMetaData(depth_metadata_);return true;
}ImageGenerator &COpenniHand::getImageGenerator()
{return image_generator_;
}DepthGenerator &COpenniHand::getDepthGenerator()
{return depth_generator_;
}GestureGenerator &COpenniHand::getGestureGenerator()
{return gesture_generator_;
}HandsGenerator &COpenniHand::getHandGenerator()
{return hand_generator_;
}bool COpenniHand::CheckError(const char *error)
{if(status_ != XN_STATUS_OK) {cerr << error << ": " << xnGetStatusString( status_ ) << endl;return true;}return false;
}void COpenniHand::CBGestureRecognized(GestureGenerator &generator, const XnChar *strGesture, const XnPoint3D *pIDPosition, const XnPoint3D *pEndPosition, void *pCookie)
{COpenniHand *openni = (COpenniHand*)pCookie;openni->hand_generator_.StartTracking(*pEndPosition);
}void COpenniHand::CBGestureProgress(GestureGenerator &generator, const XnChar *strGesture, const XnPoint3D *pPosition, XnFloat fProgress, void *pCookie)
{
}void COpenniHand::HandCreate(HandsGenerator &rHands, XnUserID xUID, const XnPoint3D *pPosition, XnFloat fTime, void *pCookie)
{COpenniHand *openni = (COpenniHand*)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 COpenniHand::HandUpdate(HandsGenerator &rHands, XnUserID xUID, const XnPoint3D *pPosition, XnFloat fTime, void *pCookie)
{COpenniHand *openni = (COpenniHand*)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 COpenniHand::HandDestroy(HandsGenerator &rHands, XnUserID xUID, XnFloat fTime, void *pCookie)
{COpenniHand *openni = (COpenniHand*)pCookie;openni->hand_points_.erase(openni->hand_points_.find(xUID));openni->hands_track_points_.erase(openni->hands_track_points_.find(xUID ));
}

  ckinectopencv.h:

#ifndef CKINECTOPENCV_H
#define CKINECTOPENCV_H#include <opencv2/core/core.hpp>
#include "copennihand.h"using namespace cv;class CKinectOpenCV
{
public:CKinectOpenCV();~CKinectOpenCV();void GetAllInformation();   //在返回有用信息前调用该函数,因为openni的数据在不断更新,信息的处理最好放在一个函数中
    Mat GetColorImage() ;Mat GetDepthImage() ;std::map<XnUserID, XnPoint3D> GetHandPoints();private:COpenniHand openni_hand_;std::map<XnUserID, XnPoint3D> hand_points_;  //为了存储不同手的实时点而设置的Mat color_image_;    //颜色图像Mat depth_image_;    //深度图像
};#endif // CKINECTOPENCV_H

  ckinectopencv.cpp:

#include "ckinectopencv.h"
#include <opencv2/imgproc/imgproc.hpp>
#include <map>using namespace cv;
using namespace std;#define DEPTH_SCALE_FACTOR 255./4096.CKinectOpenCV::CKinectOpenCV()
{   /*初始化openni对应的设备*/CV_Assert(openni_hand_.Initial());/*启动openni对应的设备*/CV_Assert(openni_hand_.Start());
}CKinectOpenCV::~CKinectOpenCV()
{
}void CKinectOpenCV::GetAllInformation()
{CV_Assert(openni_hand_.UpdateData());/*获取色彩图像*/Mat color_image_src(openni_hand_.image_metadata_.YRes(), openni_hand_.image_metadata_.XRes(),CV_8UC3, (char *)openni_hand_.image_metadata_.Data());cvtColor(color_image_src, color_image_, CV_RGB2BGR);/*获取深度图像*/Mat depth_image_src(openni_hand_.depth_metadata_.YRes(), openni_hand_.depth_metadata_.XRes(),CV_16UC1, (char *)openni_hand_.depth_metadata_.Data());//因为kinect获取到的深度图像实际上是无符号的16位数据
    depth_image_src.convertTo(depth_image_, CV_8U, DEPTH_SCALE_FACTOR);hand_points_ = openni_hand_.hand_points_;   //返回手部点的位置return;
}Mat CKinectOpenCV::GetColorImage()
{return color_image_;
}Mat CKinectOpenCV::GetDepthImage()
{return depth_image_;
}std::map<XnUserID, XnPoint3D> CKinectOpenCV::GetHandPoints()
{return hand_points_;
}

  ckinecthandsegment.h:

#ifndef KINECTHAND_H
#define KINECTHAND_H#include "ckinectopencv.h"using namespace cv;#define MAX_HANDS_COLOR 10
#define MAX_HANDS_NUMBER  10class CKinectHandSegment
{
public:CKinectHandSegment();~CKinectHandSegment();void Initial();void StartKinectHand(); //启动kinect手部设备驱动
    Mat GetColorImageWithHandsPoint();Mat GetHandSegmentImage();Mat GetHandHandlingImage();Mat GetColorImage();Mat GetDepthImage();private:CKinectOpenCV kinect_opencv_;vector<Scalar> hand_center_color_array_;//采用默认的10种颜色std::map<XnUserID, XnPoint3D> hand_points_;vector<unsigned int> hand_depth_;vector<Rect> hands_roi_;bool hand_segment_flag_;Mat color_image_with_handspoint_;   //带有手部中心位置的色彩图Mat color_image_;   //色彩图
    Mat depth_image_;Mat hand_segment_image_;Mat hand_handling_image_;Mat hand_segment_mask_;
};#endif // KINECTHAND_H

  ckinecthandsegment.cpp:

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include "ckinecthandsegment.h"
#include "copennihand.h"
#include "ckinectopencv.h"using namespace cv;
using namespace std;#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 HAND_LIKELY_AREA 2000CKinectHandSegment::CKinectHandSegment()
{
}CKinectHandSegment::~CKinectHandSegment()
{
}void CKinectHandSegment::Initial()
{color_image_with_handspoint_ = kinect_opencv_.GetColorImage();depth_image_ = kinect_opencv_.GetDepthImage();{hand_center_color_array_.push_back(Scalar(255, 0, 0));hand_center_color_array_.push_back(Scalar(0, 255, 0));hand_center_color_array_.push_back(Scalar(0, 0, 255));hand_center_color_array_.push_back(Scalar(255, 0, 255));hand_center_color_array_.push_back(Scalar(255, 255, 0));hand_center_color_array_.push_back(Scalar(0, 255, 255));hand_center_color_array_.push_back(Scalar(128, 255, 0));hand_center_color_array_.push_back(Scalar(0, 128, 255));hand_center_color_array_.push_back(Scalar(255, 0, 128));hand_center_color_array_.push_back(Scalar(255, 128, 255));}vector<unsigned int> hand_depth_temp(MAX_HANDS_NUMBER, 0);hand_depth_ = hand_depth_temp;vector<Rect> hands_roi_temp(MAX_HANDS_NUMBER, Rect(XRES/2, YRES/2, ROI_HAND_WIDTH, ROI_HAND_HEIGHT));hands_roi_ = hands_roi_temp;
}void CKinectHandSegment::StartKinectHand()
{kinect_opencv_.GetAllInformation();
}Mat CKinectHandSegment::GetColorImage()
{return kinect_opencv_.GetColorImage();
}Mat CKinectHandSegment::GetDepthImage()
{return kinect_opencv_.GetDepthImage();
}/*该函数只是在Kinect获取的色彩图片上将手的中心点位置画出来而已,图片的其它地方不变*/
Mat CKinectHandSegment::GetColorImageWithHandsPoint()
{color_image_with_handspoint_ = kinect_opencv_.GetColorImage();hand_points_ = kinect_opencv_.GetHandPoints();for(auto itUser = hand_points_.cbegin(); itUser != hand_points_.cend(); ++itUser) {circle(color_image_with_handspoint_, Point(itUser->second.X, itUser->second.Y),5, hand_center_color_array_.at(itUser->first % hand_center_color_array_.size()), 3, 8);}return color_image_with_handspoint_;
}Mat CKinectHandSegment::GetHandSegmentImage()
{hand_segment_flag_ = false;color_image_ = kinect_opencv_.GetColorImage();depth_image_ = kinect_opencv_.GetDepthImage();hand_points_ = kinect_opencv_.GetHandPoints();hand_segment_mask_ = Mat::zeros(color_image_.size(), CV_8UC1);  //  因为zeros是一个静态函数,所以不能直接用具体的对象去调用,而需要用类来调用for(auto itUser = hand_points_.cbegin(); itUser != hand_points_.cend(); ++itUser) {/*设置不同手部的深度*/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;}//取出手的mask部分,不管原图像时多少通道的,mask矩阵声明为单通道就okfor(auto itUser = hand_points_.cbegin(); itUser != hand_points_.cend(); ++itUser) {for(int i = hands_roi_.at(itUser->first % MAX_HANDS_NUMBER).x; i < 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 < 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));hand_segment_mask_.at<unsigned char>(j, i) = 255*hand_segment_mask_.at<unsigned char>(j, i);}}medianBlur(hand_segment_mask_, hand_segment_mask_, MEDIAN_BLUR_K);hand_segment_image_.convertTo(hand_segment_image_, CV_8UC3, 0, 0 ); //  需要清零
    color_image_.copyTo(hand_segment_image_, hand_segment_mask_);hand_segment_flag_ = true;  //返回之前将分割标志置位为1,表示已经完成分割函数return hand_segment_image_;
}Mat CKinectHandSegment::GetHandHandlingImage()
{/*对mask图像进行轮廓提取,并在手势识别图像中画出来*/std::vector< std::vector<Point> > contours;CV_Assert(hand_segment_flag_);  //  因为后面要用到分割函数的mask图,所以这里先要保证调用过分割函数findContours(hand_segment_mask_, contours, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);//找出mask图像的轮廓hand_handling_image_ = Mat::zeros(color_image_.rows, color_image_.cols, CV_8UC3);for(int i = 0; i < contours.size(); i++) {  //只有在检测到轮廓时才会去求它的多边形,凸包集,凹陷集/*找出轮廓图像多边形拟合曲线*/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_handling_image_, contours, i, Scalar(255, 0, 0), 1, 8); //画出轮廓
//            drawContours(hand_handling_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_handling_image_, approx_poly_curve[hull[i]], 2, Scalar(0, 255, 0), 2, 8);}/*对求出的多边形拟合曲线求出凹陷集*/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_handling_image_, approx_poly_curve[convexity_defects[i][2]] , 2, Scalar(0, 0, 255), 2, 8);}}}/**画出手势的中心点**/for(auto itUser = hand_points_.cbegin(); itUser != hand_points_.cend(); ++itUser) {circle(hand_handling_image_, Point(itUser->second.X, itUser->second.Y), 3, Scalar(0, 255, 255), 3, 8);}return hand_handling_image_;
}

  main.cpp:

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include "ckinectopencv.h"
#include "ckinecthandsegment.h"using namespace std;
using namespace cv;int main()
{CKinectHandSegment kinect_hand_segment;Mat color_image ;Mat depth_image ;Mat hand_segment;Mat hand_handling_image;kinect_hand_segment.Initial();while(1){kinect_hand_segment.StartKinectHand();color_image = kinect_hand_segment.GetColorImageWithHandsPoint();hand_segment = kinect_hand_segment.GetHandSegmentImage();hand_handling_image = kinect_hand_segment.GetHandHandlingImage();depth_image = kinect_hand_segment.GetDepthImage();imshow("color_image", color_image);imshow("depth_image", depth_image);imshow("hand_segment", hand_segment);imshow("hand_handling", hand_handling_image);waitKey(30);}return 0;
}

  实验总结:把这些基本功能类设计好了后,就可以更方面测试我后面的手势识别算法了,加油!

  参考资料:

Kinect+OpenNI学习笔记之3(获取kinect的数据并在Qt中显示的类的设计)

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

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

转载于:https://www.cnblogs.com/tornadomeet/archive/2012/11/13/2768070.html

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

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

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

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

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

  3. Kinect的学习笔记发展一Kinect引进和应用

    Kinect开发学习笔记之(一)Kinect介绍和应用 zouxy09@qq.com http://blog.csdn.net/zouxy09 一.Kinect简单介绍 Kinectfor Xbox ...

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

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

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

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

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

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

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

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

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

    前言 一般情况下,手势识别的第一步就是先手势定位,即手势所在部位的提取.本文是基于kinect来提取手势识别的,即 先通过kinect找出人体的轮廓,然后定位轮廓中与手部有关的点,在该点的周围提取出满 ...

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

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

最新文章

  1. Set的常用实现类HashSet和TreeSet
  2. 唤醒控件曾经拥有的能力
  3. (转)javascript匿名函数
  4. [vs2010 project] CppUnit快速入门
  5. docker国内镜像链接
  6. eclipse反编译插件Jadclipse介绍
  7. java set的作用,Java的自学之路-构造方法 的作用以及与set方法的区别
  8. [转]sql server 2005 中的内连接和外连接
  9. Xshel和Xftp免费版
  10. 机械制造技术基础【3】
  11. bootstrap插件bootbox参数
  12. 计算机怎么算平均分公式,平均分怎么算
  13. 从0到一开发微信小程序(2)——开发第一个小程序
  14. Python的excel表格操作,数据提取分析
  15. 谷粒商城-高级篇-Day11-商城业务
  16. 怎么用feign远程调用别人的接口_Feign - HTTP接口调用- 单独使用 - 实战
  17. 模拟手机通讯录联系人功能
  18. 【SAP学习小记】SAP中常用物料表和bom表,以及关联信息
  19. excel计算相关系数、绝对误差、均方根误差
  20. 【历史上的今天】6 月 11 日:蒙特卡罗方法的共同发明者出生;谷歌推出 Google 地球;谷歌收购 Waze

热门文章

  1. mysql did not start_mysql 服务无法启动
  2. 浙江非税设定服务器信息,非税操作手册.doc
  3. vue 如何调用微信分享_Vue开发 添加微信分享功能(全局分享)
  4. python爬虫框架学习_学习Python爬虫必备框架:Scrapy
  5. 计算机文化基础在线作业答案,地大《计算机文化基础》在线作业一答案.doc
  6. linux 内核编程 延时函数,linux中内核延时编程
  7. python调用函数获取最开始的异常_Python使用sys.exc_info()方法获取异常信息
  8. docker镜像启动后端口号是多少_初识Docker
  9. php检测是否存在敏感词,如何用PHP+Ajax判断是否有敏感词汇
  10. Fast Marching on 3D Meshes