一、簡介
????????在計算機視覺領域,表情識別是一個既有趣又具有挑戰性的任務。它在人機交互、情感分析、安防監控等眾多領域都有著廣泛的應用前景。本文將詳細介紹如何使用 Python 中的 OpenCV 庫和 Dlib 庫來實現一個簡單的實時表情識別系統。
二、實現原理
? ? ? ?表情識別系統主要基于面部關鍵點的檢測與分析。Dlib 庫提供了強大的面部關鍵點檢測器,能夠準確地定位出面部的 68 個關鍵點,這些關鍵點涵蓋了眼睛、眉毛、鼻子、嘴巴等重要面部特征部位。通過計算這些關鍵點之間的距離比例,我們可以提取出能夠表征不同表情的特征指標。
三、具體代碼實現
import cv2
import numpy as np
import dlib
from sklearn.metrics.pairwise import euclidean_distances
from PIL import Image, ImageDraw, ImageFont
上述代碼導入了實現表情識別所需的各種庫。OpenCV 用于圖像處理和視頻捕獲;Numpy 用于數值計算;Dlib 用于面部檢測和關鍵點定位;scikit - learn 的?euclidean_distances
?函數用于計算歐幾里得距離,以幫助我們計算面部關鍵點之間的距離;Pillow 庫則用于在圖像上添加中文文本。
def MAR(shape):A = euclidean_distances(shape[50].reshape(1,2), shape[58].reshape(1,2))B = euclidean_distances(shape[51].reshape(1, 2), shape[57].reshape(1, 2))C = euclidean_distances(shape[52].reshape(1, 2), shape[56].reshape(1, 2))D = euclidean_distances(shape[48].reshape(1, 2), shape[54].reshape(1, 2))return ((A + B + C) / 3) / D
在MAR函數中,通過選取嘴巴周圍的關鍵點(如嘴角、嘴唇中部等),計算它們之間的歐幾里得距離,然后根據特定的公式計算出嘴巴長寬比。這個比值越大,通常表示嘴巴張開得越大,越有可能是大笑表情。
def MJR(shape):M = euclidean_distances(shape[48].reshape(1,2), shape[54].reshape(1,2))J = euclidean_distances(shape[3].reshape(1, 2), shape[13].reshape(1, 2))return M/J
MJR函數則計算了嘴巴寬度與臉部寬度的比值。當這個比值超過一定閾值時,我們認為面部表情為微笑。這里通過比較嘴巴寬度和臉部特定寬度(兩眼外角之間的寬度近似代表臉部寬度),來捕捉微笑時嘴巴相對臉部的變化特征。
def cv2AddChineseText(img, text, position, textColor = (255, 0, 0), textSize =50):if (isinstance(img,np.ndarray)):img = Image.fromarray(cv2.cvtColor(img,cv2.COLOR_BGR2RGB))draw = ImageDraw.Draw(img)fontStyle = ImageFont.truetype("simsun.ttc", textSize, encoding="utf-8")draw.text(position, text, textColor, font=fontStyle)return cv2.cvtColor(np.asarray(img), cv2.COLOR_RGB2BGR)
函數首先將 OpenCV 的 BGR 圖像轉換為 RGB 格式,然后使用 Pillow 庫的?ImageDraw?和?ImageFont?在圖像上指定位置繪制中文文本,最后再將圖像轉換回 BGR 格式,以便后續在 OpenCV 中使用。下面是主程序部分
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")
cap = cv2.VideoCapture(0)
這里使用 Dlib 的?get_frontal_face_detector?函數初始化面部檢測器,加載預訓練的 68 個面部關鍵點模型?shape_predictor_68_face_landmarks.dat?用于關鍵點預測,并通過?cv2.VideoCapture(0)?打開電腦默認攝像頭。用
while True:ret,frame = cap.read()faces = detector(frame, 0)for face in faces:shape = predictor(frame, face)shape = np.array([[p.x, p.y] for p in shape.parts()])mar = MAR(shape)mjr = MJR(shape)result = "正常"print("mar",mar,"\tmjr",mjr)if mar > 0.5:result = "大笑"elif mjr > 0.45:result = "微笑"mouthHULL = cv2.convexHull(shape[48:61])frame = cv2AddChineseText(frame, result,mouthHULL[0,0])cv2.drawContours(frame, [mouthHULL], -1, (0, 255, 0), 1)cv2.imshow("Frame",frame)if cv2.waitKey(1) == 27:break
cv2.destroyAllWindows()
cap.release()
?
在循環中,每讀取一幀視頻圖像,首先使用面部檢測器檢測圖像中的所有面部。對于每個檢測到的面部,通過關鍵點預測器獲取面部關鍵點坐標,并將其轉換為 Numpy 數組形式以便后續計算。接著計算?MAR?和?MJR?值,并根據預設的閾值判斷表情類別,將識別結果存儲在?result?變量中。為了可視化效果,我們使用?cv2.convexHull?函數計算嘴巴區域的凸包,并使用?cv2.drawContours?函數將其繪制在圖像上。同時,調用?cv2AddChineseText?函數在嘴巴凸包的起始位置添加識別出的表情文本。最后,通過?cv2.imshow?函數顯示處理后的圖像。當用戶按下 Esc 鍵(鍵值為 27)時,循環結束,關閉所有窗口并釋放攝像頭資源。
運行結果
四、總結
通過上述步驟,我們成功構建了一個簡單的實時表情識別系統,能夠識別出大笑和微笑兩種表情。然而,當前系統還存在一些局限性。例如,僅通過兩個簡單的特征指標來判斷表情,可能無法準確識別更復雜多樣的表情,如憤怒、悲傷、驚訝等。并且,閾值的設定是基于經驗,可能在不同環境和個體上表現不穩定。
?