Python深度学习实战:人脸关键点(15点)检测pytorch实现

引言

人脸关键点检测即对人类面部若干个点位置进行检测,可以通过这些点的变化来实现许多功能,该技术可以应用到很多领域,例如捕捉人脸的关键点,然后驱动动画人物做相同的面部表情;识别人脸的面部表情,让机器能够察言观色等等。
在这里插入图片描述

如何检测人脸关键点

本文是实现15点的检测,至于N点的原理都是一样的,使用的算法模型是深度神经网络,使用CV也是可以的。

如何检测

这个问题抽象出来,就是一个使用神经网络来进行预测的功能,只不过输出是15个点的坐标,训练数据包含15个面部的特征点和面部的图像(大小为96x96),15个特征点分别是:left_eye_center, right_eye_center, left_eye_inner_corner, left_eye_outer_corner, right_eye_inner_corner, right_eye_outer_corner, left_eyebrow_inner_end, left_eyebrow_outer_end, right_eyebrow_inner_end, right_eyebrow_outer_end, nose_tip, mouth_left_corner, mouth_right_corner, mouth_center_top_lip, mouth_center_bottom_lip
因此神经网络需要学习一个从人脸图像到15个关键点坐标间的映射。

使用的网络结构

在本文中,我们使用深度神经网络来实现该功能,基本卷积块使用Google的Inception网络,也就是使用GoogLeNet网络,该结构的网络是基于卷积神经网络来改进的,是一个含有并行连接的网络。
众所周知,卷积有滤波、提取特征的作用,但到底采用多大的卷积来提取特征是最好的呢?这个问题没有确切的答案,那就集百家之长:使用多个形状不一的卷积来提取特征并进行拼接,从而学习到更为丰富的特征;特别是里面加上了1x1的卷积结构,能够实现跨通道的信息交互和整合(其本质就是在多个channel上的线性求和),同时能在feature map通道数上的降维(读者可以验证计算一下,能够极大减少卷积核的参数),也能够增加非线性映射次数使得网络能够更深。
下面是Inception块的示意图:
在这里插入图片描述
整个GoogLeNet的结构如下所示:
在这里插入图片描述
接下来是代码实现部分,后续作者会补充神经网络的相关原理知识,若对此感兴趣的读者也可继续关注支持~

代码实现

import torch as tc
from torch import nn
from torch.nn import functional as F
from torch.utils.data import DataLoader
from torch.utils.data import TensorDataset
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from sklearn.utils import shuffle

# 对图片像素的处理
def proFunc1(data,testFlag:bool=False) -> tuple:
    data['Image'] = data['Image'].apply(lambda im: np.fromstring(im, sep=' '))
    # 处理na
    data = data.dropna()  
    # 神经网络对数据范围较为敏感 /255 将所有像素都弄到[0,1]之间
    X = np.vstack(data['Image'].values) / 255
    X = X.astype(np.float32)
    # 特别注意 这里要变成 n channle w h 要跟卷积第一层相匹配
    X = X.reshape(-1, 1,96, 96) 
    # 等会神经网络的输入层就是 96 96 黑白图片 通道只有一个
    # 只有训练集才有y 测试集返回一个None出去
    if not testFlag:  
        y = data[data.columns[:-1]].values
        # 规范化
        y = (y - 48) / 48  
        X, y = shuffle(X, y, random_state=42)  
        y = y.astype(np.float32)
    else:
        y = None

    return X,y

# 工具类
class UtilClass:

    def __init__(self,model,procFun,trainFile:str='data/training.csv',testFile:str='data/test.csv') -> None:
        self.trainFile = trainFile
        self.testFile = testFile
        self.trainData = None
        self.testData = None
        self.trainTarget = None
        self.model = model
        self.procFun = procFun

    @staticmethod
    def procData(data, procFunc ,testFlag:bool=False) -> tuple:
        return procFunc(data,testFlag)

    def loadResource(self):
        rawTrain = pd.read_csv(self.trainFile)
        rawTest = pd.read_csv(self.testFile)
        self.trainData , self.trainTarget = self.procData(rawTrain,self.procFun)
        self.testData , _ = self.procData(rawTest,self.procFun,testFlag=True)

    def getTrain(self):
        return tc.from_numpy(self.trainData), tc.from_numpy(self.trainTarget)

    def getTest(self):
        return tc.from_numpy(self.testData)

    @staticmethod
    def plotData(img, keyPoints, axis):
        axis.imshow(np.squeeze(img), cmap='gray') 
        # 恢复到原始像素数据 
        keyPoints = keyPoints * 48 + 48 
        # 把keypoint弄到图上面
        axis.scatter(keyPoints[0::2], keyPoints[1::2], marker='o', c='c', s=40)

# 自定义的卷积神经网络
class MyCNN(tc.nn.Module):
    def __init__(self,imgShape = (96,96,1),keyPoint:int = 15):
        super(MyCNN, self).__init__()
        self.conv1 = tc.nn.Conv2d(in_channels=1, out_channels =10, kernel_size=3)
        self.pooling = tc.nn.MaxPool2d(kernel_size=2)
        self.conv2 = tc.nn.Conv2d(10, 5, kernel_size=3)
        # 这里的2420是通过下面的计算得出的 如果改变神经网络结构了 
        # 需要计算最后的Liner的in_feature数量 输出是固定的keyPoint*2
        self.fc = tc.nn.Linear(2420, keyPoint*2)

    def forward(self, x):
        # print("start----------------------")
        batch_size = x.size(0)
        # x = x.view((-1,1,96,96))
        # print('after view shape:',x.shape)
        x = F.relu(self.pooling(self.conv1(x)))
        # print('conv1 size',x.shape)
        x = F.relu(self.pooling(self.conv2(x)))
        # print('conv2 size',x.shape)
        # print('end--------------------------')
        # 改形状
        x = x.view(batch_size, -1)
        # print(x.shape)
        x = self.fc(x)
        # print(x.shape)
        return x

# GoogleNet基本的卷积块
class MyInception(nn.Module):

    def __init__(self,in_channels, c1, c2, c3, c4,) -> None:
        super().__init__()
        
        self.p1_1 = nn.Conv2d(in_channels, c1, kernel_size=1)
        
        self.p2_1 = nn.Conv2d(in_channels, c2[0], kernel_size=1)
        self.p2_2 = nn.Conv2d(c2[0], c2[1], kernel_size=3, padding=1)
        
        self.p3_1 = nn.Conv2d(in_channels, c3[0], kernel_size=1)
        self.p3_2 = nn.Conv2d(c3[0], c3[1], kernel_size=5, padding=2)
        
        self.p4_1 = nn.MaxPool2d(kernel_size=3, stride=1, padding=1)
        self.p4_2 = nn.Conv2d(in_channels, c4, kernel_size=1)

    def forward(self, x):
        p1 = F.relu(self.p1_1(x))
        p2 = F.relu(self.p2_2(F.relu(self.p2_1(x))))
        p3 = F.relu(self.p3_2(F.relu(self.p3_1(x))))
        p4 = F.relu(self.p4_2(self.p4_1(x)))
        # 在通道维度上连结输出
        return tc.cat((p1, p2, p3, p4), dim=1)

# GoogLeNet的设计 此处参数结果google大量实验得出
b1 = nn.Sequential(nn.Conv2d(1, 64, kernel_size=7, stride=2, padding=3),
                   nn.ReLU(),
                   nn.MaxPool2d(kernel_size=3, stride=2, padding=1))

b2 = nn.Sequential(nn.Conv2d(64, 64, kernel_size=1),
                   nn.ReLU(),
                   nn.Conv2d(64, 192, kernel_size=3, padding=1),
                   nn.ReLU(),
                   nn.MaxPool2d(kernel_size=3, stride=2, padding=1))

b3 = nn.Sequential(MyInception(192, 64, (96, 128), (16, 32), 32),
                   MyInception(256, 128, (128, 192), (32, 96), 64),
                   nn.MaxPool2d(kernel_size=3, stride=2, padding=1))

b4 = nn.Sequential(MyInception(480, 192, (96, 208), (16, 48), 64),
                   MyInception(512, 160, (112, 224), (24, 64), 64),
                   MyInception(512, 128, (128, 256), (24, 64), 64),
                   MyInception(512, 112, (144, 288), (32, 64), 64),
                   MyInception(528, 256, (160, 320), (32, 128), 128),
                   nn.MaxPool2d(kernel_size=3, stride=2, padding=1))

b5 = nn.Sequential(MyInception(832, 256, (160, 320), (32, 128), 128),
                   MyInception(832, 384, (192, 384), (48, 128), 128),
                   nn.AdaptiveAvgPool2d((1,1)),
                   nn.Flatten())


uClass = UtilClass(model=None,procFun=proFunc1)
uClass.loadResource()
xTrain ,yTrain = uClass.getTrain()
xTest = uClass.getTest()

dataset = TensorDataset(xTrain, yTrain)
trainLoader = DataLoader(dataset, 64, shuffle=True, num_workers=4)

# 训练net并进行测试 由于显示篇幅问题 只能打印出极为有限的若干测试图片效果
def testCode(net):
    optimizer = tc.optim.Adam(params=net.parameters())
    criterion = tc.nn.MSELoss()
        
    for epoch in range(30):
        trainLoss = 0.0
        # 这里是用的是mini_batch 也就是说 每次只使用mini_batch个数据大小来计算
        # 总共有total个 因此总共训练 total/mini_batch 次
        # 由于不能每组数据只使用一次 所以在下面还要使用一个for循环来对整体训练多次
        for batchIndex, data in enumerate(trainLoader, 0):
            input_, y = data
            yPred = net(input_)
            loss = criterion(yPred, y)
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()
            trainLoss += loss.item()
            # 只在每5个epoch的最后一轮打印信息
            if batchIndex % 30 ==29 and not epoch % 5 :
                print("[{},{}] loss:{}".format(epoch + 1, batchIndex + 1, trainLoss / 300))
                trainLoss = 0.0
    # 测试
    print("-----------test begin-------------")
    # print(xTest.shape)
    yPost = net(xTest)
    # print(yPost.shape)
    import matplotlib.pyplot as plt
    %matplotlib inline
        
    fig = plt.figure(figsize=(20,20))
    fig.subplots_adjust(left=0, right=1, bottom=0, top=1, hspace=0.05, wspace=0.05)
    for i in range(9,18):
        ax = fig.add_subplot(3, 3, i - 9 + 1, xticks=[], yticks=[])
        uClass.plotData(xTest[i], y[i], ax)
    print("-----------test end-------------")
    

if __name__ == "__main__":
    # 训练MyCNN网络 并可视化在9个测试数据的效果图
    myNet = MyCNN()
    testCode(myNet)
    inception = nn.Sequential(b1, b2, b3, b4, b5, nn.Linear(1024, 30))
	testCode(inception)

本文使用的数据可在此找到两个data文件,本文有你帮助的话,就给个点赞关注支持一下吧!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

线程池的讲解和实现

🚀🚀🚀🚀🚀🚀🚀大家好,今天为大家带来线程池相关知识的讲解,并且实现一个线程池 🌸🌸🌸🌸🌸🌸🌸🌸…

DM的学习心得和知识总结(一)|DM数据库Real Application Testing之Database Reply实操(一)

目录结构 注:提前言明 本文借鉴了以下博主、书籍或网站的内容,其列表如下: 1、达梦数据库产品及解决方案,点击前往 2、达梦技术文档,点击前往 3、武汉达梦数据库有限公司 官网首页,点击前往 1、本文内容全部…

OpenFeign#1 - FeignClient 是如何注册的?

文章目录EnableFeignClientsFeignClientsRegistrarregisterDefaultConfigurationregisterFeignClientsFeignClientFeignClientFactoryBeanFeignContextfeign(FeignContext)EnableFeignClients 该注解会导致 FeignClientsRegistrar 的注入. Retention(RetentionPolicy.RUNTIME…

如何用canvas制作一个华容道小游戏(乞丐版)

我大抵是废了φ(..) ,横竖都学不进去,上课知识不进脑子,学习光想划水摸鱼,心中仅剩的良知告诉我这样下去是铁定不行的哇,既然学不进去,何不打把游戏,既然要打游戏&#x…

HTML5 Video(视频)

HTML5 Video(视频) 在本节内容中,你将了解到在HTML5中视频是如何工作的、主流浏览器支持的视频格式以及如何对网页中的视频进行控制。 很多站点都会使用到视频. HTML5 提供了展示视频的标准。 检测您的浏览器是否支持 HTML5 视频: Web站点上的视频 直…

SeNet论文解读/总结

此文章为深度学习在计算机视觉领域的图片分类经典论文SeNet(Squeeze-and-Excitation Networks)论文总结。 此系列文章是非常适合深度学习领域的小白观看的图像分类经典论文。系列文章如下: AlexNet:AlexNet论文解读/总结_alexnet…

在CentOS上安装Docker引擎

1,先决条件#### 1-1操作系统要求1-2 卸载旧版本 2,安装方法2-1使用存储库安装设置存储库安装 Docker 引擎 本文永久更新地址: 官方地址:https://docs.docker.com/engine/install/centos/ 1,先决条件 #### 1-1操作系统要求 要安装 Docker Engine,您需要…

【基础算法】链表相关题目

系列综述: 💞目的:本系列是个人整理为了秋招算法的,整理期间苛求每个知识点,平衡理解简易度与深入程度。 🥰来源:材料主要源于代码随想录进行的,每个算法代码参考leetcode高赞回答和…

官宣|Apache Flink 1.17 发布公告

Apache Flink PMC(项目管理委员)很高兴地宣布发布 Apache Flink 1.17.0。Apache Flink 是领先的流处理标准,流批统一的数据处理概念在越来越多的公司中得到认可。得益于我们出色的社区和优秀的贡献者,Apache Flink 在 Apache 社区…

STM32F407控制微型推拉式电磁铁(通过继电器)

1、继电器 继电器相当于开关,单片机通过io口高低电平的控制来控制继电器的开闭。采用继电器的好处除了能够用低电压控制高电压(如32单片机控制220V的电压)外,还可以防止电流反冲,弄烧单片机。 本文采用3.3v的电磁铁&am…

三、MyBatis核心配置文件详解

核心配置文件中的标签必须按照固定的顺序(有的标签可以不写,但顺序一定不能乱): properties、settings、typeAliases、typeHandlers、objectFactory、objectWrapperFactory、reflectorFactory、plugins、environments、databaseIdProvider、mappers 一、…

b01lers(php.galf)

目录 前文 正文 前文 <?phpclass A{public $codeNULL;public $argsNULL;public function __construct($code,$argsNULL){$this->code$code;$this->args$args;print_r("2333") ;} public function __invoke($code,$args){echo $code;print_r("执行inv…

记一次若依后台管理系统渗透

前言 最近客户开始hw前的风险排查&#xff0c;让我们帮他做个渗透测试&#xff0c;只给一个单位名称。通过前期的信息收集&#xff0c;发现了这个站点&#xff1a; 没有验证码&#xff0c;再加上这个图标&#xff0c;吸引了我注意&#xff1a; 从弱口令开始 若依默认口令为ad…

Android 12.0 Settings主页面去掉FocusRecyclerView相关功能

1.前言 在12.0的系统rom产品定制化开发中,在系统Settings主页面的主菜单中,在测试某些功能的时候,比如开启护眼模式和改变系统密度会在主菜单第一项的网络菜单头部增加 自定义您的设备和设置护眼模式时间安排 等等相关的设置模块 这对于菜单布局显示相当不美观,所以根据系…

机器学习---降维算法

知其然知其所以然【写在前面】主成分分析&#xff08;PCA&#xff09;原理部分代码部分可视化部分线性判别分析&#xff08;LDA&#xff09;原理部分代码部分可视化部分独立成分分析&#xff08;ICA&#xff09;原理部分代码部分可视化部分t-SNE降维算法原理部分代码部分可视化…

请求响应数据?Controler层注解!

目录1. 请求1.1概述1.2 简单参数1.2.1 原始方式1.2.2 SpringBoot方式1.2.3 参数名不一致1.3 实体参数1.3.1 简单实体对象1.3.2 复杂实体对象1.4 数组集合参数1.4.1 数组1.4.2 集合1.5 日期参数1.6 JSON参数1.7 路径参数2. 响应2.1 ResponseBody2.2 统一响应结果1. 请求 1.1概述…

Hive数据仓库简介

文章目录Hive数据仓库简介一、数据仓库简介1. 什么是数据仓库2. 数据仓库的结构2.1 数据源2.2 数据存储与管理2.3 OLAP服务器2.4 前端工具3. 数据仓库的数据模型3.1 星状模型3.2 雪花模型二、Hive简介1. 什么是Hive2. Hive的发展历程3. Hive的本质4. Hive的优缺点4.1 优点4.2 缺…

Vue2响应式原理

目录 Object.defineProperty() 监听对象中的简单数据类型 监听对象中的对象(可以深层) 监听对象中的数组 借鉴的帖子&#xff1a;Object.defineProperty方法&#xff08;详解&#xff09;_objectdefineproperty_搞前端的小菜的博客-CSDN博客 b站视频讲解&#xff1a;Vue2响…

学习 Python 之 Pygame 开发魂斗罗(十三)

学习 Python 之 Pygame 开发魂斗罗&#xff08;十三&#xff09;继续编写魂斗罗1. 创建敌人2类2. 编写敌人2类的draw()函数3. 编写敌人越界消失函数4. 编写敌人开火函数5. 把敌人2加入地图进行测试继续编写魂斗罗 在上次的博客学习 Python 之 Pygame 开发魂斗罗&#xff08;十…

Adapter基础讲解

这一节我们要讲的UI控件都是跟Adapter(适配器)打交道的,了解并学会使用Adapter很重要, Adapter是用来帮助填充数据的中间桥梁,简单来说就是:将各种数据以合适的形式显示到view上,提供 给用户看! 1.MVC模式的简单理解 在开始学习Adapter之前我们要来了解下这个MVC模式概…
最新文章