计算机视觉(opencv)——嘴部表情检测
实时嘴部表情检测系统:从微笑到哭泣的智能识别
一、项目背景与意义
在计算机视觉领域中,人脸表情识别(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) / Ddef 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 退出breakcv2.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:表情 = "正常"