You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
gesture_rec/gesture_control/special_gestures.py

103 lines
4.6 KiB
Python

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import numpy as np
from ml_gestures.predict import MLGesturePredictor
class SpecialGestureDetector:
def __init__(self, mode='geometric', model_path=None, class_names=None):
self.mode = mode
if mode == 'ml':
if model_path is None or class_names is None:
raise ValueError("Для ML нужны model_path и class_names")
self.ml_predictor = MLGesturePredictor(model_path, class_names)
print("Использую статический ML классификатор")
else:
self.ml_predictor = None
print("Использую геометрические отношения для детекции специальных жестов")
self.debug = True # Включите для отладки
def predict(self, landmarks):
if self.mode == 'geometric':
return self._geometric_predict(landmarks)
else:
return self.ml_predictor.predict(landmarks)
def _geometric_predict(self, landmarks):
# Индексы MediaPipe
idx = {
'nose': 0,
'left_shoulder': 11,
'right_shoulder': 12,
'left_elbow': 13,
'right_elbow': 14,
'left_wrist': 15,
'right_wrist': 16,
'left_hip': 23,
'right_hip': 24,
}
# Повышенный порог уверенности для специальных жестов
min_conf = 0.7
required = ['left_shoulder', 'right_shoulder', 'left_elbow', 'right_elbow',
'left_wrist', 'right_wrist', 'left_hip', 'right_hip']
for p in required:
if landmarks[idx[p]][3] < min_conf:
if self.debug:
print(f"{p} low confidence")
return 'none'
# Координаты (x, y)
l_sh = landmarks[idx['left_shoulder']][:2]
r_sh = landmarks[idx['right_shoulder']][:2]
l_el = landmarks[idx['left_elbow']][:2]
r_el = landmarks[idx['right_elbow']][:2]
l_wr = landmarks[idx['left_wrist']][:2]
r_wr = landmarks[idx['right_wrist']][:2]
l_hip = landmarks[idx['left_hip']][:2]
r_hip = landmarks[idx['right_hip']][:2]
hip_center = (l_hip + r_hip) / 2
shoulder_width = np.linalg.norm(r_sh - l_sh)
if shoulder_width < 50 or shoulder_width > 300:
if self.debug:
print("shoulder_width out of range")
return 'none'
# ----- КРЕСТ (предплечья скрещены на груди) -----
# 1. Запястья перекрещены (левое правее правого) И на уровне груди (ниже плеч, выше бедер)
wrists_crossed = l_wr[0] > r_wr[0]
wrists_chest_level = (max(l_wr[1], r_wr[1]) > max(l_sh[1], r_sh[1]) and
min(l_wr[1], r_wr[1]) < hip_center[1])
# 2. Локти тоже на уровне груди (примерно)
elbows_chest_level = (max(l_el[1], r_el[1]) > max(l_sh[1], r_sh[1]) * 0.9 and
min(l_el[1], r_el[1]) < hip_center[1] * 1.1)
# 3. Руки согнуты (предплечья короче верхней части руки)
left_bent = np.linalg.norm(l_wr - l_el) < np.linalg.norm(l_el - l_sh) * 0.9
right_bent = np.linalg.norm(r_wr - r_el) < np.linalg.norm(r_el - r_sh) * 0.9
arms_bent = left_bent and right_bent
# 4. Запястья близко к центру тела (не сильно отведены, типично для креста на груди)
body_center_x = (l_sh[0] + r_sh[0]) / 2
wrists_near_center = (abs(l_wr[0] - body_center_x) < shoulder_width * 0.6 and
abs(r_wr[0] - body_center_x) < shoulder_width * 0.6)
cross = (wrists_crossed and wrists_chest_level and elbows_chest_level and
arms_bent and wrists_near_center)
if self.debug and cross:
print(f"CROSS: crossed={wrists_crossed}, chest_w={wrists_chest_level}, "
f"chest_e={elbows_chest_level}, bent={arms_bent}, near_center={wrists_near_center}")
if cross:
return 'cross'
# ----- ДОМИК (руки над головой) -----
head_y = landmarks[idx['nose']][1] - 50
arms_up = (l_wr[1] < head_y and r_wr[1] < head_y)
if arms_up:
dist = np.linalg.norm(l_wr - r_wr)
rel_dist = dist / shoulder_width
if rel_dist < 1.5:
return 'dome'
return 'none'