• 方案介绍
  • 相关推荐
申请入驻 产业图谱

人脸疲劳检测应用-米尔基于RK3576核心板/开发板

2024/12/20
1517
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

本文将介绍基于米尔电子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?timefrom?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[1])?-?np.array(eye[5]))
B?=?np.linalg.norm(np.array(eye[2])?-?np.array(eye[4]))
C?=?np.linalg.norm(np.array(eye[0])?-?np.array(eye[3]))
ear = (A + B) / (2.0 * C)
return ear

4、定义计算头部姿势的函数

def?get_head_pose(shape):
#?定义面部特征点的三维坐标
object_points?=?np.array([
(0.0,?0.0,?0.0),?????????????#?鼻尖
(0.0,?-330.0,?-65.0),????????#?下巴
(-225.0,?170.0,?-135.0),?????#?左眼左眼角
(225.0, 170.0, -135.0), # 右眼右眼角
(-150.0,?-150.0,?-125.0),????#?左嘴角
(150.0,?-150.0,?-125.0)??????#?右嘴角
],?dtype=np.float32)

image_pts?=?np.float32([shape[i]?for?i?in?[30,?8,?36,?45,?48,?54]])
size?=?frame.shape
focal_length?=?size[1]
center?=?(size[1]?//?2,?size[0]?//?2)
camera_matrix?=?np.array(
[[focal_length,?0,?center[0]],
[0,?focal_length,?center[1]],
[0,?0,?1]],?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/media

在代码中填入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?=?[(landmarks.part(i).x,?landmarks.part(i).y)?for?i?in?range(68)]

left_eye?=?shape[36:42]
right_eye?=?shape[42:48]

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?COUNTER?>=?EYE_AR_CONSEC_FRAMES:
TOTAL?+=?1
COUNTER?=?0

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

cv2.putText(frame,?f"Eye?AR:?{ear:.2f}",?(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:.2f}",?(10,?120),?cv2.FONT_HERSHEY_SIMPLEX,?font_scale,?(0,?0,?255),?2)
cv2.putText(frame,?f"Yaw:?{yaw:.2f}",?(10,?150),?cv2.FONT_HERSHEY_SIMPLEX,?font_scale,?(0,?0,?255),?2)
cv2.putText(frame,?f"Roll:?{roll:.2f}",?(10,?180),?cv2.FONT_HERSHEY_SIMPLEX,?font_scale,?(0,?0,?255),?2)

#?判断疲劳状态
if?COUNTER?>=?EYE_AR_CONSEC_FRAMES?or?abs(pitch)?>?30?or?abs(yaw)?>?30?or?abs(roll)?>?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[future]?=?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[future]
break

#?计算帧数
fps_counter?+=?1
elapsed_time?=?time.time()?-?start_time
if?elapsed_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) & 0xFF == 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[1])?-?np.array(eye[5]))
B?=?np.linalg.norm(np.array(eye[2])?-?np.array(eye[4]))
C?=?np.linalg.norm(np.array(eye[0])?-?np.array(eye[3]))
ear?=?(A?+?B)?/?(2.0?*?C)
return?ear

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

image_pts?=?np.float32([shape[i]?for?i?in?[30,?8,?36,?45,?48,?54]])
size?=?frame.shape
focal_length?=?size[1]
center?=?(size[1]?//?2,?size[0]?//?2)
camera_matrix?=?np.array(
[[focal_length,?0,?center[0]],
[0,?focal_length,?center[1]],
[0,?0,?1]],?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 = [(landmarks.part(i).x, landmarks.part(i).y) for i in range(68)]

left_eye?=?shape[36:42]
right_eye?=?shape[42:48]

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?COUNTER?>=?EYE_AR_CONSEC_FRAMES:
TOTAL?+=?1
COUNTER?=?0

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

cv2.putText(frame,?f"Eye?AR:?{ear:.2f}",?(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:.2f}",?(10,?120),
cv2.FONT_HERSHEY_SIMPLEX,?font_scale,?(0,?0,?255),?2)
cv2.putText(frame,?f"Yaw:?{yaw:.2f}",?(10,?150),?cv2.FONT_HERSHEY_SIMPLEX,?font_scale,?(0,?0,?255),?2) cv2.putText(frame, f"Roll: {roll:.2f}", (10, 180), cv2.FONT_HERSHEY_SIMPLEX, font_scale, (0, 0, 255), 2)

#?判断疲劳状态????????if?COUNTER?>=?EYE_AR_CONSEC_FRAMES?or?abs(pitch)?>?30?or?abs(yaw)?>?30?or?abs(roll)?>?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[future]?=?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[future]
break

#?计算帧数
fps_counter?+=?1
elapsed_time?=?time.time()?-?start_time
if?elapsed_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)?&?0xFF?==?ord('q'):
break

#?释放摄像头并关闭所有窗口
cap.release()
cv2.destroyAllWindows()

【总结】

【米尔MYC-LR3576核心板及开发板】

这块开发板性能强大,能轻松实现对人脸的疲劳检测,通过计算结果后进入非常多的工业、人工智能等等的实用功能。米尔RK3576开发板折扣活动

米尔科技

米尔科技

米尔电子,是一家专注于嵌入式处理器模组设计、研发、生产和销售于一体的国家级高新技术企业,也被评为专精特新企业。米尔电子深耕嵌入式领域10多年,致力于为企业级客户提供基于ARM、FPGA、RISC-V和AI等各种架构,稳定可靠的处理器模组,满足客户大批量产品应用部署的需求,同时为客户提供产品定制设计、行业应用解决方案和OEM的一站式服务。

米尔电子,是一家专注于嵌入式处理器模组设计、研发、生产和销售于一体的国家级高新技术企业,也被评为专精特新企业。米尔电子深耕嵌入式领域10多年,致力于为企业级客户提供基于ARM、FPGA、RISC-V和AI等各种架构,稳定可靠的处理器模组,满足客户大批量产品应用部署的需求,同时为客户提供产品定制设计、行业应用解决方案和OEM的一站式服务。收起

查看更多

相关推荐

方案定制

去合作
方案开发定制化,2000+方案商即时响应!

米尔电子,是一家专注于嵌入式处理器模组设计、研发、生产和销售于一体的国家级高新技术企业,也被评为专精特新企业。米尔电子深耕嵌入式领域10多年,致力于为企业级客户提供基于ARM、FPGA、RISC-V和AI等各种架构,稳定可靠的处理器模组,满足客户大批量产品应用部署的需求,同时为客户提供产品定制设计、行业应用解决方案和OEM的一站式服务。 米尔英文简称“MYIR”,是“Make Your Idea Real”第一个大写字母的缩写。我们的理念是“专业服务助力客户成功”,目前米尔已通过专业高效的服务,帮助全球数万家企业的产品成功上市。