【机器学习】基于 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 退出。

相关新闻

Ollama 的并发模型深度分析:从请求队列到 GPU Stream 的任务分派与同步机制

Ollama 的并发模型深度分析:从请求队列到 GPU Stream 的任务分派与同步机制

Ollama 的并发模型深度分析:从请求队列到 GPU Stream 的任务分派与同步机制 一、多用户并发推理时 GPU 利用率不饱和的根因追问 Ollama 作为本地 LLM 推理的流行方案,单用户场景下表现良好。但部署为内部推理服务后,多用户并发访问时 GPU 利用…

2026/7/21 17:01:30 阅读更多 →
Raft 集群的性能退化诊断:日志复制延迟的 flamegraph 分析与网络层优化方案

Raft 集群的性能退化诊断:日志复制延迟的 flamegraph 分析与网络层优化方案

Raft 集群的性能退化诊断:日志复制延迟的 flamegraph 分析与网络层优化方案 一、节点数增长时集群吞吐不升反降的诡异现象 在分布式系统中,直觉上增加节点应该提升吞吐量——更多节点意味着更多并行处理单元。然而 Raft 集群的实际表现恰恰相反&#xff…

2026/7/21 17:01:49 阅读更多 →
Rust 程序的内存泄漏排查:使用 heaptrack、valgrind 与自定义 Allocator 追踪分配路径

Rust 程序的内存泄漏排查:使用 heaptrack、valgrind 与自定义 Allocator 追踪分配路径

Rust 程序的内存泄漏排查:使用 heaptrack、valgrind 与自定义 Allocator 追踪分配路径 一、生产环境中的"温水煮青蛙"式内存增长 Rust 的所有权模型在编译期消除了 Use-After-Free 和 Double-Free,但这并不意味着内存泄漏从此绝缘。引用计数循…

2026/7/21 17:01:51 阅读更多 →

最新新闻

影刀RPA 社交媒体数据分析:粉丝画像与内容表现

影刀RPA 社交媒体数据分析:粉丝画像与内容表现

title: “影刀RPA 社交媒体数据分析:粉丝画像与内容表现” date: 2026-07-01 author: 林焱 影刀RPA 社交媒体数据分析:粉丝画像与内容表现 做内容运营不知道粉丝画像,不知道什么内容效果好——全靠感觉。用影刀定期采集自己账号和竞品账号的…

2026/7/22 5:22:47 阅读更多 →
C++性能优化实战:从内存分配到锁竞争,打造工业级日志处理模块

C++性能优化实战:从内存分配到锁竞争,打造工业级日志处理模块

1. 项目概述:从“能跑”到“跑得好”的C进阶之路每次看到别人写的C代码,或者review自己几个月前的项目,你是不是也有过这种感觉:功能是实现了,但总觉得哪里不对劲?可能是某个循环慢得让人心焦,可…

2026/7/22 5:22:47 阅读更多 →
NVIDIA Rubin平台:AI基础设施的架构革新与部署实践

NVIDIA Rubin平台:AI基础设施的架构革新与部署实践

1. 先搞清楚Rubin平台到底解决了什么实际问题如果你关注过AI训练和推理的硬件瓶颈,就会明白NVIDIA Rubin平台的出现意味着什么。传统AI基础设施最大的问题不是单个GPU不够快,而是当模型规模达到万亿参数级别、上下文长度扩展到数百万token时,…

2026/7/22 5:22:47 阅读更多 →
QuickJS FFI实战指南:打通JavaScript与C语言的高效交互

QuickJS FFI实战指南:打通JavaScript与C语言的高效交互

1. 项目概述:为什么我们需要QuickJS FFI?如果你是一名C/C开发者,或者是一个对系统底层、嵌入式、高性能计算感兴趣的JavaScript工程师,那么“如何让C和JS高效对话”这个问题,大概率曾让你头疼过。传统的Node.js通过Nod…

2026/7/22 5:22:47 阅读更多 →
深入解析EDMA3:DMA/QDMA通道、事件触发与PaRAM链接机制

深入解析EDMA3:DMA/QDMA通道、事件触发与PaRAM链接机制

1. 项目概述:为什么我们需要EDMA3?在嵌入式系统里干活,尤其是跟音频、视频或者高速数据采集打交道,CPU最怕的就是被数据搬运这种“体力活”给拖累。想象一下,你正在处理一个实时音频流,麦克风源源不断地送来…

2026/7/22 5:22:47 阅读更多 →
Tool Calling 前端怎么接:工具进度、错误回传、权限边界

Tool Calling 前端怎么接:工具进度、错误回传、权限边界

《AI 前端实战》第 4/8 篇 上篇:Streaming UI 工程化 下篇预告:生成式 UI(JSON Schema → React) 第 2~3 篇解决了「模型会说话,而且说的过程体验还行」。 第 4 篇进入分水岭:模型开始调用工具。…

2026/7/22 5:21:46 阅读更多 →

日新闻

TI DSP系统配置模块SYSCFG详解:中断机制与主设备优先级配置实战

TI DSP系统配置模块SYSCFG详解:中断机制与主设备优先级配置实战

1. 项目概述与SYSCFG模块的核心价值在嵌入式系统,尤其是像TI C6000系列这样的高性能DSP开发中,我们常常会与芯片手册里那些密密麻麻的寄存器打交道。很多开发者可能更关注算法实现、内存优化或者外设驱动,但对于一个稳定、高效的系统而言&…

2026/7/22 0:00:26 阅读更多 →
微信Server酱:高到达率的应急通知方案实践

微信Server酱:高到达率的应急通知方案实践

1. 为什么我们需要"最次"的通知方案? 在数字化协作环境中,消息通知系统的重要性不言而喻明。但现实情况是,企业级通知方案往往需要复杂的API对接(如企业微信、钉钉、飞书),个人开发者的小项目又经…

2026/7/22 0:00:26 阅读更多 →
甲方要的“简洁“PPT,到底是简洁还是省事?

甲方要的“简洁“PPT,到底是简洁还是省事?

甲方说"简洁一点",乙方听到的是"少做几页"。甲方说"不要太复杂",乙方理解成"别放图表了"。结果交过去,甲方说"我说的简洁不是这个意思"。"简洁"这个词在PPT语境里,是…

2026/7/22 0:00:26 阅读更多 →

周新闻

Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/21 8:48:31 阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/21 5:34:47 阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/21 8:25:39 阅读更多 →

月新闻