採用face_recognition包做人臉檢測
阿新 • • 發佈:2018-12-12
face_recognitiaon安裝要基於dlib,安裝dlib一定上官網去查對應的python安裝的版本。通過cmd中pip install XXX。
本程式碼實現了功能:
考勤,即通過資料庫的照片訓練k近鄰分類器,然後測試未知樣本。
程式碼不足:關於編碼解碼的問題沒有解決,圖片中對應中文人名是亂碼。希望集思廣益,或者自己後期完善~(程式碼位置150~151處)
# 採用face_recognition包檢測人臉去識別 ################################# # 1-引用模組 ################################# import math from sklearn import neighbors import os import os.path import pickle from PIL import Image,ImageDraw import face_recognition #安裝見https://blog.csdn.net/wyx100/article/details/78556402 import face_recognition as fr import sys #用於除錯時,使用。 from face_recognition.face_recognition_cli import image_files_in_folder #資料夾中的影象 from sklearn.neighbors import KNeighborsClassifier #匯入k近鄰分類器 import sys ################################# # 2-訓練模型 ################################# #1.建立一個數據集x:128維度加上y一個維度,總共是129維度。 # 2.對每一個照片操作 # 3.決定n # 4.訓練出分類器 # 5.儲存分類器 def train(train_dir,model_save_path='trained_knn_model.clf',n_neighbors=3,knn_algo='ball_tree'): ''' 功能:訓練一個KNN分類器 :param train_dir: 訓練目錄。其下對每個已知的人,分別以其名字,建立一個資料夾 :param model_save_path:模型儲存的位置 :param n_neighbors:鄰居數預設為3 :param knn_algo: 支援KNN的資料結構;ball_tree是一種樹型結構。 :return: KNN分類器 ''' #生成訓練集 x=[] #注意x最終是18維。 y=[] #遍歷訓練集中的每一個人 for class_dir in os.listdir(train_dir): #http://www.runoob.com/python/os-listdir.html if not os.path.isdir(os.path.join(train_dir,class_dir)): #判斷是否是目錄 continue #結束當前迴圈,進入下一個迴圈。 #遍歷這個人的每一張照片 for image_path in image_files_in_folder(os.path.join(train_dir,class_dir)): image=fr.load_image_file(image_path) #傳入人臉的位置 boxes=fr.face_locations(image) #定出人臉位置 #對於當前圖片,增加編碼到訓練集合 x.append(fr.face_encodings(image,known_face_locations=boxes)[0]) #編碼返回時一個128維度的向量。 y.append(class_dir) #決定n if n_neighbors is None: n_neighbors=3 #訓練出分類器 knn_clf=KNeighborsClassifier(n_neighbors=n_neighbors) knn_clf.fit(x,y) #儲存分類器 if model_save_path is not None: with open(model_save_path,'wb') as f:#wb,b二進位制 pickle.dump(knn_clf,f) #模型儲存 return knn_clf # 預測 # prediction=predict(full_file_path,model_path='trained_knn_model.clf') def predict(x_img_path,knn_clf=None,model_path=None,distance_threshold=0.35): ''' 利用KNN分類器識別給定的照片中的人臉 :param x_imag_path:必須對應照片的地址而不是照片的資料夾 :param knn_clf: :param distance_threshold: :return: [(人名1,邊界盒子1),...] ''' if knn_clf is None and model_path is None: raise Exception('必須提供KNN分類器:可選方式為knn_clf或model_path') #載入訓練好的KNN模型(如果有) #rb表示要讀入的二進位制資料 print('除錯斷點0') # sys.exit(0) if knn_clf is None: print(model_path) #trained_knn_model.clf with open(model_path,'rb') as f: #https://www.cnblogs.com/tianyiliang/p/8192703.html knn_clf=pickle.load(f) print(f) print('除錯斷點1','*'*20) # sys.exit(1) #載入圖片,發現人臉的位置 print(x_img_path) # sys.exit(2) x_img=fr.load_image_file(x_img_path) #https://blog.csdn.net/MG_ApinG/article/details/82252954 # x_img=fr.load_image_file(x_img_path) #Permission denied沒有許可權,原因是load_image_file需要開啟檔案的地址而不是資料夾的地址。 print('除錯斷點2','*'*20) # sys.exit(2) x_face_location=fr.face_locations(x_img) print(x_face_location) print('除錯斷點3','*'*20) # sys.exit(3) #對測試圖片中的人臉進行編碼 encodings=fr.face_encodings(x_img) #http://www.360doc.com/content/18/0403/18/48868863_742603302.shtml print(encodings) print(len(encodings[0])) #128個數據組成的向量 print('除錯斷點4','*'*20) # sys.exit(4) x_face_locations=fr.face_locations(x_img) print('除錯斷點5','*'*20) print(x_face_locations) # sys.exit(5) #利用KNN模型,找出與測試人員最匹配的人臉 #encodings:128個人臉特徵的向量 closest_distace=knn_clf.kneighbors(encodings,n_neighbors=3) print('除錯斷點6','*'*20) print(closest_distace) #(array([[0.34381298, 0.35287966, 0.35839984]]), array([[3, 2, 7]], dtype=int64)) # sys.exit(6) are_matches=[closest_distace[0][i][0]<=distance_threshold for i in range(len(x_face_locations))] #匿名函式, are_matches即是否匹配上 print('除錯斷點7','*'*20) print(are_matches) # sys.exit(7) #預測類別,並remove classifications that aren't within the threshold即移除不在閥值內的分類 print(knn_clf.predict(encodings)) print(list(x_face_locations)) print(list(are_matches)) print(list(zip(knn_clf.predict(encodings),x_face_locations,are_matches))) #pred 預測值,loc頭像位置 return [(pred,loc) if rec else ('unknown',loc) for pred,loc,rec in zip(knn_clf.predict(encodings),x_face_locations,are_matches)] #zip 壓縮http://www.runoob.com/python/python-func-zip.html #結果視覺化 def show_names_on_image(img_path,predictions): ''' 人臉識別視覺化 :param img_path: 待識別圖片的位置 :param predictions: 預測的結果 :return: ''' pil_image=Image.open(img_path).convert('RGB') #將圖片轉換成格式 #https://blog.csdn.net/icamera0/article/details/50843172 draw=ImageDraw.Draw(pil_image) #ImageDraw類支援各種幾何圖形的繪製和文字的繪製https://blog.csdn.net/guduruyu/article/details/71213717 for name,(top,right,bottom,left) in predictions: #用pillow模組畫圖人臉邊界盒子 draw.rectangle( ((left,top),(right,bottom)),outline=(255,0,255)) #pillow 裡可能生成UTF-8格式,所以這裡做如下轉換 #這裡有draw不能解碼出name字型的問題。 name=name.encode('utf-8') name=name.decode('ISO-8859-1') print('要列印的name是',type(name)) #在人臉下寫下名字,作為標籤 # sys.exit(1) text_width,text_height=draw.textsize(name) draw.rectangle(((left,bottom-text_height-10),(right,bottom)),fill=(0,0,0),outline=(0,0,0)) draw.text((left,bottom-text_height-10),name,(255,0,255)) #遍加名字到li_names li_names.append(name) #從記憶體刪除draw del draw #顯示結果圖 pil_image.show() ####################### #統計分析 ####################### #為了列印名字的集合 li_names=[] #計算總人數 def count(train_dir): ''' counts the total number of the set :param train_dir: :return: ''' path=train_dir count=0 for fn in os.listdir(path): #fn表示的是檔名 count=count+1 return count #獲取所有名字的列表 def list_all(train_dir): ''' determine the list of all names :param train_dir: :return: ''' path=train_dir result=[] for fn in os.listdir(path):#fn表示的是資料夾名 result.append(fn) return result #輸出結果 def stat_output(): s_list=set(li_names) s_list_all=set(list_all('examples/train')) if 'unknown'in s_list: s_list.remove('unknown') print('查閱',s_list) tot_num=count('examples/train') s_absent=set(s_list_all-s_list) #未到人數 print('\n') print('***********************\n') print('全體名單',s_list_all) print('已到名單',s_list) print('應到人數',tot_num) print('已到人數',len(s_list)) print('出勤率:{:.2f}'.format(float(len(s_list))/float(tot_num))) print('未到:',s_absent) ####################### #執行 ####################### if __name__ == '__main__': #主函式 # sys.exit() #1. 訓練KNN分類器(它可以儲存,以便再使用) #如果有模型時,就不要進行訓練了。 # print('正在訓練KNN分類器') # classifier=train('examples/train',model_save_path='trained_knn_model.clf',n_neighbors=3) # print('完成費時間的KNN分類器訓練,訓練結束') # 2 利用訓練好的分類器,對新圖片進行預測 for image_file in os.listdir('examples/test'): #對測試資料夾 for picture_flie in os.listdir('examples/test/{}'.format(image_file)): full_file_path=os.path.join('examples/test/{}'.format(image_file),picture_flie) #圖片對應的完整的位置,image_file對應圖片對應的資料夾 print( 'full_file_path:',full_file_path) print(picture_flie) # sys.exit(0) print('正在在 {} 中尋找人臉ing'.format(image_file)) # sys.exit(0) # 利用分類器,找出所有的人臉 # 要麼傳遞一個classifier檔名,要麼一個classifer模型例項 prediction=predict(full_file_path,model_path='trained_knn_model.clf') print(prediction) print('完成{}一次{}人臉預測'.format(image_file,picture_flie),'+'*50) # sys.exit(1) #列印結果 for name,(top,right,bottom,left) in prediction: print('發現人臉 :{}; 人臉位置:({},{},{},{})'.format(name,top,right,bottom,left)) # sys.exit() # # 在圖片上顯示預測結果 show_names_on_image(os.path.join('examples/test/{}'.format(image_file),picture_flie),prediction) # sys.exit() #3.輸出統計結果 stat_output()
執行結果:
所提及的影象編碼亂碼:如下
英文的則不會出問題,如下: