Kinect+OpenNI学习笔记之9不需要骨骼跟踪的人体手部分割.docx
《Kinect+OpenNI学习笔记之9不需要骨骼跟踪的人体手部分割.docx》由会员分享,可在线阅读,更多相关《Kinect+OpenNI学习笔记之9不需要骨骼跟踪的人体手部分割.docx(16页珍藏版)》请在冰豆网上搜索。
Kinect+OpenNI学习笔记之9不需要骨骼跟踪的人体手部分割
Kinect+OpenNI学习笔记之9(不需要骨骼跟踪的人体手部分割)
前言
手势识别非常重要的一个特点是要体验要好,即需要以用户为核心。
而手势的定位一般在手势识别过程的前面,在上一篇博文Kinect+OpenNI学习笔记之8(RobertWalter手部提取代码的分析) 中已经介绍过怎样获取手势区域,且取得了不错的效果,但是那个手势部位的提取有一个大的缺点,即需要人站立起来,当站立起来后才能够分隔出手。
而手势在人之间的交流时,并不一定要处于站立状态,所以这不是一个好的HCI。
因此本文介绍的手势部位的提取并不需要人处于站立状态,同样取得了不错的效果。
实验说明
其实,本实验实现的过程非常简单。
首先通过手部的跟踪来获取手所在的坐标,手部跟踪可以参考本人前面的博文:
Kinect+OpenNI学习笔记之7(OpenNI自带的类实现手部跟踪)。
当定位到手所在的坐标后,因为该坐标是3D的,因此在该坐标领域的3维空间领域内提取出手的部位即可,整个过程的大概流程图如下:
OpenCV知识点总结:
调用Mat:
:
copyTo()函数时,如果需要有mask操作,则不管源图像是多少通道的,其mask矩阵都要定义为单通道,另外可以对一个mask矩阵画一个填充的矩形来达到使mask矩阵中对应ROI的位置的值为设定值,这样就不需要去一一扫描赋值了。
在使用OpenCV的Mat矩阵且需要对该矩阵进行扫描时,一定要注意其取值顺序,比如说列和行的顺序,如果弄反了,则经常会报内存错误。
实验结果
本实验并不要求人的手一定要放在人体的前面,且也不需要人一定是处在比较简单的背景环境中,本实验结果允许人处在复杂的背景环境下,且手可以到处随便移动。
当然了,环境差时有时候效果就不太好。
下面是3张实验结果的截图,手势分隔图1:
手势分隔图2:
手势分隔图3:
实验主要部分代码即注释(附录有工程code下载链接):
main.cpp:
#include
#include"opencv2/highgui/highgui.hpp"
#include"opencv2/imgproc/imgproc.hpp"
#include
#include"copenni.cpp"
#include
#defineDEPTH_SCALE_FACTOR255./4096.
#defineROI_HAND_WIDTH140
#defineROI_HAND_HEIGHT140
#defineMEDIAN_BLUR_K5
intXRES=640;
intYRES=480;
#defineDEPTH_SEGMENT_THRESH5
usingnamespacecv;
usingnamespacexn;
usingnamespacestd;
intmain(intargc,char**argv)
{
COpenNIopenni;
inthand_depth;
Rectroi;
roi.x=XRES/2;
roi.y=YRES/2;
roi.width=ROI_HAND_WIDTH;
roi.height=ROI_HAND_HEIGHT;
if(!
openni.Initial())
return1;
namedWindow("colorimage",CV_WINDOW_AUTOSIZE);
namedWindow("depthimage",CV_WINDOW_AUTOSIZE);
namedWindow("hand_segment",CV_WINDOW_AUTOSIZE);//显示分割出来的手的区域
if(!
openni.Start())
return1;
while
(1){
if(!
openni.UpdateData()){
return1;
}
/*获取并显示色彩图像*/
Matcolor_image_src(openni.image_metadata.YRes(),openni.image_metadata.XRes(),
CV_8UC3,(char*)openni.image_metadata.Data());
Matcolor_image;
cvtColor(color_image_src,color_image,CV_RGB2BGR);
circle(color_image,Point(hand_point.X,hand_point.Y),5,Scalar(255,0,0),3,8);
imshow("colorimage",color_image);
/*获取并显示深度图像*/
Matdepth_image_src(openni.depth_metadata.YRes(),openni.depth_metadata.XRes(),
CV_16UC1,(char*)openni.depth_metadata.Data());//因为kinect获取到的深度图像实际上是无符号的16位数据
Matdepth_image;
depth_image_src.convertTo(depth_image,CV_8U,DEPTH_SCALE_FACTOR);
imshow("depthimage",depth_image);
/*下面的代码是提取手的轮廓部分*/
hand_depth=hand_point.Z*DEPTH_SCALE_FACTOR;
roi.x=hand_point.X-ROI_HAND_WIDTH/2;
roi.y=hand_point.Y-ROI_HAND_HEIGHT/2;
if(roi.x<=0)
roi.x=0;
if(roi.x>=XRES)
roi.x=XRES;
if(roi.y<=0)
roi.y=0;
if(roi.y>=YRES)
roi.y=YRES;
//取出手的mask部分
//不管原图像时多少通道的,mask矩阵声明为单通道就ok
Mathand_segment_mask(depth_image.size(),CV_8UC1,Scalar:
:
all(0));
for(inti=roi.x;i:
min(roi.x+roi.width,XRES);i++)
for(intj=roi.y;j:
min(roi.y+roi.height,YRES);j++){
hand_segment_mask.at(j,i)=((hand_depth-DEPTH_SEGMENT_THRESH)(j,i))
&((hand_depth+DEPTH_SEGMENT_THRESH)>depth_image.at(j,i));
}
medianBlur(hand_segment_mask,hand_segment_mask,MEDIAN_BLUR_K);
Mathand_segment(color_image.size(),CV_8UC3);
color_image.copyTo(hand_segment,hand_segment_mask);
imshow("hand_segment",hand_segment);
waitKey(20);
}
}
copenni,cpp:
#ifndefCOPENNI_CLASS
#defineCOPENNI_CLASS
#include
#include
#include
usingnamespacexn;
usingnamespacestd;
staticDepthGeneratordepth_generator;
staticHandsGeneratorhands_generator;
staticXnPoint3Dhand_point;
staticstd:
:
map>hands_track_points;
classCOpenNI
{
public:
~COpenNI(){
context.Release();//释放空间
}
boolInitial(){
//初始化
status=context.Init();
if(CheckError("Contextinitialfailed!
")){
returnfalse;
}
context.SetGlobalMirror(true);//设置镜像
xmode.nXRes=640;
xmode.nYRes=480;
xmode.nFPS=30;
//产生颜色node
status=image_generator.Create(context);
if(CheckError("Createimagegeneratorerror!
")){
returnfalse;
}
//设置颜色图片输出模式
status=image_generator.SetMapOutputMode(xmode);
if(CheckError("SetMapOutputMdoeerror!
")){
returnfalse;
}
//产生深度node
status=depth_generator.Create(context);
if(CheckError("Createdepthgeneratorerror!
")){
returnfalse;
}
//设置深度图片输出模式
status=depth_generator.SetMapOutputMode(xmode);
if(CheckError("SetMapOutputMdoeerror!
")){
returnfalse;
}
//产生手势node
status=gesture_generator.Create(context);
if(CheckError("Creategesturegeneratorerror!
")){
returnfalse;
}
/*添加手势识别的种类*/
gesture_generator.AddGesture("Wave",NULL);
gesture_generator.AddGesture("click",NULL);
gesture_generator.AddGesture("RaiseHand",NULL);
gesture_generator.AddGesture("MovingHand",NULL);
//产生手部的node
status=hands_generator.Create(context);
if(CheckError("Createhandgeneraotrerror!
")){
returnfalse;
}
//产生人体node
status=user_generator.Create(context);
if(CheckError("Creategesturengeneratorerror!
")){
returnfalse;
}
//视角校正
status=depth_generator.GetAlternativeViewPointCap().SetViewPoint(image_generator);
if(CheckError("Can'tsetthealternativeviewpointondepthgenerator!
")){
returnfalse;
}
//设置与手势有关的回调函数
XnCallbackHandlegesture_cb;
gesture_generator.RegisterGestureCallbacks(CBGestureRecognized,CBGestureProgress,NULL,gesture_cb);
//设置于手部有关的回调函数
XnCallbackHandlehands_cb;
hands_generator.RegisterHandCallbacks(HandCreate,HandUpdate,HandDestroy,NULL,hands_cb);
//设置有人进入视野的回调函数
XnCallbackHandlenew_user_handle;
user_generator.RegisterUserCallbacks(CBNewUser,NULL,NULL,new_user_handle);
user_generator.GetSkeletonCap().SetSkeletonProfile(XN_SKEL_PROFILE_ALL);//设定使用所有关节(共15个)
//设置骨骼校正完成的回调函数
XnCallbackHandlecalibration_complete;
user_generator.GetSkeletonCap().RegisterToCalibrationComplete(CBCalibrationComplete,NULL,calibration_complete);
returntrue;
}
boolStart(){
status=context.StartGeneratingAll();
if(CheckError("Startgeneratingerror!
")){
returnfalse;
}
returntrue;
}
boolUpdateData(){
status=context.WaitNoneUpdateAll();
if(CheckError("Updatedateerror!
")){
returnfalse;
}
//获取数据
image_generator.GetMetaData(image_metadata);
depth_generator.GetMetaData(depth_metadata);
returntrue;
}
//得到色彩图像的node
ImageGenerator&getImageGenerator(){
returnimage_generator;
}
//得到深度图像的node
DepthGenerator&getDepthGenerator(){
returndepth_generator;
}
//得到人体的node
UserGenerator&getUserGenerator(){
returnuser_generator;
}
//得到手势姿势node
GestureGenerator&getGestureGenerator(){
returngesture_generator;
}
public:
DepthMetaDatadepth_metadata;
ImageMetaDataimage_metadata;
//staticstd:
:
map>hands_track_points;
private:
//该函数返回真代表出现了错误,返回假代表正确
boolCheckError(constchar*error){
if(status!
=XN_STATUS_OK){
//QMessageBox:
:
critical(NULL,error,xnGetStatusString(status));
cerr<"<returntrue;
}
returnfalse;
}
//手势某个动作已经完成检测的回调函数
staticvoidXN_CALLBACK_TYPECBGestureRecognized(xn:
:
GestureGenerator&generator,constXnChar*strGesture,constXnPoint3D*pIDPosition,
constXnPoint3D*pEndPosition,void*pCookie){
//COpenNI*openni=(COpenNI*)pCookie;
//openni->hands_generator.StartTracking(*pIDPosition);
hands_generator.StartTracking(*pIDPosition);
}
//手势开始检测的回调函数
staticvoidXN_CALLBACK_TYPECBGestureProgress(xn:
:
GestureGenerator&generator,constXnChar*strGesture,constXnPoint3D*pPosition,
XnFloatfProgress,void*pCookie){
//COpenNI*openni=(COpenNI*)pCookie;
//openni->hands_generator.StartTracking(*pPosition);
hands_generator.StartTracking(*pPosition);
}
//手部开始建立的回调函数
staticvoidXN_CALLBACK_TYPEHandCreate(HandsGenerator&rHands,XnUserIDxUID,constXnPoint3D*pPosition,
XnFloatfTime,void*pCookie){
//COpenNI*openni=(COpenNI*)pCookie;
XnPoint3Dproject_pos;
depth_generator.ConvertRealWorldToProjective(1,pPosition,&project_pos);
//openni->hand_point=project_pos;//返回手部所在点的位置
hand_point=project_pos;
pair>hand_track_point(xUID,vector());
hand_track_point.second.push_back(project_pos);
hands_track_points.insert(hand_track_point);
}
//手部开始更新的回调函数
staticvoidXN_CALLBACK_TYPEHandUpdate(HandsGenerator&rHands,XnUserIDxUID,constXnPoint3D*pPosition,XnFloatfTime,
void*pCookie){
//COpenNI*openni=(COpenNI*)pCookie;
XnPoint3Dproject_pos;
depth_generator.ConvertRealWorldToProjective(1,pPosition,&project_pos);
//openni->hand_point=project_pos;//返回手部所在点的位置
hand_point=project_pos;
hands_track_points.find(xUID)->second.push_back(project_pos);
}
//销毁手部的回调函数
staticvoidXN_CALLBACK_TYPEHandDestroy(HandsGenerator&rHands,XnUserIDxUID,XnFloatfTime,
void*pCookie){
//COpenNI*openni=(COpenNI*)pCookie;
//openni->hand_point.clear();//返回手部所在点的位置
hands_track_points.erase(hands_track_points.find(xUID));
}
//有人进入视野时的回调函数
staticvoidXN_CALLBACK_TYPECBNewUser(UserGenerator&generator,XnUserIDuser,void*p_cookie){
//得到skeleton的capability,并调用RequestCalibration函数设置对新检测到的人进行骨骼校正
generator.GetSkeletonCap().RequestCalibration(user,true);
}
//完成骨骼校正的回调函数
staticvoidXN_CALLBACK_TYPECBCalibrationComplete(SkeletonCapability&skeleton,
XnUserIDuser,XnCalibrationStatuscalibration_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);//骨骼校正失败时重新设置对人体骨骼继续进行校正
}
}
private:
XnStatusstatus;
Contextcontext;