图像处理——人脸情绪识别(python卷积神经网络)


前言

项目基于科大讯飞和Datawhale的一个小比赛,模型效果一般,可以给初次接触的朋友提供一些图像处理的思路,项目做完后查阅了其他有关情绪识别的思路,在GitHub上作者priya-dwivedi也有类似项目,处理过程大部分一致,附在文末。
环境:python3.8、tensorflow2.3
pytorch版本请看文末


一、项目背景

人脸表情是传播人类情感信息与协调人际关系的重要方式,表情识别是指从静态照片或视频序列中选择出表情状态,从而确定对人物的情绪与心理变化。在日常生活中人类习惯从面部表情中吸收非言语暗示,那么计算机可以完成类似任务吗?答案是肯定的,但是需要训练它学会识别情绪。
在这里插入图片描述

二、项目任务

给定人脸照片完成具体的情绪识别,选手需要根据训练集数据构建情绪识别任务,并对测试集图像进行预测,识别人脸的7种情绪。

三、数据说明

赛题数据由训练集和测试集组成,训练集数据集按照不同情绪的文件夹进行存放。其中:

   训练集:2.8W张人脸图像;

   测试集:7K张人脸图像;

为了简化任务赛题图像只包含单张人脸,所有图像的尺寸为48*48像素。数据集包括的情绪标签包括以下7类:

   angry

   disgusted

   fearful

   happy

   neutral

   sad

   surprised

四、项目步骤

1.图片基本情况查看

完整项目下载链接:https://pan.baidu.com/s/1w72PobVwMfVCnrjRLOh4Sg
提取码:ssmb
文件目录👇
在这里插入图片描述
在这里插入图片描述

# 有些代码可以正常运行但是会提示警告,使用filterwarnings过滤
import warnings     
warnings.filterwarnings('ignore')

import os
angry_dir = os.path.join('./train/angry/')
disgusted_dir = os.path.join('./train/disgusted/')
fearful_dir = os.path.join('./train/fearful/')
happy_dir = os.path.join('./train/happy/')
neutral_dir = os.path.join('./train/neutral/')
sad_dir = os.path.join('./train/sad/')
surprised_dir = os.path.join('./train/surprised/')

print('total training angry images:', len(os.listdir(angry_dir)))
print('total training disgusted images:', len(os.listdir(disgusted_dir)))
print('total training fearful images:', len(os.listdir(fearful_dir)))
print('total training happy images:', len(os.listdir(happy_dir)))
print('total training neutral images:', len(os.listdir(neutral_dir)))
print('total training sad images:', len(os.listdir(sad_dir)))
print('total training surprised images:', len(os.listdir(surprised_dir)))
print('total training images:',len(os.listdir(angry_dir))+len(os.listdir(disgusted_dir))+len(os.listdir(fearful_dir))+
     len(os.listdir(happy_dir))+len(os.listdir(neutral_dir))+len(os.listdir(sad_dir))+len(os.listdir(surprised_dir)))

在这里插入图片描述

angry_files = os.listdir(angry_dir)
print('angry:',angry_files[:5])

disgusted_files = os.listdir(disgusted_dir)
print('disgusted:',disgusted_files[:5])

fearful_files = os.listdir(fearful_dir)
print('fearful:',fearful_files[:5])

happy_files = os.listdir(happy_dir)
print('happy:',happy_files[:5])

neutral_files = os.listdir(neutral_dir)
print('neutral:',neutral_files[:5])

sad_files = os.listdir(sad_dir)
print('sad:',sad_files[:5])

surprised_files = os.listdir(surprised_dir)
print('surprised:',surprised_files[:5])

在这里插入图片描述

import matplotlib.pyplot as plt
import cv2
%matplotlib inline

pic_index = 1

next_angry = [os.path.join(angry_dir,fname) for fname in angry_files[pic_index-1 : pic_index]]
next_happy = [os.path.join(happy_dir,fname) for fname in happy_files[pic_index-1 : pic_index]]
next_disgusted = [os.path.join(disgusted_dir,fname) for fname in disgusted_files[pic_index-1 : pic_index]]
next_fearful = [os.path.join(fearful_dir,fname) for fname in fearful_files[pic_index-1 : pic_index]]
next_neutral = [os.path.join(neutral_dir,fname) for fname in neutral_files[pic_index-1 : pic_index]]
next_sad = [os.path.join(sad_dir,fname) for fname in sad_files[pic_index-1 : pic_index]]
next_surprised = [os.path.join(surprised_dir,fname) for fname in surprised_files[pic_index-1 : pic_index]]

for i, img_path in enumerate(next_angry+next_happy+next_disgusted+next_fearful+next_neutral+next_sad+next_surprised):
    img = cv2.imread(img_path)
    plt.figure(figsize = (1, 1))
    plt.imshow(img)
    plt.axis('Off')
    plt.title(str(img_path).split('/')[2])
    plt.show()
print(img.shape)  

在这里插入图片描述

2.图片处理

from keras_preprocessing.image import ImageDataGenerator 
# ImageDataGenerator()是图片生成器,同时也可以在batch中对数据进行增强,扩充数据集大小,增强模型的泛化能力。比如进行旋转,变形,归一化等等

TRAINING_DIR = './train/'
training_datagen = ImageDataGenerator(
    rescale=1. / 255,  # 值将在执行其他处理前乘到整个图像上
    validation_split=0.25,  # 数据集划分
#     rotation_range=40,  # 整数,数据提升时图片随机转动的角度
#     width_shift_range=0.2,  # 浮点数,图片宽度的某个比例,数据提升时图片随机水平偏移的幅度
#     height_shift_range=0.2,  # 浮点数,图片高度的某个比例,数据提升时图片随机竖直偏移的幅度
#     shear_range=0.2,  # 浮点数,剪切强度(逆时针方向的剪切变换角度)。是用来进行剪切变换的程度
#     zoom_range=0.2,  # 用来进行随机的放大
#     horizontal_flip=True,  # 布尔值,进行随机水平翻转。随机的对图片进行水平翻转,这个参数适用于水平翻转不影响图片语义的时候
#     fill_mode='nearest'  # 'constant','nearest','reflect','wrap'之一,当进行变换时超出边界的点将根据本参数给定的方法进行处理
)

# flow_from_directory()以文件夹路径为参数,生成经过数据提升/归一化后的数据,在一个无限循环中无限产生batch数据
train_generator = training_datagen.flow_from_directory(    
    TRAINING_DIR,
    subset='training',
    target_size=(48, 48),
    class_mode='categorical'          #  "categorical", "binary", "sparse"或None之一
)
# 由于没有验证集文件夹,所以划分了训练集的25%作为验证集
validation_generator = training_datagen.flow_from_directory(
    TRAINING_DIR,
    subset='validation',
    target_size=(48, 48),
    class_mode='categorical'
)
print(train_generator.class_indices)

在这里插入图片描述

3.模型构建

import tensorflow as tf

print(tf.__version__)

在这里插入图片描述

model = tf.keras.models.Sequential([

    tf.keras.layers.Conv2D(32, (3, 3), activation='relu', input_shape=(48, 48, 3)),  # 卷积
    tf.keras.layers.MaxPooling2D(2, 2),   # 池化

    tf.keras.layers.Conv2D(64, (3, 3), activation='relu'),  # 卷积
    tf.keras.layers.MaxPooling2D(2, 2),   # 池化

    tf.keras.layers.Conv2D(128, (3, 3), activation='relu'),  # 卷积
    tf.keras.layers.MaxPooling2D(2, 2),   # 池化

    tf.keras.layers.Flatten(),
    tf.keras.layers.Dense(1024, activation='relu'),  # 全连接
    tf.keras.layers.Dropout(0.5),  # 一种防止神经网络过拟合的手段
    tf.keras.layers.Dense(7, activation='softmax')
])
model.summary()

在这里插入图片描述

4.模型训练

from keras.callbacks import ModelCheckpoint

# HDF5 文件一般以 .h5 或者 .hdf5 作为后缀名
model_checkpoint = ModelCheckpoint('./model/face_classify4.h5', 
                                   monitor='loss',         # monitor:'val_acc'|'loss'|'val_loss'|'acc'
                                   verbose=1,              # 如果你喜欢进度条,那就选1,如果喜欢清爽的就选0
                                   mode='min',             # 如果监视器monitor选val_acc, mode就选'max'|如果monitor选acc,mode也可以选'max'|如果monitor选loss,mode就选'min',一般情况下选'auto',
                                   period=1,               # checkpoints之间间隔的epoch数
                                   save_weights_only=False,# 若设置为True,则只保存模型权重,否则将保存整个模型(包括模型结构,配置信息等)
                                   save_best_only=True)    # 只保存最好的模型,也可以都保存

model.compile(loss='categorical_crossentropy', optimizer='Adam', metrics=['accuracy'])  # optimizer参考:https://www.cnblogs.com/GeekDanny/p/9655597.html

history = model.fit_generator(train_generator, epochs=30, validation_data=validation_generator, verbose=1, callbacks=[model_checkpoint])

# model.save('./model/face_classify4.h5')  # keras的模型一般保存为后缀名为h5的文件,上面已经存了

在这里插入图片描述
补充知识点!(save()和save_weights()的区别)

引用:https://blog.csdn.net/leviopku/article/details/86612293

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MasqbYw6-1628473290150)(attachment:image.png)]
使用save_weights()保存的模型不能直接load_model,应该重新把模型结构再描述一遍

acc = history.history['accuracy']
val_acc = history.history['val_accuracy']
loss = history.history['loss']
val_loss = history.history['val_loss']

epochs = range(len(acc))

plt.plot(epochs, acc, 'r', label='Training accuracy')
plt.plot(epochs, val_acc, 'b', label='validation accuracy')
plt.title('Training and validation accuracy')
plt.legend(loc=0)
plt.show()

在这里插入图片描述

5.验证集验证模型效果

from keras.models import load_model

model = load_model('./model/face_classify4.h5')

#Confution Matrix and Classification Report
Y_pred = model.predict_generator(validation_generator, nb_validation_samples // batch_size+1)  # batch_size = 512
y_pred = np.argmax(Y_pred, axis=1)

print('Confusion Matrix')
print(confusion_matrix(validation_generator.classes, y_pred))
print('Classification Report')
target_names = list(class_labels.values())
print(classification_report(validation_generator.classes, y_pred, target_names=target_names))

plt.figure(figsize=(8,8))
cnf_matrix = confusion_matrix(validation_generator.classes, y_pred)

plt.imshow(cnf_matrix, interpolation='nearest')
plt.colorbar()
tick_marks = np.arange(len(classes))
_ = plt.xticks(tick_marks, classes, rotation=90)
_ = plt.yticks(tick_marks, classes)

在这里插入图片描述
在这里插入图片描述

6.使用模型进行预测

from keras.models import load_model
import pandas as pd
import numpy as np
from tqdm import tqdm  # 进度条
from keras.preprocessing import image

# 在模型训练过程中使用了ModelCheckpoint来保存最优参数的模型,好像使用load_model和load_weights都行
model = load_model("./model/face_classify4.h5")   # keras.models.load_model() 读取网络、权重
# model.load_weights('./model/face_classify4.h5')  # keras.models.load_weights() 仅读取权重,load_model代码包含load_weights的代码,
                                                    # 区别在于load_weights时需要先有网络,
                                                    # 并且load_weights需要将权重数据写入到对应网络层的tensor中。

dataframe = pd.DataFrame(columns=['name','label'])
os.chdir('./test/')   # 原 D:\\python\\情绪识别

for file_name in tqdm(os.listdir()):
    img = image.load_img(file_name,target_size=(48,48))
    x = image.img_to_array(img)
    x = np.expand_dims(x, axis=0)
    images = np.vstack([x])
    classes = model.predict(images, batch_size=10)
    num = np.argmax(classes)

    if num == 0:   # {'angry': 0, 'disgusted': 1, 'fearful': 2, 'happy': 3, 'neutral': 4, 'sad': 5, 'surprised': 6}
        face = 'angry'
    if num == 1:
        face = 'disgusted'
    if num == 2:
        face = 'fearful'
    if num == 3:
        face = 'happy'
    if num == 4:
        face = 'neutral'
    if num == 5:
        face = 'sad'
    if num == 6:
        face = 'surprised'
    dataframe = dataframe.append({'name':file_name,'label':face},ignore_index=True)

dataframe.to_csv('../result/submit4.csv',index=False)

在这里插入图片描述


总结

做完了项目不容易,也尝试去提高了模型准确度,欢迎各位交流,也可以提供我一些指导。提到了priya-dwivedi的情绪检测项目,她训练的是一个六层卷积神经网络模型,GitHub地址:https://github.com/priya-dwivedi/face_and_emotion_detection,我也做了该项目的解读,链接地址:https://blog.csdn.net/weixin_45956028/article/details/119537657
个人认为图像处理过程:

Created with Raphaël 2.3.0 开始 图片查看 图片预处理 模型构建 模型训练 模型性能度量 预测 结束

pytorch版本实现,完整过程请直接进入网盘下载:
链接:https://pan.baidu.com/s/1v79Xzcb8FpFxI7EjWjwOpQ?pwd=1234
提取码:1234

class EmotionClassifier(nn.Module):
    def __init__(self):
        super(EmotionClassifier, self).__init__()
        self.conv1 = nn.Conv2d(1, 32, 3, padding=1)  # 增加卷积核数量,减小核大小
        self.conv2 = nn.Conv2d(32, 64, 3, padding=1)
        self.conv3 = nn.Conv2d(64, 128, 3, padding=1)
        self.pool = nn.MaxPool2d(2, 2)
        self.fc1 = nn.Linear(128 * 6 * 6, 256)  # 增加全连接层的神经元数量
        self.fc2 = nn.Linear(256, 7)  # 输出层保持不变
        self.dropout = nn.Dropout(0.5)

    def forward(self, x):
        x = self.pool(F.relu(self.conv1(x)))
        x = self.pool(F.relu(self.conv2(x)))
        x = self.pool(F.relu(self.conv3(x)))
        x = x.view(-1, 128 * 6 * 6)
        x = F.relu(self.fc1(x))
        x = self.dropout(x)
        x = self.fc2(x)
        return x

调整了数据训练与测试集后,训练更多轮结果:
在这里插入图片描述

在这里插入图片描述

  • 41
    点赞
  • 351
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 20
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 20
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

机智的小神仙儿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值