人脸识别经典网络-MTCNN(含Python源码实现)

打印 上一主题 下一主题

主题 651|帖子 651|积分 1953

人脸检测-mtcnn

本文参加新星筹划人工智能赛道:https://bbs.csdn.net/topics/613989052

  
1. 人脸检测

1.1 人脸检测概述

人脸检测或者识别,都是根据人的脸部特性信息举行身份识别的一种生物识别术。用摄像机或摄像头采集含有人脸的图像或视频流,并自动在图像中检测和跟踪人脸,进而对检测到的人脸举行脸部识别的一系列相干技术,通常也叫做人像识别、面部识别。
1.2 人脸检测的难点

人脸识别被认为是生物特性识别领域乃至人工智能领域最困难的研究课题之一。人脸识别的难点是由于人脸作为生物特性的特点而导致的,难点重要包括以下部门:


  • 相似性:从人脸的构造上来看,个体之间的人脸构造区别不大,乃至人脸器官的构造都很相似。这种相似性对于利用人脸举行定位是能偶提供很大的便利的,但同时对于个体的区分确实难的。
  • 易变性:抛去构造仅仅关注外形的话,人脸的外形又黑白常多变的,面部表情多变,而在不同观察角度,人脸的视觉图像也相差很大,另外,人脸识别还受光照条件(例如白天和夜晚,室内和室外等)、人脸的很多遮盖物(例如口罩、墨镜、头发、胡须等)、年岁等多方面因素的影响。
在人脸识别中,第一类的变革是应该放大而作为区分个体的标准的,而第二类的变革应该消除,由于它们可以代表同一个个体。通常称第一类变革为类间变革(inter-class difference),而称第二类变革为类内变革(intra-class difference)。对于人脸,类内变革往往大于类间变革,从而使在受类内变革干扰的环境下利用类间变革区分个体变得异常困难。
1.3 人脸检测的应用场景

人脸识别重要用于身份识别。
由于视频监控正在快速遍及,众多的视频监控应用迫切需要一种远距离、用户非共同状态下的快速身份识别技术,以求远距离快速确认人员身份,实现智能预警。人脸识别技术无疑是最佳的选择,接纳快速人脸检测技术可以从监控视频图象中实时查找人脸,并与人脸数据库举行实时比对,从而实现快速身份识别。
人脸识别产品已广泛应用于金融、司法、部队、公安、边检、政府、航天、电力、工厂、教诲、医疗及众多企奇迹单元等领域。随着技术的进一步成熟和社会认同度的提高,人脸识别技术将应用在更多的领域。
1、企业、住宅安全和管理。如人脸识别门禁考勤系统,人脸识别防盗门等。
2、电子护照及身份证。
3、公安、司法和刑侦。如利用人脸识别系统和网络,在全国范围内搜捕逃犯。
4、自助服务。
5、信息安全。如手机、盘算机登录、电子政务和电子商务。
2. mtcnn

2.1 mtcnn概述

MTCNN,英文全称是Multi-task convolutional neural network,中文全称是多任务卷积神经网络,该神经网络将人脸区域检测与人脸关键点检测放在了一起。
从工程实践上,MTCNN是一种检测速率和准确率都很不错的算法,算法的推断流程有一定的启示作用。

2.2 mtcnn的网络结构

mtcnn从整体上划分分为P-Net、R-Net、和O-Net三层网络结构。各层的作用直观上感受如下图所示:

一次mtcnn对于局部信息的运作流程如下描述:

  • 由原始图片和PNet生成预测的bounding boxes。
  • 输入原始图片和PNet生成的bounding box,通过RNet,生成校正后的bounding box。
  • 输入原始图片和RNet生成的bounding box,通过ONet,生成校正后的bounding box和人脸面部轮廓关键点。
当整个图片的局部信息都举行处理之后,就能得到所有的局部人脸信息,或有或无,举行校正处理后就可以得到最后的结果。

P-Net、R-Net、O-Net的网络结构如下图所示:

分析:
MTCNN重要包括三层网络,

  • 第一层P-Net将颠末卷积,池化操纵后输出分类(对应像素点是否存在人脸)和回归(回归box)结果。
  • 第二层网络将第一层输出的结果使用非极大克制(NMS)往复除高度重合的候选框,并将这些候选框放入R-Net中举行精致的操纵,拒绝大量错误框,再对回归框做校正,并使用NMS去除重合框,输出分支同样两个分类和回归。
  • 最后将R-Net输出认为是人脸的候选框输入到O-Net中再一次举行精致操纵,拒绝掉错误的框,此时输出分支包罗三个分类:
    a. 是否有人脸:2个输出;
    b. 回归:回归得到的框的起始点(或中心点)的xy坐标和框的长宽,4个输出;
    c. 人脸特性点定位:5个人脸特性点的xy坐标,10个输出。
   三段网络都有NMS,但是所设阈值不同。
  2.3 图像金字塔

mtcnn的输入尺度是任意大小的,那么输入是如何处理的呢?
首先对图片举行Resize操纵,将原始图像缩放成不同的尺度,生成图像金字塔。然后将不同尺度的图像送入到这三个子网络中举行训练,目标是为了可以检测到不同大小的人脸,从而实现多尺度目标检测。
构建方式是通过不同的缩放系数factor分别对图片的h和w举行缩放,每次缩小为原来的factor大小。
   缩小后的长宽最小不可以小于12。
  

图片中的人脸的尺度有大有小,让识别算法不被目标尺度影响不停是个挑衅。
MTCNN使用了图像金字塔来办理目标多尺度问题,即把原图按照一定的比例(如0.709),多次等比缩放得到多尺度的图片,很像个金字塔。
   为什么这里的缩放因子是0.709,由于缩放的时候若宽高都缩放                                                   1                               2                                            \frac{1}{2}                     21​,那么缩放后的面积就变为了原本面积的                                                   1                               4                                            \frac{1}{4}                     41​,假如考虑总面积缩放为原本的                                                   1                               2                                            \frac{1}{2}                     21​,那么就取                                                               2                                          2                                      ≈                            0.709                                  \frac{\sqrt{2}}{2}\approx 0.709                     22                     ​​≈0.709,这样就达到了将总面积缩放为原本的                                                   1                               2                                            \frac{1}{2}                     21​的目标。
  P-NET的模型是用单尺度(12*12)的图片训练出来的。推理的时候,缩小后的长宽最小不可以小于12。
对多个尺度的输入图像做训练,训练黑白常耗时的。因此通常只在推理阶段使用图像金字塔,提高算法的精度。

图像金字塔是有生成标准的,每次缩放的水平(factor)以及最小的兜底标准(minsize)都是需要合适的设置的,那么能够优化盘算效率的合适的最小人脸尺寸(minsize)和缩放因子(factor)具有什么样的依据?


  • 第一阶段会多次缩放原图得到图片金字塔,目标是为了让缩放后图片中的人脸与P-NET训练时候的图片尺度(                                        12                            p                            x                            ×                            12                            p                            x                                  12px\times 12px                     12px×12px)靠近。
  • 引申优化项:先把图像缩放到一定大小,再通过factor对这个大小举行缩放。可以淘汰盘算量。
   minsize的单元为px
  例:输入图片为                                   1200                         p                         x                         ×                         1200                         p                         x                              1200px\times 1200px                  1200px×1200px,设置缩放后的尺寸靠近训练图片的尺度(                                   12                         p                         x                         ×                         12                         p                         x                              12px\times 12px                  12px×12px)

图像金字塔也有其局限性


  • 生成图像金字塔的过程比力慢。
  • 每种尺度的图片都需要输入进模型,相称于执行了多次的模型推理流程。
2.4 P-Net

P-Net(Proposal Network)的网络结构
网络的输入为预处理中得到的图像金字塔,P-Net中筹划了一个全卷积网络(FCN)对输入的图像金字塔举行特性提取和边框回归。
   全卷积神经网络没有FC全毗连层,这就突破了输入维度的限制,那么其接受的输入尺寸是任意的。
  

在P-Net中,颠末了三次卷积和一次池化(MP:Max Pooling),输入                                   12                         ×                         12                         ×                         3                              12\times 12 \times 3                  12×12×3的尺寸变为了                                   1                         ×                         1                         ×                         32                              1\times 1\times 32                  1×1×32,                                   1                         ×                         1                         ×                         32                              1\times 1\times 32                  1×1×32的向量通过卷积得到了                                   1                         ×                         1                         ×                         2                              1\times 1\times 2                  1×1×2到了人脸的分类结果,相称于图像中的每个                                   12                         ×                         12                              12\times 12                  12×12的区域都会判断一下是否存在人脸,通道数为2,即得到两个值;第二个部门得到(bounding box regrssion)边框回归的结果,由于                                   12                         ×                         12                              12\times 12                  12×12的图像并不能保证,方形框能够完美的框住人脸,以是输出包罗的信息都是误差信息,通道数为4,有4个方面的信息,边框左上角的横坐标的相对偏移信息、边框左上角纵坐标的相对偏移信息、标定框宽度的误差、标定框高度的误差;第三个部门给出了人脸的5个关键点的位置,分别是左眼位置、右眼位置、鼻子位置、嘴巴左位置、嘴巴右位置,每个关键位置使用两个维度体现,故而输出是                                   1                         ×                         1                         ×                         10                              1\times 1\times 10                  1×1×10。
P-Net应用举例
一张                                   70                         ×                         70                              70\times 70                  70×70的图,颠末P网络全卷积后,输出为                                                        70                               −                               2                                      2                                  −                         2                         −                         2                         =                         30                              \frac{70-2}{2} -2 -2 =30                  270−2​−2−2=30,即一个5通道的                                   30                         ×                         30                              30\times 30                  30×30的特性图。这就意味着该图颠末p的一次滑窗操纵,得到                                   30                         ×                         30                         =                         900                              30\times 30=900                  30×30=900个建议框,而每个建议框对应1个置信度与4个偏移量。再经nms把置信度分数大于设定的阈值0.6对应的建议框保存下来,将其对应的边框偏移量经边框回归操纵,得到在原图中的坐标信息,即得到符合P-Net的这些建议框了。之后传给R-Net。
2.5 R-Net

R-Net(Refine Network),从网络图可以看到,该网络结构只是和P-Net网络结构多了一个全毗连层。图片在输入R-Net之前,都需要缩放到24x24x3。网络的输出与P-Net是相同的,R-Net的目标是为了去除大量的非人脸框。

2.6 O-Net

O-Net(Output Network),该层比R-Net层又多了一层卷积层,以是处理的结果会更加精致。输入的图像大小48x48x3,输出包括N个边界框的坐标信息,score以及关键点位置。

   从P-Net到R-Net,再到最后的O-Net,网络输入的图像越来越大,卷积层的通道数越来越多,网络的深度(层数)也越来越深,因此识别人脸的准确率应该也是越来越高的。
  3. 工程实践(基于Keras)

点击此处下载人脸数据集。该数据集有32,203张图片,共有93,703张脸被标记。

MTCNN网络定义,按照上述网络结构完成定义,代码按照P-Net、R-Net、O-Net举行模块化筹划,在mtcnn的网络构建过程中将其整合。mtcnn.py代码如下:
  1. from keras.layers import Conv2D, Input,MaxPool2D, Reshape,Activation,Flatten, Dense, Permute
  2. from keras.layers.advanced_activations import PReLU
  3. from keras.models import Model, Sequential
  4. import tensorflow as tf
  5. import numpy as np
  6. import utils
  7. import cv2
  8. #-----------------------------#
  9. #   粗略获取人脸框
  10. #   输出bbox位置和是否有人脸
  11. #-----------------------------#
  12. def create_Pnet(weight_path):
  13.     input = Input(shape=[None, None, 3])
  14.     x = Conv2D(10, (3, 3), strides=1, padding='valid', name='conv1')(input)
  15.     x = PReLU(shared_axes=[1,2],name='PReLU1')(x)
  16.     x = MaxPool2D(pool_size=2)(x)
  17.     x = Conv2D(16, (3, 3), strides=1, padding='valid', name='conv2')(x)
  18.     x = PReLU(shared_axes=[1,2],name='PReLU2')(x)
  19.     x = Conv2D(32, (3, 3), strides=1, padding='valid', name='conv3')(x)
  20.     x = PReLU(shared_axes=[1,2],name='PReLU3')(x)
  21.     classifier = Conv2D(2, (1, 1), activation='softmax', name='conv4-1')(x)
  22.     # 无激活函数,线性。
  23.     bbox_regress = Conv2D(4, (1, 1), name='conv4-2')(x)
  24.     model = Model([input], [classifier, bbox_regress])
  25.     model.load_weights(weight_path, by_name=True)
  26.     return model
  27. #-----------------------------#
  28. #   mtcnn的第二段
  29. #   精修框
  30. #-----------------------------#
  31. def create_Rnet(weight_path):
  32.     input = Input(shape=[24, 24, 3])
  33.     # 24,24,3 -> 11,11,28
  34.     x = Conv2D(28, (3, 3), strides=1, padding='valid', name='conv1')(input)
  35.     x = PReLU(shared_axes=[1, 2], name='prelu1')(x)
  36.     x = MaxPool2D(pool_size=3,strides=2, padding='same')(x)
  37.     # 11,11,28 -> 4,4,48
  38.     x = Conv2D(48, (3, 3), strides=1, padding='valid', name='conv2')(x)
  39.     x = PReLU(shared_axes=[1, 2], name='prelu2')(x)
  40.     x = MaxPool2D(pool_size=3, strides=2)(x)
  41.     # 4,4,48 -> 3,3,64
  42.     x = Conv2D(64, (2, 2), strides=1, padding='valid', name='conv3')(x)
  43.     x = PReLU(shared_axes=[1, 2], name='prelu3')(x)
  44.     # 3,3,64 -> 64,3,3
  45.     x = Permute((3, 2, 1))(x)
  46.     x = Flatten()(x)
  47.     # 576 -> 128
  48.     x = Dense(128, name='conv4')(x)
  49.     x = PReLU( name='prelu4')(x)
  50.     # 128 -> 2 128 -> 4
  51.     classifier = Dense(2, activation='softmax', name='conv5-1')(x)
  52.     bbox_regress = Dense(4, name='conv5-2')(x)
  53.     model = Model([input], [classifier, bbox_regress])
  54.     model.load_weights(weight_path, by_name=True)
  55.     return model
  56. #-----------------------------#
  57. #   mtcnn的第三段
  58. #   精修框并获得五个点
  59. #-----------------------------#
  60. def create_Onet(weight_path):
  61.     input = Input(shape = [48,48,3])
  62.     # 48,48,3 -> 23,23,32
  63.     x = Conv2D(32, (3, 3), strides=1, padding='valid', name='conv1')(input)
  64.     x = PReLU(shared_axes=[1,2],name='prelu1')(x)
  65.     x = MaxPool2D(pool_size=3, strides=2, padding='same')(x)
  66.     # 23,23,32 -> 10,10,64
  67.     x = Conv2D(64, (3, 3), strides=1, padding='valid', name='conv2')(x)
  68.     x = PReLU(shared_axes=[1,2],name='prelu2')(x)
  69.     x = MaxPool2D(pool_size=3, strides=2)(x)
  70.     # 8,8,64 -> 4,4,64
  71.     x = Conv2D(64, (3, 3), strides=1, padding='valid', name='conv3')(x)
  72.     x = PReLU(shared_axes=[1,2],name='prelu3')(x)
  73.     x = MaxPool2D(pool_size=2)(x)
  74.     # 4,4,64 -> 3,3,128
  75.     x = Conv2D(128, (2, 2), strides=1, padding='valid', name='conv4')(x)
  76.     x = PReLU(shared_axes=[1,2],name='prelu4')(x)
  77.     # 3,3,128 -> 128,12,12
  78.     x = Permute((3,2,1))(x)
  79.     # 1152 -> 256
  80.     x = Flatten()(x)
  81.     x = Dense(256, name='conv5') (x)
  82.     x = PReLU(name='prelu5')(x)
  83.     # 鉴别
  84.     # 256 -> 2 256 -> 4 256 -> 10
  85.     classifier = Dense(2, activation='softmax',name='conv6-1')(x)
  86.     bbox_regress = Dense(4,name='conv6-2')(x)
  87.     landmark_regress = Dense(10,name='conv6-3')(x)
  88.     model = Model([input], [classifier, bbox_regress, landmark_regress])
  89.     model.load_weights(weight_path, by_name=True)
  90.     return model
  91. class mtcnn():
  92.     def __init__(self):
  93.         self.Pnet = create_Pnet('model_data/pnet.h5')
  94.         self.Rnet = create_Rnet('model_data/rnet.h5')
  95.         self.Onet = create_Onet('model_data/onet.h5')
  96.     def detectFace(self, img, threshold):
  97.         #-----------------------------#
  98.         #   归一化,加快收敛速度
  99.         #   把[0,255]映射到(-1,1)
  100.         #-----------------------------#
  101.         copy_img = (img.copy() - 127.5) / 127.5
  102.         origin_h, origin_w, _ = copy_img.shape
  103.         #-----------------------------#
  104.         #   计算原始输入图像
  105.         #   每一次缩放的比例
  106.         #-----------------------------#
  107.         scales = utils.calculateScales(img)
  108.         out = []
  109.         #-----------------------------#
  110.         #   粗略计算人脸框
  111.         #   pnet部分
  112.         #-----------------------------#
  113.         for scale in scales:
  114.             hs = int(origin_h * scale)
  115.             ws = int(origin_w * scale)
  116.             scale_img = cv2.resize(copy_img, (ws, hs))
  117.             inputs = scale_img.reshape(1, *scale_img.shape)
  118.             # 图像金字塔中的每张图片分别传入Pnet得到output
  119.             output = self.Pnet.predict(inputs)
  120.             # 将所有output加入out
  121.             out.append(output)
  122.         image_num = len(scales)
  123.         rectangles = []
  124.         for i in range(image_num):
  125.             # 有人脸的概率
  126.             cls_prob = out[i][0][0][:,:,1]
  127.             # 其对应的框的位置
  128.             roi = out[i][1][0]
  129.             # 取出每个缩放后图片的长宽
  130.             out_h, out_w = cls_prob.shape
  131.             out_side = max(out_h, out_w)
  132.             print(cls_prob.shape)
  133.             # 解码过程
  134.             rectangle = utils.detect_face_12net(cls_prob, roi, out_side, 1 / scales[i], origin_w, origin_h, threshold[0])
  135.             rectangles.extend(rectangle)
  136.         # 进行非极大抑制
  137.         rectangles = utils.NMS(rectangles, 0.7)
  138.         if len(rectangles) == 0:
  139.             return rectangles
  140.         #-----------------------------#
  141.         #   稍微精确计算人脸框
  142.         #   Rnet部分
  143.         #-----------------------------#
  144.         predict_24_batch = []
  145.         for rectangle in rectangles:
  146.             crop_img = copy_img[int(rectangle[1]):int(rectangle[3]), int(rectangle[0]):int(rectangle[2])]
  147.             scale_img = cv2.resize(crop_img, (24, 24))
  148.             predict_24_batch.append(scale_img)
  149.         predict_24_batch = np.array(predict_24_batch)
  150.         out = self.Rnet.predict(predict_24_batch)
  151.         cls_prob = out[0]
  152.         cls_prob = np.array(cls_prob)
  153.         roi_prob = out[1]
  154.         roi_prob = np.array(roi_prob)
  155.         rectangles = utils.filter_face_24net(cls_prob, roi_prob, rectangles, origin_w, origin_h, threshold[1])
  156.         if len(rectangles) == 0:
  157.             return rectangles
  158.         #-----------------------------#
  159.         #   计算人脸框
  160.         #   onet部分
  161.         #-----------------------------#
  162.         predict_batch = []
  163.         for rectangle in rectangles:
  164.             crop_img = copy_img[int(rectangle[1]):int(rectangle[3]), int(rectangle[0]):int(rectangle[2])]
  165.             scale_img = cv2.resize(crop_img, (48, 48))
  166.             predict_batch.append(scale_img)
  167.         predict_batch = np.array(predict_batch)
  168.         output = self.Onet.predict(predict_batch)
  169.         cls_prob = output[0]
  170.         roi_prob = output[1]
  171.         pts_prob = output[2]
  172.         rectangles = utils.filter_face_48net(cls_prob, roi_prob, pts_prob, rectangles, origin_w, origin_h, threshold[2])
  173.         return rectangles
复制代码
当有了mtcnn定义之后,可以利用其作为本身的模块来举行调用,推理,detect.py代码如下:
  1. import cv2
  2. import numpy as np
  3. from mtcnn import mtcnn
  4. img = cv2.imread('img/test1.jpg')
  5. model = mtcnn()
  6. threshold = [0.5,0.6,0.7]  # 三段网络的置信度阈值不同
  7. rectangles = model.detectFace(img, threshold)
  8. draw = img.copy()
  9. for rectangle in rectangles:
  10.     if rectangle is not None:
  11.         W = -int(rectangle[0]) + int(rectangle[2])
  12.         H = -int(rectangle[1]) + int(rectangle[3])
  13.         paddingH = 0.01 * W
  14.         paddingW = 0.02 * H
  15.         crop_img = img[int(rectangle[1]+paddingH):int(rectangle[3]-paddingH), int(rectangle[0]-paddingW):int(rectangle[2]+paddingW)]
  16.         if crop_img is None:
  17.             continue
  18.         if crop_img.shape[0] < 0 or crop_img.shape[1] < 0:
  19.             continue
  20.         cv2.rectangle(draw, (int(rectangle[0]), int(rectangle[1])), (int(rectangle[2]), int(rectangle[3])), (255, 0, 0), 1)
  21.         for i in range(5, 15, 2):
  22.             cv2.circle(draw, (int(rectangle[i + 0]), int(rectangle[i + 1])), 2, (0, 255, 0))
  23. cv2.imwrite("img/out.jpg",draw)
  24. cv2.imshow("test", draw)
  25. c = cv2.waitKey(0)
复制代码
其中,用到的工具类助手如下,实现了非极大值克制已经网络的后处理等过程逻辑。
  1. import sys
  2. from operator import itemgetter
  3. import numpy as np
  4. import cv2
  5. import matplotlib.pyplot as plt
  6. #-----------------------------#
  7. #   计算原始输入图像
  8. #   每一次缩放的比例
  9. #-----------------------------#
  10. def calculateScales(img):
  11.     copy_img = img.copy()
  12.     pr_scale = 1.0
  13.     h,w,_ = copy_img.shape
  14.     # 引申优化项  = resize(h*500/min(h,w), w*500/min(h,w))
  15.     if min(w,h)>500:
  16.         pr_scale = 500.0/min(h,w)
  17.         w = int(w*pr_scale)
  18.         h = int(h*pr_scale)
  19.     elif max(w,h)<500:
  20.         pr_scale = 500.0/max(h,w)
  21.         w = int(w*pr_scale)
  22.         h = int(h*pr_scale)
  23.     scales = []
  24.     factor = 0.709
  25.     factor_count = 0
  26.     minl = min(h,w)
  27.     while minl >= 12:
  28.         scales.append(pr_scale*pow(factor, factor_count))
  29.         minl *= factor
  30.         factor_count += 1
  31.     return scales
  32. #-------------------------------------#
  33. #   对pnet处理后的结果进行处理
  34. #-------------------------------------#
  35. def detect_face_12net(cls_prob,roi,out_side,scale,width,height,threshold):
  36.     cls_prob = np.swapaxes(cls_prob, 0, 1)
  37.     roi = np.swapaxes(roi, 0, 2)
  38.     stride = 0
  39.     # stride略等于2
  40.     if out_side != 1:
  41.         stride = float(2*out_side-1)/(out_side-1)
  42.     (x,y) = np.where(cls_prob>=threshold)
  43.     boundingbox = np.array([x,y]).T
  44.     # 找到对应原图的位置
  45.     bb1 = np.fix((stride * (boundingbox) + 0 ) * scale)
  46.     bb2 = np.fix((stride * (boundingbox) + 11) * scale)
  47.     # plt.scatter(bb1[:,0],bb1[:,1],linewidths=1)
  48.     # plt.scatter(bb2[:,0],bb2[:,1],linewidths=1,c='r')
  49.     # plt.show()
  50.     boundingbox = np.concatenate((bb1,bb2),axis = 1)
  51.    
  52.     dx1 = roi[0][x,y]
  53.     dx2 = roi[1][x,y]
  54.     dx3 = roi[2][x,y]
  55.     dx4 = roi[3][x,y]
  56.     score = np.array([cls_prob[x,y]]).T
  57.     offset = np.array([dx1,dx2,dx3,dx4]).T
  58.     boundingbox = boundingbox + offset*12.0*scale
  59.    
  60.     rectangles = np.concatenate((boundingbox,score),axis=1)
  61.     rectangles = rect2square(rectangles)
  62.     pick = []
  63.     for i in range(len(rectangles)):
  64.         x1 = int(max(0     ,rectangles[i][0]))
  65.         y1 = int(max(0     ,rectangles[i][1]))
  66.         x2 = int(min(width ,rectangles[i][2]))
  67.         y2 = int(min(height,rectangles[i][3]))
  68.         sc = rectangles[i][4]
  69.         if x2>x1 and y2>y1:
  70.             pick.append([x1,y1,x2,y2,sc])
  71.     return NMS(pick,0.3)
  72. #-----------------------------#
  73. #   将长方形调整为正方形
  74. #-----------------------------#
  75. def rect2square(rectangles):
  76.     w = rectangles[:,2] - rectangles[:,0]
  77.     h = rectangles[:,3] - rectangles[:,1]
  78.     l = np.maximum(w,h).T
  79.     rectangles[:,0] = rectangles[:,0] + w*0.5 - l*0.5
  80.     rectangles[:,1] = rectangles[:,1] + h*0.5 - l*0.5
  81.     rectangles[:,2:4] = rectangles[:,0:2] + np.repeat([l], 2, axis = 0).T
  82.     return rectangles
  83. #-------------------------------------#
  84. #   非极大抑制
  85. #-------------------------------------#
  86. def NMS(rectangles,threshold):
  87.     if len(rectangles)==0:
  88.         return rectangles
  89.     boxes = np.array(rectangles)
  90.     x1 = boxes[:,0]
  91.     y1 = boxes[:,1]
  92.     x2 = boxes[:,2]
  93.     y2 = boxes[:,3]
  94.     s  = boxes[:,4]
  95.     area = np.multiply(x2-x1+1, y2-y1+1)
  96.     I = np.array(s.argsort())
  97.     pick = []
  98.     while len(I)>0:
  99.         xx1 = np.maximum(x1[I[-1]], x1[I[0:-1]]) #I[-1] have hightest prob score, I[0:-1]->others
  100.         yy1 = np.maximum(y1[I[-1]], y1[I[0:-1]])
  101.         xx2 = np.minimum(x2[I[-1]], x2[I[0:-1]])
  102.         yy2 = np.minimum(y2[I[-1]], y2[I[0:-1]])
  103.         w = np.maximum(0.0, xx2 - xx1 + 1)
  104.         h = np.maximum(0.0, yy2 - yy1 + 1)
  105.         inter = w * h
  106.         o = inter / (area[I[-1]] + area[I[0:-1]] - inter)
  107.         pick.append(I[-1])
  108.         I = I[np.where(o<=threshold)[0]]
  109.     result_rectangle = boxes[pick].tolist()
  110.     return result_rectangle
  111. #-------------------------------------#
  112. #   对Rnet处理后的结果进行处理
  113. #-------------------------------------#
  114. def filter_face_24net(cls_prob,roi,rectangles,width,height,threshold):
  115.    
  116.     prob = cls_prob[:,1]
  117.     pick = np.where(prob>=threshold)
  118.     rectangles = np.array(rectangles)
  119.     x1  = rectangles[pick,0]
  120.     y1  = rectangles[pick,1]
  121.     x2  = rectangles[pick,2]
  122.     y2  = rectangles[pick,3]
  123.    
  124.     sc  = np.array([prob[pick]]).T
  125.     dx1 = roi[pick,0]
  126.     dx2 = roi[pick,1]
  127.     dx3 = roi[pick,2]
  128.     dx4 = roi[pick,3]
  129.     w   = x2-x1
  130.     h   = y2-y1
  131.     x1  = np.array([(x1+dx1*w)[0]]).T
  132.     y1  = np.array([(y1+dx2*h)[0]]).T
  133.     x2  = np.array([(x2+dx3*w)[0]]).T
  134.     y2  = np.array([(y2+dx4*h)[0]]).T
  135.     rectangles = np.concatenate((x1,y1,x2,y2,sc),axis=1)
  136.     rectangles = rect2square(rectangles)
  137.     pick = []
  138.     for i in range(len(rectangles)):
  139.         x1 = int(max(0     ,rectangles[i][0]))
  140.         y1 = int(max(0     ,rectangles[i][1]))
  141.         x2 = int(min(width ,rectangles[i][2]))
  142.         y2 = int(min(height,rectangles[i][3]))
  143.         sc = rectangles[i][4]
  144.         if x2>x1 and y2>y1:
  145.             pick.append([x1,y1,x2,y2,sc])
  146.     return NMS(pick,0.3)
  147. #-------------------------------------#
  148. #   对onet处理后的结果进行处理
  149. #-------------------------------------#
  150. def filter_face_48net(cls_prob,roi,pts,rectangles,width,height,threshold):
  151.    
  152.     prob = cls_prob[:,1]
  153.     pick = np.where(prob>=threshold)
  154.     rectangles = np.array(rectangles)
  155.     x1  = rectangles[pick,0]
  156.     y1  = rectangles[pick,1]
  157.     x2  = rectangles[pick,2]
  158.     y2  = rectangles[pick,3]
  159.     sc  = np.array([prob[pick]]).T
  160.     dx1 = roi[pick,0]
  161.     dx2 = roi[pick,1]
  162.     dx3 = roi[pick,2]
  163.     dx4 = roi[pick,3]
  164.     w   = x2-x1
  165.     h   = y2-y1
  166.     pts0= np.array([(w*pts[pick,0]+x1)[0]]).T
  167.     pts1= np.array([(h*pts[pick,5]+y1)[0]]).T
  168.     pts2= np.array([(w*pts[pick,1]+x1)[0]]).T
  169.     pts3= np.array([(h*pts[pick,6]+y1)[0]]).T
  170.     pts4= np.array([(w*pts[pick,2]+x1)[0]]).T
  171.     pts5= np.array([(h*pts[pick,7]+y1)[0]]).T
  172.     pts6= np.array([(w*pts[pick,3]+x1)[0]]).T
  173.     pts7= np.array([(h*pts[pick,8]+y1)[0]]).T
  174.     pts8= np.array([(w*pts[pick,4]+x1)[0]]).T
  175.     pts9= np.array([(h*pts[pick,9]+y1)[0]]).T
  176.     x1  = np.array([(x1+dx1*w)[0]]).T
  177.     y1  = np.array([(y1+dx2*h)[0]]).T
  178.     x2  = np.array([(x2+dx3*w)[0]]).T
  179.     y2  = np.array([(y2+dx4*h)[0]]).T
  180.     rectangles=np.concatenate((x1,y1,x2,y2,sc,pts0,pts1,pts2,pts3,pts4,pts5,pts6,pts7,pts8,pts9),axis=1)
  181.     pick = []
  182.     for i in range(len(rectangles)):
  183.         x1 = int(max(0     ,rectangles[i][0]))
  184.         y1 = int(max(0     ,rectangles[i][1]))
  185.         x2 = int(min(width ,rectangles[i][2]))
  186.         y2 = int(min(height,rectangles[i][3]))
  187.         if x2>x1 and y2>y1:
  188.             pick.append([x1,y1,x2,y2,rectangles[i][4],
  189.                  rectangles[i][5],rectangles[i][6],rectangles[i][7],rectangles[i][8],rectangles[i][9],rectangles[i][10],rectangles[i][11],rectangles[i][12],rectangles[i][13],rectangles[i][14]])
  190.     return NMS(pick,0.3)
复制代码
test1.jpg如下所示:

推理结果out.jpg如下所示:


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

悠扬随风

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表