如果你已經熟悉程式設計的概念,了解并在C#語言方面有一定經驗,并熟悉面向對象程式設計思想和設計概念,了解3D圖像學和向量數學知識。不妨來看看吧!
Leap Motion是什麼?
一種基于計算機視覺原理的識别技術,簡單來說,Leap Motion是基于雙目視覺的手勢識别裝置。主要是利用手勢控制gameobject的變換(移動、旋轉等)等。
一隻手上有29根骨頭,29個關節,123根韌帶,48根神經,30根動脈乘以2,Leap Motion已經能夠非常逼真的進行模拟了。Leap Motion感覺你移動的雙手,讓你以一種新的方式與電腦互動,它以1毫米的100分之1的精度追蹤你的10根手指,這大大超過現有的運動控制技術的敏感度,這就是為什麼你可以在1英寸的立方體空間内畫出微型傑作的原因。

Leap Motion設定和Unity 內建
首先,你需要下載下傳Leap Motion SDK,Leap_Motion_Orion_Setup_win_3.2.0.exe。下載下傳完後,繼續安裝運作時,這個SDK支援所有的平台和語言,鼓勵大家去了解下這些平台和語言。
然後,為了達成內建到Unity 中的目标,要下載下傳Leap Motion Unity Core Assets v4.1.5。
Leap Motion Core Assets
在Project視窗裡,你最該注意的是Leap Motion檔案夾,這個檔案夾包含的OVR是Leap Motion與Oculus Rift 虛拟現實頭戴式裝置相結合的資源,這個在以後會涉及到。你應該花時間學習每個檔案夾内的結構,更重要的是内容。其中最主要的一個核心資源是Hand Controller,這個是允許你和Leap Motion裝置互動的主要預制體,它作為錨點将你的雙手渲染到場景中。
圖4.Hand Controller屬性面闆
Hand Controller 預制體有一個Hand Controller 腳本附加在上面,這樣就允許了你和裝置的互動。看一看檢視面闆裡面的一些屬性,你會發現那裡有兩個關于手的屬性合作來在場景中渲染出真正的手部模型,并且那裡有兩個實體模型,這些是碰撞,這樣設計的好處是你能建立你自己的手部模型,并且用在控制器裡來檢視效果,并且可以自定義手勢等等。
注意:Unity 和Leap Motion 都是使用的米制系統,但是有些差別:Unity 的是以米為機關,Leap Motion用的是毫米,不是什麼大問題,但是當你測量坐标的時候需要知道。
另一個關鍵屬性是Hand Movement Scale 向量,縮放值越大,裝置覆寫的實體世界範圍越大,你需要檢視文檔來找到一個合适的數值來适應你目前的應用。Hand Movement Scale向量是用來在不改變模型大小的前提下改變雙手移動的範圍。
你需要将Hand Controller放在錄影機前面,并且相對于錄影機往下一定數值範圍内,這裡沒有魔法數值,你隻需嘗試适合你的數值即可。
using UnityEngine;
using System.Collections;
public class CubeInteraction : MonoBehaviour {
public Color c;
public static Color selectedColor;
public bool selectable = false;
void OnTriggerEnter(Collider c)
{
if (c.gameObject.transform.parent.name.Equals("index"))
{
if (this.selectable)
{
CubeInteraction.selectedColor = this.c;
this.transform.Rotate(Vector3.up, 33);
return;
}
transform.gameObject.GetComponent().material.color = CubeInteraction.selectedColor;
}
}
}
邏輯的核心在OnTriggerEnter(Colliderc) 函數内,我們檢查與可選擇物體的碰撞對象是不是食指,如果是,我們設定為該顔色。
你想撿起一個物體,并且将它移動到其他地方。
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using Leap;
public class GrabMyCube : MonoBehaviour {
public GameObject cubePrefab;
public HandController hc;
private HandModel hm;
public Text lblNoDeviceDetected;
public Text lblLeftHandPosition;
public Text lblLeftHandRotation;
public Text lblRightHandPosition;
public Text lblRightHandRotation;
// Use this for initialization
void Start()
{
hc.GetLeapController().EnableGesture(Gesture.GestureType.TYPECIRCLE);
hc.GetLeapController().EnableGesture(Gesture.GestureType.TYPESWIPE);
hc.GetLeapController().EnableGesture(Gesture.GestureType.TYPE_SCREEN_TAP);
}
private GameObject cube = null;
// Update is called once per frame
Frame currentFrame;
Frame lastFrame = null;
Frame thisFrame = null;
long difference = 0;
void Update()
{
this.currentFrame = hc.GetFrame();
GestureList gestures = this.currentFrame.Gestures();
foreach (Gesture g in gestures)
{
Debug.Log(g.Type);
if (g.Type == Gesture.GestureType.TYPECIRCLE)
{
// create the cube ...
if (this.cube == null)
{
this.cube = GameObject.Instantiate(this.cubePrefab,
this.cubePrefab.transform.position,
this.cubePrefab.transform.rotation) as GameObject;
}
}
if (g.Type == Gesture.GestureType.TYPESWIPE)
{
if (this.cube != null)
{
Destroy(this.cube);
this.cube = null;
}
}
}
foreach (var h in hc.GetFrame().Hands)
{
if (h.IsRight)
{
this.lblRightHandPosition.text = string.Format("Right Hand Position: {0}", h.PalmPosition.ToUnity());
this.lblRightHandRotation.text = string.Format("Right Hand Rotation: ", h.Direction.Pitch, h.Direction.Yaw, h.Direction.Roll);
if (this.cube != null)
this.cube.transform.rotation = Quaternion.EulerRotation(h.Direction.Pitch, h.Direction.Yaw, h.Direction.Roll);
foreach (var f in h.Fingers)
{
if (f.Type() == Finger.FingerType.TYPE_INDEX)
{
// this code converts the tip position from leap motion to unity world position
Leap.Vector position = f.TipPosition;
Vector3 unityPosition = position.ToUnityScaled(false);
Vector3 worldPosition = hc.transform.TransformPoint(unityPosition);
//string msg = string.Format("Finger ID:{0} Finger Type: {1} Tip Position: {2}", f.Id, f.Type(), worldPosition);
//Debug.Log(msg);
}
}
}
if (h.IsLeft)
{
this.lblLeftHandPosition.text = string.Format("Left Hand Position: {0}", h.PalmPosition.ToUnity());
this.lblLeftHandRotation.text = string.Format("Left Hand Rotation: ", h.Direction.Pitch, h.Direction.Yaw, h.Direction.Roll);
if (this.cube != null)
this.cube.transform.rotation = Quaternion.EulerRotation(h.Direction.Pitch, h.Direction.Yaw, h.Direction.Roll);
}
}
}
}
我們定義了一個Hand Controller對象hc,這裡就引用了Hand Controller對象,我們就可以在此腳本中調用此對象中的功能,第一件事是,我們需要用HandController對象注冊手勢,這在Start()方法中完成,這裡有一些預先定義好的手勢,是以我們将使用其中的一些,在這個示例中,我們注冊了食指轉圈,掃動,螢幕輕觸手勢類型。
還定義了兩個GameObject變量,名為cubePrefab和cube,cubePrefab是我們引用的一個代表我們的立方體的預制體,這個預制體有對應材質和其他一些元件附加其上。
讓我們看看Update()函數,這是所有魔法發生的地方,我們在這裡查找手勢類型為typecircle,這将執行個體出一個我們預先制作好的名為cubePrefab的預制體,是以,第一件事是将目前幀對象傳遞到Hand Controller對象,一個幀對象包含了所有有關手部運動的資訊,下一步是獲得由傳感器檢測到的所有手勢并儲存為一個清單。
下一步我們将循環周遊每一個手勢,并檢測它的類型,如果我們檢測到CIRCLE手勢,我們就檢查,我們是否已經執行個體化了我們的立方預制體,如果沒有,執行個體化它,如何下一個手勢類型是SWIPE,這将銷毀我們執行個體化的預制體。
下一個循環基本上周遊檢測完所有的手,檢測它是左手還是右手,基于哪隻手在執行特定的操作,在這個例子裡,我們隻是獲得手的位置和旋轉,并且根據手部的旋轉來旋轉我們執行個體化的立方體,沒什麼奇怪的。
雙目視覺就是有兩個攝像頭,利用雙目立體視覺成像原理,通過兩個錄影機來提取包括三維位置在内的資訊進行手勢的綜合分析判斷,建立的是手部的立體模型。這種方法對于使用者手勢的輸入限制較小,可以實作更加自然的人機互動,但由于需要進行立體比對,且由于立體模型的複雜性,需要處理大量的資料,計算相對來說較複雜。
要實作雙目手勢識别首先需要對雙目攝像頭做标定,即是計算空間中左右兩台錄影機位置的幾何關系。首先是對單錄影機的标定,其主要任務是計算錄影機的内部參數(包含錄影機的投影變換矩陣和透鏡畸變參數)和外部參數(包含相對于某個世界坐标系的旋轉矩陣和平移向量),形象點說,錄影機本身存在畸變,如果不經過标定過程,錄影機所拍攝出的影響是存在畸變的,即可能将原本的矩形顯示成不規則的圓角四邊形。然後是标定,即計算空兩台錄影機在空間中的相對的幾何位置關系(包含旋轉矩陣和平移向量),通俗講就是使得兩台錄影機所成的影像顯示在同一水準線上。
接下來就是具體的手勢識别過程了。
首先從雙目攝像頭采集操作者手勢動作的左右視覺圖像,通過立體視覺算法生成深度圖像。具體過程:經過立體标定後擷取經過校準的立體圖像對後,進行立體比對,獲得視差圖像,再利用錄影機的内參數及外參數進行三角計算擷取深度圖像。
然後對左(或右)視覺圖像使用手勢分割算法處理,分割出的人手所在的初始位置資訊,并将該位置作為手勢跟蹤算法的起始位置。
再使用手勢跟蹤算法對人手運動進行跟蹤。
再根據跟蹤得到的結果進行手勢的識别。需要說明的是,如果跟蹤目标消失,則重新進行手勢分割,再重複上述步驟。
雙目手勢識别流程圖如下: