基于卷积神经网络(cnn)的人脸识别(代码+注释)

卷积神经网络的核心是卷积,在cnn的卷积层中存在一个个填充着数字的正方形小格子,他们被称为卷积核。原始图片经过输入层后会变为灰度或是RGB数值填充的矩阵。将卷积核与图片矩阵对齐,对应格子中的数字相乘后再相加,再将得到的数字填入新矩阵,这就是卷积。卷积核以一定的距离在图像上移动运算,这被称为步长,得到的新矩阵能反映图像的部分特征,因此被称为特征图。他们既是这一层的输出,又是下一层的输入。设定不同的卷积核,我们就能找到各种各样的特征。训练就是根据已有的数据和标签,自动确定卷积核中的数字。池化层能选取图像的主要特征,矩阵被池化后,参数能大量减少。全连接层通常在网络的最后,能将提取到的特征集合在一起。给出图片是某种事物的概率。

此项目共分四步:

一、采集我的人脸数据集

二、采集其他人脸数据集

三、CNN模型训练

四、人脸识别

一、采集我的人脸数据集

"""-----------------------------------------
一、采集我的人脸数据集
获取本人的人脸数据集10000张,使用的是dlib来
识别人脸,虽然速度比OpenCV识别慢,但是识别效
果更好。
人脸大小:64*64
-----------------------------------------"""
# 开源的视觉库,实现图像处理和计算机视觉方面的很多通用算法
import cv2
#  机器学习的开源库,(直接包含头文件即可)
import dlib
#  提供一些方便使用操作系统相关功能的函数
import os
#  生成随机数
import random

faces_my_path = './faces_my'
# 图片大小采集的64 X 64
size = 64
if not os.path.exists(faces_my_path):  # 判断是否存在
    os.makedirs(faces_my_path)   # 创建目录

"""改变图片的相关参数:亮度与对比度
处理图像经常会对图像色彩进行增强,这就是改变图像的亮度和对比度"""

#  声明函数 light表示对比度 bias表示亮度偏置
def img_change(img, light=1, bias=0):
    width = img.shape[1]   # 图像的水平尺寸(宽度)
    height = img.shape[0]  # 图像的垂直尺寸(高度)
    #  范围
    for i in range(0, width):  # 给i赋值
        for j in range(0, height):
            for k in range(3):  # 循环3次
                #  #线性调整 light表示对比度,bias代表亮度偏置
                #  temp临时
                tmp = int(img[j, i, k] * light + bias)
                if tmp > 255:
                    tmp = 255

                elif tmp < 0:
                    tmp = 0
                img[j, i, k] = tmp

    return img


"""特征提取器:dlib自带的frontal_face_detector,作为我们的特征提取器"""
detector = dlib.get_frontal_face_detector()
# VideoCapture()中参数是0,表示打开笔记本的内置第一个摄像头。参数是视频文件路径则打开视频
cap = cv2.VideoCapture(0)

num = 1
while True:
    # 存储10000张人脸特征图像
    if (num <= 300):
        print('已采集 %s' % num)

        # 从摄像头
        success, img = cap.read()   # 从摄像头读取照片
        gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)  # 转为灰度照片
        """使用detector进行人脸检测"""

        dets = detector(gray_img, 1)  # 1采样次数

        """--------------------------------------------------------------------
        使用enumerate 函数遍历序列中的元素以及它们的下标,i为人脸序号,d为i对应的元素;
        left:人脸左边距离图片左边界的距离 ;right:人脸右边距离图片左边界的距离 
        top:人脸上边距离图片上边界的距离 ;bottom:人脸下边距离图片上边界的距离
        ----------------------------------------------------------------------"""

        for i, d in enumerate(dets):
            x1 = d.top() if d.top() > 0 else 0
            y1 = d.bottom() if d.bottom() > 0 else 0
            x2 = d.left() if d.left() > 0 else 0
            y2 = d.right() if d.right() > 0 else 0

            face = img[x1:y1, x2:y2]
            """调整图片的对比度与亮度, 对比度与亮度值都取随机数,这样能增加样本的多样性"""
            face = img_change(face, random.uniform(0.5, 1.5), random.randint(-50, 50))
            face = cv2.resize(face, (size, size))  # 调整图片的大小
            cv2.imshow('image', face)  # 使用给定的变量(face)显示图像
            cv2.imwrite(faces_my_path + '/' + str(num) + '.jpg', face)
            #cv2.imwrite(r'./faces_other' + '/' + str(num) + '.jpg', face)
            num = num + 1

        key = cv2.waitKey(30)  # 设置等待30毫秒
        if key == 27:  # 大于等于0即可

            break
    else:
        print('Finished!')
        breake

二、采集其他人脸数据集

"""---------------------------------------------------------------
二、采集其他人脸数据集
本系统使用人脸数据集下载:http://vis-www.cs.umass.edu/lfw/lfw.tgz
先将下载的图片集放在img_source目录下,用dlib来批量识别图片中的人脸部分,
并保存到指定目录faces_other
人脸大小:64*64
----------------------------------------------------------------"""
# -*- coding: utf-8 -*-
# sys模块是最常用的和python解释器交互的模块,sys模块可供访问由解释器(interpreter)
# 使用或维护的变量和与解释器进行交互的函数。sys 模块提供了许多函数和变量来处理 Python 运行时环境的不同部分
import sys
import cv2
import os
# dlib是一个机器学习的开源库,包含了机器学习的很多算法,使用起来很方便。也提供了一些python接口。
import dlib

source_path = './img_source'
faces_other_path = './faces_other'

size = 64
if not os.path.exists(faces_other_path):
    os.makedirs(faces_other_path)

"""特征提取器:dlib自带的frontal_face_detector"""
detector = dlib.get_frontal_face_detector()


"""os,walk()会返回一个生成器,每次迭代都会返回一个元组,元组返回三个元素,分别是当前目
录(path),当前目录的子目录(dirnames),当前目录的文件(filenames). """

num = 1
for (path, dirnames, filenames) in os.walk(source_path):
    for filename in filenames:
        if filename.endswith('.jpg'):   # 用endswith判断一个文件名是不是以jpg
            print('Being processed picture %s' % num)
            img_path = path+'/'+filename
            img = cv2.imread(img_path)   # 读图片
            # 转为灰度图片
            gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
            """ 使用detector进行人脸检测 dets为返回的结果"""
            dets = detector(gray_img, 1)  # 1采样次数

            """--------------------------------------------------------------------
            使用enumerate 函数遍历序列中的元素以及它们的下标,i为人脸序号,d为i对应的元素;
            left:人脸左边距离图片左边界的距离 ;right:人脸右边距离图片左边界的距离 
            top:人脸上边距离图片上边界的距离 ;bottom:人脸下边距离图片上边界的距离
            ----------------------------------------------------------------------"""
            for i, d in enumerate(dets):
                x1 = d.top() if d.top() > 0 else 0
                y1 = d.bottom() if d.bottom() > 0 else 0
                x2 = d.left() if d.left() > 0 else 0
                y2 = d.right() if d.right() > 0 else 0

                face = img[x1:y1,x2:y2]
                face = cv2.resize(face, (size,size))   # 调整图片的尺寸
                cv2.imshow('image',face)
                cv2.imwrite(faces_other_path+'/'+str(num)+'.jpg', face)   # 保存
                num += 1
            # waitKey() 函数的功能是不断刷新图像 , 频率时间为delay , 单位为ms,该函数通常用在显示图像函数之后。
            """参数:delay延时时间,单位ms;
               delay>0时,延迟"delay"ms;
               当delay<=0的时,如果没有键盘触发,则一直等待"""
            key = cv2.waitKey(30)
            if key == 27:
                sys.exit(0)

三、CNN模型训练

"""--------------------------------------------------------------
三、CNN模型训练
训练模型:共八层神经网络,卷积层特征提取,池化层降维,全连接层进行分类。
训练数据:22784,测试数据:1200,训练集:测试集=20:1
共两类:我的人脸(yes),不是我的人脸(no)。
共八层: 第一、二层(卷积层1、池化层1),输入图片64*64*3,输出图片32*32*32
        第三、四层(卷积层2、池化层2),输入图片32*32*32,输出图片16*16*64
        第五、六层(卷积层3、池化层3),输入图片16*16*64,输出图片8*8*64
        第七层(全连接层),输入图片8*8*64,reshape到1*4096,输出1*512
        第八层(输出层),输入1*512,输出1*2
学习率:0.01
损失函数:交叉熵
优化器:Adam  (2014年12月, Kingma和Lei Ba两位学者提出了Adam优化器,结合AdaGrad和RMSProp两种优化算法的优点。
对梯度的一阶矩估计(First Moment Estimation,即梯度的均值)和二阶矩估计(Second Moment Estimation,即梯度的未中心化的方差)
进行综合考虑,计算出更新步长。)
------------------------------------------------------------------"""
# train_test_split()是sklearn.model_selection中的分离器函数,用于将数组或矩阵划分为训练集和测试集,函数样式为:
# X_train, X_test, y_train, y_test = train_test_split(train_data, train_target, test_size, random_state,shuffle)
from sklearn.model_selection import train_test_split
import tensorflow.compat.v1 as tf      # TensorFlow是一个基于数据流编程(dataflow programming)的符号数学系统,
import numpy as np      # 数值计算库
import random
import cv2
import sys
import os


""" 定义参数 """
faces_my_path = './faces_my'
faces_other_path = './faces_other'
batch_size = 100  # 每次取100张图片
learning_rate = 0.01  # 学习率
size = 64  # 图片大小64*64*3
imgs = []  # 存放人脸图片
labs = []  # 存放人脸图片对应的标签

"""定义读取人脸数据函数,根据不同的人名,分配不同的onehot值(独热编码,只有1,0)"""
# 当前目录(path),当前目录的子目录(dirnames),当前目录的文件(filenames)
def readData(path, h=size, w=size):
    for filename in os.listdir(path):  # os.listdir() 用于返回一个由文件名和目录名组成的列表
        # 用endswith判断一个文件名是不是以jpg
        if filename.endswith('.jpg'):
            # 根据文件名改名:文件夹名+newname
            filename = path + '/' + filename
            # 读取图片
            img = cv2.imread(filename)
            # 人脸上下左右距图片边界的距离
            top, bottom, left, right = getPaddingSize(img)
            """放大图片扩充图片边缘部分"""
            # cv2.BORDER_CONSTANT:添加的边界框像素值为常数(需要额外再给定一个参数,0是黑色)
            img = cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_CONSTANT, value=[0, 0, 0])

            # 使用函数cv2.resize()进行缩放到指定大小,传入的目标形状是(W,H),而不是(H,W),这一点要格外留意.
            img = cv2.resize(img, (w, h))
            imgs.append(img)  # 一张张人脸图片加入imgs列表中
            labs.append(path)  # 一张张人脸图片对应的path,即文件夹名faces_my和faces_other,即标签


"""定义尺寸变换函数,有一套公式"""
# 获得图片的宽和高还有深度
# padding 填充像素
def getPaddingSize(img):
    height, width, _ = img.shape
    top, bottom, left, right = (0, 0, 0, 0)
    longest = max(height, width)

    if width < longest:
        tmp = longest - width
        left = tmp // 2
        right = tmp - left
    elif height < longest:
        tmp = longest - height
        top = tmp // 2
        bottom = tmp - top
    else:
        pass
    return top, bottom, left, right


"""定义神经网络层,共五层,卷积层特征提取,池化层降维,全连接层进行分类,共两类:我的人脸(true),不是我的人脸(false)"""
# 进行卷积操作,相乘求和,滑动窗口的遍历
# 卷积网络在计算每一层的网络个数的时候要细心一些,不然容易出错
# 要注意下一层的输入是上一层的输出
# Tensorflow中的CNN layer参数(即用tensorflow框架实现简单CNN)
def cnnLayer():
    """第一、二层,输入图片64*64*3,输出图片32*32*32"""
    # 一般彩色图像有三个颜色通道(红绿蓝)
    #  tf.Variable通过变量维持图graph的状态,以便在sess.run中执行
    #  tf.random_normal取出指定个数的值
    #  tf.nn.relu线性整流函数,将输入小于0的值幅值为0,输入大于0的值不变
    """
参数 ksize:池化窗口的大小,取一个四维向量,一般是 [1, height, width, 1],因为我们不想在 batch和 channels 上做池化,所以这两个维度设为了1
参数 strides:和卷积类似,窗口在每一个维度上滑动的步长,一般也是 [1, stride,stride, 1]
参数 padding:和卷积类似,可以取 'VALID' 或者 'SAME'
"""
    W1 = tf.Variable(tf.random_normal([3, 3, 3, 32]))  # 卷积核大小(3,3), 输入通道(3), 输出通道(32)
    b1 = tf.Variable(tf.random_normal([32]))
    conv1 = tf.nn.relu(tf.nn.conv2d(x, W1, strides=[1, 1, 1, 1], padding='SAME') + b1)  # 64*64*32,卷积提取特征,增加通道数
    pool1 = tf.nn.max_pool(conv1, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')  # 32*32*32,池化降维,减小复杂度
    drop1 = tf.nn.dropout(pool1, keep_prob_fifty)  # 按一定概率随机丢弃一些神经元,以获得更高的训练速度以及防止过拟合
    # 过拟合:训练样本存在噪声,为了照顾它们,反而使真实测试时产生错误


    """第三、四层,输入图片32*32*32,输出图片16*16*64"""
    W2 = tf.Variable(tf.random_normal([3, 3, 32, 64]))  # 卷积核大小(3,3), 输入通道(32), 输出通道(64)
    b2 = tf.Variable(tf.random_normal([64]))
    conv2 = tf.nn.relu(tf.nn.conv2d(drop1, W2, strides=[1, 1, 1, 1], padding='SAME') + b2)  # 32*32*64
    pool2 = tf.nn.max_pool(conv2, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')  # 16*16*64
    drop2 = tf.nn.dropout(pool2, keep_prob_fifty)

    """第五、六层,输入图片16*16*64,输出图片8*8*64"""
    W3 = tf.Variable(tf.random_normal([3, 3, 64, 64]))  # 卷积核大小(3,3), 输入通道(64), 输出通道(64)
    b3 = tf.Variable(tf.random_normal([64]))
    conv3 = tf.nn.relu(tf.nn.conv2d(drop2, W3, strides=[1, 1, 1, 1], padding='SAME') + b3)  # 16*16*64
    pool3 = tf.nn.max_pool(conv3, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')  # 8*8*64=4096
    drop3 = tf.nn.dropout(pool3, keep_prob_fifty)

    """第七层,全连接层,将图片的卷积输出压扁成一个一维向量,输入图片8*8*64,reshape到1*4096,输出1*512"""
    Wf = tf.Variable(tf.random_normal([8 * 8 * 64, 512]))  # 输入通道(4096), 输出通道(512)
    bf = tf.Variable(tf.random_normal([512]))
    drop3_flat = tf.reshape(drop3, [-1, 8 * 8 * 64])  # -1表示行随着列的需求改变,1*4096
    dense = tf.nn.relu(tf.matmul(drop3_flat, Wf) + bf)  # [1,4096]*[4096,512]=[1,512]
    dropf = tf.nn.dropout(dense, keep_prob_seventy_five)

    """第八层,输出层,输入1*512,输出1*2,再add,输出一个数"""
    Wout = tf.Variable(tf.random_normal([512, 2]))  # 输入通道(512), 输出通道(2)
    bout = tf.Variable(tf.random_normal([2]))
    out = tf.add(tf.matmul(dropf, Wout), bout)  # (1,512)*(512,2)=(1,2) ,跟y_ [0,1]、[1,0]比较给出损失
    return out


"""定义训练函数"""


def train():
    out = cnnLayer()
    """损失函数为交叉熵(度量两个概率分布间的差异信息)
    总的计算交叉熵,需要加 tf.reduce_mean
    softmax_cross_entropy_with_logits交叉熵函数
    logits,可视为⼀种未归⼀化的“概率替代物”
    labels分类的标签  
"""
    cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=out, labels=y_))
    """采用Adam优化器
    tf.train.AdamOptimizer()函数是Adam优化算法:是一个寻找全局最优点的优化算法,引入了二次方梯度校正。
    learning_rate:张量或浮点值。学习速率
"""
    optimizer = tf.train.AdamOptimizer(learning_rate).minimize(cross_entropy)
    """求得准确率,比较标签是否相等,再求的所有数的平均值"""
    # tf.cast(强制转换类型)
    # 比较标签是否相等,再求出所有数的平均值
    accuracy = tf.reduce_mean(tf.cast(tf.equal(tf.argmax(out, 1), tf.argmax(y_, 1)), tf.float32))
    # 将loss与accuracy保存以供tensorboard使用
    tf.summary.scalar('loss', cross_entropy)
    tf.summary.scalar('accuracy', accuracy)
    # 合并所有的Op为一个Op(神经元)
    merged_summary_op = tf.summary.merge_all()
    # 数据保存器的初始化
    saver = tf.train.Saver()
    with tf.Session() as sess:  # 上下文退出时自动释放
        sess.run(tf.global_variables_initializer())  # 初始化模型参数
        # 把summary  Op返回的数据写到磁盘里
        summary_writer = tf.summary.FileWriter('./tmp', graph=tf.get_default_graph())

        num_batch = len(train_x) // batch_size  # 总体样本需要取几次
        for n in range(10):
            # """每次取100(batch_size)(训练的参数个数)张图片(前面定义的参数),"""
            for i in range(num_batch):
                batch_x = train_x[i * batch_size: (i + 1) * batch_size]  # 图片
                batch_y = train_y[i * batch_size: (i + 1) * batch_size]  # 标签:[0,1] [1,0]

                # 开始训练数据,同时训练三个变量,返回三个数据,
                _, loss, summary = sess.run([optimizer, cross_entropy, merged_summary_op],
                                            feed_dict={x: batch_x, y_: batch_y,  # feed_dict赋值
                                                       keep_prob_fifty: 0.5, keep_prob_seventy_five: 0.75})
                # 保存训练数据
                summary_writer.add_summary(summary, n * num_batch + i)

                # 打印损失
                print("step:%d,  loss:%g" % (n * num_batch + i, loss))

                if (n * num_batch + i) % 100 == 0:
                    # 获取测试数据的准确率
                    acc = accuracy.eval({x: test_x, y_: test_y, keep_prob_fifty: 1.0, keep_prob_seventy_five: 1.0})
                    print("step:%d,  acc:%g" % (n * num_batch + i, acc))
                    """训练到准确率达到98%停止训练保存退出"""
                    if acc > 0.98 and n > 2:
                        saver.save(sess, './train_faces.model', global_step=n * num_batch + i)
                        sys.exit(0)


if __name__ == '__main__':
    # 1、读取人脸数据
    readData(faces_my_path)
    readData(faces_other_path)
    imgs = np.array(imgs)  # 将图片数据与标签转换成数组
    labs = np.array([[0, 1] if lab == faces_my_path else [1, 0] for lab in labs])  # 标签:[0,1]表示是我的人脸,[1,0]表示其他的人脸
    """2、随机划分测试集与训练集"""
    train_x_1, test_x_1, train_y, test_y = train_test_split(imgs, labs, test_size=0.05,
                                                            random_state=random.randint(0, 100))
    train_x_2 = train_x_1.reshape(train_x_1.shape[0], size, size, 3)  # 参数:图片数据的总数,图片的高、宽、通道
    test_x_2 = test_x_1.reshape(test_x_1.shape[0], size, size, 3)
    """3、归一化"""
    train_x = train_x_2.astype('float32') / 255.0  # astype函数:数据类型转化函数(虽然float64比float32有更高的精度,
    # 但float64占用的内存是float32的两倍)就是4bytes或8bytes.
    test_x = test_x_2.astype('float32') / 255.0
    print('Train Size:%s, Test Size:%s' % (len(train_x), len(test_x)))

    num_batch = len(train_x) // batch_size
    # 禁用v2版本
    tf.compat.v1.disable_v2_behavior()
    x = tf.placeholder(tf.float32, [None, size, size, 3])  # 输入X:64*64*3。默认是None,就是一维值
    # placeholder()函数是在神经网络构建graph的时候在模型中的占位,此时并没有把要输入的数据传入模型,
    # 它只会分配必要的内存。等建立session,在会话中,运行模型的时候通过feed_dict()函数向占位符喂入数据。
    y_ = tf.placeholder(tf.float32, [None, 2])  # 输出Y_:1*2
    keep_prob_fifty = tf.placeholder(tf.float32)  # 50%,即0.5(设置神经元被选中的概率)
    keep_prob_seventy_five = tf.placeholder(tf.float32)  # 75%,即0.75
    """4、进行训练"""
    train()

四、人脸识别

"""---------------------------------------------------------
四、人脸识别
1、打开摄像头,获取图片并灰度化
2、人脸检测
3、加载卷积神经网络模型
4、人脸识别
------------------------------------------------------------"""
# TensorFlow™是一个基于数据流编程(dataflow programming)的符号数学系统,
# 被广泛应用于各类机器学习(machine learning)算法的编程实现,其前身是谷歌的神经网络算法库DistBelief
import tensorflow.compat.v1 as tf
import cv2
import dlib  #
import numpy as np
import os
import random
import sys
from sklearn.model_selection import train_test_split  #

"""定义参数"""
faces_my_path = './faces_my'
faces_other_path = './faces_other'
batch_size = 128  # 每次取100张图片
learning_rate = 0.01  # 学习率
size = 64  # 图片大小64*64*3
imgs = []  # 定义数组存放人脸图片
labs = []  # 存放人脸图片对应的标签

tf.compat.v1.disable_v2_behavior()
x = tf.placeholder(tf.float32, [None, size, size, 3])  # 输入X:64*64*3
y_ = tf.placeholder(tf.float32, [None, 2])  # 输出Y_:1*2
keep_prob_fifty = tf.placeholder(tf.float32)  # 50%,即0.5
keep_prob_seventy_five = tf.placeholder(tf.float32)  # 75%,即0.75

"""定义读取人脸数据函数"""


def readData(path, h=size, w=size):
    for filename in os.listdir(path):  # os.listdir() 方法用于返回指定的文件夹包含的文件或文件夹的名字的列表
        if filename.endswith('.jpg'):
            filename = path + '/' + filename
            img = cv2.imread(filename)
            top, bottom, left, right = getPaddingSize(img)
            """放大图片扩充图片边缘部分"""
            # .BORDER_CONSTANT常数值填充
            # 在OpenCV滤波算法中,有两个非常重要的基本工具函数,copyMakeBorder和borderInterpolate
            """在图像处理中,因为卷积算子有一定的大小,所以会导致图像一定范围的边界不能被处理,就需要扩充"""
            img = cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_CONSTANT, value=[0, 0, 0])
            img = cv2.resize(img, (h, w))
            imgs.append(img)  # 一张张人脸图片加入imgs列表中
            labs.append(path)  # 一张张人脸图片对应的path,即文件夹名faces_my和faces_other,即标签.将对应的图片和标签存进数组


"""定义padding大小函数,有一套公式。"""
# 这里是得到图片的大小,进行统一处理,将图片改成统一大小
def getPaddingSize(img):
    # 得到图片高,宽,深度
    height, width, _ = img.shape
    top, bottom, left, right = (0, 0, 0, 0)
    longest = max(height, width)

    if width < longest:
        tmp = longest - width
        left = tmp // 2
        right = tmp - left
    elif height < longest:
        tmp = longest - height
        top = tmp // 2
        bottom = tmp - top
    else:
        pass
    return top, bottom, left, right


"""定义神经网络层,共五层,卷积层特征提取,池化层降维,全连接层进行分类,共两类:我的人脸(true),不是我的人脸(false)"""


# 下面开始进行卷积层的处理
# 卷积层加的padding为same是不会改变卷积层的大小的
def cnnLayer():
    """第一、二层,输入图片64*64*3,输出图片32*32*32"""
    W1 = tf.Variable(tf.random_normal([3, 3, 3, 32]))  # 卷积核大小(3,3), 输入通道(3), 输出通道(32)
    b1 = tf.Variable(tf.random_normal([32]))
    conv1 = tf.nn.relu(tf.nn.conv2d(x, W1, strides=[1, 1, 1, 1], padding='SAME') + b1)  # 64*64*32,卷积提取特征,增加通道数
    pool1 = tf.nn.max_pool(conv1, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')  # 32*32*32,池化降维,减小复杂度
    drop1 = tf.nn.dropout(pool1, keep_prob_fifty)

    """第三、四层,输入图片32*32*32,输出图片16*16*64"""
    W2 = tf.Variable(tf.random_normal([3, 3, 32, 64]))  # 卷积核大小(3,3), 输入通道(32), 输出通道(64)
    b2 = tf.Variable(tf.random_normal([64]))
    conv2 = tf.nn.relu(tf.nn.conv2d(drop1, W2, strides=[1, 1, 1, 1], padding='SAME') + b2)  # 32*32*64
    pool2 = tf.nn.max_pool(conv2, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')  # 16*16*64
    drop2 = tf.nn.dropout(pool2, keep_prob_fifty)

    """第五、六层,输入图片16*16*64,输出图片8*8*64"""
    W3 = tf.Variable(tf.random_normal([3, 3, 64, 64]))  # 卷积核大小(3,3), 输入通道(64), 输出通道(64)
    b3 = tf.Variable(tf.random_normal([64]))
    conv3 = tf.nn.relu(tf.nn.conv2d(drop2, W3, strides=[1, 1, 1, 1], padding='SAME') + b3)  # 16*16*64
    pool3 = tf.nn.max_pool(conv3, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')  # 8*8*64=4096
    drop3 = tf.nn.dropout(pool3, keep_prob_fifty)

    """第七层,全连接层,输入图片8*8*64,reshape到1*4096,输出1*512"""
    Wf = tf.Variable(tf.random_normal([8 * 8 * 64, 512]))  # 输入通道(4096), 输出通道(512)
    bf = tf.Variable(tf.random_normal([512]))
    drop3_flat = tf.reshape(drop3, [-1, 8 * 8 * 64])  # -1表示行随着列的需求改变,1*4096
    dense = tf.nn.relu(tf.matmul(drop3_flat, Wf) + bf)  # [1,4096]*[4096,512]=[1,512]
    dropf = tf.nn.dropout(dense, keep_prob_seventy_five)

    """第八层,输出层,输入1*512,输出1*2,再add,输出一个数"""
    Wout = tf.Variable(tf.random_normal([512, 2]))  # 输入通道(512), 输出通道(2)
    bout = tf.Variable(tf.random_normal([2]))
    out = tf.add(tf.matmul(dropf, Wout), bout)  # (1,512)*(512,2)=(1,2) ,跟y_ [0,1]、[1,0]比较给出损失
    return out


"""定义人脸识别函数"""


def face_recognise(image):
    res = sess.run(predict, feed_dict={x: [image / 255.0], keep_prob_fifty: 1.0, keep_prob_seventy_five: 1.0})
    if res[0] == 1:
        return "Yes,my face"
    else:
        return "No,other face"


if __name__ == '__main__':

    """1、读取人脸数据"""
    readData(faces_my_path)
    readData(faces_other_path)
    imgs = np.array(imgs)  # 将图片数据与标签转换成数组
    labs = np.array([[0, 1] if lab == faces_my_path else [1, 0] for lab in labs])  # 标签:[0,1]表示是我的人脸,[1,0]表示其他的人脸
    """2、随机划分测试集与训练集"""
    #  train_x_1包括所有自变量,这些变量将用于训练模型
    # test_x_1这是数据中剩余的自变量部分
    # train_y,这是因变量,需要此模型进行预测
    # test_y 此数据具有测试数据的类别标签,这些标签将用于测试实际类别和预测类别之间的准确性。
    #  random_state控制随机状态,
    # random.randint(0, 100))返回任意整数
    #  test_size:样本占比,如果是整数的话就是样本的数量
    # train_test_split 函数将原始数据按照比例分割为“测试集”和“训练集”
    train_x_1, test_x_1, train_y, test_y = train_test_split(imgs, labs, test_size=0.05,
                                                            random_state=random.randint(0, 100))
    train_x_2 = train_x_1.reshape(train_x_1.shape[0], size, size, 3)  # 参数:图片数据的总数,图片的高、宽、通道
    test_x_2 = test_x_1.reshape(test_x_1.shape[0], size, size, 3)
    train_x = train_x_2.astype('float32') / 255.0  # 归一化
    test_x = test_x_2.astype('float32') / 255.0
    print('Train Size:%s, Test Size:%s' % (len(train_x), len(test_x)))
    num_batch = len(train_x) // batch_size  # 22784//128=178
    """3、将读取的人脸图片输出神经网络,输出out(1,2)"""
    out = cnnLayer()
    """4、预测, 1表示按行返回out中最大值的索引,而不是out与1比谁大返回谁,predict为索引值,0或1,
    因为out的shape是(1,2),一行两列,两个数字"""
    predict = tf.argmax(out, 1)  # 0或者1

    """Saver的作用是将我们训练好的模型的参数保存下来,以便下一次继续用于训练或测试;Restore则是将训练好的参数提取出来。
        Saver类训练完后,是以checkpoints文件形式保存。提取的时候也是从checkpoints文件中恢复变量。"""
    # 保存和加载模型
    """Tensorflow 会自动生成4个文件
    训练后的模型

model.ckpt.meta,保存了 Tensorflow 计算图的结构,可以简单理解为神经网络的网络结构。

model.ckpt.index  文件保存了所有变量的取值。

 checkpoint 文件,保存了一个目录下所有的模型文件列表。"""
    saver = tf.train.Saver()

    sess = tf.Session()   # Session 是 Tensorflow 为了控制,和输出文件的执行的语句.
    # tf.train.latest_checkpoint()函数的作用查找最新保存的checkpoint文件的文件名
    saver.restore(sess, tf.train.latest_checkpoint('.'))
    """5、检测人脸,特征提取器: dlib自带的frontal_face_detector"""
    detector = dlib.get_frontal_face_detector()
    cap = cv2.VideoCapture(0)  # 打开摄像头
    while True:
        _, img = cap.read()  # 读取
        gray_image = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)  # 灰度化
        dets = detector(gray_image, 1)   # 使用detector进行人脸识别
        if not len(dets):

            key = cv2.waitKey(30)
            if key == 27:
                sys.exit(0)
        """--------------------------------------------------------------------
        使用enumerate 函数遍历序列中的元素以及它们的下标,i为人脸序号,d为i对应的元素;
        left:人脸左边距离图片左边界的距离 ;right:人脸右边距离图片左边界的距离 
        top:人脸上边距离图片上边界的距离 ;bottom:人脸下边距离图片上边界的距离
         ----------------------------------------------------------------------"""
        for i, d in enumerate(dets):
            x1 = d.top() if d.top() > 0 else 0
            y1 = d.bottom() if d.bottom() > 0 else 0
            x2 = d.left() if d.left() > 0 else 0
            y2 = d.right() if d.right() > 0 else 0
            """人脸大小64*64"""
            face = img[x1:y1, x2:y2]
            # 调整图片尺寸
            face = cv2.resize(face, (size, size))
            """6、人脸识别,输出识别结果"""
            print('It recognizes my face? %s' % face_recognise(face))

            """" (x2,y2)文本显示在图片的位置
                 cv2.FONT_HERSHEY_COMPLEX:字体类型
                 0.5字体大小
                 (0,255,0):显示的文本颜色(红色)
                 2:字体粗细,数值表示占几个像素"""

            cv2.rectangle(img, (x2, x1), (y2, y1), (255, 0, 0), 3)     # cv2.rectangle 这个函数的作用是在图像上绘制一个简单的矩形。
            if face_recognise(face) == "Yes,my face":   # face_recognition库
                # 各参数依次是:图片,添加的文字,左上角坐标,字体,字体大小,颜色,字体粗细
                cv2.putText(img, 'Yes,my face', (x1, y1), cv2.FONT_HERSHEY_COMPLEX, 1, (0, 255, 0), 2)
            else:
                cv2.putText(img, 'No,other face', (x1, y1), cv2.FONT_HERSHEY_COMPLEX, 1, (0, 255, 0), 2)

        cv2.imshow('image', img)   # 展示
        key = cv2.waitKey(30)
        if key == 27:
            sys.exit(0)

    sess.close()

### 使用Anaconda导库

### 图片的构成方式(颜色协议,哔哩哔哩可看博主讲解)

### 人脸数据集下载:http://vis-www.cs.umass.edu/lfw/lfw.tgz

  • 34
    点赞
  • 382
    收藏
    觉得还不错? 一键收藏
  • 26
    评论
评论 26
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值