«Лови коллег»: приложение для корпоративов в стиле Pokémon Go

На одном из наших хакатонов мы с командой сделали игру по поиску коллег. Пользователь фотографирует человека, система определяет, кто на фото, и добавляет карточку в коллекцию. У сотрудников есть редкость, стихии и очки, при разрешённой геолокации можно видеть на карте где кого ловили (spawn map).
В команде со мной были Евгений Кувалкин и Евгений Васильков.

Откуда взялась идея
На хакатонах обычно делают что-то такое, что давно хочется попробовать, но в другое почему-то не доходят руки. У меня таких идей накопилось довольно много, и «Лови коллег» была одной из них. Проголосовали внутри команды, выбрали его, начали делать.
Нам показалось, что идея хорошая:
Выглядит интересно визуально.
Есть понятный сценарий использования — корпоративы.
У меня есть ML-опыт, и я понимал, что нужно, чтобы всё заработало.
Как это работает и выглядит
Мы подумали, что должно быть интересно: пользователь приезжает на общий большой корпоратив, видит коллегу и думает: «А вот это тот Вася, с которым я пару раз общался по задаче или нет?». Открывает приложение и начинает снимать незнакомых коллег. Знакомых тоже можно. А приложение показывает, кто все эти люди:

В MVP-версии получилось 3 основных сценария.

Во-первых, это авторизация. Для использования человек должен быть зарегистрирован на нашем портале Битрикс24. Само приложение на бэкенде связано с нашим порталом через вебхук.
Второй этап — фотоохота на коллег, которых пользователь встречает в реальной жизни.
Третье — сбор коллекции, как в Pokémon Go. Это библиотека уже сфотографированных и успешно распознанных коллег.

Авторизация по фотографии
Приложение заранее нужно связать с нужным порталом Битрикс24 и получали оттуда данные сотрудников и их фотографии.
Поэтому вместо обычного входа мы сделали так: пользователь сначала фотографирует себя, а система пробует сопоставить лицо с одним из сотрудников портала.
Если в профиле вместо фотографии стоит картинка или аватара вообще нет, авторизация может не сработать. Тогда нужно загрузить нормальную фотографию в профиль, и «Лови коллег» тебя авторизует.
Как работало распознавание
Основой проекта стало несколько open source моделей.
MediaPipe на клиенте анализировал видеопоток с камеры, находил лицо и помогал подготовить кадр для отправки.
MediaPipe на сервере ещё раз проверял изображение — действительно ли в кадре есть подходящее лицо и можно ли передавать его дальше на распознавание. Бэкенд отсеивал случаи, когда лицо не найдено, слишком маленькое, в кадре оказалось несколько человек или изображение получилось слишком размытым.
Перед отправкой фото приложение отсекало плохие кадры ещё на телефоне. MediaPipe проверял, что лицо достаточно крупное и несколько кадров остаётся примерно в одном положении. Пока человек слишком далеко или двигается, приложение просит подойти ближе и держать камеру ровно. Так на сервер не летят заведомо бесполезные снимки.
InsightFace buffalo_l и AdaFace на пару определяли сотрудника.
Каждая независимо превращала лицо в числовой вектор и сравнивала с заранее подготовленными фото коллег. Модели были обучены по-разному и имели разные особенности и ошибки. Система засчитывала результат, только если обе модели выбирали одного сотрудника, сходство было достаточно высоким, а лучший кандидат заметно опережал следующего: то есть если два сотрудника получались слишком похожими, результат считался сомнительным.
Если результаты расходились или сходство было недостаточным, приложение просило попробовать снова.
Каждая попытка распознавания получала уникальный ID. Если из-за сети один и тот же запрос прилетал повторно, бэкенд не запускал распознавание второй раз и не начислял очки повторно.
Смысл поиска близок к обычному RAG. То есть кусочки текста мы превращаем в точки в многомерном пространстве. В двумерном можно представить: «Девочка» вот здесь, «Собака» здесь, а «Тигр» вот здесь. Если у нас появится новая точка, мы можем определить, к какой она ближе:

У нас то же самое, только вместо фрагментов текста в многомерное пространство переводятся фотографии. Новая фотография тоже превращается в точку, после чего система смотрит, к группе снимков какого сотрудника она ближе. Если она попадает достаточно близко к подтверждённым фотографиям человека, система считает, что это он.

Как составили базу фото
Для приложения использовали две категории фотографий:
Актуальное фото сотрудника из профиля Битрикс24 служило исходной точкой: по нему система понимала, какому сотруднику принадлежат остальные снимки, и могла связать лицо с его ID и данными на портале.
Фотографии с прошлых корпоративов. Они расширяли базу для распознавания, чтобы у каждого сотрудника было несколько снимков в разных ракурсах и условиях. Я прогнал этот датасет через модели и сопоставил фотографии с сотрудниками по их портальным фото. Уверенные совпадения система привязывала автоматически, а сомнительные я проверял вручную, если сам знал человека — подтверждал нужного человека или исправлял ошибку.
Видеозвонки. Например, на одной из встреч по итогам квартала я наскриншотил лица и подписи, то же самое сделали ребята из команды. Потом мы всё это нарезали, распознали (через локальную VLM) и добавили в датасеты.
Так в несколько итераций для каждого сотрудника получился набор подтверждённых фотографий, по которому приложение уже распознавало новые снимки.
Редкие коллеги и механика Pokémon Go
У нас была механика по поиску редких коллег. Редкость там привязана к количеству пригодных фотографий человека в базе.
Например, условный Женя, у которого все фотки, когда он сидит в каком-то хитром ракурсе, и его попробуй распознай. Категории были такие:
1–2 фото — Легендарный
3–4 — Эпический
5–7 — Редкий
8–12 — Необычный
больше 12 — Обычный
Ещё карточкам назначались стихии: Огонь, Вода, Трава и другие. Они зависели от подразделения сотрудника: разработчики относились к Стали, продажи к Огню, поддержка к Воде. За первого пойманного сотрудника начислялось 100 очков, и в рейтинге тоже дополнительно учитывалось разнообразие собранных стихий.
Если пользователь разрешал доступ к геолокации, приложение отмечало активность на Яндекс Картах. Можно было поделиться своим местоположением или после поимки сохранить место, где встретился коллега.
Постепенно появлялась общая карта с точками, где находили разных сотрудников.
Баг с Unicode
Одного из сотрудников приложение никак не хотело распознавать. Этим человеком был некий Сергей Рыжиков… генеральный директор и сооснователь компании.
Почему так получилось: в современной кодировке это имя может записываться двумя разными Unicode-последовательностями: в одном случае использовался готовый символ «й», в другом — «и» с комбинируемым надстрочным знаком. Об этом есть такая статья: «Й» вам не «и» краткое! О важности нормализации Unicode
И вот у Сергея Владимировича в портале одно написание, а в датасете почему-то появилось другое. Для программы получилось, что есть как бы два Сергея Рыжикова: у одного «й», а у другого — «и» с надстрочным знаком. Поэтому фотографии одного человека разъехались по двум идентификаторам. Новое фото было одинаково похоже на обе группы, и система считала результат неоднозначным.
Итоговые мысли после работы
Их две.
Самой трудоемкой частью оказалось даже не само распознавание лиц, а подготовка хорошей базы фотографий и обработка пограничных случаев.
А игровая механика превратила технический эксперимент в вещь, которой действительно хотелось пользоваться на корпоративе.
KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.