Leap Motion 入门二:官方Sample个人解读
2014-11-03 18:11
190 查看
刚刚入门Leap Motion, 跑了LeapSDK中的Sample。这个Sample结构十分好,Leap Motion的API简洁而又强大。
我将注意力集中在onFrame()中,以下是我对这个例子的粗略解读。代码片后有一些调用方法和类的Reference,便于理解。
如有什么理解不当的地方,还请多多指正,互相交流学习。
手势介绍(Gesture)
class Leap::CircleGesture
代表了画圆的手势。
在用这个手势之前,必须在onConnect()中激活此手势。
Controller.enableGesture(Gesture::TYPE_CIRCLE);
状态:
State::STATE_START(刚开始), State::STATE_UPDATE(正在执行), State::STATE_STOP(结束)
可以设置最小的弧度和最小的角度来判断是否为一个TYPE_CIRCLE
controller.config().setFloat("Gesture.Circle.MinRadius",
10.0);
controller.config().setFloat("Gestrure.Circle.MinArc",
.5):
controller.config().save();
Vector normal(): 返回被追踪圆的法向量
Pointable pointable(): 返回画圆的Finger
class Leap::SwipeGesture
代表横扫的手势。
在用这个手势之前,必须在onConnect()中激活此手势。
Controller.enableGesture(Gesture::TYPE_SWIPE);
可以设置最小的距离和最小的速度来判断是否为一个TYPE_SWIPE
controller.config().setFloat("Gesture.Swipe.MinLength", 200.0);
//单位是mm
controller.config().setFloat("Gestrure.Circle.MinVelocity",
750): //单位是mm/s
controller.config().save();
class Leap::ScreenTapGesture
代表手指或者工具点击屏幕的动作。手指是不动的。
在用这个手势之前,必须在onConnect()中激活此手势。
Controller.enableGesture(Gesture::TYPE_SCREEN_TAP);
可以设置最小的手指移动距离,一段时间的最小移动速度来判断是否为一个TYPE_SCREEN_TAP
controller.config().setFloat("Gesture.ScreenTap.MinForwardVelocity", 30.0); // 单位是mm/s,Default
value为50
controller.config().setFloat("Gesture.ScreenTap.HistorySeconds",
.5): //单位是s, Default value为0.1
controller.config().setFloat("Gesture.ScreenTap.MinDistance", 1.0); // 单位是mm, Default value为5.0
controller.config().save();
class Leap::KeyTapGesture
代表手指点击键的动作。手指需要上下移动。
在用这个手势之前,必须在onConnect()中激活此手势。
Controller.enableGesture(Gesture::TYPE_KEY_TAP);
可以设置最小的手指移动距离,一段时间的最小手指按下键的运动速度来判断是否为一个TYPE_KEY_TAP
controller.config().setFloat("Gesture.KeyTap.MinDownVelocity", 40.0); // 单位是mm/s,Default
value为50
controller.config().setFloat("Gesture.KeyTap.HistorySeconds",
.2): //单位是s, Default value为0.1
controller.config().setFloat("Gesture.KeyTap.MinDistance",
8.0); // 单位是mm, Default value为3.0
controller.config().save();
PolicyFlag
介绍
void
setPolicyFlags(PolicyFlag flags)
用于改变请求策略。
请求策略的分类:
1.POLICY_BACKGROUND_FRAMES ==
(1<<0)
程序在后台运行时leap
motion依然获取数据信息。在leap motion的控制面板中也要将“允许后台应用程序”的设置开启。
2.POLICY_IMAGES ==
(1<<1)
程序在执行时同时也获取image信息。默认是不获取的,请求这项时同时也要在控制面板中将“允许图像”打勾。
3.POLICY_OPTIMIZE_HMD ==
(1<<2)
程序在执行时对头戴设备进行优化。首先你要有一个头戴式设备。
4.POLICY_DEFAULT
== 0
默认,关闭所有请求。
pitch, yaw, roll 介绍
在LeapMotion中,坐标的表示如下:
pitch() 俯仰。 代表物体绕X轴上下旋转。
想象一个飞机,机头指向Z的方向,pitch就代表飞机上升下降的角度。
yaw() 航向。 指物体绕Y轴旋转。机头左右偏转的角度,代表飞行的方向。
roll() 横滚。 指物体绕Z轴旋转。机身旋转的角度,或者说机翼水平方向上偏转的角度。
我将注意力集中在onFrame()中,以下是我对这个例子的粗略解读。代码片后有一些调用方法和类的Reference,便于理解。
如有什么理解不当的地方,还请多多指正,互相交流学习。
<span style="font-family:Courier New;">/******************************************************************************\ * Copyright (C) 2012-2014 Leap Motion, Inc. All rights reserved. * * Leap Motion proprietary and confidential. Not for distribution. * * Use subject to the terms of the Leap Motion SDK Agreement available at * * https://developer.leapmotion.com/sdk_agreement, or another agreement * * between Leap Motion and you, your company or other organization. * \******************************************************************************/ #include <iostream> #include <string.h> #include "Leap.h" #include <windows.h> // 打开Sleep(3000); using namespace Leap; // 生命周期 class SampleListener : public Listener { public: virtual void onInit(const Controller&); virtual void onConnect(const Controller&); virtual void onDisconnect(const Controller&); virtual void onExit(const Controller&); virtual void onFrame(const Controller&); virtual void onFocusGained(const Controller&); virtual void onFocusLost(const Controller&); virtual void onDeviceChange(const Controller&); virtual void onServiceConnect(const Controller&); virtual void onServiceDisconnect(const Controller&); private: }; const std::string fingerNames[] = {"Thumb", "Index", "Middle", "Ring", "Pinky"}; const std::string boneNames[] = {"Metacarpal", "Proximal", "Middle", "Distal"}; const std::string stateNames[] = {"STATE_INVALID", "STATE_START", "STATE_UPDATE", "STATE_END"}; // 初始化时 void SampleListener::onInit(const Controller& controller) { std::cout << "Initialized" << std::endl; } // 连接时 void SampleListener::onConnect(const Controller& controller) { std::cout << "Connected" << std::endl; // 打开对所有手势的识别 controller.enableGesture(Gesture::TYPE_CIRCLE); controller.enableGesture(Gesture::TYPE_KEY_TAP); controller.enableGesture(Gesture::TYPE_SCREEN_TAP); controller.enableGesture(Gesture::TYPE_SWIPE); } // 断开连接时 void SampleListener::onDisconnect(const Controller& controller) { // Note: not dispatched when running in a debugger. std::cout << "Disconnected" << std::endl; } // 退出时 void SampleListener::onExit(const Controller& controller) { std::cout << "Exited" << std::endl; } // 核心,获取一帧时 void SampleListener::onFrame(const Controller& controller) { // Get the most recent frame and report some basic information // 首先获取frame信息 const Frame frame = controller.frame(); std::cout << "Frame id: " << frame.id() << ", timestamp: " << frame.timestamp() << ", hands: " << frame.hands().count() << ", fingers: " << frame.fingers().count() << ", tools: " << frame.tools().count() << ", gestures: " << frame.gestures().count() << std::endl; // 从frame中获取所有手的HandList, 遍历HandList获取每只手Hand HandList hands = frame.hands(); for (HandList::const_iterator hl = hands.begin(); hl != hands.end(); ++hl) { // Get the first hand const Hand hand = *hl; std::string handType = hand.isLeft() ? "Left hand" : "Right hand"; std::cout << std::string(2, ' ') << handType << ", id: " << hand.id() << ", palm position: " << hand.palmPosition() << std::endl; // Get the hand's normal vector and direction // 获取手掌法向量和手掌的方向, 其中 hand.direction() is expressed as a unit vector pointing in the same direction as the directed line from the palm position to the finger. const Vector normal = hand.palmNormal(); const Vector direction = hand.direction(); // Calculate the hand's pitch, roll, and yaw angles // pitch yaw roll的介绍在代码片后方 std::cout << std::string(2, ' ') << "pitch: " << direction.pitch() * RAD_TO_DEG << " degrees, " << "roll: " << normal.roll() * RAD_TO_DEG << " degrees, " << "yaw: " << direction.yaw() * RAD_TO_DEG << " degrees" << std::endl; // 通过 Hand 获取 Arm 信息 Arm arm = hand.arm(); std::cout << std::string(2, ' ') << "Arm direction: " << arm.direction() << " wrist position: " << arm.wristPosition() << " elbow position: " << arm.elbowPosition() << std::endl; // 通过 Hand 获取 FingerList, 遍历 FingerList 获取每个 Finger 的信息 const FingerList fingers = hand.fingers(); for (FingerList::const_iterator fl = fingers.begin(); fl != fingers.end(); ++fl) { const Finger finger = *fl; std::cout << std::string(4, ' ') << fingerNames[finger.type()] << " finger, id: " << finger.id() << ", length: " << finger.length() << "mm, width: " << finger.width() << std::endl; // 手指骨的介绍见其他本博客的Leap Motion的入门简介 for (int b = 0; b < 4; ++b) { Bone::Type boneType = static_cast<Bone::Type>(b); Bone bone = finger.bone(boneType); std::cout << std::string(6, ' ') << boneNames[boneType] << " bone, start: " << bone.prevJoint() << ", end: " << bone.nextJoint() << ", direction: " << bone.direction() << std::endl; } } } // 从 frame 中获取 ToolList, 遍历 ToolList 获取每个 Tool的信息 const ToolList tools = frame.tools(); for (ToolList::const_iterator tl = tools.begin(); tl != tools.end(); ++tl) { const Tool tool = *tl; std::cout << std::string(2, ' ') << "Tool, id: " << tool.id() << ", position: " << tool.tipPosition() << ", direction: " << tool.direction() << std::endl; } // 从Frame 中获取 Gesture信息 const GestureList gestures = frame.gestures(); for (int g = 0; g < gestures.count(); ++g) { Gesture gesture = gestures[g]; switch (gesture.type()) { // 如果是 CIRCLE 的手势 case Gesture::TYPE_CIRCLE: { CircleGesture circle = gesture; std::string clockwiseness; /* circle.pointable() 代表画圈的那个 finger, finger继承自Pointable objects circle.pointable().direction() 返回的是手指三维空间上所指方向的向量 Vector circle.normal() 返回的是追踪到的圈的法向量。如果追踪到的是顺时针的圈,那么法向量是跟手指的方向是同一朝向的。逆时针则法向量朝内。 circle.pointable().direction().angleTo(circle.normal()) 通过计算圈的法向量和手指指向向量的夹角(此处返回的是弧度,PI相当于180°), 则可以推出是顺时针还是逆时针的。 */ if (circle.pointable().direction().angleTo(circle.normal()) <= PI/2) { clockwiseness = "clockwise"; // 顺时针 } else { clockwiseness = "counterclockwise"; // 逆时针 } // 计算上一帧到这一帧经过的角度 float sweptAngle = 0; /* STATE_START 表示第一帧。 circle.state() != Gesture::STATE_START: 第一帧不能计算 frame(0)表示的是当前帧,而frame(1)表示当前帧的前一帧,以此类推 */ if (circle.state() != Gesture::STATE_START) { CircleGesture previousUpdate = CircleGesture(controller.frame(1).gesture(circle.id())); sweptAngle = (circle.progress() - previousUpdate.progress()) * 2 * PI; } std::cout << std::string(2, ' ') << "Circle id: " << gesture.id() << ", state: " << stateNames[gesture.state()] << ", progress: " << circle.progress() << ", radius: " << circle.radius() << ", angle " << sweptAngle * RAD_TO_DEG << ", " << clockwiseness << std::endl; break; } // 如果是SWIPE的手势 case Gesture::TYPE_SWIPE: { SwipeGesture swipe = gesture; std::cout << std::string(2, ' ') << "Swipe id: " << gesture.id() << ", state: " << stateNames[gesture.state()] << ", direction: " << swipe.direction() << ", speed: " << swipe.speed() << std::endl; break; } // 如果是KEY_TAP的手势 case Gesture::TYPE_KEY_TAP: { KeyTapGesture tap = gesture; std::cout << std::string(2, ' ') << "Key Tap id: " << gesture.id() << ", state: " << stateNames[gesture.state()] << ", position: " << tap.position() << ", direction: " << tap.direction()<< std::endl; break; } // 如果是SCREEN_TAP的手势 case Gesture::TYPE_SCREEN_TAP: { ScreenTapGesture screentap = gesture; std::cout << std::string(2, ' ') << "Screen Tap id: " << gesture.id() << ", state: " << stateNames[gesture.state()] << ", position: " << screentap.position() << ", direction: " << screentap.direction()<< std::endl; break; } default: std::cout << std::string(2, ' ') << "Unknown gesture type." << std::endl; break; } } if (!frame.hands().isEmpty() || !gestures.isEmpty()) { std::cout << std::endl; } Sleep(3000); // 睡眠3秒钟以减慢输出。可以注释掉。 } void SampleListener::onFocusGained(const Controller& controller) { std::cout << "Focus Gained" << std::endl; } void SampleListener::onFocusLost(const Controller& controller) { std::cout << "Focus Lost" << std::endl; } void SampleListener::onDeviceChange(const Controller& controller) { std::cout << "Device Changed" << std::endl; const DeviceList devices = controller.devices(); for (int i = 0; i < devices.count(); ++i) { std::cout << "id: " << devices[i].toString() << std::endl; std::cout << " isStreaming: " << (devices[i].isStreaming() ? "true" : "false") << std::endl; } } void SampleListener::onServiceConnect(const Controller& controller) { std::cout << "Service Connected" << std::endl; } void SampleListener::onServiceDisconnect(const Controller& controller) { std::cout << "Service Disconnected" << std::endl; } int main(int argc, char** argv) { // 创建 listener 和 controller SampleListener listener; Controller controller; // 把 Sample listener 加到 controller中 controller.addListener(listener); // 此处改变请求策略 /* // 设置单个策略 controller.setPolicyFlags(Controller::PolicyFlag::POLICY_BACKGROUND_FRAMES); // 设置多个策略 controller.setPolicyFlags(static_cast<Controller::PolicyFlag>(Controller::PolicyFlag::POLICY_IMAGES | Controller::PolicyFlag::OPTIMIZE_HMD)); // 关闭所有策略 controller.setPolicyFlags(Controller::PolicyFlag::POLICY_DEFAULT); */ if (argc > 1 && strcmp(argv[1], "--bg") == 0) controller.setPolicyFlags(Leap::Controller::POLICY_BACKGROUND_FRAMES); // 输入 Enter结束 std::cout << "Press Enter to quit..." << std::endl; std::cin.get(); // 去除listener controller.removeListener(listener); return 0; } </span>
手势介绍(Gesture)
class Leap::CircleGesture
代表了画圆的手势。
在用这个手势之前,必须在onConnect()中激活此手势。
Controller.enableGesture(Gesture::TYPE_CIRCLE);
状态:
State::STATE_START(刚开始), State::STATE_UPDATE(正在执行), State::STATE_STOP(结束)
可以设置最小的弧度和最小的角度来判断是否为一个TYPE_CIRCLE
controller.config().setFloat("Gesture.Circle.MinRadius",
10.0);
controller.config().setFloat("Gestrure.Circle.MinArc",
.5):
controller.config().save();
Vector normal(): 返回被追踪圆的法向量
Pointable pointable(): 返回画圆的Finger
class Leap::SwipeGesture
代表横扫的手势。
在用这个手势之前,必须在onConnect()中激活此手势。
Controller.enableGesture(Gesture::TYPE_SWIPE);
可以设置最小的距离和最小的速度来判断是否为一个TYPE_SWIPE
controller.config().setFloat("Gesture.Swipe.MinLength", 200.0);
//单位是mm
controller.config().setFloat("Gestrure.Circle.MinVelocity",
750): //单位是mm/s
controller.config().save();
class Leap::ScreenTapGesture
代表手指或者工具点击屏幕的动作。手指是不动的。
在用这个手势之前,必须在onConnect()中激活此手势。
Controller.enableGesture(Gesture::TYPE_SCREEN_TAP);
可以设置最小的手指移动距离,一段时间的最小移动速度来判断是否为一个TYPE_SCREEN_TAP
controller.config().setFloat("Gesture.ScreenTap.MinForwardVelocity", 30.0); // 单位是mm/s,Default
value为50
controller.config().setFloat("Gesture.ScreenTap.HistorySeconds",
.5): //单位是s, Default value为0.1
controller.config().setFloat("Gesture.ScreenTap.MinDistance", 1.0); // 单位是mm, Default value为5.0
controller.config().save();
class Leap::KeyTapGesture
代表手指点击键的动作。手指需要上下移动。
在用这个手势之前,必须在onConnect()中激活此手势。
Controller.enableGesture(Gesture::TYPE_KEY_TAP);
可以设置最小的手指移动距离,一段时间的最小手指按下键的运动速度来判断是否为一个TYPE_KEY_TAP
controller.config().setFloat("Gesture.KeyTap.MinDownVelocity", 40.0); // 单位是mm/s,Default
value为50
controller.config().setFloat("Gesture.KeyTap.HistorySeconds",
.2): //单位是s, Default value为0.1
controller.config().setFloat("Gesture.KeyTap.MinDistance",
8.0); // 单位是mm, Default value为3.0
controller.config().save();
PolicyFlag
介绍
void
setPolicyFlags(PolicyFlag flags)
用于改变请求策略。
请求策略的分类:
1.POLICY_BACKGROUND_FRAMES ==
(1<<0)
程序在后台运行时leap
motion依然获取数据信息。在leap motion的控制面板中也要将“允许后台应用程序”的设置开启。
2.POLICY_IMAGES ==
(1<<1)
程序在执行时同时也获取image信息。默认是不获取的,请求这项时同时也要在控制面板中将“允许图像”打勾。
3.POLICY_OPTIMIZE_HMD ==
(1<<2)
程序在执行时对头戴设备进行优化。首先你要有一个头戴式设备。
4.POLICY_DEFAULT
== 0
默认,关闭所有请求。
pitch, yaw, roll 介绍
在LeapMotion中,坐标的表示如下:
pitch() 俯仰。 代表物体绕X轴上下旋转。
想象一个飞机,机头指向Z的方向,pitch就代表飞机上升下降的角度。
yaw() 航向。 指物体绕Y轴旋转。机头左右偏转的角度,代表飞行的方向。
roll() 横滚。 指物体绕Z轴旋转。机身旋转的角度,或者说机翼水平方向上偏转的角度。
相关文章推荐
- OpenCV基础入门 基于官方文档解读(2)--OpenCV基础特性
- OpenCV基础入门 基于官方文档解读(5)--core模块学习
- Google官方MVP Sample代码解读
- OpenCV基础入门 基于官方文档解读(3)--core模块学习
- OpenCV基础入门 基于官方文档解读(4)--core模块学习
- OpenCV 基础入门 基于官方文档的解读(1)--初识opencv
- Pandas 10分钟入门(官方说明+个人小测试)
- ASP.NET 2.0 微软官方入门视频==无比惊艳阿!!
- Linux内核解读入门
- 软件开发入门学习的个人看法
- 个人的web开发心得(一)----------非常适合入门新手,都是常识
- VC入门的一些个人想法
- 软件开发入门学习的个人看法
- 个人的web开发心得(三)----------非常适合入门新手,都是常识
- 软件开发入门学习的个人看法
- 个人的web开发心得(三)----------非常适合入门新手,都是常识
- 个人的web开发心得(五)----------非常适合入门新手,都是常识
- Linux 内核解读入门(转贴)
- COM入门教程..懒得转了,什么时候心情好再一点一点转过来..实在是写的太好了(个人意见)
- 软件开发入门学习的个人看法(转贴自dearbook)