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

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_ykp2 = 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_splittrain, 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 = 50for epoch in range(n_epochs):print(f" epoch {epoch+ 1}/50")epoch_train_loss, epoch_test_loss = 0, 0for 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 cv2file = '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.mzph.cn/news/82438.shtml

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

相关文章

算法刷题 week2

目录 week21. 二维数组中的查找题目题解(单调性扫描) O(nm) 2.替换空格题目题解(线性扫描) O(n)(双指针扫描) O(n) 3.从尾到头打印链表题目题解(遍历链表) O(n) week2 1. 二维数组中的查找 题目 题解 (单调性扫描) O(nm) 核心在于发现每个子矩阵右上角的数的性质&#xff1…

Dokcer创建MySQL容器,并在宿主机或mysql可视化工具中连接mysql容器的数据库

文章目录 一、Docker 创建 MySQL容器1. 拉取 MySQL 镜像2. 创建并运行 MySQL 容器3. 创建并运行 MySQL 容器(目录映射) 二、连接 MySQL 数据库1. 在 MySQL 容器内,连接MySQL2. 在宿主机连接 MySQL(遇到问题及解决方案)…

【kafka】可视化工具KAFKA EAGLE安装分享

目录 准备: 开始: 1.解压 2.环境变量配置 3.生效环境变量配置文件 3.修改配置文件 1.修改zookeeper集群信息 2.修改mysql配置信息 4.启动 5.异常排查 6.页面 创作不易,你的动力是我创作的动力,如果有帮助请关注我&…

路由器端口转发

什么是路由器端口转发 路由器端口转发是一种网络配置技术,用于将公共网络(如互联网)上的请求转发到私有网络中的特定设备或服务。它允许外部设备通过路由器访问内部网络中的设备或服务,实现网络上的通信和互动。 路由器端口转发…

计算机竞赛 深度学习 YOLO 实现车牌识别算法

文章目录 0 前言1 课题介绍2 算法简介2.1网络架构 3 数据准备4 模型训练5 实现效果5.1 图片识别效果5.2视频识别效果 6 部分关键代码7 最后 0 前言 🔥 优质竞赛项目系列,今天要分享的是 🚩 基于yolov5的深度学习车牌识别系统实现 该项目较…

《YOLOv5/YOLOv7/YOLOv8最新改进大作战》专栏介绍 CSDN独家改进创新实战 专栏目录

💡💡💡YOLOv5/YOLOv7/YOLOv8最新改进大作战,独家首发创新(原创),持续更新,适用于Yolov5、Yolov7、Yolov8等各个Yolo系列,专栏文章提供每一步步骤和源码,轻松带…

uniapp之uni-forms表单组件封装的双向数据绑定

前言 在uniapp中, 封装组件的props单向数据流更为严格, 不允许改变子组件的props属性, 所以记录下uniapp下的form表单的组件是如何封装的, 双向数据是如何绑定的. 版本: "dcloudio/uni-ui": "^1.4.27", "vue": "> 2.6.14 < 2.7&quo…

安卓毕业设计各种app项目,Android毕设设计,Android课程设计,毕业论文

作为一位从事软件开发多年的专业人士&#xff0c;您积累了丰富的经验和技能&#xff0c;解决了许多不同类型的问题。除了开发原创项目&#xff0c;您还愿意分享您的知识&#xff0c;指导实习生和在校生。这种乐于助人的行为对于行业的发展和新一代软件开发者的成长都起着积极的…

《优化接口设计的思路》系列:第三篇—留下用户调用接口的痕迹

系列文章导航 《优化接口设计的思路》系列&#xff1a;第一篇—接口参数的一些弯弯绕绕 《优化接口设计的思路》系列&#xff1a;第二篇—接口用户上下文的设计与实现 《优化接口设计的思路》系列&#xff1a;第三篇—留下用户调用接口的痕迹 前言 大家好&#xff01;我是sum…

阿里云交互式建模(PAI-DSW)训练并微调推理ChatGLM模型

参考内容为《轻量微调和推理ChatGLM模型实践》 点击“交互式建模&#xff08;DSW&#xff09;”&#xff0c;然后选择“创建实例” 写上实例名称&#xff0c;然后选择GPU规格&#xff0c;选择“ecs.gn6v-c8g1.2xlarge(8 vCPU&#xff0c;32GB)” 页面往下拉选择“pytorch:…

Leetcode162. 寻找峰值

力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 峰值元素是指其值严格大于左右相邻值的元素。 给你一个整数数组 nums&#xff0c;找到峰值元素并返回其索引。数组可能包含多个峰值&#xff0c;在这种情况下&#xff0c;返回 任何一个峰值 所在位置即…

物理内存分配

目录 内核物理内存分配接口 内存分配行为&#xff08;物理上&#xff09; 内存分配的行为操作 内存 三个水位线 水线计算 水位线影响内存分配行为 内存分配核心__alloc_pages 释放页 1、内核物理内存分配接口 struct page *alloc_pages(gfp_t gfp, unsigned int ord…

Redis 三种特殊的数据类型 - Geospatial地理位置 - Hyperloglog基数统计的算法 - Bitmaps位图(位存储)

目录 Redis 三种特殊的数据类型&#xff1a; Geospatial&#xff1a;地理位置 Geospatial类型常用的命令&#xff1a; GEOADD&#xff1a;添加地理位置 GEOPOS&#xff1a;获取地理位置 GEODIST&#xff1a;返回两个给定位置之间的距离 GEORADIUS&#xff1a;以给定的经纬…

Seata 源码篇之AT模式启动流程 - 上 - 02

Seata 源码篇之AT模式启动流程 - 02 自动配置两个关键点 初始化初始化TM初始化RM初始化TC 全局事务执行流程TM 发起全局事务GlobalTransactional 注解处理全局事务的开启 TM 和 RM 执行分支事务IntroductionDelegatingIntroductionInterceptorDelegatePerTargetObjectIntroduct…

在华为云服务器上CentOS 7安装单机版Redis

https://redis.io/是官网地址。 点击右上角的Download。 可以进入https://redis.io/download/——Redis官网下载最新版的网址。 然后在https://redis.io/download/页面往下拉&#xff0c;点击下图超链接这里。 进入https://download.redis.io/releases/下载自己需要的安装…

【C语言】自定义类型:结构体【结构体内存具详细】,枚举,联合

目录 一、结构体 1.结构的声明 2.特殊的声明 3.结构的自引用 4.结构体变量的定义和初始化 5.结构体内存对齐&#xff08;重点来了&#xff09; 6.为什么会存在内存对齐 7.修改默认对齐数 8.结构体传参 二、位段 1.什么是位段 2.位段的内存分配 3.位段的跨平台问题…

ajax day4

1、promise链式调用 /*** 目标&#xff1a;把回调函数嵌套代码&#xff0c;改成Promise链式调用结构* 需求&#xff1a;获取默认第一个省&#xff0c;第一个市&#xff0c;第一个地区并展示在下拉菜单中*/let pname axios({url: http://hmajax.itheima.net/api/province,}).t…

21天学会C++:Day11----运算符重载

CSDN的uu们&#xff0c;大家好。这里是C入门的第十一讲。 座右铭&#xff1a;前路坎坷&#xff0c;披荆斩棘&#xff0c;扶摇直上。 博客主页&#xff1a; 姬如祎 收录专栏&#xff1a;C专题 目录 1. 知识引入 2. 运算符重载 2.1 operator<() 2.2 operator() 2.3 o…

jvm中对象创建、内存布局以及访问定位

对象创建 Java语言层面&#xff0c;创建对象通常&#xff08;例外&#xff1a;复制、反序列化&#xff09;仅仅是一个new关键字即可&#xff0c;而在虚拟机中&#xff0c;对象&#xff08;限于普通Java对象&#xff0c;不包括数组和Class对象等&#xff09;的创建又是怎样一个过…

小米华为,化干戈为玉帛!

近日来&#xff0c;手机圈又掀起了各大厂家推出新品的高潮。首先是华为Mate60的推出&#xff0c;其自研的麒麟9000S芯片瞬间点燃了国内手机市场&#xff0c;得到了国内甚至国外业界人士的认可和好评。 而近日网上盛传的小米创始人雷军的“愿意加入华为技术生态圈”的邀请&…