2024-12-19 10:57
79 次阅读|
0 个评论
本篇源自:优秀创作者 lulugl 本文将介绍基于米尔电子MYD-LR3576开发板(米尔基于瑞芯微 RK3576开发板)的人脸疲劳检测方案测试。 米尔基于RK3576核心板/开发板 【前言】 人脸疲劳检测 :一种通过分析人脸特征来判断一个人是否处于疲劳状态的技术。其原理主要基于计算机视觉和机器学习方法。当人疲劳时,面部会出现一些特征变化,如眼睛闭合程度增加、眨眼频率变慢、打哈欠、头部姿态改变等。 例如,通过检测眼睛的状态来判断疲劳程度是一个关键部分。正常情况下,人的眨眼频率相对稳定,而当疲劳时,眨眼频率会降低,并且每次眨眼时眼睛闭合的时间可能会延长。同时,头部可能会不自觉地下垂或者摇晃,这些特征都可以作为疲劳检测的依据。米尔MYC-LR3576采用8核CPU+搭载6 TOPS的NPU加速器,3D GPU,能够非常轻松的实现这个功能,下面就如何实现这一功能分享如下: 【硬件】 1、米尔MYC-LR3576开发板 2、USB摄像头 【软件】 1、v4l2 2、openCV 3、dlib库:dlib 是一个现代化的 C++ 工具包,它包含了许多用于机器学习、图像处理、数值计算等多种任务的算法和工具。它的设计目标是提供高性能、易于使用的库,并且在开源社区中被广泛应用。 【实现步骤】 1、安装python-opencv 2、安装dlib库 3、安装v4l2库 【 代码实现】 1、引入cv2、dlib以及线程等: import cv2 import dlib import numpy as np import time from concurrent. futures import ThreadPoolExecutor import threading 2、初始化dlib的面部检测器和特征点预测器 detector = dlib.get_frontal_face_detector() predictor = dlib.shape_predictor( 'shape_predictor_68_face_landmarks.dat' ) 3、定义计算眼睛纵横比的函数 def eye_aspect_ratio (eye): A = np.linalg. norm (np. array (eye ) - np. array (eye )) B = np.linalg. norm (np. array (eye ) - np. array (eye )) C = np.linalg. norm (np. array (eye ) - np. array (eye )) ear = (A + B) / ( 2.0 * C) return ear 4、定义计算头部姿势的函数 def get_head_pose ( shape ): # 定义面部特征点的三维坐标 object_points = np.array( , dtype=np.float32) image_pts = np.float32( for i in ]) size = frame.shape focal_length = size center = (size // 2, size // 2) camera_matrix = np.array( ], ], ], dtype= "double" ) dist_coeffs = np.zeros(( 4 , 1 )) (success, rotation_vector, translation_vector) = cv2.solvePnP( object_points, image_pts, camera_matrix, dist_coeffs, flags=cv2.SOLVEPNP_ITERATIVE ) rmat, _ = cv2.Rodrigues(rotation_vector) angles, _, _, _, _, _ = cv2.RQDecomp3x3(rmat) return angles 5、定义眼睛纵横比阈值和连续帧数阈值 EYE_AR_THRESH = 0.3 EYE_AR_CONSEC_FRAMES = 48 6、打开摄像头 我们先使用v4l2-ctl --list-devices来例出接在开发板上的列表信息: USB Camera: USB Camera (usb-xhci-hcd.0.auto-1.2): /dev/video60 /dev/video61 /dev/media7 在代码中填入60为摄像头的编号: cap = cv2.VideoCapture( 60 ) cap. set (cv2.CAP_PROP_FRAME_WIDTH, 480 ) # 降低分辨率 cap. set (cv2.CAP_PROP_FRAME_HEIGHT, 320 ) 7、创建多线程处理函数,实现采集与分析分离: # 多线程处理函数 def process_frame ( frame ): global COUNTER, TOTAL gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) faces = detector(gray, 0 ) # 第二个参数为0,表示不使用upsampling for face in faces: landmarks = predictor(gray, face) shape = left_eye = shape right_eye = shape left_ear = eye_aspect_ratio(left_eye) right_ear = eye_aspect_ratio(right_eye) ear = (left_ear + right_ear) / 2.0 if ear < EYE_AR_THRESH: with lock: COUNTER += 1 else : with lock: if = EYE_AR_CONSEC_FRAMES: TOTAL += 1 COUNTER = 0 # 绘制68个特征点 for n in range ( 0 , 68 ): x, y = shape cv2.circle(frame, (x, y), 2 , ( 0 , 255 , 0 ), - 1 ) cv2.putText(frame, f"Eye AR: {ear: .2 f} " , ( 10 , 30 ), cv2.FONT_HERSHEY_SIMPLEX, font_scale, ( 0 , 0 , 255 ), 2 ) cv2.putText(frame, f"Blink Count: {TOTAL} " , ( 10 , 60 ), cv2.FONT_HERSHEY_SIMPLEX, font_scale, ( 0 , 0 , 255 ), 2 ) # 计算头部姿势 angles = get_head_pose(shape) pitch, yaw, roll = angles cv2.putText(frame, f"Pitch: {pitch: .2 f} " , ( 10 , 120 ), cv2.FONT_HERSHEY_SIMPLEX, font_scale, ( 0 , 0 , 255 ), 2 ) cv2.putText(frame, f"Yaw: {yaw: .2 f} " , ( 10 , 150 ), cv2.FONT_HERSHEY_SIMPLEX, font_scale, ( 0 , 0 , 255 ), 2 ) cv2.putText(frame, f"Roll: {roll: .2 f} " , ( 10 , 180 ), cv2.FONT_HERSHEY_SIMPLEX, font_scale, ( 0 , 0 , 255 ), 2 ) # 判断疲劳状态 if = EYE_AR_CONSEC_FRAMES or abs 30 or abs 30 or abs 30 : cv2.putText(frame, "Fatigue Detected!" , ( 10 , 210 ), cv2.FONT_HERSHEY_SIMPLEX, font_scale, ( 0 , 0 , 255 ), 2 ) return frame 8、创建图像显示线程: with ThreadPoolExecutor (max_workers= 2 ) as executor: future_to_frame = {} while True: ret, frame = cap. read () if not ret: break # 提交当前帧到线程池 future = executor. submit (process_frame, frame. copy ()) future_to_frame = frame # 获取已完成的任务结果 for future in list (future_to_frame. keys ()): if future. done (): processed_frame = future. result () cv2. imshow ( "Frame" , processed_frame) del future_to_frame break # 计算帧数 fps_counter += 1 elapsed_time = time. time 1.0 : fps = fps_counter / elapsed_time fps_counter = 0 start_time = time. time () cv2. putText (processed_frame, f "FPS: {fps:.2f}" , ( 10 , 90 ), cv2.FONT_HERSHEY_SIMPLEX, 0.7 , ( 0 , 0 , 255 ), 2 ) if cv2. waitKey ( 1 ) & 0 xFF == ord ( 'q' ): 实现效果: 根据检测的结果,我们就可以来实现疲劳提醒等等的功能。 整体代码如下: import cv2 import dlib import numpy as np import time from concurrent.futures import ThreadPoolExecutor import threading # 初始化dlib的面部检测器和特征点预测器 detector = dlib.get_frontal_face_detector() predictor = dlib.shape_predictor( 'shape_predictor_68_face_landmarks.dat' ) # 修改字体大小 font_scale = 0.5 # 原来的字体大小是0.7,现在改为0.5 # 定义计算眼睛纵横比的函数 def eye_aspect_ratio ( eye ): A = np.linalg.norm(np.array(eye ) - np.array(eye )) B = np.linalg.norm(np.array(eye ) - np.array(eye )) C = np.linalg.norm(np.array(eye ) - np.array(eye )) ear = (A + B) / ( 2.0 * C) return ear # 定义计算头部姿势的函数 def get_head_pose ( shape ): # 定义面部特征点的三维坐标 object_points = np.array( , dtype=np.float32) image_pts = np.float32( for i in ]) size = frame.shape focal_length = size center = (size // 2 , size // 2 ) camera_matrix = np.array( ], ], ], dtype= "double" ) dist_coeffs = np.zeros(( 4 , 1 )) (success, rotation_vector, translation_vector) = cv2.solvePnP( object_points, image_pts, camera_matrix, dist_coeffs, flags=cv2.SOLVEPNP_ITERATIVE ) rmat, _ = cv2.Rodrigues(rotation_vector) angles, _, _, _, _, _ = cv2.RQDecomp3x3(rmat) return angles # 定义眼睛纵横比阈值和连续帧数阈值 EYE_AR_THRESH = 0.3 EYE_AR_CONSEC_FRAMES = 48 # 初始化计数器 COUNTER = 0 TOTAL = 0 # 创建锁对象 lock = threading.Lock() # 打开摄像头 cap = cv2.VideoCapture( 60 ) cap. set (cv2.CAP_PROP_FRAME_WIDTH, 480 ) # 降低分辨率 cap. set (cv2.CAP_PROP_FRAME_HEIGHT, 320 ) # 初始化帧计数器和时间戳 fps_counter = 0 start_time = time.time() # 多线程处理函数 def process_frame ( frame ): global COUNTER, TOTAL gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) faces = detector(gray, 0 ) # 第二个参数为0,表示不使用upsampling for face in faces: landmarks = predictor(gray, face) shape = left_eye = shape right_eye = shape left_ear = eye_aspect_ratio(left_eye) right_ear = eye_aspect_ratio(right_eye) ear = (left_ear + right_ear) / 2.0 if ear < EYE_AR_THRESH: with lock: COUNTER += 1 else : with lock: if = EYE_AR_CONSEC_FRAMES: TOTAL += 1 COUNTER = 0 # 绘制68个特征点 for n in range ( 0 , 68 ): x, y = shape cv2.circle(frame, (x, y), 2 , ( 0 , 255 , 0 ), - 1 ) cv2.putText(frame, f"Eye AR: {ear: .2 f} " , ( 10 , 30 ), cv2.FONT_HERSHEY_SIMPLEX, font_scale, ( 0 , 0 , 255 ), 2 ) cv2.putText(frame, f"Blink Count: {TOTAL} " , ( 10 , 60 ), cv2.FONT_HERSHEY_SIMPLEX, font_scale, ( 0 , 0 , 255 ), 2 ) # 计算头部姿势 angles = get_head_pose(shape) pitch, yaw, roll = angles cv2.putText(frame, f"Pitch: {pitch: .2 f} " , ( 10 , 120 ), cv2.FONT_HERSHEY_SIMPLEX, font_scale, ( 0 , 0 , 255 ), 2 ) cv2.putText(frame, f"Yaw: {yaw: .2 f} " , ( 10 , 150 ), cv2.FONT_HERSHEY_SIMPLEX, font_scale, ( 0 , 0 , 255 ), 2 ) cv2.putText(frame, f"Roll: {roll: .2 f} " , ( 10 , 180 ), cv2.FONT_HERSHEY_SIMPLEX, font_scale, ( 0 , 0 , 255 ), 2 ) # 判断疲劳状态 if = EYE_AR_CONSEC_FRAMES or abs 30 or abs 30 or abs 30 : cv2.putText(frame, "Fatigue Detected!" , ( 10 , 210 ), cv2.FONT_HERSHEY_SIMPLEX, font_scale, ( 0 , 0 , 255 ), 2 ) return frame with ThreadPoolExecutor(max_workers= 2 ) as executor: future_to_frame = {} while True : ret, frame = cap.read() if not ret: break # 提交当前帧到线程池 future = executor.submit(process_frame, frame.copy()) future_to_frame = frame # 获取已完成的任务结果 for future in list (future_to_frame.keys()): if future.done(): processed_frame = future.result() cv2.imshow( "Frame" , processed_frame) del future_to_frame break # 计算帧数 fps_counter += 1 elapsed_time = time.time() - start_time if 1.0 : fps = fps_counter / elapsed_time fps_counter = 0 start_time = time.time() cv2.putText(processed_frame, f"FPS: {fps: .2 f} " , ( 10 , 90 ), cv2.FONT_HERSHEY_SIMPLEX, 0.7 , ( 0 , 0 , 255 ), 2 ) if cv2.waitKey( 1 ) & 0xFF == ord ( 'q' ): break # 释放摄像头并关闭所有窗口 cap.release() cv2.destroyAllWindows() 【总结】 【米尔MYC-LR3576核心板及开发板】 这块开发板性能强大,能轻松实现对人脸的疲劳检测,通过计算结果后进入非常多的工业、人工智能等等的实用功能。