前文博客有讲到如何安装anaconda,这是一个非常好用的环境管理工具。在这里我将使用他来快速构建环境。为了提升下载速度,建议为anaconda换清华源,操作步骤请阅读官网:Anaconda 镜像使用帮助https://mirror.tuna.tsinghua.edu.cn/help/anaconda/
之前博文中安装的python版本为3.7,对应的tensorflow为2.4.1,导致numpy版本与其他包冲突,降级为python3.6与tensorflow2.1.0可以完美解决问题。若仍出现问题,可以对numpy降级为1.19.5。在使用pip卸载,安装指定版本的numpy时,可能会出现红色警告,警告中会详细指明哪些库的版本不对,需按照提示逐一修改版本。
可能用到的指令:(其余有问题的库类似,印象中有3个库有问题,six和scipy,scikit-learn都经历过类似下面的操作)
pip show numpy pip uninstall numpy pip install numpy==1.19.5
如果你已经安装好环境,并且没有或解决了上述问题,请直接跳过此部分,可以开始实现人脸识别的代码了,如果没有,我将简要讲述如何构建环境。
conda create –n tf python=3.6 conda activate tf
conda install tensorflow
conda安装tensorflow
这里建议大家不再使用pip下载tensorflow,conda会帮你一键搞定cudann的匹配问题,并为你选择合适的tensorflow版本
conda activate tf pip install numpy matplotlib scikit-learn opencv-python
如果后续出现库版本不匹配的问题,请参考最开始的解决方法。
我们首先要解决的是深度学习需要的数据问题,我采用opencv进行图像处理。并且opencv有官方训练好的参数用于检验是否是人脸,文件haarcascade_frontalface_alt2.xml
的下载连接:https://blog.csdn.net/songjinxaing/article/details/79958082
你需要将次xml文件放在代码的文件夹下,或者记住他的绝对路径,然后在代码所在的文件夹下新建data文件夹,用于存放照片。不同的人存放于不同的文件夹下如图:(代码在tensorflow中)
关于opencv这里不再细讲,通过注释应该可以读懂逻辑。下面代码将会调用笔记本的摄像头为你采集1000张照片用于人脸识别,当然部分照片会截的不好需要你进行筛选,将只有半张脸的照片删除。
#这是take_pictures.py 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("E:\\DeepLearning\\tensorflow\\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, 'E:\\DeepLearning\\tensorflow\\data\\liujiexuan')
首先定义一个类Dataset
,他包含两个方法,初始化__init__()
,载入数据load()
初始化时我们规定三个数据集,验证集,训练集,还有测试集。以及照片所在路径
在load()
中,我们需要对数据进行预处理。首先把数据分类,使用sklearn
库中的train_test_split
函数随机挑选样本形成三个数据集,并将其转化为浮点数再除以255进行归一化。
定义一个类Model
,首先定义初始化方法__init__()
,下来build_mode()
方法定义网络结构,train()
方法用于编译,训练网络。save_model(),load_model()
方法用来保存和载入网络。evaluate()
方法可以评估模型准确度。face_predict()
方法可以对单张图片进行预测。
# 这是face_train_tensorflow.py # TensorFlow and tf.keras from numpy.lib.function_base import select import tensorflow as tf from tensorflow import keras from tensorflow.python.keras.utils.np_utils import to_categorical from tensorflow.keras import datasets, layers, models # Helper libraries import numpy as np import matplotlib.pyplot as plt import random from sklearn.model_selection import train_test_split #from sklearn.model_selection import train_test_split # load_data.py 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)) 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') #像素数据浮点化以便归一化 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 class Model: def __init__(self): self.model = None def build_model(self, dataset, nb_classes = 2): self.model = models.Sequential() self.model.add(layers.Conv2D(32, (3, 3), activation='relu',padding ="same", input_shape = dataset.input_shape)) self.model.add(layers.Conv2D(32, (3, 3), activation='relu')) self.model.add(layers.MaxPooling2D((2, 2))) self.model.add(layers.Dropout(rate=0.25)) self.model.add(layers.Conv2D(64, (3, 3), activation='relu')) self.model.add(layers.Conv2D(64, (3, 3), activation='relu')) self.model.add(layers.MaxPooling2D((2, 2))) self.model.add(layers.Dropout(rate=0.25)) self.model.add(layers.Flatten()) self.model.add(layers.Dense(512, activation='relu')) self.model.add(layers.Dropout(rate=0.5)) self.model.add(layers.Dense(nb_classes)) #输出模型概况 self.model.summary() def train(self,dataset,epochs=10): self.model.compile(optimizer='adam', loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True), metrics=['accuracy']) self.model.fit(dataset.train_images, dataset.train_labels, epochs, validation_data=(dataset.valid_images, dataset.valid_labels)) MODEL_PATH = './ljx_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 = models.load_model(file_path) self.model.summary() 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): #依然是根据后端系统确定维度顺序 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] dataset = Dataset('E:\\DeepLearning\\tensorflow\\data') dataset.load() model = Model() #先前添加的测试build_model()函数的代码 model.build_model(dataset) #测试训练函数的代码 model.train(dataset) model.save_model(file_path='./ljx_face_model.h5') model.load_model(file_path='./ljx_face_model.h5') model.evaluate(dataset)
#这是face_con.py import cv2 import sys import gc from face_train_tensorflow import Model model = Model() model.load_model(file_path = 'E:\\DeepLearning\\tensorflow\\ljx_face_model.h5') #框住人脸的矩形边框颜色 color = (0, 255, 0) #捕获指定摄像头的实时视频流 cap = cv2.VideoCapture(0) #人脸识别分类器本地存储路径 cascade_path = "E:\DeepLearning\tensorflow\haarcascade_frontalface_alt2.xml" #循环检测识别人脸 while True: ret, frame = cap.read() #读取一帧视频 # if ret is True: # #图像灰化,降低计算复杂度 # frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # else: # continue grey = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) #使用人脸识别分类器,读入分类器 classfier = cv2.CascadeClassifier("E:\\DeepLearning\\tensorflow\\haarcascade_frontalface_alt2.xml") #利用分类器识别出哪个区域为人脸 faceRects = classfier.detectMultiScale(grey, scaleFactor = 1.1, minNeighbors = 4, 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 = face_predict(image) 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,'liujiexuan', (x + 30, y + 30), #坐标 cv2.FONT_HERSHEY_SIMPLEX, #字体 1, #字号 (255,0,255), #颜色 2) #字的线宽 else: cv2.putText(frame,'liuji', (x + 30, y + 30), #坐标 cv2.FONT_HERSHEY_SIMPLEX, #字体 1, #字号 (255,0,255), #颜色 2) #字的线宽 cv2.imshow("face_predict", frame) #等待10毫秒看是否有按键输入 k = cv2.waitKey(10) #如果输入q则退出循环 if k & 0xFF == ord('q'): break #释放摄像头并销毁所有窗口 cap.release() cv2.destroyAllWindows()
前任栽树,后人乘凉。感谢就是这个七昂的博客分享
本文使用tensorflow2进行复现,改变了网络变异和训练方法,但大体结构没有改变。