『深度学习项目四』基于ResNet101人脸特征点检测

Posted 汀、

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了『深度学习项目四』基于ResNet101人脸特征点检测相关的知识,希望对你有一定的参考价值。

相关文章:
【深度学习项目一】全连接神经网络实现mnist数字识别
【深度学习项目二】卷积神经网络LeNet实现minst数字识别
【深度学习项目三】ResNet50多分类任务【十二生肖分类】
『深度学习项目四』基于ResNet101人脸特征点检测
项目链接:https://aistudio.baidu.com/aistudio/projectdetail/1932295

一、人脸检测原理简介

人脸关键点检测,是输入一张人脸图片,模型会返回人脸关键点的一系列坐标,从而定位到人脸的关键信息。

1.1 图像分类和回归的区别

1.2 损失函数

图像分类CrossEntropyLoss :信息熵的计算

loss ⁡ j = −  input  [  class  ] + log ⁡ ( ∑ i = 0 K exp ⁡ (  input  i ) ) , j = 1 , … , K \\operatorname{loss}_{j}=-\\text { input }[\\text { class }]+\\log \\left(\\sum_{i=0}^{K} \\exp \\left(\\text { input }_{i}\\right)\\right), j=1, \\ldots, K lossj= input [ class ]+log(i=0Kexp( input i)),j=1,,K

人脸关键点检测: L1Loss、L2Loss、SmoothL1Loss :距离的计算

Loss_1:
loss ⁡ ( x , y ) = 1 n ∑ i = 1 n ∣ y i − f ( x i ) ∣ \\operatorname{loss}(x, y)=\\frac{1}{n} \\sum_{i=1}^{n}\\left|y_{i}-f\\left(x_{i}\\right)\\right| loss(x,y)=n1i=1nyif(xi)

Loss_2:
loss ⁡ ( x , y ) = 1 n ∑ i = 1 n ( y i − f ( x i ) ) 2 \\operatorname{loss}(x, y)=\\frac{1}{n} \\sum_{i=1}^{n}\\left(y_{i}-f\\left(x_{i}\\right)\\right)^{2} loss(x,y)=n1i=1n(yif(xi))2

Loss_3:分段loss
loss ⁡ ( x , y ) = 1 n ∑ i = 1 n { . 5 ∗ ( y i − f ( x i ) ) 2 ,  if  ∣ y i − f ( x ) ∣ ∣ y i − f ( x i ) ∣ − 0.5 ,  otherwise  \\operatorname{loss}(x, y)=\\frac{1}{n} \\sum_{i=1}^{n}\\left\\{\\begin{array}{ll} .5 *\\left(y_{i}-f\\left(x_{i}\\right)\\right)^{2}, & \\text { if } \\mid y_{i}-f(x)| \\\\ \\left|y_{i}-f\\left(x_{i}\\right)\\right|-0.5, & \\text { otherwise } \\end{array}\\right. loss(x,y)=n1i=1n{.5(yif(xi))2,yif(xi)0.5, if yif(x) otherwise 

有利于快速收敛!

1.3 评估指标 NME

# 环境导入
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

二、数据准备

2.1 下载数据集

本次实验所采用的数据集来源为github的开源项目

加载后可以直接使用下面的命令解压。
unzip是一个常见的解压缩命令:

-l:显示压缩文件内所包含的文件;

-t:检查压缩文件是否正确;

-o:不必先询问用户,unzip执行后覆盖原有的文件;

-n:解压缩时不要覆盖原有的文件;

-q:执行时不显示任何信息;

-d<目录>:指定文件解压缩后所要存储的目录;
解压后的数据集结构为

data/
|—— test
|   |—— Abdel_Aziz_Al-Hakim_00.jpg
    ... ...
|—— test_frames_keypoints.csv
|—— training
|   |—— Abdullah_Gul_10.jpg
    ... ...
|—— training_frames_keypoints.csv

其中,trainingtest 文件夹分别存放训练集和测试集。training_frames_keypoints.csvtest_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标注,标注顺序如下

PaddleX

# 计算标签的均值和标准差,用于标签的归一化
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.2 查看图像

对以下函数的几点解释:
len(key_pts)//2 :因为key_pts里面是一个128的一维数组,本次人脸检测是68个关键点,128个数据里面应该是两个两个一组,分别组成一个关键点的(x,y)坐标。

扩展,图像的坐标分布:
图像的坐标是从左上角开始,一般以水平向右为x轴正方向,竖直向下为y轴正方向。

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='b') # 展示关键点信息
# 展示多条数据

index = [5,10,15,20] # n为数据在表格中的索引 
for n in index:
    image_name = key_pts_frame.iloc[n, 0] # 获取图像名称 
    #key_pts = key_pts_frame.iloc[n, 1:].as_matrix() # 将图像label格式转为numpy.array的格式   会报错'Series' object has no attribute 'as_matrix'改成下面
    key_pts = key_pts_frame.iloc[n, 1:].values   #主要原因是库版本升级,'as_matrix()‘改为了’values’。
    key_pts = key_pts.astype('float').reshape(-1) # 获取图像关键点信息
    print("the image's name is : {}, key_pts is : {}".format(image_name, key_pts.shape)) # 打印图像信息
    plt.figure(figsize=(5, 5)) # 展示的图像大小
    images = show_keypoints(mpimg.imread(os.path.join('data/training/', image_name)), key_pts) # 展示图像与关键点信息
    plt.show(images) # 展示图像

在这里插入图片描述
the image’s name is : Albert_Brooks_12.jpg, key_pts is : (136,)
在这里插入图片描述
the image’s name is : Paul_Otellini_01.jpg, key_pts is : (136,)

2.3 数据集定义

使用飞桨框架高层API的 ``paddle.io.Dataset`` 自定义数据集类,具体可以参考官网文档 [自定义数据集](https://www.paddlepaddle.org.cn/documentation/docs/zh/guides/02_paddle2.0_develop/02_data_load_cn.html#id3)。
import paddle
from paddle.io import Dataset

BATCH_SIZE = 64
BATCH_NUM = 20

IMAGE_SIZE = (28, 28)
CLASS_NUM = 10


class MyDataset(Dataset):
    """
    步骤一:继承paddle.io.Dataset类
    """
    def __init__(self, num_samples):
        """
        步骤二:实现构造函数,定义数据集大小
        """
        super(MyDataset, self).__init__()
        self.num_samples = num_samples

    def __getitem__(self, index):
        """
        步骤三:实现__getitem__方法,定义指定index时如何获取数据,并返回单条数据(训练数据,对应的标签)
        """
        data = paddle.uniform(IMAGE_SIZE, dtype='float32')
        label = paddle.randint(0, CLASS_NUM-1, dtype='int64')

        return data, label

    def __len__(self):
        """
        步骤四:实现__len__方法,返回数据集总数目
        """
        return self.num_samples

# 测试定义的数据集
custom_dataset = MyDataset(BATCH_SIZE * BATCH_NUM)

print('=============custom dataset=============')
for data, label in custom_dataset:
    print(data.shape, label.shape)
    break

# 按照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时如何获取数据,并返回单条数据(训练数据,对应的标签)
        """

        image_name = os.path.join(self.root_dir,
                                self.key_pts_frame.iloc[idx, 0]) #文件名
        # 获取图像
        image = mpimg.imread(image_name)
        
        # 图像格式处理,如果包含 alpha 通道,那么忽略它
        if(image.shape[2] == 4):
            image = image[:,:,0:3]
        
        # 获取关键点信息
        #key_pts = self.key_pts_frame.iloc[idx, 1:].as_matrix()  #第一列到最后一列,转为numpy
        key_pts = self.key_pts_frame.iloc[idx, 1:].values
        key_pts = key_pts.astype('float').reshape(-1) # [136, 1] 136个关键点

        # 如果定义了 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__方法,返回数据集总数目
        """
        return len(self.key_pts_frame) # 返回数据集大小,即图片的数量

2.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)  #图片和label

    

以上是关于『深度学习项目四』基于ResNet101人脸特征点检测的主要内容,如果未能解决你的问题,请参考以下文章

深度学习项目一全连接神经网络实现mnist数字识别

深度学习项目二卷积神经网络LeNet实现minst数字识别

PyTorch深度学习实战 | 基于ResNet的人脸关键点检测

深度学习(十五)基于级联卷积神经网络的人脸特征点定位

深度学习人脸特征点自动定位综述

深度学习RetinaFace人脸检测简要介绍