Python实现人脸表情识别与疲劳检测系统
1. 项目概述
这个项目将带领你使用Python构建一个集表情识别、疲劳检测和年龄性别检测于一体的综合系统。作为一名计算机视觉方向的开发者,我发现这类复合型检测系统在实际应用中需求很大,但市面上很少有完整的一站式教程。今天我就把多年积累的实战经验整理成这篇保姆级指南。
这个系统特别适合以下场景:
- 驾驶员状态监控(疲劳检测+表情识别)
- 零售业顾客分析(年龄性别+表情)
- 智能门禁系统(身份验证+情绪识别)
相比单一功能模型,这种组合方案能提供更丰富的分析维度。比如在驾驶场景中,系统不仅能判断司机是否疲劳,还能通过表情分析其情绪状态,大幅提升安全性。
2. 环境准备与工具选型
2.1 Python环境配置
推荐使用Python 3.8+版本,这个版本在深度学习框架兼容性方面表现最好。安装时务必勾选"Add Python to PATH"选项,这是很多新手容易忽略的关键步骤。
验证安装成功:
python --version
pip --version
2.2 核心库安装
我们将使用以下主流库:
- OpenCV:图像处理核心库
- Dlib:人脸检测和特征点提取
- TensorFlow/Keras:深度学习模型框架
- imutils:简化图像处理操作
安装命令:
pip install opencv-python dlib tensorflow imutils
注意:安装dlib时可能会遇到编译错误。如果出现这种情况,可以先安装CMake:
pip install cmake
2.3 预训练模型下载
为了节省训练时间,我们直接使用优质的开源预训练模型:
- 表情识别:FER2013数据集训练的CNN模型
- 年龄性别:基于IMDB-WIKI数据集的模型
- 疲劳检测:使用Dlib的68点人脸特征检测器
这些模型文件可以从我的GitHub仓库一键下载:
git clone https://github.com/your-repo/facial-analysis-models
3. 核心功能实现
3.1 人脸检测基础
所有功能都始于准确的人脸检测。我们使用OpenCV的DNN模块加载Caffe模型:
net = cv2.dnn.readNetFromCaffe(
"deploy.prototxt",
"res10_300x300_ssd_iter_140000.caffemodel"
)
def detect_faces(image):
(h, w) = image.shape[:2]
blob = cv2.dnn.blobFromImage(cv2.resize(image, (300, 300)), 1.0,
(300, 300), (104.0, 177.0, 123.0))
net.setInput(blob)
detections = net.forward()
faces = []
for i in range(0, detections.shape[2]):
confidence = detections[0, 0, i, 2]
if confidence > 0.5: # 置信度阈值
box = detections[0, 0, i, 3:7] * np.array([w, h, w, h])
faces.append(box.astype("int"))
return faces
3.2 表情识别实现
表情识别使用卷积神经网络(CNN),我们加载预训练好的模型:
from tensorflow.keras.models import load_model
emotion_model = load_model("emotion_model.hdf5")
EMOTIONS = ["愤怒", "厌恶", "恐惧", "开心", "悲伤", "惊讶", "中性"]
def analyze_emotion(face_roi):
# 预处理
gray = cv2.cvtColor(face_roi, cv2.COLOR_BGR2GRAY)
resized = cv2.resize(gray, (48, 48))
normalized = resized / 255.0
reshaped = np.reshape(normalized, (1, 48, 48, 1))
# 预测
preds = emotion_model.predict(reshaped)[0]
label = EMOTIONS[preds.argmax()]
return label, preds
3.3 疲劳检测算法
疲劳检测主要通过以下指标判断:
- 眼睛纵横比(EAR)
- 眨眼频率
- 嘴巴张开程度
计算眼睛纵横比的关键函数:
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
ear = (A + B) / (2.0 * C)
return ear
3.4 年龄性别检测
年龄性别检测使用单独的CNN模型:
age_model = load_model("age_model.hdf5")
gender_model = load_model("gender_model.hdf5")
def detect_age_gender(face_roi):
# 预处理
resized = cv2.resize(face_roi, (64, 64))
normalized = resized / 255.0
reshaped = np.reshape(normalized, (1, 64, 64, 3))
# 预测
age_pred = age_model.predict(reshaped)[0][0]
gender_pred = gender_model.predict(reshaped)[0][0]
age = int(age_pred * 100)
gender = "男" if gender_pred > 0.5 else "女"
return age, gender
4. 系统集成与优化
4.1 多任务流水线设计
为了提高效率,我们设计了一个并行处理流水线:
def process_frame(frame):
# 人脸检测
faces = detect_faces(frame)
results = []
for (x, y, w, h) in faces:
face_roi = frame[y:y+h, x:x+w]
# 并行处理
emotion_thread = Thread(target=analyze_emotion, args=(face_roi,))
age_gender_thread = Thread(target=detect_age_gender, args=(face_roi,))
emotion_thread.start()
age_gender_thread.start()
# 疲劳检测(需要连续帧)
landmarks = predictor(gray, face_roi)
left_eye = landmarks[36:42]
right_eye = landmarks[42:48]
ear = (eye_aspect_ratio(left_eye) + eye_aspect_ratio(right_eye)) / 2.0
emotion_thread.join()
age_gender_thread.join()
results.append({
"bbox": (x, y, w, h),
"emotion": emotion_result,
"age_gender": age_gender_result,
"fatigue": ear < EAR_THRESHOLD
})
return results
4.2 性能优化技巧
- 模型量化 :将浮点模型转换为8位整型,速度提升3-5倍
converter = tf.lite.TFLiteConverter.from_keras_model(model)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
tflite_model = converter.convert()
- 多尺度检测 :对小脸采用图像金字塔
def pyramid(image, scale=1.5, min_size=(30, 30)):
yield image
while True:
w = int(image.shape[1] / scale)
image = imutils.resize(image, width=w)
if image.shape[0] < min_size[1] or image.shape[1] < min_size[0]:
break
yield image
- ROI缓存 :避免重复计算人脸区域
5. 常见问题与解决方案
5.1 人脸检测不准确
症状 :漏检或误检率高 解决方案 :
- 调整置信度阈值(0.3-0.7之间尝试)
- 尝试不同的检测模型(MTCNN、RetinaFace等)
- 增加图像预处理(直方图均衡化)
5.2 表情识别结果不稳定
症状 :表情频繁切换 解决方案 :
- 加入时间平滑处理
# 维护一个表情历史队列
emotion_history = deque(maxlen=10)
def smooth_emotion(current):
emotion_history.append(current)
# 取最近10次结果中最频繁的
return max(set(emotion_history), key=emotion_history.count)
5.3 疲劳检测误报
症状 :闭眼瞬间被误判为疲劳 解决方案 :
- 设置连续帧阈值(如连续3帧EAR低于阈值才判定)
- 结合头部姿态分析(使用solvePnP计算头部角度)
6. 完整实现示例
下面是将所有功能整合的完整脚本框架:
import cv2
import dlib
import numpy as np
from threading import Thread
from collections import deque
# 初始化所有模型
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")
emotion_model = load_model("emotion_model.hdf5")
age_model = load_model("age_model.hdf5")
gender_model = load_model("gender_model.hdf5")
# 参数配置
EAR_THRESHOLD = 0.25
CONSEC_FRAMES = 3
def main():
cap = cv2.VideoCapture(0)
frame_count = 0
ear_history = deque(maxlen=CONSEC_FRAMES)
while True:
ret, frame = cap.read()
if not ret:
break
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
faces = detector(gray, 0)
for face in faces:
# 获取人脸区域
x, y, w, h = face.left(), face.top(), face.width(), face.height()
face_roi = frame[y:y+h, x:x+w]
# 并行处理各任务
emotion = analyze_emotion(face_roi)
age, gender = detect_age_gender(face_roi)
# 疲劳检测
landmarks = predictor(gray, face)
left_eye = landmarks[36:42]
right_eye = landmarks[42:48]
ear = (eye_aspect_ratio(left_eye) + eye_aspect_ratio(right_eye)) / 2.0
ear_history.append(ear)
# 判断疲劳状态
fatigue = all(e < EAR_THRESHOLD for e in ear_history)
# 绘制结果
draw_results(frame, x, y, w, h, emotion, age, gender, fatigue)
cv2.imshow("Analysis", frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
7. 部署与实用技巧
7.1 打包为可执行文件
使用PyInstaller打包:
pyinstaller --onefile --add-data "models/*;models/" facial_analysis.py
7.2 实际应用建议
- 光照条件 :确保环境光线充足均匀,避免侧光造成阴影
- 摄像头角度 :正对人脸,高度与眼睛平齐
- 性能调优 :
- 降低检测帧率(如每秒5-10帧)
- 缩小检测区域(设置ROI)
- 使用硬件加速(OpenCV的DNN模块支持CUDA)
7.3 扩展思路
- 加入语音提示 :当检测到疲劳或负面情绪时发出警告
- 数据记录 :将分析结果保存到数据库,用于长期分析
- 多摄像头支持 :扩展为多路视频分析系统
这个项目最让我惊喜的是,通过合理的模型选择和优化,即使在普通笔记本电脑上也能达到实时分析的效果。在实际测试中,我发现疲劳检测的准确率对眼睛特征点定位非常敏感,因此建议使用高质量的人脸特征点检测模型。
更多推荐



所有评论(0)