人脸检测与识别:简单的人脸检测

由唐宇迪大佬的视频和课题组的项目有感,为课题室做一个人脸打卡系统,各个部分基本都是抄袭加借鉴,希望大家海涵呀~

项目分阶段进行,实时更新,由于本人实在太菜,趟坑无数,所以更新也没个准头。。

项目1阶段:简单的人脸检测

项目环境及配置:window10+GTX 1060+python3.6+anaconda5.2.0+tensorflow1.9

1、数据获取

人脸数据网上的资源非常非常的多,如下附上了几个获取数据的网站。在下载和查找数据的时候需要同时百度一下这个数据的使用方法,如:原始图片在哪,含有标注的.txt文件或.mat文件都在哪等,否则自己瞎搞很容易浪费时间。

这两个链接包含了大多数开源的人脸数据

http://www.cvmart.net/community/article/detail/148

https://blog.csdn.net//chenriwei2/article/details/50631212

项目1阶段训练使用的人脸数据集为AFLW,如下可下载,其中aflw-images-0.tar.gz,aflw-images-2.tar.gz,aflw-images-3.tar.gz这三个文件是图片数据,解压的时候把后面那个.gz删掉就可以解压了。AFLW的标注就是那个aflw的txt文件,我也忘了从哪个文件里找的了。。你们就直接用就行,当时找这个找了好久。。。至于标注的格式是矩形框的左上角和右下角。

AFLW:https://pan.baidu.com/s/14McWGRZCnOcP2SBhK2ryrQ

验证集选用FDDB,链接一搜就有

非人脸数据部分验证采用了PASCAL VOC 2007、2012。这俩网上教程有的是,实在懒得帖多了,就贴一个吧。。

PASCAL VOC 2007教程:https://blog.csdn.net/julialove102123/article/details/78330752

训练集选用COCO非人数据,参照如下:

COCO数据集下载参照 https://blog.csdn.net/daniaokuye/article/details/78699138

具体标注信息:https://blog.csdn.net/wc781708249/article/details/79603522

API安装参照:https://www.jianshu.com/p/de455d653301

API下载参照:https://github.com/cocodataset/cocoapi

2、数据集制作

首先要从数据集种截取出人脸图片和非人脸图片。

人脸截取相对容易,对着标注截就行(由于是简单项目,这个不要求IoU大于多少多少就算正样本),总共有24384张人脸,程序如下:

import cv2
import time

begin=time.time()
addr='...\\AFLW\\'               #图片解压会出现0,2,3这三个文件夹,集体放入AFLW文件中
f=open(r'...\alfw.txt')
j=0

for line in f.readlines():
    line=line.strip().split()    
    path=addr+line[0]
    img=cv2.imread(path)
    if img is None:
        continue
    x=int(line[1])
    if x<0:x=0
    y=int(line[2])
    if y<0:y=0
    w=int(line[3])
    h=int(line[4])
    img=img[y:y+h,x:x+w]
    cv2.imwrite('...\\alfw_face\\alfw_face_%d.jpg'%(j),img)
    j+=1
print(time.time()-begin)   

对于非人脸,那么问题就来了,虽然没有人脸的图片都可以,且IoU<0.3就可以认为是非人脸,但咱们也不能瞎搞,咱们的数据没人家多,装备没人家好,但是咱们的目的不是为了去lfw争第一的(至少我不是,我只想把工程搞好,满足基本需求。。),所以我的这个项目,截取的IoU都是<0.01的。。。数据集总数为4.8W就好(强迫症不想让数据里有脏的。。),但这只是问题的开始,问题总结如下:

1、AFLW的标注不全,导致即使IoU小于你设的阈值还是可能有人脸的,所以此时此刻,需要你自己去翻了。。我是翻了好几天的。。;

2、建议随机截取图片中部的地方,AFLW的图片很多边边角角都是纯色的,你截下来一堆这样的当负样本虽然训练时很容易训练(我的AlexNet模型曾经3000次就收敛),但是实测烂到爆;

3、参考https://www.cnblogs.com/hrlnw/p/5243853.html中负样本的选择,不要只选风景图片,原因同2,模型基本学不到太多东西;

4、参考《From Facial Parts Responses to Face Detection: A Deep Learning Approach》这篇论文,他在fine-tune模型的时候非人脸数据选用了PASCAL VOC 2007的非人数据,那么我们也可以对着抄一下,不对,参考一下方法哈~~

截取的程序根据上面的改改就行,我就不贴了,我的程序贼low。。

PASCAL VOC 2007上面这个链接讲的够详细了,2012也是一样的,在main文件夹下找到person_trainval.txt文件,按照-1的label把图片都拿出来就行。(别自作聪明的把那仨person的全提取出来,person_trainval是另外俩的合集,别问我咋知道的,看教程不看全的后果。。)程序如下:

import cv2
import time
import time

#之所以采用两个是因为2012的数据集内并没有2007的label
begin=time.time()
i=0
addr='...\\JPEGImages\\'         #图片保存的路径,此程序为2012的,2007的同理
doc=['person_trainval']
for name in doc:
    f=open(r'...\VOCdevkit\VOC2012\ImageSets\Main\%s.txt'%(name))
    for line in f.readlines():
        line=line.strip().split()
        if line[1]!='1':
            path=addr+line[0]+'.jpg'
            img=cv2.imread(path)
            cv2.imwrite('...\\VOC2012_others\\VOC_others_%d.jpg'%(i),img)
            i+=1
print(time.time()-begin)  

非人脸数据多搞搞,搞到和人脸数据差不多就行了,随后我们将这两种图片转化为tensorflow的文件格式:TFRecord。

制作过程中发现了一个问题,兴许是anaconda的bug由jupyter notebook制作出的文件总会出现DATALOSS的错误,详情请看我的另一篇问题解答:

https://blog.csdn.net/rrui7739/article/details/81003577

我是用Spyder制作的,程序抄袭如下:

#注:由于不能很好shuffle,所以只能调用random模块对文件名手动shuffle后保存为TFRecord格式

import os
import tensorflow as tf 
import cv2
import time
import random

begin=time.time()
classes=['ALFW_others','ALFW_face']

face=os.listdir('C:\\Users\\312\\Desktop\\Face_Detection\\ALFW_face\\')
others=os.listdir('C:\\Users\\312\\Desktop\\Face_Detection\\ALFW_others\\')
random.shuffle(face)
random.shuffle(others)

writer = tf.python_io.TFRecordWriter("face_detection.tfrecords")
for i in range(1,480):
    if i%50==0:        
        print(i)
    for index, name in enumerate(classes):
        class_path='C:\\Users\\312\\Desktop\\Face_Detection\\'+name+'\\'
        if name=='ALFW_face':
            docu_name=face
        else:
            docu_name=others
        for img_name in docu_name[50*(i-1):50*i]:
            img_path = class_path + img_name
            img = cv2.imread(img_path)
            if img is None:
                continue
            img = cv2.resize(img,(227, 227))
            
            img_raw = img.tobytes()              #将图片转化为原生bytes
            example = tf.train.Example(features=tf.train.Features(feature={
                "label": tf.train.Feature(int64_list=tf.train.Int64List(value=[index])),
                'img': tf.train.Feature(bytes_list=tf.train.BytesList(value=[img_raw]))
            }))
            writer.write(example.SerializeToString())  #序列化为字符串
writer.close()
print(time.time()-begin)

此程序运行完以后,想看看数据制作的对不对的,可以看上面那个问题解答的链接,里面有读取实验的程序,在这我就不贴了。这段程序是制作4.8W训练数据的,以及1W验证集。如果有人想制作测试数据的话也可以根据这个照葫芦画瓢做一下,但是一定要注意,train、validate、test这三种数据一定不要重复,否则数据泄露后点子不好那对你的系统就是核打击。。

至此我们的第二阶段数据集制作就告一段落了。。呼。。。

3、AlexNet模型训练

由于本人一贯秉持着抄袭原则,所以说模型咱们也要照抄不误~

同时也由于本人秉持着重基础原则,所以说模型着全是拿原生API手撸的(除了BN层)。。暂时就先告别Keras和Slim吧(其实我也是没用过不咋熟。。)。。

import tensorflow as tf
import numpy as np
import time
from tensorflow.python.framework import graph_util
import cv2

begin=time.time()

# def kaka(image_batch):
#     a=np.mean(image_batch)
#     image_batch=image_batch-a
#     return image_batch

def wenjianliu():
    filename_queue = tf.train.string_input_producer(['E:\\friedhelm\\face_train_227.tfrecords'],shuffle=True)

    reader = tf.TFRecordReader()
    _, serialized_example = reader.read(filename_queue) #返回文件名和文件

    features = tf.parse_single_example(serialized_example,
                                   features={
                                   'label':tf.FixedLenFeature([],tf.int64),
                                   'img':tf.FixedLenFeature([],tf.string),
                                   })
    img=tf.decode_raw(features['img'],tf.uint8)
    label=tf.cast(features['label'],tf.int32)
    img = tf.reshape(img, [227,227,3])   
#     img=kaka(img)
    min_after_dequeue = 10000
    batch_size = 64
    capacity = min_after_dequeue + 10 * batch_size
    image_batch, label_batch = tf.train.shuffle_batch([img, label], 
                                                        batch_size=batch_size, 
                                                        capacity=capacity, 
                                                        min_after_dequeue=min_after_dequeue,
                                                        num_threads=7)  
    return image_batch,label_batch
    

def wenjianliu1():
    filename_queue = tf.train.string_input_producer(['E:\\friedhelm\\face_val_227.tfrecords'],shuffle=True,seed=77)

    reader = tf.TFRecordReader()
    _, serialized_example = reader.read(filename_queue) #返回文件名和文件

    features = tf.parse_single_example(serialized_example,
                                   features={
                                   'label':tf.FixedLenFeature([],tf.int64),
                                   'img':tf.FixedLenFeature([],tf.string),
                                   })
    img=tf.decode_raw(features['img'],tf.uint8)
    label=tf.cast(features['label'],tf.int32)
    img = tf.reshape(img, [227,227,3])   
#     img=kaka(img)
    min_after_dequeue = 1000
    batch_size = 64
    capacity = min_after_dequeue + 10 * batch_size
    image_batch, label_batch = tf.train.shuffle_batch([img, label], 
                                                        batch_size=batch_size, 
                                                        capacity=capacity, 
                                                        min_after_dequeue=min_after_dequeue,
                                                        num_threads=7)  
    return image_batch,label_batch    
    
    
def model(x,prob,is_training):
    
    with tf.variable_scope('conv1',reuse=tf.AUTO_REUSE):
        weight1=tf.get_variable('weight',[11,11,3,96],initializer=tf.truncated_normal_initializer(stddev=0.1))
        bias1=tf.get_variable('bias',[96],initializer=tf.constant_initializer(0.0))        
        conv1=tf.nn.conv2d(x,weight1,strides=[1,4,4,1],padding='VALID')
        he1=tf.nn.bias_add(conv1,bias1)
        bn1=tf.layers.batch_normalization(he1,fused=False,training=is_training)
        relu1=tf.nn.relu(bn1)       
    
    with tf.variable_scope('pool1',reuse=tf.AUTO_REUSE):  
        pool1=tf.nn.max_pool(relu1,ksize=[1,3,3,1],strides=[1,2,2,1],padding='VALID')

    with tf.variable_scope('conv2',reuse=tf.AUTO_REUSE):
        weight2=tf.get_variable('weight',[3,3,96,256],initializer=tf.truncated_normal_initializer(stddev=0.1))
        bias2=tf.get_variable('bias',[256],initializer=tf.constant_initializer(0.0))        
        conv2=tf.nn.conv2d(pool1,weight2,strides=[1,1,1,1],padding='SAME')
        he2=tf.nn.bias_add(conv2,bias2)
        bn2=tf.layers.batch_normalization(he2,fused=False,training=is_training)
        relu2=tf.nn.relu(bn2)     
    
    with tf.variable_scope('pool2',reuse=tf.AUTO_REUSE):  
        pool2=tf.nn.max_pool(relu2,ksize=[1,3,3,1],strides=[1,2,2,1],padding='VALID')
  
    with tf.variable_scope('conv3',reuse=tf.AUTO_REUSE):
        weight3=tf.get_variable('weight',[3,3,256,384],initializer=tf.truncated_normal_initializer(stddev=0.1))
        bias3=tf.get_variable('bias',[384],initializer=tf.constant_initializer(0.0))        
        conv3=tf.nn.conv2d(pool2,weight3,strides=[1,1,1,1],padding='SAME')
        he3=tf.nn.bias_add(conv3,bias3)
        mean3,vias3=tf.nn.moments(he3,0)
        bn3=tf.layers.batch_normalization(he3,fused=False,training=is_training)
        relu3=tf.nn.relu(bn3)    
        
    with tf.variable_scope('conv4',reuse=tf.AUTO_REUSE):
        weight4=tf.get_variable('weight',[3,3,384,384],initializer=tf.truncated_normal_initializer(stddev=0.1))
        bias4=tf.get_variable('bias',[384],initializer=tf.constant_initializer(0.0))        
        conv4=tf.nn.conv2d(relu3,weight4,strides=[1,1,1,1],padding='SAME')
        he4=tf.nn.bias_add(conv4,bias4)
        bn4=tf.layers.batch_normalization(he4,fused=False,training=is_training)
        relu4=tf.nn.relu(bn4)          
        
    with tf.variable_scope('conv5',reuse=tf.AUTO_REUSE):
        weight5=tf.get_variable('weight',[3,3,384,256],initializer=tf.truncated_normal_initializer(stddev=0.1))
        bias5=tf.get_variable('bias',[256],initializer=tf.constant_initializer(0.0))        
        conv5=tf.nn.conv2d(relu4,weight5,strides=[1,1,1,1],padding='SAME')
        he5=tf.nn.bias_add(conv5,bias5)
        bn5=tf.layers.batch_normalization(he5,fused=False,training=is_training)
        relu5=tf.nn.relu(bn5)    

    with tf.variable_scope('pool3',reuse=tf.AUTO_REUSE):  
        pool3=tf.nn.max_pool(relu5,ksize=[1,3,3,1],strides=[1,2,2,1],padding='VALID')
        
    with tf.variable_scope('fc1',reuse=tf.AUTO_REUSE):     
        weight6=tf.get_variable('weight',[6,6,256,2000],initializer=tf.truncated_normal_initializer(stddev=0.1))
        bias6=tf.get_variable('bias',[2000],initializer=tf.constant_initializer(0.0))        
        conv6=tf.nn.conv2d(pool3,weight6,strides=[1,1,1,1],padding='VALID')
        he6=tf.nn.bias_add(conv6,bias6)
        bn6=tf.layers.batch_normalization(he6,fused=False,training=is_training)
        relu6=tf.nn.relu(bn6)     
        fc1=tf.nn.dropout(relu6,prob)

    with tf.variable_scope('fc2',reuse=tf.AUTO_REUSE):     
        weight7=tf.get_variable('weight',[1,1,2000,200],initializer=tf.truncated_normal_initializer(stddev=0.1))
        bias7=tf.get_variable('bias',[200],initializer=tf.constant_initializer(0.0))        
        conv7=tf.nn.conv2d(fc1,weight7,strides=[1,1,1,1],padding='VALID')
        he7=tf.nn.bias_add(conv7,bias7)
        bn7=tf.layers.batch_normalization(he7,fused=False,training=is_training)
        relu7=tf.nn.relu(bn7)     
        fc2=tf.nn.dropout(relu7,prob)
        
    with tf.variable_scope('fc3',reuse=tf.AUTO_REUSE):     
        weight8=tf.get_variable('weight',[1,1,200,1],initializer=tf.truncated_normal_initializer(stddev=0.1))
        bias8=tf.get_variable('bias',[1],initializer=tf.constant_initializer(0.0))        
        conv8=tf.nn.conv2d(fc2,weight8,strides=[1,1,1,1],padding='VALID')
        he8=tf.nn.bias_add(conv8,bias8,name='he')
        logit=tf.nn.sigmoid(he8,name='logit')   
    return logit  
    
def train(x,y_,prob,is_training,af_y):
    y=model(x,prob,is_training)
    y=tf.reshape(y,(1,64))[0]
    with tf.name_scope('loss'):
        loss_all=tf.add(-y_*tf.log(y+1e-9),-(1-y_)*tf.log(1-y+1e-9))
        loss=tf.reduce_mean(loss_all,name='loss')
#         tf.summary.scalar('loss',loss)        
    update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
    with tf.control_dependencies(update_ops):
        opt=tf.train.AdamOptimizer(0.001).minimize(loss)      
    with tf.name_scope('accuracy'):
        accuracy=tf.reduce_mean(tf.cast(tf.equal(af_y,y_),tf.float32),name='accuracy')
        tf.summary.scalar('accuracy',accuracy)               
#     var_list = tf.trainable_variables()
#     g_list = tf.global_variables()
#     bn_moving_vars = [g for g in g_list if 'moving_mean' in g.name]
#     bn_moving_vars += [g for g in g_list if 'moving_variance' in g.name]
#     var_list += bn_moving_vars
#     saver = tf.train.Saver(var_list=var_list, max_to_keep=5)
    saver=tf.train.Saver()
    image_batch,label_batch=wenjianliu()
    image_batch1,label_batch1=wenjianliu1()    
  
    with tf.Session() as sess:
        sess.run((tf.global_variables_initializer(),
                  tf.local_variables_initializer()))
        coord = tf.train.Coordinator()
        threads = tf.train.start_queue_runners(sess=sess,coord=coord)
        writer=tf.summary.FileWriter('C:\\Users\\312\\Desktop\\',sess.graph)
        merged=tf.summary.merge_all()
        for i in range(50001):
            img,label=sess.run([image_batch,label_batch])
            label=np.reshape(label,(64))    
            sess.run(opt,feed_dict={x:img,y_:label,prob:0.7,is_training:True})
            if(i%100==0):
                img1,label1=sess.run([image_batch1,label_batch1])
                label1=np.reshape(label1,(64))
                p_y=sess.run(y,feed_dict={x:img1,y_:label1,prob:1,is_training:False})
                ppp=lambda ppp:1 if ppp>0.95 else 0 
                p_y=[ppp(i) for i in p_y]
#                 summary=sess.run(merged,feed_dict={x:img,y_:label,prob:0.7,is_training:True,af_y:p_y})               
                summary=sess.run(merged,feed_dict={y_:label1,af_y:p_y})
                writer.add_summary(summary,i)            
            if(i%1000==0):
                print('次数',i)    
                print('loss',sess.run(loss,feed_dict={x:img,y_:label,prob:0.7,is_training:True}))
                print('accuracy',sess.run(accuracy,feed_dict={af_y:p_y,y_:label1}))                
                print('time',time.time()-begin)
#                 constant_graph = graph_util.convert_variables_to_constants(sess, sess.graph_def, ["fc3/logit"])
#                 with tf.gfile.FastGFile('C:\\Users\\312\\Desktop\\face_model.pb', mode='wb') as f:
#                     f.write(constant_graph.SerializeToString())
                saver.save(sess,"C:\\Users\\312\\Desktop\\Face_Detection\\model.ckpt")
    writer.close()
                
def main():
    
    with tf.name_scope('input'):
        x=tf.placeholder(tf.float32,name='x')
        y_=tf.placeholder(tf.float32,name='y')
        prob=tf.placeholder(tf.float32,name='prob')
        is_training = tf.placeholder(tf.bool,name='is_train')
        af_y=tf.placeholder(tf.float32,name='af_y')      
    train(x,y_,prob,is_training,af_y)

if __name__=='__main__':
    main()
# tensorboard --logdir=C:\\Users\\312\\Desktop\\

模型方面我把第三层的5*5卷积核换成了3*3的,全连接层换成全卷积层,并且将深度也改变了。

这块有坑死我自己的几个地方,以及需要注意的地方,我提出来大家看看有没有用吧:

1、在建立模型并更换全卷积层的时候需要特别注意上一pooling层的输出大小,这就是我第三层的5*5卷积核换成了3*3的原因,方便我理解和计算,感受野与你的stride有关,和size无关;

2、label不是one-hot的,所以最后输出的0~1之间值即为label为1的概率值;

3、关于BN层,没有BN层的话收敛特别慢,BN层的解释可以看看别人的博客。tensorflow的BN如果要用low-level的API的话需要自己写滑动平均,而且还要写tf.cond判断,tf.layers.batch_normalization(he2,training=is_training,fused=False)是现成的(我承认我偷懒了,重基础都是假的~),通过查看源码可以深入理解一下,fused说官方文档是可以快速实现,但是源码越看越远我就放弃了,所以就不用fused了,正常不用的话是跟BN论文的实现是一样的,所以设置为fused=False,如果为None和True则会使用fused;

4、文件读取时需要先run出来再feed进去,必须同时run出来,否则出来的数据会对应不上,这是tensorflow的机制决定的(眼瞎的我踩了两天坑一直以为是读取的代码错了,直到我看到了两个run。。);

5、模型中以后用到的tensor的名称要提前指定好,否则将来用的时候会发现还需要重新训练。。。;

6、从文件中读取出的img一定要经过tf.reshape操作,否则会报shape的错误,我看很多网上的教程里都没有写这个;

7、如果在训练时长时间读取不了文件流的数据,兴许是TFRecord格式的文件损坏了,再生成一份就好了;

其他的坑要不别人都写了,要不就是我没有踩到,暂时总结到这吧。。

loss

accuracy

loss和accuracy的问题在于不是一个输入,loss我知道程序和数据必然收敛后就不想再费劲显示它了,所以只显示一个验证集的,瞅着准确率还不错。。(仅仅是瞅着)

注:这两张图是我两回训练截的图

4、正式开始识别~

具体的代码参照了唐宇迪大佬的视频,首先输入图片的纯手工造高斯金字塔(调用金字塔函数亲测反而更慢):

import tensorflow as tf 
import numpy as np
import cv2
import matplotlib.pyplot as plt

total_box=[]
scales=[]
img=cv2.imread(r'...')
factor=0.7
large=2270/max(img.shape[0:2])
scale=large
small=large*min(img.shape[0:2])
i=j=0
while small>=227:
    scales.append(scale)
    scale*=factor
    small*=factor
    j+=1
print(j)
for scale in scales:

    scale_img=cv2.resize(img,((int(img.shape[0]*scale)),(int(img.shape[1]*scale))))
    print(np.shape(scale_img))
    boxes=featuremap(scale_img,scale)

    if(boxes):
        for box in boxes:
            total_box.append(box)
            i+=1
    print(i)

其中featuremap为特征图函数,具体代码如下,经过这个函数可得到人脸框:

def featuremap(img,scale):
    boundingBox=[]
    
    graph_path='...\\model.ckpt.meta'
    model_path='...\\model.ckpt'
    saver=tf.train.import_meta_graph(graph_path)
    blue = (0, 255, 0) 
    stride=32
    
    with tf.Session() as sess:
        
        saver.restore(sess,model_path)
        graph = tf.get_default_graph()

        x= graph.get_tensor_by_name("input/x:0")
        y=graph.get_tensor_by_name("input/prob:0")
        p=graph.get_tensor_by_name("input/is_train:0")
        sliding= graph.get_tensor_by_name("fc3/logit:0")
        
        img1=np.reshape(img,(-1,img.shape[0],img.shape[1],img.shape[2]))
        a=sliding.eval(feed_dict={x:img1,y:1,p:False})[0]

        for (x,y,z),prob in np.ndenumerate(a):
            if prob>0.99:
                boundingBox.append([float(x*stride)/scale,float(y*stride)/scale, float(x*stride+227)/scale, float(y*stride+227)/scale,prob])

        return boundingBox

随后经过如下NMS函数,这个函数在人脸识别上还是蛮重要的,真心建议大家一定要看懂,理解它每一步都干了啥,像我一样,特别明白,然后就忘了,哈哈~

def NMS(box):
    
    if len(box) == 0:
        return []
    
    #xmin, ymin, xmax, ymax, score, cropped_img, scale
    box.sort(key=lambda x :x[4])
    box.reverse()

    pick = []
    x_min = np.array([box[i][0] for i in range(len(box))],np.float32)
    y_min = np.array([box[i][1] for i in range(len(box))],np.float32)
    x_max = np.array([box[i][2] for i in range(len(box))],np.float32)
    y_max = np.array([box[i][3] for i in range(len(box))],np.float32)

    area = (x_max-x_min)*(y_max-y_min)
    idxs = np.array(range(len
                          (box)))

    while len(idxs) > 0:
        i = idxs[0]
        pick.append(i)

        xx1 = np.maximum(x_min[i],x_min[idxs[1:]])
        yy1 = np.maximum(y_min[i],y_min[idxs[1:]])
        xx2 = np.minimum(x_max[i],x_max[idxs[1:]])
        yy2 = np.minimum(y_max[i],y_max[idxs[1:]])

        w = np.maximum(xx2-xx1,0)
        h = np.maximum(yy2-yy1,0)

        overlap = (w*h)/(area[idxs[1:]] + area[i] - w*h)

        idxs = np.delete(idxs, np.concatenate(([0],np.where(((overlap >= 0.7) & (overlap <= 1)))[0]+1)))
    
    return [box[i] for i in pick]

经过NMS函数后就出来检测的结果了,然后结果跟视频里的图片以及想象的不一样,烂到爆。。我就截一个图给大家看看吧。。

由于我懒得找别的图片了,直接拿AFLW的原图去测试的,这是经过了NMS的图像,基本无法框出来脸,在数据泄露的如此彻底的情况下现实和理想的差距。。

毕竟是简单的实现,对结果要求不了太多。。

5、总结

本文只是个入门文章,自己走过了一遍流程,趟过了无数的小白坑,现在咋的也算一个小灰级别了。文内主要提到了一些我遇到的坑,对于CNN理论方面并没有涉及,现在CNN的理论烂大街,大家可以随意百度,我也不在此赘述了。其中尤其重要的就是特征图的问题,大家一定要搞透,对于以后还想弄目标检测的未来大佬们,fast和faster-rcnn都是你们要踩的坑,我估计是没时间喽。。

文中不懂的地方可以查找fast和faster-rcnn的论文,以及百度,当然唐宇迪大神的视频内还是有一定的讲解的。

在接下来的文章,我会争取复现《Joint Face Detection and Alignment Using Multitask Cascaded Convolutional Networks》这篇论文,使用MTCNN。该论文目前是我看到的人脸识别相当不错的论文(由于本人只是兴趣爱好以及项目需求,更好的论文兴许没有看到,如有推荐,不胜感激!)

2018.8.26

猜你喜欢

转载自blog.csdn.net/Rrui7739/article/details/81261543