0
0

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

Posted at

メモ書き程度にしか記載していないためご了承下さい。
前回の続きとなります。

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

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

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

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

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

未入力の項目があるとメッセージ表示がされます。
sleep_page_error_qiita.png

メインページのデザインを追加しました。
以前投稿した姿勢維持判定のアプリを今後載せる予定です。
main_page_qiita.png

qiita.py
# -*- coding:utf-8 -*-
import tkinter
from tkinter import messagebox
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
from PIL import Image,ImageTk


start_flag = False
quitting_flag = False

# 測定
def measurement():
    global start_flag
    global quitting_flag

    while not quitting_flag:
        while start_flag:
            shoot_interval = shoot_txt.get()
            notice_time = notice_txt.get()
            run_time = run_txt.get()
            if len(shoot_interval) == 0 or len(notice_time) == 0 or len(run_time) == 0:
                messagebox.showerror('入力エラー', '未入力の項目があります')
                start_flag = False
                break

            shoot_interval = int(shoot_interval)
            notice_time = int(notice_time)
            run_time = int(run_time) * 60

            print(f"撮影間隔:{shoot_interval}")
            print(f"眠気の検出回数:{notice_time}")
            print(f"アプリ起動時間:{run_time}")

            #動画の読み込み
            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()

#ページ遷移の関数
def changePage(page):
        page.tkraise()

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

# メインウィンドウを作成
app = tkinter.Tk()
app.title("健康増進")
app.geometry("500x500")

# # この処理をコメントアウトすると配置がズレる
app.grid_rowconfigure(0, weight=1)
app.grid_columnconfigure(0, weight=1)

# メインページフレーム作成
main_frame = tkinter.Frame()
main_frame.grid(row=0, column=0, sticky="nsew")
main_frame.configure(bg="pink")

# タイトルラベル作成
titleLabel = tkinter.Label(main_frame, text="Main Page", font=('Helvetica', '35'),bg="pink",fg="black")
titleLabel.grid(row=0, column=0,columnspan=2, sticky=tkinter.NSEW)

# フレーム1:(姿勢感知)に移動するボタン
changePageButton = tkinter.Button(main_frame, text="姿勢維持", command=lambda : changePage(frame1))
changePageButton.grid(row=1, column=0)

# フレーム2:(眠気感知)に移動するボタン
changePageButton_2 = tkinter.Button(main_frame, text="眠気感知", command=lambda : changePage(frame2))
changePageButton_2.grid(row=1, column=1)

'''姿勢感知フレーム'''
# フレーム1:(姿勢感知)作成
frame1 = tkinter.Frame()
frame1.grid(row=0, column=0, sticky="nsew")
# フレーム1:(姿勢感知)作成
titleLabel = tkinter.Label(frame1, text="姿勢維持", font=('Helvetica', '35'))
titleLabel.grid(row=1, column=0)
# フレーム1:(姿勢感知)からmainフレームに戻るボタン
back_button = tkinter.Button(frame1, text="Go to main page", command=lambda : changePage(main_frame))
back_button.grid(row=2, column=0)


'''眠気感知フレーム'''
# フレーム2:(眠気感知)作成
frame2 = tkinter.Frame()
frame2.grid(row=0, column=0, sticky="nsew")
frame2.configure(bg="#00ffff")

# フレーム2:(眠気感知)タイトルラベル作成
titleLabel = tkinter.Label(frame2, text="眠気感知", font=('Helvetica', '35'),bg ="#00ffff",fg ="black")
titleLabel.grid(row=1, column=0)

message_shoot = tkinter.Message(frame2,text="撮影間隔(秒)",width=200)
message_shoot.grid(row=2, column=0)

shoot_txt =tkinter.Entry(frame2) 
shoot_txt.grid(row=2, column=1)

message_notice = tkinter.Message(frame2,text="眠気の検出回数(回)",width=200)
message_notice.grid(row=3, column=0)

notice_txt = tkinter.Entry(frame2)
notice_txt.grid(row=3, column=1)

message_run = tkinter.Message(frame2,text="測定時間(分)",width=200)
message_run.grid(row=4, column=0)

run_txt = tkinter.Entry(frame2)
run_txt.grid(row=4, column=1)

#スタートボタン
start_img = Image.open("./img/start.jpg")
start_img = start_img.resize((100, 100))
start_img = ImageTk.PhotoImage(start_img)
start_button = tkinter.Button(frame2,image=start_img)
start_button.grid(row=5, column=0)

#ストップボタン
stop_img = Image.open("./img/stop.jpg")
stop_img = stop_img.resize((100, 100))
stop_img = ImageTk.PhotoImage(stop_img)
stop_button = tkinter.Button(frame2,image=stop_img)
stop_button.grid(row=5, column=1)

# フレーム2:(眠気感知)からmainフレームに戻るボタン
back_button = tkinter.Button(frame2, text="Go to main page", command=lambda : changePage(main_frame))
back_button.grid(row=6, column=1)

# イベント処理の設定
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()
#main_frameを一番上に表示
main_frame.tkraise()


# メインループ
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