Распознавание лиц. 3D- реконструкция ASM модели (Face Recognition. 3D reconstruction of ASM)

Автор: | 28.04.2019

Постановка задачи
Пространственная система координат, привязанная к лицу
Алгоритм определения 3D-координат
Программа для тестирования предложенного метода

Постановка задачи

В статье Распознавание лиц на основе OpenCV для C++  изложены концепции распознавания лиц  на основе ASM (Active Shape Models). Признаки для идентификации определяются из статистических связей (расстояний) между расположением антропометрических точек лица.

Очевидно, что расстояния между точками проецируются на изображение с различными искажениями в зависимости от того, как обращено лицо к камере.

Из рисунка видно, как изменяются значения 2-х признаков при повороте головы.  Первый признак — это расстояние по вертикали от переносицы до подбородка. Второй признак — расстояние по горизонтали  между 2-я крайними точками лица. Оба размера нормализованы — отнесены к расстоянию между серединой глаз.

Обратите внимание, что расстояние по вертикали изменяется при повороте головы больше чем расстояние по горизонтали. Чем это обусловлено? Дело в том, что горизонтальная линия параллельна линии между серединой глаз, а искажение для параллельных линий одинаковое (свойство аффинных преобразований). Поэтому положение лица практически не сказывается на изменении величины этого признака.

На самом деле мы имеем дело не с аффинным преобразованием (параллельная проекция), а с проективным преобразованием (центральная проекция), при котором параллельность не сохраняется.  Но отклонения от параллельности незначительны (см. рисунок выше), также как и  различия  в искажениях отрезков — если объект достаточно удален от камеры (см. Геометрическая модель камеры). Поэтому ими можно пренебречь.

Какой вывод из этого сделаем? При нормализации признаков нужно соотносить их к размерам между точками на линиях, параллельных тем линиям, которые проходят  через точки определяемого признака.

Для нормализации можно выбрать точки на 3-х взаимно-перпендикулярных (в 3D пространстве) линиях, которые по  определяют  на изображении проекцию пространственной системы координат, привязанной к лицу.

Пространственная система координат, привязанная к лицу

Координаты Facemarks-точек обычно определяются в оконной системе координат (ОСК), у которой начало в верхнем левом углу окна, ось Y направлена вниз.

Необходимо Facemarks-точки отнести к пространственной СК, привязанной к лицу. Зная направление осей, а также единичные отрезки на них, можно  определить 3D координаты любой из Facemarks-точек.  Рассмотрим это на следующем примере.

На рисунке показана модель СК, оси которой проходят через Facemarks-точки:

  • Ось x проходит через центры глаз (определяются на основе точек 36, 39, 42 и 45).
  • Ось y  проходит через точку 33 и среднюю точку между центрами глаз.
  • Ось z  проходит через точку 27 и среднюю точку между центрами глаз.

Точки, которые определяют СК, выбраны из условий взаимной перпендикулярности осей координат, а также симметрии пар Facemarks-точек  относительно плоскости yz.

Условия взаимной перпендикулярности осей координат и симметрии пар точек выполняются приближенно — это всего лишь приемлемое допущение в рамках поставленной задачи.

На рисунке показан пример, как определить координаты симметрично расположенных  Facemarks-точек 3 и 13.  Соединяем точки отрезком. Определяем середину отрезка. Из него проводим параллельную оси z линию до пересечения с осью y.  Отрезки ломанной от точки 3 до начала координат отображают координаты (x,z,y) для точки 3.

Координаты нормализуются отрезками на осях координат, отсекаемыми точками, через которые определены оси координат.  Теперь в качестве признаков для идентификации в базе данных могут сохраняться любые из нормализованных координат Facemarks-точек.

Возможны альтернативные способы определения системы координат, привязанной к лицу, например вот такой:

Здесь ось X остается прежней, ось Y определяется точкой 8, а ось Z — серединой между точками 0 и 16. Справа на рисунке показано как определяются координаты пар симметричных точек (3, 13) и (17, 26), а также координаты (z, y) точки 30. Координаты нормализуются единичными отрезками, определяемыми точками на осях (центр между глаз на оси x;  точка 8 на оси y;  центр между точками 0 и 16 оси z).

Алгоритм определения 3D-координат

Прежде всего перейдем от оконной системы координат (ОСК) к более удобной для восприятия системе координат  с началом посредине между глаз и с направлением оси Y вверх:

СК определяется на основе Facemarks-точек 36, 39, 42 и 45.

 

XL = (X36 + X 39) /2;  YL =  (Y36 + Y39) /2;  XR =  (X42 + X45) /2;      YR = (Y42 + Y45) /2 ;   

X0 =( XL + XR)/2;    Y0 =( YL + YR)/2;

 

Определяем направления осей координат СК, привязанной к точкам лица через угловые коэффициенты прямой в плоскости (у =K*X+B):

Kx = dyx/dxx;  Ky= dyy/dxy;  Kz= dyz/dxz;

где смещения (d…) определяются через координаты точек PR,  33 и 27:

dxx= XR — X0; dyx= — (YR — Y0);

  dxy= — (X33 — X0);   dyy= Y33 — Y0;

dxz= X27 — X0;    dyz= — (Y27 — Y0);

Смещения определены в СК с центром в точке (X0,Y0) и осью Y, направленной вверх. Координаты точек в этой СК определяются из выражений:

X = Xоск — Xо;    Y = — (Yоск — Y0);

где Xоск  и  Yоск —  текущие координаты  Facemarks-точек в оконной системе координат (ОСК).

Далее в цикле определяем проекции отрезков координатной ломаной (xzy) для каждой пары симметричных Facemarks-точек. Рассмотрим на примере точек 3 и 13

Определяем расстояние между парой точек по теореме Пифагора:

DX12 = abs (X1-X2);

DY12 = abs (Y1-Y2);

D = sqrt (DX12 * DX12 + DY12 * DY12);

Определяем проекции координат x точек 13 и 3:

x13p = D/2;   x3p = — x13p;

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

Xmid=X3+(X13 — X3)/2;   Ymid= Y3+(Y13 -Y3)/2;

Переходим в СК с центром в точке (X0,Y0) и осью Y, направленной вверх:

Xmid= Xmid -X0;   Ymid= — (Ymid -Y0);

Через точку (Xmid, Ymid) проводим прямую параллельно проекции оси z до пересечения с проекцией оси y.   Находим координаты точки пересечения этих прямых:

Xint=(Ymid — Kz*Xmid)/(Ky-Kz);

Yint= Ky * Xint;

Координаты точки находим решая систему уравнений, определяющих прямые. Уравнение прямой, параллельной проекции оси z:   Y= Kz (X — Xmid) + Ymid. Уравнение прямой, проходящей по оси у:    Y= Ky * X.

Третья точка ломанной находится в начале координат: Xo=0 и Y0=0. По 3-м точкам можно определить проекции координат z и y для точек 13 и 3  —  по теореме Пифагора (также как и проекцию координаты x).

y3p = sqrt (Xint * Xint + Yint * Yint);

z3p = sqrt ((Xint -Xmid)  * (Xint -Xmid) + (Yint — Ymid) * (Yint -Ymid));

От проекций переходим к нормализованным признакам-координатам:

x13 = x13p/Lx;   x3 = — x13;   y3 = y3p/Ly;    z3 = z3p/Lz;

 Здесь Lx, Ly и Lz — отрезки на осях, отсекаемые точками PR, 33 и 27. Определяются по теореме Пифагора:

Lx= sqrt (dyx * dyx + dxx * dxx);

Ly= sqrt (dyy * dyy + dxy * dxy);

 Lz= sqrt (dxz * dxz + dyz * dyz);

В цикле аналогично определяются координаты остальных пар симметричных точек.

В программе следует предусматривать проверку условий, когда ось z проецируется в точку или когда она совпадает на изображении с одной из осей (x или y). В некоторых случаях возможно деление на ноль, необходима соответствующая обработка таких исключений.

Программа для тестирования предложенного метода

Приведенная ниже программа позволяет выбрать наиболее подходящие настройки и оценить эффективность предложенного метода 3D- реконструкция Facemarks модели.

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

Значение между координатами точек  должны быть приблизительно одинаковыми. В противном случае расстояния между точками будут зависеть лишь от одной координаты. Например, из рисунка видно, что значение координаты Z тестируемой точки 3 существенно больше  по сравнению с координатами X и Y. Какой выход? Можно уменьшать ее делением на какой-то коэффициент.

В конечном итоге, для оценки эффективности метода необходимо определить, как влияет поворот головы на стабильность признаков по сравнению с 2-мерной Facemarks моделью.

 

#include <iostream>
#include <conio.h>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/objdetect/objdetect.hpp>
#include <opencv2/face.hpp>
#include "drawLandmarks.hpp"

using namespace std;
using namespace cv;
using namespace cv::face;

int main(int argc, char** argv)
{
 // Load Face Detector
 CascadeClassifier faceDetector("haarcascade_frontalface_alt2.xml");
 // Create an instance of Facemark
 Ptr<Facemark> facemark = FacemarkLBF::create();
 // Load landmark detector
 facemark->loadModel("lbfmodel.yaml");
 // Set up webcam for video capture
 VideoCapture cam(0);
 // Variable to store a video frame and its grayscale 
 Mat frame, gray;
 // Read a frame
 while (cam.read(frame))
 {
 // Find face
 vector<Rect> faces;
 // Convert frame to grayscale because
 // faceDetector requires grayscale image.
 cvtColor(frame, gray, COLOR_BGR2GRAY);
 // Detect faces
 faceDetector.detectMultiScale(gray, faces);
 // Variable for landmarks. 
 // Landmarks for one face is a vector of points
 // There can be more than one face in the image. Hence, we 
 // use a vector of vector of points. 
 vector< vector<Point2f> > landmarks;
 // Run landmark detector
 bool success = facemark->fit(frame, faces, landmarks);

if (success)
 {
 // If successful, render the landmarks on the face
 /*for (size_t i = 0; i < faces.size(); i++)
 {
 cv::rectangle(frame, faces[i], Scalar(0, 255, 0), 3);
 }*/

for (int i = 0; i < landmarks.size(); i++)
 {
 drawLandmarks(frame, landmarks[i]);

 float XL = (landmarks[i][45].x + landmarks[i][42].x) / 2;
 float YL = (landmarks[i][45].y + landmarks[i][42].y) / 2;
 float XR = (landmarks[i][39].x + landmarks[i][36].x) / 2;
 float YR = (landmarks[i][39].y + landmarks[i][36].y) / 2;
 // The coord. center
 float X0 = (XL + XR) / 2;
 float Y0 = (YL + YR) / 2;
 
 //line(frame, Point(X0, Y0), Point(XL, YL), Scalar(0, 0, 255), 2); // Draw x
 //line(frame, Point(X0, Y0), Point(landmarks[i][33].x, landmarks[i][33].y), Scalar(0, 255, 0), 2); // Draw y 
 //line(frame, Point(X0, Y0), Point(landmarks[i][27].x, landmarks[i][27].y), Scalar(255, 0, 0), 2); // Draw z

float dxx = XR - X0; float dyx = -(YR - Y0);
 float dxy = -(landmarks[i][33].x - X0); float dyy = landmarks[i][33].y - Y0;
 float dxz = landmarks[i][27].x - X0; float dyz = -(landmarks[i][27].y - Y0);

float Kx = dyx/dxx; float Ky = dyy/dxy; float Kz = dyz/dxz;

 float Lx = sqrt(dxx * dxx + dyx * dyx);
 float Ly = sqrt(dxy * dxy + dyy * dyy);
 float Lz = sqrt(dxz * dxz + dyz * dyz);

 float X1 = (landmarks[i][3].x); // координаты 2-х симметричных точек
 float Y1 = (landmarks[i][3].y);
 float X2 = (landmarks[i][13].x);
 float Y2 = (landmarks[i][13].y);
 float DX1 = (X2 - X1);
 float DY1 = (Y2 - Y1);


 float Xmid = X1 + DX1 / 2; float Ymid = Y1 + DY1 / 2;
 
 Xmid = Xmid - X0; Ymid = -(Ymid - Y0); // переход из оконной системы координат

float Xint = (Ymid-Kz*Xmid)/(Ky - Kz); float Yint = Ky*Xint; // определяем точку пересечения с осью y.

 Xmid = Xmid + X0; Ymid = -(Ymid - Y0); // возврат в оконную систему координат
 Xint = Xint + X0; Yint = -(Yint - Y0);


 // рисуем отрезки координатной ломанной
 line(frame, Point(X1, Y1), Point(Xmid, Ymid), Scalar(0, 0, 255), 2); // Draw X3
 line(frame, Point(X0, Y0), Point(Xint, Yint), Scalar(0, 255, 0), 2); // Draw Y3
 line(frame, Point(Xmid, Ymid), Point(Xint, Yint), Scalar(255, 0, 0), 2); // Draw Z3

 float x1 = 0.5*sqrt(DX1 * DX1 + DY1 * DY1) / Lx; // нормализованная координата х1 
 float x2 = -x1; //нормализованная координата х2 (для симметричной точки)

 DX1 = (Xint - X0);
 DY1 = (Yint - Y0);
 float y1 = sqrt(DX1 * DX1 + DY1 * DY1) / Ly; //нормализованные координаты y1 и y2

 DX1 = (Xint - Xmid);
 DY1 = (Yint - Xmid);
 float z1 = sqrt(DX1 * DX1 + DY1 * DY1) / Lz; //нормализованные координаты z1 и z2

 putText(frame, std::to_string(x1), Point(X1 + 150, Y1 + 10), 1, 2, Scalar(0,0, 255), 2);
 putText(frame, std::to_string(z1), Point(X1 + 150, Y1 - 30), 1, 2, Scalar(255, 0, 0), 2);
 putText(frame, std::to_string(y1), Point(X1 + 150, Y1 - 70), 1, 2, Scalar(0, 255, 0), 2);
 
 }
 }

// Display results 
 imshow("Facial Landmark Detection", frame);

// Exit loop if ESC is pressed
 if (waitKey(1) == 27) break;

}
 return 0;
}

Автор: Николай Свирневский

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *