
Наткнулся на старую веб-камеру, которая валялась без дела. Дешевый вариант с посредственным качеством. С такой камерой не стать звездой онлифанс. Но выкидывать жалко. Пришла в голову мысль — почему не соорудить управление жестами для компьютера?
Полноценное управление компьютером, как в фильмах с Томом Крузом, пока остаётся фантастикой: руки затекают, случайные движения превращаются в случайные команды. А вот привязать к жестам пару действий — выключить звук, свернуть окна, прокрутить ленту — реально и за один вечер. Понадобится старая веб-камера, Python и готовые модели Google. В статье — пошаговая сборка, разбор кода и инструкция, как обучить модель реагировать на жесты.
Что понадобится
Я использовал камеру HP Webcam 4310, она пишет видео с разрешением 1920х1080 пикселей. Но проект должен работать и на более слабых камерах. При обработке изображения модель в любом случае будет понижать разрешение кадра.
Разработку вел под Windows, но ее без проблем можно перенести на Linux или Mac. Ключевые отличия — в системных вызовах, например, команды управления звуком.
Модель использовал готовую. У Google есть удобный проект MediaPipe Solutions с моделями детекции. Вот, что Google пишет про свой проект:
MediaPipe Solutions предоставляет набор библиотек и инструментов для быстрого применения методов искусственного интеллекта (ИИ) и машинного обучения (МО) в ваших приложениях. Решения встраиваются как есть, настраиваются под задачу и работают на разных платформах. MediaPipe Solutions является частью проекта MediaPipe с открытым исходным кодом, поэтому вы можете дополнительно настраивать код решений в соответствии с потребностями вашего приложения.
В списке доступных моделей есть две подходящие под задачу: HandLandmarker и HandGestureClassifier. HandLandmarker — это базовая модель, которая определяет руки по 21 ключевой точке и связям между ними. Различает левую и правую ладонь. Видит все руки, попавшие в кадр, даже если в нем несколько человек. Но она не понимает жесты как совокупность признаков. Загнутые пальцы или раскрытая ладонь для нее одинаковы, просто в разных местах расположены точки и соединяющие линии удлинились или укоротились. Это хорошо для управления мышкой — следишь за изменениями координат и переносишь их на плоскость экрана. Но чтобы распознать открытую ладонь, потребуется математический алгоритм, рассчитывающий относительное расположение точек.
Модель HandGestureClassifier, наоборот, обучена понимать жесты. Базово она умеет распознавать: сжатый кулак, открытая ладонь, указательный палец вверх, большой палец вверх (лайк), большой палец вниз (дизлайк), виктори (указательный и средний пальцы), и любовь. Если базового набора недостаточно, модель можно переобучить через Model Maker. HandGestureClassifier лучше подходит под мою задачу — не нужны сложные алгоритмы и кода писать меньше.
Собираем проект управления жестами
Начну с базы — получение и вывод изображения веб-камеры с помощью OpenCV. Когда видишь, что попадает в кадр и как нейросеть меняет картинку, писать код гораздо удобнее.
Скрипт будет на языке Python. Установим зависимости командой:
pip install opencv-python mediapipe
Исходник:
# OpenCV умеет: захватывать видео с камеры, открывать окна, рисовать на кадрах. import cv2 # Открываем камеру с индексом 0 (первая доступная в системе) cap = cv2.VideoCapture(0) if not cap.isOpened(): print("Ошибка: не удалось открыть камеру.") exit() print("Камера открыта. Нажми Q для выхода.") while True: # cap.read() захватывает один кадр. В ret падает True, если кадр успешно получен. # frame — numpy массив формата (высота, ширина, 3 канала BGR). ret, frame = cap.read() if not ret: print("Не удалось получить кадр.") break # Зеркалируем кадр по горизонтали, иначе будет неудобно работать с зеркальными жестами frame = cv2.flip(frame, 1) # Показываем кадр в окне с названием "Camera".Если окно ещё не создано — cv2.imshow создаст его автоматически. cv2.imshow("Camera", frame) # Ждём нажатия клавиши 1 миллисекунду, без этого вызова окно зависнет и не будет обновляться. «& 0xFF» — маска для корректной работы на 64-битных системах. key = cv2.waitKey(1) & 0xFF if key == ord("q"): print("Выход.") break # По выходу из цикла, освобождаем камеру и закрываем все окна OpenCV. cap.release() cv2.destroyAllWindows()
Основной цикл программы (while True:) просто получает кадры с веб-камеры и выводит в окно OpenCV. Без цикла, скрипт покажет окно OpenCV, выведет в него изображение и тут же закроет.

Убедившись, что все корректно запускается и есть окно с изображением с камеры, можно прикручивать определение кистей.
Внесу изменения в блоке импортов Python-скрипта. Импортирую библиотеки, связанные с MediaPipe Tasks, это основа для компьютерного зрения на моделях Google. Внутри готовые алгоритмы визуального анализа.
from mediapipe.tasks import python as mp_python from mediapipe.tasks.python import vision as mp_vision from mediapipe.tasks.python.vision import drawing_utils, HandLandmarksConnections
MediaPipe Tasks — это основной набор библиотек, который дает высокоуровневый доступ к API MediaPipe Solutions. С помощью Tasks можно запустить сложную нейросеть в 20-30 строк кода на привычном языке программирования. В данном случае подключаем инструменты детекции ключевых точек кистей и отрисовки взаимосвязей между ними.
Константы скрипта:
# Имя файла модели (скачать и положить рядом со скриптом) MODEL_PATH = "gesture_recognizer.task" # индекс камеры CAMERA_INDEX = 0 # Схема соединения ключевых точек по которым ориентируется модель HAND_CONNECTIONS = HandLandmarksConnections.HAND_CONNECTIONS
Модель определения жестов работает на основе двадцать одной ключевой точки. Находит координаты точек и по их взаимному расположению определяет жест. Подробнее с процессом можно ознакомиться в официальной документации.

Подготовка и загрузка модели:
base_options = mp_python.BaseOptions(model_asset_path=MODEL_PATH) # GestureRecognizerOptions — конфигурация распознавателя. options = mp_vision.GestureRecognizerOptions( base_options=base_options, running_mode=mp_vision.RunningMode.VIDEO, num_hands=1, # искать максимум 1 руку min_hand_detection_confidence=0.5, # порог обнаружения руки (0.0–1.0) min_hand_presence_confidence=0.5, # порог отслеживания руки между кадрами min_tracking_confidence=0.5, # порог трекинга (если ниже — ищет руку заново) ) # Создаём объект распознавателя — загружает модель в память. recognizer = mp_vision.GestureRecognizer.create_from_options(options)
RunningMode.VIDEO включает покадровую обработку видео. Модель будет искать одну руку, за это отвечает параметр num_hands. Можно указать больше, тогда будут распознаны все руки, попавшие в кадр, даже если в кадре находится 10 человек. Но это замедлит работу и снизит точность определения.
Дальше идет порог уверенности min_hand_detection_confidence. Этот параметр указывает ИИ, насколько уверенной нужно быть в своей оценке. Варьируется от 0 до 1, где 1 это 100%.. Чем выше порог, тем модель требовательнее к качеству картинки. Точность вырастет, но при малейшем падении качества (хуже освещение, ниже разрешение) она перестанет видеть жесты. Чем ниже порог, тем больше ложных срабатываний. На тестах ИИ находил «жесты» в пересвеченных областях, где руки не было вовсе. Однажды распознала «лайк» в моём плече.
Добавил сервисную функцию, которая отрисовывает кадр, как его видит нейросеть. Функция отобразит ключевые точки и связи между ними прямо на кадре OpenCV. Вызывать буду сразу после обращения к ИИ.
def draw_hand_landmarks(frame, hand_landmarks_list): for hand_landmarks in hand_landmarks_list: drawing_utils.draw_landmarks( frame, hand_landmarks, # HAND_CONNECTIONS указывает, какие точки соединять линиями HAND_CONNECTIONS, )
frame — кадр от OpenCV, трёхмерный массив, где цвет каждого пикселя хранится в формате BGR (Blue-Green-Red).
hand_landmarks_list — список рук, где каждая рука представлена в виде 21 точки.
Теперь можно вмешиваться в основной цикл программы. Пока он просто выводит кадры с веб-камеры в окно OpenCV. Начну перехватывать кадры, накладывать точки и связи между ними. Заодно выведу название распознанного нейросетью жеста.
Заменим старый цикл while True на:
while True: ret, frame = cap.read() if not ret: print("Не удалось получить кадр.") break frame = cv2.flip(frame, 1) # Подготовка кадра для MediaPipe # OpenCV хранит пиксели в BGR, MediaPipe ожидает RGB — конвертируем. rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) # mp.Image — обёртка над numpy array для Tasks API. mp_image = mp.Image(image_format=mp.ImageFormat.SRGB, data=rgb_frame) # Увеличиваем временнуую метку на 33 мс (имитируем 30 FPS). Режим VIDEO требует монотонно возрастающую временную метку frame_timestamp_ms += 33 # Анализ конвертированного кадра с помощью компьютерного зрения result = recognizer.recognize_for_video(mp_image, frame_timestamp_ms)
Важно не запутаться: OpenCV работает с форматом BGR, а не классическим RGB. Чтобы MP Tasks корректно определил координаты точек, для него выполняется конвертация из BGR в RGB:
rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
Сконвертированный кадр передается в функцию recognize_for_video объекта, отвечающего за компьютерное зрение и поиск жеста. Результат работы функции:
gestures— найденные жесты.hand_landmarks— координаты ключевых точек руки.handedness— какая это рука, левая или правая.
Продолжаем править цикл. Если нейросеть определила руки, вызываем функцию отрисовки точек и линий:
if result.hand_landmarks: # hand_landmarks — список рук. Каждый элемент — список из 21 точки. draw_hand_landmarks(frame, result.hand_landmarks)
Координаты не зависят от цветовой схемы, поэтому результат определения передается в функцию отрисовки draw_hand_landmarks как есть. Функция отобразит информацию на кадре OpenCV.
Раз распознаватель уже вернул всю информацию, напечатаем в кадре название жеста и процент, насколько модель уверена, что поняла жест:
if result.gestures: # gestures[0] — результаты для первой руки, [0] — наиболее вероятное совпадение. top_gesture = result.gestures[0][0] label = f"{top_gesture.category_name} ({top_gesture.score:.0%})" cv2.putText( frame, label, # нижний левый угол (10, frame.shape[0] - 20), cv2.FONT_HERSHEY_SIMPLEX, 0.7, # зелёный (0, 255, 0), 2, ) # Подсказка по управлению cv2.putText(frame, "Q - quit", (10, 25), cv2.FONT_HERSHEY_SIMPLEX, 0.55, (200, 200, 200), 1) cv2.imshow("Gesture — Step 2", frame) if cv2.waitKey(1) & 0xFF == ord("q"): print("Выход.") break

Для управления жестами все готово. Осталось только привязать действия к значениям category_name определенных жестов. Для теста определил действия:
Closed_Fist(кулак) — проверить состояние звука в Windows, если включен — выключить и наоборот.Victory(жест «виктори», указательный и средний пальцы) — запустить браузер Firefox.Thumb_Down— скролл вниз.Thumb_Up— скролл вверх.
Создадим функцию execute_gesture_action(),которая свяжет жесты с действиями. Серия if..elif, проверит, какой жест ИИ распознала, и запустит нужное действие на компьютере:
def execute_gesture(gesture): if not can_execute(gesture): return if gesture == GESTURE_FIST: if toggle_audio_mute(): last_gesture_time[GESTURE_FIST] = time.time() elif gesture == GESTURE_VICTORY: if launch_firefox(): last_gesture_time[GESTURE_VICTORY] = time.time() elif gesture == GESTURE_THUMB_DOWN: scroll_down() last_gesture_time[GESTURE_THUMB_DOWN] = time.time() elif gesture == GESTURE_THUMB_UP: scroll_up() last_gesture_time[GESTURE_THUMB_UP] = time.time()
Перед действием функция проверяет блокировку по таймеру. Это простейшая реализация дебаунсинга. OpenCV снимает, в среднем, от 30 до 60 кадров в секунду. Пользователь не успеет с такой скоростью убрать жест, что приведет к многократному вызову одной и той же команды. Это как залипание клавиши. Например, сжатый кулак запустит серию включений и выключений звука. Избежать этого можно временной блокировкой функции на ближайшую секунду-две.
Проверку реализовал в функции can_execute():
def can_execute(gesture): now = time.time() if gesture not in last_gesture_time: return True return (now - last_gesture_time[gesture]) >= GESTURE_COOLDOWN
Время добавить константы. Работать с константами всегда удобнее. Если хочешь поменять идентификатор жеста или время задержки между скроллами, удобнее это сделать в одном месте, чем искать в коде все вхождения:
# Жесты (точные названия из модели) GESTURE_FIST = "Closed_Fist" GESTURE_VICTORY = "Victory" GESTURE_THUMB_UP = "Thumb_Up" GESTURE_THUMB_DOWN = "Thumb_Down" # Задержка между выполнениями одного жеста (сек) GESTURE_COOLDOWN = 1.2 # Состояние: время последнего выполнения каждого жеста last_gesture_time = {}
Добавим библиотеки с системными функциями и таймером:
import time import subprocess import os
Для управления звуком и скроллом, нужен доступ к Windows API. Функции работы с WinAPI реализованы в пакете ctype.
import ctypes from ctypes import wintypes
Скролл и управление звуком реализовал через эмуляцию нажатия клавиш. Чтобы избежать вылетов на ошибки, перестраховался и обернул вызовы в try..except .
def toggle_audio_mute(): try: # Имитируем нажатие клавиши Volume Mute (VK_VOLUME_MUTE = 0xAD) ctypes.windll.user32.keybd_event(0xAD, 0, 0, 0) # Нажатие time.sleep(0.05) # Небольшая задержка ctypes.windll.user32.keybd_event(0xAD, 0, 2, 0) # Отпускание print("✓ Клавиша mute нажата - звук переключен") return True except Exception as e: print(f"Ошибка имитации клавиши: {e}") return False return False def scroll_down(): """Скролл вниз на 3 прокрута колеса.""" ctypes.windll.user32.mouse_event(0x0800, 0, 0, -120, 0) # MOUSEEVENTF_WHEEL, delta -120 print("Скролл вниз") return False def scroll_up(): if WIN_API_AVAILABLE: """Скролл вверх на 3 прокрута колеса.""" ctypes.windll.user32.mouse_event(0x0800, 0, 0, 120, 0) print("Скролл вверх")
Функция, которая открывает браузер Firefox по жесту «виктори» (два пальца вверх):
def launch_firefox(): paths = [r"C:\Program Files\Mozilla Firefox\firefox.exe", r"C:\Program Files (x86)\Mozilla Firefox\firefox.exe", "firefox"] for path in paths: if os.path.exists(path) or path == "firefox": subprocess.Popen([path]) print("? Firefox запущен") return True print("Firefox не найден") return False
Осталось добавить вызов execute_gesture() после определения жеста:
if result.gestures: top_gesture = result.gestures[0][0] execute_gesture(top_gesture.category_name)
Можно запустить скрипт и убедиться, что нейронка прекрасно видит руки и выполняет команды без клавиатуры и мыши.
Видео с тестами выложил на Ютуб:
Кастомизация модели
Стандартный набор жестов в Gesture Recognizer небольшой и нужен только для примера работы. Многие из них слишком простые. Они могут случайно сработать в обычной жизни — например, во время телефонного разговора. Облокотился на кулак — скрипт выключил звук, поднял большой палец — закрыл игру без сохранения.
В MediaPipe можно определить свой набор жестов, обучив модель с помощью MediaPipe Model Maker. Но нужно понимать, что это полноценное создание нового набора жестов с нуля. Старые жесты пропадут, это нужно учитывать при модификации кода. Мы ниже разберем, как выстроить новую модель, в том числе с добавлением жестов из стандартного набора.
Если написать небольшой скрипт автоматизации, процесс займет 15-20 минут. Исходники скрипта дам чуть ниже. Модель легковесная, поэтому спокойно обучается на CPU.
Зависимости пакета MediaPipe Model Maker могут вызвать проблемы и конфликты в Windows. Чтобы избежать проблем с зависимостями, использовал Ubuntu в WSL. Использую Python версии 3.10. В старших версиях могут выплыть проблемы с совместимостью, поэтому советую поставить именно 3.10. Из пакетов Python, нужно установить mediapipe-model-maker:
pip install mediapipe-model-maker
Вместе с пакетом подтянутся все зависимости, включая TensorFlow.
Для обучения нужен датасет с жестами. Пример датасета выложен в хранилище Google.

В примерах видно, что это набор изображения кисти с жестом в разных ракурсах с минимум посторонней информации. Кадрировать нужно максимально плотно к кисти, чтобы модель не фокусировалась на заднем фоне.
Важны не только сами картинки, но и структура папок. Названия тоже важны, но об этом ниже.
/dataset /thumbs_up /image1.jpg /image...jpg /image150.jpg /thumbs_down /image1.jpg /image...jpg /image150.jpg /none /image1.jpg /image...jpg /image150.jpg
Каждая подпапка — это набор изображений для отдельного жеста. Название подпапки — это идентификатор жеста, который будет использоваться в конечном наборе. Если подпапка называется thumbs_up, в скрипте нужно использовать идентификатор жеста thumbs_up.
Обязательно должна быть папка none. В эту папку сложил жесты, которые не входят в набор. Это негативные примеры для тренировки. Без негативных примеров модель обучится криво. Не просто станет хуже детектить, а запутается и будет детектить что угодно, кроме нужных жестов.
Для каждого жеста нужно 100-200 изображений. Если нужны жесты из набора, который идет с моделью, их тоже нужно сфотографировать или найти.
Искать сотни картинок — долго и нудно. Фотографировать отдельно тоже неудобно. Можно написать скрипт, который будет делать скриншоты с окна OpenCV. Его задача — получить изображение с OpenCV, обрезать по контуру ладони +100 пикселей по бокам и сохранить итоговое изображение в папку датасета. Сохраняет скрипт по нажатию клавиши Пробел. Обрезка нужна, чтобы убрать лишнюю информацию, при этом явно продемонстрировать, как должен выглядеть жест.
Ничего страшного, если несколько изображений будут смазанными. Это даже полезно, но лучше держать их количество в пределах 1-2%.

Важно убедиться, что в папке со скриптом лежит файл модели hand_landmarker.task. Скачать его можно из репозитория Google. Исходный код:
import cv2 import os import sys import numpy as np import mediapipe as mp from mediapipe.tasks import python as mp_python from mediapipe.tasks.python import vision as mp_vision # РАЗБОР АРГУМЕНТОВ КОМАНДНОЙ СТРОКИ def get_output_dir(): """Возвращает путь к папке из первого аргумента или значение по умолчанию.""" if len(sys.argv) > 1: return sys.argv[1] return "my_gestures/none" def confirm(prompt): """Запрашивает у пользователя подтверждение (y/n). Возвращает True, если ответ y.""" while True: answer = input(prompt + " (y/n): ").strip().lower() if answer in ('y', 'yes'): return True if answer in ('n', 'no'): return False print("Пожалуйста, введите y или n.") # НАСТРОЙКИ (из аргументов) OUTPUT_DIR = get_output_dir() MODEL_PATH = "hand_landmarker.task" PADDING_MIN = 50 PADDING_MAX = 100 # ПРОВЕРКИ БЕЗОПАСНОСТИ print(f"Целевая папка: {OUTPUT_DIR}") # 1. Существует ли папка? if os.path.exists(OUTPUT_DIR): if not confirm(f"Папка '{OUTPUT_DIR}' уже существует. Продолжить работу с ней?"): print("Выход.") sys.exit(0) # 2. Есть ли внутри файлы? try: files = [f for f in os.listdir(OUTPUT_DIR) if os.path.isfile(os.path.join(OUTPUT_DIR, f))] if files: print(f"В папке найдено {len(files)} файлов:") for fname in files[:5]: print(f" - {fname}") if len(files) > 5: print(f" ... и ещё {len(files)-5} файлов.") if not confirm("Действительно хотите работать с этой папкой?"): print("Выход.") sys.exit(0) except PermissionError: print("Ошибка доступа к папке. Выход.") sys.exit(1) else: # Папки нет – создадим после всех проверок print("Папка будет создана автоматически.") # Создаём папку (если её нет) os.makedirs(OUTPUT_DIR, exist_ok=True) # ИНИЦИАЛИЗАЦИЯ MediaPipe base_options = mp_python.BaseOptions(model_asset_path=MODEL_PATH) options = mp_vision.HandLandmarkerOptions( base_options=base_options, running_mode=mp_vision.RunningMode.VIDEO, num_hands=1, min_hand_detection_confidence=0.5 ) landmarker = mp_vision.HandLandmarker.create_from_options(options) # ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ def get_hand_bbox(landmarks, width, height): xs = [lm.x * width for lm in landmarks] ys = [lm.y * height for lm in landmarks] x_min, x_max = int(min(xs)), int(max(xs)) y_min, y_max = int(min(ys)), int(max(ys)) return x_min, y_min, x_max, y_max def expand_bbox(x_min, y_min, x_max, y_max, frame_w, frame_h): box_w = x_max - x_min box_h = y_max - y_min pad = int(min(max(box_w, box_h) * 0.3, PADDING_MAX)) pad = max(pad, PADDING_MIN) x_min = max(0, x_min - pad) y_min = max(0, y_min - pad) x_max = min(frame_w, x_max + pad) y_max = min(frame_h, y_max + pad) return x_min, y_min, x_max, y_max # КАМЕРА cap = cv2.VideoCapture(0) if not cap.isOpened(): print("Камера не найдена") exit() print("Пробел — сохранить CROP, Q — выход") timestamp = 0 counter = 0 while True: ret, frame = cap.read() if not ret: break frame = cv2.flip(frame, 1) h, w = frame.shape[:2] rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) mp_image = mp.Image(image_format=mp.ImageFormat.SRGB, data=rgb) timestamp += 33 result = landmarker.detect_for_video(mp_image, timestamp) crop_preview = None if result.hand_landmarks: landmarks = result.hand_landmarks[0] x_min, y_min, x_max, y_max = get_hand_bbox(landmarks, w, h) x_min, y_min, x_max, y_max = expand_bbox(x_min, y_min, x_max, y_max, w, h) crop = frame[y_min:y_max, x_min:x_max] crop_preview = crop cv2.rectangle(frame, (x_min, y_min), (x_max, y_max), (0, 255, 0), 2) cv2.imshow("Full Frame", frame) if crop_preview is not None: cv2.imshow("Crop Preview", crop_preview) key = cv2.waitKey(1) & 0xFF if key == ord('q'): break elif key == ord(' '): if crop_preview is not None: counter += 1 filename = f"img_{counter:04d}.jpg" path = os.path.join(OUTPUT_DIR, filename) cv2.imwrite(path, crop_preview) print(f"Saved {filename}") else: print("Рука не найдена — кадр пропущен") cap.release() cv2.destroyAllWindows() landmarker.close()
Для задач проекта достаточно собрать датасет со своей рукой. Тесты с разным освещением и фоном показали хороший процент распознавания. Переобученную модель не смутили и детские руки, все жесты хорошо отрабатывает.
Команда запуска скрипта:
python make_dataset.py <gesture_label>
При запуске скрипт проверяет папку по названию жеста. Если она есть, придется подтвердить свое желание работать с ней. На случай ошибочного «да» — вторая проверка: есть ли уже файлы в папке. Если изображения уже есть, скрипт об этом сообщит и спросит, действительно ли пользователь хочет работать с этой папкой.
После запуска откроется окно OpenCV. Когда скрипт увидит в кадре руку, появится второе окно с кадрированим по кисти плюс 50-100 пикселей. Клавиша Пробел сохранит кадрированное изображение. Зелёная рамка тоже попадёт в кадр, но на обучение это не влияет.
Мой набор жестов:
«Рога», «бык», «распальцовка», «рок-н-ролл»
«Собака» — обычная собака из театра теней руками
Указательный палец вверх
Указательный палец вниз
Жест «собака» я добавил не просто так. Это отличный пример того, что будет, если легкомысленно подойти к папке none. С недостаточным количество отрицательных примеров жест «собака» появляется даже на демонстрацию ладони. Как лицевой, так и тыльной стороны.

Чтобы это исправить, нужно добавить в none изображения прямой ладони в разных ракурсах. Либо можно добавить жест открытой ладони, это тоже поможет модели правильно понять разницу.

На скриншотах хорошо видна разница. Версия с папкой none четко видит разницу между тыльной стороной ладони и жестом «собака».
Скрипт обучения:
import os from mediapipe_model_maker import gesture_recognizer dataset_path = "my_gestures" # Загрузка датасета (MediaPipe сам извлечёт ландмарки из фото) data = gesture_recognizer.Dataset.from_folder( dirname=dataset_path, hparams=gesture_recognizer.HandDataPreprocessingParams() ) # Разбивка: 80% обучение, 10% валидация, 10% тест train_data, rest_data = data.split(0.8) validation_data, test_data = rest_data.split(0.5) # Настройка гиперпараметров. hparams = gesture_recognizer.HParams( export_dir="exported_model", epochs=20, learning_rate=0.001, batch_size=2 ) options = gesture_recognizer.GestureRecognizerOptions(hparams=hparams) # Обучение model = gesture_recognizer.GestureRecognizer.create( train_data=train_data, validation_data=validation_data, options=options ) # Оценка loss, acc = model.evaluate(test_data, batch_size=1) print(f"Test loss: {loss:.4f}, Test accuracy: {acc:.4f}") # Экспорт model.export_model() print("Модель сохранена в exported_model/gesture_recognizer.task")
По сути, в коде только две вещи: какую модель загрузить и с какими параметрами обучать. Конкретные значения можно подкрутить под себя, но в целом параметры скрипта дают рабочий результат. Даже с датасетом, который собран с одинаковым освещением, точность определения меня полностью устроила.
Скрипт запускается без параметров:
python train.py

Итогом обучения будет файл gesture_recognizer.task в папке exported_model. Это полноценная готовая модель. Скопируйте его в свой проект, замените идентификаторы жестов, допишите нужные функции и у вас будет готовый проект.
Автор текста — Евгений Хорошилов
НЛО прилетело и оставило здесь промокод для читателей нашего блога:-15% на заказ любого VDS (кроме тарифа Прогрев) — HABRFIRSTVDS.