Введение Технология компьютерного зрения — это область искусственного интеллекта и машинного обучения, основной целью которой является понимание содержания цифровых изображений.  Технология компьютерного зрения универсальна и может быть внедрена во многих отраслях, таких как ритейл, производство, сельское хозяйство, здравоохранение, безопасность и др.  При том что сама технология является зрелой, рынок ее применения находится на начальной стадии развития, но благодаря преимуществам компьютерного зрения он имеет потенциал роста. По данным Mordor Intelligence, совокупный годовой темп роста в ближайшие пять лет может составить 7,36 %. Осознавая перспективы развития рынка, наша команда в ITentika решила инвестировать свое время в проработку одного из множества направлений компьютерного зрения, которое можно назвать «осознание действий человека». В качестве действий были выбраны жесты. Если подключить к этой идее немного фантазии, то можно придумать много сфер применения: оценка качества обслуживания/сервиса (палец вверх, палец вниз); виртуальные надписи/подписи воображаемым маркером; виртуальные кнопки в компьютерных играх; компьютерный ассистент для слабовидящих людей и многое другое. В настоящей статье мы проведем анализ доступных решений для детектирования рук и распознавания их жестов, оценим эффективность и применимость таких решений на практике. Постановка задачи На Habr ранее описывался набор данных HaGRID, который содержит информацию о 18 жестах рук и примерно по 30 000 экземпляров изображений на каждый жест. В наборе есть как более интуитивно понятные жесты, например «большой палец вверх», так и менее понятные — «указательный палец вверх». В итоге мы приняли решение поработать с детектированием и распознаванием до 10 наиболее интуитивных жестов. Наличие такого качественного набора данных как бы подталкивало пойти проторенной дорогой — обучать нейронную сеть на базе HaGRID, тем более что примеры обученных моделей также представлены в статье. Но начали мы с анализа альтернатив — готовых движков для отслеживания рук.  Исследование доступных решений Для решения задачи предполагалось использовать стек технологий вокруг Python как наиболее привычный стек для наших специалистов в области машинного обучения. В результате изучения открытых источников выявили следующие движки для отслеживания рук: Решение Краткое описание Плюсы Минусы MediaPipe Решение для отслеживания рук и пальцев с высокой точностью. Ссылка: https://google.github.io/mediapipe/solutions/hands.html  21 3D-метка на руке Наличие поддержки Python Не поддерживаются позы рук Yoha Движок для отслеживания рук. Механизм развивается вокруг конкретных поз рук, которые пользователи/разработчики находят полезными. Ссылка: https://github.com/handtracking-io/yoha  Поддерживаемые позы рук: Щипок (касание указательным и большим пальцами) Кулак 21 2D-метка на руке Как язык программирования доступен только JavaScript По итогам исследования выбор оказался достаточно невелик. Можно сказать, что выбор без выбора, так как из двух решений только одно поддерживает требуемый язык программирования. Кроме того, MediaPipe поддерживает детекцию 21 метки для точек крепления фаланг руки в 3D-пространстве (есть координаты X, Y, Z), в то время как Yoha работает лишь в 2D пространстве (координаты X и Y). Координата Z расширяет диапазон возможностей использования решения за счет позиционирования руки в 3D пространстве, например для отслеживания поворотов кистей рук. Обзор базовых возможностей MediaPipe ранее публиковался на Habr. Разработка собственного решения Для создания решения обработки видео стоит изначально подумать о потребителях бизнес-логики, например таких, как front-end или диспетчер сообщений в микросервисной архитектуре. В нашем случае было необходимо отправлять результат на front-end, поэтому подходящим протоколом общения был признан WebSocket. Об особенностях технологии есть соответствующий пост на Habr. Для запуска «вещания» со стороны сервера используется следующий код: Чтобы отсылать сообщения о распознанных жестах на сторону потребителя всем пользователям, запишем все соединения в глобальную переменную CLIENTS и далее CLIENTS.add(). Сопрограмма wait_closed() идентична атрибуту closed(), за исключением того, что ее можно ожидать. Это может облегчить нам обнаружение завершения соединения, независимо от его причины, в задачах, которые взаимодействуют с WebSocket-соединением. Далее создадим функцию для запуска сообщений со стороны сервера в онлайн-режиме. Теперь поработаем над созданием самого сообщения с распознанным жестом руки: Для создания объекта hands используется следующий код: Метод get_palm_object() задает параметры возвращаемой модели, подробнее о них можно почитать в документации к проекту. Далее нам необходимо захватить видеопоток, с чем справляется библиотека opencv-python - cv2.VideoCapture(). При создании экземпляра этого класса передадим в него идентификатор устройства захвата видеопотока. Для стандартной веб-камеры ноутбука идентификатор оказался равен 0. Но если вы подключите к ноутбуку внешнюю камеру и заходите сделать захват с нее, то идентификатор потребуется изменить на 1. Также мы тестировали данное решение на ноутбуке с операционной системой Linux, где подходящий идентификатор оказался -1. Для просмотра списка устройств видеозахвата в Linux также можно попробовать воспользоваться специальной утилитой: Достаточно много проблем доставляет захват видеопотока, если разработка ведется с опорой на WSL2. Поддержка подключения USB-устройств в WSL изначально недоступна, поэтому нам потребуется установить проект с открытым кодом usbipd-win.  Альтернативное решение — превращение стандартной веб-камеры или камеры смартфона при помощи соответствующего программного обеспечения в передающее устройство. Например, Android-приложение IP Webcam позволяет это сделать. Используя IP Webcam, в параметры cv2.VideoCapture() передадим url в формате: url = "http://<ipaddress>:<port>/video" В первом блоке try / except происходит попытка двойного преобразования цветовой палитры изображения. Сначала из BGR -> RGB. Именно на такой цветовой палитре mediapipe показывает лучшую производительность и точность детектирования рук hands.process(image), после чего происходит обратное преобразование в привычный человеческому глазу формат RGB -> BGR. Но если вы не планируете выводить видеопоток для просмотра, второе преобразование делать не обязательно. Во втором блоке try / except происходит попытка определить сочетание 3D-меток, распознанных на руке, как некий жест HandGestureRecognition(hand_landmarks). Например, жест «Палец вверх», он же «Класс», предполагает, что точки на большом пальце находятся выше точек на других пальцах. Кроме того, крайняя точка большого пальца находится выше, чем точка предыдущей фаланги большого пальца, и так далее. Запуск всего потока происходит в бесконечном цикле на заданном порту. Теперь посмотрим на само программирование жестов на примере жестов: «Класс», «Победа» и «Все хорошо, OK» Для получения ключевых объектов на руке нам поможет следующая функция: Будем вызывать ее как метод класса HandGestureRecognition.gesture_to_action(), передав ему предварительно список всех точек landmark_list в момент конструирования из results.multi_hand_landmarks: Каждая точка по осям X, Y, Z может быть задана следующим образом: При этом индексы списка соответствуют порядковым номерам точек на руке. Это подробно и наглядно описано в документации к проекту. # 1 Жест «Класс» # Жест «Победа» # Жест «Все хорошо»    Результат и выводы Помимо трех продемонстрированных жестов, было запрограммировано надежное распознавание еще семи жестов. Очень важное преимущество такого подхода — быстрая масштабируемость решения без необходимости повторного обучения нейросети, что неизбежно потребовалось бы при обучении на наборе данных HaGRID. Напомню, что набор ограничен 18 жестами. В случае, если мы запланируем улучшить наше решение и построить на его основе, например, переводчик жестов или ассистент управления компьютером при помощи жестов, то 18 жестов может быть недостаточно. В нашем подходе с программируемыми жестами мы фактически не ограничены в сферах применения. Особенности получившегося решения: Надежное распознавание кисти руки на расстоянии 1-4 метра. Возможность программирования жестов, которые должна распознавать система для целей заказчика, без необходимости повторного обучения нейросети. Возможность быстрого и гибкого программирования сценариев в ответ на тот или иной жест руки: отображение надписей, картинок в видеопотоке; отправка сообщений; запуск того или иного оборудования и управление им; другие сценарии. P.S. Одним из неочевидных открытий при работе над проектом стало то, что команда приняла и посчитала вполне удобным стандартизировать подход к форматированию Python-кода с применением проекта Black (ссылка на код проекта). Стандарты форматирования данного проекта вполне жестко и бескомпромиссно позволяют «причесать» одинарные или двойные кавычки, места отступов при переносе строк и другие моменты, в которых часто возникают свободные толкования между разработчиками. Контакты разработчика: dmitry.hodykin@itentika.ru  На код проекта можно посмотреть по ссылке.   AIML Developer