前言
- 本文章使用opencv和dlib庫,使用C++代碼實(shí)現(xiàn)了人臉活體檢測,包括眨眼檢測、張嘴檢測以及搖頭檢測,可以對靜態(tài)圖片和活體進(jìn)行有效區(qū)分。
效果展示
Dlib庫介紹
-
dlib是一個(gè)開源的C++機(jī)器學(xué)習(xí)庫,它提供了一系列用于圖像處理、人臉檢測、人臉識(shí)別、物體檢測、圖像標(biāo)注等功能的算法和工具。dlib庫使用了現(xiàn)代C++和模板元編程的技術(shù),并且具有高度的可移植性和可擴(kuò)展性。
-
dlib庫的主要特點(diǎn)包括:文章來源:http://www.zghlxwxcb.cn/news/detail-851513.html
- 提供了一系列高效的機(jī)器學(xué)習(xí)算法,如支持向量機(jī)、最大間隔分類器、隨機(jī)森林等。
- 提供了用于圖像處理的算法,如圖像濾波、圖像變換、邊緣檢測等。
- 提供了用于人臉檢測和人臉識(shí)別的算法,具有較高的準(zhǔn)確性和性能。
- 提供了用于物體檢測和跟蹤的算法,如HOG特征和級(jí)聯(lián)分類器。
- 提供了用于圖像標(biāo)注和圖像分割的算法,如條件隨機(jī)場。
- 支持多線程和并行計(jì)算,可以充分利用多核處理器的性能。
- 具有豐富的文檔和示例代碼,易于學(xué)習(xí)和使用。
-
dlib庫在計(jì)算機(jī)視覺、模式識(shí)別和機(jī)器學(xué)習(xí)領(lǐng)域被廣泛應(yīng)用,是一個(gè)功能強(qiáng)大且易用的機(jī)器學(xué)習(xí)庫。對外提供了C++和python兩種接口。文章來源地址http://www.zghlxwxcb.cn/news/detail-851513.html
OpenCV庫介紹
- OpenCV是一個(gè)基于BSD許可發(fā)行的跨平臺(tái)計(jì)算機(jī)視覺庫。OpenCV作為較大眾的開源庫,擁有了豐富的常用圖像處理函數(shù)庫,采用C/C++語言編寫,可以運(yùn)行在Linux/Windows/Mac等操作系統(tǒng)上,能夠快速的實(shí)現(xiàn)一些圖像處理和識(shí)別的任務(wù)。
- 本項(xiàng)目主要使用OpenCV來打開攝像頭,讀取視頻幀。
人臉識(shí)別
- Dlib庫中有一個(gè)人臉特征點(diǎn)模型 shape_predictor_68_face_landmarks.dat,記錄了人臉的68個(gè)特征點(diǎn)。可以通過這個(gè)模型識(shí)別人臉特征從而檢測是否檢測到人臉。
- 人臉特征點(diǎn)
眨眼檢測原理
- 通過計(jì)算人眼縱橫比(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∥?
- 分子計(jì)算的是眼睛的特征點(diǎn)在垂直方向上的距離,分母計(jì)算的是眼睛的特征點(diǎn)在水平方向上的距離,由于水平特征點(diǎn)只有一組,所以乘以2保證兩組特征點(diǎn)的權(quán)重相同。
- 眼睛閉合時(shí),p2和p6、p3和p5之間的距離會(huì)減小,而p1和p4之間的距離基本保持不變。因此通過計(jì)算人眼縱橫比EAR,可以判斷眼睛的睜開和閉合狀態(tài)。
張嘴檢測原理
- 張嘴檢測的原理與眨眼檢測一樣,嘴部的特征點(diǎn)較多,可以自行選取六個(gè)特征點(diǎn),計(jì)算嘴部的縱橫比MAR
- 我這里選取的六個(gè)特征點(diǎn)是 49,55、51,59、53,57
- 按照相同的方法,計(jì)算人嘴縱橫比 MAR
搖頭檢測
- 搖頭時(shí),鼻子中心點(diǎn)到左右臉頰的距離會(huì)發(fā)生變化,我們可以通過計(jì)算該距離變化,判斷是否進(jìn)行了搖頭。
- 比如通過計(jì)算鼻子中心點(diǎn)特征點(diǎn)31到左臉頰特征點(diǎn)2和右臉頰特征點(diǎn)16的距離變化,判斷搖頭動(dòng)作。具體參考源代碼。
Dlib和OpenCV庫編譯
- 編譯前需要安裝Visual Studio 2015和CMake。
- Dlib庫編譯
- Dlib庫下載地址??梢赃x擇這個(gè)包下載 dlib-19.24.zip,同時(shí)需要下載這個(gè)人臉關(guān)鍵點(diǎn)檢測器 shape_predictor_68_face_landmarks.dat。
- 源碼下載后,需要進(jìn)行編譯。
- 先在Dlib源碼工程下 dlib/config.h文件末尾添加下面這一句
-
#define DLIB__CMAKE_GENERATED_A_CONFIG_H_FILE
-
- 然后在Dlib源碼工程下,創(chuàng)建build_x86文件夾,進(jìn)入該文件夾,執(zhí)行命令
-
cmake -G "Visual Studio 14 2015" .. cmake --build ./ --config Release
-
- 編譯完成后,會(huì)在build_x86\dlib\Release下生成靜態(tài)庫文件 dlib19.24.0_release_32bit_msvc1900.lib
- OpenCV庫編譯
- openCV庫下載地址。選擇一個(gè)版本的源碼(Sources)進(jìn)行下載,我下載的是4.5.0
- 源碼下載后,需要進(jìn)行編譯。在OpenCV源碼工程下,創(chuàng)建build_x86文件夾,進(jìn)入該文件夾,執(zhí)行命令
-
cmake -G "Visual Studio 14 2015" .. cmake --build ./ --config Release
-
- 編譯完成后,會(huì)在build_x86/lib/Release下生成靜態(tài)庫文件,會(huì)在build_x86/bin/Release下生成動(dòng)態(tài)庫文件。
創(chuàng)建工程
- 先創(chuàng)建一個(gè)facec目錄
- 在facec目錄下創(chuàng)建一個(gè)dlib-19.24目錄,將dlib源碼中的dlib目錄拷貝到該目錄下
- 在facec目錄下創(chuàng)建一個(gè)opencv-4.5.0目錄,將opecv源碼中的include和modules目錄拷貝到該目錄下
- 在facec目錄下創(chuàng)建一個(gè)lib目錄,將編譯好的dlib靜態(tài)庫和opencv靜態(tài)庫拷貝到該目錄下。
- 在facec目錄下創(chuàng)建一個(gè)src目錄,src目錄下為main.cpp源代碼。
- 在facec目錄下創(chuàng)建一個(gè)CMakeLists.txt文件。
- 目錄結(jié)構(gòu)如下
-
facec ├── dlib-19.24 │ └── dlib ├── opencv-4.5.0 │ ├── include │ └── modules ├── lib ├── src │ └── main.cpp └──CMakeLists.txt
編譯工程
- 在facec目錄創(chuàng)建build_x86目錄,進(jìn)入該目錄執(zhí)行
-
cmake -G "Visual Studio 14 2015" .. cmake --build ./ --config Release
- 會(huì)在 bin\Release 下生成可執(zhí)行程序,然后將opencv相關(guān)動(dòng)態(tài)庫和人臉關(guān)鍵點(diǎn)檢測器shape_predictor_68_face_landmarks.dat拷貝過來,雙擊可執(zhí)行程序就可以運(yùn)行了。
相關(guān)源代碼
- 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])); //計(jì)算人眼縱橫比 //左眼 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); //計(jì)算人嘴縱橫比 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)); //記錄張嘴次數(shù) if (mouthEVR < 0.62){ //閉嘴 counter_mouth += 1; }else if(mouthEVR > 0.70){ //張嘴 if (counter_mouth >= 1) { mouthIndex += 1; } counter_mouth = 0; } else { //此區(qū)間處于臨界區(qū),不穩(wěn)定,不做檢測 } // 顯示張嘴次數(shù) 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); //記錄左眼眨眼次數(shù) if (leyeEVR > 2.9) { //閉眼 counter_leye += 1; } else { //睜眼 if (counter_leye >= 1) { leyeIndex += 1; } counter_leye = 0; } //記錄右眼眨眼次數(shù) if (reyeEVR > 5.0) { //閉眼 counter_reye += 1; } else { //睜眼 if (counter_reye >= 1) { reyeIndex += 1; } counter_reye = 0; } //取最小值 if (reyeIndex > leyeIndex) { reyeIndex = leyeIndex; } //顯示眨眼次數(shù) 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文件內(nèi)容
-
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) # 設(shè)置不顯示命令框 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) # 生成可執(zhí)行程序 ADD_EXECUTABLE(faceRecongize ${SRC_LISTS}) # 鏈接庫文件 TARGET_LINK_LIBRARIES(faceRecongize ${PRO_OPENCV_LIB} ${PRO_DLIB_LIB})
到了這里,關(guān)于使用opencv和dlib庫(C++代碼)實(shí)現(xiàn)人臉活體檢測(眨眼、張嘴、搖頭檢測)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!