人脸识别系统

摘    要

人脸识别系统的研究始于20世纪60年代,80年代后随着计算机技术和光学成像技术的发展得到提高,而真正进入初级的应用阶段则在90年后期;最近几年随着以深度学习为主的人工智能技术进步,人脸识别技术得到了迅猛的发展。“人脸识别系统”集成了人工智能、机器识别、机器学习、模型理论、专家系统、视频图像处理等多种专业技术,是综合性比较强的系统工程技术。

本项目着手实现了一个基于卷积神经网络的人脸识别签到打卡系统,该系统人脸注册模块能够进行人脸的注册,采集人脸照片并灰度处理,将不同人脸对应的学号(工号)姓名信息存储于数据库,利用CNN卷积神经网络对人脸数据进行训练;人脸签到模块能实时识别当前人脸,识别成功将自动更改当前对象的签到状态,同时语音播报某学号某同学签到成功,并在系统界面输出显示签到信息;缺勤模块可以查看当前未签到成员信息,可以重置所有成员的签到状态。

关键字:卷积神经网络,人脸识别,opencv,Pyqt5,Tensorflow

    

引言

1  系统概述

1.1 人脸注册

1.2 人脸识别

1.3 缺勤名单

2  系统分析

2.1 系统需求

2.2 开发环境

2.3 可行性分析

3  功能设计

3.1 系统功能流程图

3.2 人脸注册功能模块实现

3.3 人脸识别签到功能模块实现

3.4 查看缺勤名单功能模块实现

4  所遇到的问题及分析解决

5  系统特色及关键技术

6  系统测试

6.1 启动系统

6.2 人脸注册模块功能测试

6.3人脸识别签到模块功能测试

6.4 缺勤名单查看模块功能测试

7  结论及心得体会

参考文献

引言

前不久Iphone X发布,革命性的取消了TouchID(指纹识别),而添加了更酷的 FaceID(人脸识别) 模块,FaceID 不只简单的运用在解锁上,还可以在支付,表情等场景中应用,给开发者带来更酷更丰富的应用,Iphone X 在多个硬件传感器的加持下,可以采集3万个点来感知用户的面部特征。

我们知道,深度学习出现后,人脸识别技术真正有了可用性,人脸识别在这几年应用相当广泛,人脸考勤,人脸社交,人脸支付,哪里都有这黑科技的影响,特别这几年机器学习流行,使得人脸识别在应用和准确率更是达到了一个较高的水准。

当前深度学习,识别技术等特别火热,可以利用的资源也非常多,入手门栏低,因此本次信息安全课程设计我毅然决然选择了做一个基于CNN卷积神经网络的人脸识别签到系统,这既顺应了时代发展的方向,又是我激励自己不断学习新技术新知识的好机会。

1  系统概述

1.1 人脸注册

人脸注册模块实际上就是收集人员的相关信息,包括面部信息和身份信息,将身份信息存入myaql数据库,并用神经网络模型对人脸数据进行训练。这个模块中的模型训练对本系统的实用性有着至关重要的影响,训练模型的选择直接影响到系统对人脸识别的精确度。

1.2 人脸识别

人脸识别系统的过程:人脸图像采集及检测、关键点提取、人脸规整(图像处理)、人脸特征提取和人脸识别比对。

  人脸图像采集:不同的人脸图像都能通过摄像镜头采集下来,比如静态图像、动态图像、不同的位置、不同表情等方面都可以得到很好的采集。当用户在采集设备的拍摄范围内时,采集设备会自动搜索并拍摄用户的人脸图像。

  人脸检测:人脸检测在实际中主要用于人脸识别的预处理,即在图像中准确标定出人脸的位置和大小。

关键点提取(特征提取):人脸识别系统可使用的特征通常分为视觉特征、像素统计特征、人脸图像变换系数特征、人脸图像代数特征等。人脸特征提取就是针对人脸的某些特征进行的。人脸特征提取,也称人脸表征,它是对人脸进行特征建模的过程。人脸特征提取的方法归纳起来分为两大类:一种是基于知识的表征方法;另外一种是基于代数特征或统计学习的表征方法。

人脸规整(预处理):对于人脸的图像预处理是基于人脸检测结果,对图像进行处理并最终服务于特征提取的过程。系统获取的原始图像由于受到各种条件的限制和随机干扰,往往不能直接使用,必须在图像处理的早期阶段对它进行灰度校正、噪声过滤等图像预处理。对于人脸图像而言,其预处理过程主要包括人脸图像的光线补偿、灰度变换、直方图均衡化、归一化、几何校正、滤波以及锐化等。

  人脸识别比对(匹配与识别):提取的人脸图像的特征数据与数据库中存储的特征模板进行搜索匹配,通过设定一个阈值,当相似度超过这一阈值,则把匹配得到的结果输出。人脸识别就是将待识别的人脸特征与已得到的人脸特征模板进行比较,根据相似程度对人脸的身份信息进行判断。

人脸识别模块是本系统最主要的功能。人脸识别可以分为两大部分:人脸检测和人脸验证。

关于人脸检测,没有亲自从底层去实现或者优化如何检测人脸。而是采用了opencv和dlib进行人脸检测,在使用过程中发现dlib的识别精度更为高一些,而opencv的识别速度更为快,具体体现在:当Mac摄像头打开后,dlib非常卡顿。

1.3 缺勤名单

     缺勤模块非常简单,查看当前未签到的注册人员;重置所有成员签到状态。实现只需要检索数据库中表示签到状态的值,0为未签到,1为已签到。

2  系统分析

2.1 系统需求

  1. 本系统要求界面简单可行,在操作上具有可行性,易于用户操作,有良好的提示功能如输出信息到显示界面,语言播报。

  1. 功能需求:

    能通过摄像头收集需要注册成员的人脸照片,用卷积神经网络进行训练,将人员身份信息存储到mysql数据库;

能通过计算机本地的摄像头,实时检测当前视频流中的人脸,与训练好的卷积神经网络模型中存放的人脸信息进行比对,配对成功则提示成员签到成功并自动修改数据库在的签到状态;

 能查看当前未签到成员的名单,能重置成员的签到状态。

2.2 开发环境

操作系统:Windows10

开发语言:Python3.7

开发软件:Pycharm

2.3 可行性分析

本课设作品关实现的关键技术是CNN模型和人脸识别。因为熟悉python,我知道这些技术通过python编程是完全可行的:界面实现可以依赖于pyqt5;人脸识别可以结合opencv(摄像头、图片处理),numpy(图片数字化),os(文件的操作和处理);构建神经网络进行模型训练可以利用TensorFlow。

3  功能设计

3.1 系统功能流程图

                                                                    

               

3.2 人脸注册功能模块实现

3.2.1 开启摄像头拍照

控制摄像头,需要先介绍一下OpenCV计算机视觉开源库:OpenCV是一个跨平台的库,使用它我们可以开发实时的计算机视觉应用程序。 它主要集中在图像处理,视频采集和分析,包括人脸检测和物体检测等功能。使用OpenCV库,可以读取和写入图像;捕获并保存视频;过程图像(过滤,变换);执行功能检测;检测视频或图像中的特定对象,例如脸部,眼睛,汽车;分析视频,即估计其中的运动,减去背景,并跟踪其中的对象。

开启摄像头用到了视觉库中的cv2.VideoCapture(0)方法,一般来说计算机只有1个摄像头,默认编号为0,如果有两个,想要打开第二个摄像头就把cv2.VideoCapture(0)函数的参数0替换为1就可以了。摄像头开启后你会发现摄像头的指示灯会亮。读取摄像头采集到的数据,调用cap.read()会返回两个值,ret为布尔值,Ture为读取成功,False表示读取失败;frame则是返回的一帧图像。如果你不确定是否读取成功,可以像我一样直接把这两个参数打印输出。若读取失败,[ ret  frame ]则为[ False  None],反之则为[ True   [图像数据] ]。

开启摄像头关键代码:

# 初始化摄像头
def PrepCamera(self):
    try:
        self.camera = cv2.VideoCapture(0)
        self.camera.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc('M', 'J', 'P', 'G'))
        self.MsgTE.clear()
        self.MsgTE.append('摄像头已连接!')
    except Exception as e:
        self.MsgTE.clear()
        self.MsgTE.append(str(e))

摄像头读取图像并保存图片关键代码:

# 从摄像头读取图像
def TimerOutFun(self):
    success, img = self.camera.read()
    if success:
        self.Image = img
        self.DispImg()

# 分类保存照片

def __init__(self, imgdir='./test_img/', grayfacedir='./test_img_gray'):
        """
        :param imgdir: 采集到的图片的存放路径
        :param grayfacedir: 处理后的图片(人脸灰度图)的存放路径
        """
        self.imgdir = imgdir
        self.grayfacedir = grayfacedir

3.2.2 图像灰度处理

对图片进行灰度处理的原因:我们识别物体,最关键的因素是梯度(现在很多的特征提取,SIFT,HOG等等本质都是梯度的统计信息),梯度意味着边缘,这是最本质的部分,而计算梯度,自然就用到灰度图像了。颜色本身非常容易受到光照等因素的影响,同类的物体颜色有很多变化。所以颜色本身难以提供关键信息。2010PAMI有colorSIFT的一些工作,本质也是不同通道的梯度。所以我们可以把灰度图像看作图像的强度(Intensity),来求一些梯度特征。比较常用的有 HOG,LBP,SIFT等等。

关键代码:

def facetogray(self, someone='', size=64, waitkey=100):
    """
    将指定文件夹中的所有图片转为灰度图,并保存至指定位置
    :param someone: 某人姓名,即要处理的图片文件夹
    :param size: 将图片压缩的纬度大小
    :param waitkey: 延迟时长(ms)
    :return: 处理后的灰度图
    """
    imgnames = getdata.getimgnames(path=os.path.join(self.imgdir, someone))  

# 获取指定文件夹中的所有jpg图片名称
    n = len(imgnames)  # 图片张数
    newpath = os.path.join(self.grayfacedir, someone)  # 处理后的灰度图的存放路径
    if not os.path.exists(newpath):  # 看是否需要创建路径
        os.makedirs(newpath)
    for i in range(n):  # 开始对每张图片进行灰度处理
        # img = cv2.imread(imgnames[i])  # 读入图片 无法读取中文路径
        img = cv2.imdecode(np.fromfile(imgnames[i], dtype=np.uint8), -1)
        results = detect_face(img)  # 对图片进行人脸检测,即找到图片中人脸的位置
        if results is not ():  # 判断图片中有无人脸
            faceboxes = results  # 提取人脸位置信息
            for (x, y, w, h) in faceboxes:
                face = img[y:y + h, x:x + w]  # 截取图片中的人脸图像
                face_gray = cv2.cvtColor(face, cv2.COLOR_BGR2GRAY)  

# 转为灰度图片
                face_gray = cv2.resize(face_gray, (size, size))  # 压缩成指定大小
                # cv2.imwrite(newpath + '/' + str(i) + '.jpg', face_gray)  

# 保存检测出的人脸图片 无法读取中文路径
                savePath = (newpath + "/%d.jpg" % i)
                cv2.imencode('.jpg', face_gray)[1].tofile(savePath)

3.2.3 建立数据库

  本系统采用了mysql数据库,创建users表存储注册用户的身份信息,包括id,user_id,user_name,state四个字段:user_id代表学号,user_name代表名字,state标识签到状态,0为未签到,1为已签到,注册的默认值为0。

创建users表sql语句:

create table if not exists users(

id int auto_increment primary key,

user_id varchar(20) not null,

user_name varchar(10) not null,

state int(5) not null

)default charset=utf8;

set global time_zone='+8:00';

创建好并注册过一些用户信息的users表:

id

user_id

user_name

state

11

1600300109

test

0

14

1600300101

张三

0

15

1600300102

test1

0

3.2.4 构建卷积神经网络

先介绍一下神经网络:神经网络,也指人工神经网络(Artificial Neural Networks,简称ANNs),是一种模仿生物神经网络行为特征的算法数学模型,由神经元、节点与节点之间的连接(突触)所构成。人工神经网络可以映射任意复杂的非线性关系,具有很强的鲁棒性、记忆能力、自学习等能力,在分类、预测、模式识别等方面有着广泛的应用。如图3-1:

图3-1 仿生物神经网络

每个神经网络单元抽象出来的数学模型如下,也叫感知器,它接收多个输入(x1,x2,x3...),产生一个输出,这就好比是神经末梢感受各种外部环境的变化(外部刺激),然后产生电信号,以便于转导到神经细胞(又叫神经元)。如图3-2:

图3-2 神经网络单元

  单个的感知器就构成了一个简单的模型,但在现实世界中,实际的决策模型则要复杂得多,往往是由多个感知器组成的多层网络,如图3-3,这也是经典的神经网络模型,由输入层、隐含层、输出层构成。

图3-3 神经网络模型

简单了解卷积神经网络(CNN):

卷积神经网络与普通神经网络的区别在于,卷积神经网络包含了一个由卷积层和子采样层(池化层)构成的特征抽取器。在卷积神经网络的卷积层中,一个神经元只与部分邻层神经元连接。在CNN的一个卷积层中,通常包含若干个特征图(featureMap),每个特征图由一些矩形排列的的神经元组成,同一特征图的神经元共享权值,这里共享的权值就是卷积核。卷积核一般以随机小数矩阵的形式初始化,在网络的训练过程中卷积核将学习得到合理的权值。共享权值(卷积核)带来的直接好处是减少网络各层之间的连接,同时又降低了过拟合的风险。子采样也叫做池化(pooling),通常有均值子采样(mean pooling)和最大值子采样(max pooling)两种形式。子采样可以看作一种特殊的卷积过程。卷积和子采样大大简化了模型复杂度,减少了模型的参数。

卷积神经网络通常包含以下几种层:

  • 卷积层(Convolutional layer),卷积神经网路中每层卷积层由若干卷积单元组成,每个卷积单元的参数都是通过反向传播算法优化得到的。卷积运算的目的是提取输入的不同特征,第一层卷积层可能只能提取一些低级的特征如边缘、线条和角等层级,更多层的网络能从低级特征中迭代提取更复杂的特征。
  • 线性整流层(Rectified Linear Units layer, ReLU layer),这一层神经的活性化函数(Activation function)使用线性整流(Rectified Linear Units, ReLU)f(x)=max(0,x)f(x)=max(0,x)。
  • 池化层(Pooling layer),通常在卷积层之后会得到维度很大的特征,将特征切成几个区域,取其最大值或平均值,得到新的、维度较小的特征。
  • 全连接层( Fully-Connected layer), 把所有局部特征结合变成全局特征,用来计算最后每一类的得分。

卷积神经网络的优缺点:
优点
• 共享卷积核,对高维数据处理无压力
• 无需手动选取特征,训练好权重,即得特征分类效果好
缺点
• 需要调参,需要大样本量,训练最好要GPU
• 物理含义不明确(也就说,我们并不知道没个卷积层到底提取到的是什么特征,而且神经网络本身就是一种难以解释的“黑箱模型”)

由于卷积网络模型算法实现原理和计算公式比较复杂,内容较多,时间原因本文不再详述。

3.3 人脸识别签到功能模块实现

3.3.1 人脸检测

  人脸检测用到了opencv提供的haarcascade_frontalface_default.xml级联分类器文件,是opencv进行人脸识别的文件。

关键代码:

def detect_face(img):
    clf = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
    # 找到符合xml文件里描述的特征区域
    objects = clf.detectMultiScale(img)
    return objects

3.3.2 更改签到状态

  识别成功后执行sql语句修改数据库中标识签到状态的字段值。

3.4 查看缺勤名单功能模块实现

 数据库中标识签到状态的字段值为0(未签到)和1(已签到)。

关键代码:

# 显示缺勤表格
def ShowTable(self):
    sql = 'select * from users where state=0'
    print(sql)
    cursor = mysql_conn.cursor()
    cursor.execute(sql)
    results = cursor.fetchall()
    print(results)
    if results:
        row = cursor.rowcount
        vol = len(results[0])
        self.tableWidget.setRowCount(row)
        self.tableWidget.setColumnCount(3)
        for i in range(row):
            for j in range(3):
                temp_data = results[i][j + 1]  # 临时记录,不能直接插入表格
                print(temp_data)
                data = QTableWidgetItem(str(temp_data))  # 转换后可插入表格
                data.setTextAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
                self.tableWidget.setItem(i, j, data)

4  所遇到的问题及分析解决

  1. 开始进行注册拍照和模型训练的时候,前端的ui页面就会未响应,当模型训练完成以后才恢复正常,这样给用户带来的体验是极差的。
  2. 问题分析:
  3. ui界面中,有一些按钮,点击后触发事件,这些操作会耗时,如果不能及时结束,主线程将会阻塞,这样界面就会出现未响应的状态,因此必须使用多线程来解决这个问题,而python自带的多线程函数不能将Qt的画图函数放到子线程中,一旦放了就会出错, PyQt5不能在子线程中刷新线程,这样会造成界面卡死,因此不能使用常规的多线程刷新UI。
  4. 解决办法:用pyqt的多线程QThread,这样既可以使用信号机制,又能够使用多线程。
  5. pyqt的多线程QThread以及python的多线程threading,这俩稍微有点那么区别,但基本上实现都是相通的,当启动多线程后,注册信号,槽函数为主线程中的函数,当任务完成后,发射信号,在主线程中对UI进行更新。所以尽量将ui展示页面和功能实现的代码分开,在显示页面的同时还能进行后台的运算。
  6. 识别出人脸时,opencv自带的puttext方法在人脸上方只能输出英文,输出中文会乱码,很生气,中国汉字文化博大精深,为什么不能输出汉字?中国人的名字也都是中文,总不能在系统中用拼音吧。
  7.  问题分析:
  8. 毕竟不是中国开发出来的,也不能怪人家开发,抱怨完就该查资料解决这个问题了。
  9. 解决办法:
  10. 果然没有什么是能难到中国人的,网上一搜解决相关问题的文档真不少,欣慰。可以使用PIL在图片上绘制添加中文,并且可以指定字体文件。
  11. 具体思路:
  • OpenCV图片格式转换成PIL的图片格式;
  • 使用PIL绘制文字;
  • PIL图片格式转换成OpenCV的图片格式;

由于默认的字体比较细,看着不舒服,所以我下载了了微软雅黑Bold.ttf这个字体。

代码实现:

# coding=utf-8
# cv2解决绘制中文乱码

import cv2
import numpy
from PIL import Image, ImageDraw, ImageFont
def cv2ImgAddText(img, text, left, top, textColor=(0, 0, 255), textSize=30):
    if (isinstance(img, numpy.ndarray)):  # 判断是否OpenCV图片类型
        img = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
    # 创建一个可以在给定图像上绘图的对象
    draw = ImageDraw.Draw(img)
    # 字体的格式
    fontStyle = ImageFont.truetype(
        "微软雅黑Bold.ttf", textSize, encoding="utf-8")
    # 绘制文本
    draw.text((left, top), text, textColor, font=fontStyle)
    # 转换回OpenCV格式
    return cv2.cvtColor(numpy.asarray(img), cv2.COLOR_RGB2BGR)

  

5  系统特色及关键技术

  1. 基于卷积神经网络,系统具有学习能力,理论上给它喂的数据越多,它就可以识别越多的人而且准确度会不断提高。

   使用tf创建3层cnn,3 * 3的filter,输入为rgb。

·  第一层的channel是3,图像宽高为64,输出32个filter,maxpooling是缩放一倍

·  第二层的输入为32个channel,宽高是32,输出为64个filter,maxpooling是缩放一倍

·  第三层的输入为64个channel,宽高是16,输出为64个filter,maxpooling是缩放一倍
所以最后输入的图像是8 * 8 * 64,卷积层和全连接层都设置了dropout参数,将输入的8 * 8 * 64的多维度,进行flatten,映射到512个数据上,然后进行softmax,输出到onehot类别上,类别的输入根据采集的人员的个数来确定。

   关键代码:

def __init__(self, imgs=train_x, labels=train_y, keep_prob_5=0.5, keep_prob_75=0.75,
             modelfile='./temp/train-model'):
    tf.reset_default_graph()  # 将计算图清零,以免出现代码多次运行时tensor叠加
    self.imgs = imgs  # 训练集自变量
    self.labels = labels  # 训练集目标变量
    self.size = imgs.shape[1]  # 图片宽度
    self.outnode = labels.shape[1]  # 输出层神经元个数
    self.x = tf.placeholder(tf.float32, [None, self.size, self.size, 1],
                            name='x_data')  # 注意给Tensor起的名字"x_data",后续找x就靠它了
    self.y_ = tf.placeholder(tf.float32, [None, self.outnode], name='y_data')
    self.modelfile = modelfile
    self.keep_prob_5 = np.float32(keep_prob_5)
    self.keep_prob_75 = np.float32(keep_prob_75)

def weightVariable(self, shape):  # 权值数组W
    init = tf.random_normal(shape, stddev=0.01)
    return tf.Variable(init)

def biasVariable(self, shape):  # 偏置项数组b
    init = tf.random_normal(shape)
    return tf.Variable(init)

def conv2d(self, x, W):  # 卷积
    return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')

def maxPool(self, x):  # max池化
    return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')

def dropout(self, x, keep):  # 随机让某些权重不更新,保持某个数
    return tf.nn.dropout(x, keep)

def cnnLayer(self):
    """
    cnn神经网络结构
    :return:
    """
    # ===第一次卷积====
    W1 = self.weightVariable([3, 3, 1, 32])  # 卷积核/filter大小(3,3,1), 输入通道(1),输出通道(32)
    b1 = self.biasVariable([32])  # 偏置项
    conv1 = tf.nn.relu(self.conv2d(self.x, W1) + b1)  # 卷积
    pool1 = self.maxPool(conv1)  # 池化
    drop1 = self.dropout(pool1, self.keep_prob_5)  # 减少过拟合,随机让某些权重不更新

    # ===第二次卷积====
    W2 = self.weightVariable([3, 3, 32, 64])
    b2 = self.biasVariable([64])
    conv2 = tf.nn.relu(self.conv2d(drop1, W2) + b2)
    pool2 = self.maxPool(conv2)
    drop2 = self.dropout(pool2, self.keep_prob_5)

    # ===第三次卷积====
    W3 = self.weightVariable([3, 3, 64, 64])
    b3 = self.biasVariable([64])
    conv3 = tf.nn.relu(self.conv2d(drop2, W3) + b3)
    pool3 = self.maxPool(conv3)
    drop3 = self.dropout(pool3, self.keep_prob_5)

    # ===全连接层====
    Wf = self.weightVariable([8 * 8 * 64, 512])
    bf = self.biasVariable([512])
    drop3_flat = tf.reshape(drop3, [-1, 8 * 8 * 64])
    dense = tf.nn.relu(tf.matmul(drop3_flat, Wf) + bf)
    dropf = self.dropout(dense, self.keep_prob_75)

    # ===全连接层====
    Wout = self.weightVariable([512, self.outnode])
    bout = self.weightVariable([self.outnode])
    out = tf.add(tf.matmul(dropf, Wout), bout, name='out')
    return out

def cnnTrain(self, maxiter=1000, accu=0.99, batch_size=100):
    """
    依据训练样本的模型输出与样本实际值进行模型训练
    :param maxiter: 最大迭代次数
    :param accu: 精度阈值,当训练精度大于accu时则停止训练
    :param batch_size: 每轮训练的样本数
    :return: 无返回,但是当模型精度满足要求后会将模型保存
    """
    out = self.cnnLayer()  # 模型输出/预测
    cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=out, labels=self.y_))
    train_step = tf.train.AdamOptimizer(0.01).minimize(cross_entropy)

    # 比较标签是否相等,再求的所有数的平均值,tf.cast(强制转换类型)
    accuracy = tf.reduce_mean(tf.cast(tf.equal(tf.argmax(out, 1), tf.argmax(self.y_, 1)), tf.float32))

    # 将loss与accuracy保存以供tensorboard使用
    # tf.summary.scalar('loss', cross_entropy)
    # tf.summary.scalar('accuracy', accuracy)

    saver = tf.train.Saver()  # 数据保存器的初始化
    sess = tf.Session()  # 启动会话
    sess.run(tf.global_variables_initializer())
    for n in range(maxiter):
        ind = random.sample(range(len(self.imgs)), batch_size)  # 每次取batch_size张图片
        batch_x = self.imgs[ind]
        batch_y = self.labels[ind]
        _ = sess.run(train_step, feed_dict={self.x: batch_x, self.y_: batch_y})  # 正式训练
        # 打印损失
        # print(loss)
        if n % 100 == 0:  # 每训练100轮输出一次训练精度
            acc = sess.run(accuracy, feed_dict={self.x: batch_x, self.y_: batch_y})  # 获取训练精度
            process_log = '轮数:' + str(n) + ' 训练精度:' + str(acc)
            yield process_log
            print('轮数:', n, ' the train accuracy is :', acc)
            if acc > accu and n > 499:
                saver.save(sess, self.modelfile)  # 保存模型
                break
        elif n == (maxiter - 1):
            saver.save(sess, self.modelfile)
    sess.close()

def predict(self, test_x=test_x):
    """
    预测函数,导入已训练好的模型后再将新样本数据放入,进行模型预测
    :param test_x: 测试样本的自变量
    :return: 模型对测试样本的预测结果
    1: 预测结果(数字标签:0,1,2,3,4,5,...)
    pre: 样本属于各类别的概率,形如:[[0.1, 0.1, 0.0, 0.0, 0.0, 0.8]]
    """
    out = self.cnnLayer()  # 网络输出
    with tf.Session() as sess:
        saver = tf.train.Saver()  # 启动模型保存类Saver
        saver.restore(sess, self.modelfile)  # 调用之前保存的模型
        graph = tf.get_default_graph()  # 获取计算图
        x = graph.get_tensor_by_name('x_data:0')

 # 通过tensor的名称获取相应tensor,注意到底是 x_data 还是x_data_1
        pre = sess.run(out, feed_dict={x: test_x})  # 放入测试集样本
    return np.argmax(pre, 1), pre

  1. 为了契合需求分析中提到的系统要拥有良好的提示功能这一特点,增加了语言播报功能。识别成功或失败后仅仅输出文字提示觉得太单调了,所以干脆再加个语音播报,因此系统在进行实时人脸识别时会语音播报某某学号某某同学签到成功或失败;在人脸注册环节,若发现学号输入有误等,同样也会有语音操作提示。

实现主要使用了python提供的 pyttsx3文字转语音库,支持英文,中文,可以调节语速、语调等。

  部分关键代码示例:

import pyttsx3


def say(engine,str):
    engine.say(str)
    engine.runAndWait()

engine = pyttsx3.init()
rate = engine.getProperty('rate')
engine.setProperty('rate', rate - 80) #调节语速

teacher = pyttsx3.init()

voices = teacher.getProperty('voices') #变换语调

for i in voices: 

    teacher.setProperty('voice', i.id)

    teacher.say(msg)

def log_in(self):
    if self.name != '':
        user_id, user_name, user_state = search_handler(self.name)
        if user_state == 1:
            self.Msg = '%s %s 同学签到成功.' % (user_id, user_name)
            say(engine, "学号%s%s同学签到成功!" % (user_id, user_name))  #语音播报签到情况
        elif user_id is None:
            self.Msg = '签到失败'
            say(engine, "签到失败!")
    else:
        self.Msg = '未检测到人脸'

6  系统测试

6.1 启动系统

启动系统前会检测连接数据库是否成功,若连接失败会弹框和语音提示;连接成功则开始运行系统并语音播报欢迎使用人脸识别签到系统。系统主界面有各个功能的模块按钮,点击相应按钮即可执行对应的任务。成功运行系统的界面如图6-1。

图6-1 系统启动主界面

6.2 人脸注册模块功能测试

6.2.1 进入注册界面 

      在系统主界面点击“人脸注册”按钮即可进入到人脸注册界面,进入界面后首先可以看到详细的注册流程的操作提示说明。如图6-2。

     

图6-2人脸注册界面

6.2.2 开启摄像头测试

    点击注册界面的“开启摄像头”按钮,系统会打开计算机内置摄像头,这时摄像头指示灯亮起,系统上方会显示摄像头当前捕捉到的画面。如图6-3。

              

图6-3 开启摄像头

6.2.3 拍照测试

      开启摄像头后,注册人员调整好位置,让摄像头能捕捉到自己的正脸。手动输入正确的学号姓名信息即可进行“人脸拍照”,这里为了防止误输入,对学号的格式进行了检查判断,输入错误格式的学号系统则会同时用语音播报和文字提示“请检查学号姓名是否输入正确”。输入错误格式的学号测试结果如图6-4。格式检查无误后单击“人脸拍照”按钮,摄像头会拍摄分类存储100张当前人脸照片并进行剪裁和灰度处理。正常拍照结果如图6-5。

           

图6-4输入错误格式的学号拍照测试

                 

图6-5  正常拍照测试

6.2.4 身份信息注册入库测试

    人脸拍照结束后,需要再次填写确认学号姓名身份信息,确认无误后,点击“注册入库”个人信息便会存入mysql数据库中,且默认签到状态为0(未签到)。身份信息注册入库成功如图6-6。

                 

图6-6 身份信息注册入库

6.2.5 卷积神经网络模型训练测试

卷积神经网络模型会进行1000轮训练,每轮训练100张照片,一次训练下来相当于需要处理十万张照片 ,所以需要较长的时间,对计算机配置也有一定要求。为了节约时间,可以先依次给所有需要注册信息的成员进行“人脸拍照”和“注册入库”,一位成员只需要一分钟左右的时间,全体成员注册完毕后,在选择模型训练。模型训练过程如图6-7、-8、6-9。可以看出系统人脸识别精度(识别成功率)在训练过程中逐渐提升。

图6-7人脸集CNN模型训练

             

图6-8人脸集CNN模型训练

              

图6-9人脸集CNN模型训练

6.3人脸识别签到模块功能测试

    完成“人脸注册”和“模型训练”环节后,就可以在系统主界面使用“人脸签到”功能了。开启摄像头后,会实时对画面进行人脸检测,未检测到人脸会提示“未检测的人脸”;检测到人脸会自动和系统注册存储过的人脸比对,配对失败(未在系统注册过)提示“签到失败”,配对成功则提示“某学号某同学签到成功”(语音播报+文字),并自动修改数据库中该同学签到状态为1(已签到)。识别签到成功如图6-10。

                 

图6-10 签到功能测试

6.4 缺勤名单查看模块功能测试

    点击“缺勤名单”会显示注册过信息且当前未签到的同学名单,“重置”会改变所有成员的签到状态。如图6-11。

                  

图6-11 查看缺勤名单

本系统已经达到需求分析的要求,能对各个功能进行测试成功。并且能正确的处理各个功能所产生的问题和预防各种操作上的错误。总体上来说,本系统的性能表现还是十分良好的。

7  结论及心得体会

这次信息安全课程设计的顺利完成,要感谢同学舍友的热情指点和帮助,感谢互联网上提供各种解决问题方案的网友们,同时也特别感谢我的指导老师。

通过此次课程设计,使我更加扎实的掌握了有关计算机视觉方面的知识,在设计过程中虽然遇到了一些问题,但经过一次次的思考,最终找出了原因所在,也暴露出了我在这方面知识的欠缺和经验不足。实践出真知,通过亲自动手实践,使我掌握的知识不再是纸上谈兵。

 过而能改,善莫大焉。在课程设计过程中,我不断发现错误,不断改正,不断领悟,不断获取。在今后社会的发展和学习实践过程中,也要不懈努力,不能遇到问题就想到要退缩,要不厌其烦的发现问题所在,然后想办法进行解诀,只有这样,才能成功的做成想做的事,才能在今后的道路上劈荆斩棘,而不是知难而退,那样永远不可能收获成功,收获喜悦,也永远不可能得到社会及他人对你的认可!

 信息安全课程设计对我们来说是一次不可或缺的提升自我的机会,给了我很多专业技能上的提升,给了我莫大的发挥空间。同时,设计让我感触很深。通过这次课程设计,我掌握了opencv人脸识别相关的技术,;熟悉了深度学习神经网络训练的实现原理; 了解了通过分离界面代码和计算代码来提升用户体验方法等等。

 在这次课程设计中,不仅培养了我独立思考、动手操作的能力,在各种其它能力上也都有了提高。更重要的是,在这期间,我学会了高效的学习总结知识的方法,而这是日后最实用的,受益匪浅。

 回顾起此课程设计,至今我仍感慨颇多,从理论到代码实现,在这段日子里,可以说得是苦多于甜,但是千真万确学到很多很多的东西,同时不仅可以巩固了以前所学过的知识,而且学到了很多在书本上所没有学到过的知识。通过这次课程设计使我懂得了理论与实际相结合是很重要的,只有理论知识是远远不够的,只有把所学的理论知识与实践相结合起来,从理论中得出结论,才能真正为社会服务,从而提高自己的实际动手能力和独立思考的能力。在设计的过程中遇到问题,可以说得是困难重重,但可喜的是最终都得到了解决。

此次课程设计也让我明白了思路即出路,有什么不懂不明白的地方要及时请教或上网查询,只要认真钻研,动脑思考,动手实践,就没有弄不懂的知识。

参考文献

[1]  Joe Minichino等 刘波等译. OpenCV3计算机视觉Python语言实现第二版. 机械工业出社 2016年  

[2]  王硕,孙洋洋著.  PyQt5快速开发与实战. 电子工业出版社  2017年

[3]  马春鹏 著. 模式识别与机器学习. 机械工业出版社  2018

[4]  Mark S.Nixon等著 实英等译. 特征提取与图像处理(中译第二版). 电子工业出版社 2010年

[5]  pyqt5配置: pyqt5安装与pycharm配置-CSDN博客

[6]  详解CNN卷积神经网络: 详解CNN卷积神经网络-CSDN博客

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

等天晴i

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值