Day-03 Paddle高层API带你人脸关键点检测
一、问题定义
人脸关键点检测,是输入一张人脸图片,模型会返回人脸关键点的一系列坐标,从而定位到人脸的关键信息。
环境导入
# 环境导入
import os
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import cv2
import paddle
paddle.set_device('gpu') # 设置为GPU
import warnings
warnings.filterwarnings('ignore') # 忽略 warning
二、数据准备
1、下载数据集
本次实验所采用的数据集来源为github的开源项目
目前该数据集已上传到 AI Studio 人脸关键点识别,加载后可以直接使用下面的命令解压。
!unzip data/data69065/data.zip
解压后数据集结构为
data/
|—— test
| |—— Abdel_Aziz_Al-Hakim_00.jpg
... ...
|—— test_frames_keypoints.csv
|—— training
| |—— Abdullah_Gul_10.jpg
... ...
|—— training_frames_keypoints.csv
其中,training
和 test
文件夹分别存放训练集和测试集。training_frames_keypoints.csv
和 test_frames_keypoints.csv
存放着训练集和测试集的标签。接下来,我们先来观察一下 training_frames_keypoints.csv
文件,看一下训练集的标签是如何定义的。
key_pts_frame = pd.read_csv('data/training_frames_keypoints.csv') # 读取数据集
print('Number of images: ', key_pts_frame.shape[0]) # 输出数据集大小
key_pts_frame.head(5) # 看前五条数据
上表中每一行都代表一条数据,其中,第一列是图片的文件名,之后从第0列到第135列,就是该图的关键点信息。因为每个关键点可以用两个坐标表示,所以 136/2 = 68,就可以看出这个数据集为68点人脸关键点数据集。
Tips1: 目前常用的人脸关键点标注,有如下点数的标注
- 5点
- 21点
- 68点
- 98点
Tips2:本次所采用的68标注,标注顺序如下:
笔者自己加了两条语句打印一下第一条数据的信息,更加了解数据存储形式。
print(key_pts_frame.values[0][0]) # 文件名
print(key_pts_frame.values[0, 1:]) # 关键点的数据
# 计算标签的均值和标准差,用于标签的归一化
key_pts_values = key_pts_frame.values[:,1:] # 取出标签信息
data_mean = key_pts_values.mean() # 计算均值
data_std = key_pts_values.std() # 计算标准差
print('标签的均值为:', data_mean)
print('标签的标准差为:', data_std)
标签的均值为: 104.4724870017331
标签的标准差为: 43.17302271754281
2、查看图像
def show_keypoints(image, key_pts):
"""
Args:
image: 图像信息
key_pts: 关键点信息,
展示图片和关键点信息
"""
plt.imshow(image.astype('uint8')) # 展示图片信息
for i in range(len(key_pts)//2,):
plt.scatter(key_pts[i*2], key_pts[i*2+1], s=20, marker='.', c='r') # 展示关键点信息(sctter:散点图)
# 展示单条数据
n = 14 # n为数据在表格中的索引
image_name = key_pts_frame.iloc[n, 0] # 获取图像名称
key_pts = key_pts_frame.iloc[n, 1:].as_matrix() # 将图像label格式转为numpy.array的格式
key_pts = key_pts.astype('float').reshape(-1) # 获取图像关键点信息
print(key_pts.shape)
plt.figure(figsize=(5, 5)) # 展示的图像大小
show_keypoints(mpimg.imread(os.path.join('data/training/', image_name)), key_pts) # 展示图像与关键点信息
plt.show() # 展示图像
3、数据集定义
使用飞桨框架高层API的 paddle.io.Dataset
自定义数据集类,具体可以参考官网文档 自定义数据集。
作业1:自定义 Dataset,完成人脸关键点数据集定义
按照 __init__
中的定义,实现 __getitem__
和 __len__
.
# 按照Dataset的使用规范,构建人脸关键点数据集
from paddle.io import Dataset
class FacialKeypointsDataset(Dataset):
# 人脸关键点数据集
"""
步骤一:继承paddle.io.Dataset类
"""
def __init__(self, csv_file, root_dir, transform=None):
"""
步骤二:实现构造函数,定义数据集大小
Args:
csv_file (string): 带标注的csv文件路径
root_dir (string): 图片存储的文件夹路径
transform (callable, optional): 应用于图像上的数据处理方法
"""
self.key_pts_frame = pd.read_csv(csv_file) # 读取csv文件
self.root_dir = root_dir # 获取图片文件夹路径
self.transform = transform # 获取 transform 方法
def __getitem__(self, idx):
"""
步骤三:实现__getitem__方法,定义指定index时如何获取数据,并返回单条数据(训练数据,对应的标签)
"""
# 实现 __getitem__
# 图像路径
image_dir = os.path.join(self.root_dir, self.key_pts_frame.iloc[idx, 0])
# 读取图片
image = mpimg.imread(image_dir)
# 图像格式处理,忽略alpha
if(image.shape[2] == 4):
image = image[:, :, 0:3]
# 获取关键点信息
key_pts = self.key_pts_frame.iloc[idx, 1:].as_matrix()
key_pts = key_pts.astype('float').reshape(-1)
# 如果定义了 transform 方则使用transform
if self.transform:
image, key_pts = self.transform([image, key_pts])
# 转换为numpy
image = np.array(image, dtype='float32')
key_pts = np.array(key_pts, dtype='float32')
return image, key_pts
def __len__(self):
"""
步骤四:实现__len__方法,返回数据集总数目
"""
# 实现 __len__
return len(self.key_pts_frame) # 返回数据集大小
4、训练集可视化
实例化数据集并显示一些图像。
# 构建一个数据集类
face_dataset = FacialKeypointsDataset(csv_file='data/training_frames_keypoints.csv',
root_dir='data/training/')
# 输出数据集大小
print('数据集大小为: ', len(face_dataset))
# 根据 face_dataset 可视化数据集
num_to_display = 3
for i in range(num_to_display):
# 定义图片大小
fig = plt.figure(figsize=(20,10))
# 随机选择图片
rand_i = np.random.randint(0, len(face_dataset))
sample = face_dataset[rand_i]
# 输出图片大小和关键点的数量
print(i, sample[0].shape, sample[1].shape)
# 设置图片打印信息
ax = plt.subplot(1, num_to_display, i + 1)
ax.set_title('Sample #{}'.format(i))
# 输出图片
show_keypoints(sample[0], sample[1])
数据集大小为: 3462
0 (208, 251, 3) (136,)
1 (328, 276, 3) (136,)
2 (168, 141, 3) (136,)
上述代码虽然完成了数据集的定义,但是还有一些问题,如:
- 每张图像的大小不一样,图像大小需要统一以适配网络输入要求
- 图像格式需要适配模型的格式输入要求
- 数据量比较小,没有进行数据增强
这些问题都会影响模型最终的性能,所以需要对数据进行预处理。
5、Transform
对图像进行预处理,包括灰度化、归一化、重新设置尺寸、随机裁剪,修改通道格式等等,以满足数据要求;每一类的功能如下:
- 灰度化:丢弃颜色信息,保留图像边缘信息;识别算法对于颜色的依赖性不强,加上颜色后鲁棒性会下降,而且灰度化图像维度下降(3->1),保留梯度的同时会加快计算。
- 归一化:加快收敛
- 重新设置尺寸:数据增强
- 随机裁剪:数据增强
- 修改通道格式:改为模型需要的结构
作业2:实现自定义ToCHW
实现数据预处理方法 ToCHW
# 标准化自定义 transform 方法
class