人脸68关键点与K210疲劳检测

目录

人脸68关键点检测

检测闭眼睁眼

双眼关键点检测

计算眼睛的闭合程度:

原理:

设置阈值进行判断

实时监测和更新

拓展:通过判断上下眼皮重合程度去判断是否闭眼

检测嘴巴是否闭合

提取嘴唇上下轮廓的关键点

计算嘴唇上下轮廓关键点之间的距离

计算嘴角到上嘴唇中心的距离

计算嘴角到下嘴唇中心的距离

将两个距离相加作为嘴唇的闭合程度指标

判断嘴巴是否闭合

K210疲劳检测


前两天在做项目的时候,想通过偷懒的方式去试试,不用目标检测去检测疲劳,而是通过人脸检测的68关键点去通过检测睁眼闭眼和张嘴闭嘴去检测闭眼和打瞌睡。那让我们来试试吧。

人脸68关键点检测

人脸68关键点检测是一种计算机视觉技术,旨在识别和定位人脸图像中的关键点。这些关键点通常包括眼睛、鼻子、嘴巴等面部特征的位置。通过检测这些关键点,可以实现人脸识别、表情识别、姿势估计等

主要的步骤:

  1. 检测人脸:首先需要使用人脸检测算法确定图像中人脸的位置。
  2. 提取关键点:在检测到的人脸区域内,使用特定的算法来识别和标记关键点的位置。
  3. 分类关键点:将检测到的关键点分为不同的类别,如眼睛、鼻子、嘴巴等。

检测闭眼睁眼

双眼关键点检测

在68个关键点中,一般左眼和右眼的位置会分别由多个关键点表示。这些关键点的坐标通常以 (x, y) 形式给出,其中 x 表示水平方向的位置,y 表示垂直方向的位置。

import cv2
# 加载人脸关键点检测器
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")
# 读取图像
img = cv2.imread('face_image.jpg')
# 转换为灰度图
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 检测人脸
faces = detector(gray)
for face in faces:
    # 获取关键点
    landmarks = predictor(gray, face)
    for n in range(37, 48):  # 提取左眼和右眼的关键点
        x = landmarks.part(n).x
        y = landmarks.part(n).y
        cv2.circle(img, (x, y), 2, (0, 255, 0), -1)  # 在关键点处画圆

计算眼睛的闭合程度:

原理:

通过计算眼睛关键点的纵向位置(Y 坐标)的差值,并加权求和来反映眼睛的状态。

对于每只眼睛,首先计算关键点的纵向位置(Y 坐标)的差值。可以选择一些具有代表性的关键点来进行计算,比如眼睛的上下眼睑或者眼角等位置点。 对这些差值进行加权求和,可以根据不同的应用场景进行加权,比如可以将靠近眼睛中心的关键点的差值赋予更高的权重,因为这些部位更能反映眼睛的状态。

眼睛闭合程度计算公式

  • 首先,选择上眼睑和下眼睑的关键点,分别计算它们的平均纵向位置(Y 坐标)。
  • 然后,通过上眼睑平均位置减去下眼睑平均位置,得到一个值表示眼睛的闭合程度。这个值越小,表示眼睛越闭合\

[ \text{闭合程度} = \text{上眼睑平均位置} - \text{下眼睑平均位置} ]

其中,上眼睑平均位置和下眼睑平均位置分别表示对应关键点的纵向位置的平均值。

# 计算眼睛的闭合程度
def eye_closure_ratio(eye_landmarks):
    # 假设我们选取了上眼睑关键点的索引为[1, 2, 3],下眼睑关键点的索引为[4, 5, 6]
    upper_lid_points = eye_landmarks[1:4]
    lower_lid_points = eye_landmarks[4:7]
    # 计算上下眼睑的平均纵向位置
    upper_lid_y_mean = sum([point[1] for point in upper_lid_points]) / len(upper_lid_points)
    lower_lid_y_mean = sum([point[1] for point in lower_lid_points]) / len(lower_lid_points)
    # 计算纵向位置的差值,这里可以根据具体情况加权求和
    diff = upper_lid_y_mean - lower_lid_y_mean
    return diff

设置阈值进行判断

根据实际情况,可以设置一个阈值来判断眼睛是否闭合。当加权求和的结果小于阈值时,可以认为眼睛是闭合的;反之则是睁开的。

# 判断眼睛是否闭合
def is_eye_closed(eye_landmarks, threshold):
    closure_ratio = eye_closure_ratio(eye_landmarks)
    if closure_ratio < threshold:
        return True  # 眼睛闭合
    else:
        return False  # 眼睛睁开

实时监测和更新

在实时监测过程中,不断更新眼睛关键点的位置信息,并重新计算眼睛的闭合程度,以实现对眼睛状态的准确监测。

# 循环实时监测
while True:
    ret, frame = cap.read()  # 从摄像头读取画面
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    faces = detector(gray)
    for face in faces:
        landmarks = predictor(gray, face)
        left_eye_landmarks = [(landmarks.part(n).x, landmarks.part(n).y) for n in range(36, 42)]  # 左眼关键点
        right_eye_landmarks = [(landmarks.part(n).x, landmarks.part(n).y) for n in range(42, 48)]  # 右眼关键点
        # 计算左眼闭合程度并判断
        left_eye_closed = is_eye_closed(left_eye_landmarks, 5)  # 假设阈值设为5
        # 计算右眼闭合程度并判断
        right_eye_closed = is_eye_closed(right_eye_landmarks, 5)  # 假设阈值设为5
        # 综合判断眼睛状态
        if left_eye_closed and right_eye_closed:
            print("Both eyes closed")
        elif left_eye_closed:
            print("Left eye closed")
        elif right_eye_closed:
            print("Right eye closed")
        else:
            print("Eyes open")

拓展:通过判断上下眼皮重合程度去判断是否闭眼

通过判断上下眼皮的重合程度来间接判断眼睛是否闭合。当眼睛闭合时,上下眼皮会有一定程度的重合,而睁开时则不会存在明显的重合。

具体实现方法可以是计算上下眼睑之间的垂直距离,并将其与总眼睛高度的比值作为闭合程度的度量指标。当这个比值超过一定阈值时,可以判断为闭眼状态

# 计算上下眼皮重合程度
def eyelid_overlap_ratio(eye_landmarks):
    # 假设我们选取了上眼睑关键点的索引为[1, 2, 3],下眼睑关键点的索引为[4, 5, 6]
    upper_lid_points = eye_landmarks[1:4]
    lower_lid_points = eye_landmarks[4:7]
    # 计算上下眼睑之间的垂直距离
    vertical_distance = lower_lid_points[0][1] - upper_lid_points[-1][1]
    # 计算垂直距离与总眼睛高度的比值
    eye_height = max(eye_landmarks, key=lambda x: x[1])[1] - min(eye_landmarks, key=lambda x: x[1])[1]
    overlap_ratio = vertical_distance / eye_height
    return overlap_ratio

检测嘴巴是否闭合

提取嘴唇上下轮廓的关键点

根据人脸关键点的位置信息,提取出嘴唇上下轮廓的关键点。一般而言,嘴唇上下轮廓的关键点包括嘴角、上嘴唇中心、下嘴唇中心等关键点。

计算嘴唇上下轮廓关键点之间的距离

选择合适的距离度量方法(如欧氏距离)计算嘴唇上下轮廓关键点之间的距离。具体来说,可以计算嘴角到上嘴唇中心和嘴角到下嘴唇中心的距离,并将这两个距离相加作为嘴唇的闭合程度指标。

计算嘴角到上嘴唇中心的距离

使用欧氏距离公式计算嘴角到上嘴唇中心的距离: [ distance_top = \sqrt{(x_{top} - x_{corner})^2 + (y_{top} - y_{corner})^2} ] 其中 (x_{top}) 和 (y_{top}) 分别是上嘴唇中心的 x、y 坐标,(x_{corner}) 和 (y_{corner}) 分别是嘴角的 x、y 坐标。

计算嘴角到下嘴唇中心的距离

同样使用欧氏距离公式计算嘴角到下嘴唇中心的距离: [ distance_bottom = \sqrt{(x_{bottom} - x_{corner})^2 + (y_{bottom} - y_{corner})^2} ] 其中 (x_{bottom}) 和 (y_{bottom}) 分别是下嘴唇中心的 x、y 坐标。

将两个距离相加作为嘴唇的闭合程度指标

将嘴角到上嘴唇中心的距离 (distance_top) 和嘴角到下嘴唇中心的距离 (distance_bottom) 相加,得到嘴唇的闭合程度指标: [ distance_total = distance_top + distance_bottom ]

判断嘴巴是否闭合

设定一个阈值,根据嘴唇上下轮廓关键点之间的距禀与该阈值的比较结果来判断嘴巴是否闭合。通常情况下,当嘴唇闭合时,嘴唇上下轮廓的关键点之间的距离会比较小;而当嘴巴张开时,这个距离会增大。

import math
# 计算欧氏距离
def euclidean_distance(point1, point2):
    return math.sqrt((point2[0] - point1[0])**2 + (point2[1] - point1[1])**2)
# 提取嘴唇上下轮廓的关键点索引(假设为索引0到11)
mouth_landmarks = face_landmarks[0:12]  # 假设face_landmarks包含了所有68个关键点的坐标
# 计算嘴唇上下轮廓关键点之间的距离
lip_distances = []
for i in range(len(mouth_landmarks)//2):
    distance = euclidean_distance(mouth_landmarks[i], mouth_landmarks[i + len(mouth_landmarks)//2])
    lip_distances.append(distance)
# 计算平均距离
avg_distance = sum(lip_distances) / len(lip_distances)
# 设定阈值
threshold = 5.0
# 判断嘴巴是否闭合
if avg_distance < threshold:
    print("嘴巴闭合")
else:
    print("嘴巴张开")

K210疲劳检测

项目的来源:对K210人脸检测68关键点的一种拓展

如何通过人脸检测68关键点去检测疲劳情况

 代码是根据K210的例程改的,也只是使用上下眼皮的距离和上下嘴唇的距离来检测疲劳状态

import sensor, image, time, lcd
from maix import KPU
import gc

lcd.init()
sensor.reset()

sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.set_vflip(True) # 摄像头后置

sensor.skip_frames(time=500)
clock = time.clock()

od_img = image.Image(size=(320, 256), copy_to_fb=False)

# 构建KPU对象
# 需要导入2个模型,分别是人脸检测模型和68关键点检测模型
anchor = (0.893, 1.463, 0.245, 0.389, 1.55, 2.58, 0.375, 0.594, 3.099, 5.038, 0.057, 0.090, 0.567, 0.904, 0.101, 0.160, 0.159, 0.255)
kpu_face_detect = KPU()
print("Loading face detection model")
kpu_face_detect.load_kmodel("/sd/face_detect.kmodel")
kpu_face_detect.init_yolo2(anchor, anchor_num=9, img_w=320, img_h=240, net_w=320, net_h=256, layer_w=10, layer_h=8, threshold=0.7, nms_value=0.2, classes=1)

kpu_lm68 = KPU()
print("Loading landmark 68 model")
kpu_lm68.load_kmodel("/sd/landmark68.kmodel")

RATIO = 0.08

while True:
    gc.collect()
    clock.tick()  # Update the FPS clock.
    img = sensor.snapshot()
    od_img.draw_image(img, 0, 0)
    od_img.pix_to_ai()
    kpu_face_detect.run_with_output(od_img)
    detections = kpu_face_detect.regionlayer_yolo2()
    fps = clock.fps()

    if len(detections) > 0:
        for det in detections:
            x1_t = max(int(det[0] - RATIO * det[2]), 0)
            x2_t = min(int(det[0] + det[2] + RATIO * det[2]), 319)
            y1_t = max(int(det[1] - RATIO * det[3]), 0)
            y2_t = min(int(det[1] + det[3] + RATIO * det[3]), 255)
            cut_img_w = x2_t - x1_t + 1
            cut_img_h = y2_t - y1_t + 1

            face_cut = img.cut(x1_t, y1_t, cut_img_w, cut_img_h)
            face_cut_128 = face_cut.resize(128, 128)
            face_cut_128.pix_to_ai()

            out = kpu_lm68.run_with_output(face_cut_128, getlist=True)
            if out is not None:
                left_eye_height = out[41][1] - out[37][1]
                right_eye_height = out[47][1] - out[43][1]
                eye_height_avg = (left_eye_height + right_eye_height) / 2
                mouth_height = out[66][1] - out[62][1]

                if eye_height_avg < 10 and mouth_height > 15:
                    print("Tired: Eyes closed, Mouth open")
                elif eye_height_avg < 10:
                    print("Tired: Eyes closed")
                elif mouth_height > 15:
                    print("Tired: Mouth open")

            del face_cut_128
            del face_cut

    img.draw_string(0, 0, "%2.1f fps" % fps, color=(0, 60, 255), scale=2.0)
    lcd.display(img)
    gc.collect()

kpu_face_detect.deinit()
kpu_lm68.deinit()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/494668.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

案例分析-IEEE 754浮点标准

案例一&#xff1a; 请分析IEEE 754双精度浮点数规格化数的表示范围。 案例二&#xff1a; 规格化浮点数的Bias为什么采用2k-1-1而不是2k-1​&#xff1f;非规范数的指数E1-Bias而不是0-Bias&#xff1f; &#xff08;1&#xff09; ① bias 127时 E e - 127 &#xff08;00…

Remote Desktop Manager for Mac:远程桌面管理软件

Remote Desktop Manager for Mac&#xff0c;是远程桌面管理的理想之选。它集成了多种远程连接技术&#xff0c;无论是SSH、RDP还是VNC&#xff0c;都能轻松应对&#xff0c;让您随时随地安全访问远程服务器和工作站。 软件下载&#xff1a;Remote Desktop Manager for Mac下载…

DevSecOps平台架构系列-互联网企业私有化DevSecOps平台典型架构

目录 一、概述 二、私有化DevSecOps平台建设思路 2.1 采用GitOps公有云建设 2.2 采用GitOps私有云建设 2.3 总结 三、GitOps及其生态组件 3.1 采用GitOps的好处 3.1.1 周边生态系统齐全 3.1.2 便于自动化的实现 3.1.3 开发人员属性GitOps 3.2 GitOps部分生态组件介绍…

动态内存操作函数使用过程中会遇见的问题

越界访问 首先我们上一个代码&#xff0c;看看这个的代码的问题 这个代码的问题显而易见 &#xff0c;就是在循环里面&#xff0c;产生了越界访问的问题&#xff0c;这里你开辟了10个整形空间&#xff0c;但是从0-10一共是11个整形空间。导致访问不合法的空间&#xff0c;从而…

【C++练级之路】【Lv.17】【STL】set类和map类的模拟实现

快乐的流畅&#xff1a;个人主页 个人专栏&#xff1a;《C语言》《数据结构世界》《进击的C》 远方有一堆篝火&#xff0c;在为久候之人燃烧&#xff01; 文章目录 引言一、红黑树&#xff08;改造版&#xff09;1.1 结点1.2 迭代器1.2.1 operator1.2.2 operator- - 1.3 本体1.…

【LeetCode热题100】105. 从前序与中序遍历序列构造二叉树(二叉树)

一.题目要求 给定两个整数数组 preorder 和 inorder &#xff0c;其中 preorder 是二叉树的先序遍历&#xff0c; inorder 是同一棵树的中序遍历&#xff0c;请构造二叉树并返回其根节点。 二.题目难度 中等 三.输入样例 示例 1: 输入: preorder [3,9,20,15,7], inorder…

考研数学|第一轮刚完,刷1800惨不忍睹怎么办?

1800题惨不忍睹&#xff0c;我有办法救你 1800题算是所有习题册里面难度比较低的题集了&#xff0c;特别是1800题基础阶段的题目&#xff0c;我一般推荐基础不好的同学在基础阶段做这个。如果1800题都觉得很难的话&#xff0c;那么其他题集就更不用说了 刷1800题错的很多&…

[CISCN2019 华北赛区 Day2 Web1]Hack World

本题首先考察的是sql注入 拿过滤字符集先跑一遍 发现以上字符集都被过滤了 尝试id1 id11 尝试id(1)(2) 这里就已经给出了个思路我们可以尝试盲注去打 id(select(ascii(mid(flag,1,1))102)from(flag)) 这里表跟列已经给了我们了&#xff0c;所以我们可以写脚本了 import reque…

STM32常用的开发工具有哪些

大家好&#xff0c;今天给大家介绍STM32常用的开发工具有哪些&#xff0c;文章末尾附有分享大家一个资料包&#xff0c;差不多150多G。里面学习内容、面经、项目都比较新也比较全&#xff01;可进群免费领取。 STM32常用的开发工具主要包括以下几类&#xff1a; 集成开发环境&…

C++枚举类型

枚举类型 枚举类型使我们可以将一组整型常量组织在一起。 和类一样&#xff0c;每个枚举类型定义了一种新的类型。 枚举属于字面值常量类型。 C包含两种枚举&#xff1a;限定作用域的和不限定作用域的。 限定作用域的枚举类型 C11新标准引入了限定作用域的枚举类型。 定…

阿里云实时计算Flink的产品化思考与实践【上】

摘要&#xff1a;本文整理自阿里云高级产品专家黄鹏程和阿里云技术专家陈婧敏在 FFA 2023 平台建设专场中的分享。内容主要为以下五部分&#xff1a; 阿里云实时计算 Flink 简介产品化思考产品化实践SQL 产品化思考及实践展望 该主题由黄鹏程和陈婧敏共同完成&#xff0c;前半程…

插入排序、归并排序、堆排序和快速排序的稳定性分析

插入排序、归并排序、堆排序和快速排序的稳定性分析 一、插入排序的稳定性二、归并排序的稳定性三、堆排序的稳定性四、快速排序的稳定性总结在计算机科学中,排序是将一组数据按照特定顺序进行排列的过程。排序算法的效率和稳定性是评价其优劣的两个重要指标。稳定性指的是在排…

【地图构建(1)】占用栅格地图构建Occupancy grid mapping

本文主要参考Probabilistic Robotics《概率机器人》一书。 其他参考&#xff1a; 弗莱堡大学课件 博客 含代码博客 0.引言 位姿已知的地图构建(mapping with known poses)的定义&#xff1a;已知机器人的位姿 x 1 : t x_{1:t} x1:t​和传感器的观测数据 z 1 : t z_{1:t} z1:t…

云原生(六)、CICD - Jenkins快速入门

Jenkuns快速入门 一、CICD概述 CICD是持续集成&#xff08;Continuous Integration&#xff09;和持续部署&#xff08;Continuous Deployment&#xff09;的缩写。它是软件开发中的一种流程和方法论&#xff0c;旨在通过自动化的方式频繁地将代码集成到共享存储库中&#xf…

zotero+word优化管理参考文献

写论文&#xff0c;整理参考文献&#xff0c;管理参考文献很麻烦&#xff0c;参考文献格式罗列很麻烦&#xff0c;论文需要修改时&#xff0c;重新调整参考文献顺序很麻烦。 zoteroword可以很好的帮助解决这个问题。 Step1 zotero软件安装 默认word你已经安装好了 step2 安…

线程局部存储(TLS)

线程局部存储&#xff08;Thread Local Storage&#xff0c;TLS&#xff09;&#xff0c;是一种变量的存储方法&#xff0c;这个变量在它所在的线程内是全局可访问的&#xff0c;但是不能被其他线程访问到&#xff0c;这样就保持了数据的线程独立性。而熟知的全局变量&#xff…

班级综合测评管理系统的设计与实现|Springboot+ Mysql+Java+ B/S结构(可运行源码+数据库+设计文档)

本项目包含可运行源码数据库LW&#xff0c;文末可获取本项目的所有资料。 推荐阅读100套最新项目持续更新中..... 2024年计算机毕业论文&#xff08;设计&#xff09;学生选题参考合集推荐收藏&#xff08;包含Springboot、jsp、ssmvue等技术项目合集&#xff09; 目录 1. …

二十 超级数据查看器 讲解稿 功能概述

二十 超级数据查看器 讲解稿 功能概述 ​ ​​点击此处 以新页面 打开B站 播放当前教学视频 点击访问app下载页面 豌豆荚 下载地址​ ​ 讲解稿 ​ 界面启动 ​ 导入 ​ 选excel文件 导入 ​ 原来的excel文件 ​ 导入进本地数据库sqlite ​ 导入成功 ​ 列…

MySQL---事务

目录 一、事务简介 二、事务操作 1.未控制事务 2.事务控制一 3.控制事务二 三、事务的四大特性 四、并发事务问题 五、事务隔离级别 一、事务简介 事务 是一组操作的集合&#xff0c;它是一个不可分割的工作单位&#xff0c;事务会把所有的操作作为一个整体一起向系统提交或…

喜讯!聚铭网络荣获《日志分类方法及系统》发明专利

近日&#xff0c;聚铭网络又喜获一项殊荣&#xff0c;其申报的《日志分类方法及系统》发明专利成功获得国家知识产权局的授权&#xff0c;正式荣获国家发明专利证书。 在信息化时代&#xff0c;网络安全问题日益凸显&#xff0c;日志分析作为保障网络安全的重要手段&#xff…