为什么在kinect设计空气鼠标的时候要进行坐标转换
opennig time :开放时间
没有KINECT API吗?应该有说明
最近Kinect连接Xbox玩水果忍者的视频非常红火,可惜小斤只有本本和Kinect,没法玩Xbox上的体感游戏。幸运的是,寻寻觅觅后,小斤发现水果忍者有PC版本,既然上一个教程我们已经可以让Kinect认出我们手势,在这基础上,我们用手来控制鼠标,就可以在PC上玩咯!
视频地址:http://v.youku.com/v_show/id_XMjk2OTU3MjYw.html,徒手切还需要多练练。
上个教程,我们通过RaiseHand来捕捉举起后手的位置,于是小斤决定,用RaiseHand来触发鼠标移动事件,用Click来触发鼠标单击,但测试结果不让人满意,鼠标移动一卡一卡的,原因是RaiseHand识别需要时间,达不到实时的标准,怎么办呢?小斤翻阅了OpenNI的文档,找到了tracking的相关API。这样,在我们识别出手后,使用跟踪的办法得到手的实时位置,移动鼠标的问题迎刃而解!这就好比在茫茫人海中,跟着一个人走比找到一个人更容易!
因为这个教程代码量稍微多了点,小斤就不一股脑全抛上来了,先上主函数,再解释回调函数。
【1】程序执行后,窗体和显示和上一个手势识别的例程是一样的,由于要使用mouse_event来控制鼠标,小斤选择了MFC框架,主要是一个Dialog和一个按钮。由于我们的程序执行时,是使用OpenCV的highgui进行图像显示的,这里点击窗体上按钮后创建并开始线程,线程函数KinectGestureMain(),也就是这里的主函数。
【2】KinectGestureMain()中的内容,大部分和上一个例程是一样的,在【2】这里,小斤创建了一个HandsGenerator,这个生成器主要帮我们负责跟踪的工作。它的创建方法和其它的生成器是一样的,传一个Context给Create()方法
【3】与GestureGenerator类似,我们需要为HandsGenerator注册回调函数,
[cpp] view plain copy
XnStatusxn::HandsGenerator::RegisterHandCallbacks ( HandCreate CreateCB, HandUpdate UpdateCB, HandDestroy DestroyCB, void * pCookie, XnCallbackHandle & hCallback )
其中定义HandCreate是一个新的手(跟踪)被创建时调用的,HandDestroy 则相反,在手消失后被调用,UpdateCB在手变化位置时被调用。另外,pCookie是传给回调函数的指针,可以放一些用户数据,小斤把程序的画板图像指针传入,这样可在回调函数中直接绘图了。phCallback是一个回调函数的handle,可用来注销回调函数。
【4】这边设定了,按m键,进入鼠标控制模式。
其它代码都和上一个例程差不多,我们来看看回调函数。
[cpp] view plain copy
// callback function for gesture recognized
void XN_CALLBACK_TYPEGRecognized( xn::GestureGenerator &generator,
const XnChar *strGesture,
const XnPoint3D *pIDPosition,
const XnPoint3D *pEndPosition,
void *pCookie )
{
int imgStartX=0;
int imgStartY=0;
int imgEndX=0;
int imgEndY=0;
//【5】
imgStartX=(int)(640/2-(pIDPosition->X));
imgStartY=(int)(480/2-(pIDPosition->Y));
imgEndX=(int)(640/2-(pEndPosition->X));
imgEndY=(int)(480/2-(pEndPosition->Y));
IplImage* refimage=(IplImage*)pCookie;
if(strcmp(strGesture,"Wave")==0)
{
cvLine(refimage,cvPoint(imgStartX,imgStartY),cvPoint(imgEndX,imgEndY),CV_RGB(255,255,0),6);
//【6】
handsGenerator.StartTracking(*pEndPosition);
}
else if(strcmp(strGesture,"Click")==0)
{
cvCircle(refimage,cvPoint(imgStartX,imgStartY),6,CV_RGB(0,0,255),12);
//【7】
if(isRealMouseControl)
{
messageHandler(cvPoint(imgStartX,imgStartY),0,REAL_MOUSE_CLICK);
}
}
}
// callback function forgesture progress
void XN_CALLBACK_TYPEGProgress( xn::GestureGenerator &generator,
const XnChar *strGesture,
const XnPoint3D *pPosition,
XnFloat fProgress,
void *pCookie )
{
}
【5】由于pIDPosition和pEndPosition的坐标,是以屏幕中心为(0,0)点的坐标系,在OpenCV中的显示,是以屏幕左上角为(0,0)点,所以这里做了一个转换。
【6】这一段代码,我们的GestureGenerator识别出“挥动”手势后,调用了HandsGenerator的StartTracking()方法来开始在pEndPosition这个位置跟踪手,pEndPosition便是“挥动”“手势的结束位置。
【7】中,如果识别出了“前推”手势,并开启了鼠标控制模式,就调用小斤定义的messageHandler()方法模拟鼠标点击。这个messageHandler()待会会在AppMessage.cpp中实现。
接着,小斤实现了Hands相关的回调函数:
[cpp] view plain copy
//【8】
void XN_CALLBACK_TYPEHand_Create(xn::HandsGenerator& generator,XnUserID nId,const XnPoint3D*pPosition, XnFloatfTime, void*pCookie)
{
addTrackingId(nId);
}
void XN_CALLBACK_TYPEHand_Update(xn::HandsGenerator& generator,XnUserID nId,const XnPoint3D*pPosition, XnFloatfTime, void*pCookie)
{
int imgPosX=0;
int imgPosY=0;
char locationinfo[100];
imgPosX=(int)(640/2-(pPosition->X));
imgPosY=(int)(480/2-(pPosition->Y));
IplImage* refimage=(IplImage*)pCookie;
cvSetImageROI(refimage,cvRect(40,450,640,30));
CvFont font;
cvInitFont( &font,CV_FONT_VECTOR0,1, 1, 0, 3, 5);
cvSet(refimage,cvScalar(255,255,255));
if(isRealMouseControl)
{
sprintf(locationinfo,"MouseCtrl: %dth HandLoc: %d,%d",nId,(int)pPosition->X,(int)pPosition->Y);
}
else
{
sprintf(locationinfo,"Normal: %dth HandLoc: %d,%d",nId,(int)pPosition->X,(int)pPosition->Y);
}
cvPutText(refimage,locationinfo ,cvPoint(30,30), &font, CV_RGB(0,0,0));
cvResetImageROI(refimage);
CvPoint thisLocation=cvPoint(imgPosX,imgPosY);
//【9】
if(isRealMouseControl)
{
//cvCircle(refimage,cvPoint(imgPosX,imgPosY),1,CV_RGB(255,0,0),2);
messageHandler(thisLocation,nId,REAL_MOUSE_MOVE);
}
else
{
cvCircle(refimage,cvPoint(imgPosX,imgPosY),1,CV_RGB(255,0,0),2);
}
}
void XN_CALLBACK_TYPEHand_Destroy(xn::HandsGenerator& generator,XnUserID nId,XnFloat fTime,void* pCookie)
{
//printf("Lost Hand: %d
", nId);
removeTrackingId(nId);
}
【8】中的Hand_Create()就是CreateCB,该回调函数定义如下:
[cpp] view plain copy
void XN_CALLBACK_TYPEHand_Create(xn::HandsGenerator& generator,XnUserID nId,const XnPoint3D*pPosition, XnFloatfTime, void*pCookie)
其中,generator指定触发Hands的生成器。
nId为被创建的手(跟踪)的id,这个id会随着手的不断创建和消失增加,用以标识每一个手。
pPosition是手当前的位置,fTime是一个Timestamp,而pCookie就是用户传入的数据指针了。
在该方法中,小斤使用了自己定义的addTracking()方法,该方法也在AppMessage.cpp中实现。小斤在该文件中维护了一个数组,用以标识每个id的手是否在跟踪状态。
【9】Hand_Update()和Hand_Destroy()的参数和Hand_Create()是一样的,除去一堆绘图过程之外,小斤使用messageHandler()方法模拟鼠标移动。
以下是AppMessage.cpp的内容:
[cpp] view plain copy
#include "AppMessage.h"
//Location and move anglelast time for each userId(Hand Id)
CvPoint lastLocation[MAX_HAND_NUM];
int isHandTracking[MAX_HAND_NUM]={0};
int isClickDown=0;
void addTrackingId(int userId)
{
isHandTracking[userId]=1;
}
void removeTrackingId(int userId)
{
isHandTracking[userId]=0;
}
CvPoint getLastLocation(int userId)
{
return lastLocation[userId];
}
void messageHandler(CvPoint &location,int userId,int flag)
{
//initialize the lastLocation from the location obtained bythe first time
if(lastLocation[userId].x==0&&lastLocation[userId].y==0)
{
lastLocation[userId].x=location.x;
lastLocation[userId].y=location.y;
}
if(flag==REAL_MOUSE_CLICK)
{
if(!isClickDown)
{
mouse_event(MOUSEEVENTF_LEFTDOWN,0,0,0,0);
}
else {
mouse_event(MOUSEEVENTF_LEFTUP,0,0,0,0);
}
isClickDown=1-isClickDown;
}
else if(flag==REAL_MOUSE_MOVE)
{
//【10】
int firstHandId=-1;
for(int i=0;i<MAX_HAND_NUM;i++)
{
if(isHandTracking[i]!=0)
{
if(firstHandId==-1)
{
firstHandId=i;
break;
}
}
}
if(abs(location.x-lastLocation[userId].x)<5)
{
location.x=lastLocation[userId].x;
}
if(abs(location.y-lastLocation[userId].y)<5)
{
location.y=lastLocation[userId].y;
}
//【11】
if(userId==firstHandId)
{
mouse_event(MOUSEEVENTF_ABSOLUTE|MOUSEEVENTF_MOVE,
(location.x-160)*65536/640*2,(location.y-120)*65536/480*2,0,0);
}
}
lastLocation[userId].x=location.x;
lastLocation[userId].y=location.y;
}
【10】根据程序维护的isHandTracking数组,找到被跟踪的第一个手。
【11】考虑到可能有多个手被捕捉并跟踪的情况,先来先得,这里小斤让鼠标只听第一个出现的手的指挥,进行移动。
这里使用了MOUSEEVENTF_ABSOLUTE,所谓的绝对坐标,就是把计算机屏幕定义为65536*65536个点的坐标系。如果分辨率为640*480,想把鼠标移动到屏幕正中,就可以调用mouse_event(MOUSEEVENTF_ABSOLUTE|MOUSEEVENTF_MOVE,320/640*65536,240/480*65536,0,0);
这段代码中,由于OpenNI输出的分辨率为640*480,而小斤习惯抬起右手来切水果,所以只使用了右半边区域来控制鼠标,同时做了些微调,大家可以根据自己的喜好来设置。
另外,在水果忍者PC版中,切的动作是按住鼠标左键(MOUSE_LEFTDOWN)并移动(MOUSE_MOVE)来触发的。因此,小斤在程序中使用push手势来切换MOUSE_LEFTDOWN和MOUSE_LEFTUP。
使用方法如下:打开程序,按下M键进入鼠标控制模式,进入游戏后,push一下,使鼠标处于MOUSE_LEFTDOWN状态,屏幕就会出现刀光剑影咯,不想玩的时候,再push一下即可。
好了,小斤要去切几盘水果休息一下咯,希望这篇文章对大家有所帮助和启发。
为什么在kinect设计空气鼠标的时候要进行坐标转换视频
相关评论:
颜鬼冰游戏与迪士尼皮克斯的最新动画电影同步推出,于2012年6月19日震撼上市。在游戏世界里,玩家将扮演主角,踏上一段冒险之旅。特别值得一提的是,游戏设计独特,允许第二名玩家以Will o' the Wisp的身份加入,运用空气魔法协助主角,增加了游戏的互动性和趣味性。对于Xbox 360和PS3的爱好者,游戏提供了虚拟...
颜鬼冰硬盘的作用:1、装在硬盘里面,可以省光驱,因为游戏运行后基本上就是从硬盘读取了,光驱不怎么读。所以大大的减少了光头的使用率,所以用硬盘来玩游戏比不用硬盘来玩光驱寿命要长很多。2、降低发热率 3、有效的防尘,由于光驱盘高速转动,所以会把空气中的尘土甩到光驱的周围,时间长了就会越积越多,...
颜鬼冰Xbox One与Xbox One S的区别:1、发售日期不同:Xbox One: 发售日期为2013年11月22日。Xbox One S: 发售日期为2016年8月2日。2、官方定价不同:Xbox One : 官方售价$499 (500 G同捆Kinect)。Xbox One S: 官方售价$249 (500 G) \/ $349 (1 T) \/ $399 (2 T)。3、制程工艺不同...
颜鬼冰在空气中指指点点就能完成对电脑的操作,是不是很神奇?很多人也将此视为未来人机交互最重要的一种方式。但遗憾的是,这种力反馈技术目前还很不完善,不过在我看来,即便在iPad身上实现不了如此炫酷的手势操作,可如果能搭载一个类似微软Kinect体感设备上的摄像头,也足以让众多追求新奇玩意的消费者为之掏腰包了。 另外...
颜鬼冰这也提醒我们在进行车载语音交互的设计时候,要尽量避免信息过载的情况。 手势交互设计 近年来,手势交互技术也逐渐成熟,相关产品也不断涌现,如任天堂 Wii 和微软 Kinect等已经彻底地改变了传统游戏的交互方式,这些设备能够识别常见的手势和身体姿势,而汽车手势交互作为一个较新的交互设计领域,也为交互设计提供了新的...
颜鬼冰不过它有一个缺点:暴露在氧气中很容易燃烧,哪怕在水中也是如此。这太糟糕了,因为能在空气或水中利用锂产生电的电池会提供比通常手机中的锂离子电池更多的电能。这正是史蒂夫·维斯科和同事在PolvPlus公司所发明的东西:锂水电池。PolvPlus制造了一种薄膜,将锂密封起来,使之不与水接触,从而防止...
颜鬼冰例如,在一个完整周期中,两次发射激光与两次曝光,通过对比两次曝光的输出电压,可计算出光反射的时间和目标距离。TOF深度相机的性能受多种因素影响,如双脉冲调制方式的最小深度限制(大于0,取决于噪声和系统误差)、最大深度(由激光脉冲宽度决定,通常在空气中的最大深度可达7.5m),以及反射强度测量...
颜鬼冰室内滑雪模拟器:高科技的室内滑雪模拟器可以在不受季节限制的情况下练习滑雪技巧。健身游戏:随着科技的发展,许多健身游戏如wii fit、xbox kinect等提供了在家中也能进行互动式锻炼的方式。普拉提:普拉提是一种注重核心肌群训练的室内运动,它能够帮助改善姿势、增强肌肉力量和提高身体控制能力。跳绳:跳绳...
颜鬼冰水墨山水,以虚代实,侧重笔墨神韵。在山水作品《踪迹》中,人体感应作画,为写意增添了点睛一笔。山水画的墨迹随着行人的走近而出现,随着人们的走远而消逝。在墨色浮现和消失中,模糊画作的观者与作者的界限。本作品是中央美术学院设计师魏启龙以arduino +processing+kinect 为框架进行设计和开发的软...
颜鬼冰Beamatron渲染不同表面投影图像的技术(IllumiRoom用到)。悬浮图像显示:在空气介质中显示图像,可以用于非接触的手势交互界面,悬浮对象也可以在原表面产生阴影(类似全息投影)。Digits佩戴于手腕处的3D空间交互技术。Kinect手语翻译。远距离虚拟拥抱:通过将物理交互带至远距离沟通。这里的物理反馈不仅仅是...