人脸情绪识别(使用深度学习和OpenCV)

07-13 1497阅读

前言 

在这篇博客中,我将分享如何结合深度学习和OpenCV来创建一个能够实时识别人脸情感的系统。

本文主要为我个人的学习心得与总结,鉴于网络上知识信息的零散性,我特意进行了整合与阐述。若在整理过程中存在疏漏或错误之处,恳请各位不吝赐教,给予指正。需要强调的是,本文纯粹为学习交流之用,并不产生任何经济收益。若在文中出现任何涉及禁止转载或侵权的图片、文字内容,敬请及时与我取得联系,我将立即进行修正。

目录

前言 

项目简介

模型构建

导入必备的库

数据预处理 

模型构建

训练模型

保存模型 

实现面部表情识别:加载、训练和保存模型

实时应用 

代码分享与访问

总结


项目简介

情感检测在计算机科学领域中扮演着重要的角色,尤其在人机交互和情感智能应用中。本项目旨在利用深度学习技术,特别是卷积神经网络(CNN),来实现实时的情感检测。此外,OpenCV作为一个开源的计算机视觉库,为我们提供了处理图像和视频流的强大工具。

模型构建

导入必备的库

我们需要导入用到的Python库,包括数据处理库Pandas、科学计算库NumPy、深度学习框架Keras、以及计算机视觉库OpenCV

import pandas as pd
import numpy as np
from keras.models import Sequential
from keras.layers import Conv2D,MaxPooling2D,Dense,Dropout,Flatten
from keras.losses import categorical_crossentropy
from keras.optimizers import Adam
from keras.utils import to_categorical

数据预处理 

在能够进行表情识别之前,我们需要加载并预处理数据。这一过程包括读取图像数据,将数据分割为训练和测试集,对数据进行标准化处理,以及将标签转换为one-hot编码格式。以下是数据预处理的代码段: 

def load_and_preprocess_data(file_path):
    Catch_bat = pd.read_csv(file_path)
    
    X_train, Y_train, X_test, Y_test = [], [], [], []
    
    for index, row in Catch_bat.iterrows():
        val = row['pixels'].split(' ')
        try:
            if 'Training' in row['Usage']:
                X_train.append(np.array(val, 'float32'))
                Y_train.append(row['emotion'])
            elif 'PublicTest' in row['Usage']:
                X_test.append(np.array(val, 'float32'))
                Y_test.append(row['emotion'])
        except:
            pass
    
    X_train = np.array(X_train, 'float32')
    Y_train = np.array(Y_train, 'float32')
    X_test = np.array(X_test, 'float32')
    Y_test = np.array(Y_test, 'float32')
    
    X_train = (X_train - np.mean(X_train, axis=0)) / np.std(X_train, axis=0)
    X_test = (X_test - np.mean(X_test, axis=0)) / np.std(X_test, axis=0)
    
    labels = 7
    Y_train = to_categorical(Y_train, num_classes=labels)
    Y_test = to_categorical(Y_test, num_classes=labels)
    width, height = 48, 48
    X_train = X_train.reshape(X_train.shape[0], width, height, 1)
    X_test = X_test.reshape(X_test.shape[0], width, height, 1)
    
    return X_train, Y_train, X_test, Y_test

这段代码首先使用pandas读取CSV文件,然后根据“Usage”列将数据分割为训练集和测试集。接着,它将图像数据标准化,并将标签转换为one-hot编码格式。

模型构建

此函数的目的是构建一个卷积神经网络模型,用于面部表情的分类。以下是数据预处理的代码段: 

def build_model(input_shape, num_classes):
    model = Sequential()
    model.add(Conv2D(64, kernel_size=(3, 3), activation='relu', input_shape=input_shape))
    model.add(Conv2D(64, kernel_size=(3, 3), activation='relu'))
    model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))
    model.add(Dropout(0.5))
    model.add(Conv2D(64, kernel_size=(3, 3), activation='relu'))
    model.add(Conv2D(64, kernel_size=(3, 3), activation='relu'))
    model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))
    model.add(Dropout(0.5))
    model.add(Conv2D(128, kernel_size=(3, 3), activation='relu'))
    model.add(Conv2D(128, kernel_size=(3, 3), activation='relu'))
    model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))
    model.add(Flatten())
    model.add(Dense(512, activation='relu'))
    model.add(Dropout(0.2))
    model.add(Dense(num_classes, activation='softmax'))
    return model
  1. model = Sequential():初始化一个顺序模型。这是Keras中最简单的模型类型,允许我们按顺序一层层地添加模型层。
  2. model.add(Conv2D(...)):添加卷积层。这里使用64和128个滤波器(也就是输出空间的维度),每个卷积层后都跟着一个激活函数ReLU来增加非线性。
  3. MaxPooling2D(...):添加最大池化层,用于下采样,减少参数数量,防止过拟合。
  4. Dropout(0.5)和Dropout(0.2):添加Dropout层,随机丢弃一部分神经元(分别是50%和20%),以防止过拟合。
  5. Flatten():将多维的输入一维化,常用于卷积层到全连接层的过渡。
  6. Dense(...):添加全连接层。第一个全连接层有512个神经元,最后一个输出层的神经元数目等于分类的数目,使用softmax激活函数进行多分类。 

训练模型

def train_model(model, X_train, Y_train, X_test, Y_test, batch_size=64, epochs=1):
    model.compile(loss=categorical_crossentropy, optimizer=Adam(), metrics=['accuracy'])
    model.fit(X_train, Y_train, batch_size=batch_size, epochs=epochs, verbose=1, validation_data=(X_test, Y_test), shuffle=True)

这个函数用于训练上述定义的卷积神经网络模型。主要步骤包括:

  1.  model.compile(...):配置训练模型。使用categorical_crossentropy作为损失函数,这在多分类问题中很常见。Adam优化器用于调整权重,metrics=['accuracy']意味着训练和测试期间将监控精度。
  2.   model.fit(...):这里开始训练过程。使用提供的训练数据X_train和Y_train,批量大小batch_size,重复训练epochs次。同时,使用X_test和Y_test作为验证集来监控训练过程中的性能。

保存模型 

def save_model(model, model_file, weights_file):
    model_json = model.to_json()
    with open(model_file, 'w') as json_file:
        json_file.write(model_json)
    model.save_weights(weights_file)

最后,这个函数用于保存训练好的模型,以便将来可以重新加载并使用,而无需重新训练。

  1. model_json = model.to_json():首先,将模型的架构转换为JSON格式的字符串。
  2. with open(model_file, 'w') as json_file:然后,将这个字符串写入一个文件中,保存模型的架构。
  3. model.save_weights(weights_file):最后,调用save_weights方法将模型的权重保存到另一个文件中。

实现面部表情识别:加载、训练和保存模型

在我们的项目中,我们使用了fer2013.csv数据集,它包含数千个标记了七种基本情感(愤怒、厌恶、恐惧、快乐、悲伤、惊讶和中性)的面部表情图像

人脸情绪识别(使用深度学习和OpenCV)

# 加载和预处理数据
X_train, Y_train, X_test, Y_test = load_and_preprocess_data('fer2013.csv')
# 构建模型
input_shape = X_train.shape[1:]
num_classes = 7
model = build_model(input_shape, num_classes)
# 训练模型
train_model(model, X_train, Y_train, X_test, Y_test)
# 保存模型
save_model(model, 'file_name.json', 'file_name.h5')

实时应用 

import cv2
import numpy as np
from keras.models import model_from_json
with open('file_name.json', 'r') as json_file:
    loaded_model_json = json_file.read()
    model = model_from_json(loaded_model_json)
model.load_weights('file_name.h5')
face_prototxt = 'deploy.prototxt.txt'
face_model = 'res10_300x300_ssd_iter_140000.caffemodel'
face_net = cv2.dnn.readNetFromCaffe(face_prototxt, face_model)
emotions = ('angry', 'disgust', 'fear', 'happy', 'sad', 'surprise', 'neutral')
emotion_colors = {
    'angry': (0, 0, 255),     
    'disgust': (0, 255, 0),     
    'fear': (255, 0, 0),        
    'happy': (255, 255, 0),    
    'sad': (255, 0, 255),       
    'surprise': (0, 255, 255),  
    'neutral': (255, 255, 255)  
}
font = cv2.FONT_HERSHEY_SIMPLEX
def process_image(img):
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    blob = cv2.dnn.blobFromImage(cv2.resize(img, (300, 300)), 1.0, (300, 300), (104.0, 177.0, 123.0))
    face_net.setInput(blob)
    faces = face_net.forward()
    for i in range(faces.shape[2]):
        confidence = faces[0, 0, i, 2]
        if confidence > 0.5:
            box = faces[0, 0, i, 3:7] * np.array([img.shape[1], img.shape[0], img.shape[1], img.shape[0]])
            (startX, startY, endX, endY) = box.astype("int")
            face = gray[startY:endY, startX:endX]
            face = cv2.resize(face, (48, 48))
            pixels = np.expand_dims(face, axis=0)
            pixels = pixels / 255.0
            pred = model.predict(pixels)
            max_index = np.argmax(pred[0])
            pred_emotion = emotions[max_index]
            color = emotion_colors.get(pred_emotion, (255, 255, 255))
            cv2.rectangle(img, (startX, startY), (endX, endY), color, 3)
            cv2.putText(img, pred_emotion, (int(startX), int(startY)), font, 1, color, 2)
    return img
def process_image_file(file_path):
    img = cv2.imread(file_path)
    processed_img = process_image(img)
    cv2.namedWindow('image', 0)
    cv2.imshow('image', processed_img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
process_image_file('you_image.jpg')

在这段代码中,我们加载了面部表情识别模型和人脸检测模型,然后定义了一个处理图像的函数。该函数首先将图像转换为灰度图,并使用人脸检测模型检测人脸。然后,对每个检测到的人脸进行表情识别,并根据预测结果在图像上绘制矩形和文本。最后,我们调用了处理单张图像文件的函数,并传入了一个示例图像文件的路径。 

除了能够处理静态图像,我们还可以让我们的系统实时处理视频流,并对其中的人脸进行情绪识别。下面是添加处理视频功能的代码:

def process_video(video_path, output_path):  
    cap = cv2.VideoCapture(video_path)
    fps = int(cap.get(cv2.CAP_PROP_FPS))
    frame_size = (int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)), int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)))
    
    fourcc = cv2.VideoWriter_fourcc(*'XVID')  
    out = cv2.VideoWriter(output_path, fourcc, fps, frame_size)
    
    while True:
        ret, frame = cap.read()
        if not ret:
            break
        processed_frame = process_image(frame)
        resize_frame = cv2.resize(processed_frame, frame_size)
        cv2.imshow('video', resize_frame)
        
        out.write(resize_frame)
        
        key = cv2.waitKey(1)
        if key == ord('q') or cv2.getWindowProperty('video', cv2.WND_PROP_VISIBLE)  

在上述代码中,我们添加了一个名为 process_video的函数,它接受输入视频文件的路径和输出视频文件的路径。这个函数会打开输入视频文件,逐帧读取视频流,并对每一帧进行情绪识别处理。然后,将处理后的帧写入输出视频文件。最后,释放资源并关闭所有窗口。

为了让我们的情绪识别系统更加实用和互动,我们进一步扩展了其功能,使之能够实时处理来自摄像头的视频流。以下是实现此功能的详细代码:

def process_camera():
    cap = cv2.VideoCapture(0)
    while True:
        ret, frame = cap.read()
        if not ret:
            break
        processed_frame = process_image(frame)
        resize_frame = cv2.resize(processed_frame, (1000, 700))
        cv2.imshow('frame', resize_frame)
        key = cv2.waitKey(1)
        if key == ord('q') or cv2.getWindowProperty('frame', cv2.WND_PROP_VISIBLE)  

在这段代码中,我们首先通过 cv2.VideoCapture(0) 打开系统默认的摄像头。接下来,程序进入一个循环,在这个循环中持续从摄像头读取帧。对于每一帧,我们调用 process_image 函数进行所需的图像处理。为了更好的显示效果,我们还将处理后的帧大小调整为 1000x700 像素。如果用户按下 'q' 键或关闭显示窗口,循环将终止,释放摄像头资源并关闭所有开启的窗口。 

代码分享与访问

为了方便大家更好地理解和使用我在本文中提到的技术和方法,我已经将相关代码上传至我的GitHub仓库。

您可以通过以下链接访问我GitHub仓库,并下载或查看这些代码:

GitHub - maxuan777/Face-Emotion-Recognition-: 基于opencv与深度学习的人脸情绪识别

在仓库中,您可以找到与本文内容相对应的代码文件和项目结构。这些代码已经经过我的测试与验证,应该可以正常运行。当然,如果您在使用过程中遇到任何问题或建议,欢迎在下方留言,我会尽快回复并帮助您解决。

总结

感谢您的阅读与支持!如果您对我的博客或代码有任何意见或建议,欢迎在下方留言或私信我。

VPS购买请点击我

文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

目录[+]