Keras搭建mtcnn人脸检测平台

1、mtcnn

MTCNN,英文全称是Multi-task convolutional neural network,中文全称是多任务卷积神经网络,该神经网络将人脸区域检测与人脸关键点检测放在了一起。总体可分为P-Net、R-Net、和O-Net三层网络结构。
在这里插入图片描述

2、实现流程

1、构建图像金字塔

为了检测到不同尺寸大小的人脸,在进入P-Net之前,我们要首先对图像进行金字塔操作。根据设定的min_face_size尺寸,将图像按照一定的尺寸缩小,每次将图像缩小到前级图像面积的一半,形成scales列表,直至边长小于min_face_size,此时可以得到一组不同尺寸的输入图像。
在这里插入图片描述
实现代码如下,当一个图片输入的时候,会缩放为不同大小的图片,但是缩小后的长宽最小不可以小于12:

def calculateScales(img):
    copy_img = img.copy()
    pr_scale = 1.0
    h,w,_ = copy_img.shape
    if min(w,h)>500:
        pr_scale = 500.0/min(h,w)
        w = int(w*pr_scale)
        h = int(h*pr_scale)
    elif max(w,h)<500:
        pr_scale = 500.0/max(h,w)
        w = int(w*pr_scale)
        h = int(h*pr_scale)

    scales = []
    factor = 0.709
    factor_count = 0
    minl = min(h,w)
    while minl >= 12:
        scales.append(pr_scale*pow(factor, factor_count))
        minl *= factor
        factor_count += 1
    return scales

1、Pnet输入图像金子塔
2、输入12×12固定。 将原图随机剪裁(不是缩放)成不同尺度,random(12,min(height,width)/2),在resize到12×12,也就是固定为12×12,但是输入图像是随机大小。

  • 个人感觉,如果针对迁移学习,则可用1,直接输入图像金字塔
  • 如果从头训练自己的网络,则根据方式2,PNet的训练数据主要由4部分组成,包括正label数据(IOU>0.65,面部轮廓特值为0),负label数据(IOU<0.4,面部轮廓特值为0,回归框值为0),中间数据(0.4<IOU<0.65,面部轮廓特值为0),面部轮廓数据(回归框值为0)。
  • 在这里插入图片描述

疑问:为什么要做图像金字塔?

由于原始图像中,存在大小不同的脸,为了在统一尺度下检测人脸,进入网络训练之前,就要将原始图片缩放至不同尺度。以增强网络对不同尺寸大小人脸的鲁棒性。

2、Pnet(Proposal Network)

Pnet的全称为Proposal Network,其基本的构造是一个全卷积网络。对上一步构建完成的图像金字塔,通过一个FCN进行初步特征提取与标定边框.
由于PNet的输入大小是12×12,因此单纯的PNet只能检测12×12大小的图像中的人脸。这显然是不实际的。为了让PNet能够检测多尺度范围的人脸,有必要对原图像进行缩放。这就是引入图像金字塔的原因。
在这里插入图片描述
在这里插入图片描述

Pnet的网络比较简单,实现代码如下

def create_Pnet(weight_path):
    input = Input(shape=[None, None, 3])

    x = Conv2D(10, (3, 3), strides=1, padding='valid', name='conv1')(input)
    x = PReLU(shared_axes=[1,2],name='PReLU1')(x)
    x = MaxPool2D(pool_size=2)(x)

    x = Conv2D(16, (3, 3), strides=1, padding='valid', name='conv2')(x)
    x = PReLU(shared_axes=[1,2],name='PReLU2')(x)

    x = Conv2D(32, (3, 3), strides=1, padding='valid', name='conv3')(x)
    x = PReLU(shared_axes=[1,2],name='PReLU3')(x)

    classifier = Conv2D(2, (1, 1), activation='softmax', name='conv4-1')(x)
    # 无激活函数,线性。
    bbox_regress = Conv2D(4, (1, 1), name='conv4-2')(x)

    model = Model([input], [classifier, bbox_regress])
    model.load_weights(weight_path, by_name=True)
    return model

将图像缩放完毕后,就可以进行计算了。对于 x * y 的输入,将产生大小为[(x-10)/2]*[(y-10)/2]的输出.

在mtcnn算法中的Pnet是为了得到一批人脸框。过程如下
针对金字塔中每张图,网络forward计算后都得到了人脸得分以及人脸框回归的结果。人脸分类得分是两个通道的三维矩阵m* m* 2,其实对应在网络输入图片上m* m个12* 12的滑框,结合当前图片在金字塔图片中的缩放scale,可以推算出每个滑框在原始图像中的具体坐标。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

首先要根据得分进行筛选,得分低于阈值的滑框,排除。
当金字塔中所有图片处理完后,再利用nms对汇总的滑框进行合并,然后利用最后剩余的滑框对应的Bbox结果转换成原始图像中像素坐标,也就是得到了人脸框的坐标。
Pnet最终能够得到了一批人脸框

输入是一个1212大小的图片,所以训练前需要把生成的训练数据(通过生成bounding box,然后把该bounding box 剪切成1212大小的图片),转换成12123的结构。

  1. 通过10个333的卷积核,22的Max Pooling(stride=2)操作,生成10个55的特征图
  2. 通过16个3310的卷积核,生成16个3*3的特征图
  3. 通过32个3316的卷积核,生成32个1*1的特征图。
  4. 针对32个11的特征图,可以通过2个1132的卷积核,生成2个11的
  5. 征图用于分类;4个1132的卷积核,生成4个1*1的特征图用于回归框判断。

Pnet有两个输出,classifier用于判断这个网格点上的框的可信度,bbox_regress表示框的位置。

bbox_regress并不代表这个框在图片上的真实位置,如果需要将bbox_regress映射到真实图像上,还需要进行一次解码过程。
注意:
没有将网络输出做全连接层展开,而是直接将卷积层结果作为输出。原因是在应用端,每一个输入图像都是尺度不一的。如果增加全连接层,当尺度不一的图像输入pnet时,全连接层的特征向量也会不等长。而利用卷积层能将不同size的输入图像最终输出为同一尺寸的特征图(1x1x?)
代码中,激活函数为PReLU(Parametric Rectified Linear Unit),其为ReLU的改进版,即带了参数的ReLU,该参数a会随着数据变化,而当a为定值时,则变身为Leaky ReLU。
在这里插入图片描述

解码过程利用detect_face_12net函数实现,其实现步骤如下(需要配合代码理解):
1、判断哪些网格点的置信度较高,即该网格点内存在人脸。
2、记录该网格点的x,y轴。
3、利用函数计算bb1和bb2,分别代表图中框的左上角基点和右下角基点,二者之间差了11个像素,堆叠得到boundingbox 。
4、利用bbox_regress计算解码结果,解码公式为boundingbox = boundingbox + offset12.0scale。

虽然网络定义的时候input的size是12 * 12* 3,由于Pnet只有卷积层,我们可以直接将resize后的图像给网络进行前传,只是得到的结果不是1* 1* 2和1* 1* 4,而是m* m* 2和m* m* 4。这样就不用先从resize的图上滑动截取各种12* 12* 3的图进入网络,而是一次性送入通过卷积,在根据结果回推每个结果对应的12* 12的图在输入图的什么位置。利用的就是卷积来代替原来的滑动窗口。
然后利用nms非极大值抑制,对剩下的滑框进行合并

简单理解就是Pnet的输出就是将整个网格分割成若干个网格点,;然后每个网格点初始状态下是一个11x11的框,这个由第三步得到;之后bbox_regress代表 每个网格点确定的框 的 左上角基点和右下角基点 的偏移情况。

#-------------------------------------#
#   对pnet处理后的结果进行处理
#-------------------------------------#
def detect_face_12net(cls_prob,roi,out_side,scale,width,height,threshold):
    cls_prob = np.swapaxes(cls_prob<
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值