2013-12-17 更新 :SDK1.0.8已经更新后,empty()方法不可以使用,必须换成isEmpty()才行,详情请见文章。建议使用者打开samples下的文件夹进行编译(最新的),尽量少使用本文提供的例子。
今天晚上悦读了下Leap Motion提供的官方C++例子,感觉很棒很给力!相对于微软Kinect的程序,他们的例子编码风格少许不同,十分的高效简单!对于才学编程的菜鸟来说,你要好好看下他编程的基本功。对于有一定基础的人来说,你阅读后一定会惊讶于Leap Motion API如此的简洁给力。对于从事开发Kinect的孩子们,你们遇到Leap,一定会立即投入她的怀抱。
例子在LeapSDK下的samples文件夹下面,如果不知道哪里下载SDK,可以去这个地址,也可以去官方地址(如果此文时间超过一个月了,请去官方地址下载为佳)。
同时本人准备做一个弹钢琴的应用。在Leap Motion上也有一个弹钢琴应用(还是一个从事商业音乐软件开发的公司),虽然他的界面效果很好,但是被喷的不成人样(主要是用户体验不好,不能准确识别弹钢琴手势)。嗯,我从零开始做~关注的自然是感情背后手指弹动如何准确的识别,如果做得好,我会请会弹钢琴的人辅助调试软件。如果不出意外,所有代码是开源的,大家可以互相学习和借鉴。同时,如果有人想与我合作,十分欢迎,上海的优先哦~
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 |
/******************************************************************************\ * Copyright (C) 2012-2013 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 <windows.h>//如果嫌刷屏太快打开这个定义1(一共2处) #include <iostream> #include "Leap.h" 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&); }; 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) { // 获取最新的一帧,并且返回一些基本信息 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; //非空,检测到手 if (!frame.hands().empty()) { // 得到第一只手[hands()返回HandList结构,可以向操作容器一样操作] const Hand hand = frame.hands()[0]; // 检查下手是否有手指[握拳的话,是找不到手指的] const FingerList fingers = hand.fingers(); if (!fingers.empty()) { // 计算指尖尖端平均位置坐标 Vector avgPos; for (int i = 0; i < fingers.count(); ++i) { avgPos += fingers[i].tipPosition(); } avgPos /= (float)fingers.count(); std::cout << "Hand has " << fingers.count() << " fingers, average finger tip position" << avgPos << std::endl; } // 获取手的球心半径和手掌的坐标 std::cout << "Hand sphere radius: " << hand.sphereRadius() << " mm, palm position: " << hand.palmPosition() << std::endl; // 获取手的垂直向量(垂直手心向里)和方向(以手心开始,沿着手指指尖方向) const Vector normal = hand.palmNormal(); const Vector direction = hand.direction(); // 计算手的俯仰Pitch角度、平面Roll旋转角度和左右Yaw旋转角度 std::cout << "Hand pitch: " << direction.pitch() * RAD_TO_DEG << " degrees, " << "roll: " << normal.roll() * RAD_TO_DEG << " degrees, " << "yaw: " << direction.yaw() * RAD_TO_DEG << " degrees" << std::endl; } /*他并没有使用一些函数rightmost和leftmost之类*/ // 获取手势 const GestureList gestures = frame.gestures(); for (int g = 0; g < gestures.count(); ++g) { Gesture gesture = gestures[g];//这里最好去看下Gesture的定义,里面详细描述各种手势触发的阈值 //编程技术比较厉害啊,都看不到Gesture的成员变量在哪里,完全被封装起来了,不是com就是dll封装的吧 //我也很想学习这种完全把变量封装到看不见的方法,这样程序员会很省心(眼不见心为净) //注意下来使用了switch语句先判断它是否是这种类型手势,再进行类的重新构造 switch (gesture.type()) { case Gesture::TYPE_CIRCLE: { CircleGesture circle = gesture; std::string clockwiseness; if (circle.pointable().direction().angleTo(circle.normal()) <= PI/4) { clockwiseness = "clockwise";//顺时针旋转的话,circle.normal()指向外侧[右手坐标系,学过物理磁力线神马的一下子就会明白] } else { clockwiseness = "counterclockwise";//逆时针,同上理解 } // 计算和上一帧经过的角度 float sweptAngle = 0; if (circle.state() != Gesture::STATE_START) { //如果不是第一帧[第一帧肯定不能作为判断,只能作为初始化] //frame(0)表示当前最新一帧,而frame(1)表示前一帧,以此类推 //对于id的使用十分简单明了,但很重要。这样表示只针对这个人的这个手指。 CircleGesture previousUpdate = CircleGesture(controller.frame(1).gesture(circle.id())); //progress()表示手指旋转了几圈,如果是0.5表示旋转了半圈,如果是3表示已经旋转了3圈[计数功能很赞啊] sweptAngle = (circle.progress() - previousUpdate.progress()) * 2 * PI;//一共旋转多少弧度[显示时他再次转换成角度] } std::cout << "Circle id: " << gesture.id() << ", state: " << gesture.state() << ", progress: " << circle.progress() << ", radius: " << circle.radius() << ", angle " << sweptAngle * RAD_TO_DEG << ", " << clockwiseness << std::endl; break; } case Gesture::TYPE_SWIPE: {//相对旋转手势来说,这个以及下面几个简单很多 SwipeGesture swipe = gesture; std::cout << "Swipe id: " << gesture.id() << ", state: " << gesture.state() << ", direction: " << swipe.direction()//这个可是能判断所有方向的,强大!我玩Kinect时只能识别几个特定的方向。 << ", speed: " << swipe.speed() << std::endl; break; } case Gesture::TYPE_KEY_TAP: { KeyTapGesture tap = gesture; std::cout << "Key Tap id: " << gesture.id() << ", state: " << gesture.state() << ", position: " << tap.position() << ", direction: " << tap.direction()<< std::endl; break; } case Gesture::TYPE_SCREEN_TAP: { ScreenTapGesture screentap = gesture; std::cout << "Screen Tap id: " << gesture.id() << ", state: " << gesture.state() << ", position: " << screentap.position() << ", direction: " << screentap.direction()<< std::endl; break; } default: std::cout << "Unknown gesture type." << std::endl; break; } } if (!frame.hands().empty() || !gestures.empty()) { std::cout << std::endl; } // Sleep(1000);//如果嫌刷屏太快打开这个定义2(一共2处) } void SampleListener::onFocusGained(const Controller& controller) { std::cout << "Focus Gained" << std::endl; } void SampleListener::onFocusLost(const Controller& controller) { std::cout << "Focus Lost" << std::endl; } int main() { // 创建例子监听者和控制器 SampleListener listener; Controller controller; // 让监听者获取控制器的数据并进行分析 controller.addListener(listener); // 让程序一直运行,直到输入回车键才结束 std::cout << "Press Enter to quit..." << std::endl; std::cin.get(); // 移除监听者 controller.removeListener(listener); return 0; } |
下面是运行效果: