Commit cc772060 authored by Taddeüs Kroes's avatar Taddeüs Kroes

Added hand tracker and implemented it in the test application.

parent adeaa561
from basic import BasicEventTracker
from tap import TapTracker
from transform import TransformationTracker
from hand import HandTracker
# Map of gesture type to tracker type
......@@ -31,3 +32,4 @@ def create_tracker(gesture_type, widget):
_register_tracker(BasicEventTracker)
_register_tracker(TapTracker)
_register_tracker(TransformationTracker)
_register_tracker(HandTracker)
from ..tracker import GestureTracker, Gesture
class HandGesture(Gesture):
def __init__(self, hand, finger):
self.hand = hand
self.finger = finger
def get_hand(self):
return self.hand
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 FingerDownGesture(HandGesture):
_type = 'finger_down'
class FingerMoveGesture(HandGesture):
_type = 'finger_move'
class FingerUpGesture(HandGesture):
_type = 'finger_up'
class Hand(object):
def __init__(self):
self.fingers = []
def __len__(self):
return len(self.fingers)
def __iter__(self):
return iter(self.fingers)
def __str__(self):
return '<Hand fingers=%s centroid=%s>' \
% (self.fingers, self.get_centroid())
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)
def remove_finger(self, finger):
self.fingers.remove(finger)
def get_centroid(self):
l = len(self.fingers)
coords = [f.get_position() for f in self.fingers]
all_x, all_y = zip(*coords)
return sum(all_x) / l, sum(all_y) / l
class HandTracker(GestureTracker):
supported_gestures = [FingerDownGesture, FingerMoveGesture,
FingerUpGesture]
configurable = ['max_finger_distance']
def __init__(self, area):
super(HandTracker, self).__init__(area)
# Map of finger id's to corresponding hand objects
self.finger_hands = {}
# All hands being tracked
self.hands = []
# Maximum distance between two fingers to be assigned to the same hand
self.max_finger_distance = 400
def find_hand(self, finger):
for hand in self.hands:
if hand.contains(finger, self.max_finger_distance):
return hand
self.hands.append(Hand())
return self.hands[-1]
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):
finger = event.get_touch_object()
if finger.get_id() not in self.finger_hands:
return
hand = self.finger_hands[finger.get_id()]
self.trigger(FingerMoveGesture(hand, finger))
def on_point_up(self, event):
finger = event.get_touch_object()
finger_id = finger.get_id()
if finger_id not in self.finger_hands:
return
hand = self.finger_hands[finger_id]
del self.finger_hands[finger_id]
hand.remove_finger(finger)
if not len(hand):
self.hands.remove(hand)
self.trigger(FingerUpGesture(hand, finger))
......@@ -88,7 +88,7 @@ class Polygon(BoundingBoxArea):
fullscreen = False
draw_bounding_boxes = draw_touch_points = True
draw_bounding_boxes = draw_touch_objects = True
W, H = mt.screen.screen_size
......@@ -107,8 +107,8 @@ def create_context_window(w, h, callback):
refresh()
def handle_key(win, event):
"""Handle key event. 'f' toggles fullscreen, 'q' exits the program, 'b'
toggles bounding boxes, 'p' toggles touch points."""
"""Handle key event. 'f' toggles fullscreen, 'b' toggles bounding
boxes, 'i' toggles input points, 'q' exits the program."""
if event.keyval >= 256:
return
......@@ -118,16 +118,16 @@ def create_context_window(w, h, callback):
global fullscreen
(win.unfullscreen if fullscreen else win.fullscreen)()
fullscreen = not fullscreen
elif key == 'q':
quit()
elif key == 'b':
global draw_bounding_boxes
draw_bounding_boxes = not draw_bounding_boxes
refresh()
elif key == 'p':
global draw_touch_points
draw_touch_points = not draw_touch_points
elif key == 'i':
global draw_touch_objects
draw_touch_objects = not draw_touch_objects
refresh()
elif key == 'q':
quit()
# Root area (will be synchronized with GTK window)
global root, overlay
......@@ -156,7 +156,7 @@ def create_context_window(w, h, callback):
window.show()
def draw(*args):
def draw():
if not cr:
return
......@@ -171,26 +171,44 @@ def draw(*args):
obj.draw(cr)
cr.restore()
if draw_touch_points:
if draw_touch_objects:
ox, oy = root.get_position()
cr.set_source_rgb(*WHITE)
for x, y in touch_points.itervalues():
x -= ox
y -= oy
for hand in touch_hands:
cx, cy = hand.get_centroid()
cr.set_line_width(3)
cr.arc(x, y, 20, 0, 2 * pi)
cr.stroke()
# Filled centroid circle
if len(hand) > 1:
cr.arc(cx - ox, cy - oy, 20, 0, 2 * pi)
cr.fill()
for x, y in hand:
x -= ox
y -= oy
# Circle outline
cr.set_line_width(3)
cr.arc(x, y, 20, 0, 2 * pi)
cr.stroke()
# Line to centroid
if len(hand) > 1:
cr.move_to(x, y)
cr.line_to(cx - ox, cy - oy)
cr.set_line_width(2)
cr.stroke()
# Cross
cr.set_line_width(1)
cr.move_to(x - 8, y)
cr.line_to(x + 8, y)
cr.move_to(x, y - 8)
cr.line_to(x, y + 8)
cr.stroke()
cr.set_line_width(1)
cr.move_to(x - 8, y)
cr.line_to(x + 8, y)
cr.move_to(x, y - 8)
cr.line_to(x, y + 8)
cr.stroke()
def refresh():
def refresh(*args):
window.queue_draw()
......@@ -198,10 +216,10 @@ def quit(*args):
gtk.main_quit()
# Initialization
# Global variables
window = cr = root = overlay = None
draw_objects = []
touch_points = {}
touch_hands = []
def triangle_height(width):
......@@ -231,22 +249,22 @@ def on_show(window):
# Overlay catches basic events
def handle_down(gesture):
point = gesture.get_event().get_touch_object()
touch_points[point.get_id()] = point.get_position()
if gesture.is_first():
touch_hands.append(gesture.get_hand())
if draw_touch_points:
if draw_touch_objects:
refresh()
def handle_up(gesture):
point = gesture.get_event().get_touch_object()
del touch_points[point.get_id()]
if gesture.is_last():
touch_hands.remove(gesture.get_hand())
if draw_touch_points:
if draw_touch_objects:
refresh()
overlay.on_point_down(handle_down)
overlay.on_point_move(handle_down)
overlay.on_point_up(handle_up)
overlay.on_finger_down(handle_down)
overlay.on_finger_move(lambda g: draw_touch_objects and refresh())
overlay.on_finger_up(handle_up)
root.add_area(overlay)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment