0
0

眠気を目のまばたきから判定するアプリ_part1

Posted at

メモ書き程度にしか記載していないためご了承下さい。
再度編集いたしますので、イメージだけでも掴んでいただきたいです。

今回は作成したアプリは、目のまばたきを検知することで、眠気を判定することを目標にしています。

python,mediapipe,Dlib,openCVを用いました。

以下のQiita記事を参考にスクリプトを作成しました。
https://qiita.com/mogamin/items/a65e2eaa4b27aa0a1c23

以下の論文を参考に眠気の判定をしています。
sleep_judge.png
sleep_judge_1.png

「眠くなっています」とデスクトップ通知します。
(通知のイメージ)
desktop_image.png

アプリのデザインです。
UI.png

以下はメインスクリプトになります。

qiita.py
# -*- coding:utf-8 -*-
import tkinter
import tkinter.font
import threading
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import cv2
import numpy as np
import time
import mediapipe as mp
from plyer import notification
import sys
import dlib
from imutils import face_utils
from scipy.spatial import distance

start_flag = False
quitting_flag = False

# 測定
def measurement():
    global start_flag
    global quitting_flag
 
    while not quitting_flag:
        if start_flag:
            shoot_interval = shoot_txt.get()
            shoot_interval = int(shoot_interval)
            print(f"撮影間隔:{shoot_interval}")

            notice_time = notice_txt.get()
            notice_time = int(notice_time)
            print(f"眠気の検出回数:{notice_time}")

            run_time = run_txt.get()
            run_time = int(run_time) * 60
            print(f"アプリ起動時間:{run_time}")
            print(f"{run_time/shoot_interval}")

            #動画の読み込み
            cap = cv2.VideoCapture(0) 
            #顔のモデルの読み込み
            face_cascade = cv2.CascadeClassifier('./setting/haarcascade_frontalface_alt2.xml')
            #顔のランドマークの読み込み
            face_parts_detector = dlib.shape_predictor('./setting/shape_predictor_68_face_landmarks.dat')

           
            def calc_ear(eye):
                A = distance.euclidean(eye[1], eye[5])
                B = distance.euclidean(eye[2], eye[4])
                C = distance.euclidean(eye[0], eye[3])
                eye_ear = (A + B) / (2.0 * C)
                return round(eye_ear, 3)

            def eye_marker(face_mat, position):
                for i, ((x, y)) in enumerate(position):
                    cv2.circle(face_mat, (x, y), 1, (255, 255, 255), -1)
                    cv2.putText(face_mat, str(i), (x + 2, y - 2), cv2.FONT_HERSHEY_SIMPLEX, 0.3, (255, 255, 255), 1)

            count = 0
            bad_count = 0

            while cap.isOpened():
                count += 1
                if start_flag == False:
                        print("ストップが押下されました")
                        break
                
                ret, rgb = cap.read()
                gray = cv2.cvtColor(rgb, cv2.COLOR_RGB2GRAY)
                faces = face_cascade.detectMultiScale(
                    gray, scaleFactor=1.11, minNeighbors=3, minSize=(100, 100))    

                if len(faces) == 1:
                    print("facesの検出")
                    x, y, w, h = faces[0, :]
                    cv2.rectangle(rgb, (x, y), (x + w, y + h), (255, 0, 0), 2)

                    face_gray = gray[y :(y + h), x :(x + w)]
                    scale = 480 / h
                    face_gray_resized = cv2.resize(face_gray, dsize=None, fx=scale, fy=scale)

                    face = dlib.rectangle(0, 0, face_gray_resized.shape[1], face_gray_resized.shape[0])
                    face_parts = face_parts_detector(face_gray_resized, face)
                    face_parts = face_utils.shape_to_np(face_parts)

                    left_eye = face_parts[42:48]
                    eye_marker(face_gray_resized, left_eye)

                    left_eye_ear = calc_ear(left_eye)

                    right_eye = face_parts[36:42]
                    eye_marker(face_gray_resized, right_eye)

                    right_eye_ear = calc_ear(right_eye)
                    print("left_eye>>>%f" %left_eye_ear)
                    print("right_eye>>>%f" %right_eye_ear)


                    #一定時間停止
                    time.sleep(shoot_interval)
                    print("count >>> %d" % count)

                    if count > 3:
                        if (left_eye_ear + right_eye_ear) < 0.55:
                                bad_count += 1
                                print("bad count>>>%d" % bad_count)
                                print("眠そうな目を検出")

                                #デクストップ通知する
                                if bad_count == notice_time:
                                    notification.notify(
                                                title="眠気感知アプリ",
                                                message="眠くなっています",
                                                timeout=10
                                            )
                    
                                    bad_count = 0
                    #撮影の終了
                    if count == (run_time / shoot_interval):
                        print("時間になったので撮影終了します")
                        break

                    
            print("撮影終了します")
            cap.release()
               
# スタートボタンが押された時の処理
def start_button_click(event):
    global start_flag
    start_flag = True

# ストップボタンが押された時の処理
def stop_button_click(event):
    global start_flag
    start_flag = False


# 終了ボタンが押された時の処理
def quit_app():
    global quitting_flag
    global app
    global thread1

    quitting_flag = True

    # thread1終了まで待つ
    thread1.join()
    app.destroy()

'''以下からメイン処理'''

# メインウィンドウを作成
app = tkinter.Tk()
app.title("sleepcare")
app.geometry("1000x1000")
app.configure(bg="pink")

# ボタンの作成と配置

message_shoot = tkinter.Message(app,text="撮影間隔(秒)",width=200,bg="#000fff000")
message_shoot.pack(pady=10)

shoot_txt =tkinter.Entry(app) 
shoot_txt.pack()

message_notice = tkinter.Message(app,text="眠気の検出回数(回)",width=200,bg="#000fff000")
message_notice.pack(pady=10)

notice_txt = tkinter.Entry(app)
notice_txt.pack()

message_run = tkinter.Message(app,text="測定時間(分)",width=200,bg="#000fff000")
message_run.pack(pady=10)

run_txt = tkinter.Entry(app)
run_txt.pack()

start_button = tkinter.Button(app,text="スタート",fg="blue",font=("Menlo", 30))
start_button.place(x=300,y=250)

stop_button = tkinter.Button(app,text="ストップ",fg="red",font=("Menlo", 30))
stop_button.place(x=550,y=250)

# イベント処理の設定
start_button.bind("<ButtonPress>", start_button_click)
stop_button.bind("<ButtonPress>", stop_button_click)
app.protocol("WM_DELETE_WINDOW", quit_app)

# スレッドの生成と開始
thread1 = threading.Thread(target=measurement)
thread1.start()

# メインループ
app.mainloop()
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0