Prechádzať zdrojové kódy

Improved hand tracking.

Taddeus Kroes 13 rokov pred
rodič
commit
4fdba920b2
3 zmenil súbory, kde vykonal 60 pridanie a 35 odobranie
  1. 0 3
      TODO.txt
  2. 56 26
      tests/hand.py
  3. 4 6
      tests/testapp.py

+ 0 - 3
TODO.txt

@@ -2,8 +2,5 @@ Code:
 - Rotation has a 'bump' at angle = 0.
 - Polygon tracker verplaatsen naar framework.
 
-Tests:
-- Improve hand tracker.
-
 Report/appendix reference gesture detection:
 - Point_leave(+point_enter) kan niet -> flaw v/h systeem/driver?

+ 56 - 26
tests/hand.py

@@ -1,8 +1,9 @@
 from src.tracker import GestureTracker, Gesture
+from src.geometry import Positionable
 
 
 class HandGesture(Gesture):
-    def __init__(self, hand, finger):
+    def __init__(self, hand, finger=None):
         self.hand = hand
         self.finger = finger
 
@@ -12,11 +13,13 @@ class HandGesture(Gesture):
     def get_finger(self):
         return self.finger
 
-    def is_first(self):
-        return len(self.hand) == 1
 
-    def is_last(self):
-        return not len(self.hand)
+class HandDownGesture(HandGesture):
+    _type = 'hand_down'
+
+
+class HandUpGesture(HandGesture):
+    _type = 'hand_up'
 
 
 class FingerDownGesture(HandGesture):
@@ -31,9 +34,10 @@ class FingerUpGesture(HandGesture):
     _type = 'finger_up'
 
 
-class Hand(object):
+class Hand(Positionable):
     def __init__(self):
         self.fingers = []
+        self.centroid = None
 
     def __len__(self):
         return len(self.fingers)
@@ -48,29 +52,32 @@ class Hand(object):
     def __repr__(self):
         return str(self)
 
-    def contains(self, finger, max_distance):
-        for other_finger in self.fingers:
-            if other_finger.distance_to(finger) <= max_distance:
-                return True
-
-        return False
-
     def add_finger(self, finger):
         self.fingers.append(finger)
+        self.update_centroid()
 
     def remove_finger(self, finger):
         self.fingers.remove(finger)
+        self.update_centroid()
 
-    def get_centroid(self):
+    def update_centroid(self):
         l = len(self.fingers)
+
+        if not l:
+            self.centroid = None
+            return
+
         coords = [f.get_position() for f in self.fingers]
         all_x, all_y = zip(*coords)
-        return sum(all_x) / l, sum(all_y) / l
+        self.centroid = sum(all_x) / l, sum(all_y) / l
+
+    def get_centroid(self):
+        return self.centroid
 
 
 class HandTracker(GestureTracker):
-    supported_gestures = [FingerDownGesture, FingerMoveGesture,
-                          FingerUpGesture]
+    supported_gestures = [HandDownGesture, HandUpGesture, FingerDownGesture,
+                          FingerMoveGesture, FingerUpGesture]
 
     configurable = ['max_finger_distance']
 
@@ -87,18 +94,30 @@ class HandTracker(GestureTracker):
         self.max_finger_distance = 300
 
     def find_hand(self, finger):
-        for hand in self.hands:
-            if hand.contains(finger, self.max_finger_distance):
-                return hand
+        min_dist = hand = None
+
+        for h in self.hands:
+            dist = finger.distance_to(h.get_centroid())
+
+            if min_dist is None or dist < min_dist:
+                min_dist = dist
+                hand = h
+
+        if not hand or min_dist > self.max_finger_distance:
+            hand = Hand()
+            self.hands.append(hand)
 
-        self.hands.append(Hand())
-        return self.hands[-1]
+        self.finger_hands[finger.get_id()] = hand
+        hand.add_finger(finger)
+
+        if len(hand) == 1:
+            self.trigger(HandDownGesture(hand))
+
+        return hand
 
     def on_point_down(self, event):
         finger = event.get_touch_object()
         hand = self.find_hand(finger)
-        hand.add_finger(finger)
-        self.finger_hands[finger.get_id()] = hand
         self.trigger(FingerDownGesture(hand, finger))
 
     def on_point_move(self, event):
@@ -108,6 +127,17 @@ class HandTracker(GestureTracker):
             return
 
         hand = self.finger_hands[finger.get_id()]
+        hand.update_centroid()
+
+        if finger.distance_to(hand.get_centroid()) > self.max_finger_distance:
+            hand.remove_finger(finger)
+
+            if not len(hand):
+                self.hands.remove(hand)
+                self.trigger(HandUpGesture(hand))
+
+            hand = self.find_hand(finger)
+
         self.trigger(FingerMoveGesture(hand, finger))
 
     def on_point_up(self, event):
@@ -120,8 +150,8 @@ class HandTracker(GestureTracker):
         hand = self.finger_hands[finger_id]
         del self.finger_hands[finger_id]
         hand.remove_finger(finger)
+        self.trigger(FingerUpGesture(hand, finger))
 
         if not len(hand):
             self.hands.remove(hand)
-
-        self.trigger(FingerUpGesture(hand, finger))
+            self.trigger(HandUpGesture(hand))

+ 4 - 6
tests/testapp.py

@@ -269,15 +269,13 @@ def on_show(window):
 
     # Overlay catches finger events to be able to draw touch points
     def handle_down(gesture):
-        if gesture.is_first():
-            touch_hands.append(gesture.get_hand())
+        touch_hands.append(gesture.get_hand())
 
         if draw_touch_objects:
             refresh()
 
     def handle_up(gesture):
-        if gesture.is_last():
-            touch_hands.remove(gesture.get_hand())
+        touch_hands.remove(gesture.get_hand())
 
         if draw_touch_objects:
             refresh()
@@ -288,9 +286,9 @@ def on_show(window):
     screen.add_area(root)
     screen.add_area(overlay)
 
-    overlay.on_finger_down(handle_down)
+    overlay.on_hand_down(handle_down)
     overlay.on_finger_move(lambda g: draw_touch_objects and refresh())
-    overlay.on_finger_up(handle_up)
+    overlay.on_hand_up(handle_up)
 
 
     # Root area rotation leads to rotation of all polygons around the centroid