人脸识别_人脸识别技术 - CSDN
人脸识别 订阅
人脸识别,是基于人的脸部特征信息进行身份识别的一种生物识别技术。用摄像机或摄像头采集含有人脸的图像或视频流,并自动在图像中检测和跟踪人脸,进而对检测到的人脸进行脸部识别的一系列相关技术,通常也叫做人像识别、面部识别。 展开全文
人脸识别,是基于人的脸部特征信息进行身份识别的一种生物识别技术。用摄像机或摄像头采集含有人脸的图像或视频流,并自动在图像中检测和跟踪人脸,进而对检测到的人脸进行脸部识别的一系列相关技术,通常也叫做人像识别、面部识别。
信息
处理方法
人脸识别算法
传统技术
可见光图像的人脸识别
别    名
人像识别、面部识别
用    途
身份识别
中文名
人脸识别
工    具
摄像机或摄像头
人脸识别发展历史
人脸识别系统的研究始于20世纪60年代,80年代后随着计算机技术和光学成像技术的发展得到提高,而真正进入初级的应用阶段则在90年后期,并且以美国、德国和日本的技术实现为主;人脸识别系统成功的关键在于是否拥有尖端的核心算法,并使识别结果具有实用化的识别率和识别速度;“人脸识别系统”集成了人工智能、机器识别、机器学习、模型理论、专家系统、视频图像处理等多种专业技术,同时需结合中间值处理的理论与实现,是生物特征识别的最新应用,其核心技术的实现,展现了弱人工智能向强人工智能的转化。 [1] 
收起全文
精华内容
参与话题
  • 手把手教你实现人脸识别支付系统

    千人学习 2020-07-20 10:48:04
    当我们使用人脸识别技术来构建一个支付系统的时候,在准确性、安全性,以及实时性等方面,将会有更高的要求。 所以本课程着眼于刷脸支付系统实现上的特殊性,重点阐述了一些相关的技术,比如:开放域人脸识别、支付...
  • 利用python、tensorflow、opencv实现人脸识别(包会)!

    万次阅读 多人点赞 2019-02-20 21:44:24
    本人是机械专业在读硕士,在完成暑假实践的时候接触到了人脸识别,对这一实现很感兴趣,所以花了大概十天时间做出了自己的人脸识别。这篇文章应该是很详细的了所以帮你实现人脸识别应该没什么问题。 先说本博文的...

     

    一,前言

    本人是机械专业在读硕士,在完成暑假实践的时候接触到了人脸识别,对这一实现很感兴趣,所以花了大概十天时间做出了自己的人脸识别。这篇文章应该是很详细的了所以帮你实现人脸识别应该没什么问题。

    先说本博文的最终要达到的效果:通过一系列操作,在摄像头的视频流中识别特定人的人脸,并且予以标记。

    本人通过网上资料的查询发现这类人脸识别,大多参考了一位日本程序员小哥的文章。

    链接:https://github.com/Hironsan/BossSensor

    关于这个思路的人脸识别网上资料很多,但是有很多细节没有提到,我在实践的过程中菜过了无数的坑,希望我这篇文章能够对你提供更加清晰的思路和操作。先看结果(人丑勿怪)!这个是识别我的脸,别人的脸不会识别到

    其实,这已经涉及到了一些机器学习的内容,对于像入门机器学习的同学来说是一个不错的练手的项目。

    二、前期准备工作

    首先说,我在刚开始接触的时候,主要是在各种数据包的安装上以及环境的配置上花费了巨大的时间,有些数据包升级版本之后与一些功能不兼容,出了很多问题,所以。我在这里说一下我的数据包的版本和python版本。现在可以用anaconda来下载python和各种数据包,但是最新的版本是用python3.6.X,在后面的实践中可能会出现不同的问题,所以为了安全起见python最好选择3.5.X的,不要安装2.X的,与3.X的版本不兼容,会出现很多问题。另外再安装一个tensorflow,pip,keras,sklearn,PIL,numpy,opencv等。其中keras要安装2.0版本的,opencv安装3.3.1版本的。tensorflow有CPU版本的和GPU版本的,你可以看一下你适合哪一种,这里贴出来一些供你参考:

           您必须从以下 TensorFlow 类型中选择其一来进行安装:

           仅支持 CPU 的 TensorFlow。如果您的系统没有 NVIDIA® GPU,则必须安装此版本。请注意,此版本的                                    TensorFlow 通常更容易安装(用时通常在 5 或 10 分钟内),所以即使您拥有 NVIDIA GPU,我们也建议先安                              装此版本。预编译的二进制文件将使用 AVX 指令。

            支持 GPU 的 TensorFlow。TensorFlow 程序在 GPU 上的运行速度通常要比在 CPU 上快得多。因此,如果您                           的系统配有满足以下所示先决条件的 NVIDIA® GPU,并且您需要运行性能至关重要的应用,则最终应安装此                              版本。

    另外我在安装的过程中发现了几篇比较不错的博文供你参考:

    1.https://blog.csdn.net/Eppley/article/details/79297503

    2.https://blog.csdn.net/WJ_MeiMei/article/details/79684627

    3.https://zhuanlan.zhihu.com/p/24055668

    在几篇博文里你也会看到验证安装正确的方法,如果可以的话,说明你安装成功了,这里我就不多说了。后面的话你可能还会遇到别的什么问题,如果还需要安装什么模块的话,在安装也可以。

    在硬件方面,你还需要一个USB摄像头。

     

    总结:

    1. USB摄像头一个;

    2. python  --  3.5.X

    3. tensorflow

    4. opencv  --  3.3.1

    5. keras   --  2.0.X

    6. sklearn  -- 0.19.0

     

    三、正式开始

     

    1,识别人脸

           实现人脸识别简单程序没几行,但是我们要实现的是识别这个是谁的脸。首先我们让系统识别人脸,这是opencv的工作,我们只需要调用其中的API函数就可以了。下面是调用opencv实现对于人脸的识别。咱们在程序下面对程序进行一些解释:

    
    
    import cv2
    import sys
    from PIL import Image
    
    def CatchUsbVideo(window_name, camera_idx):
        cv2.namedWindow(window_name)
        
        #视频来源,可以来自一段已存好的视频,也可以直接来自USB摄像头
        cap = cv2.VideoCapture(camera_idx)                
        
        #告诉OpenCV使用人脸识别分类器
        classfier = cv2.CascadeClassifier("H:\\OpenCV\\opencv\\build\\etc\\haarcascades\\haarcascade_frontalface_alt2.xml")
        
        #识别出人脸后要画的边框的颜色,RGB格式
        color = (0, 255, 0)
            
        while cap.isOpened():
            ok, frame = cap.read() #读取一帧数据
            if not ok:            
                break  
    
            #将当前帧转换成灰度图像
            grey = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)                 
            
            #人脸检测,1.2和2分别为图片缩放比例和需要检测的有效点数
            faceRects = classfier.detectMultiScale(grey, scaleFactor = 1.2, minNeighbors = 3, minSize = (32, 32))
            if len(faceRects) > 0:            #大于0则检测到人脸                                   
                for faceRect in faceRects:  #单独框出每一张人脸
                    x, y, w, h = faceRect        
                    cv2.rectangle(frame, (x - 10, y - 10), (x + w + 10, y + h + 10), color, 2)
                            
            #显示图像
            cv2.imshow(window_name, frame)        
            c = cv2.waitKey(10)
            if c & 0xFF == ord('q'):
                break        
        
        #释放摄像头并销毁所有窗口
        cap.release()
        cv2.destroyAllWindows() 
        
    if __name__ == '__main__':
        if len(sys.argv) != 1:
            print("Usage:%s camera_id\r\n" % (sys.argv[0]))
        else:
            CatchUsbVideo("识别人脸区域", 0)
    

        首先,第一行import cv2,实际上,”cv2”中的”2”并不表示OpenCV的版本号。我们知道,OpenCV是基于C/C++的,”cv”和”cv2”表示的是底层CAPI和C++API的区别,”cv2”表示使用的是C++API。这主要是一个历史遗留问题,是为了保持向后兼容性。PIL是一个模块,如果运行的过程中提示你缺少模块的时候你就要安装一个模块了,其余同理,就不再说了。另外在函数Catchusbvideo中,第二个参数指的是你电脑的摄像头的编号,例如是0,1,2等,如果0不行的话,试一下1。在下边的人脸识别分类器中是我自己下载的opencv,下载网站是:https://opencv.org/releases.html,如果你是windows选择对应版本就好,还有就是“H:\\OpenCV\\opencv\\build\\etc\\haarcascades\\haarcascade_frontalface_alt2.xml”这是我安装的一个路径,你也要找到这个路径并且复制到程序中,这个东西的作用主要是实现对人脸识别的功能,在安装中还有其他的功能,我也一并列在下面:

                   人脸检测器(默认):haarcascade_frontalface_default.xml 
                   人脸检测器(快速Harr):haarcascade_frontalface_alt2.xml 
                   人脸检测器(侧视):haarcascade_profileface.xml 
                   眼部检测器(左眼):haarcascade_lefteye_2splits.xml 
                   眼部检测器(右眼):haarcascade_righteye_2splits.xml 
                   嘴部检测器:haarcascade_mcs_mouth.xml 
                   鼻子检测器:haarcascade_mcs_nose.xml 
                   身体检测器:haarcascade_fullbody.xml 
                   人脸检测器(快速LBP):lbpcascade_frontalface.xml

    另外,如果我们想构建自己的分类器,比如识别火焰、汽车,数,花等,我们依然可以使用OpenCV训练构建。   

        这个函数完成对人脸的识别以及用一个框框给框起来,其中grey是要识别的图像数据,转化为灰度可以减少计算量。scaleFactor:图像缩放比例,可以理解为同一个物体与相机距离不同,其大小亦不同,必须将其缩放到一定大小才方便识别,该参数指定每次缩放的比例。minNeighbors:对特征检测点周边多少有效点同时检测,这样可避免因选取的特征检测点太小而导致遗漏。minSize:特征检测点的最小值。

            对同一个画面有可能出现多张人脸,因此,我们需要用一个for循环将所有检测到的人脸都读取出来,然后逐个用矩形框框出来,这就是接下来的for语句的作用。Opencv会给出每张人脸在图像中的起始坐标(左上角,x、y)以及长、宽(h、w),我们据此就可以截取出人脸。其中,cv2.rectangle()完成画框的工作,在这里外扩了10个像素以框出比人脸稍大一点的区域。cv2.rectangle()函数的最后两个参数一个用于指定矩形边框的颜色,一个用于指定矩形边框线条的粗细程度。

                运行结果:

                                               

    好,看来可以顺利的识别出视频中的脸,搞定!但是我们想做的是识别这个人脸是谁的,这仅仅能识别这是谁的脸,完全不能满足我们的渴望,接下来我们进行下一步!

    2.模型训练

    模型训练的目的是让电脑知道,这个脸的特征是什么,从而可以在视频流中识别。在训练之前必须先准备足够的脸部照片作为机器学习的资料。

         2.1准备机器学习的资料

    所谓机器学习就是给程序投喂足够多的资料,资料越多,准确度和效率也会越高。要想识别出这张人脸属于谁,我们肯定需要大量的自己的脸和别人的脸,这样才能区别开。然后将这些数据输入到Tensorflow中建立我们自己脸的模型。

    1.keras简介

    上面提到的日本小哥利用深度学习库keras来训练自己的人脸识别模型。 我这里找到一篇keras的中文文档可能对你有些帮助。另外关于Keras, Keras是由纯python编写的基于theano/tensorflow的深度学习框架。Keras是一个高层神经网络API,支持快速实验,能够把你的idea迅速转换为结果,如果有如下需求,可以优先选择Keras:

                    a)简易和快速的原型设计(keras具有高度模块化,极简,和可扩充特性)

                    b)支持CNN和RNN,或二者的结合

                    c)无缝CPU和GPU切换

     

    Keras的模块结构:

                   

            

    使用Keras搭建一个神经网络:

     

          

    数据格式(data_format):

            目前主要有两种方式来表示张量:
            a) th模式或channels_first模式,Theano和caffe使用此模式。
            b)tf模式或channels_last模式,TensorFlow使用此模式。

    因为我装的是tensorflow因此我直接使用了keras的Tensorflow版,同时,为了验证其它深度学习库的效率和准确率,我还使用了Theano,利用CNN——卷积神经网络来训练我的人脸识别模型。本节专注把训练数据准备好。

    完整代码如下:

    
    
    import cv2
    import sys
    
    from PIL import Image
    
    def CatchPICFromVideo(window_name, camera_idx, catch_pic_num, path_name):
        cv2.namedWindow(window_name)
        
        #视频来源,可以来自一段已存好的视频,也可以直接来自USB摄像头
        cap = cv2.VideoCapture(camera_idx)                
        
        #告诉OpenCV使用人脸识别分类器
        classfier = cv2.CascadeClassifier("H:\\OpenCV\\opencv\\build\\etc\\haarcascades\\haarcascade_frontalface_alt2.xml")
        
        #识别出人脸后要画的边框的颜色,RGB格式
        color = (0, 255, 0)
        
        num = 0    
        while cap.isOpened():
            ok, frame = cap.read() #读取一帧数据
            if not ok:            
                break                
        
            grey = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)  #将当前桢图像转换成灰度图像            
            
            #人脸检测,1.2和2分别为图片缩放比例和需要检测的有效点数
            faceRects = classfier.detectMultiScale(grey, scaleFactor = 1.2, minNeighbors = 3, minSize = (32, 32))
            if len(faceRects) > 0:          #大于0则检测到人脸                                   
                for faceRect in faceRects:  #单独框出每一张人脸
                    x, y, w, h = faceRect                        
                    
                    #将当前帧保存为图片
                    img_name = '%s/%d.jpg'%(path_name, num)                
                    image = frame[y - 10: y + h + 10, x - 10: x + w + 10]
                    cv2.imwrite(img_name, image)                                
                                    
                    num += 1                
                    if num > (catch_pic_num):   #如果超过指定最大保存数量退出循环
                        break
                    
                    #画出矩形框
                    cv2.rectangle(frame, (x - 10, y - 10), (x + w + 10, y + h + 10), color, 2)
                    
                    #显示当前捕捉到了多少人脸图片了,这样站在那里被拍摄时心里有个数,不用两眼一抹黑傻等着
                    font = cv2.FONT_HERSHEY_SIMPLEX
                    cv2.putText(frame,'num:%d' % (num),(x + 30, y + 30), font, 1, (255,0,255),4)                
            
            #超过指定最大保存数量结束程序
            if num > (catch_pic_num): break                
                           
            #显示图像
            cv2.imshow(window_name, frame)        
            c = cv2.waitKey(10)
            if c & 0xFF == ord('q'):
                break        
        
        #释放摄像头并销毁所有窗口
        cap.release()
        cv2.destroyAllWindows() 
        
    if __name__ == '__main__':
        if len(sys.argv) != 1:
            print("Usage:%s camera_id face_num_max path_name\r\n" % (sys.argv[0]))
        else:
            CatchPICFromVideo("截取人脸", 0, 1000, 'C:\\Users\\Administrator\\Desktop\\Python\\data\\liziqiang')
    

     这段代码我们只是在前面代码的基础上增加脸部图像存储功能,比较简单。

    def CatchPICFromVideo(window_name, camera_idx, catch_pic_num, path_name):

    在函数定义中,几个参数,反别是窗口名字,摄像头系列号,捕捉照片数量,以及存储路径。根据自己需要进行修改,咱们这里为了精度高一点,选择捕捉1000张脸部照片。在你捕捉的时候由于精度的问题,会捕捉许多非脸部的照片,这时候需要你将不是脸部的照片清洗掉,使数据更加准确。另外,我们还需要捕捉另一个人的图片来提高模型的准确度。然后存储到另一个文件夹下,注意,一个人的照片存储到一个文件夹下,不可弄混。截图完成,就像下图这样。

    好了,经过前面的数据搜集,咱们已经差不多准备了2000张照片,准备工作已经做好,下面我们将进行数据模型的训练!

     

    2.模型训练

    训练程序建立了一个包含4个卷积层的神经网络(CNN),程序利用这个网络训练我的人脸识别模型,并将最终训练结果保存到硬盘上。在我们实际动手操练之前我们必须先弄明白一个问题——什么是卷积神经网络(CNN)?

    想知道你可以谷歌,另外有关神经网络我会另外写一篇博客。这里就不多做介绍了。

    首先建立一个python文件,命名load_dataset。代码如下:

    
    import os
    import sys
    import numpy as np
    import cv2
    
    IMAGE_SIZE = 64
    
    #按照指定图像大小调整尺寸
    def resize_image(image, height = IMAGE_SIZE, width = IMAGE_SIZE):
        top, bottom, left, right = (0, 0, 0, 0)
        
        #获取图像尺寸
        h, w, _ = image.shape
        
        #对于长宽不相等的图片,找到最长的一边
        longest_edge = max(h, w)    
        
        #计算短边需要增加多上像素宽度使其与长边等长
        if h < longest_edge:
            dh = longest_edge - h
            top = dh // 2
            bottom = dh - top
        elif w < longest_edge:
            dw = longest_edge - w
            left = dw // 2
            right = dw - left
        else:
            pass 
        
        #RGB颜色
        BLACK = [0, 0, 0]
        
        #给图像增加边界,是图片长、宽等长,cv2.BORDER_CONSTANT指定边界颜色由value指定
        constant = cv2.copyMakeBorder(image, top , bottom, left, right, cv2.BORDER_CONSTANT, value = BLACK)
        
        #调整图像大小并返回
        return cv2.resize(constant, (height, width))
    
    #读取训练数据
    images = []
    labels = []
    def read_path(path_name):    
        for dir_item in os.listdir(path_name):
            #从初始路径开始叠加,合并成可识别的操作路径
            full_path = os.path.abspath(os.path.join(path_name, dir_item))
            
            if os.path.isdir(full_path):    #如果是文件夹,继续递归调用
                read_path(full_path)
            else:   #文件
                if dir_item.endswith('.jpg'):
                    image = cv2.imread(full_path)                
                    image = resize_image(image, IMAGE_SIZE, IMAGE_SIZE)
                    
                    #放开这个代码,可以看到resize_image()函数的实际调用效果
                    #cv2.imwrite('1.jpg', image)
                    
                    images.append(image)                
                    labels.append(path_name)                                
                        
        return images,labels
        
    
    #从指定路径读取训练数据
    def load_dataset(path_name):
        images,labels = read_path(path_name)    
        
        #将输入的所有图片转成四维数组,尺寸为(图片数量*IMAGE_SIZE*IMAGE_SIZE*3)
        #我和闺女两个人共1200张图片,IMAGE_SIZE为64,故对我来说尺寸为1200 * 64 * 64 * 3
        #图片为64 * 64像素,一个像素3个颜色值(RGB)
        images = np.array(images)
        print(images.shape)    
        
        #标注数据,'liziqiang'文件夹下都是我的脸部图像,全部指定为0,另外一个文件夹下是同学的,全部指定为1
        labels = np.array([0 if label.endswith('liziqiang') else 1 for label in labels])    
        
        return images, labels
    
    if __name__ == '__main__':
        if len(sys.argv) != 1:
            print("Usage:%s path_name\r\n" % (sys.argv[0]))    
        else:
            images, labels = load_dataset("C:\\Users\\Administrator\\Desktop\\Python\\data")
    

           

            稍微解释一下resize_image()函数。这个函数的功能是判断图片是不是正方形,如果不是则增加短边的长度使之变成正方形。这样再调用cv2.resize()函数就可以实现等比例缩放了。因为我们指定缩放的比例就是64 x 64,只有缩放之前图像为正方形才能确保图像不失真。例如:

    这样明显不是正方形。经过程序运行之后要达到这样的目的。

    大概是这么个意思。

    将你捕捉到的照片放在俩个不同的文件夹里,我在这里一块放在了data文件夹里。

     

    然后再新建一个python文件,命名为:face_train。添加如下代码。

    
    import random
    
    import numpy as np
    from sklearn.cross_validation import train_test_split
    from keras.preprocessing.image import ImageDataGenerator
    from keras.models import Sequential
    from keras.layers import Dense, Dropout, Activation, Flatten
    from keras.layers import Convolution2D, MaxPooling2D
    from keras.optimizers import SGD
    from keras.utils import np_utils
    from keras.models import load_model
    from keras import backend as K
    
    from load_data import load_dataset, resize_image, IMAGE_SIZE
    
    
    
    class Dataset:
        def __init__(self, path_name):
            #训练集
            self.train_images = None
            self.train_labels = None
            
            #验证集
            self.valid_images = None
            self.valid_labels = None
            
            #测试集
            self.test_images  = None            
            self.test_labels  = None
            
            #数据集加载路径
            self.path_name    = path_name
            
            #当前库采用的维度顺序
            self.input_shape = None
            
        #加载数据集并按照交叉验证的原则划分数据集并进行相关预处理工作
        def load(self, img_rows = IMAGE_SIZE, img_cols = IMAGE_SIZE, 
                 img_channels = 3, nb_classes = 2):
            #加载数据集到内存
            images, labels = load_dataset(self.path_name)        
            
            train_images, valid_images, train_labels, valid_labels = train_test_split(images, labels, test_size = 0.3, random_state = random.randint(0, 100))        
            _, test_images, _, test_labels = train_test_split(images, labels, test_size = 0.5, random_state = random.randint(0, 100))                
            
            #当前的维度顺序如果为'th',则输入图片数据时的顺序为:channels,rows,cols,否则:rows,cols,channels
            #这部分代码就是根据keras库要求的维度顺序重组训练数据集
            if K.image_dim_ordering() == 'th':
                train_images = train_images.reshape(train_images.shape[0], img_channels, img_rows, img_cols)
                valid_images = valid_images.reshape(valid_images.shape[0], img_channels, img_rows, img_cols)
                test_images = test_images.reshape(test_images.shape[0], img_channels, img_rows, img_cols)
                self.input_shape = (img_channels, img_rows, img_cols)            
            else:
                train_images = train_images.reshape(train_images.shape[0], img_rows, img_cols, img_channels)
                valid_images = valid_images.reshape(valid_images.shape[0], img_rows, img_cols, img_channels)
                test_images = test_images.reshape(test_images.shape[0], img_rows, img_cols, img_channels)
                self.input_shape = (img_rows, img_cols, img_channels)            
                
                #输出训练集、验证集、测试集的数量
                print(train_images.shape[0], 'train samples')
                print(valid_images.shape[0], 'valid samples')
                print(test_images.shape[0], 'test samples')
            
                #我们的模型使用categorical_crossentropy作为损失函数,因此需要根据类别数量nb_classes将
                #类别标签进行one-hot编码使其向量化,在这里我们的类别只有两种,经过转化后标签数据变为二维
                train_labels = np_utils.to_categorical(train_labels, nb_classes)                        
                valid_labels = np_utils.to_categorical(valid_labels, nb_classes)            
                test_labels = np_utils.to_categorical(test_labels, nb_classes)                        
            
                #像素数据浮点化以便归一化
                train_images = train_images.astype('float32')            
                valid_images = valid_images.astype('float32')
                test_images = test_images.astype('float32')
                
                #将其归一化,图像的各像素值归一化到0~1区间
                train_images /= 255
                valid_images /= 255
                test_images /= 255            
            
                self.train_images = train_images
                self.valid_images = valid_images
                self.test_images  = test_images
                self.train_labels = train_labels
                self.valid_labels = valid_labels
                self.test_labels  = test_labels
                
    #CNN网络模型类            
    class Model:
        def __init__(self):
            self.model = None 
            
        #建立模型
        def build_model(self, dataset, nb_classes = 2):
            #构建一个空的网络模型,它是一个线性堆叠模型,各神经网络层会被顺序添加,专业名称为序贯模型或线性堆叠模型
            self.model = Sequential() 
            
            #以下代码将顺序添加CNN网络需要的各层,一个add就是一个网络层
            self.model.add(Convolution2D(32, 3, 3, border_mode='same', 
                                         input_shape = dataset.input_shape))    #1 2维卷积层
            self.model.add(Activation('relu'))                                  #2 激活函数层
            
            self.model.add(Convolution2D(32, 3, 3))                             #3 2维卷积层                             
            self.model.add(Activation('relu'))                                  #4 激活函数层
            
            self.model.add(MaxPooling2D(pool_size=(2, 2)))                      #5 池化层
            self.model.add(Dropout(0.25))                                       #6 Dropout层
    
            self.model.add(Convolution2D(64, 3, 3, border_mode='same'))         #7  2维卷积层
            self.model.add(Activation('relu'))                                  #8  激活函数层
            
            self.model.add(Convolution2D(64, 3, 3))                             #9  2维卷积层
            self.model.add(Activation('relu'))                                  #10 激活函数层
            
            self.model.add(MaxPooling2D(pool_size=(2, 2)))                      #11 池化层
            self.model.add(Dropout(0.25))                                       #12 Dropout层
    
            self.model.add(Flatten())                                           #13 Flatten层
            self.model.add(Dense(512))                                          #14 Dense层,又被称作全连接层
            self.model.add(Activation('relu'))                                  #15 激活函数层   
            self.model.add(Dropout(0.5))                                        #16 Dropout层
            self.model.add(Dense(nb_classes))                                   #17 Dense层
            self.model.add(Activation('softmax'))                               #18 分类层,输出最终结果
            
            #输出模型概况
            self.model.summary()
            
        #训练模型
        def train(self, dataset, batch_size = 20, nb_epoch = 10, data_augmentation = True):        
            sgd = SGD(lr = 0.01, decay = 1e-6, 
                      momentum = 0.9, nesterov = True) #采用SGD+momentum的优化器进行训练,首先生成一个优化器对象  
            self.model.compile(loss='categorical_crossentropy',
                               optimizer=sgd,
                               metrics=['accuracy'])   #完成实际的模型配置工作
            
            #不使用数据提升,所谓的提升就是从我们提供的训练数据中利用旋转、翻转、加噪声等方法创造新的
            #训练数据,有意识的提升训练数据规模,增加模型训练量
            if not data_augmentation:            
                self.model.fit(dataset.train_images,
                               dataset.train_labels,
                               batch_size = batch_size,
                               nb_epoch = nb_epoch,
                               validation_data = (dataset.valid_images, dataset.valid_labels),
                               shuffle = True)
            #使用实时数据提升
            else:            
                #定义数据生成器用于数据提升,其返回一个生成器对象datagen,datagen每被调用一
                #次其生成一组数据(顺序生成),节省内存,其实就是python的数据生成器
                datagen = ImageDataGenerator(
                    featurewise_center = False,             #是否使输入数据去中心化(均值为0),
                    samplewise_center  = False,             #是否使输入数据的每个样本均值为0
                    featurewise_std_normalization = False,  #是否数据标准化(输入数据除以数据集的标准差)
                    samplewise_std_normalization  = False,  #是否将每个样本数据除以自身的标准差
                    zca_whitening = False,                  #是否对输入数据施以ZCA白化
                    rotation_range = 20,                    #数据提升时图片随机转动的角度(范围为0~180)
                    width_shift_range  = 0.2,               #数据提升时图片水平偏移的幅度(单位为图片宽度的占比,0~1之间的浮点数)
                    height_shift_range = 0.2,               #同上,只不过这里是垂直
                    horizontal_flip = True,                 #是否进行随机水平翻转
                    vertical_flip = False)                  #是否进行随机垂直翻转
    
                #计算整个训练样本集的数量以用于特征值归一化、ZCA白化等处理
                datagen.fit(dataset.train_images)                        
    
                #利用生成器开始训练模型
                self.model.fit_generator(datagen.flow(dataset.train_images, dataset.train_labels,
                                                       batch_size = batch_size),
                                         samples_per_epoch = dataset.train_images.shape[0],
                                         nb_epoch = nb_epoch,
                                         validation_data = (dataset.valid_images, dataset.valid_labels))    
        
        MODEL_PATH = './liziqiang.face.model.h5'
        def save_model(self, file_path = MODEL_PATH):
             self.model.save(file_path)
     
        def load_model(self, file_path = MODEL_PATH):
             self.model = load_model(file_path)
    
        def evaluate(self, dataset):
             score = self.model.evaluate(dataset.test_images, dataset.test_labels, verbose = 1)
             print("%s: %.2f%%" % (self.model.metrics_names[1], score[1] * 100))
    
        #识别人脸
        def face_predict(self, image):    
            #依然是根据后端系统确定维度顺序
            if K.image_dim_ordering() == 'th' and image.shape != (1, 3, IMAGE_SIZE, IMAGE_SIZE):
                image = resize_image(image)                             #尺寸必须与训练集一致都应该是IMAGE_SIZE x IMAGE_SIZE
                image = image.reshape((1, 3, IMAGE_SIZE, IMAGE_SIZE))   #与模型训练不同,这次只是针对1张图片进行预测    
            elif K.image_dim_ordering() == 'tf' and image.shape != (1, IMAGE_SIZE, IMAGE_SIZE, 3):
                image = resize_image(image)
                image = image.reshape((1, IMAGE_SIZE, IMAGE_SIZE, 3))                    
            
            #浮点并归一化
            image = image.astype('float32')
            image /= 255
            
            #给出输入属于各个类别的概率,我们是二值类别,则该函数会给出输入图像属于0和1的概率各为多少
            result = self.model.predict_proba(image)
            print('result:', result)
            
            #给出类别预测:0或者1
            result = self.model.predict_classes(image)        
    
            #返回类别预测结果
            return result[0]
    
    
    
    
    
    
    
        
    if __name__ == '__main__':
        dataset = Dataset('./data/')    
        dataset.load()
        
        model = Model()
        model.build_model(dataset)
        
        #先前添加的测试build_model()函数的代码
        model.build_model(dataset)
    
        #测试训练函数的代码
        model.train(dataset)
        
        
    if __name__ == '__main__':
        dataset = Dataset('./data/')    
        dataset.load()
        
        model = Model()
        model.build_model(dataset)
        model.train(dataset)
        model.save_model(file_path = './model/liziqiang.face.model.h5')
        
        
    if __name__ == '__main__':    
        dataset = Dataset('./data/')    
        dataset.load()
    
        
        #评估模型
        model = Model()
        model.load_model(file_path = './model/liziqiang.face.model.h5')
        model.evaluate(dataset)    
        
        
        
        
        

    运行程序结果:

    可以看到,最后我们对数据进行了验证,准确率达到了99.83%。结果较为理想,并且在model下面也得到了我i们的训练数据。

    由此,我们最重要的训练数据也完成了,加下来就是验证我们训练数据的效果的时候了。

     

     

    3、识别人脸

     

    新建python文件,命名:Face_recognition。代码如下:

    #-*- coding: utf-8 -*-
    
    import cv2
    import sys
    import gc
    from face_train import Model
    
    if __name__ == '__main__':
        if len(sys.argv) != 1:
            print("Usage:%s camera_id\r\n" % (sys.argv[0]))
            sys.exit(0)
            
        #加载模型
        model = Model()
        model.load_model(file_path = './model/liziqiang.face.model.h5')    
                  
        #框住人脸的矩形边框颜色       
        color = (0, 255, 0)
        
        #捕获指定摄像头的实时视频流
        cap = cv2.VideoCapture(0)
        
        #人脸识别分类器本地存储路径
        cascade_path = "H:\\opencv\\opencv\\build\\etc\\haarcascades\\haarcascade_frontalface_alt2.xml"    
        
        #循环检测识别人脸
        while True:
            ret, frame = cap.read()   #读取一帧视频
            
            if ret is True:
                
                #图像灰化,降低计算复杂度
                frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
            else:
                continue
            #使用人脸识别分类器,读入分类器
            cascade = cv2.CascadeClassifier(cascade_path)                
    
            #利用分类器识别出哪个区域为人脸
            faceRects = cascade.detectMultiScale(frame_gray, scaleFactor = 1.2, minNeighbors = 3, minSize = (32, 32))        
            if len(faceRects) > 0:                 
                for faceRect in faceRects: 
                    x, y, w, h = faceRect
                    
                    #截取脸部图像提交给模型识别这是谁
                    image = frame[y - 10: y + h + 10, x - 10: x + w + 10]
                    faceID = model.face_predict(image)   
                    
                    #如果是“我”
                    if faceID == 0:                                                        
                        cv2.rectangle(frame, (x - 10, y - 10), (x + w + 10, y + h + 10), color, thickness = 2)
                        
                        #文字提示是谁
                        cv2.putText(frame,'liziqiang', 
                                    (x + 30, y + 30),                      #坐标
                                    cv2.FONT_HERSHEY_SIMPLEX,              #字体
                                    1,                                     #字号
                                    (255,0,255),                           #颜色
                                    2)                                     #字的线宽
                    else:
                        pass
                                
            cv2.imshow("识别朕", frame)
            
            #等待10毫秒看是否有按键输入
            k = cv2.waitKey(10)
            #如果输入q则退出循环
            if k & 0xFF == ord('q'):
                break
    
        #释放摄像头并销毁所有窗口
        cap.release()
        cv2.destroyAllWindows()

     

    好,最终实现的结果就是文章开头的时候的效果。希望能帮到你!

    有什么问题我会尽量回答!

    展开全文
  • 基于Opencv快速实现人脸识别(完整版)

    万次阅读 多人点赞 2019-12-31 16:22:22
    上篇博客:...这次进一步地研究这一块的知识,来一波真正意义上的人脸识别,查询的资料可能有点过时,但基本思想是没有毛病的,对一些函数也进行了更新,保证了功能的正常实...

    上篇博客:https://blog.csdn.net/beyond9305/article/details/92844258严格来说标题是有误的,只是单纯地对人脸进行了检测,而并非识别,opencv内置了检测分类器和识别器,这二者还是有很大不同的。

    这次进一步地研究这一块的知识,来一波真正意义上的人脸识别,查询的资料可能有点过时,但基本思想是没有毛病的,对一些函数也进行了更新,保证了功能的正常实现。那就开始吧:

    首先看一下本实验需要的数据集,为了简便我们只进行两个人的识别,选取了我的偶像beyond乐队的主唱黄家驹和贝斯手黄家强,这哥俩长得有几分神似,这也是对人脸识别的一个考验:

    两个文件夹,一个为训练数据集,一个为测试数据集,训练数据集中有两个文件夹0和1,之前看一些资料有说这里要遵循“slabel”命名规则,但后面处理起来比较麻烦,因为目前opencv接受的人脸识别标签为整数,那我们就直接用整数命名吧:

    为了简便,我们每个人用20张照片来训练,0代表黄家驹,1代表黄家强:

    接下来就正式开始吧:

    1. 检测人脸。这应该是最基本的,给我们一张图片,我们要先检测出人脸的区域,然后才能进行操作,opencv已经内置了很多分类检测器,我们这次用haar:

    def detect_face(img):
        #将测试图像转换为灰度图像,因为opencv人脸检测器需要灰度图像
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    
        #加载OpenCV人脸检测分类器Haar
        face_cascade = cv2.CascadeClassifier('./haarcascade_frontalface_default.xml')
    
        #检测多尺度图像,返回值是一张脸部区域信息的列表(x,y,宽,高)
        faces = face_cascade.detectMultiScale(gray, scaleFactor=1.2, minNeighbors=5)
    
        # 如果未检测到面部,则返回原始图像
        if (len(faces) == 0):
            return None, None
    
        #目前假设只有一张脸,xy为左上角坐标,wh为矩形的宽高
        (x, y, w, h) = faces[0]
    
        #返回图像的正面部分
        return gray[y:y + w, x:x + h], faces[0]

    2. 有了数据集和检测人脸的功能后,我们就可以进行图片预训练处理了,最后返回所有训练图片的人脸检测信息和标签:

    def prepare_training_data(data_folder_path):
        # 获取数据文件夹中的目录(每个主题的一个目录)
        dirs = os.listdir(data_folder_path)
    
        # 两个列表分别保存所有的脸部和标签
        faces = []
        labels = []
    
        # 浏览每个目录并访问其中的图像
        for dir_name in dirs:
            # dir_name(str类型)即标签
            label = int(dir_name)
            # 建立包含当前主题主题图像的目录路径
            subject_dir_path = data_folder_path + "/" + dir_name
            # 获取给定主题目录内的图像名称
            subject_images_names = os.listdir(subject_dir_path)
    
            # 浏览每张图片并检测脸部,然后将脸部信息添加到脸部列表faces[]
            for image_name in subject_images_names:
                # 建立图像路径
                image_path = subject_dir_path + "/" + image_name
                # 读取图像
                image = cv2.imread(image_path)
                # 显示图像0.1s
                cv2.imshow("Training on image...", image)
                cv2.waitKey(100)
    
                # 检测脸部
                face, rect = detect_face(image)
                # 我们忽略未检测到的脸部
                if face is not None:
                    #将脸添加到脸部列表并添加相应的标签
                    faces.append(face)
                    labels.append(label)
    
        cv2.waitKey(1)
        cv2.destroyAllWindows()
        #最终返回值为人脸和标签列表
        return faces, labels

    3. 有了脸部信息和对应标签后,我们就可以使用opencv自带的识别器来进行训练了:

    #调用prepare_training_data()函数
    faces, labels = prepare_training_data("training_data")
    
    #创建LBPH识别器并开始训练,当然也可以选择Eigen或者Fisher识别器
    face_recognizer = cv2.face.LBPHFaceRecognizer_create()
    face_recognizer.train(faces, np.array(labels))

    4.训练完毕后就可以进行预测了,在这之前我们可以设定一下预测的格式,包括用矩形框框出人脸并标出其名字,当然最后别忘了建立标签与真实姓名直接的映射表:

    #根据给定的(x,y)坐标和宽度高度在图像上绘制矩形
    def draw_rectangle(img, rect):
        (x, y, w, h) = rect
        cv2.rectangle(img, (x, y), (x + w, y + h), (128, 128, 0), 2)
    # 根据给定的(x,y)坐标标识出人名
    def draw_text(img, text, x, y):
        cv2.putText(img, text, (x, y), cv2.FONT_HERSHEY_COMPLEX, 1, (128, 128, 0), 2)
    
    #建立标签与人名的映射列表(标签只能为整数)
    subjects = ["jiaju", "jiaqiang"]
    

    5.现在就可以定义我们的预测函数了:

    # 此函数识别传递的图像中的人物并在检测到的脸部周围绘制一个矩形及其名称
    def predict(test_img):
        #生成图像的副本,这样就能保留原始图像
        img = test_img.copy()
        #检测人脸
        face, rect = detect_face(img)
        #预测人脸
        label = face_recognizer.predict(face)
        # 获取由人脸识别器返回的相应标签的名称
        label_text = subjects[label[0]]
    
        # 在检测到的脸部周围画一个矩形
        draw_rectangle(img, rect)
        # 标出预测的名字
        draw_text(img, label_text, rect[0], rect[1] - 5)
        #返回预测的图像
        return img
    

    6.最后使用我们test_data中的图片进行预测并显示最终效果:

    #加载测试图像
    test_img1 = cv2.imread("test_data/test1.jpg")
    test_img2 = cv2.imread("test_data/test2.jpg")
    
    #执行预测
    predicted_img1 = predict(test_img1)
    predicted_img2 = predict(test_img2)
    
    #显示两个图像
    cv2.imshow(subjects[0], predicted_img1)
    cv2.imshow(subjects[1], predicted_img2)

    来看看识别结果如何:

    这就是人脸识别最基本的流程,后续还会进一步的研究,下一篇我们将讨论本次实验的一些细节和注意事项,算是对本篇的一次挖掘和总结吧。最后附上完整代码:

    # # -*- coding:utf-8 -*-
    import cv2
    import os
    import numpy as np
    
    # 检测人脸
    def detect_face(img):
        #将测试图像转换为灰度图像,因为opencv人脸检测器需要灰度图像
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    
        #加载OpenCV人脸检测分类器Haar
        face_cascade = cv2.CascadeClassifier('./haarcascade_frontalface_default.xml')
    
        #检测多尺度图像,返回值是一张脸部区域信息的列表(x,y,宽,高)
        faces = face_cascade.detectMultiScale(gray, scaleFactor=1.2, minNeighbors=5)
    
        # 如果未检测到面部,则返回原始图像
        if (len(faces) == 0):
            return None, None
    
        #目前假设只有一张脸,xy为左上角坐标,wh为矩形的宽高
        (x, y, w, h) = faces[0]
    
        #返回图像的正面部分
        return gray[y:y + w, x:x + h], faces[0]
    
    
    # 该函数将读取所有的训练图像,从每个图像检测人脸并将返回两个相同大小的列表,分别为脸部信息和标签
    def prepare_training_data(data_folder_path):
        # 获取数据文件夹中的目录(每个主题的一个目录)
        dirs = os.listdir(data_folder_path)
    
        # 两个列表分别保存所有的脸部和标签
        faces = []
        labels = []
    
        # 浏览每个目录并访问其中的图像
        for dir_name in dirs:
            # dir_name(str类型)即标签
            label = int(dir_name)
            # 建立包含当前主题主题图像的目录路径
            subject_dir_path = data_folder_path + "/" + dir_name
            # 获取给定主题目录内的图像名称
            subject_images_names = os.listdir(subject_dir_path)
    
            # 浏览每张图片并检测脸部,然后将脸部信息添加到脸部列表faces[]
            for image_name in subject_images_names:
                # 建立图像路径
                image_path = subject_dir_path + "/" + image_name
                # 读取图像
                image = cv2.imread(image_path)
                # 显示图像0.1s
                cv2.imshow("Training on image...", image)
                cv2.waitKey(100)
    
                # 检测脸部
                face, rect = detect_face(image)
                # 我们忽略未检测到的脸部
                if face is not None:
                    #将脸添加到脸部列表并添加相应的标签
                    faces.append(face)
                    labels.append(label)
    
        cv2.waitKey(1)
        cv2.destroyAllWindows()
        #最终返回值为人脸和标签列表
        return faces, labels
    
    #调用prepare_training_data()函数
    faces, labels = prepare_training_data("training_data")
    
    #创建LBPH识别器并开始训练,当然也可以选择Eigen或者Fisher识别器
    face_recognizer = cv2.face.LBPHFaceRecognizer_create()
    face_recognizer.train(faces, np.array(labels))
    
    #根据给定的(x,y)坐标和宽度高度在图像上绘制矩形
    def draw_rectangle(img, rect):
        (x, y, w, h) = rect
        cv2.rectangle(img, (x, y), (x + w, y + h), (128, 128, 0), 2)
    # 根据给定的(x,y)坐标标识出人名
    def draw_text(img, text, x, y):
        cv2.putText(img, text, (x, y), cv2.FONT_HERSHEY_COMPLEX, 1, (128, 128, 0), 2)
    
    #建立标签与人名的映射列表(标签只能为整数)
    subjects = ["jiaju", "jiaqiang"]
    
    # 此函数识别传递的图像中的人物并在检测到的脸部周围绘制一个矩形及其名称
    def predict(test_img):
        #生成图像的副本,这样就能保留原始图像
        img = test_img.copy()
        #检测人脸
        face, rect = detect_face(img)
        #预测人脸
        label = face_recognizer.predict(face)
        # 获取由人脸识别器返回的相应标签的名称
        label_text = subjects[label[0]]
    
        # 在检测到的脸部周围画一个矩形
        draw_rectangle(img, rect)
        # 标出预测的名字
        draw_text(img, label_text, rect[0], rect[1] - 5)
        #返回预测的图像
        return img
    
    #加载测试图像
    test_img1 = cv2.imread("test_data/test1.jpg")
    test_img2 = cv2.imread("test_data/test2.jpg")
    
    #执行预测
    predicted_img1 = predict(test_img1)
    predicted_img2 = predict(test_img2)
    
    #显示两个图像
    cv2.imshow(subjects[0], predicted_img1)
    cv2.imshow(subjects[1], predicted_img2)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

    ps:6.30号就是家驹去世二十六周年了,希望成都的小伙伴们能聚一下,让我们一起缅怀。

    如果文章对您有一点点帮助,还请打赏一二,您的鼓励将是我前进的不竭动力

    公众号为“非著名IT表演艺术家”,比较中二,就是灵光一闪,然后这个名字就冒出来了……

    展开全文
  • 人脸识别算法原理过程详解

    千次阅读 2019-10-11 15:53:32
    最近,由于工作需要,为了找到一款高效的人脸识别算法,对各种人脸识别算法都研究了一番,以下记录的是各算法的理论基础。 一.MTCNN 本文章主要介绍MTCNN算法的流程,MTCNN主要由三个框架组成,分别是PNet,RNet,...

    本文为转载内容,由于找不到源作者链接,故特此说明。

    人脸识别各算法详解

    最近,由于工作需要,为了找到一款高效的人脸识别算法,对各种人脸识别算法都研究了一番,以下记录的是各算法的理论基础。

    一.MTCNN

    本文章主要介绍MTCNN算法的流程,MTCNN主要由三个框架组成,分别是PNet,RNet,ONet。下面将分别介绍这三个部分。

    理论基础:
    在这里插入图片描述

    PNet

    Proposal Network (P-Net):该网络结构主要获得了人脸区域的候选窗口和边界框的回归向量。并用该边界框做回归,对候选窗口进行校准,然后通过非极大值抑制(NMS)来合并高度重叠的候选框。

    因为实际的图片大小不一,所以PNet是一个全卷积网络,对于输入的图片大小可以是任意值;将图片输入PNet之前,有一个循环,每一次循环会将图片进行缩放,再输入PNet;这样形成一个图片金字塔,图片每次缩放因子是0.80(论文的初始值应该是0.709),当宽高小于12时候这张图片对应的循环结束,12是PNet的最小图片输入尺寸。下图表示的是PNet的结构

    在这里插入图片描述
    由上面这张图可以得到,一张12x12x3的图片最终的输出的结果是1x1x32的特征图,再分成三条支路,用于人脸分类、边框回归、人脸特征点定位。

    这三条支路的损失函数分别是交叉熵(二分类问题常用)、平方和损失函数、5个特征点与标定好的数据的平方和损失。最后的总损失是三个损失乘上各自的权重比之和,在PNet里面三种损失的权重是1:0.5:0.5。将图片输入PNet后,得到了cls_cls_map, reg这两个数组,其中cls_cls_map是(H,W,2)的二维数组,就是非人脸和人脸的概率。PNet直接输出的边界框并不是传统回归中的边界坐标,而是预测人脸位置相对于输入图片的位置差,即为reg。

    将cls_cls_map里面人脸的概率和一个事先设定的阈值相比较,如果大于这个阈值,就将这张图片对应的reg数组里面的预测值提取出来,通过逆运算得到原始像素坐标。对于 x * y 的输入,将产生大小为[(x−12)/2+1]∗[(y−12)2+1]的输出。因为池化层的步长是2,所以上述式子的分母为2。将reg的坐标通过此方法可还原预测边框值在原始图片的像素坐标。最后返回的数组是(x1,y1,x2,y2,score,reg),其中(x1,y1,x2,y2)是bbox在原始图片中的像素坐标。score是cls_cls_map对应的人脸概率。

    完成这一步后,将使用非极大值抑制法(NMS)去掉一些重复框,这个算法的原理是将上一步返回的数组的score值最大的那一行元素提取出来,将剩下的所有的元素的score和一个设定好的阈值相比较,将score值大于阈值(0.5)的元素抛弃,再将剩下的元素重复之前的提取最大值并进行比较的操作。直到最后,这样就初步抛弃了那些重合度较高的人脸框。

    因为(x1,y1,x2,y2)是在原图像中的像素坐标,reg是候选框区域相对于像素坐标的偏差,这样讲将原像素坐标加上偏差值,即可得到候选框的坐标。将初步筛选后的的bbox按照上面的方法refine,到此为止,PNet这一部分就结束了。输出的是候选框的4个坐标加上对应的score值。

    RNet

    Refine Network (R-Net):该网络结构还是通过边界框回归和NMS来去掉那些false-positive区域。只是由于该网络结构和P-Net网络结构有差异,多了一个全连接层,所以会取得更好的抑制false-positive的作用。

    首先将PNet的输出resize成正方形,这主要是基于人脸一般都是正方形的。
    再将PNet生成的bbox里的元素调整一下,生成(dy, edy, dx, edx, y, ey, x, ex, tmpw, tmph)这样的数组;生成的元素的意义如下:

    	1>. dx,dy:bbox的相对本身起点坐标(0,0)。
    	2>. edx,edy:bbox的相对本身终点坐标(tmpw-1, tmph-1)。
    	3>. x,y : 原始图片的bbox起点。
    	4>. ex,ey:原始图片的bbox结束点。
    

    在生成的过程还会有检测,避免bbox的坐标超出原始图片或者为负值;接下来遍历这个数组,将里面的bbox从原始图片里面抠出来,resize成24x24同时进行归一化。

    完成了前面的操作后就是将24x24的图片喂入RNet了,下图表示的是RNet的结构:
    在这里插入图片描述
    可以看出RNet最后是采用的全连接层,这也是为什么喂入的图片统一成24x24大小。

    由上面这张图可以得到,一张24x24x3的图片最终的输出的结果是3x3x64的特征图,再经历全连接层后分成三条支路,用于人脸分类、边框回归、人脸特征点定位。这三条支路的损失函数和PNet的一样,各损失的权重比也为1:0.5:0.5。

    将图片输入RNet后,得到了cls_scores, reg这两个数组,cls_scores表示非人脸和人脸的概率,reg表示bbox的回归信息。同样将cls_scores中人脸的概率与实现设定的阈值比较,将大于阈值的图片对应的bbox提取出来,过滤掉一部分非人脸的bbox。

    接着再次调用NMS,抛弃掉大量的重叠率高的人脸框,经过两次的筛选,剩下的bbox的数量就少了很多。最后进行RNet的最后一步操作,就是回归信息reg来调整bbox的坐标,大致就是将bbox的4个坐标乘上bbox的宽或者高,其中x和宽相乘,y和高相乘。最后就是返回调整后的四个坐标

    ONet

    Output Network (O-Net):该层比R-Net层又多了一层卷基层,所以处理的结果会更加精细。作用和R-Net层作用一样。但是该层对人脸区域进行了更多的监督,同时还会输出5个地标(landmark)。
    首先将RNet的输出resize成正方形,接下来的操作和对应的RNet部分相似,只是再喂入ONet之前图片是resize乘48x48。

    在这里插入图片描述
    将48x48x3的图片喂入ONet后输出的是3x3x128的特征图,经过全连接层后同样是有着三条支路。三条支路的损失函数与PNet、RNet一样,但是三个损失函数的权重比为1:0.5:1。

    这次从ONet的输出接受cls_scores, reg, landmark这三个数组,同样先根据cls_scores的人脸概率是否大于设定的阈值来抛弃一部分非人脸框。接下来就是确定landmark的值,因为前面直接得到的关键点的x、y坐标相关信息并不是x、y的值,而是一个相对于宽高的偏置值,最终的关键点的x、y值可以通过这个偏置值和bbox的宽或者高(x与宽,y与高)相乘再与bbox的坐标相加得到。

    接下来就是回归信息reg来调整bbox的坐标,与RNet输出前的操作一样。完成之后经历两次的NMS操作,但是这次的NMS操作与之前的略有不用,大家可以看详细的代码解释。最后就可以输出bbox和landmark了,至此算法就结束了。

    二.PFLD

    PFLD算法,目前主流数据集上达到最高精度、ARM安卓机140fps,模型大小仅2.1M!

    算法思想:
    在这里插入图片描述

    其中,黄色曲线包围的是主网络,用于预测特征点的位置;
    绿色曲线包围的部分为辅网络,在训练时预测人脸姿态(有文献表明给网络加这个辅助任务可以提高定位精度,具体参考原论文),这部分在测试时不需要。

    对于上述影响精度的挑战,修改loss函数在训练时关注那些稀有样本,而提高计算速度和减小模型size则是使用轻量级模型。

    Loss函数设计

    Loss函数用于神经网络在每次训练时预测的形状和标注形状的误差。
    考虑到样本的不平衡,作者希望能对那些稀有样本赋予更高的权重,这种加权的Loss函数被表达为:
    在这里插入图片描述
    M为样本个数,N为特征点个数,Yn为不同的权重,|| * ||为特征点的距离度量(L1或L2距离)。(以Y代替公式里的希腊字母)

    进一步细化Yn:
    在这里插入图片描述
    其中:
    在这里插入图片描述
    即为最终的样本权重。
    K=3,这一项代表着人脸姿态的三个维度,即yaw, pitch, roll 角度,可见角度越高,权重越大。
    C为不同的人脸类别数,作者将人脸分成多个类别,比如侧脸、正脸、抬头、低头、表情、遮挡等,w为与类别对应的给定权重,如果某类别样本少则给定权重大。

    主网络

    作者使用轻量级的MobileNet,其参数如下:
    在这里插入图片描述

    辅网络

    参数如下:
    在这里插入图片描述

    验证结果如下:
    在这里插入图片描述

    三. 虹软人脸识别算法

    虹软属于人脸检测技术,能够帮助您检测并且定位到影像(图片或者视频)中的人脸。

    本文将以这三个库为基础,从人脸注册开始,到人脸识别结束。全程演示人脸识别的流程。

    人脸信息是保存在AFR_FSDKFace类中的。这的主要结构为

      public static final int FEATURE_SIZE = 22020;
      byte[] mFeatureData;
    

    如果要进行人脸注册,我们需要定义另外一个类来把人脸信息和姓名关联起来。

    class FaceRegist {
            String mName;
            List<AFR_FSDKFace> mFaceList;
    
        public FaceRegist(String name) {
            mName = name;
            mFaceList = new ArrayList<>();
        }
    }
    

    包含特征信息的长度和内容的byte数组。
    我们把这些功能定义在类FaceDB中。FaceDB需要包含引擎定义,初始化,把人脸信息保存在版本库和从版本库中读出人脸信息这些功能

    初始化引擎
    为了程序结构性考虑,我们将人脸识别相关的代码独立出来一个类FaceDB,并定义必要的变量

    	public static String appid = "bCx99etK9Ns4Saou1EbFdC18xHdY9817EKw****";
    	public static String ft_key = "CopwZarSihp1VBu5AyGxfuLQdRMPyoGV2C2opc****";
    	public static String fd_key = "CopwZarSihp1VBu5AyGxfuLXnpccQbWAjd86S8****";
    	public static String fr_key = "CopwZarSihp1VBu5AyGxfuLexDsi8yyELdgsj4****";
    	 
    	 
    	String mDBPath;
    	List<FaceRegist> mRegister;
    	AFR_FSDKEngine mFREngine;
    	AFR_FSDKVersion mFRVersion;
    
    • 定义有参数的构造函数来初始化引擎
    public FaceDB(String path) {
            mDBPath = path;
            mRegister = new ArrayList<>();
            mFRVersion = new AFR_FSDKVersion();
            mUpgrade = false;
            mFREngine = new AFR_FSDKEngine();
            AFR_FSDKError error = mFREngine.AFR_FSDK_InitialEngine(FaceDB.appid, FaceDB.fr_key);
            if (error.getCode() != AFR_FSDKError.MOK) {
                Log.e(TAG, "AFR_FSDK_InitialEngine fail! error code :" + error.getCode());
            } else {
                mFREngine.AFR_FSDK_GetVersion(mFRVersion);
                Log.d(TAG, "AFR_FSDK_GetVersion=" + mFRVersion.toString());
            }
        }
    

    定义析构函数释放引擎占用的系统资源

    public void destroy() {
            if (mFREngine != null) {
                mFREngine.AFR_FSDK_UninitialEngine();
            }
     
        }
    

    实现人脸增加和读取功能
    通常人脸库会存放在数据库中,本次我们使用List来进行简单的模拟,并将其保存在文本文件中,需要时从文本中读取,保存时写入到文件中。

    我们使用addFace方法将待注册的人脸信息添加到人脸库中

    public  void addFace(String name, AFR_FSDKFace face) {
            try {
                //check if already registered.
                boolean add = true;
                for (FaceRegist frface : mRegister) {
                    if (frface.mName.equals(name)) {
                        frface.mFaceList.add(face);
                        add = false;
                        break;
                    }
                }
                if (add) { // not registered.
                    FaceRegist frface = new FaceRegist(name);
                    frface.mFaceList.add(face);
                    mRegister.add(frface);
                }
     
                if (!new File(mDBPath + "/face.txt").exists()) {
                    if (!saveInfo()) {
                        Log.e(TAG, "save fail!");
                    }
                }
     
                //save name
                FileOutputStream fs = new FileOutputStream(mDBPath + "/face.txt", true);
                ExtOutputStream bos = new ExtOutputStream(fs);
                bos.writeString(name);
                bos.close();
                fs.close();
     
                //save feature
                fs = new FileOutputStream(mDBPath + "/" + name + ".data", true);
                bos = new ExtOutputStream(fs);
                bos.writeBytes(face.getFeatureData());
                bos.close();
                fs.close();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    

    使用loadFaces从文件中读取人脸

    public boolean loadFaces(){
            if (loadInfo()) {
                try {
                    for (FaceRegist face : mRegister) {
                        Log.d(TAG, "load name:" + face.mName + "'s face feature data.");
                        FileInputStream fs = new FileInputStream(mDBPath + "/" + face.mName + ".data");
                        ExtInputStream bos = new ExtInputStream(fs);
                        AFR_FSDKFace afr = null;
                        do {
                            if (afr != null) {
                                if (mUpgrade) {
                                    //upgrade data.
                                }
                                face.mFaceList.add(afr);
                            }
                            afr = new AFR_FSDKFace();
                        } while (bos.readBytes(afr.getFeatureData()));
                        bos.close();
                        fs.close();
                    }
                    return true;
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            } else {
                if (!saveInfo()) {
                    Log.e(TAG, "save fail!");
                }
            }
            return false;
        }
    

    实现业务逻辑
    实现人脸注册功能
    人脸识别的前提条件就是人脸信息要先注册到人脸库中,注册人脸库

    第一步当然是获取待注册的照片,我们可以可以使用摄像头,也可以使用照片。我们使用AlertDialog弹出选择框

    new AlertDialog.Builder(this)
                            .setTitle("请选择注册方式")
                            .setIcon(android.R.drawable.ic_dialog_info)
                            .setItems(new String[]{"打开图片", "拍摄照片"}, this)
                            .show();
    

    在对应的事件处理函数中进行处理

    switch (which){
        case 1://摄像头
            Intent getImageByCamera = new Intent("android.media.action.IMAGE_CAPTURE");
            ContentValues values = new ContentValues(1);
            values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
            mPath = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
            getImageByCamera.putExtra(MediaStore.EXTRA_OUTPUT, mPath);
            startActivityForResult(getImageByCamera, REQUEST_CODE_IMAGE_CAMERA);
            break;
        case 0://图片
            Intent getImageByalbum = new Intent(Intent.ACTION_GET_CONTENT);
            getImageByalbum.addCategory(Intent.CATEGORY_OPENABLE);
            getImageByalbum.setType("image/jpeg");
            startActivityForResult(getImageByalbum, REQUEST_CODE_IMAGE_OP);
            break;
        default:;
    }
    

    获取一张照片后,后续我们就需要实现人脸检测功能。

        if (requestCode == REQUEST_CODE_IMAGE_OP && resultCode == RESULT_OK) {
                mPath = data.getData();
                String file = getPath(mPath);
                //TODO: add image coversion
            }
    

    在上面的代码中,我们获取到了我们需要的图像数据bmp,把图片取出来
    我们在Application类用函数 decodeImage中实现这段代码

    public static Bitmap decodeImage(String path) {
            Bitmap res;
            try {
                ExifInterface exif = new ExifInterface(path);
                int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
     
                BitmapFactory.Options op = new BitmapFactory.Options();
                op.inSampleSize = 1;
                op.inJustDecodeBounds = false;
                //op.inMutable = true;
                res = BitmapFactory.decodeFile(path, op);
                //rotate and scale.
                Matrix matrix = new Matrix();
    
            if (orientation == ExifInterface.ORIENTATION_ROTATE_90) {
                matrix.postRotate(90);
            } else if (orientation == ExifInterface.ORIENTATION_ROTATE_180) {
                matrix.postRotate(180);
            } else if (orientation == ExifInterface.ORIENTATION_ROTATE_270) {
                matrix.postRotate(270);
            }
    
            Bitmap temp = Bitmap.createBitmap(res, 0, 0, res.getWidth(), res.getHeight(), matrix, true);
            Log.d("com.arcsoft", "check target Image:" + temp.getWidth() + "X" + temp.getHeight());
    
            if (!temp.equals(res)) {
                res.recycle();
            }
            return temp;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    

    调用AFD_FSDK_StillImageFaceDetection返回检测到的人脸信息
    人脸注册 ,首先要先检测出来人脸,对于静态图片,虹软人脸SDK中对应的是FD,提供了一个方法名称,叫AFD_FSDK_StillImageFaceDetection 。
    我们来看一下参数列表

    类型 名称 说明
    byte[] data 输入的图像数据
    int width 图像宽度
    int height 图像高度
    int format 图像格式
    List<AFD_FSDKFace> list 检测到的人脸会放到到该列表里。
    注意AFD_FSDKFace对象引擎内部重复使用,如需保存,请clone一份AFD_FSDKFace对象或另外保存

    AFD_FSDKFace是人脸识别的结果,定义如下

    public class AFD_FSDKFace {
        Rect mRect;
        int mDegree;
        }
    

    mRect定义一个了一个矩形框Rect

    在此之前我们需要注意虹软人脸SDK使用的图像格式是NV21的格式,所以我们需要将获取到的图像转化为对应的格式。在Android_extend.jar中提供了对应的转换函数

    byte[] data = new byte[mBitmap.getWidth() * mBitmap.getHeight() * 3 / 2];
                ImageConverter convert = new ImageConverter();
                convert.initial(mBitmap.getWidth(), mBitmap.getHeight(), ImageConverter.CP_PAF_NV21);
                if (convert.convert(mBitmap, data)) {
                    Log.d(TAG, "convert ok!");
                }
                convert.destroy();
    

    现在我们就可以调用AFD_FSDK_StillImageFaceDetection方法了

    err  = engine.AFD_FSDK_StillImageFaceDetection(data, mBitmap.getWidth(), mBitmap.getHeight(), AFD_FSDKEngine.CP_PAF_NV21, result);
    

    绘出人脸框
    在List<AFD_FSDKFace>中保存了检测到的人脸的位置信息和深度信息。
    我们可以将检测到的人脸位置信息在图片上用一个矩形框绘制出来表示检测到的人脸信息。

    Canvas canvas = mSurfaceHolder.lockCanvas();
       if (canvas != null) {
          Paint mPaint = new Paint();
          boolean fit_horizontal = canvas.getWidth() / (float)src.width() < canvas.getHeight() / (float)src.height() ? true : false;
          float scale = 1.0f;
          if (fit_horizontal) {
             scale = canvas.getWidth() / (float)src.width();
             dst.left = 0;
             dst.top = (canvas.getHeight() - (int)(src.height() * scale)) / 2;
             dst.right = dst.left + canvas.getWidth();
             dst.bottom = dst.top + (int)(src.height() * scale);
          } else {
             scale = canvas.getHeight() / (float)src.height();
             dst.left = (canvas.getWidth() - (int)(src.width() * scale)) / 2;
             dst.top = 0;
             dst.right = dst.left + (int)(src.width() * scale);
             dst.bottom = dst.top + canvas.getHeight();
          }
          canvas.drawBitmap(mBitmap, src, dst, mPaint);
          canvas.save();
          canvas.scale((float) dst.width() / (float) src.width(), (float) dst.height() / (float) src.height());
          canvas.translate(dst.left / scale, dst.top / scale);
          for (AFD_FSDKFace face : result) {
             mPaint.setColor(Color.RED);
             mPaint.setStrokeWidth(10.0f);
             mPaint.setStyle(Paint.Style.STROKE);
             canvas.drawRect(face.getRect(), mPaint);
          }
          canvas.restore();
          mSurfaceHolder.unlockCanvasAndPost(canvas);
          break;
       }
    }
    

    将人脸注册到人脸库
    检测到了人脸,我们可以输入相应的描述信息,加入到人脸库中。

    为了提高识别的准确性,我们可以对一个人多次注册人脸信息。

    public  void addFace(String name, AFR_FSDKFace face) {
       try {
          //check if already registered.
          boolean add = true;
          for (FaceRegist frface : mRegister) {
             if (frface.mName.equals(name)) {
                frface.mFaceList.add(face);
                add = false;
                break;
             }
          }
          if (add) { // not registered.
         FaceRegist frface = new FaceRegist(name);
         frface.mFaceList.add(face);
         mRegister.add(frface);
      }
    
      if (!new File(mDBPath + "/face.txt").exists()) {
         if (!saveInfo()) {
            Log.e(TAG, "save fail!");
         }
      }
      //save name
      FileOutputStream fs = new FileOutputStream(mDBPath + "/face.txt", true);
      ExtOutputStream bos = new ExtOutputStream(fs);
      bos.writeString(name);
      bos.close();
      fs.close();
      //save feature
      fs = new FileOutputStream(mDBPath + "/" + name + ".data", true);
      bos = new ExtOutputStream(fs);
      bos.writeBytes(face.getFeatureData());
      bos.close();
      fs.close();
       } catch (FileNotFoundException e) {
          e.printStackTrace();
       } catch (IOException e) {
          e.printStackTrace();
       }
    }
    

    最后,别忘记了销毁人脸检测引擎哦

    err = engine.AFD_FSDK_UninitialFaceEngine(); 
    Log.d("com.arcsoft", "AFD_FSDK_UninitialFaceEngine =" + err.getCode()); 
    

    实现人脸识别
    上面的代码准备完毕后,就可以开始我们的人脸识别的功能了。我们使用一个第三方的扩展库,ExtGLSurfaceView的扩展 库CameraGLSurfaceView,用ImageView和TextView显示检测到的人脸和相应的描述信息。

    首先是定义layout。

    <?xml version="1.0" encoding="utf-8"?>
    

     

    <com.guo.android_extend.widget.CameraSurfaceView
        android:id="@+id/surfaceView"
        android:layout_width="1dp"
        android:layout_height="1dp"/>
    
    <com.guo.android_extend.widget.CameraGLSurfaceView
        android:id="@+id/glsurfaceView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_centerInParent="true"/>
    
    <ImageView
        android:id="@+id/imageView"
        android:layout_width="120dp"
        android:layout_height="120dp"
        android:layout_marginLeft="10dp"
        android:layout_marginTop="10dp"/>
    
    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/imageView"
        android:layout_alignRight="@+id/imageView"
        android:layout_below="@+id/imageView"
        android:layout_marginTop="10dp"
        android:text="@string/app_name"
        android:textAlignment="center"/>
    
    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/imageView"
        android:layout_alignRight="@+id/imageView"
        android:layout_below="@+id/textView"
        android:layout_marginTop="10dp"
        android:text="@string/app_name"
        android:textAlignment="center"/>
    

    因为引擎需要的图像格式是NV21的,所以需要将摄像头中的图像格式预设置为NV21

    public Camera setupCamera() {
       // TODO Auto-generated method stub
       mCamera = Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK);
       try {
          Camera.Parameters parameters = mCamera.getParameters();
          parameters.setPreviewSize(mWidth, mHeight);
          parameters.setPreviewFormat(ImageFormat.NV21);
     
          for( Camera.Size size : parameters.getSupportedPreviewSizes()) {
             Log.d(TAG, "SIZE:" + size.width + "x" + size.height);
          }
          for( Integer format : parameters.getSupportedPreviewFormats()) {
         Log.d(TAG, "FORMAT:" + format);
      }
    
      List<int[]> fps = parameters.getSupportedPreviewFpsRange();
      for(int[] count : fps) {
         Log.d(TAG, "T:");
         for (int data : count) {
            Log.d(TAG, "V=" + data);
         }
      }
          mCamera.setParameters(parameters);
       } catch (Exception e) {
          e.printStackTrace();
       }
       if (mCamera != null) {
          mWidth = mCamera.getParameters().getPreviewSize().width;
          mHeight = mCamera.getParameters().getPreviewSize().height;
       }
       return mCamera;
    }
    

    从摄像头识别人脸,需要使用FT库,FT库在人脸跟踪算法上对人脸检测部分进行了优化,是专门为视频处理而优化的库。

    初始化人脸检测引擎(FT)
    和FD一样,我们需要初始化人脸识别FT引擎。

    Log.d(TAG, "AFT_FSDK_InitialFaceEngine =" + err.getCode());
    err = engine.AFT_FSDK_GetVersion(version);
    Log.d(TAG, "AFT_FSDK_GetVersion:" + version.toString() + "," + err.getCode());
    

    在摄像头的预览事件处理函数中,先调用FT的人脸识函数函数,然后再调用FR中的人脸信息特征提取数函数。

    AFT_FSDKError err = engine.AFT_FSDK_FaceFeatureDetect(data, width, height, AFT_FSDKEngine.CP_PAF_NV21, result);
     
    AFR_FSDKError error = engine.AFR_FSDK_ExtractFRFeature(mImageNV21, mWidth, mHeight, AFR_FSDKEngine.CP_PAF_NV21,mAFT_FSDKFace.getRect(), mAFT_FSDKFace.getDegree(), result);
    

    这里面的result中保存了人脸特征信息。我们可以将其保存下来或下来并与系统中的其它信息进行对比。

    AFR_FSDKMatching score = new AFR_FSDKMatching();
    float max = 0.0f;
    String name = null;
    for (FaceDB.FaceRegist fr : mResgist) {
       for (AFR_FSDKFace face : fr.mFaceList) {
          error = engine.AFR_FSDK_FacePairMatching(result, face, score);
          Log.d(TAG,  "Score:" + score.getScore() + ", AFR_FSDK_FacePairMatching=" + error.getCode());
          if (max < score.getScore()) {
             max = score.getScore();
             name = fr.mName;
          }
       }
    }
    

    当score的特征信息大于0.6时,我们就可以认为匹配到了人脸。显示人脸匹配信息。

    上面的循环中,可以看到,是遍历了真个库进行寻找。我们的目的是为了演示,实际情况下,我们可以在找到一个匹配值比较高的人脸后,就跳出循环。

    MTCNN算法demo: https://download.csdn.net/download/weixin_42713739/11081627
    PFLD人脸算法demo: https://download.csdn.net/download/weixin_42713739/11106807
    虹软算法demo:https://download.csdn.net/download/weixin_42713739/11087890

    展开全文
  • python基于openCV人脸识别

    千人学习 2019-10-30 14:34:06
    python 基于openCV人脸识别,简单入门级项目,初学者可以看懂。
  • 人脸识别国内外现状及发展

    千次阅读 2019-11-05 09:23:01
    分几个方面分别的论述一下人脸识别的技术和产业发展的相关状况。 第一,人脸识别技术的价值在哪里。我们把人脸作为一个生物学特征,作为一个商业化运用,只是备选的一个方案之一。生物学当中,唯一的判断的标准,...

    分几个方面分别的论述一下人脸识别的技术和产业发展的相关状况。 

    第一,人脸识别技术的价值在哪里。我们把人脸作为一个生物学特征,作为一个商业化运用,只是备选的一个方案之一。生物学当中,唯一的判断的标准,其实识别从精准度的角度和不可替代的角度来讲,最精准的是虹膜,但是虹膜的识别采集成本非常高,识别的效率相对不是很高,需要等待的时间。所以这两个条件约束了整个的产业化运用只能局限在相对小众的,对识别要求极高的军工、国防等安全性非常高的远的投入,不适合大范围的推广。 

    第二,指纹。我们知道指纹的唯一性比较强,指纹同时采集成本是比较低的,比对成本也不高。但是为什么指纹没有成为一个特别大的可供支付、刷脸可替代的方案呢?实际上主要的原因是因为指纹的可复制性,是一个静态图像之间的比对,现在我们可以看到淘宝也好,各种各样的大量的指纹贴,指纹膜,可复制的特征,不适合支付。所以指纹现在也大致上被pass了。 

    第 三和第四分别是人脸识别和声音识别技术。这两个在现在横向来相比,采集成本和比对的效率,以及生命特征的唯一性来讲,性价比比较高。所以现阶段来看,人脸识别浮出水面,是有它的道理的,这是它的价值。商业特征的应用场景到底在哪里。 

    人脸识别的应用场景是非常宽泛的,现在主要两块, 一个是金融行业,一个是安保行业 。金融行业,已经从马云的蚂蚁金服演示中看到了场景,通过 刷脸进行支付 ,显然刷脸可以付钱了,为什么不可以签收快递呢,下一步淘宝应该会把淘宝签收快递的功能打通。我相信有一天,我们会收到无人机送来的快递,无人机在你的面前拍一张照片,进行对比,就知道这个用户就是需要的用户,完成整个的支付过程。实际上这种场景,是经过多方面的讨论和认证的。基于这样的场景,是跟第三方的支付认证相关的,包括我们看到的腾讯的银行,第一张远程开卡,就是通过人脸识别的技术,把人证合一进行认证,这样远程开户,远程开卡的功能,在我们的券商,在我们的网络银行上面,应该有广泛的应用。 

    对于安保行业来说,刷脸开门,现阶段,人脸识别的应用应该说达到了一个可具备商业化的水平,我们举个例子,在去年的时候,香港有一个导演叫许鞍华,他在南京地铁中丢了一个他的皮包,这个案件的破获,只花了5个小时。视频监控里面获取了一张照片截图,截到了嫌疑人的照片,是极其模糊的,侧脸的照片,如果肉眼比对,发现不了什么。但是有一家非上市公司,在这里不能提供他的公司名称,他们通过一个图像还原技术,把那个照片还原出可能嫌疑人的样子,清晰照,用这个照片到图库当中比对,锁定嫌疑人的身份,把嫌疑人抓获,只需要了5个小时的时间。现在安防领域的监控,我们可以看到各个省市以及地级市,都在上大量的视频监控,人脸识别的大平台。在整个安防的投入当中,上一代的安防只是静态的记录下来数据,但是下一代的安防,是对实时数据的采集、辨认,就是一个核心的技术,这个技术,人脸识别在其中发挥的作用是很大的。 

    我 们再拓展一下,未来的商业用途,到底有没有第二代人脸识别技术的潜在的应用的场景呢。我们说在未来,应该说原来整个确定身份的身份证,但是证和人的比对需要人工来完成。如果我们直接界定,达到了这样的一个标准,实际上每个人所对应的唯一的ID就是脸部的生物特征。这个识别了以后,所有的地方都可以用刷脸的方式,所有的地方都可以用刷脸去开门,用刷脸去做各种各样的事情。你刷脸的数据,包括你去坐火车、坐飞机、去哪儿吃饭、购物、收快递等等,这些数据都会掌握到人脸识别中,刷脸的数据将取代现在线上的点击量. 

    现在信用卡、银行卡消费的数据,其实有助于知道用户消费习惯和消费数据,做大数据的营销和征信,但是刷脸时代来临之后,这个的价值更大了。有很多张卡,但是只有一张脸,这是唯一的。刷脸数据是2.0时代当中,我们重点看到的。 

    为什么在这个时间段,人脸识别的技术会大范围的爆发出来,大范围的应用起来,成熟度到底怎么样呢?我们首先要界定一下人脸识别技术要达到产品化的应用,是两阶段的过程。 第一阶段,需要获取大量的样本数据,这些数据是用于训练的,训练的是学习算法,这个是深度学习算法,把这些数据和相互人之间的关系提取出来,进行一个特别的比对。耦合度高,超过一定的水平之后,我们会认定这两个人是一个人,但是这个模型是需要投入大量的成本,这个成本包括优化的成本,包括数据训练的成本,包括运算的成本 ,我们当时人脸识别的一个业内的公司,这家公司的创始人,曾经说,人脸识别的技术意味着什么呢?太上老君的炼丹炉,有了这个炉之后,大数据是炉子炼的原料,解决计算能力资源的稀缺。因此这些合在一起,形成了现在人脸识别大爆发的时代,就是我们说的技术上的突破。 

    但是在产业上面的应用来看,目前我们可以看到,美国和以色列的人脸识别,特别是动态识别的水平是国际领先的。全网的实时监控当中,FBI在去年推出了他们的下一代的电子识别系统,总的投入是超过10亿美金的。在美国将来无论是在什么地方犯了事,监控锁定犯罪嫌疑人,进行全网追捕。 

    国内是什么水平呢?顶尖的学术水平,就代表着国内产业发展的阶段。目前主要是三种力量,一个是 清华大学的苏光大教授 ,他是中国的人脸识别之父。第二个是 中科院的自动化所的李教授 ,他早年在微软的亚洲研究院当中获得了非常高的成就,后来到了中科院的自动化所,专攻人脸识别。在奥运会当中,以及后来很多的人脸识别的应用当中,提供了比较好的技术。 第三支就是 香港中文大学的汤晓鸥教授 的团队,每年会进行学术界的比赛,他是高记录的保持者。目前的识别率是超过了人类的脸部识别的总体水平,汤教授帮助讯飞在语音识别领域之后,在人脸识别的领域当中,建立了自己的行业地位。所以国内基本上目前是这样的发展阶段,我们去推导下面的阶段,我们怎么去甄别人脸识别的技术,到底哪一家靠谱,哪一家不靠谱,我们可以提出一些关键的甄别的关键点。这些点在哪里呢? 

    第一,我们要区分的,动态和静态配合式的识别还是非配合式的识别。配合式的就是像蚂蚁金服那样的,需要数据的比对方进行配合,可以很好的去采集正脸的二维的数据。另外,就是非配合式的,非配合式的没有办法对排除方的配合,是需要随机采集的图片进行比对,这个识别的效果会差一些,但是识别的时效性会很高。 
     

    这两种模式当中,我们关注三点。

    第一点,你的人脸建模当中到底提取了多少个特征点进行比对,这个跟我们人脸上面的一些特征是关键节点,每个人的差异很大,而你选取的特征点的数据越多,比对的准确率就会越高。我们也采访了一些专家,他们目前能够做到的特征点的比对,应该是在700个点以上。目前大部分做刷脸的门禁这样系统产品的公司,特征点的选取大概是在50个左右。所以我们去做调研和交流,可以问一下整个公司人脸识别建模当中特征点的数量。


    第二点,人脸识别数据库的数据样本和大小,这是一个非常重要的指标。样本及大小,是我们可供的数据集,这些必须要对人脸,比如说一个人有500张照片,拍的都是他的脸,不同的角度和位置、光线,把这些数据进行合理的清洗,供机器去训练包括比对和识别之后,可以告诉你是识别对了还是识别错了,这样的样本数非常重要,有助于训练,提高模型的准确率。因此可标签的数据样本集的大小,这个大小目前至少是百万以上的级别,才会使得现在识别率能够提升到世界领先的水平,这个也是可以甄别的关键点之一。 

    第三点,是不是你的商业模式能够对你的整个的数据的获取,我们说人脸数据的比对,形成一个 正循环的模式。实际上数据来源,人脸的样本来源,是来源于两个非常重要的渠道,美图秀秀和美颜照相机,这是一个商业的互换,这个数据,因为考虑到做一个脱敏的处理,剩下的只有几百个关键的特征点的数据,其他的都被略去,用脱敏的技术之后,形成了从获取数据到训练模型,再到优化模型,持续的反馈结果,获取新的数据,这样的一个正循环的过程。有了这个以后,你的模型的数据就会获取的很好了,这是商业模式上非常重要的一个指标。 

    如果有了这三个指标之后,应当说同时具备了这三个,可能是在人脸识别领域当中有非常大的领先优势,或者是未来发展潜力的东西。同时我们在直观的性能方面去分析,直观的到底识别的表现上有两个非常重要的指标,一个是识别的准确率,我们界定了刚才说的学术界当中,每年一比的人脸识别大赛,现在基本上测试水平都在95%以上,但是是人和图片之间相互比对,说明是这个人,这算一个,再比对一个,又对了,算第二个。所有的人和照片都是匹配好的,最后正确率在99.2%左右,这是我们说的目前的正常的比对方法。 

    还有一个非常重要的方法,我们看到商业银行和淘宝在内的一些人脸识别的技术,会提出一个 错误率 的问题,这个数据,目前来看可以做到十万分之一的错误率,别人拿着我的身份证去比对,如果机器能够区分出来,是不通过,这是对的。如果机器把我的身份证给别人的时候也通过了,这可能就是一个错误的,错误率要在十万分之一左右才可以,目前能达到这样错误率的公司是屈指可数的,这是一个识别准确率的问题. 

    另外还是在多大样本中可以实现这样的准确率,这个是至关重要的。一个公司里面也就是两三百个人,在这些人当中,挑选出来通过,没有什么难度。但是在公安部的大平台当中,省级的平台当中,都是上亿人的身份证照片中,要准确的挑出来十个或者是一百个候选人,这个范围缩小到这个概率当中,你的准确率能有多大,这是一个很重要的指标。 

    第二点, 识别的速度问题 。同样还是刚才我们说到的样本集的大小决定了识别的速度。本身你在可供比对的样本中,没有很大的数据,比如说是成千上万的,识别的数大家都是差不多,都是在1秒之内作出反映,但是如果在一个上亿的大的样本当中,去把照片准确的识别出来,这样对时间的要求,对效率反映的要求就提高了。所以识别速度是一个很重要的指标。 

    以上我们说了五个指标,我们说这个确实是可以对公司的具体能力和技术进行综合判断的。 

    基于以上我们说的这些,关注的公司是有识别技术的公司,这个识别技术是人脸识别的技术。我们前面讲了,本身国内发言的几支学术界的力量大家非常清楚,来源于哪一支,背靠着哪一支强大的学术团队,研究团队的力量,使得这家公司是一个很好的位置。比如说我们前面讲到的科大讯飞,在汤晓鸥教授的支持下,他们的团队是学术界第一的力量在支持他们,这是一个资源性的优势。比如说川大智胜,这个和李教授他们有密切的合作,同时他们自己在图象识别领域当中,也有自己独特的技术,承担着国家大量的科研基金的项目,同时我们也特别强调一个就是川大智胜的人脸识别技术,是目前我们看到的 人机交互 ,因为这个和二维的平面识别有很大的区别,优势非常明显,因为采集到了五官之间立体曲面之间的结合,所以采集到的数据量更丰富。可供比对的特征也是更多的,我们之前在视频当中找到拍到的侧脸,不清晰的照片,很难去识别出来犯罪嫌疑人到底是谁,是因为我们二代身份证库当中,本身就是只有正脸的可供比对的数据。三代或者是四代身份证采集数据的过程当中生物特征肯定要被提取出来,首先是指纹,三维的人脸识别会更快,三代四代可能就会被提取。 

    一旦需要被提取到三维的人脸的数据,那么这个时候川大智胜作为国内目前唯一一家有产品和技术的公司,面临的是广阔的市场。但是我们同时也要看到,三维人脸识别虽然有非常惊人的优势,同时劣势也是非常明显的,特征点的选取,包括侧脸的选取,是有难度的。同时表情的因素,其实对于数据处理的影响,没有在立体表情的因素那么好,提取的时候效率是偏低的,消耗的数据也非常大。所以现在来看,我们能够看到的应用场景目前还是小范围的,包括像美国对犯罪的有案底的犯人,我们国内目前在监狱当中也逐步的推广,将来全民都要采集,这肯定是一个非常巨大的市场。 同时这家公司在人脸识别公司当中,技术特点和现在持续的对三维人脸识别加码,有一个项目是1.8个亿,要投入到研发当中,国家自然科学基金也已经持续的支持他们三维人脸识别的学术研究的项目,已经支持了很多年。所以在这个领域,应该是到了开花结果的地步。所以这一点,我们特别提示大家要关注这个公司,在技术上确实是有稀缺性的。 

    科大讯飞,就是典型的我们刚才讲的商业模式,可以实现人脸识别数据正循环的公司,是拥有互联网端的入口的。之前在语音的领域当中,讯飞语音云走的就是这样的模式,我获取的是你语音的数据,用你的数据持续的训练我后台的算法,使得他们提升和保持和其他竞争对手的领先优势。这样的话,数据端的循环,从语音的这个领域当中,复制到图像识别,就是人脸识别当中。大家如果关注讯飞,大家可以看到,在上个星期的时候,推出了双重生物特征的识别的因素,双重是什么呢?两重加密以后,确实就是这个人,把出错的概率降到非常低的水平。同时识别,双重加密之后,这个身份验证的过程可以做到数量级上面的提升。 

    有了这样一种开放云的平台之后,讯飞的数据正规化的过程也在逐步的建立,他下一步会和非常多的第三方的应用方合作,包括可以刷脸开锁的智能硬件方面,包括和电话银行,电话客服,还有邮箱去实现他的数据入口的正循环的过程。我们核心的问题就是以上的这样的一些判断的标准来去甄别的。我认为讯飞实际上是非常有希望的人脸识别的公司。我们在报告当中,也提到了讯飞是一个生态级的公司,不光是在人脸识别的这个领域当中有比较强的资源优势和技术优势,以及商业模式的优势。同时在我们整个的人工智能领域当中,讯飞超脑可以不断的用它孵化,基于学习的模式,从语音迁移到现在的图像,下一步迁移到语义当中,不断的做技术的衍生,这样的生态链一旦形成的话,在人工智能产业的地位是不可动摇的。所以人工智能整个的产业,我们想推的是科大讯飞。 

    人脸识别的领域当中,讯飞的优势也是非常明显的,同时我们也是看好川大智胜拥有的三维人脸识别的技术。其他的品牌公司,我们可以看到欧比特收购的公司,在安防领域的人脸识别当中,在监狱当中是超过50%的,在产品化方面也做的非常好。其他的两家,刚刚推出了自己的识别技术,现在了解的信息当中,还没有办法很好的甄别他们现在是否拥有满足我们以上的五个标准。在以后的调研和跟踪当中,我们会对他们的标准进行梳理和进一步的分解。这是对识别类公司的分析. 

    下一个阶段,我们觉得还有比较好的投资机会,除了第一类识别类的,第二类应该是数据资源类的,数据资源目前来看就是视频资源,有比较好的视频资源的公司,可以通过视频资源进行持续的深度学习的算法和优化,也许他自己没有这个技术和能力,但是可以通过技术合作的方式,找到研发团队或者是公司进行合作,共同开发优势。目前在视频资源当中的这些公司进行梳理的话,我觉得东方网力在这个当中步子迈的最前。目前产品端还是没有关于人脸识别成型的产品推出来,但是他的应用是在于多年的视频数据的积累。这个是和后期有密不可分的关系。先收购了广州的安防领域的视频监控的智能化的公司,这个步子一迈出去,布局的意图非常的明显。摄象头公司会往视频的公司侵占,后面的公司将来可能会往存储的环节去挤压,有可能将来会把分析和存储在一个环节当中就完成了,这个时候面临的压力是比较大的,所以转型的动力也是最迫切的,意愿也是最强烈的。 

    所以总体总结下来,现在人脸识别技术大爆发,并不是偶然的,应该说很好的满足了我们讲的人工智能的三大条件。深度学习的算法,大数据和云计算,这三个条件成熟了以后,在拐点到来的时候,大规模的商业化应用是水到渠成的。下一个阶段,基于计算机视觉的应用,在视频监控领域当中,对人的行为模式的识别、跟踪和分析,这些都会成为一个非常大的市场,成熟度还有待于进一步的检验。但是这个市场我们已经都看得到了,所以现在我跟大家探讨人脸识别的产业的发展机会,我觉得其实大家需要关注的不仅仅在于人脸识别技术本身的发展,也不仅仅在于哪几家上市公司拥有哪几项技术,而是看到背后代表的是整个计算机视觉的兴起。 

    人工智能报告当中也提到过,计算机视觉的1.0版本,是对静态图像的识别,2.0版本,肯定是动态视频内容的理解和学习,包括像谷歌的无人驾驶汽车,包括报告里面提到过的以色列的那个公司,也是纳斯达克上市的,他们用计算机视觉的技术实现了汽车的辅助的无人驾驶。在这个领域当中,实际上计算机视觉可供开发的应用非常丰富的。现在还有一个法律的问题,就是允许不允许无人驾驶的汽车上路,合法不合法的问题,大家不用担心这个问题。因为这个公司IPO的时候,这个公司的CEO说过一句话,他说现在还在担心无人驾驶的汽车上路合法不合法,但是我可以肯定的告诉你,十年以后,人开车上路是不合法的,这肯定是一个大的方向和趋势。这就是我从人工智能的领域延伸出来的,人脸识别只是一个点,更多的还有待于大家去一点一点的发掘。

    展开全文
  • 人脸识别主要算法原理

    千次阅读 2019-06-04 14:24:17
    人脸识别主要算法原理 主流的人脸识别技术基本上可以归结为三类,即:基于几何特征的方法、基于模板的方法和基于模型的方法。 1.基于几何特征的方法是最早、最传统的方法,通常需要和其他算法结合才能有比较好的...
  • 人脸识别各算法详解

    万次阅读 多人点赞 2020-10-12 18:22:54
    人脸识别各算法详解 最近,由于工作需要,为了找到一款高效的认识识别算法,对各种人脸识别算法都研究了一番,以下记录的是各算法的理论基础。 一.MTCNN 本文章主要介绍MTCNN算法的流程,MTCNN主要由三个框架组成...
  • 人脸识别学习总结

    千次阅读 2019-07-07 17:24:36
    人脸识别算法进行了一定程度的学习,从最开始的特征脸到如今的CNN人脸检测,有了较为全面的了解。重点掌握了基于PCA的特征脸检测,LDA线性判别分析(Fisher线性判别),以及基于级联器的Haar特征,LBP特征的人脸...
  • 人脸识别之特征脸方法(Eigenface)

    万次阅读 多人点赞 2015-04-25 22:16:27
    人脸识别之特征脸方法(Eigenface)zouxy09@qq.comhttp://blog.csdn.net/zouxy09 因为需要,花了一点时间写了下经典的基于特征脸(EigenFace)的人脸识别方法的Matlab代码。这里仅把该代码分享出来。其实,在较新...
  • 人脸识别经典算法一:特征脸方法(Eigenface)

    万次阅读 多人点赞 2014-04-06 00:07:53
    这篇文章是撸主要介绍人脸识别经典方法的第一篇,后续会有其他方法更新。特征脸方法基本是将人脸识别推向真正可用的第一种方法,了解一下还是很有必要的。
  • opencv(人脸检测和识别

    万次阅读 多人点赞 2018-11-05 13:20:08
    Opencv的人脸检测函数,定义了具体可跟踪对象类型的数据文件。 Haar级联分类器,通过对比分析相邻图像区域来判断给定图像或子图像与已知对象是否匹配。 两个图像的相似程度可以通过它们对应特征的欧式距离来...
  • 基于深度学习的人脸识别技术综述

    万次阅读 2018-10-30 14:37:37
    原文地址:基于深度学习的人脸识别技术综述 简介:人脸识别是计算机视觉研究领域的一个热点,同时人脸识别的研究领域非常广泛。因此,本技术综述限定于:一,在LFW数据集上(Labeled Faces in the Wild)获得优秀...
  • 2019-05-27 06:40:46 引言 如今,人脸识别和大数据是很热门的...*因为知识有限,并且人脸识别也一直更新发展中,可能牛顿时代,他认为他的理论是完美的,但是到了爱因斯坦时代,其认为的“完美”已经有了缺陷。...
  • Matlab实现人脸识别

    万次阅读 多人点赞 2018-06-13 21:45:59
    最近在学习matlab图像处理部分,发现人脸识别这一块非常好玩,在这里做个总结。人脸识别之一:查找图片中的人脸并用方框圈出 这种类似于智能手机拍照时,屏幕里那个框任务头部的红框。大致步骤为:获取RGB图片---&...
  • 一、开发环境介绍Visual Studio 2015 Qt 5.6.3 (该版本及以上版本都可以) dlib-19.7 opencv-2.4.13.3-vc14 mkl_2018.0.124 (编译dlib时需要) ... Visual studio+Qt的开发环境搭建参考:Visual Stud
  • 看完之后如有不懂,请看:关于人脸和指纹识别共同交流方案,也可以关注微信公众号:雄雄的小课堂,回复:人脸识别群获取群号,群内有直接可以运行的源码可供下载,人脸识别所需的软件群内也有!!! 人脸识别,...
  • DeepID人脸识别算法之三代

    万次阅读 多人点赞 2020-10-14 11:32:45
    DeepID人脸识别算法之三代 DeepID,目前最强人脸识别算法,已经三代。 如今,深度学习方兴未艾,大数据风起云涌,各个领域都在处于使用深度学习进行强突破的阶段,人脸识别也不例外,香港中文大学的团队使用卷积神经...
  • 人脸识别二次开发包,免费,可商用,有演示、范例、说明书
  • FaceNet--Google的人脸识别

    万次阅读 多人点赞 2020-10-14 11:18:00
    引入随着深度学习的出现,...并没有用传统的softmax的方式去进行分类学习,然后抽取其中某一层作为特征,而是直接进行端对端学习一个从图像到欧式空间的编码方法,然后基于这个编码再做人脸识别、人脸验证和人脸聚类。
1 2 3 4 5 ... 20
收藏数 95,760
精华内容 38,304
关键字:

人脸识别