基于卷积网络的人脸表情识别及其应用


前言

提示:本文将从卷积网络以及人脸表情识别技术背景开始详细且完整地描述一个基于卷积神经网络的模型是如何搭建、训练、优化以及实际应用。
ps:在实际应用阶段将使用人脸表情识别模型以及百度AI人体姿态识别接口实现对学生学习状态的初步评估和分析。


注:未经作者授权,请勿转载

一、人脸表情识别技术现状

人脸表情识别技术是一种基于人工智能技术与生物学联系再一起的技术,是交叉型学科研究。这项技术的出现表明了计算机技术发展方向的多样性以及应用领域的多样性,但同时也意味着人脸表情识别技术的发展不仅需要计算机技术的支持还需要生物学领域的探索进步。

目前人脸表情识别理论将面部情感状态识别过程划分为三个阶段即人脸检测阶段、人脸表情特征提取阶段、人脸表情识别阶段。

  • 人脸检测是面部表情识别的第一阶段,这一阶段是保证面部表情识别精准性的首要环节,是整个面部表情识别的基础。人脸检测阶段要准确检测并提取出到目标区域中人脸的位置,有了基础数据准确性的保障之后特征提取、表情识别阶段才会有良好的效果。

  • 人脸表情识别阶段主要是通过卷积神经网络模型的计算(卷积核)提取面部图像相关特征数据,为之后表情识别提供有效的数据特征。

  • 第三阶段面部表情的识别,通过对数据特征的分类判断当前的面部图像属于什么表情。

目前人脸面部表情的识别还面临着一些问题,比如:

  • 人脸识别等任务数据不足、不精准,对与一些面部表情的生物学界限鉴定不清晰,数据中基本只有正面图像,深度学习容易过拟合。

  • 人脸表情数据受光照、色彩等外界条件影响大。

  • 个体间差异过大,不同个体间面部特征和情感状态的表达存在较大差异;不同表情之间有叠加的情况发生,增大识别难度。

  • 面部表情的识别更加抽象细微,不仅需要识别眼睛、嘴等人脸基本特征,还需要识别上述人脸基本特征的相对位置、角度、形变程度等细节。

二、卷积神经网络技术概述

1.卷积神经网络图像理解过程

卷积网络模型人脸表情识别的过程如下:
初始卷积层对局部线条或图形边界进行数据特征提取,识别局部图像中基本的曲线、边界等内容;中间层级的卷积层将初始卷积层识别到的特征信息进行整合实现人脸局部特征的识别,比如眼睛、嘴、鼻子等;较深层级的卷积层对眼睛、鼻子等人脸局部特征进行更上层整体分析判断,最终对人脸面部表情进行分类完成人脸表情的识别。

2.卷积神经网络组成结构

卷积神经网络主要由卷积层、池化层、全连接层、软化层(softmax层)以及实现不同功能的操纵组成。合理的设置上述层结构并在不同层级之间按需进行Dropout、NB等操作才能最终形成一个高效、准确率高的卷积神经网络模型。

  • 卷积层
    卷积核是一系列的滤波器,用来提取某一种特征。我们用卷积核来处理一个图片,当图像特征与过滤器表示的特征相似时,卷积操作可以得到一个比较大的值,当图像特征与过滤器不相似时,卷积操作可以得到一个比较小的值,实际上,卷积的结果特征映射图显示的是对应卷积核所代表的特征在原始特征图上的分布情况。每个卷积核生成一个特征图,这些特征图堆叠起来组成整个卷积层的输出结果。卷积计算体现了参数共享和局部连接的模式。每个卷积核的大小代表了一个感受野的大小。卷积计算后得到的特征图大小为(W-F+2*P)/s+1;P 为填充 s 为步长[11]。
  • 池化层
    依据图像局部相关性的原理,即局部相邻的像素点间存在一定的共性和联系,一定数值的像素点可以代表局部区域所有像素点的特征。常见的池化层思想认为最大值或者均值代表了这个局部的特征,从局部区域选择最有代表性的像素点数值代替该区域,可以减少数据处理量同时保留最具特征的数据信息。池化操作可以逐渐降低数据体的空间尺寸并保持一定的数据关联性,能有效减少网络中参数的数量,使得计算资源耗费降低,也能对过拟合产生一定的预防。
  • 全连接层
    全连接层将特征图转化为类别输出。全连接层不止一层,为了防止过拟合会在各全连接层之间引入DropOut操作。
  • Softmax层
    Softmax层不属于CNN中特有的结构层级,将图像分类的结果以概率的形式输出。Softmax层所有的输出相加为1,按照这个概率的大小判断图像最终属于哪一类别。

3.卷积神经网络的优势

  • 简化参数
    传统神经网络处理图像信息需要将图片转换成n个像素点,并通过对n个像素点的就算提取图像特征,需要处理的数据量太大,导致成本很高,效率很低,卷积神经网络通过卷积计算、池化等操作,从原始图片信息种提炼最突出的数据特征再做处理。在大部分场景下降维并不会影响人类对于图片数据的理解。
  • 局部连接
  • 局部连接,就是卷积层的节点仅仅和其前一层的部分节点相连接,只用来学习局部特征。在计算机视觉中,图像中的某一块区域
    ,像素之间的相关性与像素之间的距离同样相关,相邻的像素点往往表示的是一个局部特征信息,相隔很远的像素之间表示的是无关联的因素。卷积计算非常符合局部相关性原理,卷积网络可以做到逐层提炼图像数据的局部数据特征。

三、人脸表情识别卷积网络模型

1. 模型搭建、训练环境

Keras是基于神经网络引擎的高层神经网络API,python语言编写,支持Tensorflow、Theano等主流神经网络框架作为后端。Keras 将常见的神经网络计算过程封装为独立的模块并提供简单易用的接口。keras能够快速地搭建神经网络,支持CNN、RNN或二者的结合。

本次模型大家将以Tensorflow为神经网络计算环境,使用keras进行卷积网络模型的搭建、训练和优化。

2. 数据集的选择及划分

经广泛搜集和查找目前能够获取到CK+、MMI、FER-2013三个用于人脸面部表情识别数据集。三个数据集都包含笑、哭、惊讶等六个基本地人脸表情状态,CK+和MMI数据集图片数据量较少,虽然FER-2013数据集部分图片确实精准程度不高但其图片规格较小能够较好的适应博主目前软、硬件条件且能基本满足本次课题需求,笔者选择FER-2013作为课题研究的数据集。

FER-2013数据集包含35887张人脸表情单通道灰度图片,包括生气、厌恶、恐惧、开心、伤心、惊讶、中性七种表情。之后模型的应用会将采集的图像进行灰度处理再输入卷积神经网络模型进行分析判断,可以一定程度上解决面部表情识别易受光感、色彩影响的问题。

3. 模型初步设计及配置

初步设计网络结构

初步设计:整个卷积神经网络由三个卷积段、三个全连接层、一个软化层组成,每个卷积段包含具有相同卷积操作的两个卷积层。由于笔者物理机内存空间有限,模型的训练采用批处理的形式 batch_size 设置为:256;采用Adam优化器训练过程中自动调节学习率;所有卷积层均采用3*3的卷积核;采用sigmoid 激活函数。

4. 卷积网络模型的优化

选择高效的激活函数:

初始的卷积神经网络结构中使用的是sigmod激活函数,sigmod激活函数在饱和区梯度趋于零或显著减缓梯度下降速度甚至导致梯度消失。在大量的中间层计算中难免会输入数据落在使sigmod函数值趋近于1和-1的饱和区间,因此改用relu激活函数避免上述问题。Relu激活函数在整个函数域内是非线性的,但在局部范围内是线性的。Relu函数对于小于零的输入置为0,对于非零的输入数值进行线性的y=x运算操作,所以relu函数在非负输入实际上并不需要进行数值计算,只要判断阈值,判断输入是否为零即可。因此relu函数可以很好地加快神经网络的运算速度并且规避sigmod、arctanx激活函数所引起梯度消失的问题。

添加L2 regularization(权重衰减):

L2 正则化是指在损失函数后面加上L2正则项,在求导之后正则项会对W即权值的更新进行影响,求导之后L2正则化的参数会使原始W权重的系数小于1,从而对权重进行衰减,减缓梯度下降的速度,减缓模型的学习效果,防止模型在前期的下降过程中对训练集的学习效果过于“理想”但在验证集达准确率较低,没有很好的泛化效果
L2正则化在反向传播梯度下降求导更新权重参数的时候会使原始w的系数小于1,从而达到减缓w更新速度防止模型的过拟合。

增加Batch Normalization操作:

在卷积层完成卷积计算之后,数据进入激活函数之前加入批标准化处理,在数据进入激活函数之前将一个batch中的数据进行标准化处理使数据尽量落在激活函数梯度较陡的区域避免梯度消失,提高模型的泛化性。但加入批标准化处理后正确率没有较大的提升稳定在61%,有可能是因为使用了较小的卷积核。

增加Dropout操作:

对应的卷积层之后的进行Dropout操作会随机放弃一定概率的节点信息,以放弃部分计算结果的方式防止模型的“过度学习”导致过拟合的发生。在全连接层计算后增加Dropout操作,将放弃节点概率设置为0.2即在每层全连接层计算以及每个卷积段计算后放弃20%的节点信息;在每个卷积段之后增加Dropout操作,将放弃节点概率设置为0.5。增加Dropout操作后正确率有了较大提升,达到65%。由于在卷积层放弃了很多计算节点有效缓解了过拟合问题,在迭代30次之前模型学习效果都非常明显,在迭代30-80次val_loss下降趋势减缓趋于但依然缓慢下降。

优化后的卷积网络模型如下图:
在这里插入图片描述

5. 模型最终效果

经过上述训练、优化卷积神经网络模型在验证集的准确率稳定在65%,批训练期间测试集达到的最高准确率为68%。基本达到人类对生气、厌恶、恐惧、开心、伤心、惊讶、中性七种面部所展现出的情感状态认知水平。其中对于开心、伤心、生气三种特征较为明显的表情识别成功率较高超过65%的平均水平,而对于恐惧、厌恶、惊讶、中性表情的识别识别成功率较低,容易在相似表情之间判断出现错误,比如将中性判断成厌恶。

通过调节简化卷积神经网络结构,在卷积计算后增加批标准化处理操作,在全连接层计算之后加入Dropout操作可以较为有效地改进模型学习效果、缓解过拟合的问题。后期的实验应该寻求数据的改变,运用相应的数据增强方法进一步提高模型在验证集的准确率。
在这里插入图片描述

四 、人脸表情识别模型实际应用(学生学习状态实时评估系统)

提示:
在模型实际应用阶段我们将搭建一个简单的学生学习状态实时评估系统。该系统将通过对学生面部表情的分析以及学生坐姿的检测结合一些心理学的结论实现对学生学习状态的评估和分析。

由于文章篇幅过长,文章中所涉及到的代码并未完全展示,之后会把详细完整代码更新至单独一篇文章中。

1. Opencv 图像处理

视频图像获取

学生学习状态检测所需要的数据由GetView函数进行获取。GetView函数中使用Opencv库提供的VideoCapture()方法完成对摄像设备的捕获和控制。通过调整输入VideoCapture()的参数可以实现对本地视频的读取,获取本机摄像头获取实时视频资源等操作。

人脸目标检测

人脸目标检测是实现人脸表情识别的首要环节,使用Opencv库中自带的Haar分类器以及其他图像分隔方法实现人脸的目标检测并架构面部表情图像区域与其他无关区域进行分割,保留单一的面部区域,从而保证卷积神经网络模型表情识别的精准程度。

Haar分类器以xml文档的形式保存于OpenCV安装目录下的Lib\site-packages\cv2\data路径。其中由专门针对于肩膀、人脸等目标的分类检测器。Haar分类器在使用时需要提前加载相应的xml文件。在人脸目标检测阶段需要加载haarcascade_frontalface_default.xml人脸正面目标检测器文件。在将获取的的图像输入分类器前要进行灰度处理,经分类器检测后分类器会返回人脸在图像中的左上角的坐标位置和人脸区域图像的长和宽,如果由多个面部表情出现则会返回上述信息组成的数组。

使用python的切片功能按照分类器返回的人脸面部图像的区域对灰度图像进行切割只保留面部区域,再对分割后得到的面部图像进行灰度处理并将大小调整为48*48,之后才可以输入卷积神经网络进行分析。

2. 人体坐姿正确性检测

由于受限于物理环境和时间,人体坐姿正确性的检测部分,不再在本地搭建和训练卷积神经网络模型直接采用百度AI人体姿态检测接口,将本地获取的图片数据转换成字符串的形式传输到百度AI云端进行数据分析。分析完成之后将返回以json的格式返回人体头部、肩部、肘部、腿部等12个人体关键点在图像中的像素点坐标信息。由于本次课题研究主要采用第一机位获取图像数据,获取的信息一般只限人体于头部到腹部的图像数据,所以坐姿的正确性检测只能完成头部、颈部、肩部区域。若后期时间充裕会考虑使用第二机位结合外部视频获取设备从侧面以及整体的角度更完整地评估坐姿的正确性。
根据教育相关心理学结论,学生处余自然放松状态下坐姿时学习状态和表现最为良好,若学生或周围的大部分人以环抱双臂的状态听课时,听课的效果相比自然放松状态下的效果差至少26%。因此在坐姿检测阶段,系统将重点检测学生是否处于一种非自然放松状态下的坐姿,具体包括环抱双臂、两肩部高度不一致等。

3. 采集及检测数据分析

根据相关论文论述和分析:大部分学生处于良好的学状态时身心状态都处于自然放松的状态,面部的表情反馈处于中性状态,没有突出的情绪状态倾向,有可能会出现短暂的微笑和皱眉的面部特征变化。当学生面部表情在一段时间内多次出现明显甚至较为夸张的面部表情时,大部分学生的学习状态会明显下降。比如:学生一段时间内多次出现微笑甚至大笑的表情时很有可能正处于“走神”、“溜号”状态。

此次课题研究将通过卷积神经网络对学生学习时面部表情进行识别并记录,综合微表情及教育心理学相关论文对表情反应出学生学习状态的阐述[16],统计一段时间内学生学习时的面部表情分析学生此段时间的学习状态。学生在一段时间内中性表情和微笑表情占比越高且没有其他不良坐姿则学习状态越好,若出现了多次悲伤、惊讶、愤怒等表情学习状态将被评估为较差的状态[17]。

4. 学习状态分析报告及邮件发送

在完成对学生面部表情、坐姿状态的统计分析之后,会对这一时间段内的学生的学习状态进行评估并形成最终的学习状态分析报告。包括阶段内学生的表情信息统计和坐姿状态。报告将会自动发送到指定邮箱。

学习状态分析报告将统计出学生一段学习时间内生气、厌恶、恐惧、开心、伤心、惊讶、中性七种面部所展现出的情感状态,各种非自然放松状态坐姿出现的次数和频率,并根据面部表情和坐姿的数据统计分析出学生一段时间内占比最高的表情,并根据不同表情的占比和坐姿综合评估学生的学习状态。若学生一段时间内中性面部表情和微笑面部表情出现占比较高且无过多非自然放松状态的动作则其学习状态将会被评估为较好的学习状态;反之则学生学习状态的评价将会较差。当学生出现明显的非自然放松状态的动作时比如环抱双臂、肩颈部部平衡不协调时,学习状态分析报告内将呈现对学生非自然放松状态动作的分析评估和提醒。若在较长的时间内系统没有在获取的图像和视频资源中分析出人脸面部,该学生将被判定为“旷课”。

5. 最终系统实现效果

人脸表情识别效果展示:
在这里插入图片描述
在这里插入图片描述
人体关键点识别效果:
在这里插入图片描述
学生学习状态评估系统效果展示:

实时表情 及 坐姿分析效果
在这里插入图片描述
学习状态评估报告邮件转发效果
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 13
    点赞
  • 211
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
由于openpose官方提供了python API接口,因此我们可以使用Python来实现基于openpose的学生上课不认真听课坐姿识别。 代码如下: ``` import cv2 import numpy as np import math import time import argparse # 加载OpenPose模型 net = cv2.dnn.readNetFromTensorflow("models/graph_opt.pb") # 人体关键点连接的索引 POSE_PAIRS = [ [0, 1], [1, 2], [2, 3], [3, 4], # 身体连接 [1, 5], [5, 6], [6, 7], [1, 8], [8, 9], [9, 10], # 左臂连接 [1, 11], [11, 12], [12, 13], [1, 0], [0, 14], [14, 15], # 右臂连接 [14, 16], [0, 17], [17, 18], [18, 19], [19, 20] # 身体连接 ] # 坐姿的关键点索引 SITTING_POINTS = [8, 9, 10, 11, 12, 13] # 计算两个关键点之间的距离 def get_distance(p1, p2): return math.sqrt((p1[0]-p2[0])**2 + (p1[1]-p2[1])**2) # 判断学生是否坐姿不正确 def is_not_sitting(points): # 计算左右膝盖之间的距离 knee_distance = get_distance(points[9], points[10]) # 计算左右脚踝到左右膝盖之间的距离 left_leg_distance = get_distance(points[9], points[11]) + get_distance(points[11], points[13]) right_leg_distance = get_distance(points[10], points[12]) + get_distance(points[12], points[14]) # 计算左右脚踝到左右膝盖之间的距离与膝盖之间的距离的比值 left_leg_ratio = left_leg_distance / knee_distance right_leg_ratio = right_leg_distance / knee_distance # 如果比值小于1.5,说明坐姿不正确 if left_leg_ratio < 1.5 or right_leg_ratio < 1.5: return True return False # 绘制关键点和连接线 def draw_keypoints(frame, points): for i, point in enumerate(points): x, y = point cv2.circle(frame, (x, y), 3, (0, 0, 255), thickness=-1, lineType=cv2.FILLED) cv2.putText(frame, "{}".format(i), (x, y), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), 1, lineType=cv2.LINE_AA) for pair in POSE_PAIRS: partA = pair[0] partB = pair[1] if points[partA] and points[partB]: cv2.line(frame, points[partA], points[partB], (0, 255, 255), 2, lineType=cv2.LINE_AA) # 识别学生坐姿 def detect_sitting(frame): # 转换帧为blob blob = cv2.dnn.blobFromImage(frame, 1.0 / 255, (368, 368), (0, 0, 0), swapRB=False, crop=False) # 通过网络进行前向传播 net.setInput(blob) output = net.forward() # 提取关键点坐标 points = [] for i in range(len(SITTING_POINTS)): prob_map = output[0, SITTING_POINTS[i], :, :] min_val, prob, min_loc, point = cv2.minMaxLoc(prob_map) x = int(frame.shape[1] * point[0] / output.shape[3]) y = int(frame.shape[0] * point[1] / output.shape[2]) if prob > 0.1: points.append((x, y)) else: points.append(None) # 绘制关键点和连接线 draw_keypoints(frame, points) # 判断学生是否坐姿不正确 if is_not_sitting(points): cv2.putText(frame, "Sitting position is not correct!", (50, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2, lineType=cv2.LINE_AA) # 主函数 if __name__ == "__main__": # 解析命令行参数 parser = argparse.ArgumentParser() parser.add_argument("--input", type=str, default="", help="Path to input video file. If empty, camera's stream will be used") args = parser.parse_args() # 打开视频文件或启动摄像头 cap = cv2.VideoCapture(args.input if args.input else 0) # 逐帧处理视频 while True: # 读取帧 ret, frame = cap.read() if not ret: break # 识别学生坐姿 detect_sitting(frame) # 显示帧 cv2.imshow("Video", frame) # 按下q键退出 if cv2.waitKey(1) & 0xFF == ord('q'): break # 释放资源 cap.release() cv2.destroyAllWindows() ``` 运行代码后,程序会识别视频中学生的坐姿,如果学生坐姿不正确,程序会在视频中显示警告信息。可以通过命令行参数指定输入的视频文件路径,如果不指定,则程序将启动摄像头并获取实时视频流。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值