メモ書き程度にしか記載していないためご了承下さい。
再度編集いたしますので、イメージだけでも掴んでいただきたいです。
今回は作成したアプリは、目のまばたきを検知することで、眠気を判定することを目標にしています。
python,mediapipe,Dlib,openCVを用いました。
以下のQiita記事を参考にスクリプトを作成しました。
https://qiita.com/mogamin/items/a65e2eaa4b27aa0a1c23
「眠くなっています」とデスクトップ通知します。
(通知のイメージ)
以下はメインスクリプトになります。
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()