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.
 
 
Go to file
gestures3 b745a2018b poses_detection 8 hours ago
camera finished oak and web cams and run files 1 month ago
gesture_control poses_detection 8 hours ago
ml_gestures finished oak and web cams and run files 1 month ago
ml_gestures_dynamic finished oak and web cams and run files 1 month ago
skeleton oak detector skelet 4 weeks ago
submodules oak detector skelet 4 weeks ago
utils finished oak and web cams and run files 1 month ago
.gitignore problem with fps 4 months ago
.gitmodules oak detector skelet 4 weeks ago
README.md Update 'README.md' 3 weeks ago
install_deps.sh changed cross detection to silly and fixed deps 3 weeks ago

README.md

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

Проект позволяет распознавать статические и динамические жесты человека по данным скелета, полученным с помощью 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/
│   ├── arm_control.py           # Преобразование позы в значения для угловой и линейной скоростей
│   └── 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.10:

python3.10 -m venv venv
source venv/bin/activate

Если у вас не скачен питон этой версии, сначала выполните:

sudo apt install python3.10 python3.10-venv

Если у нас не устанавливается питон 3.10, то это потому что он отсутствует в официальных репозиториях по умолчанию, надо добавить репозиторий перед скачиванием:

sudo apt update && sudo apt install -y software-properties-common
sudo add-apt-repository ppa:deadsnakes/ppa
sudo apt update

To install pip:

sudo apt install -y python3-pip

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

3.1. Способ 1

Сделайте bash скрипт исполняемым и запустите последовательность установки:

chmod +x install_deps.sh
./install_deps.sh

3.2. Способ 2

Вручную:

pip install --upgrade pip
pip install numpy==1.24.3
pip install pandas==2.0.3
pip install pyyaml
pip install opencv-python==4.12.0.88
pip install opencv-python-headless==4.12.0.88
pip install matplotlib==3.7.5
pip install protobuf==3.20.3
pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu
pip install --no-deps mediapipe==0.10.11
pip install attrs
pip install scikit-learn==1.3.2
pip install joblib==1.4.2
pip install depthai==2.28.0
pip install --no-deps tensorflow==2.13.1
pip install \
    absl-py \
    astunparse \
    flatbuffers \
    gast \
    google-pasta \
    grpcio \
    h5py \
    keras==2.13.1 \
    libclang \
    opt-einsum \
    tensorboard==2.13.0 \
    tensorflow-estimator==2.13.0 \
    termcolor \
    wrapt \
    requests
pip install numpy==1.24.3
pip install scipy==1.11.0

Типичные проблемы во время установки:

Важно: красные предупреждения о нехватке зависимостей для tensorflow будут, но можно их игнорировать, т.к. эти модули не используются в данном проекте, а их установка мешает зависимостям MediaPipe.

  1. Если будет ошибка с "AttributeError: google..." но это потому что tensorflow подменяет версию библиотеки protobuf, выполните:
pip uninstall protobuf google protobuf
pip install protobuf==3.20.3

Примечание: если у вас слабая видеокарта или нет CUDA, используйте tensorflow-cpu вместо tensorflow.

  1. Если будет проблема с функцией cv2.imshow() то, переустановите cv2 без заголовков:
pip uninstall opencv-python opencv-python-headless
pip install opencv-python==4.12.0.88
  1. Если будет ошибка с PIL, попробуйте обновить библиотеку:
pip upgrage pillow
  1. Внимание: если вам пришлось делать после основной установки какие-то дополнительные, обязательно зафиксируйте еще раз версию numpy!
pip install numpy==1.24.3

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

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

  1. Подключите камеру по usb, если сразу запустите скрипт, то вылетит с ошибкой No available devices.
  2. Выполните команду:
echo 'SUBSYSTEM=="usb", ATTRS{idVendor}=="03e7", MODE="0666"' | sudo tee /etc/udev/rules.d/80-movidius.rules
  1. Затем:
sudo udevadm control --reload-rules && sudo udevadm trigger
  1. После этого отключите камеру от USB и подключите снова. Теперь камера должна быть доступна.

Также 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. Определение значений скоростей

По умолчанию преобразование позы в скорости реализовано в классе ArmController (файл arm_control.py).

Чтобы изменить логику управления, выполните одно из действий:

  1. Изменить метод compute_speeds в arm_control.py он должен принимать аргумент landmarks (список из 33 точек MediaPipe) и возвращать кортеж (linear, angular) числа с плавающей точкой.
  2. Создать свой класс-наследник от ArmController и переопределить compute_speeds. Затем в main.py заменить создание экземпляра на свой класс.

После этого не забудьте изменить параметры словаря ARM_CONTROL, вы можете добавлять туда свои поля и читать их в методе. Текущий класс создается и используется следующим образом:

from gesture_control.arm_control import ArmController

mirror = True # для всех фронтальных камер
ARM_CONTROL = {
        'linear_arm': 'right',
        'angular_arm': 'left',
        'max_speed_linear': 1.0,
        'max_speed_angular': 1.0,
        'dead_zone': 0.2,
        'debug': False
    }
arm_control = ArmController(ARM_CONTROL, mirror=mirror)
linear, angular = arm_control.compute_speeds(landmarks) # Дальше эти скорости можно подавать на контроллер
  • ARM_CONTROL словарь:
    • linear_arm рука для линейной скорости ('left' или 'right')
    • angular_arm рука для угловой скорости
    • max_speed_linear макс. линейная скорость (м/с)
    • max_speed_angular макс. угловая скорость (рад/с)
    • dead_zone зона нечувствительности (0.01.0)
    • debug выводить отладочную информацию в консоль

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

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

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

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

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

2.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.

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

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

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

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

3.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 чтобы закончить.

3.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 нормализованных координат).

3.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 по классам, матрица ошибок).

3.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).

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

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

from ml_gestures.predict import MLGesturePredictor

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

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

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

4.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 в обучении (можно округлить вверх).

4.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) с метриками.

4.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).

4.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 (отсутствие жеста)