Keras深度学习——面部特征点检测

1,134 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第11天,点击查看活动详情

0. 前言

在计算机视觉中,面部关键点(也称为面部特征点)的定位通常是许多面部分析方法和算法中的关键步骤。在本节中,我们将训练卷积神经网络来检测面部的关键点,即左右眼,鼻子和嘴巴的四个坐标的边界。以下是两个绘制了面部关键点的示例图片:

面部关键点示例

1. 数据集和模型分析

1.1 数据集分析

对于关键点检测任务,我们所使用的数据集可以从 Github下载,数据集中标注了中图片的人物面部的关键点。在此任务中,输入数据是需要在其上检测关键点的图像,输出数据是图像中人物面部关键点的 xy 坐标。 在构建模型前,首先将数据集下载至本地,查看数据集中标记的面部关键点信息,文件路径为 P1_Facial_Keypoints/data/training_frames_keypoints.csv

数据集

检查此数据集中的面部关键点信息,可以看到,文件中共有 137 列,其中第一列是图像的名称,其余 136 列代表相应图像中 68 个面部关键点的 xy 坐标值。

1.2 模型分析

接下来,继续分析此任务以及我们将使用的模型架构:

  • 根据下载数据集
  • 将图像调整为可以用于网络输入的形状
    • 调整图像大小时,需要确保同时修改关键点,以便它们可以对应于已调整大小后的图像
  • 使用预训练的 VGG16 模型提取输入图像特征
  • 创建面部关键点检测任务的输入和输出数组,其中输入数组是通过 VGG16 模型处理后的图像特征,而输出数组是修改后的面部关键点位置坐标
  • 最后,训练模型以减少面部预测关键点与实际关键点之间的绝对平均误差值

2. 面部特征点检测

本节中,我们将编程实现上一节中所分析的面部关键点检测模型。 导入相关库、数据集,并实例化预训练的 `VGG16模型:

import pandas as pd
import cv2
import numpy as np
from copy import deepcopy
from keras.applications.vgg16 import preprocess_input
from keras.applications import vgg16
from matplotlib import pyplot as plt

data = pd.read_csv('P1_Facial_Keypoints/data/training_frames_keypoints.csv')
vgg16_model = vgg16.VGG16(include_top=False, weights='imagenet',input_shape=(224,224,3))

初始化用于创建输出和输出的列表:

x = []
x_img = []
y = []

循环构建图像文件名,并读取图像:

for i in range(data.shape[0]):
    img_path = 'P1_Facial_Keypoints/data/training/' + data.iloc[i,0]
    img = cv2.imread(img_path)

捕获面部关键点值并进行存储,然后调整图像大小,以满足模型输入形状,并预处理图像,以便可以将其传递给 VGG16 模型并提取特征::

    kp = deepcopy(data.iloc[i,1:].tolist())
    kp_x = (np.array(kp[0::2])/img.shape[1]).tolist()
    kp_y = (np.array(kp[1::2])/img.shape[0]).tolist()
    kp2 = kp_x +kp_y
    img = cv2.resize(img, (224, 224))
    preprocess_img = preprocess_input(img.reshape(1,224,224,3))
    vgg16_img = vgg16_model.predict(preprocess_img)

将输入和输出值追加到相应的列表中:

    x_img.append(img)
    x.append(vgg16_img)
    y.append(kp2)

创建输入和输出数组:

x = np.array(x)
x = x.reshape(x.shape[0], x.shape[2], x.shape[3], x.shape[4])
y = np.array(y)

建立并编译模型

from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout
model_fine_tuning = Sequential()
model_fine_tuning.add(Conv2D(512, kernel_size=(3, 3), activation='relu',input_shape=(x.shape[1],x.shape[2],x.shape[3])))
model_fine_tuning.add(MaxPooling2D(pool_size=(2, 2)))
model_fine_tuning.add(Flatten())
model_fine_tuning.add(Dense(512, activation='relu'))
model_fine_tuning.add(Dropout(0.5))
model_fine_tuning.add(Dense(y.shape[1], activation='sigmoid'))
model_fine_tuning.summary()

输出用于检测面部关键点的微调模型简要信息如下:

Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d (Conv2D)              (None, 5, 5, 512)         2359808   
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 2, 2, 512)         0         
_________________________________________________________________
flatten (Flatten)            (None, 2048)              0         
_________________________________________________________________
dense (Dense)                (None, 512)               1049088   
_________________________________________________________________
dropout (Dropout)            (None, 512)               0         
_________________________________________________________________
dense_1 (Dense)              (None, 136)               69768     
=================================================================
Total params: 3,478,664
Trainable params: 3,478,664
Non-trainable params: 0
_________________________________________________________________

最后,编译并拟合模型:

model_fine_tuning.compile(loss='mean_absolute_error',optimizer='adam')
history = model_fine_tuning.fit(x/np.max(x), y,
                            epochs=10,
                            batch_size=32,
                            verbose=1,
                            validation_split = 0.1)

需要注意的是,我们将输入数组除以输入数组的最大值,以缩放输入数据集,而 validation_split 可选参数用于在没有提供验证集的时候,按一定比例(此处为 10% )从训练集中取出一部分训练数据作为测试集。训练和测试损失随 epoch 增加的变化情况如下:

训练过程监测

3. 模型测试

预测测试图像。在下面的代码中,我们预测输入数组中倒数第 6 和第 7 个图像,由于 validation_split0.1,训练时模型并未见到这 2 张图像,可以用于衡量训练后的模型性能。我们确保将图像通过 preprocess_input 方法进行预处理,然后通过 vgg16_model 提取图像特征,最后将 vgg16_model 的输出传递给构建的 model_fine_tuning

test_img_input = preprocess_input(x_img[-7].reshape(1,224,224,3))
pred = model_fine_tuning.predict(vgg16_model.predict(test_img_input/np.max(test_img_input)))

plt.subplot(221)
plt.title('Original image')
plt.imshow(cv2.cvtColor(x_img[-7], cv2.COLOR_BGR2RGB))
plt.subplot(222)
plt.title('Image with facial keypoints')
plt.imshow(cv2.cvtColor(x_img[-7], cv2.COLOR_BGR2RGB))
kp = pred.flatten()
plt.scatter(kp[0:68]*224, kp[68:]*224)

test_img_input = preprocess_input(x_img[-6].reshape(1,224,224,3))
pred = model_fine_tuning.predict(vgg16_model.predict(test_img_input/np.max(test_img_input)))

plt.subplot(223)
plt.title('Original image')
plt.imshow(cv2.cvtColor(x_img[-6], cv2.COLOR_BGR2RGB))
plt.subplot(224)
plt.title('Image with facial keypoints')
plt.imshow(cv2.cvtColor(x_img[-6], cv2.COLOR_BGR2RGB))
kp = pred.flatten()
plt.scatter(kp[0:68]*224, kp[68:]*224)
plt.show()

先前对测试图像的预测可以如下所示,我们可以看到,在测试图像上可以非常准确地检测到图片中人物的面部关键点。

模型测试

相关链接

Keras深度学习——使用卷积神经网络实现性别分类

Keras深度学习——使用预训练VGG16模型实现性别分类