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 疲劳检测算法

疲劳检测主要通过以下指标判断:

  1. 眼睛纵横比(EAR)
  2. 眨眼频率
  3. 嘴巴张开程度

计算眼睛纵横比的关键函数:

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 性能优化技巧

  1. 模型量化 :将浮点模型转换为8位整型,速度提升3-5倍
converter = tf.lite.TFLiteConverter.from_keras_model(model)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
tflite_model = converter.convert()
  1. 多尺度检测 :对小脸采用图像金字塔
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
  1. 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 实际应用建议

  1. 光照条件 :确保环境光线充足均匀,避免侧光造成阴影
  2. 摄像头角度 :正对人脸,高度与眼睛平齐
  3. 性能调优
    • 降低检测帧率(如每秒5-10帧)
    • 缩小检测区域(设置ROI)
    • 使用硬件加速(OpenCV的DNN模块支持CUDA)

7.3 扩展思路

  1. 加入语音提示 :当检测到疲劳或负面情绪时发出警告
  2. 数据记录 :将分析结果保存到数据库,用于长期分析
  3. 多摄像头支持 :扩展为多路视频分析系统

这个项目最让我惊喜的是,通过合理的模型选择和优化,即使在普通笔记本电脑上也能达到实时分析的效果。在实际测试中,我发现疲劳检测的准确率对眼睛特征点定位非常敏感,因此建议使用高质量的人脸特征点检测模型。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐