PyTorch深度学习实战(16)——面部关键点检测

news2024/11/23 2:55:08

PyTorch深度学习实战(16)——面部关键点检测

    • 0. 前言
    • 1. 关键点检测
      • 1.1 关键点检测模型分析
      • 1.2 数据集分析
    • 2. 面部关键点检测
    • 3. 2D 和 3D 面部关键点检测
    • 小结
    • 系列链接

0. 前言

我们已经学习了如何解决二分类(猫狗分类)和多分类( fashionMNIST )问题。本节中,我们将学习如何解决回归问题,研究多个自变量对多个因变量的影响。假如我们需要预测面部图像上的关键点,例如眼睛、鼻子和下巴的位置,就需要采用新的策略构建模型来检测面部关键点。在本文中,我们将基于预训练 VGG16 架构提取图像特征,然后微调模型检测图像中人物面部关键点。

1. 关键点检测

1.1 关键点检测模型分析

面部关键点检测( Facial Landmark Detection )旨在自动识别并捕捉面部照片或视频中的关键点位置,例如眼睛、鼻子、嘴巴、眉毛等。通常使用深度学习算法通过对丰富的面部数据进行训练,自动提取面部特征,识别面部关键点位置,并将其标记在面部图片或视频的相应位置上。面部关键点检测可以使计算机更好地理解和学习面部图像和视频中的信息,提取面部特征,为人脸识别、表情识别和面部特征分析等应用提供基础数据。

关键点示例
如上图所示,面部关键点表示包含人脸的图像上的各个关键点的标记。要检测面部关键点,我们首先要解决以下问题:

  • 图像具有不同的尺寸
    • 调整图像尺寸以使它们具有标准图像尺寸
  • 面部关键点类似于散点图上的点,但其基于某种模式散布
    • 将图像尺寸调整为 224 x 224 x 3,像素介于 0224 之间
  • 根据图像尺寸对因变量(面部关键点的位置)进行归一化
    • 考虑它们相对于图像尺寸的位置,则关键点值介于 01 之间
    • 由于因变量值始终介于 01 之间,因此可以在最后使用 sigmoid 函数来得到介于 01 之间的值
  • 定义用于加载数据集的数据管道:
    • 定义准备数据集的类,对输入图像进行预处理以执行迁移学习,并获取关键点相对于处理后的图像的相对位置
  • 定义模型、损失函数和优化器
    • 使用平均绝对误差作为损失函数,因为输出是介于 01 之间的连续值

1.2 数据集分析

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

数据集
检查此数据集中的面部关键点信息,可以看到,文件中共有 137 列,其中第 1 列是图像的名称,其余 136 列代表相应图像中 68 个面部关键点的 xy 坐标值,偶数列表示面部 68 个关键点中每个关键点对应的 x 轴坐标,奇数列(第 1 列除外)表示面部 68 个关键点中每个关键点对应的 y 轴坐标。

2. 面部关键点检测

接下来,使用 PyTorch 实现面部关键点检测模型。

(1) 导入相关库:

import torch.nn as nn
import torch
from torchvision import transforms, models
import numpy as np, pandas as pd, os, glob
import matplotlib.pyplot as plt
import glob
device = 'cuda' if torch.cuda.is_available() else 'cpu'

(2) 下载并导入相关数据,下载包含图像及其对应的面部关键点的数据集:

root_dir = 'P1_Facial_Keypoints/data/training/'
all_img_paths = glob.glob(os.path.join(root_dir, '*.jpg'))
data = pd.read_csv('P1_Facial_Keypoints/data/training_frames_keypoints.csv')

(3) 定义为数据加载器提供输入和输出数据样本的 FacesData 类:

class FacesData(Dataset):

定义 __init__ 方法,以二维数据表格( df )作为输入:

    def __init__(self, df):
        super(FacesData).__init__()
        self.df = df

定义用于预处理图像的均值和标准差,供预训练 VGG16 模型使用:

        self.normalize = transforms.Normalize(mean=[0.485, 0.456, 0.406],
                                              std=[0.229, 0.224, 0.225])

定义 __len__ 方法:

    def __len__(self):
        return len(self.df)

定义 __getitem__ 方法,获取与给定索引对应的图像,对其进行缩放,获取与给定索引对应的关键点值,对关键点进行归一化,以便我们获取关键点的相对位置,并对图像进行预处理。

定义 __getitem__ 方法并获取与给定索引 (ix) 对应的图像路径:

    def __getitem__(self, ix):
        img_path = 'P1_Facial_Keypoints/data/training/' + self.df.iloc[ix,0]

缩放图像:

        img = cv2.imread(img_path)/255.

将目标输出值(关键点位置)根据原始图像尺寸的比例进行归一化:

        kp = deepcopy(self.df.iloc[ix,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 )和图像( img ):

        kp2 = kp_x + kp_y
        kp2 = torch.tensor(kp2) 
        img = self.preprocess_input(img)
        return img, kp2

定义预处理图像函数( preprocess_input ):

    def preprocess_input(self, img):
        img = cv2.resize(img, (224,224))
        img = torch.tensor(img).permute(2,0,1)
        img = self.normalize(img).float()
        return img.to(device)

定义函数加载图像,用于可视化测试图像和测试图像的预测关键点:

    def load_img(self, ix):
        img_path = 'P1_Facial_Keypoints/data/training/' + self.df.iloc[ix,0]        
        img = cv2.imread(img_path)
        img =cv2.cvtColor(img, cv2.COLOR_BGR2RGB)/255.
        img = cv2.resize(img, (224,224))
        return img

(4) 拆分训练和测试数据集,并构建训练和测试数据集和数据加载器:

from sklearn.model_selection import train_test_split

train, test = train_test_split(data, test_size=0.2, random_state=101)
train_dataset = FacesData(train.reset_index(drop=True))
test_dataset = FacesData(test.reset_index(drop=True))

train_loader = DataLoader(train_dataset, batch_size=32)
test_loader = DataLoader(test_dataset, batch_size=32)

(5) 定义用于识别图像中关键点的模型。

加载预训练的 VGG16 模型:

def get_model():
    model = models.vgg16(pretrained=True)

冻结预训练模型的参数:

    for param in model.parameters():
        param.requires_grad = False

重建模型最后两层并训练参数:

    model.avgpool = nn.Sequential(nn.Conv2d(512,512,3),
                nn.MaxPool2d(2),
                nn.Flatten())
    model.classifier = nn.Sequential(
                nn.Linear(2048, 512),
                nn.ReLU(),
                nn.Dropout(0.5),
                nn.Linear(512, 136),
                nn.Sigmoid())

分类器模块中最后一层使用 sigmoid 函数,它返回介于 01 之间的值,因为关键点位置是相对于原始图像尺寸的相对位置,因此预期输出将始终介于 01 之间。

定义损失函数(使用平均绝对误差)和优化器:

    criterion = nn.L1Loss()
    optimizer = torch.optim.Adam(model.parameters(), lr=1e-4)
    return model.to(device), criterion, optimizer

(6) 初始化模型,损失函数,以及对应的优化器:

model, criterion, optimizer = get_model()

(7) 定义函数在训练数据集上进行训练并在测试数据集上进行验证。

train_batch() 函数根据输入计算模型输出、损失值并执行反向传播以更新权重:

def train_batch(img, kps, model, optimizer, criterion):
    model.train()
    optimizer.zero_grad()
    _kps = model(img.to(device))
    loss = criterion(_kps, kps.to(device))
    loss.backward()
    optimizer.step()
    return loss

构建函数返回测试数据的损失和预测的关键点:

@torch.no_grad()
def validate_batch(img, kps, model, criterion):
    model.eval()
    _kps = model(img.to(device))
    loss = criterion(_kps, kps.to(device))
    return _kps, loss

(8) 训练模型,并在测试数据上对其进行测试:

train_loss, test_loss = [], []
n_epochs = 50

for epoch in range(n_epochs):
    print(f" epoch {epoch+ 1}/50")
    epoch_train_loss, epoch_test_loss = 0, 0
    for ix, (img,kps) in enumerate(train_loader):
        loss = train_batch(img, kps, model, optimizer, criterion)
        epoch_train_loss += loss.item() 
    epoch_train_loss /= (ix+1)

    for ix,(img,kps) in enumerate(test_loader):
        ps, loss = validate_batch(img, kps, model, criterion)
        epoch_test_loss += loss.item() 
    epoch_test_loss /= (ix+1)

    train_loss.append(epoch_train_loss)
    test_loss.append(epoch_test_loss)

(9) 绘制模型训练过程中训练和测试损失:

epochs = np.arange(50)+1
import matplotlib.pyplot as plt
plt.plot(epochs, train_loss, 'bo', label='Training loss')
plt.plot(epochs, test_loss, 'r', label='Test loss')
plt.title('Training and Test loss over increasing epochs')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()
plt.grid('off')
plt.show()

训练和测试损失
(10) 使用随机测试图像的测试模型,利用 FacesData 类中的 load_img 方法:

ix = 20
plt.figure(figsize=(10,10))
plt.subplot(121)
plt.title('Original image')
im = test_dataset.load_img(ix)
plt.imshow(im)
plt.grid(False)
plt.subplot(122)
plt.title('Image with facial keypoints')
x, _ = test_dataset[ix]
plt.imshow(im)
kp = model(x[None]).flatten().detach().cpu()
plt.scatter(kp[:68]*224, kp[68:]*224, c='r')
plt.grid(False)
plt.show()

测试模型

从以上图像中可以看出,给定输入图像,模型能够相当准确地识别面部关键点。

3. 2D 和 3D 面部关键点检测

在上一小节中,我们从零开始构建了面部关键点检测器模型。在本节中,我们将学习如何利用专门为 2D3D 关键点检测而构建的预训练模型来获取面部的 2D3D 关键点。为了完成此任务,我们将使用 face-alignment 库。

(1) 使用 pip 安装 face-alignment 库:

pip install face-alignment

(2) 加载所需库和图片:

import face_alignment
import cv2

file = '4.jpeg'
im = cv2.imread(file)
input = cv2.cvtColor(im, cv2.COLOR_BGR2RGB)

(3) 定义人脸对齐方法,指定是要获取 2D 还是 3D 关键点坐标:

fa = face_alignment.FaceAlignment(face_alignment.LandmarksType._2D, flip_input=False, device='cpu')

(4) 读取输入图像并将其作为 get_landmarks 方法的输入:

preds = fa.get_landmarks(input)[0]
print(preds.shape)
# (68, 2)

在以上代码,利用 FaceAlignment 类的对象 fa 中的 get_landmarks 方法来获取与面部关键点对应的 68xy 坐标。

(5) 用检测到的关键点绘制图像:

import matplotlib.pyplot as plt
fig,ax = plt.subplots(figsize=(5,5))
plt.imshow(input)
ax.scatter(preds[:,0], preds[:,1], marker='+', c='r')
plt.show()

2D关键点
(6) 获得面部关键点的 3D 投影:

fa = face_alignment.FaceAlignment(face_alignment.LandmarksType._3D, flip_input=False, device='cpu')
im = cv2.imread(file)
input = cv2.cvtColor(im, cv2.COLOR_BGR2RGB)
preds = fa.get_landmarks(input)[0]
import pandas as pd
df = pd.DataFrame(preds)
df.columns = ['x','y','z']
import plotly.express as px
fig = px.scatter_3d(df, x = 'x', y = 'y', z = 'z')
fig.show()

2D 关键点检测中使用的代码的唯一变化是将 LandmarksType 指定为 3D 而不是 2D,输出结果如下所示:

3D面部关键点
通过利用 face_alignment 库,可以看到利用预训练的面部关键点检测模型在预测新图像时具有较高精度。

小结

面部关键点的定位通常是许多面部分析方法和算法中的关键步骤。在本节中,我们介绍了如何通过训练卷积神经网络来检测面部的关键点,首先通过预训练模型提取特征,然后利用微调模型预测图像中人物的面部关键点,并利用 face_alignment 库来获取图像中人物面部的 2D3D 关键点。

系列链接

PyTorch深度学习实战(1)——神经网络与模型训练过程详解
PyTorch深度学习实战(2)——PyTorch基础
PyTorch深度学习实战(3)——使用PyTorch构建神经网络
PyTorch深度学习实战(4)——常用激活函数和损失函数详解
PyTorch深度学习实战(5)——计算机视觉基础
PyTorch深度学习实战(6)——神经网络性能优化技术
PyTorch深度学习实战(7)——批大小对神经网络训练的影响
PyTorch深度学习实战(8)——批归一化
PyTorch深度学习实战(9)——学习率优化
PyTorch深度学习实战(10)——过拟合及其解决方法
PyTorch深度学习实战(11)——卷积神经网络
PyTorch深度学习实战(12)——数据增强
PyTorch深度学习实战(13)——可视化神经网络中间层输出
PyTorch深度学习实战(14)——类激活图
PyTorch深度学习实战(15)——迁移学习

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1022172.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python(八十七)函数的定义与调用

❤️ 专栏简介:本专栏记录了我个人从零开始学习Python编程的过程。在这个专栏中,我将分享我在学习Python的过程中的学习笔记、学习路线以及各个知识点。 ☀️ 专栏适用人群 :本专栏适用于希望学习Python编程的初学者和有一定编程基础的人。无…

ffmpeg抠图

1.不用png,用AVFrame 2.合流 3.图片抠图透明 (1.)mp4扣yuv图,(2)用1. (3)用抠图算法函数对yuv进行处理 ffmpeg -y -i TJTV.png -vf colorkey0x0000FF:0.5:0.0 -c:v png t.png0x0000FF:六位每两位分别表示红绿蓝,因此它是对…

如何提高技术领导力?与你分享 5 个心得

技术领导力于很多人而言都是谜一般的存在。有观点认为,实战经验丰富的资深开发最终只有成为技术管理者才能继续成长。从某些方面来看,这可能是对的,但考虑到公司结构和规章制度等,想要完成从「个人贡献者」到「技术管理者」的跨越…

mysql自动删除过期的binlog

一、binlog_expire_logs_seconds 配置项 mysql 8.0使用配置项 binlog_expire_logs_seconds 设置binlog过期时间,单位为秒。 mysql旧版本使用配置项 expire_logs_days 设置binlog过期时间,单位为天,不方便测试。 在 8.0 使用 expire_logs_d…

CH2--x86系统架构概览

2.1 OVERVIEW OF THE SYSTEM-LEVEL ARCHITECTURE IA-32 与 IA32-e 模式 图中的实线箭头表示线性地址,虚线表示段选择器,虚线箭头表示物理地址 2.1.1 Global and Local Descriptor Tables 全局描述符表 (GDT) GDT是一个全局的段描述符表,它存储…

「聊设计模式」之适配器模式(Adapter)

🏆本文收录于《聊设计模式》专栏,专门攻坚指数级提升,助你一臂之力,带你早日登顶🚀,欢迎持续关注&&收藏&&订阅! 前言 在软件开发中,经常会涉及到现有系统的改造和升…

微信小程序云开发手搓微标提示,逻辑思路记录及代码实现

目录 写前小叙 功能需求背景 首页js的逻辑思路第一部分 发布公告js逻辑 首页js显示“新”公告思路实现 首页js关闭“新”公告思路实现 管理员“已阅读”js逻辑 首页js显示“新”邮件思路实现 首页js关闭“新”邮件思路实现 写前小叙 今儿凌晨,我又是一个人…

【Python】福利彩票复式模拟选号程序

【效果】 【注意】 逻辑是用Random模拟10000次复试彩票选号,然后给出最大可能性一组。但是模拟终究是模拟,和现实彩票结果没有任何联系,下载下来玩就是了,没人能保证模拟出中奖号码,不要投机,不要投机! 【修改】 代码很简单,如果想改成不是复式的,自行修改即可。 如…

linux学习实操计划0103-安装软件

本系列内容全部给基于Ubuntu操作系统。 系统版本:#32~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Fri Aug 18 10:40:13 UTC 1 安装deb格式软件 Debian包是Unixar的标准归档,将包文件信息以及包内容,经过gzip和tar打包而成。 处理这些包的经典程序是…

【IntelliJ IDEA】切换jdk版本配置

需求描述 idea 2020.3.1 原来idea使用的是jdk8的版本,想换成jdk7的版本,该怎么配置呢?配置哪些地方呢? 解决方法 local1 先在Project Structure中,添加上刚安装的jdk7(它的安装目录) local…

数据结构——线性表之顺序表

目录 一.线性表 二.顺序表实现 2.1 概念及结构 2.2 动态顺序表 2.2.1 初始化与销毁函数 2.2.2 打印函数 2.2.3 尾插函数 2.2.4 尾删函数 2.2.5 扩容函数 2.2.6 头插函数 2.2.7 头删函数 2.2.8 任意位置插入函数 2.2.9 查找函数 2.2.10 任意位置删除函数 2.2.11 修…

2023年9月中国数据库排行榜:阿里叱咤云端登榜眼,华为中兴厚积薄发显峥嵘

鸿鹄展翅凌云志,骏马扬蹄踏雪心。 2023年9月的 墨天轮中国数据库流行度排行 火热出炉,本月共有287个数据库参与排名。本月排行榜前十中,OceanBase 连续10个月稳居榜首,头部产品得分差距日益缩小,阿里华为两大云厂商持续…

react使用内联css样式的注意点

react使用内联css样式: 就是直接在元素标签的style属性中写css样式,但是这里有三个注意点: 1. style等号后面必须接双大括号也就是 style{{ xx: xx }} 这样 2. css的属性必须写成驼峰型,不能有中横线,比如marginRight, 而不能说margin-righ…

Axure原型设计累加器计时器设计效果(职业院校技能大赛物联网技术应用项目原型设计题目)

目录 前言 一、本题实现效果 二、操作步骤 1.新建文件 2.界面设计 2.1文本框 2.2 按钮 2.3设计界面完成 3.交互 3.1启动交互设置 3.2 分别设置三个属性 3.2.1 设置值为“0” 3.2.2 文字于文本框 3.2.3 获取焦点时 3.3 停止按钮的交互动作 3.3.1 设置变量值 3.4 重…

CAC2023交流群(将持续更新至会议结束)

为青年才俊们提供一个信息交换平台 看到大家对中国自动化大会非常感兴趣并踊跃投稿,虽然我和组委会没有什么直接联系,也是一个菜菜。只是把之前走过的坑写了出来,当然,这个会议你会投了,其它会议也是大同小异。 想必…

java刷题知识点

文章目录 牛客网volatile字符串数组和字符数组管道解决hash冲突Java的体系结构包含下面有关servlet的层级结构和常用的类,说法正确的有?关于ThreadLocal类 以下说法正确的是off-heap是指那种内存集合和map的的继承实现关系抽象类和接口的区别节点流与处理流语句&am…

Solidity 小白教程:21. 调用其他合约

Solidity 小白教程:21. 调用其他合约 调用已部署合约 开发者写智能合约来调用其他合约,这让以太坊网络上的程序可以复用,从而建立繁荣的生态。很多web3项目依赖于调用其他合约,比如收益农场(yield farming&#xff0…

SAP FI 中的重要报告

目录 总账科目表 GL总账清单 GL总账余额 总账科目总计和余额 AR客户主数据列表 AR 客户余额 AR客户订单项 AR客户未清项目 AR客户付款记录 AR供应商主数据 AR供应商余额 AR供应商订单额 AR供应商未清项目 总账科目表 在 SAP 命令字段中输入交易代码 S_ALR_870123…

数据分享|R语言生态学种群空间点格局分析:聚类泊松点过程对植物、蚂蚁巢穴分布数据可视化...

全文链接 :https://tecdat.cn/?p33676 点模式分析(点格局分析)是一组用于分析空间点数据的技术。在生态学中,这种类型的分析可能在客户的几个情境下出现,但对数据生成方式做出了特定的假设,因此让我们首先看看哪些生态…

中秋国庆假期——模板推荐

要说最近能让人开心的事情是什么?大概就是下周将迎来8天假,小编帮大家数了数还有11天,就要放中秋国庆的假期了。作为一个资深打工人,本周的日常即将变成:上班想放假、下班想放假、想放假… 但是宝子们要注意,大家在盼…