Как распознавание лиц помогает находить тестовые телефоны
Привет, хабровчане! В EastBanc Technologies ведётся большое количество проектов, связанных с мобильной разработкой. В связи с чем необходим целый зоопарк устройств для тестирования на всех этапах. И, что характерно, каждый отдельный девайс постоянно оказывается нужен самым разным людям, а найти его даже в одном отделе мобильной разработки из нескольких десятков человек — это целая история. Не говоря уже о том, что есть тестировщики, дизайнеры, PM«ы, в конце концов!
И чтобы не потерять телефон, а четко знать, где он и с кем, мы используем онлайн-базу, которая распознает сотрудников по лицам. Сейчас расскажем, как мы к этому пришли и реализовали её.
Исторический контекст
У нас была доска с «карточками» устройств с основной информацией и местом под магнитик, обозначающим сотрудника. Каждый отмечался о взятии устройства.
Эта система имеет свои недостатки — не критичные, но в целом неудобные:
- Магнитики не так легко переместить с одного места на другое.
- Чтобы посмотреть на такую доску, обязательно придётся идти в другой кабинет.
- А ещё кому-то может понадобиться сразу много устройств… А значит, нужно несколько магнитиков на одного сотрудника.
- Ах да, ещё сотрудники иногда увольняются и приходят новые, на которых тоже нужно сделать магнитики.
Мобильное приложение
В компании, которая занимается автоматизацией бизнес-процессов, использовать описанное выше «аналоговое» решение — не очень здорово. Естественно, мы решили автоматизировать задачу поиска нужного устройства. Первым шагом стало написание мобильного приложения, которое умеет определять и сообщать о своем местоположении в комнатах по Wi-Fi точкам доступа. Попутно для удобства наделили девайсы умением сообщать на сервер о версии ОС, а также показывать такую важную характеристику, как заряд батареи.
Казалось бы, задача решена. Смотришь на список в базе данных, где последний раз устройство видело Wi-Fi, идёшь туда и…
В эксплуатации оказалось, что не всё так просто. Мы установили приложение на тестовые устройства и поработали с ним несколько месяцев. Оказалось, что такой вариант удобен, но тоже не идеален.
Устройства разряжаются, просто выключаются, точки доступа Wi-Fi переставляются из одного места в другое, а геолокация сама по себе говорит только о том, что устройство находится в офисе. Спасибо, капитан!
Можно, конечно, пытаться оптимизировать существующую систему, но почему не переизобрести её на основе технологий ХХI века? Сказано — сделано.
Как мы хотели, чтобы это было
Мы придумали концепт системы, которая бы распознавала сотрудников по лицам, тестовые устройства — по специальным меткам, запрашивала бы подтверждение смены статуса устройства, а потом вносила изменения в онлайн-базу, которую любой сотрудник может посмотреть, не вставая с кресла.
Распознавание лиц
Распознавание лиц в целом решенная задача в 2018 году. Поэтому мы не стали изобретать велосипед и пытаться обучать собственные модели, а воспользовались готовым решением. Самым удобным вариантом показался модуль FaceRecognition, т.к. он не требует дообучения и работает весьма быстро даже без ускорения на GPU.
С помощью функции face_locations на фотографиях сотрудников обнаруживались лица, а с помощью face_encodings из них извлекались признаки лица конкретного сотрудника.
Полученные данные собирались в базу. Для определения конкретного сотрудника с помощью функции face_distance считалась «разница» между кодировкой обнаруженного сотрудника и кодировками из базы.
В целом, на этом этапе можно было пойти дальше и создать классификатор, например, на основе KNN, чтобы система была менее чувствительна к динамике лиц сотрудников. Однако на практике это требует значительно больших временных затрат. Да и банального усреднения кодировки лица человека между той, что сейчас в базе, и той, которую система обнаружила для смены статуса устройства, оказалось уже достаточно, чтобы на практике избегать накопления ошибки.
face_locations = face_recognition.face_locations(rgb_small_frame)
face_encodings = face_recognition.face_encodings(rgb_small_frame, face_locations)
face_names = []
for face_encoding in face_encodings:
matches = face_recognition.face_distance(
known_face_encodings, face_encoding)
name = "Unknown"
if np.min(matches) <= 0.45:
best_match_index = np.argmin(matches)
name = known_face_info[str(best_match_index)]['name']
else:
best_match_index = None
Распознавание устройств
Изначально возникла мысль использовать для распознавания устройств QR-коды, в которые вносить заодно и информацию об устройстве. Однако для устойчивого распознавания QR-кода его приходилось подносить очень близко к камере, что неудобно.
В результате возникла мысль об использовании маркеров дополненной реальности. Они несут меньше информации, зато гораздо более устойчиво распознаются. В ходе экспериментов маркер размером в 30 миллиметров распознавался камерой при небольших отклонениях от вертикали (3–5 градусов) на расстоянии до двух с половиной метров.
Такой вариант выглядел уже куда лучше. Из всего множества маркеров дополненной реальности были выбраны ARuco, т.к. все необходимые инструменты для работы с ними присутствуют в поставке opencv-contrib-python.
self.video_capture = cv2.VideoCapture(0)
ret, frame = self.video_capture.read()
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
markers = cv2.aruco.detectMarkers(gray, self.dictionary)
В итоге, каждому устройству был присвоен числовой индекс, с которым сопоставлялся и маркер с этим индексом.
Дело в шляпе?
Казалось бы, мы научились распознавать устройства и лица, работа сделана. Фанфары, овации! Что ещё может быть нужно?
На самом деле, работа только начинается. Теперь все компоненты системы нужно заставить стабильно и быстро работать «на бою».
Нужно оптимизировать затраты ресурсов сервера в idle«е, продумать юзкейсы и понять, как это вообще должно выглядеть графически.
Интерфейс
Едва ли не самым важным пунктом в разработке подобных систем является интерфейс. Кто-то возможно будет спорить, но пользователь — центральный элемент в такой ситуации.
Максимально быстро можно реализовать фронтенд-часть с помощью Tkinter.
Несколько замечаний о Tkinter
Интерфейс состоит из карточек с информацией об устройстве и пользователе, использующем в текущий момент это устройство. БОльшую часть экрана занимает каталог карточек — основной инструмент учета. Сверху находится фильтр, с помощью которого можно отфильтровать каталог по платформе или версии операционной системы.
Вот ключевые компоненты интерфейса:
Оптимизация
Один проход любого компонента системы не занимает слишком много времени. Однако, если запустить распознавание маркеров и лиц одновременно, то попытка распознать все 30 кадров в секунду, предоставляемых камерой, приведет к полному исчерпанию ресурсов компьютера без GPU.
При этом понятно, что 99% времени система будет производить эту работу вхолостую.
Чтобы этого избежать, были приняты следующие оптимизационные решения:
- В обработку подаётся лишь каждый восьмой кадр.Код
if self.lastseen + self.rec_threshold > time.time() or self.frame_number != 8: ret, frame = self.video_capture.read() self.frame_number += 1 if self.frame_number > 8: self.frame_number = 8 return frame, None, None, None
Задержка реакции системы повышается примерно до 8/30 секунды, при этом время реакции человека — примерно одна секунда. Соответственно, такая задержка всё ещё не будет заметной для пользователя. А мы уже в восемь раз снизили нагрузку на систему. - Сначала в кадре ищется маркер устройства, и лишь при его обнаружении запускается распознавание лиц. Так как поиск маркеров в кадре примерно в 300 раз менее затратен, чем поиск лиц, мы решили, что в режиме ожидания будем проверять только наличие маркера.
- Чтобы уменьшить «тормоза» при поиске лиц, когда лиц на изображении нет, в функции face_locations был отключен параметр number_of_time_to_upsample.
face_locations = face_recognition.face_locations (rgb_small_frame, number_of_times_to_upsample=0)
Благодаря этому время обработки кадра, на котором нет лиц, сравнялось со временем обработки кадра, где лица легко обнаруживаются.
Что в итоге?
На текущий момент, система успешно развернута на подвернувшемся под руку MacMini Late 2009, на двух ядрах Core 2 Duo. В рамках тестирования, она вполне успешно работала даже на одном виртуальном ядре с 1024 мегабайтами оперативной и 4 гигабайтами постоянной памяти в контейнере Docker. К MacMini подключили сенсорный дисплей, чтобы внешний вид стал минималистичным.
Сейчас даже те пользователи, которые не пользовались старой доской, с энтузиазмом и улыбкой регистрируют на себя устройства, а случаев безуспешных поисков стало намного меньше!
Что дальше?
В текущей системе, несомненно, ещё много моментов, которые можно и хочется улучшить:
- Сделать так, чтобы элементы управления ОС не проявлялись при появлении диалоговых окон (сейчас это messagebox из пакета Tkinter).
- Разнести вычисления и запросы к серверу в разные потоки с обработкой интерфейса (сейчас они выполняются в основном потоке, mainloop Tkinter«а, что замораживает интерфейс в момент отправки запросов в онлайн-базу).
- Привести интерфейс к одному дизайну с другими корпоративными ресурсами.
- Сделать полноценный веб-интерфейс для удаленного просмотра данных.
- Использовать распознавание голоса для подтверждения/отмены действий и заполнения текстовых полей.
- Реализовать регистрацию нескольких устройств одновременно.
P.S. А вот так это работает
Видео записано на бета-версии GUI