PyImageSearch / imutils

A series of convenience functions to make basic image processing operations such as translation, rotation, resizing, skeletonization, and displaying Matplotlib images easier with OpenCV and Python.
MIT License
4.54k stars 1.03k forks source link

Camera Read and Write FPS not syncced - Python #203

Closed weihan08 closed 4 years ago

weihan08 commented 4 years ago

Hi I am using Raspberry Pi 4b+ (4GB RAM) to implement a driver drowsiness detection system. The problem is apparently the processing of image and drowsiness determination is very taxing, so when the camera points at me the FPS reduces to 13 (Normally it is able to handle 30 FPS @ Full HD), whereas when the camera points to anywhere without a face, the FPS rises to 30. I have used threads to optimize the I/O read, but it doesn't seem to improve the FPS.

In the end, I don't mind if the whole video is 13 FPS, but the problem is when writing the video using openCV VideoWriter, I can only initialize the FPS once. So, when the camera FPS is variable but the video write is fixed, the video written will be fluctuating in speed all the time (slow with no face; normal with face).

As you can see from the code, for some reason, initializing the camera framerate to match the Video Write doesn't help. Writing is still not in sync for some reason.

Hope someone can help! Thanks in advance.

`#python drowniness_yawn.py --webcam webcam_index

from scipy.spatial import distance as dist

from imutils.video import VideoStream

from imutils.video.pivideostream import PiVideoStream from imutils.video import FPS from imutils import face_utils from threading import Thread import numpy as np import argparse import imutils import datetime as dt import time import dlib import cv2 import os

def alarm(msg): global alarm_status global alarm_status2 global saying

while alarm_status:
    print('Eyes Closed')
    saying = True

s = 'espeak "'+msg+'"'

os.system(s)

    saying = False

if alarm_status2:
    print('Yawn')
    saying = True

s = 'espeak "' + msg + '"'

os.system(s)

    saying = False

def eye_aspect_ratio(eye): A = dist.euclidean(eye[1], eye[5]) B = dist.euclidean(eye[2], eye[4])

C = dist.euclidean(eye[0], eye[3])

ear = (A + B) / (2.0 * C)

return ear

def final_ear(shape): (lStart, lEnd) = face_utils.FACIAL_LANDMARKS_IDXS["left_eye"] (rStart, rEnd) = face_utils.FACIAL_LANDMARKS_IDXS["right_eye"]

leftEye = shape[lStart:lEnd]
rightEye = shape[rStart:rEnd]

leftEAR = eye_aspect_ratio(leftEye)
rightEAR = eye_aspect_ratio(rightEye)

ear = (leftEAR + rightEAR) / 2.0
return (ear, leftEye, rightEye)

def lip_distance(shape): top_lip = shape[50:53] top_lip = np.concatenate((top_lip, shape[61:64]))

low_lip = shape[56:59]
low_lip = np.concatenate((low_lip, shape[65:68]))

top_mean = np.mean(top_lip, axis=0)
low_mean = np.mean(low_lip, axis=0)

distance = abs(top_mean[1] - low_mean[1])
return distance

ap = argparse.ArgumentParser() ap.add_argument("-w", "--webcam", type=int, default=0, help="index of webcam on system") args = vars(ap.parse_args())

EYE_AR_THRESH = 0.3 EYE_AR_CONSEC_FRAMES = 45 YAWN_THRESH = 40 alarm_status = False alarm_status2 = False saying = False COUNTER = 0

print("-> Loading the predictor and detector...")

detector = dlib.get_frontal_face_detector()

detector = cv2.CascadeClassifier("haarcascade_frontalface_default.xml") #Faster but less accurate predictor = dlib.shape_predictor('shape_predictor_68_face_landmarks.dat')

print("-> Starting Video Stream")

vs = VideoStream(src=args["webcam"]).start()

vs= PiVideoStream().start() vs.camera.framerate = 13

vs= VideoStream(usePiCamera=True, framerate = 15).start() #For Raspberry Pi

time.sleep(1.0)

fps = FPS().start()

fourcc = cv2.VideoWriter_fourcc(*"mp4v") writer = None (h,w) = (None, None)

while True: timestamp = dt.datetime.now().strftime("%Y-%m-%d %H:%M:%S") frame = vs.read() frame = imutils.resize(frame, width=400) gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

fps.update()
fps.stop()

NEW_FPS = round(fps.fps(),2)
print(NEW_FPS)
if writer is None:
    (height,width,layers)= frame.shape
    writer = cv2.VideoWriter('/home/pi/Desktop/video.mp4', fourcc, 13 , (width,height), True)
cv2.putText(frame, timestamp,(130, 280), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (0, 0, 255), 2)
#rects = detector(gray, 0)
rects = detector.detectMultiScale(gray, scaleFactor=1.1, 
    minNeighbors=5, minSize=(30, 30),
    flags=cv2.CASCADE_SCALE_IMAGE)

#for rect in rects:
for (x, y, w, h) in rects:
    rect = dlib.rectangle(int(x), int(y), int(x + w),int(y + h))

    shape = predictor(gray, rect)
    shape = face_utils.shape_to_np(shape)

    eye = final_ear(shape)
    ear = eye[0]
    leftEye = eye[1]
    rightEye = eye[2]

    distance = lip_distance(shape)

    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)

    lip = shape[48:60]
    cv2.drawContours(frame, [lip], -1, (0, 255, 0), 1)

    if ear < EYE_AR_THRESH:
        COUNTER += 1

        if COUNTER >= EYE_AR_CONSEC_FRAMES:
            if alarm_status == False and saying == False:
                alarm_status = True
                t = Thread(target=alarm, args=('wake up sir',))
                t.deamon = True
                t.start()

            cv2.putText(frame, "DROWSINESS ALERT!", (10, 30),
                        cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)

    else:
        COUNTER = 0
        alarm_status = False

    if (distance > YAWN_THRESH):
            cv2.putText(frame, "Yawn Alert", (10, 30),
                        cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
            if alarm_status2 == False and saying == False:
                alarm_status2 = True
                t = Thread(target=alarm, args=('take some fresh air sir',))
                t.deamon = True
                t.start()
    else:
        alarm_status2 = False

    cv2.putText(frame, "EAR: {:.2f}".format(ear), (250, 30),
                cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
    cv2.putText(frame, "YAWN: {:.2f}".format(distance), (250, 60),
                cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)

writer.write(frame)
cv2.imshow("Frame", frame)
key = cv2.waitKey(1) & 0xFF

if key == ord("q"):
    fps.stop()
    print("Elapsed Time: {:.2f}".format(fps.elapsed()))
    print("approx FPS: {:.2f}".format(fps.fps()))
    break

cv2.destroyAllWindows() vs.stop() writer.release()`