dlib 人臉識別及表情分析
阿新 • • 發佈:2019-01-11
本文利用dlib庫進行人臉識別與特徵標定,並利用嘴巴的張開比例,眼睛的睜開程度,眉毛的傾斜程度作為表情分析的三個指標。方法較為簡單,識別的效率不是很高,可以在此基礎上進行改善。
識別規則:
1. 嘴巴張開距離佔面部識別框寬度的比例越大,說明情緒越激動,可能是非常開心,也可能是極度憤怒。
2. 眉毛上揚,17-21 或者 22-26 號特徵點距離面部識別框頂部與識別框高度的比值越小,說明眉毛上揚越厲害,可表示驚訝、開心。眉毛的傾斜角度,開心時眉毛一般是上揚,憤怒時皺眉,同時眉毛下壓的比較厲害。
3. 眯眼睛,人在開懷大笑的時候會不自覺的眯起眼睛,憤怒或者驚訝的時候會瞪大眼睛。
具體步驟:
首先開啟攝像頭,捕捉人臉。利用dlib中dlib.get_frontal_face_detector()識別人臉,利用dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")預測人臉的68點。之後綜合計算,作為每個表情的判斷指標。由於人離攝像頭距離的遠近,導致人臉識別狂的大小不一,故選擇比例作為判斷指標。指標的判定參考http://www.php.cn/python-tutorials-393748.html
程式碼實現:
# -*- coding: utf-8 -*- """ 從視訊中識別人臉,並實時標出面部特徵點,簡單判斷情緒 """ import dlib #人臉識別的庫dlib import numpy as np #資料處理的庫numpy import cv2 #影象處理的庫OpenCv class face_emotion(): #類 def __init__(self): #類在例項化成物件的時候首先呼叫的方法 # 使用特徵提取器get_frontal_face_detector self.detector = dlib.get_frontal_face_detector() # dlib的68點模型,使用訓練好的特徵預測器 self.predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat") #使用電腦自帶攝像頭。 self.cap = cv2.VideoCapture(0) # 設定視訊引數,propId設定的視訊引數,value設定的引數值 self.cap.set(3, 480) def learning_face(self): # 眉毛直線擬合數據緩衝 line_brow_x = [] line_brow_y = [] # cap.isOpened() 返回true/false 檢查初始化是否成功 while(self.cap.isOpened()): # cap.read() # 返回兩個值: # 一個布林值true/false,用來判斷讀取視訊是否成功/是否到視訊末尾 # 影象物件,影象的三維矩陣 flag, im_rd = self.cap.read() # 每幀資料延時1ms,延時為0讀取的是靜態幀 k = cv2.waitKey(1) # 取灰度 img_gray = cv2.cvtColor(im_rd, cv2.COLOR_RGB2GRAY) # 使用人臉檢測器檢測每一幀影象中的人臉。並返回人臉數rects rects = self.detector(img_gray, 0) # 要顯示在螢幕上的字型 font = cv2.FONT_HERSHEY_SIMPLEX # 如果檢測到人臉 if(len(rects)!=0): # 對每個人臉都標出68個特徵點 for i in range(len(rects)): # enumerate方法同時返回資料物件的索引和資料,k為索引,d為faces中的物件 for k, d in enumerate(rects): # 用紅色矩形框出人臉 cv2.rectangle(im_rd, (d.left(), d.top()), (d.right(), d.bottom()), (0, 0, 255)) # 計算人臉熱別框邊長 self.face_width = d.right() - d.left() self.face_higth = d.top() - d.bottom() # 使用預測器得到68點資料的座標 shape = self.predictor(im_rd, d) # 圓圈顯示每個特徵點 for i in range(68): cv2.circle(im_rd, (shape.part(i).x, shape.part(i).y), 2, (0, 255, 0), -1, 8) cv2.putText(im_rd, str(i), (shape.part(i).x, shape.part(i).y), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255)) # 分析點的位置關係來作為表情識別的依據 mouth_width = (shape.part(54).x - shape.part(48).x) / self.face_width # 嘴巴咧開程度 mouth_higth = (shape.part(66).y - shape.part(62).y) / self.face_width # 嘴巴張開程度 # 通過兩個眉毛上的10個特徵點,分析挑眉程度和皺眉程度 brow_sum = 0 # 高度之和 frown_sum = 0 # 兩邊眉毛距離之和 for j in range(17, 21): brow_sum += (shape.part(j).y - d.top()) + (shape.part(j + 5).y - d.top()) frown_sum += shape.part(j + 5).x - shape.part(j).x line_brow_x.append(shape.part(j).x) line_brow_y.append(shape.part(j).y) tempx = np.array(line_brow_x) tempy = np.array(line_brow_y) #np.ployfit(x,a,n)擬合點集a得到n級多項式,其中x為橫軸長度 z1 = np.polyfit(tempx, tempy, 1) # 擬合成一次直線 #round(x [,n])返回浮點數x的四捨五入值 round(80.23456, 2)返回80.23 self.brow_k = -round(z1[0], 3) # 擬合出曲線的斜率和實際眉毛的傾斜方向是相反的 brow_hight = (brow_sum / 10) / self.face_width # 眉毛高度佔比 brow_width = (frown_sum / 5) / self.face_width # 眉毛距離佔比 # print("眉毛高度與識別框高度之比:",round(brow_arv/self.face_width,3)) # print("眉毛間距與識別框高度之比:",round(frown_arv/self.face_width,3)) # 眼睛睜開程度 eye_sum = (shape.part(41).y - shape.part(37).y + shape.part(40).y - shape.part(38).y + shape.part(47).y - shape.part(43).y + shape.part(46).y - shape.part(44).y) eye_hight = (eye_sum / 4) / self.face_width # print("眼睛睜開距離與識別框高度之比:",round(eye_open/self.face_width,3)) # 分情況討論 # 張嘴,可能是開心或者驚訝 if round(mouth_higth >= 0.03): if eye_hight >= 0.056: cv2.putText(im_rd, "amazing", (d.left(), d.bottom() + 20), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 255), 2, 4) else: cv2.putText(im_rd, "happy", (d.left(), d.bottom() + 20), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 255), 2, 4) # 沒有張嘴,可能是正常和生氣 else: if self.brow_k <= -0.3: cv2.putText(im_rd, "angry", (d.left(), d.bottom() + 20), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 255), 2, 4) else: cv2.putText(im_rd, "nature", (d.left(), d.bottom() + 20), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 255), 2, 4) # 標出人臉數 cv2.putText(im_rd, "Faces: "+str(len(rects)), (20,50), font, 1, (0, 0, 255), 1, cv2.LINE_AA) else: # 沒有檢測到人臉 cv2.putText(im_rd, "No Face", (20, 50), font, 1, (0, 0, 255), 1, cv2.LINE_AA) # 新增說明 #im_rd = cv2.putText(im_rd, "S: screenshot", (20, 400), font, 0.8, (0, 0, 255), 1, cv2.LINE_AA) #im_rd = cv2.putText(im_rd, "Q: quit", (20, 450), font, 0.8, (0, 0, 255), 1, cv2.LINE_AA) # 按下ESC鍵退出 if cv2.waitKey(10) == 27: break # 視窗顯示 cv2.imshow("camera", im_rd) # 釋放攝像頭 self.cap.release() # 刪除建立的視窗 cv2.destroyAllWindows() if __name__ == "__main__": my_face = face_emotion() my_face.learning_face()
執行結果就是可以實時標定朝向攝像頭的人臉表情啦~