实时嘴部表情检测系统:从微笑到哭泣的智能识别
一、项目背景与意义
在计算机视觉领域中,人脸表情识别(Facial Expression Recognition, FER)是一项重要的研究方向。传统的表情识别通常依赖于整张人脸的特征提取,但在很多场景下,仅仅通过嘴部的形变就能准确反映一个人的情绪变化。例如:
-
驾驶员监控系统中,嘴角上扬代表心情放松,嘴角下垂可能表示疲劳或情绪低落;
-
课堂学习状态分析中,微笑往往代表专注或兴趣;
-
智能客服系统中,用户的嘴型可以作为辅助情绪输入。
本文基于 dlib
的 68 点人脸特征定位模型 ,结合 OpenCV
实现实时嘴部表情检测。我们将实现以下功能:
-
检测人脸;
-
获取嘴部关键点;
-
计算嘴部形态参数(MAR 与 MJR);
-
识别"正常"、"微笑"、"大笑";
-
拓展实现"哭/悲伤"的检测。
二、算法核心原理
本项目的核心思想是通过计算嘴部的几何比例参数来判断表情。
(1)68 点特征模型
shape_predictor_68_face_landmarks.dat
模型由 dlib 提供,能检测出人脸上的 68 个关键点:
-
1--17:下颌轮廓;
-
18--27:眉毛;
-
28--36:鼻子;
-
37--48:眼睛;
-
49--68:嘴部。
嘴部的关键点如下图所示:
48 ------ 54:嘴部外轮廓的左右端点
50, 52, 56, 58:嘴部上下边缘点
这些点为我们计算嘴部的几何特征提供了基础。
(2)嘴部高宽比 MAR(Mouth Aspect Ratio)
嘴部高宽比定义为:
MAR = \\frac{(A + B + C) / 3}{D}
其中:
-
A、B、C 分别是上下嘴唇之间的三条垂直距离;
-
D 是嘴的水平宽度(48 到 54 号点)。
在代码中实现如下:
def MAR(shape):
A = euclidean_distances(np.array(shape[50]), np.array(shape[58]))
B = euclidean_distances(np.array(shape[51]), np.array(shape[57]))
C = euclidean_distances(np.array(shape[52]), np.array(shape[56]))
D = euclidean_distances(np.array(shape[48]), np.array(shape[54]))
return ((A + B + C) / 3) / D
当人嘴张开时,A、B、C 增大,因此 MAR 值上升。
-
正常闭嘴:MAR ≈ 0.2~0.35
-
微笑:MAR ≈ 0.4
-
大笑或张嘴:MAR > 0.5
(3)嘴宽与脸颊宽比 MJR(Mouth--Jaw Ratio)
微笑不仅会让嘴角上扬,还会拉宽嘴部。因此,我们再引入一个嘴宽/脸颊宽比:
MJR = \\frac{嘴宽度}{下颌宽度} = \\frac{d_{48,54}}{d_{3,13}}
def MJR(shape):
M = euclidean_distances(np.array(shape[48]), np.array(shape[54])) # 嘴宽
J = euclidean_distances(np.array(shape[3]), np.array(shape[13])) # 下颌宽
return M / J
一般规律为:
-
正常:MJR < 0.38
-
微笑:MJR ≈ 0.40~0.45
-
大笑:MJR > 0.46
三、系统功能实现与代码解析
完整代码如下:
import numpy as np
import dlib
import cv2
from sklearn.metrics.pairwise import euclidean_distances
from PIL import Image, ImageDraw, ImageFont
# ------------------- 计算嘴部参数函数 -------------------
def MAR(shape):
A = euclidean_distances(np.array(shape[50]), np.array(shape[58]))
B = euclidean_distances(np.array(shape[51]), np.array(shape[57]))
C = euclidean_distances(np.array(shape[52]), np.array(shape[56]))
D = euclidean_distances(np.array(shape[48]), np.array(shape[54]))
return ((A + B + C) / 3) / D
def MJR(shape):
M = euclidean_distances(np.array(shape[48]), np.array(shape[54]))
J = euclidean_distances(np.array(shape[3]), np.array(shape[13]))
return M / J
# ------------------- 中文输出函数 -------------------
def cv2AddChineseText(img, text, position, textColor=(0, 255, 0), textSize=30):
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)
# ------------------- 模型加载 -------------------
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")
cap = cv2.VideoCapture(0)
while True:
ret, frame = cap.read()
rects = detector(frame, 0)
for rect in rects:
shape = predictor(frame, rect)
shape = np.matrix([[p.x, p.y] for p in shape.parts()])
mar = MAR(shape)
mjr = MJR(shape)
result = "正常"
if mar > 0.5:
result = "大笑"
elif mjr > 0.45:
result = "微笑"
# 绘制嘴部区域
mouthHull = cv2.convexHull(shape[48:61])
cv2.drawContours(frame, [mouthHull], -1, (0, 255, 0), 1)
frame = cv2AddChineseText(frame, f"{result}", (50, 100))
print(f"MAR: {mar:.3f}\tMJR: {mjr:.3f}\t表情: {result}")
cv2.imshow("Frame", frame)
if cv2.waitKey(1) == 27: # ESC 退出
break
cv2.destroyAllWindows()
cap.release()
四、运行效果与结果分析
系统运行后,会实时捕获摄像头图像,检测人脸并计算嘴部参数。程序每帧打印 MAR
和 MJR
的值,并在窗口中用中文显示当前表情类型。
表情 | MAR 范围 | MJR 范围 | 特征描述 |
---|---|---|---|
正常 | 0.25--0.35 | 0.35--0.40 | 嘴闭合,嘴角自然 |
微笑 | 0.35--0.45 | 0.40--0.45 | 嘴角上扬,嘴部稍宽 |
大笑 | >0.5 | >0.46 | 嘴张开明显,上下唇分离 |
通过打印值可以自行调整阈值,以适应不同光线和人脸大小。
五、算法拓展:加入"哭/悲伤"检测
"哭"或"悲伤"表情通常伴随嘴角下垂、嘴部略张。为了检测此类表情,可以结合嘴角坐标高度差。
(1)嘴角高度差计算
设左嘴角点为 48,右嘴角点为 54,嘴部中心点为 51(上唇中点)。
如果嘴角比中点低很多,说明嘴角下垂,可视为"悲伤"或"哭泣"。
计算公式如下:
Sadness = \\frac{y_{48} + y_{54}}{2} - y_{51}
def SAD(shape):
left = shape[48][1, 0]
right = shape[54][1, 0]
top = shape[51][1, 0]
return ((left + right) / 2) - top
一般规律:
-
正常/微笑:SAD < 0;
-
哭/悲伤:SAD > 2~5(像素差)。
(2)集成到主循环
加入悲伤检测逻辑:
sad_value = SAD(shape)
if mar > 0.5:
result = "大笑"
elif mjr > 0.45:
result = "微笑"
elif sad_value > 3:
result = "哭泣/悲伤"
else:
result = "正常"
通过组合三个特征(MAR、MJR、SAD),可以区分更多情绪状态。
六、性能优化与注意事项
-
关键点模型加载时间
-
shape_predictor_68_face_landmarks.dat
文件较大(约 100MB),建议在程序开头加载一次; -
若需速度更快,可使用
shape_predictor_5_face_landmarks.dat
轻量模型,仅包含眼睛和嘴角特征。
-
-
摄像头分辨率
-
默认分辨率可能较低(640×480),可以通过:
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)
提升检测精度。
-
-
光照条件
-
建议使用均匀光线,避免强背光;
-
可加入
cv2.equalizeHist
对图像亮度进行均衡化。
-
-
多线程优化
- 如果需要在实时监控系统中使用,可将检测与绘图分离至两个线程,提高帧率。
七、应用场景拓展
应用场景 | 功能说明 |
---|---|
驾驶员监控 | 检测驾驶员微笑/打哈欠,判断疲劳或情绪 |
教育场景 | 检测学生表情变化,分析学习兴趣 |
智能客服 | 根据客户表情动态调整语气或推荐内容 |
互动娱乐 | 表情控制游戏角色(如微笑触发动作) |
医疗康复 | 辅助评估患者面部肌肉恢复情况 |
八、未来方向
未来可结合深度学习模型(如 CNN + LSTM)实现更精准的多情绪识别,同时加入眼部、眉毛等特征,实现更全面的情绪判断。此外,还可采用面部 3D 重建或光流分析,识别更细微的表情动态。
九、总结
本文从嘴部几何特征出发,通过计算 MAR(高宽比) 与 MJR(嘴宽比),实现了实时的"微笑"、"大笑"、"正常"表情识别,并扩展实现了"哭/悲伤"的检测。该系统具有以下优点:
-
轻量级:无需深度学习模型;
-
实时性强:CPU 即可流畅运行;
-
可扩展性高:容易加入更多表情类型。
这种基于几何分析的嘴部表情识别方法,虽然精度略低于深度神经网络,但在嵌入式设备、实时监控、教学分析等场景中具备极高的实用价值。
✅ 完整检测公式汇总
参数 | 定义 | 功能 |
---|---|---|
MAR |
嘴部高度 / 宽度 | 检测张嘴程度 |
MJR |
嘴宽 / 下颌宽 | 检测微笑程度 |
SAD |
嘴角高度差 | 检测悲伤/哭泣 |
通过组合判断:
if mar > 0.5:
表情 = "大笑"
elif mjr > 0.45:
表情 = "微笑"
elif sad_value > 3:
表情 = "哭泣/悲伤"
else:
表情 = "正常"