Рано или поздно карантин закончится, и жители городов смогут снова покидать дома. Но уже сейчас становится ясно, что месяцы самоизоляции не пройдут бесследно. Наши привычки из жизни до карантина изменятся, и окружающему миру придется под это подстраиваться.


Экономический кризис станет двигателем быстрой адаптации изменений: компании, которые первыми поймут, как дать пользователям дополнительную ценность или удобство, окажутся в топах. Перед IT-индустрией будет масса челленджей. И в этом материале мы поделимся своим решением одной из проблем нового мира. Но обо всем по порядку.


Люди уже переосмыслили то, как они контактируют с общедоступными поверхностями: одни надевают перчатки, другие протирают все санитайзером. Мы стали более осознанно нажимать кнопки в лифте и браться за дверные ручки. Но не во всех случаях помогают перчатки, и не всегда можно обойтись локтем вместо руки: нам по-прежнему нужно вводить пин-код в банкомате и брать талончик в терминале.


Попытки сделать удобное бесконтактное управление экранами с помощью жестов предпринимались множество раз. Но всегда стояли какие-то ограничения по их внедрению: требовались периферийные устройства (Intel RealSense, Leap Motion) или утомительная настройка под отдельного пользователя (Npointer).


Но сегодня алгоритмы машинного обучения помогут решить эти проблемы. В этом материале мы пошагово продемонстрируем, как настроить управление экраном с помощью жестов и обычной вебкамеры.



Отображение интерфейса и видео


Программа будет работать в браузере, поэтому в будущем оформим ее как расширение для Chrome. Сперва разворачиваем локально верстку. Чтобы протестировать гипотезу, хватит самого простого списка с кнопками.


В правом верхнем углу размещаем блок для отображения видео с камеры.




Для передачи потока данных с web камеры в элемент видео используем TypeScript.




Готово, видео есть!



Скроллинг страницы


Раз мы решили, что трогать клавиатуру и мышь — это плодить всякие бактерии, то воспользуемся лицом. Для начала определяем его положение в пространстве.


Чтобы получить 3D-маппинг лица в пространстве воспользуемся библиотекой Facemesh, о которой писали в мартовском дайджесте.


C Facemash мы можем получить координату любой точки на нашем лице, а значит, простой наклон головы можно использовать для скроллинга.


В идеале хотелось бы дать пользователю возможность пролистывать страницу направлением взгляда, но данная библиотека не предоставляет таких данных. Значит, будем работать с тем, что уже есть.


Для этого по точкам в 3D пространстве определяем степень наклона головы: берем две точки координат на оси Z так, чтобы при наклоне головы вперед или назад их положение заметно разнилось.





Выбрав эти две точки, мы сможем определить, куда произошел наклон головы (тангаж), а также сказать, насколько сильно наклонена голова по значению разности Z координат.


После этого остается только написать интерфейс взаимодействия с web-браузером. Для скролла web-страницы используем TypeScript.


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





Для того, чтобы найти headPitch, нужно побольше узнать о данных из этой библиотеки. Если загрузить в нее снимок лица, то мы получим следующее.





Нас интересует массив mesh. Как видим, это просто куча неразмеченных координат точек в пространстве. Как же понять, какая точка за что отвечает? Ответ находим в документации.





Теперь мы знаем координаты точек и можем вычислить разность Z координат, и найти headPitch. Вообще pitch это угол, а в нашем случае это, скорее, коэффициент.





Соединяем куски кода вместе. Теперь мы можем пролистывать страницу без рук:





Управление курсором


Природа уже наделила нас отличным курсором — это наш указательный палец. Он нам и заменит мышь. К счастью, у нас есть библиотека Handpose для трекинга пальцев рук. Берем координату края верхней фаланги.





Здесь код похож на то, что мы делали ра��ьше, но посложнее.





HandEstimator работает аналогично FaceEstimator.


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





Пример создания и движения курсора:





Дальше нужно подружить палец с экраном. Для это преобразуем относительные координаты пальца в абсолютные координаты экрана.





И проверим, как это работает:





Видно, что курсор дергается. Сделаем его движения более плавными. Здесь нам поможет интерполяция входных данных методом скользящего среднего.







Так удается сгладить путь курсора:





Определение жестов


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



Библиотека не поддерживает определение жестов. Это придется делать самостоятельно. Добавим определение жеста pointer.



Как определить жест с картинки ниже?
— Указательный палец выше всех;
— Остальные пальцы согнуты.





Чтобы определять жест, рассчитываем длины 2 векторов:
— От основания ладони до верхней фаланги указательного пальца;
— От основания ладони до верхней фаланги среднего пальца.



Используем формулу вычисления расстояния между двумя точками A(xa, ya, za) и B(xb, yb, zb) в пространстве:


AB = √(xb — xa)2 + (yb — ya)2 + (zb — za)2



Определяем жест pointer, если (firstLenght / secondLenght) > 1.5.




Ссылка на репозиторий с кодом: github.com/worksolutions/screen-gesture-control



Заключение


Вот так буквально за один день можно реализовать жизнеспособную альтернативу
антивандальным клавиатурам. Конечно, это только начало. Еще нужно оптимизировать код, поработать над определением жестов и реализовать маппинг жестов с API браузеров, но уже виден потенциал этой технологии.


В подготовке материала помогал заросший на самоизоляции dpereverza. Именно его лицо вы видели на всех демонстрациях. Уверен, Дима с радостью ответит на любые комментарии и вопросы. На этом все, спасибо за внимание!