项目简介
通过opencv-python识别出人脸
然后用fer2013的数据集训练深度卷积神经网络构建的模型识别人脸表情
使用训练好的模型识别人脸的表情情绪
根据识别结果,匹配合适的emoji遮住人脸
代码已上传Github | Github地址
数据集介绍
FER2013
训练模型的数据集选用了kaggle挑战赛上的fer2013数据集
下载得到的csv格式可以通过Excel看到格式为:
Emotion | Pixels | Usage |
---|---|---|
0 | 4 0 170 118 101 88 88 75 78 82 66 74 68 59 63 64 65 90 89 73 80 80 85 88 95 117 … 129 | Training |
2 | 200 197 149 139 156 89 111 58 62 95 113 117 116 116 112 111 96 86 99 113 120 1 … 116 | Training |
所以首先打开csv文件,根据usage把数据集分为:训练集、测试集和验证集
1 | with open(csv_file) as f: |
如果直接用当前数据是一个扁平的向量,没有空间局部性。用这样的数据直接进行训练,就会失去空间结构和图像关系信息。卷积神经网络可以保留空间信息,并且更适合图像分类问题,所以要把数据转为图片方便下面采用卷积神经网络进行训练
1 | num = 1 |
顺便把图片灰度化处理(防止黑人和白人的肤色对模型造成影响 O(∩_∩)O哈哈哈)
Emoji表情集
替代人脸的卡通表情采用了Android 9的Emoji
深度卷积神经网络模型
构建模型
这里用到了很多神经网络层
这里图像使用tf(tensorflow)顺序,它在三个通道上的形状为(48,48),正常图片可以表示为(48, 48, 3)。只不过在刚刚生成图片的时候,已经做过灰度化处理,所以这个时候,只有一个通道了。
卷积阶段
使用keras添加一层二维滤波器,输出维度是32并且每个二维滤波器是1 * 1的卷积层
1 | self.model.add(Conv2D(32, (1, 1), strides=1, padding='same', input_shape=(img_size, img_size, 1))) |
padding=’same’表示保留边界处的卷积计算结果。总共只有两种设置,这种表示输出和输入的大小相同,输入的区域边界填充为0;padding=’valid’表示只对输入和滤波器完全叠加的部分做卷积运算,因而输出将会少于输入。不过讲道理,这里strides这个处理步幅已经是1了,不管设置什么都不会超过边界
使用ReLU激活函数
1 | self.model.add(Activation('relu')) |
然后给网络学习32个5 * 5的滤波器,也用ReLU激活。并且紧接着一个最大池化层方法
1 | self.model.add(Conv2D(32, (5, 5), padding='same')) |
之后第二层卷积阶段和第三层卷积阶段都是用ReLU激活函数,后面再次跟着最大池化层方法。第二层仍然是32个3 * 3大小的滤波器,第三层滤波器增加到64个5 * 5,在更深的网络层增加滤波器数目是深度学习中一个普遍采用的技术
1 | self.model.add(Conv2D(32, (3, 3), padding='same')) |
深度管道的下一个阶段
首先用Flatten()获得一个扁平的网络
1 | self.model.add(Flatten()) |
用ReLU激活一个有2048个神经元的隐藏层,用Dropout丢弃到一半的网络,再添加一个1024个神经元的隐藏层,跟着一个关闭50%神经元的dropout层
1 | self.model.add(Activation('relu')) |
输出层
添加作为输出7个类的softmax层,每个类对应一个类别
1 | self.model.add(Dense(num_classes)) |
MODEL_SUMMARY
1 | _________________________________________________________________ |
训练模型
编译模型
这里选择随机梯度下降算法作为优化器
1 | sgd = SGD(lr=0.01, decay=1e-6, momentum=0.9, nesterov=True) |
通过数据增加改善性能
通常提高性能有两种方法,一种是定义一个更深、有更多卷积操作的网络,另一种训练更多的图片。这里用keras自带的ImageDataGenerator方法扩展数据集
1 | # 自动扩充训练样本 |
考虑到效率问题,keras提供了生成器针对模型的并发运行。我的理解就是CPU处理生成图像,GPU上并行进行训练
1 | # 归一化验证集 |
保存模型结构及权重
把结构保存为JSON字串,把权重保存到HDF5文件
1 | model_json = self.model.to_json() |
识别模块
加载权重及模型结构
1 | # 从json中加载模型 |
使用OPENCV-PYTHON识别人脸
用opencv打开摄像头,使用opencv提供的一个训练好的模型识别人脸人类器
1 | # 创建VideoCapture对象 |
识别人脸情绪
根据识别出的脸部特征点,裁剪出脸部图像,然后调用模型预测情绪
1 | if len(faceLands) > 0: |
根据识别结果,用cv的rectangle在视频流上框出脸部并且用putText打上标签
1 | # 框出脸部并且写上标签 |
用EMOJI盖住人脸
先在第一次获取视频画面的时候就copy一个没有灰度化处理的视频画面
1 | # 呈现用emoji替代后的画面 |
直接把emoji图片遮盖人脸会出现emoji背景变为黑色盖上去了。所以这里要蒙版处理一下,也就是保持emoji透明背景的特性,当然,这里所有图像都要归一化处理
1 | def face2emoji(face, emotion_index, position): |