人脸表情识别系统介绍——上篇(python实现,含UI界面及完整代码)

简介: 人脸表情识别系统介绍——上篇(python实现,含UI界面及完整代码)

1. 前言


   在这个人工智能成为超级大热门的时代,人脸表情识别已成为其中的一项研究热点,而卷积神经网络、深度信念网络和多层感知器等相关算法在人脸面部表情识别领域的运用最为广泛。面部的表情中包含了太多的信息,轻微的表情变化都会反映出人心理的变化,可想而知如果机器能敏锐地识别人脸中表达的情感该是多么令人兴奋的事。

   当前深度学习发展迅速,关于表情识别IEEE上面有许多质量很高的文章,里面介绍的是利用深度神经网络实现的面部表情识别,可以学习和参考。于是自己动手做了这个项目,这里特此将前期工作作个总结,希望能给类似工作的朋友带来一点帮助。这里使用的是已有的模型——如今CNN的主流框架之mini_XCEPTION,该模型性能也已是不错的了,关于更高性能模型以后更新,后面也会分享给大家,敬请关注。



2. 表情识别数据集


   目前,现有的公开的人脸表情数据集比较少,并且数量级比较小。比较有名的广泛用于人脸表情识别系统的数据集Extended Cohn-Kanada (CK+)是由P.Lucy收集的。CK+数据集包含123 个对象的327 个被标记的表情图片序列,共分为正常、生气、蔑视、厌恶、恐惧、开心和伤心七种表情。对于每一个图片序列,只有最后一帧被提供了表情标签,所以共有327 个图像被标记。为了增加数据,我们把每个视频序列的最后三帧图像作为训练样本。这样CK+数据总共被标记的有981 张图片。这个数据库是人脸表情识别中比较流行的一个数据库,很多文章都会用到这个数据做测试,可通过下面的链接下载。


官网链接:The Extended Cohn-Kanade Dataset(CK+)

网盘链接:百度网盘下载(提取码:8r15


   KaggleKaggle人脸表情分析比赛提供的一个数据集。该数据集含28709 张训练样本,3859 张验证数据集和3859 张测试样本,共35887 张包含生气、厌恶、恐惧、高兴、悲伤、惊讶和正常七种类别的图像,图像分辨率为48×48。该数据集中的图像大都在平面和非平面上有旋转,并且很多图像都有手、头发和围巾等的遮挡物的遮挡。该数据库是2013年Kaggle比赛的数据


官网链接:FER2013

网盘链接:百度网盘下载(提取码:t7xj



   由于FER2013数据集数据更加齐全,同时更加符合实际生活的场景,所以这里主要选取FER2013训练和测试模型。为了防止网络过快地过拟合,可以人为的做一些图像变换,例如翻转,旋转,切割等。上述操作称为数据增强。数据操作还有另一大好处是扩大数据库的数据量,使得训练的网络鲁棒性更强。下载数据集保存在fer2013的文件夹下,为了对数据集进行处理,采用如下代码载入和进行图片预处理:

python
import pandas as pd
import cv2
import numpy as np
dataset_path = 'fer2013/fer2013/fer2013.csv' # 文件保存位置
image_size=(48,48) # 图片大小
# 载入数据
def load_fer2013():
        data = pd.read_csv(dataset_path)
        pixels = data['pixels'].tolist()
        width, height = 48, 48
        faces = []
        for pixel_sequence in pixels:
            face = [int(pixel) for pixel in pixel_sequence.split(' ')]
            face = np.asarray(face).reshape(width, height)
            face = cv2.resize(face.astype('uint8'),image_size)
            faces.append(face.astype('float32'))
        faces = np.asarray(faces)
        faces = np.expand_dims(faces, -1)
        emotions = pd.get_dummies(data['emotion']).as_matrix()
        return faces, emotions
# 将数据归一化
def preprocess_input(x, v2=True):
    x = x.astype('float32')
    x = x / 255.0
    if v2:
        x = x - 0.5
        x = x * 2.0
    return x
    载入数据后将数据集划分为训练集和测试集,在程序中调用上面的函数代码如下:
python
from load_and_process import load_fer2013
from load_and_process import preprocess_input
from sklearn.model_selection import train_test_split
# 载入数据集
faces, emotions = load_fer2013()
faces = preprocess_input(faces)
num_samples, num_classes = emotions.shape
# 划分训练、测试集
xtrain, xtest,ytrain,ytest = train_test_split(faces, emotions,test_size=0.2,shuffle=True)

3. 搭建表情识别的模型


   接下来就是搭建表情识别的模型了,这里用到的是CNN的主流框架之mini_XCEPTIONXCEPTIONGoogleInception后提出的对Inception v3的另一种改进,主要是采用深度可分离的卷积(depthwise separable convolution)来替换原来Inception v3中的卷积操作。XCEPTION的网络结构在ImageNet数据集(Inception v3的设计解决目标)上略优于Inception v3,并且在包含3.5亿个图像甚至更大的图像分类数据集上明显优于Inception v3,而两个结构保持了相同数目的参数,性能增益来自于更加有效地使用模型参数,详细可参考论文:Xception: Deep Learning with Depthwise Separable Convolutions,论文Real-time Convolutional Neural Networks for Emotion and Gender Classification等。



   既然这样的网络能获得更好结果又是主流,那当然有必要作为对比算法实现以下了,这里博主模型这部分的代码引用了GitHub:https://github.com/oarriaga/face_classification中的模型(其他地方也能找到这个模型的类似代码),模型框图如上图所示,其代码如下:

python
def mini_XCEPTION(input_shape, num_classes, l2_regularization=0.01):
    regularization = l2(l2_regularization)
    # base
    img_input = Input(input_shape)
    x = Conv2D(8, (3, 3), strides=(1, 1), kernel_regularizer=regularization,
                                            use_bias=False)(img_input)
    x = BatchNormalization()(x)
    x = Activation('relu')(x)
    x = Conv2D(8, (3, 3), strides=(1, 1), kernel_regularizer=regularization,
                                            use_bias=False)(x)
    x = BatchNormalization()(x)
    x = Activation('relu')(x)
    # module 1
    residual = Conv2D(16, (1, 1), strides=(2, 2),
                      padding='same', use_bias=False)(x)
    residual = BatchNormalization()(residual)
    x = SeparableConv2D(16, (3, 3), padding='same',
                        kernel_regularizer=regularization,
                        use_bias=False)(x)
    x = BatchNormalization()(x)
    x = Activation('relu')(x)
    x = SeparableConv2D(16, (3, 3), padding='same',
                        kernel_regularizer=regularization,
                        use_bias=False)(x)
    x = BatchNormalization()(x)
    x = MaxPooling2D((3, 3), strides=(2, 2), padding='same')(x)
    x = layers.add([x, residual])
    # module 2
    residual = Conv2D(32, (1, 1), strides=(2, 2),
                      padding='same', use_bias=False)(x)
    residual = BatchNormalization()(residual)
    x = SeparableConv2D(32, (3, 3), padding='same',
                        kernel_regularizer=regularization,
                        use_bias=False)(x)
    x = BatchNormalization()(x)
    x = Activation('relu')(x)
    x = SeparableConv2D(32, (3, 3), padding='same',
                        kernel_regularizer=regularization,
                        use_bias=False)(x)
    x = BatchNormalization()(x)
    x = MaxPooling2D((3, 3), strides=(2, 2), padding='same')(x)
    x = layers.add([x, residual])
    # module 3
    residual = Conv2D(64, (1, 1), strides=(2, 2),
                      padding='same', use_bias=False)(x)
    residual = BatchNormalization()(residual)
    x = SeparableConv2D(64, (3, 3), padding='same',
                        kernel_regularizer=regularization,
                        use_bias=False)(x)
    x = BatchNormalization()(x)
    x = Activation('relu')(x)
    x = SeparableConv2D(64, (3, 3), padding='same',
                        kernel_regularizer=regularization,
                        use_bias=False)(x)
    x = BatchNormalization()(x)
    x = MaxPooling2D((3, 3), strides=(2, 2), padding='same')(x)
    x = layers.add([x, residual])
    # module 4
    residual = Conv2D(128, (1, 1), strides=(2, 2),
                      padding='same', use_bias=False)(x)
    residual = BatchNormalization()(residual)
    x = SeparableConv2D(128, (3, 3), padding='same',
                        kernel_regularizer=regularization,
                        use_bias=False)(x)
    x = BatchNormalization()(x)
    x = Activation('relu')(x)
    x = SeparableConv2D(128, (3, 3), padding='same',
                        kernel_regularizer=regularization,
                        use_bias=False)(x)
    x = BatchNormalization()(x)
    x = MaxPooling2D((3, 3), strides=(2, 2), padding='same')(x)
    x = layers.add([x, residual])
    x = Conv2D(num_classes, (3, 3),
            #kernel_regularizer=regularization,
            padding='same')(x)
    x = GlobalAveragePooling2D()(x)
    output = Activation('softmax',name='predictions')(x)
    model = Model(img_input, output)
    return model

4. 数据增强的批量训练


   神经网络的训练需要大量的数据,数据的量决定了网络模型可以达到的高度,网络模型尽量地逼近这个高度。然而对于人脸表情的数据来说,都只存在少量的数据Extended Cohn-Kanada (CK+)的数据量是远远不够的,并且CK+多是比较夸张的数据。Kaggle Fer2013数据集也不过只有3万多数据量,而且有很多遮挡、角度等外界影响因素。既然收集数据要花费很大的人力物力,那么我们就用技术解决这个问题,为避免重复开发首先还是看看有没有写好的库。博主又通读了遍Keras官方文档,其中ImageDataGenerator的图片生成器就可完成这一目标。

为了尽量利用我们有限的训练数据,我们将通过一系列随机变换堆数据进行提升,这样我们的模型将看不到任何两张完全相同的图片,这有利于我们抑制过拟合,使得模型的泛化能力更好。在Keras中,这个步骤可以通过keras.preprocessing.image.ImageGenerator来实现,这个类使你可以:在训练过程中,设置要施行的随机变换通过.flow或.flow_from_directory(directory)方法实例化一个针对图像batch的生成器,这些生成器可以被用作keras模型相关方法的输入,如fit_generator,evaluate_generatorpredict_generator。——Keras官方文档

   ImageDataGenerator()是一个图片生成器,同时也可以在batch中对数据进行增强,扩充数据集大小(比如进行旋转,变形,归一化等),增强模型的泛化能力。结合前面的模型和数据训练部分的代码如下:


python
"""
Description: 训练人脸表情识别程序
"""
from keras.callbacks import CSVLogger, ModelCheckpoint, EarlyStopping
from keras.callbacks import ReduceLROnPlateau
from keras.preprocessing.image import ImageDataGenerator
from load_and_process import load_fer2013
from load_and_process import preprocess_input
from models.cnn import mini_XCEPTION
from sklearn.model_selection import train_test_split
# 参数
batch_size = 32
num_epochs = 10000
input_shape = (48, 48, 1)
validation_split = .2
verbose = 1
num_classes = 7
patience = 50
base_path = 'models/'
# 构建模型
model = mini_XCEPTION(input_shape, num_classes)
model.compile(optimizer='adam', # 优化器采用adam
              loss='categorical_crossentropy', # 多分类的对数损失函数
              metrics=['accuracy'])
model.summary()
# 定义回调函数 Callbacks 用于训练过程
log_file_path = base_path + '_emotion_training.log'
csv_logger = CSVLogger(log_file_path, append=False)
early_stop = EarlyStopping('val_loss', patience=patience)
reduce_lr = ReduceLROnPlateau('val_loss', factor=0.1,
                              patience=int(patience/4),
                              verbose=1)
# 模型位置及命名
trained_models_path = base_path + '_mini_XCEPTION'
model_names = trained_models_path + '.{epoch:02d}-{val_acc:.2f}.hdf5'
# 定义模型权重位置、命名等
model_checkpoint = ModelCheckpoint(model_names,
                                   'val_loss', verbose=1,
                                    save_best_only=True)
callbacks = [model_checkpoint, csv_logger, early_stop, reduce_lr]
# 载入数据集
faces, emotions = load_fer2013()
faces = preprocess_input(faces)
num_samples, num_classes = emotions.shape
# 划分训练、测试集
xtrain, xtest,ytrain,ytest = train_test_split(faces, emotions,test_size=0.2,shuffle=True)
# 图片产生器,在批量中对数据进行增强,扩充数据集大小
data_generator = ImageDataGenerator(
                        featurewise_center=False,
                        featurewise_std_normalization=False,
                        rotation_range=10,
                        width_shift_range=0.1,
                        height_shift_range=0.1,
                        zoom_range=.1,
                        horizontal_flip=True)
# 利用数据增强进行训练
model.fit_generator(data_generator.flow(xtrain, ytrain, batch_size),
                        steps_per_epoch=len(xtrain) / batch_size,
                        epochs=num_epochs,
                        verbose=1, callbacks=callbacks,
                        validation_data=(xtest,ytest))


   以上代码中设置了训练时的结果输出,在训练结束后会将训练的模型保存为hdf5文件到自己指定的文件夹下,由于数据量大模型的训练时间会比较长,建议使用GPU加速。训练结束后测试得到混淆矩阵如下:



   训练的模型综合在FER2013数据集上的分类准确率为71%,算是中等偏上水平,其实并非模型不好而是在数据预处理、超参数的选取上有很大的可提升空间,当然也可使用其他的模型,譬如可参考论文:Extended deep neural network for facial emotion recognition,大家可自行研究,这里就不多介绍了。


5. 系统UI界面的实现


   上面的模型训练好了,但对于我们来说它的作用就只是知道了其准确率还行,其实深度学习的目的最重要还是应用,是时候用上面的模型做点酷酷的东西了。可不可以用上面的模型识别下自己表达的情绪呢?不如做个系统调取摄像头对实时画面中的表情进行识别并显示识别结果,既能可视化的检测模型的实用性能,同时使得整个项目生动有趣激发自己的创造性,当你向别人介绍你的项目时也显得高大上。这里采用PyQt5进行设计,首先看一下最后的效果图,运行后的界面如下:



   设计功能:

   (1)可选择模型文件后基于该模型进行识别;

   (2)打开摄像头识别实时画面中的人脸表情;

   (3)选择一张人脸图片,对其中的表情进行识别;

   (4)选择要识别的视频文件,识别画面中的表情;

   我们可以选择一张图片测试识别效果,如下图所示:



   博主对UI界面的要求是可以简单但颜值必须高,必须高,实用简约高颜值是我奉行的标准,以上的界面几经修改才有了上面的效果。当然博主的目的并不单纯的想秀,而是借此做一个测试模型的系统,可以选择模型、训练测试集等以便界面化地对后面的模型进行各种测试评估,生成特定结果数据图或表格等,这个测试系统后面有机会分享给大家。

   系统UI界面的实现这部分又设计PyQt5的许多内容,在这一篇博文中介绍恐怕尾大不掉,效果也不好,所以更多的细节内容将在后面的博文中介绍,敬请期待!有需要的朋友可通过下面的链接下载这部分的文件。

【下载链接】

   若您想获得博文中涉及的实现完整全部程序文件(包括数据集,py, UI文件等,如下图),这里已打包上传至博主的面包多下载资源中。文件下载链接如下:



数据链接:训练用到的数据集(提取码:t7xj

   本资源已上传至面包多网站,可以点击以下链接获取,已将数据集同时打包到里面,点击即可运行,完整文件下载链接如下:


完整资源下载链接博主在面包多网站上的完整资源下载页

人脸表情识别介绍与演示视频链接https://www.bilibili.com/video/BV18C4y1H7mH/

【运行程序须知】


   请配置Python3.7,要安装的库如下(以下是博主安装的版本),如您想直接运行界面程序,只需在下载链接中的文件和按照人脸表情识别系统介绍——离线环境配置篇的安装步骤配置环境后,运行runMain.py程序。

powershell
    keras==2.2.4
    PyQt5==5.11.3
    pandas==0.24.2
    scikit-learn==0.21.2
    tensorflow==1.13.1
    imutils==0.5.2
    opencv-python==4.10.25
    matplotlib==3.2.1  # 注意:此依赖包为第二版新增,请注意安装
相关文章
|
5月前
|
存储 分布式计算 大数据
基于Python大数据的的电商用户行为分析系统
本系统基于Django、Scrapy与Hadoop技术,构建电商用户行为分析平台。通过爬取与处理海量用户数据,实现行为追踪、偏好分析与个性化推荐,助力企业提升营销精准度与用户体验,推动电商智能化发展。
|
5月前
|
机器学习/深度学习 大数据 关系型数据库
基于python大数据的台风灾害分析及预测系统
针对台风灾害预警滞后、精度不足等问题,本研究基于Python与大数据技术,构建多源数据融合的台风预测系统。利用机器学习提升路径与强度预测准确率,结合Django框架实现动态可视化与实时预警,为防灾决策提供科学支持,显著提高应急响应效率,具有重要社会经济价值。
|
5月前
|
机器学习/深度学习 大数据 关系型数据库
基于python大数据的青少年网络使用情况分析及预测系统
本研究基于Python大数据技术,构建青少年网络行为分析系统,旨在破解现有防沉迷模式下用户画像模糊、预警滞后等难题。通过整合多平台亿级数据,运用机器学习实现精准行为预测与实时干预,推动数字治理向“数据驱动”转型,为家庭、学校及政府提供科学决策支持,助力青少年健康上网。
|
10月前
|
开发框架 前端开发 JavaScript
【HarmonyOS Next之旅】基于ArkTS开发(二) -> UI开发一
本文介绍了方舟开发框架(ArkUI)及其两种开发范式:基于ArkTS的声明式开发范式和类Web开发范式。ArkUI是用于构建HarmonyOS应用界面的UI框架,提供极简UI语法和基础设施。声明式开发范式使用ArkTS语言,以组件、动画和状态管理为核心,适合复杂团队协作;类Web开发范式采用HML、CSS、JavaScript三段式开发,适用于简单界面应用,贴近Web开发者习惯。文中还概述了两者的架构和基础能力,帮助开发者选择合适的范式进行高效开发。
339 15
|
10月前
|
编解码 前端开发 Java
【HarmonyOS Next之旅】基于ArkTS开发(二) -> UI开发三
本文介绍了基于声明式UI范式的图形绘制与动画效果实现方法,涵盖绘制图形、添加动画效果及常见组件说明三部分内容。在绘制图形部分,详细讲解了如何通过Circle组件为食物成分表添加圆形标签,以及使用Path组件结合SVG命令绘制自定义图形(如应用Logo)。动画效果部分则展示了如何利用animateTo实现闪屏动画,包括渐出、放大效果,并设置页面跳转;同时介绍了页面间共享元素转场动画的实现方式。最后,文章列举了声明式开发范式中的各类组件及其功能,帮助开发者快速上手构建复杂交互页面。
368 11
|
6月前
|
存储 开发者 容器
鸿蒙 HarmonyOS NEXT星河版APP应用开发-ArkTS面向对象及组件化UI开发使用实例
本文介绍了ArkTS语言中的Class类、泛型、接口、模块化、自定义组件及状态管理等核心概念,并结合代码示例讲解了对象属性、构造方法、继承、静态成员、访问修饰符等内容,同时涵盖了路由管理、生命周期和Stage模型等应用开发关键知识点。
496 1
鸿蒙 HarmonyOS NEXT星河版APP应用开发-ArkTS面向对象及组件化UI开发使用实例
|
9月前
|
JavaScript 前端开发 UED
【HarmonyOS Next之旅】基于ArkTS开发(二) -> UI开发四
本文介绍了Web组件开发与性能优化的相关内容。在Web组件开发部分,涵盖创建组件、设置样式与属性、添加事件和方法以及场景示例,如动态播放视频。性能提升方面,推荐使用数据懒加载、条件渲染替代显隐控制、Column/Row替代Flex、设置List组件宽高及调整cachedCount减少滑动白块等方法,以优化应用性能与用户体验。
323 56
|
9月前
|
编解码 UED 开发者
【HarmonyOS Next之旅】基于ArkTS开发(二) -> UI开发之常见布局
本文主要介绍了自适应布局与响应式布局的相关内容。自适应布局部分涵盖线性布局、层叠布局、弹性布局和网格布局,详细说明了各布局的特性及使用方法,例如线性布局中的排列、拉伸与缩放,弹性布局的方向、换行与对齐方式等。响应式布局则重点讲解了栅格系统和媒体查询,阐述如何通过栅格组件和媒体查询条件实现不同设备上的适配效果。这些技术帮助开发者灵活应对多尺寸屏幕的设计需求,提升用户体验。
463 55
|
前端开发 安全 开发工具
【11】flutter进行了聊天页面的开发-增加了即时通讯聊天的整体页面和组件-切换-朋友-陌生人-vip开通详细页面-即时通讯sdk准备-直播sdk准备-即时通讯有无UI集成的区别介绍-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
【11】flutter进行了聊天页面的开发-增加了即时通讯聊天的整体页面和组件-切换-朋友-陌生人-vip开通详细页面-即时通讯sdk准备-直播sdk准备-即时通讯有无UI集成的区别介绍-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
883 90
【11】flutter进行了聊天页面的开发-增加了即时通讯聊天的整体页面和组件-切换-朋友-陌生人-vip开通详细页面-即时通讯sdk准备-直播sdk准备-即时通讯有无UI集成的区别介绍-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex

热门文章

最新文章

推荐镜像

更多