Распознавание лиц с использованием библиотеки DLIB С++

Развитие средств видеомониторинга определяет необходимость внедрения методов распознавания лиц людей для решения различных задач: от аутентификации до поиска преступников. В данной работе рассматривается вопрос применения достаточного эффективного метода распознавания лиц людей.

Процесс распознавания лица человека делится на следующие основные этапы:

- выделение контура лица на изображении;

- извлечение характерных признаков текущего образа лица человека;

- сравнение признаков с эталоном.

Все данные этапы реализованы в библиотеки dlib и могут быть использованы в «коробочном» режиме, используя готовые функции и объекты:

- get_frontal_face_detector. Возвращает объект для поиска контура лица  изображения. В данной работе используется простой метод поиска лица при помощи HOG и SVM;

- shape_predictor. Выделяет ключевые точки на лице. В рамках работы используется модель лица, задающаяся 68 точками. Пример расположения данных точек представлен на рисунке 1. В библиотеке dlib используется быстродействующий алгоритм определения ключевых точек с учетом ориентации лица на изображении;

- get_face_chip_details. Функция, которая возвращает изображения лица с правильной ориентаций и его центрированием;

-  нейронная сеть net, которая вычисляет вектор характерных признаков для образа лица. Вектор представляет собой 128 чисел;

- length. Функция определения дистанции между двумя дескрипторами образов.

 

Рисунок 1 – Ключевые точки лица

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

Первоначально скачиваем библиотеку Dlib по адресу http://dlib.net/. При компиляции библиотеки для повышения производительности отмечаем необходимость применения CUDA. Первоначально подключаются необходимые библиотеки для разрабатываемого приложения:

 

#include <vector>

//===============dlib

#include <dlib/dnn.h>

#include <dlib/image_processing/frontal_face_detector.h>

#include <dlib/opencv/cv_image.h>

#include <dlib/opencv/to_open_cv.h>

//===============OpenCV

#include <opencv2/core/core.hpp>

#include <opencv2/imgproc/imgproc.hpp>

#include <opencv2/highgui/highgui.hpp>

//=====Поиск файлов

#include "src/FindFilesToVector.h"

 

В первом блоке указаны модули библиотеки dlib, во втором – OpenCV и в третьем – модуль поиска файлов в папке.

Далее определяются основные структуры сети RestNet:

 

using namespace dlib;

using namespace std;

  

template <template <int, template<typename>class, int, typename> class block, int N, template<typename>class BN, typename SUBNET>

using residual = add_prev1<block<N, BN, 1, tag1<SUBNET>>>;

template <template <int, template<typename>class, int, typename> class block, int N, template<typename>class BN, typename SUBNET>

using residual_down = add_prev2<avg_pool<2, 2, 2, 2, skip1<tag2<block<N, BN, 2, tag1<SUBNET>>>>>>;

template <int N, template <typename> class BN, int stride, typename SUBNET>

using block = BN<con<N, 3, 3, 1, 1, relu<BN<con<N, 3, 3, stride, stride, SUBNET>>>>>;

template <int N, typename SUBNET> using ares = relu<residual<block, N, affine, SUBNET>>;

template <int N, typename SUBNET> using ares_down = relu<residual_down<block, N, affine, SUBNET>>;

template <typename SUBNET> using alevel0 = ares_down<256, SUBNET>;

template <typename SUBNET> using alevel1 = ares<256, ares<256, ares_down<256, SUBNET>>>;

template <typename SUBNET> using alevel2 = ares<128, ares<128, ares_down<128, SUBNET>>>;

template <typename SUBNET> using alevel3 = ares<64, ares<64, ares<64, ares_down<64, SUBNET>>>>;

template <typename SUBNET> using alevel4 = ares<32, ares<32, ares<32, SUBNET>>>;

using anet_type = loss_metric<fc_no_bias<128, avg_pool_everything<

       alevel0<

       alevel1<

       alevel2<

       alevel3<

       alevel4<

       max_pool<3, 3, 2, 2, relu<affine<con<32, 7, 7, 2, 2,

       input_rgb_image_sized<150>

       >>>>>>>>>>>>;

 

На первом шаге программы определяются основные объекты и производится их инициализация:

 

shape_predictor sp;//сеть для определения ключевых точек лица

       frontal_face_detector detector;// распознаватель лица на основе HOG

       anet_type net;//сеть для выявления дексриптора изображения

       detector = get_frontal_face_detector();

       deserialize("shape_predictor_68_face_landmarks.dat") >> sp;

deserialize("dlib_face_recognition_resnet_model_v1.dat") >> net;

 

Детектор характерных точек лица использует модель с 68 точками. Также неплохие результаты показывает модель только с 5 точками (shape_predictor_5_face_landmarks.dat).

На следующем этапе определяем дескриптор текущего образа. Так как структуры данных библиотеки dlib и OpenCV отличаются, то первоначально производим преобразование кадра из cv::Mat в matrix<rgb_pixel>:

 

//преобразование исходного изображения в формат dlib

       matrix<rgb_pixel> dlib_img1;

       cv_image<bgr_pixel> dlibIm(frame1);

assign_image(dlib_img1, dlibIm);

После этого осуществляем поиск лиц в изображении dlib_img1.и выделение прямоугольником:

std::vector<rectangle> dets; //вектор для хранения  прямоугольников,

//обрамляющих найденные лица

dets= detector(dlib_img1); //непосредственный поиск лиц

       if (dets.size() < 1)

       {

             printf("Not found faces in image!\n");

             return -1;

       }

//прорисовка прямоугольника первого лица

       cv::Rect rect(int(dets[0].left()), int(dets[0].top()), int(dets[0].width()), int(dets[0].height()));

cv::rectangle(frame1, rect, CV_RGB(255, 0, 0), 2);

 

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

 

auto shape = sp(dlib_img1, dets[0]); //определение ключевых точек лица

//прорисовка данных точек

       for (int i = 0; i < 68; i++)

       {

             int x = shape.part(i).x();

             int y = shape.part(i).y();

             cv::circle(frame1, cv::Point(x, y), 4, CV_RGB(0, 255, 0),2);

}

 

На рисунке 2 представлены результаты выделения лица и ключевых точек.

 

Рисунок 2 – Схема размещения ключевых точек лица

 

Далее осуществляется выделение участка лица с учетом искажений:

 

std::vector<matrix<rgb_pixel>> faces; //вектор восстановленных изображений

matrix<rgb_pixel> face_chip;

//формируется определяется восстановленное изображение face_chip размером 150x150

//на основе модели лица на базе ключевых точек

       extract_image_chip(dlib_img1, get_face_chip_details(shape, 150, 0.25), face_chip);

faces.push_back(std::move(face_chip));

 

На рисунке 3 отображен пример восстановленного изображения face_chip.

 

 

Рисунок 3 – Выделение лица с учетом модели на базе ключевых точек

 

Далее производится вычисление дескрипторов для вектора изображений (в данном случае вектор дескрипторов будет содержать один элемент):

 

std::vector<matrix<float, 0, 1>> face_descriptors1;

face_descriptors1 = net(faces);

 

Для сравнения с эталонным образом аналогично рассчитываем его дескриптор. В качестве разделяющей функции используется length. Ниже приведен пример сравнения дескрипторов двух лиц:

 

float len = length(face_descriptors1[0] -face_descriptors2[0]);

 

Чем меньше значение len, тем выше достоверность сходства двух образов. С высокой долей вероятности о сходстве двух образов можно говорить, при значении len<0.6.

В ходе вычислительного эксперимента исходное изображение сравнивалось с 60 эталонными. Изображения взяты из общедоступного набора данных, состоящим из 450 фотографий различных людей (http://www.vision.caltech.edu/Image_Datasets/faces/faces.tar).   На рисунках 4 и 5 представлены результаты оценки близости образов. Для двух разных фото одного и того же человека данная оценка составляет 0.31, а для других не менее 0.6.

Рисунок 4 – Результаты эксперимента

 

 

                     а)                                  б)                                                в)

Рисунок 5 – Результаты эксперимента: а) –исходный образ; б) – эталон с отклонение 0.31; в) - эталон с отклонение 0.86

 

При этом временные затраты на вычисление дескриптора лица и сравнение с эталоном на ПК i3-8100 и GT730 составляет около  0.13 секунд.

Таким образом, библиотека dlib позволяет эффективно и с высоким быстродействием решать задачи распознавания лиц.