王明东
基于Kinect骨骼跟踪功能实现PC的手势控制
王明东1,2
(1.华侨大学 计算机学院,福建 厦门 361000;2.黎明职业大学 计算机与信息工程系,福建 泉州 362000)
主要介绍了基于Kinect骨骼跟踪功能开发一套手势模拟鼠标操作的设计思路与实现方法,以实现对计算机的远距离手势遥控。
Kinect;骨骼跟踪;手势控制
微软几年前就预测计算领域的下一个重大变革是图形用户界面向自然用户界面的转移,Kinect便是自然用户界面应用的成功案例。Kinect是一种3D体感摄影机主要用来进行体感游戏动作捕捉。Kinect所有动作捕捉和识别都是做在硬件设备里面,且微软并没有对Kinect的输出做任何加密。于是,基于Kinect的各种应用层出不穷,如:虚拟试衣间、体感购物、Kinect Robot等。随着2011年6月微软推出Kinect for Windows SDK Beta,基于Kinect应用开发成为计算机应用领域研究的一个热点。
Kinect有三个Camera,其中中间的一个是RGB Camera,用来获取640x480的彩色图像,每秒钟最多获取30帧图像;两侧是两个景深(3D Depth)传感器,使用红外线检测玩家的相对位置,原理和人眼立体成像是一样的;Kinect两侧各有两组麦克风用来做3D语音识别,能够把声源附近之内各种各样信息捕捉到;下边底座内有一个马达用来调整Kinect的仰角。【1】
2.2.1获取原始传感器流
从深度传感器,彩色摄像头传感器,和四个麦克风阵列,获得原始数据流。使开发人员能够基于低级别的Kinect感应器生成的数据流进行开发。
2.2.2骨骼跟踪(Skeleton Tracking)
跟踪Kinect的监测领域内的一个或两个人的骨架图像的能力,使创建手势驱动的应用程序可以很容易。Kinect最多可以追踪每个人的20个骨骼点,目前只能追踪人体[2]。是Kinect的核心技术,正因为有了这项技术,很多有趣的功能才得以实现。图2介绍了Kinect骨骼点的分布情况。
2.2.3先进的音频能力
音频处理能力包括先进的噪音抑制和回声消除,波束形成以确定当前的声源,并与Windows语音识别API集成。
图1 Kinect整体结构
图2 Kinect骨骼点的分布情况
图3 屏幕区域划分
运用Kinect的骨骼跟踪(Skeleton Tracking)功能,捕获操作者骨骼点的运动,并将相关骨骼点的运动信息通过API函数SendInput映射成鼠标的相关动作,从而实现手势操控。具体方法:
①将左手的移动转换成鼠标的移动。
②将右手在不同区域的上下挥动转换成鼠标的左右键单击,首先将计算机的屏幕划分为四个区域如图3。当右手在右键区域位置高于AB时表示为按下鼠标右键,再回落到AB下面时表示放开鼠标右键;当右手在左键区域位置高于AB时表示为按下鼠标左键,再回落到AB下面时表示放开鼠标左键;如此一上一下表示一次单击。
③以左右滕盖的距离(大于一个阀值)表示启用滚轮, 右手水平挥动以CD为界一个来回表示一次后滚;右手上下挥动以AB为界一个来回表示一次前滚。
图4SkeletonFrameReady参数对象结构
当Kinect正确识别操作者时SkeletonFrameReady事件被触发来处理骨骼跟踪,该事件的参数包含两个重要的对象:SkeletonFrame和Skeletons(SkeletonData对象)(参见图4[3])。在SkeletonData对象的Joints属性集合中保存了所有骨骼点的信息。每个骨骼点的信息都是一个Joint对象,其中的Position的X、Y、Z表示了三维位置。其中X和Y的范围都是-1到1,而Z是Kinect到识别对象的距离。
基于Kinect开发一套手势模拟鼠标操作,要解决的关键问题:将骨骼点的平面坐标X和Y的取值[-1,1]转换成在屏幕的位置值[4]。可以用下面的代码,将Joint的位置缩放到合适的比例:
Joint scaledLeft = skeleton.Joints[JointID.HandLeft].ScaleTo((int)SystemParameters.PrimaryScreenWidth, (int)SystemParameters.PrimaryScreenHeight, SkeletonMaxX, SkeletonMaxY);
上面的语句相当于将骨骼点的平面坐标,X:由-SkeletonMaxX到SkeletonMaxX的范围扩大为0到屏幕的宽度的范围,Y:由-SkeletonMaxY到SkeletonMaxY的范围扩大为0到屏幕的高度的范围。
下面是在VS2010环境下运用WPF实现手势模拟鼠标移动、左右键单击的源代码。
3.3.1鼠标控制类代码:
//定义鼠标数据结构
internal struct MouseInput
{ public int X;
public int Y;
public uint MouseData;
public uint Flags;
public uint Time;
public IntPtr ExtraInfo;
}
internal struct Input
{ public int Type;
public MouseInput MouseInput;
}
public static class MouseSetting
{ public const int InputMouse = 0;
public const int MouseEventMove = 0x01;
public const int MouseEventKeyDown = 0x02;
public const int MouseEventLeftUp = 0x04;
public const int MouseEventRightDown = 0x08;
public const int MouseEventRightUp = 0x10;
private static bool lastDown; //标识鼠标键的前一状态, true表示按下
[DllImport("user32.dll", SetLastError = true)]
private static extern uint SendInput(uint numInputs, Input[] inputs, int size);
public static void SendMouseInput(int positionX, int positionY, int maxX, int maxY, bool MouseClick, int keyType)
// positionX, positionY:鼠标位置;maxX, maxY:鼠标移动范围;MouseClick:是否按下键; keyType:标识左右键
{ Input[] i = new Input[2];
//移动鼠标
i[0] = new Input();
i[0].Type = InputMouse;
i[0].MouseInput.X = (positionX * 65535) / maxX;
i[0].MouseInput.Y = (positionY * 65535) / maxY;
i[0].MouseInput.Flags = MouseEventAbsolute | MouseEventMove;
//判断要发送的鼠标事件
if (keyType == 1)
{ if (!lastDown && MouseClick)
{ i[1] = new Input();
i[1].Type = InputMouse;
i[1].MouseInput.Flags = MouseEventKeyDown; }
else if (lastDown && ! MouseClick)
{ i[1] = new Input();
i[1].Type = InputMouse;
i[1].MouseInput.Flags = MouseEventLeftUp; }
lastDown = MouseClick; //记录当前鼠标状态
}
else
{ if (!lastDown && MouseClick)
{ i[1] = new Input();
i[1].Type = InputMouse;
i[1].MouseInput.Flags = MouseEventRightDown; }
else if (lastDown && ! MouseClick)
{ i[1] = new Input();
i[1].Type = InputMouse;
i[1].MouseInput.Flags = MouseEventRightUp; }
lastDown = MouseClick;
}
// 将鼠标事件通知操作系统
uint result = SendInput(2, i, Marshal.SizeOf(i[0]));
if (result == 0)
throw new Win32Exception(Marshal.GetLastWin32Error());
}}
3.3.2骨骼跟踪代码:
public partial class MainWindow : Window
{ private float MouseDownThreshold = 0.10f;//将屏幕分为按下与放开区域(图4 AB的位置)。
private float LeftRightThreshold = -0.12f;//将屏幕分为左右键区域(图4 CD的位置)。
private const float SkeletonMaxX = 0.50f;//界定骨骼跟踪的水平范围
private const float SkeletonMaxY = 0.33f; //界定骨骼跟踪的垂直范围
private Runtime _runtime = new Runtime(); //声明并实例化一个kinect运行时对象
private int userid = -1;//用于记录用户的userindex
private void Window_Loaded(object sender, RoutedEventArgs e)
{ //指定用于骨骼跟踪的用户处理事件
_runtime.SkeletonFrameReady += _runtime_SkeletonFrameReady;
try { // 初始化Kinect对象,并告诉Kinect将用到景深与骨骼信息
_runtime.Initialize(RuntimeOptions.UseDepth|RuntimeOptions.UseSkeletalTracking);
} catch (Exception ex)
{ MessageBox.Show("Could not initialize Kinect device: " + ex.Message); }
}
void _runtime_SkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e)
{ SkeletonData sd = e.SkeletonFrame.Skeletons.First();
//将第一个被捕获者固定为操控者
if (userid == -1) { userid = sd.UserIndex; }
foreach (SkeletonData sd1 in e.SkeletonFrame.Skeletons)
{ if (sd1.TrackingState == SkeletonTrackingState.Tracked)
{ if (userid == 0)
{ userid = sd1.UserIndex;
sd = sd1;
break; }
else
if (sd1.UserIndex == userid)
{ sd = sd1;
break; }
} }
//确保双手被跟踪
if (sd.Joints[JointID.HandLeft].TrackingState == JointTrackingState.Tracked &&
sd.Joints[JointID.HandRight].TrackingState == JointTrackingState.Tracked)
{ int cursorX, cursorY;
//取得左右手的骨骼点
Joint jointRight = sd.Joints[JointID.HandRight];
Joint jointLeft = sd.Joints[JointID.HandLeft];
//将左手的骨骼点的位置信息换算成对应的主屏幕的宽度和高度
Joint scaledLeft = jointLeft.ScaleTo((int)SystemParameters.PrimaryScreenWidth, (int)SystemParameters.PrimaryScreenHeight, SkeletonMaxX, SkeletonMaxY);
//根据左手的位置找出鼠标光标的位置
cursorX = (int)scaledLeft.Position.X;
cursorY = (int)scaledLeft.Position.Y;
bool MouseClick; //标识是否按下鼠标键
int keyType; //标识鼠标左右键
//根据右手的位置找出鼠标左/右键是否按下
if (jointRight.Position.Y > MouseDownThreshold)
MouseClick = true;
else
MouseClick = false;
if (jointRight.Position.X < LeftRightThreshold)
keyType = 1; //左键
else
keyType = 2; //右键
SendMouseInput(cursorX, cursorY, (int)SystemParameters.PrimaryScreenWidth, (int)SystemParameters.PrimaryScreenHeight, MouseClick, keyType);
return;
} }
Kinect在精确定位上尚有不足;但基于Kinect骨骼跟踪实现的手势模拟鼠标操作可以通过悬停选择和手势控制等简单且容易理解的控制方式方便的实现计算机的遥感控制,据此可以开发出很多有趣的应用。
[1] 马宁博客:http://www.cnblogs.com/aawolf/archive/2011/06/17/2083249.html[EB/OL].
[2] Kinect SDK的开发指南:http://research.microsoft.com/en-us/um/redmond/projects/kinectsdk/guides.aspx[EB/OL].
[3] Kinect SDK API Reference[EB/OL].
[4] Kinect SDK的官方论坛:http://social.msdn.microsoft.com/Forums/en-US/kinectsdk/threads[EB/OL].
Virtualizing gesture control of PC by Skeleton Tracking through Kinect
WANG Ming-dong1,2
(1.School of Computer Science,Huaqiao University,Xiamen 361000,China;2.Computer and Information Engineering department; Liming Vocational University; Quanzhou Fujian 362000)
The article explores the application of gesture-imitating mouse action by Skeleton Tracking through Kinect, to realize the remote gesture control of PC.
Kinect;Skeleton Tracking;Gesture control
(责任编辑:季平)
2012-03-10
王明东(1976—),男,福建泉州人,讲师,在读硕士,主要从事管理信息系统方面的研究。
TP312
A
1673-1417(2012)02-0011-06