人脸关键点检测

人脸关键点检测是诸如人脸识别、表情分析、三维人脸重建等其他人脸相关任务的基础。在人脸识别技术中是人脸检测的下一步任务。

关键点检测基础

人脸关键点检测是指给定人脸图像,定位出人脸面部的关键点,包括眉毛、眼睛、鼻子、嘴、脸部轮廓区域的点,由于受到姿态和遮挡等因素的影响,关键点检测是一个富有挑战性的任务。

人脸关键点有以下主要的应用:

  • 人脸姿态对齐、人脸识别等算法都需要对人脸进行特征点检测,实现姿态对齐,从而提高模型的精度。
  • 人脸美艳与编辑,基于关键点可以精确分析脸型、眼睛形状、鼻子形状等,从而对人脸的特定位置进行修饰加工,实现人脸的特效美颜、贴片等娱乐功能,也能辅助一些人脸编辑算法更好地发挥作用。
  • 人脸表情分析,基于关键点可以对人的面部表情进行分析,从而用于互动娱乐,行为预测等场景。

以深度学习的人脸关键点检测:

1、模型训练

(1)数据集接口
数据集接口用于实现图像和标签的获取;

class WLFWDatasets(data.Dataset):   def __init__(self, file_list, transforms=None):       self.line = None      self.path = None       self.landmarks = None       self.attribute = None       self.filenames = None       self.euler_angle = None       self.transforms = transforms        # 读取txt文件        with open(file_list, 'r') as f:            self.lines = f.readlines()   def __getitem__(self, index):       # 每一行存储的分别是图像路径、关键点、属性、欧拉角  self.line = self.lines[index].strip().split()        # 使用opencv读取图像        self.img = cv2.imread(self.line[0])        # 获得98个关键点        self.landmark = np.asarray(self.line[1:197], dtype=np.float32)       # 获得6个人俩属性        self.attribute = np.asarray(self.line[197:203], dtype=np.int32)       # 获得3个欧拉角        self.euler_angle = np.asarray(self.line[203:206], dtype=np.float32)if self.transforms:            # 预处理            self.img = self.transforms(self.img)        return self.img, self.landmark, self.attribute, self.euler_angledef __len__(self):        return len(self.lines)

上面代码中,所有的数据和标签都按行存为txt文件,因此需要定义类从txt文件中按行读取图像、关键点、人脸属性、欧拉角。
其中关键点为98个,因此维度为196。
人脸属性包括6个,分别为:姿态、表情、光照、化妆、遮挡、模糊。

(2)模型定义
模型包含两个部分,第一部分是关键点回归模型,第二部分是姿态估计模型。首先我们看关键点模型的定义,这是一个基于MobileNet V2的回归模型。它使用了“漏斗形”的残差模块,这是区别于MobileNet V1的基本结构单元。

class InvertedResidual(nn.Module):def __init__(self, inp, oup, stride, use_res_connect, expand_ratio=6):super(InvertedResidual, self).__init__()self.stride = stride# 仅支持1或2两种步长assert stride in [1, 2]# 是否使用跳层链接self.use_res_connect = use_res_connectself.conv = nn.Sequential(# 1*1卷积层,通道数从inp升维为inp*expand_rationn.Conv2d(inp, inp * expand_ratio, 1, 1, 0, bias=False),nn.BatchNorm2d(inp * expand_ratio),nn.ReLU(inplace=True),# 3*3分组卷积层nn.Conv2d(inp * expand_ratio,inp * expand_ratio,3,stride,1,groups=inp * expand_ratio,bias=False),nn.BatchNorm2d(inp * expand_ratio),nn.ReLU(inplace=True),# 1*1卷积层,通道数从inp*expand_ratio变为oupnn.Conv2d(inp * expand_ratio, oup, 1, 1, 0, bias=False),nn.BatchNorm2d(oup),)def forward(self, x):if self.use_res_connect:return x + self.conv(x)else:return self.conv(x)

从上面的定义可以看出,“漏斗形”残差块的定义包含3个卷积层,其中,第一个1×1卷积层,实现升维;第二个是3×3的分组卷积;第三个个是1×1卷积层,实现降维。因为中间的纬度高,所以被称为“漏斗形”残差结构。另外,还可以根据变量控制是否使用跳层链接。
再看PFLD的模型定义。

class PFLDInference(nn.Module):def __init__(self):super(PFLDInference, self).__init__()# 第一个卷积层,输入为图像,输出64个通道,卷积核大小为3,步长为2self.conv1 = nn.Conv2d(3, 64, kernel_size=3, stride=2, padding=1, bias=False)self.bn1 = nn.BatchNorm2d(64)self.relu = nn.ReLU(inplace=True)# 第二个卷积层,输入输出都是64通道,卷积核大小3,步长1self.conv2 = nn.Conv2d(64, 64, kernel_size=3, stride=1, padding=1, bias=False)self.bn2 = nn.BatchNorm2d(64)self.relu = nn.ReLU(inplace=True)# 堆叠的漏斗形模块self.conv3_1 = InvertedResidual(64, 64, 2, False, 2)self.block3_2 = InvertedResidual(64, 64, 1, True, 2)self.block3_3 = InvertedResidual(64, 64, 1, True, 2)self.block3_4 = InvertedResidual(64, 64, 1, True, 2)self.block3_5 = InvertedResidual(64, 64, 1, True, 2)self.conv4_1 = InvertedResidual(64, 128, 2, False, 2)self.conv5_1 = InvertedResidual(128, 128, 1, False, 4)self.block5_2 = InvertedResidual(128, 128, 1, True, 4)self.block5_3 = InvertedResidual(128, 128, 1, True, 4)self.block5_4 = InvertedResidual(128, 128, 1, True, 4)self.block5_5 = InvertedResidual(128, 128, 1, True, 4)self.block5_6 = InvertedResidual(128, 128, 1, True, 4)self.conv6_1 = InvertedResidual(128, 16, 1, False, 2)  # [16, 14, 14]# 卷积层, 输入16通道,输出32通道,卷积核大小3,步长2self.conv7 = conv_bn(16, 32, 3, 2)  # [32, 7, 7]# 卷积层, 输入32通道,输出128通道, 卷积核大小7,步长1self.conv8 = nn.Conv2d(32, 128, 7, 1, 0)  # [128, 1, 1]self.bn8 = nn.BatchNorm2d(128)# 两个全局平均池化层self.avg_pool1 = nn.AvgPool2d(14)self.avg_pool2 = nn.AvgPool2d(7)# 全链接层self.fc = nn.Linear(176, 196)def forward(self, x):  # x: 3, 112, 112x = self.relu(self.bn1(self.conv1(x)))  # [64, 56, 56]x = self.relu(self.bn2(self.conv2(x)))  # [64, 56, 56]x = self.conv3_1(x)x = self.block3_2(x)x = self.block3_3(x)x = self.block3_4(x)out1 = self.block3_5(x)x = self.conv4_1(out1)x = self.conv5_1(x)x = self.block5_2(x)x = self.block5_3(x)x = self.block5_4(x)x = self.block5_5(x)x = self.block5_6(x)x = self.conv6_1(x)x1 = self.avg_pool1(x)x1 = x1.view(x1.size(0), -1)  # 第一个特征,维度16x = self.conv7(x)x2 = self.avg_pool2(x)x2 = x2.view(x2.size(0), -1)  # 第二个特征,维度32x3 = self.relu(self.conv8(x))x3 = x3.view(x1.size(0), -1)  # 第三个特征,维度128multi_scale = torch.cat([x1, x2, x3], 1)  # 多尺度特征融合landmarks = self.fc(multi_scale)  # 关键点return out1, landmarks

从上面的结构可以看出,首先经过了两个卷积层,然后经过了若干个漏斗形残差模块,最后使用了3个特征尺度进行融合。其中,第一个尺度为14×14,然后进行了全局平均池化,维度是16;第二个尺度为7×7,进行全局平均池化,维度是32;第三个尺度为1×1,维度是128。
3个特征变量维度变换后进行了串接,得到总的特征向量维度是176,然后经过全链接层输出196维的关键点坐标landmarks。另一方面,block3_5的输出out1将被作为姿态估计网络输入。
最后看看姿态网络估计网络的定义。

class AuxiliaryNet(nn.Module):def __init__(self):super(AuxiliaryNet, self).__init__()# 四个卷积层self.conv1 = conv_bn(64, 128, 3, 2)self.conv2 = conv_bn(128, 128, 3, 1)self.conv3 = conv_bn(128, 32, 3, 2)self.conv4 = conv_bn(32, 128, 7, 1)# 3×3最大池化层self.max_pool1 = nn.MaxPool2d(3)# 两个全链接层self.fc1 = nn.Linear(128, 32)self.fc2 = nn.Linear(32, 3)def forward(self, x):x = self.conv1(x)x = self.conv2(x)x = self.conv3(x)x = self.conv4(x)x = self.max_pool1(x)x = x.view(x.size(0), -1)x = self.fc1(x)x = self.fc2(x)return x

可以看出,姿态估计网络包含4个卷积层及1个最大池化层和两个全链接层。其中,第一个卷积层输入是64通道,来自于MobileNet V2的中间特征层,可见两个网络实现了底层特征共享。

(3)损失定义

class PFLDLoss(nn.Module):def __init__(self):super(PFLDLoss, self).__init__()def forward(self, attribute_gt, landmark_gt, euler_angle_gt, angle, landmarks, train_batchsize):# 角度权重weight_angle = torch.sum(1 - torch.cos(angle - euler_angle_gt), axis=1)# 属性权重attributes_w_n = attribute_gt[:, 1:6].float()mat_ratio = torch.mean(attributes_w_n, axis=0)mat_ratio = torch.Tensor([1.0 / (x) if x > 0 else train_batchsize for x in mat_ratio]).to(device)weight_attribute = torch.sum(attributes_w_n.mul(mat_ratio), axis=1)# 欧式距离损失计算l2_distant = torch.sum((landmark_gt - landmarks) * (landmark_gt - landmarks), axis=1)return torch.mean(weight_angle * weight_attribute * l2_distant), torch.mean(l2_distant)

可以看出,在计算出角度权重和属性权重后就可以结合欧式距离得到最终的损失。

2、模型测试


训练完成后我们可以看到人脸图像上的关键点检测结果

3、总结

总体来说,pfld模型对大部分人脸图像有着非常不错的定位结果,对一般姿态和遮挡效果也非常不错,当然也还有比较大的提升空间,包括以下两个方面:

  • 增加训练数据集,虽然这里已经是7500张人脸训练的结果,但是对于工业级的人脸关键点检测来说还是远远不够,应该增加年龄、姿态及遮挡类型,从而进一步提高模型的鲁棒性。
  • 增加数据增强操作,此处为了验证模型,没有采用在线的数据增强方案,当使用更多的数据增强操作时,比如多尺度训练、颜色扰动和光照变化等,可以提升模型性能。

人脸关键点检测PFLD相关推荐

  1. [人脸关键点检测] PFLD:简单、快速、超高精度人脸关键检测

    转载请注明作者和出处: http://blog.csdn.net/john_bh/ 论文链接:PFLD: A Practical Facial Landmark Detector 作者及团队: 天津大 ...

  2. 【论文解读】PFLD:高精度实时人脸关键点检测算法

    这篇文章作者分别来自天津大学.武汉大学.腾讯AI实验室.美国天普大学.该算法对在高通ARM 845处理器可达140fps:另外模型大小较小,仅2.1MB:此外在许多关键点检测的benchmark中也取 ...

  3. Tensorflow2实现人脸关键点检测算法PFLD——一个精度高,速度快,模型小的人脸关键点检测模型

    1. 前言 最近,学了人脸关键点检测算法,发现一个比较好的人脸关键点检测模型,打算学一学,让我们来看看算法是如何实现的吧! 论文地址:https://arxiv.org/pdf/1902.10859. ...

  4. 【项目实战课】基于Pytorch的PFLD人脸关键点检测实战

    欢迎大家来到我们的项目实战课,本期内容是<基于Pytorch的PFLD人脸关键点检测实战>.所谓项目课,就是以简单的原理回顾+详细的项目实战的模式,针对具体的某一个主题,进行代码级的实战讲 ...

  5. PFLD:简单高效的实用人脸关键点检测算法

    作者丨杜敏 学校丨华中科技大学硕士 研究方向丨模式识别与智能系统 研究背景 人脸关键点检测,在很多人脸相关的任务中,属于基础模块,很关键.比如人脸识别.人脸验证.人脸编辑等等.想做人脸相关的更深层次的 ...

  6. caffe 人脸关键点检测_密集人脸关键点检测

    把人脸关键点检测的门槛给我打下来, 本文的代码可以在mrlandmark下载,提供一键式运行的能力 MTCNN联合人脸检测和对齐任务提供了5点关键点的能力,但是对于姿态姿态恢复等应用是远远不够的,经常 ...

  7. 【技术综述】人脸关键点检测的数据集与核心算法

    人脸关键点检测是诸如人脸识别.表情分析.三维人脸重建等其它人脸相关任务的基础.近些年来,深度学习方法已被成功应用到了人脸的关键点检测,本章将介绍深度学习方法在人脸关键点检测方向的研究.包括人脸关键点任 ...

  8. paddle2.0高层API实现人脸关键点检测(人脸关键点检测综述_自定义网络_paddleHub_趣味ps)

    paddle2.0高层API实现人脸关键点检测(人脸关键点检测综述_自定义网络_paddleHub_趣味ps) 本文包含了: - 人脸关键点检测综述 - 人脸关键点检测数据集介绍以及数据处理实现 - ...

  9. 【机器视觉案例】(11) 眨眼计数器,人脸关键点检测,附python完整代码

    各位同学好,今天和大家分享一下如何使用 mediapipe+opencv 实现眨眼计数器.先放张图看效果. 下图左侧为视频图像,右侧为平滑后的人眼开合比曲线.以左眼为例,若眼眶上下边界的距离与左右边界 ...

  10. 「每周CV论文推荐」 初学深度学习人脸关键点检测必读文章

    人脸关键点检测是人脸图像中重要的基石,今天给大家介绍入门深度学习人脸关键点检测必读的文章. 作者&编辑 | 言有三 1 DCNN Cascade 听这个名字就知道是一个很早期的,使用Casca ...

最新文章

  1. 《Pro Ogre 3D Programming》 读书笔记 之 第七章 资源管理(转)
  2. Serverless Kubernetes:理想,现实与未来
  3. linux lvm管理实例,Linux系统管理之LVM案例
  4. 【华为云技术分享】iSulad轻量化容器实践解析
  5. Android笔记 pacth图形
  6. 开源软件公司易犯的 5 大错误,又该如何避免?
  7. 计算机专业排版有哪些,计算机专业英语词汇(完美排版_大容量打印版).pdf
  8. php数组超索引,php数组多维索引
  9. golang开发:go并发的建议
  10. 东南亚跨境电商ERP怎么选?萌店长ERP,含大数据分析的免费erp系统
  11. err = Problems with launching via XPC. XPC error : Connection interrupted (0x00000005)
  12. c++学习 cout endl使用
  13. 企查猫app破解以及数据解密
  14. Python---如何实现千图成像:初级篇(从图片爬取到图片合成)
  15. h5活动是什么意思_浅谈什么是H5页面,怎么制作h5页面
  16. EXCEL中如何提取身份证出生日期和性别信息以及检验身份证号码的正确性
  17. 树莓派入门(一) - 下载安装系统镜像,Raspbian和Centos
  18. C语言修饰词之violate
  19. Opencores上的i2c controller core代码解析
  20. win10系统下Eplan使用过程中卡死现象

热门文章

  1. win10远程连接Ubuntu
  2. MySQL5.5安装步骤
  3. 串口通信协议c语言程序,串口通信协议源代码.doc
  4. 32位联想台式机更新xp到win10工作站版本
  5. uniapp-UI设计
  6. Niushop wap端前台模板设置
  7. 如何更改Eclipse中Properties文件编码格式
  8. 数据库备份的三种方式 不要再干掉数据库跑路啦~
  9. 棉猴论坛VIP之驱动基础系列教程 视频教程
  10. 教的好、口碑好的Web前端培训机构有哪些?