OpenCV實戰:人臉關鍵點檢測(FaceMark)

Summary:利用OpenCV中的LBF算法進行人臉關鍵點檢測(Facial Landmark Detection)

Author: Amusi

Date: 2018-03-20

Note: OpenCV3.4以及上支持Facemark

PS:點擊“閱讀原文”,可以下載所有源碼和模型,記得給star哦!

教程目錄

測試環境

引言

Facemark API

Facemark訓練好的模型

利用OpenCV代碼進行實時人臉關鍵點檢測

步驟

代碼

實驗結果

Reference

測試環境

Windows10

Visual Studio 2013

OpenCV3.4.1

引言

人臉一般是有68個關鍵點,常用的人臉開源庫有Dlib,還有很多深度學習的方法。

OpenCV實戰:人臉關鍵點檢測(FaceMark)

本教程僅利用OpenCV,不依賴任何其它第三方庫來實現人臉關鍵點檢測,這一特性是之前沒有的。因爲OpenCV自帶的samples中只有常見的人臉檢測、眼睛檢測和眼鏡檢測等(方法是harr+cascade或lbp+cascade)。

本教程主要參考Facemark : Facial Landmark Detection using OpenCV[1]

截止到2018-03-20,OpenCV3.4可支持三種人臉關鍵點檢測,但目前只能找到一種已訓練好的模型,所以本教程只介紹一種實現人臉關鍵點檢測的算法。而且此類算法還沒有Python接口,所以這裏只介紹C++的代碼實現。

Facemark API

OpenCV官方的人臉關鍵點檢測API稱爲Facemark。Facemark目前分別基於下述三篇論文,實現了三種人臉關鍵點檢測的方法。

FacemarkKazemi[2]: This implementation is based on a paper titled “One Millisecond Face Alignment with an Ensemble of Regression Trees” by V.Kazemi and J. Sullivan published in CVPR 2014[3]. An alternative implementation of this algorithm can be found in DLIB

FacemarkAAM[4]: This implementation uses an Active Appearance Model (AAM) and is based on an the paper titled “Optimization problems for fast AAM fitting in-the-wild” by G. Tzimiropoulos and M. Pantic, published in ICCV 2013[5].

FacemarkLBF[6]: This implementation is based a paper titled “Face alignment at 3000 fps via regressing local binary features” by S. Ren published in CVPR 2014[7].

在寫這篇文章的時候,FacemarkKazemi類似乎不是從Facemark類派生的,而其他兩個類都是。

Facemark訓練好的模型

儘管Facemark API包含三種不同的實現,但只有FacemarkLBF(local binary features,LBF)才提供經過訓練的模型。 (之後在我們根據公共數據集訓練我們自己的模型後,這篇文章將在未來更新)

你可以從中下載已訓練好的模型:

lbfmodel.yaml[8]

利用OpenCV代碼進行實時人臉關鍵點檢測

步驟

  1. 加載人臉檢測器(face detector)

所有的人臉關鍵點檢測算法的輸入都是一個截切的人臉圖像。因爲,我們的第一步就是在圖像中檢測所有的人臉,並將所有的人臉矩形框輸入到人臉關鍵點檢測器中。這裏,我們可以使用OpenCV的Haar人臉檢測器或者lbp人臉檢測器來檢測人臉。

  1. 創建Facemark對象

創建Facemark類的對象。在OpenCV中,Facemark是使用智能指針(smart pointer,PTR),所以我們不需要考慮內存泄漏問題。

  1. 加載landmark檢測器

加載關鍵點檢測器(lbfmodel.yaml)。此人臉檢測器是在幾千幅帶有關鍵點標籤的人臉圖像上訓練得到的。

帶有註釋/標籤關鍵點的人臉圖像公共數據集可以訪問這個鏈接下載:https://ibug.doc.ic.ac.uk/resources/facial-point-annotations/

4.從網絡攝像頭中捕獲幀

捕獲視頻幀並處理。我們既可以打開一個本地視頻(.mp4),也可以打開網絡攝像機(如果電腦有的話)來進行人臉關鍵點檢測。

  1. 檢測人臉

我們對視頻的每一幀運行人臉檢測器。人臉檢測器的輸出是一個包含一個或多個矩形(rectangles)的容器(vector),即視頻幀中可能有一張或者多張人臉。

  1. 運行人臉關鍵點檢測器

我們根據人臉矩形框截取原圖中的人臉ROI,再利用人臉關鍵點檢測器(facial landmark detector)對人臉ROI進行檢測。

對於每張臉我們獲得,我們可以獲得68個關鍵點,並將其存儲在點的容器中。因爲視頻幀中可能有多張臉,所以我們應採用點的容器的容器。

  1. 繪製人臉關鍵點

根據獲得關鍵點,我們可以在視頻幀上繪製出來並顯示。

代碼

本教程的代碼一共有兩個程序,分別爲faceLandmarkDetection.cpp和drawLandmarks.hpp。

faceLandmarkDetection.cpp實現視頻幀捕獲、人臉檢測、人臉關鍵點檢測;

drawLandmarks.hpp實現人臉關鍵點繪製和多邊形線繪製。

faceLandmarkDetection.cpp


 1// Summary: 利用OpenCV的LBF算法進行人臉關鍵點檢測
 2// Author:  Amusi
 3// Date:    2018-03-20
 4// Reference:
 5//        [1]Tutorial: https://www.learnopencv.com/facemark-facial-landmark-detection-using-opencv/
 6//        [2]Code: https://github.com/spmallick/learnopencv/tree/master/FacialLandmarkDetection
 7
 8// Note: OpenCV3.4以及上支持Facemark
 9
10#include <opencv2/opencv.hpp>
11#include <opencv2/face.hpp>
12#include "drawLandmarks.hpp"
13
14
15using namespace std;
16using namespace cv;
17using namespace cv::face;
18
19
20int main(int argc,char** argv)
21{
22    // 加載人臉檢測器(Face Detector)
23    // [1]Haar Face Detector
24    //CascadeClassifier faceDetector("haarcascade_frontalface_alt2.xml");
25    // [2]LBP Face Detector
26    CascadeClassifier faceDetector("lbpcascade_frontalface.xml");
27
28    // 創建Facemark類的對象
29    Ptr<Facemark> facemark = FacemarkLBF::create();
30
31    // 加載人臉檢測器模型
32    facemark->loadModel("lbfmodel.yaml");
33
34    // 設置網絡攝像頭用來捕獲視頻
35    VideoCapture cam(0);
36
37    // 存儲視頻幀和灰度圖的變量
38    Mat frame, gray;
39
40    // 讀取幀
41    while(cam.read(frame))
42    {
43
44      // 存儲人臉矩形框的容器
45      vector<Rect> faces;
46      // 將視頻幀轉換至灰度圖, 因爲Face Detector的輸入是灰度圖
47      cvtColor(frame, gray, COLOR_BGR2GRAY);
48
49      // 人臉檢測
50      faceDetector.detectMultiScale(gray, faces);
51
52      // 人臉關鍵點的容器
53      vector< vector<Point2f> > landmarks;
54
55      // 運行人臉關鍵點檢測器(landmark detector)
56      bool success = facemark->fit(frame,faces,landmarks);
57
58      if(success)
59      {
60        // 如果成功, 在視頻幀上繪製關鍵點
61        for(int i = 0; i < landmarks.size(); i++)
62        {
63            // 自定義繪製人臉特徵點函數, 可繪製人臉特徵點形狀/輪廓
64            drawLandmarks(frame, landmarks[i]);
65            // OpenCV自帶繪製人臉關鍵點函數: drawFacemarks
66            drawFacemarks(frame, landmarks[i], Scalar(0, 0, 255));
67        }
68
69      }
70
71      // 顯示結果
72      imshow("Facial Landmark Detection", frame);
73
74      // 如果按下ESC鍵, 則退出程序
75      if (waitKey(1) == 27) break;
76
77    }
78    return 0;
79}

drawLandmarks.hpp

 1// Summary: 繪製人臉關鍵點和多邊形線
 2// Author:  Amusi
 3// Date:    2018-03-20
 4
 5#ifndef _renderFace_H_
 6#define _renderFace_H_
 7
 8#include <iostream>
 9#include <opencv2/opencv.hpp>
10
11using namespace cv; 
12using namespace std; 
13
14#define COLOR Scalar(255, 200,0)
15
16// drawPolyline通過連接開始和結束索引之間的連續點來繪製多邊形線。
17void drawPolyline
18(
19  Mat &im,
20  const vector<Point2f> &landmarks,
21  const int start,
22  const int end,
23  bool isClosed = false
24)
25{
26    // 收集開始和結束索引之間的所有點
27    vector <Point> points;
28    for (int i = start; i <= end; i++)
29    {
30        points.push_back(cv::Point(landmarks[i].x, landmarks[i].y));
31    }
32
33    // 繪製多邊形曲線
34    polylines(im, points, isClosed, COLOR, 2, 16);
35
36}
37
38// 繪製人臉關鍵點
39void drawLandmarks(Mat &im, vector<Point2f> &landmarks)
40{
41    // 在臉上繪製68點及輪廓(點的順序是特定的,有屬性的)
42    if (landmarks.size() == 68)
43    {
44      drawPolyline(im, landmarks, 0, 16);           // Jaw line
45      drawPolyline(im, landmarks, 17, 21);          // Left eyebrow
46      drawPolyline(im, landmarks, 22, 26);          // Right eyebrow
47      drawPolyline(im, landmarks, 27, 30);          // Nose bridge
48      drawPolyline(im, landmarks, 30, 35, true);    // Lower nose
49      drawPolyline(im, landmarks, 36, 41, true);    // Left eye
50      drawPolyline(im, landmarks, 42, 47, true);    // Right Eye
51      drawPolyline(im, landmarks, 48, 59, true);    // Outer lip
52      drawPolyline(im, landmarks, 60, 67, true);    // Inner lip
53    }
54    else 
55    { 
56        // 如果人臉關鍵點數不是68,則我們不知道哪些點對應於哪些面部特徵。所以,我們爲每個landamrk畫一個圓圈。
57        for(int i = 0; i < landmarks.size(); i++)
58        {
59            circle(im,landmarks[i],3, COLOR, FILLED);
60        }
61    }
62
63}
64
65#endif // _renderFace_H_

實驗結果

OpenCV實戰:人臉關鍵點檢測(FaceMark)

Reference

[1]Tutorial:https://www.learnopencv.com/facemark-facial-landmark-detection-using-opencv/

[2]FacemarkKazemi:https://docs.opencv.org/trunk/dc/de0/classcv_1_1face_1_1FacemarkKazemi.html

[3]One Millisecond Face Alignment with an Ensemble of Regression Trees:http://www.csc.kth.se/~vahidk/face_ert.html

[4]FacemarkAAM:

https://docs.opencv.org/trunk/d5/d7b/classcv_1_1face_1_1FacemarkAAM.html

[5]Optimization problems for fast AAM fitting in-the-wild:

https://ibug.doc.ic.ac.uk/media/uploads/documents/tzimiro_pantic_iccv2013.pdf

[6]FacemarkLBF:https://docs.opencv.org/trunk/dc/d63/classcv_1_1face_1_1FacemarkLBF.html

[7]Face alignment at 3000 fps via regressing local binary features:http://www.jiansun.org/papers/CVPR14_FaceAlignment.pdf

[8]lbfmodel.yaml:https://github.com/kurnianggoro/GSOC2017/blob/master/data/lbfmodel.yaml

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章