Facemark:使用OpenCV进行面部特征点检测

 面部特征检测应用很多,我将在下一节介绍当前项目用到一个典型例子,因为疲劳检测有一张方案是通过检测人眼的闭合时间来实现的,在实际装车应用中效果还不错。本节先介绍一下opencv中自带的特征点检测功能,后续将讲解如何使用opencv+dlib实现疲劳检测功能。

现在OpenCV支持几种本地特征检测算法。然而,由于两个原因,实际使用中还需要做更多的工作

1、Python支持:截至OpenCV3.4似乎仍然没有python支持

2、缺乏训练过的模型:在三种用于特征检测的算法中,我只能找到一个训练过的模型。这不是很难解决的问题。在最坏的情况下,我们将尝试我们自己的模型,并使它可供人们使用。

预训练模型:

在进行本实验之前请先下载模型文件,https://github.com/kurnianggoro/GSOC2017/blob/master/data/lbfmodel.yaml(用于获取特征点),还要用到一个haarcascade_frontalface_alt2.xml(用于检测人脸),该模型在OpenCV安装目录中的\data\ haarcascades下。

先上代码:

1个头文件:drawLandmarks.hpp

#ifndef _renderFace_H_
#define _renderFace_H_

using namespace cv; 
using namespace std; 

#define COLOR Scalar(255, 200,0)

// drawPolyLine draws a poly line by joining 
// successive points between the start and end indices. 
void drawPolyline
(
  Mat &im,
  const vector<Point2f> &landmarks,
  const int start,
  const int end,
  bool isClosed = false
)
{
    // Gather all points between the start and end indices
    vector <Point> points;
    for (int i = start; i <= end; i++)
    {
        points.push_back(cv::Point(landmarks[i].x, landmarks[i].y));
    }
    // Draw polylines. 
    polylines(im, points, isClosed, COLOR, 2, 16);
    
}


void renderFace(cv::Mat &im, vector<Point2f> &landmarks)
{
    // Draw face for the 68-point model.
    if (landmarks.size() == 68)
    {
      drawPolyline(im, landmarks, 0, 16);           // Jaw line
      drawPolyline(im, landmarks, 17, 21);          // Left eyebrow
      drawPolyline(im, landmarks, 22, 26);          // Right eyebrow
      drawPolyline(im, landmarks, 27, 30);          // Nose bridge
      drawPolyline(im, landmarks, 30, 35, true);    // Lower nose
      drawPolyline(im, landmarks, 36, 41, true);    // Left eye
      drawPolyline(im, landmarks, 42, 47, true);    // Right Eye
      drawPolyline(im, landmarks, 48, 59, true);    // Outer lip
      drawPolyline(im, landmarks, 60, 67, true);    // Inner lip
    }
    else 
    { // If the number of points is not 68, we do not know which 
      // points correspond to which facial features. So, we draw 
      // one dot per landamrk. 
      for(int i = 0; i < landmarks.size(); i++)
      {
        circle(im,landmarks[i],3, COLOR, FILLED);
      }
    }
    
}

1个cpp文件:facialLandmarkDetection.cpp

#include <opencv2/opencv.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(1);
    
    // 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(int i = 0; i < landmarks.size(); i++)
        {
          drawLandmarks(frame, landmarks[i]);
        }
      }

      // Display results 
      imshow("Facial Landmark Detection", frame);
      // Exit loop if ESC is pressed
      if (waitKey(1) == 27) break;
      
    }
    return 0;
}

最后是cmake文件:

cmake_minimum_required(VERSION 2.8)
project(fd)
find_package(OpenCV REQUIRED)
include_directories(${OpenCV_INClUDE_DIRS})
add_executable(fd facialLandmarkDetection.cpp)
target_link_libraries(fd ${OpenCV_LIBS})

代码解析:

1、加载人脸检测器:

     所有的面部特征检测算法都以裁剪出的面部图像作为输入。因此,我们的第一步是检测图像中的所有人脸,并将这些人脸矩形框传递给特征点检测器。CascadeClassifier faceDetector("haarcascade_frontalface_alt2.xml");加载OpenCV的HAAR人脸检测器(haarcascade_frontalface_alt2.xml)。

2、创建Facemark实例:

Ptr<Facemark> facemark = FacemarkLBF::create();,我们创建了Facemark类的一个实例。它被包裹在OpenC V智能指针(Ptr)中,所以您不必担心内存管理。智能指针是c++的基础知识,请自行百度。

3、加载特征点检测器:

接下来,我们加载特征点检测器(lbfmodel.yaml)。这个地标探测器是在几千幅面部图像和相应的特征点上训练的。带有标注特征点的面部图像的公共数据集可以在这里找到https://ibug.doc.ic.ac.uk/resources/facial-point-annotations/

4、从摄像头捕捉帧:

下一步是抓取一个视频帧并处理它。VideoCapture cam(0);设置从本机第一个摄像头捕获视频,对应的设备文件为/dev/video0。可以使用 VideoCapture cap(“myvideo.mp4”);来读取视频文件, 然后,我们不断从视频抓取帧,直到ESC被按下。

5、检测面部:

我们在视频的每个帧上运行人脸检测器。人脸检测器的输出是矩形的向量,对应图像中的一个或多个人脸。faceDetector.detectMultiScale(gray, faces);

6、运行面部地标检测器:

我们将原始图像和检测到的人脸矩形传送到面部特征点检测器。对于每一张脸,我们得到68个特征点,这些特征点被存储在一个向量中。因为一个帧中可能有多个人脸,所以我们必须通过一个点向量的向量来存储地标,每个子向量对应一张脸。

bool success = facemark->fit(frame,faces,landmarks);

7、绘制地标:

一旦我们获得了特征点,我们就可以把它们画在图像上进行展示。绘图的代码在drawLandmarks.hpp中。

drawLandmarks(frame, landmarks[i]);

 

最后,实际代码运行效果如下:(没错,this guy is me ~ _~)

 

 

 

 

  • 1
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 14
    评论
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值