当前位置:   article > 正文

人脸识别 openCV+tensorflow_tensorflow训练opencv人脸模型

tensorflow训练opencv人脸模型

环境

win10,jupyter notebook,python3.7,tensorflow2,cpu

sklearn datasets数据集

先用一个sklearn自带的数据集做一个简单的人脸识别测试。

  1. from sklearn import datasets
  2. from matplotlib import pyplot as plt
  3. import tensorflow.keras as keras
  4. import numpy as np
  5. faces = datasets.fetch_olivetti_faces()
  6. print(faces.images.shape)
  7. i = 0
  8. plt.figure(figsize=(20, 20))
  9. for img in faces.images:
  10. #总共400张图,把图像分割成20X20
  11. plt.subplot(20, 20, i+1)
  12. plt.imshow(img, cmap="gray")
  13. #关闭x,y轴显示
  14. plt.xticks([])
  15. plt.yticks([])
  16. plt.xlabel(faces.target[i])
  17. i = i + 1
  18. plt.show()
  19. #人脸数据
  20. X = faces.images
  21. #人脸对应的标签
  22. y = faces.target
  23. print(X[0])
  24. print(y[0])
  25. X = X.reshape(400, 64, 64, 1)
  26. from sklearn.model_selection import train_test_split
  27. X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)
  28. model = keras.Sequential()
  29. # 第一层卷积,卷积的数量为128,卷积的高和宽是3x3,激活函数使用relu
  30. model.add(keras.layers.Conv2D(128, kernel_size=3, activation='relu', input_shape=(64, 64, 1)))
  31. # 第二层卷积
  32. model.add(keras.layers.Conv2D(64, kernel_size=3, activation='relu'))
  33. #把多维数组压缩成一维,里面的操作可以简单理解为reshape,方便后面Dense使用
  34. model.add(keras.layers.Flatten())
  35. #对应cnn的全链接层,可以简单理解为把上面的小图汇集起来,进行分类
  36. model.add(keras.layers.Dense(40, activation='softmax'))
  37. model.compile(optimizer='adam',
  38. loss='sparse_categorical_crossentropy',
  39. metrics=['accuracy'])
  40. model.fit(X_train, y_train, epochs=10)
  41. y_predict = model.predict(X_test)
  42. print(y_test[0], np.argmax(y_predict[0]))

ok。下面我们利用openCV和tensorflow建立自己的人脸识别模型

openCV+tensorflow人脸识别模型

导入模块

  1. import cv2
  2. import sys
  3. import os
  4. import numpy as np
  5. import random
  6. import tensorflow as tf
  7. from tensorflow.keras.layers import Dense, Flatten, Conv2D
  8. from tensorflow.keras import Model
  9. from sklearn.model_selection import train_test_split
  10. from keras import backend as K
  11. from PIL import Image, ImageDraw, ImageFont
  12. from __future__ import absolute_import, division, print_function, unicode_literals

捕捉图像

该步骤使用openCV捕捉摄像头或视频,按帧数取图片,每个标签取200帧。

摄像头和视频地址设置见注释,需要注意的是级联分类器的地址需改为自己的openCV 库安装位置

  1. def CatchPICFromVideo(window_name, camera_idx, catch_pic_num, path_name):
  2. cv2.namedWindow(window_name)
  3. #设置视频来源,0为摄像头,“视频地址,如C:/Users/lishu/Desktop/ob/x/face/zjm/zjm.mp4”
  4. cap = cv2.VideoCapture(0)
  5. #openCV级联分类器地址:openCV库安装地址找haarcascade_frontalface_alt2.xml文件
  6. classfier = cv2.CascadeClassifier("C:/Users/lishu/Anaconda3/Lib/site-packages/cv2/data/haarcascade_frontalface_alt2.xml")
  7. color = (255, 0, 0)
  8. num = 0
  9. while cap.isOpened():
  10. ok, frame = cap.read()#frame是读取的图像,三阶矩阵
  11. if not ok:
  12. break
  13. grey = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
  14. faceRects = classfier.detectMultiScale(grey, scaleFactor = 1.2, minNeighbors = 3, minSize = (32, 32))
  15. if len(faceRects) > 0:
  16. for faceRect in faceRects:
  17. x, y, w, h = faceRect
  18. img_name = '%s%d.jpg'%(path_name, num)#保存当前帧为图片
  19. image = frame[y - 10: y + h + 10, x - 10: x + w + 10]
  20. cv2.imwrite(img_name, image)
  21. num += 1
  22. if num > (catch_pic_num):
  23. break
  24. cv2.rectangle(frame, (x - 10, y - 10), (x + w + 10, y + h + 10), color, 2)
  25. font = cv2.FONT_HERSHEY_SIMPLEX
  26. cv2.putText(frame, 'num:%d' % (num), (30, 30), font, 1, (255, 0, 0), 1)
  27. if num > (catch_pic_num):break
  28. cv2.imshow(window_name, frame)
  29. c = cv2.waitKey(10)
  30. if c & 0xFF == ord('q'):
  31. break
  32. cap.release()
  33. cv2.destroyAllWindows()
  34. if __name__ == '__main__':
  35. CatchPICFromVideo("catch_face_data", 0, 200-1, 'C:/Users/lishu/Desktop/ob/xx/face/axe/axe')

数据预处理

  1. IMAGE_SIZE = 64 #将图片大小设置为64*64
  2. #按照指定图像大小调整尺寸
  3. def resize_image(image, height = IMAGE_SIZE, width = IMAGE_SIZE):
  4. top, bottom, left, right = (0, 0, 0, 0)
  5. #获取图像尺寸
  6. h, w, _ = image.shape
  7. #对于长宽不相等的图片,找到最长的一边
  8. longest_edge = max(h, w)
  9. #计算短边需要增加多少像素宽度使其与长边等长
  10. if h < longest_edge:
  11. dh = longest_edge - h
  12. top = dh // 2
  13. bottom = dh - top
  14. elif w < longest_edge:
  15. dw = longest_edge - w
  16. left = dw // 2
  17. right = dw - left
  18. else:
  19. pass
  20. #RGB颜色
  21. BLACK = [0, 0, 0]
  22. #给图像增加边界,是图片长、宽等长,cv2.BORDER_CONSTANT指定边界颜色由value指定
  23. constant = cv2.copyMakeBorder(image, top , bottom, left, right, cv2.BORDER_CONSTANT, value = BLACK)
  24. #将图像设置为灰度图
  25. constant = cv2.cvtColor(constant,cv2.COLOR_BGR2GRAY)
  26. #调整图像大小并返回
  27. return cv2.resize(constant, (height, width))
  28. def read_path(path_name):
  29. for dir_item in os.listdir(path_name):
  30. #从初始路径开始叠加,合并成可识别的操作路径
  31. full_path = os.path.abspath(os.path.join(path_name, dir_item))
  32. if os.path.isdir(full_path): #如果是文件夹,继续递归调用
  33. read_path(full_path)
  34. else: #文件
  35. if dir_item.endswith('.jpg'):
  36. image = cv2.imread(full_path)
  37. image = resize_image(image, IMAGE_SIZE, IMAGE_SIZE)
  38. images.append(image)
  39. labels.append(path_name)
  40. return images,labels
  41. #从指定路径读取训练数据
  42. def load_dataset(path_name):
  43. images,labels = read_path(path_name)
  44. #将输入的所有图片转成四维数组,尺寸为(图片数量*IMAGE_SIZE*IMAGE_SIZE*3)
  45. #尺寸为 200*5* 64 * 64 * 3
  46. #5个人 每个人200张 图片为64 * 64像素,一个像素3个颜色值(RGB)
  47. images = np.array(images)
  48. print(images.shape)
  49. #标注数据(采用onehot编码),分别不同指定标签(请注意必须从0开始算标签)
  50. temp=0
  51. for label in labels :
  52. if label.endswith('axe') :
  53. labels[temp]=0
  54. elif label.endswith('ef') :
  55. labels[temp]=1
  56. temp=temp+1
  57. return images, labels

训练模型

adam优化器参数learning_rate根据经验设置为0.001,

batch设置为2的幂次,数值越大,收敛越快,但单次迭代时间越长。

epoch参数,设置学习轮数,根据训练准确度来设置,这里设置为100,不宜过大,防止过拟合。

  1. class Dataset:
  2. def __init__(self, path_name):
  3. #训练集
  4. self.train_images = None
  5. self.train_labels = None
  6. #测试集
  7. self.test_images = None
  8. self.test_labels = None
  9. #数据集加载路径
  10. self.path_name = path_name
  11. #当前库采用的维度顺序
  12. self.input_shape = None
  13. self.nb_classes=None
  14. #加载数据集并按照交叉验证的原则划分数据集并进行相关预处理工作
  15. def load(self, img_rows = IMAGE_SIZE, img_cols = IMAGE_SIZE,
  16. img_channels = 1, nb_classes = 5): #灰度图所以通道数为1-5个类别 所以分组数为5
  17. #加载数据集到内存
  18. images, labels = load_dataset(self.path_name)
  19. train_images, test_images, train_labels, test_labels = train_test_split(images, labels, test_size = 0.3, random_state = random.randint(0, 100)) #将总数据按0.3比重随机分配给训练集和测试集
  20. train_images = train_images.reshape(train_images.shape[0], img_rows, img_cols, img_channels) #由于TensorFlow需要通道数,我们上一步设置为灰度图,所以这里为1,否则彩色图为3
  21. test_images = test_images.reshape(test_images.shape[0], img_rows, img_cols, img_channels)
  22. self.input_shape = (img_rows, img_cols, img_channels)
  23. #输出训练集、测试集的数量
  24. print(train_images.shape[0], 'train samples')
  25. print(test_images.shape[0], 'test samples')
  26. #像素数据浮点化以便归一化
  27. train_images = train_images.astype('float32')
  28. test_images = test_images.astype('float32')
  29. #将其归一化,图像的各像素值归一化到0~1区间
  30. train_images /= 255
  31. test_images /= 255
  32. self.train_images = train_images
  33. self.test_images = test_images
  34. self.train_labels = train_labels
  35. self.test_labels = test_labels
  36. self.nb_classes = nb_classes
  37. #建立CNN模型
  38. class CNN(tf.keras.Model):
  39. #模型初始化
  40. def __init__(self):
  41. super().__init__()
  42. self.conv1 = tf.keras.layers.Conv2D(
  43. filters=32, # 卷积层神经元(卷积核)数目
  44. kernel_size=[3, 3], # 感受野大小
  45. padding='same', # padding策略(vaild 或 same)
  46. activation=tf.nn.relu, # 激活函数
  47. )
  48. self.conv3=tf.keras.layers.Conv2D( filters=32, kernel_size=[3, 3], activation=tf.nn.relu )
  49. self.pool3 = tf.keras.layers.MaxPool2D(pool_size=[2, 2])
  50. self.conv4=tf.keras.layers.Conv2D( filters=64, kernel_size=[3, 3], padding='same', activation=tf.nn.relu )
  51. self.conv5=tf.keras.layers.Conv2D( filters=64, kernel_size=[3, 3], activation=tf.nn.relu )
  52. self.pool4 = tf.keras.layers.MaxPool2D(pool_size=[2, 2])
  53. self.flaten1=tf.keras.layers.Flatten()
  54. self.dense3 = tf.keras.layers.Dense(units=512,activation=tf.nn.relu)
  55. self.dense4 = tf.keras.layers.Dense(units=5) #最后分类 5个单位
  56. #模型输出
  57. def call(self, inputs):
  58. x = self.conv1(inputs)
  59. x = self.conv3(x)
  60. x = self.pool3(x)
  61. x = self.conv4(x)
  62. x = self.conv5(x)
  63. x = self.pool4(x)
  64. x = self.flaten1(x)
  65. x = self.dense3(x)
  66. x = self.dense4(x)
  67. output = tf.nn.softmax(x)
  68. return output
  69. #识别人脸
  70. def face_predict(self, image):
  71. image = resize_image(image)
  72. image = image.reshape((1, IMAGE_SIZE, IMAGE_SIZE, 1))
  73. #浮点并归一化
  74. image = image.astype('float32')
  75. image /= 255
  76. #给出输入属于各个类别的概率
  77. result = self.predict(image)
  78. #print('result:',result[0])
  79. #返回类别预测结果
  80. return result[0]
  1. if __name__ == '__main__':
  2. learning_rate = 0.001 #学习率
  3. batch = 32 #batch数
  4. EPOCHS = 200 #学习轮数
  5. dataset = Dataset('./face/') #数据都保存在这个文件夹下
  6. images = []
  7. labels = []
  8. dataset.load()
  9. model = CNN()#模型初始化
  10. optimizer = tf.keras.optimizers.Adam(learning_rate=learning_rate) #选择优化器
  11. loss_object = tf.keras.losses.SparseCategoricalCrossentropy() #选择损失函数
  12. train_loss = tf.keras.metrics.Mean(name='train_loss') #设置变量保存训练集的损失值
  13. train_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(name='train_accuracy')#设置变量保存训练集的准确值
  14. test_loss = tf.keras.metrics.Mean(name='test_loss')#设置变量保存测试集的损失值
  15. test_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(name='test_accuracy')#设置变量保存测试集的准确值
  16. @tf.function
  17. def train_step(images, labels):
  18. with tf.GradientTape() as tape:
  19. predictions = model(images)
  20. loss = loss_object(labels, predictions)
  21. gradients = tape.gradient(loss, model.trainable_variables)
  22. optimizer.apply_gradients(zip(gradients, model.trainable_variables))#优化器更新数据
  23. train_loss(loss)#更新损失值
  24. train_accuracy(labels, predictions)#更新准确值
  25. @tf.function
  26. def test_step(images, labels):
  27. predictions = model(images)
  28. t_loss = loss_object(labels, predictions)
  29. test_loss(t_loss)
  30. test_accuracy(labels, predictions)
  31. for epoch in range(EPOCHS):
  32. train_ds = tf.data.Dataset.from_tensor_slices((dataset.train_images, dataset.train_labels)).shuffle(300).batch(batch)
  33. test_ds = tf.data.Dataset.from_tensor_slices((dataset.test_images, dataset.test_labels)).shuffle(300).batch(batch)
  34. for images, labels in train_ds:
  35. train_step(images, labels)
  36. for test_images, test_labels in test_ds:
  37. test_step(test_images, test_labels)
  38. template = 'Epoch {} \nTrain Loss:{:.2f},Train Accuracy:{:.2%}\nTest Loss :{:.2f},Test Accuracy :{:.2%}'
  39. print (template.format(epoch+1,train_loss.result(),train_accuracy.result(),test_loss.result(),test_accuracy.result())) #打印
  40. model.save_weights('./model/face') #保存权重模型 命名为face

测试模型

使用摄像头检测人脸,如检测视频中的人脸,改变cap = cv2.VideoCapture()参数,()中添加视频地址。

按q键退出检测窗口。

  1. if __name__ == '__main__':
  2. #加载模型
  3. model = CNN()
  4. model.load_weights('./model/face') #读取模型权重参数
  5. #框住人脸的矩形边框颜色
  6. color = (0, 255, 255)
  7. #捕获指定摄像头的实时视频流
  8. cap = cv2.VideoCapture(0)
  9. #人脸识别分类器本地存储路径
  10. cascade_path ="C:/Users/lishu/Anaconda3/Lib/site-packages/cv2/data/haarcascade_frontalface_alt2.xml"
  11. #循环检测识别人脸
  12. while True:
  13. ret, frame = cap.read() #读取一帧视频
  14. if ret is True:
  15. #图像灰化,降低计算复杂度
  16. frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
  17. else:
  18. continue
  19. #使用人脸识别分类器,读入分类器
  20. cascade = cv2.CascadeClassifier(cascade_path)
  21. #利用分类器识别出哪个区域为人脸
  22. faceRects = cascade.detectMultiScale(frame_gray, scaleFactor = 1.2, minNeighbors = 3, minSize = (32, 32))
  23. if len(faceRects) > 0:
  24. for faceRect in faceRects:
  25. x, y, w, h = faceRect
  26. #截取脸部图像提交给模型识别这是谁
  27. image = frame[y - 10: y + h + 10, x - 10: x + w + 10]
  28. face_probe = model.face_predict(image) #获得预测值
  29. cv2.rectangle(frame, (x - 10, y - 10), (x + w + 10, y + h + 10), color, thickness = 2)
  30. frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) # cv2和PIL中颜色的hex码的储存顺序不同
  31. pilimg = Image.fromarray(frame)
  32. draw = ImageDraw.Draw(pilimg) # 图片上打印出所有人的预测值
  33. font = ImageFont.truetype("simkai.ttf", 20, encoding="utf-8") # 参数1:字体文件路径,参数2:字体大小q
  34. # draw.text((x+25,y-95), 'axe:{:.2%}'.format(face_probe[0]), (0, 255, 0), font=font)
  35. # draw.text((x+25,y-70), 'ef:{:.2%}'.format(face_probe[1]), (0, 0, 255), font=font)
  36. if face_probe[0] > 0.7:
  37. draw.text((x+25,y-75), 'axe:{:.2%}'.format(face_probe[0]), (0, 0, 255), font=font)
  38. elif face_probe[1] > 0.7:
  39. draw.text((x+25,y-75), 'ef:{:.2%}'.format(face_probe[1]), (0, 255, 0), font=font)
  40. else:
  41. draw.text((x+25, y-75), 'stranger',(255, 0, 0), font=font)
  42. frame = cv2.cvtColor(np.array(pilimg), cv2.COLOR_RGB2BGR)
  43. cv2.imshow("ShowTime", frame)
  44. #等待10毫秒看是否有按键输入
  45. k = cv2.waitKey(10)
  46. #如果输入q则退出循环
  47. if k & 0xFF == ord('q'):
  48. break
  49. #释放摄像头并销毁所有窗口
  50. cap.release()
  51. cv2.destroyAllWindows()

对if __name__ == '__main__':的解释,作为脚本时会执行,如果作为模块加载到其他脚本不会执行。

本文使用jupyter notebook执行代码。

捕捉图像模块,建议捕捉两人以上人脸,否则容易过拟合。在face文件夹下建立人脸数据文件夹。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/360451
推荐阅读
相关标签
  

闽ICP备14008679号