赞
踩
面部特征检测应用很多,我将在下一节介绍当前项目用到一个典型例子,因为疲劳检测有一张方案是通过检测人眼的闭合时间来实现的,在实际装车应用中效果还不错。本节先介绍一下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 ~ _~)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。