【机器学习】基于 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/10/17 22:09:06 阅读更多 →
Raft 集群的性能退化诊断:日志复制延迟的 flamegraph 分析与网络层优化方案

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

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

2026/10/18 22:44:41 阅读更多 →
Rust 程序的内存泄漏排查:使用 heaptrack、valgrind 与自定义 Allocator 追踪分配路径

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

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

2026/10/18 10:33:54 阅读更多 →

最新新闻

基于STC12C5A60S2的智能油烟机毕业设计:ADC、PWM与状态机实战

基于STC12C5A60S2的智能油烟机毕业设计:ADC、PWM与状态机实战

简介:面向单片机、嵌入式方向的本科毕业设计人员,这份智能油烟机项目以STC12C5A60S2单片机为核心,完整覆盖油烟机控制系统的程序编写、硬件设计与联调环节,适合作为课程设计、毕业设计或入门级嵌入式项目的参考资料。 压缩包共21…

2026/10/19 17:01:18 阅读更多 →
Claude Code CLI 安装配置与实战指南:Node.js 环境、BASE_URL 与 AUTH_TOKEN 详解

Claude Code CLI 安装配置与实战指南:Node.js 环境、BASE_URL 与 AUTH_TOKEN 详解

1. 环境准备与基础依赖梳理1.1 为什么 Node.js 是绕不开的第一道坎Claude Code 这个 CLI 工具本质上是跑在 Node.js 运行时上的一个命令行程序,所以不管你用 Windows、macOS 还是 Linux,第一步都得先把 Node.js 装好。很多人卡在第一步不是因为不会装&am…

2026/10/19 17:01:17 阅读更多 →
从内置到自定义:LangChain复杂任务编排的Custom Chain与Agent实战

从内置到自定义:LangChain复杂任务编排的Custom Chain与Agent实战

把LangChain从“会用”推向“能真正处理复杂任务”,核心就在于自己动手写Custom Chain和Custom Agent。内置的链解决线性流程还行,一旦任务要求循环判断、多渠道取数、多工具协同、中途修改参数,搭积木式的内置方案就完全不够用了。这篇文章我…

2026/10/19 17:01:17 阅读更多 →
老毛桃v7.0 UEFI启动盘制作全指南:解决双模式识别失败

老毛桃v7.0 UEFI启动盘制作全指南:解决双模式识别失败

简介:老毛桃U盘启动盘制作工具(UEFI版 装机版)v7.0是一款面向电脑初学者与系统维护人员的轻量级系统辅助工具,专为快速制作兼容UEFI与传统BIOS的U盘启动盘、安装原版Windows系统及执行PE环境下的系统维护任务而设计。资源包共2个文…

2026/10/19 17:00:16 阅读更多 →
open-code-review:面向上下文的代码审查新范式

open-code-review:面向上下文的代码审查新范式

1. “open-code-review”不是工具名,而是一套可落地的代码审查新范式你有没有遇到过这样的场景:团队里刚入职的新人提交了一段看似逻辑通顺的Python函数,用pytest跑全量测试都通过了,但上线后在高并发下偶发空指针——不是语法错误…

2026/10/19 17:00:16 阅读更多 →
open-code-review:基于git diff的轻量级AI代码审查协议

open-code-review:基于git diff的轻量级AI代码审查协议

1. 这不是另一个“AI代码审查工具”,而是一套可落地的开源协作新范式“open-code-review”这个词,最近在技术社区里出现得越来越频繁,但它远不止是“用AI做Code Review”的简单包装。我从去年底开始在三个不同规模的团队里推动类似实践——从…

2026/10/19 17:00:16 阅读更多 →

日新闻

频率跟踪型PWM电源设计:臭氧发生器谐振点漂移与软开关实现

频率跟踪型PWM电源设计:臭氧发生器谐振点漂移与软开关实现

简介:本资源为电源技术方向的学术研究文档,聚焦介质阻挡放电型臭氧发生器电源的频率跟踪型PWM控制策略,面向电力电子、高压电源及臭氧应用领域的科研人员与工程技术人员。针对负载呈容性、等效电容随外加电压升高而增大、谐振频率随之漂移的难…

2026/10/19 0:00:47 阅读更多 →
Vibe Coding 遇上 TaoToken:把开发者思维圣殿的灵感流接入统一 Key 通道

Vibe Coding 遇上 TaoToken:把开发者思维圣殿的灵感流接入统一 Key 通道

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 0:00:47 阅读更多 →
HundredCV-Chat话题建模实战教程:利用4,871个内置话题标签构建主题分析系统

HundredCV-Chat话题建模实战教程:利用4,871个内置话题标签构建主题分析系统

【免费下载链接】HundredCV-Chat 项目地址: https://ai.gitcode.com/hf_mirrors/Jax-dan/HundredCV-Chat 点击查看 免费下载 HundredCV-Chat 是一个由 100 位青年简历数据开发而来的中文多轮对话数据集,共包含 24,750 组日常闲聊对话和 4,871 个话题标签…

2026/10/19 0:00:47 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/19 10:02:04 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/19 1:30:52 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 2:54:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 9:10:22 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 12:16:16 阅读更多 →