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

    千人学习 2020-10-11 17:34:03
    人脸识别的技术及业务已经广泛应用于各行各业,如住宅安全管理、电子身份证护照、公安、司法和刑侦、自助服务、信息安全等。本训练营连续4天的时间,通过Opencv来采集需要识别的人脸数据库,并建立相关的ID信息标签...
  • 手把手教你实现人脸识别支付系统

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

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

     

    一,前言

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

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

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

    链接: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-06-26 12:03:06
    上篇博客:...这次进一步地研究这一块的知识,来一波真正意义上的人脸识别,查询的资料可能有点过时,但基本思想是没有毛病的,对一些函数也进行了更新,保证了功能的正常实...

    上篇博客: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:40:45
    最近,由于工作需要,为了找到一款高效的人脸识别算法,对各种人脸识别算法都研究了一番,以下记录的是各算法的理论基础。 一.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

    展开全文
  • 简述几种人脸识别的主要方法

    千次阅读 2019-05-29 08:33:58
    人脸识别的方法很多,以下介绍一些主要的人脸识别方法。 (1)几何特征的人脸识别方法 几何特征可以是眼、鼻、嘴等的形状和它们之间的几何关系(如相互之间的距离)。这些算法识别速度快,需要的内存小,但识别率较低...

    https://www.toutiao.com/a6695985589000339981/

     

    人脸识别的方法很多,以下介绍一些主要的人脸识别方法。

    (1)几何特征的人脸识别方法

    几何特征可以是眼、鼻、嘴等的形状和它们之间的几何关系(如相互之间的距离)。这些算法识别速度快,需要的内存小,但识别率较低。

    简述几种人脸识别的主要方法

     

    (2)基于特征脸(PCA)的人脸识别方法

    特征脸方法是基于KL变换的人脸识别方法,KL变换是图像压缩的一种最优正交变换。高维的图像空间经过KL变换后得到一组新的正交基,保留其中重要的正交基,由这些基可以张成低维线性空间。如果假设人脸在这些低维线性空间的投影具有可分性,就可以将这些投影用作识别的特征矢量,这就是特征脸方法的基本思想。这些方法需要较多的训练样本,而且完全是基于图像灰度的统计特性的。目前有一些改进型的特征脸方法。

    (3)神经网络的人脸识别方法

    神经网络的输入可以是降低分辨率的人脸图像、局部区域的自相关函数、局部纹理的二阶矩等。这类方法同样需要较多的样本进行训练,而在许多应用中,样本数量是很有限的。

    简述几种人脸识别的主要方法

     

    (4)弹性图匹配的人脸识别方法

    弹性图匹配法在二维的空间中定义了一种对于通常的人脸变形具有一定的不变性的距离,并采用属性拓扑图来代表人脸,拓扑图的任一顶点均包含一特征向量,用来记录人脸在该顶点位置附近的信息。该方法结合了灰度特性和几何因素,在比对时可以允许图像存在弹性形变,在克服表情变化对识别的影响方面收到了较好的效果,同时对于单个人也不再需要多个样本进行训练。

    (5)线段Hausdorff 距离(LHD) 的人脸识别方法

    心理学的研究表明,人类在识别轮廓图(比如漫画)的速度和准确度上丝毫不比识别灰度图差。LHD是基于从人脸灰度图像中提取出来的线段图的,它定义的是两个线段集之间的距离,与众不同的是,LHD并不建立不同线段集之间线段的一一对应关系,因此它更能适应线段图之间的微小变化。实验结果表明,LHD在不同光照条件下和不同姿态情况下都有非常出色的表现,但是它在大表情的情况下识别效果不好。

    简述几种人脸识别的主要方法

     

    (6)支持向量机(SVM) 的人脸识别方法

    近年来,支持向量机是统计模式识别领域的一个新的热点,它试图使得学习机在经验风险和泛化能力上达到一种妥协,从而提高学习机的性能。支持向量机主要解决的是一个2分类问题,它的基本思想是试图把一个低维的线性不可分的问题转化成一个高维的线性可分的问题。通常的实验结果表明SVM有较好的识别率,但是它需要大量的训练样本(每类300个),这在实际应用中往往是不现实的。而且支持向量机训练时间长,方法实现复杂,该函数的取法没有统一的理论。

    人脸识别的方法很多,当前的一个研究方向是多方法的融合,以提高识别率。

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

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

    2018-08-07 15:23:42
    基于face_recognition库的人脸识别代码,包含单张图片识别、实时视频识别。有详细完整的注释
  • 人脸识别发展史

    2019-03-26 14:37:00
    人脸识别经历了早期算法,人工特征+分类器,深度学习三个阶段。 一.早期算法 1.基于几何特征的方法  每个人的面部器官例如眼睛、鼻子、嘴巴等都存在差异,因此每个人的面貌也千差万别。基于几何特征进行人脸识别...
  • 一种基于人脸识别

    2017-03-16 10:52:15
    一种基于人脸识别专利
  • 人脸识别之特征脸方法(Eigenface)

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

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

    2020-11-01 12:27:03
    选用正面的脸部特征。 使用dlib 导入官方训练好的模型进行检验,节省了建模和训练的步骤。 使用这张图作为训练材料 ...这是脸部特征描述。 首先导入图片 ...导入官方的训练数据 ...detector = dlib.get_frontal_face_...
  • 就了解,有几家做脸部特征识别的算法公司都顺利拿到了VC投资, 因此我们可以看到表情识别的前景还是非常广泛。 举例,识别上课学生动态表情,可识别20种人类情绪,并在其中选出8种情绪进行应用:正面情绪“高兴...
  • 机器学习(8)-人脸识别和 人脸定位

    万次阅读 热门讨论 2017-12-23 00:36:27
    过程概要训练一个能识别一张227*227的图像是否是人脸的二分类模型(使用AlexNet网络) =>人脸 =>非人脸 修改训练好的网络模型,数据层改为输入层,全链接层改为全卷积层(起到窗口滑动的作用) 将输入的图片进行...
  • Matlab实现人脸识别

    万次阅读 多人点赞 2018-06-13 21:35:23
    最近在学习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人脸识别算法之三代

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

空空如也

1 2 3 4 5 ... 20
收藏数 98,203
精华内容 39,281
关键字:

人脸识别