【机器学习】基于 dlib 面部关键点的多表情分类 文章目录完整代码一览一、环境准备与模型文件二、核心原理三个关键指标1. EAR眼睛纵横比—— 判断睁眼/闭眼2. MAR嘴巴纵横比—— 判断嘴巴张开程度3. MJR嘴宽脸宽比—— 判断嘴巴拉宽程度三、逐行代码详解1. 导入库与定义指标函数MAR 函数MJR 函数辅助函数中文绘制 cv2AddChineseText绘制眼睛轮廓 drawEye3. 主程序结构4. 主循环提取关键点并计算指标表情判定逻辑绘制与显示完整代码一览import numpy as np import dlib import cv2 from sklearn.metrics.pairwise import euclidean_distances from PIL import Image,ImageDraw,ImageFont#MAR嘴巴纵横比defMAR(shape):Aeuclidean_distances(shape[50].reshape(1,2),shape[58].reshape(1,2))Beuclidean_distances(shape[51].reshape(1,2),shape[57].reshape(1,2))Ceuclidean_distances(shape[52].reshape(1,2),shape[56].reshape(1,2))Deuclidean_distances(shape[48].reshape(1,2),shape[54].reshape(1,2))return((ABC)/3)/D#EAR眼睛纵横比defeye_aspect_ratio(eye):Aeuclidean_distances(eye[1].reshape(1,2),eye[5].reshape(1,2))Beuclidean_distances(eye[2].reshape(1,2),eye[4].reshape(1,2))Ceuclidean_distances(eye[0].reshape(1,2),eye[3].reshape(1,2))ear((AB)/2.0)/Creturnear#MJR嘴宽/脸宽比值defMJR(shape):Meuclidean_distances(shape[48].reshape(1,2),shape[54].reshape(1,2))Jeuclidean_distances(shape[3].reshape(1,2),shape[13].reshape(1,2))returnM/J#OpenCV 中文绘制defcv2AddChineseText(img,text,position,textColor(255,0,0),textSize30):ifisinstance(img,np.ndarray):imgImage.fromarray(cv2.cvtColor(img,cv2.COLOR_BGR2RGB))drawImageDraw.Draw(img)fontImageFont.truetype(simhei.ttf,textSize,encodingutf-8)draw.text(position,text,textColor,fontfont)returncv2.cvtColor(np.array(img),cv2.COLOR_RGB2BGR)defdrawEye(eye):eyeHullcv2.convexHull(eye)cv2.drawContours(frame,[eyeHull],-1,(0,255,0),2)detectordlib.get_frontal_face_detector()predictordlib.shape_predictor(shape_predictor_68_face_landmarks.dat)capcv2.VideoCapture(0)COUNTER0whileTrue:ret,framecap.read()ifnot ret:breakfacesdetector(frame,0)forface in faces:shapepredictor(frame,face)shapenp.array([[p.x,p.y]forp in shape.parts()])rightEyeshape[36:42]leftEyeshape[42:48]rightEAReye_aspect_ratio(rightEye)leftEAReye_aspect_ratio(leftEye)ear(leftEARrightEAR)/2.0marMAR(shape)mjrMJR(shape)print(mar,mar,\tmjr,mjr)result正常ifmar0.5:result大笑elif mjr0.45:result微笑elif mar0.45and ear0.3:COUNTER1ifCOUNTER30:result哭elif ear0.35:result愤怒else:COUNTER0drawEye(leftEye)drawEye(rightEye)infoEAR: {:.2f}.format(ear[0][0])framecv2AddChineseText(frame,info,(0,30))framecv2AddChineseText(frame,result,(50,100))mouthHullcv2.convexHull(shape[48:61])cv2.drawContours(frame,[mouthHull],-1,(0,255,0),1)cv2.imshow(Frame,frame)ifcv2.waitKey(1)27:breakcap.release()cv2.destroyAllWindows()一、环境准备与模型文件在运行代码之前请确保已安装以下库pip install dlib opencv-python scikit-learn Pillow并下载 dlib 的 68 点关键点模型文件下载地址解压后得到 shape_predictor_68_face_landmarks.dat放在脚本同一目录。中文绘制需要字体文件本代码使用 simhei.ttf黑体你可以从 Windows 系统字体文件夹C:\Windows\Fonts复制或使用其他中文字体如 msyh.ttc并修改代码中的字体名称。二、核心原理三个关键指标1. EAR眼睛纵横比—— 判断睁眼/闭眼我们在上一篇文章中已经详细介绍过它基于眼睛 6 个点睁眼时 EAR ≈ 0.25~0.3闭眼时接近 0。用于区分“愤怒”眼睛睁圆EAR 较大和“哭”眼睛闭合EAR 很小。2. MAR嘴巴纵横比—— 判断嘴巴张开程度MAR 是 Mouth Aspect Ratio 的缩写基于嘴巴 6 个点内唇上下和宽度嘴巴张开大笑、惊讶时MAR 明显增大 0.5。嘴巴闭合时MAR 较小 0.4。3. MJR嘴宽脸宽比—— 判断嘴巴拉宽程度MJR 是 Mouth-to-Jaw Ratio即嘴巴宽度与下颌宽度脸部宽度的比值微笑时嘴巴会向两侧拉宽MJR 增大 0.45。正常表情时MJR 较小。通过这三个指标的组合我们可以区分多种表情。三、逐行代码详解1. 导入库与定义指标函数import numpy as np import dlib import cv2 from sklearn.metrics.pairwise import euclidean_distances #提供欧氏距离计算 from PIL import Image,ImageDraw,ImageFont #中文绘制MAR 函数defMAR(shape):Aeuclidean_distances(shape[50].reshape(1,2),shape[58].reshape(1,2))Beuclidean_distances(shape[51].reshape(1,2),shape[57].reshape(1,2))Ceuclidean_distances(shape[52].reshape(1,2),shape[56].reshape(1,2))Deuclidean_distances(shape[48].reshape(1,2),shape[54].reshape(1,2))return((ABC)/3)/Dshape 是 68 个点的坐标数组索引 48~60 是嘴巴区域。50/58、51/57、52/56 是三对垂直方向上的内唇点取平均得到嘴巴高度48 和 54 是嘴角两端代表嘴巴宽度。MAR 平均高度 / 宽度张嘴时增大。MJR 函数defMJR(shape):Meuclidean_distances(shape[48].reshape(1,2),shape[54].reshape(1,2))# 嘴宽 Jeuclidean_distances(shape[3].reshape(1,2),shape[13].reshape(1,2))# 下颌宽度returnM/Jshape[48] 和 shape[54] 是嘴角其距离即嘴宽。shape[3] 和 shape[13] 是下巴轮廓两端的点相当于脸部宽度。微笑时嘴巴变宽比值增大。辅助函数中文绘制 cv2AddChineseTextdefcv2AddChineseText(img,text,position,textColor(255,0,0),textSize30):ifisinstance(img,np.ndarray):imgImage.fromarray(cv2.cvtColor(img,cv2.COLOR_BGR2RGB))drawImageDraw.Draw(img)fontImageFont.truetype(simhei.ttf,textSize,encodingutf-8)draw.text(position,text,textColor,fontfont)returncv2.cvtColor(np.array(img),cv2.COLOR_RGB2BGR)将 OpenCV 图像转为 PIL 图像绘制文字后再转回 OpenCV BGR 格式。绘制眼睛轮廓 drawEyedefdrawEye(eye):eyeHullcv2.convexHull(eye)cv2.drawContours(frame,[eyeHull],-1,(0,255,0),2)3. 主程序结构detectordlib.get_frontal_face_detector()predictordlib.shape_predictor(shape_predictor_68_face_landmarks.dat)capcv2.VideoCapture(0)COUNTER0初始化检测器、预测器、摄像头COUNTER 用于连续闭眼帧计数。4. 主循环whileTrue:ret,framecap.read()ifnot ret:breakfacesdetector(frame,0)逐帧读取检测人脸。提取关键点并计算指标forface in faces:shapepredictor(frame,face)shapenp.array([[p.x,p.y]forp in shape.parts()])rightEyeshape[36:42]leftEyeshape[42:48]rightEAReye_aspect_ratio(rightEye)leftEAReye_aspect_ratio(leftEye)ear(leftEARrightEAR)/2.0marMAR(shape)mjrMJR(shape)print(mar,mar,\tmjr,mjr)获取 68 点截取左右眼和嘴巴计算三个指标。print 输出到控制台便于调试。表情判定逻辑result正常ifmar0.5:result大笑elif mjr0.45:result微笑elif mar0.45and ear0.3:COUNTER1ifCOUNTER30:result哭elif ear0.35:result愤怒else:COUNTER0采用级联判断如果 MAR 0.5判定为“大笑”嘴巴张得很大。否则如果 MJR 0.45判定为“微笑”嘴巴拉宽但未张很大。否则如果 MAR 0.45 且 EAR 0.3嘴巴张开且眼睛闭合可能是“哭”需要连续 30 帧确认与疲劳检测类似。否则如果 EAR 0.35眼睛睁得很圆判定为“愤怒”。否则为“正常”。绘制与显示drawEye(leftEye)drawEye(rightEye)infoEAR: {:.2f}.format(ear[0][0])framecv2AddChineseText(frame,info,(0,30))framecv2AddChineseText(frame,result,(50,100))mouthHullcv2.convexHull(shape[48:61])cv2.drawContours(frame,[mouthHull],-1,(0,255,0),1)绘制眼睛和嘴巴的凸包轮廓绿色显示 EAR 值和表情结果。最后显示画面按 ESC 退出。

本周精选

本月热点