Распознавание лиц на видеопотоке
В этом туториале Вы узнаете, как распознавать лица на видеопотоке. Для распознавания будет использоваться готовая база лиц из дистрибутива Face SDK (в базе находятся фотографии нескольких знаменитых людей). Распознанные на видеопотоке лица будут подсвечиваться зеленым прямоугольником, рядом с лицом будет отображаться фотография соответствующего человека из базы лиц и его имя. В основе данного туториала лежит туториал Детекция и трекинг лиц на видеопотоке и соответствующий проект, данный туториал является его продолжением и дополнением.
Готовый демо-проект вы можете найти в дистрибутиве Face SDK: examples/tutorials/face_recognition_with_video_worker
#
Подготовка проекта- Поскольку в туториале Детекция и трекинг лиц на видеопотоке мы указывали только два параметра Face SDK (путь до дистрибутива Face SDK и имя конфигурационного файла для объекта
VideoWorker
), а в данном проекте их будет несколько (добавляется путь до базы лиц, имя конфигурационного файла с методом распознавания, FAR), для удобства мы модифицируем несколько файлов. Укажем все параметры в структуреFaceSdkParameters
. В заголовочном файлеfacesdkparameters.h
указываем путь до конфигурационного файлаvideo_worker_lbf.xml
.
facesdkparameters.h
- Передадим структуру
face_sdk_parameters
в конструктор объектаWorker
.
viewwindow.h
viewwindow.cpp
worker.h
worker.cpp
- В данном проекте нас интересует исключительно детекция лица на видеопотоке (создание ограничивающего прямоугольника) и последующее распознавание. Обратите внимание, что в первом проекте (detection_and_tracking_with_video_worker), который Вы можете использовать для создания данного проекта, помимо ограничивающего прямоугольника также отображаются антропометрические точки и углы. При необходимости Вы можете удалить ненужную визуализацию из первого проекта.
#
Создание базы лиц- Прежде всего нам нужно создать базу лиц. Чтобы проверить распознавание лиц, вы можете использовать готовую базу из дистрибутива Face SDK. Она включает в себя фотографии трех знаменитостей (Илон Маск, Эмилия Кларк, Лионель Месси). Для проверки распознавания вы можете скопировать готовую базу в корневую папку проекта (рядом с .pro файлом), запустить проект, открыть фотографию из базы и направить камеру на монитор. Также вы можете добавить в базу свою фотографию для распознавания. Для этого вам нужно создать папку в базе, указать ваше имя в названии и поместить в эту папку ваше фото (по аналогии с другими папками).
- Создаем новый класс
Database
для работы с базой лиц: Add New > C++ > C++ Class > Choose… > Class name – Database > Next > Project Management (настройки по умолчанию) > Finish. В заголовочном файлеdatabase.h
подключаем заголовочные файлыQImage
,QString
для работы с изображениями и строками иlibfacerec.h
для интеграции Face SDK.
database.h
- В файле
database.cpp
подключаем заголовочные файлыdatabase.h
иvideoframe.h
(реализация интерфейсаIRawImage
, через которыйVideoWorker
принимает кадры). Также добавляем необходимые заголовочные файлы для работы с файловой системой, отладки, обработки исключений и работы с файлами.
database.cpp
- В заголовочном файле
database.h
добавляем конструктор, где прописываем путь до базы лиц, а также указываем объектRecognizer
для создания шаблонов, объектCapturer
для детекции лиц и far (вероятность того, что один человек может быть принят за другого). Векторvw_elements
содержит в себе элементы базы данныхVideoWorker
. Векторыthumbnails
иnames
содержат в себе превью фотографий и имена лиц из базы.
database.h
- В файле
database.cpp
добавляем реализацию конструктораDatabase
, объявленного в предыдущем пункте. Величинаdistance_threshold
означает порог распознавания. Поскольку для разных методов этот порог разный, мы получаем его из значения FAR с помощью методаgetROCCurvePointByFAR
.
database.cpp
- В переменной
database_dir
указываем путь до директории с базой лиц. Если путь не существует, то будет выводиться ошибка “database directory doesn’t exist”. Создаем переменнуюperson_id
для хранения id человека из базы (соотносится с папкой “person...”) и переменнуюelement_id_counter
для хранения id элемента в базе (соотносится с фотографией человека из базы). В спискеdirs
формируем список всех поддиректорий из указанной директории с базой.
database.cpp
Примечание: Более подробную информацию о значениях FAR и TAR для разных методов распознавания см. в пункте Характеристики идентификации.
- В цикле
for(const auto &dir: dirs)
обрабатываем каждую поддиректорию – данные для отдельного человека. В качестве имени человека берем название директории. Формируем список изображений вperson_files
.
database.cpp
- Во вложенном цикле
for(const auto &person_file: person_files)
обрабатываем каждое изображение. В случае, если фото не существует, выводится предупреждение “Can’t read image”.
database.cpp
- Детектируем лицо на фото, используя объект
Capturer
. В случае, если фото невозможно прочитать, на фото не удалось найти лицо или найдено более одного лица, выводится предупреждение и фото игнорируется.
database.cpp
- При помощи метода
recognizer->processing
создаем шаблон лица для распознавания.
database.cpp
- В структуре
pbio::VideoWorker::DatabaseElement vw_element
указываем всю информацию об элементе базы, которая в дальнейшем будет передана для обработки объектуVideoWorker
(уникальный идентификатор элемента, уникальный идентификатор человека, шаблон лица, порог распознавания). Используя методpush_back
, добавляем элемент в конец списка.
database.cpp
- В заголовочном файле
database.h
добавляем методmakeThumbnail
для создания превью фотографии лица из базы.
database.cpp
- В файле
database.cpp
добавляем реализацию метода, используя методmakeThumbnail
для создания превью фотографии из базы лиц, которое будет отображаться рядом с распознанным лицом. Задаем его размер (120 пикселей) и масштабируем (при уменьшении пропорции изображения будут сохраняться).
database.cpp
- В pro-файле зададим путь до базы лиц.
face_recognition_with_video_worker.pro
- В заголовочном файле facesdkparameters.h указываем путь до базы лиц, а также значение FAR.
facesdkparameters.h
#
Поиск лица по базе и отображение результата в консоли- В заголовочном файле
facesdkparameters.h
указываем путь до конфигурационного файла с методом распознавания. В данном проекте мы используем метод 6.7, потому что он подходит для обработки видеопотока и обеспечивает оптимальную скорость распознавания и хорошее качество. Более подробную информацию о рекомендуемых методах распознавания см. в пункте Идентификация лиц.
facesdkparameters.h
Примечание: Если вы хотите распознавать лица на видеопотоке на низкопроизводительных устройствах, вы можете использовать метод 9.30. При этом скорость распознавания будет выше, чем с методом 6.7, но качество распознавания будет ниже.
- В заголовочном файле
worker.h
в структуреFaceData
добавляем переменнуюmatch_database_index
, в которой в случае совпадения лица с человеком из базы будет записан номер элемента из базы или"-1"
в случае несовпадения. Также подключаем базу лицDatabase
и добавляем коллбэк распознаванияMatchFoundCallback
.
worker.h
- В файле
worker.cpp
переопределяем значение параметра в конфигурационном файле, чтобы коллбэкMatchFoundCallback
приходил в том числе и для нераспознанных лиц. Укажем параметры объектаVideoWorker
: в первом туториале распознавание лиц не производилось, поэтому мы указывали только значениеstreams_count
. Поскольку в этом проекте мы распознаем лица на видеопотоке, нам нужно указать в конструкторе путь до конфигурационного файла с методом распознавания, а также значенияprocessing_threads_count
(количество потоков для создания шаблонов) иmatching_threads_count
(количество потоков для сравнения шаблонов). В данном проекте используется один видеопоток (подключенная камера). Подключаем базу лиц: передаем путь до базы, создаем объектCapturer
для детекции лиц и объектRecognizer
для построения шаблонов, а также указываем коэффициент FAR. Используя методsetDatabase
, задаем базу лиц для объектаVideoWorker
. Используя методaddMatchFoundCallback
, добавляем обработчик события распознаванияMatchFound
.
worker.cpp
- В деструкторе
Worker::~Worker()
удаляем коллбэкMatchFoundCallback
.
worker.cpp
- В коллбэке
MatchFoundCallback
результат приходит в виде структурыMatchFoundCallbackData
, которая хранит данные о распознанных и нераспознанных лицах.
worker.cpp
- Когда для отслеживаемого лица создается шаблон, он сравнивается с каждым шаблоном из базы, и если расстояние до ближайшего элемента оказывается меньше порога
distance_threshold
, указанного в этом элементе, то фиксируется совпадение. В случае, если лицо на видеопотоке не распознано, выводится сообщение “Match not found”. Если лицо распознано, то в консоли выводится сообщение “Match found with...” и имя человека, с которым найдено соответствие.
worker.cpp
- Сохраняем данные о распознанном лице для отрисовки превью.
worker.cpp
- Запускаем проект. В консоли будут отображаться результаты распознавания. Если лицо распознано, будет отображаться id лица и имя распознанного человека из базы. Если лицо не распознано, будет отображаться сообщение “Match not found”.
#
Отображение превью распознанного лица из базы- Для удобства восприятия добавим отображение фото и имени человека из базы рядом с лицом на видеопотоке. В файле
drawfunction.h
добавляем ссылку на базу лиц, поскольку она потребуется при отрисовке результатов распознавания.
drawfunction.h
- В файле
drawfunction.cpp
модифицируем функциюDrawFunction::Draw
, передав в нее базу лиц.
drawfunction.cpp
- Сохраняем ограничивающий прямоугольник лица в структуре
pbio::RawSample::Rectangle
. Передаем его параметры (x, y, ширина, высота) объектуQRect rect
.
drawfunction.cpp
- Создаем булеву переменную
recognized
, которая будет обозначать, распознано лицо или не распознано. Если лицо распознано, ограничивающий прямоугольник будет зеленого цвета, если нет – красного.
drawfunction.cpp
- По номеру
face.match_database_index
получаем изображение из базы для превью. Рассчитываем положение превью в кадре.
drawfunction.cpp
- Рисуем на превью изображение из базы. Создаем объект
QImage face_preview
, который больше по высоте, чемthumbnail
наtext_bar_height
. В позиции (0, 0) рисуется исходное изображение превью. В результате мы получаем превью с черным прямоугольником в нижней части, в котором выводится имя человека. Зададим параметры шрифта, рассчитаем положение надписи и отобразим текст на превью.
drawfunction.cpp
- Отрисовываем превью
face_preview
на кадре, используя методdrawPixmap
.
drawfunction.cpp
- В файл
worker.h
добавляем метод, возвращающий ссылку на базу лиц.
worker.h
- Модифицируем вызов функции
DrawFunction::Draw
, передав в нее базу лиц.
viewwindow.cpp
- Запускаем проект. Если лицо распознано, оно будет выделяться зеленым прямоугольником, и справа от него будет отображаться уменьшенная копия фото из базы и имя. Нераспознанные лица будут выделяться красным прямоугольником.