LeapMotion(2):追蹤五指


上一篇文章,我們實現了Leap Motion的簡單測試。追蹤其中一個手指並用紅色圓形表示其在空間的位置。

這篇文章,我們來實現五指的追蹤。

其實,能夠實現一指的追蹤,那么五指的追蹤自然不成問題。但是,還是有幾個問題我們需要考慮一下。

1、並不是每一幀都會包含五指的全部信息。

比如,當前幀包含了五指信息,那么,窗口上就會顯示五個紅色圓。如果此時用戶握拳,那么,下一幀就可能只會有一指的信息。此時,就應從窗口中移除多余的四個紅色圓。

2、手指如何和紅色圓對應。

 因為Hand.Fingers集合對應的不一定是拇指、食指、中指、無名指、小指(可能對應的是小指、無名指、中指),所以,得想個辦法把某個指尖和某個紅色圓對應起來。幸好,Leap為每個對象都定義了ID。這樣,我們就可以將指尖的ID和紅色圓綁定在一起。自然地,我們會想到用Dictionary<int, Ellipse>。

還有一點,假設上一幀檢測到了拇指(id為5)、這一幀沒檢測到拇指,而下一幀又檢測到了拇指,那么,它的id可能是5,但也有可能不是5。

3、如何刪除上一幀有的而這一幀中沒有的紅色圓。

這個問題相對簡單,做一個List<int>,把這一幀中id一次加進去,然后,再從Dictionary<int, Ellipse>的Keys里面刪除那些不在List<int>中的id所對應的紅色圓。

 

OK,大部分問題都有了思路,那么,我們開始寫代碼吧。記得,一定要先看看上一篇文章啊。

Step1:構造下面的用戶界面。

Step2:聲明MyLeapListener類和窗口Closing事件。代碼和LeapMotion(1)中的一樣。

Step3:添加成員變量Dictionary<int, Ellipse>表示手指ID和紅色圓的對應,添加成員變量List<int>表示當前幀追蹤到的手指編號。代碼如下:

1         private Dictionary<int, Ellipse> ellipses;
2         private List<int> fingerIds;

Step4:編寫“連接設備”的單擊事件和“斷開設備”的單擊事件。與之前不同的是,在“連接設備”的單擊事件中,需要初始化ellipses成員變量,在“斷開設備”的單擊事件中,需要清空ellipses成員變量。

 1         private void connect_device_button_Click(object sender, RoutedEventArgs e)
 2         {
 3             listener = new MyLeapListener();
 4             listener.OnFrameEvent += listener_OnFrameEvent;
 5             controller = new Controller();
 6             controller.AddListener(listener);
 7 
 8             connect_device_button.IsEnabled = false;
 9             disconnect_device_button.IsEnabled = true;
10 
11             ellipses = new Dictionary<int, Ellipse>();
12             fingerIds = new List<int>();
13         }
14 
15         private void disconnect_device_button_Click(object sender, RoutedEventArgs e)
16         {
17             controller.RemoveListener(listener);
18 
19             connect_device_button.IsEnabled = true;
20             disconnect_device_button.IsEnabled = false;
21 
22             ellipses.Clear();
23         }

Step5:編寫OnFrameEvent事件。還是先放上事件聲明。

1         void listener_OnFrameEvent(object sender, EventArgs e)
2         {
3             
4         }

和之前一樣,在事件中,我們首先要獲取追蹤到的手部的信息。

 1             LeapFrame frame = controller.Frame();//獲取當前幀
 2             if (!frame.Hands.IsEmpty)//判斷是否追蹤到手部
 3             {
 4                 Hand hand = frame.Hands.FirstOrDefault();//獲取追蹤到的第一只手
 5                 LeapVector palmPosition = hand.PalmPosition;//獲取手部位置
 6                 float palmHeight = palmPosition.y;
 7                 float detectionWidth = (float)(palmHeight * Math.Tan(75.0 / 180.0 * Math.PI) * 2);//計算當前高度的檢測寬度
 8 
 9                 //將要放下面的代碼
10 
11             }

接下來,就需要找到追蹤到的每一個指尖(是指尖,而不是筆之類的東西歐)。

1                 foreach (Finger finger in hand.Fingers.Where(f => f.IsFinger))
2                 {
3                     //將要放下面的代碼
4                 }

獲取指尖id放入List<int>,然后判斷Dictionary<int, Ellipse>中是否有指定id對應的ellipse。代碼如下:

 1                     //獲取指尖ID,放入List<int>
 2                     fingerIds.Add(finger.Id);
 3 
 4                     Ellipse ellipse = null;
 5                     if (ellipses.ContainsKey(finger.Id))//如果在Dictionary<int, Ellipse>中有,則用ellipse表示其
 6                     {
 7                         ellipse = ellipses[finger.Id];
 8                     }
 9                     else//Dictionary<int, Ellipse>中不存在,則創建一個ellipse
10                     {
11                         this.Dispatcher.Invoke(new Action(delegate
12                         {
13                             ellipse = new Ellipse();
14                             ellipse.Width = 10;
15                             ellipse.Height = 10;
16                             ellipse.Fill = Brushes.Red;//10x10大小的紅色圓
17                             ellipses.Add(finger.Id, ellipse);
18                             container_canvas.Children.Add(ellipse);
19                         }), null);
20                     }

然后,就是在Canvas中設置ellipse的位置了。代碼比較簡單(和上一篇中的代碼類似),如下:

 1                     //設置ellipse的位置
 2                     LeapVector position = finger.TipPosition;
 3 
 4                     double x = position.x;
 5                     double y = position.y;
 6 
 7                     double screenWidth = container_canvas.ActualWidth;
 8                     double screenHeight = container_canvas.ActualHeight;
 9 
10                     x = x / detectionWidth * screenWidth + (screenWidth / 2);
11                     y = screenHeight - y / 600 * screenHeight;
12 
13                     this.Dispatcher.BeginInvoke(new Action(delegate
14                     {
15                         Canvas.SetLeft(ellipse, x);
16                         Canvas.SetTop(ellipse, y);
17                     }), null);

這樣,我們就完成了指尖位置的繪制。

但是,要記得,在Dictionary<int, Ellipse>中可能存在本幀中沒有檢測到的指尖的id。為此,我們需要移除Dictionary<int, Ellipse>中那些多余的Key。代碼如下:

 1                 //去掉這一幀中沒追蹤到的手指
 2                 IEnumerable<int> deletedIds = ellipses.Keys.Except(fingerIds);
 3                 foreach (int id in deletedIds.ToList())//這里要記得ToList()一下,否則會出現異常。
 4                 {
 5                     Ellipse ellipse = ellipses[id];
 6 
 7                     this.Dispatcher.Invoke(new Action(delegate
 8                     {
 9                         container_canvas.Children.Remove(ellipse);
10                     }), null);
11 
12                     ellipses.Remove(id);
13                 }
14 
15                 //完成本次繪制,清空List<int>
16                 fingerIds.Clear();

ok,這樣就完成了。運行程序看看吧。

你會發現,基本上還是我們要的效果。但是,

當手越高,指尖距離越近,這是為什么呢?考慮一下。

 

附上源代碼


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM