基于MTCNN的人脸自动对齐技术原理及其Tensorflow实现测试

脸识别是计算机视觉研究领域的一个热点。而人脸识别包含了诸多步骤,其实现技术流程如下图所示(摘自http://www.techshino.com/upfiles/images/%E4%BA%BA%E8%84%B8%E8%AF%86%E5%88%AB%E6%8A%80%E6%9C%AF%E6%B5%81%E7%A8%8B(2).png):

https://github.com/AITTSMD/MTCNN-Tensorflow



       在上述过程中,人脸检测是非常关键的一步,特别是在大多数应用场景条件下,监控视频图像中问题包含了自然场景,而针对此类的应用,首要的是实现人脸检测。


      在人脸检测实现过程中,有个著名的MTCNN模型。该模型见论文:mtcnn:Joint Face Detection and Alignment using Multi-task Cascaded Convolutional Neural Networks。其原理如下(图片摘自:https://img-blog.csdn.net/20160927151542441):



从上述原理图可以看出,该模型由三个步骤组成:

步骤一:P-NET,该步骤主要生成了一堆候选区域的边框,并采用NMS(非极大值)机制进行相应的合并。这与目标检测过程中的原理类似。

步骤二:R-NET,即对步骤一的结果再进一步细划,得到更精细的候选区域。

步骤三:O-NET,输出结果。(人脸边框和特征点位置)


上述三个模型的具体卷积细节原理如下图所示:



         下面基于Tensorflow进行实验,其中MTCNN相应的代码如下所示:

[python]  view plain  copy
  1. class PNet(Network):  
  2.     def setup(self):  
  3.         (self.feed('data'#pylint: disable=no-value-for-parameter, no-member  
  4.              .conv(331011, padding='VALID', relu=False, name='conv1')  
  5.              .prelu(name='PReLU1')  
  6.              .max_pool(2222, name='pool1')  
  7.              .conv(331611, padding='VALID', relu=False, name='conv2')  
  8.              .prelu(name='PReLU2')  
  9.              .conv(333211, padding='VALID', relu=False, name='conv3')  
  10.              .prelu(name='PReLU3')  
  11.              .conv(11211, relu=False, name='conv4-1')  
  12.              .softmax(3,name='prob1'))  
  13.   
  14.         (self.feed('PReLU3'#pylint: disable=no-value-for-parameter  
  15.              .conv(11411, relu=False, name='conv4-2'))  
  16.           
  17. class RNet(Network):  
  18.     def setup(self):  
  19.         (self.feed('data'#pylint: disable=no-value-for-parameter, no-member  
  20.              .conv(332811, padding='VALID', relu=False, name='conv1')  
  21.              .prelu(name='prelu1')  
  22.              .max_pool(3322, name='pool1')  
  23.              .conv(334811, padding='VALID', relu=False, name='conv2')  
  24.              .prelu(name='prelu2')  
  25.              .max_pool(3322, padding='VALID', name='pool2')  
  26.              .conv(226411, padding='VALID', relu=False, name='conv3')  
  27.              .prelu(name='prelu3')  
  28.              .fc(128, relu=False, name='conv4')  
  29.              .prelu(name='prelu4')  
  30.              .fc(2, relu=False, name='conv5-1')  
  31.              .softmax(1,name='prob1'))  
  32.   
  33.         (self.feed('prelu4'#pylint: disable=no-value-for-parameter  
  34.              .fc(4, relu=False, name='conv5-2'))  
  35.   
  36. class ONet(Network):  
  37.     def setup(self):  
  38.         (self.feed('data'#pylint: disable=no-value-for-parameter, no-member  
  39.              .conv(333211, padding='VALID', relu=False, name='conv1')  
  40.              .prelu(name='prelu1')  
  41.              .max_pool(3322, name='pool1')  
  42.              .conv(336411, padding='VALID', relu=False, name='conv2')  
  43.              .prelu(name='prelu2')  
  44.              .max_pool(3322, padding='VALID', name='pool2')  
  45.              .conv(336411, padding='VALID', relu=False, name='conv3')  
  46.              .prelu(name='prelu3')  
  47.              .max_pool(2222, name='pool3')  
  48.              .conv(2212811, padding='VALID', relu=False, name='conv4')  
  49.              .prelu(name='prelu4')  
  50.              .fc(256, relu=False, name='conv5')  
  51.              .prelu(name='prelu5')  
  52.              .fc(2, relu=False, name='conv6-1')  
  53.              .softmax(1, name='prob1'))  
  54.   
  55.         (self.feed('prelu5'#pylint: disable=no-value-for-parameter  
  56.              .fc(4, relu=False, name='conv6-2'))  
  57.   
  58.         (self.feed('prelu5'#pylint: disable=no-value-for-parameter  
  59.              .fc(10, relu=False, name='conv6-3'))  
  60.   
  61. # 创建MTCNN模型  
  62. def create_mtcnn(sess, model_path):  
  63.     if not model_path:  
  64.         model_path,_ = os.path.split(os.path.realpath(__file__))  
  65.   
  66.     with tf.variable_scope('pnet'):  
  67.         data = tf.placeholder(tf.float32, (None,None,None,3), 'input')  
  68.         pnet = PNet({'data':data})  
  69.         pnet.load(os.path.join(model_path, 'det1.npy'), sess)  
  70.     with tf.variable_scope('rnet'):  
  71.         data = tf.placeholder(tf.float32, (None,24,24,3), 'input')  
  72.         rnet = RNet({'data':data})  
  73.         rnet.load(os.path.join(model_path, 'det2.npy'), sess)  
  74.     with tf.variable_scope('onet'):  
  75.         data = tf.placeholder(tf.float32, (None,48,48,3), 'input')  
  76.         onet = ONet({'data':data})  
  77.         onet.load(os.path.join(model_path, 'det3.npy'), sess)  
  78.           
  79.     pnet_fun = lambda img : sess.run(('pnet/conv4-2/BiasAdd:0''pnet/prob1:0'), feed_dict={'pnet/input:0':img})  
  80.     rnet_fun = lambda img : sess.run(('rnet/conv5-2/conv5-2:0''rnet/prob1:0'), feed_dict={'rnet/input:0':img})  
  81.     onet_fun = lambda img : sess.run(('onet/conv6-2/conv6-2:0''onet/conv6-3/conv6-3:0''onet/prob1:0'), feed_dict={'onet/input:0':img})  
  82.     return pnet_fun, rnet_fun, onet_fun  

       在LFW数据集进行测试,发现结果还是相当的好。测试的结果如下:

     

     


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值