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/README.md

24 KiB

Распознавание жестов на основе скелета

Проект позволяет распознавать статические и динамические жесты человека по данным скелета, полученным с помощью MediaPipe (или OAKкамеры). Реализованы:

  • детекция скелета (33 ключевые точки);
  • геометрическое и MLраспознавание специальных жестов (например, «домик», «крест»);
  • сбор и разметка собственных наборов данных;
  • обучение моделей (MLP, Random Forest, Logistic Regression для статики; LSTM для динамики);
  • оценка качества моделей.

Проект не включает симулятор робота или управление движением это отдельный репозиторий. Здесь представлена только библиотека распознавания жестов.

Структура проекта

gesture_rec/
├── camera                       # Методы для захвата видеоряда с разных источников
│   ├── base_camera.py           # Базовый класс
│   ├── factory.py               # Конвейер создания камеры
│   ├── oak_camera.py            # Реализация для oak
│   └── web_camera.py            # Реализация для web
├── skeleton/                    # Детекция скелета
│   ├── oak_pose_detector.py     # Детектор на oak (efficienthrnet)
│   └── mediapipe_detector.py    # MediaPipe
├── gesture_control/             # Распознавание статичных жестов
│   └── special_gestures.py      # Специальные жесты (геометрия или ML)
├── ml_gestures/                 # ML для статичных жестов
│   ├── feature_extractor.py     # Нормализация landmarks
│   ├── predict.py               # класс-обёртка для предсказания
│   └── train.py                 # обучение классификаторов
├── ml_gestures_dynamic/         # ML для динамических жестов
│   ├── feature_extractor.py
│   ├── sequence_utils.py        # Загрузка данных
│   ├── predict.py               # Класс для предсказания (с буфером)
│   ├── evaluate.py              # Оценка LSTM
│   └── train.py                 # Обучение LSTM
├── utils/                       # Вспомогательные скрипты
│   ├── annotate.py              # Разметка изображений
│   ├── capture_photo.py         # Съёмка фото с камеры
│   └── record_dynamic.py        # Разметка видеопоследовательности
└── submodules                   # Внешние зависимости (git submodule)
    └── OAK-HumanPoseEstimation  # Репозиторий для работы с OAK

Установка и настройка

1. Клонирование репозитория

git clone --recursive https://git.robofob.ru/sirius/gesture_rec.git
cd gesture_rec

Если вдруг уже склонировали без --recursive, выполните подгрузку модулей:

cd gesture_rec
git submodule update --init --recursive

2. Создание виртуального окружения

Рекомендуется использовать виртуальное окружение (Python 3.8+):

python3 -m venv venv
source venv/bin/activate

3. Установка зависимостей

pip install -r requirements.txt

4. Проверка работы камеры

Для веб-камеры достаточно, чтобы она была доступна по индексу (встроенная 0). Для OAK-D потребуется подключить устройство и установить драйверы.

Также OAK-D можно использовать, чтобы прям на камере выполнять вычисление скелета, для этого убедитесь, что модуль OAK-HumanPoseEstimation загружен - его код используется в skeleton/oak_pose_detector.py.

Затем перейдите в эту подпапку и скачайте модели детектирования скелета:

cd submodules/OAK-HumanPoseEstimation/models/
wget "https://drive.google.com/uc?export=download&id=1AUszSCMSc5dCATnZn1jK8PzMMyLQ__5M" -O models.zip
unzip models.zip

Работа с проектом

Все скрипты (обучение, сбор данных, оценка) рассчитаны на запуск из корневой директории проекта.

0. Распознавание скелета

0.1. MediaPipe

Основной класс skeleton.mediapipe_detector.MediaPipeDetector. Этот детектор должен принимать на вход изображение:

from skeleton.mediapipe_detector import MediaPipeDetector

detector = MediaPipeDetector(model_complexity=1, min_detection_confidence=0.5)
result = detector.detect(frame_bgr)
if result['success']:
    landmarks = result['landmarks']   # (33,4)  x, y, z, visibility
    # для отрисовки:
    vis = detector.draw_landmarks(frame, result['pose_landmarks'])

Параметры:

  • model_complexity 0 (лёгкая), 1 (средняя) или 2 (тяжёлая). Влияет на точность и FPS.
  • min_detection_confidence порог уверенности для детекции (0.50.9).

Важно: этой модели нужно получать само изображение, поэтому тип камеры для неё не важен, работает со всеми.

0.2. На борту OAK

Для экономии ресурсов, можно детектировать скелет сразу с камеры. Такой метод работает только с OAK камерой, и недоступен для web. Класс - skeleton.oak_pose_detector.OakPoseDetector. Хоть функционал похож с детектором MediaPipe - основное отличие в том, что нельзя подать на вход изображение.

from skeleton.oak_pose_detector import OakPoseDetector

detector = OakPoseDetector(
            model_type='efficienthrnet1',
            detection_threshold=0.2,
            shaves=6
        )
frame, landmarks = detector.get_frame_and_pose()
# для отрисовки:
vis = detector.draw_landmarks(frame, landmarks)

Подробнее о параметрах модели в репозитории.

1. Геометрическое распознавание жестов

Класс SpecialGestureDetector в режиме mode='geometric' анализирует координаты скелета и применяет набор правил.

На текущий момент геометрически распознаются два жеста:

  • "домик"
  • "крест"

Все пороги (уверенность min_conf, коэффициенты) заданы внутри _geometric_predict и могут быть подстроены под ваши условия. Чтобы распознавать собственный жест, отредактируйте метод _geometric_predict в файле gesture_control/special_gestures.py.

1.1. Порядок действий:

  1. Определите, какие ключевые точки MediaPipe участвуют в жесте (список индексов см. в коде или в документации MediaPipe).
  2. Напишите условие, используя координаты (x, y) нужных точек. Например, жест «рука в сторону»: left_wrist[0] > left_shoulder[0] + width и right_wrist[0] < right_shoulder[0] - width.
  3. Вставьте проверку до финального return 'none', чтобы при совпадении условий возвращать строку с именем вашего жеста.

Пример добавления жеста "руки в стороны":

arms_out = (l_wr[0] > l_sh[0] + shoulder_width*0.5 and 
            r_wr[0] < r_sh[0] - shoulder_width*0.5)
if arms_out:
    return 'arms_out'

Важно: геометрический режим не требует обучения, но чувствителен к позе и ракурсу. Для более сложных жестов рекомендуем использовать ML.

1.2. Использование в коде:

from gesture_control.special_gestures import SpecialGestureDetector
detector = SpecialGestureDetector(mode='geometric')
gesture = detector.predict(landmarks) # вернет none или название жеста

2. ML-распознаванием статичных жестов

Распознавание отдельных кадров с помощью обученного классификатора.

2.1. Сбор датасета

Создай папку для изображений. Можешь поместить туда фотографии жестов из интернета. Либо же можешь самостоятельно снять изображения с камеры: Скрипт utils/capture_photo.py сохраняет изображения с камеры.

python3 utils/capture_photo.py --dir data/raw --camera_type web

Параметры запуска:

  • --dir - папка для сохранения (по умолчанию captured в корне проекта).
  • --camera - ID веб-камеры (по умолчанию 0).
  • --camera_type - web или oak (по умолчанию web).
  • --no-mirror - отключить зеркалирование (по умолчанию включено).

Управление: s начать обратный отсчёт (3 сек) и сохранить фото, q выход. Нажмите s, подождите 3 секунды, фото сохранится в папку data/raw. Нажмите q чтобы закончить.

2.2. Разметка

Скрипт utils/annotate.py показывает каждое фото с распознанным скелетом и позволяет назначить класс:

python3 utils/annotate.py --folder data/raw --classes dome,cross,none --output data.csv

Параметры запуска:

  • --folder папка с изображениями.
  • --classes список классов через запятую (порядок соответствует цифрам 1,2,3…).
  • --output выходной CSV (по умолчанию gesture_data.csv).
  • --max_display_size размер окна для предпросмотра (ширина,высота, по умолчанию 800,600).

Управление: для каждого фото нажмите цифру, соответствующую жесту (1dome, 2cross, 3none), или n для пропуска или q выйти. При выходе данные сохранятся в gesture_data.csv. CSV с колонками class, f0…f98 (99 нормализованных координат).

2.3. Обучение модели

python3 ml_gestures/train.py --csv data.csv --model ml_gestures/models/special_model.pkl --type mlp --balance --target_classes dome,cross

Параметры запуска:

  • --csv путь к размеченному CSV (обязательно).
  • --model путь для сохранения модели (обязательно, расширение .pkl).
  • --type тип модели: linear (Logistic Regression), mlp (MLPClassifier) или rf (RandomForestClassifier). По умолчанию mlp.
  • --test_size доля тестовой выборки (по умолчанию 0.2).
  • --random_state seed для воспроизводимости (по умолчанию 42).
  • --balance если указан, балансирует целевые классы (указанные в --target_classes) до минимального размера среди них. Класс none не трогается.
  • --target_classes список классов для балансировки через запятую (по умолчанию все классы, кроме none).

После обучения сохраняются модель и отчёт special_model_report.json с метриками (accuracy, precision/recall/f1 по классам, матрица ошибок).

2.4. Оценка модели

После обучения модель сохраняется, и создаётся отчёт special_model_report.json с метриками (accuracy, precision, recall, f1, confusion matrix). Для повторной оценки используйте:

python3 ml_gestures/evaluate.py --csv data.csv --model ml_gestures/models/special_model.pkl --test_size 0.2
  • --csv путь к размеченному CSV (обязательно).
  • --model путь к сохраненной модели (обязательно, расширение .pkl).
  • --test_size доля тестовой выборки (по умолчанию 0.2).
  • --random_state seed для воспроизводимости (по умолчанию 42).

Печатает результаты тестирования в консоль.

2.5. Использование обученной модели

from ml_gestures.predict import MLGesturePredictor

predictor = MLGesturePredictor('model.pkl', class_names=['dome','cross','none'])
gesture = predictor.predict(landmarks)   # возвращает строку с классом

3. ML-распознавание динамических жестов

Распознавание жестов по последовательности кадров с помощью LSTM.

3.1. Сбор датасета

Скрипт utils/record_dynamic.py записывает серию кадров (скелет) в течение заданной длительности.

python3 utils/record_dynamic --label wave_right --output dynamic_data.csv --duration 2.0

Параметры запуска:

  • --label название жеста (обязательно).
  • --output CSV-файл для сохранения (по умолчанию dynamic_data.csv).
  • --duration длительность записи в секундах (по умолчанию 3.0).
  • --camera_type web или oak (по умолчанию web).
  • --no-mirror отключить зеркалирование

Управление: нажмите space, через 3 секунды начнется запись, последовательность точек с меткой сохранится в файл dynamic_data.csv. В CSV сохраняются колонки: label, sequence_id, frame_idx, f0…f98. Нажмите q чтобы закончить.

Важно: при записи в консоль выводится число кадров последовательности используйте его как ориентир для --max_len в обучении (можно округлить вверх).

3.2. Обучение LSTM

python3 ml_gestures_dynamic/train --data dynamic_data.csv --model dynamic_model.h5 --max_len 14 --epochs 50 --test_size 0.2

Параметры запуска:

  • --data путь к CSV-файлу или папке с несколькими CSV (все будут объединены).
  • --model путь для сохранения модели (.h5).
  • --max_len длина последовательности (количество кадров). Должен совпадать с длиной, использованной при записи. Если последовательности короче, они дополняются нулями; если длиннее обрезаются.
  • --lstm_units число нейронов в LSTM (по умолчанию 64).
  • --epochs количество эпох (по умолчанию 50).
  • --batch_size размер батча (по умолчанию 16).
  • --test_size доля тестовой выборки (по умолчанию 0.2).

После обучения сохраняются: модель (.h5), файл с классами (_classes.pkl) и отчёт (_report.json) с метриками.

3.3. Оценка модели

После обучения модель сохраняется, и создаётся отчёт dynamic_model_report.json с метриками (accuracy, precision, recall, f1, confusion matrix). Для повторной оценки используйте:

python3 ml_gestures_dynamic/evaluate --data dynamic_data.csv --model dynamic_model.h5 --max_len 14 --test_size 0.2

Параметры запуска:

  • --data путь к CSV-файлу или папке с несколькими CSV (все будут объединены).
  • --model путь для сохранения модели (.h5).
  • --max_len длина последовательности (количество кадров). Должен совпадать с длиной, использованной при записи. Если последовательности короче, они дополняются нулями; если длиннее обрезаются.
  • --test_size доля тестовой выборки (по умолчанию 0.2).

3.4. Использование в коде

Класс DynamicGesturePredictor накапливает кадры в буфере и выдаёт предсказание, когда накоплено достаточно данных.

from ml_gestures_dynamic.predict import DynamicGesturePredictor

predictor = DynamicGesturePredictor(
    model_path='dynamic_model.h5',
    classes_path='dynamic_model_classes.pkl',
    window_size=14,          # должно совпадать с max_len при обучении
    threshold=0.5            # минимальная уверенность для выдачи класса
)

# В цикле обработки кадров:
predictor.add_frame(landmarks)   # landmarks (33,4) или None
gesture = predictor.predict()    # возвращает класс или None, если недостаточно данных/уверенность ниже порога

Возможные проблемы

  1. Камера не работает - проверьте права доступа, индекс камеры (--camera). Для OAK убедитесь, что устройство подключено и depthai установлен.
  2. Скелет не определяется стойте на расстоянии 12 м, плечи в кадре. Снизьте min_detection_confidence или увеличьте model_complexity в MediaPipeDetector.
  3. Ложные срабатывания жестов в геометрическом режиме увеличьте min_conf в special_gestures.py или переключитесь на ML-режим с большим количеством примеров.
  4. Не хватает памяти для LSTM уменьшите --max_len или --batch_size, используйте меньше --lstm_units.
  5. Ошибка импорта при запуске скриптов все исполняемые скрипты автоматически добавляют корень проекта в sys.path, поэтому запускайте из корня проекта.

Датасеты (на точках MediaPipe)

Название Описание Ссылка Количество примеров Классы
special_gestures_v1 Набор фотографий для распознавания специальных жестов (домик, крест, none). Собран с помощью capture_photo.py и стоковых изображений, размечен через annotate.py. Скачать 77 (после балансировки, 13 на класс dome/cross, 51 none) dome, cross, none
dynamic_data_v1 csv файл с последовательностью точек скелета для 14 кадров Скачать 81 (по 20 на махи правой и левой рукой, 61 на none) none, wave_left, wave_right

Модели (на точках MediaPipe)

Название Тип Архитектура / параметры Вход Выход Точность (test) Precision / Recall (по классам) Ссылка Распознаваемые жесты
special_gestures_mlp MLP (scikit-learn) Скрытые слои: (64, 32), активация ReLU, Adam, early stopping, 500 эпох 99 нормализованных координат скелета (33 точки × 3) 3 класса (dome, cross, none) 0.56 dome: P=0.00, R=0.00
cross: P=0.00, R=0.00
none: P=0.60, R=0.90
Скачать dome (руки над головой домиком)
cross (предплечья скрещены на груди)
none (остальные)
special_gestures_rf Random Forest 50 деревьев, max_depth=10, random_state=42 99 нормализованных координат скелета (33 точки × 3) 3 класса (dome, cross, none) 0.87 dome: P=0.67, R=1.00
cross: P=0.750, R=1.00
none: P=0.90, R=0.90
Скачать dome (руки над головой домиком)
cross (предплечья скрещены на груди)
none (остальные)
special_gestures_lin Logistic Regression max_iter=1000, random_state=42 99 нормализованных координат скелета (33 точки × 3) 3 класса (dome, cross, none) 0.94 dome: P=1.00, R=1.00
cross: P=0.750, R=1.00
none: P=1.00, R=0.90
Скачать dome (руки над головой домиком)
cross (предплечья скрещены на груди)
none (остальные)
dynamic_model LSTM 2 слоя LSTM по 64 нейрона, Dropout(0.3), Dense(3, softmax), optimizer=Adam, loss=sparse_categorical_crossentropy, epochs=50, batch_size=16 Последовательность из 14 кадров, каждый кадр 99 признаков (нормализованные координаты скелета) 3 класса (none, wave_left, wave_right) 0.8235 Precision / Recall (по классам) Скачать wave_left (махание левой рукой) wave_right (махание правой рукой) none (отсутствие жеста)