hand / hand_tracker.py
Neil67's picture
Deploy gesture image player
e47efb2 verified
Raw
History Blame Contribute Delete
3.33 kB
import cv2
import mediapipe as mp
import numpy as np
_MP_HANDS = getattr(getattr(mp, "solutions", None), "hands", None)
_HANDS = None
if _MP_HANDS is not None:
_HANDS = _MP_HANDS.Hands(
static_image_mode=False,
max_num_hands=1,
min_detection_confidence=0.5,
min_tracking_confidence=0.5,
)
def _empty_result(frame):
return {
"hand_found": False,
"hand_center_x": None,
"hand_center_y": None,
"fingers_extended": {
"thumb": False,
"index": False,
"middle": False,
"ring": False,
"pinky": False,
},
"landmarks": None,
"frame": frame,
}
def _finger_status(landmarks, handedness_label):
# Landmark shortcuts for finger tips and lower joints.
tips = {
"thumb": _MP_HANDS.HandLandmark.THUMB_TIP,
"index": _MP_HANDS.HandLandmark.INDEX_FINGER_TIP,
"middle": _MP_HANDS.HandLandmark.MIDDLE_FINGER_TIP,
"ring": _MP_HANDS.HandLandmark.RING_FINGER_TIP,
"pinky": _MP_HANDS.HandLandmark.PINKY_TIP,
}
pips = {
"thumb": _MP_HANDS.HandLandmark.THUMB_IP,
"index": _MP_HANDS.HandLandmark.INDEX_FINGER_PIP,
"middle": _MP_HANDS.HandLandmark.MIDDLE_FINGER_PIP,
"ring": _MP_HANDS.HandLandmark.RING_FINGER_PIP,
"pinky": _MP_HANDS.HandLandmark.PINKY_PIP,
}
status = {}
for finger in ("index", "middle", "ring", "pinky"):
tip_y = landmarks[tips[finger]].y
pip_y = landmarks[pips[finger]].y
status[finger] = tip_y < pip_y
thumb_tip_x = landmarks[tips["thumb"]].x
thumb_ip_x = landmarks[pips["thumb"]].x
if handedness_label == "Right":
status["thumb"] = thumb_tip_x < thumb_ip_x
else:
status["thumb"] = thumb_tip_x > thumb_ip_x
return status
def detect_hand(frame):
"""
Detect one hand from a webcam frame and return basic rule-friendly data.
"""
if _HANDS is None:
return _empty_result(frame)
if frame is None:
return _empty_result(frame)
if not isinstance(frame, np.ndarray) or frame.size == 0:
return _empty_result(frame)
if frame.dtype != np.uint8:
frame = np.clip(frame, 0, 255).astype(np.uint8)
# Most Gradio webcam frames are RGB. Some environments may provide BGR.
# Try RGB first, then fallback to BGR->RGB conversion.
results = _HANDS.process(frame)
if not results.multi_hand_landmarks and frame.ndim == 3 and frame.shape[2] == 3:
rgb_fallback = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
results = _HANDS.process(rgb_fallback)
if not results.multi_hand_landmarks:
return _empty_result(frame)
hand_landmarks = results.multi_hand_landmarks[0]
handedness_label = "Right"
if results.multi_handedness:
handedness_label = results.multi_handedness[0].classification[0].label
lm = hand_landmarks.landmark
center_x = sum(point.x for point in lm) / len(lm)
center_y = sum(point.y for point in lm) / len(lm)
fingers = _finger_status(lm, handedness_label)
return {
"hand_found": True,
"hand_center_x": center_x,
"hand_center_y": center_y,
"fingers_extended": fingers,
"landmarks": hand_landmarks,
"frame": frame,
}