设为首页收藏labplus社区产品资料库
30
积分值+2
12
掌控币+1
0  关注
0  粉丝
2  帖子
云天(宋秀双)
2021年08月29日

#第二届掌控板教学应用设计大赛#瞌睡AI掌控提醒器

【项目背景】

     上班族因工作量大、上网课的学生因学业繁重,长期久坐在电脑前,容易疲劳瞌睡。很多人在电脑前打瞌睡,既没有休息好,也没有工作、学习好。所以本人制作了一个“瞌睡AI掌控提醒器”,提醒去休息或提神。

【功能简介】

    本项目采用Python语言利用人脸识别库,获取人眼坐标点(两眼共取12个坐标点),采用EAR开源算法判断是否瞌睡,通过Pinpong库连接掌控板,发提示音、闪灯、文字提示,并控制舵机进行“喷雾”提神。

【硬件连接】

   注:因舵机工作时的电流较大,所以单独供电,但要注意,电源与掌控板要“共地”。


【软件程序】

一、测试舵机

要不断测试和调整,确保舵机转动时让装置“喷雾”。

代码如下:

# -*- coding: utf-8 -*-

import time

from pinpong.board import Board,Pin,Servo

Board("handpy").begin()  #初始化,选择板型和端口号,不输入端口号则进行自动识别

#Board("handpy","COM36").begin()   #windows下指定端口初始化

#Board("handpy","/dev/ttyACM0").begin()   #linux下指定端口初始化

#Board("handpy","/dev/cu.usbmodem14101").begin()   #mac下指定端口初始化

s1 = Servo(Pin(Pin.P0)) #将Pin传入Servo中初始化舵机引脚

while True:

  s1.angle(30) #控制舵机转到0度位置

  print("30")

  time.sleep(0.5)

  s1.angle(160) #控制舵机转到90度位置

  print("160")

  time.sleep(1)


喷雾测试视频


二、测试掌控板显示屏

显示屏显示“请去休息”

代码如下:

# -*- coding: utf-8 -*-

import time

from pinpong.board import Board

from pinpong.extension.handpy import *

Board("handpy").begin()#初始化,选择板型和端口号,不输入端口号则进行自动识别

#Board("handpy","COM36").begin()   #windows下指定端口初始化

#Board("handpy","/dev/ttyACM0").begin()   #linux下指定端口初始化

#Board("handpy","/dev/cu.usbmodem14101").begin()   #mac下指定端口初始化

oled.DispChar("请去休息", 42, 22)

oled.show()

while True:

  pass


三、测试“RGB LED”

使用随机函数random,生成0-255的随机数,让三个LED灯颜色不断变化。

代码如下:

# -*- coding: utf-8 -*-

import time

from pinpong.board import Board

from pinpong.extension.handpy import *

import random

Board("handpy").begin()#初始化,选择板型和端口号,不输入端口号则进行自动识别

#Board("handpy","COM36").begin()   #windows下指定端口初始化

#Board("handpy","/dev/ttyACM0").begin()   #linux下指定端口初始化

#Board("handpy","/dev/cu.usbmodem14101").begin()   #mac下指定端口初始化

#rgb.disable(-1)                                       #关闭LED灯,-1代表3个灯(可以填灯号0,1,2)

rgb.brightness(7)                                     #设置LED灯的亮度,范围0-9

while True:

   rgb[0] = (int(random.random()*255)+1, int(random.random()*255)+1, int(random.random()*255)+1)  

   rgb[1] = (int(random.random()*255)+1, int(random.random()*255)+1, int(random.random()*255)+1)

   rgb[2] = (int(random.random()*255)+1, int(random.random()*255)+1, int(random.random()*255)+1)  

   rgb.write()

   time.sleep(1)


四、测试掌控板播放音乐

# -*- coding: utf-8 -*-

import time

from pinpong.board import Board

from pinpong.extension.handpy import *

Board("handpy").begin()#初始化,选择板型和端口号,不输入端口号则进行自动识别

#Board("handpy","COM36").begin()   #windows下指定端口初始化

#Board("handpy","/dev/ttyACM0").begin()   #linux下指定端口初始化

#Board("handpy","/dev/cu.usbmodem14101").begin()   #mac下指定端口初始化

tune = ["C4:4", "D4:4", "E4:4", "C4:4", "C4:4", "D4:4", "E4:4", "C4:4",

        "E4:4", "F4:4", "G4:4", "E4:4", "F4:4", "G4:4"]

music.play(tune)

#music.set_tempo(4,60)

#设置每一拍等同于4分音符,每分钟节拍数

#music.play("C4:4")

#time.sleep(2)

#music.stop()

#停止后台播放

while True:

    pass

五、获取人脸坐标

1、face_recognition人脸识别库

    face_recognition使用世界上最简单的人脸识别库,在Python或命令行中识别和操作人脸。 使用dlib最先进的人脸识别技术构建而成,并具有深度学习功能。

2、安装:

pip install face_recognition

3、识别人脸关键点

    加载图像后,调用face_recognition.face_landmarks(image)可识别出人脸关键点信息,包括眼睛、鼻子、嘴巴和下巴等,参数仍是加载的图像image,返回值是包含面部特征字典的列表,列表中每一项对应一张人脸,包括nose_bridge、right_eyebrow、right_eye、chine、left_eyebrow、bottom_lip、nose_tip、top_lip、left_eye几个部分,每个部分包含若干个特征点(x,y),总共有68个特征点。列表长度就是图中识别出的人脸数,可遍历此列表和字典的键值对,打印出所有面部特征点,也可在图像上画出来.

4、代码如下:

import numpy as np

import cv2

import face_recognition

cap = cv2.VideoCapture(0)

while True:

    ret, frame = cap.read()

    frame = cv2.resize(frame, (0,0), fx=1, fy=1)

    # Find all facial features in all the faces in the video

    face_landmarks_list = face_recognition.face_landmarks(frame)

    for face_landmarks in face_landmarks_list:

        # Loop over each facial feature (eye, nose, mouth, lips, etc)

        for name, list_of_points in face_landmarks.items():

            hull = np.array(face_landmarks[name])

            hull_landmark = cv2.convexHull(hull)

            cv2.drawContours(frame, hull_landmark, -1, (0, 255, 0), 3)

    cv2.imshow("Frame", frame)

    ch = cv2.waitKey(1)

    if ch & 0xFF == ord('q'):

        break

cap.release()

cv2.destroyAllWindows()


六、EAR开源算法判断瞌睡

人眼疲倦检测开源算法EAR(eye aspect ratio)计算函数

我们首先需要确定眼睛的位置,在确定眼睛位置之后,选择6个点来表示眼睛,具体如下图所示:


标号的顺序是从眼睛的左角开始,然后顺时针绕着眼睛进行编号。

根据这六个点我们便可以表示眼睛的睁开和闭上的状态。当开启的时候,上图中竖着的黄色箭头会变得比较高,而眼睛闭上(疲劳状态)这个箭头就会变矮。但是由于观看的距离不同,单纯用高度来表示状态缺少参考比较,因此提出如下公式表示状态:


用这个数据便可以相对客观的表示眼睛的状态,于是通过大量测试发现一个统计结果,当EAR小于0.25很多的时候,便是疲劳状态。


七、提取两眼坐标

每只眼睛获取六个坐标点,并进行标注

代码如下:

import numpy as np

import cv2

import face_recognition

cap = cv2.VideoCapture(0)

while True:

    ret, frame = cap.read()

    frame = cv2.resize(frame, (0,0), fx=1, fy=1)

    # Find all facial features in all the faces in the video

    face_landmarks_list = face_recognition.face_landmarks(frame)

    for face_landmarks in face_landmarks_list:

        # Loop over each facial feature (eye, nose, mouth, lips, etc)

        for name, list_of_points in face_landmarks.items():

           if name=='left_eye':

            print(list_of_points)

            hull = np.array(face_landmarks[name])

            hull_landmark = cv2.convexHull(hull)

            cv2.drawContours(frame, hull_landmark, -1, (0, 255, 0), 3)

            font = cv2.FONT_HERSHEY_SIMPLEX # 获取内置字体

            k=1

            for points in list_of_points:

              cv2.putText(frame,str(k), points, font, 0.5, (255,0,255), 4) # 调用函数,对人脸坐标位置,

              k=k+1

           if name=='right_eye':

            print(list_of_points)

            hull = np.array(face_landmarks[name])

            hull_landmark = cv2.convexHull(hull)

            cv2.drawContours(frame, hull_landmark, -1, (0, 255, 0), 3)

            font = cv2.FONT_HERSHEY_SIMPLEX # 获取内置字体

            k=1

            for points in list_of_points:

              cv2.putText(frame,str(k), points, font, 0.5, (255,0,255), 4) # 调用函数,对人脸坐标位置,

              k=k+1

    cv2.imshow("Frame", frame)

    ch = cv2.waitKey(1)

    if ch & 0xFF == ord('q'):

        break

cap.release()

cv2.destroyAllWindows()


八、眼睛纵横比(EAR)函数

# 这个方程的分子是计算垂直眼睛标志之间的距离,而分母是计算水平眼睛标志之间的距离,由于水平点只有一组,而两组垂直点,所以分母乘上了2,以保证两组特征点的权重相同。使用这个简单的方程,我们可以避免使用图像处理技术,简单地依靠眼睛地标距离的比例来确定一个人是否眨眼。

def eye_aspect_ratio(eye):

             # 计算距离,竖直的

         A = dist.euclidean(eye[1], eye[2])

         B = dist.euclidean(eye[4], eye[5])

             # 计算距离,水平的

         C = dist.euclidean(eye[0], eye[3])

             # ear值

         ear = (A + B) / (2.0 * C)

         return ear

九、判断瞌睡代码

import numpy as np

import cv2

import face_recognition

from scipy.spatial import distance as dist

cap = cv2.VideoCapture(0)

# 眼睛纵横比(EAR)

# 这个方程的分子是计算垂直眼睛标志之间的距离,而分母是计算水平眼睛标志之间的距离,由于水平点只有一组,而两组垂直点,所以分母乘上了2,以保证两组特征点的权重相同。使用这个简单的方程,我们可以避免使用图像处理技术,简单地依靠眼睛地标距离的比例来确定一个人是否眨眼。

def eye_aspect_ratio(eye):

        #计算距离,竖直的

        A=dist.euclidean(eye[1],eye[5])

        B=dist.euclidean(eye[2],eye[4])

        #计算距离,水平的

        C=dist.euclidean(eye[0],eye[3])

        #ear值

        ear=(A+B)/(2.0*C)

        return ear

close_eye=0 # 闭眼计数

ClOSE_EYE=25 # 闭眼次数阈值

EYE_EAR = 0.25 # EAR阈值

while True:

    ret, frame = cap.read()

    frame = cv2.resize(frame, (0,0), fx=0.5, fy=0.5)

    # Find all facial features in all the faces in the video

    face_landmarks_list = face_recognition.face_landmarks(frame)

    left_eye_points=[]

    right_eye_points=[]

    for face_landmarks in face_landmarks_list:

        # Loop over each facial feature (eye, nose, mouth, lips, etc)

        for name, list_of_points in face_landmarks.items():

           if name=='left_eye':

            left_eye_points=list_of_points

            hull = np.array(face_landmarks[name])

            hull_landmark = cv2.convexHull(hull)

            cv2.drawContours(frame, hull_landmark, -1, (0, 255, 0), 3)

           if name=='right_eye':

            right_eye_points=list_of_points

            hull = np.array(face_landmarks[name])

            hull_landmark = cv2.convexHull(hull)

            cv2.drawContours(frame, hull_landmark, -1, (0, 255, 0), 3)

    if len(right_eye_points)!=0 and len(left_eye_points)!=0:

      # 分别计算两眼ear值

      leftEAR = eye_aspect_ratio(left_eye_points)

      rightEAR = eye_aspect_ratio(right_eye_points)

      # 算一个平均的ear值

      ear = (leftEAR + rightEAR) / 2.0

      # 当“ear”小于阈值时,为闭眼一次

      if ear<EYE_EAR:

          close_eye=close_eye+1

      else:

          close_eye=0

      if close_eye>ClOSE_EYE:

          cv2.putText(frame,"Sleep!",(20,200),cv2.FONT_HERSHEY_SIMPLEX,2,(0,0,255),3)

      cv2.putText(frame,"close_eye:"+str(close_eye),(30,30),cv2.FONT_HERSHEY_SIMPLEX,1,(0,0,255),1)

      print(ear)

    cv2.imshow("Frame", frame)

    ch = cv2.waitKey(1)

    if ch & 0xFF == ord('q'):

        break

cap.release()

cv2.destroyAllWindows()


十、完整代码

import numpy as np #导入科学计算库,CV2库需要

import cv2         #opencv-python库,用来控制摄像头,获取人脸图像

import face_recognition

from scipy.spatial import distance as dist

import time,random

from pinpong.board import Board,Pin,Servo

from pinpong.extension.handpy import *

Board("handpy").begin()  #初始化,选择板型和端口号,不输入端口号则进行自动识别

s1 = Servo(Pin(Pin.P1)) #将Pin传入Servo中初始化舵机引脚

k=0

cap = cv2.VideoCapture(0)

# 眼睛纵横比(EAR)

# 这个方程的分子是计算垂直眼睛标志之间的距离,而分母是计算水平眼睛标志之间的距离,由于水平点只有一组,而两组垂直点,所以分母乘上了2,以保证两组特征点的权重相同。使用这个简单的方程,我们可以避免使用图像处理技术,简单地依靠眼睛地标距离的比例来确定一个人是否眨眼。

def eye_aspect_ratio(eye):

        #计算距离,竖直的

        A=dist.euclidean(eye[1],eye[5])

        B=dist.euclidean(eye[2],eye[4])

        #计算距离,水平的

        C=dist.euclidean(eye[0],eye[3])

        #ear值

        ear=(A+B)/(2.0*C)

        return ear

close_eye=0 # 闭眼计数

ClOSE_EYE=25 # 闭眼次数阈值

EYE_EAR = 0.28 # EAR阈值

while True:

    ret, frame = cap.read()

    frame = cv2.resize(frame, (0,0), fx=0.5, fy=0.5)

    # Find all facial features in all the faces in the video

    face_landmarks_list = face_recognition.face_landmarks(frame)

    left_eye_points=[]

    right_eye_points=[]

    for face_landmarks in face_landmarks_list:

        # Loop over each facial feature (eye, nose, mouth, lips, etc)

        for name, list_of_points in face_landmarks.items():

           if name=='left_eye':

            left_eye_points=list_of_points

            hull = np.array(face_landmarks[name])

            hull_landmark = cv2.convexHull(hull)

            cv2.drawContours(frame, hull_landmark, -1, (0, 255, 0), 3)

           if name=='right_eye':

            right_eye_points=list_of_points

            hull = np.array(face_landmarks[name])

            hull_landmark = cv2.convexHull(hull)

            cv2.drawContours(frame, hull_landmark, -1, (0, 255, 0), 3)

    if len(right_eye_points)!=0 and len(left_eye_points)!=0:

      # 分别计算两眼ear值

      leftEAR = eye_aspect_ratio(left_eye_points)

      rightEAR = eye_aspect_ratio(right_eye_points)

      # 算一个平均的ear值

      ear = (leftEAR + rightEAR) / 2.0

      # 当“ear”小于阈值时,为闭眼一次

      if ear<EYE_EAR:

          close_eye=close_eye+1

      else:

          close_eye=0

          

      if close_eye>ClOSE_EYE:

          cv2.putText(frame,"Sleep!",(20,200),cv2.FONT_HERSHEY_SIMPLEX,2,(0,0,255),3)

          oled.DispChar("请去休息", 42, 22)

          oled.show()

          rgb[0] = (int(random.random()*255)+1, int(random.random()*255)+1, int(random.random()*255)+1)  

          rgb[1] = (int(random.random()*255)+1, int(random.random()*255)+1, int(random.random()*255)+1)

          rgb[2] = (int(random.random()*255)+1, int(random.random()*255)+1, int(random.random()*255)+1)  

          rgb.write()

          tune = ["C4:4", "D4:4", "E4:4"]

          music.play(tune)

          time.sleep(3)

          music.stop()

          s1.angle(30) #控制舵机转到0度位置

          time.sleep(0.5)

          s1.angle(160) #控制舵机转到90度位置

          rgb.disable(-1)                                       #关闭LED灯,-1代表3个灯(可以填灯号0,1,2)

          oled.fill(0)

      cv2.putText(frame,"close_eye:"+str(close_eye),(30,30),cv2.FONT_HERSHEY_SIMPLEX,1,(0,0,255),1)

    

      print(ear)

    cv2.imshow("Frame", frame)

    ch = cv2.waitKey(1)

    if ch & 0xFF == ord('q'):

        break

cap.release()

cv2.destroyAllWindows()

【演示视频】

疲劳AI提醒器演示视频

2

点赞

671 次阅读5 条评论1 条回复2 人赞1 人订阅
5条评论