如何使用 EmotiEffLib 模型进行情绪识别

如何使用 EmotiEffLib 模型进行情绪识别

最近在研究情绪识别模型,整理一下EmotiEffLib模型。

如何使用 EmotiEffLib 进行情绪识别

EmotiEffLib 是一个为高效识别图像和视频中人物情绪与投入度而设计的轻量级库。本文档将作为一份快速入门指南,一步步教你如何使用其 Python 库进行情绪识别。

1. 安装

EmotiEffLib 支持 PyTorchONNX 两种深度学习后端。你需要根据自己的环境和偏好选择其中一种进行安装。同时,还需要安装 facenet-pytorch 用于人脸检测。

# 安装 emotiefflib (默认使用 ONNX 后端)
pip install emotiefflib

# 如果你希望使用 PyTorch 后端,请使用以下命令安装
pip install emotiefflib[torch]

# 安装人脸检测库
pip install facenet-pytorch

# Image/Video processing
opencv-python

# Scientific computing and helpers
numpy<2.0

# Model backbones (pinned to a compatible version)
timm==0.9.12 

2. 核心组件

使用此库主要涉及两个核心部分:

  1. 人脸检测: 在进行情绪识别之前,必须先从图片中定位并裁剪出人脸。本项目推荐使用 facenet-pytorch 包中的 MTCNN 模型来完成此项任务。
  2. 情绪识别器: emotiefflib 的核心功能由 EmotiEffLibRecognizer 类提供。你需要先创建一个该类的实例,然后用它来识别人脸图片中的情绪。

3. 使用步骤

下面是一个完整的使用流程,从加载图片到最终识别出情绪。

第一步:导入所需库

首先,我们需要导入所有必要的库,包括图像处理的 cv2,用于人脸检测的 MTCNN,以及 emotiefflib 的核心识别器 EmotiEffLibRecognizer

import cv2
import torch
from facenet_pytorch import MTCNN
from emotiefflib.facial_analysis import EmotiEffLibRecognizer, get_model_list

print("可用的模型列表:", get_model_list())

第二步:初始化人脸检测器和情绪识别器

  • 人脸检测器: 创建一个 MTCNN 实例。参数 keep_all=True 表示我们希望检测到图中的所有的人脸。device 参数可以设置为 'cpu''cuda'
  • 情绪识别器: 创建 EmotiEffLibRecognizer 的实例。
    • engine: 指定后端,'torch''onnx'
    • model_name: 从 get_model_list() 返回的列表中选择一个模型。
    • device: 同样,指定 'cpu''cuda'
# 确定运行设备
device = "cuda" if torch.cuda.is_available() else "cpu"
print(f"正在使用设备: {device}")

# 初始化人脸检测器
mtcnn = MTCNN(keep_all=True, device=device)

# 初始化情绪识别器
# 注意:第一次使用时,会自动从网络下载所需的模型文件
model_name = "enet_b0_8_best_vgaf" # 选择一个模型
emotion_recognizer = EmotiEffLibRecognizer(engine="torch", model_name=model_name, device=device)

第三步:加载图片并检测人脸

使用 OpenCV (cv2) 读取一张图片,然后将其传入 mtcnn 对象。mtcnn 会返回检测到的人脸边界框(boxes)和裁剪后的人脸图像张量(faces)。

# 加载你的图片
image_path = 'path/to/your/image.jpg' # <--- 请替换成你的图片路径
try:
    frame = cv2.imread(image_path)
    # OpenCV 读取的是 BGR 格式,需要转换为 RGB
    frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
except Exception as e:
    print(f"无法加载图片: {image_path}, 错误: {e}")
    # 在此处退出或使用一张备用图片
    exit()


# 检测人脸
# 返回值:边界框,概率,裁剪后的人脸张量
boxes, _, faces = mtcnn.detect(frame_rgb, landmarks=True)

# 检查是否检测到人脸
if faces is None:
    print("在图片中没有检测到人脸。")
    exit()

print(f"检测到 {len(faces)} 张人脸。")

faces 是一个 PyTorch 张量列表,每个张量代表一张裁剪好的人脸。后续处理需要将其转换成 numpy 数组。

第四步:进行情绪识别

遍历所有检测到的人脸,将它们逐一送入情绪识别器的 predict_emotions 方法。

# predict_emotions 需要 numpy 格式的图片
# (H, W, C) 且色彩通道为 RGB
facial_images_np = []
for face in faces:
    # PyTorch 张量是 (C, H, W),需要转换为 (H, W, C)
    # 同时要将像素值从 [-1, 1] 转换到 [0, 255]
    face_np = face.permute(1, 2, 0).cpu().numpy()
    face_np = (face_np * 128 + 127.5).astype('uint8')
    facial_images_np.append(face_np)

# 对所有裁剪出的人脸进行情绪识别
# 返回值:情绪标签列表,原始输出分数列表
emotions, _ = emotion_recognizer.predict_emotions(facial_images_np, logits=True)

第五步:展示结果

最后,我们可以将识别结果(情绪标签)和边界框一起绘制在原始图片上进行展示。

import matplotlib.pyplot as plt

# 在图片上绘制边界框和情绪标签
for i, box in enumerate(boxes):
    x1, y1, x2, y2 = [int(b) for b in box]
    emotion = emotions[i]
    
    # 绘制矩形框
    cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2)
    # 标注情绪
    cv2.putText(frame, emotion, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2)

# 显示结果
# Matplotlib 显示的是 RGB 图像,而我们绘制的图像是 BGR
plt.imshow(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
plt.axis('off')
plt.title("情绪识别结果")
plt.show()

# 也可以保存文件
# cv2.imwrite("result.jpg", frame)

完整代码如下:

import argparse
import os

import cv2
import matplotlib.pyplot as plt
import numpy as np
import torch
from facenet_pytorch import MTCNN

from emotiefflib.facial_analysis import EmotiEffLibRecognizer, get_model_list


def analyze_image(image_path: str):
    """
    对单张图片进行完整的情绪识别流程。
    """
    print("--- 开始处理单张图片情绪识别 ---")

    # --- 第一步:初始化模型 ---
    print("1. 初始化模型...")
    print("   可用的情绪识别模型:", get_model_list())
    device = "cuda" if torch.cuda.is_available() else "cpu"
    print(f"   正在使用设备: {device}")

    # 初始化人脸检测器 (MTCNN)
    mtcnn = MTCNN(keep_all=True, device=device)

    # 初始化情绪识别器
    model_name = "enet_b0_8_best_vgaf"  # 选择一个模型
    emotion_recognizer = EmotiEffLibRecognizer(engine="torch", model_name=model_name, device=device)
    print(f"   模型加载成功: 人脸检测(MTCNN), 情绪识别({model_name})")

    # --- 第二步:加载图片并检测人脸 ---
    print("\n2. 加载图片并检测人脸...")
    try:
        frame = cv2.imread(image_path)
        if frame is None:
            # 如果imread失败,它会返回None
            raise FileNotFoundError(f"无法读取图片文件,请检查路径或文件是否损坏: {image_path}")
        # 将OpenCV的BGR图像转换为RGB,这是人脸检测器需要的格式
        frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
    except Exception as e:
        print(f"   错误: {e}")
        return

    # 检测人脸边界框
    boxes, _ = mtcnn.detect(frame_rgb)
    if boxes is None:
        print("   在图片中没有检测到人脸。")
        return

    # 提取人脸图像张量
    faces = mtcnn(frame_rgb)
    if faces is None:
        print("   在图片中没有检测到人脸。")
        return

    print(f"   检测到 {len(faces)} 张人脸。")

    # --- 第三步:进行情绪识别 ---
    print("\n3. 识别情绪...")
    facial_images_np = []
    for face in faces:
        # 将 PyTorch 张量 (C, H, W) 转换为 NumPy 数组 (H, W, C)
        # 同时将像素值从 [-1, 1] 转换到 [0, 255] 的 uint8 类型
        face_np = face.permute(1, 2, 0).cpu().numpy()
        face_np = (face_np * 128 + 127.5).clip(0, 255).astype(np.uint8)
        facial_images_np.append(face_np)

    emotions, _ = emotion_recognizer.predict_emotions(facial_images_np, logits=True)
    print("   识别完成:", emotions)

    # --- 第四步:展示并保存结果 ---
    print("\n4. 生成结果图像...")
    # 在原始的BGR图像上绘制
    for i, box in enumerate(boxes):
        x1, y1, x2, y2 = [int(b) for b in box]
        emotion = emotions[i]
        cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2)
        cv2.putText(frame, emotion, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2)

    # 将绘制后的BGR图像转换为RGB以供matplotlib显示
    result_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
    plt.figure(figsize=(10, 8))
    plt.imshow(result_rgb)
    plt.axis('off')
    plt.title("情绪识别结果")

    # 保存结果文件
    output_filename = "result_" + os.path.basename(image_path)
    # 使用matplotlib保存,因为它保存的是我们看到的RGB图像
    plt.savefig(output_filename, bbox_inches='tight', pad_inches=0)
    print(f"   结果已保存为: {output_filename}")

    # 显示图像窗口
    plt.show()
    print("\n--- 处理完成 ---")


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="对单张图片进行情绪识别,并显示和保存结果。")
    parser.add_argument("image_path", type=str, help="输入图片文件的路径。")
    args = parser.parse_args()

    if not os.path.exists(args.image_path):
        print(f"错误: 图片文件不存在于路径 {args.image_path}")
    else:
        analyze_image(args.image_path)

我已经将上面的代码片段整合成了一个完整的、可执行的 single_image_emotion_test.py 脚本。

如何使用这个新脚本:

  1. 找一张包含人脸的图片,放在你的项目目录中。
  2. 打开命令行,运行以下命令,将 your_image.jpg 替换成你的图片文件名:
    python single_image_emotion_test.py your_image.jpg

脚本会执行完整的识别流程,在命令行打印出每一步的信息,最后会弹出一个窗口显示带有情绪标注的图片,并自动将这张结果图保存到你的项目文件夹中(文件名为 result_your_image.jpg)。

可识别的情绪类别

该库可以识别 7 种或 8 种情绪,具体取决于你所选择的模型。

  • 8 种情绪模型 (默认, 例如 enet_b0_8_best_vgaf):

    • Anger (愤怒)
    • Contempt (蔑视)
    • Disgust (厌恶)
    • Fear (恐惧)
    • Happiness (开心)
    • Neutral (中性)
    • Sadness (悲伤)
    • Surprise (惊讶)
  • 7 种情绪模型 (名称中带 _7, 例如 enet_b2_7):

    • Anger (愤怒)
    • Disgust (厌恶)
    • Fear (恐惧)
    • Happiness (开心)
    • Neutral (中性)
    • Sadness (悲伤)
    • Surprise (惊讶)

模型列表:

Model AffectNet (8 classes) AffectNet (7 classes) AFEW VGAF LSD MTL Inference time, ms Model size, MB
mobilenet_7.h5 - 64.71 55.35 68.92 - 1.099 16 ± 5 14
enet_b0_8_best_afew.pt 60.95 64.63 59.89 66.80 59.32 1.110 59 ± 26 16
enet_b0_8_best_vgaf.pt 61.32 64.57 55.14 68.29 59.72 1.123 59 ± 26 16
enet_b0_8_va_mtl.pt 61.93 64.94 56.73 66.58 60.94 1.276 60 ± 32 16
enet_b0_7.pt - 65.74 56.99 65.18 - 1.111 59 ± 26 16
enet_b2_7.pt - 66.34 59.63 69.84 - 1.134 191 ± 18 30
enet_b2_8.pt 63.03 66.29 57.78 70.23 52.06 1.147 191 ± 18 30
enet_b2_8_best.pt 63.125 66.51 56.73 71.12 - - 191 ± 18 30
posted @ 2025-06-07 22:45  woden  阅读(259)  评论(0)    收藏  举报