前言
- 本文章使用opencv和dlib库,使用C++代码实现了人脸活体检测,包括眨眼检测、张嘴检测以及摇头检测,可以对静态图片和活体进行有效区分。
效果展示
Dlib库介绍
-
dlib是一个开源的C++机器学习库,它提供了一系列用于图像处理、人脸检测、人脸识别、物体检测、图像标注等功能的算法和工具。dlib库使用了现代C++和模板元编程的技术,并且具有高度的可移植性和可扩展性。
-
dlib库的主要特点包括:文章来源:https://www.toymoban.com/news/detail-851513.html
- 提供了一系列高效的机器学习算法,如支持向量机、最大间隔分类器、随机森林等。
- 提供了用于图像处理的算法,如图像滤波、图像变换、边缘检测等。
- 提供了用于人脸检测和人脸识别的算法,具有较高的准确性和性能。
- 提供了用于物体检测和跟踪的算法,如HOG特征和级联分类器。
- 提供了用于图像标注和图像分割的算法,如条件随机场。
- 支持多线程和并行计算,可以充分利用多核处理器的性能。
- 具有丰富的文档和示例代码,易于学习和使用。
-
dlib库在计算机视觉、模式识别和机器学习领域被广泛应用,是一个功能强大且易用的机器学习库。对外提供了C++和python两种接口。文章来源地址https://www.toymoban.com/news/detail-851513.html
OpenCV库介绍
- OpenCV是一个基于BSD许可发行的跨平台计算机视觉库。OpenCV作为较大众的开源库,拥有了丰富的常用图像处理函数库,采用C/C++语言编写,可以运行在Linux/Windows/Mac等操作系统上,能够快速的实现一些图像处理和识别的任务。
- 本项目主要使用OpenCV来打开摄像头,读取视频帧。
人脸识别
- Dlib库中有一个人脸特征点模型 shape_predictor_68_face_landmarks.dat,记录了人脸的68个特征点。可以通过这个模型识别人脸特征从而检测是否检测到人脸。
- 人脸特征点
眨眼检测原理
- 通过计算人眼纵横比(EAR),可以判断眼睛是张开还是闭合,从而判断是否眨眼。
- E A R = ∥ p 2 − p 6 ∥ + ∥ p 3 − p 5 ∥ 2 ∥ p 1 − p 4 ∥ EAR=\frac{\|p2-p6\| + \|p3-p5\|}{ 2\|p1-p4\|} EAR=2∥p1−p4∥∥p2−p6∥+∥p3−p5∥
- 分子计算的是眼睛的特征点在垂直方向上的距离,分母计算的是眼睛的特征点在水平方向上的距离,由于水平特征点只有一组,所以乘以2保证两组特征点的权重相同。
- 眼睛闭合时,p2和p6、p3和p5之间的距离会减小,而p1和p4之间的距离基本保持不变。因此通过计算人眼纵横比EAR,可以判断眼睛的睁开和闭合状态。
张嘴检测原理
- 张嘴检测的原理与眨眼检测一样,嘴部的特征点较多,可以自行选取六个特征点,计算嘴部的纵横比MAR
- 我这里选取的六个特征点是 49,55、51,59、53,57
- 按照相同的方法,计算人嘴纵横比 MAR
摇头检测
- 摇头时,鼻子中心点到左右脸颊的距离会发生变化,我们可以通过计算该距离变化,判断是否进行了摇头。
- 比如通过计算鼻子中心点特征点31到左脸颊特征点2和右脸颊特征点16的距离变化,判断摇头动作。具体参考源代码。
Dlib和OpenCV库编译
- 编译前需要安装Visual Studio 2015和CMake。
- Dlib库编译
- Dlib库下载地址。可以选择这个包下载 dlib-19.24.zip,同时需要下载这个人脸关键点检测器 shape_predictor_68_face_landmarks.dat。
- 源码下载后,需要进行编译。
- 先在Dlib源码工程下 dlib/config.h文件末尾添加下面这一句
-
#define DLIB__CMAKE_GENERATED_A_CONFIG_H_FILE
-
- 然后在Dlib源码工程下,创建build_x86文件夹,进入该文件夹,执行命令
-
cmake -G "Visual Studio 14 2015" .. cmake --build ./ --config Release
-
- 编译完成后,会在build_x86\dlib\Release下生成静态库文件 dlib19.24.0_release_32bit_msvc1900.lib
- OpenCV库编译
- openCV库下载地址。选择一个版本的源码(Sources)进行下载,我下载的是4.5.0
- 源码下载后,需要进行编译。在OpenCV源码工程下,创建build_x86文件夹,进入该文件夹,执行命令
-
cmake -G "Visual Studio 14 2015" .. cmake --build ./ --config Release
-
- 编译完成后,会在build_x86/lib/Release下生成静态库文件,会在build_x86/bin/Release下生成动态库文件。
创建工程
- 先创建一个facec目录
- 在facec目录下创建一个dlib-19.24目录,将dlib源码中的dlib目录拷贝到该目录下
- 在facec目录下创建一个opencv-4.5.0目录,将opecv源码中的include和modules目录拷贝到该目录下
- 在facec目录下创建一个lib目录,将编译好的dlib静态库和opencv静态库拷贝到该目录下。
- 在facec目录下创建一个src目录,src目录下为main.cpp源代码。
- 在facec目录下创建一个CMakeLists.txt文件。
- 目录结构如下
-
facec ├── dlib-19.24 │ └── dlib ├── opencv-4.5.0 │ ├── include │ └── modules ├── lib ├── src │ └── main.cpp └──CMakeLists.txt
编译工程
- 在facec目录创建build_x86目录,进入该目录执行
-
cmake -G "Visual Studio 14 2015" .. cmake --build ./ --config Release
- 会在 bin\Release 下生成可执行程序,然后将opencv相关动态库和人脸关键点检测器shape_predictor_68_face_landmarks.dat拷贝过来,双击可执行程序就可以运行了。
相关源代码
- main.cpp源代码
-
#include <dlib/opencv.h> #include <dlib/image_processing/frontal_face_detector.h> #include <dlib/image_processing/render_face_detections.h> #include <dlib/image_processing.h> #include <dlib/gui_widgets.h> #include <stdio.h> #include <opencv2/opencv.hpp> using namespace dlib; using namespace std; int main() { try { // 打开摄像头 cv::VideoCapture cap(0, cv::CAP_DSHOW); if (!cap.isOpened()) { printf("open VideoCapture failed.\n"); return 1; } printf("open VideoCapture success.\n"); image_window win; // Load face detection and pose estimation models. frontal_face_detector detector = get_frontal_face_detector(); shape_predictor pose_model; deserialize("shape_predictor_68_face_landmarks.dat") >> pose_model; static int mouthIndex = 0; static int leyeIndex = 0; static int reyeIndex = 0; int counter_mouth = 0; int counter_leye = 0; int counter_reye = 0; // Grab and process frames until the main window is closed by the user. while(!win.is_closed()) { // Grab a frame cv::Mat frame; if (!cap.read(frame)) { break; } cv_image<bgr_pixel> cimg(frame); // 保存图片 cv::Mat imgPng = dlib::toMat(cimg); cv::imwrite("face.png", imgPng); // Detect faces std::vector<rectangle> faces = detector(cimg); // Find the pose of each face. std::vector<full_object_detection> shapes; printf("faces number is %d\n", faces.size()); for (unsigned long i = 0; i < faces.size(); ++i) { shapes.push_back(pose_model(cimg, faces[i])); //计算人眼纵横比 //左眼 dlib::point leyeLeft = shapes.at(i).part(37); dlib::point leyeRight = shapes.at(i).part(40); dlib::point leyeLeftUp = shapes.at(i).part(38); dlib::point leyeLeftDowm = shapes.at(i).part(42); dlib::point leyeRightUp = shapes.at(i).part(39); dlib::point leyeRightDowm = shapes.at(i).part(41); float leyeA = sqrt(pow(leyeLeftUp.x() - leyeLeftDowm.x(), 2) + pow(leyeLeftUp.y() - leyeLeftDowm.y(), 2)); float leyeB = sqrt(pow(leyeRightUp.x() - leyeRightDowm.x(), 2) + pow(leyeRightUp.y() - leyeRightDowm.y(), 2)); float leyeC = sqrt(pow(leyeLeft.x() - leyeRight.x(), 2) + pow(leyeLeft.y() - leyeRight.y(), 2)); float leyeEVR = (leyeA + leyeB) / (2 * leyeC); //右眼 dlib::point reyeLeft = shapes.at(i).part(43); dlib::point reyeRight = shapes.at(i).part(46); dlib::point reyeLeftUp = shapes.at(i).part(44); dlib::point reyeLeftDowm = shapes.at(i).part(48); dlib::point reyeRightUp = shapes.at(i).part(45); dlib::point reyeRightDowm = shapes.at(i).part(47); float reyeA = sqrt(pow(reyeLeftUp.x() - reyeLeftDowm.x(), 2) + pow(reyeLeftUp.y() - reyeLeftDowm.y(), 2)); float reyeB = sqrt(pow(reyeRightUp.x() - reyeRightDowm.x(), 2) + pow(reyeRightUp.y() - reyeRightDowm.y(), 2)); float reyeC = sqrt(pow(reyeLeft.x() - reyeRight.x(), 2) + pow(reyeLeft.y() - reyeRight.y(), 2)); float reyeEVR = (reyeA + reyeB) / (2 * reyeC); //计算人嘴纵横比 dlib::point mouth_left = shapes.at(i).part(49); dlib::point mouth_right = shapes.at(i).part(55); dlib::point mouth_leftUp = shapes.at(i).part(51); dlib::point mouth_leftDown = shapes.at(i).part(59); dlib::point mouth_rightUp = shapes.at(i).part(53); dlib::point mouth_rightDown = shapes.at(i).part(57); float mouthA = sqrt(pow(mouth_leftUp.x() - mouth_leftDown.x(), 2) + pow(mouth_leftUp.y() - mouth_leftDown.y(), 2)); float mouthB = sqrt(pow(mouth_rightUp.x() - mouth_rightDown.x(), 2) + pow(mouth_rightUp.y() - mouth_rightDown.y(), 2)); float mouthC = sqrt(pow(mouth_left.x() - mouth_right.x(), 2) + pow(mouth_left.y() - mouth_right.y(), 2)); float mouthEVR = (mouthA + mouthB) / (2 * mouthC); //摇头 //左脸边缘 dlib::point face_left = shapes.at(i).part(2); //右脸边缘 dlib::point face_right = shapes.at(i).part(16); //鼻子中心 dlib::point face_nose = shapes.at(i).part(31); //鼻子到左脸边缘距离 float lfaceLength = sqrt(pow(face_nose.x() - face_left.x(), 2) + pow(face_nose.y() - face_left.y(), 2)); //鼻子到右脸边缘距离 float rfaceLength = sqrt(pow(face_nose.x() - face_right.x(), 2) + pow(face_nose.y() - face_right.y(), 2)); //记录张嘴次数 if (mouthEVR < 0.62){ //闭嘴 counter_mouth += 1; }else if(mouthEVR > 0.70){ //张嘴 if (counter_mouth >= 1) { mouthIndex += 1; } counter_mouth = 0; } else { //此区间处于临界区,不稳定,不做检测 } // 显示张嘴次数 char mouthBuf[100] = { 0 }; sprintf(mouthBuf, "mouth couent : %d", mouthIndex); cv::putText(frame, mouthBuf, cv::Point(0, 20), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(0, 0, 255), 2); //记录左眼眨眼次数 if (leyeEVR > 2.9) { //闭眼 counter_leye += 1; } else { //睁眼 if (counter_leye >= 1) { leyeIndex += 1; } counter_leye = 0; } //记录右眼眨眼次数 if (reyeEVR > 5.0) { //闭眼 counter_reye += 1; } else { //睁眼 if (counter_reye >= 1) { reyeIndex += 1; } counter_reye = 0; } //取最小值 if (reyeIndex > leyeIndex) { reyeIndex = leyeIndex; } //显示眨眼次数 char eyeBuf[100] = { 0 }; sprintf(eyeBuf, "eye count: %d", reyeIndex); cv::putText(frame, eyeBuf, cv::Point(0, 45), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(0, 0, 255), 2); } // Display it all on the screen win.clear_overlay(); win.set_image(cimg); win.add_overlay(render_face_detections(shapes)); } } catch(serialization_error& e) { cout << endl << e.what() << endl; } catch(exception& e) { cout << e.what() << endl; } }
- CMakeLists.txt文件内容
-
cmake_minimum_required (VERSION 3.5) project (faceRecongize) MESSAGE(STATUS "PROJECT_SOURCE_DIR " ${PROJECT_SOURCE_DIR}) SET(SRC_LISTS ${PROJECT_SOURCE_DIR}/src/main.cpp) set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin) # 配置头文件目录 include_directories(${PROJECT_SOURCE_DIR}/dlib-19.24) include_directories(${PROJECT_SOURCE_DIR}/opencv-4.5.0/include) include_directories(${PROJECT_SOURCE_DIR}/opencv-4.5.0/modules/core/include) include_directories(${PROJECT_SOURCE_DIR}/opencv-4.5.0/modules/calib3d/include) include_directories(${PROJECT_SOURCE_DIR}/opencv-4.5.0/modules/features2d/include) include_directories(${PROJECT_SOURCE_DIR}/opencv-4.5.0/modules/flann/include) include_directories(${PROJECT_SOURCE_DIR}/opencv-4.5.0/modules/dnn/include) include_directories(${PROJECT_SOURCE_DIR}/opencv-4.5.0/modules/highgui/include) include_directories(${PROJECT_SOURCE_DIR}/opencv-4.5.0/modules/imgcodecs/include) include_directories(${PROJECT_SOURCE_DIR}/opencv-4.5.0/modules/videoio/include) include_directories(${PROJECT_SOURCE_DIR}/opencv-4.5.0/modules/imgproc/include) include_directories(${PROJECT_SOURCE_DIR}/opencv-4.5.0/modules/ml/include) include_directories(${PROJECT_SOURCE_DIR}/opencv-4.5.0/modules/objdetect/include) include_directories(${PROJECT_SOURCE_DIR}/opencv-4.5.0/modules/photo/include) include_directories(${PROJECT_SOURCE_DIR}/opencv-4.5.0/modules/stitching/include) include_directories(${PROJECT_SOURCE_DIR}/opencv-4.5.0/modules/video/include) # 设置不显示命令框 if(MSVC) #set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup") endif() # 添加库文件 set(PRO_OPENCV_LIB ${PROJECT_SOURCE_DIR}/lib/opencv_video450.lib ${PROJECT_SOURCE_DIR}/lib/opencv_core450.lib ${PROJECT_SOURCE_DIR}/lib/opencv_videoio450.lib ${PROJECT_SOURCE_DIR}/lib/opencv_calib3d450.lib ${PROJECT_SOURCE_DIR}/lib/opencv_dnn450.lib ${PROJECT_SOURCE_DIR}/lib/opencv_features2d450.lib ${PROJECT_SOURCE_DIR}/lib/opencv_flann450.lib ${PROJECT_SOURCE_DIR}/lib/opencv_highgui450.lib ${PROJECT_SOURCE_DIR}/lib/opencv_gapi450.lib ${PROJECT_SOURCE_DIR}/lib/opencv_imgcodecs450.lib ${PROJECT_SOURCE_DIR}/lib/opencv_imgproc450.lib ) set(PRO_DLIB_LIB ${PROJECT_SOURCE_DIR}/lib/dlib19.24.0_release_32bit_msvc1900.lib) # 生成可执行程序 ADD_EXECUTABLE(faceRecongize ${SRC_LISTS}) # 链接库文件 TARGET_LINK_LIBRARIES(faceRecongize ${PRO_OPENCV_LIB} ${PRO_DLIB_LIB})
到了这里,关于使用opencv和dlib库(C++代码)实现人脸活体检测(眨眼、张嘴、摇头检测)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!