人脸关键点检测9——DAN

《Deep Alignment Network:A convolutional neural network for robust face alignment》
  • CVPR-2017,Marek Kowalski et al,DAN

DAN(Deep Alignment Network),与以往级联神经网络输入的是图像的某一部分不同,DAN各阶段网络的输入均为整张图片。当网络均采用整张图片作为输入时,DAN可以有效的克服头部姿态以及初始化带来的问题,从而得到更好的检测效果。之所以DAN能将整张图片作为输入,是因为其加入了关键点热图(Landmark Heatmaps),关键点热图的使用是本文的重要创新点。
注:速度略差,但想法很好。

创新点:

1)与以往的级联模型不同,网络模型输入是整张人脸图,可获取更多信息。
2)关键点热图。

DAN的基本框架:

这里写图片描述
迭代处理的框架,可以利用上一帧的预测结果来预测下一帧 Landmark 的位置。
(1)初始
输入灰度图I以及标准关键点模板 S 0 S{_{0}} S0,预测得到新的关键点位置 S 1 S{_{1}} S1
注: S 0 S{_{0}} S0可取所有训练样本的平均关键点。
其中“FEED FORWARD NN”结构如下,输出136个值用于预测68个关键点:
这里写图片描述
新的关键点位置会送入 “CONNECTION LAYERS”,该网络结构如下:
这里写图片描述
首先计算一个 S 1 S{_{1}} S1 S 0 S{_{0}} S0的相似变换矩阵 T 1 T{_{1}} T1
注:这里不采用仿射变换,是为了防止局部畸变,而是采用了相似变换。
通过 T 1 T{_{1}} T1我们可以对图像进行矫正得到 T 1 ( I ) T{_{1}}(I) T1(I),同时对关键点进行变换得到关键点热图 H 1 H{_{1}} H1
特征图 F 1 F{_{1}} F1是通过“FEED FORWARD NN”的fc1层特征进一步得到的。
其中关键点热图通过下式计算得到:(其实就是一个中心衰减,关键点处值最大,越远则值越小)。

其中特征图的计算如下:使用dense layer使fc1层输出单元为3136,使用ReLU激活,reshape为5656,然后上采样到112112,和输入图像一样大。(之所以一开始不直接生成112112,是因为实验发现提升不大但计算量会增大比较多。)目的:人为给CNN增加上一阶段信息。
注:图像的转换使用双线性插值。
(2)初始迭代
每次迭代输入 T N ( I ) T{_{N}}(I) TN(I) H N H{_{N}} HN F N F{_{N}} FN,它们的维度均为112
112。然后计算新的 S t + 1 S{_{t+1}} St+1
需要注意的是,由于图像进行了相似变换,因此为了和最初的输入图像相匹配,需要做如下矫正:
这里写图片描述
IBUG数据集上的第一阶段输出:
这里写图片描述
从图中发现,DAN要做的“变换”,就是把图片给矫正了。那么DAN对姿态变换具有很好的适应能力,或许就得益于这个“变换”。


实现过程:

数据准备: 扩大1.3倍人脸框裁剪图片,同时计算平均人脸关键点。
输入图像进行灰度化处理
网络学习的是已知标准关键点的偏移量。
第一阶段:
输入灰度图 -> 前向传播 -> 直到fc2层输出136个偏移值,再加上标准的平均关键点来计算损失(L2损失/双眼间距离做归一化)。
第二阶段:
1)计算相似变换矩阵,根据标准关键点和第一阶段输出的关键点。
2)对原始图像和第一阶段输出的关键点应用相似变换。
3)对变换后的输出关键点计算热图
4) 对第一阶段的fc1层增加一个全连接层,输出为56x56,之后reshape为[None,56,56,1],上采样到112x112大小得到特征图。
将变换后的原始图像、热图及特征图进行concat连接,作为第二阶段的输入,不断迭代,输出为fc2。
损失计算:fc2+变换后的关键点来作为最后的预测关键点,因为输入时进行了相似变换,
因此计算损失时,需要对预测的关键点进行反变换回原图。
…综上:可以增加N个阶段,每个阶段都需要关键点对齐操作。

热图的计算:
Python代码:

import os
import numpy as np
import itertools
import cv2
from matplotlib import pyplot as plt
np.set_printoptions(suppress=True,threshold=np.NaN)

HalfSize = 8   #半径的尺寸
IMGSIZE = 112	#图像的尺寸
#定义16*16*2的矩阵,0为y,从上到下[-8,7];1为x,从左到右[-8,7]。
Offsets = np.array(list(itertools.product(range(-HalfSize, HalfSize), \
    range(-HalfSize, HalfSize))), dtype=np.int32).reshape((16,16,2))
#限定landmark的值域范围
def clip_by_value(landmark):
    h,w = landmark.shape
    for i in range(h):
        for j in range(w):
            if landmark[i][j] <HalfSize:
                landmark[i][j] = HalfSize
            if landmark[i][j] > (IMGSIZE-1-HalfSize):
                landmark[i][j] = (IMGSIZE-1-HalfSize)
    return landmark
#针对每个点位,画点热图
def draw_landmark(Point):
    iniLandmark = Point.astype(np.int)	#对点值取整
    locations = Offsets + iniLandmark		#移动16*16区域到关键点区域
    dxdy = Point - iniLandmark.astype(np.float) 		#关键点的偏移量
    offsetsSubPix = Offsets.astype(np.float) - dxdy	#公式中的(x,y)- Si
    vals = 1/(1 + np.linalg.norm(offsetsSubPix,axis =2)) 	#公式H(x,y)
    img = draw_point(locations,vals)
    return img
#在图像上画点
def draw_point(location,vals):
    img = np.zeros((IMGSIZE,IMGSIZE,1),np.float32)
    h,w,c = location.shape
    for i in range(h):
        for j in range(w):
            img[location[i][j][0]][location[i][j][1]] = vals[i][j] 
    return img

if __name__ == '__main__':
    aa = '0.162805  0.337997  0.160961  0.434602  *** *** 0.693376'
    landmark = np.array(aa.strip().split()).astype(np.float32)*112
    land = landmark.reshape((-1,2))[:,::-1]	#reshape成68*2,并进行x和y转换
    land = clip_by_value(land)
    #定义68个点位热图
    R = np.zeros((68,112,112,1)).astype(np.float32)   
    for i in range(68):    
        R[i] = draw_landmark(land[i])
L = np.zeros((112,112)).astype(np.float32)  
#选取68个热图中值最大的进行保留
    for i in range(112):
        for j in range(112):
            maxP = 0
            for k in range(68):
                if maxP < R[k][i][j][:]:
                    maxP = R[k][i][j][:]
            L[i][j] = maxP
    np.save('p2.6.np',L)
    plt.imshow(L)
    plt.show()
    
    #pic = L.reshape((112,112,1))
    #cv2.imshow('aaa',pic)
    #cv2.waitKey(0)
最终的效果:

这里写图片描述

参考:https://blog.csdn.net/shuzfan/article/details/77839176


注:博众家之所长,集群英之荟萃。
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Peanut_范

您的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值