一、用卷积神经网络实现,做笑脸、非笑脸等表情识别
1.1 研究背景
面部表情识别 (Facial Expression Recognition )
在日常工作和生活中,人们情感的表达方式主要有:语言、声音、肢体行为(如手势)、以及面部表情等。在这些行为方式中,面部表情所携带的表达人类内心情感活动的信息最为丰富,据研究表明,人类的面部表情所携带的内心活动的信息在所有的上述的形式中比例最高,大约占比55%。
人类的面部表情变化可以传达出其内心的情绪变化,表情是人类内心世界的真实写照。上世纪70年代,美国著名心理学家保罗•艾克曼经过大量实验之后,将人类的基本表情定义为悲伤、害怕、厌恶、快乐、气愤和惊讶六种。同时,他们根据不同的面部表情类别建立了相应的表情图像数据库。随着研究的深入,中性表情也被研究学者加入基本面部表情中,组成了现今的人脸表情识别研究中的七种基础面部表情。
2.将下载里面的datasets,放到D盘新建的smile中,
1.2面部表情识别框架
面部表情识别通常可以划分为四个进程。包括图像获取,面部检测,图像预处理和表情分类。其中,面部检测,脸部特征提取和面部表情分类是面部表情识别的三个关键环节面部表情识别的基本框架如下图所示。
1.3根据猫狗数据集训练的方法来训练笑脸数据集
1.首先将train_folder文件夹下俩个文件夹内的图片的名字做修改。(修改成猫狗的图片格式
#coding=gbk
import os
import sys
def rename():
path=input("请输入路径(例如D:\\\\picture):")
name=input("请输入开头名:")
startNumber=input("请输入开始数:")
fileType=input("请输入后缀名(如 .jpg、.txt等等):")
print("正在生成以"+name+startNumber+fileType+"迭代的文件名")
count=0
filelist=os.listdir(path)
for files in filelist:
Olddir=os.path.join(path,files)
if os.path.isdir(Olddir):
continue
Newdir=os.path.join(path,name+str(count+int(startNumber))+fileType)
os.rename(Olddir,Newdir)
count+=1
print("一共修改了"+str(count)+"个文件")
rename()
2)图片分类
import os, shutil #复制文件
# 原始目录所在的路径
# 数据集未压缩
original_dataset_dir1 = 'D:\\smile\\datasets\\train_folder\\1' ##笑脸
original_dataset_dir0 = 'D:\\smile\\datasets\\train_folder\\0' ##非笑脸
# 我们将在其中的目录存储较小的数据集
base_dir = 'D:\\smile1'
os.mkdir(base_dir)
# # 训练、验证、测试数据集的目录
train_dir = os.path.join(base_dir, 'train')
os.mkdir(train_dir)
validation_dir = os.path.join(base_dir, 'validation')
os.mkdir(validation_dir)
test_dir = os.path.join(base_dir, 'test')
os.mkdir(test_dir)
# 猫训练图片所在目录
train_cats_dir = os.path.join(train_dir, 'smile')
os.mkdir(train_cats_dir)
# 狗训练图片所在目录
train_dogs_dir = os.path.join(train_dir, 'unsmile')
os.mkdir(train_dogs_dir)
# 猫验证图片所在目录
validation_cats_dir = os.path.join(validation_dir, 'smile')
os.mkdir(validation_cats_dir)
# 狗验证数据集所在目录
validation_dogs_dir = os.path.join(validation_dir, 'unsmile')
os.mkdir(validation_dogs_dir)
# 猫测试数据集所在目录
test_cats_dir = os.path.join(test_dir, 'smile')
os.mkdir(test_cats_dir)
# 狗测试数据集所在目录
test_dogs_dir = os.path.join(test_dir, 'unsmile')
os.mkdir(test_dogs_dir)
# 将前1000张笑脸图像复制到train_cats_dir
fnames = ['smile.{}.jpg'.format(i) for i in range(1000)]
for fname in fnames:
src = os.path.join(original_dataset_dir1, fname)
dst = os.path.join(train_cats_dir, fname)
shutil.copyfile(src, dst)
# 将下500张笑脸图像复制到validation_cats_dir
fnames = ['smile.{}.jpg'.format(i) for i in range(500)]
for fname in fnames:
src = os.path.join(original_dataset_dir1, fname)
dst = os.path.join(validation_cats_dir, fname)
shutil.copyfile(src, dst)
# 将下500张笑脸图像复制到test_cats_dir
fnames = ['smile.{}.jpg'.format(i) for i in range(500)]
for fname in fnames:
src = os.path.join(original_dataset_dir1, fname)
dst = os.path.join(test_cats_dir, fname)
shutil.copyfile(src, dst)
# 将前1000张非笑脸图像复制到train_dogs_dir
fnames = ['unsmile.{}.jpg'.format(i) for i in range(1000)]
for fname in fnames:
src = os.path.join(original_dataset_dir0, fname)
dst = os.path.join(train_dogs_dir, fname)
shutil.copyfile(src, dst)
# 将下500张非笑脸图像复制到validation_dogs_dir
fnames = ['unsmile.{}.jpg'.format(i) for i in range(500)]
for fname in fnames:
src = os.path.join(original_dataset_dir0, fname)
dst = os.path.join(validation_dogs_dir, fname)
shutil.copyfile(src, dst)
# 将下500张非笑脸图像复制到test_dogs_dir
fnames = ['unsmile.{}.jpg'.format(i) for i in range(500)]
for fname in fnames:
src = os.path.join(original_dataset_dir0, fname)
dst = os.path.join(test_dogs_dir, fname)
shutil.copyfile(src, dst)
3)作为健全性检查,让我们计算一下在每个训练分割中我们有多少图片(训练/验证/测试):
print('total training cat images:', len(os.listdir(train_cats_dir)))
print('total training dog images:', len(os.listdir(train_dogs_dir)))
print('total validation cat images:', len(os.listdir(validation_cats_dir)))
print('total validation dog images:', len(os.listdir(validation_dogs_dir)))
print('total test cat images:', len(os.listdir(test_cats_dir)))
print('total test dog images:', len(os.listdir(test_dogs_dir)))
4)卷积网络模型搭建
from keras import layers
from keras import models
model = models.Sequential()
model.add(layers.Conv2D(32, (3, 3), activation='relu',
input_shape=(150, 150, 3)))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(128, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(128, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Flatten())
model.add(layers.Dense(512, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))
model.summary()
5)图像生成器读取文件中数据,进行数据预处理。
from keras import optimizers
model.compile(loss='binary_crossentropy',
optimizer=optimizers.RMSprop(lr=1e-4),
metrics=['acc'])
from keras.preprocessing.image import ImageDataGenerator
# 所有图像将按1/255重新缩放
train_datagen = ImageDataGenerator(rescale=1./255)
test_datagen = ImageDataGenerator(rescale=1./255)
train_generator = train_datagen.flow_from_directory(
# 这是目标目录
train_dir,
# 所有图像将调整为150x150
target_size=(150, 150),
batch_size=20,
# 因为我们使用二元交叉熵损失,我们需要二元标签
class_mode='binary')
validation_generator = test_datagen.flow_from_directory(
validation_dir,
target_size=(150, 150),
batch_size=20,
class_mode='binary')
6)开始训练
history = model.fit_generator(
train_generator,
steps_per_epoch=100,
epochs=30,
validation_data=validation_generator,
validation_steps=50)
7)保存训练模型`
model.save('D:\\smile1\\smiles_and_unsmiles_small_1.h5')
8)在培训和验证数据上绘制模型的损失和准确性(可视化界面)
import matplotlib.pyplot as plt
acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(len(acc))
plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and validation accuracy')
plt.legend()
plt.figure()
plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.legend()
plt.show()
9)使用数据扩充
datagen = ImageDataGenerator(
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')
# 这是带有图像预处理实用程序的模块
from keras.preprocessing import image
fnames = [os.path.join(train_cats_dir, fname) for fname in os.listdir(train_cats_dir)]
# 我们选择一个图像来“增强”
img_path = fnames[3]
# 读取图像并调整其大小
img = image.load_img(img_path, target_size=(150, 150))
# 将其转换为具有形状的Numpy数组(150、150、3)
x = image.img_to_array(img)
# 把它改成(1150150,3)
x = x.reshape((1,) + x.shape)
# 下面的.flow()命令生成一批随机转换的图像。
# 它将无限循环,所以我们需要在某个时刻“打破”循环!
i = 0
for batch in datagen.flow(x, batch_size=1):
plt.figure(i)
imgplot = plt.imshow(image.array_to_img(batch[0]))
i += 1
if i % 4 == 0:
break
plt.show()
10)使用数据扩充和退出来训练我们的网络
train_datagen = ImageDataGenerator(
rescale=1./255,
rotation_range=40,
width_shift_range=0.2,
height_shift_range=0.2,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True,)
# 请注意,不应增加验证数据!
test_datagen = ImageDataGenerator(rescale=1./255)
train_generator = train_datagen.flow_from_directory(
# 这是目标目录
train_dir,
# 所有图像将调整为150x150
target_size=(150, 150),
batch_size=32,
# 因为我们使用二元交叉熵损失,我们需要二元标签
class_mode='binary')
validation_generator = test_datagen.flow_from_directory(
validation_dir,
target_size=(150, 150),
batch_size=32,
class_mode='binary')
history = model.fit_generator(
train_generator,
steps_per_epoch=100,
epochs=100,
validation_data=validation_generator,
validation_steps=50)
11)保存模型
model.save('D:\\smile1\\smiles_and_unsmiles_small_2.h5')
12)在培训和验证数据上绘制模型的损失和准确性(可视化界面)
acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(len(acc))
plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and validation accuracy')
plt.legend()
plt.figure()
plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.legend()
plt.show()
二、完成一个摄像头采集自己人脸、并对表情(笑脸和非笑脸)的实时分类判读(输出分类文字)的程序;
1.基于上面卷积神经网络的笑脸识别
#检测视频或者摄像头中的人脸
import cv2
from keras.preprocessing import image
from keras.models import load_model
import numpy as np
import dlib
from PIL import Image
model = load_model('D:\\smile1\\smiles_and_unsmiles_small_2.h5')
detector = dlib.get_frontal_face_detector()
video=cv2.VideoCapture(0)
font = cv2.FONT_HERSHEY_SIMPLEX
def rec(img):
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
dets=detector(gray,1)
if dets is not None:
for face in dets:
left=face.left()
top=face.top()
right=face.right()
bottom=face.bottom()
cv2.rectangle(img,(left,top),(right,bottom),(0,255,0),2)
img1=cv2.resize(img[top:bottom,left:right],dsize=(150,150))
img1=cv2.cvtColor(img1,cv2.COLOR_BGR2RGB)
img1 = np.array(img1)/255.
img_tensor = img1.reshape(-1,150,150,3)
prediction =model.predict(img_tensor)
if prediction[0][0]>0.5:
result='unsmile'
else:
result='smile'
cv2.putText(img, result, (left,top), font, 2, (0, 255, 0), 2, cv2.LINE_AA)
cv2.imshow('Video', img)
while video.isOpened():
res, img_rd = video.read()
if not res:
break
rec(img_rd)
if cv2.waitKey(5) & 0xFF == ord('q'):
break
video.release()
cv2.destroyAllWindows()