0. 引言
利用 Python 開發,借助 Dlib 庫捕獲攝像頭中的人臉,進行實時人臉 68 個特征點標定;
支持多張人臉;
有截圖功能;
圖 1 工程效果示例( gif )
圖 2 工程效果示例( 靜態圖片 )
1. 開發環境
Python: 3.6.3
Dlib: 19.7
OpenCv, NumPy
import dlib # 人臉檢測的庫 Dlib
import numpy as np # 數據處理的庫 NumPy
import cv2 # 圖像處理的庫 OpenCv
2. 源碼介紹
其實實現很簡單,主要分為兩個部分:攝像頭調用 + 人臉特征點標定
2.1 攝像頭調用
介紹下 OpenCv 中攝像頭的調用方法,用到以下幾個 OpenCv 函數:
cv2.VideoCapture(0) 創建一個對象;
cv2.VideoCapture.set(propId, value) 設置視頻參數;
cv2.VideoCapture.isOpened() 檢測讀取視頻是否成功;
cv2.VideoCapture.read() 返回是否讀取成功和讀取到的幀圖像;
(具體可以參考官方文檔:https://docs.opencv.org/2.4/modules/highgui/doc/reading_and_writing_images_and_video.html)
關於 cv2 中 VideoCapture 類的一些函數參數的說明如下:
# Sort by coneypo # Updated at 10, Oct # Blog: http://www.cnblogs.com/AdaminXie
###### 1. cv2.VideoCapture(), 創建cv2攝像頭對象 / Open the default camera ######
""" Python: cv2.VideoCapture() → <VideoCapture object> Python: cv2.VideoCapture(filename) → <VideoCapture object> filename – name of the opened video file (eg. video.avi) or image sequence (eg. img_%02d.jpg, which will read samples like img_00.jpg, img_01.jpg, img_02.jpg, ...) Python: cv2.VideoCapture(device) → <VideoCapture object> device – id of the opened video capturing device (i.e. a camera index). If there is a single camera connected, just pass 0. """ cap = cv2.VideoCapture(0) ##### 2. cv2.VideoCapture.set(propId, value),設置視頻參數; #####
""" propId: CV_CAP_PROP_POS_MSEC Current position of the video file in milliseconds. CV_CAP_PROP_POS_FRAMES 0-based index of the frame to be decoded/captured next. CV_CAP_PROP_POS_AVI_RATIO Relative position of the video file: 0 - start of the film, 1 - end of the film. CV_CAP_PROP_FRAME_WIDTH Width of the frames in the video stream. CV_CAP_PROP_FRAME_HEIGHT Height of the frames in the video stream. CV_CAP_PROP_FPS Frame rate. ... value: 設置的參數值/ Value of the property """ cap.set(3, 480) ##### 3. cv2.VideoCapture.isOpened(), 檢查攝像頭初始化是否成功, 返回true或false / check if open camera successfully #####
cap.isOpened() ##### 4. cv2.VideoCapture.read([imgage]) -> retval,image, 讀取視頻 / Grabs, decodes and returns the next video frame #####
""" 返回兩個值: 一個是布爾值true/false,用來判斷讀取視頻是否成功/是否到視頻末尾 圖像對象,圖像的三維矩陣 """ flag, im_rd = cap.read()
2.2 人臉特征點標定
調用預測器 “shape_predictor_68_face_landmarks.dat” 進行 68 點標定,這是 Dlib 訓練好的模型,可以直接調用,進行人臉 68 個人臉特征點的標定;
需要進行的后續工作是,利用坐標值進行特征點繪制;
具體可以參考我的另一篇博客(Python 3 利用 Dlib 19.7 實現人臉68個特征點的標定);
圖 3 Dlib 中人臉 68 個特征點位置說明
2.3 源碼
既然已經能夠利用訓練好的模型進行特征點檢測,需要進行的工作是將攝像頭捕獲到的視頻流,轉換為 OpenCv 的圖像對象,這樣才能進行人臉特征點檢測;
然后利用返回的特征點坐標值,繪制特征點,實時的再繪制到攝像頭界面上,達到實時人臉檢測追蹤的目的;
利用 cv2.VideoCapture() 創建攝像頭對象,然后利用 flag, im_rd = cv2.VideoCapture.read() 讀取攝像頭視頻,im_rd 就是視頻中的一幀幀圖像;
然后就類似於單張圖像進行人臉檢測,對這一幀幀的圖像 im_rd 利用 Dlib 進行特征點標定,然后繪制特征點;
可以按下 's' 鍵來獲取當前截圖,會存下帶有時間戳的本地圖像文件如 “screenshoot_1_2018-05-14-11-04-23.jpg”;
或者按下 'q' 鍵來退出攝像頭窗口;
有四個 python 文件:
how_to_use_camera.py OpenCv 調用攝像頭;
get_features_from_image.py 繪制 "data/face_to_detect/face_x.jpg" 本地圖像人臉文件的特征點;
get_features_from_camera.py 從攝像頭實時人臉檢測繪制特征點;
remove_ss.py: 刪除 "data/screenshots/" 路徑下的所有截圖;
( 源碼都上傳到了 GitHub: https://github.com/coneypo/Dlib_face_detection_from_camera )
這里只給出 get_features_from_camera.py 的源碼;
get_features_from_camera.py:
1 # 調用攝像頭,進行人臉捕獲,和 68 個特征點的追蹤 2 3 # Author: coneypo 4 # Blog: http://www.cnblogs.com/AdaminXie 5 # GitHub: https://github.com/coneypo/Dlib_face_detection_from_camera 6 7 # Created at 2018-02-26 8 # Updated at 2019-01-28 9 10 import dlib # 機器學習的庫 Dlib 11 import numpy as np # 數據處理的庫 numpy 12 import cv2 # 圖像處理的庫 OpenCv 13 import time 14 import timeit 15 import statistics 16 17 # 儲存截圖的目錄 18 path_screenshots = "data/screenshots/" 19 20 detector = dlib.get_frontal_face_detector() 21 predictor = dlib.shape_predictor('data/dlib/shape_predictor_68_face_landmarks.dat') 22 23 # 創建 cv2 攝像頭對象 24 cap = cv2.VideoCapture(0) 25 26 # cap.set(propId, value) 27 # 設置視頻參數,propId 設置的視頻參數,value 設置的參數值 28 cap.set(3, 480) 29 30 # 截圖 screenshots 的計數器 31 cnt = 0 32 33 time_cost_list = [] 34 35 # cap.isOpened() 返回 true/false 檢查初始化是否成功 36 while cap.isOpened(): 37 38 # cap.read() 39 # 返回兩個值: 40 # 一個布爾值 true/false,用來判斷讀取視頻是否成功/是否到視頻末尾 41 # 圖像對象,圖像的三維矩陣 42 flag, im_rd = cap.read() 43 44 # 每幀數據延時 1ms,延時為 0 讀取的是靜態幀 45 k = cv2.waitKey(1) 46 47 # 取灰度 48 img_gray = cv2.cvtColor(im_rd, cv2.COLOR_RGB2GRAY) 49 50 # start point 51 start = timeit.default_timer() 52 53 # 人臉數 54 faces = detector(img_gray, 0) 55 56 # print(len(faces)) 57 58 # 待會要寫的字體 59 font = cv2.FONT_HERSHEY_SIMPLEX 60 61 # 標 68 個點 62 if len(faces) != 0: 63 # 檢測到人臉 64 for i in range(len(faces)): 65 landmarks = np.matrix([[p.x, p.y] for p in predictor(im_rd, faces[i]).parts()]) 66 67 for idx, point in enumerate(landmarks): 68 # 68 點的坐標 69 pos = (point[0, 0], point[0, 1]) 70 71 # 利用 cv2.circle 給每個特征點畫一個圈,共 68 個 72 cv2.circle(im_rd, pos, 2, color=(139, 0, 0)) 73 74 # 利用 cv2.putText 輸出 1-68 75 cv2.putText(im_rd, str(idx + 1), pos, font, 0.2, (187, 255, 255), 1, cv2.LINE_AA) 76 77 cv2.putText(im_rd, "faces: " + str(len(faces)), (20, 50), font, 1, (0, 0, 0), 1, cv2.LINE_AA) 78 79 # end point 80 stop = timeit.default_timer() 81 time_cost_list.append(stop - start) 82 print("%-15s %f" % ("Time cost:", (stop - start))) 83 84 else: 85 # 沒有檢測到人臉 86 cv2.putText(im_rd, "no face", (20, 50), font, 1, (0, 0, 0), 1, cv2.LINE_AA) 87 88 89 # 添加說明 90 im_rd = cv2.putText(im_rd, "press 'S': screenshot", (20, 400), font, 0.8, (255, 255, 255), 1, cv2.LINE_AA) 91 im_rd = cv2.putText(im_rd, "press 'Q': quit", (20, 450), font, 0.8, (255, 255, 255), 1, cv2.LINE_AA) 92 93 # 按下 's' 鍵保存 94 if k == ord('s'): 95 cnt += 1 96 print(path_screenshots + "screenshot" + "_" + str(cnt) + "_" + time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime()) + ".jpg") 97 cv2.imwrite(path_screenshots + "screenshot" + "_" + str(cnt) + "_" + time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime()) + ".jpg", im_rd) 98 99 # 按下 'q' 鍵退出 100 if k == ord('q'): 101 break 102 103 # 窗口顯示 104 # 參數取 0 可以拖動縮放窗口,為 1 不可以 105 # cv2.namedWindow("camera", 0) 106 cv2.namedWindow("camera", 1) 107 108 cv2.imshow("camera", im_rd) 109 110 # 釋放攝像頭 111 cap.release() 112 113 # 刪除建立的窗口 114 cv2.destroyAllWindows() 115 116 print("%-15s" % "Result:") 117 print("%-15s %f" % ("Max time:", (max(time_cost_list)))) 118 print("%-15s %f" % ("Min time:", (min(time_cost_list)))) 119 print("%-15s %f" % ("Average time:", statistics.mean(time_cost_list)))
實時輸出處理時間:
...
Time cost: 0.065273 Time cost: 0.059348 Time cost: 0.093570 Time cost: 0.091448 Time cost: 0.084946 Time cost: 0.089457 Time cost: 0.084367 Time cost: 0.094103 Time cost: 0.096082 Time cost: 0.073331 Time cost: 0.073685 Time cost: 0.065583 Time cost: 0.061161 Time cost: 0.061650 Time cost: 0.060952 Time cost: 0.084485 Result: Max time: 0.112430 Min time: 0.057525 Average time: 0.085478
筆記本 CPU: i5-6200U@2.30GHz, Memory: 8G 處理時間平均在 0.08s;
# 請尊重他人勞動成果,轉載或者使用源碼請注明出處:http://www.cnblogs.com/AdaminXie
# 如果對您有幫助,歡迎在 GitHub 上 star 支持下: https://github.com/coneypo/Dlib_face_detection_from_camera
# 如有問題可以聯系本人郵箱,商業合作勿擾謝謝: coneypo@foxmail.com