导航菜单

「深度学习」用TensorFlow实现人脸识别(附源码,快速get技能)

  本文将会带你使用python码一个卷积神经网络模型,实现人脸识别,操作难度比较低,动手跟着做吧,让你的电脑认出你那帅气的脸。

  由于代码篇幅较长,而且最重要的缩进都没了,建议直接打开源码或者点击分享->复制链接,然后到浏览器里观看,执行顺序(源码在src目录下)

  获取人脸数据:catchFace.py

  制作数据集:python mkDataset.py

  训练cnn:python trainFace.py

  测试:python testFace.py

  源码地址:https://gitee.com/face-id/practice/tree/release%2Fopencv-tensorflow-convol-camera-sample/

  适合的读者:了解机器学习基础知识(线性代数、微积分求导求偏导);了解卷积神经网络;了解TensorFlow;熟悉python;最好用卷积神经网络实践过“手写数字识别”

  「深度学习」用TensorFlow实现人脸识别(附源码,快速get技能)

  图片来源于网络,有疑问请联系庄大叔

  目录

  准备工作获取人脸数据制作人脸数据集(分为训练集和测试集)构建并训练cnn(重点)识别人脸总结

  Python3.x;

  tensorflow(深度学习框架,可以用anaconda安装);

  opencv(Python图像处理库);

  摄像头(用于捕获人脸数据);

  出于习惯,我把各个独立的小功能都封装成一个函数,因此函数比较多,但都比较简单。

  1.导包

  import sys

  import cv2

  import os

  import shutil

  2.声明配置

  #type取值

  TYPE_TRAIN = 'train'

  TYPE_TEST = 'test'

  #cv2识别出人脸后使用的颜色

  COLOR_CV2_FRONTALFACE = (0, 255, 0)

  #识别出的人脸坐标要往外拓宽多少

  FACE_GIRD_EXT_SIZE = 10

  #图片储存地址

  PATH_FACE_SAVE = "D:\\\\bruce\\\\face-id\\\\practice\\data\\\\face"

  #一个人需要取多少张训练样本和测试样本

  SUM_OF_FACE_TRAIN = 200

  SUM_OF_FACE_TEST = 50

  #cv2人脸识别分类器地址

  PATH_CLASSFIER_CV2_FRONTALFACE_ALT2 = "C:\\\\ProgramData\\\\Anaconda3\\\\Library\\\\etc\\\\haarcascades\\\\haarcascade_frontalface_alt2.xml"

  # 告诉OpenCV使用人脸识别分类器

  classfier = cv2.CascadeClassifier(PATH_CLASSFIER_CV2_FRONTALFACE_ALT2)

  #统计已经保存了多少张人脸

  num = 0

  3.函数定义

  def getFaceSavePath(name, num, type):

  '''

  获取该张人脸的存放路径

  :param name '人名'

  :param num '第几张人脸'

  :param type '训练类型'

  :return '存放路径'

  '''

  return '{}/{}/{}/{}.jpg'.format(PATH_FACE_SAVE, name, type, num+1)

  #-----------------头条里不能换行,函数分割线------------------

  def getFaceGird(face_rect):

  '''

  获取人脸坐标

  '''

  x, y, w, h = face_rect

  t = y + h + FACE_GIRD_EXT_SIZE #top

  r = x + w + FACE_GIRD_EXT_SIZE #right

  b = y - FACE_GIRD_EXT_SIZE #bottom

  l = x - FACE_GIRD_EXT_SIZE #left

  return t, b, r, l

  #-----------------头条里不能换行,函数分割线------------------

  def discernAndSaveFace(frame, name, type, sum):

  '''

  识别人脸并保存

  '''

  global num

  # 将当前桢图像转换成灰度图像

  grey = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

  # 人脸检测,scaleFactor和minNeighbors分别为图片缩放比例和需要检测的有效点数

  face_rects = classfier.detectMultiScale(grey, scaleFactor=1.2, minNeighbors=3, minSize=(32, 32))

  #如果只有一张人脸则保存当前帧

  if len(face_rects) == 1:

  t, b, r, l = getFaceGird(face_rects[0]) #坐标

  image = frame[b: t, l: r]

  cv2.imwrite(getFaceSavePath(name, num, type), image)

  num += 1

  #标记人脸

  if len(face_rects) > 0:

  for face_rect in face_rects: # 单独框出每一张人脸

  t, b, r, l = getFaceGird(face_rect) #坐标

  # 框出人脸

  cv2.rectangle(frame, (l, b), (r, t), COLOR_CV2_FRONTALFACE, 2)

  # 统计当前捕捉的人脸数量

  cv2.putText(frame, '{}num:{}/{}'.format(type, num, sum), (l + 30, b + 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 255), 4)

  #-----------------头条里不能换行,函数分割线------------------

  def buildFaceDir(name):

  '''

  创建/清空目标文件夹

  :param name '人名'

  '''

  dir = '{}/{}'.format('D:\\\\bruce\\\\face-id\\\\practice\\data\\\\face', name)

  if os.path.isdir(dir):

  shutil.rmtree(dir)

  os.mkdir(dir)

  #存放训练数据

  os.mkdir('{}/{}'.format(dir, TYPE_TRAIN))

  #存放测试数据

  os.mkdir('{}/{}'.format(dir, TYPE_TEST))

  #-----------------头条里不能换行,函数分割线------------------

  def catchFaceFromCamera(name):

  '''

  从摄像头捕捉人脸并保存为训练/测试样本

  :param name 人名

  '''

  global num

  print('start to catch face of{}'.format(name))

  #要捕捉的人脸总数

  catch_sum = SUM_OF_FACE_TRAIN

  # 调用摄像头,conf.CAMERA_IDX为摄像头索引,默认为0

  cap = cv2.VideoCapture(0)

  #构建该人脸的存放目录

  buildFaceDir(name)

  #开始捕捉数据(人脸)

  current_type = TYPE_TRAIN

  while True:

  #训练数据捕捉完了,则捕捉测试数据

  if num >= catch_sum and current_type == TYPE_TRAIN:

  current_type = TYPE_TEST

  num = 0

  catch_sum = SUM_OF_FACE_TEST

  #测试数据也捕捉完了,则退出

  elif num >= catch_sum and current_type == TYPE_TEST:

  break

  #其他情况则退出

  elif num >= catch_sum:

  raise Exception('current_type error')

  # 读取一帧数据

  if cap.isOpened()==False:

  break

  ok, frame = cap.read()

  if not ok:

  break

  #识别人脸并保存

  if current_type==TYPE_TRAIN:

  discernAndSaveFace(frame, name, TYPE_TRAIN, SUM_OF_FACE_TRAIN)

  else:

  discernAndSaveFace(frame, name, TYPE_TEST, SUM_OF_FACE_TEST)

  # 显示图像

  cv2.imshow(name, frame)

  #监听输入,按esc退出

  c = cv2.waitKey(10)

  if c & 0xFF == 27:

  break

  # 释放摄像头并销毁所有窗口

  cap.release()

  cv2.destroyAllWindows()

  4.执行

  if __name__ == '__main__':

  # 开始截取脸部图像

  catchFaceFromCamera('bruce')

  「深度学习」用TensorFlow实现人脸识别(附源码,快速get技能)

  捕捉人脸结果

  「深度学习」用TensorFlow实现人脸识别(附源码,快速get技能)

  捕捉人脸结果

  执行上述代码后你对这摄像头两分钟就能收获到几百张人脸啦

  1.导包

  import os

  import numpy as np

  import cv2

  2.变量声明

  #type取值

  TYPE_TRAIN = 'train'

  TYPE_TEST = 'test'

  #图片储存地址

  PATH_FACE_SAVE = "D:\\\\bruce\\\\face-id\\\\practice\\data\\\\face"

  #数据集储存地址

  PATH_DATASET_SAVE = "D:\\\\bruce\\\\face-id\\\\practice\\data\\\\dataset"

  #调整图片大小时扩充的地方填充的颜色

  RESIZE_FILL_COLOR = (0,0,0)

  #人脸图片的大小

  FACE_SIZE = 64

  3.函数定义

  #-----------------头条里不能换行,函数分割线------------------

  def resizeImage(image, height, width):

  '''按照指定图像大小调整尺寸'''

  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

  # 给图像增加边界,是图片长、宽等长,cv2.BORDER_CONSTANT指定边界颜色由value指定

  constant = cv2.copyMakeBorder(image, top, bottom, left, right, cv2.BORDER_CONSTANT, value=RESIZE_FILL_COLOR)

  # 调整图像大小并返回

  return cv2.resize(constant, (height, width))

  #-----------------头条里不能换行,函数分割线------------------

  def readFace(path):

  '''

  读取人脸

  '''

  print(path)

  image = cv2.imread(path)

  image = resizeImage(image, FACE_SIZE, FACE_SIZE)

  # 放开这个代码,可以看到resize_image()函数的实际调用效果

  # cv2.imwrite('{}.jpg'.format(1), image)

  return image

  #-----------------头条里不能换行,函数分割线------------------

  def readFacesAndLabels(dir, label, images=[], labels=[]):

  '''

  读取脸部图片,返回图片数组和标签数组

  '''

  for face in os.listdir(dir):

  path = '{}/{}'.format(dir, face)

  #读取成图片

  image = readFace(path)

  images.append(image)

  labels.append(label)

  return images, labels

  #-----------------头条里不能换行,函数分割线------------------

  def getFaceDir(name):

  '''

  获取目标文件夹的训练/测试数据目录

  '''

  train_dir = '{}/{}/{}'.format(PATH_FACE_SAVE, name, TYPE_TRAIN)

  test_dir = '{}/{}/{}'.format(PATH_FACE_SAVE, name, TYPE_TEST)

  return train_dir, test_dir

  #-----------------头条里不能换行,函数分割线------------------

  def getNameList():

  '''

  获取名称列表

  '''

  names =[];

  for dir_item in os.listdir(PATH_FACE_SAVE):

  names.append(dir_item)

  return names

  #-----------------头条里不能换行,函数分割线------------------

  def loadDataset(type):

  '''

  读取数据集

  '''

  dir = PATH_DATASET_SAVE

  images = np.load('{}/{}_images.npy'.format(dir, type))

  labels = np.load('{}/{}_labels.npy'.format(dir, type))

  names_map = loadDict('{}/{}_names_map.npy'.format(dir, type))

  return images, labels, names_map

  #-----------------头条里不能换行,函数分割线------------------

  def saveDataset(images, labels, names_map, type):

  '''

  保存数据集

  '''

  dir = PATH_DATASET_SAVE

  if os.path.isdir(dir)==False:

  os.mkdir(dir)

  np.save('{}/{}_images.npy'.format(dir, type), images)

  np.save('{}/{}_labels.npy'.format(dir, type), labels)

  saveDict('{}/{}_names_map.npy'.format(dir, type), names_map)

  #-----------------头条里不能换行,函数分割线------------------

  def saveDict(path, dict):

  '''

  保存字典

  '''

  f = open(path, 'w')

  f.write(str(dict))

  f.close()

  #-----------------头条里不能换行,函数分割线------------------

  def loadDict(path):

  '''

  读取字典

  '''

  f = open(path, 'r')

  a = f.read()

  dict = eval(a)

  f.close()

  return dict

  #-----------------头条里不能换行,函数分割线------------------

  def mkDataset(type):

  '''

  制作数据集

  '''

  images, labels = ([],[])

  names = getNameList()

  #读取所有名字的所有人脸图片数据

  for name in names:

  #读取该名字下的具体人脸目录

  train_dir, test_dir = getFaceDir(name)

  #将目录下的所有人脸读进images,并同步记录labels

  if type==TYPE_TRAIN:

  images, labels = readFacesAndLabels(train_dir, name, images, labels)

  else:

  images, labels = readFacesAndLabels(test_dir, name, images, labels)

  # 将输入的所有图片转成四维矩阵(数组),方便计算,尺寸为(图片数量*FACE_SIZE*FACE_SIZE*3)

  # 两个人共1200张图片,IMAGE_SIZE为64,故对我来说尺寸为1200 * 64 * 64 * 3

  # 图片为64 * 64像素,一个像素3个颜色值(RGB)

  images = np.array(images)

  # 名字用数字对应

  names_map ={};

  for i in range(len(names)):

  names_map[names[i]]= i

  #将名称字典打印出来,以便后面进行对照

  print(names_map);

  labels = np.array([names_map[label]for label in labels])

  #将标签转为one-hot形式

  labels = ((np.arange(len(names))==labels[:,None]).astype(np.integer))

  return images, labels, names_map

  4.执行

  if __name__ == '__main__':

  # 制作训练集

  images, labels, names_map = mkDataset(TYPE_TRAIN)

  saveDataset(images, labels, names_map, TYPE_TRAIN)

  # 制作测试集

  images, labels, names_map = mkDataset(TYPE_TEST)

  saveDataset(images, labels, names_map, TYPE_TEST)

  「深度学习」用TensorFlow实现人脸识别(附源码,快速get技能)

  数据集

  制作数据集以后会有6个文件,训练集有三个(图片、标签/人名、标签/人名索引字典)文件,测试集也有对应的3个文件。

  这里所谓的索引字典,其实只是为了建立【string->int】这样的映射,因为人脸识别,最终还是属于分类,分类集合适合用one-hot编码去运算,如用1表示星期一,2表示星期二;也可以用one-hot编码,如用[1,0,0,0,0,0,0]表示星期一,[0,1,0,0,0,0,0]表示星期二,这样的话集合里的元素肯定不能是字符串,所以就有索引字典(我自己是这么叫,可能名字起得不太好,见谅)这个东西了。

  这里的卷积神经网络需要先了解这个概念,没了解过的同学可以先看看吴恩达的视频入入门,然后再来看这个项目。

  1.导包

  import numpy as np

  import logging as log

  import tensorflow as tf

  2.变量定义

  #type取值

  TYPE_TRAIN = 'train'

  TYPE_TEST = 'test'

  #数据集储存地址

  PATH_DATASET_SAVE = "D:\\\\bruce\\\\face-id\\\\practice\\data\\\\dataset"

  #人脸图片的大小

  SIZE = 64

  #模型储存地址

  PATH_MODEL_SAVE = "D:\\\\bruce\\\\face-id\\\\practice\\data\\\\model"

  #循环一次训练多少样本

  TRAIN_BATCH_SIZE = 100

  #循环训练次数

  TRAIN_TIMES = 20

  3.函数定义

  x_data = tf.placeholder(tf.float32,[None, SIZE, SIZE, 3])

  y_data = tf.placeholder(tf.float32,[None, None])

  keep_prob_5 = tf.placeholder(tf.float32)

  keep_prob_75 = tf.placeholder(tf.float32)

  #-----------------头条里不能换行,函数分割线------------------

  def loadDict(path):

  '''

  读取字典

  '''

  f = open(path, 'r')

  a = f.read()

  dict = eval(a)

  f.close()

  return dict

  #-----------------头条里不能换行,函数分割线------------------

  def loadDataset(type):

  '''

  读取数据集

  '''

  dir = PATH_DATASET_SAVE

  images = np.load('{}/{}_images.npy'.format(dir, type))

  labels = np.load('{}/{}_labels.npy'.format(dir, type))

  names_map = loadDict('{}/{}_names_map.npy'.format(dir, type))

  return images, labels, names_map

  #-----------------头条里不能换行,函数分割线------------------

  def weightVariable(shape):

  '''定义Weight变量,输入shape,返回变量的参数。其中我们使用了tf.random_normal产生随机变量来进行初始化'''

  init = tf.random_normal(shape, stddev=0.01)

  #init = tf.truncated_normal(shape, stddev=0.01)

  return tf.Variable(init)

  #-----------------头条里不能换行,函数分割线------------------

  def biasVariable(shape):

  ''' 定义biase变量,输入shape,返回变量的一些参数。'''

  init = tf.random_normal(shape)

  #init = tf.truncated_normal(shape, stddev=0.01)

  return tf.Variable(init)

  #-----------------头条里不能换行,函数分割线------------------

  def conv2d(x, W):

  '''

  定义卷积操作。tf.nn.conv2d函数是Tensorflow里面的二维的卷积函数,x是图片的所有参数,W是卷积层的权重,然后定义步长strides=[1,1,1,1]值。strides[0]和strides[3]的两个1是默认值,意思是不对样本个数和channel进行卷积,中间两个1代表padding是在x方向运动一步,y方向运动一步,padding采用的方式实“SAME”就是0填充。

  :param x:

  :param W:

  :return:

  '''

  return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')

  #-----------------头条里不能换行,函数分割线------------------

  def maxPool(x):

  '''定义池化操作。为了得到更多的图片信息,卷积时我们选择的是一次一步,也就是strides[1]=strides[2]=1,这样得到的图片尺寸没有变化,而我们希望压缩一下图片也就是参数能少一些从而减少系统的复杂度,因此我们采用pooling来稀疏化参数,也就是卷积神经网络中所谓的下采样层。'''

  return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')

  #-----------------头条里不能换行,函数分割线------------------

  def dropout(x, keep):

  '''为了防止过拟合的问题,可以加一个dropout的处理。'''

  return tf.nn.dropout(x, keep)

  #-----------------头条里不能换行,函数分割线------------------

  def cnnLayer(classnum):

  '''创建卷积层'''

  # 第一层

  W1 = weightVariable([3, 3, 3, 32]) # 卷积核大小(3,3), 输入通道(3), 输出通道(32)

  b1 = biasVariable([32])

  conv1 = tf.nn.relu(conv2d(x_data, W1) + b1)

  pool1 = maxPool(conv1)

  # 减少过拟合,随机让某些权重不更新

  drop1 = dropout(pool1, keep_prob_5) # 32 * 32 * 32 多个输入channel 被filter内积掉了

  # 第二层

  W2 = weightVariable([3, 3, 32, 64])

  b2 = biasVariable([64])

  conv2 = tf.nn.relu(conv2d(drop1, W2) + b2)

  pool2 = maxPool(conv2)

  drop2 = dropout(pool2, keep_prob_5) # 64 * 16 * 16

  # 第三层

  W3 = weightVariable([3, 3, 64, 64])

  b3 = biasVariable([64])

  conv3 = tf.nn.relu(conv2d(drop2, W3) + b3)

  pool3 = maxPool(conv3)

  drop3 = dropout(pool3, keep_prob_5) # 64 * 8 * 8

  # 全连接层

  Wf = weightVariable([8*16*32, 512])

  bf = biasVariable([512])

  drop3_flat = tf.reshape(drop3,[-1, 8*16*32])

  dense = tf.nn.relu(tf.matmul(drop3_flat, Wf) + bf)

  dropf = dropout(dense, keep_prob_75)

  # 输出层

  Wout = weightVariable([512, classnum])

  bout = weightVariable([classnum])

  #out = tf.matmul(dropf, Wout) + bout

  out = tf.add(tf.matmul(dropf, Wout), bout)

  return out

  '''

  开始训练模型

  '''

  def train(train_x, train_y, test_x, test_y, tfsavepath):

  log.debug('train')

  out = cnnLayer(train_y.shape[1])

  cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=out, labels=y_data))

  train_step = tf.train.AdamOptimizer(0.01).minimize(cross_entropy)

  accuracy = tf.reduce_mean(tf.cast(tf.equal(tf.argmax(out, 1), tf.argmax(y_data, 1)), tf.float32))

  saver = tf.train.Saver()

  with tf.Session() as sess:

  sess.run(tf.global_variables_initializer())

  train_times = TRAIN_TIMES

  batch_size = TRAIN_BATCH_SIZE

  num_batch = len(train_x) // batch_size

  for n in range(train_times):

  r = np.random.permutation(len(train_x))

  train_x = train_x[r, :]

  train_y = train_y[r, :]

  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]

  #loss:损失函数的值,当实际输出接近预期,那么损失函数应该接近0

  _, loss = sess.run([train_step, cross_entropy],\\

  feed_dict={x_data:batch_x, y_data:batch_y,

  keep_prob_5:0.75, keep_prob_75:0.75})

  print('batch times:{}, loss:{}'.format(n*num_batch+i+1, loss))

  # 获取测试数据的准确率

  acc = accuracy.eval({x_data:test_x, y_data:test_y, keep_prob_5:1.0, keep_prob_75:1.0})

  print('after{}times run: accuracy is{}'.format(n+1, acc))

  # 获取测试数据的准确率

  # acc = accuracy.eval({x_data:test_x, y_data:test_y, keep_prob_5:1.0, keep_prob_75:1.0})

  # print('after{}times run: accuracy is{}'.format(train_times, acc))

  saver.save(sess, tfsavepath)

  4.执行

  if __name__ == '__main__':

  train_x, train_y, names_map = loadDataset(TYPE_TRAIN)

  train_x = train_x.astype(np.float32) / 255.0

  test_x, test_y, names_map = loadDataset(TYPE_TEST)

  test_x = test_x.astype(np.float32) / 255.0

  train(train_x, train_y, test_x, test_y, "{}/model.ckpt".format(PATH_MODEL_SAVE))

  「深度学习」用TensorFlow实现人脸识别(附源码,快速get技能)

  训练结果

  从图片可以看出,这个模型构建的还是不错的,第9次训练以后损失函数值稳步下降,到了最后快趋近0了(越接近0越好,说明预测值离实际值很接近),测试正确率也是稳步上升,后面几乎都是1(100%预测正确),测试集的样本是一人200张,两个人加起来400张,也就是后面的预测结果基本都是400次全对。

  1.导包

  import cv2

  import tensorflow as tf

  import numpy as np

  2.变量声明

  #type取值

  TYPE_TRAIN = 'train'

  TYPE_TEST = 'test'

  #计算机摄像设备索引

  CAMERA_IDX = 0

  #cv2人脸识别分类器地址

  PATH_CLASSFIER_CV2_FRONTALFACE_ALT2 = "C:\\\\ProgramData\\\\Anaconda3\\\\Library\\\\etc\\\\haarcascades\\\\haarcascade_frontalface_alt2.xml"

  #数据集储存地址

  PATH_DATASET_SAVE = "D:\\\\bruce\\\\face-id\\\\practice\\data\\\\dataset"

  #模型储存地址

  PATH_MODEL_SAVE = "D:\\\\bruce\\\\face-id\\\\practice\\data\\\\model"

  #识别出的人脸坐标要往外拓宽多少

  FACE_GIRD_EXT_SIZE = 10

  #人脸图片的大小

  SIZE = 64

  #cv2识别出人脸后使用的颜色

  COLOR_CV2_FRONTALFACE = (0, 255, 0)

  #调整图片大小时扩充的地方填充的颜色

  RESIZE_FILL_COLOR = (0,0,0)

  # 告诉OpenCV使用人脸识别分类器

  classfier = cv2.CascadeClassifier(PATH_CLASSFIER_CV2_FRONTALFACE_ALT2)

  #统计已经保存了多少张人脸

  num = 0

  3.函数定义

  x_data = tf.placeholder(tf.float32,[None, SIZE, SIZE, 3])

  y_data = tf.placeholder(tf.float32,[None, None])

  keep_prob_5 = tf.placeholder(tf.float32)

  keep_prob_75 = tf.placeholder(tf.float32)

  def weightVariable(shape):

  '''定义Weight变量,输入shape,返回变量的参数。其中我们使用了tf.random_normal产生随机变量来进行初始化'''

  init = tf.random_normal(shape, stddev=0.01)

  #init = tf.truncated_normal(shape, stddev=0.01)

  return tf.Variable(init)

  def biasVariable(shape):

  ''' 定义biase变量,输入shape,返回变量的一些参数。'''

  init = tf.random_normal(shape)

  #init = tf.truncated_normal(shape, stddev=0.01)

  return tf.Variable(init)

  def conv2d(x, W):

  '''

  定义卷积操作。tf.nn.conv2d函数是Tensorflow里面的二维的卷积函数,x是图片的所有参数,W是卷积层的权重,然后定义步长strides=[1,1,1,1]值。strides[0]和strides[3]的两个1是默认值,意思是不对样本个数和channel进行卷积,中间两个1代表padding是在x方向运动一步,y方向运动一步,padding采用的方式实“SAME”就是0填充。

  :param x:

  :param W:

  :return:

  '''

  return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')

  def maxPool(x):

  '''定义池化操作。为了得到更多的图片信息,卷积时我们选择的是一次一步,也就是strides[1]=strides[2]=1,这样得到的图片尺寸没有变化,而我们希望压缩一下图片也就是参数能少一些从而减少系统的复杂度,因此我们采用pooling来稀疏化参数,也就是卷积神经网络中所谓的下采样层。'''

  return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')

  def dropout(x, keep):

  '''为了防止过拟合的问题,可以加一个dropout的处理。'''

  return tf.nn.dropout(x, keep)

  def cnnLayer(classnum):

  '''创建卷积层'''

  # 第一层

  W1 = weightVariable([3, 3, 3, 32]) # 卷积核大小(3,3), 输入通道(3), 输出通道(32)

  b1 = biasVariable([32])

  conv1 = tf.nn.relu(conv2d(x_data, W1) + b1)

  pool1 = maxPool(conv1)

  # 减少过拟合,随机让某些权重不更新

  drop1 = dropout(pool1, keep_prob_5) # 32 * 32 * 32 多个输入channel 被filter内积掉了

  # 第二层

  W2 = weightVariable([3, 3, 32, 64])

  b2 = biasVariable([64])

  conv2 = tf.nn.relu(conv2d(drop1, W2) + b2)

  pool2 = maxPool(conv2)

  drop2 = dropout(pool2, keep_prob_5) # 64 * 16 * 16

  # 第三层

  W3 = weightVariable([3, 3, 64, 64])

  b3 = biasVariable([64])

  conv3 = tf.nn.relu(conv2d(drop2, W3) + b3)

  pool3 = maxPool(conv3)

  drop3 = dropout(pool3, keep_prob_5) # 64 * 8 * 8

  # 全连接层

  Wf = weightVariable([8*16*32, 512])

  bf = biasVariable([512])

  drop3_flat = tf.reshape(drop3,[-1, 8*16*32])

  dense = tf.nn.relu(tf.matmul(drop3_flat, Wf) + bf)

  dropf = dropout(dense, keep_prob_75)

  # 输出层

  Wout = weightVariable([512, classnum])

  bout = weightVariable([classnum])

  #out = tf.matmul(dropf, Wout) + bout

  out = tf.add(tf.matmul(dropf, Wout), bout)

  return out

  def loadDataset(type):

  '''

  读取数据集

  '''

  dir = PATH_DATASET_SAVE

  images = np.load('{}/{}_images.npy'.format(dir, type))

  labels = np.load('{}/{}_labels.npy'.format(dir, type))

  names_map = loadDict('{}/{}_names_map.npy'.format(dir, type))

  return images, labels, names_map

  def loadDict(path):

  '''

  读取字典

  '''

  f = open(path, 'r')

  a = f.read()

  dict = eval(a)

  f.close()

  return dict

  def getFaceGird(face_rect):

  '''

  获取人脸坐标

  '''

  x, y, w, h = face_rect

  t = y + h + FACE_GIRD_EXT_SIZE #top

  r = x + w + FACE_GIRD_EXT_SIZE #right

  b = y - FACE_GIRD_EXT_SIZE #bottom

  l = x - FACE_GIRD_EXT_SIZE #left

  return t, b, r, l

  '''

  识别摄像头里的人脸并打上名字

  '''

  def testFaceFromCamera():

  chkpoint = "{}/model.ckpt".format(PATH_MODEL_SAVE)

  # 调用摄像头,conf.CAMERA_IDX为摄像头索引,默认为0,也可以这样写cap = cv2.VideoCapture(0)

  cap = cv2.VideoCapture(CAMERA_IDX)

  #

  test_x, test_y, names_map = loadDataset(TYPE_TEST)

  output = cnnLayer(test_y.shape[1])

  predict = output

  saver = tf.train.Saver()

  with tf.Session() as sess:

  #还原模型变量

  saver.restore(sess, chkpoint)

  while True:

  # 读取一帧数据

  if cap.isOpened()==False:

  break

  ok, frame = cap.read()

  if not ok:

  break

  #识别

  discernAndCallFace(frame, sess, predict, output, names_map)

  #显示图像

  cv2.imshow('testFace', frame)

  #监听输入,按esc退出

  c = cv2.waitKey(10)

  if c & 0xFF == 27:

  break

  # 释放摄像头并销毁所有窗口

  cap.release()

  cv2.destroyAllWindows()

  def resizeImage(image, height, width):

  '''按照指定图像大小调整尺寸'''

  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

  # 给图像增加边界,是图片长、宽等长,cv2.BORDER_CONSTANT指定边界颜色由value指定

  constant = cv2.copyMakeBorder(image, top, bottom, left, right, cv2.BORDER_CONSTANT, value=RESIZE_FILL_COLOR)

  # 调整图像大小并返回

  return cv2.resize(constant, (height, width))

  '''

  识别人脸并标注名字

  '''

  def discernAndCallFace(frame, sess, predict, output, names_map):

  # 将当前桢图像转换成灰度图像

  grey = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

  # 人脸检测,scaleFactor和minNeighbors分别为图片缩放比例和需要检测的有效点数

  face_rects = classfier.detectMultiScale(grey, scaleFactor=1.2, minNeighbors=3, minSize=(32, 32))

  #标记人脸

  if len(face_rects) > 0:

  for face_rect in face_rects: # 单独框出每一张人脸

  t, b, r, l = getFaceGird(face_rect) #坐标

  # 框出人脸

  cv2.rectangle(frame, (l, b), (r, t), COLOR_CV2_FRONTALFACE, 2)

  #调整图像尺寸

  image = frame[b: t, l: r]

  image = resizeImage(image, SIZE, SIZE)

  #转换成模型输入变量

  test_x = np.array([image])

  test_x = test_x.astype(np.float32) / 255.0

  #计算分类概率,我们用到的是res[1][0],res的结构为[([[-2.8184052, 2.8337383]], dtype=float32), ([1], dtype=int64)]

  res = sess.run([predict, tf.argmax(output, 1)], \\

  feed_dict={x_data: test_x, \\

  keep_prob_5: 1.0, keep_prob_75: 1.0})

  print(res)

  # 显示名字

  # names_map ={'bruce': 0, 'canzhou': 1}

  names_map = dict(zip(names_map.values(), names_map.keys()))

  print(names_map)

  cv2.putText(frame, '{}'.format(names_map[res[1][0]]), (l + 30, b + 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 255), 4)

  4.执行

  if __name__ == '__main__':

  testFaceFromCamera()

  「深度学习」用TensorFlow实现人脸识别(附源码,快速get技能)

  人脸识别结果

  从截图可以看到人脸成功被识别(布鲁斯就是我,专门跑到老友宿舍测试,啧啧)

  本篇文章的目的在于实践卷积神经网络,如果之前了解过cnn,再来跟着实践一次,效果会非常好。

  另外,项目原本的代码是按照模块区分,统一入口为main.py,为了同学们更容易尝试,我简单粗暴的把几个步骤独立成一个个py文件,独立运行,有强迫症的点这个地址看原本的代码:https://gitee.com/face-id/practice/tree/release/opencv-tensorflow-convol-camera-sample

  我是搞技术的庄大叔,以上内容如果有误,请广大条友指正。