📜  使用 FER 的面部表情识别器——使用深度神经网络(1)

📅  最后修改于: 2023-12-03 15:22:11.123000             🧑  作者: Mango

使用 FER 的面部表情识别器——使用深度神经网络

FER (Facial Expression Recognition) 是一种通过计算机视觉技术和面部表情识别技术,将面部表情转化为情感分类的方法。在近年来,FER 被广泛应用于人机交互、智能家居、医疗诊断等领域。

在本篇文章中,我们将介绍使用深度神经网络进行 FER 的实现,并给出相应的代码实现。

模型选择

在深度学习领域中,卷积神经网络 (CNN) 是常用的处理图像数据的模型。因此我们选择使用 CNN 来实现 FER。

为了能够得到好的模型效果,我们采用经典的卷积神经网络模型 —— VGG16。该模型已经被证明在图像识别方面效果非常好。

数据集选择

构建好模型,我们需要一份数据集来训练模型。在 FER 方面,我们可以使用 CK+ 和 FER-2013 两个数据集。

CK+ 数据集是由来自加拿大皇家军事学院 (RMCC) 的 Lucey 等人构建的,包含了各种表情的照片以及相应的情感标注。

FER-2013 数据集是由 Goodfellow 等人构建的大规模面部表情识别数据集。

在本篇文章中,我们将使用 CK+ 数据集来训练我们的模型。你可以到 Wiki 链接下载数据集,我们选择使用 data 文件夹中的数据。

数据处理

我们需要预处理 CK+ 数据集。首先,我们要将数据转换为灰度图。然后,我们将每张图片调整为 48x48 大小,并归一化到 [0,1]。最后,将每个图像和对应的标签(情感)存储在两个列表中。

import numpy as np
import cv2
import os

data_dir = 'data'
IMG_SIZE = 48
emotion_classes = {
    0: 'anger',
    1: 'contempt',
    2: 'disgust',
    3: 'fear',
    4: 'happy',
    5: 'sadness',
    6: 'surprise'
}

def load_data():

    images = []
    labels = []

    for emotion_class in os.listdir(data_dir):

        class_path = os.path.join(data_dir, emotion_class)

        for image_name in os.listdir(class_path):

            image_path = os.path.join(class_path, image_name)
            image = cv2.imread(image_path, 0)
            image = cv2.resize(image, (IMG_SIZE, IMG_SIZE))
            image = np.array(image, dtype='float32') / 255.0
            label = int(emotion_class)

            images.append(image)
            labels.append(label)

    return np.array(images), np.array(labels)
模型构建

我们使用 Keras 来实现我们的模型。VGG16 模型的预训练权重已经被保存在 Keras 库中,我们可以很方便地使用它。

from keras.models import Model
from keras.layers import Input, Conv2D, MaxPooling2D, Flatten, Dense, Dropout

def build_model():

    input_tensor = Input(shape=(IMG_SIZE, IMG_SIZE, 1))

    block1_conv1 = Conv2D(64, (3, 3), activation='relu', padding='same', name='block1_conv1')(input_tensor)
    block1_conv2 = Conv2D(64, (3, 3), activation='relu', padding='same', name='block1_conv2')(block1_conv1)
    block1_pool = MaxPooling2D((2, 2), strides=(2, 2), name='block1_pool')(block1_conv2)

    block2_conv1 = Conv2D(128, (3, 3), activation='relu', padding='same', name='block2_conv1')(block1_pool)
    block2_conv2 = Conv2D(128, (3, 3), activation='relu', padding='same', name='block2_conv2')(block2_conv1)
    block2_pool = MaxPooling2D((2, 2), strides=(2, 2), name='block2_pool')(block2_conv2)

    block3_conv1 = Conv2D(256, (3, 3), activation='relu', padding='same', name='block3_conv1')(block2_pool)
    block3_conv2 = Conv2D(256, (3, 3), activation='relu', padding='same', name='block3_conv2')(block3_conv1)
    block3_conv3 = Conv2D(256, (3, 3), activation='relu', padding='same', name='block3_conv3')(block3_conv2)
    block3_pool = MaxPooling2D((2, 2), strides=(2, 2), name='block3_pool')(block3_conv3)

    block4_conv1 = Conv2D(512, (3, 3), activation='relu', padding='same', name='block4_conv1')(block3_pool)
    block4_conv2 = Conv2D(512, (3, 3), activation='relu', padding='same', name='block4_conv2')(block4_conv1)
    block4_conv3 = Conv2D(512, (3, 3), activation='relu', padding='same', name='block4_conv3')(block4_conv2)
    block4_pool = MaxPooling2D((2, 2), strides=(2, 2), name='block4_pool')(block4_conv3)

    block5_conv1 = Conv2D(512, (3, 3), activation='relu', padding='same', name='block5_conv1')(block4_pool)
    block5_conv2 = Conv2D(512, (3, 3), activation='relu', padding='same', name='block5_conv2')(block5_conv1)
    block5_conv3 = Conv2D(512, (3, 3), activation='relu', padding='same', name='block5_conv3')(block5_conv2)
    block5_pool = MaxPooling2D((2, 2), strides=(2, 2), name='block5_pool')(block5_conv3)

    flatten = Flatten(name='flatten')(block5_pool)

    fc1 = Dense(4096, activation='relu', name='fc1')(flatten)
    fc1_dropout = Dropout(0.5)(fc1)

    fc2 = Dense(4096, activation='relu', name='fc2')(fc1_dropout)
    fc2_dropout = Dropout(0.5)(fc2)

    output = Dense(7, activation='softmax', name='output')(fc2_dropout)

    model = Model(inputs=input_tensor, outputs=output)

    return model
模型训练

我们需要将数据集划分为训练集和测试集。然后我们就可以开始训练我们的模型了。我们使用交叉熵损失函数和 Adam 优化器来训练模型。在每个 epoch 完成后,我们会评估模型在测试集上的准确率。

from sklearn.model_selection import train_test_split

images, labels = load_data()

images = images.reshape(-1, IMG_SIZE, IMG_SIZE, 1)

X_train, X_test, y_train, y_test = train_test_split(images, labels, test_size=0.15, random_state=0)

model = build_model()
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])

model.fit(X_train, y_train, batch_size=32, epochs=10, validation_data=(X_test, y_test))
模型评估

模型训练完成后,我们可以使用测试集来评估模型。我们计算模型在测试集上的准确率并输出结果。

scores = model.evaluate(X_test, y_test, verbose=0)
print('Accuracy: ', scores[1])
模型应用

模型训练完毕后,我们可以对新的图片进行表情识别。

def predict_emotions(image_path):

    image = cv2.imread(image_path, 0)
    image = cv2.resize(image, (IMG_SIZE, IMG_SIZE))
    image = np.array(image, dtype='float32') / 255.0
    image = np.expand_dims(image, axis=0)
    image = np.expand_dims(image, axis=3)

    predictions = model.predict(image)

    emotion_label = np.argmax(predictions)
    emotion = emotion_classes[emotion_label]

    return emotion
结论

在本篇文章中,我们介绍了使用深度神经网络构建面部表情识别器的方法。我们选择了经典的 VGG16 模型,使用了 CK+ 数据集进行训练,并实现了表情识别功能。

通过这篇文章,你将学到:

  • 如何选择 CNN 模型进行 FER;
  • 如何预处理图像数据;
  • 如何使用 Keras 来构建模型;
  • 如何训练和评估模型。

我们希望这篇文章对你有所帮助。