欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

juypter 基于keras训练的笑脸检测+口罩人脸识别

程序员文章站 2022-07-13 10:17:52
...

一、笑脸数据集(genki4k)正负样本的划分、模型训练和测试的过程(至少包括SVM、CNN),输出模型训练精度和测试精度(F1-score和ROC)

1.训练数据

1) 导入Keras

import keras
keras.__version__
'2.3.1'

2) 读取数据集,训练

import os, shutil
# The path to the directory where the original
# dataset was uncompressed
original_dataset_dir = 'smileface/genki4k'

# The directory where we will
# store our smaller dataset
base_dir = 'smileface/1'
os.mkdir(base_dir)

# Directories for our training,
# validation and test splits
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)

# Directory with our training smile pictures
train_smile_dir = os.path.join(train_dir, 'smiles')
os.mkdir(train_smile_dir)

# Directory with our training nosmile pictures
train_nosmile_dir = os.path.join(train_dir, 'no_smiles')
os.mkdir(train_nosmile_dir)

# Directory with our validation smile pictures
validation_smile_dir = os.path.join(validation_dir, 'smiles')
os.mkdir(validation_smile_dir)

# Directory with our validation nosmile pictures
validation_nosmile_dir = os.path.join(validation_dir, 'no_smiles')
os.mkdir(validation_nosmile_dir)

# Directory with our validation smile pictures
test_smile_dir = os.path.join(test_dir, 'smiles')
os.mkdir(test_smile_dir)

# Directory with our validation nosmile pictures
test_nosmile_dir = os.path.join(test_dir, 'no_smiles')
os.mkdir(test_nosmile_dir)

3)将笑脸数据存入对应文件夹

juypter 基于keras训练的笑脸检测+口罩人脸识别

print('total training smile images:', len(os.listdir(train_smile_dir)))
print('total training nosmile images:', len(os.listdir(train_nosmile_dir)))
print('total validation smile images:', len(os.listdir(validation_smile_dir)))
print('total validation nosmile images:', len(os.listdir(validation_nosmile_dir)))
print('total test smile images:', len(os.listdir(test_smile_dir)))
print('total test nosmile images:', len(os.listdir(test_nosmile_dir)))
total training smile images: 2162
total training nosmile images: 1837
total validation smile images: 2162
total validation nosmile images: 1837
total test smile images: 2162
total test nosmile images: 1837

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()
Model: "sequential_2"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_5 (Conv2D)            (None, 148, 148, 32)      896       
_________________________________________________________________
max_pooling2d_5 (MaxPooling2 (None, 74, 74, 32)        0         
_________________________________________________________________
conv2d_6 (Conv2D)            (None, 72, 72, 64)        18496     
_________________________________________________________________
max_pooling2d_6 (MaxPooling2 (None, 36, 36, 64)        0         
_________________________________________________________________
conv2d_7 (Conv2D)            (None, 34, 34, 128)       73856     
_________________________________________________________________
max_pooling2d_7 (MaxPooling2 (None, 17, 17, 128)       0         
_________________________________________________________________
conv2d_8 (Conv2D)            (None, 15, 15, 128)       147584    
_________________________________________________________________
max_pooling2d_8 (MaxPooling2 (None, 7, 7, 128)         0         
_________________________________________________________________
flatten_2 (Flatten)          (None, 6272)              0         
_________________________________________________________________
dense_3 (Dense)              (None, 512)               3211776   
_________________________________________________________________
dense_4 (Dense)              (None, 1)                 513       
=================================================================
Total params: 3,453,121
Trainable params: 3,453,121
Non-trainable params: 0
_________________________________________________________________
from keras import optimizers
model.compile(loss='binary_crossentropy',
              optimizer=optimizers.RMSprop(lr=1e-4),
              metrics=['acc'])

5) 数据预处理

from keras.preprocessing.image import ImageDataGenerator
# All images will be rescaled by 1./255
train_datagen = ImageDataGenerator(rescale=1./255)
test_datagen = ImageDataGenerator(rescale=1./255)

train_generator = train_datagen.flow_from_directory(
        # This is the target directory
        train_dir,
        # All images will be resized to 150x150
        target_size=(150, 150),
        batch_size=20,
        # Since we use binary_crossentropy loss, we need binary labels
        class_mode='binary')

validation_generator = test_datagen.flow_from_directory(
        validation_dir,
        target_size=(150, 150),
        batch_size=20,
        class_mode='binary')
Found 3999 images belonging to 2 classes.
Found 3999 images belonging to 2 classes.

生成器的输出

for data_batch, labels_batch in train_generator:
    print('data batch shape:', data_batch.shape)
    print('labels batch shape:', labels_batch.shape)
    break
data batch shape: (20, 150, 150, 3)
labels batch shape: (20,)

6) 训练模型

input_shape=(784),
history = model.fit_generator(
    train_generator,
    steps_per_epoch=50,
    epochs=15,
    validation_data=validation_generator,
    validation_steps=50)
WARNING:tensorflow:From C:\Users\xieru\Anaconda3\lib\site-packages\keras\backend\tensorflow_backend.py:422: The name tf.global_variables is deprecated. Please use tf.compat.v1.global_variables instead.

Epoch 1/15
50/50 [==============================] - 28s 560ms/step - loss: 0.6920 - acc: 0.5510 - val_loss: 0.7255 - val_acc: 0.5630
Epoch 2/15
50/50 [==============================] - 27s 547ms/step - loss: 0.6781 - acc: 0.5760 - val_loss: 0.6440 - val_acc: 0.5820
Epoch 3/15
50/50 [==============================] - 27s 545ms/step - loss: 0.6776 - acc: 0.5820 - val_loss: 0.7515 - val_acc: 0.5050
Epoch 4/15
50/50 [==============================] - 27s 539ms/step - loss: 0.6846 - acc: 0.5536 - val_loss: 0.6532 - val_acc: 0.6196
Epoch 5/15
50/50 [==============================] - 28s 552ms/step - loss: 0.6688 - acc: 0.6016 - val_loss: 0.6846 - val_acc: 0.6400
Epoch 6/15
50/50 [==============================] - 28s 555ms/step - loss: 0.6520 - acc: 0.6280 - val_loss: 0.6407 - val_acc: 0.6370
Epoch 7/15
50/50 [==============================] - 28s 557ms/step - loss: 0.6586 - acc: 0.6190 - val_loss: 0.6581 - val_acc: 0.6010
Epoch 8/15
50/50 [==============================] - 27s 547ms/step - loss: 0.6496 - acc: 0.6250 - val_loss: 0.6380 - val_acc: 0.6847
Epoch 9/15
50/50 [==============================] - 28s 562ms/step - loss: 0.6359 - acc: 0.6350 - val_loss: 0.6808 - val_acc: 0.5910
Epoch 10/15
50/50 [==============================] - 29s 571ms/step - loss: 0.6255 - acc: 0.6590 - val_loss: 0.7137 - val_acc: 0.6760
Epoch 11/15
50/50 [==============================] - 29s 581ms/step - loss: 0.6171 - acc: 0.6680 - val_loss: 0.6744 - val_acc: 0.7000
Epoch 12/15
50/50 [==============================] - 28s 568ms/step - loss: 0.6147 - acc: 0.6677 - val_loss: 0.5016 - val_acc: 0.6897
Epoch 13/15
50/50 [==============================] - 28s 562ms/step - loss: 0.5846 - acc: 0.7090 - val_loss: 0.4679 - val_acc: 0.6950
Epoch 14/15
50/50 [==============================] - 28s 557ms/step - loss: 0.5835 - acc: 0.7190 - val_loss: 0.5299 - val_acc: 0.7010
Epoch 15/15
50/50 [==============================] - 30s 609ms/step - loss: 0.5677 - acc: 0.7180 - val_loss: 0.6687 - val_acc: 0.6950

这里使用fit_generator方法来完成此操作,对于我们这样的数据生成器,它相当于fit方法。由于电脑性能原因,我训练的轮数较短。

保存模型

model.save('smile_and_nosmile.h5')

保存好模型之后便可以进行此模型的人脸识别了,不过精度不会太高,毕竟只使用了这么少量的图片

绘制模型的损失和准确性

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()

juypter 基于keras训练的笑脸检测+口罩人脸识别
juypter 基于keras训练的笑脸检测+口罩人脸识别

8) 数据增强

数据增强采用的方法是从现有的训练样本中生成更多的训练数据,方法是通过一系列随机变换来“增强”样本,从而产生看上去可信的图像。我们的目标是在训练时,我们的模型不会两次看到完全相同的图像。这有助于将模型暴露于数据的更多方面,并更好地泛化。

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')
# This is module with image preprocessing utilities
from keras.preprocessing import image

fnames = [os.path.join(train_smile_dir, fname) for fname in os.listdir(train_smile_dir)]

# We pick one image to "augment"
img_path = fnames[3]

# Read the image and resize it
img = image.load_img(img_path, target_size=(150, 150))

# Convert it to a Numpy array with shape (150, 150, 3)
x = image.img_to_array(img)

# Reshape it to (1, 150, 150, 3)
x = x.reshape((1,) + x.shape)

# The .flow() command below generates batches of randomly transformed images.
# It will loop indefinitely, so we need to `break` the loop at some point!
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()

juypter 基于keras训练的笑脸检测+口罩人脸识别
juypter 基于keras训练的笑脸检测+口罩人脸识别
juypter 基于keras训练的笑脸检测+口罩人脸识别
juypter 基于keras训练的笑脸检测+口罩人脸识别

如果我们使用这种数据增加配置训练一个新的网络,我们的网络将永远不会看到两次相同的输入。然而,它看到的输入仍然是高度相关的,因为它们来自少量的原始图像——我们不能产生新的信息,我们只能混合现有的信息。因此,这可能还不足以完全消除过度拟合。

为了进一步对抗过拟合,我们还将在我们的模型中增加一个Dropout层,就在密集连接分类器之前:

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.Dropout(0.5))
model.add(layers.Dense(512, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))

model.compile(loss='binary_crossentropy',
              optimizer=optimizers.RMSprop(lr=1e-4),
              metrics=['acc'])

用数据增强和退出训练网络

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,)

# Note that the validation data should not be augmented!
test_datagen = ImageDataGenerator(rescale=1./255)

train_generator = train_datagen.flow_from_directory(
        # This is the target directory
        train_dir,
        # All images will be resized to 150x150
        target_size=(150, 150),
        batch_size=32,
        # Since we use binary_crossentropy loss, we need binary labels
        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=30,
      epochs=30,
      validation_data=validation_generator,
      validation_steps=25)
Found 3999 images belonging to 2 classes.
Found 3999 images belonging to 2 classes.
Epoch 1/30
30/30 [==============================] - 26s 853ms/step - loss: 0.6824 - acc: 0.5656 - val_loss: 0.6342 - val_acc: 0.5725
Epoch 2/30
30/30 [==============================] - 25s 836ms/step - loss: 0.6790 - acc: 0.5552 - val_loss: 0.6786 - val_acc: 0.5725
Epoch 3/30
30/30 [==============================] - 25s 839ms/step - loss: 0.6871 - acc: 0.5558 - val_loss: 0.6456 - val_acc: 0.5975
Epoch 4/30
30/30 [==============================] - 25s 847ms/step - loss: 0.6800 - acc: 0.5740 - val_loss: 0.6984 - val_acc: 0.5788
Epoch 5/30
30/30 [==============================] - 25s 838ms/step - loss: 0.6841 - acc: 0.5506 - val_loss: 0.6941 - val_acc: 0.5695
Epoch 6/30
30/30 [==============================] - 26s 858ms/step - loss: 0.6797 - acc: 0.5625 - val_loss: 0.6757 - val_acc: 0.5875
Epoch 7/30
30/30 [==============================] - 26s 856ms/step - loss: 0.6849 - acc: 0.5635 - val_loss: 0.6757 - val_acc: 0.5888
Epoch 8/30
30/30 [==============================] - 26s 859ms/step - loss: 0.6797 - acc: 0.5844 - val_loss: 0.6609 - val_acc: 0.5600
Epoch 9/30
30/30 [==============================] - 26s 856ms/step - loss: 0.6791 - acc: 0.5625 - val_loss: 0.6076 - val_acc: 0.6075
Epoch 10/30
30/30 [==============================] - 25s 828ms/step - loss: 0.6730 - acc: 0.5719 - val_loss: 0.6391 - val_acc: 0.5682
Epoch 11/30
30/30 [==============================] - 25s 849ms/step - loss: 0.6791 - acc: 0.5740 - val_loss: 0.7491 - val_acc: 0.5150
Epoch 12/30
30/30 [==============================] - 25s 829ms/step - loss: 0.6762 - acc: 0.5892 - val_loss: 0.6416 - val_acc: 0.5875
Epoch 13/30
30/30 [==============================] - 25s 833ms/step - loss: 0.6809 - acc: 0.5813 - val_loss: 0.6868 - val_acc: 0.5962
Epoch 14/30
30/30 [==============================] - 25s 836ms/step - loss: 0.6680 - acc: 0.5854 - val_loss: 0.6515 - val_acc: 0.6100
Epoch 15/30
30/30 [==============================] - 26s 851ms/step - loss: 0.6757 - acc: 0.5938 - val_loss: 0.6267 - val_acc: 0.6245
Epoch 16/30
30/30 [==============================] - 25s 846ms/step - loss: 0.6706 - acc: 0.5892 - val_loss: 0.6812 - val_acc: 0.5825
Epoch 17/30
30/30 [==============================] - 25s 830ms/step - loss: 0.6764 - acc: 0.5823 - val_loss: 0.6789 - val_acc: 0.6237
Epoch 18/30
30/30 [==============================] - 25s 841ms/step - loss: 0.6729 - acc: 0.5798 - val_loss: 0.7280 - val_acc: 0.6025
Epoch 19/30
30/30 [==============================] - 25s 832ms/step - loss: 0.6756 - acc: 0.5938 - val_loss: 0.6452 - val_acc: 0.6125
Epoch 20/30
30/30 [==============================] - 25s 835ms/step - loss: 0.6722 - acc: 0.5917 - val_loss: 0.5814 - val_acc: 0.6058
Epoch 21/30
30/30 [==============================] - 25s 841ms/step - loss: 0.6654 - acc: 0.6156 - val_loss: 0.6956 - val_acc: 0.5900
Epoch 22/30
30/30 [==============================] - 25s 830ms/step - loss: 0.6787 - acc: 0.5704 - val_loss: 0.6486 - val_acc: 0.6025
Epoch 23/30
30/30 [==============================] - 25s 842ms/step - loss: 0.6671 - acc: 0.6062 - val_loss: 0.6033 - val_acc: 0.6212
Epoch 24/30
30/30 [==============================] - 25s 827ms/step - loss: 0.6582 - acc: 0.6083 - val_loss: 0.7576 - val_acc: 0.5975
Epoch 25/30
30/30 [==============================] - 25s 835ms/step - loss: 0.6678 - acc: 0.5844 - val_loss: 0.7062 - val_acc: 0.6045
Epoch 26/30
30/30 [==============================] - 25s 835ms/step - loss: 0.6756 - acc: 0.5860 - val_loss: 0.5878 - val_acc: 0.6162
Epoch 27/30
30/30 [==============================] - 25s 841ms/step - loss: 0.6727 - acc: 0.5719 - val_loss: 0.6711 - val_acc: 0.6187
Epoch 28/30
30/30 [==============================] - 25s 844ms/step - loss: 0.6657 - acc: 0.5969 - val_loss: 0.6661 - val_acc: 0.5950
Epoch 29/30
30/30 [==============================] - 25s 828ms/step - loss: 0.6669 - acc: 0.6031 - val_loss: 0.6573 - val_acc: 0.6250
Epoch 30/30
30/30 [==============================] - 25s 841ms/step - loss: 0.6563 - acc: 0.6246 - val_loss: 0.7193 - val_acc: 0.5695
model.save('smile_and_nosmile_1.h5')

再查看模型的损失和准确性

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()

juypter 基于keras训练的笑脸检测+口罩人脸识别
juypter 基于keras训练的笑脸检测+口罩人脸识别

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('smile_and_nosmile_1.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)    
            print(prediction)
            if prediction[0][0]<0.5:
                result='no_smiles'
            else:
                result='smiles'
            cv2.putText(img, result, (left,top), font, 2, (0, 255, 0), 2, cv2.LINE_AA)
        cv2.imshow('smile detector', img)
while video.isOpened():
    res, img_rd = video.read()
    if not res:
        break
    rec(img_rd)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break
video.release()
cv2.destroyAllWindows()
[[0.373922]]
[[0.37300688]]
[[0.36894965]]
[[0.37052]]
[[0.36977816]]
[[0.36832398]]
[[0.36462715]]
[[0.37091285]]
[[0.37168673]]
[[0.3695552]]
[[0.37056762]]
[[0.3709046]]
[[0.3715356]]
[[0.3716728]]
[[0.37013638]]
[[0.3702656]]
[[0.37569472]]
[[0.3751066]]
[[0.37662053]]
[[0.37343165]]
[[0.3769425]]
[[0.3725439]]
[[0.369594]]
[[0.37417337]]
[[0.3706985]]
[[0.37367338]]
[[0.37272263]]
[[0.37169605]]
[[0.37007022]]
[[0.37382162]]
[[0.36791837]]
[[0.37170732]]
[[0.37154534]]
[[0.37069517]]
[[0.3701343]]

在这里插入图片描述
juypter 基于keras训练的笑脸检测+口罩人脸识别

二、将数据集换为口罩数据集,并进行训练,实现佩戴口罩的人脸识别

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('mask_and_nomask.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)    
            print(prediction)
            if prediction[0][0]>0.5:
                result='nomask'
            else:
                result='mask'
            cv2.putText(img, result, (left,top), font, 2, (0, 255, 0), 2, cv2.LINE_AA)
        cv2.imshow('mask detector', img)
while video.isOpened():
    res, img_rd = video.read()
    if not res:
        break
    rec(img_rd)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break
video.release()
cv2.destroyAllWindows()

juypter 基于keras训练的笑脸检测+口罩人脸识别
juypter 基于keras训练的笑脸检测+口罩人脸识别
参考链接: Python — 笑脸检测+口罩人脸识别.