Детекция и трекинг лиц на видеопотоке
В этом туториале вы узнаете, как осуществлять детекцию и трекинг лиц на видеопотоке с камеры при помощи объекта VideoWorker
из Face SDK API. Трекаемые лица будут выделяться зеленым прямоугольником.
Помимо Face SDK и Qt, для данного проекта Вам потребуется камера, подключенная к ПК (например, веб-камера). Проект можно запустить как на Windows, так и на Ubuntu (версия 16.04 или выше).
Готовый демо-проект вы можете найти в дистрибутиве Face SDK: examples/tutorials/detection_and_tracking_with_video_worker
#
Создаем проект Qt- Запускаем Qt и создаем новый проект: File > New File or Project > Application > Qt Widgets Application > Choose...
- Называем проект, например, detection_and_tracking_with_video_worker, и выбираем путь. Кликаем Next, в разделе Kit Selection выбираем платформу для приложения, например, Desktop. Кликаем Details и отмечаем конфигурацию сборки Release (Debug в данном проекте нам не понадобится).
- В окне Class Information оставляем настройки по умолчанию, кликаем Next, в окне Project Management также оставляем настройки по умолчанию и кликаем Finish.
- Дадим заголовок главному окну приложения: в дереве проекта открываем двойным кликом файл Forms > mainwindow.ui. В правой части редактора во вкладке свойств указываем название окна: windowTitle > Face SDK Tracking.
- Для выравнивания виджетов по сетке перенесем на виджет окна MainWindow объект Grid Layout. Вызываем у виджета MainWindow контекстное меню правым кликом и выбираем Layout > Lay Out in a Grid. Объект Grid Layout должен растянуться по размерам виджета MainWindow. Изменим имя Layout > layoutName > viewLayout.
- Запускаем проект, кликнув Run (Ctrl+R). На экране должно появиться пустое окно с заголовком Face SDK Tracking.
#
Выводим изображение с камеры- Для того, чтобы мы могли использовать в проекте камеру, нам нужно подключить мультимедийные виджеты Qt. Для этого добавляем в pro-файл проекта строку:
detection_and_tracking_with_video_worker.pro
- Создадим новый класс
QCameraCapture
для получения изображения с камеры: Add New > C++ > C++ Class > Choose… > Class name – QCameraCapture > Base class – QObject > Next > Project Management (настройки по умолчанию) > Finish. В заголовочном файлеqcameracapture.h
создаем классCameraSurface
, который будет предоставлять кадры с камеры через коллбэк-функциюpresent
.
qcameracapture.h
- Описываем реализацию класса в файле
qcameracapture.cpp
. Определяем конструкторCameraSurface::CameraSurface
и методsupportedPixelFormats
. В методеCameraSurface::supportedPixelFormats
перечисляем форматы, поддерживаемые Face SDK (RGB24, BGR24, NV12, NV21). Изображение с некоторых камер приходит в формате RGB32, поэтому мы также указываем его в списке. Этот формат не поддерживается Face SDK, поэтому далее мы оформим преобразование изображения формата RGB32 в формат RGB24.
qcameracapture.cpp
- В методе
CameraSurface::start
проверяем формат изображения. Запускаем камеру, если формат поддерживается, иначе обрабатываем ошибку.
qcameracapture.cpp
- В методе
CameraSurface::present
обрабатываем новый кадр. Если кадр прошел проверку, то посылается сигнал обновления кадраframeUpdatedSignal
. Далее с этим сигналом будет связан слотframeUpdatedSlot
, где кадр будет обработан.
qcameracapture.cpp
- Конструктор класса
QCameraCapture
принимает указатель на родительский виджет (parent
), id камеры и разрешение изображения (ширина и высота), которые будут сохранены в соответствующие поля класса.
qcameracapture.h
- Добавляем в класс
QCameraCapture
объекты камерыm_camera
иm_surface
.
qcameracapture.h
- Подключаем заголовочный файл
stdexcept
в файлеqcameracapture.cpp
для генерации исключений. В списке инициализации конструктораQCameraCapture::QCameraCapture
сохраняем указатель на родительский виджет, id камеры и разрешение изображения. В теле конструктора получаем список доступных камер. Список камер должен содержать хотя бы одну камеру, в противном случае будет выброшено исключениеruntime_error
. Также проверяем, что камера с запрашиваемым id есть в списке. Создаем камеру и подключаем сигналы камеры к слотам-обработчикам объекта. При изменении статуса камера посылает сигналstatusChanged
. Создаем объектCameraSurface
для отрисовки кадров с камеры. Подключаем сигналCameraSurface::frameUpdatedSignal
к слотуQCameraCapture::frameUpdatedSlot
.
qcameracapture.cpp
- В деструкторе
QCameraCapture
останавливаем камеру.
qcameracapture.h
qcameracapture.cpp
- Добавляем метод
QCameraCapture::frameUpdatedSlot
‒ обработчик сигналаCameraSurface::frameUpdatedSignal
. В этом методе мы конвертируем объектQVideoFrame
вQImage
и отправляем сигнал о том, что доступен новый кадр. СоздаемFramePtr
– указатель на изображение. Если изображение получено в формате RGB32, преобразуем его в RGB888.
qcameracapture.h
qcameracapture.cpp
- Добавляем методы запуска (
start
) и остановки (stop
) камерыQCameraCapture
.
qcameracapture.h
qcameracapture.cpp
- В методе
QCameraCapture::onStatusChanged
обрабатываем изменение статуса камеры наLoadedStatus
. Проверяем, поддерживает ли камера запрашиваемое разрешение. Устанавливаем запрашиваемое разрешение, если оно поддерживается камерой, иначе устанавливается разрешение по умолчанию (640 x 480), заданное статическими полями классаdefault_res_width
,default_res_height
.
qcameracapture.h
qcameracapture.cpp
- В методе
cameraError
выводим сообщение об ошибках камеры, если они возникают.
qcameracapture.h
qcameracapture.cpp
- Создаем новый класс
Worker
: Add New > C++ > C++ Class > Choose… > Class name - Worker > Next > Finish. КлассWorker
через методaddFrame
будет сохранять последний кадр с камеры и отдавать этот кадр через методgetDataToDraw
.
worker.h
worker.cpp
- Отображение кадров будет выполняться в классе
ViewWindow
. Создаем виджет ViewWindow: Add > New > Qt > Designer Form Class > Choose... > Template > Widget (настройки по умолчанию) > Next > Name – ViewWindow > Project Management (настройки по умолчанию) > Finish. - В редакторе (Design) перетаскиваем на виджет объект Grid Layout: вызываем у виджета ViewWindow контекстное меню правым кликом и выбираем Lay out > Lay Out in a Grid. Объект Grid Layout позволяет размещать виджеты в сетке, он растягивается по размерам виджета ViewWindow. Далее добавляем на gridLayout объект Label и на панели свойств задаем ему имя frame: QObject > objectName > frame.
- Удаляем текст по умолчанию в QLabel > text.
- В класс
ViewWindow
добавляем камеру_qCamera
и инициализируем ее в конструкторе. Через статические поляcamera_image_width
иcamera_image_height
задаем требуемое разрешение изображения 1280x720. Флаг_running
хранит состояние запуска камеры:true
- камера запущена,false
- не запущена (остановлена).
viewwindow.h
viewwindow.cpp
- Добавляем в класс
ViewWindow
объектWorker
и инициализируем его в конструкторе.
viewwindow.h
viewwindow.cpp
- Кадры будут подаваться в
Worker
изQCameraCapture
. Модифицируем классыQCameraCapture
иViewWindow
.
qcameracapture.h
qcameracapture.cpp
viewwindow.cpp
- Сигнал о появлении нового кадра
QCameraCapture::newFrameAvailable
обрабатывается в слотеViewWindow::draw
, который выводит изображение с камеры на виджет кадров.
viewwindow.h
viewwindow.cpp
- В методе
runProcessing
запускаем камеру, в методеstopProcessing
– останавливаем.
viewwindow.h
viewwindow.cpp
- Останавливаем камеру в деструкторе
~ViewWindow
.
viewwindow.cpp
- Подключаем виджет камеры к главному окну приложения: создаем окно просмотра и запускаем обработку в конструкторе
MainWindow
. В деструкторе~MainWindow
останавливаем обработку.
mainwindow.h
mainwindow.cpp
- Модифицируем функцию
main
для перехвата возможных исключений.
main.cpp
- Запустите проект. Должно появиться окно с изображением с камеры.
Примечание: При запуске проекта на Windows с некоторыми камерами изображение может перевернуто или отзеркалено, это связано с особенностями обработки изображений Qt. В этом случае Вам потребуется дополнительно обработать изображение, например, при помощи QImage::mirrored().
#
Детектим и отслеживаем лица на видео- Скачайте и распакуйте дистрибутив Face SDK, как это описано в пункте Приступая к работе. В корневой папке дистрибутива должны находиться папки bin и lib, соответствующие Вашей платформе.
- Для того, чтобы осуществлять детекцию и трекинг лиц на изображении с камеры, нам необходимо интегрировать Face SDK в наш проект. Указываем путь до корневой папки дистрибутива Face SDK (там находятся нужные нам заголовочные файлы) в переменной
FACE_SDK_PATH
в pro-файле проекта. Также указываем путь до папкиinclude
. В случае, если пути не прописаны, будет выводиться ошибка “Empty path to Face SDK”.
detection_and_tracking_with_video_worker.pro
Примечание: При написании пути до Face SDK используйте слэш ("/").
- [Для Linux] Для сборки проекта добавляем в pro-файл следующую опцию:
detection_and_tracking_with_video_worker.pro
- Кроме того, требуется указать путь до библиотеки facerec и конфигурационных файлов. Создадим класс
FaceSdkParameters
для хранения конфигурации (Add New > C++ > C++ Header File > FaceSdkParameters) и используем его вMainWindow
.
facesdkparameters.h
mainwindow.h
- Подключаем Face SDK: добавляем заголовочные файлы в
mainwindow.h
и методinitFaceSdkService
для инициализации сервисов Face SDK. Создаем объектFacerecService
– компонент для создания модулей Face SDK, вызывая статический методFacerecService::createService
, передаем путь до библиотеки и путь до директории с конфигурационными файлами в try-catch блоке, чтобы перехватить возможные исключения. В случае успешной инициализации функцияinitFaceSdkService
вернетtrue
, иначе появится окно с ошибкой и вернетсяfalse
.
mainwindow.h
mainwindow.cpp
- В конструкторе
MainWindow::MainWindow
добавляем вызов инициализации сервиса. В случае ошибки выбрасываем исключениеstd::runtime_error
.
mainwindow.cpp
- Добавим передачу сервиса
FacerecService
и параметров Face SDK в конструкторViewWindow
, где они будут использованы для создания модуля трекингаVideoWorker
. В поля класса сохраняем сервис и параметры.
mainwindow.cpp
viewwindow.h
viewwindow.cpp
- Модифицируем класс
Worker
для работы с Face SDK. КлассWorker
будет принимать указатель на объектFacerecService
и имя конфигурационного файла модуля трекинга. КлассWorker
создает компонентVideoWorker
из Face SDK, осуществляющий трекинг лиц, подает ему кадры и обрабатывает коллбэки, в которых приходят результаты трекинга. Реализуем конструктор – в нем создаем объектVideoWorker
, указывая конфигурационный файл, метод распознавателя (в данном случае это пустая строка, т.к. распознавание лиц не используется), количество видеопотоков (в данном случае1
, т.к. используется только одна камера).
worker.h
worker.cpp
Примечание: Помимо детекции и трекинга лиц объект VideoWorker
может использоваться для распознавания лиц на нескольких потоках. В этом случае указывается метод распознавателя и потоки processing_threads_count
и matching_threads_count
.
- Подписываемся на коллбэки от класса
VideoWorker
–TrackingCallback
(лицо найдено и отслеживается),TrackingLostCallback
(потеря трекинга лица) – и удаляем их в деструкторе.
worker.h
worker.cpp
- Для обработки исключений подключаем заголовочный файл
cassert
. В коллбэкеTrackingCallback
результат приходит в виде структурыTrackingCallbackData
, которая хранит данные обо всех отслеживаемых лицах. Вывод превью синхронизируется с выводом результата. Мы не можем сразу вывести кадр, который отдается вVideoWorker
, потому что он будет обработан чуть позже. Поэтому кадры складываются в очередь, и при получении результата мы можем найти кадр, который соответствует этому результату. Часть кадров может быть пропущена объектомVideoWorker
при большой нагрузке, поэтому не всегда для каждого кадра есть результат. В алгоритме ниже из очереди извлекается изображение, соответствующее последнему полученному результату. Сохраняем найденные лица для кадра, чтобы потом их использовать при визуализации. Для синхронизации изменения общих данных вTrackingCallback
иTrackingLostCallback
используются мьютексыstd::mutex
.
worker.h
worker.cpp
- Реализуем коллбэк
TrackingLostCallback
, в котором помечаем, что трекаемое лицо пропало из кадра.
worker.cpp
- Объект
VideoWorker
принимает кадры через интерфейсpbio::IRawImage
. Создадим заголовочный файлVideoFrame
: Add New > C++ > C++ Header File > VideoFrame. Подключаем интерфейсpbio::IRawImage
в файлеvideoframe.h
и реализуем этот интерфейс для классаQImage
. Интерфейсpbio::IRawImage
позволяет получить указатель на данные изображения, его формат, высоту и ширину.
videoframe.h
Нажмите, чтобы развернуть
- В методе
addFrame
подаем кадры вVideoWorker
. Если при обработке коллбэков возникают исключения, они выбрасываются повторно в методеcheckExceptions
. Для хранения кадров заведем очередь_frames
, в которую будем складывать id кадра и соответствующее изображение, чтобы в коллбэкеTrackingCallback
найти кадр, соответствующий результату обработки. Для синхронизации изменения общих данных также используются мьютексыstd::mutex
.
worker.h
worker.cpp
- Модифицируем метод
getDataToDraw
, чтобы не рисовать лица, для которых был вызванTrackingLostCallback
.
worker.cpp
- Модифицируем класс
QCameraCapture
для перехвата исключений, которые могут возникнуть вWorker::addFrame
.
qcameracapture.cpp
- Создадим класс
DrawFunction
, который будет содержать метод для отрисовки результатов трекинга на изображении: Add New > C++ > C++ Class > Choose… > Class name – DrawFunction.
drawfunction.h
drawfunction.cpp
- В конструкторе
ViewWindow
передаем указатель на объектFacerecService
и имя конфигурационного файла модуля трекинга при созданииWorker
. В методеDraw
рисуем результаты трекинга на изображении через вызовDrawFunction::Draw
.
viewwindow.cpp
- Запустите проект. Теперь на изображении с камеры детектятся и отслеживаются лица (они выделяются зеленым прямугольником). Больше информации об использовании объекта
VideoWorker
вы можете найти в разделе Обработка видеопотока.