如何用OpenCV进行人脸疲劳检测--基于米尔瑞芯微RK3576开发板

发布时间:2024-12-19 11:37    发布者:swiftman
关键词: 瑞芯微 , RK3576 , 嵌入式 , OpenCV , 米尔电子 , OpenCV
本篇源自:优秀创作者 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以及线程等:
  1. import cv2
  2. import dlib
  3. import numpy as np
  4. import time
  5. from concurrent.futures import ThreadPoolExecutor
  6. import threading
复制代码

2、初始化dlib的面部检测器和特征点预测器
  1. detector = dlib.get_frontal_face_detector()
  2. predictor = dlib.shape_predictor('shape_predictor_68_face_landmarks.dat')
复制代码

3、定义计算眼睛纵横比的函数
  1. def eye_aspect_ratio(eye):
  2.     A = np.linalg.norm(np.array(eye[1]) - np.array(eye[5]))
  3.     B = np.linalg.norm(np.array(eye[2]) - np.array(eye[4]))
  4.     C = np.linalg.norm(np.array(eye[0]) - np.array(eye[3]))
  5.     ear = (A + B) / (2.0 * C)                                                                                
  6.     return ear
复制代码

4、定义计算头部姿势的函数
  1. def get_head_pose(shape):
  2.     # 定义面部特征点的三维坐标
  3.     object_points = np.array([
  4.         (0.0, 0.0, 0.0),             # 鼻尖
  5.         (0.0, -330.0, -65.0),        # 下巴
  6.         (-225.0, 170.0, -135.0),     # 左眼左眼角
  7.         (225.0, 170.0, -135.0),      # 右眼右眼角
  8.         (-150.0, -150.0, -125.0),    # 左嘴角
  9.         (150.0, -150.0, -125.0)      # 右嘴角
  10.     ], dtype=np.float32)

  11.     image_pts = np.float32([shape[i] for i in [30, 8, 36, 45, 48, 54]])
  12.     size = frame.shape
  13.     focal_length = size[1]
  14.     center = (size[1] // 2, size[0] // 2)
  15.     camera_matrix = np.array(
  16.         [[focal_length, 0, center[0]],
  17.          [0, focal_length, center[1]],
  18.          [0, 0, 1]], dtype="double"
  19.     )

  20.     dist_coeffs = np.zeros((4, 1))
  21.     (success, rotation_vector, translation_vector) = cv2.solvePnP(
  22.         object_points, image_pts, camera_matrix, dist_coeffs, flags=cv2.SOLVEPNP_ITERATIVE
  23.     )

  24.     rmat, _ = cv2.Rodrigues(rotation_vector)
  25.     angles, _, _, _, _, _ = cv2.RQDecomp3x3(rmat)
  26.     return angles
复制代码

5、定义眼睛纵横比阈值和连续帧数阈值
  1. EYE_AR_THRESH = 0.3
  2. EYE_AR_CONSEC_FRAMES = 48
复制代码

6、打开摄像头
我们先使用v4l2-ctl --list-devices来例出接在开发板上的列表信息:
  1. USB Camera: USB Camera (usb-xhci-hcd.0.auto-1.2):
  2.         /dev/video60
  3.         /dev/video61
  4.         /dev/media7
复制代码

在代码中填入60为摄像头的编号:
  1. cap = cv2.VideoCapture(60)
  2. cap.set(cv2.CAP_PROP_FRAME_WIDTH, 480)  # 降低分辨率
  3. cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 320)
复制代码

7、创建多线程处理函数,实现采集与分析分离:
  1. # 多线程处理函数
  2. def process_frame(frame):
  3.     global COUNTER, TOTAL
  4.     gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
  5.     faces = detector(gray, 0)  # 第二个参数为0,表示不使用upsampling

  6.     for face in faces:
  7.         landmarks = predictor(gray, face)
  8.         shape = [(landmarks.part(i).x, landmarks.part(i).y) for i in range(68)]
  9.         
  10.         left_eye = shape[36:42]
  11.         right_eye = shape[42:48]

  12.         left_ear = eye_aspect_ratio(left_eye)
  13.         right_ear = eye_aspect_ratio(right_eye)
  14.         ear = (left_ear + right_ear) / 2.0

  15.         if ear < EYE_AR_THRESH:
  16.             with lock:
  17.                 COUNTER += 1
  18.         else:
  19.             with lock:
  20.                 if COUNTER >= EYE_AR_CONSEC_FRAMES:
  21.                     TOTAL += 1
  22.                 COUNTER = 0

  23.         # 绘制68个特征点
  24.         for n in range(0, 68):
  25.             x, y = shape[n]
  26.             cv2.circle(frame, (x, y), 2, (0, 255, 0), -1)

  27.         cv2.putText(frame, f"Eye AR: {ear:.2f}", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, font_scale, (0, 0, 255), 2)
  28.         cv2.putText(frame, f"Blink Count: {TOTAL}", (10, 60), cv2.FONT_HERSHEY_SIMPLEX, font_scale, (0, 0, 255), 2)

  29.         # 计算头部姿势
  30.         angles = get_head_pose(shape)
  31.         pitch, yaw, roll = angles
  32.         cv2.putText(frame, f"Pitch: {pitch:.2f}", (10, 120), cv2.FONT_HERSHEY_SIMPLEX, font_scale, (0, 0, 255), 2)
  33.         cv2.putText(frame, f"Yaw: {yaw:.2f}", (10, 150), cv2.FONT_HERSHEY_SIMPLEX, font_scale, (0, 0, 255), 2)
  34.         cv2.putText(frame, f"Roll: {roll:.2f}", (10, 180), cv2.FONT_HERSHEY_SIMPLEX, font_scale, (0, 0, 255), 2)

  35.         # 判断疲劳状态
  36.         if COUNTER >= EYE_AR_CONSEC_FRAMES or abs(pitch) > 30 or abs(yaw) > 30 or abs(roll) > 30:
  37.             cv2.putText(frame, "Fatigue Detected!", (10, 210), cv2.FONT_HERSHEY_SIMPLEX, font_scale, (0, 0, 255), 2)

  38.     return frame
复制代码
8、创建图像显示线程:
  1. with ThreadPoolExecutor(max_workers=2) as executor:
  2.     future_to_frame = {}
  3.     while True:
  4.         ret, frame = cap.read()
  5.         if not ret:
  6.             break

  7.         # 提交当前帧到线程池
  8.         future = executor.submit(process_frame, frame.copy())
  9.         future_to_frame[future] = frame

  10.         # 获取已完成的任务结果
  11.         for future in list(future_to_frame.keys()):
  12.             if future.done():
  13.                 processed_frame = future.result()
  14.                 cv2.imshow("Frame", processed_frame)
  15.                 del future_to_frame[future]
  16.                 break

  17.         # 计算帧数
  18.         fps_counter += 1
  19.         elapsed_time = time.time() - start_time
  20.         if elapsed_time > 1.0:
  21.             fps = fps_counter / elapsed_time
  22.             fps_counter = 0
  23.             start_time = time.time()
  24.             cv2.putText(processed_frame, f"FPS: {fps:.2f}", (10, 90), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)

  25.         if cv2.waitKey(1) & 0xFF == ord('q'):
复制代码

实现效果:

根据检测的结果,我们就可以来实现疲劳提醒等等的功能。
整体代码如下:
  1. import cv2
  2. import dlib
  3. import numpy as np
  4. import time
  5. from concurrent.futures import ThreadPoolExecutor
  6. import threading

  7. # 初始化dlib的面部检测器和特征点预测器
  8. detector = dlib.get_frontal_face_detector()
  9. predictor = dlib.shape_predictor('shape_predictor_68_face_landmarks.dat')

  10. # 修改字体大小
  11. font_scale = 0.5  # 原来的字体大小是0.7,现在改为0.5

  12. # 定义计算眼睛纵横比的函数
  13. def eye_aspect_ratio(eye):
  14.     A = np.linalg.norm(np.array(eye[1]) - np.array(eye[5]))
  15.     B = np.linalg.norm(np.array(eye[2]) - np.array(eye[4]))
  16.     C = np.linalg.norm(np.array(eye[0]) - np.array(eye[3]))
  17.     ear = (A + B) / (2.0 * C)                                                                                
  18.     return ear

  19. # 定义计算头部姿势的函数
  20. def get_head_pose(shape):
  21.     # 定义面部特征点的三维坐标
  22.     object_points = np.array([
  23.         (0.0, 0.0, 0.0),             # 鼻尖
  24.         (0.0, -330.0, -65.0),        # 下巴
  25.         (-225.0, 170.0, -135.0),     # 左眼左眼角
  26.         (225.0, 170.0, -135.0),      # 右眼右眼角
  27.         (-150.0, -150.0, -125.0),    # 左嘴角
  28.         (150.0, -150.0, -125.0)      # 右嘴角
  29.     ], dtype=np.float32)

  30.     image_pts = np.float32([shape[i] for i in [30, 8, 36, 45, 48, 54]])
  31.     size = frame.shape
  32.     focal_length = size[1]
  33.     center = (size[1] // 2, size[0] // 2)
  34.     camera_matrix = np.array(
  35.         [[focal_length, 0, center[0]],
  36.          [0, focal_length, center[1]],
  37.          [0, 0, 1]], dtype="double"
  38.     )

  39.     dist_coeffs = np.zeros((4, 1))
  40.     (success, rotation_vector, translation_vector) = cv2.solvePnP(
  41.         object_points, image_pts, camera_matrix, dist_coeffs, flags=cv2.SOLVEPNP_ITERATIVE
  42.     )

  43.     rmat, _ = cv2.Rodrigues(rotation_vector)
  44.     angles, _, _, _, _, _ = cv2.RQDecomp3x3(rmat)
  45.     return angles

  46. # 定义眼睛纵横比阈值和连续帧数阈值
  47. EYE_AR_THRESH = 0.3
  48. EYE_AR_CONSEC_FRAMES = 48

  49. # 初始化计数器
  50. COUNTER = 0
  51. TOTAL = 0

  52. # 创建锁对象
  53. lock = threading.Lock()

  54. # 打开摄像头
  55. cap = cv2.VideoCapture(60)
  56. cap.set(cv2.CAP_PROP_FRAME_WIDTH, 480)  # 降低分辨率
  57. cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 320)

  58. # 初始化帧计数器和时间戳
  59. fps_counter = 0
  60. start_time = time.time()

  61. # 多线程处理函数
  62. def process_frame(frame):
  63.     global COUNTER, TOTAL
  64.     gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
  65.     faces = detector(gray, 0)  # 第二个参数为0,表示不使用upsampling

  66.     for face in faces:
  67.         landmarks = predictor(gray, face)
  68.         shape = [(landmarks.part(i).x, landmarks.part(i).y) for i in range(68)]
  69.         
  70.         left_eye = shape[36:42]
  71.         right_eye = shape[42:48]

  72.         left_ear = eye_aspect_ratio(left_eye)
  73.         right_ear = eye_aspect_ratio(right_eye)
  74.         ear = (left_ear + right_ear) / 2.0

  75.         if ear < EYE_AR_THRESH:
  76.             with lock:
  77.                 COUNTER += 1
  78.         else:
  79.             with lock:
  80.                 if COUNTER >= EYE_AR_CONSEC_FRAMES:
  81.                     TOTAL += 1
  82.                 COUNTER = 0

  83.         # 绘制68个特征点
  84.         for n in range(0, 68):
  85.             x, y = shape[n]
  86.             cv2.circle(frame, (x, y), 2, (0, 255, 0), -1)

  87.         cv2.putText(frame, f"Eye AR: {ear:.2f}", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, font_scale, (0, 0, 255), 2)
  88.         cv2.putText(frame, f"Blink Count: {TOTAL}", (10, 60), cv2.FONT_HERSHEY_SIMPLEX, font_scale, (0, 0, 255), 2)

  89.         # 计算头部姿势
  90.         angles = get_head_pose(shape)
  91.         pitch, yaw, roll = angles
  92.         cv2.putText(frame, f"Pitch: {pitch:.2f}", (10, 120), cv2.FONT_HERSHEY_SIMPLEX, font_scale, (0, 0, 255), 2)
  93.         cv2.putText(frame, f"Yaw: {yaw:.2f}", (10, 150), cv2.FONT_HERSHEY_SIMPLEX, font_scale, (0, 0, 255), 2)
  94.         cv2.putText(frame, f"Roll: {roll:.2f}", (10, 180), cv2.FONT_HERSHEY_SIMPLEX, font_scale, (0, 0, 255), 2)

  95.         # 判断疲劳状态
  96.         if COUNTER >= EYE_AR_CONSEC_FRAMES or abs(pitch) > 30 or abs(yaw) > 30 or abs(roll) > 30:
  97.             cv2.putText(frame, "Fatigue Detected!", (10, 210), cv2.FONT_HERSHEY_SIMPLEX, font_scale, (0, 0, 255), 2)

  98.     return frame

  99. with ThreadPoolExecutor(max_workers=2) as executor:
  100.     future_to_frame = {}
  101.     while True:
  102.         ret, frame = cap.read()
  103.         if not ret:
  104.             break

  105.         # 提交当前帧到线程池
  106.         future = executor.submit(process_frame, frame.copy())
  107.         future_to_frame[future] = frame

  108.         # 获取已完成的任务结果
  109.         for future in list(future_to_frame.keys()):
  110.             if future.done():
  111.                 processed_frame = future.result()
  112.                 cv2.imshow("Frame", processed_frame)
  113.                 del future_to_frame[future]
  114.                 break

  115.         # 计算帧数
  116.         fps_counter += 1
  117.         elapsed_time = time.time() - start_time
  118.         if elapsed_time > 1.0:
  119.             fps = fps_counter / elapsed_time
  120.             fps_counter = 0
  121.             start_time = time.time()
  122.             cv2.putText(processed_frame, f"FPS: {fps:.2f}", (10, 90), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)

  123.         if cv2.waitKey(1) & 0xFF == ord('q'):
  124.             break

  125. # 释放摄像头并关闭所有窗口
  126. cap.release()
  127. cv2.destroyAllWindows()
复制代码

【总结】
【米尔MYC-LR3576核心板及开发板】
这块开发板性能强大,能轻松实现对人脸的疲劳检测,通过计算结果后进入非常多的工业、人工智能等等的实用功能。
本文地址:https://www.eechina.com/thread-878896-1-1.html     【打印本页】

本站部分文章为转载或网友发布,目的在于传递和分享信息,并不代表本网赞同其观点和对其真实性负责;文章版权归原作者及原出处所有,如涉及作品内容、版权和其它问题,我们将根据著作权人的要求,第一时间更正或删除。
您需要登录后才可以发表评论 登录 | 立即注册

厂商推荐

  • Microchip视频专区
  • PIC18-Q71系列MCU概述
  • 无线充电基础知识及应用培训教程3
  • 无线充电基础知识及应用培训教程2
  • 了解一下Microchip强大的PIC18-Q24 MCU系列
  • 贸泽电子(Mouser)专区

相关视频

关于我们  -  服务条款  -  使用指南  -  站点地图  -  友情链接  -  联系我们
电子工程网 © 版权所有   京ICP备16069177号 | 京公网安备11010502021702
快速回复 返回顶部 返回列表