0. 引言
利用 Python 開發,借助 Dlib 庫捕獲攝像頭中的人臉,提取人臉特征,通過計算特征值之間的歐氏距離,來和預存的人臉特征進行對比,判斷是否匹配,達到人臉識別的目的;
可以從攝像頭中摳取人臉圖片存儲到本地,然后提取構建預設人臉特征;
根據摳取的 / 已有的同一個人多張人臉圖片提取 128D 特征值,然后計算該人的 128D 特征均值;
然后和攝像頭中實時獲取到的人臉提取出的特征值,計算歐氏距離,判定是否為同一張人臉;
Python + OpenCV + Dlib ;
識別模型:基於 Dlib 的 ResNet 預訓練模型(dlib_face_recognition_resnet_model_v1.dat)
識別算法:ResNet 神經網絡(This model is a ResNet network with 29 conv layers. It's essentially a version of the ResNet-34 network from the paper Deep Residual Learning for Image Recognition by He, Zhang, Ren, and Sun with a few layers removed and the number of filters per layer reduced by half)
1. 人臉檢測
faces = detector(img_gray, 0) -> <class 'dlib.dlib.rectangles'> ->
2. 計算特征點
shape = predictor(img_rd, faces[i]) -> <class 'dlib.dlib.full_object_detection'> ->
3. 特征描述子
facerec.compute_face_descriptor(img_rd, shape) -> <class 'dlib.dlib.vector'>
博客中代碼以 GitHub 為准,博客中可能沒有及時更新;
# Blog : http://www.cnblogs.com/AdaminXie
# GitHub : https://github.com/coneypo/Dlib_face_recognition_from_camera
Features :
- 支持人臉數據采集,自行建立人臉數據庫 / Support face register
- 調用攝像頭實時人臉檢測和識別 / Using camera to real-time detect and recognize faces
- 支持多張人臉 / Support multi-faces
人臉識別 / Face Recognition 的說明:
Wikipedia 上關於人臉識別系統 / Face Recognition System 的描述:they work by comparing selected facial features from given image with faces within a database.
本項目中就是比較 預設的人臉的特征 和 攝像頭實時獲取到的人臉的特征 ;
核心就是 提取 128D 人臉特征,然后計算 攝像頭人臉特征 和 預設的特征臉的歐式距離,進行比對;
效果如下:

圖 1 攝像頭多個人臉時識別效果
1. 總體流程
先說下 人臉檢測 ( Face detection ) 和 人臉識別 ( Face Recognition ) ,前者是達到檢測出場景中人臉的目的就可以了,而后者不僅需要檢測出人臉,還要和已有人臉數據進行比對,識別出是否在數據庫中,或者進行身份標注之類處理,人臉檢測和人臉識別兩者有時候可能會被理解混淆;
我的之前一些項目都是用 Dlib 做人臉檢測這塊,這個項目想要實現的功能是人臉識別功能,借助的是 Dlib 官網中 face_recognition.py 這個例程 ( Link:http://dlib.net/face_recognition.py.html );
我們直接利用“dlib_face_recognition_resnet_model_v1.dat” 這個 pre-trained model,提取人臉圖像的 128D 特征,然后比對不同人臉圖片的 128D 特征的歐式距離,設定一個 Threshold / 閾值 來判斷是否為同一張臉;
1 # face recognition model, the object maps human faces into 128D vectors
2 facerec = dlib.face_recognition_model_v1("dlib_face_recognition_resnet_model_v1.dat") 3
4 shape = predictor(img, dets[0]) 5 face_descriptor = facerec.compute_face_descriptor(img, shape)
圖 2 總體設計流程
2.源碼介紹
主要有
- get_faces_from_camera.py
- features_extraction_to_csv.py
- face_reco_from_camera.py
這三個 Python 文件,接下來會分別介紹實現功能;
2.1 get_faces_from_camera.py / 人臉注冊錄入
人臉識別需要將 提取到的圖像數據 和 已有圖像數據 進行比對分析,所以這部分代碼實現的功能就是 人臉錄入;
程序會生成一個窗口,顯示調用的攝像頭實時獲取的圖像;
(關於攝像頭的調用方式可以參考這里: Python 3 利用 Dlib 19.7 實現攝像頭人臉檢測特征點標定);
然后根據鍵盤輸入進行人臉捕獲:
- “N” 新錄入人臉,新建文件夾 person_X/ 用來存儲某人的人臉圖像
- "S" 開始捕獲人臉,將捕獲到的人臉放到 person_X/ 路徑下
- “Q” 退出窗口
攝像頭的調用是利用 opencv 庫的 cv2.VideoCapture(0), 此處參數為 0 代表調用的是筆記本的默認攝像頭,你也可以讓它調用傳入已有視頻文件;
可以參考 https://github.com/coneypo/Dlib_face_recognition_from_camera/blob/master/how_to_use_camera.py 如何通過 OpenCV 調用攝像頭;
圖 3 get_face_from_camera.py 的界面
“N”+“S”之后捕獲到的一組人臉示例;
圖 4 捕獲到的一組人臉
1 # 進行人臉錄入 / face register 2 # 錄入多張人臉 / support multi-faces 3 4 # Author: coneypo 5 # Blog: http://www.cnblogs.com/AdaminXie 6 # GitHub: https://github.com/coneypo/Dlib_face_recognition_from_camera 7 # Mail: coneypo@foxmail.com 8 9 # Created at 2018-05-11 10 # Updated at 2020-04-02 11 12 import dlib # 人臉處理的庫 Dlib 13 import numpy as np # 數據處理的庫 Numpy 14 import cv2 # 圖像處理的庫 OpenCv 15 import os # 讀寫文件 16 import shutil # 讀寫文件 17 18 # Dlib 正向人臉檢測器 / frontal face detector 19 detector = dlib.get_frontal_face_detector() 20 21 # OpenCv 調用攝像頭 / Use camera 22 cap = cv2.VideoCapture(0) 23 24 # 人臉截圖的計數器 / The counter for screen shoot 25 cnt_ss = 0 26 27 # 存儲人臉的文件夾 / The folder to save face images 28 current_face_dir = "" 29 30 # 保存 faces images 的路徑 / The directory to save images of faces 31 path_photos_from_camera = "data/data_faces_from_camera/" 32 33 34 # 1. 新建保存人臉圖像文件和數據CSV文件夾 35 # 1. Mkdir for saving photos and csv 36 def pre_work_mkdir(): 37 38 # 新建文件夾 / make folders to save faces images and csv 39 if os.path.isdir(path_photos_from_camera): 40 pass 41 else: 42 os.mkdir(path_photos_from_camera) 43 44 45 pre_work_mkdir() 46 47 48 ##### optional/可選, 默認關閉 ##### 49 # 2. 刪除之前存的人臉數據文件夾 50 # 2. Delete the old data of faces 51 def pre_work_del_old_face_folders(): 52 # 刪除之前存的人臉數據文件夾 53 # 刪除 "/data_faces_from_camera/person_x/"... 54 folders_rd = os.listdir(path_photos_from_camera) 55 for i in range(len(folders_rd)): 56 shutil.rmtree(path_photos_from_camera+folders_rd[i]) 57 58 if os.path.isfile("data/features_all.csv"): 59 os.remove("data/features_all.csv") 60 61 # 這里在每次程序錄入之前, 刪掉之前存的人臉數據 62 # 如果這里打開,每次進行人臉錄入的時候都會刪掉之前的人臉圖像文件夾 person_1/,person_2/,person_3/... 63 # If enable this function, it will delete all the old data in dir person_1/,person_2/,/person_3/... 64 # pre_work_del_old_face_folders() 65 ################################## 66 67 68 # 3. Check people order: person_cnt 69 # 如果有之前錄入的人臉 / If the old folders exists 70 # 在之前 person_x 的序號按照 person_x+1 開始錄入 / Start from person_x+1 71 if os.listdir("data/data_faces_from_camera/"): 72 # 獲取已錄入的最后一個人臉序號 / Get the num of latest person 73 person_list = os.listdir("data/data_faces_from_camera/") 74 person_num_list = [] 75 for person in person_list: 76 person_num_list.append(int(person.split('_')[-1])) 77 person_cnt = max(person_num_list) 78 79 # 如果第一次存儲或者沒有之前錄入的人臉, 按照 person_1 開始錄入 80 # Start from person_1 81 else: 82 person_cnt = 0 83 84 # 之后用來控制是否保存圖像的 flag / The flag to control if save 85 save_flag = 1 86 87 # 之后用來檢查是否先按 'n' 再按 's' / The flag to check if press 'n' before 's' 88 press_n_flag = 0 89 90 while cap.isOpened(): 91 flag, img_rd = cap.read() 92 # print(img_rd.shape) 93 # It should be 480 height * 640 width in Windows and Ubuntu by default 94 # Maybe 1280x720 in macOS 95 96 kk = cv2.waitKey(1) 97 98 # 人臉 / Faces 99 faces = detector(img_rd, 0) 100 101 # 待會要寫的字體 / Font to write 102 font = cv2.FONT_ITALIC 103 104 # 4. 按下 'n' 新建存儲人臉的文件夾 / press 'n' to create the folders for saving faces 105 if kk == ord('n'): 106 person_cnt += 1 107 current_face_dir = path_photos_from_camera + "person_" + str(person_cnt) 108 os.makedirs(current_face_dir) 109 print('\n') 110 print("新建的人臉文件夾 / Create folders: ", current_face_dir) 111 112 cnt_ss = 0 # 將人臉計數器清零 / clear the cnt of faces 113 press_n_flag = 1 # 已經按下 'n' / have pressed 'n' 114 115 # 檢測到人臉 / Face detected 116 if len(faces) != 0: 117 # 矩形框 / Show the rectangle box of face 118 for k, d in enumerate(faces): 119 # 計算矩形大小 120 # Compute the width and height of the box 121 # (x,y), (寬度width, 高度height) 122 pos_start = tuple([d.left(), d.top()]) 123 pos_end = tuple([d.right(), d.bottom()]) 124 125 # 計算矩形框大小 / compute the size of rectangle box 126 height = (d.bottom() - d.top()) 127 width = (d.right() - d.left()) 128 129 hh = int(height/2) 130 ww = int(width/2) 131 132 # 設置顏色 / the color of rectangle of faces detected 133 color_rectangle = (255, 255, 255) 134 135 # 判斷人臉矩形框是否超出 480x640 136 if (d.right()+ww) > 640 or (d.bottom()+hh > 480) or (d.left()-ww < 0) or (d.top()-hh < 0): 137 cv2.putText(img_rd, "OUT OF RANGE", (20, 300), font, 0.8, (0, 0, 255), 1, cv2.LINE_AA) 138 color_rectangle = (0, 0, 255) 139 save_flag = 0 140 if kk == ord('s'): 141 print("請調整位置 / Please adjust your position") 142 else: 143 color_rectangle = (255, 255, 255) 144 save_flag = 1 145 146 cv2.rectangle(img_rd, 147 tuple([d.left() - ww, d.top() - hh]), 148 tuple([d.right() + ww, d.bottom() + hh]), 149 color_rectangle, 2) 150 151 # 根據人臉大小生成空的圖像 / Create blank image according to the shape of face detected 152 img_blank = np.zeros((int(height*2), width*2, 3), np.uint8) 153 154 if save_flag: 155 # 5. 按下 's' 保存攝像頭中的人臉到本地 / Press 's' to save faces into local images 156 if kk == ord('s'): 157 # 檢查有沒有先按'n'新建文件夾 / check if you have pressed 'n' 158 if press_n_flag: 159 cnt_ss += 1 160 for ii in range(height*2): 161 for jj in range(width*2): 162 img_blank[ii][jj] = img_rd[d.top()-hh + ii][d.left()-ww + jj] 163 cv2.imwrite(current_face_dir + "/img_face_" + str(cnt_ss) + ".jpg", img_blank) 164 print("寫入本地 / Save into:", str(current_face_dir) + "/img_face_" + str(cnt_ss) + ".jpg") 165 else: 166 print("請在按 'S' 之前先按 'N' 來建文件夾 / Please press 'N' before 'S'") 167 168 # 顯示人臉數 / Show the numbers of faces detected 169 cv2.putText(img_rd, "Faces: " + str(len(faces)), (20, 100), font, 0.8, (0, 255, 0), 1, cv2.LINE_AA) 170 171 # 添加說明 / Add some statements 172 cv2.putText(img_rd, "Face Register", (20, 40), font, 1, (0, 0, 0), 1, cv2.LINE_AA) 173 cv2.putText(img_rd, "N: Create face folder", (20, 350), font, 0.8, (0, 0, 0), 1, cv2.LINE_AA) 174 cv2.putText(img_rd, "S: Save current face", (20, 400), font, 0.8, (0, 0, 0), 1, cv2.LINE_AA) 175 cv2.putText(img_rd, "Q: Quit", (20, 450), font, 0.8, (0, 0, 0), 1, cv2.LINE_AA) 176 177 # 6. 按下 'q' 鍵退出 / Press 'q' to exit 178 if kk == ord('q'): 179 break 180 181 # 如果需要攝像頭窗口大小可調 / Uncomment this line if you want the camera window is resizeable 182 # cv2.namedWindow("camera", 0) 183 184 cv2.imshow("camera", img_rd) 185 186 # 釋放攝像頭 / Release camera and destroy all windows 187 cap.release() 188 cv2.destroyAllWindows()
考慮到有可能需要保存的矩形框超出攝像頭范圍,對於這種異常,如果矩形框超出范圍,矩形框會從白變紅,然后提示 "OUT OF RANGE";
圖 5 人臉錄入異常(Out of range)處理
get_face_from_camera.py 的輸出 log
新建的人臉文件夾 / Create folders: data/data_faces_from_camera/person_1 寫入本地 / Save into: data/data_faces_from_camera/person_1/img_face_1.jpg 寫入本地 / Save into: data/data_faces_from_camera/person_1/img_face_2.jpg 寫入本地 / Save into: data/data_faces_from_camera/person_1/img_face_3.jpg 寫入本地 / Save into: data/data_faces_from_camera/person_1/img_face_4.jpg 新建的人臉文件夾 / Create folders: data/data_faces_from_camera/person_2 寫入本地 / Save into: data/data_faces_from_camera/person_2/img_face_1.jpg 寫入本地 / Save into: data/data_faces_from_camera/person_2/img_face_2.jpg 新建的人臉文件夾 / Create folders: data/data_faces_from_camera/person_3 寫入本地 / Save into: data/data_faces_from_camera/person_3/img_face_1.jpg 寫入本地 / Save into: data/data_faces_from_camera/person_3/img_face_2.jpg
2.2 features_extraction_to_csv.py / 將圖像文件中人臉數據提取出來存入 CSV
這部分代碼實現的功能是將之前捕獲到的人臉圖像文件,提取出 128D 特征,然后計算出某人人臉數據的特征均值存入 CSV 中,方便之后識別時候進行比對;
利用 numpy.mean() 計算特征均值,生成一個存儲所有錄入人臉數據 database 的 "features_all.csv";
features_extraction_to_csv.py 源碼:
1 # 從人臉圖像文件中提取人臉特征存入 CSV 2 # Features extraction from images and save into features_all.csv 3 4 # Author: coneypo 5 # Blog: http://www.cnblogs.com/AdaminXie 6 # GitHub: https://github.com/coneypo/Dlib_face_recognition_from_camera 7 # Mail: coneypo@foxmail.com 8 9 # Created at 2018-05-11 10 # Updated at 2020-04-02 11 12 import cv2 13 import os 14 import dlib 15 from skimage import io 16 import csv 17 import numpy as np 18 19 # 要讀取人臉圖像文件的路徑 20 path_images_from_camera = "data/data_faces_from_camera/" 21 22 # Dlib 正向人臉檢測器 23 detector = dlib.get_frontal_face_detector() 24 25 # Dlib 人臉預測器 26 predictor = dlib.shape_predictor("data/data_dlib/shape_predictor_68_face_landmarks.dat") 27 28 # Dlib 人臉識別模型 29 # Face recognition model, the object maps human faces into 128D vectors 30 face_rec = dlib.face_recognition_model_v1("data/data_dlib/dlib_face_recognition_resnet_model_v1.dat") 31 32 33 # 返回單張圖像的 128D 特征 34 def return_128d_features(path_img): 35 img_rd = io.imread(path_img) 36 faces = detector(img_rd, 1) 37 38 print("%-40s %-20s" % ("檢測到人臉的圖像 / image with faces detected:", path_img), '\n') 39 40 # 因為有可能截下來的人臉再去檢測,檢測不出來人臉了 41 # 所以要確保是 檢測到人臉的人臉圖像 拿去算特征 42 if len(faces) != 0: 43 shape = predictor(img_rd, faces[0]) 44 face_descriptor = face_rec.compute_face_descriptor(img_rd, shape) 45 else: 46 face_descriptor = 0 47 print("no face") 48 49 return face_descriptor 50 51 52 # 將文件夾中照片特征提取出來, 寫入 CSV 53 def return_features_mean_personX(path_faces_personX): 54 features_list_personX = [] 55 photos_list = os.listdir(path_faces_personX) 56 if photos_list: 57 for i in range(len(photos_list)): 58 # 調用return_128d_features()得到128d特征 59 print("%-40s %-20s" % ("正在讀的人臉圖像 / image to read:", path_faces_personX + "/" + photos_list[i])) 60 features_128d = return_128d_features(path_faces_personX + "/" + photos_list[i]) 61 # print(features_128d) 62 # 遇到沒有檢測出人臉的圖片跳過 63 if features_128d == 0: 64 i += 1 65 else: 66 features_list_personX.append(features_128d) 67 else: 68 print("文件夾內圖像文件為空 / Warning: No images in " + path_faces_personX + '/', '\n') 69 70 # 計算 128D 特征的均值 71 # personX 的 N 張圖像 x 128D -> 1 x 128D 72 if features_list_personX: 73 features_mean_personX = np.array(features_list_personX).mean(axis=0) 74 else: 75 features_mean_personX = '0' 76 77 return features_mean_personX 78 79 80 # 獲取已錄入的最后一個人臉序號 / get the num of latest person 81 person_list = os.listdir("data/data_faces_from_camera/") 82 person_num_list = [] 83 for person in person_list: 84 person_num_list.append(int(person.split('_')[-1])) 85 person_cnt = max(person_num_list) 86 87 with open("data/features_all.csv", "w", newline="") as csvfile: 88 writer = csv.writer(csvfile) 89 for person in range(person_cnt): 90 # Get the mean/average features of face/personX, it will be a list with a length of 128D 91 print(path_images_from_camera + "person_"+str(person+1)) 92 features_mean_personX = return_features_mean_personX(path_images_from_camera + "person_"+str(person+1)) 93 writer.writerow(features_mean_personX) 94 print("特征均值 / The mean of features:", list(features_mean_personX)) 95 print('\n') 96 print("所有錄入人臉數據存入 / Save all the features of faces registered into: data/features_all.csv")
我們可以看下對於某張圖片,face_descriptor 這個 128D vectors 的輸出結果:
綠色框內是我們的返回 128D 特征的函數;
在紅色框內調用該函數來計算 img_face_13.jpg;
可以看到黃色框中的輸出為 128D 的向量;
圖 6 返回單張圖像的 128D 特征的計算結果
之后就需要人臉圖像進行批量化操作,提取出 128D 的特征,然后計算特征均值,存入 features_all.csv;
features_all.csv 是一個 n 行 128 列的 CSV, n 是錄入的人臉數,128 列是某人的 128D 特征;
這存儲的就是 錄入的人臉數據,之后 攝像頭捕獲的人臉 將要拿過來和 這些特征值 進行比對,如果歐式距離比較近的話,就可以認為是同一張人臉;
get_features_into_CSV.py 的輸出 log:
##### person_1 ##### data/data_csvs_from_camera/person_1.csv 正在讀的人臉圖像 / image to read: data/data_faces_from_camera/person_1/img_face_1.jpg 檢測到人臉的圖像 / image with faces detected: data/data_faces_from_camera/person_1/img_face_1.jpg 正在讀的人臉圖像 / image to read: data/data_faces_from_camera/person_1/img_face_2.jpg 檢測到人臉的圖像 / image with faces detected: data/data_faces_from_camera/person_1/img_face_2.jpg 正在讀的人臉圖像 / image to read: data/data_faces_from_camera/person_1/img_face_3.jpg 檢測到人臉的圖像 / image with faces detected: data/data_faces_from_camera/person_1/img_face_3.jpg 正在讀的人臉圖像 / image to read: data/data_faces_from_camera/person_1/img_face_4.jpg 檢測到人臉的圖像 / image with faces detected: data/data_faces_from_camera/person_1/img_face_4.jpg ##### person_2 ##### data/data_csvs_from_camera/person_2.csv 正在讀的人臉圖像 / image to read: data/data_faces_from_camera/person_2/img_face_1.jpg 檢測到人臉的圖像 / image with faces detected: data/data_faces_from_camera/person_2/img_face_1.jpg 正在讀的人臉圖像 / image to read: data/data_faces_from_camera/person_2/img_face_2.jpg 檢測到人臉的圖像 / image with faces detected: data/data_faces_from_camera/person_2/img_face_2.jpg ##### person_3 ##### data/data_csvs_from_camera/person_3.csv 正在讀的人臉圖像 / image to read: data/data_faces_from_camera/person_3/img_face_1.jpg 檢測到人臉的圖像 / image with faces detected: data/data_faces_from_camera/person_3/img_face_1.jpg 正在讀的人臉圖像 / image to read: data/data_faces_from_camera/person_3/img_face_2.jpg 檢測到人臉的圖像 / image with faces detected: data/data_faces_from_camera/person_3/img_face_2.jpg ...
2.3 face_reco_from_camera.py / 實時人臉識別對比分析
這部分源碼實現的功能:調用攝像頭,捕獲攝像頭中的人臉,然后如果檢測到人臉,將 攝像頭中的人臉提取出 128D 的特征,然后和 之前錄入人臉的 128D 特征 進行計算歐式距離,如果比較小,可以判定為一個人,否則不是一個人;
偽代碼如下:
# 人臉檢測器/預測器/識別模型 detector = dlib.get_frontal_face_detector() predictor = dlib.shape_predictor('data/data_dlib/shape_predictor_68_face_landmarks.dat') facerec = dlib.face_recognition_model_v1("data/data_dlib/dlib_face_recognition_resnet_model_v1.dat") faces = detector(img_gray, 0) # 如果檢測到人臉 if len(faces) != 0: # 遍歷所有檢測到的人臉 for i in range(len(faces)): # 進行人臉比對 shape = predictor(img_rd, faces[i]) facerec.compute_face_descriptor(img_rd, shape)
關於用到的 dlib 檢測器,預測器,識別器:
1. dlib.get_frontal_face_detector
Link:
http://dlib.net/python/index.html#dlib.get_frontal_face_detector
簡介 / intro:
返回默認的人臉檢測器,為下面的 fhog_object_detectorm / Returns the default face detector
2. class dlib.fhog_object_detector
Link:
http://dlib.net/python/index.html#dlib.fhog_object_detector
簡介 / intro:
基於滑動窗的HOG進行目標檢測;
This object represents a sliding window histogram-of-oriented-gradients based object detector.
參數 / parameters:
__call__(self: dlib.fhog_object_detector, image: array, upsample_num_times: int=0L) → dlib.rectangles
3. class dlib.shape_predictor
Link:
http://dlib.net/python/index.html#dlib.shape_predictor
簡介 / intro:
人臉圖像作為輸入, 輸出面部特征點;
This object is a tool that takes in an image region containing some object and outputs a set of point locations that define the pose of the object.
The classic example of this is human face pose prediction, where you take an image of a human face as input and are expected to identify
the locations of important facial landmarks such as the corners of the mouth and eyes, tip of the nose, and so forth.
參數 / parameters:
__call__(self: dlib.shape_predictor, image: array, box: dlib.rectangle) → dlib.full_object_detection
輸入: dlib.rectangle 輸出: dlib.full_object_detection
4. class dlib.face_recognition_model_v1
Link:
http://dlib.net/python/index.html#dlib.face_recognition_model_v1
簡介 / intro:
將人臉轉換為128D特征向量, 這樣的話相似人臉會比較相近, 不相像的會比較遠;
This object maps human faces into 128D vectors where pictures of the same person are mapped near to each other and pictures of different people are mapped far apart.
The constructor loads the face recognition model from a file. The model file is available here: http://dlib.net/files/dlib_face_recognition_resnet_model_v1.dat.bz2
參數 / parameters:
compute_face_descriptor(self: dlib.face_recognition_model_v1, img: numpy.ndarray[(rows,cols,3),uint8], face: dlib.full_object_detection, num_jitters: int=0L, padding: float=0.25) -> dlib.vector
通過 print(type()) 可以更清楚的看到 dlib 對象的傳遞:
# 人臉檢測器/預測器/識別模型 detector = dlib.get_frontal_face_detector() predictor = dlib.shape_predictor('data/data_dlib/shape_predictor_68_face_landmarks.dat') facerec = dlib.face_recognition_model_v1("data/data_dlib/dlib_face_recognition_resnet_model_v1.dat") faces = detector(img_gray, 0) # 如果檢測到人臉 if len(faces) != 0: print(type(faces) # <class 'dlib.dlib.rectangles'> # 遍歷所有檢測到的人臉 for i in range(len(faces)): # 進行人臉比對 shape = predictor(img_rd, faces[i]) print(type(shape)) # <class 'dlib.dlib.full_object_detection'> facerec.compute_face_descriptor(img_rd, shape) print(type(facerec.compute_face_descriptor(img_rd, shape)) # <class 'dlib.dlib.vector'>
這樣一個對象傳遞過程:
faces = detector(img_gray, 0) -> <class 'dlib.dlib.rectangles'> -> shape = predictor(img_rd, faces[i]) -> <class 'dlib.dlib.full_object_detection'> -> facerec.compute_face_descriptor(img_rd, shape) -> <class 'dlib.dlib.vector'>
歐氏距離對比的閾值設定,是在 return_euclidean_distance 函數的 dist 變量;
我這里程序里面指定的 歐氏距離判斷閾值是 0.4,具體閾值可以根據實際情況或者測得結果進行修改;
這邊做了一個,讓人名跟隨顯示在頭像下方,如果想要在人臉矩形框下方顯示人名,首先需要知道 Dlib 生成的矩形框的尺寸怎么讀取;
Dlib 返回的 dets 變量是一系列人臉的數據,此處對單張人臉處理,所以取 dets[0] 的參數;
可以通過 dets[0].top(), dets[0].bottom(), dets[0].left() 和 dets[0].right() 來確定要顯示的人名的坐標;
圖 7 dets[0].top() 等參數說明
得到矩形框的坐標,就可以獲取人名的相對位置;
這是我這邊取的坐標:
pos_text_1 = tuple([dets[0].left(), int(dets[0].bottom()+(dets[0].bottom()-dets[0].top())/4)])
圖 8 face_reco_from_camera.py 生成的人臉識別窗口界面
如果想定制輸出顯示的名字而不是“Person_1”,"Person_2"...;

圖 9 定制顯示名字
1 # 攝像頭實時人臉識別 2 # Real-time face recognition 3 4 # Author: coneypo 5 # Blog: http://www.cnblogs.com/AdaminXie 6 # GitHub: https://github.com/coneypo/Dlib_face_recognition_from_camera 7 8 # Created at 2018-05-11 9 # Updated at 2020-04-02 10 11 import dlib # 人臉處理的庫 Dlib 12 import numpy as np # 數據處理的庫 numpy 13 import cv2 # 圖像處理的庫 OpenCv 14 import pandas as pd # 數據處理的庫 Pandas 15 import os 16 17 # 人臉識別模型,提取128D的特征矢量 18 # face recognition model, the object maps human faces into 128D vectors 19 # Refer this tutorial: http://dlib.net/python/index.html#dlib.face_recognition_model_v1 20 facerec = dlib.face_recognition_model_v1("data/data_dlib/dlib_face_recognition_resnet_model_v1.dat") 21 22 23 # 計算兩個128D向量間的歐式距離 24 # Compute the e-distance between two 128D features 25 def return_euclidean_distance(feature_1, feature_2): 26 feature_1 = np.array(feature_1) 27 feature_2 = np.array(feature_2) 28 dist = np.sqrt(np.sum(np.square(feature_1 - feature_2))) 29 return dist 30 31 32 # 1. Check 存放所有人臉特征的 csv 33 if os.path.exists("data/features_all.csv"): 34 path_features_known_csv = "data/features_all.csv" 35 csv_rd = pd.read_csv(path_features_known_csv, header=None) 36 37 # 用來存放所有錄入人臉特征的數組 38 # The array to save the features of faces in the database 39 features_known_arr = [] 40 41 # 2. 讀取已知人臉數據 42 # Print known faces 43 for i in range(csv_rd.shape[0]): 44 features_someone_arr = [] 45 for j in range(0, len(csv_rd.iloc[i])): 46 features_someone_arr.append(csv_rd.iloc[i][j]) 47 features_known_arr.append(features_someone_arr) 48 print("Faces in Database:", len(features_known_arr)) 49 50 # Dlib 檢測器和預測器 51 # The detector and predictor will be used 52 detector = dlib.get_frontal_face_detector() 53 predictor = dlib.shape_predictor('data/data_dlib/shape_predictor_68_face_landmarks.dat') 54 55 # 創建 cv2 攝像頭對象 56 cap = cv2.VideoCapture(0) 57 58 # 3. When the camera is open 59 while cap.isOpened(): 60 61 flag, img_rd = cap.read() 62 faces = detector(img_rd, 0) 63 64 # 待會要寫的字體 font to write later 65 font = cv2.FONT_ITALIC 66 67 # 存儲當前攝像頭中捕獲到的所有人臉的坐標/名字 68 # The list to save the positions and names of current faces captured 69 pos_namelist = [] 70 name_namelist = [] 71 72 kk = cv2.waitKey(1) 73 74 # 按下 q 鍵退出 75 # press 'q' to exit 76 if kk == ord('q'): 77 break 78 else: 79 # 檢測到人臉 when face detected 80 if len(faces) != 0: 81 # 4. 獲取當前捕獲到的圖像的所有人臉的特征,存儲到 features_cap_arr 82 # 4. Get the features captured and save into features_cap_arr 83 features_cap_arr = [] 84 for i in range(len(faces)): 85 shape = predictor(img_rd, faces[i]) 86 features_cap_arr.append(facerec.compute_face_descriptor(img_rd, shape)) 87 88 # 5. 遍歷捕獲到的圖像中所有的人臉 89 # 5. Traversal all the faces in the database 90 for k in range(len(faces)): 91 print("##### camera person", k+1, "#####") 92 # 讓人名跟隨在矩形框的下方 93 # 確定人名的位置坐標 94 # 先默認所有人不認識,是 unknown 95 # Set the default names of faces with "unknown" 96 name_namelist.append("unknown") 97 98 # 每個捕獲人臉的名字坐標 the positions of faces captured 99 pos_namelist.append(tuple([faces[k].left(), int(faces[k].bottom() + (faces[k].bottom() - faces[k].top())/4)])) 100 101 # 對於某張人臉,遍歷所有存儲的人臉特征 102 # For every faces detected, compare the faces in the database 103 e_distance_list = [] 104 for i in range(len(features_known_arr)): 105 # 如果 person_X 數據不為空 106 if str(features_known_arr[i][0]) != '0.0': 107 print("with person", str(i + 1), "the e distance: ", end='') 108 e_distance_tmp = return_euclidean_distance(features_cap_arr[k], features_known_arr[i]) 109 print(e_distance_tmp) 110 e_distance_list.append(e_distance_tmp) 111 else: 112 # 空數據 person_X 113 e_distance_list.append(999999999) 114 # Find the one with minimum e distance 115 similar_person_num = e_distance_list.index(min(e_distance_list)) 116 print("Minimum e distance with person", int(similar_person_num)+1) 117 118 if min(e_distance_list) < 0.4: 119 ####### 在這里修改 person_1, person_2 ... 的名字 ######## 120 # 可以在這里改稱 Jack, Tom and others 121 # Here you can modify the names shown on the camera 122 name_namelist[k] = "Person "+str(int(similar_person_num)+1) 123 print("May be person "+str(int(similar_person_num)+1)) 124 else: 125 print("Unknown person") 126 127 # 矩形框 128 # draw rectangle 129 for kk, d in enumerate(faces): 130 # 繪制矩形框 131 cv2.rectangle(img_rd, tuple([d.left(), d.top()]), tuple([d.right(), d.bottom()]), (0, 255, 255), 2) 132 print('\n') 133 134 # 6. 在人臉框下面寫人臉名字 135 # 6. write names under rectangle 136 for i in range(len(faces)): 137 cv2.putText(img_rd, name_namelist[i], pos_namelist[i], font, 0.8, (0, 255, 255), 1, cv2.LINE_AA) 138 139 print("Faces in camera now:", name_namelist, "\n") 140 141 cv2.putText(img_rd, "Press 'q': Quit", (20, 450), font, 0.8, (84, 255, 159), 1, cv2.LINE_AA) 142 cv2.putText(img_rd, "Face Recognition", (20, 40), font, 1, (0, 0, 0), 1, cv2.LINE_AA) 143 cv2.putText(img_rd, "Faces: " + str(len(faces)), (20, 100), font, 1, (0, 0, 255), 1, cv2.LINE_AA) 144 145 cv2.imshow("camera", img_rd) 146 147 cap.release() 148 cv2.destroyAllWindows() 149 150 else: 151 print('##### Warning #####', '\n') 152 print("'features_all.py' not found!") 153 print("Please run 'get_faces_from_camera.py' and 'features_extraction_to_csv.py' before 'face_reco_from_camera.py'", '\n') 154 print('##### Warning #####')
face_reco_from_camera.py 輸出 log:
##### camera person 1 ##### with person 1 the e distance: 0.21153867687451736 with person 2 the e distance: 0.20646924127167549 with person 4 the e distance: 0.19824469336759548 Minimum e distance with person 4 May be person 4 ##### camera person 2 ##### with person 1 the e distance: 0.7403020289640347 with person 2 the e distance: 0.7375521667680703 with person 4 the e distance: 0.7077921161820342 Minimum e distance with person 4 Unknown person ##### camera person 3 ##### with person 1 the e distance: 0.6975665799095466 with person 2 the e distance: 0.7070867672498581 with person 4 the e distance: 0.6727276688350984 Minimum e distance with person 4 Unknown person Faces in camera now: ['Person 4', 'unknown', 'unknown']
如果對單個人臉,進行實時對比輸出:
圖 10 實時輸出的歐氏距離結果
通過實時的輸出結果,看的比較明顯;
輸出綠色部分:當是我自己時,計算出來的歐式距離基本都在 0.2 左右;
輸出紅色部分:而換一張圖片上去比如特朗普,明顯看到歐式距離計算結果 達到了 0.8,此時就可以判定,后來這張人臉不是一張人臉;
所以之前提到的歐式距離計算對比的閾值可以由此設定,本項目中取的是 dist=0.4;
dist 的確切取值自己權衡,http://dlib.net/face_recognition.py.html 的說明:
# When using a distance threshold of 0.6, the dlib model obtains an accuracy
# of 99.38% on the standard LFW face recognition benchmark, which is # comparable to other state-of-the-art methods for face recognition as of # February 2017. This accuracy means that, when presented with a pair of face # images, the tool will correctly identify if the pair belongs to the same # person or is from different people 99.38% of the time.
3. 總結
核心就是 提取人臉特征,然后計算歐式距離和預設的特征臉進行比對;
不過這個實時獲取攝像頭人臉進行比對,要實時的進行計算攝像頭臉的特征值,然后還要計算歐氏距離,所以計算量比較大,可能攝像頭視頻流會出現卡頓;
此項目僅個人學習愛好研究,開源供大家一起學習;
# 請尊重他人勞動成果,轉載或者使用源碼請注明出處:http://www.cnblogs.com/AdaminXie
# 代碼已上傳到了我的 GitHub,如果對您有幫助歡迎 Star 支持我下:https://github.com/coneypo/Dlib_face_recognition_from_camera
# 如有問題請留言或者聯系郵箱: coneypo@foxmail.com
# Last update: 6 Apr