Last active
February 5, 2020 07:52
-
-
Save gwanryo/c29a2858ee69a69040650fe3f7a7c388 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# https://github.com/mans-men/eye-blink-detection-demo | |
# import the necessary packages | |
from scipy.spatial import distance as dist | |
from imutils.video import FileVideoStream | |
from imutils.video import VideoStream | |
from imutils import face_utils | |
import argparse | |
import imutils | |
import time | |
import dlib | |
import cv2 | |
import serial | |
def eye_aspect_ratio(eye): | |
# compute the euclidean distances between the two sets of | |
# vertical eye landmarks (x, y)-coordinates | |
A = dist.euclidean(eye[1], eye[5]) | |
B = dist.euclidean(eye[2], eye[4]) | |
# compute the euclidean distance between the horizontal | |
# eye landmark (x, y)-coordinates | |
C = dist.euclidean(eye[0], eye[3]) | |
# compute the eye aspect ratio | |
ear = (A + B) / (2.0 * C) | |
# return the eye aspect ratio | |
return ear | |
# construct the argument parse and parse the arguments | |
ap = argparse.ArgumentParser() | |
ap.add_argument("-p", "--shape-predictor",default="shape_predictor_68_face_landmarks.dat", | |
help="path to facial landmark predictor") | |
ap.add_argument("-v", "--video", type=str, default="camera", | |
help="path to input video file") | |
ap.add_argument("-t", "--threshold", type = float, default=0.20, | |
help="threshold to determine closed eyes") | |
ap.add_argument("-f", "--frames", type = int, default=2, | |
help="the number of consecutive frames the eye must be below the threshold") | |
ap.add_argument("-s", "--serial", type=str, default="COM3", | |
help="serial port path") | |
ap.add_argument("-e", "--speed", type=int, default=9600, | |
help="serial connection speed") | |
def main() : | |
args = vars(ap.parse_args()) | |
EYE_AR_THRESH = args['threshold'] | |
EYE_AR_CONSEC_FRAMES = args['frames'] | |
# initialize the frame counters and the total number of blinks | |
COUNTER = 0 | |
TOTAL = 0 | |
SLEEP = 0 | |
SLEEP_TIME = 0 | |
# initialize serial port | |
print("[INFO] connect to serial port...") | |
ser = serial.Serial() | |
ser.port = args['serial'] | |
ser.baudrate = args['speed'] | |
try: | |
ser.open() | |
except: | |
print("[WARN] serial connection failed") | |
exit() | |
print("[INFO] verify serial is ready") | |
while not ser.is_open: | |
pass | |
# initialize dlib's face detector (HOG-based) and then create | |
# the facial landmark predictor | |
print("[INFO] loading facial landmark predictor...") | |
detector = dlib.get_frontal_face_detector() | |
predictor = dlib.shape_predictor(args["shape_predictor"]) | |
# grab the indexes of the facial landmarks for the left and | |
# right eye, respectively | |
(lStart, lEnd) = face_utils.FACIAL_LANDMARKS_IDXS["left_eye"] | |
(rStart, rEnd) = face_utils.FACIAL_LANDMARKS_IDXS["right_eye"] | |
# start the video stream thread | |
print("[INFO] starting video stream thread...") | |
print("[INFO] print q to quit...") | |
if args['video'] == "camera": | |
vs = VideoStream(src=0).start() | |
fileStream = False | |
else: | |
vs = FileVideoStream(args["video"]).start() | |
fileStream = True | |
time.sleep(1.0) | |
# verify crane is okay | |
ser.write(b'Hello, this is sleep & smash') | |
print("[INFO] crane is ready!") | |
# loop over frames from the video stream | |
while True: | |
# if this is a file video stream, then we need to check if | |
# there any more frames left in the buffer to process | |
if fileStream and not vs.more(): | |
break | |
# grab the frame from the threaded video file stream, resize | |
# it, and convert it to grayscale | |
# channels) | |
frame = vs.read() | |
frame = imutils.resize(frame, width=450) | |
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) | |
# detect faces in the grayscale frame | |
rects = detector(gray, 0) | |
# student is sleeping if camera cannot detect eyes | |
if not rects: | |
SLEEP = 1 | |
# loop over the face detections | |
for rect in rects: | |
# determine the facial landmarks for the face region, then | |
# convert the facial landmark (x, y)-coordinates to a NumPy | |
# array | |
shape = predictor(gray, rect) | |
shape = face_utils.shape_to_np(shape) | |
# extract the left and right eye coordinates, then use the | |
# coordinates to compute the eye aspect ratio for both eyes | |
leftEye = shape[lStart:lEnd] | |
rightEye = shape[rStart:rEnd] | |
leftEAR = eye_aspect_ratio(leftEye) | |
rightEAR = eye_aspect_ratio(rightEye) | |
# average the eye aspect ratio together for both eyes | |
ear = (leftEAR + rightEAR) / 2.0 | |
# compute the convex hull for the left and right eye, then | |
# visualize each of the eyes | |
leftEyeHull = cv2.convexHull(leftEye) | |
rightEyeHull = cv2.convexHull(rightEye) | |
cv2.drawContours(frame, [leftEyeHull], -1, (0, 255, 0), 1) | |
cv2.drawContours(frame, [rightEyeHull], -1, (0, 255, 0), 1) | |
# check to see if the eye aspect ratio is below the blink | |
# threshold, and if so, increment the blink frame counter | |
# student is sleeping if ear value under threshold | |
if ear < EYE_AR_THRESH: | |
COUNTER += 1 | |
SLEEP = 1 | |
# otherwise, the eye aspect ratio is not below the blink | |
# threshold | |
else: | |
# if the eyes were closed for a sufficient number of | |
# then increment the total number of blinks | |
if COUNTER >= EYE_AR_CONSEC_FRAMES: | |
TOTAL += 1 | |
# reset the eye frame counter | |
COUNTER = 0 | |
# draw the total number of blinks on the frame along with | |
# the computed eye aspect ratio for the frame | |
cv2.putText(frame, "Blinks: {}".format(TOTAL), (10, 30), | |
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2) | |
cv2.putText(frame, "EAR: {:.2f}".format(ear), (300, 30), | |
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2) | |
# if student is not SLEEP, initialize timer | |
if not SLEEP: | |
SLEEP_TIME = time.time() | |
else: | |
SLEEP = 0 | |
# if sleep time is over 3s, detect as sleeping student | |
if time.time() - SLEEP_TIME > 3.0: | |
cv2.putText(frame, "DO NOT SLEEP!", (10, 90), | |
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2) | |
ser.write(b'W') | |
SLEEP_TIME = time.time() | |
cv2.putText(frame, "SLEEP: {0:.1f}s".format(time.time() - SLEEP_TIME), (10, 60), | |
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2) | |
# show the frame | |
cv2.imshow("Frame", frame) | |
key = cv2.waitKey(1) & 0xFF | |
# if the `q` key was pressed, break from the loop | |
if key == ord("q"): | |
break | |
# do a bit of cleanup | |
ser.close() | |
cv2.destroyAllWindows() | |
vs.stop() | |
if __name__ == '__main__' : | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment