如何使用OpenCV-Python-dlib实现有关闭眼的检测、眨眼次数的计算?(附源码,绝对可用)

OpenCV杂谈_10


一. 需要做的前期准备

  1. 环境配置:
    Python版本:3.6.0(这里需要注意一下,如果你的python版本为>=3.9,对于dlib包的导入将会很困难,具体导入方法博主还没有解决)
    功能包:scipy (1.5.4)、imutils (0.5.4)、argparse (1.4.0)、dlib (19.22.0)、opencv-python (4.5.2.54)
  2. 如果想要对录制好的视频进行处理则需要提前获得的视频
  3. 下载shape_predictor_68_face_landmarks.dat,作者把它传到了云盘里,需要的自取:链接在此 提取码:demo
  4. 一个用的顺手的IDE(本人推荐Pycharm)

二. 须知

  1. 68 points face landmark:
    如何使用OpenCV-Python-dlib实现有关闭眼的检测、眨眼次数的计算?(附源码,绝对可用)_第1张图片
  2. 眼睛距离计算
    如何使用OpenCV-Python-dlib实现有关闭眼的检测、眨眼次数的计算?(附源码,绝对可用)_第2张图片
    如何使用OpenCV-Python-dlib实现有关闭眼的检测、眨眼次数的计算?(附源码,绝对可用)_第3张图片

三. 源码如下(闭眼检测)

import cv2
import dlib
from scipy.spatial import distance


def calculate_EAR(eye):
"""计算眼睛之间的距离"""
    A = distance.euclidean(eye[1], eye[5])
    B = distance.euclidean(eye[2], eye[4])
    C = distance.euclidean(eye[0], eye[3])
    ear_aspect_ratio = (A + B) / (2.0 * C)
    return ear_aspect_ratio

# 读取webcamera、dector、dat文件
cap = cv2.VideoCapture(0)
hog_face_detector = dlib.get_frontal_face_detector()
dlib_facelandmark = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")

# 循环处理
while True:
    _, frame = cap.read()
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)  # 转灰度

    faces = hog_face_detector(gray)  # 检测脸部特征点
    for face in faces:
        face_landmarks = dlib_facelandmark(gray, face)
        leftEye = []
        rightEye = []
		
		# 找左眼	
        for n in range(36, 42):  # 36~41 代表左眼
            x = face_landmarks.part(n).x
            y = face_landmarks.part(n).y
            leftEye.append((x, y))
            next_point = n + 1
            if n == 41:  # 绕回来好实现最后对眼睛的画圈
                next_point = 36
            x2 = face_landmarks.part(next_point).x
            y2 = face_landmarks.part(next_point).y
            cv2.line(frame, (x, y), (x2, y2), (0, 255, 0), 1)  # 圈出左眼
		
		# 找右眼
        for n in range(42, 48):  # 42~47代表右眼
            x = face_landmarks.part(n).x
            y = face_landmarks.part(n).y
            rightEye.append((x, y))
            next_point = n + 1
            if n == 47:  # 绕回来好实现对眼睛的画圈
                next_point = 42
            x2 = face_landmarks.part(next_point).x
            y2 = face_landmarks.part(next_point).y
            cv2.line(frame, (x, y), (x2, y2), (0, 255, 0), 1)  # 圈出右眼

		# 计算每只眼睛的睁开程度
        left_ear = calculate_EAR(leftEye)
        right_ear = calculate_EAR(rightEye)
		# 做个平均
        EAR = (left_ear + right_ear) / 2
        EAR = round(EAR, 2)
        if EAR < 0.26:  # 如果距离小于0.26,进行提示
            cv2.putText(frame, "WAKE UP", (20, 100),
                        cv2.FONT_HERSHEY_SIMPLEX, 3, (0, 0, 255), 4)
            cv2.putText(frame, "Are you Sleepy?", (20, 400),
                        cv2.FONT_HERSHEY_SIMPLEX, 2, (0, 0, 255), 4)
            print("!!!")
        print(EAR)

    cv2.imshow("Are you Sleepy", frame)

    key = cv2.waitKey(1)
    if key == 27:
        break
cap.release()
cv2.destroyAllWindows()

四. 源码如下(眨眼次数计算,注释懒得写成中文了,自行理解)

from scipy.spatial import distance as dist
from imutils.video import FileVideoStream
from imutils.video import VideoStream
from imutils import face_utils
import imutils
import time
import dlib
import cv2


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

# read the dat file and video to be processed
p = 'shape_predictor_68_face_landmarks.dat'
v = "video.mp4"

# define two constants, one for the eye aspect ratio to indicate
# blink and then a second constant for the number of consecutive frames the eye must be below the threshold

EYE_AR_THRESH = 0.25
EYE_AR_CONSEC_FRAMES = 2

# initialize the frame counters and the total number of blinks
COUNTER = 0
TOTAL = 0

# 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(p)

# 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...")

# when you want use your wbcamera un# these two lines
# vs = FileVideoStream(v).start()
# fileStream = True

# when you want to read videos use these two lines
vs = VideoStream(src=0).start()
fileStream = False

time.sleep(1.0)

# 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)
    rects = detector(gray, 0)

    # 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
        if ear < EYE_AR_THRESH:
            COUNTER += 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)
        cv2.putText(frame, "COUNTER: {}".format(COUNTER), (140, 30),
                    cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)

    # show the frame 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

# cleanup
cv2.destroyAllWindows()
vs.stop()

五. 感悟与分享

  1. 有关闭眼检测的教程:https://www.youtube.com/watch?v=OCJSJ-anywc&t=305s&ab_channel=MisbahMohammed
  2. 有关眨眼次数计算的教程:https://www.pyimagesearch.com/2017/04/24/eye-blink-detection-opencv-python-dlib/
    以上内容均为英文,且需要。
  3. 其实原理并不复杂,就是通过检测出眼睛,之后再计算眼皮之间的距离,来获得最终想要的数据。

如有问题,敬请指正。欢迎转载,但请注明出处。

你可能感兴趣的:(python,人工智能,opencv)