当前位置: 首页 > news >正文

使用OpenCV 和Dlib 实现表情识别

文章目录

  • 引言
  • 1.代码主要概述
  • 2.代码解析
    • 2.1 面部特征计算函数
      • (1) 嘴部宽高比(MAR)
      • (2) 嘴宽与脸颊宽比值(MJR)
      • (3) 眼睛纵横比(EAR)
      • (4) 眉毛弯曲比(EBR)
    • 2.2 自定义函数显示中文
    • 2.3 表情分类逻辑
    • 2.4 实时视频处理
  • 3.系统特点
  • 4.总结

引言

面部表情是人类情感交流的重要方式,计算机视觉技术的发展使得机器能够自动识别和理解人类表情。本文将介绍一个基于dlib库和OpenCV实现的实时表情识别系统,该系统通过分析面部关键点来计算嘴部、眼睛和眉毛的特征,进而判断当前的表情状态(如大笑、微笑、愤怒、哭泣等)。


1.代码主要概述

该系统主要包含以下几个功能模块:

  1. 面部关键点检测
  2. 四种面部特征计算
  3. 表情分类逻辑
  4. 实时视频处理与结果显示

2.代码解析

2.1 面部特征计算函数

系统定义了四个核心函数来计算不同的面部特征:

(1) 嘴部宽高比(MAR)

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 的函数,用于计算嘴部的宽高比(Mouth Aspect Ratio)。这个比例通常用于面部特征分析,比如检测嘴巴是否张开等。下面是对代码的逐步解释:

假设 shape 是一个包含68个面部关键点的数组(这是dlib等面部检测库的常见输出),那么:

  • 点48到54是嘴巴的外轮廓点。
  • 点50、51、52是上嘴唇的点。
  • 点56、57、58是下嘴唇的点。
  1. 计算高度(A、B、C)

    • A :点50和点58之间的欧氏距离(上嘴唇中点到下嘴唇中点)。
    • B :点51和点57之间的欧氏距离(上嘴唇左侧点到下嘴唇左侧点)。
    • C :点52和点56之间的欧氏距离(上嘴唇右侧点到下嘴唇右侧点)。
    • (A+B+C)/3 :计算这三个高度的平均值。
  2. 计算宽度(D)

    • D :点48和点54之间的欧氏距离(嘴巴左角到右角)。
  3. 宽高比

    • 最终的 MAR 是高度的平均值除以宽度:((A+B+C)/3)/D

细节部分

  • euclidean_distances 是计算两点之间欧氏距离的函数。
  • reshape(1,2) 是为了确保输入的坐标是二维的(将数据转换为 [[x, y]] ,以满足 euclidean_distances 的输入要求)。

返回值

  • 返回的是嘴的宽高比。这个值越小,表示嘴巴越闭合;越大,表示嘴巴越张开。

(2) 嘴宽与脸颊宽比值(MJR)

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(Mouth-to-Jaw Ratio)的函数,用于计算嘴部宽度与下颌宽度的比值,这是面部表情分析中常用的一个特征指标。以下是对代码的详细解析:


函数功能

  • 输入shape(一个包含68个面部关键点坐标的数组,通常来自dlib库的检测结果)。
  • 输出:嘴部宽度与下颌宽度的比值(浮点数),用于量化嘴部相对于面部宽度的张开程度。

关键点定位
代码使用了68点面部关键点模型中的特定点:

  • 嘴宽(M)

    • shape[48]:嘴巴左外角
    • shape[54]:嘴巴右外角
    • 计算这两点之间的直线距离(欧氏距离),得到嘴的物理宽度。
  • 下颌宽(J)

    • shape[3]:左下颌角(靠近耳朵下方)
    • shape[13]:右下颌角
    • 计算这两点距离,代表脸颊的大致宽度。

数学表达
在这里插入图片描述

  • 比值意义
    • ≈0.3~0.4:自然闭合状态
    • >0.45:微笑(嘴角横向拉伸)
    • 接近0.5~0.6:大笑或惊讶(嘴部明显张开)
    • >1:检测异常或极端表情(罕见)

代码细节

  • euclidean_distances:计算两点之间的欧式距离(直线距离)。
  • reshape(1,2):将坐标从**[x,y]转换为[[x,y]]**格式,满足函数输入要求。

实际应用

  • 微笑检测(如原代码中的判断逻辑):
    if mjr > 0.45:  # 阈值可调result = "微笑"
    
  • 与其他指标配合
    结合MAR(嘴高比)、EAR(眼睛纵横比)等,可更准确识别复杂表情(如大笑、愤怒)。

示例计算
假设检测到:

  • 嘴宽 M = 50像素

  • 下颌宽 J = 120像素
    则:
    在这里插入图片描述

  • 若阈值为0.45,判定为自然状态;

  • 若微笑时嘴宽增至60像素,则MJR=0.5,触发微笑判断。


(3) 眼睛纵横比(EAR)

def EAR(shape):# 左眼A = euclidean_distances(shape[37].reshape(1, 2), shape[41].reshape(1, 2))B = euclidean_distances(shape[38].reshape(1, 2), shape[40].reshape(1, 2))C = euclidean_distances(shape[36].reshape(1, 2), shape[39].reshape(1, 2))ear_left = (A + B) / (2.0 * C)# 右眼A = euclidean_distances(shape[43].reshape(1, 2), shape[47].reshape(1, 2))B = euclidean_distances(shape[44].reshape(1, 2), shape[46].reshape(1, 2))C = euclidean_distances(shape[42].reshape(1, 2), shape[45].reshape(1, 2))ear_right = (A + B) / (2.0 * C)return (ear_left + ear_right) / 2.0

这段代码定义了一个名为 EAR的函数,用于计算眼睛纵横比(Eye Aspect Ratio, EAR),这是一种常用于检测眼睛是否闭合或疲劳的方法。下面是对代码的逐步解析:


  • shape 是一个包含68个面部关键点的数组(通常由 dlib 等面部检测库生成),其中:
    • 左眼的点索引是 36-41
    • 右眼的点索引是 42-47

左眼 EAR 计算

A = euclidean_distances(shape[37].reshape(1, 2), shape[41].reshape(1, 2))  # 37-41 的垂直距离
B = euclidean_distances(shape[38].reshape(1, 2), shape[40].reshape(1, 2))  # 38-40 的垂直距离
C = euclidean_distances(shape[36].reshape(1, 2), shape[39].reshape(1, 2))  # 36-39 的水平距离
ear_left = (A + B) / (2.0 * C)
  • A:计算左眼上眼皮(点37)和下眼皮(点41)之间的垂直距离。
  • B:计算左眼上眼皮(点38)和下眼皮(点40)之间的垂直距离。
  • C:计算左眼外眼角(点36)和内眼角(点39)之间的水平距离。
  • ear_left:计算左眼的纵横比,公式为:
    在这里插入图片描述

右眼 EAR 计算

A = euclidean_distances(shape[43].reshape(1, 2), shape[47].reshape(1, 2))  # 43-47 的垂直距离
B = euclidean_distances(shape[44].reshape(1, 2), shape[46].reshape(1, 2))  # 44-46 的垂直距离
C = euclidean_distances(shape[42].reshape(1, 2), shape[45].reshape(1, 2))  # 42-45 的水平距离
ear_right = (A + B) / (2.0 * C)
  • 同理,计算右眼的纵横比:
    在这里插入图片描述

最终 EAR

return (ear_left + ear_right) / 2.0
  • 返回左右眼 EAR 的平均值,使结果更稳定。

EAR 的意义

  • EAR ≈ 0.3(或更高):眼睛睁开。
  • EAR ≈ 0.2 或更低:眼睛闭合(或半闭)。
  • 连续几帧 EAR < 阈值:可能表示疲劳、眨眼或打瞌睡。

关键点图示

左眼关键点(36-41):
36 —— 37 —— 38
|       |       |
39 —— 40 —— 41右眼关键点(42-47):
42 —— 43 —— 44
|       |       |
45 —— 46 —— 47
  • 垂直距离(A、B):37-41、38-40(左眼);43-47、44-46(右眼)。
  • 水平距离(C):36-39(左眼);42-45(右眼)。

总结
这段代码通过计算眼睛关键点的垂直和水平距离比,量化眼睛的睁开程度,适用于实时面部行为分析。


(4) 眉毛弯曲比(EBR)

def EBR(shape):  # 计算眉毛的弯曲程度# 左眉弯曲度left_eyebrow = shape[17:22]left_eyebrow_hull = cv2.convexHull(left_eyebrow)left_eyebrow_length = euclidean_distances(left_eyebrow[0].reshape(1, 2), left_eyebrow[-1].reshape(1, 2))left_eyebrow_height = cv2.contourArea(left_eyebrow_hull) / left_eyebrow_length# 右眉弯曲度right_eyebrow = shape[22:27]right_eyebrow_hull = cv2.convexHull(right_eyebrow)right_eyebrow_length = euclidean_distances(right_eyebrow[0].reshape(1, 2), right_eyebrow[-1].reshape(1, 2))right_eyebrow_height = cv2.contourArea(right_eyebrow_hull) / right_eyebrow_lengthreturn (left_eyebrow_height + right_eyebrow_height) / 2.0

这段代码定义了一个名为 EBR(Eyebrow Bend Ratio,眉毛弯曲比)的函数,用于计算眉毛的弯曲程度,通常用于检测愤怒、皱眉或惊讶等面部表情。下面是对代码的逐步解析:


  • shape 是一个包含68个面部关键点的数组(通常由 dlib 等面部检测库生成),其中:
    • 左眉的点索引是 17-21(代码中 shape[17:22] 表示 17-21)
    • 右眉的点索引是 22-26(代码中 shape[22:27] 表示 22-26)

左眉弯曲度计算

left_eyebrow = shape[17:22]  # 提取左眉5个关键点
left_eyebrow_hull = cv2.convexHull(left_eyebrow)  # 计算凸包(包围眉毛的最小凸多边形)
left_eyebrow_length = euclidean_distances(left_eyebrow[0].reshape(1, 2), left_eyebrow[-1].reshape(1, 2))  # 眉毛长度(首尾点距离)
left_eyebrow_height = cv2.contourArea(left_eyebrow_hull) / left_eyebrow_length  # 弯曲高度 = 凸包面积 / 眉毛长度
  1. 提取左眉关键点shape[17:22] 获取左眉的5个点(17-21)。
  2. 计算凸包cv2.convexHull 返回包围眉毛的最小凸多边形(用于近似眉毛形状)。
  3. 眉毛长度:计算左眉起点(点17)和终点(点21)之间的欧氏距离。
  4. 弯曲高度
    • cv2.contourArea 计算凸包的面积(反映眉毛的“隆起程度”)。
    • 公式弯曲高度 = 凸包面积 / 眉毛长度
      (值越大,眉毛越弯曲,可能表示愤怒或皱眉)。

右眉弯曲度计算

right_eyebrow = shape[22:27]  # 提取右眉5个关键点
right_eyebrow_hull = cv2.convexHull(right_eyebrow)  # 计算凸包
right_eyebrow_length = euclidean_distances(right_eyebrow[0].reshape(1, 2), right_eyebrow[-1].reshape(1, 2))  # 眉毛长度
right_eyebrow_height = cv2.contourArea(right_eyebrow_hull) / right_eyebrow_length  # 弯曲高度
  • 右眉的计算逻辑与左眉完全相同(点22-26)。

最终 EBR

return (left_eyebrow_height + right_eyebrow_height) / 2.0
  • 返回左右眉弯曲高度的平均值,使结果更稳定。

EBR 的意义

  • EBR 值高:眉毛弯曲明显(可能表示愤怒、惊讶或集中注意力)。
  • EBR 值低:眉毛平直(自然状态)。

关键点图示

左眉关键点(17-21):
17 —— 18 —— 19 —— 20 —— 21右眉关键点(22-26):
22 —— 23 —— 24 —— 25 —— 26
  • 眉毛长度:17到21(左眉)、22到26(右眉)的直线距离。
  • 凸包面积:反映眉毛的弯曲程度(面积越大,弯曲越明显)。

2.2 自定义函数显示中文

def cv2AddChineseText(img,text,position,textColor=(0,255,0),textSize=30):"""向图片中添加中文"""if (isinstance(img,np.ndarray)):   # 判断是否OpenCV图片类型img = Image.fromarray(cv2.cvtColor(img,cv2.COLOR_BGR2RGB))  # 实现array到image的转换draw = ImageDraw.Draw(img) # 在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_BGR2RGB) # 转换回OpenCV格式

2.3 表情分类逻辑

系统使用简单的阈值判断来分类表情:

result = "正常"  # 默认表情# 大笑检测:嘴部高度比较高
if mar > 0.5:result = "大笑"
# 微笑检测:嘴部高度比较宽但高度不高
elif mjr > 0.45:result = "微笑"
# 愤怒检测: 眉毛弯曲度高且眼睛稍微闭合
elif ebr > 0.08 and ear < 0.25:result = "愤怒"
# 哭检测: 嘴部较宽但不高,眼睛闭合或半闭合
elif mar < 0.3 and mjr > 0.4 and ear < 0.2:result = "哭泣"

2.4 实时视频处理

系统通过OpenCV捕获摄像头视频流,并对每一帧进行处理:

cap = 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)ebr = EBR(shape)ear = EAR(shape)result = "正常"    # 默认是正常表情# 大笑检测:嘴部高度比较高if mar > 0.5:    # 可更具项目要求调整阈值result = "大笑"# 微笑检测:嘴部高度比较宽但高度不高elif mjr > 0.45:    #超过阈值为微笑result = "微笑"# 愤怒检测: 眉毛弯曲度高且眼睛稍微闭合elif ebr > 0.08 and ear < 0.25:result = "愤怒"# 哭检测: 嘴部较宽但不高(嘴角下拉), 眼睛闭合或半闭合elif mar < 0.3 and mjr > 0.4 and ear < 0.2:result = "哭泣"# 绘制嘴部轮廓mouthHull = cv2.convexHull(shape[48:61])   # 嘴型构造凸包# 绘制眉毛轮廓leftEyebrowHull = cv2.convexHull(shape[17:22])rightEyebrowHull = cv2.convexHull(shape[22:27])frame = cv2AddChineseText(frame,result,mouthHull[0,0]) #多人脸cv2.drawContours(frame,[mouthHull],-1,(0,255,0),1)cv2.drawContours(frame,[leftEyebrowHull],-1,(0,255,0),1)cv2.drawContours(frame,[rightEyebrowHull],-1,(0,255,0),1)cv2.imshow("表情识别",frame)if cv2.waitKey(1) == 27:break
cv2.destroyAllWindows()
cap.release()    # 增加几项表情的检测能力,哭、愤怒

3.系统特点

  1. 实时性:能够实时处理摄像头视频流
  2. 多表情识别:可识别大笑、微笑、愤怒、哭泣等多种表情
  3. 可视化反馈:在视频画面上直观显示识别结果和面部轮廓
  4. 中文支持:使用自定义函数实现中文文本显示

4.总结

本文介绍了一个基于面部关键点的实时表情识别系统,该系统通过计算嘴部、眼睛和眉毛的特征参数,使用简单的阈值判断实现了基本表情分类。虽然相对简单,但展示了计算机视觉在情感识别领域的典型应用方法。读者可以在此基础上进一步扩展和完善,开发出更强大的表情识别应用。

完整代码已在上文给出,建议读者实际运行体验,并根据需要调整阈值参数以获得更好的识别效果。

因为有目标,所以一切皆有可能。竭尽全力,才能知道自己有多出色。加油各位! 🚀🚀🚀

相关文章:

  • ReSearch:强化学习赋能大模型,推理与搜索的创新融合
  • 典籍知识问答模块AI问答功能feedbackBug修改+添加对话名称修改功能
  • Debian系统上PostgreSQL15版本安装调试插件及DBeaver相应配置
  • 【C语言干货】野指针
  • 香港维尔利健康科技集团与亚洲医学研究院达成战略合作,联合打造智慧医疗应用技术实验室
  • 聚类分析的原理、常用算法及其应用
  • Python生活手册-Numpy数组索引:从快递柜到咖啡店的数字化生活指南
  • Vue 3 全面详解:从基础到进阶实战
  • Vue 权限管理终极实践:动态路由 + 按钮级权限控制
  • AI基础知识(02):机器学习的任务类型、学习方式、工作流程
  • Linux 网络编程 day5 多路IO转接之改进select and poll
  • 并发设计模式实战系列(16):屏障(Barrier)
  • Facebook如何运用AI实现元宇宙的无限可能?
  • RabbitMQ 添加新用户和配置权限
  • [监控看板]Grafana+Prometheus+Exporter监控疑难排查
  • 模型状态量
  • WPF之高级布局技术
  • 从设备交付到并网调试:CET中电技术分布式光伏全流程管控方案详解
  • 如何打造系统级低延迟RTSP/RTMP播放引擎?
  • 机器人系统设置
  • 诺和诺德一季度减重版司美格鲁肽收入增83%,美国市场竞争激烈下调全年业绩预期
  • 公积金利率降至历史低位,百万房贷30年省5万
  • 詹丹|高考语文阅读题设计和答案拟制的一些缺憾
  • 山东滕州市醉驾交通事故肇事人员已被刑拘
  • 印巴矛盾已达近年“最高点”:军政经文全面紧张,巴将向联合国通报局势
  • 青岛双星名人集团董事长发公开信称家人逼迫交出管理权?公司回应