Python 3 利用 Dlib 實現攝像頭實時人臉識別


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 捕獲到的一組人臉

 

get_faces_from_camera.py 源碼:

 

  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 定制顯示名字

 

face_reco_from_camera.py 源碼:

  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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM